Add dependency-graph-continue-on-failure input param

- Translate to env var for init-script support
- Use when deciding whether to log or rethrow errors
- Add a custom error type to trigger failure in post action
This commit is contained in:
daz 2024-01-12 11:15:01 -07:00
parent 369fcc54d8
commit a01f794d92
No known key found for this signature in database
8 changed files with 70 additions and 16 deletions

View file

@ -20,8 +20,6 @@ env:
jobs: jobs:
unsupported-gradle-version-failure: unsupported-gradle-version-failure:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -32,6 +30,7 @@ jobs:
with: with:
gradle-version: 7.0.1 gradle-version: 7.0.1
dependency-graph: generate dependency-graph: generate
dependency-graph-continue-on-failure: false
- name: Run with unsupported Gradle version - name: Run with unsupported Gradle version
working-directory: .github/workflow-samples/groovy-dsl working-directory: .github/workflow-samples/groovy-dsl
run: | run: |
@ -42,8 +41,6 @@ jobs:
unsupported-gradle-version-warning: unsupported-gradle-version-warning:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -54,11 +51,7 @@ jobs:
with: with:
gradle-version: 7.0.1 gradle-version: 7.0.1
dependency-graph: generate dependency-graph: generate
- name: Run with unsupported Gradle version dependency-graph-continue-on-failure: true
working-directory: .github/workflow-samples/groovy-dsl
run: |
gradle help
- name: Run with unsupported Gradle version - name: Run with unsupported Gradle version
working-directory: .github/workflow-samples/groovy-dsl working-directory: .github/workflow-samples/groovy-dsl
run: | run: |

View file

@ -73,6 +73,11 @@ inputs:
required: false required: false
default: 'disabled' default: 'disabled'
dependency-graph-continue-on-failure:
description: When 'false' a failure to generate or submit a dependency graph will fail the Step or Job. When 'true' a warning will be emitted but no failure will result.
required: false
default: true
artifact-retention-days: artifact-retention-days:
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

View file

