From f238f4bcadb49bde4de155ad9874e13dde2c0536 Mon Sep 17 00:00:00 2001 From: Jerome Prinet Date: Wed, 13 Sep 2023 14:18:12 +0200 Subject: [PATCH] Add Publish Maven Build Scan composite action --- ...ave.yml => test-maven-build-scan-save.yml} | 23 ++++- ...s.yml => test-terms-of-service-verify.yml} | 4 +- README.md | 97 +++++++++++++++++-- maven/build-scan-save/action.yml | 11 --- maven/build-scan/load-metadata/action.yml | 32 ++++++ maven/build-scan/publish/action.yml | 94 ++++++++++++++++++ maven/build-scan/save/action.yml | 32 ++++++ pull-request-check/verify/action.yml | 41 ++++++++ .../verify}/action.yml | 6 +- 9 files changed, 312 insertions(+), 28 deletions(-) rename .github/workflows/{test-build-scan-save.yml => test-maven-build-scan-save.yml} (66%) rename .github/workflows/{test-check-tos.yml => test-terms-of-service-verify.yml} (85%) delete mode 100644 maven/build-scan-save/action.yml create mode 100644 maven/build-scan/load-metadata/action.yml create mode 100644 maven/build-scan/publish/action.yml create mode 100644 maven/build-scan/save/action.yml create mode 100644 pull-request-check/verify/action.yml rename {check-tos => terms-of-service/verify}/action.yml (93%) diff --git a/.github/workflows/test-build-scan-save.yml b/.github/workflows/test-maven-build-scan-save.yml similarity index 66% rename from .github/workflows/test-build-scan-save.yml rename to .github/workflows/test-maven-build-scan-save.yml index f76b8d6..a2fcc5a 100644 --- a/.github/workflows/test-build-scan-save.yml +++ b/.github/workflows/test-maven-build-scan-save.yml @@ -22,26 +22,27 @@ jobs: java-version: '8' distribution: 'temurin' - name: Save Maven Build Scan (no-op) - uses: ./maven/build-scan-save + uses: ./maven/build-scan/save - name: Run Maven Build 1 working-directory: ./sample/common-gradle-enterprise-maven-configuration run: mvn clean -B - name: Save Maven Build Scan 1 - uses: ./maven/build-scan-save + uses: ./maven/build-scan/save - name: Run Maven Build 2 working-directory: ./sample/common-gradle-enterprise-maven-configuration run: mvn initialize -B - name: Save Maven Build Scan 2 - uses: ./maven/build-scan-save + uses: ./maven/build-scan/save check-saved-build-scans: - name: Check Save Build Scan + name: Verify Saved Build Scans needs: create-and-save-build-scans runs-on: ubuntu-latest env: DATA_ARTIFACT_NAME: 'maven-build-scan-data' + METADATA_ARTIFACT_NAME: 'maven-build-scan-metadata' steps: - - name: Download Workflow Artifacts + - name: Download data workflow artifact uses: actions/download-artifact@v3 with: name: ${{ env.DATA_ARTIFACT_NAME }} @@ -53,3 +54,15 @@ jobs: echo "Found $scanCount scans (2 expected)" exit 1 fi + - name: Download metadata workflow artifact + uses: actions/download-artifact@v3 + with: + name: ${{ env.METADATA_ARTIFACT_NAME }} + path: ${{ env.METADATA_ARTIFACT_NAME }} + - name: Verify metadata files count + run: | + metadataFilesCount=$(find ${{ env.METADATA_ARTIFACT_NAME }} -type f -name '*-ge-extension-versions.txt' | wc -l) + if [ "$metadataFilesCount" != "2" ]; then + echo "Found $metadataFilesCount metadata files (2 expected)" + exit 1 + fi diff --git a/.github/workflows/test-check-tos.yml b/.github/workflows/test-terms-of-service-verify.yml similarity index 85% rename from .github/workflows/test-check-tos.yml rename to .github/workflows/test-terms-of-service-verify.yml index 0d03a45..4a6eda0 100644 --- a/.github/workflows/test-check-tos.yml +++ b/.github/workflows/test-terms-of-service-verify.yml @@ -4,7 +4,7 @@ on: pull_request: jobs: - gradle-check-tos: + check-terms-of-service-approval: runs-on: ubuntu-latest permissions: contents: write @@ -15,7 +15,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Gradle - Terms of Service approval verification - uses: ./check-tos + uses: ./terms-of-service/verify with: tos-location: 'https://foo.bar/tos.html' white-list: '*' diff --git a/README.md b/README.md index 29208a7..975a867 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,12 @@ A collection of composite Github Actions -## gradle-check-tos +## terms-of-service/verify -A composite action to check that Gradle Terms of Service have been approved. +A composite action to verify that Gradle Terms of Service have been approved. The action succeeds if the pull-request contributors are recorded in the signature file, fails otherwise. +Contributors can approve the Terms of Service by commenting the pull-request, explore the [cla-assistant-lite documentation](https://github.com/marketplace/actions/cla-assistant-lite) for more details. **Dependencies**: @@ -42,7 +43,7 @@ on: pull_request_target: jobs: - gradle-check-tos: + check-terms-of-service-approval: runs-on: ubuntu-latest permissions: # required to update signature file @@ -54,7 +55,7 @@ jobs: statuses: write steps: - name: Gradle - Terms of Service approval verification - uses: gradle/github-actions/check-tos@v1.0 + uses: gradle/github-actions/terms-of-service/verify@v1.0 with: # tos-location can also point to a file in a Github repository with this syntax: ///blob//tos.html tos-location: 'https://foo.bar/tos.html' @@ -68,10 +69,11 @@ jobs: #github-token: ${{ secrets.MY_PAT }} ``` -## maven/build-scan-save +## maven/build-scan/save A Composite action to save an unpublished Maven Build Scan®. The action saves unpublished Build Scan® data as a workflow artifact with name `maven-build-scan-data`, which can then be published in a dependent workflow. +To simplify the Build Scan® publication process later on, a file containing the Gradle Enterprise Maven extension version(s) is saved as an additional workflow artifact with name `maven-build-scan-metadata`. Use this action in your existing pull-request workflows to allow Build Scan® to be published. Since these workflows are running in an untrusted context, they do not have access to the required secrets to publish the Build Scan® directly. @@ -98,5 +100,86 @@ Insert the `Save Build Scan` step after each Maven execution step in the Github - name: Build with Maven run: mvn clean package - name: Save Build Scan - uses: gradle/github-actions/maven/build-scan-save@v1.0 -[...]``` + uses: gradle/github-actions/maven/build-scan/save@v1.0 +[...] +``` + +## maven/build-scan/publish + +A composite action to publish all Maven Build Scans® saved as workflow artifacts when validating a pull-request (by the `maven/build-scan/save` action). + +This action is called from a new workflow with a `workflow_run` event trigger in order to run just after the existing pull-request workflow has completed. +This event allows access to the repository secrets (_Gradle Enterprise Access Key_) which is required to publish Build Scans® to Gradle Enterprise when authentication is enabled. + +The Build Scan® publication requires the Gradle Terms of Service to be approved, this can be achieved by adding a workflow using the `terms-of-service/verify` action. +The `pull-request-check/verify` action is used to ensure this workflow passed successfully. + +`dawidd6/action-download-artifact` action is used to download Artifacts uploaded by a different workflow. + +**Dependencies**: + +- [dawidd6/action-download-artifact](https://github.com/marketplace/actions/download-workflow-artifact) + +**Event Trigger**: +- `workflow_run` + +**Action inputs**: + +| Name | Description | Default | +|---------------------------------------|----------------------------------------------------|---------| +| `gradle-enterprise-url` | Gradle Enterprise URL | | +| `gradle-enterprise-extension-version` | Gradle Enterprise Maven extension version | | +| `gradle-enterprise-access-key` | *Optional*: Gradle Enterprise access key | | +| `gradle-enterprise-allow-untrusted` | *Optional*: Gradle Enterprise allow-untrusted flag | `false` | + +**Usage**: + +_Note:_ +Some parameters need to be adjusted here: +- The workflow name (here `PR Check`) has to be adjusted to the `name` used in the workflow run to validate pull-requests +- The workflow-job-name (here `check-terms-of-service-approval`) has to be adjusted to the job `name` used in the workflow to verify the Terms of Service approval. +- The Gradle Enterprise URL (here `https://`) +- The secret name holding the Gradle Enterprise access key (here ``) + +```yaml +name: Publish Maven Build Scans + +on: + workflow_run: + workflows: [ "PR Check" ] + types: [ completed ] + +jobs: + + verify-terms-of-service-approval: + runs-on: ubuntu-latest + steps: + - name: Verify check terms of service approval job passed + uses: gradle/github-actions/pull-request-check/verify@v1.0 + with: + workflow-job-name: 'check-terms-of-service-approval' + + load-metadata: + runs-on: ubuntu-latest + needs: verify-terms-of-service-approval + outputs: + extension-versions: ${{ steps.load.outputs.extension-versions }} + steps: + - name: Load Gradle Enterprise extension versions to publish Build Scans for + id: load + uses: gradle/github-actions/maven/build-scan/load-metadata@v1.0 + + publish-build-scan: + runs-on: ubuntu-latest + needs: load-metadata + strategy: + matrix: + version: ${{ fromJson(needs.load-metadata.outputs.extension-versions) }} + steps: + - name: Publish Maven Build Scans + uses: gradle/github-actions/maven/build-scan/publish@v1.0 + with: + gradle-enterprise-url: 'https://' + gradle-enterprise-extension-version: ${{ matrix.version }} + gradle-enterprise-access-key: ${{ secrets. }} +``` diff --git a/maven/build-scan-save/action.yml b/maven/build-scan-save/action.yml deleted file mode 100644 index a9f1ea8..0000000 --- a/maven/build-scan-save/action.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: Save Maven Build Scan -description: Save Maven Build Scan - -runs: - using: composite - steps: - - name: Upload Build Scan as workflow Artifact - uses: actions/upload-artifact@v3 - with: - name: 'maven-build-scan-data' - path: '~/.m2/.gradle-enterprise/build-scan-data/' diff --git a/maven/build-scan/load-metadata/action.yml b/maven/build-scan/load-metadata/action.yml new file mode 100644 index 0000000..a5da469 --- /dev/null +++ b/maven/build-scan/load-metadata/action.yml @@ -0,0 +1,32 @@ +name: Collect Gradle Enterprise extension versions from Build Metadata +description: Collect Gradle Enterprise extension versions from Build Metadata + +outputs: + extension-versions: + description: 'Array of Gradle Enterprise Maven Extension versions to publish Build Scans for' + value: ${{ steps.collect-versions.outputs.VERSIONS }} + +runs: + using: 'composite' + steps: + - name: Download Build Scan metadata + uses: dawidd6/action-download-artifact@v2 + env: + METADATA_ARTIFACT_NAME: 'maven-build-scan-metadata' + with: + run_id: ${{ github.event.workflow_run.id }} + name: ${{ env.METADATA_ARTIFACT_NAME }} + path: ${{ env.METADATA_ARTIFACT_NAME }} + - name: Collect Gradle Enterprise extension versions + env: + METADATA_ARTIFACT_NAME: 'maven-build-scan-metadata' + METADATA_FILE_NAME: 'ge-extension-versions.txt' + id: collect-versions + run: | + # concatenate all metadata files in one single file + find ${{ env.METADATA_ARTIFACT_NAME }}/ -type f -name '*-${{ env.METADATA_FILE_NAME }}' -exec cat {} \; > ${{ env.METADATA_FILE_NAME }} + # create json array from unified file + VERSIONS=$(jq -R -s -c 'split("\n") | unique | map(select(length > 0))' < ${{ env.METADATA_FILE_NAME }}) + # add as output + echo "VERSIONS=$VERSIONS" >> $GITHUB_OUTPUT + shell: bash diff --git a/maven/build-scan/publish/action.yml b/maven/build-scan/publish/action.yml new file mode 100644 index 0000000..b3f676b --- /dev/null +++ b/maven/build-scan/publish/action.yml @@ -0,0 +1,94 @@ +name: Publish Maven Build Scans +description: Publish Maven Build Scans + +inputs: + gradle-enterprise-url: + description: 'Gradle Enterprise URL' + required: true + gradle-enterprise-access-key: + description: 'Gradle Enterprise access key' + required: false + gradle-enterprise-extension-version: + description: 'Gradle Enterprise extension version' + required: true + gradle-enterprise-allow-untrusted: + description: 'Gradle Enterprise allow-untrusted flag' + default: 'false' + +runs: + using: 'composite' + steps: + - name: Download Build Scans + uses: dawidd6/action-download-artifact@v2 + env: + ARTIFACT_NAME: 'maven-build-scan-data' + with: + run_id: ${{ github.event.workflow_run.id }} + name: ${{ env.ARTIFACT_NAME }} + path: ${{ env.ARTIFACT_NAME }} + - name: Restore Build Scans + env: + ARTIFACT_NAME: 'maven-build-scan-data' + BUILD_SCAN_DIR: '~/.m2/.gradle-enterprise/build-scan-data/' + run: | + mkdir -p ${{ env.BUILD_SCAN_DIR }} + cp -r ${{ env.ARTIFACT_NAME }}/* ${{ env.BUILD_SCAN_DIR }} + shell: bash + - name: Create Maven Project Structure + env: + PROJECT_DIR: 'maven-build-scan-publisher' + run: | + mkdir -p ${{ env.PROJECT_DIR }}/.mvn + + cat > ${{ env.PROJECT_DIR }}/pom.xml << EOF + + 4.0.0 + com.gradle + ${{ env.PROJECT_DIR }} + 1.0 + Maven Build Scan Publisher + + EOF + + cat > ${{ env.PROJECT_DIR }}/.mvn/extensions.xml << EOF + + + + com.gradle + gradle-enterprise-maven-extension + ${{ inputs.gradle-enterprise-extension-version }} + + + EOF + + cat > ${{ env.PROJECT_DIR }}/.mvn/gradle-enterprise.xml << EOF + + + + ${{ inputs.gradle-enterprise-url }} + ${{ inputs.gradle-enterprise-allow-untrusted }} + + + EOF + shell: bash + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + - name: Publish build scan + env: + BUILD_SCAN_DIR: '~/.m2/.gradle-enterprise/build-scan-data/' + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ inputs.gradle-enterprise-access-key }} + PROJECT_DIR: 'maven-build-scan-publisher' + working-directory: ${{ env.PROJECT_DIR }} + run: | + set +e + NB_SCANS=$(find ${{ env.BUILD_SCAN_DIR }}${{ inputs.gradle-enterprise-extension-version }} -type f -name "scan.scan" | wc -l) + for ((i=1; i <= $NB_SCANS; i++)) + do + echo "BUILD SCAN PUBLICATION $i/$NB_SCANS" + mvn gradle-enterprise:build-scan-publish-previous + done + shell: bash diff --git a/maven/build-scan/save/action.yml b/maven/build-scan/save/action.yml new file mode 100644 index 0000000..b719f7a --- /dev/null +++ b/maven/build-scan/save/action.yml @@ -0,0 +1,32 @@ +name: Save Maven Build Scan +description: Save Maven Build Scan + +runs: + using: composite + steps: + - name: Generate UUID + id: generate-uuid + run: | + # create a unique file name to avoid issues with actions/upload-artifact if this composite action is called multiple times + echo "UUID=$(cat /proc/sys/kernel/random/uuid)" >> "$GITHUB_OUTPUT" + shell: bash + - name: Dump Gradle Enterprise extension versions in file + env: + BUILD_SCAN_DIR: '~/.m2/.gradle-enterprise/build-scan-data/' + run: | + if [ -d ${{ env.BUILD_SCAN_DIR }} ]; then + find ${{ env.BUILD_SCAN_DIR }} -type d -name "*.*" -maxdepth 1 -mindepth 1 -exec basename {} \; > ${{ steps.generate-uuid.outputs.UUID }}-ge-extension-versions.txt + fi + shell: bash + - name: Upload Build Scan metadata as workflow Artifact + uses: actions/upload-artifact@v3 + with: + name: 'maven-build-scan-metadata' + path: '*-ge-extension-versions.txt' + retention-days: 1 + - name: Upload Build Scan as workflow Artifact + uses: actions/upload-artifact@v3 + with: + name: 'maven-build-scan-data' + path: '~/.m2/.gradle-enterprise/build-scan-data/' + retention-days: 1 diff --git a/pull-request-check/verify/action.yml b/pull-request-check/verify/action.yml new file mode 100644 index 0000000..ff8a6d0 --- /dev/null +++ b/pull-request-check/verify/action.yml @@ -0,0 +1,41 @@ +name: Verify pull-request check +description: Verify pull-request check + +inputs: + workflow-job-name: + description: 'Workflow job name to verify' + required: true + github-token: + description: 'The token used for Github API requests' + default: ${{ github.token }} + required: false + +runs: + using: 'composite' + steps: + - name: Verify pull-request check + uses: actions/github-script@v6 + env: + sha: ${{ github.event.workflow_run.head_sha }} + with: + github-token: ${{ inputs.github-token }} + result-encoding: string + script: | + // returns most recent check runs first by default + const checkRuns = await github.paginate('GET /repos/${{ github.repository }}/commits/{ref}/check-runs', { + ref: process.env.sha, + per_page: 50 + }); + for await (const cr of checkRuns) { + // check only last execution of the workflow + console.log('Checking execution of ' + cr.name); + if(cr.name == '${{ inputs.workflow-job-name }}') { + console.log('Found execution of ${{ inputs.workflow-job-name }} at ' + cr.completed_at); + if(cr.conclusion == 'success') { + return; + } else { + throw new Error('Found failed execution of ${{ inputs.workflow-job-name }} at ' + cr.completed_at); + } + } + } + throw new Error('No execution found for ${{ inputs.workflow-job-name }}'); diff --git a/check-tos/action.yml b/terms-of-service/verify/action.yml similarity index 93% rename from check-tos/action.yml rename to terms-of-service/verify/action.yml index 53cb9f2..aa43eb7 100644 --- a/check-tos/action.yml +++ b/terms-of-service/verify/action.yml @@ -1,5 +1,5 @@ -name: Gradle - Terms of Service approval verification -description: Check Gradle Terms of Service approval +name: Verify Gradle Terms of Service approval +description: Verify Gradle Terms of Service approval inputs: tos-location: @@ -31,7 +31,7 @@ inputs: runs: using: 'composite' steps: - - name: Gradle - Terms of Service approval verification + - name: Verify Gradle Terms of Service approval if: (github.event.comment.body == 'recheck' || github.event.comment.body == ${{ inputs.pr-comment-tos-approval-request }}) || github.event_name == 'pull_request_target' || github.event_name == 'pull_request' uses: contributor-assistant/github-action@v2.3.0 env: