diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 17137925..5a68bd0f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,8 +19,8 @@ jobs: release: outputs: pr: ${{ steps.release.outputs.pr }} + release: ${{ steps.release.outputs.release }} releases: ${{ steps.release.outputs.releases }} - release-flags: ${{ steps.release.outputs.release-flags }} branch: ${{ steps.release.outputs.pr-branch }} pr-number: ${{ steps.release.outputs.pr-number }} comment-id: ${{ steps.pr-comment.outputs.result }} @@ -63,26 +63,25 @@ jobs: REF_NAME: ${{ github.ref_name }} with: script: | - const { REF_NAME, PR_NUMBER } = process.env - const repo = { owner: context.repo.owner, repo: context.repo.repo } - const issue = { ...repo, issue_number: PR_NUMBER } + const { REF_NAME, PR_NUMBER: issue_number } = process.env + const { runId, repo: { owner, repo } } = context - const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + const { data: workflow } = await github.rest.actions.getWorkflowRun({ owner, repo, run_id: runId }) let body = '## Release Manager\n\n' - const comments = await github.paginate(github.rest.issues.listComments, issue) - let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + let commentId = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id body += `Release workflow run: ${workflow.html_url}\n\n#### Force CI to Update This Release\n\n` body += `This PR will be updated and CI will run for every non-\`chore:\` commit that is pushed to \`main\`. ` body += `To force CI to update this PR, run this command:\n\n` - body += `\`\`\`\ngh workflow run release.yml -r ${REF_NAME}\n\`\`\`` + body += `\`\`\`\ngh workflow run release.yml -r ${REF_NAME} -R ${owner}/${repo}\n\`\`\`` if (commentId) { - await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + await github.rest.issues.updateComment({ owner, repo, comment_id: commentId, body }) } else { - const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + const { data: comment } = await github.rest.issues.createComment({ owner, repo, issue_number, body }) commentId = comment?.id } @@ -272,6 +271,45 @@ jobs: name: Post Release - Release if: github.repository_owner == 'npm' && needs.release.outputs.releases runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Create Release PR Comment + uses: actions/github-script@v6 + env: + RELEASES: ${{ needs.release.outputs.releases }} + with: + script: | + const releases = JSON.parse(process.env.RELEASES) + const { runId, repo: { owner, repo } } = context + const issue_number = releases[0].prNumber + + let body = '## Release Workflow\n\n' + for (const { pkgName, version, url } of releases) { + body += `- \`${pkgName}@${version}\` ${url}\n` + } + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + const releaseComments = comments.filter(c => c.user.login === 'github-actions[bot]' && c.body.includes('Release is at')) + + for (const comment of releaseComments) { + await github.rest.issues.deleteComment({ owner, repo, comment_id: comment.id }) + } + + const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}` + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: `${body}- Workflow run: :arrows_counterclockwise: ${runUrl}`, + }) + + release-integration: + needs: release + name: Release Integration + if: needs.release.outputs.release + runs-on: ubuntu-latest defaults: run: shell: bash @@ -290,10 +328,54 @@ jobs: run: npm i --prefer-online --no-fund --no-audit -g npm@latest - name: npm Version run: npm -v - - name: Install Dependencies - run: npm i --ignore-scripts --no-audit --no-fund - - name: Run Post Release Actions - env: - RELEASES: ${{ needs.release.outputs.releases }} + - name: View in Registry + run: | + name=$(cat package.json | jq -r .name) + version="${{ fromJSON(needs.release.output.release).version }}" + npm view ${name}@${version} + + post-release-integration: + needs: [ release, release-integration ] + name: Post Release Integration - Release + if: github.repository_owner == 'npm' && needs.release.outputs.release && always() + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Get Needs Result + id: needs-result run: | - npm run rp-release --ignore-scripts --if-present ${{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} + result="" + if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="x" + elif [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="heavy_multiplication_x" + else + result="white_check_mark" + fi + echo "::set-output name=result::$result" + - name: Update Release PR Comment + uses: actions/github-script@v6 + env: + PR_NUMBER: ${{ fromJSON(needs.release.outputs.release).prNumber }} + RESULT: ${{ steps.needs-result.outputs.result }} + with: + script: | + const { PR_NUMBER: issue_number, RESULT } = process.env + const { repo: { owner, repo } } = context + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + const updateComment = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith('## Release Workflow\n\n')) + + if (updateComment) { + console.log('Found comment to update:', JSON.stringify(updateComment, null, 2)) + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: updateComment.id, + body: updateComment.body.replace(/Workflow run: :[a-z_]+:/, `Workflow run: :${RESULT}:`), + }) + } else { + console.log('No matching comments found:', JSON.stringify(comments, null, 2)) + } diff --git a/bin/release-manager.js b/bin/release-manager.js index b4e448e6..5fdbc0ec 100755 --- a/bin/release-manager.js +++ b/bin/release-manager.js @@ -53,7 +53,7 @@ const DEFAULT_RELEASE_PROCESS = ` Release Please will run on the just pushed release commit and create GitHub releases and tags for each package. \`\`\` - gh run watch \`gh run list -w release -b -L 1 --json databaseId -q ".[0].databaseId"\` + gh run watch \`gh run list -R {NWO} -w release -b -L 1 --json databaseId -q ".[0].databaseId"\` \`\`\` ` /* eslint-enable max-len */ @@ -82,7 +82,7 @@ const getReleaseProcess = async ({ owner, repo }) => { } catch (e) { log('Release wiki not found', e.message) log('Using default release process') - releaseProcess = DEFAULT_RELEASE_PROCESS.trim() + '\n' + releaseProcess = DEFAULT_RELEASE_PROCESS.replace(/\{NWO\}/g, `${owner}/${repo}`).trim() + '\n' } // XXX: the release steps need to always be the last thing in the doc for this to work diff --git a/bin/release-please.js b/bin/release-please.js index b7189fdd..17c39442 100755 --- a/bin/release-please.js +++ b/bin/release-please.js @@ -28,6 +28,18 @@ const debugPr = (val) => { } } +const debugRelease = (val) => { + if (dryRun) { + console.log('ROOT RELEASE:', JSON.stringify(val, null, 2)) + } +} + +const debugReleases = (val) => { + if (dryRun) { + console.log('ALL RELEASES:', JSON.stringify(val, null, 2)) + } +} + main({ token: process.env.GITHUB_TOKEN, repo: process.env.GITHUB_REPOSITORY, @@ -44,18 +56,13 @@ main({ } if (release) { + debugRelease(release) core.setOutput('release', JSON.stringify(release)) - core.setOutput('release-path', release.path) - core.setOutput('release-version', release.version) - core.setOutput('release-tag', release.tagName) - core.setOutput('release-url', release.url) } if (releases) { + debugReleases(releases) core.setOutput('releases', JSON.stringify(releases)) - core.setOutput('release-flags', JSON.stringify(releases.map((r) => { - return r.path === '.' ? '-iwr' : `-w ${r.path}` - }))) } return null diff --git a/lib/content/_job-release-integration.yml b/lib/content/_job-release-integration.yml new file mode 100644 index 00000000..f9777298 --- /dev/null +++ b/lib/content/_job-release-integration.yml @@ -0,0 +1,12 @@ +runs-on: ubuntu-latest +defaults: + run: + shell: bash +steps: + {{> stepGit }} + {{> stepNode }} + - name: View in Registry + run: | + name=$(cat package.json | jq -r .name) + version="$\{{ fromJSON(needs.release.output.release).version }}" + npm view ${name}@${version} diff --git a/lib/content/release.yml b/lib/content/release.yml index 33360b87..af8e54fe 100644 --- a/lib/content/release.yml +++ b/lib/content/release.yml @@ -18,8 +18,8 @@ jobs: release: outputs: pr: $\{{ steps.release.outputs.pr }} + release: $\{{ steps.release.outputs.release }} releases: $\{{ steps.release.outputs.releases }} - release-flags: $\{{ steps.release.outputs.release-flags }} branch: $\{{ steps.release.outputs.pr-branch }} pr-number: $\{{ steps.release.outputs.pr-number }} comment-id: $\{{ steps.pr-comment.outputs.result }} @@ -40,26 +40,25 @@ jobs: REF_NAME: $\{{ github.ref_name }} with: script: | - const { REF_NAME, PR_NUMBER } = process.env - const repo = { owner: context.repo.owner, repo: context.repo.repo } - const issue = { ...repo, issue_number: PR_NUMBER } + const { REF_NAME, PR_NUMBER: issue_number } = process.env + const { runId, repo: { owner, repo } } = context - const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + const { data: workflow } = await github.rest.actions.getWorkflowRun({ owner, repo, run_id: runId }) let body = '## Release Manager\n\n' - const comments = await github.paginate(github.rest.issues.listComments, issue) - let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + let commentId = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id body += `Release workflow run: ${workflow.html_url}\n\n#### Force CI to Update This Release\n\n` body += `This PR will be updated and CI will run for every non-\`chore:\` commit that is pushed to \`{{ defaultBranch }}\`. ` body += `To force CI to update this PR, run this command:\n\n` - body += `\`\`\`\ngh workflow run release.yml -r ${REF_NAME}\n\`\`\`` + body += `\`\`\`\ngh workflow run release.yml -r ${REF_NAME} -R ${owner}/${repo}\n\`\`\`` if (commentId) { - await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + await github.rest.issues.updateComment({ owner, repo, comment_id: commentId, body }) } else { - const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + const { data: comment } = await github.rest.issues.createComment({ owner, repo, issue_number, body }) commentId = comment?.id } @@ -123,9 +122,79 @@ jobs: post-release: needs: release - {{> job jobName="Post Release - Release" jobIf="needs.release.outputs.releases" }} - - name: Run Post Release Actions + {{> job jobName="Post Release - Release" jobIf="needs.release.outputs.releases" jobSkipSetup=true }} + - name: Create Release PR Comment + uses: actions/github-script@v6 env: RELEASES: $\{{ needs.release.outputs.releases }} + with: + script: | + const releases = JSON.parse(process.env.RELEASES) + const { runId, repo: { owner, repo } } = context + const issue_number = releases[0].prNumber + + let body = '## Release Workflow\n\n' + for (const { pkgName, version, url } of releases) { + body += `- \`${pkgName}@${version}\` ${url}\n` + } + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + const releaseComments = comments.filter(c => c.user.login === 'github-actions[bot]' && c.body.includes('Release is at')) + + for (const comment of releaseComments) { + await github.rest.issues.deleteComment({ owner, repo, comment_id: comment.id }) + } + + const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}` + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: `${body}- Workflow run: :arrows_counterclockwise: ${runUrl}`, + }) + + release-integration: + needs: release + name: Release Integration + if: needs.release.outputs.release + {{> jobReleaseIntegration }} + + post-release-integration: + needs: [release, release-integration] + {{> job jobName="Post Release Integration - Release" jobIf="needs.release.outputs.release && always()" jobSkipSetup=true }} + - name: Get Needs Result + id: needs-result run: | - {{ rootNpmPath }} run rp-release --ignore-scripts --if-present $\{{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} + result="" + if [[ "$\{{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="x" + elif [[ "$\{{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="heavy_multiplication_x" + else + result="white_check_mark" + fi + echo "::set-output name=result::$result" + - name: Update Release PR Comment + uses: actions/github-script@v6 + env: + PR_NUMBER: $\{{ fromJSON(needs.release.outputs.release).prNumber }} + RESULT: $\{{ steps.needs-result.outputs.result }} + with: + script: | + const { PR_NUMBER: issue_number, RESULT } = process.env + const { repo: { owner, repo } } = context + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + const updateComment = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith('## Release Workflow\n\n')) + + if (updateComment) { + console.log('Found comment to update:', JSON.stringify(updateComment, null, 2)) + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: updateComment.id, + body: updateComment.body.replace(/Workflow run: :[a-z_]+:/, `Workflow run: :${RESULT}:`), + }) + } else { + console.log('No matching comments found:', JSON.stringify(comments, null, 2)) + } diff --git a/lib/release-please/index.js b/lib/release-please/index.js index bea524df..9ecf4ab7 100644 --- a/lib/release-please/index.js +++ b/lib/release-please/index.js @@ -9,17 +9,21 @@ RP.registerChangelogNotes('default', (o) => new ChangelogNotes(o)) RP.registerVersioningStrategy('default', (o) => new Version(o)) RP.registerPlugin('node-workspace', (o) => new NodeWs(o.github, o.targetBranch, o.repositoryConfig)) -const main = async ({ repo: fullRepo, token, dryRun, branch, force }) => { +const main = async ({ repo: _fullRepo, token, dryRun, branch, force }) => { if (!token) { throw new Error('Token is required') } - if (!fullRepo) { + if (!_fullRepo) { throw new Error('Repo is required') } - const [owner, repo] = fullRepo.split('/') - const github = await RP.GitHub.create({ owner, repo, token }) + const fullRepo = _fullRepo.split('/') + const github = await RP.GitHub.create({ owner: fullRepo[0], repo: fullRepo[1], token }) + const { + octokit, + repository: { owner, repo, defaultBranch }, + } = github // This is mostly for testing and debugging. Use environs with the // format `RELEASE_PLEASE_` (eg @@ -29,7 +33,7 @@ const main = async ({ repo: fullRepo, token, dryRun, branch, force }) => { .filter(([k, v]) => k.startsWith('RELEASE_PLEASE_') && v != null) .map(([k, v]) => [k.replace('RELEASE_PLEASE_', ''), v]) - const baseBranch = branch ?? github.repository.defaultBranch + const baseBranch = branch ?? defaultBranch const manifest = await RP.Manifest.fromManifest( github, @@ -40,7 +44,7 @@ const main = async ({ repo: fullRepo, token, dryRun, branch, force }) => { ) if (force) { - const { data: releasePrs } = await github.octokit.pulls.list({ + const { data: releasePrs } = await octokit.pulls.list({ owner, repo, head: `release-please--branches--${baseBranch}`, @@ -59,7 +63,7 @@ const main = async ({ repo: fullRepo, token, dryRun, branch, force }) => { // to have a different body string so we append a message a message that CI // is running. This will force release-please to rebase the PR but it // wont update the body again, so we only append to it. - await github.octokit.pulls.update({ + await octokit.pulls.update({ owner, repo, pull_number: releasePr.number, @@ -73,29 +77,46 @@ const main = async ({ repo: fullRepo, token, dryRun, branch, force }) => { // We only ever get a single pull request with our current release-please settings const rootPr = pullRequests.filter(Boolean)?.[0] if (rootPr?.number) { - const commits = await github.octokit.paginate(github.octokit.rest.pulls.listCommits, { - owner: github.repository.owner, - repo: github.repository.repo, + const commits = await octokit.paginate(octokit.rest.pulls.listCommits, { + owner, + repo, pull_number: rootPr.number, }) rootPr.sha = commits?.[commits.length - 1]?.sha } const releases = allReleases.filter(Boolean) - const [rootRelease, workspaceReleases] = releases.reduce((acc, r) => { - if (r.path === '.') { - acc[0] = r - } else { - acc[1].push(r) + let rootRelease = releases[0] + + for (const release of releases) { + const prefix = release.path === '.' ? '' : release.path + + if (!prefix) { + rootRelease = release } - return acc - }, [null, []]) + + const [releasePr, releasePkg] = await Promise.all([ + octokit.rest.repos.listPullRequestsAssociatedWithCommit({ + owner, + repo, + commit_sha: release.sha, + }).then(r => r.data[0]), + octokit.rest.repos.getContent({ + owner, + repo, + ref: baseBranch, + path: `${prefix}/package.json`, + }).then(r => JSON.parse(Buffer.from(r.data.content, r.data.encoding))), + ]) + + release.prNumber = releasePr.number + release.pkgName = releasePkg.name + } return { pr: rootPr, release: rootRelease, releases: releases.length ? releases : null, - workspaceReleases: workspaceReleases.length ? workspaceReleases : null, } } diff --git a/tap-snapshots/test/apply/source-snapshots.js.test.cjs b/tap-snapshots/test/apply/source-snapshots.js.test.cjs index 1baaf4d0..fbc6431e 100644 --- a/tap-snapshots/test/apply/source-snapshots.js.test.cjs +++ b/tap-snapshots/test/apply/source-snapshots.js.test.cjs @@ -773,8 +773,8 @@ jobs: release: outputs: pr: \${{ steps.release.outputs.pr }} + release: \${{ steps.release.outputs.release }} releases: \${{ steps.release.outputs.releases }} - release-flags: \${{ steps.release.outputs.release-flags }} branch: \${{ steps.release.outputs.pr-branch }} pr-number: \${{ steps.release.outputs.pr-number }} comment-id: \${{ steps.pr-comment.outputs.result }} @@ -817,26 +817,25 @@ jobs: REF_NAME: \${{ github.ref_name }} with: script: | - const { REF_NAME, PR_NUMBER } = process.env - const repo = { owner: context.repo.owner, repo: context.repo.repo } - const issue = { ...repo, issue_number: PR_NUMBER } + const { REF_NAME, PR_NUMBER: issue_number } = process.env + const { runId, repo: { owner, repo } } = context - const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + const { data: workflow } = await github.rest.actions.getWorkflowRun({ owner, repo, run_id: runId }) let body = '## Release Manager/n/n' - const comments = await github.paginate(github.rest.issues.listComments, issue) - let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + let commentId = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id body += \`Release workflow run: \${workflow.html_url}/n/n#### Force CI to Update This Release/n/n\` body += \`This PR will be updated and CI will run for every non-/\`chore:/\` commit that is pushed to /\`main/\`. \` body += \`To force CI to update this PR, run this command:/n/n\` - body += \`/\`/\`/\`/ngh workflow run release.yml -r \${REF_NAME}/n/\`/\`/\`\` + body += \`/\`/\`/\`/ngh workflow run release.yml -r \${REF_NAME} -R \${owner}/\${repo}/n/\`/\`/\`\` if (commentId) { - await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + await github.rest.issues.updateComment({ owner, repo, comment_id: commentId, body }) } else { - const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + const { data: comment } = await github.rest.issues.createComment({ owner, repo, issue_number, body }) commentId = comment?.id } @@ -1026,6 +1025,45 @@ jobs: name: Post Release - Release if: github.repository_owner == 'npm' && needs.release.outputs.releases runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Create Release PR Comment + uses: actions/github-script@v6 + env: + RELEASES: \${{ needs.release.outputs.releases }} + with: + script: | + const releases = JSON.parse(process.env.RELEASES) + const { runId, repo: { owner, repo } } = context + const issue_number = releases[0].prNumber + + let body = '## Release Workflow/n/n' + for (const { pkgName, version, url } of releases) { + body += \`- /\`\${pkgName}@\${version}/\` \${url}/n\` + } + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + const releaseComments = comments.filter(c => c.user.login === 'github-actions[bot]' && c.body.includes('Release is at')) + + for (const comment of releaseComments) { + await github.rest.issues.deleteComment({ owner, repo, comment_id: comment.id }) + } + + const runUrl = \`https://github.com/\${owner}/\${repo}/actions/runs/\${runId}\` + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: \`\${body}- Workflow run: :arrows_counterclockwise: \${runUrl}\`, + }) + + release-integration: + needs: release + name: Release Integration + if: needs.release.outputs.release + runs-on: ubuntu-latest defaults: run: shell: bash @@ -1044,13 +1082,57 @@ jobs: run: npm i --prefer-online --no-fund --no-audit -g npm@latest - name: npm Version run: npm -v - - name: Install Dependencies - run: npm i --ignore-scripts --no-audit --no-fund - - name: Run Post Release Actions - env: - RELEASES: \${{ needs.release.outputs.releases }} + - name: View in Registry run: | - npm run rp-release --ignore-scripts --if-present \${{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} + name=$(cat package.json | jq -r .name) + version="\${{ fromJSON(needs.release.output.release).version }}" + npm view \${name}@\${version} + + post-release-integration: + needs: [ release, release-integration ] + name: Post Release Integration - Release + if: github.repository_owner == 'npm' && needs.release.outputs.release && always() + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Get Needs Result + id: needs-result + run: | + result="" + if [[ "\${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="x" + elif [[ "\${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="heavy_multiplication_x" + else + result="white_check_mark" + fi + echo "::set-output name=result::$result" + - name: Update Release PR Comment + uses: actions/github-script@v6 + env: + PR_NUMBER: \${{ fromJSON(needs.release.outputs.release).prNumber }} + RESULT: \${{ steps.needs-result.outputs.result }} + with: + script: | + const { PR_NUMBER: issue_number, RESULT } = process.env + const { repo: { owner, repo } } = context + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + const updateComment = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith('## Release Workflow/n/n')) + + if (updateComment) { + console.log('Found comment to update:', JSON.stringify(updateComment, null, 2)) + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: updateComment.id, + body: updateComment.body.replace(/Workflow run: :[a-z_]+:/, \`Workflow run: :\${RESULT}:\`), + }) + } else { + console.log('No matching comments found:', JSON.stringify(comments, null, 2)) + } .gitignore ======================================== @@ -2243,8 +2325,8 @@ jobs: release: outputs: pr: \${{ steps.release.outputs.pr }} + release: \${{ steps.release.outputs.release }} releases: \${{ steps.release.outputs.releases }} - release-flags: \${{ steps.release.outputs.release-flags }} branch: \${{ steps.release.outputs.pr-branch }} pr-number: \${{ steps.release.outputs.pr-number }} comment-id: \${{ steps.pr-comment.outputs.result }} @@ -2287,26 +2369,25 @@ jobs: REF_NAME: \${{ github.ref_name }} with: script: | - const { REF_NAME, PR_NUMBER } = process.env - const repo = { owner: context.repo.owner, repo: context.repo.repo } - const issue = { ...repo, issue_number: PR_NUMBER } + const { REF_NAME, PR_NUMBER: issue_number } = process.env + const { runId, repo: { owner, repo } } = context - const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + const { data: workflow } = await github.rest.actions.getWorkflowRun({ owner, repo, run_id: runId }) let body = '## Release Manager/n/n' - const comments = await github.paginate(github.rest.issues.listComments, issue) - let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + let commentId = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id body += \`Release workflow run: \${workflow.html_url}/n/n#### Force CI to Update This Release/n/n\` body += \`This PR will be updated and CI will run for every non-/\`chore:/\` commit that is pushed to /\`main/\`. \` body += \`To force CI to update this PR, run this command:/n/n\` - body += \`/\`/\`/\`/ngh workflow run release.yml -r \${REF_NAME}/n/\`/\`/\`\` + body += \`/\`/\`/\`/ngh workflow run release.yml -r \${REF_NAME} -R \${owner}/\${repo}/n/\`/\`/\`\` if (commentId) { - await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + await github.rest.issues.updateComment({ owner, repo, comment_id: commentId, body }) } else { - const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + const { data: comment } = await github.rest.issues.createComment({ owner, repo, issue_number, body }) commentId = comment?.id } @@ -2496,6 +2577,45 @@ jobs: name: Post Release - Release if: github.repository_owner == 'npm' && needs.release.outputs.releases runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Create Release PR Comment + uses: actions/github-script@v6 + env: + RELEASES: \${{ needs.release.outputs.releases }} + with: + script: | + const releases = JSON.parse(process.env.RELEASES) + const { runId, repo: { owner, repo } } = context + const issue_number = releases[0].prNumber + + let body = '## Release Workflow/n/n' + for (const { pkgName, version, url } of releases) { + body += \`- /\`\${pkgName}@\${version}/\` \${url}/n\` + } + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + const releaseComments = comments.filter(c => c.user.login === 'github-actions[bot]' && c.body.includes('Release is at')) + + for (const comment of releaseComments) { + await github.rest.issues.deleteComment({ owner, repo, comment_id: comment.id }) + } + + const runUrl = \`https://github.com/\${owner}/\${repo}/actions/runs/\${runId}\` + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: \`\${body}- Workflow run: :arrows_counterclockwise: \${runUrl}\`, + }) + + release-integration: + needs: release + name: Release Integration + if: needs.release.outputs.release + runs-on: ubuntu-latest defaults: run: shell: bash @@ -2514,13 +2634,57 @@ jobs: run: npm i --prefer-online --no-fund --no-audit -g npm@latest - name: npm Version run: npm -v - - name: Install Dependencies - run: npm i --ignore-scripts --no-audit --no-fund - - name: Run Post Release Actions - env: - RELEASES: \${{ needs.release.outputs.releases }} + - name: View in Registry run: | - npm run rp-release --ignore-scripts --if-present \${{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} + name=$(cat package.json | jq -r .name) + version="\${{ fromJSON(needs.release.output.release).version }}" + npm view \${name}@\${version} + + post-release-integration: + needs: [ release, release-integration ] + name: Post Release Integration - Release + if: github.repository_owner == 'npm' && needs.release.outputs.release && always() + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Get Needs Result + id: needs-result + run: | + result="" + if [[ "\${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="x" + elif [[ "\${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="heavy_multiplication_x" + else + result="white_check_mark" + fi + echo "::set-output name=result::$result" + - name: Update Release PR Comment + uses: actions/github-script@v6 + env: + PR_NUMBER: \${{ fromJSON(needs.release.outputs.release).prNumber }} + RESULT: \${{ steps.needs-result.outputs.result }} + with: + script: | + const { PR_NUMBER: issue_number, RESULT } = process.env + const { repo: { owner, repo } } = context + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + const updateComment = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith('## Release Workflow/n/n')) + + if (updateComment) { + console.log('Found comment to update:', JSON.stringify(updateComment, null, 2)) + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: updateComment.id, + body: updateComment.body.replace(/Workflow run: :[a-z_]+:/, \`Workflow run: :\${RESULT}:\`), + }) + } else { + console.log('No matching comments found:', JSON.stringify(comments, null, 2)) + } .gitignore ======================================== @@ -3556,8 +3720,8 @@ jobs: release: outputs: pr: \${{ steps.release.outputs.pr }} + release: \${{ steps.release.outputs.release }} releases: \${{ steps.release.outputs.releases }} - release-flags: \${{ steps.release.outputs.release-flags }} branch: \${{ steps.release.outputs.pr-branch }} pr-number: \${{ steps.release.outputs.pr-number }} comment-id: \${{ steps.pr-comment.outputs.result }} @@ -3600,26 +3764,25 @@ jobs: REF_NAME: \${{ github.ref_name }} with: script: | - const { REF_NAME, PR_NUMBER } = process.env - const repo = { owner: context.repo.owner, repo: context.repo.repo } - const issue = { ...repo, issue_number: PR_NUMBER } + const { REF_NAME, PR_NUMBER: issue_number } = process.env + const { runId, repo: { owner, repo } } = context - const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + const { data: workflow } = await github.rest.actions.getWorkflowRun({ owner, repo, run_id: runId }) let body = '## Release Manager/n/n' - const comments = await github.paginate(github.rest.issues.listComments, issue) - let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + let commentId = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id body += \`Release workflow run: \${workflow.html_url}/n/n#### Force CI to Update This Release/n/n\` body += \`This PR will be updated and CI will run for every non-/\`chore:/\` commit that is pushed to /\`main/\`. \` body += \`To force CI to update this PR, run this command:/n/n\` - body += \`/\`/\`/\`/ngh workflow run release.yml -r \${REF_NAME}/n/\`/\`/\`\` + body += \`/\`/\`/\`/ngh workflow run release.yml -r \${REF_NAME} -R \${owner}/\${repo}/n/\`/\`/\`\` if (commentId) { - await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + await github.rest.issues.updateComment({ owner, repo, comment_id: commentId, body }) } else { - const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + const { data: comment } = await github.rest.issues.createComment({ owner, repo, issue_number, body }) commentId = comment?.id } @@ -3809,6 +3972,45 @@ jobs: name: Post Release - Release if: github.repository_owner == 'npm' && needs.release.outputs.releases runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Create Release PR Comment + uses: actions/github-script@v6 + env: + RELEASES: \${{ needs.release.outputs.releases }} + with: + script: | + const releases = JSON.parse(process.env.RELEASES) + const { runId, repo: { owner, repo } } = context + const issue_number = releases[0].prNumber + + let body = '## Release Workflow/n/n' + for (const { pkgName, version, url } of releases) { + body += \`- /\`\${pkgName}@\${version}/\` \${url}/n\` + } + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + const releaseComments = comments.filter(c => c.user.login === 'github-actions[bot]' && c.body.includes('Release is at')) + + for (const comment of releaseComments) { + await github.rest.issues.deleteComment({ owner, repo, comment_id: comment.id }) + } + + const runUrl = \`https://github.com/\${owner}/\${repo}/actions/runs/\${runId}\` + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: \`\${body}- Workflow run: :arrows_counterclockwise: \${runUrl}\`, + }) + + release-integration: + needs: release + name: Release Integration + if: needs.release.outputs.release + runs-on: ubuntu-latest defaults: run: shell: bash @@ -3827,13 +4029,57 @@ jobs: run: npm i --prefer-online --no-fund --no-audit -g npm@latest - name: npm Version run: npm -v - - name: Install Dependencies - run: npm i --ignore-scripts --no-audit --no-fund - - name: Run Post Release Actions - env: - RELEASES: \${{ needs.release.outputs.releases }} + - name: View in Registry run: | - npm run rp-release --ignore-scripts --if-present \${{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} + name=$(cat package.json | jq -r .name) + version="\${{ fromJSON(needs.release.output.release).version }}" + npm view \${name}@\${version} + + post-release-integration: + needs: [ release, release-integration ] + name: Post Release Integration - Release + if: github.repository_owner == 'npm' && needs.release.outputs.release && always() + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Get Needs Result + id: needs-result + run: | + result="" + if [[ "\${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="x" + elif [[ "\${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="heavy_multiplication_x" + else + result="white_check_mark" + fi + echo "::set-output name=result::$result" + - name: Update Release PR Comment + uses: actions/github-script@v6 + env: + PR_NUMBER: \${{ fromJSON(needs.release.outputs.release).prNumber }} + RESULT: \${{ steps.needs-result.outputs.result }} + with: + script: | + const { PR_NUMBER: issue_number, RESULT } = process.env + const { repo: { owner, repo } } = context + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + const updateComment = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith('## Release Workflow/n/n')) + + if (updateComment) { + console.log('Found comment to update:', JSON.stringify(updateComment, null, 2)) + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: updateComment.id, + body: updateComment.body.replace(/Workflow run: :[a-z_]+:/, \`Workflow run: :\${RESULT}:\`), + }) + } else { + console.log('No matching comments found:', JSON.stringify(comments, null, 2)) + } .release-please-manifest.json ========================================