├── .github ├── PULL_REQUEST_TEMPLATE.md ├── RELEASE_TEMPLATES │ └── release_checklist.md └── workflows │ ├── gradle-wrapper-validation.yml │ ├── prepare-release.yml │ ├── sonar.yml │ └── unit-tests.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── first-party ├── README.md ├── build.gradle ├── config │ └── checkstyle │ │ ├── checkstyle-suppressions.xml │ │ └── copyright-java.header ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jib-layer-filter-extension-gradle │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── tools │ │ │ │ └── jib │ │ │ │ └── gradle │ │ │ │ └── extension │ │ │ │ └── layerfilter │ │ │ │ ├── Configuration.java │ │ │ │ └── JibLayerFilterExtension.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension │ │ └── test │ │ └── java │ │ └── com │ │ └── google │ │ └── cloud │ │ └── tools │ │ └── jib │ │ └── gradle │ │ └── extension │ │ └── layerfilter │ │ └── JibLayerFilterExtensionTest.java ├── jib-layer-filter-extension-maven │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── tools │ │ │ │ └── jib │ │ │ │ └── maven │ │ │ │ └── extension │ │ │ │ └── layerfilter │ │ │ │ ├── Configuration.java │ │ │ │ └── JibLayerFilterExtension.java │ │ └── resources │ │ │ └── META-INF │ │ │ ├── services │ │ │ └── com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension │ │ │ └── sisu │ │ │ └── javax.inject.Named │ │ └── test │ │ └── java │ │ └── com │ │ └── google │ │ └── cloud │ │ └── tools │ │ └── jib │ │ └── maven │ │ └── extension │ │ └── layerfilter │ │ └── JibLayerFilterExtensionTest.java ├── jib-native-image-extension-gradle │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── tools │ │ │ │ └── jib │ │ │ │ └── gradle │ │ │ │ └── extension │ │ │ │ └── nativeimage │ │ │ │ └── JibNativeImageExtension.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension │ │ └── test │ │ └── java │ │ └── com │ │ └── google │ │ └── cloud │ │ └── tools │ │ └── jib │ │ └── gradle │ │ └── extension │ │ └── nativeimage │ │ └── JibNativeImageExtensionTest.java ├── jib-native-image-extension-maven │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── tools │ │ │ │ └── jib │ │ │ │ └── maven │ │ │ │ └── extension │ │ │ │ └── nativeimage │ │ │ │ ├── ConfigValueLocation.java │ │ │ │ └── JibNativeImageExtension.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension │ │ └── test │ │ └── java │ │ └── com │ │ └── google │ │ └── cloud │ │ └── tools │ │ └── jib │ │ └── maven │ │ └── extension │ │ └── nativeimage │ │ └── JibNativeImageExtensionTest.java ├── jib-ownership-extension-gradle │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── tools │ │ │ │ └── jib │ │ │ │ └── gradle │ │ │ │ └── extension │ │ │ │ └── ownership │ │ │ │ ├── Configuration.java │ │ │ │ └── JibOwnershipExtension.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension │ │ └── test │ │ └── java │ │ └── com │ │ └── google │ │ └── cloud │ │ └── tools │ │ └── jib │ │ └── gradle │ │ └── extension │ │ └── ownership │ │ └── JibOwnershipExtensionTest.java ├── jib-ownership-extension-maven │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── tools │ │ │ │ └── jib │ │ │ │ └── maven │ │ │ │ └── extension │ │ │ │ └── ownership │ │ │ │ ├── Configuration.java │ │ │ │ └── JibOwnershipExtension.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension │ │ └── test │ │ └── java │ │ └── com │ │ └── google │ │ └── cloud │ │ └── tools │ │ └── jib │ │ └── maven │ │ └── extension │ │ └── ownership │ │ └── JibOwnershipExtensionTest.java ├── jib-quarkus-extension-gradle │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── tools │ │ │ │ └── jib │ │ │ │ └── gradle │ │ │ │ └── extension │ │ │ │ └── quarkus │ │ │ │ ├── JibQuarkusExtension.java │ │ │ │ ├── PackageType.java │ │ │ │ └── resolvers │ │ │ │ ├── JarResolver.java │ │ │ │ ├── JarResolverFactory.java │ │ │ │ └── impl │ │ │ │ ├── FastJarResolver.java │ │ │ │ └── LegacyJarResolver.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension │ │ └── test │ │ └── java │ │ └── com │ │ └── google │ │ └── cloud │ │ └── tools │ │ └── jib │ │ └── gradle │ │ └── extension │ │ └── quarkus │ │ ├── JibQuarkusExtensionTest.java │ │ ├── MockComponentArtifactIdentifier.java │ │ └── MockProjectComponentIdentifier.java ├── jib-quarkus-extension-maven │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── tools │ │ │ │ └── jib │ │ │ │ └── maven │ │ │ │ └── extension │ │ │ │ └── quarkus │ │ │ │ ├── JibQuarkusExtension.java │ │ │ │ ├── PackageType.java │ │ │ │ └── resolvers │ │ │ │ ├── JarResolver.java │ │ │ │ ├── JarResolverFactory.java │ │ │ │ └── impl │ │ │ │ ├── FastJarResolver.java │ │ │ │ └── LegacyJarResolver.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension │ │ └── test │ │ └── java │ │ └── com │ │ └── google │ │ └── cloud │ │ └── tools │ │ └── jib │ │ └── maven │ │ └── extension │ │ └── quarkus │ │ └── JibQuarkusExtensionTest.java ├── jib-spring-boot-extension-gradle │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── tools │ │ │ │ └── jib │ │ │ │ └── gradle │ │ │ │ └── extension │ │ │ │ └── springboot │ │ │ │ └── JibSpringBootExtension.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension │ │ └── test │ │ └── java │ │ └── com │ │ └── google │ │ └── cloud │ │ └── tools │ │ └── jib │ │ └── gradle │ │ └── extension │ │ └── springboot │ │ └── JibSpringBootExtensionTest.java ├── jib-spring-boot-extension-maven │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── tools │ │ │ │ └── jib │ │ │ │ └── maven │ │ │ │ └── extension │ │ │ │ └── springboot │ │ │ │ └── JibSpringBootExtension.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── services │ │ │ └── com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension │ │ └── test │ │ └── java │ │ └── com │ │ └── google │ │ └── cloud │ │ └── tools │ │ └── jib │ │ └── maven │ │ └── extension │ │ └── springboot │ │ └── JibSpringBootExtensionTest.java ├── kokoro │ └── release_build.sh └── settings.gradle └── third-party └── README.md /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for your interest in contributing! For general guidelines, please refer to 2 | the [contributing guide](https://github.com/GoogleContainerTools/jib-extensions/blob/master/CONTRIBUTING.md). 3 | 4 | Before submitting a pull request, please make sure to: 5 | 6 | - [ ] Identify an existing [issue](https://github.com/GoogleContainerTools/jib-extensions/issues) to associate 7 | with your proposed change, or [file a new issue](https://github.com/GoogleContainerTools/jib-extensions/issues/new). 8 | - [ ] Describe any implementation plans in the issue and wait for a review from the repository maintainers. 9 | 10 | Fixes # 🛠️ -------------------------------------------------------------------------------- /.github/RELEASE_TEMPLATES/release_checklist.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Release {{ env.RELEASE_NAME }} 3 | labels: release 4 | --- 5 | ## Requirements 6 | - [ ] ⚠️ Ensure the release process has succeeded before proceeding 7 | 8 | ## Github 9 | - [ ] Update versions in main [README.md](https://github.com/GoogleContainerTools/jib-extensions/blob/master/README.md) (if applicable) 10 | - [ ] Update extension [README.md]({{ env.README_URL }}) 11 | - [ ] Merge PR ({{ env.RELEASE_PR }}) 12 | -------------------------------------------------------------------------------- /.github/workflows/gradle-wrapper-validation.yml: -------------------------------------------------------------------------------- 1 | name: "Validate Gradle Wrapper" 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | validation: 6 | name: "Gradle wrapper validation" 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: gradle/wrapper-validation-action@v1 11 | -------------------------------------------------------------------------------- /.github/workflows/prepare-release.yml: -------------------------------------------------------------------------------- 1 | name: Prepare Jib extension release 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | jib_extension: 6 | description: Jib extension to release 7 | required: true 8 | default: (jib-layer-filter-extension-gradle | ...) 9 | release_version: 10 | description: new release version 11 | required: true 12 | default: (for example, 0.2.0) 13 | 14 | jobs: 15 | release: 16 | name: Prepare Jib extension release 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Check out code 20 | uses: actions/checkout@v2.3.4 21 | 22 | - name: Check input 23 | run: | 24 | echo '* input Jib extension: "${{ github.event.inputs.jib_extension }}"' 25 | 26 | if [[ ! "${{ github.event.inputs.release_version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then 27 | echo 'version "${{ github.event.inputs.release_version }}" not in ###.###.### format' 28 | exit 1 29 | fi 30 | 31 | - name: Build Jib extension 32 | run: | 33 | cd first-party 34 | ./gradlew clean ${{ github.event.inputs.jib_extension }}:integrationTest --stacktrace 35 | 36 | - name: Run Gradle release 37 | run: | 38 | git checkout -b ${{ github.event.inputs.jib_extension }}-release-v${{ github.event.inputs.release_version }} 39 | git config user.email ${{ github.actor }}@users.noreply.github.com 40 | git config user.name ${{ github.actor }} 41 | 42 | cd first-party 43 | # This creates the tag (e.g., "v0.2.0-jib-layer-filter-extension-gradle") and pushes the updated 44 | # branch (e.g., "jib-layer-filter-extension-gradle-release-v0.2.0") and the new tag. 45 | ./gradlew ${{ github.event.inputs.jib_extension }}:release \ 46 | -Prelease.useAutomaticVersion=true \ 47 | -Prelease.releaseVersion=${{ github.event.inputs.release_version }} 48 | 49 | - name: Create pull request 50 | uses: repo-sync/pull-request@v2.6.2 51 | id: create-pr 52 | with: 53 | github_token: ${{ secrets.CLOUD_JAVA_BOT_GITHUB_TOKEN }} 54 | source_branch: ${{ github.event.inputs.jib_extension }}-release-v${{ github.event.inputs.release_version }} 55 | pr_title: "${{ github.event.inputs.jib_extension }} release v${{ github.event.inputs.release_version }}" 56 | pr_body: "To be merged after the release is complete." 57 | pr_label: "PR: Merge After Release" 58 | 59 | - name: Create release checklist issue 60 | uses: JasonEtco/create-an-issue@v2.5.0 61 | env: 62 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 63 | RELEASE_NAME: v${{ github.event.inputs.release_version }}-${{ github.event.inputs.jib_extension }} 64 | README_URL: https://github.com/GoogleContainerTools/jib-extensions/blob/master/first-party/${{ github.event.inputs.jib_extension }}/README.md 65 | RELEASE_PR: ${{steps.create-pr.outputs.pr_url}} 66 | with: 67 | filename: .github/RELEASE_TEMPLATES/release_checklist.md 68 | 69 | -------------------------------------------------------------------------------- /.github/workflows/sonar.yml: -------------------------------------------------------------------------------- 1 | name: SonarCloud Analysis 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | types: [opened, synchronize, reopened] 8 | workflow_dispatch: 9 | schedule: 10 | - cron: '00 6 * * *' # 06:00 UTC every day 11 | 12 | jobs: 13 | sonar: 14 | if: github.repository == 'GoogleContainerTools/jib-extensions' # Only run on upstream branch 15 | name: Build with Sonar 16 | runs-on: ubuntu-20.04 17 | steps: 18 | - name: Get current date 19 | id: date 20 | run: echo "date=$(date +'%Y-%m-%d' --utc)" >> $GITHUB_OUTPUT 21 | - uses: actions/checkout@v2 22 | with: 23 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 24 | - name: Set up JDK 11 25 | uses: actions/setup-java@v1 26 | with: 27 | java-version: 11 28 | - name: Cache SonarCloud packages 29 | uses: actions/cache@v2 30 | with: 31 | path: ~/.sonar/cache 32 | key: ${{ runner.os }}-sonar-${{ steps.date.outputs.date }} 33 | - uses: actions/cache@v2 34 | with: 35 | path: | 36 | ~/.m2/repository 37 | ~/.gradle/caches 38 | ~/.gradle/wrapper 39 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 40 | restore-keys: | 41 | ${{ runner.os }}-gradle- 42 | - name: Test w/ coverage 43 | continue-on-error: true 44 | run: | 45 | cd first-party 46 | ./gradlew clean build jacocoTestReport --stacktrace 47 | - name: Build and analyze 48 | continue-on-error: true 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 51 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 52 | run: | 53 | cd first-party 54 | ./gradlew sonarqube --stacktrace 55 | -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | unit-tests: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | java: [8, 11] 16 | env: 17 | # for gradle 18 | TERM: dumb 19 | steps: 20 | - uses: actions/checkout@v2 21 | with: 22 | fetch-depth: 2 23 | - uses: actions/setup-java@v1 24 | with: 25 | java-version: ${{ matrix.java }} 26 | - uses: actions/cache@v4 27 | with: 28 | path: | 29 | ~/.m2/repository 30 | ~/.gradle/caches 31 | ~/.gradle/wrapper 32 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 33 | restore-keys: | 34 | ${{ runner.os }}-gradle- 35 | - name: Run tests 36 | run: | 37 | cd first-party 38 | ./gradlew clean build jacocoTestReport --stacktrace 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | target/ 3 | bin/ 4 | *.iml 5 | *.ipr 6 | *.iws 7 | .idea 8 | .gradle/ 9 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 10 | **/.mvn/wrapper/maven-wrapper.jar 11 | .settings/ 12 | .classpath 13 | .project 14 | .DS_Store 15 | *.swp 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | This project is currently stable, and we are primarily focused on critical bug fixes and platform evolution to ensure it continues to work for its supported use cases. 2 | 3 | # Contributing to Jib 4 | 5 | Please follow the guidelines below before opening an issue or a PR: 6 | 1. Ensure the issue was not already reported. 7 | 2. Open a new issue if you are unable to find an existing issue addressing your problem. Make sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behavior that is not occurring. 8 | 3. Discuss the priority and potential solutions with the maintainers in the issue. The maintainers would review the issue and add a label "Accepting Contributions" once the issue is ready for accepting contributions. 9 | 4. Open a PR only if the issue is labeled with "Accepting Contributions", ensure the PR description clearly describes the problem and solution. Note that an open PR without an issues labeled with "Accepting Contributions" will not be accepted. 10 | 11 | ## Contributor License Agreement 12 | 13 | Contributions to this project must be accompanied by a Contributor License 14 | Agreement. You (or your employer) retain the copyright to your contribution; 15 | this simply gives us permission to use and redistribute your contributions as 16 | part of the project. Head over to to see 17 | your current agreements on file or to sign a new one. 18 | 19 | You generally only need to submit a CLA once, so if you've already submitted one 20 | (even if it was for a different project), you probably don't need to do it 21 | again. 22 | 23 | ### Code reviews 24 | 25 | All submissions, including submissions by project members, require review. We 26 | use Github pull requests for this purpose. 27 | 28 | Before submitting a pull request, please make sure to: 29 | 30 | - Identify an existing [issue](https://github.com/GoogleContainerTools/jib-extensions/issues) to associate 31 | with your proposed change, or [file a new issue](https://github.com/GoogleContainerTools/jib-extensions/issues/new). 32 | - Describe any implementation plans in the issue and wait for a review from the repository maintainers. 33 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | To report a security issue, please use [https://g.co/vulnz](https://g.co/vulnz). 6 | We use g.co/vulnz for our intake, and do coordination and disclosure here on 7 | GitHub (including using GitHub Security Advisory). The Google Security Team will 8 | respond within 5 working days of your report on g.co/vulnz. 9 | -------------------------------------------------------------------------------- /first-party/README.md: -------------------------------------------------------------------------------- 1 | # First-Party Extensions 2 | 3 | A list of Jib extensions developed and maintained by the Jib developer team. For genenal instructions on how to apply an extension, see ["Using Jib Plugin Extensions"](../README.md#using-jib-plugin-extensions). 4 | 5 | - Jib Layer-Filter Extension ([Maven](jib-layer-filter-extension-maven) / [Gradle](jib-layer-filter-extension-gradle)) 6 | - Jib Quarkus Extension ([Maven](jib-quarkus-extension-maven) / [Gradle](jib-quarkus-extension-gradle)) 7 | - Jib Ownership Extension ([Maven](jib-ownership-extension-maven) / [Gradle](jib-ownership-extension-gradle)) 8 | - Jib Spring Boot Extension ([Maven](jib-spring-boot-extension-maven) / [Gradle](jib-spring-boot-extension-gradle)) 9 | - Jib GraalVM Native Image Extension ([Maven](jib-native-image-extension-maven)) 10 | 11 | If you have written a useful extension that you think will benefit the Jib community, file a PR to add a link to the [Third-Party Extensions](../third-party/) list. Jib users will greatly appreciate it! 12 | 13 | Got interested in writing an extension? It's easy! Take a look at ["Writing Your Own Extensions"](../README.md#writing-your-own-extensions). 14 | -------------------------------------------------------------------------------- /first-party/config/checkstyle/checkstyle-suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /first-party/config/checkstyle/copyright-java.header: -------------------------------------------------------------------------------- 1 | ^/\*$ 2 | ^ \* Copyright 20[12][0-9] Google LLC\.$ 3 | ^ \*$ 4 | ^ \* Licensed under the Apache License, Version 2\.0 \(the "License"\); you may not$ 5 | ^ \* use this file except in compliance with the License\. You may obtain a copy of$ 6 | ^ \* the License at 7 | ^ \*$ 8 | ^ \* http://www\.apache\.org/licenses/LICENSE-2\.0$ 9 | ^ \*$ 10 | ^ \* Unless required by applicable law or agreed to in writing, software$ 11 | ^ \* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT$ 12 | ^ \* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied\. See the$ 13 | ^ \* License for the specific language governing permissions and limitations under$ 14 | ^ \* the License\.$ 15 | ^ \*/$ 16 | -------------------------------------------------------------------------------- /first-party/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleContainerTools/jib-extensions/a9fbf6b3582ccfbb7e43453af714ec82539b65d0/first-party/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /first-party/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /first-party/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /first-party/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | @rem Execute Gradle 88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 89 | 90 | :end 91 | @rem End local scope for the variables with windows NT shell 92 | if "%ERRORLEVEL%"=="0" goto mainEnd 93 | 94 | :fail 95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 96 | rem the _cmd.exe /c_ return code! 97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 98 | exit /b 1 99 | 100 | :mainEnd 101 | if "%OS%"=="Windows_NT" endlocal 102 | 103 | :omega 104 | -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-gradle/README.md: -------------------------------------------------------------------------------- 1 | # Jib Layer-Filter Extension 2 | 3 | A general-purpose layer-filter extension that enables fine-grained layer control, including deleting files and moving files into new layers. Note that the extension can only filter files put by Jib; it cannot filter files in the base image. 4 | 5 | ## Examples 6 | 7 | Check out the [genenal instructions](../../README.md#using-jib-plugin-extensions) for applying a Jib plugin extension. 8 | 9 | ```gradle 10 | // should be at the top of build.gradle 11 | buildscript { 12 | dependencies { 13 | classpath('com.google.cloud.tools:jib-layer-filter-extension-gradle:0.3.0') 14 | } 15 | } 16 | 17 | ... 18 | 19 | jib { 20 | ... 21 | pluginExtensions { 22 | pluginExtension { 23 | implementation = 'com.google.cloud.tools.jib.gradle.extension.layerfilter.JibLayerFilterExtension' 24 | configuration { 25 | filters { 26 | // Delete all jar files (unless they match the filters below). --> 27 | filter { 28 | glob = '**/*.jar' 29 | } 30 | // However, retain and move google-*.jar into the new "google libraries" layer. 31 | filter { 32 | glob = '**/google-*.jar' 33 | toLayer = 'google libraries' 34 | } 35 | // Also retain and move in-house-*.jar into the new "in-house dependencies" layer. 36 | filter { 37 | glob = '/app/libs/in-house-*.jar' 38 | toLayer = 'in-house dependencies' 39 | } 40 | // These go into the same "in-house dependencies" layer. 41 | filter { 42 | glob = '/app/libs/other-in-house-*.jar' 43 | toLayer = 'in-house dependencies' 44 | } 45 | filter { 46 | glob = '/nothing/matches/this/filter' 47 | toLayer = 'this layer will not be created' 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | ``` 55 | 56 | Kotlin requires specifying the type for `pluginExtension.configuration`. 57 | 58 | ```kotlin 59 | pluginExtension { 60 | implementation = "com.google.cloud.tools.jib.gradle.extension.layerfilter.JibLayerFilterExtension" 61 | configuration(Action { 62 | filters { 63 | filter { 64 | glob = "**/*.jar" 65 | } 66 | ... 67 | } 68 | }) 69 | } 70 | ``` 71 | 72 | ## Detailed Filtering Rules 73 | 74 | - If multiple filters match a file, the last filter in the order applies to the file. 75 | - Omitting `toLayer` discards the matching files. 76 | - You may write multiple filters moving files into the same layer. It does not create multiple layers with the same name. 77 | - You cannot move files into existing layers. You can only create new layers when moving files. If you see an error message "moving files into existing layer '...' is prohibited", it means you accidentally chose a conflicting name. Simply use a different `toLayer` name. 78 | - New layers are created in the order they appear in `filters`. 79 | - The extension does not create an empty layer when no files are matched. 80 | -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-gradle/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-gradle-plugin' 3 | id 'net.researchgate.release' 4 | id 'maven-publish' 5 | } 6 | 7 | dependencies { 8 | compileOnly dependencyStrings.JIB_GRADLE_EXTENSION 9 | compileOnly dependencyStrings.GUAVA 10 | 11 | testImplementation dependencyStrings.JIB_GRADLE_EXTENSION 12 | testImplementation dependencyStrings.GUAVA 13 | testImplementation dependencyStrings.JUNIT 14 | testImplementation dependencyStrings.MOCKITO_CORE 15 | } 16 | 17 | jar { 18 | manifest { 19 | attributes 'Implementation-Version': version 20 | attributes 'Automatic-Module-Name': 'com.google.cloud.tools.jib.gradle.extension.layerfilter' 21 | 22 | // OSGi metadata 23 | attributes 'Bundle-SymbolicName': 'com.google.cloud.tools.jib.gradle.extension.layerfilter' 24 | attributes 'Bundle-Name': 'Layer Filter Extension for Jib Gradle Plugin' 25 | attributes 'Bundle-Vendor': 'Google LLC' 26 | attributes 'Bundle-DocURL': 'https://github.com/GoogleContainerTools/jib-extensions' 27 | attributes 'Bundle-License': 'https://www.apache.org/licenses/LICENSE-2.0' 28 | attributes 'Export-Package': 'com.google.cloud.tools.jib.*' 29 | } 30 | } 31 | 32 | /* RELEASE */ 33 | configureMavenRelease() 34 | 35 | publishing { 36 | publications { 37 | mavenJava(MavenPublication) { 38 | pom { 39 | name = 'Layer Filter Extension for Jib Gradle Plugin' 40 | description = 'Allows fine-grained control of files in image layers.' 41 | } 42 | from components.java 43 | } 44 | } 45 | } 46 | 47 | // Release plugin (git release commits and version updates) 48 | release { 49 | tagTemplate = 'v$version-jib-layer-filter-extension-gradle' 50 | git { 51 | requireBranch = /^jib-layer-filter-extension-gradle-release-v\d+.*$/ //regex 52 | } 53 | } 54 | /* RELEASE */ 55 | -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-gradle/gradle.properties: -------------------------------------------------------------------------------- 1 | version = 0.3.1-SNAPSHOT 2 | -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/layerfilter/Configuration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.layerfilter; 18 | 19 | import java.util.List; 20 | import javax.inject.Inject; 21 | import org.gradle.api.Action; 22 | import org.gradle.api.Project; 23 | import org.gradle.api.provider.ListProperty; 24 | import org.gradle.api.tasks.Input; 25 | import org.gradle.api.tasks.Nested; 26 | import org.gradle.api.tasks.Optional; 27 | 28 | /** 29 | * Extension-specific Gradle configuration. 30 | * 31 | *

Example usage in {@code build.gradle}: 32 | * 33 | *

{@code
 34 |  * configuration {
 35 |  *   filters {
 36 |  *     filter {
 37 |  *       glob = '**/google-*.jar'
 38 |  *       toLayer = 'google libraries'
 39 |  *     }
 40 |  *     filter {
 41 |  *       glob = '/app/libs/in-house-*.jar'
 42 |  *       toLayer = 'in-house dependencies'
 43 |  *     }
 44 |  *   }
 45 |  * }
 46 |  * }
47 | */ 48 | public class Configuration { 49 | 50 | public static class Filter { 51 | private String glob = ""; 52 | private String toLayer = ""; 53 | 54 | @Input 55 | public String getGlob() { 56 | return glob; 57 | } 58 | 59 | public void setGlob(String glob) { 60 | this.glob = glob; 61 | } 62 | 63 | @Input 64 | @Optional 65 | public String getToLayer() { 66 | return toLayer; 67 | } 68 | 69 | public void setToLayer(String toLayer) { 70 | this.toLayer = toLayer; 71 | } 72 | } 73 | 74 | public static class FiltersSpec { 75 | 76 | private final Project project; 77 | private final ListProperty filters; 78 | 79 | @Inject 80 | public FiltersSpec(Project project) { 81 | this.project = project; 82 | filters = project.getObjects().listProperty(Filter.class).empty(); 83 | } 84 | 85 | private ListProperty getFilters() { 86 | return filters; 87 | } 88 | 89 | /** 90 | * Adds a new filter configuration to the filters list. 91 | * 92 | * @param action closure representing a filter configuration 93 | */ 94 | public void filter(Action action) { 95 | Filter filter = project.getObjects().newInstance(Filter.class); 96 | action.execute(filter); 97 | filters.add(filter); 98 | } 99 | } 100 | 101 | private final FiltersSpec filtersSpec; 102 | 103 | /** 104 | * Constructor used to inject a Gradle project. 105 | * 106 | * @param project the injected Gradle project 107 | */ 108 | @Inject 109 | public Configuration(Project project) { 110 | filtersSpec = project.getObjects().newInstance(FiltersSpec.class, project); 111 | } 112 | 113 | @Nested 114 | public List getFilters() { 115 | return filtersSpec.getFilters().get(); 116 | } 117 | 118 | public void filters(Action action) { 119 | action.execute(filtersSpec); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/layerfilter/JibLayerFilterExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.layerfilter; 18 | 19 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 20 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 21 | import com.google.cloud.tools.jib.api.buildplan.FileEntry; 22 | import com.google.cloud.tools.jib.api.buildplan.LayerObject; 23 | import com.google.cloud.tools.jib.gradle.extension.GradleData; 24 | import com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension; 25 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 26 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 27 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 28 | import com.google.common.annotations.VisibleForTesting; 29 | import com.google.common.base.Verify; 30 | import java.nio.file.FileSystems; 31 | import java.nio.file.Path; 32 | import java.nio.file.PathMatcher; 33 | import java.nio.file.Paths; 34 | import java.util.ArrayList; 35 | import java.util.Collections; 36 | import java.util.LinkedHashMap; 37 | import java.util.List; 38 | import java.util.Map; 39 | import java.util.Optional; 40 | import java.util.stream.Collectors; 41 | 42 | public class JibLayerFilterExtension implements JibGradlePluginExtension { 43 | 44 | private Map pathMatchers = new LinkedHashMap<>(); 45 | 46 | // (layer name, layer builder) map for new layers of configured 47 | @VisibleForTesting Map newToLayers = new LinkedHashMap<>(); 48 | 49 | @Override 50 | public Optional> getExtraConfigType() { 51 | return Optional.of(Configuration.class); 52 | } 53 | 54 | @Override 55 | public ContainerBuildPlan extendContainerBuildPlan( 56 | ContainerBuildPlan buildPlan, 57 | Map properties, 58 | Optional config, 59 | GradleData gradleData, 60 | ExtensionLogger logger) 61 | throws JibPluginExtensionException { 62 | logger.log(LogLevel.LIFECYCLE, "Running Jib Layer Filter Extension"); 63 | if (!config.isPresent()) { 64 | logger.log(LogLevel.WARN, "Nothing configured for Jib Layer Filter Extension"); 65 | return buildPlan; 66 | } 67 | 68 | preparePathMatchersAndLayerBuilders(buildPlan, config.get()); 69 | 70 | ContainerBuildPlan.Builder newPlanBuilder = buildPlan.toBuilder(); 71 | newPlanBuilder.setLayers(Collections.emptyList()); 72 | 73 | @SuppressWarnings("unchecked") 74 | List originalLayers = (List) buildPlan.getLayers(); 75 | // Start filtering original layers. 76 | for (FileEntriesLayer layer : originalLayers) { 77 | List filesToKeep = new ArrayList<>(); 78 | 79 | for (FileEntry entry : layer.getEntries()) { 80 | Optional finalLayerName = determineFinalLayerName(entry, layer.getName()); 81 | // Either keep, move, or delete this FileEntry. 82 | if (finalLayerName.isPresent()) { 83 | if (finalLayerName.get().equals(layer.getName())) { 84 | filesToKeep.add(entry); 85 | } else { 86 | FileEntriesLayer.Builder targetLayerBuilder = 87 | Verify.verifyNotNull(newToLayers.get(finalLayerName.get())); 88 | targetLayerBuilder.addEntry(entry); 89 | } 90 | } 91 | } 92 | 93 | if (!filesToKeep.isEmpty()) { 94 | newPlanBuilder.addLayer(layer.toBuilder().setEntries(filesToKeep).build()); 95 | } 96 | } 97 | 98 | // Add newly created non-empty to-layers (if any). 99 | newToLayers.values().stream() 100 | .map(FileEntriesLayer.Builder::build) 101 | .filter(layer -> !layer.getEntries().isEmpty()) 102 | .forEach(newPlanBuilder::addLayer); 103 | return newPlanBuilder.build(); 104 | } 105 | 106 | private void preparePathMatchersAndLayerBuilders( 107 | ContainerBuildPlan buildPlan, Configuration config) throws JibPluginExtensionException { 108 | List originalLayerNames = 109 | buildPlan.getLayers().stream().map(LayerObject::getName).collect(Collectors.toList()); 110 | 111 | newToLayers.clear(); // ensure empty (in case previously built module already populated it) 112 | for (Configuration.Filter filter : config.getFilters()) { 113 | String toLayerName = filter.getToLayer(); 114 | if (!toLayerName.isEmpty() && originalLayerNames.contains(toLayerName)) { 115 | throw new JibPluginExtensionException( 116 | getClass(), 117 | "moving files into existing layer '" 118 | + toLayerName 119 | + "' is prohibited; specify a new layer name in 'filter.toLayer'."); 120 | } 121 | if (filter.getGlob().isEmpty()) { 122 | throw new JibPluginExtensionException( 123 | getClass(), "glob pattern not given in filter configuration"); 124 | } 125 | 126 | pathMatchers.put( 127 | FileSystems.getDefault().getPathMatcher("glob:" + filter.getGlob()), filter.getToLayer()); 128 | 129 | newToLayers.computeIfAbsent( 130 | toLayerName, layerName -> FileEntriesLayer.builder().setName(layerName)); 131 | } 132 | } 133 | 134 | /** 135 | * Determines where this {@code fileEntry} finally belongs after filtering. The last matching 136 | * filter in the configuration order wins. 137 | * 138 | * @param fileEntry file entry in question 139 | * @param originalLayerName name of the original layer where {@code fileEntry} exists 140 | * @return final layer name into which {@code fileEntry} should move. May be same as {@code 141 | * originalLayerName}. {@link Optional#empty()} indicates deletion. 142 | */ 143 | private Optional determineFinalLayerName(FileEntry fileEntry, String originalLayerName) { 144 | Optional finalLayerName = Optional.of(originalLayerName); 145 | 146 | for (Map.Entry mapEntry : pathMatchers.entrySet()) { 147 | PathMatcher matcher = mapEntry.getKey(); 148 | Path pathInContainer = Paths.get(fileEntry.getExtractionPath().toString()); 149 | if (matcher.matches(pathInContainer)) { 150 | String toLayerName = mapEntry.getValue(); 151 | if (toLayerName.isEmpty()) { 152 | finalLayerName = Optional.empty(); // Mark deletion. 153 | } else { 154 | finalLayerName = Optional.of(toLayerName); 155 | } 156 | } 157 | } 158 | return finalLayerName; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-gradle/src/main/resources/META-INF/services/com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension: -------------------------------------------------------------------------------- 1 | com.google.cloud.tools.jib.gradle.extension.layerfilter.JibLayerFilterExtension -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-maven/README.md: -------------------------------------------------------------------------------- 1 | # Jib Layer-Filter Extension 2 | 3 | A general-purpose layer-filter extension that enables fine-grained layer control, including deleting files and moving files into new layers. Note that the extension can only filter files put by Jib; it cannot filter files in the base image. 4 | 5 | ## Examples 6 | 7 | Check out the [genenal instructions](../../README.md#using-jib-plugin-extensions) for applying a Jib plugin extension. 8 | 9 | ```xml 10 | 11 | com.google.cloud.tools 12 | jib-maven-plugin 13 | 3.4.5 14 | 15 | 16 | 17 | com.google.cloud.tools 18 | jib-layer-filter-extension-maven 19 | 0.3.0 20 | 21 | 22 | 23 | 24 | ... 25 | 26 | 27 | com.google.cloud.tools.jib.maven.extension.layerfilter.JibLayerFilterExtension 28 | 29 | 30 | 31 | 32 | **/*.jar 33 | 34 | 35 | 36 | **/google-*.jar 37 | google libraries 38 | 39 | 40 | 41 | /app/libs/in-house-*.jar 42 | in-house dependencies 43 | 44 | 45 | 46 | /app/libs/other-in-house-*.jar 47 | in-house dependencies 48 | 49 | 50 | /nothing/matches/this/filter 51 | this layer will not be created 52 | 53 | 54 | 55 | true 56 | 57 | 58 | 59 | 60 | 61 | ``` 62 | 63 | ## Detailed Filtering Rules 64 | 65 | - If multiple filters match a file, the last filter in the order applies to the file. 66 | - Omitting `toLayer` discards the matching files. 67 | - You may write multiple filters moving files into the same layer. It does not create multiple layers with the same name. 68 | - You cannot move files into existing layers. You can only create new layers when moving files. If you see an error message "moving files into existing layer '...' is prohibited", it means you accidentally chose a conflicting name. Simply use a different `toLayer` name. 69 | - New layers are created in the order they appear in `filters`. 70 | - The extension does not create an empty layer when no files are matched. 71 | 72 | ## Separate Layers for Parent Dependencies 73 | 74 | Setting `createParentDependencyLayers` to `true` will move all dependencies that come from the parent POM to separate layers with layer name suffixed by `-parent`. 75 | 76 | - This runs after the filtering. Hence, it also considers each `toLayer` that has been created. 77 | - The extension will never create an empty parent dependency layer. 78 | - If a layer contains only parent dependencies, it will be removed, since all its content will be moved to its corresponding parent dependency layer. 79 | -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-maven/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'net.researchgate.release' 3 | id 'maven-publish' 4 | } 5 | 6 | dependencies { 7 | compileOnly dependencyStrings.JIB_MAVEN_EXTENSION 8 | compileOnly dependencyStrings.GUAVA 9 | 10 | testImplementation dependencyStrings.JIB_MAVEN_EXTENSION 11 | testImplementation dependencyStrings.JUNIT 12 | testImplementation dependencyStrings.MOCKITO_CORE 13 | } 14 | 15 | jar { 16 | manifest { 17 | attributes 'Implementation-Version': version 18 | attributes 'Automatic-Module-Name': 'com.google.cloud.tools.jib.maven.extension.layerfilter' 19 | 20 | // OSGi metadata 21 | attributes 'Bundle-SymbolicName': 'com.google.cloud.tools.jib.maven.extension.layerfilter' 22 | attributes 'Bundle-Name': 'Layer Filter Extension for Jib Maven Plugin' 23 | attributes 'Bundle-Vendor': 'Google LLC' 24 | attributes 'Bundle-DocURL': 'https://github.com/GoogleContainerTools/jib-extensions' 25 | attributes 'Bundle-License': 'https://www.apache.org/licenses/LICENSE-2.0' 26 | attributes 'Export-Package': 'com.google.cloud.tools.jib.*' 27 | } 28 | } 29 | 30 | /* RELEASE */ 31 | configureMavenRelease() 32 | 33 | publishing { 34 | publications { 35 | mavenJava(MavenPublication) { 36 | pom { 37 | name = 'Layer Filter Extension for Jib Maven Plugin' 38 | description = 'Allows fine-grained control of files in image layers.' 39 | } 40 | from components.java 41 | } 42 | } 43 | } 44 | 45 | // Release plugin (git release commits and version updates) 46 | release { 47 | tagTemplate = 'v$version-jib-layer-filter-extension-maven' 48 | git { 49 | requireBranch = /^jib-layer-filter-extension-maven-release-v\d+.*$/ //regex 50 | } 51 | } 52 | /* RELEASE */ 53 | -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-maven/gradle.properties: -------------------------------------------------------------------------------- 1 | version = 0.3.1-SNAPSHOT 2 | -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/layerfilter/Configuration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.layerfilter; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * Extension-specific Maven configuration. 24 | * 25 | *

Example usage in {@code pom.xml}: 26 | * 27 | *

{@code
28 |  * 
29 |  *   
30 |  *     
31 |  *       **/google-*.jar
32 |  *       google libraries
33 |  *     
34 |  *     
35 |  *       /app/libs/in-house-*.jar
36 |  *       in-house dependencies
37 |  *     
38 |  *   
39 |  *   true
40 |  * 
41 |  * }
42 | */ 43 | public class Configuration { 44 | 45 | public static class Filter { 46 | private String glob = ""; 47 | private String toLayer = ""; 48 | 49 | public String getGlob() { 50 | return glob; 51 | } 52 | 53 | public String getToLayer() { 54 | return toLayer; 55 | } 56 | } 57 | 58 | private List filters = new ArrayList<>(); 59 | 60 | /** 61 | * Whether to create separate layers for dependencies that stem from the parent POM. The parent 62 | * layers are created after the filters have been applied. For every original layer and every 63 | * layer defined by the filters, a parent layer will be created (if not empty). 64 | */ 65 | private boolean createParentDependencyLayers; 66 | 67 | public List getFilters() { 68 | return filters; 69 | } 70 | 71 | public boolean isCreateParentDependencyLayers() { 72 | return createParentDependencyLayers; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-maven/src/main/resources/META-INF/services/com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension: -------------------------------------------------------------------------------- 1 | com.google.cloud.tools.jib.maven.extension.layerfilter.JibLayerFilterExtension -------------------------------------------------------------------------------- /first-party/jib-layer-filter-extension-maven/src/main/resources/META-INF/sisu/javax.inject.Named: -------------------------------------------------------------------------------- 1 | com.google.cloud.tools.jib.maven.extension.layerfilter.JibLayerFilterExtension -------------------------------------------------------------------------------- /first-party/jib-native-image-extension-gradle/README.md: -------------------------------------------------------------------------------- 1 | # Jib GraalVM Native Image Extension 2 | 3 | This extension containerizes a [GraalVM native-image](https://www.graalvm.org/docs/reference-manual/native-image/) application configured with [Native Image Gradle Plugin](https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html). 4 | 5 | The extension expects the `org.graalvm.buildtools.native` to do the heavy lifting of generating a "native image" (with the `nativeCompile` task). (The "image" in "native image" refers to an executable binary, not a container image.) Then the extension simply copies the binary, say, `/build/native/nativeCompile/com.example.mymainclass`, into a container image and sets executable bits. It also auto-sets the container image entrypoint to the binary, say, `/app/com.example.mymainclass` (unless you manually configure `container.entrypoint` in the main Jib configuration). 6 | However, note that this extension doesn't autoconfigure the native image name. We are required to specify it through the `imageName` property as shown in the Examples section. 7 | 8 | ## Examples 9 | 10 | Check out the [general instructions](../../README.md#using-jib-plugin-extensions) for applying a Jib plugin extension. 11 | 12 | ```gradle 13 | // should be at the top of build.gradle 14 | buildscript { 15 | dependencies { 16 | classpath('com.google.cloud.tools:jib-native-image-extension-gradle:0.1.0') 17 | } 18 | } 19 | 20 | ... 21 | 22 | jib { 23 | ... 24 | pluginExtensions { 25 | pluginExtension { 26 | implementation = 'com.google.cloud.tools.jib.gradle.extension.nativeimage.JibNativeImageExtension' 27 | properties = [ 28 | imageName: 'com.example.mymainclass' 29 | ] 30 | } 31 | } 32 | } 33 | ``` 34 | 35 | ## Troubleshooting 36 | 37 | Unlike Java bytecode, a native image is not portable but platform-specific. The Native Image Gradle Plugin doesn't support cross-compilation [See issue](https://github.com/oracle/graal/issues/407), so the native-image binary should be built on the same architecture as the runtime architecture. Otherwise, you may see a puzzling error like the following: 38 | 39 | ``` 40 | $ docker run -it --rm native-hello 41 | standard_init_linux.go:211: exec user process caused "exec format error" 42 | ``` 43 | -------------------------------------------------------------------------------- /first-party/jib-native-image-extension-gradle/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-gradle-plugin' 3 | id 'net.researchgate.release' 4 | id 'maven-publish' 5 | } 6 | 7 | repositories { 8 | gradlePluginPortal() 9 | } 10 | 11 | dependencies { 12 | compileOnly dependencyStrings.JIB_GRADLE_EXTENSION 13 | compileOnly dependencyStrings.JIB_GRADLE 14 | compileOnly dependencyStrings.GUAVA 15 | 16 | testImplementation dependencyStrings.JIB_GRADLE_EXTENSION 17 | testImplementation dependencyStrings.JIB_GRADLE 18 | testImplementation dependencyStrings.GUAVA 19 | testImplementation dependencyStrings.JUNIT 20 | testImplementation dependencyStrings.MOCKITO_CORE 21 | testImplementation dependencyStrings.TRUTH 22 | testImplementation dependencyStrings.TRUTH8 23 | } 24 | 25 | jar { 26 | manifest { 27 | attributes 'Implementation-Version': version 28 | attributes 'Automatic-Module-Name': 'com.google.cloud.tools.jib.gradle.extension.nativeimage' 29 | 30 | // OSGi metadata 31 | attributes 'Bundle-SymbolicName': 'com.google.cloud.tools.jib.gradle.extension.nativeimage' 32 | attributes 'Bundle-Name': 'Native Image Extension for Jib Gradle Plugin' 33 | attributes 'Bundle-Vendor': 'Google LLC' 34 | attributes 'Bundle-DocURL': 'https://github.com/GoogleContainerTools/jib-extensions' 35 | attributes 'Bundle-License': 'https://www.apache.org/licenses/LICENSE-2.0' 36 | attributes 'Export-Package': 'com.google.cloud.tools.jib.*' 37 | } 38 | } 39 | 40 | /* RELEASE */ 41 | configureMavenRelease() 42 | 43 | publishing { 44 | publications { 45 | mavenJava(MavenPublication) { 46 | pom { 47 | name = 'Native Image Extension for Jib Gradle Plugin' 48 | description = 'Provides support for containerizing native image (standalone executable).' 49 | } 50 | from components.java 51 | } 52 | } 53 | } 54 | 55 | // Release plugin (git release commits and version updates) 56 | release { 57 | tagTemplate = 'v$version-jib-native-image-extension-gradle' 58 | git { 59 | requireBranch = /^jib-native-image-extension-gradle-release-v\d+.*$/ //regex 60 | } 61 | } 62 | /* RELEASE */ 63 | -------------------------------------------------------------------------------- /first-party/jib-native-image-extension-gradle/gradle.properties: -------------------------------------------------------------------------------- 1 | version = 0.1.1-SNAPSHOT 2 | -------------------------------------------------------------------------------- /first-party/jib-native-image-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/nativeimage/JibNativeImageExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.nativeimage; 18 | 19 | import com.google.cloud.tools.jib.api.JavaContainerBuilder; 20 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 21 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 22 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 23 | import com.google.cloud.tools.jib.api.buildplan.FilePermissions; 24 | import com.google.cloud.tools.jib.gradle.ContainerParameters; 25 | import com.google.cloud.tools.jib.gradle.JibExtension; 26 | import com.google.cloud.tools.jib.gradle.extension.GradleData; 27 | import com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension; 28 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 29 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 30 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 31 | import com.google.common.base.Strings; 32 | import java.nio.file.Files; 33 | import java.nio.file.Path; 34 | import java.nio.file.Paths; 35 | import java.util.Collections; 36 | import java.util.Map; 37 | import java.util.Objects; 38 | import java.util.Optional; 39 | import javax.annotation.Nullable; 40 | import org.gradle.api.Project; 41 | import org.gradle.internal.impldep.com.google.common.annotations.VisibleForTesting; 42 | 43 | public class JibNativeImageExtension implements JibGradlePluginExtension { 44 | 45 | @Override 46 | public Optional> getExtraConfigType() { 47 | return Optional.empty(); 48 | } 49 | 50 | @Override 51 | public ContainerBuildPlan extendContainerBuildPlan( 52 | ContainerBuildPlan buildPlan, 53 | Map properties, 54 | Optional config, 55 | GradleData gradleData, 56 | ExtensionLogger logger) 57 | throws JibPluginExtensionException { 58 | 59 | logger.log(LogLevel.LIFECYCLE, "Running Jib Native Image extension"); 60 | 61 | Project project = gradleData.getProject(); 62 | 63 | JibExtension jibPlugin = project.getExtensions().findByType(JibExtension.class); 64 | if (jibPlugin == null) { 65 | throw new JibPluginExtensionException(getClass(), "Can't find jib plugin!"); 66 | } 67 | ContainerParameters jibContainer = jibPlugin.getContainer(); 68 | 69 | Optional executableName = getExecutableName(jibContainer, properties); 70 | if (!executableName.isPresent()) { 71 | throw new JibPluginExtensionException( 72 | getClass(), 73 | "cannot auto-detect native-image executable name; consider setting 'imageName' property"); 74 | } 75 | 76 | String outputDirectory = project.getBuildDir().getAbsolutePath(); 77 | Path localExecutable = Paths.get(outputDirectory, "native/nativeCompile", executableName.get()); 78 | if (!Files.isRegularFile(localExecutable)) { 79 | throw new JibPluginExtensionException( 80 | getClass(), 81 | "Native-image executable does not exist or not a file: " 82 | + localExecutable 83 | + "\nDid you run the 'native-image:native-image' goal?"); 84 | } 85 | 86 | // TODO: also check system and gradle properties (e.g., -Djib.container.appRoot). 87 | String appRoot = getOptionalProperty(jibContainer.getAppRoot()).orElse("/app"); 88 | AbsoluteUnixPath targetExecutable = AbsoluteUnixPath.get(appRoot).resolve(executableName.get()); 89 | ContainerBuildPlan.Builder planBuilder = buildPlan.toBuilder(); 90 | FileEntriesLayer nativeImageLayer = 91 | FileEntriesLayer.builder() 92 | .setName("native image") 93 | .addEntry(localExecutable, targetExecutable, FilePermissions.fromOctalString("755")) 94 | .build(); 95 | planBuilder.setLayers(Collections.singletonList(nativeImageLayer)); 96 | 97 | // Preserve extra directories layers. 98 | String extraFilesLayerName = JavaContainerBuilder.LayerType.EXTRA_FILES.getName(); 99 | buildPlan.getLayers().stream() 100 | .filter(layer -> layer.getName().startsWith(extraFilesLayerName)) 101 | .forEach(planBuilder::addLayer); 102 | 103 | // TODO: also check system and gradle properties (e.g., -Djib.container.entrypoint). 104 | if (jibContainer.getEntrypoint() == null 105 | || Objects.requireNonNull(jibContainer.getEntrypoint()).isEmpty()) { 106 | planBuilder.setEntrypoint(Collections.singletonList(targetExecutable.toString())); 107 | } 108 | return planBuilder.build(); 109 | } 110 | 111 | @VisibleForTesting 112 | static Optional getExecutableName( 113 | ContainerParameters jibContainer, Map properties) { 114 | String customName = properties.get("imageName"); 115 | if (!Strings.isNullOrEmpty(customName)) { 116 | return Optional.of(customName); 117 | } 118 | 119 | Optional imageName = getOptionalProperty(jibContainer.getMainClass()); 120 | if (imageName.isPresent()) { 121 | return imageName; 122 | } 123 | 124 | return Optional.empty(); 125 | } 126 | 127 | static Optional getOptionalProperty(@Nullable T value) { 128 | if (value == null) { 129 | return Optional.empty(); 130 | } 131 | 132 | if (value.getClass() == String.class && !Strings.isNullOrEmpty((String) value)) { 133 | return Optional.of(value); 134 | } 135 | return Optional.empty(); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /first-party/jib-native-image-extension-gradle/src/main/resources/META-INF/services/com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension: -------------------------------------------------------------------------------- 1 | com.google.cloud.tools.jib.gradle.extension.nativeimage.JibNativeImageExtension -------------------------------------------------------------------------------- /first-party/jib-native-image-extension-maven/README.md: -------------------------------------------------------------------------------- 1 | # Jib GraalVM Native Image Extension 2 | 3 | This extension containerizes a [GraalVM native-imgae](https://www.graalvm.org/docs/reference-manual/native-image/) application configured with [Native Image Maven Plugin](https://graalvm.github.io/native-build-tools/latest/maven-plugin.html). 4 | 5 | The extension expects the `native-image-maven-plugin` to do the the heavy lifting of generating a "native image" (with the `native-image:native-image` goal). (The "image" in "native image" refers to an executable binary, not a container image.) Then the extension simply copies the binary, say, `/target/com.example.mymainclass`, into a container image and sets executable bits. It also auto-sets the container image entrypoint to the binary, say, `/app/com.example.mymainclass` (unless you manually configure `` in the main Jib configuration). 6 | 7 | You can still put extra files into a container image using Jib's [`` feature](https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#adding-arbitrary-files-to-the-image). 8 | 9 | ## Examples 10 | 11 | Check out the [general instructions](../../README.md#using-jib-plugin-extensions) for applying a Jib plugin extension. 12 | 13 | ```xml 14 | 15 | com.google.cloud.tools 16 | jib-maven-plugin 17 | 3.4.5 18 | 19 | 20 | 21 | com.google.cloud.tools 22 | jib-native-image-extension-maven 23 | 0.1.0 24 | 25 | 26 | 27 | 28 | ... 29 | 30 | 31 | com.google.cloud.tools.jib.maven.extension.nativeimage.JibNativeImageExtension 32 | 33 | 34 | 35 | 36 | ``` 37 | 38 | If for some reason the extension fails to auto-detect the native-image binary name, you can manually set the `` property. 39 | ```xml 40 | 41 | com.google.cloud.tools.jib.maven.extension.nativeimage.JibNativeImageExtension 42 | 43 | 45 | my-binary-name 46 | 47 | 48 | ``` 49 | 50 | ## Troubleshooting 51 | 52 | Unlike Java bytecode, a native image is not portable but platform-specific. The Native Image Maven Plugin doesn't support cross-compilation, so the native-image binary should be built on the same architecture as the runtime architecture. Otherwise, you may see a puzzling error like the following: 53 | 54 | ``` 55 | $ docker run -it --rm native-hello 56 | standard_init_linux.go:211: exec user process caused "exec format error" 57 | ``` 58 | -------------------------------------------------------------------------------- /first-party/jib-native-image-extension-maven/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'net.researchgate.release' 3 | id 'maven-publish' 4 | } 5 | 6 | dependencies { 7 | compileOnly dependencyStrings.JIB_MAVEN_EXTENSION 8 | compileOnly dependencyStrings.JIB_CORE 9 | compileOnly dependencyStrings.JSR305 10 | 11 | testImplementation dependencyStrings.JIB_MAVEN_EXTENSION 12 | testImplementation dependencyStrings.JIB_CORE 13 | testImplementation dependencyStrings.JUNIT 14 | testImplementation dependencyStrings.MOCKITO_CORE 15 | } 16 | 17 | jar { 18 | manifest { 19 | attributes 'Implementation-Version': version 20 | attributes 'Automatic-Module-Name': 'com.google.cloud.tools.jib.maven.extension.nativeimage' 21 | 22 | // OSGi metadata 23 | attributes 'Bundle-SymbolicName': 'com.google.cloud.tools.jib.maven.extension.nativeimage' 24 | attributes 'Bundle-Name': 'Native Image Extension for Jib Maven Plugin' 25 | attributes 'Bundle-Vendor': 'Google LLC' 26 | attributes 'Bundle-DocURL': 'https://github.com/GoogleContainerTools/jib-extensions' 27 | attributes 'Bundle-License': 'https://www.apache.org/licenses/LICENSE-2.0' 28 | attributes 'Export-Package': 'com.google.cloud.tools.jib.*' 29 | } 30 | } 31 | 32 | /* RELEASE */ 33 | configureMavenRelease() 34 | 35 | publishing { 36 | publications { 37 | mavenJava(MavenPublication) { 38 | pom { 39 | name = 'Native Image Extension for Jib Maven Plugin' 40 | description = 'Provides support for containerizing native image (standalone executable).' 41 | } 42 | from components.java 43 | } 44 | } 45 | } 46 | 47 | // Release plugin (git release commits and version updates) 48 | release { 49 | tagTemplate = 'v$version-jib-native-image-extension-maven' 50 | git { 51 | requireBranch = /^jib-native-image-extension-maven-release-v\d+.*$/ //regex 52 | } 53 | } 54 | /* RELEASE */ 55 | -------------------------------------------------------------------------------- /first-party/jib-native-image-extension-maven/gradle.properties: -------------------------------------------------------------------------------- 1 | version = 0.1.1-SNAPSHOT 2 | -------------------------------------------------------------------------------- /first-party/jib-native-image-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/nativeimage/ConfigValueLocation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.nativeimage; 18 | 19 | /** 20 | * Represents a location of a plugin configuration value in POM. For example, for the {@code 21 | * location-in-question} value in the following POM, 22 | * 23 | *
{@code
24 |  * org.apache.maven.plugins
25 |  * maven-shade-plugin
26 |  * 
27 |  *   
28 |  *     
29 |  *       location-in-question
30 |  *     
31 |  *   
32 |  * 
33 |  *
34 |  * 
    35 | *
  • "plugin ID" is {@code org.apache.maven.plugins:maven-shade-plugin}. 36 | *
  • "value container" is {@code } (as opposed to {@code }). 37 | *
  • "dom path" is {@code archive/manifest/mainclass}. 38 | *
39 | * }
40 | */ 41 | class ConfigValueLocation { 42 | 43 | static enum ValueContainer { 44 | CONFIGURATION, 45 | EXECUTIONS 46 | } 47 | 48 | final String pluginId; 49 | final ValueContainer valueContainer; 50 | final String[] domPath; 51 | 52 | ConfigValueLocation(String pluginId, ValueContainer valueContainer, String[] domPath) { 53 | this.pluginId = pluginId; 54 | this.valueContainer = valueContainer; 55 | this.domPath = domPath; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /first-party/jib-native-image-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/nativeimage/JibNativeImageExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.nativeimage; 18 | 19 | import com.google.cloud.tools.jib.api.JavaContainerBuilder; 20 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 21 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 22 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 23 | import com.google.cloud.tools.jib.api.buildplan.FilePermissions; 24 | import com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension; 25 | import com.google.cloud.tools.jib.maven.extension.MavenData; 26 | import com.google.cloud.tools.jib.maven.extension.nativeimage.ConfigValueLocation.ValueContainer; 27 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 28 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 29 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 30 | import com.google.common.annotations.VisibleForTesting; 31 | import com.google.common.base.Strings; 32 | import com.google.common.collect.ImmutableList; 33 | import java.nio.file.Files; 34 | import java.nio.file.Path; 35 | import java.nio.file.Paths; 36 | import java.util.Collections; 37 | import java.util.Locale; 38 | import java.util.Map; 39 | import java.util.Optional; 40 | import javax.annotation.Nullable; 41 | import org.apache.maven.model.Plugin; 42 | import org.apache.maven.project.MavenProject; 43 | import org.codehaus.plexus.util.xml.Xpp3Dom; 44 | 45 | public class JibNativeImageExtension implements JibMavenPluginExtension { 46 | 47 | private static final ConfigValueLocation JIB_APP_ROOT = 48 | new ConfigValueLocation( 49 | "com.google.cloud.tools:jib-maven-plugin", 50 | ValueContainer.CONFIGURATION, 51 | new String[] {"container", "appRoot"}); 52 | 53 | private static final ConfigValueLocation JIB_ENTRYPOINT = 54 | new ConfigValueLocation( 55 | "com.google.cloud.tools:jib-maven-plugin", 56 | ValueContainer.CONFIGURATION, 57 | new String[] {"container", "entrypoint"}); 58 | 59 | private static final String MAIN_CLASS = "mainClass"; 60 | 61 | // If is not specified, then native-image uses the mainClass name as the executable. 62 | // If is not specified, then native-image-maven-plugin looks at the configurations 63 | // for a set of other well-known plugins. 64 | // https://github.com/oracle/graal/blob/master/substratevm/src/native-image-maven-plugin/src/main/java/com/oracle/substratevm/NativeImageMojo.java 65 | private static final ConfigValueLocation NATIVE_IMAGE_PLUGIN_IMAGE_NAME = 66 | new ConfigValueLocation( 67 | "org.graalvm.nativeimage:native-image-maven-plugin", 68 | ValueContainer.CONFIGURATION, 69 | new String[] {"imageName"}); 70 | 71 | private static final ImmutableList MAIN_CLASS_LOCATIONS = 72 | ImmutableList.of( 73 | new ConfigValueLocation( 74 | "org.graalvm.nativeimage:native-image-maven-plugin", 75 | ValueContainer.CONFIGURATION, 76 | new String[] {MAIN_CLASS}), 77 | new ConfigValueLocation( 78 | "org.apache.maven.plugins:maven-shade-plugin", 79 | ValueContainer.EXECUTIONS, 80 | new String[] {"transformers", "transformer", MAIN_CLASS}), 81 | new ConfigValueLocation( 82 | "org.apache.maven.plugins:maven-assembly-plugin", 83 | ValueContainer.CONFIGURATION, 84 | new String[] {"archive", "manifest", MAIN_CLASS}), 85 | new ConfigValueLocation( 86 | "org.apache.maven.plugins:maven-jar-plugin", 87 | ValueContainer.CONFIGURATION, 88 | new String[] {"archive", "manifest", MAIN_CLASS})); 89 | 90 | @Override 91 | public Optional> getExtraConfigType() { 92 | return Optional.empty(); 93 | } 94 | 95 | @Override 96 | public ContainerBuildPlan extendContainerBuildPlan( 97 | ContainerBuildPlan buildPlan, 98 | Map properties, 99 | Optional config, 100 | MavenData mavenData, 101 | ExtensionLogger logger) 102 | throws JibPluginExtensionException { 103 | logger.log(LogLevel.LIFECYCLE, "Running Jib Native Image extension"); 104 | 105 | MavenProject project = mavenData.getMavenProject(); 106 | Optional executableName = getExecutableName(project, properties); 107 | if (!executableName.isPresent()) { 108 | throw new JibPluginExtensionException( 109 | getClass(), 110 | "cannot auto-detect native-image executable name; consider setting 'imageName' property"); 111 | } 112 | 113 | String outputDirectory = project.getBuild().getDirectory(); 114 | Path localExecutable = Paths.get(outputDirectory, executableName.get()); 115 | if (!Files.isRegularFile(localExecutable)) { 116 | throw new JibPluginExtensionException( 117 | getClass(), 118 | "Native-image executable does not exist or not a file: " 119 | + localExecutable 120 | + "\nDid you run the 'native-image:native-image' goal?"); 121 | } 122 | 123 | // TODO: also check system and Maven properties (e.g., -Djib.container.appRoot). 124 | String appRoot = getPluginConfigValue(project, JIB_APP_ROOT).orElse("/app"); 125 | AbsoluteUnixPath targetExecutable = AbsoluteUnixPath.get(appRoot).resolve(executableName.get()); 126 | 127 | ContainerBuildPlan.Builder planBuilder = buildPlan.toBuilder(); 128 | FileEntriesLayer nativeImageLayer = 129 | FileEntriesLayer.builder() 130 | .setName("native image") 131 | .addEntry(localExecutable, targetExecutable, FilePermissions.fromOctalString("755")) 132 | .build(); 133 | planBuilder.setLayers(Collections.singletonList(nativeImageLayer)); 134 | 135 | // Preserve extra directories layers. 136 | String extraFilesLayerName = JavaContainerBuilder.LayerType.EXTRA_FILES.getName(); 137 | buildPlan.getLayers().stream() 138 | .filter(layer -> layer.getName().startsWith(extraFilesLayerName)) 139 | .forEach(planBuilder::addLayer); 140 | 141 | // TODO: also check system and Maven properties (e.g., -Djib.container.entrypoint). 142 | if (!getPluginConfigValue(project, JIB_ENTRYPOINT).isPresent()) { 143 | planBuilder.setEntrypoint(Collections.singletonList(targetExecutable.toString())); 144 | } 145 | return planBuilder.build(); 146 | } 147 | 148 | @VisibleForTesting 149 | static Optional getExecutableName(MavenProject project, Map properties) { 150 | String customName = properties.get("imageName"); 151 | if (!Strings.isNullOrEmpty(customName)) { 152 | return Optional.of(customName); 153 | } 154 | 155 | Optional imageName = getPluginConfigValue(project, NATIVE_IMAGE_PLUGIN_IMAGE_NAME); 156 | if (imageName.isPresent()) { 157 | return imageName; 158 | } 159 | 160 | return MAIN_CLASS_LOCATIONS.stream() 161 | .map(location -> getPluginConfigValue(project, location)) 162 | .filter(Optional::isPresent) 163 | .map(Optional::get) 164 | .map(name -> name.toLowerCase(Locale.US)) 165 | .findFirst(); 166 | } 167 | 168 | private static Optional getPluginConfigValue( 169 | MavenProject project, ConfigValueLocation location) { 170 | Plugin plugin = project.getPlugin(location.pluginId); 171 | if (plugin == null) { 172 | return Optional.empty(); 173 | } 174 | 175 | switch (location.valueContainer) { 176 | case CONFIGURATION: 177 | return getDomValue((Xpp3Dom) plugin.getConfiguration(), location.domPath); 178 | 179 | case EXECUTIONS: 180 | return plugin.getExecutions().stream() 181 | .map(execution -> getDomValue((Xpp3Dom) execution.getConfiguration(), location.domPath)) 182 | .filter(Optional::isPresent) 183 | .map(Optional::get) 184 | .findFirst(); 185 | 186 | default: 187 | throw new IllegalArgumentException("unknown enum value: " + location.valueContainer); 188 | } 189 | } 190 | 191 | @VisibleForTesting 192 | static Optional getDomValue(@Nullable Xpp3Dom dom, String... nodePath) { 193 | if (dom == null) { 194 | return Optional.empty(); 195 | } 196 | 197 | Xpp3Dom node = dom; 198 | for (String child : nodePath) { 199 | node = node.getChild(child); 200 | if (node == null) { 201 | return Optional.empty(); 202 | } 203 | } 204 | return Optional.ofNullable(node.getValue()); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /first-party/jib-native-image-extension-maven/src/main/resources/META-INF/services/com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension: -------------------------------------------------------------------------------- 1 | com.google.cloud.tools.jib.maven.extension.nativeimage.JibNativeImageExtension -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-gradle/README.md: -------------------------------------------------------------------------------- 1 | # Jib Ownership Extension 2 | 3 | _A word of caution: use of this extension for production images is against the container best practices, which 1) increases security risks; and 2) may result in improper tracking and/or lifecycle management of ephemeral files dynamically generated at runtime (for example, log files or temp files). This is the main reason that the Jib plugins do not have built-in support for changing file ownership. However, there are a few legitimate use-cases to change ownership for non-proudction images such as when using [Skaffold](https://skaffold.dev/) to dynamically update files on Kubernetes during development. For more details, see the discussions in [jib#1257](https://github.com/GoogleContainerTools/jib/issues/1257)._ 4 | 5 | This extension enables changing ownership (not to be confused with file and directory permissions) of files and directories in the container image. Note the extension can only set ownership on files and directories that are put by Jib. 6 | 7 | ## Examples 8 | 9 | Check out the [genenal instructions](../../README.md#using-jib-plugin-extensions) for applying a Jib plugin extension. 10 | 11 | ```gradle 12 | // should be at the top of build.gradle 13 | buildscript { 14 | dependencies { 15 | classpath('com.google.cloud.tools:jib-ownership-extension-gradle:0.1.0') 16 | } 17 | } 18 | 19 | ... 20 | 21 | jib { 22 | ... 23 | pluginExtensions { 24 | pluginExtension { 25 | implementation = 'com.google.cloud.tools.jib.gradle.extension.ownership.JibOwnershipExtension' 26 | configuration { 27 | rules { 28 | rule { 29 | glob = '/app/classes/**' 30 | ownership = '300' 31 | } 32 | rule { 33 | glob = '/static/**' 34 | ownership = '300:500' 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } 41 | ``` 42 | 43 | Kotlin requires specifying the type for `pluginExtension.configuration`. 44 | 45 | ```kotlin 46 | pluginExtension { 47 | implementation = "com.google.cloud.tools.jib.gradle.extension.ownership.JibOwnershipExtension" 48 | configuration(Action { 49 | rules { 50 | rule { 51 | glob = "/app/classes/**" 52 | ownership = "300" 53 | } 54 | ... 55 | } 56 | }) 57 | } 58 | ``` 59 | 60 | ## Known Issues 61 | 62 | #### Unable to change ownership of some parent directories. 63 | 64 | When the Jib plugin assembles files into an image layer tarball, it automatically creates [supplemental parent directory entries](https://github.com/GoogleContainerTools/jib/issues/1270) for each file. The extension cannot change the ownership of these directories. For a workaround, you can create empty directories under `/src/main/jib/` (that is, utilizing the [`jib.extraDirectories` feature](https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#adding-arbitrary-files-to-the-image)) to explicitly list the directories, after which the extension can see and change the ownership of such directories. For example, if you create an empty directory structure with `/src/main/jib/app/classes/`, you can change the ownership of `/app` and `/app/classes`. See this [example](https://stackoverflow.com/a/70977481/1701388). 65 | -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-gradle/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-gradle-plugin' 3 | id 'net.researchgate.release' 4 | id 'maven-publish' 5 | } 6 | 7 | dependencies { 8 | compileOnly dependencyStrings.JIB_GRADLE_EXTENSION 9 | 10 | testImplementation dependencyStrings.JIB_GRADLE_EXTENSION 11 | testImplementation dependencyStrings.JUNIT 12 | testImplementation dependencyStrings.MOCKITO_CORE 13 | } 14 | 15 | jar { 16 | manifest { 17 | attributes 'Implementation-Version': version 18 | attributes 'Automatic-Module-Name': 'com.google.cloud.tools.jib.gradle.extension.ownership' 19 | 20 | // OSGi metadata 21 | attributes 'Bundle-SymbolicName': 'com.google.cloud.tools.jib.gradle.extension.ownership' 22 | attributes 'Bundle-Name': 'Ownership Extension for Jib Gradle Plugin' 23 | attributes 'Bundle-Vendor': 'Google LLC' 24 | attributes 'Bundle-DocURL': 'https://github.com/GoogleContainerTools/jib-extensions' 25 | attributes 'Bundle-License': 'https://www.apache.org/licenses/LICENSE-2.0' 26 | attributes 'Export-Package': 'com.google.cloud.tools.jib.*' 27 | } 28 | } 29 | 30 | /* RELEASE */ 31 | configureMavenRelease() 32 | 33 | publishing { 34 | publications { 35 | mavenJava(MavenPublication) { 36 | pom { 37 | name = 'Ownership Extension for Jib Gradle Plugin' 38 | description = 'Allows customizing ownership of files in image layers.' 39 | } 40 | from components.java 41 | } 42 | } 43 | } 44 | 45 | // Release plugin (git release commits and version updates) 46 | release { 47 | tagTemplate = 'v$version-jib-ownership-extension-gradle' 48 | git { 49 | requireBranch = /^jib-ownership-extension-gradle-release-v\d+.*$/ //regex 50 | } 51 | } 52 | /* RELEASE */ 53 | -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-gradle/gradle.properties: -------------------------------------------------------------------------------- 1 | version = 0.1.1-SNAPSHOT 2 | -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/ownership/Configuration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.ownership; 18 | 19 | import java.util.List; 20 | import javax.inject.Inject; 21 | import org.gradle.api.Action; 22 | import org.gradle.api.Project; 23 | import org.gradle.api.provider.ListProperty; 24 | import org.gradle.api.tasks.Input; 25 | import org.gradle.api.tasks.Optional; 26 | 27 | /** 28 | * Extension-specific Gradle configuration. 29 | * 30 | *

Example usage in {@code build.gradle}: 31 | * 32 | *

{@code
 33 |  * configuration {
 34 |  *   rules {
 35 |  *     // sets UID 300 for all files under /app/classes/
 36 |  *     rule {
 37 |  *       glob = '/app/classes/**'
 38 |  *       ownership = '300'
 39 |  *     }
 40 |  *     // sets UID 300 and GID 500 for all files under /static/
 41 |  *     rule {
 42 |  *       glob = '/static/**'
 43 |  *       ownership = '300:500'
 44 |  *     }
 45 |  *   }
 46 |  * }
 47 |  * }
48 | */ 49 | public class Configuration { 50 | 51 | public static class Rule { 52 | private String glob = ""; 53 | private String ownership = ""; 54 | 55 | @Input 56 | public String getGlob() { 57 | return glob; 58 | } 59 | 60 | public void setGlob(String glob) { 61 | this.glob = glob; 62 | } 63 | 64 | @Input 65 | @Optional 66 | public String getOwnership() { 67 | return ownership; 68 | } 69 | 70 | public void setOwnership(String ownership) { 71 | this.ownership = ownership; 72 | } 73 | } 74 | 75 | public static class RulesSpec { 76 | 77 | private final Project project; 78 | private final ListProperty rules; 79 | 80 | @Inject 81 | public RulesSpec(Project project) { 82 | this.project = project; 83 | rules = project.getObjects().listProperty(Rule.class).empty(); 84 | } 85 | 86 | private ListProperty getRules() { 87 | return rules; 88 | } 89 | 90 | /** 91 | * Adds a new rule configuration to the rules list. 92 | * 93 | * @param action closure representing a rule configuration 94 | */ 95 | public void rule(Action action) { 96 | Rule filter = project.getObjects().newInstance(Rule.class); 97 | action.execute(filter); 98 | rules.add(filter); 99 | } 100 | } 101 | 102 | private final RulesSpec rulesSpec; 103 | 104 | /** 105 | * Constructor used to inject a Gradle project. 106 | * 107 | * @param project the injected Gradle project 108 | */ 109 | @Inject 110 | public Configuration(Project project) { 111 | rulesSpec = project.getObjects().newInstance(RulesSpec.class, project); 112 | } 113 | 114 | public List getRules() { 115 | return rulesSpec.getRules().get(); 116 | } 117 | 118 | public void rules(Action action) { 119 | action.execute(rulesSpec); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/ownership/JibOwnershipExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.ownership; 18 | 19 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 20 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 21 | import com.google.cloud.tools.jib.api.buildplan.FileEntry; 22 | import com.google.cloud.tools.jib.gradle.extension.GradleData; 23 | import com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension; 24 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 25 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 26 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 27 | import java.nio.file.FileSystems; 28 | import java.nio.file.Path; 29 | import java.nio.file.PathMatcher; 30 | import java.nio.file.Paths; 31 | import java.util.LinkedHashMap; 32 | import java.util.List; 33 | import java.util.Map; 34 | import java.util.Map.Entry; 35 | import java.util.Optional; 36 | import java.util.stream.Collectors; 37 | 38 | public class JibOwnershipExtension implements JibGradlePluginExtension { 39 | 40 | private Map pathMatchers = new LinkedHashMap<>(); 41 | 42 | @Override 43 | public Optional> getExtraConfigType() { 44 | return Optional.of(Configuration.class); 45 | } 46 | 47 | @Override 48 | public ContainerBuildPlan extendContainerBuildPlan( 49 | ContainerBuildPlan buildPlan, 50 | Map properties, 51 | Optional config, 52 | GradleData gradleData, 53 | ExtensionLogger logger) 54 | throws JibPluginExtensionException { 55 | logger.log(LogLevel.LIFECYCLE, "Running Jib Ownership Extension"); 56 | if (!config.isPresent()) { 57 | logger.log(LogLevel.WARN, "Nothing configured for Jib Ownership Extension"); 58 | return buildPlan; 59 | } 60 | 61 | for (Configuration.Rule rule : config.get().getRules()) { 62 | if (rule.getGlob().isEmpty()) { 63 | throw new JibPluginExtensionException( 64 | getClass(), "glob pattern not given in ownership configuration"); 65 | } 66 | pathMatchers.put( 67 | FileSystems.getDefault().getPathMatcher("glob:" + rule.getGlob()), rule.getOwnership()); 68 | } 69 | 70 | @SuppressWarnings("unchecked") 71 | List layers = (List) buildPlan.getLayers(); 72 | List newLayers = 73 | layers.stream().map(this::applyRulesToLayer).collect(Collectors.toList()); 74 | return buildPlan.toBuilder().setLayers(newLayers).build(); 75 | } 76 | 77 | private FileEntriesLayer applyRulesToLayer(FileEntriesLayer layer) { 78 | List entries = 79 | layer.getEntries().stream().map(this::applyRulesToFileEntry).collect(Collectors.toList()); 80 | return layer.toBuilder().setEntries(entries).build(); 81 | } 82 | 83 | private FileEntry applyRulesToFileEntry(FileEntry entry) { 84 | String newOwnership = null; 85 | 86 | for (Entry mapEntry : pathMatchers.entrySet()) { 87 | PathMatcher matcher = mapEntry.getKey(); 88 | Path pathInContainer = Paths.get(entry.getExtractionPath().toString()); 89 | if (matcher.matches(pathInContainer)) { 90 | newOwnership = mapEntry.getValue(); 91 | } 92 | } 93 | return newOwnership == null 94 | ? entry 95 | : new FileEntry( 96 | entry.getSourceFile(), 97 | entry.getExtractionPath(), 98 | entry.getPermissions(), 99 | entry.getModificationTime(), 100 | newOwnership); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-gradle/src/main/resources/META-INF/services/com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension: -------------------------------------------------------------------------------- 1 | com.google.cloud.tools.jib.gradle.extension.ownership.JibOwnershipExtension -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-gradle/src/test/java/com/google/cloud/tools/jib/gradle/extension/ownership/JibOwnershipExtensionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.ownership; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertSame; 21 | import static org.junit.Assert.fail; 22 | import static org.mockito.Mockito.mock; 23 | import static org.mockito.Mockito.verify; 24 | import static org.mockito.Mockito.when; 25 | 26 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 27 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 28 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 29 | import com.google.cloud.tools.jib.api.buildplan.FileEntry; 30 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 31 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 32 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 33 | import java.nio.file.Paths; 34 | import java.util.Arrays; 35 | import java.util.List; 36 | import java.util.Optional; 37 | import java.util.function.Function; 38 | import java.util.stream.Collectors; 39 | import org.junit.Test; 40 | import org.junit.runner.RunWith; 41 | import org.mockito.Mock; 42 | import org.mockito.junit.MockitoJUnitRunner; 43 | 44 | /** Tests for {@link JibOwnershipExtension}. */ 45 | @RunWith(MockitoJUnitRunner.class) 46 | public class JibOwnershipExtensionTest { 47 | 48 | @Mock private Configuration config; 49 | @Mock private ExtensionLogger logger; 50 | 51 | private static List mapLayerEntries( 52 | FileEntriesLayer layer, Function mapper) { 53 | return layer.getEntries().stream().map(mapper).collect(Collectors.toList()); 54 | } 55 | 56 | private static Configuration.Rule mockRule(String glob, String ownership) { 57 | Configuration.Rule filter = mock(Configuration.Rule.class); 58 | when(filter.getGlob()).thenReturn(glob); 59 | when(filter.getOwnership()).thenReturn(ownership); 60 | return filter; 61 | } 62 | 63 | @Test 64 | public void testExtendContainerBuildPlan_noConfiguration() throws JibPluginExtensionException { 65 | ContainerBuildPlan buildPlan = ContainerBuildPlan.builder().build(); 66 | ContainerBuildPlan newPlan = 67 | new JibOwnershipExtension() 68 | .extendContainerBuildPlan(buildPlan, null, Optional.empty(), null, logger); 69 | assertSame(buildPlan, newPlan); 70 | verify(logger).log(LogLevel.WARN, "Nothing configured for Jib Ownership Extension"); 71 | } 72 | 73 | @Test 74 | public void testExtendContainerBuildPlan_noGlobGiven() { 75 | ContainerBuildPlan buildPlan = ContainerBuildPlan.builder().build(); 76 | 77 | Configuration.Rule rule = mockRule("", "doesn't matter"); 78 | when(config.getRules()).thenReturn(Arrays.asList(rule)); 79 | 80 | try { 81 | new JibOwnershipExtension() 82 | .extendContainerBuildPlan(buildPlan, null, Optional.of(config), null, logger); 83 | fail(); 84 | } catch (JibPluginExtensionException ex) { 85 | assertEquals(JibOwnershipExtension.class, ex.getExtensionClass()); 86 | assertEquals("glob pattern not given in ownership configuration", ex.getMessage()); 87 | } 88 | } 89 | 90 | @Test 91 | public void testExtendContainerBuildPlan() throws JibPluginExtensionException { 92 | FileEntriesLayer layer1 = 93 | FileEntriesLayer.builder() 94 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/file")) 95 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/another")) 96 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/sub/dir/file")) 97 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/sub/dir/another")) 98 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/untouched/file")) 99 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/untouched/another")) 100 | .build(); 101 | FileEntriesLayer layer2 = 102 | FileEntriesLayer.builder() 103 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/foo")) 104 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/bar")) 105 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/sub/dir/foo")) 106 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/sub/dir/bar")) 107 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/untouched/foo")) 108 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/untouched/bar")) 109 | .build(); 110 | ContainerBuildPlan buildPlan = 111 | ContainerBuildPlan.builder().addLayer(layer1).addLayer(layer2).build(); 112 | 113 | Configuration.Rule rule1 = mockRule("/target/**", "10:20"); 114 | Configuration.Rule rule2 = mockRule("**/bar", "999:777"); 115 | when(config.getRules()).thenReturn(Arrays.asList(rule1, rule2)); 116 | 117 | ContainerBuildPlan newPlan = 118 | new JibOwnershipExtension() 119 | .extendContainerBuildPlan(buildPlan, null, Optional.of(config), null, logger); 120 | 121 | FileEntriesLayer newLayer1 = (FileEntriesLayer) newPlan.getLayers().get(0); 122 | FileEntriesLayer newLayer2 = (FileEntriesLayer) newPlan.getLayers().get(1); 123 | 124 | assertEquals( 125 | Arrays.asList( 126 | AbsoluteUnixPath.get("/target/file"), 127 | AbsoluteUnixPath.get("/target/another"), 128 | AbsoluteUnixPath.get("/target/sub/dir/file"), 129 | AbsoluteUnixPath.get("/target/sub/dir/another"), 130 | AbsoluteUnixPath.get("/untouched/file"), 131 | AbsoluteUnixPath.get("/untouched/another")), 132 | mapLayerEntries(newLayer1, FileEntry::getExtractionPath)); 133 | assertEquals( 134 | Arrays.asList("10:20", "10:20", "10:20", "10:20", "", ""), 135 | mapLayerEntries(newLayer1, FileEntry::getOwnership)); 136 | 137 | assertEquals( 138 | Arrays.asList( 139 | AbsoluteUnixPath.get("/target/foo"), 140 | AbsoluteUnixPath.get("/target/bar"), 141 | AbsoluteUnixPath.get("/target/sub/dir/foo"), 142 | AbsoluteUnixPath.get("/target/sub/dir/bar"), 143 | AbsoluteUnixPath.get("/untouched/foo"), 144 | AbsoluteUnixPath.get("/untouched/bar")), 145 | mapLayerEntries(newLayer2, FileEntry::getExtractionPath)); 146 | assertEquals( 147 | Arrays.asList("10:20", "999:777", "10:20", "999:777", "", "999:777"), 148 | mapLayerEntries(newLayer2, FileEntry::getOwnership)); 149 | } 150 | 151 | @Test 152 | public void testExtendContainerBuildPlan_lastConfigWins() throws JibPluginExtensionException { 153 | FileEntriesLayer layer = 154 | FileEntriesLayer.builder() 155 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/file")) 156 | .build(); 157 | ContainerBuildPlan buildPlan = ContainerBuildPlan.builder().addLayer(layer).build(); 158 | 159 | Configuration.Rule rule1 = mockRule("**", "10:20"); 160 | Configuration.Rule rule2 = mockRule("**", "999:777"); 161 | when(config.getRules()).thenReturn(Arrays.asList(rule1, rule2)); 162 | 163 | ContainerBuildPlan newPlan = 164 | new JibOwnershipExtension() 165 | .extendContainerBuildPlan(buildPlan, null, Optional.of(config), null, logger); 166 | 167 | FileEntriesLayer newLayer = (FileEntriesLayer) newPlan.getLayers().get(0); 168 | assertEquals(Arrays.asList("999:777"), mapLayerEntries(newLayer, FileEntry::getOwnership)); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-maven/README.md: -------------------------------------------------------------------------------- 1 | # Jib Ownership Extension 2 | 3 | _A word of caution: use of this extension for production images is against the container best practices, which 1) increases security risks; and 2) may result in improper tracking and/or lifecycle management of ephemeral files dynamically generated at runtime (for example, log files or temp files). This is the main reason that the Jib plugins do not have built-in support for changing file ownership. However, there are a few legitimate use-cases to change ownership for non-proudction images such as when using [Skaffold](https://skaffold.dev/) to dynamically update files on Kubernetes during development. For more details, see the discussions in [jib#1257](https://github.com/GoogleContainerTools/jib/issues/1257)._ 4 | 5 | This extension enables changing ownership (not to be confused with file and directory permissions) of files and directories in the container image. Note the extension can only set ownership on files and directories that are put by Jib. 6 | 7 | ## Examples 8 | 9 | Check out the [genenal instructions](../../README.md#using-jib-plugin-extensions) for applying a Jib plugin extension. 10 | 11 | ```xml 12 | 13 | com.google.cloud.tools 14 | jib-maven-plugin 15 | 3.4.5 16 | 17 | 18 | 19 | com.google.cloud.tools 20 | jib-ownership-extension-maven 21 | 0.1.0 22 | 23 | 24 | 25 | 26 | ... 27 | 28 | 29 | com.google.cloud.tools.jib.maven.extension.ownership.JibOwnershipExtension 30 | 31 | 32 | 33 | /app/classes/** 34 | 300 35 | 36 | 37 | /static/** 38 | 300:500 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ``` 47 | 48 | ## Known Issues 49 | 50 | #### Unable to change ownership of some parent directories. 51 | 52 | When the Jib plugin assemables files into an image layer tarball, it automatically creates [supplemental parent directory entries](https://github.com/GoogleContainerTools/jib/issues/1270) for each file. The extension cannot change the ownership of these directories. For a workaround, you can create empty directories under `/src/main/jib/` (that is, utilizing the [`` feature](ihttps://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#adding-arbitrary-files-to-the-image)) to explicitly list the directories, after which the extension can see and change the ownership of such directories. For example, if you create an empty directory structure with `/src/main/jib/app/classes/`, you can change the ownership of `/app` and `/app/classes`. See this [example](https://stackoverflow.com/a/70977481/1701388). 53 | -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-maven/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'net.researchgate.release' 3 | id 'maven-publish' 4 | } 5 | 6 | dependencies { 7 | compileOnly dependencyStrings.JIB_MAVEN_EXTENSION 8 | 9 | testImplementation dependencyStrings.JIB_MAVEN_EXTENSION 10 | testImplementation dependencyStrings.JUNIT 11 | testImplementation dependencyStrings.MOCKITO_CORE 12 | } 13 | 14 | jar { 15 | manifest { 16 | attributes 'Implementation-Version': version 17 | attributes 'Automatic-Module-Name': 'com.google.cloud.tools.jib.maven.extension.ownership' 18 | 19 | // OSGi metadata 20 | attributes 'Bundle-SymbolicName': 'com.google.cloud.tools.jib.maven.extension.ownership' 21 | attributes 'Bundle-Name': 'Ownership Extension for Jib Maven Plugin' 22 | attributes 'Bundle-Vendor': 'Google LLC' 23 | attributes 'Bundle-DocURL': 'https://github.com/GoogleContainerTools/jib-extensions' 24 | attributes 'Bundle-License': 'https://www.apache.org/licenses/LICENSE-2.0' 25 | attributes 'Export-Package': 'com.google.cloud.tools.jib.*' 26 | } 27 | } 28 | 29 | /* RELEASE */ 30 | configureMavenRelease() 31 | 32 | publishing { 33 | publications { 34 | mavenJava(MavenPublication) { 35 | pom { 36 | name = 'Ownership Extension for Jib Maven Plugin' 37 | description = 'Allows customizing ownership of files in image layers.' 38 | } 39 | from components.java 40 | } 41 | } 42 | } 43 | 44 | // Release plugin (git release commits and version updates) 45 | release { 46 | tagTemplate = 'v$version-jib-ownership-extension-maven' 47 | git { 48 | requireBranch = /^jib-ownership-extension-maven-release-v\d+.*$/ //regex 49 | } 50 | } 51 | /* RELEASE */ 52 | -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-maven/gradle.properties: -------------------------------------------------------------------------------- 1 | version = 0.1.1-SNAPSHOT 2 | -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/ownership/Configuration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.ownership; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * Extension-specific Maven configuration. 24 | * 25 | *

Example usage in {@code pom.xml}: 26 | * 27 | *

{@code
28 |  * 
29 |  *   
30 |  *     
31 |  *     
32 |  *       /app/classes/**
33 |  *       300
34 |  *     
35 |  *     
36 |  *     
37 |  *       /static/**
38 |  *       300:500
39 |  *     
40 |  *   
41 |  * 
42 |  * }
43 | */ 44 | public class Configuration { 45 | 46 | public static class Rule { 47 | private String glob = ""; 48 | private String ownership = ""; 49 | 50 | public String getGlob() { 51 | return glob; 52 | } 53 | 54 | public String getOwnership() { 55 | return ownership; 56 | } 57 | } 58 | 59 | private List rules = new ArrayList<>(); 60 | 61 | public List getRules() { 62 | return rules; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/ownership/JibOwnershipExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.ownership; 18 | 19 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 20 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 21 | import com.google.cloud.tools.jib.api.buildplan.FileEntry; 22 | import com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension; 23 | import com.google.cloud.tools.jib.maven.extension.MavenData; 24 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 25 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 26 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 27 | import java.nio.file.FileSystems; 28 | import java.nio.file.Path; 29 | import java.nio.file.PathMatcher; 30 | import java.nio.file.Paths; 31 | import java.util.LinkedHashMap; 32 | import java.util.List; 33 | import java.util.Map; 34 | import java.util.Map.Entry; 35 | import java.util.Optional; 36 | import java.util.stream.Collectors; 37 | 38 | public class JibOwnershipExtension implements JibMavenPluginExtension { 39 | 40 | private Map pathMatchers = new LinkedHashMap<>(); 41 | 42 | @Override 43 | public Optional> getExtraConfigType() { 44 | return Optional.of(Configuration.class); 45 | } 46 | 47 | @Override 48 | public ContainerBuildPlan extendContainerBuildPlan( 49 | ContainerBuildPlan buildPlan, 50 | Map properties, 51 | Optional config, 52 | MavenData mavenData, 53 | ExtensionLogger logger) 54 | throws JibPluginExtensionException { 55 | logger.log(LogLevel.LIFECYCLE, "Running Jib Ownership Extension"); 56 | if (!config.isPresent()) { 57 | logger.log(LogLevel.WARN, "Nothing configured for Jib Ownership Extension"); 58 | return buildPlan; 59 | } 60 | 61 | for (Configuration.Rule rule : config.get().getRules()) { 62 | if (rule.getGlob().isEmpty()) { 63 | throw new JibPluginExtensionException( 64 | getClass(), "glob pattern not given in ownership configuration"); 65 | } 66 | pathMatchers.put( 67 | FileSystems.getDefault().getPathMatcher("glob:" + rule.getGlob()), rule.getOwnership()); 68 | } 69 | 70 | @SuppressWarnings("unchecked") 71 | List layers = (List) buildPlan.getLayers(); 72 | List newLayers = 73 | layers.stream().map(this::applyRulesToLayer).collect(Collectors.toList()); 74 | return buildPlan.toBuilder().setLayers(newLayers).build(); 75 | } 76 | 77 | private FileEntriesLayer applyRulesToLayer(FileEntriesLayer layer) { 78 | List entries = 79 | layer.getEntries().stream().map(this::applyRulesToFileEntry).collect(Collectors.toList()); 80 | return layer.toBuilder().setEntries(entries).build(); 81 | } 82 | 83 | private FileEntry applyRulesToFileEntry(FileEntry entry) { 84 | String newOwnership = null; 85 | 86 | for (Entry mapEntry : pathMatchers.entrySet()) { 87 | PathMatcher matcher = mapEntry.getKey(); 88 | Path pathInContainer = Paths.get(entry.getExtractionPath().toString()); 89 | if (matcher.matches(pathInContainer)) { 90 | newOwnership = mapEntry.getValue(); 91 | } 92 | } 93 | return newOwnership == null 94 | ? entry 95 | : new FileEntry( 96 | entry.getSourceFile(), 97 | entry.getExtractionPath(), 98 | entry.getPermissions(), 99 | entry.getModificationTime(), 100 | newOwnership); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-maven/src/main/resources/META-INF/services/com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension: -------------------------------------------------------------------------------- 1 | com.google.cloud.tools.jib.maven.extension.ownership.JibOwnershipExtension -------------------------------------------------------------------------------- /first-party/jib-ownership-extension-maven/src/test/java/com/google/cloud/tools/jib/maven/extension/ownership/JibOwnershipExtensionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.ownership; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertSame; 21 | import static org.junit.Assert.fail; 22 | import static org.mockito.Mockito.mock; 23 | import static org.mockito.Mockito.verify; 24 | import static org.mockito.Mockito.when; 25 | 26 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 27 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 28 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 29 | import com.google.cloud.tools.jib.api.buildplan.FileEntry; 30 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 31 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 32 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 33 | import java.nio.file.Paths; 34 | import java.util.Arrays; 35 | import java.util.List; 36 | import java.util.Optional; 37 | import java.util.function.Function; 38 | import java.util.stream.Collectors; 39 | import org.junit.Test; 40 | import org.junit.runner.RunWith; 41 | import org.mockito.Mock; 42 | import org.mockito.junit.MockitoJUnitRunner; 43 | 44 | /** Tests for {@link JibOwnershipExtension}. */ 45 | @RunWith(MockitoJUnitRunner.class) 46 | public class JibOwnershipExtensionTest { 47 | 48 | @Mock private Configuration config; 49 | @Mock private ExtensionLogger logger; 50 | 51 | private static List mapLayerEntries( 52 | FileEntriesLayer layer, Function mapper) { 53 | return layer.getEntries().stream().map(mapper).collect(Collectors.toList()); 54 | } 55 | 56 | private static Configuration.Rule mockRule(String glob, String ownership) { 57 | Configuration.Rule filter = mock(Configuration.Rule.class); 58 | when(filter.getGlob()).thenReturn(glob); 59 | when(filter.getOwnership()).thenReturn(ownership); 60 | return filter; 61 | } 62 | 63 | @Test 64 | public void testExtendContainerBuildPlan_noConfiguration() throws JibPluginExtensionException { 65 | ContainerBuildPlan buildPlan = ContainerBuildPlan.builder().build(); 66 | ContainerBuildPlan newPlan = 67 | new JibOwnershipExtension() 68 | .extendContainerBuildPlan(buildPlan, null, Optional.empty(), null, logger); 69 | assertSame(buildPlan, newPlan); 70 | verify(logger).log(LogLevel.WARN, "Nothing configured for Jib Ownership Extension"); 71 | } 72 | 73 | @Test 74 | public void testExtendContainerBuildPlan_noGlobGiven() { 75 | ContainerBuildPlan buildPlan = ContainerBuildPlan.builder().build(); 76 | 77 | Configuration.Rule rule = mockRule("", "doesn't matter"); 78 | when(config.getRules()).thenReturn(Arrays.asList(rule)); 79 | 80 | try { 81 | new JibOwnershipExtension() 82 | .extendContainerBuildPlan(buildPlan, null, Optional.of(config), null, logger); 83 | fail(); 84 | } catch (JibPluginExtensionException ex) { 85 | assertEquals(JibOwnershipExtension.class, ex.getExtensionClass()); 86 | assertEquals("glob pattern not given in ownership configuration", ex.getMessage()); 87 | } 88 | } 89 | 90 | @Test 91 | public void testExtendContainerBuildPlan() throws JibPluginExtensionException { 92 | FileEntriesLayer layer1 = 93 | FileEntriesLayer.builder() 94 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/file")) 95 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/another")) 96 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/sub/dir/file")) 97 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/sub/dir/another")) 98 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/untouched/file")) 99 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/untouched/another")) 100 | .build(); 101 | FileEntriesLayer layer2 = 102 | FileEntriesLayer.builder() 103 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/foo")) 104 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/bar")) 105 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/sub/dir/foo")) 106 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/sub/dir/bar")) 107 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/untouched/foo")) 108 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/untouched/bar")) 109 | .build(); 110 | ContainerBuildPlan buildPlan = 111 | ContainerBuildPlan.builder().addLayer(layer1).addLayer(layer2).build(); 112 | 113 | Configuration.Rule rule1 = mockRule("/target/**", "10:20"); 114 | Configuration.Rule rule2 = mockRule("**/bar", "999:777"); 115 | when(config.getRules()).thenReturn(Arrays.asList(rule1, rule2)); 116 | 117 | ContainerBuildPlan newPlan = 118 | new JibOwnershipExtension() 119 | .extendContainerBuildPlan(buildPlan, null, Optional.of(config), null, logger); 120 | 121 | FileEntriesLayer newLayer1 = (FileEntriesLayer) newPlan.getLayers().get(0); 122 | FileEntriesLayer newLayer2 = (FileEntriesLayer) newPlan.getLayers().get(1); 123 | 124 | assertEquals( 125 | Arrays.asList( 126 | AbsoluteUnixPath.get("/target/file"), 127 | AbsoluteUnixPath.get("/target/another"), 128 | AbsoluteUnixPath.get("/target/sub/dir/file"), 129 | AbsoluteUnixPath.get("/target/sub/dir/another"), 130 | AbsoluteUnixPath.get("/untouched/file"), 131 | AbsoluteUnixPath.get("/untouched/another")), 132 | mapLayerEntries(newLayer1, FileEntry::getExtractionPath)); 133 | assertEquals( 134 | Arrays.asList("10:20", "10:20", "10:20", "10:20", "", ""), 135 | mapLayerEntries(newLayer1, FileEntry::getOwnership)); 136 | 137 | assertEquals( 138 | Arrays.asList( 139 | AbsoluteUnixPath.get("/target/foo"), 140 | AbsoluteUnixPath.get("/target/bar"), 141 | AbsoluteUnixPath.get("/target/sub/dir/foo"), 142 | AbsoluteUnixPath.get("/target/sub/dir/bar"), 143 | AbsoluteUnixPath.get("/untouched/foo"), 144 | AbsoluteUnixPath.get("/untouched/bar")), 145 | mapLayerEntries(newLayer2, FileEntry::getExtractionPath)); 146 | assertEquals( 147 | Arrays.asList("10:20", "999:777", "10:20", "999:777", "", "999:777"), 148 | mapLayerEntries(newLayer2, FileEntry::getOwnership)); 149 | } 150 | 151 | @Test 152 | public void testExtendContainerBuildPlan_lastConfigWins() throws JibPluginExtensionException { 153 | FileEntriesLayer layer = 154 | FileEntriesLayer.builder() 155 | .addEntry(Paths.get("whatever"), AbsoluteUnixPath.get("/target/file")) 156 | .build(); 157 | ContainerBuildPlan buildPlan = ContainerBuildPlan.builder().addLayer(layer).build(); 158 | 159 | Configuration.Rule rule1 = mockRule("**", "10:20"); 160 | Configuration.Rule rule2 = mockRule("**", "999:777"); 161 | when(config.getRules()).thenReturn(Arrays.asList(rule1, rule2)); 162 | 163 | ContainerBuildPlan newPlan = 164 | new JibOwnershipExtension() 165 | .extendContainerBuildPlan(buildPlan, null, Optional.of(config), null, logger); 166 | 167 | FileEntriesLayer newLayer = (FileEntriesLayer) newPlan.getLayers().get(0); 168 | assertEquals(Arrays.asList("999:777"), mapLayerEntries(newLayer, FileEntry::getOwnership)); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/README.md: -------------------------------------------------------------------------------- 1 | # Jib Quarkus Extension 2 | 3 | ***Experimental***: may fail to work on complex projects. Does not cover "native" Quarkus packaging type. 4 | 5 | Enables containerizing a Quarkus app built with [Quarkus Gradle Plugin](https://plugins.gradle.org/plugin/io.quarkus). 6 | 7 | The Quarkus app framework has two different package types: 8 | * legacy-jar: original Quarkus "runner" JAR 9 | * fast-jar: default Quarkus package type since [1.12](https://quarkus.io/blog/quarkus-1-12-0-final-released/) 10 | 11 | You can use either package type with Jib Quarkus extension by configuring the `pluginExtensions.pluginExtension.properties`. Default value is the legacy-jar, but can be easily changed to fast-jar. 12 | 13 | ## Examples 14 | 15 | Check out the [general instructions](../../README.md#using-jib-plugin-extensions) for applying a Jib plugin extension. 16 | 17 | Note that `container.mainClass` should be set to some placeholder value to suppress Jib warning about missing main class. Package type has two valid values: `fast-jar` and `legacy-jar`. 18 | 19 | ```gradle 20 | // should be at the top of build.gradle 21 | buildscript { 22 | dependencies { 23 | classpath('com.google.cloud.tools:jib-quarkus-extension-gradle:0.1.2') 24 | } 25 | } 26 | 27 | ... 28 | 29 | jib { 30 | ... 31 | container { 32 | mainClass = 'bogus' // to suppress Jib warning about missing main class 33 | ... 34 | jvmFlags = ['-Dquarkus.http.host=0.0.0.0', '-Djava.util.logging.manager=org.jboss.logmanager.LogManager'] 35 | ports = ['8080'] 36 | user = '1001' 37 | } 38 | pluginExtensions { 39 | pluginExtension { 40 | implementation = 'com.google.cloud.tools.jib.gradle.extension.quarkus.JibQuarkusExtension' 41 | properties = [packageType: 'fast-jar'] // to use Quarkus fast-jar package type 42 | } 43 | } 44 | } 45 | ``` 46 | 47 | ## Standard Jib Configurations 48 | 49 | By the way Quarkus needs to run (via `java -jar quarkus-runner.jar` or `java -jar quarkus-app/quarkus-run.jar`), some standard Jib configurations will have no effect: 50 | 51 | - `container.mainClass` 52 | - `container.entrypoint` (Note `container.args` continues to work.) 53 | - `container.extraClasspath` 54 | - `containerizingMode` 55 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-gradle-plugin' 3 | id 'net.researchgate.release' 4 | id 'maven-publish' 5 | } 6 | 7 | repositories { 8 | gradlePluginPortal() 9 | } 10 | 11 | dependencies { 12 | compileOnly dependencyStrings.JIB_GRADLE_EXTENSION 13 | compileOnly dependencyStrings.JIB_GRADLE 14 | compileOnly dependencyStrings.GUAVA 15 | 16 | testImplementation dependencyStrings.JIB_GRADLE_EXTENSION 17 | testImplementation dependencyStrings.JIB_GRADLE 18 | testImplementation dependencyStrings.GUAVA 19 | testImplementation dependencyStrings.JUNIT 20 | testImplementation dependencyStrings.MOCKITO_CORE 21 | } 22 | 23 | jar { 24 | manifest { 25 | attributes 'Implementation-Version': version 26 | attributes 'Automatic-Module-Name': 'com.google.cloud.tools.jib.gradle.extension.quarkus' 27 | 28 | // OSGi metadata 29 | attributes 'Bundle-SymbolicName': 'com.google.cloud.tools.jib.gradle.extension.quarkus' 30 | attributes 'Bundle-Name': 'Quarkus Extension for Jib Gradle Plugin' 31 | attributes 'Bundle-Vendor': 'Google LLC' 32 | attributes 'Bundle-DocURL': 'https://github.com/GoogleContainerTools/jib-extensions' 33 | attributes 'Bundle-License': 'https://www.apache.org/licenses/LICENSE-2.0' 34 | attributes 'Export-Package': 'com.google.cloud.tools.jib.*' 35 | } 36 | } 37 | 38 | /* RELEASE */ 39 | configureMavenRelease() 40 | 41 | publishing { 42 | publications { 43 | mavenJava(MavenPublication) { 44 | pom { 45 | name = 'Quarkus Extension for Jib Gradle Plugin' 46 | description = 'Allows building images for Quarkus projects.' 47 | } 48 | from components.java 49 | } 50 | } 51 | } 52 | 53 | // Release plugin (git release commits and version updates) 54 | release { 55 | tagTemplate = 'v$version-jib-quarkus-extension-gradle' 56 | git { 57 | requireBranch = /^jib-quarkus-extension-gradle-release-v\d+.*$/ //regex 58 | } 59 | } 60 | /* RELEASE */ 61 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/gradle.properties: -------------------------------------------------------------------------------- 1 | version = 0.1.3-SNAPSHOT 2 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/quarkus/JibQuarkusExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.quarkus; 18 | 19 | import com.google.cloud.tools.jib.api.JavaContainerBuilder; 20 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 21 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 22 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 23 | import com.google.cloud.tools.jib.gradle.JibExtension; 24 | import com.google.cloud.tools.jib.gradle.extension.GradleData; 25 | import com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension; 26 | import com.google.cloud.tools.jib.gradle.extension.quarkus.resolvers.JarResolver; 27 | import com.google.cloud.tools.jib.gradle.extension.quarkus.resolvers.JarResolverFactory; 28 | import com.google.cloud.tools.jib.gradle.extension.quarkus.resolvers.impl.LegacyJarResolver; 29 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 30 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 31 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 32 | import com.google.common.base.Strings; 33 | import com.google.common.base.Verify; 34 | import java.io.File; 35 | import java.io.IOException; 36 | import java.nio.file.Files; 37 | import java.nio.file.Path; 38 | import java.util.ArrayList; 39 | import java.util.Collections; 40 | import java.util.List; 41 | import java.util.Map; 42 | import java.util.Optional; 43 | import java.util.function.Predicate; 44 | import java.util.stream.Collectors; 45 | import java.util.stream.Stream; 46 | import org.gradle.api.Project; 47 | import org.gradle.api.artifacts.ResolvedArtifact; 48 | import org.gradle.api.artifacts.component.ProjectComponentIdentifier; 49 | import org.gradle.api.plugins.JavaPlugin; 50 | 51 | public class JibQuarkusExtension implements JibGradlePluginExtension { 52 | 53 | private AbsoluteUnixPath appRoot = AbsoluteUnixPath.get("/app"); 54 | private List jvmFlags = Collections.emptyList(); 55 | 56 | private JarResolver jarResolver = new LegacyJarResolver(); 57 | private PackageType packageType = PackageType.LEGACY; 58 | 59 | @Override 60 | public Optional> getExtraConfigType() { 61 | return Optional.empty(); 62 | } 63 | 64 | @Override 65 | public ContainerBuildPlan extendContainerBuildPlan( 66 | ContainerBuildPlan buildPlan, 67 | Map properties, 68 | Optional config, 69 | GradleData gradleData, 70 | ExtensionLogger logger) 71 | throws JibPluginExtensionException { 72 | try { 73 | logger.log(LogLevel.LIFECYCLE, "Running Quarkus Jib extension"); 74 | 75 | packageType = 76 | PackageType.getPackageTypeByName(properties.getOrDefault("packageType", "legacy-jar")); 77 | 78 | readJibConfigurations(gradleData.getProject()); 79 | 80 | Project project = gradleData.getProject(); 81 | Path jar = jarResolver.getPathToLocalJar(project); 82 | 83 | ContainerBuildPlan.Builder planBuilder = buildPlan.toBuilder(); 84 | planBuilder.setLayers(Collections.emptyList()); 85 | 86 | // Dependency layers 87 | for (Path path : jarResolver.getPathsToDependencies(project)) { 88 | addDependencyLayers(project, planBuilder, path); 89 | } 90 | 91 | // Quarkus JAR layer 92 | AbsoluteUnixPath appRootJar = jarResolver.getPathToJarInContainer(appRoot); 93 | 94 | FileEntriesLayer jarLayer = 95 | FileEntriesLayer.builder().setName("quarkus jar").addEntry(jar, appRootJar).build(); 96 | planBuilder.addLayer(jarLayer); 97 | 98 | // Preserve extra directories layers 99 | String extraFilesLayerName = JavaContainerBuilder.LayerType.EXTRA_FILES.getName(); 100 | buildPlan.getLayers().stream() 101 | .filter(layer -> layer.getName().startsWith(extraFilesLayerName)) 102 | .forEach(planBuilder::addLayer); 103 | 104 | // Set entrypoint 105 | List entrypoint = new ArrayList<>(); 106 | entrypoint.add("java"); 107 | entrypoint.addAll(jvmFlags); 108 | entrypoint.add("-jar"); 109 | entrypoint.add(appRootJar.toString()); 110 | planBuilder.setEntrypoint(entrypoint); 111 | 112 | return planBuilder.build(); 113 | 114 | } catch (IOException ex) { 115 | throw new JibPluginExtensionException(getClass(), Verify.verifyNotNull(ex.getMessage()), ex); 116 | } 117 | } 118 | 119 | private void addDependencyLayers( 120 | Project project, ContainerBuildPlan.Builder planBuilder, Path libDirectory) 121 | throws IOException { 122 | List projectDependencyFilenames = 123 | project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) 124 | .getResolvedConfiguration().getResolvedArtifacts().stream() 125 | .filter( 126 | artifact -> 127 | artifact.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier) 128 | .map(ResolvedArtifact::getFile) 129 | .map(File::getName) 130 | .collect(Collectors.toList()); 131 | 132 | Predicate isProjectDependency = 133 | path -> 134 | projectDependencyFilenames.stream() 135 | // endsWith: Quarkus prepends group ID to the augmented JARs in target/lib. 136 | .anyMatch(jarFilename -> path.getFileName().toString().endsWith(jarFilename)); 137 | Predicate isSnapshot = 138 | path -> 139 | path.getFileName().toString().contains("SNAPSHOT") && !isProjectDependency.test(path); 140 | Predicate isThirdParty = isProjectDependency.negate().and(isSnapshot.negate()); 141 | 142 | addDependencyLayer(project, planBuilder, libDirectory, "dependencies", isThirdParty); 143 | addDependencyLayer(project, planBuilder, libDirectory, "snapshot dependencies", isSnapshot); 144 | addDependencyLayer( 145 | project, planBuilder, libDirectory, "project dependencies", isProjectDependency); 146 | } 147 | 148 | private void addDependencyLayer( 149 | Project project, 150 | ContainerBuildPlan.Builder planBuilder, 151 | Path libDirectory, 152 | String layerName, 153 | Predicate predicate) 154 | throws IOException { 155 | FileEntriesLayer.Builder layerBuilder = FileEntriesLayer.builder().setName(layerName); 156 | 157 | String relativePathInLib = 158 | project.getBuildDir().toURI().relativize(libDirectory.toUri()).toString(); 159 | 160 | try (Stream files = Files.list(libDirectory)) { 161 | files 162 | .filter(predicate) 163 | .forEach( 164 | path -> { 165 | layerBuilder.addEntry( 166 | path, 167 | appRoot.resolve(relativePathInLib).resolve(path.getFileName().toString())); 168 | }); 169 | } 170 | 171 | FileEntriesLayer layer = layerBuilder.build(); 172 | if (!layer.getEntries().isEmpty()) { 173 | planBuilder.addLayer(layer); 174 | } 175 | } 176 | 177 | private void readJibConfigurations(Project project) { 178 | JibExtension jibPlugin = project.getExtensions().findByType(JibExtension.class); 179 | String appRootValue = jibPlugin.getContainer().getAppRoot(); 180 | if (!Strings.isNullOrEmpty(appRootValue)) { 181 | appRoot = AbsoluteUnixPath.get(appRootValue); 182 | } 183 | jvmFlags = jibPlugin.getContainer().getJvmFlags(); 184 | jarResolver = new JarResolverFactory().getJarResolver(packageType); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/quarkus/PackageType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.quarkus; 18 | 19 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 20 | import java.util.Arrays; 21 | import java.util.Optional; 22 | 23 | public enum PackageType { 24 | LEGACY("legacy-jar"), 25 | FAST("fast-jar"); 26 | 27 | private final String packageType; 28 | 29 | PackageType(String packageType) { 30 | this.packageType = packageType; 31 | } 32 | 33 | /** 34 | * Retrieves package type by the config property value. 35 | * 36 | * @param packageType name of the package type 37 | * @return enum of the package type 38 | * @throws JibPluginExtensionException if there is an unknown package type value given to the 39 | * extension config 40 | */ 41 | public static PackageType getPackageTypeByName(String packageType) 42 | throws JibPluginExtensionException { 43 | Optional packageTypeEnum = 44 | Arrays.stream(values()).filter(el -> packageType.equals(el.packageType)).findFirst(); 45 | if (packageTypeEnum.isPresent()) { 46 | return packageTypeEnum.get(); 47 | } 48 | throw new JibPluginExtensionException( 49 | JibQuarkusExtension.class, "Unknown packageType, possible values: legacy-jar, fast-jar"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/quarkus/resolvers/JarResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.quarkus.resolvers; 18 | 19 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 20 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 21 | import java.nio.file.Path; 22 | import java.util.List; 23 | import org.gradle.api.Project; 24 | 25 | public interface JarResolver { 26 | 27 | /** 28 | * Retrieves path to Jar in local environment. 29 | * 30 | * @param project Gradle project for which this extension is run 31 | * @return path to jar in build directory 32 | * @throws JibPluginExtensionException if there is no jar 33 | */ 34 | Path getPathToLocalJar(Project project) throws JibPluginExtensionException; 35 | 36 | /** 37 | * Retrieves a list of dependencies that need to be packed together with jar. 38 | * 39 | * @param project Gradle project for which this extension is run 40 | * @return list of paths to dependencies 41 | */ 42 | List getPathsToDependencies(Project project); 43 | 44 | /** 45 | * Retrieves a path where jar will be located in the container. 46 | * 47 | * @param appRoot path root where application will reside in container 48 | * @return path to jar in the container 49 | */ 50 | AbsoluteUnixPath getPathToJarInContainer(AbsoluteUnixPath appRoot); 51 | } 52 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/quarkus/resolvers/JarResolverFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.quarkus.resolvers; 18 | 19 | import com.google.cloud.tools.jib.gradle.extension.quarkus.PackageType; 20 | import com.google.cloud.tools.jib.gradle.extension.quarkus.resolvers.impl.FastJarResolver; 21 | import com.google.cloud.tools.jib.gradle.extension.quarkus.resolvers.impl.LegacyJarResolver; 22 | 23 | public class JarResolverFactory { 24 | 25 | /** 26 | * Factory that returns a correct jar resolver depending on given package type. 27 | * 28 | * @param packageType package type of Quarkus application 29 | * @return jar resolver for given package type 30 | */ 31 | public JarResolver getJarResolver(PackageType packageType) { 32 | switch (packageType) { 33 | case LEGACY: 34 | return new LegacyJarResolver(); 35 | case FAST: 36 | return new FastJarResolver(); 37 | default: 38 | throw new IllegalArgumentException( 39 | "Quarkus packaging is set wrong! It has to be either legacy or fast."); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/quarkus/resolvers/impl/FastJarResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.quarkus.resolvers.impl; 18 | 19 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 20 | import com.google.cloud.tools.jib.gradle.extension.quarkus.JibQuarkusExtension; 21 | import com.google.cloud.tools.jib.gradle.extension.quarkus.resolvers.JarResolver; 22 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | import org.gradle.api.Project; 28 | 29 | public class FastJarResolver implements JarResolver { 30 | 31 | private static final String FAST_JAR_LOCATION = "quarkus-app/quarkus-run.jar"; 32 | 33 | @Override 34 | public Path getPathToLocalJar(Project project) throws JibPluginExtensionException { 35 | Path buildDir = project.getBuildDir().toPath(); 36 | Path jar = buildDir.resolve(FAST_JAR_LOCATION); 37 | 38 | if (!Files.isRegularFile(jar)) { 39 | throw new JibPluginExtensionException( 40 | JibQuarkusExtension.class, 41 | "quarkus-app/quarkus-run.jar doesn't exist; did you run the Quarkus Gradle plugin ('quarkusBuild' task)?"); 42 | } 43 | 44 | return jar; 45 | } 46 | 47 | @Override 48 | public List getPathsToDependencies(Project project) { 49 | return Arrays.asList( 50 | project.getBuildDir().toPath().resolve("quarkus-app/lib/main"), 51 | project.getBuildDir().toPath().resolve("quarkus-app/lib/boot"), 52 | project.getBuildDir().toPath().resolve("quarkus-app/app"), 53 | project.getBuildDir().toPath().resolve("quarkus-app/quarkus")); 54 | } 55 | 56 | @Override 57 | public AbsoluteUnixPath getPathToJarInContainer(AbsoluteUnixPath appRoot) { 58 | return appRoot.resolve(FAST_JAR_LOCATION); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/quarkus/resolvers/impl/LegacyJarResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.quarkus.resolvers.impl; 18 | 19 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 20 | import com.google.cloud.tools.jib.gradle.extension.quarkus.JibQuarkusExtension; 21 | import com.google.cloud.tools.jib.gradle.extension.quarkus.resolvers.JarResolver; 22 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import java.util.Collections; 26 | import java.util.List; 27 | import org.gradle.api.Project; 28 | import org.gradle.jvm.tasks.Jar; 29 | 30 | public class LegacyJarResolver implements JarResolver { 31 | 32 | @Override 33 | public Path getPathToLocalJar(Project project) throws JibPluginExtensionException { 34 | Path buildDir = project.getBuildDir().toPath(); 35 | Jar jarTask = (Jar) project.getTasks().findByName("jar"); 36 | String jarName = jarTask.getArchiveFile().get().getAsFile().getName(); 37 | Path jar = buildDir.resolve(jarName.replaceAll("\\.jar$", "-runner.jar")); 38 | 39 | if (!Files.isRegularFile(jar)) { 40 | throw new JibPluginExtensionException( 41 | JibQuarkusExtension.class, 42 | jar + " doesn't exist; did you run the Quarkus Gradle plugin ('quarkusBuild' task)?"); 43 | } 44 | 45 | return jar; 46 | } 47 | 48 | @Override 49 | public List getPathsToDependencies(Project project) { 50 | return Collections.singletonList(project.getBuildDir().toPath().resolve("lib")); 51 | } 52 | 53 | @Override 54 | public AbsoluteUnixPath getPathToJarInContainer(AbsoluteUnixPath appRoot) { 55 | return appRoot.resolve("app.jar"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/src/main/resources/META-INF/services/com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension: -------------------------------------------------------------------------------- 1 | com.google.cloud.tools.jib.gradle.extension.quarkus.JibQuarkusExtension 2 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/src/test/java/com/google/cloud/tools/jib/gradle/extension/quarkus/MockComponentArtifactIdentifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.quarkus; 18 | 19 | import org.gradle.api.artifacts.component.ComponentArtifactIdentifier; 20 | import org.gradle.api.artifacts.component.ComponentIdentifier; 21 | 22 | class MockComponentArtifactIdentifier implements ComponentArtifactIdentifier { 23 | 24 | private final ComponentIdentifier componentIdentifier; 25 | 26 | MockComponentArtifactIdentifier(ComponentIdentifier componentIdentifier) { 27 | this.componentIdentifier = componentIdentifier; 28 | } 29 | 30 | @Override 31 | public ComponentIdentifier getComponentIdentifier() { 32 | return componentIdentifier; 33 | } 34 | 35 | @Override 36 | public String getDisplayName() { 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-gradle/src/test/java/com/google/cloud/tools/jib/gradle/extension/quarkus/MockProjectComponentIdentifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.quarkus; 18 | 19 | import org.gradle.api.artifacts.component.BuildIdentifier; 20 | import org.gradle.api.artifacts.component.ProjectComponentIdentifier; 21 | 22 | class MockProjectComponentIdentifier implements ProjectComponentIdentifier { 23 | 24 | @Override 25 | public String getDisplayName() { 26 | return null; 27 | } 28 | 29 | @Override 30 | public BuildIdentifier getBuild() { 31 | return null; 32 | } 33 | 34 | @Override 35 | public String getProjectName() { 36 | return null; 37 | } 38 | 39 | @Override 40 | public String getProjectPath() { 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-maven/README.md: -------------------------------------------------------------------------------- 1 | # Jib Quarkus Extension 2 | 3 | ***Experimental***: may fail to work on complex projects. Does not cover "native" Quarkus packaging type. 4 | 5 | Enables containerizing a Quarkus app built with [Quarkus Maven Plugin](https://search.maven.org/artifact/io.quarkus/quarkus-maven-plugin). 6 | 7 | The Quarkus app framework has two different package types: 8 | * legacy-jar: original Quarkus "runner" JAR 9 | * fast-jar: default Quarkus package type since [1.12](https://quarkus.io/blog/quarkus-1-12-0-final-released/) 10 | 11 | You can use either package type with Jib Quarkus extension by configuring the ``. Default value is the legacy-jar, but can be easily changed to fast-jar. 12 | 13 | 14 | ## Examples 15 | 16 | Check out the [general instructions](../../README.md#using-jib-plugin-extensions) for applying a Jib plugin extension. 17 | 18 | Note that `` should be set to some placeholder value to suppress Jib warning about missing main class. Package type has two valid values: `fast-jar` and `legacy-jar`. 19 | 20 | ```xml 21 | 22 | com.google.cloud.tools 23 | jib-maven-plugin 24 | 3.4.5 25 | 26 | 27 | 28 | com.google.cloud.tools 29 | jib-quarkus-extension-maven 30 | 0.1.1 31 | 32 | 33 | 34 | 35 | 36 | 37 | bogus 38 | ... 39 | 40 | -Dquarkus.http.host=0.0.0.0 41 | -Djava.util.logging.manager=org.jboss.logmanager.LogManager 42 | 43 | 44 | 8080 45 | 46 | 1001 47 | 48 | ... 49 | 50 | 51 | com.google.cloud.tools.jib.maven.extension.quarkus.JibQuarkusExtension 52 | 53 | fast-jar 54 | 55 | 56 | 57 | 58 | 59 | ``` 60 | 61 | ## Standard Jib Configurations Being Ignored 62 | 63 | By the way Quarkus needs to run (via `java -jar quarkus-runner.jar` or `java -jar quarkus-app/quarkus-run.jar`), some standard Jib configurations will have no effect: 64 | 65 | - `` 66 | - `` (Note `` continues to work.) 67 | - `` 68 | - `` 69 | 70 | Additionally, overriding the following configurations using Maven and Java system properties (for example, passing `-Djib.container.jvmFlags=...` on the command-line) is not yet supported. 71 | 72 | - `` 73 | - `` 74 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-maven/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'net.researchgate.release' 3 | id 'maven-publish' 4 | } 5 | 6 | dependencies { 7 | compileOnly dependencyStrings.JIB_MAVEN_EXTENSION 8 | compileOnly dependencyStrings.JIB_CORE 9 | 10 | testImplementation dependencyStrings.JIB_MAVEN_EXTENSION 11 | testImplementation dependencyStrings.JIB_CORE 12 | testImplementation dependencyStrings.JUNIT 13 | testImplementation dependencyStrings.MOCKITO_CORE 14 | } 15 | 16 | jar { 17 | manifest { 18 | attributes 'Implementation-Version': version 19 | attributes 'Automatic-Module-Name': 'com.google.cloud.tools.jib.maven.extension.quarkus' 20 | 21 | // OSGi metadata 22 | attributes 'Bundle-SymbolicName': 'com.google.cloud.tools.jib.maven.extension.quarkus' 23 | attributes 'Bundle-Name': 'Quarkus Extension for Jib Maven Plugin' 24 | attributes 'Bundle-Vendor': 'Google LLC' 25 | attributes 'Bundle-DocURL': 'https://github.com/GoogleContainerTools/jib-extensions' 26 | attributes 'Bundle-License': 'https://www.apache.org/licenses/LICENSE-2.0' 27 | attributes 'Export-Package': 'com.google.cloud.tools.jib.*' 28 | } 29 | } 30 | 31 | /* RELEASE */ 32 | configureMavenRelease() 33 | 34 | publishing { 35 | publications { 36 | mavenJava(MavenPublication) { 37 | pom { 38 | name = 'Quarkus Extension for Jib Maven Plugin' 39 | description = 'Allows building images for Quarkus projects.' 40 | } 41 | from components.java 42 | } 43 | } 44 | } 45 | 46 | // Release plugin (git release commits and version updates) 47 | release { 48 | tagTemplate = 'v$version-jib-quarkus-extension-maven' 49 | git { 50 | requireBranch = /^jib-quarkus-extension-maven-release-v\d+.*$/ //regex 51 | } 52 | } 53 | /* RELEASE */ 54 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-maven/gradle.properties: -------------------------------------------------------------------------------- 1 | version = 0.1.2-SNAPSHOT 2 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/quarkus/JibQuarkusExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.quarkus; 18 | 19 | import com.google.cloud.tools.jib.api.JavaContainerBuilder; 20 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 21 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 22 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 23 | import com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension; 24 | import com.google.cloud.tools.jib.maven.extension.MavenData; 25 | import com.google.cloud.tools.jib.maven.extension.quarkus.resolvers.JarResolver; 26 | import com.google.cloud.tools.jib.maven.extension.quarkus.resolvers.JarResolverFactory; 27 | import com.google.cloud.tools.jib.maven.extension.quarkus.resolvers.impl.LegacyJarResolver; 28 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 29 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 30 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 31 | import com.google.common.base.Verify; 32 | import java.io.File; 33 | import java.io.IOException; 34 | import java.nio.file.Files; 35 | import java.nio.file.Path; 36 | import java.nio.file.Paths; 37 | import java.util.ArrayList; 38 | import java.util.Arrays; 39 | import java.util.Collections; 40 | import java.util.List; 41 | import java.util.Map; 42 | import java.util.Objects; 43 | import java.util.Optional; 44 | import java.util.Set; 45 | import java.util.function.Predicate; 46 | import java.util.stream.Collectors; 47 | import java.util.stream.Stream; 48 | import org.apache.maven.artifact.Artifact; 49 | import org.apache.maven.execution.MavenSession; 50 | import org.apache.maven.model.Plugin; 51 | import org.apache.maven.project.MavenProject; 52 | import org.codehaus.plexus.util.xml.Xpp3Dom; 53 | 54 | public class JibQuarkusExtension implements JibMavenPluginExtension { 55 | 56 | private AbsoluteUnixPath appRoot = AbsoluteUnixPath.get("/app"); 57 | private List jvmFlags = Collections.emptyList(); 58 | 59 | private JarResolver jarResolver = new LegacyJarResolver(); 60 | private PackageType packageType = PackageType.LEGACY; 61 | 62 | @Override 63 | public Optional> getExtraConfigType() { 64 | return Optional.empty(); 65 | } 66 | 67 | @Override 68 | public ContainerBuildPlan extendContainerBuildPlan( 69 | ContainerBuildPlan buildPlan, 70 | Map properties, 71 | Optional config, 72 | MavenData mavenData, 73 | ExtensionLogger logger) 74 | throws JibPluginExtensionException { 75 | try { 76 | logger.log(LogLevel.LIFECYCLE, "Running Quarkus Jib extension"); 77 | 78 | packageType = 79 | PackageType.getPackageTypeByName(properties.getOrDefault("packageType", "legacy-jar")); 80 | 81 | readJibConfigurations(mavenData.getMavenProject()); 82 | 83 | Path jar = jarResolver.getPathToLocalJar(mavenData.getMavenProject()); 84 | 85 | ContainerBuildPlan.Builder planBuilder = buildPlan.toBuilder(); 86 | planBuilder.setLayers(Collections.emptyList()); 87 | 88 | // Dependency layers 89 | for (Path path : jarResolver.getPathsToDependencies(mavenData.getMavenProject())) { 90 | addDependencyLayers( 91 | mavenData.getMavenProject(), mavenData.getMavenSession(), planBuilder, path); 92 | } 93 | 94 | // Quarkus runner JAR layer 95 | AbsoluteUnixPath appRootJar = jarResolver.getPathToJarInContainer(appRoot); 96 | FileEntriesLayer jarLayer = 97 | FileEntriesLayer.builder().setName("quarkus jar").addEntry(jar, appRootJar).build(); 98 | planBuilder.addLayer(jarLayer); 99 | 100 | // Preserve extra directories layers. 101 | String extraFilesLayerName = JavaContainerBuilder.LayerType.EXTRA_FILES.getName(); 102 | buildPlan.getLayers().stream() 103 | .filter(layer -> layer.getName().startsWith(extraFilesLayerName)) 104 | .forEach(planBuilder::addLayer); 105 | 106 | // set entrypoint 107 | List entrypoint = new ArrayList<>(); 108 | entrypoint.add("java"); 109 | entrypoint.addAll(jvmFlags); 110 | entrypoint.add("-jar"); 111 | entrypoint.add(appRootJar.toString()); 112 | planBuilder.setEntrypoint(entrypoint); 113 | 114 | return planBuilder.build(); 115 | 116 | } catch (IOException ex) { 117 | throw new JibPluginExtensionException(getClass(), Verify.verifyNotNull(ex.getMessage()), ex); 118 | } 119 | } 120 | 121 | private void addDependencyLayers( 122 | MavenProject project, 123 | MavenSession session, 124 | ContainerBuildPlan.Builder planBuilder, 125 | Path libDirectory) 126 | throws IOException { 127 | // Collect all artifact files involved in this Maven session. 128 | Set projectArtifactFilenames = 129 | session.getProjects().stream() 130 | .map(MavenProject::getArtifact) 131 | .map(Artifact::getFile) 132 | .filter(Objects::nonNull) // excludes root POM project 133 | .map(File::getName) 134 | .collect(Collectors.toSet()); 135 | 136 | Predicate isProjectDependency = 137 | path -> 138 | projectArtifactFilenames.stream() 139 | // endsWith: Quarkus prepends group ID to the augmented JARs in target/lib. 140 | .anyMatch(jarFilename -> path.getFileName().toString().endsWith(jarFilename)); 141 | Predicate isSnapshot = 142 | path -> 143 | path.getFileName().toString().contains("SNAPSHOT") && !isProjectDependency.test(path); 144 | Predicate isThirdParty = isProjectDependency.negate().and(isSnapshot.negate()); 145 | 146 | addDependencyLayer(project, planBuilder, libDirectory, "dependencies", isThirdParty); 147 | addDependencyLayer(project, planBuilder, libDirectory, "snapshot dependencies", isSnapshot); 148 | addDependencyLayer( 149 | project, planBuilder, libDirectory, "project dependencies", isProjectDependency); 150 | } 151 | 152 | private void addDependencyLayer( 153 | MavenProject project, 154 | ContainerBuildPlan.Builder planBuilder, 155 | Path libDirectory, 156 | String layerName, 157 | Predicate predicate) 158 | throws IOException { 159 | FileEntriesLayer.Builder layerBuilder = FileEntriesLayer.builder().setName(layerName); 160 | 161 | String relativePathInLib = 162 | Paths.get(project.getBuild().getDirectory()) 163 | .toUri() 164 | .relativize(libDirectory.toUri()) 165 | .toString(); 166 | 167 | try (Stream files = Files.list(libDirectory)) { 168 | files 169 | .filter(predicate) 170 | .forEach( 171 | path -> { 172 | layerBuilder.addEntry( 173 | path, 174 | appRoot.resolve(relativePathInLib).resolve(path.getFileName().toString())); 175 | }); 176 | } 177 | 178 | FileEntriesLayer layer = layerBuilder.build(); 179 | if (!layer.getEntries().isEmpty()) { 180 | planBuilder.addLayer(layer); 181 | } 182 | } 183 | 184 | // TODO: also check system and Maven properties (e.g., -Djib.container.appRoot). 185 | private void readJibConfigurations(MavenProject project) { 186 | Plugin jibPlugin = project.getPlugin("com.google.cloud.tools:jib-maven-plugin"); 187 | if (jibPlugin != null) { 188 | jarResolver = new JarResolverFactory().getJarResolver(packageType); 189 | Xpp3Dom configurationDom = (Xpp3Dom) jibPlugin.getConfiguration(); 190 | if (configurationDom != null) { 191 | Xpp3Dom containerDom = configurationDom.getChild("container"); 192 | if (containerDom != null) { 193 | Xpp3Dom appRootDom = containerDom.getChild("appRoot"); 194 | if (appRootDom != null) { 195 | appRoot = AbsoluteUnixPath.get(appRootDom.getValue()); 196 | } 197 | Xpp3Dom jvmFlagsDom = containerDom.getChild("jvmFlags"); 198 | if (jvmFlagsDom != null) { 199 | jvmFlags = 200 | Arrays.asList(jvmFlagsDom.getChildren()).stream() 201 | .map(Xpp3Dom::getValue) 202 | .collect(Collectors.toList()); 203 | } 204 | } 205 | } 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/quarkus/PackageType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.quarkus; 18 | 19 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 20 | import java.util.Arrays; 21 | import java.util.Optional; 22 | 23 | public enum PackageType { 24 | LEGACY("legacy-jar"), 25 | FAST("fast-jar"); 26 | 27 | private final String name; 28 | 29 | PackageType(String name) { 30 | this.name = name; 31 | } 32 | 33 | /** 34 | * Retrieves package type by the config property value. 35 | * 36 | * @param name name of the package type 37 | * @return enum of the package type 38 | * @throws JibPluginExtensionException if there is an unknown package type value given to the 39 | * extension config 40 | */ 41 | public static PackageType getPackageTypeByName(String name) throws JibPluginExtensionException { 42 | Optional packageTypeEnum = 43 | Arrays.stream(values()).filter(el -> name.equals(el.name)).findFirst(); 44 | if (packageTypeEnum.isPresent()) { 45 | return packageTypeEnum.get(); 46 | } 47 | throw new JibPluginExtensionException( 48 | JibQuarkusExtension.class, "Unknown packageType, possible values: legacy-jar, fast-jar"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/quarkus/resolvers/JarResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.quarkus.resolvers; 18 | 19 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 20 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 21 | import java.nio.file.Path; 22 | import java.util.List; 23 | import org.apache.maven.project.MavenProject; 24 | 25 | public interface JarResolver { 26 | 27 | /** 28 | * Retrieves path to Jar in local environment. 29 | * 30 | * @param project Maven project for which this extension is run 31 | * @return path to jar in build directory 32 | * @throws JibPluginExtensionException if there is no jar 33 | */ 34 | Path getPathToLocalJar(MavenProject project) throws JibPluginExtensionException; 35 | 36 | /** 37 | * Retrieves a list of dependencies that need to be packed together with jar. 38 | * 39 | * @param project Maven project for which this extension is run 40 | * @return list of paths to dependencies 41 | */ 42 | List getPathsToDependencies(MavenProject project); 43 | 44 | /** 45 | * Retrieves a path where jar will be located in the container. 46 | * 47 | * @param appRoot path root where application will reside in container 48 | * @return path to jar in the container 49 | */ 50 | AbsoluteUnixPath getPathToJarInContainer(AbsoluteUnixPath appRoot); 51 | } 52 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/quarkus/resolvers/JarResolverFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.quarkus.resolvers; 18 | 19 | import com.google.cloud.tools.jib.maven.extension.quarkus.PackageType; 20 | import com.google.cloud.tools.jib.maven.extension.quarkus.resolvers.impl.FastJarResolver; 21 | import com.google.cloud.tools.jib.maven.extension.quarkus.resolvers.impl.LegacyJarResolver; 22 | 23 | public class JarResolverFactory { 24 | 25 | /** 26 | * Factory that returns a correct jar resolver depending on given package type. 27 | * 28 | * @param packageType package type of Quarkus application 29 | * @return jar resolver for given package type 30 | */ 31 | public JarResolver getJarResolver(PackageType packageType) { 32 | switch (packageType) { 33 | case LEGACY: 34 | return new LegacyJarResolver(); 35 | case FAST: 36 | return new FastJarResolver(); 37 | default: 38 | throw new IllegalArgumentException( 39 | "Unable to determine the packaging type. Please ensure that it is either legacy-jar or fast-jar."); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/quarkus/resolvers/impl/FastJarResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.quarkus.resolvers.impl; 18 | 19 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 20 | import com.google.cloud.tools.jib.maven.extension.quarkus.JibQuarkusExtension; 21 | import com.google.cloud.tools.jib.maven.extension.quarkus.resolvers.JarResolver; 22 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import java.nio.file.Paths; 26 | import java.util.Arrays; 27 | import java.util.List; 28 | import org.apache.maven.project.MavenProject; 29 | 30 | public class FastJarResolver implements JarResolver { 31 | 32 | private static final String FAST_JAR_LOCATION = "quarkus-app/quarkus-run.jar"; 33 | 34 | @Override 35 | public Path getPathToLocalJar(MavenProject project) throws JibPluginExtensionException { 36 | 37 | Path buildDir = Paths.get(project.getBuild().getDirectory()); 38 | Path jar = buildDir.resolve(FAST_JAR_LOCATION); 39 | 40 | if (!Files.isRegularFile(jar)) { 41 | throw new JibPluginExtensionException( 42 | JibQuarkusExtension.class, 43 | "quarkus-app/quarkus-run.jar doesn't exist; did you run the Quarkus Maven plugin " 44 | + "('compile' and 'quarkus:build' Maven goals)?"); 45 | } 46 | 47 | return jar; 48 | } 49 | 50 | @Override 51 | public List getPathsToDependencies(MavenProject project) { 52 | Path outputPath = Paths.get(project.getBuild().getDirectory()); 53 | return Arrays.asList( 54 | outputPath.resolve("quarkus-app/lib/main"), 55 | outputPath.resolve("quarkus-app/lib/boot"), 56 | outputPath.resolve("quarkus-app/app"), 57 | outputPath.resolve("quarkus-app/quarkus")); 58 | } 59 | 60 | @Override 61 | public AbsoluteUnixPath getPathToJarInContainer(AbsoluteUnixPath appRoot) { 62 | return appRoot.resolve(FAST_JAR_LOCATION); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/quarkus/resolvers/impl/LegacyJarResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.quarkus.resolvers.impl; 18 | 19 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 20 | import com.google.cloud.tools.jib.maven.extension.quarkus.JibQuarkusExtension; 21 | import com.google.cloud.tools.jib.maven.extension.quarkus.resolvers.JarResolver; 22 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import java.nio.file.Paths; 26 | import java.util.Collections; 27 | import java.util.List; 28 | import org.apache.maven.model.Build; 29 | import org.apache.maven.project.MavenProject; 30 | 31 | public class LegacyJarResolver implements JarResolver { 32 | 33 | @Override 34 | public Path getPathToLocalJar(MavenProject project) throws JibPluginExtensionException { 35 | Build build = project.getBuild(); 36 | Path outputDirectory = Paths.get(build.getDirectory()); 37 | Path jar = outputDirectory.resolve(build.getFinalName() + "-runner.jar"); 38 | 39 | if (!Files.isRegularFile(jar)) { 40 | throw new JibPluginExtensionException( 41 | JibQuarkusExtension.class, 42 | jar 43 | + " doesn't exist; did you run the Quarkus Maven plugin " 44 | + "('compile' and 'quarkus:build' Maven goals)?"); 45 | } 46 | return jar; 47 | } 48 | 49 | @Override 50 | public List getPathsToDependencies(MavenProject project) { 51 | return Collections.singletonList(Paths.get(project.getBuild().getDirectory()).resolve("lib")); 52 | } 53 | 54 | @Override 55 | public AbsoluteUnixPath getPathToJarInContainer(AbsoluteUnixPath appRoot) { 56 | return appRoot.resolve("app.jar"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /first-party/jib-quarkus-extension-maven/src/main/resources/META-INF/services/com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension: -------------------------------------------------------------------------------- 1 | com.google.cloud.tools.jib.maven.extension.quarkus.JibQuarkusExtension -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-gradle/README.md: -------------------------------------------------------------------------------- 1 | # Jib Spring Boot Extension 2 | 3 | Provides extra support for Spring Boot applications. As of now, this extension provides the following functionalities: 4 | 5 | - Including and excluding `spring-boot-devtools` 6 | 7 | Handles the [Spring Boot developer tools issue](https://github.com/GoogleContainerTools/jib/issues/2336) that Jib always (correctly) packages the [`spring-boot-devtools`](https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-devtools) dependency. Applying this extension makes Jib include or exclude the dependency in the same way Spring Boot does for their repackaged fat JAR. For example, Spring Boot by default excludes `spring-boot-devtools` from the repackaged JAR, so the extension by default excludes it from an image too. On the other hand, if you include the dependency in the classpath of the `bootJar` task (with Spring Boot 2.3.0 or later), the extension does nothing (keeps the dependency in the image). 8 | 9 | Note that one can still properly and correctly resolve this "issue" without this extension, for example, by setting up two Gradle profiles, as explained in the issue link above. 10 | 11 | ## Examples 12 | 13 | Check out the [genenal instructions](../../README.md#using-jib-plugin-extensions) for applying a Jib plugin extension. 14 | 15 | - Spring Boot 2.3.0 and later 16 | 17 | Starting with 2.3.0, they changed the way the Spring Boot plugin includes or excludes `spring-boot-devtools` in the Spring Boot-repackaged JAR. Spring Boot now ignores the `excludeDevtools` option and requires the user to [explicitly include or exclude the dependency in the task classpath](https://docs.spring.io/spring-boot/docs/2.3.2.RELEASE/gradle-plugin/reference/html/#packaging-executable-configuring-including-development-only-dependencies). As such, the Jib extension also ignores `excludeDevtools` and only checks if `bootJar.classpath` contains `spring-boot-devtools`. 18 | 19 | ```gradle 20 | // should be at the top of build.gradle 21 | buildscript { 22 | dependencies { 23 | classpath('com.google.cloud.tools:jib-spring-boot-extension-gradle:0.1.0') 24 | } 25 | } 26 | 27 | ... 28 | 29 | jib { 30 | ... 31 | pluginExtensions { 32 | pluginExtension { 33 | implementation = 'com.google.cloud.tools.jib.gradle.extension.springboot.JibSpringBootExtension' 34 | } 35 | } 36 | } 37 | ``` 38 | 39 | - Pre-Spring Boot 2.3.0 40 | 41 | Pre-Spring Boot 2.3.0 had been checking the `excludeDevtools` option (true by default) of Spring Boot tasks. To make the Jib extension also check the option, set the `useDeprecatedExcludeDevtoolsOption` property. 42 | 43 | ```gradle 44 | pluginExtensions { 45 | pluginExtension { 46 | implementation = 'com.google.cloud.tools.jib.gradle.extension.springboot.JibSpringBootExtension' 47 | properties = [useDeprecatedExcludeDevtoolsOption: 'true'] 48 | } 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-gradle/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-gradle-plugin' 3 | id 'net.researchgate.release' 4 | id 'maven-publish' 5 | } 6 | 7 | repositories { 8 | gradlePluginPortal() 9 | } 10 | 11 | dependencies { 12 | compileOnly dependencyStrings.JIB_GRADLE_EXTENSION 13 | compileOnly dependencyStrings.JIB_GRADLE 14 | compileOnly dependencyStrings.GUAVA 15 | compileOnly dependencyStrings.SPRING_BOOT 16 | 17 | testImplementation dependencyStrings.JIB_GRADLE_EXTENSION 18 | testImplementation dependencyStrings.JIB_GRADLE 19 | testImplementation dependencyStrings.GUAVA 20 | testImplementation dependencyStrings.JUNIT 21 | testImplementation dependencyStrings.MOCKITO_CORE 22 | testImplementation dependencyStrings.SPRING_BOOT 23 | } 24 | 25 | jar { 26 | manifest { 27 | attributes 'Implementation-Version': version 28 | attributes 'Automatic-Module-Name': 'com.google.cloud.tools.jib.gradle.extension.springboot' 29 | 30 | // OSGi metadata 31 | attributes 'Bundle-SymbolicName': 'com.google.cloud.tools.jib.gradle.extension.springboot' 32 | attributes 'Bundle-Name': 'Spring Boot Extension for Jib Gradle Plugin' 33 | attributes 'Bundle-Vendor': 'Google LLC' 34 | attributes 'Bundle-DocURL': 'https://github.com/GoogleContainerTools/jib-extensions' 35 | attributes 'Bundle-License': 'https://www.apache.org/licenses/LICENSE-2.0' 36 | attributes 'Export-Package': 'com.google.cloud.tools.jib.*' 37 | } 38 | } 39 | 40 | /* RELEASE */ 41 | configureMavenRelease() 42 | 43 | publishing { 44 | publications { 45 | mavenJava(MavenPublication) { 46 | pom { 47 | name = 'Spring Boot Extension for Jib Gradle Plugin' 48 | description = 'Provides extended support for Spring Boot projects.' 49 | } 50 | from components.java 51 | } 52 | } 53 | } 54 | 55 | // Release plugin (git release commits and version updates) 56 | release { 57 | tagTemplate = 'v$version-jib-spring-boot-extension-gradle' 58 | git { 59 | requireBranch = /^jib-spring-boot-extension-gradle-release-v\d+.*$/ //regex 60 | } 61 | } 62 | /* RELEASE */ 63 | -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-gradle/gradle.properties: -------------------------------------------------------------------------------- 1 | version = 0.1.1-SNAPSHOT 2 | -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-gradle/src/main/java/com/google/cloud/tools/jib/gradle/extension/springboot/JibSpringBootExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.springboot; 18 | 19 | import com.google.cloud.tools.jib.api.JavaContainerBuilder; 20 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 21 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 22 | import com.google.cloud.tools.jib.api.buildplan.FileEntry; 23 | import com.google.cloud.tools.jib.api.buildplan.LayerObject; 24 | import com.google.cloud.tools.jib.gradle.extension.GradleData; 25 | import com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension; 26 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 27 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 28 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 29 | import com.google.common.annotations.VisibleForTesting; 30 | import java.io.File; 31 | import java.util.List; 32 | import java.util.Map; 33 | import java.util.Optional; 34 | import java.util.function.Predicate; 35 | import java.util.stream.Collectors; 36 | import org.gradle.api.Project; 37 | import org.gradle.api.Task; 38 | import org.gradle.api.UnknownTaskException; 39 | import org.gradle.api.tasks.TaskProvider; 40 | import org.springframework.boot.gradle.tasks.bundling.BootJar; 41 | 42 | public class JibSpringBootExtension implements JibGradlePluginExtension { 43 | 44 | @Override 45 | public Optional> getExtraConfigType() { 46 | return Optional.empty(); 47 | } 48 | 49 | @Override 50 | public ContainerBuildPlan extendContainerBuildPlan( 51 | ContainerBuildPlan buildPlan, 52 | Map properties, 53 | Optional config, 54 | GradleData gradleData, 55 | ExtensionLogger logger) 56 | throws JibPluginExtensionException { 57 | logger.log(LogLevel.LIFECYCLE, "Running Jib Spring Boot extension"); 58 | 59 | if (!shouldExcludeDevtools(gradleData.getProject(), properties, logger)) { 60 | logger.log(LogLevel.INFO, "Keeping spring-boot-devtools (if any)"); 61 | return buildPlan; 62 | } 63 | logger.log(LogLevel.INFO, "Removing spring-boot-devtools (if any)"); 64 | 65 | List newLayers = 66 | buildPlan.getLayers().stream() 67 | .map(JibSpringBootExtension::filterOutDevtools) 68 | .collect(Collectors.toList()); 69 | return buildPlan.toBuilder().setLayers(newLayers).build(); 70 | } 71 | 72 | @VisibleForTesting 73 | static boolean isDevtoolsJar(File file) { 74 | return file.getName().startsWith("spring-boot-devtools-") && file.getName().endsWith(".jar"); 75 | } 76 | 77 | @VisibleForTesting 78 | static boolean shouldExcludeDevtools( 79 | Project project, Map extensionProperties, ExtensionLogger logger) { 80 | try { 81 | TaskProvider taskProvider = project.getTasks().named("bootJar"); 82 | if (taskProvider.getOrNull() instanceof BootJar) { 83 | BootJar bootJar = ((BootJar) taskProvider.get()); 84 | 85 | boolean useDeprecatedExcludeDevtools = 86 | Boolean.valueOf( 87 | extensionProperties.getOrDefault("useDeprecatedExcludeDevtoolsOption", "false")); 88 | if (useDeprecatedExcludeDevtools) { 89 | return bootJar.isExcludeDevtools(); 90 | } 91 | 92 | // Since Spring 2.3.0, "excludeDevtools" is deprecated and has no effect. (Also its default 93 | // is no longer "true".) The new guide to include devtools is to explicitly add 94 | // "developmentOnly" to "bootJar.classpath". 95 | // https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/html/#packaging-executable-configuring-including-development-only-dependencies 96 | boolean noDevtoolsJarOnClasspath = 97 | bootJar.getClasspath().filter(JibSpringBootExtension::isDevtoolsJar).isEmpty(); 98 | return noDevtoolsJarOnClasspath; 99 | } 100 | } catch (UnknownTaskException ignored) { // fall through 101 | } 102 | 103 | logger.log(LogLevel.WARN, "Jib Spring Boot extension: project doesn't have bootJar task?"); 104 | return true; 105 | } 106 | 107 | @VisibleForTesting 108 | static LayerObject filterOutDevtools(LayerObject layerObject) { 109 | String dependencyLayerName = JavaContainerBuilder.LayerType.DEPENDENCIES.getName(); 110 | if (!dependencyLayerName.equals(layerObject.getName())) { 111 | return layerObject; 112 | } 113 | 114 | FileEntriesLayer layer = (FileEntriesLayer) layerObject; 115 | Predicate notDevtoolsJar = 116 | fileEntry -> !isDevtoolsJar(fileEntry.getSourceFile().toFile()); 117 | List newEntries = 118 | layer.getEntries().stream().filter(notDevtoolsJar).collect(Collectors.toList()); 119 | return layer.toBuilder().setEntries(newEntries).build(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-gradle/src/main/resources/META-INF/services/com.google.cloud.tools.jib.gradle.extension.JibGradlePluginExtension: -------------------------------------------------------------------------------- 1 | com.google.cloud.tools.jib.gradle.extension.springboot.JibSpringBootExtension 2 | -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-gradle/src/test/java/com/google/cloud/tools/jib/gradle/extension/springboot/JibSpringBootExtensionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.gradle.extension.springboot; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertFalse; 21 | import static org.junit.Assert.assertSame; 22 | import static org.junit.Assert.assertTrue; 23 | import static org.mockito.Mockito.verify; 24 | import static org.mockito.Mockito.when; 25 | 26 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 27 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 28 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 29 | import com.google.cloud.tools.jib.api.buildplan.LayerObject; 30 | import com.google.cloud.tools.jib.gradle.extension.GradleData; 31 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 32 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 33 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 34 | import java.io.File; 35 | import java.nio.file.Path; 36 | import java.nio.file.Paths; 37 | import java.util.Arrays; 38 | import java.util.HashMap; 39 | import java.util.List; 40 | import java.util.Map; 41 | import java.util.Optional; 42 | import java.util.Set; 43 | import java.util.stream.Collectors; 44 | import org.gradle.api.Project; 45 | import org.gradle.api.Task; 46 | import org.gradle.api.internal.file.AbstractFileCollection; 47 | import org.gradle.api.tasks.TaskContainer; 48 | import org.gradle.api.tasks.TaskProvider; 49 | import org.junit.Before; 50 | import org.junit.Test; 51 | import org.junit.runner.RunWith; 52 | import org.mockito.Mock; 53 | import org.mockito.junit.MockitoJUnitRunner; 54 | import org.springframework.boot.gradle.tasks.bundling.BootJar; 55 | 56 | /** Tests for {@link JibSpringBootExtension}. */ 57 | @RunWith(MockitoJUnitRunner.class) 58 | public class JibSpringBootExtensionTest { 59 | 60 | private static class MockFileCollection extends AbstractFileCollection { 61 | private final Set files; 62 | 63 | private MockFileCollection(Path... files) { 64 | this.files = Arrays.asList(files).stream().map(Path::toFile).collect(Collectors.toSet()); 65 | } 66 | 67 | @Override 68 | public Set getFiles() { 69 | return files; 70 | } 71 | 72 | @Override 73 | public String getDisplayName() { 74 | return null; 75 | } 76 | } 77 | 78 | @Mock private ExtensionLogger logger; 79 | 80 | @Mock private Project project; 81 | @Mock private TaskContainer taskContainer; 82 | @Mock private TaskProvider taskProvider; 83 | @Mock private BootJar bootJar; 84 | 85 | private GradleData gradleData = () -> project; 86 | private final Map properties = new HashMap<>(); 87 | 88 | private static FileEntriesLayer buildLayer(String layerName, Path... paths) { 89 | FileEntriesLayer.Builder builder = FileEntriesLayer.builder().setName(layerName); 90 | for (Path path : paths) { 91 | builder.addEntry(path, AbsoluteUnixPath.get("/dest/" + path.getFileName())); 92 | } 93 | return builder.build(); 94 | } 95 | 96 | private static List layerToExtractionPaths(FileEntriesLayer layer) { 97 | return layer.getEntries().stream() 98 | .map(layerEntry -> layerEntry.getExtractionPath().toString()) 99 | .collect(Collectors.toList()); 100 | } 101 | 102 | @Before 103 | public void setUp() { 104 | when(project.getTasks()).thenReturn(taskContainer); 105 | when(taskContainer.named("bootJar")).thenReturn(taskProvider); 106 | when(taskProvider.getOrNull()).thenReturn(bootJar); 107 | when(taskProvider.get()).thenReturn(bootJar); 108 | } 109 | 110 | @Test 111 | public void testIsDevtoolsJar() { 112 | File file = Paths.get("sub", "folder", "spring-boot-devtools-1.2.3-SNAPSHOT.jar").toFile(); 113 | assertTrue(JibSpringBootExtension.isDevtoolsJar(file)); 114 | } 115 | 116 | @Test 117 | public void testIsDevtoolsJar_noJarExtension() { 118 | File file = Paths.get("sub", "folder", "spring-boot-devtools-1.2.3-SNAPSHOT").toFile(); 119 | assertFalse(JibSpringBootExtension.isDevtoolsJar(file)); 120 | } 121 | 122 | @Test 123 | public void testIsDevtoolsJar_differentJar() { 124 | File file = Paths.get("sub", "folder", "not-spring-boot-devtools-1.2.3-SNAPSHOT.jar").toFile(); 125 | assertFalse(JibSpringBootExtension.isDevtoolsJar(file)); 126 | } 127 | 128 | @Test 129 | public void testShouldExcludeDevtools_noBootJarTask() { 130 | when(project.getTasks().named("bootJar").getOrNull()).thenReturn(null); 131 | 132 | assertTrue(JibSpringBootExtension.shouldExcludeDevtools(project, properties, logger)); 133 | 134 | verify(logger) 135 | .log(LogLevel.WARN, "Jib Spring Boot extension: project doesn't have bootJar task?"); 136 | } 137 | 138 | @Test 139 | public void testShouldExcludeDevtools_devtoolsOnBootJarClasspath() { 140 | when(bootJar.getClasspath()) 141 | .thenReturn( 142 | new MockFileCollection( 143 | Paths.get("lib").resolve("spring-boot-devtools-1.2.3.jar"), 144 | Paths.get("another-lib.jar"))); 145 | 146 | assertFalse(JibSpringBootExtension.shouldExcludeDevtools(project, properties, logger)); 147 | } 148 | 149 | @Test 150 | public void testShouldExcludeDevtools_devtoolsNotOnBootJarClasspath() { 151 | when(bootJar.getClasspath()) 152 | .thenReturn(new MockFileCollection(Paths.get("static").resolve("foo.txt"))); 153 | 154 | assertTrue(JibSpringBootExtension.shouldExcludeDevtools(project, properties, logger)); 155 | } 156 | 157 | @Test 158 | public void testShouldExcludeDevtools_useExcludeDevtoolsOption_isExcludeDevtoolsTrue() { 159 | properties.put("useDeprecatedExcludeDevtoolsOption", "TrUe"); 160 | when(bootJar.isExcludeDevtools()).thenReturn(true); 161 | 162 | assertTrue(JibSpringBootExtension.shouldExcludeDevtools(project, properties, logger)); 163 | } 164 | 165 | @Test 166 | public void testShouldExcludeDevtools_useExcludeDevtoolsOption_isExcludeDevtoolsFalse() { 167 | properties.put("useDeprecatedExcludeDevtoolsOption", "true"); 168 | when(bootJar.isExcludeDevtools()).thenReturn(false); 169 | 170 | assertFalse(JibSpringBootExtension.shouldExcludeDevtools(project, properties, logger)); 171 | } 172 | 173 | @Test 174 | public void testShouldExcludeDevtools_devtoolsOnBootJarClasspath_useExcludeDevtoolsOptionFalse() { 175 | properties.put("useDeprecatedExcludeDevtoolsOption", "FaLsE"); 176 | 177 | when(bootJar.getClasspath()) 178 | .thenReturn( 179 | new MockFileCollection( 180 | Paths.get("lib").resolve("spring-boot-devtools-1.2.3.jar"), 181 | Paths.get("another-lib.jar"))); 182 | 183 | assertFalse(JibSpringBootExtension.shouldExcludeDevtools(project, properties, logger)); 184 | } 185 | 186 | @Test 187 | public void 188 | testShouldExcludeDevtools_devtoolsNotOnBootJarClasspath_useExcludeDevtoolsOptionFalse() { 189 | properties.put("useDeprecatedExcludeDevtoolsOption", "false"); 190 | 191 | when(bootJar.getClasspath()) 192 | .thenReturn(new MockFileCollection(Paths.get("static").resolve("foo.txt"))); 193 | 194 | assertTrue(JibSpringBootExtension.shouldExcludeDevtools(project, properties, logger)); 195 | } 196 | 197 | @Test 198 | public void testFilterOutDevtools() { 199 | FileEntriesLayer layer = 200 | buildLayer( 201 | "dependencies", 202 | Paths.get("static").resolve("foo.txt"), 203 | Paths.get("lib").resolve("spring-boot-devtools-1.2.3.jar"), 204 | Paths.get("archive").resolve("bar.zip")); 205 | FileEntriesLayer filtered = (FileEntriesLayer) JibSpringBootExtension.filterOutDevtools(layer); 206 | 207 | assertEquals(Arrays.asList("/dest/foo.txt", "/dest/bar.zip"), layerToExtractionPaths(filtered)); 208 | } 209 | 210 | @Test 211 | public void testFilterOutDevtools_differentDependencyLayerName() { 212 | FileEntriesLayer layer = 213 | buildLayer( 214 | "NOT dependencies", 215 | Paths.get("lib").resolve("spring-boot-devtools-1.2.3.jar"), 216 | Paths.get("archive").resolve("bar.zip")); 217 | LayerObject newLayer = JibSpringBootExtension.filterOutDevtools(layer); 218 | assertSame(layer, newLayer); 219 | assertEquals(layer.getEntries(), ((FileEntriesLayer) newLayer).getEntries()); 220 | } 221 | 222 | @Test 223 | public void testExtendContainerBuildPlan_devtoolsFiltered() throws JibPluginExtensionException { 224 | when(bootJar.getClasspath()).thenReturn(new MockFileCollection()); 225 | 226 | FileEntriesLayer layer1 = 227 | buildLayer( 228 | "dependencies", 229 | Paths.get("spring-boot-devtools-1.2.3.jar"), 230 | Paths.get("archive").resolve("bar.zip")); 231 | FileEntriesLayer layer2 = 232 | buildLayer("NOT dependencies", Paths.get("spring-boot-devtools-1.2.3.jar")); 233 | ContainerBuildPlan buildPlan = 234 | ContainerBuildPlan.builder().addLayer(layer1).addLayer(layer2).build(); 235 | 236 | ContainerBuildPlan newPlan = 237 | new JibSpringBootExtension() 238 | .extendContainerBuildPlan(buildPlan, properties, Optional.empty(), gradleData, logger); 239 | 240 | assertEquals(2, newPlan.getLayers().size()); 241 | FileEntriesLayer newLayer1 = (FileEntriesLayer) newPlan.getLayers().get(0); 242 | FileEntriesLayer newLayer2 = (FileEntriesLayer) newPlan.getLayers().get(1); 243 | 244 | assertEquals(Arrays.asList("/dest/bar.zip"), layerToExtractionPaths(newLayer1)); 245 | assertEquals( 246 | Arrays.asList("/dest/spring-boot-devtools-1.2.3.jar"), layerToExtractionPaths(newLayer2)); 247 | 248 | verify(logger).log(LogLevel.INFO, "Removing spring-boot-devtools (if any)"); 249 | } 250 | 251 | @Test 252 | public void testExtendContainerBuildPlan_noFiltering() throws JibPluginExtensionException { 253 | when(bootJar.getClasspath()) 254 | .thenReturn(new MockFileCollection(Paths.get("spring-boot-devtools-1.2.3.jar"))); 255 | 256 | FileEntriesLayer layer1 = 257 | buildLayer( 258 | "dependencies", 259 | Paths.get("spring-boot-devtools-1.2.3.jar"), 260 | Paths.get("archive").resolve("bar.zip")); 261 | FileEntriesLayer layer2 = 262 | buildLayer("NOT dependencies", Paths.get("spring-boot-devtools-1.2.3.jar")); 263 | ContainerBuildPlan buildPlan = 264 | ContainerBuildPlan.builder().addLayer(layer1).addLayer(layer2).build(); 265 | 266 | ContainerBuildPlan newPlan = 267 | new JibSpringBootExtension() 268 | .extendContainerBuildPlan(buildPlan, properties, Optional.empty(), gradleData, logger); 269 | assertSame(buildPlan, newPlan); 270 | 271 | verify(logger).log(LogLevel.INFO, "Keeping spring-boot-devtools (if any)"); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-maven/README.md: -------------------------------------------------------------------------------- 1 | # Jib Spring Boot Extension 2 | 3 | Provides extra support for Spring Boot applications. As of now, this extension provides the following functionalities: 4 | 5 | - Including and excluding `spring-boot-devtools` 6 | 7 | Handles the [Spring Boot developer tools issue](https://github.com/GoogleContainerTools/jib/issues/2336) that Jib always (correctly) packages the [`spring-boot-devtools`](https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-devtools) dependency. Applying this extension makes Jib include or exclude the dependency in the same way Spring Boot does for their repackaged fat JAR. For example, Spring Boot by default excludes `spring-boot-devtools` from the repackaged JAR, so the extension by default excludes it from an image too. On the other hand, if you set `false` in Spring Boot, the extension does nothing (keeps the dependency in the image). 8 | 9 | Note that one can still properly and correctly resolve this "issue" without this extension, for example, by setting up two Maven profiles, as explained in the issue link above. 10 | 11 | ## Examples 12 | 13 | Check out the [genenal instructions](../../README.md#using-jib-plugin-extensions) for applying a Jib plugin extension. 14 | 15 | ```xml 16 | 17 | com.google.cloud.tools 18 | jib-maven-plugin 19 | 3.4.5 20 | 21 | 22 | 23 | com.google.cloud.tools 24 | jib-spring-boot-extension-maven 25 | 0.1.0 26 | 27 | 28 | 29 | 30 | ... 31 | 32 | 33 | com.google.cloud.tools.jib.maven.extension.springboot.JibSpringBootExtension 34 | 35 | 36 | 37 | 38 | ``` 39 | -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-maven/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'net.researchgate.release' 3 | id 'maven-publish' 4 | } 5 | 6 | dependencies { 7 | compileOnly dependencyStrings.JIB_MAVEN_EXTENSION 8 | compileOnly dependencyStrings.JIB_CORE 9 | 10 | testImplementation dependencyStrings.JIB_MAVEN_EXTENSION 11 | testImplementation dependencyStrings.JIB_CORE 12 | testImplementation dependencyStrings.JUNIT 13 | testImplementation dependencyStrings.MOCKITO_CORE 14 | } 15 | 16 | jar { 17 | manifest { 18 | attributes 'Implementation-Version': version 19 | attributes 'Automatic-Module-Name': 'com.google.cloud.tools.jib.maven.extension.springboot' 20 | 21 | // OSGi metadata 22 | attributes 'Bundle-SymbolicName': 'com.google.cloud.tools.jib.maven.extension.springboot' 23 | attributes 'Bundle-Name': 'Spring Boot Extension for Jib Maven Plugin' 24 | attributes 'Bundle-Vendor': 'Google LLC' 25 | attributes 'Bundle-DocURL': 'https://github.com/GoogleContainerTools/jib-extensions' 26 | attributes 'Bundle-License': 'https://www.apache.org/licenses/LICENSE-2.0' 27 | attributes 'Export-Package': 'com.google.cloud.tools.jib.*' 28 | } 29 | } 30 | 31 | /* RELEASE */ 32 | configureMavenRelease() 33 | 34 | publishing { 35 | publications { 36 | mavenJava(MavenPublication) { 37 | pom { 38 | name = 'Spring Boot Extension for Jib Maven Plugin' 39 | description = 'Provides extended support for Spring Boot projects.' 40 | } 41 | from components.java 42 | } 43 | } 44 | } 45 | 46 | // Release plugin (git release commits and version updates) 47 | release { 48 | tagTemplate = 'v$version-jib-spring-boot-extension-maven' 49 | git { 50 | requireBranch = /^jib-spring-boot-extension-maven-release-v\d+.*$/ //regex 51 | } 52 | } 53 | /* RELEASE */ 54 | -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-maven/gradle.properties: -------------------------------------------------------------------------------- 1 | version = 0.1.1-SNAPSHOT 2 | -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-maven/src/main/java/com/google/cloud/tools/jib/maven/extension/springboot/JibSpringBootExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.springboot; 18 | 19 | import com.google.cloud.tools.jib.api.JavaContainerBuilder; 20 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 21 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 22 | import com.google.cloud.tools.jib.api.buildplan.FileEntry; 23 | import com.google.cloud.tools.jib.api.buildplan.LayerObject; 24 | import com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension; 25 | import com.google.cloud.tools.jib.maven.extension.MavenData; 26 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 27 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 28 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 29 | import com.google.common.annotations.VisibleForTesting; 30 | import java.io.File; 31 | import java.util.List; 32 | import java.util.Map; 33 | import java.util.Optional; 34 | import java.util.function.Predicate; 35 | import java.util.stream.Collectors; 36 | import org.apache.maven.model.Plugin; 37 | import org.apache.maven.project.MavenProject; 38 | import org.codehaus.plexus.util.xml.Xpp3Dom; 39 | 40 | public class JibSpringBootExtension implements JibMavenPluginExtension { 41 | 42 | @Override 43 | public Optional> getExtraConfigType() { 44 | return Optional.empty(); 45 | } 46 | 47 | @Override 48 | public ContainerBuildPlan extendContainerBuildPlan( 49 | ContainerBuildPlan buildPlan, 50 | Map properties, 51 | Optional config, 52 | MavenData mavenData, 53 | ExtensionLogger logger) 54 | throws JibPluginExtensionException { 55 | logger.log(LogLevel.LIFECYCLE, "Running Jib Spring Boot extension"); 56 | 57 | if (!shouldExcludeDevtools(mavenData.getMavenProject(), logger)) { 58 | logger.log(LogLevel.INFO, "Keeping spring-boot-devtools (if any)"); 59 | return buildPlan; 60 | } 61 | logger.log(LogLevel.INFO, "Removing spring-boot-devtools (if any)"); 62 | 63 | List newLayers = 64 | buildPlan.getLayers().stream() 65 | .map(JibSpringBootExtension::filterOutDevtools) 66 | .collect(Collectors.toList()); 67 | return buildPlan.toBuilder().setLayers(newLayers).build(); 68 | } 69 | 70 | @VisibleForTesting 71 | static boolean isDevtoolsJar(File file) { 72 | return file.getName().startsWith("spring-boot-devtools-") && file.getName().endsWith(".jar"); 73 | } 74 | 75 | @VisibleForTesting 76 | static boolean shouldExcludeDevtools(MavenProject project, ExtensionLogger logger) { 77 | Plugin bootPlugin = project.getPlugin("org.springframework.boot:spring-boot-maven-plugin"); 78 | if (bootPlugin == null) { 79 | logger.log( 80 | LogLevel.WARN, 81 | "Jib Spring Boot extension: project doesn't have spring-boot-maven-plugin?"); 82 | return true; 83 | } 84 | 85 | Xpp3Dom configuration = (Xpp3Dom) bootPlugin.getConfiguration(); 86 | if (configuration != null) { 87 | Xpp3Dom excludeDevtools = configuration.getChild("excludeDevtools"); 88 | if (excludeDevtools != null) { 89 | return "true".equalsIgnoreCase(excludeDevtools.getValue()); 90 | } 91 | } 92 | return true; // Spring Boot's default is true. 93 | } 94 | 95 | @VisibleForTesting 96 | static LayerObject filterOutDevtools(LayerObject layerObject) { 97 | String dependencyLayerName = JavaContainerBuilder.LayerType.DEPENDENCIES.getName(); 98 | if (!dependencyLayerName.equals(layerObject.getName())) { 99 | return layerObject; 100 | } 101 | 102 | FileEntriesLayer layer = (FileEntriesLayer) layerObject; 103 | Predicate notDevtoolsJar = 104 | fileEntry -> !isDevtoolsJar(fileEntry.getSourceFile().toFile()); 105 | List newEntries = 106 | layer.getEntries().stream().filter(notDevtoolsJar).collect(Collectors.toList()); 107 | return layer.toBuilder().setEntries(newEntries).build(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-maven/src/main/resources/META-INF/services/com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension: -------------------------------------------------------------------------------- 1 | com.google.cloud.tools.jib.maven.extension.springboot.JibSpringBootExtension -------------------------------------------------------------------------------- /first-party/jib-spring-boot-extension-maven/src/test/java/com/google/cloud/tools/jib/maven/extension/springboot/JibSpringBootExtensionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | package com.google.cloud.tools.jib.maven.extension.springboot; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertFalse; 21 | import static org.junit.Assert.assertSame; 22 | import static org.junit.Assert.assertTrue; 23 | import static org.mockito.Mockito.verify; 24 | import static org.mockito.Mockito.when; 25 | 26 | import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; 27 | import com.google.cloud.tools.jib.api.buildplan.ContainerBuildPlan; 28 | import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; 29 | import com.google.cloud.tools.jib.api.buildplan.LayerObject; 30 | import com.google.cloud.tools.jib.maven.extension.MavenData; 31 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger; 32 | import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger.LogLevel; 33 | import com.google.cloud.tools.jib.plugins.extension.JibPluginExtensionException; 34 | import java.io.File; 35 | import java.nio.file.Path; 36 | import java.nio.file.Paths; 37 | import java.util.Arrays; 38 | import java.util.List; 39 | import java.util.Optional; 40 | import java.util.stream.Collectors; 41 | import org.apache.maven.model.Plugin; 42 | import org.apache.maven.project.MavenProject; 43 | import org.codehaus.plexus.util.xml.Xpp3Dom; 44 | import org.junit.Before; 45 | import org.junit.Test; 46 | import org.junit.runner.RunWith; 47 | import org.mockito.Mock; 48 | import org.mockito.junit.MockitoJUnitRunner; 49 | 50 | /** Tests for {@link JibSpringBootExtension}. */ 51 | @RunWith(MockitoJUnitRunner.class) 52 | public class JibSpringBootExtensionTest { 53 | 54 | @Mock private MavenData mavenData; 55 | @Mock private ExtensionLogger logger; 56 | 57 | @Mock private MavenProject project; 58 | @Mock private Plugin bootPlugin; 59 | 60 | private static FileEntriesLayer buildLayer(String layerName, Path... paths) { 61 | FileEntriesLayer.Builder builder = FileEntriesLayer.builder().setName(layerName); 62 | for (Path path : paths) { 63 | builder.addEntry(path, AbsoluteUnixPath.get("/dest/" + path.getFileName())); 64 | } 65 | return builder.build(); 66 | } 67 | 68 | private static List layerToExtractionPaths(FileEntriesLayer layer) { 69 | return layer.getEntries().stream() 70 | .map(layerEntry -> layerEntry.getExtractionPath().toString()) 71 | .collect(Collectors.toList()); 72 | } 73 | 74 | @Before 75 | public void setUp() { 76 | when(mavenData.getMavenProject()).thenReturn(project); 77 | } 78 | 79 | @Test 80 | public void testIsDevtoolsJar() { 81 | File file = Paths.get("sub", "folder", "spring-boot-devtools-1.2.3-SNAPSHOT.jar").toFile(); 82 | assertTrue(JibSpringBootExtension.isDevtoolsJar(file)); 83 | } 84 | 85 | @Test 86 | public void testIsDevtoolsJar_noJarExtension() { 87 | File file = Paths.get("sub", "folder", "spring-boot-devtools-1.2.3-SNAPSHOT").toFile(); 88 | assertFalse(JibSpringBootExtension.isDevtoolsJar(file)); 89 | } 90 | 91 | @Test 92 | public void testIsDevtoolsJar_differentJar() { 93 | File file = Paths.get("sub", "folder", "not-spring-boot-devtools-1.2.3-SNAPSHOT.jar").toFile(); 94 | assertFalse(JibSpringBootExtension.isDevtoolsJar(file)); 95 | } 96 | 97 | @Test 98 | public void testShouldExcludeDevtools_noSpringBootPlugin() { 99 | assertTrue(JibSpringBootExtension.shouldExcludeDevtools(project, logger)); 100 | 101 | verify(logger) 102 | .log( 103 | LogLevel.WARN, 104 | "Jib Spring Boot extension: project doesn't have spring-boot-maven-plugin?"); 105 | } 106 | 107 | @Test 108 | public void testShouldExcludeDevtools_trueByDefault() { 109 | when(project.getPlugin("org.springframework.boot:spring-boot-maven-plugin")) 110 | .thenReturn(bootPlugin); 111 | 112 | assertTrue(JibSpringBootExtension.shouldExcludeDevtools(project, logger)); 113 | } 114 | 115 | @Test 116 | public void testShouldExcludeDevtools_false() { 117 | Xpp3Dom configuration = new Xpp3Dom("configuration"); 118 | Xpp3Dom excludeDevtools = new Xpp3Dom("excludeDevtools"); 119 | excludeDevtools.setValue("FaLsE"); 120 | configuration.addChild(excludeDevtools); 121 | 122 | when(project.getPlugin("org.springframework.boot:spring-boot-maven-plugin")) 123 | .thenReturn(bootPlugin); 124 | when(bootPlugin.getConfiguration()).thenReturn(configuration); 125 | 126 | assertFalse(JibSpringBootExtension.shouldExcludeDevtools(project, logger)); 127 | } 128 | 129 | @Test 130 | public void testShouldExcludeDevtools_true() { 131 | Xpp3Dom configuration = new Xpp3Dom("configuration"); 132 | Xpp3Dom excludeDevtools = new Xpp3Dom("excludeDevtools"); 133 | excludeDevtools.setValue("tRuE"); 134 | configuration.addChild(excludeDevtools); 135 | 136 | when(project.getPlugin("org.springframework.boot:spring-boot-maven-plugin")) 137 | .thenReturn(bootPlugin); 138 | when(bootPlugin.getConfiguration()).thenReturn(configuration); 139 | 140 | assertTrue(JibSpringBootExtension.shouldExcludeDevtools(project, logger)); 141 | } 142 | 143 | @Test 144 | public void testFilterOutDevtools() { 145 | FileEntriesLayer layer = 146 | buildLayer( 147 | "dependencies", 148 | Paths.get("static").resolve("foo.txt"), 149 | Paths.get("lib").resolve("spring-boot-devtools-1.2.3.jar"), 150 | Paths.get("archive").resolve("bar.zip")); 151 | FileEntriesLayer filtered = (FileEntriesLayer) JibSpringBootExtension.filterOutDevtools(layer); 152 | 153 | assertEquals(Arrays.asList("/dest/foo.txt", "/dest/bar.zip"), layerToExtractionPaths(filtered)); 154 | } 155 | 156 | @Test 157 | public void testFilterOutDevtools_differentDependencyLayerName() { 158 | FileEntriesLayer layer = 159 | buildLayer( 160 | "NOT dependencies", 161 | Paths.get("lib").resolve("spring-boot-devtools-1.2.3.jar"), 162 | Paths.get("archive").resolve("bar.zip")); 163 | LayerObject newLayer = JibSpringBootExtension.filterOutDevtools(layer); 164 | assertSame(layer, newLayer); 165 | assertEquals(layer.getEntries(), ((FileEntriesLayer) newLayer).getEntries()); 166 | } 167 | 168 | @Test 169 | public void testExtendContainerBuildPlan_devtoolsFiltered() throws JibPluginExtensionException { 170 | when(project.getPlugin("org.springframework.boot:spring-boot-maven-plugin")) 171 | .thenReturn(bootPlugin); 172 | 173 | FileEntriesLayer layer1 = 174 | buildLayer( 175 | "dependencies", 176 | Paths.get("spring-boot-devtools-1.2.3.jar"), 177 | Paths.get("archive").resolve("bar.zip")); 178 | FileEntriesLayer layer2 = 179 | buildLayer("NOT dependencies", Paths.get("spring-boot-devtools-1.2.3.jar")); 180 | ContainerBuildPlan buildPlan = 181 | ContainerBuildPlan.builder().addLayer(layer1).addLayer(layer2).build(); 182 | 183 | ContainerBuildPlan newPlan = 184 | new JibSpringBootExtension() 185 | .extendContainerBuildPlan(buildPlan, null, Optional.empty(), mavenData, logger); 186 | 187 | assertEquals(2, newPlan.getLayers().size()); 188 | FileEntriesLayer newLayer1 = (FileEntriesLayer) newPlan.getLayers().get(0); 189 | FileEntriesLayer newLayer2 = (FileEntriesLayer) newPlan.getLayers().get(1); 190 | 191 | assertEquals(Arrays.asList("/dest/bar.zip"), layerToExtractionPaths(newLayer1)); 192 | assertEquals( 193 | Arrays.asList("/dest/spring-boot-devtools-1.2.3.jar"), layerToExtractionPaths(newLayer2)); 194 | 195 | verify(logger).log(LogLevel.INFO, "Removing spring-boot-devtools (if any)"); 196 | } 197 | 198 | @Test 199 | public void testExtendContainerBuildPlan_noFiltering() throws JibPluginExtensionException { 200 | // set up false (no filtering required) 201 | Xpp3Dom configuration = new Xpp3Dom("configuration"); 202 | Xpp3Dom excludeDevtools = new Xpp3Dom("excludeDevtools"); 203 | excludeDevtools.setValue("false"); 204 | configuration.addChild(excludeDevtools); 205 | 206 | when(project.getPlugin("org.springframework.boot:spring-boot-maven-plugin")) 207 | .thenReturn(bootPlugin); 208 | when(bootPlugin.getConfiguration()).thenReturn(configuration); 209 | 210 | FileEntriesLayer layer1 = 211 | buildLayer( 212 | "dependencies", 213 | Paths.get("spring-boot-devtools-1.2.3.jar"), 214 | Paths.get("archive").resolve("bar.zip")); 215 | FileEntriesLayer layer2 = 216 | buildLayer("NOT dependencies", Paths.get("spring-boot-devtools-1.2.3.jar")); 217 | ContainerBuildPlan buildPlan = 218 | ContainerBuildPlan.builder().addLayer(layer1).addLayer(layer2).build(); 219 | 220 | ContainerBuildPlan newPlan = 221 | new JibSpringBootExtension() 222 | .extendContainerBuildPlan(buildPlan, null, Optional.empty(), mavenData, logger); 223 | assertSame(buildPlan, newPlan); 224 | 225 | verify(logger).log(LogLevel.INFO, "Keeping spring-boot-devtools (if any)"); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /first-party/kokoro/release_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Fail on any error. 4 | set -o errexit 5 | # Display commands to stderr. 6 | set -o xtrace 7 | 8 | cd github/jib-extensions/first-party 9 | 10 | git describe --tags # non-zero exit code (no tag found) will fail the script 11 | readonly EXTENSION_NAME=$( git describe --tags | sed -e 's/^v[0-9.]\+-//') 12 | 13 | ./gradlew ":${EXTENSION_NAME}:prepareRelease" 14 | -------------------------------------------------------------------------------- /first-party/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':jib-ownership-extension-maven' 2 | include ':jib-ownership-extension-gradle' 3 | include ':jib-layer-filter-extension-maven' 4 | include ':jib-layer-filter-extension-gradle' 5 | include ':jib-quarkus-extension-maven' 6 | include ':jib-quarkus-extension-gradle' 7 | include ':jib-spring-boot-extension-maven' 8 | include ':jib-spring-boot-extension-gradle' 9 | include ':jib-native-image-extension-maven' 10 | include ':jib-native-image-extension-gradle' 11 | -------------------------------------------------------------------------------- /third-party/README.md: -------------------------------------------------------------------------------- 1 | # Third-Party Extensions 2 | 3 | This page serves as a central portal for Jib plugin extensions developed by the Jib community. Note that this page merely provides links to third-party extensions, and the Jib team does not give any kind of endorsement on these extensions. 4 | 5 | If you have written a useful extension that you think will benefit the Jib community, file a PR to add a link to the list. Jib users will greatly appreciate it! 6 | 7 | Interested in writing an extension? It's easy! Take a look at ["Writing Your Own Extensions"](../README.md#writing-your-own-extensions). 8 | 9 | - AWS Lambda (+ general file path customizer) ([Maven](https://github.com/jdimeo/jib-extension-aws-lambda)): an extension to change file paths to conform to the convention that AWS Java Lambda expects. However, the extension is general and can customize any files that Jib puts. 10 | - Layer With Modification Time ([Maven](https://github.com/infobip/jib-layer-with-modification-time-extension-maven)): an extension for selectively setting file timestamps to build time (eg. for hosted web resources) 11 | - OSGi Bundle Packaging Plugin Extension ([Maven](https://github.com/thought-gang/jib-maven-plugin-extension.git)): an extension to containerize an OSGI bundle (Maven packaging type `bundle`) 12 | - Javaagent Attachment Plugin Extension ([Gradle](https://github.com/ryandens/javaagent-gradle-plugin#jib-integration)): An extension that allows you to automatically add a javaagent from a Maven repository as a layer in your container image built by Jib and modifies the entrypoint of the image to include the `-javaagent` flag. 13 | 14 | - to be added 15 | - ... 16 | - ... 17 | --------------------------------------------------------------------------------