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 { 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 { 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 { 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 { 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 { // 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 { 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) }