使用 bitbucket 管道部署到 heroku 的 Spring Boot gradle 项目在 build/libs/app-SNAPSHOT-plain.jar 中给出错误 no main m

Posted

技术标签:

【中文标题】使用 bitbucket 管道部署到 heroku 的 Spring Boot gradle 项目在 build/libs/app-SNAPSHOT-plain.jar 中给出错误 no main manifest 属性【英文标题】:Spring boot gradle project deployed to heroku using bitbucket pipeline gives error no main manifest attribute, in build/libs/app-SNAPSHOT-plain.jar 【发布时间】:2021-12-11 12:16:03 【问题描述】:

我有一个使用 IntelliJIDEA 和 gradle.build 构建的 Spring boot gradle 项目:

plugins 
    id 'org.springframework.boot' version '2.5.5'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'


group = 'pk.inlab.app.web'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations 
    compileOnly 
        extendsFrom annotationProcessor
    


repositories 
    mavenCentral()


dependencies 
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-mail'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-hateoas'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

    // Apache commons
    implementation 'commons-beanutils:commons-beanutils:1.9.4'
  
    // Jwt
    implementation 'io.jsonwebtoken:jjwt:0.9.1'

    // OpenAPI and Swagger
    implementation 'org.springdoc:springdoc-openapi-ui:1.5.11'
    implementation 'org.springdoc:springdoc-openapi-data-rest:1.5.11'

    // Passy
    implementation 'org.passay:passay:1.6.1'

    // Thymeleaf for registration email with template
    implementation 'org.thymeleaf:thymeleaf-spring5'


    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'org.postgresql:postgresql'

    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
    // Rest Assured
    testImplementation 'io.rest-assured:spring-mock-mvc:4.4.0'
    // hamcrest-library
    testImplementation 'org.hamcrest:hamcrest-library:2.2'



test 
    useJUnitPlatform()

当使用管道设置文件 bitbucket-pipelines.yml 推送到 bitbucket 时,该项目在本地机器上运行良好:

#  Template Java Gradle build

#  This template allows you to test and build your Java project with Gradle.
#  The workflow allows running tests, code checkstyle and security scans on the default branch.

# Prerequisites: appropriate project structure should exist in the repository.

image: gradle:7.2

pipelines:
  default:
    - parallel:
      - step:
          name: Build and Test
          caches:
            - gradle
          script:
            - gradle clean build
            - git archive --format=tar.gz master -o application.tar.gz 
            - pipe: atlassian/heroku-deploy:1.2.1
              variables:
                HEROKU_API_KEY: $HEROKU_API_KEY
                HEROKU_APP_NAME: $HEROKU_APP_NAME
                ZIP_FILE : "application.tar.gz"
          after-script:
            - pipe: atlassian/checkstyle-report:0.2.0
      - step:
          name: Security Scan
          script:
            # Run a security scan for sensitive data.
            # See more security tools at https://bitbucket.org/product/features/pipelines/integrations?&category=security
            - pipe: atlassian/git-secrets-scan:0.4.3

构建日志都是正确的,它显示:

-----> Building on the Heroku-20 stack
-----> Using buildpack: heroku/gradle
-----> Gradle app detected
-----> Spring Boot detected
-----> Installing JDK 1.8... done
-----> Building Gradle app...
-----> executing ./gradlew build -x check
       To honour the JVM settings for this build a single-use Daemon process will be forked. See https://docs.gradle.org/7.2/userguide/gradle_daemon.html#sec:disabling_the_daemon.
       Daemon will be stopped at the end of the build 
       > Task :compileJava
       > Task :processResources
       > Task :classes
       > Task :bootJarMainClassName
       > Task :bootJar
       > Task :jar
       > Task :assemble
       > Task :build
       
       BUILD SUCCESSFUL in 20s
       5 actionable tasks: 5 executed
-----> Discovering process types
       Procfile declares types     -> (none)
       Default types for buildpack -> web
-----> Compressing...
       Done: 99.4M
-----> Launching...
       Released v8
       https://rest-todo-work.herokuapp.com/ deployed to Heroku

毕竟当我访问它的网站时:

