name: Clean up closed PR artifacts on: pull_request_target: types: - closed permissions: actions: write pull-requests: read jobs: delete-artifacts: name: Delete PR artifacts runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Delete artifacts from PR workflow runs uses: actions/github-script@v9 with: script: | const { owner, repo } = context.repo; const pullRequest = context.payload.pull_request; const workflowRuns = await github.paginate( github.rest.actions.listWorkflowRunsForRepo, { owner, repo, event: 'pull_request', branch: pullRequest.head.ref, per_page: 100 } ); const pullRequestRuns = []; const associatedPullRequestsBySha = new Map(); for (const run of workflowRuns) { if (!associatedPullRequestsBySha.has(run.head_sha)) { const associatedPullRequests = await github.paginate( github.rest.repos.listPullRequestsAssociatedWithCommit, { owner, repo, commit_sha: run.head_sha, per_page: 100 } ); associatedPullRequestsBySha.set(run.head_sha, associatedPullRequests); } const belongsToPullRequest = associatedPullRequestsBySha .get(run.head_sha) .some(pr => pr.number === pullRequest.number); if (belongsToPullRequest) { pullRequestRuns.push(run); } } let deletedArtifacts = 0; let deletedBytes = 0; for (const run of pullRequestRuns) { const artifacts = await github.paginate( github.rest.actions.listWorkflowRunArtifacts, { owner, repo, run_id: run.id, per_page: 100 } ); for (const artifact of artifacts) { if (artifact.expired) { core.info(`Skipping expired artifact ${artifact.name} (${artifact.id}).`); continue; } try { await github.rest.actions.deleteArtifact({ owner, repo, artifact_id: artifact.id }); deletedArtifacts += 1; deletedBytes += artifact.size_in_bytes; core.info(`Deleted artifact ${artifact.name} (${artifact.id}).`); } catch (error) { if (error.status === 404) { core.info(`Artifact ${artifact.name} (${artifact.id}) was already deleted.`); continue; } throw error; } } } const deletedMiB = (deletedBytes / 1024 / 1024).toFixed(1); core.info( `Deleted ${deletedArtifacts} artifact(s) (${deletedMiB} MiB) ` + `from ${pullRequestRuns.length} workflow run(s) for PR #${pullRequest.number}.` ); await core.summary .addHeading('PR artifact cleanup') .addRaw(`Pull request: #${pullRequest.number}`, true) .addRaw(`Workflow runs inspected: ${pullRequestRuns.length}`, true) .addRaw(`Artifacts deleted: ${deletedArtifacts}`, true) .addRaw(`Storage released: ${deletedMiB} MiB`, true) .write();