Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/detect-gutenberg-sha-change.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Generates a list of changes and a commit message when the pinned gutenberg repository hash changes.
name: Draft commit message

on:
# This workflow was introduced in WordPress 7.0.
pull_request:
branches:
- trunk
- '[7-9].[0-9]'
paths:
- 'package.json'

# Cancels all previous workflow runs for pull requests that have not completed.
concurrency:
# The concurrency group contains the workflow name and the branch name for pull requests
# or the commit hash for any other events.
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
cancel-in-progress: true

# Disable permissions for all available scopes by default.
# Any needed permissions should be configured at the job level.
permissions: {}

jobs:
# Detects whether the gutenberg.sha value in the package.json file has changed.
detect-hash-change:
name: Detect Gutenberg SHA change
uses: ./.github/workflows/reusable-detect-gutenberg-hash-change-v1.yml
permissions:
contents: read

# Generates a list of changes between two specified hashes.
generate-changelog:
name: Generate a list of changes between the hashes
uses: ./.github/workflows/reusable-generate-gutenberg-changelog-v1.yml
needs: [ 'detect-hash-change' ]
if: ${{ needs.detect-hash-change.outputs.sha_changed == 'true' }}
permissions:
contents: read
with:
base-sha: ${{ needs.detect-hash-change.outputs.base_sha }}
head-sha: ${{ needs.detect-hash-change.outputs.head_sha }}

# Drafts a commit message containing a detailed list of changes being merged.
#
# Performs the following steps:
# - Downloads the changelog artifact.
# - Builds a commit message.
# - Uploads the commit message as an artifact.
generate-commit-message:
name: Generate commit message
uses: ./.github/workflows/reusable-generate-commit-message-v1.yml
needs: [ 'detect-hash-change', 'generate-changelog' ]
if: ${{ needs.generate-changelog.outputs.has_changes == 'true' }}
permissions:
contents: read
with:
previous-hash: ${{ needs.detect-hash-change.outputs.base_sha }}
new-hash: ${{ needs.detect-hash-change.outputs.head_sha }}
119 changes: 118 additions & 1 deletion .github/workflows/pull-request-comments.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
pull_request_target:
types: [ 'opened', 'synchronize', 'reopened', 'edited' ]
workflow_run:
workflows: [ 'Test Build Processes' ]
workflows: [ 'Test Build Processes', 'Draft commit message' ]
types:
- completed

Expand Down Expand Up @@ -228,3 +228,120 @@ jobs:
`,
} );
}

# Adds a comment with a drafted commit message.
drafted-commit-message:
name: Manage commit message draft
runs-on: ubuntu-24.04
permissions:
issues: write
pull-requests: write
if: ${{ github.repository == 'WordPress/wordpress-develop' && github.event_name == 'pull_request_target' && ! github.event.pull_request.draft && github.event.pull_request.state == 'open' }}

steps:
- name: Download artifact
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const fs = require( 'fs' );

const artifacts = await github.rest.actions.listWorkflowRunArtifacts( {
owner: context.repo.owner,
repo: context.repo.repo,
run_id: process.env.RUN_ID,
} );

const matchPrNumberArtifact = artifacts.data.artifacts.filter( ( artifact ) => {
return artifact.name === 'pr-number'
} )[0];

if ( ! matchPrNumberArtifact ) {
core.setFailed( 'No PR number artifact found!' );
return;
}

const downloadPrNumber = await github.rest.actions.downloadArtifact( {
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchPrNumberArtifact.id,
archive_format: 'zip',
} );

fs.writeFileSync( '${{github.workspace}}/pr-number.zip', Buffer.from( downloadPrNumber.data ) )

const matchMessageArtifact = artifacts.data.artifacts.filter( ( artifact ) => {
return artifact.name === 'commit-message.md'
} )[0];

if ( ! matchMessageArtifact ) {
core.setFailed( 'No commit message artifact found!' );
return;
}

const downloadCommitMessage = await github.rest.actions.downloadArtifact( {
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchMessageArtifact.id,
archive_format: 'zip',
} );

fs.writeFileSync( '${{github.workspace}}/commit-message.zip', Buffer.from( downloadCommitMessage.data ) )
env:
RUN_ID: ${{ github.event.workflow_run.id }}

- name: Unzip the artifact containing the PR number
run: unzip pr-number.zip

- name: Unzip the artifact containing the commit mesage
run: unzip commit-message.zip

- name: Leave or update a comment with the commit message draft
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const fs = require( 'fs' );
const issue_number = Number( fs.readFileSync( './NR' ) );

core.info( `Checking pull request #${issue_number}.` );

// Confirm that the pull request is still open before leaving a comment.
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: issue_number,
});

if ( pr.data.state !== 'open' ) {
core.info( 'The pull request has been closed. No comment will be left.' );
return;
}

// Comments are only added once and then updated on future commits.
const commentInfo = {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number,
};

const comments = ( await github.rest.issues.listComments( commentInfo ) ).data;
const hasCommitMessageDraft = comments.find( comment => comment.user.type === 'Bot' && comment.body.includes( 'Editor: Bump pinned hash' ) );

// Construct comment
commentInfo.body = '## Commit Message Draft\n' +
'This pull request changes the pinned hash for the Gutenberg repository. Here is a commit message draft containing a compiled list of changes included with additional required information:\n\n' +
```\n' +
commitMessageContents +
'\n```\n' +
'**Always verify these commit message drafts before using. And don't forget to include props!**';

