Vaadin + Spring Boot Production mode build with a runtime error: Failed to determine 'npm' tool

Vaadin + Spring Boot Production mode build with a runtime error: Failed to determine 'npm' tool

解决方案更新 这个错误似乎是问题的原因:https://github.com/vaadin/flow/issues/6657

修复推出后,标记的答案可用作解决方法

背景

我正在创建基于 Spring Boot + Vaadin 流的 dockerized portal for my IoT project

问题

我的机器在 Vaadin 的开发模式下安装了 npm,门户网站运行良好 运行,但我似乎无法获得正确的设置来创建已经包含所有生成的前端的轻量级部署 jar端组件。

当我运行 Docker 容器中的服务时,我收到错误消息,指出它找不到 npm,但我认为生产部署不需要它

at com.vaadin.flow.server.startup.DevModeInitializer.initDevModeHandler(DevModeInitializer.java:327)
    at com.vaadin.flow.spring.VaadinServletContextInitializer$DevModeServletContextListener.contextInitialized(VaadinServletContextInitializer.java:323)
    ... 46 common frames omitted
Caused by: com.vaadin.flow.server.ExecutionFailedException: 

Failed to determine 'npm' tool.
Please install it either:
  - by following the https://nodejs.org/en/download/ guide to install it globally
  - or by running the frontend-maven-plugin goal to install it in this project:
  $ mvn com.github.eirslett:frontend-maven-plugin:1.7.6:install-node-and-npm -DnodeVersion="v12.13.0" 

Docker 文件 没有 npm:

FROM openjdk:11-jre-slim

ARG PROJECT
ARG SERVICE_PORT
ARG JAR_FILE

EXPOSE ${SERVICE_PORT}

RUN mkdir /${PROJECT}
WORKDIR /${PROJECT}

ADD target/${JAR_FILE} ./app.jar

CMD ["java","-jar","app.jar"]

pom.xml(已更新!)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.tlvlp</groupId>
    <artifactId>iot-portal</artifactId>
    <version>0.0.2</version>
    <name>iot-portal</name>
    <description>tlvlp IoT server portal</description>

    <properties>
        <java.version>11</java.version>
        <vaadin.version>14.0.12</vaadin.version>
        <dockerfile.maven.version>1.4.13</dockerfile.maven.version>
        <flow.server.prod.version>2.0.17</flow.server.prod.version>
        <!--    DOCKER IMAGE ARGS   -->
        <docker.project.repository>tlvlp/iot-portal</docker.project.repository>
        <service.port>8600</service.port>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-bom</artifactId>
                <version>${vaadin.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

        </plugins>
    </build>

    <profiles>
        <profile>
            <id>prod</id>
            <properties>
                <activatedProperties>prod</activatedProperties>
                <vaadin.productionMode>true</vaadin.productionMode>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>com.vaadin</groupId>
                    <artifactId>flow-server-production-mode</artifactId>
                    <version>${flow.server.prod.version}</version>
                </dependency>
            </dependencies>

            <build>
                <plugins>
                    <plugin>
                        <groupId>com.vaadin</groupId>
                        <artifactId>flow-maven-plugin</artifactId>
                        <version>${flow.server.prod.version}</version>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>build-frontend</goal>
                                    <goal>copy-production-files</goal>
                                    <goal>package-for-production</goal>
                                </goals>
                                <phase>compile</phase>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>com.spotify</groupId>
                        <artifactId>dockerfile-maven-plugin</artifactId>
                        <version>${dockerfile.maven.version}</version>
                        <executions>
                            <execution>
                                <id>prod</id>
                                <goals>
                                    <goal>build</goal>
                                    <goal>push</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <repository>${docker.project.repository}</repository>
                            <tag>${project.version}</tag>
                            <tag>latest</tag>
                            <buildArgs>
                                <PROJECT>${project.groupId}.${project.artifactId}</PROJECT>
                                <SERVICE_PORT>${service.port}</SERVICE_PORT>
                                <JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
                            </buildArgs>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

        <profile>
            <id>dev</id>
            <properties>
                <activatedProperties>dev</activatedProperties>
                <vaadin.productionMode>false</vaadin.productionMode>
            </properties>
        </profile>
    </profiles>

</project>

application.properties

spring.profiles.active=@activatedProperties@

申请-prod.properties

vaadin.compatibilityMode=false
vaadin.servlet.productionMode=true

感谢任何指导:)

所有代码都可以在项目 public 存储库的主分支上找到:https://github.com/tlvlp/iot-portal

由于我最近遇到了同样的问题,所以我有以下解决方案。我发现拥有 Java 和 Nodejs/NPM 的最简单方法是将其安装到 java 基本映像。这不会是一个小 docker 图片。 mvn clean package -Pproduction vaadin 根目录。然后编辑 Dockerfile 以包含安装,如下所示:https://gitlab.com/snippets/1913782

然后构建并 运行 映像。构建时间和图像大小会增加很多。我很乐意看到在此过程中取得的一些建议或其他进展。从 Vaadin8 进入 14 已经造成了许多令人头疼的问题,因为必须了解为什么非 Java 的东西会被破坏。

我终于找到了解决问题的方法。 运行 生产应用程序不需要 npm,这是一个正确的假设。 不清楚的是 from even the official guide 尽管将以下 属性 添加到产品构建配置文件中确实会填充到构建的 jar 文件中,但是

本身不会在生产模式下触发 Vaadin 到 运行(至少不与 SpringBoot 一起使用):

<vaadin.productionMode>true</vaadin.productionMode>

解法:

最终解决问题的方法是在SpringBoot属性文件中也为相同的参数赋值。 然后它开始使用在构建时使用 npm 生成的资产。

这里是 application.properties 文件(由 dev 和 prod 属性文件共享)。

spring.profiles.active=@spring.activatedProperties@
vaadin.productionMode=@vaadin.productionMode@
vaadin.compatibilityMode=false

这是完整的、更新的和工作的 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.tlvlp</groupId>
    <artifactId>iot-portal</artifactId>
    <version>0.0.2</version>
    <name>iot-portal</name>
    <description>tlvlp IoT server portal</description>

    <properties>
        <java.version>11</java.version>
        <vaadin.version>14.0.12</vaadin.version>
        <dockerfile.maven.version>1.4.13</dockerfile.maven.version>
        <flow.server.prod.version>2.0.17</flow.server.prod.version>
        <!--    DOCKER IMAGE ARGS   -->
        <docker.project.repository>tlvlp/iot-portal</docker.project.repository>
        <service.port>8600</service.port>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-bom</artifactId>
                <version>${vaadin.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

        </plugins>
    </build>

    <profiles>
        <profile>
            <id>prod</id>
            <properties>
                <spring.activatedProperties>prod</spring.activatedProperties>
                <vaadin.productionMode>true</vaadin.productionMode>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>com.vaadin</groupId>
                    <artifactId>flow-server-production-mode</artifactId>
                    <version>${flow.server.prod.version}</version>
                </dependency>
            </dependencies>

            <build>
                <plugins>
                    <plugin>
                        <groupId>com.vaadin</groupId>
                        <artifactId>flow-maven-plugin</artifactId>
                        <version>${flow.server.prod.version}</version>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>prepare-frontend</goal>
                                    <goal>build-frontend</goal>

                                </goals>
                                <phase>compile</phase>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>com.spotify</groupId>
                        <artifactId>dockerfile-maven-plugin</artifactId>
                        <version>${dockerfile.maven.version}</version>
                        <executions>
                            <execution>
                                <id>prod</id>
                                <goals>
                                    <goal>build</goal>
                                    <goal>push</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <repository>${docker.project.repository}</repository>
                            <tag>${project.version}</tag>
                            <tag>latest</tag>
                            <buildArgs>
                                <PROJECT>${project.groupId}.${project.artifactId}</PROJECT>
                                <SERVICE_PORT>${service.port}</SERVICE_PORT>
                                <JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
                            </buildArgs>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

        <profile>
            <id>dev</id>
            <properties>
                <spring.activatedProperties>dev</spring.activatedProperties>
                <vaadin.productionMode>false</vaadin.productionMode>
            </properties>
        </profile>
    </profiles>

</project>