diff --git a/.github/workflows/update-lavamoat-policies.yml b/.github/workflows/update-lavamoat-policies.yml index 1a07adfc0886..6aa7f79b293c 100644 --- a/.github/workflows/update-lavamoat-policies.yml +++ b/.github/workflows/update-lavamoat-policies.yml @@ -4,26 +4,17 @@ on: issue_comment: types: created -env: - INFURA_PROJECT_ID: 00000000000 - INFURA_PROD_PROJECT_ID: 00000000000 - SEGMENT_PROD_WRITE_KEY: 00000000000 - GOOGLE_PROD_CLIENT_ID: 00000000000 - APPLE_PROD_CLIENT_ID: 00000000000 - GOOGLE_BETA_CLIENT_ID: 00000000000 - APPLE_BETA_CLIENT_ID: 00000000000 - GOOGLE_EXPERIMENTAL_CLIENT_ID: 00000000000 - APPLE_EXPERIMENTAL_CLIENT_ID: 00000000000 - GOOGLE_FLASK_CLIENT_ID: 00000000000 - APPLE_FLASK_CLIENT_ID: 00000000000 +concurrency: + group: update-lavamoat-policies-${{ github.event.issue.number }} + cancel-in-progress: true jobs: prepare: - name: Prepare dependencies + name: Prepare # Only run when the comment is on a pull request containing '@metamaskbot update-policies' if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '@metamaskbot update-policies') }} runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 5 env: GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} REPO: ${{ github.repository }} @@ -31,7 +22,8 @@ jobs: COMMENT_ID: ${{ github.event.comment.id }} outputs: IS_FORK: ${{ steps.is-fork.outputs.IS_FORK }} - COMMIT_SHA: ${{ steps.commit-sha.outputs.COMMIT_SHA }} + RUN_ID: ${{ steps.find-ci-run.outputs.RUN_ID }} + VALIDATION_PENDING: ${{ steps.find-ci-run.outputs.VALIDATION_PENDING }} steps: - name: Determine whether this PR is from a fork id: is-fork @@ -47,365 +39,140 @@ jobs: "/repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" \ -f content='+1' - - name: Checkout repository - if: ${{ steps.is-fork.outputs.IS_FORK == 'false' }} - uses: actions/checkout@v6 - - - name: Checkout pull request - if: ${{ steps.is-fork.outputs.IS_FORK == 'false' }} - run: gh pr checkout "${PR_NUMBER}" - - - name: Checkout and setup environment + - name: Find CI run for PR head commit if: ${{ steps.is-fork.outputs.IS_FORK == 'false' }} - uses: MetaMask/action-checkout-and-setup@v3 - with: - is-high-risk-environment: false - cache-node-modules: true - skip-allow-scripts: true - - - name: Get commit SHA - if: ${{ steps.is-fork.outputs.IS_FORK == 'false' }} - id: commit-sha - run: echo "COMMIT_SHA=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT" - - update-lavamoat-build-policy: - name: Update LavaMoat build policy - needs: - - prepare - if: ${{ needs.prepare.outputs.IS_FORK == 'false' }} - runs-on: ubuntu-latest - timeout-minutes: 30 - env: - GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} - PR_NUMBER: ${{ github.event.issue.number }} - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Checkout pull request - run: gh pr checkout "${PR_NUMBER}" - - - name: Checkout and setup environment - uses: MetaMask/action-checkout-and-setup@v3 - with: - is-high-risk-environment: false - skip-allow-scripts: true - - - name: Update LavaMoat build policy - run: yarn lavamoat:build:auto - - - name: Cache build policy - uses: actions/cache/save@v5 - with: - path: lavamoat/build-system - key: cache-build-${{ needs.prepare.outputs.COMMIT_SHA }} - - update-lavamoat-webapp-policy: - name: Update LavaMoat webapp policy - needs: - - prepare - - update-lavamoat-build-policy - if: ${{ needs.prepare.outputs.IS_FORK == 'false' }} - runs-on: ubuntu-latest - timeout-minutes: 30 - env: - GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} - PR_NUMBER: ${{ github.event.issue.number }} - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Checkout pull request - run: gh pr checkout "${PR_NUMBER}" - - - name: Checkout and setup environment - uses: MetaMask/action-checkout-and-setup@v3 - with: - is-high-risk-environment: false - skip-allow-scripts: true - - - name: Restore build policy - uses: actions/cache/restore@v5 - with: - path: lavamoat/build-system - key: cache-build-${{ needs.prepare.outputs.COMMIT_SHA }} - fail-on-cache-miss: true - - - name: Update LavaMoat webapp policy - run: yarn lavamoat:webapp:auto - - - name: Cache webapp application policy - uses: actions/cache/save@v5 - with: - path: lavamoat/browserify - key: cache-webapp-${{ needs.prepare.outputs.COMMIT_SHA }} - - update-lavamoat-webpack-policy-build: - name: Update LavaMoat webpack build policy - needs: - - prepare - if: ${{ needs.prepare.outputs.IS_FORK == 'false' }} - runs-on: ubuntu-latest - timeout-minutes: 30 - env: - GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} - PR_NUMBER: ${{ github.event.issue.number }} - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Checkout pull request - run: gh pr checkout "${PR_NUMBER}" - - - name: Checkout and setup environment - uses: MetaMask/action-checkout-and-setup@v3 - with: - is-high-risk-environment: false - skip-allow-scripts: true - - - name: Update LavaMoat webpack build policy - run: yarn webpack:tsc && yarn webpack:lavamoat:policy:build - - - name: Cache webpack build policy - uses: actions/cache/save@v5 - with: - path: lavamoat/webpack/build - key: cache-webpack-build-${{ needs.prepare.outputs.COMMIT_SHA }} - - update-lavamoat-webpack-policy-mv2: - name: Update LavaMoat webpack MV2 policy - needs: - - prepare - - update-lavamoat-webpack-policy-build - if: ${{ needs.prepare.outputs.IS_FORK == 'false' }} - runs-on: ubuntu-latest - timeout-minutes: 30 - env: - GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} - PR_NUMBER: ${{ github.event.issue.number }} - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Checkout pull request - run: gh pr checkout "${PR_NUMBER}" - - - name: Checkout and setup environment - uses: MetaMask/action-checkout-and-setup@v3 - with: - is-high-risk-environment: false - skip-allow-scripts: true - - - name: Restore webpack build policy - uses: actions/cache/restore@v5 - with: - path: lavamoat/webpack/build - key: cache-webpack-build-${{ needs.prepare.outputs.COMMIT_SHA }} - fail-on-cache-miss: true - - - name: Update LavaMoat webpack MV2 policy - run: yarn webpack:tsc && yarn webpack:lavamoat:policy:mv2 - - - name: Cache webpack MV2 policy - uses: actions/cache/save@v5 - with: - path: lavamoat/webpack/mv2 - key: cache-webpack-mv2-${{ needs.prepare.outputs.COMMIT_SHA }} - - update-lavamoat-webpack-policy-mv3: - name: Update LavaMoat webpack MV3 policy - needs: - - prepare - - update-lavamoat-webpack-policy-build - if: ${{ needs.prepare.outputs.IS_FORK == 'false' }} - runs-on: ubuntu-latest - timeout-minutes: 30 - env: - GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} - PR_NUMBER: ${{ github.event.issue.number }} - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Checkout pull request - run: gh pr checkout "${PR_NUMBER}" + id: find-ci-run + run: | + HEAD_SHA=$(gh pr view "${PR_NUMBER}" --repo "${REPO}" --json headRefOid --jq '.headRefOid') + echo "Looking for main.yml run for commit ${HEAD_SHA}..." + + RUN_ID=$(gh run list \ + --workflow=main.yml \ + --repo "${REPO}" \ + --commit="${HEAD_SHA}" \ + --limit=1 \ + --json databaseId \ + --jq '.[0].databaseId // empty') + + if [ -z "$RUN_ID" ]; then + echo "No CI run found" + exit 0 + fi - - name: Checkout and setup environment - uses: MetaMask/action-checkout-and-setup@v3 - with: - is-high-risk-environment: false - skip-allow-scripts: true + echo "Found run ${RUN_ID}, checking validation job status..." - - name: Restore webpack build policy - uses: actions/cache/restore@v5 - with: - path: lavamoat/webpack/build - key: cache-webpack-build-${{ needs.prepare.outputs.COMMIT_SHA }} - fail-on-cache-miss: true + PENDING=$(gh api "/repos/${REPO}/actions/runs/${RUN_ID}/jobs" --paginate \ + --jq '.jobs[] | select(.workflow_name == "Validate LavaMoat policies") | select(.status != "completed") | .id') - - name: Update LavaMoat webpack MV3 policy - run: yarn webpack:tsc && yarn webpack:lavamoat:policy:mv3 + if [ -n "$PENDING" ]; then + echo "LavaMoat validation still running" + echo "VALIDATION_PENDING=true" >> "$GITHUB_OUTPUT" + exit 0 + fi - - name: Cache webpack MV3 policy - uses: actions/cache/save@v5 - with: - path: lavamoat/webpack/mv3 - key: cache-webpack-mv3-${{ needs.prepare.outputs.COMMIT_SHA }} + echo "LavaMoat validation completed" + echo "RUN_ID=${RUN_ID}" >> "$GITHUB_OUTPUT" - commit-updated-policies: - name: Commit the updated LavaMoat policies - needs: - - prepare - - update-lavamoat-build-policy - - update-lavamoat-webapp-policy - - update-lavamoat-webpack-policy-build - - update-lavamoat-webpack-policy-mv2 - - update-lavamoat-webpack-policy-mv3 - if: ${{ always() && needs.prepare.outputs.IS_FORK == 'false' }} + apply-and-commit: + name: Apply policy diffs and commit + needs: prepare + if: ${{ !cancelled() && needs.prepare.outputs.IS_FORK == 'false' }} runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 10 env: GITHUB_TOKEN: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} REPO: ${{ github.repository }} PR_NUMBER: ${{ github.event.issue.number }} + RUN_ID: ${{ needs.prepare.outputs.RUN_ID }} + VALIDATION_PENDING: ${{ needs.prepare.outputs.VALIDATION_PENDING }} ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - UPSTREAM_SUCCESS: ${{ needs.prepare.result == 'success' && needs.update-lavamoat-build-policy.result == 'success' && needs.update-lavamoat-webapp-policy.result == 'success' && needs.update-lavamoat-webpack-policy-build.result == 'success' && needs.update-lavamoat-webpack-policy-mv2.result == 'success' && needs.update-lavamoat-webpack-policy-mv3.result == 'success' }} steps: - name: Checkout repository - if: ${{ env.UPSTREAM_SUCCESS == 'true' }} + if: ${{ env.RUN_ID }} uses: actions/checkout@v6 with: - # Use PAT to ensure that the commit later can trigger status check workflows token: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} - name: Checkout pull request - if: ${{ env.UPSTREAM_SUCCESS == 'true' }} + if: ${{ env.RUN_ID }} run: gh pr checkout "${PR_NUMBER}" - - name: Restore browserify build policy - if: ${{ env.UPSTREAM_SUCCESS == 'true' }} - uses: actions/cache/restore@v5 + - name: Download policy diffs from CI + if: ${{ env.RUN_ID }} + uses: actions/download-artifact@v7 with: - path: lavamoat/build-system - key: cache-build-${{ needs.prepare.outputs.COMMIT_SHA }} - fail-on-cache-miss: true + path: lavamoat-policy-diffs + pattern: lavamoat-policy-diff-* + merge-multiple: true + run-id: ${{ env.RUN_ID }} + github-token: ${{ secrets.LAVAMOAT_UPDATE_TOKEN }} - - name: Restore browserify webapp policy - if: ${{ env.UPSTREAM_SUCCESS == 'true' }} - uses: actions/cache/restore@v5 - with: - path: lavamoat/browserify - key: cache-webapp-${{ needs.prepare.outputs.COMMIT_SHA }} - fail-on-cache-miss: true - - - name: Restore webpack build policy - if: ${{ env.UPSTREAM_SUCCESS == 'true' }} - uses: actions/cache/restore@v5 - with: - path: lavamoat/webpack/build - key: cache-webpack-build-${{ needs.prepare.outputs.COMMIT_SHA }} - fail-on-cache-miss: true - - - name: Restore webpack MV2 policy - if: ${{ env.UPSTREAM_SUCCESS == 'true' }} - uses: actions/cache/restore@v5 - with: - path: lavamoat/webpack/mv2 - key: cache-webpack-mv2-${{ needs.prepare.outputs.COMMIT_SHA }} - fail-on-cache-miss: true - - - name: Restore webpack MV3 policy - if: ${{ env.UPSTREAM_SUCCESS == 'true' }} - uses: actions/cache/restore@v5 - with: - path: lavamoat/webpack/mv3 - key: cache-webpack-mv3-${{ needs.prepare.outputs.COMMIT_SHA }} - fail-on-cache-miss: true + - name: Check for policy diffs + id: check-diffs + run: echo "HAS_DIFFS=$([ -d lavamoat-policy-diffs ] && echo true || echo false)" >> "$GITHUB_OUTPUT" - - name: Check whether there are policy changes - if: ${{ env.UPSTREAM_SUCCESS == 'true' }} - id: policy-changes + - name: Apply policy diffs + if: ${{ steps.check-diffs.outputs.HAS_DIFFS == 'true' }} run: | - if git diff --exit-code - then - echo "HAS_CHANGES=false" >> "$GITHUB_OUTPUT" - else - echo "HAS_CHANGES=true" >> "$GITHUB_OUTPUT" - fi + for patch in lavamoat-policy-diffs/*.patch; do + echo "Applying ${patch}..." + git apply "${patch}" + done - name: Commit the updated policies - if: ${{ env.UPSTREAM_SUCCESS == 'true' && steps.policy-changes.outputs.HAS_CHANGES == 'true' }} + if: ${{ steps.check-diffs.outputs.HAS_DIFFS == 'true' }} run: | git config --global user.name 'MetaMask Bot' git config --global user.email 'metamaskbot@users.noreply.github.com' git commit -am "Update LavaMoat policies" git push - ## Yes, it would be nice to use the local diff before the commit is made, but we need to include potential multiple updates in the history of the PR - name: Compare policy changes - if: ${{ env.UPSTREAM_SUCCESS == 'true' && steps.policy-changes.outputs.HAS_CHANGES == 'true' }} + if: ${{ steps.check-diffs.outputs.HAS_DIFFS == 'true' }} run: | BASE_REF=$(gh pr view "${PR_NUMBER}" --json baseRefName --jq .baseRefName) git fetch origin "${BASE_REF}" - browserify_main_diff=$(git diff "origin/${BASE_REF}" -- lavamoat/browserify/main/policy.json | md5sum | cut -d' ' -f1) - { - for folder in lavamoat/browserify/*/; do - if [ "$folder" != "lavamoat/browserify/main/" ]; then - file="${folder}policy.json" - if [ -f "$file" ]; then - diff=$(git diff "origin/${BASE_REF}" -- "$file" | md5sum | cut -d' ' -f1) - if [ "$diff" = "$browserify_main_diff" ]; then - echo "āœ… ${folder}policy.json changes match main/policy.json policy changes" - else - echo "šŸ‘€ ${folder}policy.json changes **differ from** main/policy.json policy changes" - fi - fi - fi - done - - mv2_main_diff=$(git diff "origin/${BASE_REF}" -- lavamoat/webpack/mv2/main/policy.json | md5sum | cut -d' ' -f1) - for folder in lavamoat/webpack/mv2/*/; do - if [ "$folder" != "lavamoat/webpack/mv2/main/" ]; then - file="${folder}policy.json" - if [ -f "$file" ]; then - diff=$(git diff "origin/${BASE_REF}" -- "$file" | md5sum | cut -d' ' -f1) - if [ "$diff" = "$mv2_main_diff" ]; then - echo "āœ… ${folder}policy.json changes match mv2/main/policy.json policy changes" - else - echo "šŸ‘€ ${folder}policy.json changes **differ from** mv2/main/policy.json policy changes" + compare_policies() { + local main_dir="$1" + local parent_dir="$2" + git diff "origin/${BASE_REF}" -- "${main_dir}policy.json" > /tmp/main_policy_diff + for folder in "${parent_dir}"*/; do + if [ "$folder" != "$main_dir" ]; then + local file="${folder}policy.json" + if [ -f "$file" ]; then + if diff -q /tmp/main_policy_diff <(git diff "origin/${BASE_REF}" -- "$file") > /dev/null 2>&1; then + echo "āœ… ${file} changes match ${main_dir}policy.json changes" + else + echo "šŸ‘€ ${file} changes **differ from** ${main_dir}policy.json changes" + fi fi fi - fi - done + done + } - mv3_main_diff=$(git diff "origin/${BASE_REF}" -- lavamoat/webpack/mv3/main/policy.json | md5sum | cut -d' ' -f1) - for folder in lavamoat/webpack/mv3/*/; do - if [ "$folder" != "lavamoat/webpack/mv3/main/" ]; then - file="${folder}policy.json" - if [ -f "$file" ]; then - diff=$(git diff "origin/${BASE_REF}" -- "$file" | md5sum | cut -d' ' -f1) - if [ "$diff" = "$mv3_main_diff" ]; then - echo "āœ… ${folder}policy.json changes match mv3/main/policy.json policy changes" - else - echo "šŸ‘€ ${folder}policy.json changes **differ from** mv3/main/policy.json policy changes" - fi - fi - fi - done + { + compare_policies "lavamoat/browserify/main/" "lavamoat/browserify/" + compare_policies "lavamoat/webpack/mv2/main/" "lavamoat/webpack/mv2/" + compare_policies "lavamoat/webpack/mv3/main/" "lavamoat/webpack/mv3/" } > does_diff_diff.txt - name: Post comment (policies updated) - if: ${{ env.UPSTREAM_SUCCESS == 'true' && steps.policy-changes.outputs.HAS_CHANGES == 'true' }} + if: ${{ steps.check-diffs.outputs.HAS_DIFFS == 'true' }} run: | echo -e 'Policies updated. \nšŸ‘€ Please review the diff for suspicious new powers. \n\n> [!TIP] \n> Follow the policy review process outlined in the [LavaMoat Policy Review Process doc](https://github.com/MetaMask/metamask-extension/blob/main/docs/lavamoat-policy-review-process.md) before expecting an approval from Policy Reviewers. \n🧠 Learn how to read policy diffs: https://lavamoat.github.io/guides/policy-diff/#what-to-look-for-when-reviewing-a-policy-diff \n\n' | cat - does_diff_diff.txt | gh pr comment "${PR_NUMBER}" --body-file - + - name: Post comment (validation still running) + if: ${{ env.VALIDATION_PENDING == 'true' }} + run: | + gh pr comment "${PR_NUMBER}" --body "LavaMoat validation is still running. Please retry \`@metamaskbot update-policies\` after CI validation completes." + - name: Post comment (no policy changes) - if: ${{ env.UPSTREAM_SUCCESS == 'true' && steps.policy-changes.outputs.HAS_CHANGES == 'false' }} + if: ${{ needs.prepare.result == 'success' && !env.VALIDATION_PENDING && steps.check-diffs.outputs.HAS_DIFFS != 'true' }} run: | gh pr comment "${PR_NUMBER}" --body "No policy changes" - name: Post comment if the update failed - if: ${{ env.UPSTREAM_SUCCESS != 'true' || failure() }} + if: ${{ failure() || needs.prepare.result == 'failure' }} run: | gh pr comment "${PR_NUMBER}" --repo "${REPO}" --body "Policy update failed. You can [review the logs or retry the policy update here](${ACTION_RUN_URL})" diff --git a/.github/workflows/validate-lavamoat-policies.yml b/.github/workflows/validate-lavamoat-policies.yml index 18baca47bff6..b6ebc9b49795 100644 --- a/.github/workflows/validate-lavamoat-policies.yml +++ b/.github/workflows/validate-lavamoat-policies.yml @@ -32,12 +32,21 @@ jobs: run: yarn lavamoat:build:auto - name: Check working tree + id: check-working-tree run: | - if ! git diff --exit-code; then + if ! git diff --exit-code > lavamoat-policy-diff-build.patch; then + cat lavamoat-policy-diff-build.patch echo "::error::Working tree dirty." exit 1 fi + - name: Upload policy diff on failure + if: ${{ failure() && steps.check-working-tree.outcome == 'failure' }} + uses: actions/upload-artifact@v7 + with: + name: lavamoat-policy-diff-build + path: lavamoat-policy-diff-build.patch + validate-lavamoat-policy-webapp: name: Validate LavaMoat webapp policy runs-on: ubuntu-latest @@ -53,12 +62,21 @@ jobs: run: yarn lavamoat:webapp:auto - name: Check working tree + id: check-working-tree run: | - if ! git diff --exit-code; then + if ! git diff --exit-code > lavamoat-policy-diff-webapp.patch; then + cat lavamoat-policy-diff-webapp.patch echo "::error::Working tree dirty." exit 1 fi + - name: Upload policy diff on failure + if: ${{ failure() && steps.check-working-tree.outcome == 'failure' }} + uses: actions/upload-artifact@v7 + with: + name: lavamoat-policy-diff-webapp + path: lavamoat-policy-diff-webapp.patch + validate-lavamoat-policy-webpack-build: name: Validate LavaMoat webpack build policy runs-on: ubuntu-latest @@ -74,12 +92,21 @@ jobs: run: yarn webpack:tsc && yarn webpack:lavamoat:policy:build - name: Check working tree + id: check-working-tree run: | - if ! git diff --exit-code; then + if ! git diff --exit-code > lavamoat-policy-diff-webpack-build.patch; then + cat lavamoat-policy-diff-webpack-build.patch echo "::error::Working tree dirty." exit 1 fi + - name: Upload policy diff on failure + if: ${{ failure() && steps.check-working-tree.outcome == 'failure' }} + uses: actions/upload-artifact@v7 + with: + name: lavamoat-policy-diff-webpack-build + path: lavamoat-policy-diff-webpack-build.patch + validate-lavamoat-policy-webpack-mv2: name: Validate LavaMoat webpack MV2 policy runs-on: ubuntu-latest @@ -95,12 +122,21 @@ jobs: run: yarn webpack:tsc && yarn webpack:lavamoat:policy:mv2 - name: Check working tree + id: check-working-tree run: | - if ! git diff --exit-code; then + if ! git diff --exit-code > lavamoat-policy-diff-webpack-mv2.patch; then + cat lavamoat-policy-diff-webpack-mv2.patch echo "::error::Working tree dirty." exit 1 fi + - name: Upload policy diff on failure + if: ${{ failure() && steps.check-working-tree.outcome == 'failure' }} + uses: actions/upload-artifact@v7 + with: + name: lavamoat-policy-diff-webpack-mv2 + path: lavamoat-policy-diff-webpack-mv2.patch + validate-lavamoat-policy-webpack-mv3: name: Validate LavaMoat webpack MV3 policy runs-on: ubuntu-latest @@ -116,8 +152,17 @@ jobs: run: yarn webpack:tsc && yarn webpack:lavamoat:policy:mv3 - name: Check working tree + id: check-working-tree run: | - if ! git diff --exit-code; then + if ! git diff --exit-code > lavamoat-policy-diff-webpack-mv3.patch; then + cat lavamoat-policy-diff-webpack-mv3.patch echo "::error::Working tree dirty." exit 1 fi + + - name: Upload policy diff on failure + if: ${{ failure() && steps.check-working-tree.outcome == 'failure' }} + uses: actions/upload-artifact@v7 + with: + name: lavamoat-policy-diff-webpack-mv3 + path: lavamoat-policy-diff-webpack-mv3.patch