// Only update the comment when there are changes.
if ( hasCommitMessageDraft ) {
if ( hasCommitMessageDraft.body != commentInfo.body ) {
commentInfo.comment_id = hasCommitMessageDraft.id;

github.rest.issues.updateComment( commentInfo );
}
return;
}

github.rest.issues.createComment( commentInfo );
64 changes: 64 additions & 0 deletions .github/workflows/reusable-detect-gutenberg-hash-change-v1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
##
# A reusable workflow that detects when the `gutenberg.sha` value changes in the `package.json` file.
##
name: Detect hash change (reusable)

on:
workflow_call:
outputs:
sha_changed:
description: 'Whether the pinned Gutenberg hash has changed.'
value: ${{ jobs.detect-hash-change.outputs.sha_changed }}
base_sha:
description: 'The previous pinned Gutenberg hash.'
value: ${{ jobs.detect-hash-change.outputs.base_sha }}
head_sha:
description: 'The new pinned Gutenberg hash.'
value: ${{ jobs.detect-hash-change.outputs.head_sha }}

# Disable permissions for all available scopes by default.
# Any needed permissions should be configured at the job level.
permissions: {}

jobs:
# Detects whether the gutenberg.sha value in the package.json file has changed.
#
# Performs the following steps:
# - Checks out the repository.
# - Check if the pinned hash has changed.
detect-hash-change:
name: Detect Gutenberg SHA change
runs-on: ubuntu-24.04
permissions:
contents: read
timeout-minutes: 5
outputs:
sha_changed: ${{ steps.check-sha.outputs.sha_changed }}
base_sha: ${{ steps.check-sha.outputs.base_sha }}
head_sha: ${{ steps.check-sha.outputs.head_sha }}

steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false

- name: Check if the pinned Gutenberg hash has changed
id: check-sha
run: |
BASE_GUTENBERG_SHA=$(git show ${{ github.event.pull_request.base.sha }}:package.json | jq -r '.gutenberg.sha // empty')
HEAD_GUTENBERG_SHA=$(git show ${{ github.event.pull_request.head.sha }}:package.json | jq -r '.gutenberg.sha // empty')

echo "base_sha=$BASE_GUTENBERG_SHA" >> "$GITHUB_OUTPUT"
echo "head_sha=$HEAD_GUTENBERG_SHA" >> "$GITHUB_OUTPUT"

if [ "$BASE_GUTENBERG_SHA" != "$HEAD_GUTENBERG_SHA" ]; then
echo "sha_changed=true" >> "$GITHUB_OUTPUT"
echo "The pinned Gutenberg Repository hash has changed."
echo " Previous SHA (base branch): $BASE_GUTENBERG_SHA"
echo " New SHA (head branch): $HEAD_GUTENBERG_SHA"
else
echo "sha_changed=false" >> "$GITHUB_OUTPUT"
echo "The pinned Gutenberg repository hash has not changed: $HEAD_GUTENBERG_SHA"
fi
85 changes: 85 additions & 0 deletions .github/workflows/reusable-generate-commit-message-v1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
##
# A reusable workflow that detects when the `gutenberg.sha` value changes in the `package.json` file.
##
name: Detect hash change (reusable)

on:
workflow_call:
inputs:
previous-hash:
description: 'The previous commit SHA value.'
required: true
type: 'string'
new-hash:
description: 'The new commit hash value.'
required: true
type: 'string'

# Disable permissions for all available scopes by default.
# Any needed permissions should be configured at the job level.
permissions: {}

jobs:
# Drafts a commit message containing a detailed list of changes being merged.
#
# Performs the following steps:
# - Downloads the changelog artifact.
# - Builds a commit message.
# - Uploads the commit message as an artifact.
# - Saves the pull request number to a text file.
# - Uploads the pull request number as an artifact.
generate-commit-message:
name: Generate commit message
runs-on: ubuntu-24.04
permissions:
contents: read

steps:
- name: Download changelog artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: changelog.txt
skip-decompress: true

- name: Build commit message
env:
BASE_SHA: ${{ inputs.previous-hash }}
HEAD_SHA: ${{ inputs.new-hash }}
run: |
{
printf 'Editor: Bump pinned hash for the Gutenberg repository.\n\n'
printf "This updates the pinned hash from the gutenberg from \`%s\` to \`%s\`.\n\n" "$BASE_SHA" "$HEAD_SHA"

if [ "$HAS_CHANGES" = "false" ]; then
printf '> [!WARNING]\n'
printf '> No pull request references were found in the commits between the two hashes. Please verify the hash range is correct.\n\n'
else
printf 'The following changes are included:\n\n'
cat changelog.txt
printf '\n'
fi

printf 'A full list of changes can be found on GitHub: https://github.com/WordPress/gutenberg/compare/%s...%s.\n\n' "$BASE_SHA" "$HEAD_SHA"
printf 'See #64595, #64393.\n'
} > commit-message.md

- name: Upload commit message as an artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: commit-message
path: commit-message.md

- name: Save PR number
run: |
mkdir -p ./pr-number
echo "${EVENT_NUMBER}" > ./pr-number/NR
env:
EVENT_NUMBER: ${{ github.event.number }}

# Uploads the PR number as an artifact for the Pull Request Commenting workflow to download and then
# leave a comment detailing how to test the PR within WordPress Playground.
- name: Upload PR number as artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: pr-number
path: pr-number/
Loading
Loading