This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Create a PR to add an entry to the CHANGELOG.md file in this repo | |
# **What it does**: If a member of the github org posts a changelog comment, it creates a PR to update the CHANGELOG.md file. | |
# **Why we have it**: This surfaces docs changelog details publicly. | |
# **Who does it impact**: GitHub users and staff. | |
on: | |
issue_comment: | |
types: [created] | |
workflow_dispatch: | |
permissions: | |
contents: write | |
pull-requests: write | |
env: | |
CHANGELOG_FILE: CHANGELOG.md | |
jobs: | |
docs-changelog-pr: | |
if: ${{ github.repository == 'github/docs-internal' && github.event.issue.pull_request }} | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- name: 'Ensure ${{ env.CHANGELOG_FILE }} exists' | |
run: | | |
if [ ! -f ${{ env.CHANGELOG_FILE }} ]; then | |
echo "${{ env.CHANGELOG_FILE }} is missing at the root of the repository." | |
exit 1 | |
fi | |
- name: Check that the user belongs to the github org | |
id: hubber_check | |
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea | |
with: | |
github-token: ${{ secrets.DOCS_BOT_PAT_BASE }} | |
script: | | |
try { | |
await github.rest.teams.getMembershipForUserInOrg({ | |
org: 'github', | |
team_slug: 'employees', | |
username: context.payload.sender.login, | |
}); | |
core.exportVariable('CONTINUE_WORKFLOW', 'true'); | |
} catch(err) { | |
core.info("Workflow triggered by a comment, but the commenter is not a Hubber. Exiting."); | |
core.exportVariable('CONTINUE_WORKFLOW', 'false'); | |
} | |
- name: Check if comment starts with '## Changelog summary' | |
if: env.CONTINUE_WORKFLOW == 'true' | |
id: check_summary | |
env: | |
COMMENT_BODY: ${{ github.event.comment.body }} | |
run: | | |
# Get the first line of the comment and trim the leading/trailing whitespace: | |
FIRST_LINE=$(printf "%s\n" "$COMMENT_BODY" | head -n1 | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') | |
if [[ "$FIRST_LINE" != '## Changelog summary' ]]; then | |
echo "FIRST_LINE=|$FIRST_LINE|" | |
echo "The pull request comment is not a changelog summary. Exiting." | |
echo "CONTINUE_WORKFLOW=false" >> $GITHUB_ENV | |
fi | |
- name: Create changelog text | |
if: env.CONTINUE_WORKFLOW == 'true' | |
id: create_text | |
env: | |
COMMENT_BODY: ${{ github.event.comment.body }} | |
run: | | |
set -euo pipefail | |
DATE=$(date +"**%-d %B %Y**") | |
BODY="$(printf "%s\n" "$COMMENT_BODY" | tail -n +2)" | |
CHANGELOG_TEXT="$(printf "%s\n" "$BODY" | awk '/^:writing_hand:/{exit} {print}')" | |
{ | |
echo "$DATE" | |
echo -e "$CHANGELOG_TEXT\n<hr>" | |
} > changelog_entry.txt | |
- name: Set up git | |
if: env.CONTINUE_WORKFLOW == 'true' | |
run: | | |
git config user.name "github-actions[bot]" | |
git config user.email "github-actions[bot]@users.noreply.github.com" | |
- name: Prepare branch | |
if: env.CONTINUE_WORKFLOW == 'true' | |
run: | | |
BRANCH="changelog-update-$(date +%s)" | |
echo "BRANCH=$BRANCH" >> $GITHUB_ENV | |
git checkout -b "$BRANCH" | |
# Insert new changelog entry after the first heading, as follows: | |
# Print the first line of the existing CHANGELOG.md file into a `tmp` file, followed by an empty line. | |
# Then, print the contents of `changelog_entry.txt` into the `tmp` file. | |
# Then, print the rest of the existing CHANGELOG.md file into the `tmp` file. | |
# Finally, replace the existing CHANGELOG.md file with the `tmp` file. | |
awk 'NR==1{print; print ""; while ((getline line < "changelog_entry.txt") > 0) print line; next}1' CHANGELOG.md > tmp && mv tmp CHANGELOG.md | |
git add CHANGELOG.md | |
git commit -m "Update changelog for $(head -n1 changelog_entry.txt)" | |
git push origin "$BRANCH" | |
- name: Create a pull request | |
if: env.CONTINUE_WORKFLOW == 'true' | |
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea | |
id: create_pull_request | |
with: | |
github-token: ${{ secrets.DOCS_BOT_PAT_BASE }} | |
script: | | |
const { data: pullRequest } = await github.rest.pulls.create({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
title: `Update docs changelog (for PR #${context.payload.issue.number})`, | |
body: `### Automated docs changelog update\n\n**Purpose:** Update the <code>${{ env.CHANGELOG_FILE }}</code> file with details of a recent docs change.\n\nThis PR is an automated update, generated by the <code>create-changelog-pr.yml</code> Actions workflow as a result of a "Changelog summary" comment being added to [PR #${context.payload.issue.number}](${context.payload.issue.html_url}).\n\n**Note for reviewer**: This change to the <code>${{ env.CHANGELOG_FILE }}</code> file will be synced to the public docs site, so make sure that the content of the entry is appropriate for public consumption. If the content is wholly inappropriate for public consumption, then this PR can be closed.\n\n<details><summary>Original PR comment posted by @${context.payload.comment.user.login}, using the <code>/changelog</code> slash command:</summary>\n\n${context.payload.comment.body}</details>`, | |
head: process.env.BRANCH, | |
base: 'main' | |
}); | |
core.setOutput('pull-request-number', pullRequest.number); | |
core.setOutput('pull-request-url', pullRequest.html_url); | |
- name: Add 'ready-for-doc-review' label to PR | |
if: env.CONTINUE_WORKFLOW == 'true' | |
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea | |
env: | |
# Get the number of the PR that was just created: | |
PULL_REQUEST_NUMBER: ${{ steps.create_pull_request.outputs.pull-request-number }} | |
with: | |
github-token: ${{ secrets.DOCS_BOT_PAT_BASE }} | |
script: | | |
await github.rest.issues.addLabels({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: Number(process.env.PULL_REQUEST_NUMBER), | |
labels: ['ready-for-doc-review'] | |
}); | |
- uses: ./.github/actions/slack-alert | |
if: ${{ failure() && github.event_name != 'workflow_dispatch' }} | |
with: | |
slack_channel_id: ${{ secrets.DOCS_ALERTS_SLACK_CHANNEL_ID }} | |
slack_token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }} |