Compare commits

...

53 Commits

Author SHA1 Message Date
bigdaz
d9336dac04 [bot] Update dist directory 2024-06-13 19:36:18 +00:00
daz
8dbe9a3802 Update DV access key regex to be more selective
This should address the code-scanning alert
  https://github.com/gradle/actions/security/code-scanning/1
2024-06-13 13:35:19 -06:00
bigdaz
9c3430720d [bot] Update dist directory 2024-06-13 19:32:23 +00:00
daz
30c82f0068 Fail on invalid boolean for Develocity inputs 2024-06-13 13:31:25 -06:00
daz
e3bc05f224 Run CodeQL on PRs 2024-06-13 13:15:23 -06:00
daz
485ea107b7 Run CodeQL on dev/* branches 2024-06-13 13:01:56 -06:00
bigdaz
c1091c9c8e [bot] Update dist directory 2024-06-13 18:43:53 +00:00
Iñaki Villar
d0a116fff5 Adding Develocity input actions (#244)
Adding Develocity input actions. 

If an input is configured in the action, it will generate the environment variable, 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.4

    - name: Run a Gradle build with Develocity injection enabled from input actions
      run: ./gradlew build
```

This configuration will create the environment variables:
```
DEVELOCITY_INJECTION_ENABLED=true
DEVELOCITY_URL=https://develocity.your-server.com
DEVELOCITY_PLUGIN_VERSION=3.17.4
```

Relation variable-input available:

| Variable | Input |

|--------------------------------------|--------------------------------------|
| 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
|
2024-06-13 12:42:47 -06:00
bigdaz
e238a7ad22 [bot] Update dist directory 2024-06-13 16:01:59 +00:00
bot-githubaction
1d2ea6e5a8 Bump references to Develocity Gradle plugin from 3.17.4 to 3.17.5 2024-06-13 10:01:04 -06:00
dependabot[bot]
114c1c234e Bump braces from 3.0.2 to 3.0.3 in /sources
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-13 09:10:17 -06:00
daz
2db3ae936e Update to Gradle 8.8 2024-06-13 09:03:40 -06:00
dependabot[bot]
a68381d359 Bump com.google.guava:guava in /.github/workflow-samples/kotlin-dsl
Bumps [com.google.guava:guava](https://github.com/google/guava) from 33.2.0-jre to 33.2.1-jre.
- [Release notes](https://github.com/google/guava/releases)
- [Commits](https://github.com/google/guava/commits)

---
updated-dependencies:
- dependency-name: com.google.guava:guava
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-13 07:41:41 -06:00
bigdaz
52ae27f7bb [bot] Update dist directory 2024-06-13 13:41:01 +00:00
bigdaz
d1cd62d80a Update known wrapper checksums 2024-06-13 07:40:03 -06:00
Anton Mostovoy
af6e576724 add missing permission for downloading dependency graph artifact
per https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts, `actions: read` is required. Without it the action fails after logging `Fetching artifact list for workflow ` due to `Error: HttpError: Resource not accessible by integration`
2024-05-23 09:09:36 -06:00
bigdaz
775b4d10d7 [bot] Update dist directory 2024-05-23 14:57:08 +00:00
Bot Githubaction
30610bc983 Bump references to Develocity Gradle plugin from 3.17.3 to 3.17.4 (#236)
This PR bumps references to Develocity Gradle plugin from 3.17.3 to 3.17.4.

---------

Co-authored-by: Eric Haag <ehaag@gradle.com>
2024-05-23 08:56:09 -06:00
bigdaz
d4d72c9934 [bot] Update dist directory 2024-05-17 21:08:44 +00:00
Alexis Tual
96b9cb4988 Set both DEVELOCITY_ACCESS_KEY and GRADLE_ENTERPRISE_ACCESS_KEY env vars (#225)
Follow up of https://github.com/gradle/actions/pull/224, we now attempt to set both old and new access key env variables to a short lived token.
If a short-lived token cannot be obtained, then:
- DEVELOCITY_ACCESS_KEY is set to an empty string, preventing this from being used.
- GRADLE_ENTERPRISE_ACCESS_KEY is left intact, with a deprecation warning being issued.
2024-05-17 15:07:50 -06:00
Bot Githubaction
db270b9337 Update develocity-injection init script to v1.0
Updates the develocity-injection init script to the latest reference script content
from https://github.com/gradle/develocity-ci-injection.
2024-05-17 14:18:19 -06:00
Bot Githubaction
d91e2960eb Update develocity-injection init script to v0.5.1
Updates the develocity-injection init script to the latest reference script content
from https://github.com/gradle/develocity-ci-injection.
2024-05-17 11:10:27 -06:00
bigdaz
0498421560 Update develocity-injection init script to v0.5.0 2024-05-17 07:27:33 -06:00
bigdaz
edb13383f3 Update develocity-injection init script to v0.4.0 2024-05-15 16:56:41 -06:00
bigdaz
cd560aa3ad [bot] Update dist directory 2024-05-15 22:50:56 +00:00
Alexis Tual
500e0ee5b3 Add support for short-lived tokens (#224)
The setup-gradle action tries to get a short-lived access token given the supplied Develocity access key.
This key can be passed either with the `DEVELOCITY_ACCESS_KEY` env var or via the  `develocity-access-key` input parameter.
If a token can be retrieved, then the `DEVELOCITY_ACCESS_KEY` env var will be set to the token. 
Otherwise the `DEVELOCITY_ACCESS_KEY` will be set to a blank string, to avoid a leak.

---------

Co-authored-by: daz <daz@gradle.com>
2024-05-15 16:49:55 -06:00
bigdaz
eb13cf7170 [bot] Update dist directory 2024-05-11 13:46:41 +00:00
Daz DeBoer
ea14aa9caf Dependency updates (#222)
- Bump to com.gradle.develocity plugin v3.17.3
- Bump JVM dependencies in sample projects
2024-05-11 07:45:47 -06:00
lokalpage-safe
063cfaf0eb Update dependency-submission.md
fix: Fixed document typo (additonal -> additional)
2024-05-09 15:54:39 -06:00
Maxim Mironyuk
35f9242e22 Update setup-gradle.md
repsitory -> repository
2024-05-09 15:54:08 -06:00
bigdaz
90f1de0556 [bot] Update dist directory 2024-04-28 14:50:56 +00:00
bigdaz
da512b52a5 Update known wrapper checksums 2024-04-28 15:49:59 +01:00
bigdaz
db19848a5f [bot] Update dist directory 2024-04-25 20:24:26 +00:00
daz
941b289d84 Avoid running Gradle 3.5.1 on MacOS where Java 8 is not available 2024-04-25 21:23:15 +01:00
Daz DeBoer
bce7daca54 Improve build scan badge readability with long tasks (#200)
Improve readability of build scan when requested tasks is very long, as
agreed in #175. HTML diff for each case of job summary is clearer in
cd62d9c9ef.

- Ensure a minimum size for the badge, at least the size of "Build
scan®", by preventing a line break with `&nbsp;`
- Reduce the size of the badge by tweaking the inner text

Also fix a typo in the build shell script.
2024-04-25 21:22:36 +01:00
daz
11eaed9738 Avoid Java 8 since it is not available on MacOS runners 2024-04-25 20:17:16 +01:00
Gabriel Feo
cd62d9c9ef Improve job summary readability with long tasks
Change tests for nbsp and concise badge

Make tests pass

Signed-off-by: Gabriel Feo <gabriel@gabrielfeo.com>
2024-04-25 19:40:04 +01:00
Gabriel Feo
a54fb6a5bb Add tests on current job-summary behavior
Signed-off-by: Gabriel Feo <gabriel@gabrielfeo.com>
2024-04-25 17:44:41 +01:00
Gabriel Feo
ef36f81b41 Fix typo in build shell script
Signed-off-by: Gabriel Feo <gabriel@gabrielfeo.com>
2024-04-25 17:42:29 +01:00
bigdaz
18998bc43e [bot] Update dist directory 2024-04-25 10:20:39 +00:00
daz
a772c14b33 Avoid updating real dependency graph in tests 2024-04-25 11:19:43 +01:00
daz
7763d71170 Set the report dir for download-and-submit
Fixes #196
2024-04-25 11:19:43 +01:00
bigdaz
9ab93ee864 [bot] Update dist directory 2024-04-19 16:25:39 +00:00
daz
d124ec149f Retain and log stacktrace for submission errors 2024-04-19 10:24:45 -06:00
Alex Serbin
6ccde15122 Use logger in dependency graph init script (#191)
Dependency graph init script now uses Gradle logger to emit warning/info messages.
This allows these messages to be suppressed using `--quiet`.
2024-04-19 07:03:17 -06:00
bigdaz
750cdda3ed [bot] Update dist directory 2024-04-18 19:41:35 +00:00
Daz DeBoer
c198d84863 Support custom report dir for dependency-submission (#189)
If the `DEPENDENCY_GRAPH_REPORT_DIR` var is set, use this value when locating dependency-graph files to upload/submit.

Fixes #188
2024-04-18 13:40:41 -06:00
Daz DeBoer
d211a39090 Documentation updates (#187)
* Use consistent YAML syntax in example workflows
* Add link to plugin env vars
2024-04-18 09:39:15 -06:00
bigdaz
439ed0a0ac [bot] Update dist directory 2024-04-18 01:09:48 +00:00
daz
eef9b10930 Bump Develocity and CCUD plugin versions 2024-04-17 19:08:51 -06:00
Marcono1234
8be796e9fa Update link to 'update checksums' workflow 2024-04-16 10:06:09 -06:00
daz
3c2d3b6f2a Include VS code workspace file
This workspace sets things up so that source code navigation works
propertly in VS code.
2024-04-12 15:39:30 -06:00
daz
0fa10b26b8 Update releasing guide 2024-04-12 14:01:48 -06:00
66 changed files with 3093 additions and 724 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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

View File

@@ -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")
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -1,5 +1,5 @@
plugins {
id "com.gradle.develocity" version "3.17.1"
id "com.gradle.develocity" version "3.17.5"
}
develocity {

View File

@@ -1,5 +1,5 @@
plugins {
id "com.gradle.develocity" version "3.17.1"
id "com.gradle.develocity" version "3.17.5"
}
develocity {

View File

@@ -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

View File

@@ -1,5 +1,5 @@
plugins {
id "com.gradle.develocity" version "3.17"
id "com.gradle.develocity" version "3.17.5"
}
develocity {

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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
@@ -62,3 +67,135 @@ jobs:
with:
script: |
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')

View File

@@ -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

View File

@@ -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
View File

@@ -1,3 +1,2 @@
.git
.vscode
actions.code-workspace

View File

@@ -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:

View File

@@ -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
View File

@@ -0,0 +1,11 @@
{
"folders": [
{
"path": "."
},
{
"path": "sources"
}
],
"settings": {}
}

10
build
View File

@@ -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,12 +16,18 @@ 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

View File

@@ -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

View File

@@ -125,6 +125,14 @@ inputs:
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'.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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

View File

@@ -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.

View File

@@ -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:
@@ -197,7 +198,7 @@ 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.
@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -100,6 +100,54 @@ inputs:
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: |

View File

@@ -1,3 +1,3 @@
# Configuration file for asdf version manager
nodejs 20.10.0
gradle 8.7
gradle 8.8

View File

@@ -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"

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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)

View 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)
}
}

View 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)
}
}

View File

@@ -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) {

View File

@@ -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&nbsp;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>`
}

View File

@@ -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'

View File

@@ -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,42 +97,62 @@ 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 ->
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
}
}
// finish early if configuration parameters passed in via system properties are not valid/supported
if (ccudPluginVersion && isNotAtLeast(ccudPluginVersion, '1.7')) {
// 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')) {
// 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
@@ -231,7 +243,7 @@ if (GradleVersion.current() < GradleVersion.version('6.0')) {
}
}
}
} else {
} else {
gradle.settingsEvaluated { settings ->
if (develocityPluginVersion) {
if (!settings.pluginManager.hasPlugin(GRADLE_ENTERPRISE_PLUGIN_ID) && !settings.pluginManager.hasPlugin(DEVELOCITY_PLUGIN_ID)) {
@@ -307,6 +319,7 @@ if (GradleVersion.current() < GradleVersion.version('6.0')) {
}
}
}
}
}
void applyPluginExternally(def pluginManager, String pluginClassName) {
@@ -361,3 +374,41 @@ 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 {}

View File

@@ -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
}

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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'

View File

@@ -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)

View 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&nbsp;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&nbsp;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&nbsp;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&nbsp;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&nbsp;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&nbsp;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>
`);
})
})
})

View 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"}]})
})
})

View File

@@ -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: