mirror of
https://github.com/gradle/actions.git
synced 2025-11-26 17:09:10 +08:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9336dac04 | ||
|
|
8dbe9a3802 | ||
|
|
9c3430720d | ||
|
|
30c82f0068 | ||
|
|
e3bc05f224 | ||
|
|
485ea107b7 | ||
|
|
c1091c9c8e | ||
|
|
d0a116fff5 | ||
|
|
e238a7ad22 | ||
|
|
1d2ea6e5a8 | ||
|
|
114c1c234e | ||
|
|
2db3ae936e | ||
|
|
a68381d359 | ||
|
|
52ae27f7bb | ||
|
|
d1cd62d80a | ||
|
|
af6e576724 | ||
|
|
775b4d10d7 | ||
|
|
30610bc983 | ||
|
|
d4d72c9934 | ||
|
|
96b9cb4988 | ||
|
|
db270b9337 | ||
|
|
d91e2960eb | ||
|
|
0498421560 | ||
|
|
edb13383f3 | ||
|
|
cd560aa3ad | ||
|
|
500e0ee5b3 | ||
|
|
eb13cf7170 | ||
|
|
ea14aa9caf | ||
|
|
063cfaf0eb | ||
|
|
35f9242e22 | ||
|
|
90f1de0556 | ||
|
|
da512b52a5 | ||
|
|
db19848a5f | ||
|
|
941b289d84 | ||
|
|
bce7daca54 | ||
|
|
11eaed9738 | ||
|
|
cd62d9c9ef | ||
|
|
a54fb6a5bb | ||
|
|
ef36f81b41 | ||
|
|
18998bc43e | ||
|
|
a772c14b33 | ||
|
|
7763d71170 | ||
|
|
9ab93ee864 | ||
|
|
d124ec149f | ||
|
|
6ccde15122 | ||
|
|
750cdda3ed | ||
|
|
c198d84863 | ||
|
|
d211a39090 | ||
|
|
439ed0a0ac | ||
|
|
eef9b10930 | ||
|
|
8be796e9fa | ||
|
|
3c2d3b6f2a | ||
|
|
0fa10b26b8 |
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "3.17.1"
|
||||
id "com.gradle.common-custom-user-data-gradle-plugin" version "2.0"
|
||||
id "com.gradle.develocity" version "3.17.5"
|
||||
id "com.gradle.common-custom-user-data-gradle-plugin" version "2.0.1"
|
||||
}
|
||||
|
||||
develocity {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -8,7 +8,7 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
api("org.apache.commons:commons-math3:3.6.1")
|
||||
implementation("com.google.guava:guava:33.1.0-jre")
|
||||
implementation("com.google.guava:guava:33.2.1-jre")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id("com.gradle.develocity") version "3.17.1"
|
||||
id("com.gradle.common-custom-user-data-gradle-plugin") version "2.0"
|
||||
id("com.gradle.develocity") version "3.17.5"
|
||||
id("com.gradle.common-custom-user-data-gradle-plugin") version "2.0.1"
|
||||
}
|
||||
|
||||
develocity {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "3.17.1"
|
||||
id "com.gradle.develocity" version "3.17.5"
|
||||
}
|
||||
|
||||
develocity {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "3.17.1"
|
||||
id "com.gradle.develocity" version "3.17.5"
|
||||
}
|
||||
|
||||
develocity {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "3.17"
|
||||
id "com.gradle.develocity" version "3.17.5"
|
||||
}
|
||||
|
||||
develocity {
|
||||
|
||||
6
.github/workflows/ci-codeql.yml
vendored
6
.github/workflows/ci-codeql.yml
vendored
@@ -5,6 +5,12 @@ on:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'release/**'
|
||||
- 'dev/**' # Allow running Code QL on dev branches without a PR
|
||||
paths-ignore:
|
||||
- 'dist/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- 'dist/**'
|
||||
schedule:
|
||||
|
||||
2
.github/workflows/ci-init-script-check.yml
vendored
2
.github/workflows/ci-init-script-check.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
java-version: 11
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3 # Use a released version to avoid breakages
|
||||
- name: Run integration tests
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
java-version: 11
|
||||
- name: Setup Gradle
|
||||
id: setup-gradle
|
||||
uses: ./setup-gradle
|
||||
|
||||
@@ -18,6 +18,7 @@ permissions:
|
||||
env:
|
||||
SKIP_DIST: ${{ inputs.skip-dist }}
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: dependency-graph-${{ inputs.cache-key-prefix }}
|
||||
GITHUB_DEPENDENCY_GRAPH_REF: 'refs/tags/v0.0.1' # Use a different ref to avoid updating the real dependency graph for the repository
|
||||
|
||||
jobs:
|
||||
groovy-generate:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Test dependency graph
|
||||
name: Test dependency submission failures
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
@@ -15,6 +15,7 @@ on:
|
||||
env:
|
||||
SKIP_DIST: ${{ inputs.skip-dist }}
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: dependency-submission-failures-${{ inputs.cache-key-prefix }}
|
||||
GITHUB_DEPENDENCY_GRAPH_REF: 'refs/tags/v0.0.1' # Use a different ref to avoid updating the real dependency graph for the repository
|
||||
|
||||
jobs:
|
||||
failing-build:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Test dependency graph
|
||||
name: Test dependency submission
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
@@ -18,6 +18,7 @@ permissions:
|
||||
env:
|
||||
SKIP_DIST: ${{ inputs.skip-dist }}
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: dependency-submission-${{ inputs.cache-key-prefix }}
|
||||
GITHUB_DEPENDENCY_GRAPH_REF: 'refs/tags/v0.0.1' # Use a different ref to avoid updating the real dependency graph for the repository
|
||||
|
||||
jobs:
|
||||
groovy-generate-and-upload:
|
||||
@@ -223,8 +224,6 @@ jobs:
|
||||
with:
|
||||
gradle-version: ${{ matrix.gradle }}
|
||||
build-root-directory: .github/workflow-samples/no-wrapper${{ matrix.build-root-suffix }}
|
||||
env:
|
||||
GITHUB_DEPENDENCY_GRAPH_REF: 'refs/tags/v0.0.1' # Use a different ref to avoid updating the real dependency graph for the repository
|
||||
|
||||
after-setup-gradle:
|
||||
strategy:
|
||||
@@ -245,3 +244,79 @@ jobs:
|
||||
uses: ./dependency-submission
|
||||
with:
|
||||
build-root-directory: .github/workflow-samples/groovy-dsl
|
||||
|
||||
custom-report-dir-submit:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Generate dependency graph
|
||||
id: dependency-graph
|
||||
uses: ./dependency-submission
|
||||
with:
|
||||
dependency-graph: generate-and-submit
|
||||
build-root-directory: .github/workflow-samples/groovy-dsl
|
||||
env:
|
||||
DEPENDENCY_GRAPH_REPORT_DIR: '${{ github.workspace }}/custom/report-dir'
|
||||
- name: Check generated dependency graphs
|
||||
shell: bash
|
||||
run: |
|
||||
echo "report file: ${{ steps.dependency-graph.outputs.dependency-graph-file }}"
|
||||
|
||||
if [ ! -e "${{ steps.dependency-graph.outputs.dependency-graph-file }}" ]; then
|
||||
echo "Did not find dependency graph file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$(ls -A "${{ github.workspace }}/custom/report-dir")" ]; then
|
||||
echo "No dependency graph files found in custom directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
custom-report-dir-upload:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Generate and upload dependency graph
|
||||
id: dependency-graph
|
||||
uses: ./dependency-submission
|
||||
with:
|
||||
dependency-graph: generate-and-upload
|
||||
build-root-directory: .github/workflow-samples/groovy-dsl
|
||||
env:
|
||||
DEPENDENCY_GRAPH_REPORT_DIR: '${{ github.workspace }}/custom/report-dir'
|
||||
|
||||
custom-report-dir-download-and-submit:
|
||||
needs: custom-report-dir-upload
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Download and submit dependency graph
|
||||
uses: ./dependency-submission
|
||||
with:
|
||||
dependency-graph: download-and-submit
|
||||
build-root-directory: .github/workflow-samples/groovy-dsl
|
||||
env:
|
||||
DEPENDENCY_GRAPH_REPORT_DIR: '${{ github.workspace }}/custom/report-dir'
|
||||
- name: Check downloaded dependency graph
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -z "$(ls -A "${{ github.workspace }}/custom/report-dir")" ]; then
|
||||
echo "No dependency graph files found in custom directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
7
.github/workflows/integ-test-execution.yml
vendored
7
.github/workflows/integ-test-execution.yml
vendored
@@ -61,12 +61,17 @@ jobs:
|
||||
gradle: [7.5.1, 6.9.2, 5.6.4, 4.10.3, 3.5.1]
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
include:
|
||||
- java-version: 11
|
||||
- gradle: 5.6.4
|
||||
build-root-suffix: -gradle-5
|
||||
- gradle: 4.10.3
|
||||
build-root-suffix: -gradle-4
|
||||
- gradle: 3.5.1
|
||||
build-root-suffix: -gradle-4
|
||||
java-version: 8
|
||||
exclude:
|
||||
- os: macos-latest # Java 8 is not supported on macos-latest, so we cannot test Gradle 3.5.1
|
||||
gradle: 3.5.1
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
@@ -78,7 +83,7 @@ jobs:
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
java-version: ${{ matrix.java-version }}
|
||||
- name: Run Gradle build
|
||||
uses: ./setup-gradle
|
||||
id: gradle
|
||||
|
||||
147
.github/workflows/integ-test-inject-develocity.yml
vendored
147
.github/workflows/integ-test-inject-develocity.yml
vendored
@@ -26,14 +26,19 @@ jobs:
|
||||
DEVELOCITY_URL: https://ge.solutions-team.gradle.com
|
||||
DEVELOCITY_PLUGIN_VERSION: ${{ matrix.plugin-version }}
|
||||
DEVELOCITY_CCUD_PLUGIN_VERSION: '2.0'
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} # required to test against GE plugin 3.16.2
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
${{matrix.accessKeyEnv}}: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
gradle: [current, 7.6.2, 6.9.4, 5.6.4]
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
plugin-version: [3.16.2, 3.17]
|
||||
plugin-version: [3.16.2, 3.17.4]
|
||||
include:
|
||||
- plugin-version: 3.16.2
|
||||
accessKeyEnv: GRADLE_ENTERPRISE_ACCESS_KEY
|
||||
- plugin-version: 3.17.4
|
||||
accessKeyEnv: DEVELOCITY_ACCESS_KEY
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
@@ -45,7 +50,7 @@ jobs:
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
java-version: 11
|
||||
- name: Setup Gradle
|
||||
id: setup-gradle
|
||||
uses: ./setup-gradle
|
||||
@@ -61,4 +66,136 @@ jobs:
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('No Build Scan detected')
|
||||
core.setFailed('No Build Scan detected')
|
||||
- name: Check short lived token (DEVELOCITY_ACCESS_KEY)
|
||||
run: "[ ${#DEVELOCITY_ACCESS_KEY} -gt 500 ] || (echo 'DEVELOCITY_ACCESS_KEY does not look like a short lived token'; exit 1)"
|
||||
- name: Check short lived token (GRADLE_ENTERPRISE_ACCESS_KEY)
|
||||
run: "[ ${#GRADLE_ENTERPRISE_ACCESS_KEY} -gt 500 ] || (echo 'GRADLE_ENTERPRISE_ACCESS_KEY does not look like a short lived token'; exit 1)"
|
||||
|
||||
inject-develocity-with-access-key:
|
||||
env:
|
||||
DEVELOCITY_INJECTION_ENABLED: true
|
||||
DEVELOCITY_URL: 'https://ge.solutions-team.gradle.com'
|
||||
DEVELOCITY_PLUGIN_VERSION: ${{ matrix.plugin-version }}
|
||||
DEVELOCITY_CCUD_PLUGIN_VERSION: '2.0'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
gradle: [current, 7.6.2, 6.9.4, 5.6.4]
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
plugin-version: [3.16.2, 3.17.4]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
- name: Setup Gradle
|
||||
id: setup-gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||
gradle-version: ${{ matrix.gradle }}
|
||||
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
develocity-token-expiry: 1
|
||||
- name: Run Gradle build
|
||||
id: gradle
|
||||
working-directory: .github/workflow-samples/no-ge
|
||||
run: gradle help
|
||||
- name: Check short lived token (DEVELOCITY_ACCESS_KEY)
|
||||
run: "[ ${#DEVELOCITY_ACCESS_KEY} -gt 500 ] || (echo 'DEVELOCITY_ACCESS_KEY does not look like a short lived token'; exit 1)"
|
||||
- name: Check short lived token (GRADLE_ENTERPRISE_ACCESS_KEY)
|
||||
run: "[ ${#GRADLE_ENTERPRISE_ACCESS_KEY} -gt 500 ] || (echo 'GRADLE_ENTERPRISE_ACCESS_KEY does not look like a short lived token'; exit 1)"
|
||||
- name: Check Build Scan url
|
||||
if: ${{ !steps.gradle.outputs.build-scan-url }}
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('No Build Scan detected')
|
||||
|
||||
inject-develocity-short-lived-token-failed:
|
||||
env:
|
||||
DEVELOCITY_INJECTION_ENABLED: true
|
||||
DEVELOCITY_URL: 'https://localhost:3333/'
|
||||
DEVELOCITY_PLUGIN_VERSION: ${{ matrix.plugin-version }}
|
||||
DEVELOCITY_CCUD_PLUGIN_VERSION: '2.0'
|
||||
# Access key also set as an env var, we want to check it does not leak
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
gradle: [ current, 7.6.2, 6.9.4, 5.6.4 ]
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
plugin-version: [ 3.16.2, 3.17.4 ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
- name: Setup Gradle
|
||||
id: setup-gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
- name: Run Gradle build
|
||||
id: gradle
|
||||
working-directory: .github/workflow-samples/no-ge
|
||||
run: gradle help
|
||||
- name: Check access key is blank (DEVELOCITY_ACCESS_KEY)
|
||||
run: "[ \"${DEVELOCITY_ACCESS_KEY}\" == \"\" ] || (echo 'DEVELOCITY_ACCESS_KEY has leaked!'; exit 1)"
|
||||
- name: Check access key is not blank (GRADLE_ENTERPRISE_ACCESS_KEY)
|
||||
run: "[ \"${GRADLE_ENTERPRISE_ACCESS_KEY}\" != \"\" ] || (echo 'GRADLE_ENTERPRISE_ACCESS_KEY is still supported in v3!'; exit 1)"
|
||||
|
||||
inject-develocity-with-access-key-from-input-actions:
|
||||
env:
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
gradle: [ current, 7.6.2, 6.9.4, 5.6.4 ]
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
plugin-version: [ 3.16.2, 3.17.4 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
- name: Setup Gradle
|
||||
id: setup-gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||
gradle-version: ${{ matrix.gradle }}
|
||||
develocity-injection-enabled: true
|
||||
develocity-url: 'https://ge.solutions-team.gradle.com'
|
||||
develocity-plugin-version: ${{ matrix.plugin-version }}
|
||||
- name: Run Gradle build
|
||||
id: gradle
|
||||
working-directory: .github/workflow-samples/no-ge
|
||||
run: gradle help
|
||||
- name: Check Build Scan url
|
||||
if: ${{ !steps.gradle.outputs.build-scan-url }}
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('No Build Scan detected')
|
||||
|
||||
@@ -76,12 +76,17 @@ jobs:
|
||||
gradle: [7.3, 6.9, 5.6.4, 4.10.3, 3.5.1]
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
include:
|
||||
- java-version: 11
|
||||
- gradle: 5.6.4
|
||||
build-root-suffix: -gradle-5
|
||||
- gradle: 4.10.3
|
||||
build-root-suffix: -gradle-4
|
||||
- gradle: 3.5.1
|
||||
build-root-suffix: -gradle-4
|
||||
java-version: 8
|
||||
exclude:
|
||||
- os: macos-latest # Java 8 is not supported on macos-latest, so we cannot test Gradle 3.5.1
|
||||
gradle: 3.5.1
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
@@ -93,7 +98,7 @@ jobs:
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
java-version: ${{ matrix.java-version }}
|
||||
- name: Setup Gradle
|
||||
id: setup-gradle
|
||||
uses: ./setup-gradle
|
||||
|
||||
2
.github/workflows/update-checksums-file.yml
vendored
2
.github/workflows/update-checksums-file.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
body: |
|
||||
Automatically generated pull request to update the known wrapper checksums.
|
||||
|
||||
In case of conflicts, manually run the workflow from the [Actions tab](https://github.com/gradle/wrapper-validation-action/actions/workflows/update-checksums-file.yml), the changes will then be force-pushed onto this pull request branch.
|
||||
In case of conflicts, manually run the workflow from the [Actions tab](https://github.com/gradle/actions/actions/workflows/update-checksums-file.yml), the changes will then be force-pushed onto this pull request branch.
|
||||
Do not manually update the pull request branch; those changes might get overwritten.
|
||||
|
||||
> [!IMPORTANT]
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,2 @@
|
||||
.git
|
||||
.vscode
|
||||
actions.code-workspace
|
||||
|
||||
12
README.md
12
README.md
@@ -15,7 +15,8 @@ The recommended way to execute any Gradle build is with the help of the [Gradle
|
||||
```yaml
|
||||
name: Build
|
||||
|
||||
on: [ push ]
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -48,7 +49,9 @@ Simply add this as a new workflow file to your repository (eg `.github/workflows
|
||||
```yaml
|
||||
name: Dependency Submission
|
||||
|
||||
on: [ push ]
|
||||
on:
|
||||
push:
|
||||
branches: [ 'main' ]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -80,7 +83,10 @@ The action should be run in the root of the repository, as it will recursively s
|
||||
|
||||
```yaml
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
|
||||
24
RELEASING.md
24
RELEASING.md
@@ -1,8 +1,9 @@
|
||||
# Gradle GitHub Actions release process
|
||||
|
||||
## Preparation
|
||||
- Push any outstanding changes to branch main. For any change that impacts the released action, you must run npm via `./build all` and commit the various files generated into the dist directory.
|
||||
- Push any outstanding changes to branch main.
|
||||
- Check that https://github.com/gradle/actions/actions is green for all workflows for the main branch.
|
||||
- This should include any workflows triggered by `[bot] Update dist directory`
|
||||
- Decide on the version number to use for the release. The action releases should follow semantic versioning.
|
||||
- By default, a patch release is assumed (eg. `3.0.0` → `3.0.1`)
|
||||
- If new features have been added, bump the minor version (eg `3.1.1` → `3.2.0`)
|
||||
@@ -11,14 +12,12 @@
|
||||
|
||||
## Release gradle/actions
|
||||
- Create a tag for the release. The tag should have the format `v3.1.0`
|
||||
- From CLI: `git tag v3.1.0`
|
||||
- Push the commit and tag
|
||||
- From CLI: `git push --tags`
|
||||
- From CLI: `git tag v3.1.0 && git push --tags`
|
||||
- Go to https://github.com/gradle/actions/releases and "Draft new release"
|
||||
- Use the newly created tag and copy the tag name exactly as the release title.
|
||||
- Craft release notes content based on issues closed, PRs merged and commits
|
||||
- Include a Full changelog link in the format https://github.com/gradle/actions/compare/v2.12.0...v3.0.0
|
||||
- Publish the release. Before using "Publish release", check that [action workflows](https://github.com/gradle/actions/actions) are green for the version tag. eg https://github.com/gradle/actions/actions?query=branch%3Av3.0.0
|
||||
- Publish the release.
|
||||
- Force push the `v3` tag (or current major version) to point to the new release. It is conventional for users to bind to a major release version using this tag.
|
||||
- From CLI: `git tag -f -a -m "v3.0.0" v3 v3.0.0 && git push -f --tags`
|
||||
- Note that we set the commit message for the tag to the newly released version.
|
||||
@@ -38,6 +37,21 @@ During the 3.x release series, we will continue to publish parallel releases of
|
||||
- Force push the `v3` tag (or current major version) to point to the new release.
|
||||
- From CLI: `git tag -f -a -m "v3.0.0" v3 v3.0.0 && git push -f --tags`
|
||||
|
||||
## Release gradle/wrapper-validation-action
|
||||
|
||||
During the 3.x release series, we will continue to publish parallel releases of `gradle/wrapper-validation-action`. These releases will simply delegate to `gradle/actions/wrapper-validation` with the same version.
|
||||
|
||||
- Update the [wrapper-validation-action action.yml](https://github.com/gradle/wrapper-validation-action/blob/main/action.yml#L162) file to point to the newly released version of `gradle/actions/wrapper-validation`.
|
||||
- Ensure that any parameters that have been added to the `wrapper-validation` action (if any) are added to the action definition, and that these are passed on to setup-gradle.
|
||||
- Create and push a tag for the release.
|
||||
- From CLI: `git tag v3.1.0 && git push --tags`
|
||||
- Go to https://github.com/gradle/wrapper-validation-action/releases and "Draft new release"
|
||||
- Use the newly created tag and copy the tag name exactly as the release title.
|
||||
- In the release notes, point users to the gradle/actions release. Include a header informing users to switch to `gradle/actions/wrapper-validation`.
|
||||
- Publish the release.
|
||||
- Force push the `v3` tag (or current major version) to point to the new release.
|
||||
- From CLI: `git tag -f -a -m "v3.0.0" v3 v3.0.0 && git push -f --tags`
|
||||
|
||||
## Post release steps
|
||||
|
||||
Submit PRs to update the GitHub starter workflow. Starter workflows contain content that should reference the Git hash of the current gradle/actions release:
|
||||
|
||||
11
actions.code-workspace
Normal file
11
actions.code-workspace
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"path": "sources"
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
12
build
12
build
@@ -5,7 +5,7 @@ cd sources
|
||||
case "$1" in
|
||||
all)
|
||||
npm clean-install
|
||||
nprm run all
|
||||
npm run all
|
||||
;;
|
||||
act)
|
||||
# Build and copy outputs to the dist directory
|
||||
@@ -16,14 +16,20 @@ case "$1" in
|
||||
# Run act
|
||||
$@
|
||||
# Revert the changes to the dist directory
|
||||
git co -- dist
|
||||
git checkout -- dist
|
||||
;;
|
||||
init-scripts)
|
||||
cd test/init-scripts
|
||||
./gradlew check
|
||||
;;
|
||||
dist)
|
||||
npm install
|
||||
npm run build
|
||||
cd ..
|
||||
cp -r sources/dist .
|
||||
;;
|
||||
*)
|
||||
npm install
|
||||
npm run build
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
|
||||
@@ -10,7 +10,9 @@ Simply add this as a new workflow file to your repository (eg `.github/workflows
|
||||
```yaml
|
||||
name: Dependency Submission
|
||||
|
||||
on: [ push ]
|
||||
on:
|
||||
push:
|
||||
branches: ['main']
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -15,13 +15,13 @@ inputs:
|
||||
|
||||
dependency-resolution-task:
|
||||
description: |
|
||||
Task(s) that should be executed in order to resolve all project dependencies.
|
||||
Task(s) that should be executed in order to resolve all project dependencies.
|
||||
By default, the built-in `:ForceDependencyResolutionPlugin_resolveAllDependencies` task is executed.
|
||||
required: false
|
||||
|
||||
additional-arguments:
|
||||
description: |
|
||||
Additional arguments to pass to Gradle when generating the dependency graph.
|
||||
Additional arguments to pass to Gradle when generating the dependency graph.
|
||||
For example, `--no-configuration-cache --stacktrace`.
|
||||
required: false
|
||||
|
||||
@@ -40,7 +40,7 @@ inputs:
|
||||
|
||||
cache-write-only:
|
||||
description: |
|
||||
When 'true', entries will not be restored from the cache but will be saved at the end of the Job.
|
||||
When 'true', entries will not be restored from the cache but will be saved at the end of the Job.
|
||||
Setting this to 'true' implies cache-read-only will be 'false'.
|
||||
required: false
|
||||
default: false
|
||||
@@ -52,7 +52,7 @@ inputs:
|
||||
|
||||
cache-encryption-key:
|
||||
description: |
|
||||
A base64 encoded AES key used to encrypt the configuration-cache data. The key is exported as 'GRADLE_ENCRYPTION_KEY' for later steps.
|
||||
A base64 encoded AES key used to encrypt the configuration-cache data. The key is exported as 'GRADLE_ENCRYPTION_KEY' for later steps.
|
||||
A suitable key can be generated with `openssl rand -base64 16`.
|
||||
Configuration-cache data will not be saved/restored without an encryption key being provided.
|
||||
required: false
|
||||
@@ -92,7 +92,7 @@ inputs:
|
||||
'generate-and-submit' (default): Generates a dependency graph for the project and submits it in the same Job.
|
||||
'generate-and-upload': Generates a dependency graph for the project and saves it as a workflow artifact.
|
||||
'download-and-submit': Retrieves a previously saved dependency-graph and submits it to the repository.
|
||||
|
||||
|
||||
The `generate-and-upload` and `download-and-submit` options are designed to be used in an untrusted workflow scenario,
|
||||
where the workflow generating the dependency-graph cannot (or should not) be given the `contents: write` permissions
|
||||
required to submit via the Dependency Submission API.
|
||||
@@ -120,11 +120,19 @@ inputs:
|
||||
build-scan-terms-of-use-url:
|
||||
description: The URL to the Build Scan® terms of use. This input must be set to 'https://gradle.com/terms-of-service' or 'https://gradle.com/help/legal-terms-of-use'.
|
||||
required: false
|
||||
|
||||
|
||||
build-scan-terms-of-use-agree:
|
||||
description: Indicate that you agree to the Build Scan® terms of use. This input value must be "yes".
|
||||
required: false
|
||||
|
||||
develocity-access-key:
|
||||
description: Develocity access key. Should be set to a secret containing the Develocity Access key.
|
||||
required: false
|
||||
|
||||
develocity-token-expiry:
|
||||
description: The Develocity short-lived access tokens expiry in hours. Default is 2 hours.
|
||||
required: false
|
||||
|
||||
# DEPRECATED ACTION INPUTS
|
||||
build-scan-terms-of-service-url:
|
||||
description: The URL to the Build Scan® terms of use. This input must be set to 'https://gradle.com/terms-of-service'.
|
||||
@@ -150,7 +158,7 @@ inputs:
|
||||
description: When 'true', the action will not attempt to restore the Gradle User Home entries from other Jobs.
|
||||
required: false
|
||||
default: false
|
||||
|
||||
|
||||
# INTERNAL ACTION INPUTS
|
||||
# These inputs should not be configured directly, and are only used to pass environmental information to the action
|
||||
workflow-job-context:
|
||||
|
||||
465
dist/dependency-submission/main/index.js
vendored
465
dist/dependency-submission/main/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/dependency-submission/main/index.js.map
vendored
2
dist/dependency-submission/main/index.js.map
vendored
File diff suppressed because one or more lines are too long
423
dist/dependency-submission/post/index.js
vendored
423
dist/dependency-submission/post/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/dependency-submission/post/index.js.map
vendored
2
dist/dependency-submission/post/index.js.map
vendored
File diff suppressed because one or more lines are too long
465
dist/setup-gradle/main/index.js
vendored
465
dist/setup-gradle/main/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/setup-gradle/main/index.js.map
vendored
2
dist/setup-gradle/main/index.js.map
vendored
File diff suppressed because one or more lines are too long
465
dist/setup-gradle/post/index.js
vendored
465
dist/setup-gradle/post/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/setup-gradle/post/index.js.map
vendored
2
dist/setup-gradle/post/index.js.map
vendored
File diff suppressed because one or more lines are too long
59
dist/wrapper-validation/main/index.js
vendored
59
dist/wrapper-validation/main/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/wrapper-validation/main/index.js.map
vendored
2
dist/wrapper-validation/main/index.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -25,7 +25,9 @@ Simply add this as a new workflow file to your repository (eg `.github/workflows
|
||||
```yaml
|
||||
name: Dependency Submission
|
||||
|
||||
on: [ push ]
|
||||
on:
|
||||
push:
|
||||
branches: [ 'main' ]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -93,7 +95,7 @@ In some cases, the default action configuration will not be sufficient, and addi
|
||||
dependency-resolution-task: myDependencyResolutionTask
|
||||
|
||||
# Additional arguments that should be passed to execute Gradle
|
||||
additonal-arguments: --no-configuration-cache
|
||||
additional-arguments: --no-configuration-cache
|
||||
|
||||
# Enable configuration-cache reuse for this build.
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
@@ -104,6 +106,11 @@ In some cases, the default action configuration will not be sufficient, and addi
|
||||
|
||||
See the [Action Metadata file](../dependency-submission/action.yml) for a more detailed description of each input parameter.
|
||||
|
||||
The `GitHub Dependency Graph Gradle Plugin` can be further
|
||||
[configured via a number of environment variables](https://github.com/gradle/github-dependency-graph-gradle-plugin?#required-environment-variables).
|
||||
These will be automatically set by the `dependency-submission` action, but you may override these values
|
||||
by setting them explicitly in your workflow file.
|
||||
|
||||
# Resolving a dependency vulnerability
|
||||
|
||||
## Finding the source of a dependency vulnerability
|
||||
@@ -292,7 +299,8 @@ Example of a pull request workflow that executes a build for a pull request and
|
||||
```yaml
|
||||
name: Dependency review for pull requests
|
||||
|
||||
on: [ pull_request ]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -327,7 +335,8 @@ Because of this restriction, we require 2 separate workflows in order to generat
|
||||
```yaml
|
||||
name: Generate and save dependency graph
|
||||
|
||||
on: [ pull_request ]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read # 'write' permission is not available
|
||||
@@ -358,6 +367,7 @@ on:
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
@@ -381,7 +391,8 @@ Here's an example of a separate "Dependency Review" workflow that will wait for
|
||||
```yaml
|
||||
name: dependency-review
|
||||
|
||||
on: [ pull_request ]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Deprecation upgrade guide
|
||||
|
||||
As these actions evolve, certain inputs, behaviour and usages are deprecated for removal.
|
||||
As these actions evolve, certain inputs, behaviour and usages are deprecated for removal.
|
||||
Deprecated functionality will be fully supported during the current major release, and will be
|
||||
removed in the next major release.
|
||||
Users will receive a deprecation warning when they rely on deprecated functionality,
|
||||
removed in the next major release.
|
||||
Users will receive a deprecation warning when they rely on deprecated functionality,
|
||||
prompting them to update their workflows.
|
||||
|
||||
## The action `gradle/gradle-build-action` has been replaced by `gradle/actions/setup-gradle`
|
||||
@@ -25,10 +25,10 @@ with
|
||||
|
||||
## The action `gradle/wrapper-validation-action` has been replaced by `gradle/actions/wrapper-validation`
|
||||
|
||||
To facilitate ongoing development, the `wrapper-validation-action` action implementation has been merged into
|
||||
To facilitate ongoing development, the `wrapper-validation-action` action implementation has been merged into
|
||||
the https://github.com/gradle/actions repository, and the `gradle/wrapper-validation-action` has been replaced by the `gradle/actions/wrapper-validation` action.
|
||||
|
||||
As of `v3.x`, the `gradle/wrapper-validation-action` and `gradle/actions/wrappper-validation` actions are
|
||||
As of `v3.x`, the `gradle/wrapper-validation-action` and `gradle/actions/wrappper-validation` actions are
|
||||
functionally identical, and are released with the same versions.
|
||||
|
||||
In a future major version (likely `v4.x`) we will stop releasing new versions of `gradle/wrapper-validation-action`:
|
||||
@@ -101,7 +101,7 @@ The exact syntax depends on whether or not your project is configured with the [
|
||||
- name: Setup Gradle for a non-wrapper project
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
gradle-version: 8.7
|
||||
gradle-version: 8.8
|
||||
|
||||
- name: Assemble the project
|
||||
run: gradle assemble
|
||||
@@ -143,3 +143,8 @@ to this:
|
||||
build-scan-terms-of-use-agree: "yes"
|
||||
```
|
||||
These deprecated build-scan parameters are scheduled to be removed in `setup-gradle@v4` and `dependency-submission@v4`.
|
||||
|
||||
## The GRADLE_ENTERPRISE_ACCESS_KEY env var is deprecated
|
||||
Gradle Enterprise has been renamed to Develocity starting from Gradle plugin 3.17 and Develocity server 2024.1.
|
||||
In v4 release of the action, it will require setting the access key with the `develocity-access-key` input and Develocity 2024.1 at least to generate short-lived tokens.
|
||||
If those requirements are not met, the `GRADLE_ENTERPRISE_ACCESS_KEY` env var will be cleared out and build scan publication or other authenticated Develocity operations won't be possible.
|
||||
|
||||
@@ -4,22 +4,22 @@ This GitHub Action can be used to configure Gradle for optimal execution on any
|
||||
|
||||
## Why use the `setup-gradle` action?
|
||||
|
||||
It is possible to directly invoke Gradle in your workflow, and the `actions/setup-java@v4` action provides a simple way to cache Gradle dependencies.
|
||||
It is possible to directly invoke Gradle in your workflow, and the `actions/setup-java@v4` action provides a simple way to cache Gradle dependencies.
|
||||
|
||||
However, the `setup-gradle` action offers a several advantages over this approach:
|
||||
|
||||
- Easily [configure your workflow to use a specific version of Gradle](#build-with-a-specific-gradle-version) using the `gradle-version` parameter. Gradle distributions are automatically downloaded and cached.
|
||||
- Easily [configure your workflow to use a specific version of Gradle](#build-with-a-specific-gradle-version) using the `gradle-version` parameter. Gradle distributions are automatically downloaded and cached.
|
||||
- More sophisticated and more efficient caching of Gradle User Home between invocations, compared to `setup-java` and most custom configurations using `actions/cache`. [More details below](#caching-build-state-between-jobs).
|
||||
- Detailed reporting of cache usage and cache configuration options allow you to [optimize the use of the GitHub actions cache](#optimizing-cache-effectiveness).
|
||||
- [Generate and Submit a GitHub Dependency Graph](#github-dependency-graph-support) for your project, enabling Dependabot security alerts.
|
||||
- [Automatic capture of Build Scan® links](#build-reporting) from the build, making them easier to locate in workflow runs.
|
||||
|
||||
The `setup-gradle` action is designed to provide these benefits with minimal configuration.
|
||||
The `setup-gradle` action is designed to provide these benefits with minimal configuration.
|
||||
These features work both when Gradle is executed via `setup-gradle` and for any Gradle execution in subsequent steps.
|
||||
|
||||
## General usage
|
||||
|
||||
The `setup-gradle` action works by configuring environment variables and by adding a set of Gradle init-scripts to the Gradle User Home. These will apply to all Gradle executions on the runner, no matter how Gradle is invoked.
|
||||
The `setup-gradle` action works by configuring environment variables and by adding a set of Gradle init-scripts to the Gradle User Home. These will apply to all Gradle executions on the runner, no matter how Gradle is invoked.
|
||||
This means that if you have an existing workflow that executes Gradle with a `run` step, you can add an initial "Setup Gradle" Step to benefit from caching, build-scan capture, and other features of this action.
|
||||
|
||||
The recommended way to execute any Gradle build is with the help of the [Gradle Wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html), and the following examples assume that the Gradle Wrapper has been configured for the project. See [this example](#build-with-a-specific-gradle-version) if your project doesn't use the Gradle Wrapper.
|
||||
@@ -28,7 +28,8 @@ The recommended way to execute any Gradle build is with the help of the [Gradle
|
||||
```yaml
|
||||
name: Run Gradle on every push
|
||||
|
||||
on: push
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
gradle:
|
||||
@@ -45,7 +46,7 @@ jobs:
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
|
||||
|
||||
- name: Execute Gradle build
|
||||
run: ./gradlew build
|
||||
```
|
||||
@@ -125,7 +126,7 @@ cache-disabled: true
|
||||
### Using the cache read-only
|
||||
|
||||
By default, The `setup-gradle` action will only write to the cache from Jobs on the default (`main`/`master`) branch.
|
||||
Jobs on other branches will read entries from the cache but will not write updated entries.
|
||||
Jobs on other branches will read entries from the cache but will not write updated entries.
|
||||
See [Optimizing cache effectiveness](#select-which-branches-should-write-to-the-cache) for a more detailed explanation.
|
||||
|
||||
In some circumstances, it makes sense to change this default and configure a workflow Job to read existing cache entries but not to write changes back.
|
||||
@@ -157,7 +158,7 @@ cache-write-only: true
|
||||
When the action detects that the Gradle User Home caches directory already exists (`~/.gradle/caches`), then by default it will not overwrite the existing content of this directory.
|
||||
This can occur when a prior action initializes this directory, or when using a self-hosted runner that retains this directory between uses.
|
||||
|
||||
In this case, the Job Summary will display a message like:
|
||||
In this case, the Job Summary will display a message like:
|
||||
> Caching for Gradle actions was disabled due to pre-existing Gradle User Home
|
||||
|
||||
If you want to override the default and have the caches of the `setup-gradle` action overwrite existing content in the Gradle User Home, you can set the `cache-overwrite-existing` parameter to `true`:
|
||||
@@ -197,13 +198,13 @@ jobs:
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The configuration cache cannot be saved or restored in workflows triggered by a pull requests from a repsitory fork.
|
||||
> The configuration cache cannot be saved or restored in workflows triggered by a pull requests from a repository fork.
|
||||
> This is because [GitHub secrets are not passed to workflows triggered by PRs from forks](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#using-secrets-in-a-workflow).
|
||||
> This prevents a malicious PR from reading the configuration-cache data, which may encode secrets read by Gradle.
|
||||
|
||||
### Incompatibility with other caching mechanisms
|
||||
|
||||
When using `setup-gradle` we recommend that you avoid using other mechanisms to save and restore the Gradle User Home.
|
||||
When using `setup-gradle` we recommend that you avoid using other mechanisms to save and restore the Gradle User Home.
|
||||
|
||||
Specifically:
|
||||
- Avoid using `actions/cache` configured to cache the Gradle User Home, [as described in this example](https://github.com/actions/cache/blob/main/examples.md#java---gradle).
|
||||
@@ -255,7 +256,7 @@ The Gradle User Home cache key is composed of:
|
||||
|
||||
Specifically, the cache key is: `${cache-protocol}-gradle|${runner-os}|${job-id}[${hash-of-job-matrix-and-workflow-name}]-${git-sha}`
|
||||
|
||||
As such, the cache key is likely to change on each subsequent run of GitHub actions.
|
||||
As such, the cache key is likely to change on each subsequent run of GitHub actions.
|
||||
This allows the most recent state to always be available in the GitHub actions cache.
|
||||
|
||||
### Finding a matching cache entry
|
||||
@@ -270,7 +271,7 @@ Due to branch scoping of cache entries, the above match will be first performed
|
||||
|
||||
After the Job is complete, the current Gradle User Home state will be collected and written as a new cache entry with the complete cache key. Old entries will be expunged from the GitHub Actions cache on a least recently used basis.
|
||||
|
||||
Note that while effective, this mechanism is not inherently efficient. It requires the entire Gradle User Home directory to be stored separately for each branch, for every OS+Job+Matrix combination. In addition, it writes a new cache entry on every GitHub Actions run.
|
||||
Note that while effective, this mechanism is not inherently efficient. It requires the entire Gradle User Home directory to be stored separately for each branch, for every OS+Job+Matrix combination. In addition, it writes a new cache entry on every GitHub Actions run.
|
||||
|
||||
This inefficiency is effectively mitigated by [Deduplication of Gradle User Home cache entries](#deduplication-of-gradle-user-home-cache-entries) and can be further optimized for a workflow using the techniques described in [Optimizing cache effectiveness](#optimizing-cache-effectiveness).
|
||||
|
||||
@@ -288,7 +289,7 @@ For example, this means that all jobs executing a particular version of the Grad
|
||||
|
||||
### Stopping the Gradle daemon
|
||||
|
||||
By default, the action will stop all running Gradle daemons in the post-action step, before saving the Gradle User Home state.
|
||||
By default, the action will stop all running Gradle daemons in the post-action step, before saving the Gradle User Home state.
|
||||
This allows for any Gradle User Home cleanup to occur, and avoid file-locking issues on Windows.
|
||||
|
||||
If caching is disabled or the cache is in read-only mode, the daemon will not be stopped and will continue running after the job is completed.
|
||||
@@ -315,23 +316,23 @@ Some techniques can be used to avoid/mitigate this issue:
|
||||
|
||||
### Select which branches should write to the cache
|
||||
|
||||
GitHub cache entries are not shared between builds on different branches.
|
||||
GitHub cache entries are not shared between builds on different branches.
|
||||
Workflow runs can restore caches created in either the current branch or the default branch (usually main).
|
||||
This means that each branch will have its own Gradle User Home cache scope, and will not benefit from cache entries written for other (non-default) branches.
|
||||
|
||||
By default, The `setup-gradle` action will only _write_ to the cache for builds run on the default (`master`/`main`) branch.
|
||||
Jobs running on other branches will only read from the cache. In most cases, this is the desired behavior.
|
||||
This is because Jobs running on other branches will benefit from the cached Gradle User Home from `main`,
|
||||
By default, The `setup-gradle` action will only _write_ to the cache for builds run on the default (`master`/`main`) branch.
|
||||
Jobs running on other branches will only read from the cache. In most cases, this is the desired behavior.
|
||||
This is because Jobs running on other branches will benefit from the cached Gradle User Home from `main`,
|
||||
without writing private cache entries which could lead to evicting these shared entries.
|
||||
|
||||
If you have other long-lived development branches that would benefit from writing to the cache,
|
||||
you can configure this by disabling the `cache-read-only` action parameter for these branches.
|
||||
If you have other long-lived development branches that would benefit from writing to the cache,
|
||||
you can configure this by disabling the `cache-read-only` action parameter for these branches.
|
||||
See [Using the cache read-only](#using-the-cache-read-only) for more details.
|
||||
|
||||
Note there are some cases where writing cache entries is typically unhelpful (these are disabled by default):
|
||||
- For `pull_request` triggered runs, the cache scope is limited to the merge ref (`refs/pull/.../merge`) and can only be restored by re-runs of the same pull request.
|
||||
- For `merge_group` triggered runs, the cache scope is limited to a temporary branch with a special prefix created to validate pull request changes, and won't be available on subsequent Merge Queue executions.
|
||||
|
||||
|
||||
### Exclude content from Gradle User Home cache
|
||||
|
||||
As well as any wrapper distributions, the action will attempt to save and restore the `caches` and `notifications` directories from Gradle User Home.
|
||||
@@ -352,7 +353,7 @@ gradle-home-cache-excludes: |
|
||||
caches/keyrings
|
||||
```
|
||||
|
||||
You can specify any number of fixed paths or patterns to include or exclude.
|
||||
You can specify any number of fixed paths or patterns to include or exclude.
|
||||
File pattern support is documented at https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#patterns-to-match-file-paths.
|
||||
|
||||
### Remove unused files from Gradle User Home before saving to the cache
|
||||
@@ -361,7 +362,7 @@ The Gradle User Home directory tends to grow over time. When you switch to a new
|
||||
the old files are not automatically and immediately removed. While this can make sense in a local environment, in a GitHub Actions environment
|
||||
it can lead to ever-larger Gradle User Home cache entries being saved and restored.
|
||||
|
||||
To avoid this situation, The `setup-gradle` action supports the `gradle-home-cache-cleanup` parameter.
|
||||
To avoid this situation, The `setup-gradle` action supports the `gradle-home-cache-cleanup` parameter.
|
||||
When enabled, this feature will attempt to delete any files in the Gradle User Home that were not used by Gradle during the GitHub Actions Workflow, before saving the Gradle User Home to the GitHub Actions cache.
|
||||
|
||||
Gradle Home cache cleanup is considered experimental and is disabled by default. You can enable this feature for the action as follows:
|
||||
@@ -375,7 +376,7 @@ If you have a remote build-cache available for your build, then it is recommende
|
||||
- Enable [remote build-cache push](https://docs.gradle.org/current/userguide/build_cache.html#sec:build_cache_configure_use_cases) for your GitHub Actions builds
|
||||
- Disable [local build-cache]() for your GitHub Actions build
|
||||
|
||||
As well as reducing the content that needs to be saved to the GitHub Actions cache,
|
||||
As well as reducing the content that needs to be saved to the GitHub Actions cache,
|
||||
this setup will ensure that your CI builds populate the remote cache and keep the cache entries fresh by reading these entries.
|
||||
Local builds can then benefit from the remote cache.
|
||||
|
||||
@@ -388,8 +389,8 @@ You can enable debug logging either by:
|
||||
|
||||
### Increased logging from Gradle builds
|
||||
|
||||
When debug logging is enabled, this action will cause all builds to run with the `--info` and `--stacktrace` options.
|
||||
This is done by inserting the relevant [Gradle properties](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties)
|
||||
When debug logging is enabled, this action will cause all builds to run with the `--info` and `--stacktrace` options.
|
||||
This is done by inserting the relevant [Gradle properties](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties)
|
||||
at the top of the `${GRADLE_USER_HOME}/gradle.properties` file.
|
||||
|
||||
If the additional Gradle logging produced is problematic, you may opt out of this behavior by setting these properties manually in your project `gradle.properties` file:
|
||||
@@ -402,7 +403,7 @@ org.gradle.logging.stacktrace=internal
|
||||
|
||||
### Cache debugging and analysis
|
||||
|
||||
A report of all cache entries restored and saved is printed to the Job Summary when saving the cache entries.
|
||||
A report of all cache entries restored and saved is printed to the Job Summary when saving the cache entries.
|
||||
This report can provide valuable insight into how much cache space is being used.
|
||||
|
||||
When debug logging is enabled, more detailed logging of cache operations is included in the GitHub actions log.
|
||||
@@ -460,18 +461,18 @@ Note that to add a Pull Request comment, the workflow must be configured with th
|
||||
As well as reporting all [Build Scan](https://gradle.com/build-scans/) links in the Job Summary,
|
||||
The `setup-gradle` action makes this link available as an output of any Step that executes Gradle.
|
||||
|
||||
The output name is `build-scan-url`. You can then use the build scan link in subsequent actions of your workflow.
|
||||
The output name is `build-scan-url`. You can then use the build scan link in subsequent actions of your workflow.
|
||||
|
||||
### Saving arbitrary build outputs
|
||||
|
||||
By default, a GitHub Actions workflow using `setup-gradle` will record the log output and any Build Scan
|
||||
By default, a GitHub Actions workflow using `setup-gradle` will record the log output and any Build Scan
|
||||
links for your build, but any output files generated by the build will not be saved.
|
||||
|
||||
To save selected files from your build execution, you can use the core [Upload-Artifact](https://github.com/actions/upload-artifact) action.
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
jobs:
|
||||
gradle:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -505,7 +506,7 @@ Since Gradle applies init scripts in alphabetical order, one way to ensure this
|
||||
|
||||
## Gradle Wrapper validation
|
||||
|
||||
Instead of using the [wrapper-validation action](./wrapper-validation.md) separately, you can enable
|
||||
Instead of using the [wrapper-validation action](./wrapper-validation.md) separately, you can enable
|
||||
wrapper validation directly in your Setup Gradle step.
|
||||
|
||||
```yaml
|
||||
@@ -529,7 +530,7 @@ You can use the `setup-gradle` action on GitHub Enterprise Server, and benefit f
|
||||
## GitHub Dependency Graph support
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The simplest (and recommended) way to generate a dependency graph is via a separate workflow
|
||||
> The simplest (and recommended) way to generate a dependency graph is via a separate workflow
|
||||
> using `gradle/actions/dependency-submission`. This action will attempt to detect all dependencies used by your build
|
||||
> without building and testing the project itself.
|
||||
>
|
||||
@@ -543,7 +544,7 @@ The dependency graph snapshot is generated via integration with the [GitHub Depe
|
||||
The generated dependency graph snapshot reports all of the dependencies that were resolved during a build execution, and is used by GitHub to generate [Dependabot Alerts](https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts) for vulnerable dependencies, as well as to populate the [Dependency Graph insights view](https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/exploring-the-dependencies-of-a-repository#viewing-the-dependency-graph).
|
||||
|
||||
### Basic usage
|
||||
|
||||
|
||||
You enable GitHub Dependency Graph support by setting the `dependency-graph` action parameter. Valid values are:
|
||||
|
||||
| Option | Behaviour |
|
||||
@@ -559,7 +560,7 @@ Example of a CI workflow that generates and submits a dependency graph:
|
||||
name: CI build
|
||||
on:
|
||||
push:
|
||||
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -581,13 +582,13 @@ jobs:
|
||||
run: ./gradlew build
|
||||
```
|
||||
|
||||
The `contents: write` permission is required to submit (but not generate) the dependency graph file.
|
||||
The `contents: write` permission is required to submit (but not generate) the dependency graph file.
|
||||
Depending on [repository settings](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token), this permission may be available by default or may need to be explicitly enabled in the workflow file (as above).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The above configuration will work for workflows that run as a result of commits to a repository branch,
|
||||
> The above configuration will work for workflows that run as a result of commits to a repository branch,
|
||||
> but not when a workflow is triggered by a PR from a repository fork.
|
||||
> This is because the `contents: write` permission is not available when executing a workflow
|
||||
> This is because the `contents: write` permission is not available when executing a workflow
|
||||
> for a PR submitted from a forked repository.
|
||||
> For a configuration that supports this setup, see [Dependency Graphs for pull request workflows](dependency-submission.md#usage-with-pull-requests-from-public-forked-repositories).
|
||||
|
||||
@@ -609,7 +610,7 @@ graph cannot be generated or submitted. You can enable this behavior with the `d
|
||||
|
||||
### Using a custom plugin repository
|
||||
|
||||
By default, the action downloads the `github-dependency-graph-gradle-plugin` from the Gradle Plugin Portal (https://plugins.gradle.org). If your GitHub Actions environment does not have access to this URL, you can specify a custom plugin repository to use.
|
||||
By default, the action downloads the `github-dependency-graph-gradle-plugin` from the Gradle Plugin Portal (https://plugins.gradle.org). If your GitHub Actions environment does not have access to this URL, you can specify a custom plugin repository to use.
|
||||
|
||||
Do so by setting the `GRADLE_PLUGIN_REPOSITORY_URL` environment variable with your Gradle invocation.
|
||||
The `GRADLE_PLUGIN_REPOSITORY_USERNAME` and `GRADLE_PLUGIN_REPOSITORY_PASSWORD` can be used when the plugin repository requires authentication.
|
||||
@@ -669,7 +670,7 @@ jobs:
|
||||
|
||||
### Filtering which Gradle Configurations contribute to the dependency graph
|
||||
|
||||
If you do not want the dependency graph to include every dependency configuration in every project in your build,
|
||||
If you do not want the dependency graph to include every dependency configuration in every project in your build,
|
||||
you can limit the dependency extraction to a subset of these.
|
||||
|
||||
See the documentation for [dependency-submission](dependency-submission.md) and the
|
||||
@@ -677,7 +678,7 @@ See the documentation for [dependency-submission](dependency-submission.md) and
|
||||
|
||||
### Gradle version compatibility
|
||||
|
||||
Dependency-graph generation is compatible with most versions of Gradle >= `5.2`, and is tested regularly against
|
||||
Dependency-graph generation is compatible with most versions of Gradle >= `5.2`, and is tested regularly against
|
||||
Gradle versions `5.2.1`, `5.6.4`, `6.0.1`, `6.9.4`, `7.1.1` and `7.6.3`, as well as all patched versions of Gradle 8.x.
|
||||
|
||||
A known exception to this is that Gradle `7.0`, `7.0.1`, and `7.0.2` are not supported.
|
||||
@@ -686,7 +687,7 @@ See [here](https://github.com/gradle/github-dependency-graph-gradle-plugin?tab=r
|
||||
|
||||
### Reducing storage costs for saved dependency graph artifacts
|
||||
|
||||
When `generate` or `generate-and-submit` is used with the action, the dependency graph that is generated is stored as a workflow artifact.
|
||||
When `generate` or `generate-and-submit` is used with the action, the dependency graph that is generated is stored as a workflow artifact.
|
||||
By default, these artifacts are retained for 30 days (or as configured for the repository).
|
||||
To reduce storage costs for these artifacts, you can set the `artifact-retention-days` value to a lower number.
|
||||
|
||||
@@ -707,13 +708,32 @@ The same auto-injection behavior is available for the Common Custom User Data Gr
|
||||
|
||||
## Enabling Develocity injection
|
||||
|
||||
To enable Develocity injection for your build, you must provide the required configuration via environment variables.
|
||||
To enable Develocity injection for your build, you must provide the required configuration via inputs.
|
||||
|
||||
Here's a minimal example:
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
develocity-injection-enabled: true
|
||||
develocity-url: https://develocity.your-server.com
|
||||
develocity-plugin-version: 3.17.5
|
||||
|
||||
- name: Run a Gradle build with Develocity injection enabled
|
||||
run: ./gradlew build
|
||||
```
|
||||
|
||||
This configuration will automatically apply `v3.17.5` of the [Develocity Gradle plugin](https://docs.gradle.com/develocity/gradle-plugin/), and publish build scans to https://develocity.your-server.com.
|
||||
|
||||
This example assumes that the `develocity.your-server.com` server allows anonymous publishing of build scans.
|
||||
In the likely scenario that your Develocity server requires authentication, you will also need to pass a valid [Develocity access key](https://docs.gradle.com/develocity/gradle-plugin/#via_environment_variable) taken from a secret:
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.MY_DEVELOCITY_ACCESS_KEY }}
|
||||
|
||||
- name: Run a Gradle build with Develocity injection enabled
|
||||
run: ./gradlew build
|
||||
@@ -723,11 +743,14 @@ Here's a minimal example:
|
||||
DEVELOCITY_PLUGIN_VERSION: 3.17
|
||||
```
|
||||
|
||||
This configuration will automatically apply `v3.17` of the [Develocity Gradle plugin](https://docs.gradle.com/develocity/gradle-plugin/), and publish build scans to https://develocity.your-server.com.
|
||||
This access key will be used during the action execution to get a short-lived token and set it to the DEVELOCITY_ACCESS_KEY environment variable.
|
||||
|
||||
This example assumes that the `develocity.your-server.com` server allows anonymous publishing of build scans.
|
||||
In the likely scenario that your Develocity server requires authentication, you will also need to configure an additional environment variable
|
||||
with a valid [Develocity access key](https://docs.gradle.com/develocity/gradle-plugin/#via_environment_variable).
|
||||
### Short-lived access tokens
|
||||
Develocity access keys are long-lived, creating risks if they are leaked. To avoid this, users can use short-lived access tokens to authenticate with Develocity. Access tokens can be used wherever an access key would be used. Access tokens are only valid for the Develocity instance that created them.
|
||||
If a short-lived token fails to be retrieved (for example, if the Develocity server version is lower than `2024.1`):
|
||||
- if a `GRADLE_ENTERPRISE_ACCESS_KEY` env var has been set, we're falling back to it with a deprecation warning
|
||||
- otherwise no access key env var will be set. In that case Develocity authenticated operations like build cache read/write and build scan publication will fail without failing the build.
|
||||
For more information on short-lived tokens, see [Develocity API documentation](https://docs.gradle.com/develocity/api-manual/#short_lived_access_tokens).
|
||||
|
||||
## Configuring Develocity injection
|
||||
|
||||
@@ -735,16 +758,46 @@ The `init-script` supports several additional configuration parameters that you
|
||||
|
||||
| Variable | Required | Description |
|
||||
|--------------------------------------| --- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| DEVELOCITY_INJECTION_ENABLED | :white_check_mark: | enables Develocity injection |
|
||||
| DEVELOCITY_URL | :white_check_mark: | the URL of the Develocity server |
|
||||
| DEVELOCITY_ALLOW_UNTRUSTED_SERVER | | allow communication with an untrusted server; set to _true_ if your Develocity instance is using a self-signed certificate |
|
||||
| DEVELOCITY_CAPTURE_FILE_FINGERPRINTS | | enables capturing the paths and content hashes of each individual input file |
|
||||
| DEVELOCITY_ENFORCE_URL | | enforce the configured Develocity URL over a URL configured in the project's build; set to _true_ to enforce publication of build scans to the configured Develocity URL |
|
||||
| DEVELOCITY_PLUGIN_VERSION | :white_check_mark: | the version of the [Develocity Gradle plugin](https://docs.gradle.com/develocity/gradle-plugin/) to apply |
|
||||
| DEVELOCITY_CCUD_PLUGIN_VERSION | | the version of the [Common Custom User Data Gradle plugin](https://github.com/gradle/common-custom-user-data-gradle-plugin) to apply, if any |
|
||||
| GRADLE_PLUGIN_REPOSITORY_URL | | the URL of the repository to use when resolving the Develocity and CCUD plugins; the Gradle Plugin Portal is used by default |
|
||||
| GRADLE_PLUGIN_REPOSITORY_USERNAME | | the username for the repository URL to use when resolving the Develocity and CCUD plugins |
|
||||
| GRADLE_PLUGIN_REPOSITORY_PASSWORD | | the password for the repository URL to use when resolving the Develocity and CCUD plugins; Consider using secrets to pass the value to this variable |
|
||||
| develocity-injection-enabled | :white_check_mark: | enables Develocity injection |
|
||||
| develocity-url | :white_check_mark: | the URL of the Develocity server |
|
||||
| develocity-allow-untrusted-server | | allow communication with an untrusted server; set to _true_ if your Develocity instance is using a self-signed certificate |
|
||||
| develocity-capture-file-fingerprints | | enables capturing the paths and content hashes of each individual input file |
|
||||
| develocity-enforce-url | | enforce the configured Develocity URL over a URL configured in the project's build; set to _true_ to enforce publication of build scans to the configured Develocity URL |
|
||||
| develocity-plugin-version | :white_check_mark: | the version of the [Develocity Gradle plugin](https://docs.gradle.com/develocity/gradle-plugin/) to apply |
|
||||
| develocity-ccud-plugin-version | | the version of the [Common Custom User Data Gradle plugin](https://github.com/gradle/common-custom-user-data-gradle-plugin) to apply, if any |
|
||||
| gradle-plugin-repository-url | | the URL of the repository to use when resolving the Develocity and CCUD plugins; the Gradle Plugin Portal is used by default |
|
||||
| gradle-plugin-repository-username | | the username for the repository URL to use when resolving the Develocity and CCUD plugins |
|
||||
| gradle-plugin-repository-password | | the password for the repository URL to use when resolving the Develocity and CCUD plugins; Consider using secrets to pass the value to this variable |
|
||||
|
||||
The input parameters can be expressed as environment variables following the relationships outlined in the table below:
|
||||
|
||||
| Input | Environment Variable |
|
||||
|--------------------------------------|--------------------------------------|
|
||||
| develocity-injection-enabled | DEVELOCITY_INJECTION_ENABLED |
|
||||
| develocity-url | DEVELOCITY_URL |
|
||||
| develocity-allow-untrusted-server | DEVELOCITY_ALLOW_UNTRUSTED_SERVER |
|
||||
| develocity-capture-file-fingerprints | DEVELOCITY_CAPTURE_FILE_FINGERPRINTS |
|
||||
| develocity-enforce-url | DEVELOCITY_ENFORCE_URL |
|
||||
| develocity-plugin-version | DEVELOCITY_PLUGIN_VERSION |
|
||||
| develocity-ccud-plugin-version | DEVELOCITY_CCUD_PLUGIN_VERSION |
|
||||
| gradle-plugin-repository-url | GRADLE_PLUGIN_REPOSITORY_URL |
|
||||
| gradle-plugin-repository-username | GRADLE_PLUGIN_REPOSITORY_USERNAME |
|
||||
| gradle-plugin-repository-password | GRADLE_PLUGIN_REPOSITORY_PASSWORD |
|
||||
|
||||
|
||||
Here's an example using the env vars:
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
|
||||
- name: Run a Gradle build with Develocity injection enabled with environment variables
|
||||
run: ./gradlew build
|
||||
env:
|
||||
DEVELOCITY_INJECTION_ENABLED: true
|
||||
DEVELOCITY_URL: https://develocity.your-server.com
|
||||
DEVELOCITY_PLUGIN_VERSION: 3.17.5
|
||||
```
|
||||
|
||||
## Publishing to scans.gradle.com
|
||||
|
||||
|
||||
@@ -56,7 +56,10 @@ Here's a sample complete workflow you can add to your repositories:
|
||||
**`.github/workflows/gradle-wrapper-validation.yml`**
|
||||
```yaml
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
|
||||
@@ -11,7 +11,8 @@ The recommended way to execute any Gradle build is with the help of the [Gradle
|
||||
```yaml
|
||||
name: Build
|
||||
|
||||
on: [ push ]
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -23,7 +23,7 @@ inputs:
|
||||
|
||||
cache-write-only:
|
||||
description: |
|
||||
When 'true', entries will not be restored from the cache but will be saved at the end of the Job.
|
||||
When 'true', entries will not be restored from the cache but will be saved at the end of the Job.
|
||||
Setting this to 'true' implies cache-read-only will be 'false'.
|
||||
required: false
|
||||
default: false
|
||||
@@ -35,7 +35,7 @@ inputs:
|
||||
|
||||
cache-encryption-key:
|
||||
description: |
|
||||
A base64 encoded AES key used to encrypt the configuration-cache data. The key is exported as 'GRADLE_ENCRYPTION_KEY' for later steps.
|
||||
A base64 encoded AES key used to encrypt the configuration-cache data. The key is exported as 'GRADLE_ENCRYPTION_KEY' for later steps.
|
||||
A suitable key can be generated with `openssl rand -base64 16`.
|
||||
Configuration-cache data will not be saved/restored without an encryption key being provided.
|
||||
required: false
|
||||
@@ -70,7 +70,7 @@ inputs:
|
||||
# Dependency Graph configuration
|
||||
dependency-graph:
|
||||
description: |
|
||||
Specifies if a GitHub dependency snapshot should be generated for each Gradle build, and if so, how.
|
||||
Specifies if a GitHub dependency snapshot should be generated for each Gradle build, and if so, how.
|
||||
Valid values are 'disabled' (default), 'generate', 'generate-and-submit', 'generate-and-upload', 'download-and-submit' and 'clear'.
|
||||
required: false
|
||||
default: 'disabled'
|
||||
@@ -95,11 +95,59 @@ inputs:
|
||||
build-scan-terms-of-use-url:
|
||||
description: The URL to the Build Scan® terms of use. This input must be set to 'https://gradle.com/terms-of-service' or 'https://gradle.com/help/legal-terms-of-use'.
|
||||
required: false
|
||||
|
||||
|
||||
build-scan-terms-of-use-agree:
|
||||
description: Indicate that you agree to the Build Scan® terms of use. This input value must be "yes".
|
||||
required: false
|
||||
|
||||
develocity-access-key:
|
||||
description: Develocity access key. Should be set to a secret containing the Develocity Access key.
|
||||
required: false
|
||||
|
||||
develocity-token-expiry:
|
||||
description: The Develocity short-lived access tokens expiry in hours. Default is 2 hours.
|
||||
required: false
|
||||
|
||||
develocity-injection-enabled:
|
||||
description: Enables Develocity injection.
|
||||
required: false
|
||||
|
||||
develocity-url:
|
||||
description: The URL for the Develocity server.
|
||||
required: false
|
||||
|
||||
develocity-allow-untrusted-server:
|
||||
description: Allow communication with an untrusted server; set to _true_ if your Develocity instance is using a self-signed.
|
||||
required: false
|
||||
|
||||
develocity-capture-file-fingerprints:
|
||||
description: Enables capturing the paths and content hashes of each individual input file.
|
||||
required: false
|
||||
|
||||
develocity-enforce-url:
|
||||
description: Enforce the configured Develocity URL over a URL configured in the project's build; set to _true_ to enforce publication of build scans to the configured Develocity URL.
|
||||
required: false
|
||||
|
||||
develocity-plugin-version:
|
||||
description: The version of the Develocity Gradle plugin to apply.
|
||||
required: false
|
||||
|
||||
develocity-ccud-plugin-version:
|
||||
description: The version of the Common Custom User Data Gradle plugin to apply, if any.
|
||||
required: false
|
||||
|
||||
gradle-plugin-repository-url:
|
||||
description: The URL of the repository to use when resolving the Develocity and CCUD plugins; the Gradle Plugin Portal is used by default.
|
||||
required: false
|
||||
|
||||
gradle-plugin-repository-username:
|
||||
description: The username for the repository URL to use when resolving the Develocity and CCUD.
|
||||
required: false
|
||||
|
||||
gradle-plugin-repository-password:
|
||||
description: The password for the repository URL to use when resolving the Develocity and CCUD plugins; Consider using secrets to pass the value to this variable.
|
||||
required: false
|
||||
|
||||
# Wrapper validation configuration
|
||||
validate-wrappers:
|
||||
description: |
|
||||
@@ -143,7 +191,7 @@ inputs:
|
||||
description: When 'true', the action will not attempt to restore the Gradle User Home entries from other Jobs.
|
||||
required: false
|
||||
default: false
|
||||
|
||||
|
||||
# INTERNAL ACTION INPUTS
|
||||
# These inputs should not be configured directly, and are only used to pass environmental information to the action
|
||||
workflow-job-context:
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Configuration file for asdf version manager
|
||||
nodejs 20.10.0
|
||||
gradle 8.7
|
||||
gradle 8.8
|
||||
|
||||
28
sources/package-lock.json
generated
28
sources/package-lock.json
generated
@@ -3316,12 +3316,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -4736,9 +4736,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
@@ -11628,12 +11628,12 @@
|
||||
}
|
||||
},
|
||||
"braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
@@ -12659,9 +12659,9 @@
|
||||
}
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import * as core from '@actions/core'
|
||||
import {BuildScanConfig} from './configuration'
|
||||
|
||||
export function setup(config: BuildScanConfig): void {
|
||||
maybeExportVariable('DEVELOCITY_INJECTION_INIT_SCRIPT_NAME', 'gradle-actions.inject-develocity.init.gradle')
|
||||
maybeExportVariable('DEVELOCITY_AUTO_INJECTION_CUSTOM_VALUE', 'gradle-actions')
|
||||
if (config.getBuildScanPublishEnabled()) {
|
||||
maybeExportVariable('DEVELOCITY_INJECTION_ENABLED', 'true')
|
||||
maybeExportVariable('DEVELOCITY_PLUGIN_VERSION', '3.17')
|
||||
maybeExportVariable('DEVELOCITY_CCUD_PLUGIN_VERSION', '2.0')
|
||||
maybeExportVariable('DEVELOCITY_TERMS_OF_USE_URL', config.getBuildScanTermsOfUseUrl())
|
||||
maybeExportVariable('DEVELOCITY_TERMS_OF_USE_AGREE', config.getBuildScanTermsOfUseAgree())
|
||||
}
|
||||
}
|
||||
|
||||
function maybeExportVariable(variableName: string, value: unknown): void {
|
||||
if (!process.env[variableName]) {
|
||||
core.exportVariable(variableName, value)
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,10 @@ export class DependencyGraphConfig {
|
||||
return DependencyGraphConfig.constructJobCorrelator(github.context.workflow, github.context.job, getJobMatrix())
|
||||
}
|
||||
|
||||
getReportDirectory(): string {
|
||||
return path.resolve(getWorkspaceDirectory(), 'dependency-graph-reports')
|
||||
}
|
||||
|
||||
static constructJobCorrelator(workflow: string, jobId: string, matrixJson: string): string {
|
||||
const matrixString = this.describeMatrix(matrixJson)
|
||||
const label = matrixString ? `${workflow}-${jobId}-${matrixString}` : `${workflow}-${jobId}`
|
||||
@@ -184,6 +188,9 @@ export enum JobSummaryOption {
|
||||
}
|
||||
|
||||
export class BuildScanConfig {
|
||||
static DevelocityAccessKeyEnvVar = 'DEVELOCITY_ACCESS_KEY'
|
||||
static GradleEnterpriseAccessKeyEnvVar = 'GRADLE_ENTERPRISE_ACCESS_KEY'
|
||||
|
||||
getBuildScanPublishEnabled(): boolean {
|
||||
return getBooleanInput('build-scan-publish') && this.verifyTermsOfUseAgreement()
|
||||
}
|
||||
@@ -196,6 +203,59 @@ export class BuildScanConfig {
|
||||
return this.getTermsOfUseProp('build-scan-terms-of-use-agree', 'build-scan-terms-of-service-agree')
|
||||
}
|
||||
|
||||
getDevelocityAccessKey(): string {
|
||||
return (
|
||||
core.getInput('develocity-access-key') ||
|
||||
process.env[BuildScanConfig.DevelocityAccessKeyEnvVar] ||
|
||||
process.env[BuildScanConfig.GradleEnterpriseAccessKeyEnvVar] ||
|
||||
''
|
||||
)
|
||||
}
|
||||
|
||||
getDevelocityTokenExpiry(): string {
|
||||
return core.getInput('develocity-token-expiry')
|
||||
}
|
||||
|
||||
getDevelocityInjectionEnabled(): boolean | undefined {
|
||||
return getOptionalBooleanInput('develocity-injection-enabled')
|
||||
}
|
||||
|
||||
getDevelocityUrl(): string {
|
||||
return core.getInput('develocity-url')
|
||||
}
|
||||
|
||||
getDevelocityAllowUntrustedServer(): boolean | undefined {
|
||||
return getOptionalBooleanInput('develocity-allow-untrusted-server')
|
||||
}
|
||||
|
||||
getDevelocityCaptureFileFingerprints(): boolean | undefined {
|
||||
return getOptionalBooleanInput('develocity-capture-file-fingerprints')
|
||||
}
|
||||
|
||||
getDevelocityEnforceUrl(): boolean | undefined {
|
||||
return getOptionalBooleanInput('develocity-enforce-url')
|
||||
}
|
||||
|
||||
getDevelocityPluginVersion(): string {
|
||||
return core.getInput('develocity-plugin-version')
|
||||
}
|
||||
|
||||
getDevelocityCcudPluginVersion(): string {
|
||||
return core.getInput('develocity-ccud-plugin-version')
|
||||
}
|
||||
|
||||
getGradlePluginRepositoryUrl(): string {
|
||||
return core.getInput('gradle-plugin-repository-url')
|
||||
}
|
||||
|
||||
getGradlePluginRepositoryUsername(): string {
|
||||
return core.getInput('gradle-plugin-repository-username')
|
||||
}
|
||||
|
||||
getGradlePluginRepositoryPassword(): string {
|
||||
return core.getInput('gradle-plugin-repository-password')
|
||||
}
|
||||
|
||||
private verifyTermsOfUseAgreement(): boolean {
|
||||
if (
|
||||
(this.getBuildScanTermsOfUseUrl() !== 'https://gradle.com/terms-of-service' &&
|
||||
@@ -309,3 +369,11 @@ function getBooleanInput(paramName: string, paramDefault = false): boolean {
|
||||
}
|
||||
throw TypeError(`The value '${paramValue} is not valid for '${paramName}. Valid values are: [true, false]`)
|
||||
}
|
||||
|
||||
function getOptionalBooleanInput(paramName: string): boolean | undefined {
|
||||
const paramValue = core.getInput(paramName)
|
||||
if (paramValue === '') {
|
||||
return undefined
|
||||
}
|
||||
return getBooleanInput(paramName)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ export async function setup(config: DependencyGraphConfig): Promise<void> {
|
||||
}
|
||||
// Download and submit early, for compatability with dependency review.
|
||||
if (option === DependencyGraphOption.DownloadAndSubmit) {
|
||||
maybeExportVariable('DEPENDENCY_GRAPH_REPORT_DIR', config.getReportDirectory())
|
||||
await downloadAndSubmitDependencyGraphs(config)
|
||||
return
|
||||
}
|
||||
@@ -34,10 +35,7 @@ export async function setup(config: DependencyGraphConfig): Promise<void> {
|
||||
maybeExportVariable('GITHUB_DEPENDENCY_GRAPH_REF', github.context.ref)
|
||||
maybeExportVariable('GITHUB_DEPENDENCY_GRAPH_SHA', getShaFromContext())
|
||||
maybeExportVariable('GITHUB_DEPENDENCY_GRAPH_WORKSPACE', getWorkspaceDirectory())
|
||||
maybeExportVariable(
|
||||
'DEPENDENCY_GRAPH_REPORT_DIR',
|
||||
path.resolve(getWorkspaceDirectory(), 'dependency-graph-reports')
|
||||
)
|
||||
maybeExportVariable('DEPENDENCY_GRAPH_REPORT_DIR', config.getReportDirectory())
|
||||
|
||||
// To clear the dependency graph, we generate an empty graph by excluding all projects and configurations
|
||||
if (option === DependencyGraphOption.Clear) {
|
||||
@@ -62,22 +60,22 @@ export async function complete(config: DependencyGraphConfig): Promise<void> {
|
||||
return
|
||||
case DependencyGraphOption.GenerateAndSubmit:
|
||||
case DependencyGraphOption.Clear: // Submit the empty dependency graph
|
||||
await submitDependencyGraphs(await findGeneratedDependencyGraphFiles())
|
||||
await submitDependencyGraphs(await findDependencyGraphFiles())
|
||||
return
|
||||
case DependencyGraphOption.GenerateAndUpload:
|
||||
await uploadDependencyGraphs(await findGeneratedDependencyGraphFiles(), config)
|
||||
await uploadDependencyGraphs(await findDependencyGraphFiles(), config)
|
||||
}
|
||||
} catch (e) {
|
||||
warnOrFail(config, option, e)
|
||||
}
|
||||
}
|
||||
|
||||
async function findGeneratedDependencyGraphFiles(): Promise<string[]> {
|
||||
const workspaceDirectory = getWorkspaceDirectory()
|
||||
return await findDependencyGraphFiles(workspaceDirectory)
|
||||
}
|
||||
|
||||
async function uploadDependencyGraphs(dependencyGraphFiles: string[], config: DependencyGraphConfig): Promise<void> {
|
||||
if (dependencyGraphFiles.length === 0) {
|
||||
core.info('No dependency graph files found to upload.')
|
||||
return
|
||||
}
|
||||
|
||||
if (isRunningInActEnvironment()) {
|
||||
core.info('Dependency graph upload not supported in the ACT environment.')
|
||||
core.info(`Would upload: ${dependencyGraphFiles.join(', ')}`)
|
||||
@@ -111,6 +109,11 @@ async function downloadAndSubmitDependencyGraphs(config: DependencyGraphConfig):
|
||||
}
|
||||
|
||||
async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise<void> {
|
||||
if (dependencyGraphFiles.length === 0) {
|
||||
core.info('No dependency graph files found to submit.')
|
||||
return
|
||||
}
|
||||
|
||||
if (isRunningInActEnvironment()) {
|
||||
core.info('Dependency graph submit not supported in the ACT environment.')
|
||||
core.info(`Would submit: ${dependencyGraphFiles.join(', ')}`)
|
||||
@@ -122,17 +125,16 @@ async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise<v
|
||||
await submitDependencyGraphFile(dependencyGraphFile)
|
||||
} catch (error) {
|
||||
if (error instanceof RequestError) {
|
||||
throw new Error(translateErrorMessage(dependencyGraphFile, error))
|
||||
} else {
|
||||
throw error
|
||||
error.message = translateErrorMessage(dependencyGraphFile, error)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function translateErrorMessage(jsonFile: string, error: RequestError): string {
|
||||
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile)
|
||||
const mainWarning = `Dependency submission failed for ${relativeJsonFile}.\n${String(error)}`
|
||||
const mainWarning = `Dependency submission failed for ${relativeJsonFile}.\n${error.message}`
|
||||
if (error.message === 'Resource not accessible by integration') {
|
||||
return `${mainWarning}
|
||||
Please ensure that the 'contents: write' permission is available for the workflow job.
|
||||
@@ -156,8 +158,6 @@ async function submitDependencyGraphFile(jsonFile: string): Promise<void> {
|
||||
}
|
||||
|
||||
async function downloadDependencyGraphs(): Promise<string[]> {
|
||||
const workspaceDirectory = getWorkspaceDirectory()
|
||||
|
||||
const findBy = github.context.payload.workflow_run
|
||||
? {
|
||||
token: getGithubToken(),
|
||||
@@ -168,34 +168,37 @@ async function downloadDependencyGraphs(): Promise<string[]> {
|
||||
: undefined
|
||||
|
||||
const artifactClient = new DefaultArtifactClient()
|
||||
const downloadPath = path.resolve(workspaceDirectory, 'dependency-graph')
|
||||
|
||||
const dependencyGraphArtifacts = (
|
||||
await artifactClient.listArtifacts({
|
||||
latest: true,
|
||||
findBy
|
||||
})
|
||||
).artifacts.filter(candidate => candidate.name.startsWith(DEPENDENCY_GRAPH_PREFIX))
|
||||
).artifacts.filter(artifact => artifact.name.startsWith(DEPENDENCY_GRAPH_PREFIX))
|
||||
|
||||
for (const artifact of dependencyGraphArtifacts) {
|
||||
const downloadedArtifact = await artifactClient.downloadArtifact(artifact.id, {
|
||||
path: downloadPath,
|
||||
findBy
|
||||
})
|
||||
core.info(`Downloading dependency-graph artifact ${artifact.name} to ${downloadedArtifact.downloadPath}`)
|
||||
}
|
||||
|
||||
return findDependencyGraphFiles(downloadPath)
|
||||
return findDependencyGraphFiles()
|
||||
}
|
||||
|
||||
async function findDependencyGraphFiles(dir: string): Promise<string[]> {
|
||||
const globber = await glob.create(`${dir}/dependency-graph-reports/*.json`)
|
||||
async function findDependencyGraphFiles(): Promise<string[]> {
|
||||
const globber = await glob.create(`${getReportDirectory()}/**/*.json`)
|
||||
const allFiles = await globber.glob()
|
||||
const unprocessedFiles = allFiles.filter(file => !isProcessed(file))
|
||||
unprocessedFiles.forEach(markProcessed)
|
||||
core.info(`Found dependency graph files: ${unprocessedFiles.join(', ')}`)
|
||||
return unprocessedFiles
|
||||
}
|
||||
|
||||
function getReportDirectory(): string {
|
||||
return process.env.DEPENDENCY_GRAPH_REPORT_DIR!
|
||||
}
|
||||
|
||||
function isProcessed(dependencyGraphFile: string): boolean {
|
||||
const markerFile = `${dependencyGraphFile}.processed`
|
||||
return fs.existsSync(markerFile)
|
||||
|
||||
48
sources/src/develocity/build-scan.ts
Normal file
48
sources/src/develocity/build-scan.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import * as core from '@actions/core'
|
||||
import {BuildScanConfig} from '../configuration'
|
||||
import {setupToken} from './short-lived-token'
|
||||
|
||||
export async function setup(config: BuildScanConfig): Promise<void> {
|
||||
maybeExportVariable('DEVELOCITY_INJECTION_INIT_SCRIPT_NAME', 'gradle-actions.inject-develocity.init.gradle')
|
||||
maybeExportVariable('DEVELOCITY_AUTO_INJECTION_CUSTOM_VALUE', 'gradle-actions')
|
||||
if (config.getBuildScanPublishEnabled()) {
|
||||
maybeExportVariable('DEVELOCITY_INJECTION_ENABLED', 'true')
|
||||
maybeExportVariable('DEVELOCITY_PLUGIN_VERSION', '3.17.5')
|
||||
maybeExportVariable('DEVELOCITY_CCUD_PLUGIN_VERSION', '2.0')
|
||||
maybeExportVariable('DEVELOCITY_TERMS_OF_USE_URL', config.getBuildScanTermsOfUseUrl())
|
||||
maybeExportVariable('DEVELOCITY_TERMS_OF_USE_AGREE', config.getBuildScanTermsOfUseAgree())
|
||||
}
|
||||
|
||||
maybeExportVariableNotEmpty('DEVELOCITY_INJECTION_ENABLED', config.getDevelocityInjectionEnabled())
|
||||
maybeExportVariableNotEmpty('DEVELOCITY_URL', config.getDevelocityUrl())
|
||||
maybeExportVariableNotEmpty('DEVELOCITY_ALLOW_UNTRUSTED_SERVER', config.getDevelocityAllowUntrustedServer())
|
||||
maybeExportVariableNotEmpty('DEVELOCITY_CAPTURE_FILE_FINGERPRINTS', config.getDevelocityCaptureFileFingerprints())
|
||||
maybeExportVariableNotEmpty('DEVELOCITY_ENFORCE_URL', config.getDevelocityEnforceUrl())
|
||||
maybeExportVariableNotEmpty('DEVELOCITY_PLUGIN_VERSION', config.getDevelocityPluginVersion())
|
||||
maybeExportVariableNotEmpty('GRADLE_PLUGIN_REPOSITORY_URL', config.getGradlePluginRepositoryUrl())
|
||||
maybeExportVariableNotEmpty('GRADLE_PLUGIN_REPOSITORY_USERNAME', config.getGradlePluginRepositoryUsername())
|
||||
maybeExportVariableNotEmpty('GRADLE_PLUGIN_REPOSITORY_PASSWORD', config.getGradlePluginRepositoryPassword())
|
||||
|
||||
setupToken(
|
||||
config.getDevelocityAccessKey(),
|
||||
config.getDevelocityTokenExpiry(),
|
||||
getEnv('DEVELOCITY_ENFORCE_URL'),
|
||||
getEnv('DEVELOCITY_URL')
|
||||
)
|
||||
}
|
||||
|
||||
function getEnv(variableName: string): string | undefined {
|
||||
return process.env[variableName]
|
||||
}
|
||||
|
||||
function maybeExportVariable(variableName: string, value: unknown): void {
|
||||
if (!process.env[variableName]) {
|
||||
core.exportVariable(variableName, value)
|
||||
}
|
||||
}
|
||||
|
||||
function maybeExportVariableNotEmpty(variableName: string, value: unknown): void {
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
maybeExportVariable(variableName, value)
|
||||
}
|
||||
}
|
||||
206
sources/src/develocity/short-lived-token.ts
Normal file
206
sources/src/develocity/short-lived-token.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import * as httpm from 'typed-rest-client/HttpClient'
|
||||
import * as core from '@actions/core'
|
||||
import {BuildScanConfig} from '../configuration'
|
||||
import {recordDeprecation} from '../deprecation-collector'
|
||||
|
||||
export async function setupToken(
|
||||
develocityAccessKey: string,
|
||||
develocityTokenExpiry: string,
|
||||
enforceUrl: string | undefined,
|
||||
develocityUrl: string | undefined
|
||||
): Promise<void> {
|
||||
if (develocityAccessKey) {
|
||||
try {
|
||||
core.debug('Fetching short-lived token...')
|
||||
const tokens = await getToken(enforceUrl, develocityUrl, develocityAccessKey, develocityTokenExpiry)
|
||||
if (tokens != null && !tokens.isEmpty()) {
|
||||
core.debug(`Got token(s), setting the access key env vars`)
|
||||
const token = tokens.raw()
|
||||
core.setSecret(token)
|
||||
exportAccessKeyEnvVars(token)
|
||||
} else {
|
||||
// In case of not being able to generate a token we set the env variable to empty to avoid leaks
|
||||
clearAccessKeyEnvVarsWithDeprecationWarning()
|
||||
}
|
||||
} catch (e) {
|
||||
clearAccessKeyEnvVarsWithDeprecationWarning()
|
||||
core.warning(`Failed to fetch short-lived token, reason: ${e}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function exportAccessKeyEnvVars(value: string): void {
|
||||
;[BuildScanConfig.DevelocityAccessKeyEnvVar, BuildScanConfig.GradleEnterpriseAccessKeyEnvVar].forEach(key =>
|
||||
core.exportVariable(key, value)
|
||||
)
|
||||
}
|
||||
|
||||
function clearAccessKeyEnvVarsWithDeprecationWarning(): void {
|
||||
if (process.env[BuildScanConfig.GradleEnterpriseAccessKeyEnvVar]) {
|
||||
// We do not clear the GRADLE_ENTERPRISE_ACCESS_KEY env var in v3, to let the users upgrade to DV 2024.1
|
||||
recordDeprecation(`The ${BuildScanConfig.GradleEnterpriseAccessKeyEnvVar} env var is deprecated`)
|
||||
}
|
||||
core.exportVariable(BuildScanConfig.DevelocityAccessKeyEnvVar, '')
|
||||
}
|
||||
|
||||
export async function getToken(
|
||||
enforceUrl: string | undefined,
|
||||
serverUrl: string | undefined,
|
||||
accessKey: string,
|
||||
expiry: string
|
||||
): Promise<DevelocityAccessCredentials | null> {
|
||||
const empty: Promise<DevelocityAccessCredentials | null> = new Promise(r => r(null))
|
||||
const develocityAccessKey = DevelocityAccessCredentials.parse(accessKey)
|
||||
const shortLivedTokenClient = new ShortLivedTokenClient()
|
||||
|
||||
async function promiseError(message: string): Promise<DevelocityAccessCredentials | null> {
|
||||
return new Promise((resolve, reject) => reject(new Error(message)))
|
||||
}
|
||||
|
||||
if (develocityAccessKey == null) {
|
||||
return empty
|
||||
}
|
||||
if (enforceUrl === 'true' || develocityAccessKey.isSingleKey()) {
|
||||
if (!serverUrl) {
|
||||
return promiseError('Develocity Server URL not configured')
|
||||
}
|
||||
const hostname = extractHostname(serverUrl)
|
||||
if (hostname == null) {
|
||||
return promiseError('Could not extract hostname from Develocity server URL')
|
||||
}
|
||||
const hostAccessKey = develocityAccessKey.forHostname(hostname)
|
||||
if (!hostAccessKey) {
|
||||
return promiseError(`Could not find corresponding key for hostname ${hostname}`)
|
||||
}
|
||||
try {
|
||||
const token = await shortLivedTokenClient.fetchToken(serverUrl, hostAccessKey, expiry)
|
||||
return DevelocityAccessCredentials.of([token])
|
||||
} catch (e) {
|
||||
return new Promise((resolve, reject) => reject(e))
|
||||
}
|
||||
}
|
||||
|
||||
const tokens = new Array<HostnameAccessKey>()
|
||||
for (const k of develocityAccessKey.keys) {
|
||||
try {
|
||||
const token = await shortLivedTokenClient.fetchToken(`https://${k.hostname}`, k, expiry)
|
||||
tokens.push(token)
|
||||
} catch (e) {
|
||||
// Ignoring failed token, TODO: log this ?
|
||||
}
|
||||
}
|
||||
if (tokens.length > 0) {
|
||||
return DevelocityAccessCredentials.of(tokens)
|
||||
}
|
||||
return empty
|
||||
}
|
||||
|
||||
function extractHostname(serverUrl: string): string | null {
|
||||
try {
|
||||
const parsedUrl = new URL(serverUrl)
|
||||
return parsedUrl.hostname
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
class ShortLivedTokenClient {
|
||||
httpc = new httpm.HttpClient('gradle/setup-gradle')
|
||||
maxRetries = 3
|
||||
retryInterval = 1000
|
||||
|
||||
async fetchToken(serverUrl: string, accessKey: HostnameAccessKey, expiry: string): Promise<HostnameAccessKey> {
|
||||
const queryParams = expiry ? `?expiresInHours${expiry}` : ''
|
||||
const sanitizedServerUrl = !serverUrl.endsWith('/') ? `${serverUrl}/` : serverUrl
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${accessKey.key}`
|
||||
}
|
||||
|
||||
let attempts = 0
|
||||
while (attempts < this.maxRetries) {
|
||||
try {
|
||||
const requestUrl = `${sanitizedServerUrl}api/auth/token${queryParams}`
|
||||
core.debug(`Attempt ${attempts} to fetch short lived token at ${requestUrl}`)
|
||||
const response = await this.httpc.post(requestUrl, '', headers)
|
||||
if (response.message.statusCode === 200) {
|
||||
const text = await response.readBody()
|
||||
return new Promise<HostnameAccessKey>(resolve => resolve({hostname: accessKey.hostname, key: text}))
|
||||
}
|
||||
// This should be only 404
|
||||
attempts++
|
||||
if (attempts === this.maxRetries) {
|
||||
return new Promise((resolve, reject) =>
|
||||
reject(
|
||||
new Error(
|
||||
`Develocity short lived token request failed ${serverUrl} with status code ${response.message.statusCode}`
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
attempts++
|
||||
if (attempts === this.maxRetries) {
|
||||
return new Promise((resolve, reject) => reject(error))
|
||||
}
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, this.retryInterval))
|
||||
}
|
||||
return new Promise((resolve, reject) => reject(new Error('Illegal state')))
|
||||
}
|
||||
}
|
||||
|
||||
type HostnameAccessKey = {
|
||||
hostname: string
|
||||
key: string
|
||||
}
|
||||
|
||||
export class DevelocityAccessCredentials {
|
||||
static readonly accessKeyRegexp = /^([^;=\s]+=\w+)(;[^;=\s]+=\w+)*$/
|
||||
readonly keys: HostnameAccessKey[]
|
||||
|
||||
private constructor(allKeys: HostnameAccessKey[]) {
|
||||
this.keys = allKeys
|
||||
}
|
||||
|
||||
static of(allKeys: HostnameAccessKey[]): DevelocityAccessCredentials {
|
||||
return new DevelocityAccessCredentials(allKeys)
|
||||
}
|
||||
|
||||
private static readonly keyDelimiter = ';'
|
||||
private static readonly hostDelimiter = '='
|
||||
|
||||
static parse(rawKey: string): DevelocityAccessCredentials | null {
|
||||
if (!this.isValid(rawKey)) {
|
||||
return null
|
||||
}
|
||||
return new DevelocityAccessCredentials(
|
||||
rawKey.split(this.keyDelimiter).map(hostKey => {
|
||||
const pair = hostKey.split(this.hostDelimiter)
|
||||
return {hostname: pair[0], key: pair[1]}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this.keys.length === 0
|
||||
}
|
||||
|
||||
isSingleKey(): boolean {
|
||||
return this.keys.length === 1
|
||||
}
|
||||
|
||||
forHostname(hostname: string): HostnameAccessKey | undefined {
|
||||
return this.keys.find(hostKey => hostKey.hostname === hostname)
|
||||
}
|
||||
|
||||
raw(): string {
|
||||
return this.keys
|
||||
.map(k => `${k.hostname}${DevelocityAccessCredentials.hostDelimiter}${k.key}`)
|
||||
.join(DevelocityAccessCredentials.keyDelimiter)
|
||||
}
|
||||
|
||||
private static isValid(allKeys: string): boolean {
|
||||
return this.accessKeyRegexp.test(allKeys)
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,10 @@ export function handleMainActionError(error: unknown): void {
|
||||
}
|
||||
}
|
||||
} else if (error instanceof JobFailure) {
|
||||
core.setFailed(String(error)) // No stack trace for JobFailure: these are known errors
|
||||
core.setFailed(String(error))
|
||||
if (error.stack) {
|
||||
core.info(error.stack)
|
||||
}
|
||||
} else {
|
||||
core.setFailed(String(error))
|
||||
if (error instanceof Error && error.stack) {
|
||||
@@ -34,6 +37,9 @@ export function handleMainActionError(error: unknown): void {
|
||||
export function handlePostActionError(error: unknown): void {
|
||||
if (error instanceof JobFailure) {
|
||||
core.setFailed(String(error))
|
||||
if (error.stack) {
|
||||
core.info(error.stack)
|
||||
}
|
||||
} else {
|
||||
core.warning(`Unhandled error in Gradle post-action - job will continue: ${error}`)
|
||||
if (error instanceof Error && error.stack) {
|
||||
|
||||
@@ -78,7 +78,7 @@ Note that this permission is never available for a workflow triggered from a rep
|
||||
return mainWarning
|
||||
}
|
||||
|
||||
function renderSummaryTable(results: BuildResult[]): string {
|
||||
export function renderSummaryTable(results: BuildResult[]): string {
|
||||
return `${renderDeprecations()}\n${renderBuildResults(results)}`
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ function renderBuildResults(results: BuildResult[]): string {
|
||||
<th>Requested Tasks</th>
|
||||
<th>Gradle Version</th>
|
||||
<th>Build Outcome</th>
|
||||
<th>Build Scan®</th>
|
||||
<th>Build Scan®</th>
|
||||
</tr>${results.map(result => renderBuildResultRow(result)).join('')}
|
||||
</table>
|
||||
`
|
||||
@@ -134,23 +134,46 @@ function renderOutcome(result: BuildResult): string {
|
||||
return result.buildFailed ? ':x:' : ':white_check_mark:'
|
||||
}
|
||||
|
||||
function renderBuildScan(result: BuildResult): string {
|
||||
if (result.buildScanFailed) {
|
||||
return renderBuildScanBadge(
|
||||
'PUBLISH_FAILED',
|
||||
'orange',
|
||||
'https://docs.gradle.com/develocity/gradle-plugin/#troubleshooting'
|
||||
)
|
||||
}
|
||||
if (result.buildScanUri) {
|
||||
return renderBuildScanBadge('PUBLISHED', '06A0CE', result.buildScanUri)
|
||||
}
|
||||
return renderBuildScanBadge('NOT_PUBLISHED', 'lightgrey', 'https://scans.gradle.com')
|
||||
interface BadgeSpec {
|
||||
text: string
|
||||
alt: string
|
||||
color: string
|
||||
logo: boolean
|
||||
targetUrl: string
|
||||
}
|
||||
|
||||
function renderBuildScanBadge(outcomeText: string, outcomeColor: string, targetUrl: string): string {
|
||||
const badgeUrl = `https://img.shields.io/badge/Build%20Scan%C2%AE-${outcomeText}-${outcomeColor}?logo=Gradle`
|
||||
const badgeHtml = `<img src="${badgeUrl}" alt="Build Scan ${outcomeText}" />`
|
||||
function renderBuildScan(result: BuildResult): string {
|
||||
if (result.buildScanFailed) {
|
||||
return renderBuildScanBadge({
|
||||
text: 'Publish failed',
|
||||
alt: 'Build Scan publish failed',
|
||||
color: 'orange',
|
||||
logo: false,
|
||||
targetUrl: 'https://docs.gradle.com/develocity/gradle-plugin/#troubleshooting'
|
||||
})
|
||||
}
|
||||
if (result.buildScanUri) {
|
||||
return renderBuildScanBadge({
|
||||
text: 'Build Scan®',
|
||||
alt: 'Build Scan published',
|
||||
color: '06A0CE',
|
||||
logo: true,
|
||||
targetUrl: result.buildScanUri
|
||||
})
|
||||
}
|
||||
return renderBuildScanBadge({
|
||||
text: 'Not published',
|
||||
alt: 'Build Scan not published',
|
||||
color: 'lightgrey',
|
||||
logo: false,
|
||||
targetUrl: 'https://scans.gradle.com'
|
||||
})
|
||||
}
|
||||
|
||||
function renderBuildScanBadge({text, alt, color, logo, targetUrl}: BadgeSpec): string {
|
||||
const encodedText = encodeURIComponent(text)
|
||||
const badgeUrl = `https://img.shields.io/badge/${encodedText}-${color}${logo ? '?logo=Gradle' : ''}`
|
||||
const badgeHtml = `<img src="${badgeUrl}" alt="${alt}" />`
|
||||
return `<a href="${targetUrl}" rel="nofollow" target="_blank">${badgeHtml}</a>`
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ if (gradleVersion < GradleVersion.version("5.2") ||
|
||||
if (getVariable('GITHUB_DEPENDENCY_GRAPH_CONTINUE_ON_FAILURE') != "true") {
|
||||
throw new GradleException("Dependency Graph is not supported for ${gradleVersion}. No dependency snapshot will be generated.")
|
||||
}
|
||||
println "::warning::Dependency Graph is not supported for ${gradleVersion}. No dependency snapshot will be generated."
|
||||
logger.warn("::warning::Dependency Graph is not supported for ${gradleVersion}. No dependency snapshot will be generated.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ if (isTopLevelBuild) {
|
||||
def reportFile = getUniqueReportFile(getVariable('GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR'))
|
||||
|
||||
if (reportFile == null) {
|
||||
println "::warning::No dependency snapshot generated for step. Could not determine unique job correlator - specify GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR var for this step."
|
||||
logger.warn("::warning::No dependency snapshot generated for step. Could not determine unique job correlator - specify GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR var for this step.")
|
||||
return
|
||||
}
|
||||
|
||||
println "Generating dependency graph into '${reportFile}'"
|
||||
logger.lifecycle("Generating dependency graph into '${reportFile}'")
|
||||
}
|
||||
|
||||
apply from: 'gradle-actions.github-dependency-graph-gradle-plugin-apply.groovy'
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
/*
|
||||
* Initscript for injection of Develocity into Gradle builds.
|
||||
* Version: v1.0
|
||||
*/
|
||||
|
||||
import org.gradle.util.GradleVersion
|
||||
|
||||
// note that there is no mechanism to share code between the initscript{} block and the main script, so some logic is duplicated
|
||||
|
||||
// conditionally apply the Develocity plugin to the classpath so it can be applied to the build further down in this script
|
||||
initscript {
|
||||
// NOTE: there is no mechanism to share code between the initscript{} block and the main script, so some logic is duplicated
|
||||
def isTopLevelBuild = !gradle.parent
|
||||
if (!isTopLevelBuild) {
|
||||
return
|
||||
}
|
||||
|
||||
def getInputParam = { String name ->
|
||||
def envVarName = name.toUpperCase().replace('.', '_').replace('-', '_')
|
||||
def ENV_VAR_PREFIX = ''
|
||||
def envVarName = ENV_VAR_PREFIX + name.toUpperCase().replace('.', '_').replace('-', '_')
|
||||
return System.getProperty(name) ?: System.getenv(envVarName)
|
||||
}
|
||||
|
||||
@@ -20,9 +24,9 @@ initscript {
|
||||
return
|
||||
}
|
||||
|
||||
// finish early if injection is disabled
|
||||
def gradleInjectionEnabled = getInputParam("develocity.injection-enabled")
|
||||
if (gradleInjectionEnabled != "true") {
|
||||
// Plugin loading is only required for Develocity injection. Abort early if not enabled.
|
||||
def develocityInjectionEnabled = Boolean.parseBoolean(getInputParam("develocity.injection-enabled"))
|
||||
if (!develocityInjectionEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -75,29 +79,17 @@ initscript {
|
||||
}
|
||||
}
|
||||
|
||||
def BUILD_SCAN_PLUGIN_ID = 'com.gradle.build-scan'
|
||||
def BUILD_SCAN_PLUGIN_CLASS = 'com.gradle.scan.plugin.BuildScanPlugin'
|
||||
|
||||
def GRADLE_ENTERPRISE_PLUGIN_ID = 'com.gradle.enterprise'
|
||||
def GRADLE_ENTERPRISE_PLUGIN_CLASS = 'com.gradle.enterprise.gradleplugin.GradleEnterprisePlugin'
|
||||
|
||||
def DEVELOCITY_PLUGIN_ID = 'com.gradle.develocity'
|
||||
def DEVELOCITY_PLUGIN_CLASS = 'com.gradle.develocity.agent.gradle.DevelocityPlugin'
|
||||
|
||||
def CI_AUTO_INJECTION_CUSTOM_VALUE_NAME = 'CI auto injection'
|
||||
def CCUD_PLUGIN_ID = 'com.gradle.common-custom-user-data-gradle-plugin'
|
||||
def CCUD_PLUGIN_CLASS = 'com.gradle.CommonCustomUserDataGradlePlugin'
|
||||
static getInputParam(String name) {
|
||||
def ENV_VAR_PREFIX = ''
|
||||
def envVarName = ENV_VAR_PREFIX + name.toUpperCase().replace('.', '_').replace('-', '_')
|
||||
return System.getProperty(name) ?: System.getenv(envVarName)
|
||||
}
|
||||
|
||||
def isTopLevelBuild = !gradle.parent
|
||||
if (!isTopLevelBuild) {
|
||||
return
|
||||
}
|
||||
|
||||
def getInputParam = { String name ->
|
||||
def envVarName = name.toUpperCase().replace('.', '_').replace('-', '_')
|
||||
return System.getProperty(name) ?: System.getenv(envVarName)
|
||||
}
|
||||
|
||||
def requestedInitScriptName = getInputParam('develocity.injection.init-script-name')
|
||||
def initScriptName = buildscript.sourceFile.name
|
||||
if (requestedInitScriptName != initScriptName) {
|
||||
@@ -105,205 +97,226 @@ if (requestedInitScriptName != initScriptName) {
|
||||
return
|
||||
}
|
||||
|
||||
// finish early if injection is disabled
|
||||
def gradleInjectionEnabled = getInputParam("develocity.injection-enabled")
|
||||
if (gradleInjectionEnabled != "true") {
|
||||
return
|
||||
def develocityInjectionEnabled = Boolean.parseBoolean(getInputParam("develocity.injection-enabled"))
|
||||
if (develocityInjectionEnabled) {
|
||||
enableDevelocityInjection()
|
||||
}
|
||||
|
||||
def develocityUrl = getInputParam('develocity.url')
|
||||
def develocityAllowUntrustedServer = Boolean.parseBoolean(getInputParam('develocity.allow-untrusted-server'))
|
||||
def develocityEnforceUrl = Boolean.parseBoolean(getInputParam('develocity.enforce-url'))
|
||||
def buildScanUploadInBackground = Boolean.parseBoolean(getInputParam('develocity.build-scan.upload-in-background'))
|
||||
def develocityCaptureFileFingerprints = getInputParam('develocity.capture-file-fingerprints') ? Boolean.parseBoolean(getInputParam('develocity.capture-file-fingerprints')) : true
|
||||
def develocityPluginVersion = getInputParam('develocity.plugin.version')
|
||||
def ccudPluginVersion = getInputParam('develocity.ccud-plugin.version')
|
||||
def buildScanTermsOfUseUrl = getInputParam('develocity.terms-of-use.url')
|
||||
def buildScanTermsOfUseAgree = getInputParam('develocity.terms-of-use.agree')
|
||||
def ciAutoInjectionCustomValueValue = getInputParam('develocity.auto-injection.custom-value')
|
||||
// To enable build-scan capture, a `captureBuildScanLink(String)` method must be added to `BuildScanCollector`.
|
||||
def buildScanCollector = new BuildScanCollector()
|
||||
def buildScanCaptureEnabled = buildScanCollector.metaClass.respondsTo(buildScanCollector, 'captureBuildScanLink', String)
|
||||
if (buildScanCaptureEnabled) {
|
||||
enableBuildScanLinkCapture(buildScanCollector)
|
||||
}
|
||||
|
||||
def atLeastGradle5 = GradleVersion.current() >= GradleVersion.version('5.0')
|
||||
def atLeastGradle4 = GradleVersion.current() >= GradleVersion.version('4.0')
|
||||
def shouldApplyDevelocityPlugin = atLeastGradle5 && develocityPluginVersion && isAtLeast(develocityPluginVersion, '3.17')
|
||||
void enableDevelocityInjection() {
|
||||
def BUILD_SCAN_PLUGIN_ID = 'com.gradle.build-scan'
|
||||
def BUILD_SCAN_PLUGIN_CLASS = 'com.gradle.scan.plugin.BuildScanPlugin'
|
||||
|
||||
def dvOrGe = { def dvValue, def geValue ->
|
||||
if (shouldApplyDevelocityPlugin) {
|
||||
return dvValue instanceof Closure<?> ? dvValue() : dvValue
|
||||
def GRADLE_ENTERPRISE_PLUGIN_ID = 'com.gradle.enterprise'
|
||||
def GRADLE_ENTERPRISE_PLUGIN_CLASS = 'com.gradle.enterprise.gradleplugin.GradleEnterprisePlugin'
|
||||
|
||||
def DEVELOCITY_PLUGIN_ID = 'com.gradle.develocity'
|
||||
def DEVELOCITY_PLUGIN_CLASS = 'com.gradle.develocity.agent.gradle.DevelocityPlugin'
|
||||
|
||||
def CI_AUTO_INJECTION_CUSTOM_VALUE_NAME = 'CI auto injection'
|
||||
def CCUD_PLUGIN_ID = 'com.gradle.common-custom-user-data-gradle-plugin'
|
||||
def CCUD_PLUGIN_CLASS = 'com.gradle.CommonCustomUserDataGradlePlugin'
|
||||
|
||||
def develocityUrl = getInputParam('develocity.url')
|
||||
def develocityAllowUntrustedServer = Boolean.parseBoolean(getInputParam('develocity.allow-untrusted-server'))
|
||||
def develocityEnforceUrl = Boolean.parseBoolean(getInputParam('develocity.enforce-url'))
|
||||
def buildScanUploadInBackground = Boolean.parseBoolean(getInputParam('develocity.build-scan.upload-in-background'))
|
||||
def develocityCaptureFileFingerprints = getInputParam('develocity.capture-file-fingerprints') ? Boolean.parseBoolean(getInputParam('develocity.capture-file-fingerprints')) : true
|
||||
def develocityPluginVersion = getInputParam('develocity.plugin.version')
|
||||
def ccudPluginVersion = getInputParam('develocity.ccud-plugin.version')
|
||||
def buildScanTermsOfUseUrl = getInputParam('develocity.terms-of-use.url')
|
||||
def buildScanTermsOfUseAgree = getInputParam('develocity.terms-of-use.agree')
|
||||
def ciAutoInjectionCustomValueValue = getInputParam('develocity.auto-injection.custom-value')
|
||||
|
||||
def atLeastGradle5 = GradleVersion.current() >= GradleVersion.version('5.0')
|
||||
def atLeastGradle4 = GradleVersion.current() >= GradleVersion.version('4.0')
|
||||
def shouldApplyDevelocityPlugin = atLeastGradle5 && develocityPluginVersion && isAtLeast(develocityPluginVersion, '3.17')
|
||||
|
||||
def dvOrGe = { def dvValue, def geValue ->
|
||||
if (shouldApplyDevelocityPlugin) {
|
||||
return dvValue instanceof Closure<?> ? dvValue() : dvValue
|
||||
}
|
||||
return geValue instanceof Closure<?> ? geValue() : geValue
|
||||
}
|
||||
return geValue instanceof Closure<?> ? geValue() : geValue
|
||||
}
|
||||
|
||||
// finish early if configuration parameters passed in via system properties are not valid/supported
|
||||
if (ccudPluginVersion && isNotAtLeast(ccudPluginVersion, '1.7')) {
|
||||
logger.warn("Common Custom User Data Gradle plugin must be at least 1.7. Configured version is $ccudPluginVersion.")
|
||||
return
|
||||
}
|
||||
// finish early if configuration parameters passed in via system properties are not valid/supported
|
||||
if (ccudPluginVersion && isNotAtLeast(ccudPluginVersion, '1.7')) {
|
||||
logger.warn("Common Custom User Data Gradle plugin must be at least 1.7. Configured version is $ccudPluginVersion.")
|
||||
return
|
||||
}
|
||||
|
||||
// register buildScanPublished listener and optionally apply the Develocity plugin
|
||||
if (GradleVersion.current() < GradleVersion.version('6.0')) {
|
||||
rootProject {
|
||||
buildscript.configurations.getByName("classpath").incoming.afterResolve { ResolvableDependencies incoming ->
|
||||
def resolutionResult = incoming.resolutionResult
|
||||
// Conditionally apply and configure the Develocity plugin
|
||||
if (GradleVersion.current() < GradleVersion.version('6.0')) {
|
||||
rootProject {
|
||||
buildscript.configurations.getByName("classpath").incoming.afterResolve { ResolvableDependencies incoming ->
|
||||
def resolutionResult = incoming.resolutionResult
|
||||
|
||||
if (develocityPluginVersion) {
|
||||
def scanPluginComponent = resolutionResult.allComponents.find {
|
||||
it.moduleVersion.with { group == "com.gradle" && ['build-scan-plugin', 'gradle-enterprise-gradle-plugin', 'develocity-gradle-plugin'].contains(name) }
|
||||
}
|
||||
if (!scanPluginComponent) {
|
||||
def pluginClass = dvOrGe(DEVELOCITY_PLUGIN_CLASS, BUILD_SCAN_PLUGIN_CLASS)
|
||||
logger.lifecycle("Applying $pluginClass via init script")
|
||||
applyPluginExternally(pluginManager, pluginClass)
|
||||
def rootExtension = dvOrGe(
|
||||
if (develocityPluginVersion) {
|
||||
def scanPluginComponent = resolutionResult.allComponents.find {
|
||||
it.moduleVersion.with { group == "com.gradle" && ['build-scan-plugin', 'gradle-enterprise-gradle-plugin', 'develocity-gradle-plugin'].contains(name) }
|
||||
}
|
||||
if (!scanPluginComponent) {
|
||||
def pluginClass = dvOrGe(DEVELOCITY_PLUGIN_CLASS, BUILD_SCAN_PLUGIN_CLASS)
|
||||
logger.lifecycle("Applying $pluginClass via init script")
|
||||
applyPluginExternally(pluginManager, pluginClass)
|
||||
def rootExtension = dvOrGe(
|
||||
{ develocity },
|
||||
{ buildScan }
|
||||
)
|
||||
def buildScanExtension = dvOrGe(
|
||||
)
|
||||
def buildScanExtension = dvOrGe(
|
||||
{ rootExtension.buildScan },
|
||||
{ rootExtension }
|
||||
)
|
||||
if (develocityUrl) {
|
||||
logger.lifecycle("Connection to Develocity: $develocityUrl, allowUntrustedServer: $develocityAllowUntrustedServer, captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
rootExtension.server = develocityUrl
|
||||
rootExtension.allowUntrustedServer = develocityAllowUntrustedServer
|
||||
}
|
||||
if (!shouldApplyDevelocityPlugin) {
|
||||
// Develocity plugin publishes scans by default
|
||||
buildScanExtension.publishAlways()
|
||||
}
|
||||
// uploadInBackground not available for build-scan-plugin 1.16
|
||||
if (buildScanExtension.metaClass.respondsTo(buildScanExtension, 'setUploadInBackground', Boolean)) buildScanExtension.uploadInBackground = buildScanUploadInBackground
|
||||
buildScanExtension.value CI_AUTO_INJECTION_CUSTOM_VALUE_NAME, ciAutoInjectionCustomValueValue
|
||||
if (isAtLeast(develocityPluginVersion, '2.1') && atLeastGradle5) {
|
||||
logger.lifecycle("Setting captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
if (isAtLeast(develocityPluginVersion, '3.17')) {
|
||||
buildScanExtension.capture.fileFingerprints.set(develocityCaptureFileFingerprints)
|
||||
} else if (isAtLeast(develocityPluginVersion, '3.7')) {
|
||||
buildScanExtension.capture.taskInputFiles = develocityCaptureFileFingerprints
|
||||
} else {
|
||||
buildScanExtension.captureTaskInputFiles = develocityCaptureFileFingerprints
|
||||
)
|
||||
if (develocityUrl) {
|
||||
logger.lifecycle("Connection to Develocity: $develocityUrl, allowUntrustedServer: $develocityAllowUntrustedServer, captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
rootExtension.server = develocityUrl
|
||||
rootExtension.allowUntrustedServer = develocityAllowUntrustedServer
|
||||
}
|
||||
if (!shouldApplyDevelocityPlugin) {
|
||||
// Develocity plugin publishes scans by default
|
||||
buildScanExtension.publishAlways()
|
||||
}
|
||||
// uploadInBackground not available for build-scan-plugin 1.16
|
||||
if (buildScanExtension.metaClass.respondsTo(buildScanExtension, 'setUploadInBackground', Boolean)) buildScanExtension.uploadInBackground = buildScanUploadInBackground
|
||||
buildScanExtension.value CI_AUTO_INJECTION_CUSTOM_VALUE_NAME, ciAutoInjectionCustomValueValue
|
||||
if (isAtLeast(develocityPluginVersion, '2.1') && atLeastGradle5) {
|
||||
logger.lifecycle("Setting captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
if (isAtLeast(develocityPluginVersion, '3.17')) {
|
||||
buildScanExtension.capture.fileFingerprints.set(develocityCaptureFileFingerprints)
|
||||
} else if (isAtLeast(develocityPluginVersion, '3.7')) {
|
||||
buildScanExtension.capture.taskInputFiles = develocityCaptureFileFingerprints
|
||||
} else {
|
||||
buildScanExtension.captureTaskInputFiles = develocityCaptureFileFingerprints
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (develocityUrl && develocityEnforceUrl) {
|
||||
logger.lifecycle("Enforcing Develocity: $develocityUrl, allowUntrustedServer: $develocityAllowUntrustedServer, captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
}
|
||||
|
||||
pluginManager.withPlugin(BUILD_SCAN_PLUGIN_ID) {
|
||||
// Only execute if develocity plugin isn't applied.
|
||||
if (gradle.rootProject.extensions.findByName("develocity")) return
|
||||
afterEvaluate {
|
||||
if (develocityUrl && develocityEnforceUrl) {
|
||||
buildScan.server = develocityUrl
|
||||
buildScan.allowUntrustedServer = develocityAllowUntrustedServer
|
||||
}
|
||||
}
|
||||
|
||||
if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) {
|
||||
buildScan.termsOfServiceUrl = buildScanTermsOfUseUrl
|
||||
buildScan.termsOfServiceAgree = buildScanTermsOfUseAgree
|
||||
}
|
||||
}
|
||||
|
||||
pluginManager.withPlugin(DEVELOCITY_PLUGIN_ID) {
|
||||
afterEvaluate {
|
||||
if (develocityUrl && develocityEnforceUrl) {
|
||||
develocity.server = develocityUrl
|
||||
develocity.allowUntrustedServer = develocityAllowUntrustedServer
|
||||
}
|
||||
}
|
||||
|
||||
if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) {
|
||||
develocity.buildScan.termsOfUseUrl = buildScanTermsOfUseUrl
|
||||
develocity.buildScan.termsOfUseAgree = buildScanTermsOfUseAgree
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ccudPluginVersion && atLeastGradle4) {
|
||||
def ccudPluginComponent = resolutionResult.allComponents.find {
|
||||
it.moduleVersion.with { group == "com.gradle" && name == "common-custom-user-data-gradle-plugin" }
|
||||
}
|
||||
if (!ccudPluginComponent) {
|
||||
logger.lifecycle("Applying $CCUD_PLUGIN_CLASS via init script")
|
||||
pluginManager.apply(initscript.classLoader.loadClass(CCUD_PLUGIN_CLASS))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gradle.settingsEvaluated { settings ->
|
||||
if (develocityPluginVersion) {
|
||||
if (!settings.pluginManager.hasPlugin(GRADLE_ENTERPRISE_PLUGIN_ID) && !settings.pluginManager.hasPlugin(DEVELOCITY_PLUGIN_ID)) {
|
||||
def pluginClass = dvOrGe(DEVELOCITY_PLUGIN_CLASS, GRADLE_ENTERPRISE_PLUGIN_CLASS)
|
||||
logger.lifecycle("Applying $pluginClass via init script")
|
||||
applyPluginExternally(settings.pluginManager, pluginClass)
|
||||
if (develocityUrl) {
|
||||
logger.lifecycle("Connection to Develocity: $develocityUrl, allowUntrustedServer: $develocityAllowUntrustedServer, captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
eachDevelocitySettingsExtension(settings) { ext ->
|
||||
ext.server = develocityUrl
|
||||
ext.allowUntrustedServer = develocityAllowUntrustedServer
|
||||
}
|
||||
}
|
||||
|
||||
eachDevelocitySettingsExtension(settings) { ext ->
|
||||
ext.buildScan.uploadInBackground = buildScanUploadInBackground
|
||||
ext.buildScan.value CI_AUTO_INJECTION_CUSTOM_VALUE_NAME, ciAutoInjectionCustomValueValue
|
||||
}
|
||||
|
||||
eachDevelocitySettingsExtension(settings,
|
||||
{ develocity ->
|
||||
logger.lifecycle("Setting captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
develocity.buildScan.capture.fileFingerprints = develocityCaptureFileFingerprints
|
||||
},
|
||||
{ gradleEnterprise ->
|
||||
gradleEnterprise.buildScan.publishAlways()
|
||||
if (isAtLeast(develocityPluginVersion, '2.1')) {
|
||||
logger.lifecycle("Setting captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
if (isAtLeast(develocityPluginVersion, '3.7')) {
|
||||
gradleEnterprise.buildScan.capture.taskInputFiles = develocityCaptureFileFingerprints
|
||||
} else {
|
||||
gradleEnterprise.buildScan.captureTaskInputFiles = develocityCaptureFileFingerprints
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (develocityUrl && develocityEnforceUrl) {
|
||||
logger.lifecycle("Enforcing Develocity: $develocityUrl, allowUntrustedServer: $develocityAllowUntrustedServer, captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
}
|
||||
|
||||
pluginManager.withPlugin(BUILD_SCAN_PLUGIN_ID) {
|
||||
// Only execute if develocity plugin isn't applied.
|
||||
if (gradle.rootProject.extensions.findByName("develocity")) return
|
||||
afterEvaluate {
|
||||
if (develocityUrl && develocityEnforceUrl) {
|
||||
buildScan.server = develocityUrl
|
||||
buildScan.allowUntrustedServer = develocityAllowUntrustedServer
|
||||
}
|
||||
}
|
||||
|
||||
if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) {
|
||||
buildScan.termsOfServiceUrl = buildScanTermsOfUseUrl
|
||||
buildScan.termsOfServiceAgree = buildScanTermsOfUseAgree
|
||||
}
|
||||
}
|
||||
|
||||
pluginManager.withPlugin(DEVELOCITY_PLUGIN_ID) {
|
||||
afterEvaluate {
|
||||
eachDevelocitySettingsExtension(settings,
|
||||
{ develocity ->
|
||||
if (develocityUrl && develocityEnforceUrl) {
|
||||
develocity.server = develocityUrl
|
||||
develocity.allowUntrustedServer = develocityAllowUntrustedServer
|
||||
}
|
||||
}
|
||||
|
||||
if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) {
|
||||
develocity.buildScan.termsOfUseUrl = buildScanTermsOfUseUrl
|
||||
develocity.buildScan.termsOfUseAgree = buildScanTermsOfUseAgree
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ccudPluginVersion && atLeastGradle4) {
|
||||
def ccudPluginComponent = resolutionResult.allComponents.find {
|
||||
it.moduleVersion.with { group == "com.gradle" && name == "common-custom-user-data-gradle-plugin" }
|
||||
}
|
||||
if (!ccudPluginComponent) {
|
||||
logger.lifecycle("Applying $CCUD_PLUGIN_CLASS via init script")
|
||||
pluginManager.apply(initscript.classLoader.loadClass(CCUD_PLUGIN_CLASS))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gradle.settingsEvaluated { settings ->
|
||||
if (develocityPluginVersion) {
|
||||
if (!settings.pluginManager.hasPlugin(GRADLE_ENTERPRISE_PLUGIN_ID) && !settings.pluginManager.hasPlugin(DEVELOCITY_PLUGIN_ID)) {
|
||||
def pluginClass = dvOrGe(DEVELOCITY_PLUGIN_CLASS, GRADLE_ENTERPRISE_PLUGIN_CLASS)
|
||||
logger.lifecycle("Applying $pluginClass via init script")
|
||||
applyPluginExternally(settings.pluginManager, pluginClass)
|
||||
if (develocityUrl) {
|
||||
logger.lifecycle("Connection to Develocity: $develocityUrl, allowUntrustedServer: $develocityAllowUntrustedServer, captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
eachDevelocitySettingsExtension(settings) { ext ->
|
||||
ext.server = develocityUrl
|
||||
ext.allowUntrustedServer = develocityAllowUntrustedServer
|
||||
}
|
||||
}
|
||||
|
||||
eachDevelocitySettingsExtension(settings) { ext ->
|
||||
ext.buildScan.uploadInBackground = buildScanUploadInBackground
|
||||
ext.buildScan.value CI_AUTO_INJECTION_CUSTOM_VALUE_NAME, ciAutoInjectionCustomValueValue
|
||||
}
|
||||
|
||||
eachDevelocitySettingsExtension(settings,
|
||||
{ develocity ->
|
||||
logger.lifecycle("Setting captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
develocity.buildScan.capture.fileFingerprints = develocityCaptureFileFingerprints
|
||||
if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) {
|
||||
develocity.buildScan.termsOfUseUrl = buildScanTermsOfUseUrl
|
||||
develocity.buildScan.termsOfUseAgree = buildScanTermsOfUseAgree
|
||||
}
|
||||
},
|
||||
{ gradleEnterprise ->
|
||||
gradleEnterprise.buildScan.publishAlways()
|
||||
if (isAtLeast(develocityPluginVersion, '2.1')) {
|
||||
logger.lifecycle("Setting captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
if (isAtLeast(develocityPluginVersion, '3.7')) {
|
||||
gradleEnterprise.buildScan.capture.taskInputFiles = develocityCaptureFileFingerprints
|
||||
} else {
|
||||
gradleEnterprise.buildScan.captureTaskInputFiles = develocityCaptureFileFingerprints
|
||||
}
|
||||
if (develocityUrl && develocityEnforceUrl) {
|
||||
gradleEnterprise.server = develocityUrl
|
||||
gradleEnterprise.allowUntrustedServer = develocityAllowUntrustedServer
|
||||
}
|
||||
|
||||
if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) {
|
||||
gradleEnterprise.buildScan.termsOfServiceUrl = buildScanTermsOfUseUrl
|
||||
gradleEnterprise.buildScan.termsOfServiceAgree = buildScanTermsOfUseAgree
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (develocityUrl && develocityEnforceUrl) {
|
||||
logger.lifecycle("Enforcing Develocity: $develocityUrl, allowUntrustedServer: $develocityAllowUntrustedServer, captureFileFingerprints: $develocityCaptureFileFingerprints")
|
||||
}
|
||||
|
||||
eachDevelocitySettingsExtension(settings,
|
||||
{ develocity ->
|
||||
if (develocityUrl && develocityEnforceUrl) {
|
||||
develocity.server = develocityUrl
|
||||
develocity.allowUntrustedServer = develocityAllowUntrustedServer
|
||||
}
|
||||
|
||||
if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) {
|
||||
develocity.buildScan.termsOfUseUrl = buildScanTermsOfUseUrl
|
||||
develocity.buildScan.termsOfUseAgree = buildScanTermsOfUseAgree
|
||||
}
|
||||
},
|
||||
{ gradleEnterprise ->
|
||||
if (develocityUrl && develocityEnforceUrl) {
|
||||
gradleEnterprise.server = develocityUrl
|
||||
gradleEnterprise.allowUntrustedServer = develocityAllowUntrustedServer
|
||||
}
|
||||
|
||||
if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) {
|
||||
gradleEnterprise.buildScan.termsOfServiceUrl = buildScanTermsOfUseUrl
|
||||
gradleEnterprise.buildScan.termsOfServiceAgree = buildScanTermsOfUseAgree
|
||||
}
|
||||
if (ccudPluginVersion) {
|
||||
if (!settings.pluginManager.hasPlugin(CCUD_PLUGIN_ID)) {
|
||||
logger.lifecycle("Applying $CCUD_PLUGIN_CLASS via init script")
|
||||
settings.pluginManager.apply(initscript.classLoader.loadClass(CCUD_PLUGIN_CLASS))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (ccudPluginVersion) {
|
||||
if (!settings.pluginManager.hasPlugin(CCUD_PLUGIN_ID)) {
|
||||
logger.lifecycle("Applying $CCUD_PLUGIN_CLASS via init script")
|
||||
settings.pluginManager.apply(initscript.classLoader.loadClass(CCUD_PLUGIN_CLASS))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -360,4 +373,42 @@ static boolean isAtLeast(String versionUnderTest, String referenceVersion) {
|
||||
|
||||
static boolean isNotAtLeast(String versionUnderTest, String referenceVersion) {
|
||||
!isAtLeast(versionUnderTest, referenceVersion)
|
||||
}
|
||||
}
|
||||
|
||||
void enableBuildScanLinkCapture(BuildScanCollector collector) {
|
||||
def BUILD_SCAN_PLUGIN_ID = 'com.gradle.build-scan'
|
||||
def DEVELOCITY_PLUGIN_ID = 'com.gradle.develocity'
|
||||
|
||||
// Conditionally apply and configure the Develocity plugin
|
||||
if (GradleVersion.current() < GradleVersion.version('6.0')) {
|
||||
rootProject {
|
||||
pluginManager.withPlugin(BUILD_SCAN_PLUGIN_ID) {
|
||||
// Only execute if develocity plugin isn't applied.
|
||||
if (gradle.rootProject.extensions.findByName("develocity")) return
|
||||
buildScanPublishedAction(buildScan, collector)
|
||||
}
|
||||
|
||||
pluginManager.withPlugin(DEVELOCITY_PLUGIN_ID) {
|
||||
buildScanPublishedAction(develocity.buildScan, collector)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gradle.settingsEvaluated { settings ->
|
||||
eachDevelocitySettingsExtension(settings) { ext ->
|
||||
buildScanPublishedAction(ext.buildScan, collector)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Action will only be called if a `BuildScanCollector.captureBuildScanLink` method is present.
|
||||
// Add `void captureBuildScanLink(String) {}` to the `BuildScanCollector` class to respond to buildScanPublished events
|
||||
static buildScanPublishedAction(def buildScanExtension, BuildScanCollector collector) {
|
||||
if (buildScanExtension.metaClass.respondsTo(buildScanExtension, 'buildScanPublished', Action)) {
|
||||
buildScanExtension.buildScanPublished { scan ->
|
||||
collector.captureBuildScanLink(scan.buildScanUri.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BuildScanCollector {}
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as path from 'path'
|
||||
import * as os from 'os'
|
||||
import * as caches from './caching/caches'
|
||||
import * as jobSummary from './job-summary'
|
||||
import * as buildScan from './build-scan'
|
||||
import * as buildScan from './develocity/build-scan'
|
||||
|
||||
import {loadBuildResults, markBuildResultsProcessed} from './build-results'
|
||||
import {CacheListener, generateCachingReport} from './caching/cache-reporting'
|
||||
@@ -41,7 +41,7 @@ export async function setup(cacheConfig: CacheConfig, buildScanConfig: BuildScan
|
||||
|
||||
core.saveState(CACHE_LISTENER, cacheListener.stringify())
|
||||
|
||||
buildScan.setup(buildScanConfig)
|
||||
await buildScan.setup(buildScanConfig)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
[
|
||||
{
|
||||
"version": "8.8",
|
||||
"checksum": "cb0da6751c2b753a16ac168bb354870ebb1e162e9083f116729cec9c781156b8"
|
||||
},
|
||||
{
|
||||
"version": "8.8-rc-2",
|
||||
"checksum": "cb0da6751c2b753a16ac168bb354870ebb1e162e9083f116729cec9c781156b8"
|
||||
},
|
||||
{
|
||||
"version": "8.8-rc-1",
|
||||
"checksum": "cb0da6751c2b753a16ac168bb354870ebb1e162e9083f116729cec9c781156b8"
|
||||
},
|
||||
{
|
||||
"version": "8.7",
|
||||
"checksum": "cb0da6751c2b753a16ac168bb354870ebb1e162e9083f116729cec9c781156b8"
|
||||
|
||||
@@ -20,7 +20,7 @@ dependencies {
|
||||
testImplementation ('io.ratpack:ratpack-groovy-test:1.9.0') {
|
||||
exclude group: 'org.codehaus.groovy', module: 'groovy-all'
|
||||
}
|
||||
testImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.17.0'
|
||||
testImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.17.1'
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "3.17.1"
|
||||
id "com.gradle.common-custom-user-data-gradle-plugin" version "2.0"
|
||||
id "com.gradle.develocity" version "3.17.5"
|
||||
id "com.gradle.common-custom-user-data-gradle-plugin" version "2.0.1"
|
||||
}
|
||||
|
||||
develocity {
|
||||
|
||||
@@ -16,8 +16,8 @@ import java.nio.file.Files
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
class BaseInitScriptTest extends Specification {
|
||||
static final String DEVELOCITY_PLUGIN_VERSION = '3.17'
|
||||
static final String CCUD_PLUGIN_VERSION = '2.0'
|
||||
static final String DEVELOCITY_PLUGIN_VERSION = '3.17.5'
|
||||
static final String CCUD_PLUGIN_VERSION = '2.0.1'
|
||||
|
||||
static final TestGradleVersion GRADLE_3_X = new TestGradleVersion(GradleVersion.version('3.5.1'), 7, 9)
|
||||
static final TestGradleVersion GRADLE_4_X = new TestGradleVersion(GradleVersion.version('4.10.3'), 7, 10)
|
||||
|
||||
@@ -190,7 +190,7 @@ class TestBuildResultRecorder extends BaseInitScriptTest {
|
||||
when:
|
||||
settingsFile.text = """
|
||||
plugins {
|
||||
id 'com.gradle.develocity' version '3.17' apply(false)
|
||||
id 'com.gradle.develocity' version '3.17.5' apply(false)
|
||||
}
|
||||
gradle.settingsEvaluated {
|
||||
apply plugin: 'com.gradle.develocity'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as exec from '@actions/exec'
|
||||
import * as core from '@actions/core'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import {CacheCleaner} from '../../src/caching/cache-cleaner'
|
||||
@@ -23,13 +24,13 @@ test('will cleanup unused dependency jars and build-cache entries', async () =>
|
||||
|
||||
expect(fs.existsSync(commonsMath31)).toBe(true)
|
||||
expect(fs.existsSync(commonsMath311)).toBe(true)
|
||||
expect(fs.readdirSync(buildCacheDir).length).toBe(6)
|
||||
expect(fs.readdirSync(buildCacheDir).length).toBe(4) // gc.properties, build-cache-1.lock, and 2 task entries
|
||||
|
||||
await cacheCleaner.forceCleanup()
|
||||
|
||||
expect(fs.existsSync(commonsMath31)).toBe(false)
|
||||
expect(fs.existsSync(commonsMath311)).toBe(true)
|
||||
expect(fs.readdirSync(buildCacheDir).length).toBe(5)
|
||||
expect(fs.readdirSync(buildCacheDir).length).toBe(3) // 1 task entry has been cleaned up
|
||||
})
|
||||
|
||||
test('will cleanup unused gradle versions', async () => {
|
||||
@@ -49,7 +50,7 @@ test('will cleanup unused gradle versions', async () => {
|
||||
|
||||
const gradle802 = path.resolve(gradleUserHome, "caches/8.0.2")
|
||||
const wrapper802 = path.resolve(gradleUserHome, "wrapper/dists/gradle-8.0.2-bin")
|
||||
const gradleCurrent = path.resolve(gradleUserHome, "caches/8.7")
|
||||
const gradleCurrent = path.resolve(gradleUserHome, "caches/8.8")
|
||||
|
||||
expect(fs.existsSync(gradle802)).toBe(true)
|
||||
expect(fs.existsSync(wrapper802)).toBe(true)
|
||||
|
||||
177
sources/test/jest/job-summary.test.ts
Normal file
177
sources/test/jest/job-summary.test.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import { BuildResult } from '../../src/build-results'
|
||||
import { renderSummaryTable } from '../../src/job-summary'
|
||||
import dedent from 'dedent'
|
||||
|
||||
|
||||
const successfulHelpBuild: BuildResult = {
|
||||
rootProjectName: 'root',
|
||||
rootProjectDir: '/',
|
||||
requestedTasks: 'help',
|
||||
gradleVersion: '8.0',
|
||||
gradleHomeDir: '/opt/gradle',
|
||||
buildFailed: false,
|
||||
buildScanUri: 'https://scans.gradle.com/s/abc123',
|
||||
buildScanFailed: false
|
||||
}
|
||||
|
||||
const failedHelpBuild: BuildResult = {
|
||||
...successfulHelpBuild,
|
||||
buildFailed: true
|
||||
}
|
||||
|
||||
const longArgsBuild: BuildResult = {
|
||||
...successfulHelpBuild,
|
||||
requestedTasks: 'check publishMyLongNamePluginPublicationToMavenCentral publishMyLongNamePluginPublicationToPluginPortal',
|
||||
}
|
||||
|
||||
const scanPublishDisabledBuild: BuildResult = {
|
||||
...successfulHelpBuild,
|
||||
buildScanUri: '',
|
||||
buildScanFailed: false,
|
||||
}
|
||||
|
||||
const scanPublishFailedBuild: BuildResult = {
|
||||
...successfulHelpBuild,
|
||||
buildScanUri: '',
|
||||
buildScanFailed: true,
|
||||
}
|
||||
|
||||
describe('renderSummaryTable', () => {
|
||||
describe('renders', () => {
|
||||
it('successful build', () => {
|
||||
const table = renderSummaryTable([successfulHelpBuild])
|
||||
expect(table.trim()).toBe(dedent`
|
||||
<table>
|
||||
<tr>
|
||||
<th>Gradle Root Project</th>
|
||||
<th>Requested Tasks</th>
|
||||
<th>Gradle Version</th>
|
||||
<th>Build Outcome</th>
|
||||
<th>Build Scan®</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>root</td>
|
||||
<td>help</td>
|
||||
<td align='center'>8.0</td>
|
||||
<td align='center'>:white_check_mark:</td>
|
||||
<td><a href="https://scans.gradle.com/s/abc123" rel="nofollow" target="_blank"><img src="https://img.shields.io/badge/Build%20Scan%C2%AE-06A0CE?logo=Gradle" alt="Build Scan published" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
`);
|
||||
})
|
||||
it('failed build', () => {
|
||||
const table = renderSummaryTable([failedHelpBuild])
|
||||
expect(table.trim()).toBe(dedent`
|
||||
<table>
|
||||
<tr>
|
||||
<th>Gradle Root Project</th>
|
||||
<th>Requested Tasks</th>
|
||||
<th>Gradle Version</th>
|
||||
<th>Build Outcome</th>
|
||||
<th>Build Scan®</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>root</td>
|
||||
<td>help</td>
|
||||
<td align='center'>8.0</td>
|
||||
<td align='center'>:x:</td>
|
||||
<td><a href="https://scans.gradle.com/s/abc123" rel="nofollow" target="_blank"><img src="https://img.shields.io/badge/Build%20Scan%C2%AE-06A0CE?logo=Gradle" alt="Build Scan published" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
`);
|
||||
})
|
||||
describe('when build scan', () => {
|
||||
it('publishing disabled', () => {
|
||||
const table = renderSummaryTable([scanPublishDisabledBuild])
|
||||
expect(table.trim()).toBe(dedent`
|
||||
<table>
|
||||
<tr>
|
||||
<th>Gradle Root Project</th>
|
||||
<th>Requested Tasks</th>
|
||||
<th>Gradle Version</th>
|
||||
<th>Build Outcome</th>
|
||||
<th>Build Scan®</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>root</td>
|
||||
<td>help</td>
|
||||
<td align='center'>8.0</td>
|
||||
<td align='center'>:white_check_mark:</td>
|
||||
<td><a href="https://scans.gradle.com" rel="nofollow" target="_blank"><img src="https://img.shields.io/badge/Not%20published-lightgrey" alt="Build Scan not published" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
`);
|
||||
})
|
||||
it('publishing failed', () => {
|
||||
const table = renderSummaryTable([scanPublishFailedBuild])
|
||||
expect(table.trim()).toBe(dedent`
|
||||
<table>
|
||||
<tr>
|
||||
<th>Gradle Root Project</th>
|
||||
<th>Requested Tasks</th>
|
||||
<th>Gradle Version</th>
|
||||
<th>Build Outcome</th>
|
||||
<th>Build Scan®</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>root</td>
|
||||
<td>help</td>
|
||||
<td align='center'>8.0</td>
|
||||
<td align='center'>:white_check_mark:</td>
|
||||
<td><a href="https://docs.gradle.com/develocity/gradle-plugin/#troubleshooting" rel="nofollow" target="_blank"><img src="https://img.shields.io/badge/Publish%20failed-orange" alt="Build Scan publish failed" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
`);
|
||||
})
|
||||
})
|
||||
it('multiple builds', () => {
|
||||
const table = renderSummaryTable([successfulHelpBuild, failedHelpBuild])
|
||||
expect(table.trim()).toBe(dedent`
|
||||
<table>
|
||||
<tr>
|
||||
<th>Gradle Root Project</th>
|
||||
<th>Requested Tasks</th>
|
||||
<th>Gradle Version</th>
|
||||
<th>Build Outcome</th>
|
||||
<th>Build Scan®</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>root</td>
|
||||
<td>help</td>
|
||||
<td align='center'>8.0</td>
|
||||
<td align='center'>:white_check_mark:</td>
|
||||
<td><a href="https://scans.gradle.com/s/abc123" rel="nofollow" target="_blank"><img src="https://img.shields.io/badge/Build%20Scan%C2%AE-06A0CE?logo=Gradle" alt="Build Scan published" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>root</td>
|
||||
<td>help</td>
|
||||
<td align='center'>8.0</td>
|
||||
<td align='center'>:x:</td>
|
||||
<td><a href="https://scans.gradle.com/s/abc123" rel="nofollow" target="_blank"><img src="https://img.shields.io/badge/Build%20Scan%C2%AE-06A0CE?logo=Gradle" alt="Build Scan published" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
`);
|
||||
})
|
||||
it('truncating long requested tasks', () => {
|
||||
const table = renderSummaryTable([longArgsBuild])
|
||||
expect(table.trim()).toBe(dedent`
|
||||
<table>
|
||||
<tr>
|
||||
<th>Gradle Root Project</th>
|
||||
<th>Requested Tasks</th>
|
||||
<th>Gradle Version</th>
|
||||
<th>Build Outcome</th>
|
||||
<th>Build Scan®</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>root</td>
|
||||
<td><div title='check publishMyLongNamePluginPublicationToMavenCentral publishMyLongNamePluginPublicationToPluginPortal'>check publishMyLongNamePluginPublicationToMavenCentral publ…</div></td>
|
||||
<td align='center'>8.0</td>
|
||||
<td align='center'>:white_check_mark:</td>
|
||||
<td><a href="https://scans.gradle.com/s/abc123" rel="nofollow" target="_blank"><img src="https://img.shields.io/badge/Build%20Scan%C2%AE-06A0CE?logo=Gradle" alt="Build Scan published" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
`);
|
||||
})
|
||||
})
|
||||
})
|
||||
137
sources/test/jest/short-lived-token.test.ts
Normal file
137
sources/test/jest/short-lived-token.test.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import {DevelocityAccessCredentials, getToken} from "../../src/develocity/short-lived-token";
|
||||
import nock from "nock";
|
||||
|
||||
describe('short lived tokens', () => {
|
||||
it('parse valid access key should return an object', async () => {
|
||||
let develocityAccessCredentials = DevelocityAccessCredentials.parse('some-host.local=key1;host2=key2');
|
||||
|
||||
expect(develocityAccessCredentials).toStrictEqual(DevelocityAccessCredentials.of([
|
||||
{hostname: 'some-host.local', key: 'key1'},
|
||||
{hostname: 'host2', key: 'key2'}])
|
||||
)
|
||||
})
|
||||
|
||||
it('parse wrong access key should return null', async () => {
|
||||
let develocityAccessCredentials = DevelocityAccessCredentials.parse('random;foo');
|
||||
|
||||
expect(develocityAccessCredentials).toBeNull()
|
||||
})
|
||||
|
||||
it('parse empty access key should return null', async () => {
|
||||
let develocityAccessCredentials = DevelocityAccessCredentials.parse('');
|
||||
|
||||
expect(develocityAccessCredentials).toBeNull()
|
||||
})
|
||||
|
||||
it('access key as raw string', async () => {
|
||||
let develocityAccessCredentials = DevelocityAccessCredentials.parse('host1=key1;host2=key2');
|
||||
|
||||
expect(develocityAccessCredentials?.raw()).toBe('host1=key1;host2=key2')
|
||||
})
|
||||
|
||||
it('get short lived token fails when cannot connect', async () => {
|
||||
nock('http://localhost:3333')
|
||||
.post('/api/auth/token')
|
||||
.times(3)
|
||||
.replyWithError({
|
||||
message: 'connect ECONNREFUSED 127.0.0.1:3333',
|
||||
code: 'ECONNREFUSED'
|
||||
})
|
||||
try {
|
||||
await getToken('true', 'http://localhost:3333', 'localhost=xyz;host1=key1', '')
|
||||
expect('should have thrown').toBeUndefined()
|
||||
} catch (e) {
|
||||
// @ts-ignore
|
||||
expect(e.code).toBe('ECONNREFUSED')
|
||||
}
|
||||
})
|
||||
|
||||
it('get short lived token fails when request fails', async () => {
|
||||
nock('http://dev:3333')
|
||||
.post('/api/auth/token')
|
||||
.times(3)
|
||||
.reply(500, 'Internal error')
|
||||
expect.assertions(1)
|
||||
await expect(getToken('true', 'http://dev:3333', 'dev=xyz;host1=key1', ''))
|
||||
.rejects
|
||||
.toThrow('Develocity short lived token request failed http://dev:3333 with status code 500')
|
||||
})
|
||||
|
||||
it('get short lived token fails when server url is not set', async () => {
|
||||
expect.assertions(1)
|
||||
await expect(getToken('true', undefined, 'localhost=xyz;host1=key1', ''))
|
||||
.rejects
|
||||
.toThrow('Develocity Server URL not configured')
|
||||
})
|
||||
|
||||
it('get short lived token returns null when access key is empty', async () => {
|
||||
expect.assertions(1)
|
||||
await expect(getToken('true', 'http://dev:3333', '', ''))
|
||||
.resolves
|
||||
.toBeNull()
|
||||
})
|
||||
|
||||
it('get short lived token fails when host cannot be extracted from server url', async () => {
|
||||
expect.assertions(1)
|
||||
await expect(getToken('true', 'not_a_url', 'localhost=xyz;host1=key1', ''))
|
||||
.rejects
|
||||
.toThrow('Could not extract hostname from Develocity server URL')
|
||||
})
|
||||
|
||||
it('get short lived token fails when access key does not contain corresponding host', async () => {
|
||||
expect.assertions(1)
|
||||
await expect(getToken('true', 'http://dev', 'host1=xyz;host2=key2', ''))
|
||||
.rejects
|
||||
.toThrow('Could not find corresponding key for hostname dev')
|
||||
})
|
||||
|
||||
it('get short lived token succeeds when enforce url is true', async () => {
|
||||
nock('https://dev')
|
||||
.post('/api/auth/token')
|
||||
.reply(200, 'token')
|
||||
expect.assertions(1)
|
||||
await expect(getToken('true', 'https://dev', 'dev=key1;host1=key2', ''))
|
||||
.resolves
|
||||
.toEqual({"keys": [{"hostname": "dev", "key": "token"}]})
|
||||
})
|
||||
|
||||
it('get short lived token succeeds when enforce url is false and single key is set', async () => {
|
||||
nock('https://dev')
|
||||
.post('/api/auth/token')
|
||||
.reply(200, 'token')
|
||||
expect.assertions(1)
|
||||
await expect(getToken('false', 'https://dev', 'dev=key1', ''))
|
||||
.resolves
|
||||
.toEqual({"keys": [{"hostname": "dev", "key": "token"}]})
|
||||
})
|
||||
|
||||
it('get short lived token succeeds when enforce url is false and multiple keys are set', async () => {
|
||||
nock('https://dev')
|
||||
.post('/api/auth/token')
|
||||
.reply(200, 'token1')
|
||||
nock('https://prod')
|
||||
.post('/api/auth/token')
|
||||
.reply(200, 'token2')
|
||||
expect.assertions(1)
|
||||
await expect(getToken('false', 'https://dev', 'dev=key1;prod=key2', ''))
|
||||
.resolves
|
||||
.toEqual({"keys": [{"hostname": "dev", "key": "token1"}, {"hostname": "prod", "key": "token2"}]})
|
||||
})
|
||||
|
||||
it('get short lived token succeeds when enforce url is false and multiple keys are set and one is failing', async () => {
|
||||
nock('https://dev')
|
||||
.post('/api/auth/token')
|
||||
.reply(200, 'token1')
|
||||
nock('https://bogus')
|
||||
.post('/api/auth/token')
|
||||
.times(3)
|
||||
.reply(500, 'Internal Error')
|
||||
nock('https://prod')
|
||||
.post('/api/auth/token')
|
||||
.reply(200, 'token2')
|
||||
expect.assertions(1)
|
||||
await expect(getToken('false', 'https://dev', 'dev=key1;bogus=key0;prod=key2', ''))
|
||||
.resolves
|
||||
.toEqual({"keys": [{"hostname": "dev", "key": "token1"}, {"hostname": "prod", "key": "token2"}]})
|
||||
})
|
||||
})
|
||||
@@ -8,7 +8,10 @@ The action should be run in the root of the repository, as it will recursively s
|
||||
|
||||
```yaml
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
|
||||
Reference in New Issue
Block a user