diff --git a/.github/workflows/ci-dependency-review.yml b/.github/workflows/ci-dependency-review.yml index 8e4d6a3..5876390 100644 --- a/.github/workflows/ci-dependency-review.yml +++ b/.github/workflows/ci-dependency-review.yml @@ -16,5 +16,9 @@ jobs: steps: - name: 'Checkout Repository' uses: actions/checkout@v4 + - name: 'Dependency Submission' + uses: ./dependency-submission + with: + build-root-directory: .github/workflow-samples/groovy-dsl - name: 'Dependency Review' uses: actions/dependency-review-action@v4 diff --git a/.github/workflows/integ-test-dependency-submission.yml b/.github/workflows/integ-test-dependency-submission.yml index 6d736e2..df348e9 100644 --- a/.github/workflows/integ-test-dependency-submission.yml +++ b/.github/workflows/integ-test-dependency-submission.yml @@ -89,51 +89,72 @@ jobs: with: build-root-directory: .github/workflow-samples/kotlin-dsl - # TODO - Test this scenario (and make it work) - # multiple-builds: - # strategy: - # matrix: - # os: ${{fromJSON(inputs.runner-os)}} - # runs-on: ${{ matrix.os }} - # steps: - # - name: Checkout sources - # uses: actions/checkout@v4 - # - name: Initialize integ-test - # uses: ./.github/actions/init-integ-test + multiple-builds: + strategy: + matrix: + os: ${{fromJSON(inputs.runner-os)}} + runs-on: ${{ matrix.os }} + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Initialize integ-test + uses: ./.github/actions/init-integ-test + + - id: kotlin-dsl + uses: ./dependency-submission + with: + build-root-directory: .github/workflow-samples/kotlin-dsl + - id: groovy-dsl + uses: ./dependency-submission + with: + build-root-directory: .github/workflow-samples/groovy-dsl + - id: groovy-dsl-again + uses: ./dependency-submission + with: + build-root-directory: .github/workflow-samples/groovy-dsl + additional-arguments: --no-build-cache + - name: Check generated dependency graphs + shell: bash + run: | + echo "kotlin-dsl report file: ${{ steps.kotlin-dsl.outputs.dependency-graph-file }}" + echo "groovy-dsl report file: ${{ steps.groovy-dsl.outputs.dependency-graph-file }}" + echo "groovy-dsl-again report file: ${{ steps.groovy-dsl-again.outputs.dependency-graph-file }}" + ls -l dependency-graph-reports + if [ ! -e "${{ steps.kotlin-dsl.outputs.dependency-graph-file }}" ]; then + echo "Did not find kotlin-dsl dependency graph file" + exit 1 + fi + if [ ! -e "${{ steps.groovy-dsl.outputs.dependency-graph-file }}" ]; then + echo "Did not find groovy-dsl dependency graph file" + exit 1 + fi + if [ ! -e "${{ steps.groovy-dsl-again.outputs.dependency-graph-file }}" ]; then + echo "Did not find groovy-dsl-again dependency graph file" + exit 1 + fi + + multiple-builds-upload: + strategy: + matrix: + os: ${{fromJSON(inputs.runner-os)}} + runs-on: ${{ matrix.os }} + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Initialize integ-test + uses: ./.github/actions/init-integ-test + + - id: kotlin-dsl + uses: ./dependency-submission + with: + dependency-graph: generate-and-upload + build-root-directory: .github/workflow-samples/kotlin-dsl + - id: groovy-dsl + uses: ./dependency-submission + with: + dependency-graph: generate-and-upload + build-root-directory: .github/workflow-samples/groovy-dsl - # - name: Setup Gradle for dependency-graph generate - # uses: ./setup-gradle - # with: - # dependency-graph: generate-and-submit - # - id: gradle-assemble - # run: ./gradlew assemble - # working-directory: .github/workflow-samples/groovy-dsl - # - id: gradle-build - # run: ./gradlew build - # working-directory: .github/workflow-samples/groovy-dsl - # - id: gradle-build-again - # run: ./gradlew build - # working-directory: .github/workflow-samples/groovy-dsl - # - name: Check generated dependency graphs - # shell: bash - # run: | - # echo "gradle-assemble report file: ${{ steps.gradle-assemble.outputs.dependency-graph-file }}" - # echo "gradle-build report file: ${{ steps.gradle-build.outputs.dependency-graph-file }}" - # echo "gradle-build-again report file: ${{ steps.gradle-build-again.outputs.dependency-graph-file }}" - # ls -l dependency-graph-reports - # if [ ! -e "${{ steps.gradle-assemble.outputs.dependency-graph-file }}" ]; then - # echo "Did not find gradle-assemble dependency graph file" - # exit 1 - # fi - # if [ ! -e "${{ steps.gradle-build.outputs.dependency-graph-file }}" ]; then - # echo "Did not find gradle-build dependency graph files" - # exit 1 - # fi - # if [ ! -e "${{ steps.gradle-build-again.outputs.dependency-graph-file }}" ]; then - # echo "Did not find gradle-build-again dependency graph files" - # exit 1 - # fi - config-cache: runs-on: ubuntu-latest steps: @@ -154,7 +175,7 @@ jobs: echo "Did not find config-cache-store dependency graph files" exit 1 fi - rm ${{ steps.config-cache-store.outputs.dependency-graph-file }} + rm ${{ steps.config-cache-store.outputs.dependency-graph-file }}* - id: config-cache-reuse uses: ./dependency-submission with: diff --git a/docs/dependency-submission.md b/docs/dependency-submission.md index 5282e74..576bed5 100644 --- a/docs/dependency-submission.md +++ b/docs/dependency-submission.md @@ -288,18 +288,11 @@ jobs: - name: Generate and submit dependency graph uses: gradle/actions/dependency-submission@v3 - - dependency-review: - needs: dependency-submission - runs-on: ubuntu-latest - steps: + - name: Perform dependency review uses: actions/dependency-review-action@v3 ``` -Note that the `dependency-submission` action submits the dependency graph at the completion of the workflow Job. -For this reason, the `dependency-review-action` must be executed in a dependent job, and not as a subsequent step in the job that generates the dependency graph. - ## Usage with pull requests from public forked repositories This `contents: write` permission is [not available for any workflow that is triggered by a pull request submitted from a public forked repository](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token). diff --git a/sources/.eslintrc.json b/sources/.eslintrc.json index 76f2192..a1e71f4 100644 --- a/sources/.eslintrc.json +++ b/sources/.eslintrc.json @@ -14,6 +14,7 @@ "no-unused-vars": "off", "no-shadow": "off", "sort-imports": "off", + "github/array-foreach": "off", "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], "@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}], "@typescript-eslint/no-require-imports": "error", diff --git a/sources/src/dependency-graph.ts b/sources/src/dependency-graph.ts index a539924..bfa0e4a 100644 --- a/sources/src/dependency-graph.ts +++ b/sources/src/dependency-graph.ts @@ -54,11 +54,6 @@ function maybeExportVariable(variableName: string, value: unknown): void { } export async function complete(config: DependencyGraphConfig): Promise { - if (isRunningInActEnvironment()) { - core.info('Dependency graph upload and submit not supported in the ACT environment.') - return - } - const option = config.getDependencyGraphOption() try { switch (option) { @@ -84,6 +79,12 @@ async function findGeneratedDependencyGraphFiles(): Promise { } async function uploadDependencyGraphs(dependencyGraphFiles: string[], config: DependencyGraphConfig): Promise { + if (isRunningInActEnvironment()) { + core.info('Dependency graph upload not supported in the ACT environment.') + core.info(`Would upload: ${dependencyGraphFiles.join(', ')}`) + return + } + const workspaceDirectory = layout.workspaceDirectory() const artifactClient = new DefaultArtifactClient() @@ -111,12 +112,18 @@ async function downloadAndSubmitDependencyGraphs(config: DependencyGraphConfig): } async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise { - for (const jsonFile of dependencyGraphFiles) { + if (isRunningInActEnvironment()) { + core.info('Dependency graph submit not supported in the ACT environment.') + core.info(`Would submit: ${dependencyGraphFiles.join(', ')}`) + return + } + + for (const dependencyGraphFile of dependencyGraphFiles) { try { - await submitDependencyGraphFile(jsonFile) + await submitDependencyGraphFile(dependencyGraphFile) } catch (error) { if (error instanceof RequestError) { - throw new Error(translateErrorMessage(jsonFile, error)) + throw new Error(translateErrorMessage(dependencyGraphFile, error)) } else { throw error } @@ -184,8 +191,20 @@ async function downloadDependencyGraphs(): Promise { async function findDependencyGraphFiles(dir: string): Promise { const globber = await glob.create(`${dir}/dependency-graph-reports/*.json`) - const graphFiles = globber.glob() - return graphFiles + const allFiles = await globber.glob() + const unprocessedFiles = allFiles.filter(file => !isProcessed(file)) + unprocessedFiles.forEach(markProcessed) + return unprocessedFiles +} + +function isProcessed(dependencyGraphFile: string): boolean { + const markerFile = `${dependencyGraphFile}.processed` + return fs.existsSync(markerFile) +} + +function markProcessed(dependencyGraphFile: string): void { + const markerFile = `${dependencyGraphFile}.processed` + fs.writeFileSync(markerFile, '') } function warnOrFail(config: DependencyGraphConfig, option: String, error: unknown): void { diff --git a/sources/src/dependency-submission/main.ts b/sources/src/dependency-submission/main.ts index df68a4d..0a1007f 100644 --- a/sources/src/dependency-submission/main.ts +++ b/sources/src/dependency-submission/main.ts @@ -42,6 +42,8 @@ export async function run(): Promise { const args: string[] = parseArgsStringToArgv(executionArgs) const buildRootDirectory = layout.buildRootDirectory() await execution.executeGradleBuild(executable, buildRootDirectory, args) + + await dependencyGraph.complete(config) } catch (error) { core.setFailed(String(error)) if (error instanceof Error && error.stack) { diff --git a/sources/src/dependency-submission/post.ts b/sources/src/dependency-submission/post.ts index d1b99ba..91fe87f 100644 --- a/sources/src/dependency-submission/post.ts +++ b/sources/src/dependency-submission/post.ts @@ -1,8 +1,7 @@ import * as core from '@actions/core' import * as setupGradle from '../setup-gradle' -import * as dependencyGraph from '../dependency-graph' -import {CacheConfig, DependencyGraphConfig, SummaryConfig} from '../input-params' +import {CacheConfig, SummaryConfig} from '../input-params' import {PostActionJobFailure} from '../errors' // Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in @@ -15,10 +14,7 @@ process.on('uncaughtException', e => handleFailure(e)) */ export async function run(): Promise { try { - if (await setupGradle.complete(new CacheConfig(), new SummaryConfig())) { - // Only submit the dependency graphs once per job - await dependencyGraph.complete(new DependencyGraphConfig()) - } + await setupGradle.complete(new CacheConfig(), new SummaryConfig()) } catch (error) { if (error instanceof PostActionJobFailure) { core.setFailed(String(error))