mirror of
				https://github.com/gradle/gradle-build-action.git
				synced 2025-10-31 15:18:57 +08:00 
			
		
		
		
	Adapt dependency-graph support for new artifact API
- Don't upload artifacts when using 'generate-and-submit' - New option 'generate-and-upload' to be used with 'download-and-submit' - Use Artifact API for downloading in the same and different workflow
This commit is contained in:
		| @@ -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 |         dependency-graph: generate-and-upload | ||||||
|     - 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, kotlin-generate] |     needs: [groovy-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 |         dependency-graph: generate-and-submit | ||||||
|     - 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 | ||||||
|   | |||||||
| @@ -64,7 +64,7 @@ inputs: | |||||||
|     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' 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', 'generate-and-upload' and 'download-and-submit'. | ||||||
|     required: false |     required: false | ||||||
|     default: 'disabled' |     default: 'disabled' | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,8 +1,7 @@ | |||||||
| 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 * as toolCache from '@actions/tool-cache' | import {DefaultArtifactClient} from '@actions/artifact' | ||||||
| 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' | ||||||
| @@ -13,7 +12,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_ARTIFACT = 'dependency-graph' | const DEPENDENCY_GRAPH_PREFIX = '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) { | ||||||
| @@ -39,37 +38,48 @@ export async function setup(option: DependencyGraphOption): Promise<void> { | |||||||
| } | } | ||||||
|  |  | ||||||
| export async function complete(option: DependencyGraphOption): Promise<void> { | export async function complete(option: DependencyGraphOption): Promise<void> { | ||||||
|     switch (option) { |     try { | ||||||
|         case DependencyGraphOption.Disabled: |         switch (option) { | ||||||
|         case DependencyGraphOption.DownloadAndSubmit: // Performed in setup |             case DependencyGraphOption.Disabled: | ||||||
|             return |             case DependencyGraphOption.Generate: // Performed via init-script: nothing to do here | ||||||
|         case DependencyGraphOption.Generate: |             case DependencyGraphOption.DownloadAndSubmit: // Performed in setup | ||||||
|             await uploadDependencyGraphs() |                 return | ||||||
|             return |             case DependencyGraphOption.GenerateAndSubmit: | ||||||
|         case DependencyGraphOption.GenerateAndSubmit: |                 await submitDependencyGraphs(await findGeneratedDependencyGraphFiles()) | ||||||
|             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 uploadDependencyGraphs(): Promise<string[]> { | async function findGeneratedDependencyGraphFiles(): Promise<string[]> { | ||||||
|     const workspaceDirectory = layout.workspaceDirectory() |     const workspaceDirectory = layout.workspaceDirectory() | ||||||
|     const graphFiles = await findDependencyGraphFiles(workspaceDirectory) |     return await findDependencyGraphFiles(workspaceDirectory) | ||||||
|  | } | ||||||
|  |  | ||||||
|     const relativeGraphFiles = graphFiles.map(x => getRelativePathFromWorkspace(x)) | async function uploadDependencyGraphs(dependencyGraphFiles: string[]): Promise<void> { | ||||||
|     core.info(`Uploading dependency graph files: ${relativeGraphFiles}`) |     const workspaceDirectory = layout.workspaceDirectory() | ||||||
|  |  | ||||||
|     const artifactClient = artifact.create() |     const artifactClient = new DefaultArtifactClient() | ||||||
|     artifactClient.uploadArtifact(DEPENDENCY_GRAPH_ARTIFACT, graphFiles, workspaceDirectory, { |     for (const dependencyGraphFile of dependencyGraphFiles) { | ||||||
|         retentionDays: getArtifactRetentionDays() |         const relativePath = getRelativePathFromWorkspace(dependencyGraphFile) | ||||||
|     }) |         core.info(`Uploading dependency graph file: ${relativePath}`) | ||||||
|  |         const artifactName = `${DEPENDENCY_GRAPH_PREFIX}${path.basename(dependencyGraphFile)}` | ||||||
|     return graphFiles |         await artifactClient.uploadArtifact(artifactName, [dependencyGraphFile], workspaceDirectory, { | ||||||
|  |             retentionDays: getArtifactRetentionDays() | ||||||
|  |         }) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| async function downloadAndSubmitDependencyGraphs(): Promise<void> { | async function downloadAndSubmitDependencyGraphs(): Promise<void> { | ||||||
|     const workspaceDirectory = layout.workspaceDirectory() |     try { | ||||||
|     submitDependencyGraphs(await retrieveDependencyGraphs(workspaceDirectory)) |         await submitDependencyGraphs(await downloadDependencyGraphs()) | ||||||
|  |     } 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> { | ||||||
| @@ -111,56 +121,37 @@ async function submitDependencyGraphFile(jsonFile: string): Promise<void> { | |||||||
|     core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`) |     core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`) | ||||||
| } | } | ||||||
|  |  | ||||||
| async function retrieveDependencyGraphs(workspaceDirectory: string): Promise<string[]> { | async function downloadDependencyGraphs(): Promise<string[]> { | ||||||
|     if (github.context.payload.workflow_run) { |     const workspaceDirectory = layout.workspaceDirectory() | ||||||
|         return await retrieveDependencyGraphsForWorkflowRun(github.context.payload.workflow_run.id, workspaceDirectory) |  | ||||||
|     } |  | ||||||
|     return retrieveDependencyGraphsForCurrentWorkflow(workspaceDirectory) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function retrieveDependencyGraphsForWorkflowRun(runId: number, workspaceDirectory: string): Promise<string[]> { |     const findBy = github.context.payload.workflow_run | ||||||
|     const octokit = getOctokit() |         ? { | ||||||
|  |               token: getGithubToken(), | ||||||
|  |               workflowRunId: github.context.payload.workflow_run.id, | ||||||
|  |               repositoryName: github.context.repo.repo, | ||||||
|  |               repositoryOwner: github.context.repo.owner | ||||||
|  |           } | ||||||
|  |         : undefined | ||||||
|  |  | ||||||
|     // Find the workflow run artifacts named "dependency-graph" |     const artifactClient = new DefaultArtifactClient() | ||||||
|     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) |  | ||||||
|     return await findDependencyGraphFiles(downloadPath) |     const dependencyGraphArtifacts = ( | ||||||
|  |         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[]> { | ||||||
|   | |||||||
| @@ -84,11 +84,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 | ||||||
|     } |     } | ||||||
|     throw TypeError( |     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'.` |         `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'.` | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -123,8 +125,9 @@ function getBooleanInput(paramName: string, paramDefault = false): boolean { | |||||||
| } | } | ||||||
|  |  | ||||||
| export enum DependencyGraphOption { | export enum DependencyGraphOption { | ||||||
|     Disabled, |     Disabled = 'disabled', | ||||||
|     Generate, |     Generate = 'generate', | ||||||
|     GenerateAndSubmit, |     GenerateAndSubmit = 'generate-and-submit', | ||||||
|     DownloadAndSubmit |     GenerateAndUpload = 'generate-and-upload', | ||||||
|  |     DownloadAndSubmit = 'download-and-submit' | ||||||
| } | } | ||||||
|   | |||||||
| @@ -27,7 +27,6 @@ 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}'" | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user