当我检查 heroku 日志时,它显示:

2021-10-26T04:35:07.000000+00:00 app[api]: Build started by user superuseremail@gmail.com
2021-10-26T04:35:46.654664+00:00 app[api]: Release v8 created by user superuseremail@gmail.com
2021-10-26T04:35:46.654664+00:00 app[api]: Deploy cf272d57 by user superuseremail@gmail.com
2021-10-26T04:35:46.918113+00:00 heroku[web.1]: State changed from crashed to starting
2021-10-26T04:35:50.193127+00:00 heroku[web.1]: Starting process with command `java -Dserver.port=37836 $JAVA_OPTS -jar build/libs/*.jar`
2021-10-26T04:35:50.877195+00:00 app[web.1]: Create a Procfile to customize the command used to run this process: https://devcenter.heroku.com/articles/procfile
2021-10-26T04:35:50.890431+00:00 app[web.1]: Setting JAVA_TOOL_OPTIONS defaults based on dyno size. Custom settings will override them.
2021-10-26T04:35:50.894017+00:00 app[web.1]: Picked up JAVA_TOOL_OPTIONS: -Xmx300m -Xss512k -XX:CICompilerCount=2 -Dfile.encoding=UTF-8
2021-10-26T04:35:50.954182+00:00 app[web.1]: no main manifest attribute, in build/libs/to-do-0.0.1-SNAPSHOT-plain.jar
2021-10-26T04:35:51.082759+00:00 heroku[web.1]: Process exited with status 1
2021-10-26T04:35:51.148321+00:00 heroku[web.1]: State changed from starting to crashed
2021-10-26T04:36:03.000000+00:00 app[api]: Build succeeded
2021-10-26T04:39:01.736780+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=rest-todo-work.herokuapp.com request_id=21a8013c-0771-4758-8245-a575145bc3b0 fwd="111.119.183.2" dyno= connect= service= status=503 bytes= protocol=https
2021-10-26T04:39:02.249498+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/favicon.ico" host=rest-todo-work.herokuapp.com request_id=7fd63420-a032-49ef-bd81-990df93bc5d1 fwd="111.119.183.2" dyno= connect= service= status=503 bytes= protocol=https

好吧,当我想解决no main manifest attribute,时,我发现了一个SO post,它建议添加:

jar 
    manifest 
        attributes 'Main-Class': 'pk.inlab.app.web.todo.ToDoApplication'
    

在 gradle 文件中,我添加了 heroku 日志:

2021-10-26T04:28:04.396482+00:00 heroku[web.1]: State changed from crashed to starting
2021-10-26T04:28:07.756486+00:00 heroku[web.1]: Starting process with command `java -Dserver.port=45162 $JAVA_OPTS -jar build/libs/*.jar`
2021-10-26T04:28:09.250355+00:00 app[web.1]: Create a Procfile to customize the command used to run this process: https://devcenter.heroku.com/articles/procfile
2021-10-26T04:28:09.274928+00:00 app[web.1]: Setting JAVA_TOOL_OPTIONS defaults based on dyno size. Custom settings will override them.
2021-10-26T04:28:09.278725+00:00 app[web.1]: Picked up JAVA_TOOL_OPTIONS: -Xmx300m -Xss512k -XX:CICompilerCount=2 -Dfile.encoding=UTF-8
2021-10-26T04:28:09.420855+00:00 app[web.1]: Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication
2021-10-26T04:28:09.420930+00:00 app[web.1]: at pk.inlab.app.web.todo.ToDoApplication.main(ToDoApplication.java:12)
2021-10-26T04:28:09.420987+00:00 app[web.1]: Caused by: java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
2021-10-26T04:28:09.421018+00:00 app[web.1]: at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
2021-10-26T04:28:09.421046+00:00 app[web.1]: at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
2021-10-26T04:28:09.421075+00:00 app[web.1]: at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
2021-10-26T04:28:09.421102+00:00 app[web.1]: at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
2021-10-26T04:28:09.421129+00:00 app[web.1]: ... 1 more
2021-10-26T04:28:09.609559+00:00 heroku[web.1]: Process exited with status 1