@ -10,7 +10,13 @@ import * as path from 'path'
import fs from 'fs' import fs from 'fs'
import * as layout from './repository-layout' import * as layout from './repository-layout'
import {DependencyGraphOption, getJobMatrix, getArtifactRetentionDays} from './input-params' import {PostActionJobFailure} from './errors'
import {
DependencyGraphOption,
getDependencyGraphContinueOnFailure,
getJobMatrix,
getArtifactRetentionDays
} from './input-params'
const DEPENDENCY_GRAPH_PREFIX = 'dependency-graph_' const DEPENDENCY_GRAPH_PREFIX = 'dependency-graph_'
@ -26,6 +32,7 @@ export async function setup(option: DependencyGraphOption): Promise<void> {
core.info('Enabling dependency graph generation') core.info('Enabling dependency graph generation')
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_ENABLED', 'true') core.exportVariable('GITHUB_DEPENDENCY_GRAPH_ENABLED', 'true')
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_CONTINUE_ON_FAILURE', getDependencyGraphContinueOnFailure())
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR', getJobCorrelator()) core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR', getJobCorrelator())
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_ID', github.context.runId) core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_ID', github.context.runId)
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_REF', github.context.ref) core.exportVariable('GITHUB_DEPENDENCY_GRAPH_REF', github.context.ref)
@ -51,7 +58,7 @@ export async function complete(option: DependencyGraphOption): Promise<void> {
await uploadDependencyGraphs(await findGeneratedDependencyGraphFiles()) await uploadDependencyGraphs(await findGeneratedDependencyGraphFiles())
} }
} catch (e) { } catch (e) {
core.warning(`Failed to ${option} dependency graph. Will continue. ${String(e)}`) warnOrFail(option, e)
} }
} }
@ -78,7 +85,7 @@ async function downloadAndSubmitDependencyGraphs(): Promise<void> {
try { try {
await submitDependencyGraphs(await downloadDependencyGraphs()) await submitDependencyGraphs(await downloadDependencyGraphs())
} catch (e) { } catch (e) {
core.warning(`Download and submit dependency graph failed. Will continue. ${String(e)}`) warnOrFail(DependencyGraphOption.DownloadAndSubmit, e)
} }
} }
@ -88,7 +95,7 @@ async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise<v
await submitDependencyGraphFile(jsonFile) await submitDependencyGraphFile(jsonFile)
} catch (error) { } catch (error) {
if (error instanceof RequestError) { if (error instanceof RequestError) {
core.warning(buildWarningMessage(jsonFile, error)) throw new Error(translateErrorMessage(jsonFile, error))
} else { } else {
throw error throw error
} }
@ -96,9 +103,9 @@ async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise<v
} }
} }
function buildWarningMessage(jsonFile: string, error: RequestError): string { function translateErrorMessage(jsonFile: string, error: RequestError): string {
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile) const relativeJsonFile = getRelativePathFromWorkspace(jsonFile)
const mainWarning = `Failed to submit dependency graph ${relativeJsonFile}.\n${String(error)}` const mainWarning = `Dependency submission failed for ${relativeJsonFile}.\n${String(error)}`
if (error.message === 'Resource not accessible by integration') { if (error.message === 'Resource not accessible by integration') {
return `${mainWarning} return `${mainWarning}
Please ensure that the 'contents: write' permission is available for the workflow job. Please ensure that the 'contents: write' permission is available for the workflow job.
@ -160,6 +167,14 @@ async function findDependencyGraphFiles(dir: string): Promise<string[]> {
return graphFiles return graphFiles
} }
function warnOrFail(option: String, error: unknown): void {
if (!getDependencyGraphContinueOnFailure()) {
throw new PostActionJobFailure(error)
}
core.warning(`Failed to ${option} dependency graph. Will continue.\n${String(error)}`)
}
function getOctokit(): InstanceType<typeof GitHub> { function getOctokit(): InstanceType<typeof GitHub> {
return github.getOctokit(getGithubToken()) return github.getOctokit(getGithubToken())
} }

11
src/errors.ts Normal file
View file

@ -0,0 +1,11 @@
export class PostActionJobFailure extends Error {
constructor(error: unknown) {
if (error instanceof Error) {
super(error.message)
this.name = error.name
this.stack = error.stack
} else {
super(String(error))
}
}
}

View file

@ -107,6 +107,10 @@ export function getDependencyGraphOption(): DependencyGraphOption {
) )
} }
export function getDependencyGraphContinueOnFailure(): boolean {
return getBooleanInput('dependency-graph-continue-on-failure', true)
}
export function getArtifactRetentionDays(): number { export function getArtifactRetentionDays(): number {
const val = core.getInput('artifact-retention-days') const val = core.getInput('artifact-retention-days')
return parseNumericInput('artifact-retention-days', val, 0) return parseNumericInput('artifact-retention-days', val, 0)

View file

@ -1,5 +1,6 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as setupGradle from './setup-gradle' import * as setupGradle from './setup-gradle'
import {PostActionJobFailure} from './errors'
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in // Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to // @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
@ -13,6 +14,11 @@ export async function run(): Promise<void> {
try { try {
await setupGradle.complete() await setupGradle.complete()
} catch (error) { } catch (error) {
if (error instanceof PostActionJobFailure) {
core.setFailed(String(error))
return
}
handleFailure(error) handleFailure(error)
} }
} }

View file

@ -9,6 +9,9 @@ if (getVariable('GITHUB_DEPENDENCY_GRAPH_ENABLED') != "true") {
def gradleVersion = GradleVersion.current().baseVersion def gradleVersion = GradleVersion.current().baseVersion
if (gradleVersion < GradleVersion.version("5.2") || if (gradleVersion < GradleVersion.version("5.2") ||
(gradleVersion >= GradleVersion.version("7.0") && gradleVersion < GradleVersion.version("7.1"))) { (gradleVersion >= GradleVersion.version("7.0") && gradleVersion < GradleVersion.version("7.1"))) {
if (getVariable('GITHUB_DEPENDENCY_GRAPH_CONTINUE_ON_FAILURE') != "true") {
throw new GradleException("Dependency Graph is not supported for ${gradleVersion}. No dependency snapshot will be generated.")
}
println "::warning::Dependency Graph is not supported for ${gradleVersion}. No dependency snapshot will be generated." println "::warning::Dependency Graph is not supported for ${gradleVersion}. No dependency snapshot will be generated."
return return
} }

View file

@ -61,7 +61,23 @@ class TestDependencyGraph extends BaseInitScriptTest {
then: then:
assert !reportsDir.exists() assert !reportsDir.exists()
assert result.output.contains("::warning::Dependency Graph is not supported") assert result.output.contains("::warning::Dependency Graph is not supported for ${testGradleVersion}")
where:
testGradleVersion << NO_DEPENDENCY_GRAPH_VERSIONS
}
def "fails build when enabled for older Gradle versions if continue-on-failure is false"() {
assumeTrue testGradleVersion.compatibleWithCurrentJvm
when:
def vars = envVars
vars.put('GITHUB_DEPENDENCY_GRAPH_CONTINUE_ON_FAILURE', 'false')
def result = runAndFail(['help'], initScript, testGradleVersion.gradleVersion, [], vars)
then:
assert !reportsDir.exists()
assert result.output.contains("Dependency Graph is not supported for ${testGradleVersion}")
where: where:
testGradleVersion << NO_DEPENDENCY_GRAPH_VERSIONS testGradleVersion << NO_DEPENDENCY_GRAPH_VERSIONS
@ -114,6 +130,7 @@ class TestDependencyGraph extends BaseInitScriptTest {
def getEnvVars() { def getEnvVars() {
return [ return [
GITHUB_DEPENDENCY_GRAPH_ENABLED: "true", GITHUB_DEPENDENCY_GRAPH_ENABLED: "true",
GITHUB_DEPENDENCY_GRAPH_CONTINUE_ON_FAILURE: "true",
GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR: "CORRELATOR", GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR: "CORRELATOR",
GITHUB_DEPENDENCY_GRAPH_JOB_ID: "1", GITHUB_DEPENDENCY_GRAPH_JOB_ID: "1",
GITHUB_DEPENDENCY_GRAPH_REF: "main", GITHUB_DEPENDENCY_GRAPH_REF: "main",