mirror of
				https://github.com/gradle/gradle-build-action.git
				synced 2025-11-04 09:58:56 +08:00 
			
		
		
		
	First cut at actions for dependency-graph
- Dependency graph init-script references published version of plugin jar.
- `dependency-graph-generate` action will:
     - Provision Gradle if required
     - Execute Gradle with dependency-graph plugin to generate graph JSON
     - Upload dependency-graph JSON file as workflow artifact
- `dependency-graph-submit` action will:
    - Download dependency-graph JSON artifact
    - Submit the graph via the GitHub dependency submission API
			
			
This commit is contained in:
		
							
								
								
									
										24
									
								
								src/dependency-graph-generate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/dependency-graph-generate.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
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()
 | 
			
		||||
							
								
								
									
										16
									
								
								src/dependency-graph-submit.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/dependency-graph-submit.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
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()
 | 
			
		||||
							
								
								
									
										155
									
								
								src/dependency-graph.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								src/dependency-graph.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,155 @@
 | 
			
		||||
import * as core from '@actions/core'
 | 
			
		||||
import * as artifact from '@actions/artifact'
 | 
			
		||||
import * as github from '@actions/github'
 | 
			
		||||
import * as glob from '@actions/glob'
 | 
			
		||||
import * as toolCache from '@actions/tool-cache'
 | 
			
		||||
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'
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
    const initScript = path.resolve(
 | 
			
		||||
        __dirname,
 | 
			
		||||
        '..',
 | 
			
		||||
        '..',
 | 
			
		||||
        'src',
 | 
			
		||||
        'resources',
 | 
			
		||||
        'init-scripts',
 | 
			
		||||
        'github-dependency-graph.init.gradle'
 | 
			
		||||
    )
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
    const artifactClient = artifact.create()
 | 
			
		||||
    artifactClient.uploadArtifact(DEPENDENCY_GRAPH_ARTIFACT, [dependencyGraphJson], workspaceDirectory)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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> {
 | 
			
		||||
    const workspaceDirectory = layout.workspaceDirectory()
 | 
			
		||||
    const octokit: Octokit = getOctokit()
 | 
			
		||||
 | 
			
		||||
    for (const jsonFile of await retrieveDependencyGraphs(octokit, workspaceDirectory)) {
 | 
			
		||||
        const jsonContent = fs.readFileSync(jsonFile, 'utf8')
 | 
			
		||||
 | 
			
		||||
        const jsonObject = JSON.parse(jsonContent)
 | 
			
		||||
        jsonObject.owner = github.context.repo.owner
 | 
			
		||||
        jsonObject.repo = github.context.repo.repo
 | 
			
		||||
        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[]> {
 | 
			
		||||
    if (github.context.payload.workflow_run) {
 | 
			
		||||
        return await retrieveDependencyGraphsForWorkflowRun(
 | 
			
		||||
            github.context.payload.workflow_run.id,
 | 
			
		||||
            octokit,
 | 
			
		||||
            workspaceDirectory
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    return retrieveDependencyGraphsForCurrentWorkflow(workspaceDirectory)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function retrieveDependencyGraphsForWorkflowRun(
 | 
			
		||||
    runId: number,
 | 
			
		||||
    octokit: Octokit,
 | 
			
		||||
    workspaceDirectory: string
 | 
			
		||||
): Promise<string[]> {
 | 
			
		||||
    // 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')
 | 
			
		||||
    await artifactClient.downloadArtifact(DEPENDENCY_GRAPH_ARTIFACT, downloadPath)
 | 
			
		||||
    return await findDependencyGraphFiles(downloadPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getOctokit(): Octokit {
 | 
			
		||||
    return new Octokit({
 | 
			
		||||
        auth: getGithubToken()
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getGithubToken(): string {
 | 
			
		||||
    return core.getInput('github-token', {required: true})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getRelativePathFromWorkspace(file: string): string {
 | 
			
		||||
    const workspaceDirectory = layout.workspaceDirectory()
 | 
			
		||||
    return path.relative(workspaceDirectory, file)
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
import org.gradle.github.GitHubDependencyGraphPlugin
 | 
			
		||||
initscript {
 | 
			
		||||
  repositories {
 | 
			
		||||
    maven {
 | 
			
		||||
      url = uri("https://plugins.gradle.org/m2/")
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  dependencies {
 | 
			
		||||
    classpath("org.gradle:github-dependency-graph-gradle-plugin:+")
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
apply plugin: GitHubDependencyGraphPlugin
 | 
			
		||||
		Reference in New Issue
	
	Block a user