这增加了一个新错误。对这些问题的任何建议。

【问题讨论】:

【参考方案1】:

您似乎正在部署/启动错误的 jar 文件。

2021-10-26T04:35:50.954182+00:00 app[web.1]:没有主清单属性,在 build/libs/to-do-0.0.1-SNAPSHOT-plain.jar 中

Spring Boot Gradle 插件在执行gradlew build 时会给你两个jar 文件:

    build/libs/to-do-0.0.1-SNAPSHOT-plain.jar - 默认创建 jar 任务,仅包含项目的编译源和资源。即使添加 Main-Class 清单属性,依赖项也不存在,因此会引发 ClassNotFoundExceptionbuild/libs/to-do-0.0.1-SNAPSHOT.jar - 由bootJar 任务贡献,除了常规的jar 内容外,还包括运行应用程序所需的所有依赖项,如java -jar to-do-0.0.1-SNAPSHOT.jar

话虽如此,您必须部署build/libs/to-do-0.0.1-SNAPSHOT.jar 而不是-plain

根据 Heroku 输出 (build/libs/*.jar),可能两者都已部署并启动:

使用命令java -Dserver.port=37836 $JAVA_OPTS -jar build/libs/*.jar启动进程

如果不了解 Heroku,您可以通过仅构建 bootJar 来防止这种情况发生,例如通过在构建管道中跳过jar 任务:gradlew clean build -x jar

【讨论】:

怎么才能知道呢?我应该在哪里为特定的build/libs/to-do-0.0.1-SNAPSHOT.jar 添加配置。 @ArshadAli 答案已更新,尝试使用 gradlew clean build -x jar 构建【参考方案2】:

我遇到了同样的问题,并试图搜索一个合法的解决方案,最后我发现,你所要做的就是添加:

bootJar 
    archiveClassifier.set('boot')


jar 
    archiveClassifier.set('')

让 gradle 处理所有其他事情,让我粘贴有效的 gradle.build 文件的内容:

plugins 
    id 'org.springframework.boot' version '2.5.5'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'


group = 'pk.inlab.app.web'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations 
    compileOnly 
        extendsFrom annotationProcessor
    


repositories 
    mavenCentral()


dependencies 
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-mail'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-hateoas'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

    // Apache commons
    implementation 'commons-beanutils:commons-beanutils:1.9.4'
    // implementation 'org.apache.commons:commons-collections4:4.4'


    // Jwt
    implementation 'io.jsonwebtoken:jjwt:0.9.1'

    // OpenAPI and Swagger
    implementation 'org.springdoc:springdoc-openapi-ui:1.5.11'
    implementation 'org.springdoc:springdoc-openapi-data-rest:1.5.11'

    // Passy
    implementation 'org.passay:passay:1.6.1'

    // Thymeleaf for registration email with template
    implementation 'org.thymeleaf:thymeleaf-spring5'


    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'org.postgresql:postgresql'

    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
    // Rest Assured
    testImplementation 'io.rest-assured:spring-mock-mvc:4.4.0'
    // hamcrest-library
    testImplementation 'org.hamcrest:hamcrest-library:2.2'



// Add these START
bootJar 
    archiveClassifier.set('boot')


jar 
    archiveClassifier.set('')
    
// Add these END 

test 
    useJUnitPlatform()

这是对我有用的解决方案,希望对你也有用。

【讨论】:

太棒了!这是唯一对我有用的东西(使用 gradle + spring boot) @scibuff 很高兴听到这个消息!!!

以上是关于使用 bitbucket 管道部署到 heroku 的 Spring Boot gradle 项目在 build/libs/app-SNAPSHOT-plain.jar 中给出错误 no main m的主要内容,如果未能解决你的问题,请参考以下文章

Bitbucket管道无法推送到heroku

Bitbucket管道,在NodeJS脚本中使用ENV VARS部署到S3 Deploy

通过 ssh 堡垒主机使用 kubectl 从 bitbucket 管道进行部署

没有容器的 Bitbucket 管道

Bitbucket管道.net部署

使用 BitBucket Pipelines 通过 SSH 访问部署到 VPS