diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000000..24e2573546f53 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,213 @@ +# Configure Dependabot scanning. +version: 2 + +updates: + # Check for updates to GitHub Actions. + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + groups: + github-actions: + patterns: + - "*" + + # Check for updates to Composer packages. + - package-ecosystem: "composer" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + ignore: + # These dependencies do not currently need to be managed with Dependabot. + - dependency-name: "squizlabs/php_codesniffer" + - dependency-name: "wp-coding-standards/wpcs" + - dependency-name: "phpcompatibility/php-compatibility" + - dependency-name: "yoast/phpunit-polyfills" + groups: + composer-packages: + patterns: + - "composer/ca-bundle" + + # Monitor some npm dependencies for updates in groups. + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 20 + ignore: + - dependency-name: "@wordpress/*" + groups: + ## + # Groups for updating devDependencies. + ## + + # Dependencies related to Playwright testing (E2E, performance). + tests-playwright: + patterns: + - "*playwright*" + # Dependencies related to JavaScript testing with QUnit. + tests-qunit: + patterns: + - "*qunit*" + - "sinon*" + # Dependencies related to CSS and SASS building and manilupating. + dev-css-sass: + patterns: + - "autoprefixer" + # postcss and css related dependencies. + - "*css*" + - "*sass" + # Dependencies related to the Webpack build process. + dev-webpack: + patterns: + - "*webpack*" + - "react-refresh" + - "source-map-loader" + # Dependencies related to the local Docker development environment. + dev-docker: + patterns: + - "dotenv*" + - "wait-on" + # Dependencies that do not fall into a specific grouping. + dev-miscellaneous: + patterns: + - "chalk" + - "check-node-version" + - "ink-docstrap" + - "install-changed" + - "matchdep" + - "uuid" + # Dependencies related to JavaScript minification. + dev-uglify: + patterns: + - "*uglify*" + # All GruntJS related dependencies that do not relate to another group. + dev-grunt: + patterns: + - "*grunt*" + + ## + # Groups for updating production dependencies. + ## + + # Dependencies related to jQuery and its ecosystem. + external-jquery: + patterns: + - "jquery*" + # Dependencies related to React and its ecosystem. + external-react: + patterns: + - "react*" + - "!react-refresh" + # Dependencies used for bundling polyfill libraries into WordPress. + external-polyfills: + patterns: + - "core-js-url-browser" + - "element-closest" + - "formdata-polyfill" + - "imagesloaded" + - "objectFitPolyfill" + - "polyfill-library" + - "regenerator-runtime" + - "whatwg-fetch" + - "wicg-inert" + # Dependencies related to the Masonry library. + external-masonry: + patterns: + - "masonry-layout" + # Dependencies that do not fall into a specific grouping. + external-miscellaneous: + patterns: + - "backbone" + - "clipboard" + - "hoverintent" + - "json2php" + - "lodash" + - "moment" + - "underscore" + + # Monitor npm dependencies within default themes. + - package-ecosystem: "npm" + directory: "/src/wp-content/themes/twentytwentyfive" + schedule: + interval: "weekly" + open-pull-requests-limit: 20 + groups: + twentytwentyfive-css: + patterns: + - "**browserslist*" + - "*css*" + + - package-ecosystem: "npm" + directory: "/src/wp-content/themes/twentytwentytwo" + schedule: + interval: "weekly" + open-pull-requests-limit: 20 + groups: + twentytwentytwo-css: + patterns: + - "**browserslist*" + - "*css*" + + - package-ecosystem: "npm" + directory: "/src/wp-content/themes/twentytwentyone" + schedule: + interval: "weekly" + open-pull-requests-limit: 20 + groups: + twentytwentyone-sass-css: + patterns: + - "**browserslist*" + - "autoprefixer" + - "*css*" + - "*sass*" + - "!*stylelint*" + twentytwentyone-eslint: + patterns: + - "**eslint*" + twentytwentyone-stylelint: + patterns: + - "**stylelint*" + twentytwentyone-miscellaneous: + patterns: + - "chokidar-cli" + - "minimist" + - "npm-run-all" + + - package-ecosystem: "npm" + directory: "/src/wp-content/themes/twentytwenty" + schedule: + interval: "weekly" + open-pull-requests-limit: 20 + groups: + twentytwenty-css: + patterns: + - "**browserslist*" + - "autoprefixer" + - "*css*" + twentytwenty-stylelint: + patterns: + - "*stylelint*" + twentytwenty-miscellaneous: + patterns: + - "concurrently" + - "@wordpress/scripts" + + - package-ecosystem: "npm" + directory: "/src/wp-content/themes/twentynineteen" + schedule: + interval: "weekly" + open-pull-requests-limit: 20 + groups: + twentynineteen-css-sass: + patterns: + - "**browserslist*" + - "autoprefixer" + - "*css*" + - "*sass*" + twentynineteen-miscellaneous: + patterns: + - "chokidar-cli" + - "npm-run-all" diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 0000000000000..e5ac3bb1e4562 --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,268 @@ +name: Create backport pull requests + +on: + workflow_dispatch: + inputs: + end_branch: + description: 'The branch to end at (e.g. 4.7). Defaults to the oldest branch receiving security updates.' + required: false + type: string + default: '4.7' + pr-name: + description: 'The name for the pull requests. When provided, PRs are titled " - branch".' + required: false + type: string + default: '' + commit-sha: + description: 'The full length commit hash to cherry-pick. When provided, PR numbers are ignored.' + required: false + type: string + default: '' + pr_numbers: + description: 'Comma-separated PR numbers. Each PR is merged as a single commit using the PR title as the commit message. Ignored when a commit SHA is provided.' + required: false + type: string + default: '' + pr-source: + description: 'The repository the PR numbers belong to.' + required: false + type: choice + default: 'upstream' + options: + - upstream + - current + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + +jobs: + get-branches: + name: Get target branches + runs-on: ubuntu-24.04 + outputs: + branches: ${{ steps.branches.outputs.result }} + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + # Read keys from .version-support-php.json, filter to those >= end_branch, + # convert dashes to dots, and sort numerically descending. + # The first key is always the version in active development on trunk, so skip it. + - name: Get target branches + id: branches + env: + INPUTS_END_BRANCH: ${{ inputs.end_branch }} + run: | + END_X=$(echo "${INPUTS_END_BRANCH}" | cut -d. -f1) + END_Y=$(echo "${INPUTS_END_BRANCH}" | cut -d. -f2) + + BRANCHES=$(jq -c \ + --argjson x "$END_X" \ + --argjson y "$END_Y" \ + '[ keys[] | + . as $k | ($k | split("-")) as $p | + select( ($p[0]|tonumber) > $x or + (($p[0]|tonumber) == $x and ($p[1]|tonumber) >= $y) ) | + { v: ($k | gsub("-"; ".")), x: ($p[0]|tonumber), y: ($p[1]|tonumber) } + ] | sort_by(.x, .y) | reverse | .[1:] | map(.v)' \ + .version-support-php.json) + + echo "result=$BRANCHES" >> "$GITHUB_OUTPUT" + + backport: + name: 'Backport to ${{ matrix.branch }}' + needs: [ 'get-branches' ] + if: ${{ needs.get-branches.outputs.branches != '[]' }} + runs-on: ubuntu-24.04 + permissions: + contents: write + pull-requests: write + strategy: + fail-fast: false + matrix: + branch: ${{ fromJson( needs.get-branches.outputs.branches ) }} + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: 'true' + + - name: Set up git identity + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Add upstream remote + id: upstream + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + UPSTREAM=$(gh repo view "${{ github.repository }}" --json parent --jq '.parent.full_name // empty') + if [ -n "$UPSTREAM" ]; then + git remote add upstream "https://github.com/${UPSTREAM}.git" + git fetch upstream + echo "repo=$UPSTREAM" >> "$GITHUB_OUTPUT" + else + echo "repo=${{ github.repository }}" >> "$GITHUB_OUTPUT" + fi + + # Determine the name of the branch for the pull request. + # + # 1. pr-name (normalized to alphanumeric, hyphens, and periods only) + # 2. commit-sha + # 3. pr_numbers with commas replaced by hyphens + - name: Determine backport branch name + id: backport-branch + env: + INPUTS_PR_NAME: ${{ inputs.pr-name }} + MATRIX_BRANCH: ${{ matrix.branch }} + INPUTS_COMMIT_SHA: ${{ inputs.commit-sha }} + INPUTS_PR_NUMBERS: ${{ inputs.pr_numbers }} + run: | + if [ -n "${INPUTS_PR_NAME}" ]; then + echo "name=backport/${MATRIX_BRANCH}-$(echo "${INPUTS_PR_NAME}" | tr -cs '[:alnum:].-' '-' | sed 's/^-//;s/-$//')" >> "$GITHUB_OUTPUT" + elif [ -n "${INPUTS_COMMIT_SHA}" ]; then + echo "name=backport/${MATRIX_BRANCH}-${INPUTS_COMMIT_SHA}" >> "$GITHUB_OUTPUT" + else + echo "name=backport/${MATRIX_BRANCH}-$(echo "${INPUTS_PR_NUMBERS}" | tr -d ' ' | tr ',' '-')" >> "$GITHUB_OUTPUT" + fi + + - name: Create backport branch + env: + STEPS_BACKPORT_BRANCH_OUTPUTS_NAME: ${{ steps.backport-branch.outputs.name }} + MATRIX_BRANCH: ${{ matrix.branch }} + run: | + if git ls-remote --exit-code --heads origin "${STEPS_BACKPORT_BRANCH_OUTPUTS_NAME}" > /dev/null 2>&1; then + echo "::error::Branch '${STEPS_BACKPORT_BRANCH_OUTPUTS_NAME}' already exists on origin." + exit 1 + fi + + git checkout -b "${STEPS_BACKPORT_BRANCH_OUTPUTS_NAME}" "origin/${MATRIX_BRANCH}" + + - name: Cherry-pick commit + if: ${{ inputs['commit-sha'] != '' }} + env: + INPUTS_COMMIT_SHA: ${{ inputs.commit-sha }} + run: | + COMMIT="${INPUTS_COMMIT_SHA}" + PARENTS=$(git cat-file -p "$COMMIT" | grep -c '^parent ' || true) + + if [ "$PARENTS" -gt 1 ]; then + git cherry-pick -m 1 "$COMMIT" + else + git cherry-pick "$COMMIT" + fi + + - name: Merge PRs + if: ${{ inputs['commit-sha'] == '' && inputs.pr_numbers != '' }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + STEPS_UPSTREAM_OUTPUTS_REPO: ${{ steps.upstream.outputs.repo }} + INPUTS_PR_NUMBERS: ${{ inputs.pr_numbers }} + INPUTS_PR_SOURCE: ${{ inputs.pr-source }} + run: | + if [ "${INPUTS_PR_SOURCE}" = "upstream" ]; then + PR_REPO="${STEPS_UPSTREAM_OUTPUTS_REPO}" + else + PR_REPO="${GITHUB_REPOSITORY}" + fi + + IFS=',' read -ra PR_LIST <<< "${INPUTS_PR_NUMBERS}" + + UPSTREAM_URL="https://github.com/${STEPS_UPSTREAM_OUTPUTS_REPO}.git" + + for PR_NUMBER in "${PR_LIST[@]}"; do + PR_NUMBER=$(echo "$PR_NUMBER" | tr -d ' ') + + PR_DATA=$(gh pr view "$PR_NUMBER" --repo "$PR_REPO" --json title,mergeCommit,baseRefName) + PR_TITLE=$(echo "$PR_DATA" | jq -r '.title') + MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid') + + if [ -n "$MERGE_COMMIT" ] && [ "$MERGE_COMMIT" != "null" ]; then + # PR is merged: cherry-pick its merge commit. + # Determine if it is a merge commit or squash commit. + PARENTS=$(git cat-file -p "$MERGE_COMMIT" | grep -c '^parent ' || true) + + if [ "$PARENTS" -gt 1 ]; then + git cherry-pick -m 1 --no-commit "$MERGE_COMMIT" + else + git cherry-pick --no-commit "$MERGE_COMMIT" + fi + else + # PR is open or closed without merging: apply its changes as a diff + # against the point where it diverged from its base branch. + BASE_REF=$(echo "$PR_DATA" | jq -r '.baseRefName') + + git fetch "$UPSTREAM_URL" "$BASE_REF" + BASE_SHA=$(git rev-parse FETCH_HEAD) + + git fetch "$UPSTREAM_URL" "refs/pull/${PR_NUMBER}/head" + PR_HEAD_SHA=$(git rev-parse FETCH_HEAD) + + MERGE_BASE=$(git merge-base "$PR_HEAD_SHA" "$BASE_SHA") + git diff "$MERGE_BASE" "$PR_HEAD_SHA" | git apply --index + fi + + git commit -m "$PR_TITLE" + done + + - name: Push backport branch + env: + STEPS_BACKPORT_BRANCH_OUTPUTS_NAME: ${{ steps.backport-branch.outputs.name }} + run: git push -u origin "${STEPS_BACKPORT_BRANCH_OUTPUTS_NAME}" + + - name: Create pull request + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + STEPS_UPSTREAM_OUTPUTS_REPO: ${{ steps.upstream.outputs.repo }} + INPUTS_PR_NAME: ${{ inputs.pr-name }} + MATRIX_BRANCH: ${{ matrix.branch }} + INPUTS_COMMIT_SHA: ${{ inputs.commit-sha }} + INPUTS_PR_NUMBERS: ${{ inputs.pr_numbers }} + INPUTS_PR_SOURCE: ${{ inputs.pr-source }} + STEPS_BACKPORT_BRANCH_OUTPUTS_NAME: ${{ steps.backport-branch.outputs.name }} + run: | + if [ "${INPUTS_PR_SOURCE}" = "upstream" ]; then + PR_REPO="${STEPS_UPSTREAM_OUTPUTS_REPO}" + else + PR_REPO="${GITHUB_REPOSITORY}" + fi + + if [ -n "${INPUTS_PR_NAME}" ]; then + PR_TITLE="${INPUTS_PR_NAME} - ${MATRIX_BRANCH} branch" + else + PR_TITLE="Backport to ${MATRIX_BRANCH}" + fi + + if [ -n "${INPUTS_COMMIT_SHA}" ]; then + BODY="This pull request backports \`${INPUTS_COMMIT_SHA}\` to the \`${MATRIX_BRANCH}\` branch." + else + BODY="Backport to the \`${MATRIX_BRANCH}\` branch." + fi + + BODY="${BODY}\n\n## Changes\n" + + if [ -n "${INPUTS_COMMIT_SHA}" ]; then + COMMIT_MESSAGE=$(git log --format=%B -n 1 "${INPUTS_COMMIT_SHA}") + BLOCKQUOTE=$(echo "${COMMIT_MESSAGE}" | sed 's/^/> /') + BODY="${BODY}\n${BLOCKQUOTE}" + fi + + if [ -n "${INPUTS_PR_NUMBERS}" ] && [ -z "${INPUTS_COMMIT_SHA}" ]; then + IFS=',' read -ra PR_LIST <<< "${INPUTS_PR_NUMBERS}" + for PR_NUMBER in "${PR_LIST[@]}"; do + PR_NUMBER=$(echo "$PR_NUMBER" | tr -d ' ') + BODY="${BODY}\n- ${PR_REPO}#${PR_NUMBER}" + done + fi + + gh pr create \ + --base "${MATRIX_BRANCH}" \ + --head "${STEPS_BACKPORT_BRANCH_OUTPUTS_NAME}" \ + --title "$PR_TITLE" \ + --assignee "${GITHUB_ACTOR}" \ + --body "$(echo -e "$BODY")" diff --git a/.github/workflows/check-built-files.yml b/.github/workflows/check-built-files.yml index 01a239c4eb3b0..72a1ad4e1a0a4 100644 --- a/.github/workflows/check-built-files.yml +++ b/.github/workflows/check-built-files.yml @@ -1,4 +1,4 @@ -# Checks for uncommitted changes to built files in pull requests. +# Checks for uncommitted or unexpected changes to built files within pull requests. name: Check Built Files (PRs) on: @@ -31,6 +31,7 @@ on: # Confirm any changes to relevant workflow files. - '.github/workflows/check-built-files.yml' - '.github/workflows/reusable-check-built-files.yml' + - '.github/workflows/reusable-compare-built-files-*.yml' # Changes to the default themes should be handled by the themes workflows. - '!src/wp-content/themes/twenty**' @@ -46,9 +47,18 @@ concurrency: permissions: {} jobs: + # Checks built files for uncommitted changes. check-for-built-file-changes: - name: Check built files - if: ${{ github.repository == 'wordpress/wordpress-develop' }} + name: Check for uncommitted changes + if: ${{ github.repository == 'WordPress/wordpress-develop' || github.event_name == 'pull_request' }} uses: ./.github/workflows/reusable-check-built-files.yml permissions: contents: read + + # Compares the build directory with the WordPress/WordPress repository. + compare-with-build-server: + name: Compare built files to WordPress/WordPress + uses: ./.github/workflows/reusable-compare-built-files-v1.yml + permissions: + contents: read + #if: ${{ github.repository == 'WordPress/wordpress-develop' || github.event_name == 'pull_request' }} diff --git a/.github/workflows/pull-request-comments.yml b/.github/workflows/pull-request-comments.yml index da30e2feb7f11..8744fe7be4444 100644 --- a/.github/workflows/pull-request-comments.yml +++ b/.github/workflows/pull-request-comments.yml @@ -5,7 +5,7 @@ on: pull_request_target: types: [ 'opened', 'synchronize', 'reopened', 'edited' ] workflow_run: - workflows: [ 'Test Build Processes' ] + workflows: [ 'Check Built Files (PRs)', 'Test Build Processes' ] types: - completed @@ -22,6 +22,7 @@ permissions: {} jobs: # Comments on a pull request when the author is a first time contributor. post-welcome-message: + name: Contributor welcome message runs-on: ubuntu-24.04 permissions: issues: write @@ -79,7 +80,7 @@ jobs: # Leaves a comment on a pull request with a link to test the changes in a WordPress Playground instance. playground-details: - name: Comment on a pull request with Playground details + name: Leave Playground testing details runs-on: ubuntu-24.04 permissions: issues: write @@ -87,6 +88,7 @@ jobs: if: > github.repository == 'WordPress/wordpress-develop' && github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.name == 'Test Build Processes' && github.event.workflow_run.conclusion == 'success' steps: - name: Download artifact @@ -180,6 +182,145 @@ jobs: github.rest.issues.createComment( commentInfo ); + # Leaves a comment on a pull request noting differences between the PR and the build server. + build-server-comparison: + name: Note differences with the build server + runs-on: ubuntu-24.04 + permissions: + issues: write + pull-requests: write + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.name == 'Check Built Files (PRs)' && + github.event.workflow_run.conclusion == 'success' + steps: + - name: Download artifact + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + RUN_ID: ${{ github.event.workflow_run.id }} + with: + script: | + const artifacts = await github.rest.actions.listWorkflowRunArtifacts( { + owner: context.repo.owner, + repo: context.repo.repo, + run_id: process.env.RUN_ID, + } ); + + const matchArtifact = artifacts.data.artifacts.filter( ( artifact ) => { + return artifact.name === 'build-server-comparison' + } )[0]; + + if ( ! matchArtifact ) { + core.setFailed( 'No artifact found!' ); + return; + } + + const download = await github.rest.actions.downloadArtifact( { + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + } ); + + const fs = require( 'fs' ); + fs.writeFileSync( '${{github.workspace}}/build-server-comparison.zip', Buffer.from( download.data ) ) + + - name: Unzip the artifact containing the comparison info + run: unzip build-server-comparison.zip + + - name: Leave a comment with any differences noticed + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const fs = require( 'fs' ); + const issue_number = Number( fs.readFileSync( './NR' ) ); + const fileChanges = fs.readFileSync( './file-changes.txt', 'utf8' ); + const diffContents = fs.readFileSync( './changes.diff', 'utf8' ); + const MAX_DIFF_LENGTH = 50000; // GitHub has a 65,536 character limit for comments. + + 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 after the first successful build. Check for the presence of a comment and bail early. + const commentInfo = { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number, + }; + + const comments = ( await github.rest.issues.listComments( commentInfo ) ).data; + + for ( const currentComment of comments ) { + if ( currentComment.user.type === 'Bot' && currentComment.body.includes( 'Build Server Comparison' ) ) { + commentInfo.comment_id = currentComment.id; + break; + } + }; + + commentInfo.body = "## Build Server Comparison\n\n"; + + // Post or update the comment. + if ( fileChanges.trim() === '' ) { + commentInfo.body += 'The contents of the `build` directory after running `npm run build` matches the contents of the WordPress/WordPress repository. No differences were found.'; + } else { + commentInfo.body += `The contents of the \`build\` directory after running \`npm run build\` has been compared with the contents of the WordPress/WordPress repository. + + **Review these differences carefully for any unexpected results.** + + ### List of Modified Files + + \`\`\` + ${ fileChanges } + \`\`\` + + ### Full Diff File + `; + + if ( diffContents.length > MAX_DIFF_LENGTH ) { + const cutoff = diffContents.lastIndexOf( '\n', MAX_DIFF_LENGTH ); + const truncated = diffContents.substring( 0, cutoff ); + + commentInfo.body += `
+ Click to expand (truncated) + + \`\`\`diff + ${ truncated } + \`\`\` + + ⚠️ The diff was too large to display in full. + +
`; + } else { + commentInfo.body += `
+ Click to expand + + \`\`\`diff + ${ diffContents } + \`\`\` + +
`; + } + + commentInfo.body += `\n\n[Download the complete .diff file from the workflow run](https://github.com/${ context.repo.owner }/${ context.repo.repo }/actions/runs/${ context.payload.workflow_run.id }).`; + } + + if ( commentInfo.comment_id ) { + github.rest.issues.updateComment( commentInfo ); + } else { + github.rest.issues.createComment( commentInfo ); + } + # Manages comments reminding contributors to include a Trac ticket link when opening a pull request. trac-ticket-check: name: Manage Trac ticket reminders for pull requests diff --git a/.github/workflows/reusable-check-built-files.yml b/.github/workflows/reusable-check-built-files.yml index 11d97639a30fc..30bb3ed5021b4 100644 --- a/.github/workflows/reusable-check-built-files.yml +++ b/.github/workflows/reusable-check-built-files.yml @@ -1,7 +1,7 @@ ## # A reusable workflow that checks for uncommitted changes to built files in pull requests. ## -name: Check Built Files (PRs) +name: Check for Changes to Versioned Files (reusable) on: workflow_call: @@ -9,7 +9,7 @@ on: permissions: {} jobs: - # Checks a PR for uncommitted changes to built files. + # Checks a PR for uncommitted changes to versioned files. # # When changes are detected, the patch is stored as an artifact for processing by the Commit Built File Changes # workflow. @@ -29,8 +29,8 @@ jobs: # - Displays the result of git diff for debugging purposes. # - Saves the diff to a patch file. # - Uploads the patch file as an artifact. - update-built-files: - name: Check and update built files + check-versioned-files: + name: Check for changes runs-on: ubuntu-24.04 timeout-minutes: 10 permissions: diff --git a/.github/workflows/reusable-compare-built-files-v1.yml b/.github/workflows/reusable-compare-built-files-v1.yml new file mode 100644 index 0000000000000..59fa79dc5e111 --- /dev/null +++ b/.github/workflows/reusable-compare-built-files-v1.yml @@ -0,0 +1,110 @@ +## +# A reusable workflow that compares the results of the build script with the most recent commit to WordPress/WordPress. +## +name: Compare Built Files (reusable) + +on: + workflow_call: + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + +jobs: + # Runs the PHP coding standards checks. + # + # Violations are reported inline with annotations. + # + # Performs the following steps: + # - Checks out the repository. + # - Sets up Node.js. + # - Sets up PHP. + # - Installs Composer dependencies. + # - Logs debug information about the GitHub Action runner. + # - Installs npm dependencies. + # - Builds WordPress to run from the build directory. + # - Ensures version-controlled files are not modified or deleted. + # - Checks out the WordPress/WordPress repository. + # - Creates a directory for text files to be uploaded as an artifact. + # - Stores a list of files that differ in the build directory from WordPress/WordPress. + # - Stores a diff file comparing the build directory to WordPress/WordPress. + # - Saves the pull request number to a text file. + # - Uploads the generated files as an artifact. + compare-built-files: + name: Compare built files to WordPress/WordPress + runs-on: ubuntu-24.04 + permissions: + contents: read + timeout-minutes: 10 + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: '.nvmrc' + cache: npm + + - name: Set up PHP + uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.37.0 + with: + php-version: '8.4' + coverage: none + + # Since Composer dependencies are installed using `composer update` and no lock file is in version control, + # passing a custom cache suffix ensures that the cache is flushed at least once per week. + - name: Install Composer dependencies + uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # v4.0.0 + with: + custom-cache-suffix: $(/bin/date -u --date='last Mon' "+%F") + + - name: Log debug information + run: | + npm --version + node --version + git --version + + - name: Install npm Dependencies + run: npm ci + + - name: Build WordPress to run from build directory + run: npm run build + + - name: Ensure version-controlled files are not modified or deleted + run: git diff --exit-code + + - name: Checkout WordPress/WordPress + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: 'WordPress/WordPress' + ref: ${{ github.base_ref == 'trunk' && 'master' || format( '{0}-branch', github.base_ref ) }} + path: ${{ github.workspace }}/build-server + show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false + + - name: Create directory for artifacts + run: mkdir artifacts + + - name: Create a list of files that have changed + run: diff -rq ${{ github.workspace }}/build ${{ github.workspace }}/build-server | sed "s|${{ github.workspace }}/||g" > artifacts/file-changes.txt + + - name: Create a list of files that have changed + run: diff -r ${{ github.workspace }}/build ${{ github.workspace }}/build-server | sed "s|${{ github.workspace }}/||g" > artifacts/changes.diff + + - name: Save PR number + run: echo "${EVENT_NUMBER}" > ./artifacts/NR + env: + EVENT_NUMBER: ${{ github.event.number }} + + # Uploads the associated text files as an artifact. + - name: Upload comparison results as an artifact + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + if: ${{ github.repository == 'WordPress/wordpress-develop' && github.event_name == 'pull_request' }} + with: + name: build-server-comparison + path: artifacts/ diff --git a/package-lock.json b/package-lock.json index a48bff6270b72..76bd12a158802 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,7 +56,7 @@ "chalk": "5.6.2", "check-node-version": "4.2.1", "cssnano": "7.1.3", - "dotenv": "17.3.1", + "dotenv": "17.4.2", "dotenv-expand": "12.0.3", "grunt": "1.6.1", "grunt-banner": "^0.6.0", @@ -91,7 +91,7 @@ "terser-webpack-plugin": "5.4.0", "typescript": "5.9.3", "uuid": "13.0.0", - "wait-on": "9.0.4", + "wait-on": "9.0.5", "webpack": "5.105.4" }, "engines": { @@ -4733,9 +4733,9 @@ "dev": true }, "node_modules/@standard-schema/spec": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", - "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, "license": "MIT" }, @@ -8826,15 +8826,15 @@ } }, "node_modules/axios": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", - "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", + "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", "dev": true, "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", - "proxy-from-env": "^1.1.0" + "proxy-from-env": "^2.1.0" } }, "node_modules/axios/node_modules/form-data": { @@ -8854,6 +8854,16 @@ "node": ">= 6" } }, + "node_modules/axios/node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -12755,9 +12765,9 @@ } }, "node_modules/dotenv": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", - "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -22266,9 +22276,9 @@ } }, "node_modules/joi": { - "version": "18.0.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-18.0.2.tgz", - "integrity": "sha512-RuCOQMIt78LWnktPoeBL0GErkNaJPTBGcYuyaBvUOQSpcpcLfWrHPPihYdOGbV5pam9VTWbeoF7TsGiHugcjGA==", + "version": "18.1.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.1.2.tgz", + "integrity": "sha512-rF5MAmps5esSlhCA+N1b6IYHDw9j/btzGaqfgie522jS02Ju/HXBxamlXVlKEHAxoMKQL77HWI8jlqWsFuekZA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -22278,7 +22288,7 @@ "@hapi/pinpoint": "^2.0.1", "@hapi/tlds": "^1.1.1", "@hapi/topo": "^6.0.2", - "@standard-schema/spec": "^1.0.0" + "@standard-schema/spec": "^1.1.0" }, "engines": { "node": ">= 20" @@ -32626,15 +32636,15 @@ } }, "node_modules/wait-on": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.0.4.tgz", - "integrity": "sha512-k8qrgfwrPVJXTeFY8tl6BxVHiclK11u72DVKhpybHfUL/K6KM4bdyK9EhIVYGytB5MJe/3lq4Tf0hrjM+pvJZQ==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.0.5.tgz", + "integrity": "sha512-qgnbHDfDTRIp73ANEJNRW/7kn8CrDUcvZz18xotJQku/P4saTGkbIzvnMZebPmVvVNUiRq1qWAPyqCH+W4H8KA==", "dev": true, "license": "MIT", "dependencies": { - "axios": "^1.13.5", - "joi": "^18.0.2", - "lodash": "^4.17.23", + "axios": "^1.15.0", + "joi": "^18.1.2", + "lodash": "^4.18.1", "minimist": "^1.2.8", "rxjs": "^7.8.2" }, @@ -32645,6 +32655,13 @@ "node": ">=20.0.0" } }, + "node_modules/wait-on/node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/wait-on/node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", diff --git a/package.json b/package.json index 4d0b8110e0a9f..11d56a5ce6eb7 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "chalk": "5.6.2", "check-node-version": "4.2.1", "cssnano": "7.1.3", - "dotenv": "17.3.1", + "dotenv": "17.4.2", "dotenv-expand": "12.0.3", "grunt": "1.6.1", "grunt-banner": "^0.6.0", @@ -78,7 +78,7 @@ "terser-webpack-plugin": "5.4.0", "typescript": "5.9.3", "uuid": "13.0.0", - "wait-on": "9.0.4", + "wait-on": "9.0.5", "webpack": "5.105.4" }, "dependencies": {