Compare commits

..

4 Commits

Author SHA1 Message Date
daz
a8f75513ea Build outputs 2024-01-23 16:12:23 -07:00
daz
9283312acb Add new option to clear dependency-graph
When changing workflow names or when changing to the new 'dependency-submission'
action, it can be useful to clear existing dependency graph snapshots from previous
submissions. While the old graphs will eventually "age out", the 'clear' option will
submit an empty dependency graph for an existing Job correlator, ensuring that old
dependency graphs don't linger.
2024-01-23 16:11:35 -07:00
daz
7c8a278ea0 Remove old clear-dependency-graph action 2024-01-23 15:07:13 -07:00
daz
d8ca9b7d2e Do full checks on release branches 2024-01-23 15:07:13 -07:00
35 changed files with 15761 additions and 107062 deletions

View File

@@ -1,6 +1,6 @@
plugins { plugins {
id "com.gradle.enterprise" version "3.16.1" id "com.gradle.enterprise" version "3.15.1"
id "com.gradle.common-custom-user-data-gradle-plugin" version "1.12.1" id "com.gradle.common-custom-user-data-gradle-plugin" version "1.12"
} }
gradleEnterprise { gradleEnterprise {

View File

@@ -8,7 +8,7 @@ repositories {
dependencies { dependencies {
api("org.apache.commons:commons-math3:3.6.1") api("org.apache.commons:commons-math3:3.6.1")
implementation("com.google.guava:guava:33.0.0-jre") implementation("com.google.guava:guava:32.1.3-jre")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1") testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
} }

View File

@@ -1,6 +1,6 @@
plugins { plugins {
id("com.gradle.enterprise") version "3.16.1" id("com.gradle.enterprise") version "3.15.1"
id("com.gradle.common-custom-user-data-gradle-plugin") version "1.12.1" id("com.gradle.common-custom-user-data-gradle-plugin") version "1.12"
} }
gradleEnterprise { gradleEnterprise {

View File

@@ -1,5 +1,5 @@
plugins { plugins {
id "com.gradle.build-scan" version "3.16.1" id "com.gradle.build-scan" version "3.15.1"
} }
gradleEnterprise { gradleEnterprise {

View File

@@ -1,5 +1,5 @@
plugins { plugins {
id "com.gradle.enterprise" version "3.16.1" id "com.gradle.enterprise" version "3.15.1"
} }
gradleEnterprise { gradleEnterprise {

View File

@@ -42,7 +42,7 @@ jobs:
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v2
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@@ -53,7 +53,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v3 uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@@ -67,4 +67,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v2

View File

@@ -9,6 +9,7 @@ on:
push: push:
branches: branches:
- main - main
- release/**
paths: paths:
- '.github/**' - '.github/**'
- 'dist/**' - 'dist/**'
@@ -25,7 +26,7 @@ jobs:
cache-key-prefix: ${{github.run_number}}- cache-key-prefix: ${{github.run_number}}-
caching-config: caching-config:
uses: ./.github/workflows/integ-test-caching-config.yml uses: ./.github/workflows/integ-test-action-inputs-caching.yml
with: with:
cache-key-prefix: ${{github.run_number}}- cache-key-prefix: ${{github.run_number}}-

View File

@@ -20,7 +20,7 @@ jobs:
distribution: temurin distribution: temurin
java-version: 8 java-version: 8
- name: Setup Gradle - name: Setup Gradle
uses: gradle/gradle-build-action@v2.11.0 # Use a released version to avoid breakages uses: gradle/gradle-build-action@v2.10.0 # Use a released version to avoid breakages
- name: Run integration tests - name: Run integration tests
working-directory: test/init-scripts working-directory: test/init-scripts
run: ./gradlew check run: ./gradlew check

View File

@@ -3,7 +3,9 @@ name: CI-quick-check
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
branches-ignore: main branches-ignore:
- main
- release/**
jobs: jobs:
build-distribution: build-distribution:
@@ -45,7 +47,7 @@ jobs:
caching-config: caching-config:
needs: build-distribution needs: build-distribution
uses: ./.github/workflows/integ-test-caching-config.yml uses: ./.github/workflows/integ-test-action-inputs-caching.yml
with: with:
runner-os: '["ubuntu-latest"]' runner-os: '["ubuntu-latest"]'
download-dist: true download-dist: true
@@ -87,12 +89,12 @@ jobs:
runner-os: '["ubuntu-latest"]' runner-os: '["ubuntu-latest"]'
download-dist: true download-dist: true
restore-configuration-cache: # restore-configuration-cache:
needs: build-distribution # needs: build-distribution
uses: ./.github/workflows/integ-test-restore-configuration-cache.yml # uses: ./.github/workflows/integ-test-restore-configuration-cache.yml
with: # with:
runner-os: '["ubuntu-latest"]' # runner-os: '["ubuntu-latest"]'
download-dist: true # download-dist: true
restore-containerized-gradle-home: restore-containerized-gradle-home:
needs: build-distribution needs: build-distribution

View File

@@ -8,6 +8,7 @@ on:
push: push:
branches: branches:
- main - main
- release/**
- dependabot/** - dependabot/**
jobs: jobs:
@@ -33,7 +34,7 @@ jobs:
id: diff id: diff
# If index.js was different than expected, upload the expected version as an artifact # If index.js was different than expected, upload the expected version as an artifact
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v3
if: ${{ failure() && steps.diff.conclusion == 'failure' }} if: ${{ failure() && steps.diff.conclusion == 'failure' }}
with: with:
name: dist name: dist

View File

@@ -1,4 +1,4 @@
name: Test caching configuration name: Test action inputs for caching
on: on:
workflow_call: workflow_call:
@@ -38,8 +38,7 @@ jobs:
enterprise enterprise
# Exclude build-cache from main cache entry # Exclude build-cache from main cache entry
gradle-home-cache-excludes: | gradle-home-cache-excludes: |
caches/build-cache-* caches/build-cache-1
caches/*/executionHistory
- name: Build using Gradle wrapper - name: Build using Gradle wrapper
working-directory: .github/workflow-samples/groovy-dsl working-directory: .github/workflow-samples/groovy-dsl
run: ./gradlew test run: ./gradlew test
@@ -64,8 +63,7 @@ jobs:
caches caches
enterprise enterprise
gradle-home-cache-excludes: | gradle-home-cache-excludes: |
caches/build-cache-* caches/build-cache-1
caches/*/executionHistory
cache-read-only: true cache-read-only: true
- name: Execute Gradle build with --offline - name: Execute Gradle build with --offline
working-directory: .github/workflow-samples/groovy-dsl working-directory: .github/workflow-samples/groovy-dsl

View File

@@ -31,7 +31,7 @@ jobs:
- name: Setup Gradle for dependency-graph generate - name: Setup Gradle for dependency-graph generate
uses: ./ uses: ./
with: with:
dependency-graph: generate-and-upload dependency-graph: generate
- name: Run gradle build - name: Run gradle build
run: ./gradlew build run: ./gradlew build
working-directory: .github/workflow-samples/groovy-dsl working-directory: .github/workflow-samples/groovy-dsl
@@ -55,7 +55,7 @@ jobs:
working-directory: .github/workflow-samples/kotlin-dsl working-directory: .github/workflow-samples/kotlin-dsl
submit: submit:
needs: [groovy-generate] needs: [groovy-generate, kotlin-generate]
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"
steps: steps:
- name: Checkout sources - name: Checkout sources
@@ -80,7 +80,7 @@ jobs:
- name: Setup Gradle for dependency-graph generate - name: Setup Gradle for dependency-graph generate
uses: ./ uses: ./
with: with:
dependency-graph: generate-and-submit dependency-graph: generate
- id: gradle-assemble - id: gradle-assemble
run: ./gradlew assemble run: ./gradlew assemble
working-directory: .github/workflow-samples/groovy-dsl working-directory: .github/workflow-samples/groovy-dsl

View File

@@ -21,8 +21,8 @@ env:
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
GRADLE_ENTERPRISE_INJECTION_ENABLED: true GRADLE_ENTERPRISE_INJECTION_ENABLED: true
GRADLE_ENTERPRISE_URL: https://ge.solutions-team.gradle.com GRADLE_ENTERPRISE_URL: https://ge.solutions-team.gradle.com
GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.16.1 GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.15.1
GRADLE_ENTERPRISE_CCUD_PLUGIN_VERSION: 1.12.1 GRADLE_ENTERPRISE_CCUD_PLUGIN_VERSION: 1.12
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
jobs: jobs:

View File

@@ -34,11 +34,9 @@ jobs:
uses: ./ uses: ./
with: with:
cache-read-only: false # For testing, allow writing cache entries on non-default branches cache-read-only: false # For testing, allow writing cache entries on non-default branches
cache-encryption-key: Da25KUVSE5jbGds2zXmfXw==
gradle-version: release-nightly
- name: Groovy build with configuration-cache enabled - name: Groovy build with configuration-cache enabled
working-directory: .github/workflow-samples/groovy-dsl working-directory: .github/workflow-samples/groovy-dsl
run: gradle test --configuration-cache run: ./gradlew test --configuration-cache
verify-build-groovy: verify-build-groovy:
env: env:
@@ -57,12 +55,10 @@ jobs:
uses: ./ uses: ./
with: with:
cache-read-only: true cache-read-only: true
cache-encryption-key: Da25KUVSE5jbGds2zXmfXw==
gradle-version: release-nightly
- name: Groovy build with configuration-cache enabled - name: Groovy build with configuration-cache enabled
id: execute id: execute
working-directory: .github/workflow-samples/groovy-dsl working-directory: .github/workflow-samples/groovy-dsl
run: gradle test --configuration-cache run: ./gradlew test --configuration-cache
- name: Check that configuration-cache was used - name: Check that configuration-cache was used
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
@@ -92,11 +88,9 @@ jobs:
GRADLE_BUILD_ACTION_SKIP_RESTORE: "generated-gradle-jars|wrapper-zips|java-toolchains|instrumented-jars|dependencies|kotlin-dsl" GRADLE_BUILD_ACTION_SKIP_RESTORE: "generated-gradle-jars|wrapper-zips|java-toolchains|instrumented-jars|dependencies|kotlin-dsl"
with: with:
cache-read-only: true cache-read-only: true
cache-encryption-key: Da25KUVSE5jbGds2zXmfXw==
gradle-version: release-nightly
- name: Check execute Gradle build with configuration cache enabled (but not restored) - name: Check execute Gradle build with configuration cache enabled (but not restored)
working-directory: .github/workflow-samples/groovy-dsl working-directory: .github/workflow-samples/groovy-dsl
run: gradle test --configuration-cache run: ./gradlew test --configuration-cache
seed-build-kotlin: seed-build-kotlin:
env: env:
@@ -114,11 +108,9 @@ jobs:
uses: ./ uses: ./
with: with:
cache-read-only: false # For testing, allow writing cache entries on non-default branches cache-read-only: false # For testing, allow writing cache entries on non-default branches
cache-encryption-key: Da25KUVSE5jbGds2zXmfXw==
gradle-version: release-nightly
- name: Execute 'help' with configuration-cache enabled - name: Execute 'help' with configuration-cache enabled
working-directory: .github/workflow-samples/kotlin-dsl working-directory: .github/workflow-samples/kotlin-dsl
run: gradle help --configuration-cache run: ./gradlew help --configuration-cache
modify-build-kotlin: modify-build-kotlin:
env: env:
@@ -137,11 +129,9 @@ jobs:
uses: ./ uses: ./
with: with:
cache-read-only: false # For testing, allow writing cache entries on non-default branches cache-read-only: false # For testing, allow writing cache entries on non-default branches
cache-encryption-key: Da25KUVSE5jbGds2zXmfXw==
gradle-version: release-nightly
- name: Execute 'test' with configuration-cache enabled - name: Execute 'test' with configuration-cache enabled
working-directory: .github/workflow-samples/kotlin-dsl working-directory: .github/workflow-samples/kotlin-dsl
run: gradle test --configuration-cache run: ./gradlew test --configuration-cache
# Test restore configuration-cache from the third build invocation # Test restore configuration-cache from the third build invocation
verify-build-kotlin: verify-build-kotlin:
@@ -161,12 +151,10 @@ jobs:
uses: ./ uses: ./
with: with:
cache-read-only: true cache-read-only: true
cache-encryption-key: Da25KUVSE5jbGds2zXmfXw==
gradle-version: release-nightly
- name: Execute 'test' again with configuration-cache enabled - name: Execute 'test' again with configuration-cache enabled
id: execute id: execute
working-directory: .github/workflow-samples/kotlin-dsl working-directory: .github/workflow-samples/kotlin-dsl
run: gradle test --configuration-cache run: ./gradlew test --configuration-cache
- name: Check that configuration-cache was used - name: Check that configuration-cache was used
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:

View File

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

126
README.md
View File

@@ -97,6 +97,7 @@ jobs:
- run: echo "The release-candidate version was ${{ steps.setup-gradle.outputs.gradle-version }}" - run: echo "The release-candidate version was ${{ steps.setup-gradle.outputs.gradle-version }}"
``` ```
## Caching build state between Jobs ## Caching build state between Jobs
The `gradle-build-action` will use the GitHub Actions cache to save and restore reusable state that may be speed up a subsequent build invocation. This includes most content that is downloaded from the internet as part of a build, as well as expensive to create content like compiled build scripts, transformed Jar files, etc. The `gradle-build-action` will use the GitHub Actions cache to save and restore reusable state that may be speed up a subsequent build invocation. This includes most content that is downloaded from the internet as part of a build, as well as expensive to create content like compiled build scripts, transformed Jar files, etc.
@@ -160,30 +161,6 @@ If you want override the default and have the `gradle-build-action` caches overw
cache-overwrite-existing: true cache-overwrite-existing: true
``` ```
### Saving configuration-cache data
When Gradle is executed with the [configuration-cache](https://docs.gradle.org/current/userguide/configuration_cache.html) enabled, the configuration-cache data is stored
in the project directory, at `<project-dir>/.gradle/configuration-cache`. Due to the way the configuration-cache works, [this file may contain stored credentials and other
secrets](https://docs.gradle.org/release-nightly/userguide/configuration_cache.html#config_cache:secrets), and this data needs to be encrypted in order to be safely stored in the GitHub Actions cache.
In order to benefit from configuration caching in your GitHub Actions workflow, you must:
- Enable the configuration cache for your build
- Execute your build with Gradle 8.6 or newer. This can be achieved directly, or via the Gradle Wrapper.
- Provide a [valid Gradle encryption key](https://docs.gradle.org/release-nightly/userguide/configuration_cache.html#config_cache:secrets:configuring_encryption_key) via the `cache-encryption-key` action parameter
```yaml
jobs:
gradle-with-configuration-cache:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/gradle-build-action@v3
with:
gradle-version: 8.6
cache-encryption-key: Da25KUVSE5jbGds2zXmfXw==
- run: gradle build --configuration-cache
```
### Incompatibility with other caching mechanisms ### Incompatibility with other caching mechanisms
When using `gradle-build-action` we recommend that you avoid using other mechanisms to save and restore the Gradle User Home. When using `gradle-build-action` we recommend that you avoid using other mechanisms to save and restore the Gradle User Home.
@@ -446,6 +423,103 @@ jobs:
path: build/reports/ path: build/reports/
``` ```
## Use the action to invoke Gradle
If the `gradle-build-action` is configured with an `arguments` input, then Gradle will execute a Gradle build with the arguments provided. NOTE: We recommend using the `gradle-build-action` as a "Setup Gradle" step as described above, with Gradle being invoked via a regular `run` command.
If no `arguments` are provided, the action will not execute Gradle, but will still cache Gradle state and configure build-scan capture for all subsequent Gradle executions.
```yaml
name: Run Gradle on PRs
on: pull_request
jobs:
gradle:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Setup and execute Gradle 'test' task
uses: gradle/gradle-build-action@v2
with:
arguments: test
```
### Multiple Gradle executions in the same Job
It is possible to configure multiple Gradle executions to run sequentially in the same job.
The initial Action step will perform the Gradle setup.
```yaml
- uses: gradle/gradle-build-action@v2
with:
arguments: assemble
- uses: gradle/gradle-build-action@v2
with:
arguments: check
```
### Gradle command-line arguments
The `arguments` input can be used to pass arbitrary arguments to the `gradle` command line.
Arguments can be supplied in a single line, or as a multi-line input.
Here are some valid examples:
```yaml
arguments: build
arguments: check --scan
arguments: some arbitrary tasks
arguments: build -PgradleProperty=foo
arguments: |
build
--scan
-PgradleProperty=foo
-DsystemProperty=bar
```
If you need to pass environment variables, use the GitHub Actions workflow syntax:
```yaml
- uses: gradle/gradle-build-action@v2
env:
CI: true
with:
arguments: build
```
### Gradle build located in a subdirectory
By default, the action will execute Gradle in the root directory of your project.
Use the `build-root-directory` input to target a Gradle build in a subdirectory.
```yaml
- uses: gradle/gradle-build-action@v2
with:
arguments: build
build-root-directory: some/subdirectory
```
### Using a specific Gradle executable
The action will first look for a Gradle wrapper script in the root directory of your project.
If not found, `gradle` will be executed from the PATH.
Use the `gradle-executable` input to execute using a specific Gradle installation.
```yaml
- uses: gradle/gradle-build-action@v2
with:
arguments: build
gradle-executable: /path/to/installed/gradle
```
This mechanism can also be used to target a Gradle wrapper script that is located in a non-default location.
## Support for GitHub Enterprise Server (GHES) ## Support for GitHub Enterprise Server (GHES)
You can use the `gradle-build-action` on GitHub Enterprise Server, and benefit from the improved integration with Gradle. Depending on the version of GHES you are running, certain features may be limited: You can use the `gradle-build-action` on GitHub Enterprise Server, and benefit from the improved integration with Gradle. Depending on the version of GHES you are running, certain features may be limited:
@@ -795,7 +869,7 @@ name: Run build with Gradle Enterprise injection
env: env:
GRADLE_ENTERPRISE_INJECTION_ENABLED: true GRADLE_ENTERPRISE_INJECTION_ENABLED: true
GRADLE_ENTERPRISE_URL: https://ge.gradle.org GRADLE_ENTERPRISE_URL: https://ge.gradle.org
GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.16.1 GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.15.1
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_KEY }} # Required to publish scans to ge.gradle.org GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_KEY }} # Required to publish scans to ge.gradle.org
jobs: jobs:
@@ -809,7 +883,7 @@ jobs:
run: ./gradlew build run: ./gradlew build
``` ```
This configuration will automatically apply `v3.16.1` of the [Gradle Enterprise Gradle plugin](https://docs.gradle.com/enterprise/gradle-plugin/), and publish build scans to https://ge.gradle.org. This configuration will automatically apply `v3.15.1` of the [Gradle Enterprise Gradle plugin](https://docs.gradle.com/enterprise/gradle-plugin/), and publish build scans to https://ge.gradle.org.
Note that the `ge.gradle.org` server requires authentication in order to publish scans. The provided `GRADLE_ENTERPRISE_ACCESS_KEY` isn't required by the Gradle Enterprise injection script, Note that the `ge.gradle.org` server requires authentication in order to publish scans. The provided `GRADLE_ENTERPRISE_ACCESS_KEY` isn't required by the Gradle Enterprise injection script,
but will be used by the GE plugin in order to authenticate with the server. but will be used by the GE plugin in order to authenticate with the server.

View File

@@ -8,6 +8,14 @@ inputs:
description: Gradle version to use. If specified, this Gradle version will be downloaded, added to the PATH and used for invoking Gradle. description: Gradle version to use. If specified, this Gradle version will be downloaded, added to the PATH and used for invoking Gradle.
required: false required: false
gradle-executable:
description: Path to the Gradle executable. If specified, this executable will be added to the PATH and used for invoking Gradle.
required: false
build-root-directory:
description: Path to the root directory of the build. Default is the root of the GitHub workspace.
required: false
cache-disabled: cache-disabled:
description: When 'true', all caching is disabled. No entries will be written to or read from the cache. description: When 'true', all caching is disabled. No entries will be written to or read from the cache.
required: false required: false
@@ -32,13 +40,6 @@ inputs:
required: false required: false
default: false default: false
cache-encryption-key:
description: |
A base64 encoded AES key used to encrypt the configuration-cache data. The key is exported as 'GRADLE_ENCRYPTION_KEY' for later steps.
A suitable key can be generated with `openssl rand -base64 16`.
Configuration-cache data will not be saved/restored without an encryption key being provided.
required: false
gradle-home-cache-includes: gradle-home-cache-includes:
description: Paths within Gradle User Home to cache. description: Paths within Gradle User Home to cache.
required: false required: false
@@ -58,13 +59,17 @@ inputs:
required: false required: false
default: false default: false
arguments:
description: Gradle command line arguments (supports multi-line input)
required: false
generate-job-summary: generate-job-summary:
description: When 'false', no Job Summary will be generated for the Job. description: When 'false', no Job Summary will be generated for the Job.
required: false required: false
default: true default: true
dependency-graph: dependency-graph:
description: Specifies if a GitHub dependency snapshot should be generated for each Gradle build, and if so, how. Valid values are 'disabled' (default), 'generate', 'generate-and-submit', 'generate-and-upload' and 'download-and-submit'. description: Specifies if a GitHub dependency snapshot should be generated for each Gradle build, and if so, how. Valid values are 'disabled' (default), 'generate', 'generate-and-submit', 'download-and-submit' and 'clear'.
required: false required: false
default: 'disabled' default: 'disabled'
@@ -72,22 +77,6 @@ inputs:
description: Specifies the number of days to retain any artifacts generated by the action. If not set, the default retention settings for the repository will apply. description: Specifies the number of days to retain any artifacts generated by the action. If not set, the default retention settings for the repository will apply.
required: false required: false
# DEPRECATED ACTION INPUTS
arguments:
description: Gradle command line arguments (supports multi-line input)
required: false
deprecation-message: Using the action to execute Gradle directly is deprecated in favor of using the action to setup Gradle, and executing Gradle in a subsequent Step. See https://github.com/gradle/gradle-build-action?tab=readme-ov-file#use-the-action-to-setup-gradle.
build-root-directory:
description: Path to the root directory of the build. Default is the root of the GitHub workspace.
required: false
deprecation-message: Using the action to execute Gradle directly is deprecated in favor of using the action to setup Gradle, and executing Gradle in a subsequent Step. See https://github.com/gradle/gradle-build-action?tab=readme-ov-file#use-the-action-to-setup-gradle.
gradle-executable:
description: Path to the Gradle executable. If specified, this executable will be added to the PATH and used for invoking Gradle.
required: false
deprecation-message: Using the action to execute Gradle directly is deprecated in favor of using the action to setup Gradle, and executing Gradle in a subsequent Step. See https://github.com/gradle/gradle-build-action?tab=readme-ov-file#use-the-action-to-setup-gradle.
# EXPERIMENTAL & INTERNAL ACTION INPUTS # EXPERIMENTAL & INTERNAL ACTION INPUTS
# The following action properties allow fine-grained tweaking of the action caching behaviour. # The following action properties allow fine-grained tweaking of the action caching behaviour.
# These properties are experimental and not (yet) designed for production use, and may change without notice in a subsequent release of `gradle-build-action`. # These properties are experimental and not (yet) designed for production use, and may change without notice in a subsequent release of `gradle-build-action`.
@@ -96,7 +85,6 @@ inputs:
description: When 'true', the action will not attempt to restore the Gradle User Home entries from other Jobs. description: When 'true', the action will not attempt to restore the Gradle User Home entries from other Jobs.
required: false required: false
default: false default: false
workflow-job-context: workflow-job-context:
description: Used to uniquely identify the current job invocation. Defaults to the matrix values for this job; this should not be overridden by users (INTERNAL). description: Used to uniquely identify the current job invocation. Defaults to the matrix values for this job; this should not be overridden by users (INTERNAL).
required: false required: false
@@ -116,7 +104,7 @@ outputs:
description: Version of Gradle that was setup by the action description: Version of Gradle that was setup by the action
runs: runs:
using: 'node20' using: 'node16'
main: 'dist/main/index.js' main: 'dist/main/index.js'
post: 'dist/post/index.js' post: 'dist/post/index.js'

View File

@@ -1,24 +0,0 @@
name: 'Clear dependency graph for a correlator'
inputs:
job-correlator:
required: true
runs:
using: "composite"
steps:
- name: Set current timestamp as env variable
shell: bash
run: echo "NOW=$(date -Iseconds)" >> $GITHUB_ENV
- name: Submit empty dependency graph
shell: bash
run: |
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ github.token }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${{ github.repository }}/dependency-graph/snapshots \
-d '{ "version" : 0, "job" : { "id" : "${{ github.run_id }}", "correlator" : "${{ inputs.job-correlator }} " }, "sha" : "${{ github.sha }}", "ref" : "${{ github.ref }}", "detector" : { "name" : "GitHub Dependency Graph Gradle Plugin", "version" : "0.0.3", "url" : "https://github.com/gradle/github-dependency-graph-gradle-plugin" }, "manifests" : {}, "scanned" : "${{ env.NOW }}" }'
- run: echo "::notice ::Cleared dependency graph for job correlator '${{ inputs.job-correlator }}'"
shell: bash

59972
dist/main/index.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

59127
dist/post/index.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2744
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,9 +8,11 @@
"format": "prettier --write **/*.ts", "format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts", "format-check": "prettier --check **/*.ts",
"lint": "eslint src/**/*.ts", "lint": "eslint src/**/*.ts",
"compile-main": "ncc build src/main.ts --out dist/main --source-map --no-source-map-register", "compile-main": "ncc build src/main.ts --out dist/main --source-map --no-source-map-register",
"compile-post": "ncc build src/post.ts --out dist/post --source-map --no-source-map-register", "compile-post": "ncc build src/post.ts --out dist/post --source-map --no-source-map-register",
"compile": "npm run compile-main && npm run compile-post", "compile": "npm run compile-main && npm run compile-post",
"test": "jest", "test": "jest",
"check": "npm run format && npm run lint", "check": "npm run format && npm run lint",
"build": "npm run check && npm run compile", "build": "npm run check && npm run compile",
@@ -28,29 +30,28 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/artifact": "2.0.0", "@actions/artifact": "1.1.2",
"@actions/cache": "3.2.2", "@actions/cache": "3.2.2",
"@actions/core": "1.10.1", "@actions/core": "1.10.1",
"@actions/exec": "1.1.1", "@actions/exec": "1.1.1",
"@actions/github": "6.0.0", "@actions/github": "5.1.1",
"@actions/glob": "0.4.0", "@actions/glob": "0.4.0",
"@actions/http-client": "2.2.0", "@actions/http-client": "2.2.0",
"@actions/tool-cache": "2.0.1", "@actions/tool-cache": "2.0.1",
"@octokit/rest": "19.0.13", "@octokit/rest": "19.0.13",
"@octokit/webhooks-types": "7.3.1", "@octokit/webhooks-types": "7.3.1",
"semver": "7.5.4",
"string-argv": "0.3.2" "string-argv": "0.3.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "16.18.38",
"@types/jest": "29.5.11", "@types/jest": "29.5.11",
"@types/node": "20.10.0",
"@types/unzipper": "0.10.9", "@types/unzipper": "0.10.9",
"@typescript-eslint/parser": "6.15.0", "@typescript-eslint/parser": "6.14.0",
"@vercel/ncc": "0.38.1", "@vercel/ncc": "0.38.1",
"eslint": "8.56.0", "eslint": "8.55.0",
"eslint-plugin-github": "4.10.1", "eslint-plugin-github": "4.10.1",
"eslint-plugin-jest": "27.6.0", "eslint-plugin-jest": "27.6.0",
"eslint-plugin-prettier": "5.1.1", "eslint-plugin-prettier": "5.0.1",
"jest": "29.7.0", "jest": "29.7.0",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"patch-package": "8.0.0", "patch-package": "8.0.0",

View File

@@ -1,13 +1,11 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as exec from '@actions/exec' import * as exec from '@actions/exec'
import * as glob from '@actions/glob'
import path from 'path' import path from 'path'
import fs from 'fs' import fs from 'fs'
import * as params from './input-params' import * as params from './input-params'
import {CacheListener} from './cache-reporting' import {CacheListener} from './cache-reporting'
import {saveCache, restoreCache, cacheDebug, isCacheDebuggingEnabled, tryDelete, generateCacheKey} from './cache-utils' import {saveCache, restoreCache, cacheDebug, isCacheDebuggingEnabled, tryDelete, generateCacheKey} from './cache-utils'
import {GradleHomeEntryExtractor, ConfigurationCacheEntryExtractor} from './cache-extract-entries' import {GradleHomeEntryExtractor} from './cache-extract-entries'
const RESTORED_CACHE_KEY_KEY = 'restored-cache-key' const RESTORED_CACHE_KEY_KEY = 'restored-cache-key'
@@ -81,7 +79,7 @@ export class GradleStateCache {
async afterRestore(listener: CacheListener): Promise<void> { async afterRestore(listener: CacheListener): Promise<void> {
await this.debugReportGradleUserHomeSize('as restored from cache') await this.debugReportGradleUserHomeSize('as restored from cache')
await new GradleHomeEntryExtractor(this.gradleUserHome).restore(listener) await new GradleHomeEntryExtractor(this.gradleUserHome).restore(listener)
await new ConfigurationCacheEntryExtractor(this.gradleUserHome).restore(listener) // await new ConfigurationCacheEntryExtractor(this.gradleUserHome).restore(listener)
await this.debugReportGradleUserHomeSize('after restoring common artifacts') await this.debugReportGradleUserHomeSize('after restoring common artifacts')
} }
@@ -129,10 +127,10 @@ export class GradleStateCache {
*/ */
async beforeSave(listener: CacheListener): Promise<void> { async beforeSave(listener: CacheListener): Promise<void> {
await this.debugReportGradleUserHomeSize('before saving common artifacts') await this.debugReportGradleUserHomeSize('before saving common artifacts')
await this.deleteExcludedPaths() this.deleteExcludedPaths()
await Promise.all([ await Promise.all([
new GradleHomeEntryExtractor(this.gradleUserHome).extract(listener), new GradleHomeEntryExtractor(this.gradleUserHome).extract(listener)
new ConfigurationCacheEntryExtractor(this.gradleUserHome).extract(listener) // new ConfigurationCacheEntryExtractor(this.gradleUserHome).extract(listener)
]) ])
await this.debugReportGradleUserHomeSize( await this.debugReportGradleUserHomeSize(
"after extracting common artifacts (only 'caches' and 'notifications' will be stored)" "after extracting common artifacts (only 'caches' and 'notifications' will be stored)"
@@ -142,21 +140,13 @@ export class GradleStateCache {
/** /**
* Delete any file paths that are excluded by the `gradle-home-cache-excludes` parameter. * Delete any file paths that are excluded by the `gradle-home-cache-excludes` parameter.
*/ */
private async deleteExcludedPaths(): Promise<void> { private deleteExcludedPaths(): void {
const rawPaths: string[] = params.getCacheExcludes() const rawPaths: string[] = params.getCacheExcludes()
rawPaths.push('caches/*/cc-keystore')
const resolvedPaths = rawPaths.map(x => path.resolve(this.gradleUserHome, x)) const resolvedPaths = rawPaths.map(x => path.resolve(this.gradleUserHome, x))
for (const p of resolvedPaths) { for (const p of resolvedPaths) {
cacheDebug(`Removing excluded path: ${p}`) cacheDebug(`Deleting excluded path: ${p}`)
const globber = await glob.create(p, { tryDelete(p)
implicitDescendants: false
})
for (const toDelete of await globber.glob()) {
cacheDebug(`Removing excluded file: ${toDelete}`)
await tryDelete(toDelete)
}
} }
} }

View File

@@ -1,16 +1,14 @@
import path from 'path' import path from 'path'
import fs from 'fs' import fs from 'fs'
import crypto from 'crypto'
import * as core from '@actions/core' import * as core from '@actions/core'
import * as glob from '@actions/glob' import * as glob from '@actions/glob'
import * as semver from 'semver'
import * as params from './input-params' import * as params from './input-params'
import {META_FILE_DIR} from './cache-base' import {META_FILE_DIR} from './cache-base'
import {CacheEntryListener, CacheListener} from './cache-reporting' import {CacheEntryListener, CacheListener} from './cache-reporting'
import {cacheDebug, getCacheKeyPrefix, hashFileNames, restoreCache, saveCache, tryDelete} from './cache-utils' import {cacheDebug, getCacheKeyPrefix, hashFileNames, restoreCache, saveCache, tryDelete} from './cache-utils'
import {BuildResult, loadBuildResults} from './build-results' import {loadBuildResults} from './build-results'
const SKIP_RESTORE_VAR = 'GRADLE_BUILD_ACTION_SKIP_RESTORE' const SKIP_RESTORE_VAR = 'GRADLE_BUILD_ACTION_SKIP_RESTORE'
@@ -48,7 +46,6 @@ class ExtractedCacheEntryDefinition {
pattern: string pattern: string
bundle: boolean bundle: boolean
uniqueFileNames = true uniqueFileNames = true
notCacheableReason: string | undefined
constructor(artifactType: string, pattern: string, bundle: boolean) { constructor(artifactType: string, pattern: string, bundle: boolean) {
this.artifactType = artifactType this.artifactType = artifactType
@@ -56,24 +53,10 @@ class ExtractedCacheEntryDefinition {
this.bundle = bundle this.bundle = bundle
} }
/**
* Indicate that the file names matching the cache entry pattern are NOT sufficient to uniquely identify the contents.
* If the file names are sufficient, then we use a hash of the file names to identify the entry.
* With non-unique-file-names, we hash the file contents to identify the cache entry.
*/
withNonUniqueFileNames(): ExtractedCacheEntryDefinition { withNonUniqueFileNames(): ExtractedCacheEntryDefinition {
this.uniqueFileNames = false this.uniqueFileNames = false
return this return this
} }
/**
* Specify that the cache entry, should not be saved for some reason, even though the contents exist.
* This is used to prevent configuration-cache entries being cached when they were generated by Gradle < 8.6,
*/
notCacheableBecause(reason: string): ExtractedCacheEntryDefinition {
this.notCacheableReason = reason
return this
}
} }
/** /**
@@ -159,11 +142,6 @@ abstract class AbstractEntryExtractor {
const artifactType = cacheEntryDefinition.artifactType const artifactType = cacheEntryDefinition.artifactType
const pattern = cacheEntryDefinition.pattern const pattern = cacheEntryDefinition.pattern
if (cacheEntryDefinition.notCacheableReason) {
listener.entry(pattern).markNotSaved(cacheEntryDefinition.notCacheableReason)
continue
}
// Find all matching files for this cache entry definition // Find all matching files for this cache entry definition
const globber = await glob.create(pattern, { const globber = await glob.create(pattern, {
implicitDescendants: false implicitDescendants: false
@@ -278,7 +256,7 @@ abstract class AbstractEntryExtractor {
} }
const filedata = fs.readFileSync(cacheMetadataFile, 'utf-8') const filedata = fs.readFileSync(cacheMetadataFile, 'utf-8')
cacheDebug(`Loaded cache metadata for ${this.extractorName}: ${filedata}`) cacheDebug(`Loaded cache metadata: ${filedata}`)
const extractedCacheEntryMetadata = JSON.parse(filedata) as ExtractedCacheEntryMetadata const extractedCacheEntryMetadata = JSON.parse(filedata) as ExtractedCacheEntryMetadata
return extractedCacheEntryMetadata.entries return extractedCacheEntryMetadata.entries
} }
@@ -286,12 +264,12 @@ abstract class AbstractEntryExtractor {
/** /**
* Saves information about the extracted cache entries into the 'cache-metadata.json' file. * Saves information about the extracted cache entries into the 'cache-metadata.json' file.
*/ */
protected saveMetadataForCacheResults(results: ExtractedCacheEntry[]): void { private saveMetadataForCacheResults(results: ExtractedCacheEntry[]): void {
const extractedCacheEntryMetadata = new ExtractedCacheEntryMetadata() const extractedCacheEntryMetadata = new ExtractedCacheEntryMetadata()
extractedCacheEntryMetadata.entries = results.filter(x => x.cacheKey !== undefined) extractedCacheEntryMetadata.entries = results.filter(x => x.cacheKey !== undefined)
const filedata = JSON.stringify(extractedCacheEntryMetadata) const filedata = JSON.stringify(extractedCacheEntryMetadata)
cacheDebug(`Saving cache metadata for ${this.extractorName}: ${filedata}`) cacheDebug(`Saving cache metadata: ${filedata}`)
fs.writeFileSync(this.getCacheMetadataFile(), filedata, 'utf-8') fs.writeFileSync(this.getCacheMetadataFile(), filedata, 'utf-8')
} }
@@ -373,104 +351,37 @@ export class ConfigurationCacheEntryExtractor extends AbstractEntryExtractor {
* entry is not reusable. * entry is not reusable.
*/ */
async restore(listener: CacheListener): Promise<void> { async restore(listener: CacheListener): Promise<void> {
if (!listener.fullyRestored) { if (listener.fullyRestored) {
this.markNotRestored(listener, 'Gradle User Home was not fully restored') return super.restore(listener)
return
} }
if (!params.getCacheEncryptionKey()) { core.info('Not restoring configuration-cache state, as Gradle User Home was not fully restored')
this.markNotRestored(listener, 'Encryption Key was not provided') for (const cacheEntry of this.loadExtractedCacheEntries()) {
return listener.entry(cacheEntry.pattern).markRequested('NOT_RESTORED')
} }
const encryptionKey = this.getAESEncryptionKey()
core.exportVariable('GRADLE_ENCRYPTION_KEY', encryptionKey)
return await super.restore(listener)
}
private markNotRestored(listener: CacheListener, reason: string): void {
const cacheEntries = this.loadExtractedCacheEntries()
if (cacheEntries.length > 0) {
core.info(`Not restoring configuration-cache state, as ${reason}`)
for (const cacheEntry of cacheEntries) {
listener.entry(cacheEntry.pattern).markNotRestored(reason)
}
// Update the results file based on no entries restored
this.saveMetadataForCacheResults([])
}
}
async extract(listener: CacheListener): Promise<void> {
if (!params.getCacheEncryptionKey()) {
const cacheEntryDefinitions = this.getExtractedCacheEntryDefinitions()
if (cacheEntryDefinitions.length > 0) {
core.info('Not saving configuration-cache state, as no encryption key was provided')
for (const cacheEntry of cacheEntryDefinitions) {
listener.entry(cacheEntry.pattern).markNotSaved('No encryption key provided')
}
}
return
}
await super.extract(listener)
}
private getAESEncryptionKey(): string | undefined {
const secret = params.getCacheEncryptionKey()
const key = crypto.pbkdf2Sync(secret, '', 1000, 16, 'sha256')
return key.toString('base64')
} }
/** /**
* Extract cache entries for the configuration cache in each project. * Extract cache entries for the configuration cache in each project.
*/ */
protected getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[] { protected getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[] {
// Group BuildResult by existing configCacheDir return this.getProjectRoots().map(projectRoot => {
const groupedResults = this.getConfigCacheDirectoriesWithAssociatedBuildResults() const configCachePath = path.resolve(projectRoot, '.gradle/configuration-cache')
return new ExtractedCacheEntryDefinition(
return Object.entries(groupedResults).map(([configCachePath, pathResults]) => {
// Create a entry definition for each unique configuration cache directory
const definition = new ExtractedCacheEntryDefinition(
'configuration-cache', 'configuration-cache',
configCachePath, configCachePath,
true true
).withNonUniqueFileNames() ).withNonUniqueFileNames()
// If any associated build result used Gradle < 8.6, then mark it as not cacheable
if (
pathResults.find(result => {
const gradleVersion = semver.coerce(result.gradleVersion)
return gradleVersion && semver.lt(gradleVersion, '8.6.0')
})
) {
core.info(
`Not saving config-cache data for ${configCachePath}. Configuration cache data is only saved for Gradle 8.6+`
)
definition.notCacheableBecause('Configuration cache data only saved for Gradle 8.6+')
}
return definition
}) })
} }
private getConfigCacheDirectoriesWithAssociatedBuildResults(): Record<string, BuildResult[]> { /**
return loadBuildResults().reduce( * For every Gradle invocation, we record the project root directory. This method returns the entire
(acc, buildResult) => { * set of project roots, to allow saving of configuration-cache entries for each.
// For each build result, find the config-cache dir */
const configCachePath = path.resolve(buildResult.rootProjectDir, '.gradle/configuration-cache') private getProjectRoots(): string[] {
// Ignore case where config-cache dir doesn't exist const buildResults = loadBuildResults()
if (!fs.existsSync(configCachePath)) { const projectRootDirs = buildResults.map(x => x.rootProjectDir)
return acc return [...new Set(projectRootDirs)] // Remove duplicates
}
// Group by unique config cache directories and collect associated build results
if (!acc[configCachePath]) {
acc[configCachePath] = []
}
acc[configCachePath].push(buildResult)
return acc
},
{} as Record<string, BuildResult[]>
)
} }
} }

View File

@@ -198,9 +198,6 @@ function getSavedMessage(entry: CacheEntryListener, cacheReadOnly: boolean): str
if (cacheReadOnly) { if (cacheReadOnly) {
return '(Entry not saved: cache is read-only)' return '(Entry not saved: cache is read-only)'
} }
if (entry.notRestored) {
return '(Entry not saved: not restored)'
}
return '(Entry not saved: reason unknown)' return '(Entry not saved: reason unknown)'
} }
if (entry.savedSize === 0) { if (entry.savedSize === 0) {

View File

@@ -11,7 +11,7 @@ import * as params from './input-params'
import {CacheEntryListener} from './cache-reporting' import {CacheEntryListener} from './cache-reporting'
const CACHE_PROTOCOL_VERSION = 'v9-' const CACHE_PROTOCOL_VERSION = 'v8-'
const CACHE_KEY_PREFIX_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX' const CACHE_KEY_PREFIX_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX'
const CACHE_KEY_OS_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_ENVIRONMENT' const CACHE_KEY_OS_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_ENVIRONMENT'

View File

@@ -1,7 +1,8 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as artifact from '@actions/artifact'
import * as github from '@actions/github' import * as github from '@actions/github'
import * as glob from '@actions/glob' import * as glob from '@actions/glob'
import {DefaultArtifactClient} from '@actions/artifact' import * as toolCache from '@actions/tool-cache'
import {GitHub} from '@actions/github/lib/utils' import {GitHub} from '@actions/github/lib/utils'
import {RequestError} from '@octokit/request-error' import {RequestError} from '@octokit/request-error'
import type {PullRequestEvent} from '@octokit/webhooks-types' import type {PullRequestEvent} from '@octokit/webhooks-types'
@@ -12,7 +13,7 @@ import fs from 'fs'
import * as layout from './repository-layout' import * as layout from './repository-layout'
import {DependencyGraphOption, getJobMatrix, getArtifactRetentionDays} from './input-params' import {DependencyGraphOption, getJobMatrix, getArtifactRetentionDays} from './input-params'
const DEPENDENCY_GRAPH_PREFIX = 'dependency-graph_' const DEPENDENCY_GRAPH_ARTIFACT = 'dependency-graph'
export async function setup(option: DependencyGraphOption): Promise<void> { export async function setup(option: DependencyGraphOption): Promise<void> {
if (option === DependencyGraphOption.Disabled) { if (option === DependencyGraphOption.Disabled) {
@@ -35,51 +36,47 @@ export async function setup(option: DependencyGraphOption): Promise<void> {
'DEPENDENCY_GRAPH_REPORT_DIR', 'DEPENDENCY_GRAPH_REPORT_DIR',
path.resolve(layout.workspaceDirectory(), 'dependency-graph-reports') path.resolve(layout.workspaceDirectory(), 'dependency-graph-reports')
) )
// To clear the dependency graph, we generate an empty graph by excluding all projects and configurations
if (option === DependencyGraphOption.Clear) {
core.exportVariable('DEPENDENCY_GRAPH_INCLUDE_PROJECTS', '')
core.exportVariable('DEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS', '')
}
} }
export async function complete(option: DependencyGraphOption): Promise<void> { export async function complete(option: DependencyGraphOption): Promise<void> {
try {
switch (option) { switch (option) {
case DependencyGraphOption.Disabled: case DependencyGraphOption.Disabled:
case DependencyGraphOption.Generate: // Performed via init-script: nothing to do here
case DependencyGraphOption.DownloadAndSubmit: // Performed in setup case DependencyGraphOption.DownloadAndSubmit: // Performed in setup
return return
case DependencyGraphOption.GenerateAndSubmit: case DependencyGraphOption.Generate:
await submitDependencyGraphs(await findGeneratedDependencyGraphFiles()) await uploadDependencyGraphs()
return
case DependencyGraphOption.GenerateAndSubmit:
case DependencyGraphOption.Clear: // Submit the empty dependency graph
await submitDependencyGraphs(await uploadDependencyGraphs())
return return
case DependencyGraphOption.GenerateAndUpload:
await uploadDependencyGraphs(await findGeneratedDependencyGraphFiles())
}
} catch (e) {
core.warning(`Failed to ${option} dependency graph. Will continue. ${String(e)}`)
} }
} }
async function findGeneratedDependencyGraphFiles(): Promise<string[]> { async function uploadDependencyGraphs(): Promise<string[]> {
const workspaceDirectory = layout.workspaceDirectory() const workspaceDirectory = layout.workspaceDirectory()
return await findDependencyGraphFiles(workspaceDirectory) const graphFiles = await findDependencyGraphFiles(workspaceDirectory)
}
async function uploadDependencyGraphs(dependencyGraphFiles: string[]): Promise<void> { const relativeGraphFiles = graphFiles.map(x => getRelativePathFromWorkspace(x))
const workspaceDirectory = layout.workspaceDirectory() core.info(`Uploading dependency graph files: ${relativeGraphFiles}`)
const artifactClient = new DefaultArtifactClient() const artifactClient = artifact.create()
for (const dependencyGraphFile of dependencyGraphFiles) { artifactClient.uploadArtifact(DEPENDENCY_GRAPH_ARTIFACT, graphFiles, workspaceDirectory, {
const relativePath = getRelativePathFromWorkspace(dependencyGraphFile)
core.info(`Uploading dependency graph file: ${relativePath}`)
const artifactName = `${DEPENDENCY_GRAPH_PREFIX}${path.basename(dependencyGraphFile)}`
await artifactClient.uploadArtifact(artifactName, [dependencyGraphFile], workspaceDirectory, {
retentionDays: getArtifactRetentionDays() retentionDays: getArtifactRetentionDays()
}) })
}
return graphFiles
} }
async function downloadAndSubmitDependencyGraphs(): Promise<void> { async function downloadAndSubmitDependencyGraphs(): Promise<void> {
try { const workspaceDirectory = layout.workspaceDirectory()
await submitDependencyGraphs(await downloadDependencyGraphs()) submitDependencyGraphs(await retrieveDependencyGraphs(workspaceDirectory))
} catch (e) {
core.warning(`Download and submit dependency graph failed. Will continue. ${String(e)}`)
}
} }
async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise<void> { async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise<void> {
@@ -121,37 +118,56 @@ async function submitDependencyGraphFile(jsonFile: string): Promise<void> {
core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`) core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`)
} }
async function downloadDependencyGraphs(): Promise<string[]> { async function retrieveDependencyGraphs(workspaceDirectory: string): Promise<string[]> {
const workspaceDirectory = layout.workspaceDirectory() if (github.context.payload.workflow_run) {
return await retrieveDependencyGraphsForWorkflowRun(github.context.payload.workflow_run.id, workspaceDirectory)
const findBy = github.context.payload.workflow_run
? {
token: getGithubToken(),
workflowRunId: github.context.payload.workflow_run.id,
repositoryName: github.context.repo.repo,
repositoryOwner: github.context.repo.owner
} }
: undefined return retrieveDependencyGraphsForCurrentWorkflow(workspaceDirectory)
}
const artifactClient = new DefaultArtifactClient() async function retrieveDependencyGraphsForWorkflowRun(runId: number, workspaceDirectory: string): Promise<string[]> {
const octokit = getOctokit()
// Find the workflow run artifacts named "dependency-graph"
const artifacts = await octokit.rest.actions.listWorkflowRunArtifacts({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
run_id: runId
})
const matchArtifact = artifacts.data.artifacts.find(candidate => {
return candidate.name === DEPENDENCY_GRAPH_ARTIFACT
})
if (matchArtifact === undefined) {
throw new Error(`Dependency graph artifact not found. Has it been generated by workflow run '${runId}'?`)
}
// Download the dependency-graph artifact
const download = await octokit.rest.actions.downloadArtifact({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip'
})
const downloadBuffer = download.data as ArrayBuffer
const downloadZip = path.resolve(workspaceDirectory, 'dependency-graph.zip')
fs.writeFileSync(downloadZip, Buffer.from(downloadBuffer))
// Expance the dependency-graph zip and locate each dependency-graph JSON file
const extractDir = path.resolve(workspaceDirectory, 'dependency-graph')
const extracted = await toolCache.extractZip(downloadZip, extractDir)
core.info(`Extracted dependency graph artifacts to ${extracted}: ${fs.readdirSync(extracted)}`)
return findDependencyGraphFiles(extracted)
}
async function retrieveDependencyGraphsForCurrentWorkflow(workspaceDirectory: string): Promise<string[]> {
const artifactClient = artifact.create()
const downloadPath = path.resolve(workspaceDirectory, 'dependency-graph') const downloadPath = path.resolve(workspaceDirectory, 'dependency-graph')
await artifactClient.downloadArtifact(DEPENDENCY_GRAPH_ARTIFACT, downloadPath)
const dependencyGraphArtifacts = ( return await findDependencyGraphFiles(downloadPath)
await artifactClient.listArtifacts({
latest: true,
findBy
})
).artifacts.filter(candidate => candidate.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)
} }
async function findDependencyGraphFiles(dir: string): Promise<string[]> { async function findDependencyGraphFiles(dir: string): Promise<string[]> {

View File

@@ -29,10 +29,6 @@ export function isCacheCleanupEnabled(): boolean {
return getBooleanInput('gradle-home-cache-cleanup') return getBooleanInput('gradle-home-cache-cleanup')
} }
export function getCacheEncryptionKey(): string {
return core.getInput('cache-encryption-key')
}
export function getCacheIncludes(): string[] { export function getCacheIncludes(): string[] {
return core.getMultilineInput('gradle-home-cache-includes') return core.getMultilineInput('gradle-home-cache-includes')
} }
@@ -84,13 +80,13 @@ export function getDependencyGraphOption(): DependencyGraphOption {
return DependencyGraphOption.Generate return DependencyGraphOption.Generate
case 'generate-and-submit': case 'generate-and-submit':
return DependencyGraphOption.GenerateAndSubmit return DependencyGraphOption.GenerateAndSubmit
case 'generate-and-upload':
return DependencyGraphOption.GenerateAndUpload
case 'download-and-submit': case 'download-and-submit':
return DependencyGraphOption.DownloadAndSubmit return DependencyGraphOption.DownloadAndSubmit
case 'clear':
return DependencyGraphOption.Clear
} }
throw TypeError( throw TypeError(
`The value '${val} is not valid for 'dependency-graph. Valid values are: [disabled, generate, generate-and-submit, generate-and-upload, download-and-submit]. The default value is 'disabled'.` `The value '${val} is not valid for 'dependency-graph. Valid values are: [disabled, generate-and-upload, generate-and-submit, download-and-submit]. The default value is 'disabled'.`
) )
} }
@@ -125,9 +121,9 @@ function getBooleanInput(paramName: string, paramDefault = false): boolean {
} }
export enum DependencyGraphOption { export enum DependencyGraphOption {
Disabled = 'disabled', Disabled,
Generate = 'generate', Generate,
GenerateAndSubmit = 'generate-and-submit', GenerateAndSubmit,
GenerateAndUpload = 'generate-and-upload', DownloadAndSubmit,
DownloadAndSubmit = 'download-and-submit' Clear
} }

View File

@@ -27,6 +27,7 @@ if (isTopLevelBuild) {
new File(githubOutput) << "dependency-graph-file=${reportFile.absolutePath}\n" new File(githubOutput) << "dependency-graph-file=${reportFile.absolutePath}\n"
} }
println "Generating dependency graph into '${reportFile}'" println "Generating dependency graph into '${reportFile}'"
} }

View File

@@ -46,7 +46,7 @@ export async function complete(): Promise<void> {
core.info('Gradle setup post-action only performed for first gradle-build-action step in workflow.') core.info('Gradle setup post-action only performed for first gradle-build-action step in workflow.')
return return
} }
core.info('In post-action step') core.info('In final post-action step, saving state and writing summary')
const buildResults = loadBuildResults() const buildResults = loadBuildResults()
@@ -63,8 +63,6 @@ export async function complete(): Promise<void> {
} }
await dependencyGraph.complete(params.getDependencyGraphOption()) await dependencyGraph.complete(params.getDependencyGraphOption())
core.info('Completed post-action step')
} }
async function determineGradleUserHome(): Promise<string> { async function determineGradleUserHome(): Promise<string> {

View File

@@ -1,6 +1,6 @@
plugins { plugins {
id "com.gradle.enterprise" version "3.16.1" id "com.gradle.enterprise" version "3.15.1"
id "com.gradle.common-custom-user-data-gradle-plugin" version "1.12.1" id "com.gradle.common-custom-user-data-gradle-plugin" version "1.12"
} }
gradleEnterprise { gradleEnterprise {

View File

@@ -16,8 +16,8 @@ import java.nio.file.Files
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
class BaseInitScriptTest extends Specification { class BaseInitScriptTest extends Specification {
static final String GE_PLUGIN_VERSION = '3.16.1' static final String GE_PLUGIN_VERSION = '3.15.1'
static final String CCUD_PLUGIN_VERSION = '1.12.1' static final String CCUD_PLUGIN_VERSION = '1.12'
static final TestGradleVersion GRADLE_3_X = new TestGradleVersion(GradleVersion.version('3.5.1'), 7, 9) 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) static final TestGradleVersion GRADLE_4_X = new TestGradleVersion(GradleVersion.version('4.10.3'), 7, 10)

View File

@@ -171,7 +171,7 @@ class TestBuildResultRecorder extends BaseInitScriptTest {
when: when:
settingsFile.text = """ settingsFile.text = """
plugins { plugins {
id 'com.gradle.enterprise' version '3.16.1' apply(false) id 'com.gradle.enterprise' version '3.15.1' apply(false)
} }
gradle.settingsEvaluated { gradle.settingsEvaluated {
apply plugin: 'com.gradle.enterprise' apply plugin: 'com.gradle.enterprise'