mirror of
				https://github.com/gradle/gradle-build-action.git
				synced 2025-11-04 01:28:56 +08:00 
			
		
		
		
	Add basic support for GitHub Dependency Graph (#782)
This commit is contained in:
		@@ -12,6 +12,7 @@
 | 
			
		||||
      "import/no-namespace": "off",
 | 
			
		||||
      "i18n-text/no-en": "off",
 | 
			
		||||
      "no-unused-vars": "off",
 | 
			
		||||
      "no-shadow": "off",
 | 
			
		||||
      "sort-imports": "off",
 | 
			
		||||
      "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
 | 
			
		||||
      "@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
 | 
			
		||||
@@ -30,6 +31,7 @@
 | 
			
		||||
      "@typescript-eslint/no-misused-new": "error",
 | 
			
		||||
      "@typescript-eslint/no-namespace": "error", 
 | 
			
		||||
      "@typescript-eslint/no-non-null-assertion": "off",
 | 
			
		||||
      "@typescript-eslint/no-shadow": "error",
 | 
			
		||||
      "@typescript-eslint/no-unnecessary-qualifier": "error",
 | 
			
		||||
      "@typescript-eslint/no-unnecessary-type-assertion": "error",
 | 
			
		||||
      "@typescript-eslint/no-useless-constructor": "error",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								.github/workflows/ci-full-check.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/ci-full-check.yml
									
									
									
									
										vendored
									
									
								
							@@ -29,6 +29,11 @@ jobs:
 | 
			
		||||
    with:
 | 
			
		||||
      cache-key-prefix: ${{github.run_number}}-
 | 
			
		||||
 | 
			
		||||
  dependency-graph:
 | 
			
		||||
    uses: ./.github/workflows/integ-test-dependency-graph.yml
 | 
			
		||||
    with:
 | 
			
		||||
      cache-key-prefix: ${{github.run_number}}-
 | 
			
		||||
 | 
			
		||||
  execution-with-caching:
 | 
			
		||||
    uses: ./.github/workflows/integ-test-execution-with-caching.yml
 | 
			
		||||
    with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								.github/workflows/ci-quick-check.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/ci-quick-check.yml
									
									
									
									
										vendored
									
									
								
							@@ -50,6 +50,13 @@ jobs:
 | 
			
		||||
      runner-os: '["ubuntu-latest"]'
 | 
			
		||||
      download-dist: true
 | 
			
		||||
 | 
			
		||||
  dependency-graph:
 | 
			
		||||
    needs: build-distribution
 | 
			
		||||
    uses: ./.github/workflows/integ-test-dependency-graph.yml
 | 
			
		||||
    with:
 | 
			
		||||
      runner-os: '["ubuntu-latest"]'
 | 
			
		||||
      download-dist: true
 | 
			
		||||
 | 
			
		||||
  execution-with-caching:
 | 
			
		||||
    needs: build-distribution
 | 
			
		||||
    uses: ./.github/workflows/integ-test-execution-with-caching.yml
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								.github/workflows/integ-test-dependency-graph.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								.github/workflows/integ-test-dependency-graph.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
name: Test execution with caching
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  workflow_call:
 | 
			
		||||
    inputs:
 | 
			
		||||
      cache-key-prefix:
 | 
			
		||||
        type: string
 | 
			
		||||
      runner-os:
 | 
			
		||||
        type: string
 | 
			
		||||
        default: '["ubuntu-latest", "windows-latest", "macos-latest"]'
 | 
			
		||||
      download-dist:
 | 
			
		||||
        type: boolean
 | 
			
		||||
        default: false
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  DOWNLOAD_DIST: ${{ inputs.download-dist }}
 | 
			
		||||
  GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: dependency-graph-${{ inputs.cache-key-prefix }}
 | 
			
		||||
  GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  groovy-generate:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: ${{fromJSON(inputs.runner-os)}}
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout sources
 | 
			
		||||
      uses: actions/checkout@v3
 | 
			
		||||
    - name: Download distribution if required
 | 
			
		||||
      uses: ./.github/actions/download-dist
 | 
			
		||||
    - name: Setup Gradle for dependency-graph generate
 | 
			
		||||
      uses: ./
 | 
			
		||||
      with:
 | 
			
		||||
        dependency-graph: generate
 | 
			
		||||
    - name: Run gradle build
 | 
			
		||||
      run: ./gradlew build
 | 
			
		||||
      working-directory: .github/workflow-samples/groovy-dsl
 | 
			
		||||
 | 
			
		||||
  kotlin-generate:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: ${{fromJSON(inputs.runner-os)}}
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout sources
 | 
			
		||||
      uses: actions/checkout@v3
 | 
			
		||||
    - name: Download distribution if required
 | 
			
		||||
      uses: ./.github/actions/download-dist
 | 
			
		||||
    - name: Setup Gradle for dependency-graph generate
 | 
			
		||||
      uses: ./
 | 
			
		||||
      with:
 | 
			
		||||
        dependency-graph: generate-and-submit
 | 
			
		||||
    - name: Run gradle build
 | 
			
		||||
      run: ./gradlew build
 | 
			
		||||
      working-directory: .github/workflow-samples/kotlin-dsl
 | 
			
		||||
  
 | 
			
		||||
  submit:
 | 
			
		||||
    needs: [groovy-generate, kotlin-generate]
 | 
			
		||||
    runs-on: "ubuntu-latest"
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout sources
 | 
			
		||||
      uses: actions/checkout@v3
 | 
			
		||||
    - name: Download distribution if required
 | 
			
		||||
      uses: ./.github/actions/download-dist
 | 
			
		||||
    - name: Submit dependency graphs
 | 
			
		||||
      uses: ./
 | 
			
		||||
      with:
 | 
			
		||||
        dependency-graph: download-and-submit
 | 
			
		||||
							
								
								
									
										117
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										117
									
								
								README.md
									
									
									
									
									
								
							@@ -408,3 +408,120 @@ You can use the `gradle-build-action` on GitHub Enterprise Server, and benefit f
 | 
			
		||||
- Easily run your build with different versions of Gradle
 | 
			
		||||
- Save/restore of Gradle User Home (requires GHES v3.5+ : GitHub Actions cache was introduced in GHES 3.5)
 | 
			
		||||
- Support for GitHub Actions Job Summary (requires GHES 3.6+ : GitHub Actions Job Summary support was introduced in GHES 3.6). In earlier versions of GHES the build-results summary and caching report will be written to the workflow log, as part of the post-action step.
 | 
			
		||||
 | 
			
		||||
# GitHub Dependency Graph support (Experimental)
 | 
			
		||||
 | 
			
		||||
The `gradle-build-action` has experimental support for submitting a [GitHub Dependency Graph](https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-the-dependency-graph) snapshot via the [GitHub Dependency Submission API](https://docs.github.com/en/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28). 
 | 
			
		||||
 | 
			
		||||
The dependency graph snapshot is generated via integration with the [GitHub Dependency Graph Gradle Plugin](https://plugins.gradle.org/plugin/org.gradle.github-dependency-graph-gradle-plugin), and saved as a workflow artifact. The generated snapshot files can be submitted either in the same job, or in a subsequent job (in the same or a dependent workflow).
 | 
			
		||||
 | 
			
		||||
You enable GitHub Dependency Graph support by setting the `dependency-graph` action parameter. Valid values are:
 | 
			
		||||
 | 
			
		||||
|<div style="width:290px">Option</div> | Behaviour |
 | 
			
		||||
| --- |---|
 | 
			
		||||
| `disabled`           | Do not generate a dependency graph for any build invocations.<p>This is the default. |
 | 
			
		||||
| `generate`           | Generate a dependency graph snapshot for each build invocation, saving as a workflow artifact. |
 | 
			
		||||
| `generate-and-submit` | As per `generate`, but any generated dependency graph snapshots will be submitted at the end of the job. |
 | 
			
		||||
| `download-and-submit` | Download any previously saved dependency graph snapshots, submitting them via the Dependency Submission API. This can be useful to collect all snapshots in a matrix of builds and submit them in one step. |
 | 
			
		||||
 | 
			
		||||
- 'disabled': Do not generate a dependency graph for any build invocations. This is the default.
 | 
			
		||||
- 'generate': Generate a dependency graph snapshot for each build invocation, saving as a workflow artifact.
 | 
			
		||||
- 'generate-and-submit': As per 'generate', but any generated dependency graph snapshots will be submitted at the end of the job.
 | 
			
		||||
- 'download-and-submit': Download any previously saved dependency graph snapshots, submitting them via the Dependency Submission API. This can be useful to collect all snapshots in a matrix of builds and submit them in one step.
 | 
			
		||||
 | 
			
		||||
Dependency Graph _submission_ (but not generation) requires the `contents: write` permission, which may need to be explicitly enabled in the workflow file.
 | 
			
		||||
 | 
			
		||||
Example of a simple workflow that generates and submits a dependency graph:
 | 
			
		||||
```yaml
 | 
			
		||||
name: Submit dependency graph
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
  
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: write
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Setup Gradle to generate and submit dependency graphs
 | 
			
		||||
      uses: gradle/gradle-build-action@dependency-graph
 | 
			
		||||
      with:
 | 
			
		||||
        dependency-graph: generate-and-submit
 | 
			
		||||
    - name: Run a build, generating the dependency graph snapshot which will be submitted
 | 
			
		||||
      run: ./gradlew build
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Running multiple builds in a single Job
 | 
			
		||||
 | 
			
		||||
GitHub tracks dependency snapshots based on the `job.correlator` value that is embedded in the snapshot. When a newer snapshot for an existing correlator is submitted, the previous snapshot is replaced. Snapshots with different `job.correlator` values are additive to the overall dependency graph for the repository.
 | 
			
		||||
 | 
			
		||||
The `gradle-build-action` will generate a `job.correlator` value based on the workflow name, job id and matrix values. However, if your job steps contains multiple Gradle invocations, then a unique correlator value must be assigned to each. You assign a correlator by setting the `GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR` environment variable.
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
name: dependency-graph
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Setup Gradle to generate and submit dependency graphs
 | 
			
		||||
      uses: gradle/gradle-build-action@dependency-graph
 | 
			
		||||
      with:
 | 
			
		||||
        dependency-graph: generate-and-submit
 | 
			
		||||
    - name: Run first build using the default job correlator 'dependency-graph-build'
 | 
			
		||||
      run: ./gradlew build
 | 
			
		||||
    - name: Run second build providing a unique job correlator
 | 
			
		||||
      run: ./gradlew test
 | 
			
		||||
      env:
 | 
			
		||||
         GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR: dependency-graph-test
 | 
			
		||||
      
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Dependency snapshots generated for pull requests
 | 
			
		||||
 | 
			
		||||
This `contents: write` permission is not available for any workflow that is triggered by a pull request submitted from a forked repository, since it would permit a malicious pull request to make repository changes. 
 | 
			
		||||
 | 
			
		||||
Because of this restriction, it is not possible to `generate-and-submit` a dependency graph generated for a pull-request that comes from a repository fork. In order to do so, 2 workflows will be required:
 | 
			
		||||
1. The first workflow runs directly against the pull request sources and will generate the dependency graph snapshot.
 | 
			
		||||
2. The second workflow is triggered on `workflow_run` of the first workflow, and will submit the previously saved dependency snapshots.
 | 
			
		||||
 | 
			
		||||
Note: when `download-and-submit` is used in a workflow triggered via [workflow_run](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run), the action will download snapshots saved in the triggering workflow.
 | 
			
		||||
 | 
			
		||||
***Main workflow file***
 | 
			
		||||
```yaml
 | 
			
		||||
name: run-build-and-generate-dependency-snapshot
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
    - name: Setup Gradle to generate and submit dependency graphs
 | 
			
		||||
      uses: gradle/gradle-build-action@v2
 | 
			
		||||
      with:
 | 
			
		||||
        dependency-graph: generate # Only generate in this job
 | 
			
		||||
    - name: Run a build, generating the dependency graph snapshot which will be submitted
 | 
			
		||||
      run: ./gradlew build
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
***Dependent workflow file***
 | 
			
		||||
```yaml
 | 
			
		||||
name: submit-dependency-snapshot
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  workflow_run:
 | 
			
		||||
    workflows: ['run-build-and-generate-dependency-snapshot']
 | 
			
		||||
    types: [completed]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  submit-snapshots:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Retrieve dependency graph artifact and submit
 | 
			
		||||
        uses: gradle/gradle-build-action@v2
 | 
			
		||||
      with:
 | 
			
		||||
        dependency-graph: download-and-submit
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								action.yml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								action.yml
									
									
									
									
									
								
							@@ -58,6 +58,11 @@ inputs:
 | 
			
		||||
    required: false
 | 
			
		||||
    default: true
 | 
			
		||||
 | 
			
		||||
  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' and 'download-and-submit'.
 | 
			
		||||
    required: false
 | 
			
		||||
    default: 'disabled'
 | 
			
		||||
 | 
			
		||||
  # EXPERIMENTAL & INTERNAL ACTION INPUTS
 | 
			
		||||
  # 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`.
 | 
			
		||||
@@ -75,6 +80,11 @@ inputs:
 | 
			
		||||
    required: false
 | 
			
		||||
    default: false
 | 
			
		||||
 | 
			
		||||
  github-token:
 | 
			
		||||
    description: The GitHub token used to authenticate when submitting via the Dependency Submission API.
 | 
			
		||||
    default: ${{ github.token }}
 | 
			
		||||
    required: false
 | 
			
		||||
 | 
			
		||||
outputs:
 | 
			
		||||
  build-scan-url:
 | 
			
		||||
    description: Link to the build scan if any
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								actions/clear-dependency-graph/action.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								actions/clear-dependency-graph/action.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
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
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
name: "Dependency Graph Generate"
 | 
			
		||||
description: Calculates the complete dependency graph for the repository, saving it as a JSON artifact.
 | 
			
		||||
 | 
			
		||||
inputs:
 | 
			
		||||
  gradle-version:
 | 
			
		||||
    description: Gradle version to use. If specified, this Gradle version will be downloaded, added to the PATH and used for invoking Gradle.
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
runs:
 | 
			
		||||
  using: 'node16'
 | 
			
		||||
  main: '../../dist/dependency-graph-generate/index.js'
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
name: "Dependency Graph Submit"
 | 
			
		||||
description: Retrieves a previously created dependency graph JSON and submits via the GitHub Dependency Submission API.
 | 
			
		||||
 | 
			
		||||
inputs:
 | 
			
		||||
  github-token:
 | 
			
		||||
    description: The GitHub token used to authenticate when submitting via the Dependency Submission API.
 | 
			
		||||
    default: ${{ github.token }}
 | 
			
		||||
    required: false
 | 
			
		||||
 | 
			
		||||
runs:
 | 
			
		||||
  using: 'node16'
 | 
			
		||||
  main: '../../dist/dependency-graph-submit/index.js'
 | 
			
		||||
							
								
								
									
										8367
									
								
								dist/main/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8367
									
								
								dist/main/index.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								dist/main/index.js.map
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/main/index.js.map
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										9264
									
								
								dist/post/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9264
									
								
								dist/post/index.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								dist/post/index.js.map
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/post/index.js.map
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -11,9 +11,7 @@
 | 
			
		||||
 | 
			
		||||
    "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-dependency-graph-generate": "ncc build src/dependency-graph-generate.ts --out dist/dependency-graph-generate --source-map --no-source-map-register",
 | 
			
		||||
    "compile-dependency-graph-submit": "ncc build src/dependency-graph-submit.ts --out dist/dependency-graph-submit --source-map --no-source-map-register",
 | 
			
		||||
    "compile": "npm run compile-main && npm run compile-post && npm run compile-dependency-graph-generate && npm run compile-dependency-graph-submit",
 | 
			
		||||
    "compile": "npm run compile-main && npm run compile-post",
 | 
			
		||||
 | 
			
		||||
    "test": "jest",
 | 
			
		||||
    "check": "npm run format && npm run lint",
 | 
			
		||||
 
 | 
			
		||||
@@ -172,7 +172,12 @@ export class GradleStateCache {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private initializeGradleUserHome(gradleUserHome: string, initScriptsDir: string): void {
 | 
			
		||||
        const initScriptFilenames = ['build-result-capture.init.gradle', 'build-result-capture-service.plugin.groovy']
 | 
			
		||||
        const initScriptFilenames = [
 | 
			
		||||
            'build-result-capture.init.gradle',
 | 
			
		||||
            'build-result-capture-service.plugin.groovy',
 | 
			
		||||
            'github-dependency-graph.init.gradle',
 | 
			
		||||
            'github-dependency-graph-gradle-plugin-apply.groovy'
 | 
			
		||||
        ]
 | 
			
		||||
        for (const initScriptFilename of initScriptFilenames) {
 | 
			
		||||
            const initScriptContent = this.readInitScriptAsString(initScriptFilename)
 | 
			
		||||
            const initScriptPath = path.resolve(initScriptsDir, initScriptFilename)
 | 
			
		||||
 
 | 
			
		||||
@@ -125,10 +125,25 @@ function getCacheKeyJobInstance(): string {
 | 
			
		||||
 | 
			
		||||
    // By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation
 | 
			
		||||
    // The only way we can obtain the `matrix` data is via the `workflow-job-context` parameter in action.yml.
 | 
			
		||||
    const workflowJobContext = params.getJobContext()
 | 
			
		||||
    const workflowJobContext = params.getJobMatrix()
 | 
			
		||||
    return hashStrings([workflowJobContext])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getUniqueLabelForJobInstance(): string {
 | 
			
		||||
    return getUniqueLabelForJobInstanceValues(github.context.workflow, github.context.job, params.getJobMatrix())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getUniqueLabelForJobInstanceValues(workflow: string, jobId: string, matrixJson: string): string {
 | 
			
		||||
    const matrix = JSON.parse(matrixJson)
 | 
			
		||||
    const matrixString = Object.values(matrix).join('-')
 | 
			
		||||
    const label = matrixString ? `${workflow}-${jobId}-${matrixString}` : `${workflow}-${jobId}`
 | 
			
		||||
    return sanitize(label)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sanitize(value: string): string {
 | 
			
		||||
    return value.replace(/[^a-zA-Z0-9_-]/g, '').toLowerCase()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getCacheKeyJobExecution(): string {
 | 
			
		||||
    // Used to associate a cache key with a particular execution (default is bound to the git commit sha)
 | 
			
		||||
    return process.env[CACHE_KEY_JOB_EXECUTION_VAR] || github.context.sha
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
import * as core from '@actions/core'
 | 
			
		||||
 | 
			
		||||
import * as provisioner from './provision'
 | 
			
		||||
import * as dependencyGraph from './dependency-graph'
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The main entry point for the action, called by Github Actions for the step.
 | 
			
		||||
 */
 | 
			
		||||
export async function run(): Promise<void> {
 | 
			
		||||
    try {
 | 
			
		||||
        // Download and install Gradle if required
 | 
			
		||||
        const executable = await provisioner.provisionGradle()
 | 
			
		||||
 | 
			
		||||
        // Generate and upload dependency graph artifact
 | 
			
		||||
        await dependencyGraph.generateDependencyGraph(executable)
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        core.setFailed(String(error))
 | 
			
		||||
        if (error instanceof Error && error.stack) {
 | 
			
		||||
            core.info(error.stack)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
run()
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
import * as core from '@actions/core'
 | 
			
		||||
import * as dependencyGraph from './dependency-graph'
 | 
			
		||||
 | 
			
		||||
export async function run(): Promise<void> {
 | 
			
		||||
    try {
 | 
			
		||||
        // Retrieve the dependency graph artifact and submit via Dependency Submission API
 | 
			
		||||
        await dependencyGraph.submitDependencyGraph()
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        core.setFailed(String(error))
 | 
			
		||||
        if (error instanceof Error && error.stack) {
 | 
			
		||||
            core.info(error.stack)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
run()
 | 
			
		||||
@@ -8,59 +8,64 @@ import {Octokit} from '@octokit/rest'
 | 
			
		||||
import * as path from 'path'
 | 
			
		||||
import fs from 'fs'
 | 
			
		||||
 | 
			
		||||
import * as execution from './execution'
 | 
			
		||||
import * as layout from './repository-layout'
 | 
			
		||||
import {DependencyGraphOption, getJobMatrix} from './input-params'
 | 
			
		||||
 | 
			
		||||
const DEPENDENCY_GRAPH_ARTIFACT = 'dependency-graph'
 | 
			
		||||
const DEPENDENCY_GRAPH_FILE = 'dependency-graph.json'
 | 
			
		||||
 | 
			
		||||
export async function generateDependencyGraph(executable: string | undefined): Promise<void> {
 | 
			
		||||
    const workspaceDirectory = layout.workspaceDirectory()
 | 
			
		||||
    const buildRootDirectory = layout.buildRootDirectory()
 | 
			
		||||
    const buildPath = getRelativePathFromWorkspace(buildRootDirectory)
 | 
			
		||||
export function setup(option: DependencyGraphOption): void {
 | 
			
		||||
    if (option === DependencyGraphOption.Disabled || option === DependencyGraphOption.DownloadAndSubmit) {
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const initScript = path.resolve(
 | 
			
		||||
        __dirname,
 | 
			
		||||
        '..',
 | 
			
		||||
        '..',
 | 
			
		||||
        'src',
 | 
			
		||||
        'resources',
 | 
			
		||||
        'init-scripts',
 | 
			
		||||
        'github-dependency-graph.init.gradle'
 | 
			
		||||
    core.info('Enabling dependency graph generation')
 | 
			
		||||
    const jobCorrelator = getJobCorrelator()
 | 
			
		||||
    core.exportVariable('GITHUB_DEPENDENCY_GRAPH_ENABLED', 'true')
 | 
			
		||||
    core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR', jobCorrelator)
 | 
			
		||||
    core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_ID', github.context.runId)
 | 
			
		||||
    core.exportVariable(
 | 
			
		||||
        'GITHUB_DEPENDENCY_GRAPH_REPORT_DIR',
 | 
			
		||||
        path.resolve(layout.workspaceDirectory(), 'dependency-graph-reports')
 | 
			
		||||
    )
 | 
			
		||||
    const args = [
 | 
			
		||||
        `-Dorg.gradle.github.env.GRADLE_BUILD_PATH=${buildPath}`,
 | 
			
		||||
        '--init-script',
 | 
			
		||||
        initScript,
 | 
			
		||||
        ':GitHubDependencyGraphPlugin_generateDependencyGraph'
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    await execution.executeGradleBuild(executable, buildRootDirectory, args)
 | 
			
		||||
    const dependencyGraphJson = copyDependencyGraphToBuildRoot(buildRootDirectory)
 | 
			
		||||
export async function complete(option: DependencyGraphOption): Promise<void> {
 | 
			
		||||
    switch (option) {
 | 
			
		||||
        case DependencyGraphOption.Disabled:
 | 
			
		||||
            return
 | 
			
		||||
        case DependencyGraphOption.Generate:
 | 
			
		||||
            await uploadDependencyGraphs()
 | 
			
		||||
            return
 | 
			
		||||
        case DependencyGraphOption.GenerateAndSubmit:
 | 
			
		||||
            await submitDependencyGraphs(await uploadDependencyGraphs())
 | 
			
		||||
            return
 | 
			
		||||
        case DependencyGraphOption.DownloadAndSubmit:
 | 
			
		||||
            await downloadAndSubmitDependencyGraphs()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function uploadDependencyGraphs(): Promise<string[]> {
 | 
			
		||||
    const workspaceDirectory = layout.workspaceDirectory()
 | 
			
		||||
    const graphFiles = await findDependencyGraphFiles(workspaceDirectory)
 | 
			
		||||
 | 
			
		||||
    const relativeGraphFiles = graphFiles.map(x => getRelativePathFromWorkspace(x))
 | 
			
		||||
    core.info(`Uploading dependency graph files: ${relativeGraphFiles}`)
 | 
			
		||||
 | 
			
		||||
    const artifactClient = artifact.create()
 | 
			
		||||
    artifactClient.uploadArtifact(DEPENDENCY_GRAPH_ARTIFACT, [dependencyGraphJson], workspaceDirectory)
 | 
			
		||||
    artifactClient.uploadArtifact(DEPENDENCY_GRAPH_ARTIFACT, graphFiles, workspaceDirectory)
 | 
			
		||||
 | 
			
		||||
    return graphFiles
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function copyDependencyGraphToBuildRoot(buildRootDirectory: string): string {
 | 
			
		||||
    const sourceFile = path.resolve(
 | 
			
		||||
        buildRootDirectory,
 | 
			
		||||
        'build',
 | 
			
		||||
        'reports',
 | 
			
		||||
        'github-dependency-graph-plugin',
 | 
			
		||||
        'github-dependency-snapshot.json'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    const destFile = path.resolve(buildRootDirectory, DEPENDENCY_GRAPH_FILE)
 | 
			
		||||
    fs.copyFileSync(sourceFile, destFile)
 | 
			
		||||
    return destFile
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function submitDependencyGraph(): Promise<void> {
 | 
			
		||||
async function downloadAndSubmitDependencyGraphs(): Promise<void> {
 | 
			
		||||
    const workspaceDirectory = layout.workspaceDirectory()
 | 
			
		||||
    submitDependencyGraphs(await retrieveDependencyGraphs(workspaceDirectory))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise<void> {
 | 
			
		||||
    const octokit: Octokit = getOctokit()
 | 
			
		||||
 | 
			
		||||
    for (const jsonFile of await retrieveDependencyGraphs(octokit, workspaceDirectory)) {
 | 
			
		||||
    for (const jsonFile of dependencyGraphFiles) {
 | 
			
		||||
        const jsonContent = fs.readFileSync(jsonFile, 'utf8')
 | 
			
		||||
 | 
			
		||||
        const jsonObject = JSON.parse(jsonContent)
 | 
			
		||||
@@ -69,34 +74,20 @@ export async function submitDependencyGraph(): Promise<void> {
 | 
			
		||||
        const response = await octokit.request('POST /repos/{owner}/{repo}/dependency-graph/snapshots', jsonObject)
 | 
			
		||||
 | 
			
		||||
        const relativeJsonFile = getRelativePathFromWorkspace(jsonFile)
 | 
			
		||||
        core.info(`Submitted ${relativeJsonFile}: ${JSON.stringify(response)}`)
 | 
			
		||||
        core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function findDependencyGraphFiles(dir: string): Promise<string[]> {
 | 
			
		||||
    const globber = await glob.create(`${dir}/**/${DEPENDENCY_GRAPH_FILE}`)
 | 
			
		||||
    const graphFiles = globber.glob()
 | 
			
		||||
    core.info(`Found graph files in ${dir}: ${graphFiles}`)
 | 
			
		||||
    return graphFiles
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function retrieveDependencyGraphs(octokit: Octokit, workspaceDirectory: string): Promise<string[]> {
 | 
			
		||||
async function retrieveDependencyGraphs(workspaceDirectory: string): Promise<string[]> {
 | 
			
		||||
    if (github.context.payload.workflow_run) {
 | 
			
		||||
        return await retrieveDependencyGraphsForWorkflowRun(
 | 
			
		||||
            github.context.payload.workflow_run.id,
 | 
			
		||||
            octokit,
 | 
			
		||||
            workspaceDirectory
 | 
			
		||||
        )
 | 
			
		||||
        return await retrieveDependencyGraphsForWorkflowRun(github.context.payload.workflow_run.id, workspaceDirectory)
 | 
			
		||||
    }
 | 
			
		||||
    return retrieveDependencyGraphsForCurrentWorkflow(workspaceDirectory)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function retrieveDependencyGraphsForWorkflowRun(
 | 
			
		||||
    runId: number,
 | 
			
		||||
    octokit: Octokit,
 | 
			
		||||
    workspaceDirectory: string
 | 
			
		||||
): Promise<string[]> {
 | 
			
		||||
async function retrieveDependencyGraphsForWorkflowRun(runId: number, workspaceDirectory: string): Promise<string[]> {
 | 
			
		||||
    const octokit: Octokit = getOctokit()
 | 
			
		||||
 | 
			
		||||
    // Find the workflow run artifacts named "dependency-graph"
 | 
			
		||||
    const artifacts = await octokit.rest.actions.listWorkflowRunArtifacts({
 | 
			
		||||
        owner: github.context.repo.owner,
 | 
			
		||||
@@ -139,6 +130,12 @@ async function retrieveDependencyGraphsForCurrentWorkflow(workspaceDirectory: st
 | 
			
		||||
    return await findDependencyGraphFiles(downloadPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function findDependencyGraphFiles(dir: string): Promise<string[]> {
 | 
			
		||||
    const globber = await glob.create(`${dir}/dependency-graph-reports/*.json`)
 | 
			
		||||
    const graphFiles = globber.glob()
 | 
			
		||||
    return graphFiles
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getOctokit(): Octokit {
 | 
			
		||||
    return new Octokit({
 | 
			
		||||
        auth: getGithubToken()
 | 
			
		||||
@@ -153,3 +150,29 @@ function getRelativePathFromWorkspace(file: string): string {
 | 
			
		||||
    const workspaceDirectory = layout.workspaceDirectory()
 | 
			
		||||
    return path.relative(workspaceDirectory, file)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getJobCorrelator(): string {
 | 
			
		||||
    return constructJobCorrelator(github.context.workflow, github.context.job, getJobMatrix())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function constructJobCorrelator(workflow: string, jobId: string, matrixJson: string): string {
 | 
			
		||||
    const matrixString = describeMatrix(matrixJson)
 | 
			
		||||
    const label = matrixString ? `${workflow}-${jobId}-${matrixString}` : `${workflow}-${jobId}`
 | 
			
		||||
    return sanitize(label)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function describeMatrix(matrixJson: string): string {
 | 
			
		||||
    core.debug(`Got matrix json: ${matrixJson}`)
 | 
			
		||||
    const matrix = JSON.parse(matrixJson)
 | 
			
		||||
    if (matrix) {
 | 
			
		||||
        return Object.values(matrix).join('-')
 | 
			
		||||
    }
 | 
			
		||||
    return ''
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sanitize(value: string): string {
 | 
			
		||||
    return value
 | 
			
		||||
        .replace(/[^a-zA-Z0-9_-\s]/g, '')
 | 
			
		||||
        .replace(/\s+/g, '_')
 | 
			
		||||
        .toLowerCase()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ export function getArguments(): string[] {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Internal parameters
 | 
			
		||||
export function getJobContext(): string {
 | 
			
		||||
export function getJobMatrix(): string {
 | 
			
		||||
    return core.getInput('workflow-job-context')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -63,6 +63,27 @@ export function isJobSummaryEnabled(): boolean {
 | 
			
		||||
    return getBooleanInput('generate-job-summary', true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function isDependencyGraphEnabled(): boolean {
 | 
			
		||||
    return getBooleanInput('generate-dependency-graph', true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getDependencyGraphOption(): DependencyGraphOption {
 | 
			
		||||
    const val = core.getInput('dependency-graph')
 | 
			
		||||
    switch (val.toLowerCase().trim()) {
 | 
			
		||||
        case 'disabled':
 | 
			
		||||
            return DependencyGraphOption.Disabled
 | 
			
		||||
        case 'generate':
 | 
			
		||||
            return DependencyGraphOption.Generate
 | 
			
		||||
        case 'generate-and-submit':
 | 
			
		||||
            return DependencyGraphOption.GenerateAndSubmit
 | 
			
		||||
        case 'download-and-submit':
 | 
			
		||||
            return DependencyGraphOption.DownloadAndSubmit
 | 
			
		||||
    }
 | 
			
		||||
    throw TypeError(
 | 
			
		||||
        `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'.`
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getBooleanInput(paramName: string, paramDefault = false): boolean {
 | 
			
		||||
    const paramValue = core.getInput(paramName)
 | 
			
		||||
    switch (paramValue.toLowerCase().trim()) {
 | 
			
		||||
@@ -75,3 +96,10 @@ function getBooleanInput(paramName: string, paramDefault = false): boolean {
 | 
			
		||||
    }
 | 
			
		||||
    throw TypeError(`The value '${paramValue} is not valid for '${paramName}. Valid values are: [true, false]`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum DependencyGraphOption {
 | 
			
		||||
    Disabled,
 | 
			
		||||
    Generate,
 | 
			
		||||
    GenerateAndSubmit,
 | 
			
		||||
    DownloadAndSubmit
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
buildscript {
 | 
			
		||||
  repositories {
 | 
			
		||||
    maven { url "https://plugins.gradle.org/m2/" }
 | 
			
		||||
  }
 | 
			
		||||
  dependencies {
 | 
			
		||||
    classpath "org.gradle:github-dependency-graph-gradle-plugin:0.0.3"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
apply plugin: org.gradle.github.GitHubDependencyGraphPlugin
 | 
			
		||||
@@ -1,12 +1,24 @@
 | 
			
		||||
import org.gradle.github.GitHubDependencyGraphPlugin
 | 
			
		||||
initscript {
 | 
			
		||||
  repositories {
 | 
			
		||||
    maven {
 | 
			
		||||
      url = uri("https://plugins.gradle.org/m2/")
 | 
			
		||||
import org.gradle.util.GradleVersion
 | 
			
		||||
 | 
			
		||||
if (System.env.GITHUB_DEPENDENCY_GRAPH_ENABLED != "true") {
 | 
			
		||||
  return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (GradleVersion.current().baseVersion < GradleVersion.version("5.0")) {
 | 
			
		||||
  println "::warning::Dependency Graph is not supported for Gradle versions < 5.0. No dependency snapshot will be generated."
 | 
			
		||||
  return
 | 
			
		||||
}
 | 
			
		||||
  dependencies {
 | 
			
		||||
    classpath("org.gradle:github-dependency-graph-gradle-plugin:+")
 | 
			
		||||
 | 
			
		||||
def reportDir = System.env.GITHUB_DEPENDENCY_GRAPH_REPORT_DIR
 | 
			
		||||
def jobCorrelator = System.env.GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR
 | 
			
		||||
def reportFile = new File(reportDir, jobCorrelator + ".json")
 | 
			
		||||
 | 
			
		||||
if (reportFile.exists()) {
 | 
			
		||||
  println "::warning::No dependency snapshot generated for step: report file for '${jobCorrelator}' created in earlier step. Each build invocation requires a unique job correlator: specify GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR var for this step."
 | 
			
		||||
  return
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
apply plugin: GitHubDependencyGraphPlugin
 | 
			
		||||
 | 
			
		||||
println "Generating dependency graph for '${jobCorrelator}'"
 | 
			
		||||
 | 
			
		||||
// TODO:DAZ This should be conditionally applied, since the script may be present when not required.
 | 
			
		||||
apply from: 'github-dependency-graph-gradle-plugin-apply.groovy'
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import * as os from 'os'
 | 
			
		||||
import * as caches from './caches'
 | 
			
		||||
import * as layout from './repository-layout'
 | 
			
		||||
import * as params from './input-params'
 | 
			
		||||
import * as dependencyGraph from './dependency-graph'
 | 
			
		||||
 | 
			
		||||
import {logJobSummary, writeJobSummary} from './job-summary'
 | 
			
		||||
import {loadBuildResults} from './build-results'
 | 
			
		||||
@@ -36,6 +37,8 @@ export async function setup(): Promise<void> {
 | 
			
		||||
    await caches.restore(gradleUserHome, cacheListener)
 | 
			
		||||
 | 
			
		||||
    core.saveState(CACHE_LISTENER, cacheListener.stringify())
 | 
			
		||||
 | 
			
		||||
    dependencyGraph.setup(params.getDependencyGraphOption())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function complete(): Promise<void> {
 | 
			
		||||
@@ -58,6 +61,8 @@ export async function complete(): Promise<void> {
 | 
			
		||||
    } else {
 | 
			
		||||
        logJobSummary(buildResults, cacheListener)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dependencyGraph.complete(params.getDependencyGraphOption())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function determineGradleUserHome(): Promise<string> {
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,87 @@
 | 
			
		||||
package com.gradle.gradlebuildaction
 | 
			
		||||
 | 
			
		||||
import static org.junit.Assume.assumeTrue
 | 
			
		||||
 | 
			
		||||
class TestDependencyGraph extends BaseInitScriptTest {
 | 
			
		||||
    def initScript = 'github-dependency-graph.init.gradle'
 | 
			
		||||
 | 
			
		||||
    static final List<TestGradleVersion> NO_DEPENDENCY_GRAPH_VERSIONS = [GRADLE_3_X, GRADLE_4_X]
 | 
			
		||||
    static final List<TestGradleVersion> DEPENDENCY_GRAPH_VERSIONS = ALL_VERSIONS - NO_DEPENDENCY_GRAPH_VERSIONS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def "does not produce dependency graph when not enabled"() {
 | 
			
		||||
        assumeTrue testGradleVersion.compatibleWithCurrentJvm
 | 
			
		||||
 | 
			
		||||
        when:
 | 
			
		||||
        run(['help'], initScript, testGradleVersion.gradleVersion)
 | 
			
		||||
 | 
			
		||||
        then:
 | 
			
		||||
        assert !reportsDir.exists()
 | 
			
		||||
 | 
			
		||||
        where:
 | 
			
		||||
        testGradleVersion << ALL_VERSIONS
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def "produces dependency graph when enabled"() {
 | 
			
		||||
        assumeTrue testGradleVersion.compatibleWithCurrentJvm
 | 
			
		||||
 | 
			
		||||
        when:
 | 
			
		||||
        run(['help'], initScript, testGradleVersion.gradleVersion, [], envVars)
 | 
			
		||||
 | 
			
		||||
        then:
 | 
			
		||||
        assert reportFile.exists()
 | 
			
		||||
 | 
			
		||||
        where:
 | 
			
		||||
        testGradleVersion << DEPENDENCY_GRAPH_VERSIONS
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def "warns and produces no dependency graph when enabled for older Gradle versions"() {
 | 
			
		||||
        assumeTrue testGradleVersion.compatibleWithCurrentJvm
 | 
			
		||||
 | 
			
		||||
        when:
 | 
			
		||||
        def result = run(['help'], initScript, testGradleVersion.gradleVersion, [], envVars)
 | 
			
		||||
 | 
			
		||||
        then:
 | 
			
		||||
        assert !reportsDir.exists()
 | 
			
		||||
        assert result.output.contains("::warning::Dependency Graph is not supported")
 | 
			
		||||
 | 
			
		||||
        where:
 | 
			
		||||
        testGradleVersion << NO_DEPENDENCY_GRAPH_VERSIONS
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def "warns and does not overwrite existing report file"() {
 | 
			
		||||
        assumeTrue testGradleVersion.compatibleWithCurrentJvm
 | 
			
		||||
 | 
			
		||||
        when:
 | 
			
		||||
        reportsDir.mkdirs()
 | 
			
		||||
        reportFile << "DUMMY CONTENT"
 | 
			
		||||
        def result = run(['help'], initScript, testGradleVersion.gradleVersion, [], envVars)
 | 
			
		||||
 | 
			
		||||
        then:
 | 
			
		||||
        assert reportFile.text == "DUMMY CONTENT"
 | 
			
		||||
        assert result.output.contains("::warning::No dependency snapshot generated for step")
 | 
			
		||||
 | 
			
		||||
        where:
 | 
			
		||||
        testGradleVersion << DEPENDENCY_GRAPH_VERSIONS
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def getEnvVars() {
 | 
			
		||||
        return [
 | 
			
		||||
            GITHUB_DEPENDENCY_GRAPH_ENABLED: "true",
 | 
			
		||||
            GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR: "CORRELATOR",
 | 
			
		||||
            GITHUB_DEPENDENCY_GRAPH_JOB_ID: "1",
 | 
			
		||||
            GITHUB_DEPENDENCY_GRAPH_REPORT_DIR: reportsDir.absolutePath,
 | 
			
		||||
            GITHUB_REF: "main",
 | 
			
		||||
            GITHUB_SHA: "123456",
 | 
			
		||||
            GITHUB_WORKSPACE: testProjectDir.absolutePath
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def getReportsDir() {
 | 
			
		||||
        return new File(testProjectDir, 'build/reports/github-dependency-graph-snapshots')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def getReportFile() {
 | 
			
		||||
        return new File(reportsDir, "CORRELATOR.json")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								test/jest/dependency-graph.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								test/jest/dependency-graph.test.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
import * as dependencyGraph from '../../src/dependency-graph'
 | 
			
		||||
 | 
			
		||||
describe('dependency-graph', () => {
 | 
			
		||||
    describe('constructs job correlator', () => {
 | 
			
		||||
        it('removes commas from workflow name', () => {
 | 
			
		||||
            const id = dependencyGraph.constructJobCorrelator('Workflow, with,commas', 'jobid', '{}')
 | 
			
		||||
            expect(id).toBe('workflow_withcommas-jobid')
 | 
			
		||||
        })
 | 
			
		||||
        it('removes non word characters', () => {
 | 
			
		||||
            const id = dependencyGraph.constructJobCorrelator('Workflow!_with()characters', 'job-*id', '{"foo": "bar!@#$%^&*("}')
 | 
			
		||||
            expect(id).toBe('workflow_withcharacters-job-id-bar')
 | 
			
		||||
        })
 | 
			
		||||
        it('replaces spaces', () => {
 | 
			
		||||
            const id = dependencyGraph.constructJobCorrelator('Workflow !_ with () characters, and   spaces', 'job-*id', '{"foo": "bar!@#$%^&*("}')
 | 
			
		||||
            expect(id).toBe('workflow___with_characters_and_spaces-job-id-bar')
 | 
			
		||||
        })
 | 
			
		||||
        it('without matrix', () => {
 | 
			
		||||
            const id = dependencyGraph.constructJobCorrelator('workflow', 'jobid', 'null')
 | 
			
		||||
            expect(id).toBe('workflow-jobid')
 | 
			
		||||
        })
 | 
			
		||||
        it('with dashes in values', () => {
 | 
			
		||||
            const id = dependencyGraph.constructJobCorrelator('workflow-name', 'job-id', '{"os": "ubuntu-latest"}')
 | 
			
		||||
            expect(id).toBe('workflow-name-job-id-ubuntu-latest')
 | 
			
		||||
        })
 | 
			
		||||
        it('with single matrix value', () => {
 | 
			
		||||
            const id = dependencyGraph.constructJobCorrelator('workflow', 'jobid', '{"os": "windows"}')
 | 
			
		||||
            expect(id).toBe('workflow-jobid-windows')
 | 
			
		||||
        })
 | 
			
		||||
        it('with composite matrix value', () => {
 | 
			
		||||
            const id = dependencyGraph.constructJobCorrelator('workflow', 'jobid', '{"os": "windows", "java-version": "21.1", "other": "Value, with COMMA"}')
 | 
			
		||||
            expect(id).toBe('workflow-jobid-windows-211-value_with_comma')
 | 
			
		||||
        })
 | 
			
		||||
    })
 | 
			
		||||
})
 | 
			
		||||
		Reference in New Issue
	
	Block a user