From 1e31e45b928564e8eeb323e2b25ff4882f67fd4c Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 17 Apr 2026 11:35:58 -0400 Subject: [PATCH 1/9] Reintroduce a Dependabot configuration file. --- .github/dependabot.yml | 213 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 .github/dependabot.yml 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" From 21d1fc06ee2dffa49ef0a45563b67b59e546fabd Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 17 Apr 2026 11:41:49 -0400 Subject: [PATCH 2/9] Add a workflow to create backport PRs. --- .github/workflows/backport.yml | 369 +++++++++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 .github/workflows/backport.yml diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 0000000000000..a6091199be590 --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,369 @@ +name: Create backport pull requests + +on: + workflow_dispatch: + inputs: + end_branch: + description: 'The branch to end at (e.g. 6.9). Defaults to the current supported branch.' + required: false + type: string + default: '7.0' + pr-name: + description: 'Pull request name (format is " - branch".' + required: false + type: string + default: '' + commit-sha: + description: 'Full length commit hash to stage for backport.' + required: false + type: string + default: '' + pr_numbers: + description: 'Comma-separated PR numbers. Ignored when a SHA is provided.' + required: false + type: string + default: '' + repo-source: + description: 'Repository to merge changes from.' + required: false + type: choice + default: 'upstream' + options: + - upstream + - current + pr-target: + description: 'Repository to submit pull requests to.' + required: false + type: choice + default: 'current' + options: + - upstream + - current + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + +jobs: + validate-inputs: + name: Validate inputs + runs-on: ubuntu-24.04 + steps: + - name: Ensure a commit SHA or PR numbers are provided + env: + INPUTS_COMMIT_SHA: ${{ inputs.commit-sha }} + INPUTS_PR_NUMBERS: ${{ inputs.pr_numbers }} + run: | + if [ -z "${INPUTS_COMMIT_SHA}" ] && [ -z "${INPUTS_PR_NUMBERS}" ]; then + echo "::error::A commit SHA or PR number(s) must be included." + exit 1 + fi + + get-branches: + name: Get target branches + needs: [ 'validate-inputs' ] + 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: [ 'validate-inputs', '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 'if .parent then "\(.parent.owner.login)/\(.parent.name)" else empty end') + 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 + id: 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_REPO_SOURCE: ${{ inputs.repo-source }} + run: | + if [ "${INPUTS_REPO_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" + RESULTS="" + FAILED=false + + 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') + + set +e + 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 + APPLY_EXIT=$? + set -e + + if [ $APPLY_EXIT -eq 0 ]; then + git commit -m "$PR_TITLE" + RESULTS="${RESULTS}${PR_NUMBER}=✅ " + else + git cherry-pick --abort 2>/dev/null || git reset --hard HEAD + RESULTS="${RESULTS}${PR_NUMBER}=❌ " + FAILED=true + break + fi + done + + echo "results=${RESULTS}" >> "$GITHUB_OUTPUT" + + if [ "$FAILED" = "true" ]; then + exit 1 + fi + + - 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 + id: create-pr + 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_REPO_SOURCE: ${{ inputs.repo-source }} + INPUTS_PR_TARGET: ${{ inputs.pr-target }} + STEPS_BACKPORT_BRANCH_OUTPUTS_NAME: ${{ steps.backport-branch.outputs.name }} + run: | + if [ "${INPUTS_REPO_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}\` (https://github.com/${STEPS_UPSTREAM_OUTPUTS_REPO}/commit/${INPUTS_COMMIT_SHA}) to the \`${MATRIX_BRANCH}\` branch." + else + BODY="Backports to the \`${MATRIX_BRANCH}\` branch." + fi + + BODY="${BODY}\n\n## Changes Included\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 + + if [ "${INPUTS_PR_TARGET}" = "upstream" ]; then + PR_REPO="${STEPS_UPSTREAM_OUTPUTS_REPO}" + PR_HEAD="${GITHUB_REPOSITORY_OWNER}:${STEPS_BACKPORT_BRANCH_OUTPUTS_NAME}" + else + PR_REPO="${GITHUB_REPOSITORY}" + PR_HEAD="${STEPS_BACKPORT_BRANCH_OUTPUTS_NAME}" + fi + + PR_URL=$(gh pr create \ + --repo "${PR_REPO}" \ + --base "${MATRIX_BRANCH}" \ + --head "${PR_HEAD}" \ + --title "$PR_TITLE" \ + --assignee "${GITHUB_ACTOR}" \ + --draft \ + --body "$(echo -e "$BODY")") + + if gh label list --repo "${PR_REPO}" --json name --jq '[.[].name] | contains(["Auto-backport"])' | grep -q 'true'; then + gh pr edit "$PR_URL" --repo "${PR_REPO}" --add-label 'Auto-backport' + else + echo "::notice::The 'Auto-backport' label does not exist on ${PR_REPO}. Consider adding it so that backport pull requests can be identified easily." + fi + + echo "url=${PR_URL}" >> "$GITHUB_OUTPUT" + + - name: Write job summary + if: always() + env: + MATRIX_BRANCH: ${{ matrix.branch }} + INPUTS_COMMIT_SHA: ${{ inputs.commit-sha }} + INPUTS_PR_NUMBERS: ${{ inputs.pr_numbers }} + STEPS_MERGE_PRS_OUTPUTS_RESULTS: ${{ steps.merge-prs.outputs.results }} + STEPS_CREATE_PR_OUTPUTS_URL: ${{ steps.create-pr.outputs.url }} + run: | + PR_DISPLAY="${STEPS_CREATE_PR_OUTPUTS_URL:-N/A}" + + if [ -n "${INPUTS_PR_NUMBERS}" ] && [ -z "${INPUTS_COMMIT_SHA}" ]; then + IFS=',' read -ra PR_LIST <<< "${INPUTS_PR_NUMBERS}" + + HEADER="| Branch |" + SEPARATOR="| :--- |" + for PR_NUM in "${PR_LIST[@]}"; do + PR_NUM=$(echo "$PR_NUM" | tr -d ' ') + HEADER="${HEADER} #${PR_NUM} |" + SEPARATOR="${SEPARATOR} :---: |" + done + HEADER="${HEADER} Pull Request |" + SEPARATOR="${SEPARATOR} :--- |" + + ROW="| \`${MATRIX_BRANCH}\` |" + for PR_NUM in "${PR_LIST[@]}"; do + PR_NUM=$(echo "$PR_NUM" | tr -d ' ') + STATUS=$(echo "${STEPS_MERGE_PRS_OUTPUTS_RESULTS}" | tr ' ' '\n' | grep "^${PR_NUM}=" | cut -d= -f2) + ROW="${ROW} ${STATUS:-❌} |" + done + ROW="${ROW} ${PR_DISPLAY} |" + + printf '%s\n%s\n%s\n' "$HEADER" "$SEPARATOR" "$ROW" >> "$GITHUB_STEP_SUMMARY" + else + printf '| Branch | Pull Request |\n| :--- | :--- |\n| `%s` | %s |\n' \ + "${MATRIX_BRANCH}" "${PR_DISPLAY}" >> "$GITHUB_STEP_SUMMARY" + fi From 18f328e0016ae62265d3ad95d5417933f4e5af44 Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 17 Apr 2026 13:32:23 -0400 Subject: [PATCH 3/9] Add workflow for testing backport PRs. --- .github/workflows/create-backport-prs.yml | 515 ++++++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100644 .github/workflows/create-backport-prs.yml diff --git a/.github/workflows/create-backport-prs.yml b/.github/workflows/create-backport-prs.yml new file mode 100644 index 0000000000000..6329761f79ba9 --- /dev/null +++ b/.github/workflows/create-backport-prs.yml @@ -0,0 +1,515 @@ +name: Create backport pull requests + +on: + workflow_dispatch: + inputs: + end_branch: + description: 'The branch to end at (e.g. 6.9). Defaults to the current supported branch.' + required: false + type: string + default: '7.0' + pr-name: + description: 'Pull request name (format is " - branch".' + required: false + type: string + default: '' + commit-sha: + description: 'Full length commit hash to stage for backport.' + required: false + type: string + default: '' + pr_numbers: + description: 'Comma-separated PR numbers. Ignored when a SHA is provided.' + required: false + type: string + default: '' + repo-source: + description: 'Repository to merge changes from.' + required: false + type: choice + default: 'upstream' + options: + - upstream + - current + pr-target: + description: 'Repository to submit pull requests to.' + required: false + type: choice + default: 'current' + options: + - upstream + - current + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + +jobs: + # Confirms that enough information is included to attempt a backport. + validate-inputs: + name: Validate inputs + runs-on: ubuntu-24.04 + steps: + - name: Ensure a commit SHA or PR numbers are provided + env: + COMMIT_SHA: ${{ inputs.commit-sha }} + PR_NUMBERS: ${{ inputs.pr_numbers }} + run: | + if [ -z "${COMMIT_SHA}" ] && [ -z "${PR_NUMBERS}" ]; then + echo "::error::A commit SHA or PR number(s) must be included." + exit 1 + fi + + # Generates a list of branches to create backport PRs for. + # + # The keys are read from .version-support-php.json, filtered to only include + # any after the specified end branch, and sort numerically descending. + # + # The first key in the file is always skipped because it represents the next + # version of WordPress in active development in trunk. + # + # Performs the following steps: + # - Checks out the repository. + # - Reads branch versions from .version-support-php.json and outputs a filtered, sorted list. + get-branches: + name: Get target branches + needs: [ 'validate-inputs' ] + 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 + + - name: Get target branches + id: branches + env: + END_BRANCH: ${{ inputs.end_branch }} + run: | + END_X=$(echo "${END_BRANCH}" | cut -d. -f1) + END_Y=$(echo "${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" + + # Resolves shared context and variables used by all matrix jobs. + # + # The branch name suffix is determined in the following order: + # 1. pr-name (normalized to alphanumeric, hyphens, and periods only) + # 2. commit-sha + # 3. pr_numbers with commas replaced by hyphens + # + # Performs the following steps: + # - Determines whether the repository is a fork. + # - Constructs the branch name suffix. + resolve-context: + name: Resolve context + needs: [ 'validate-inputs', 'get-branches' ] + if: ${{ needs.get-branches.outputs.branches != '[]' }} + runs-on: ubuntu-24.04 + permissions: + contents: read + outputs: + upstream-repo: ${{ steps.upstream.outputs.repo }} + branch-suffix: ${{ steps.branch-suffix.outputs.value }} + steps: + - name: Detect upstream repository + id: upstream + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + UPSTREAM=$(gh repo view "${{ github.repository }}" --json parent --jq 'if .parent then "\(.parent.owner.login)/\(.parent.name)" else empty end') + if [ -n "$UPSTREAM" ]; then + echo "This repository is a fork of ${UPSTREAM}. Original repository configured as \`upstream\` remote." + echo "repo=$UPSTREAM" >> "$GITHUB_OUTPUT" + else + echo "This repository is not a fork. No \`upstream\` remote configured." + echo "repo=${{ github.repository }}" >> "$GITHUB_OUTPUT" + fi + + - name: Determine branch name suffix + id: branch-suffix + env: + PR_NAME: ${{ inputs.pr-name }} + COMMIT_SHA: ${{ inputs.commit-sha }} + PR_NUMBERS: ${{ inputs.pr_numbers }} + run: | + if [ -n "${PR_NAME}" ]; then + echo "value=$(echo "${PR_NAME}" | tr -cs '[:alnum:].-' '-' | sed 's/^-//;s/-$//')" >> "$GITHUB_OUTPUT" + elif [ -n "${COMMIT_SHA}" ]; then + echo "value=${COMMIT_SHA}" >> "$GITHUB_OUTPUT" + else + echo "value=$(echo "${PR_NUMBERS}" | tr -d ' ' | tr ',' '-')" >> "$GITHUB_OUTPUT" + fi + + # Attempts to backport the specified changes in the desired branches. + # + # Performs the following steps: + # - Checks out the repository. + # - Configures the Git author. + # - Configures the upstream remote (forks only). + # - Creates a new branch. + # - Performs a `git cherry-pick` when a SHA value is specified. + # - Attempts to merge changes from the pull requests specified. + # - Pushes the new branch to the origin remote. + backport: + name: 'Backport to ${{ matrix.branch }}' + needs: [ 'validate-inputs', 'get-branches', 'resolve-context' ] + runs-on: ubuntu-24.04 + permissions: + contents: 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: Configure git user name and email + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Add upstream remote + env: + UPSTREAM_REPO: ${{ needs.resolve-context.outputs.upstream-repo }} + run: | + if [ "${UPSTREAM_REPO}" != "${{ github.repository }}" ]; then + git remote add upstream "https://github.com/${UPSTREAM_REPO}.git" + git fetch upstream + fi + + - name: Create backport branch + env: + MATRIX_BRANCH: ${{ matrix.branch }} + HEAD_BRANCH_SUFFIX: ${{ needs.resolve-context.outputs.branch-suffix }} + run: | + BRANCH_NAME="backport/${MATRIX_BRANCH}-${HEAD_BRANCH_SUFFIX}" + + if git ls-remote --exit-code --heads origin "${BRANCH_NAME}" > /dev/null 2>&1; then + echo "::error::Branch '${BRANCH_NAME}' already exists on origin." + exit 1 + fi + + git checkout -b "${BRANCH_NAME}" "origin/${MATRIX_BRANCH}" + + - name: Cherry-pick commit + if: ${{ inputs['commit-sha'] != '' }} + env: + COMMIT_SHA: ${{ inputs.commit-sha }} + run: | + COMMIT="${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 + id: merge-prs + if: ${{ inputs['commit-sha'] == '' && inputs.pr_numbers != '' }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + UPSTREAM_REPO: ${{ needs.resolve-context.outputs.upstream-repo }} + PR_NUMBERS: ${{ inputs.pr_numbers }} + REPO_SOURCE: ${{ inputs.repo-source }} + run: | + if [ "${REPO_SOURCE}" = "upstream" ]; then + PR_REPO="${UPSTREAM_REPO}" + else + PR_REPO="${GITHUB_REPOSITORY}" + fi + + IFS=',' read -ra PR_LIST <<< "${PR_NUMBERS}" + + UPSTREAM_URL="https://github.com/${UPSTREAM_REPO}.git" + RESULTS="" + FAILED=false + + 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') + + set +e + 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 + APPLY_EXIT=$? + set -e + + if [ $APPLY_EXIT -eq 0 ]; then + git commit -m "$PR_TITLE" + RESULTS="${RESULTS}${PR_NUMBER}=✅ " + else + git cherry-pick --abort 2>/dev/null || git reset --hard HEAD + RESULTS="${RESULTS}${PR_NUMBER}=❌ " + FAILED=true + break + fi + done + + echo "results=${RESULTS}" >> "$GITHUB_OUTPUT" + + if [ "$FAILED" = "true" ]; then + exit 1 + fi + + - name: Push backport branch + env: + MATRIX_BRANCH: ${{ matrix.branch }} + HEAD_BRANCH_SUFFIX: ${{ needs.resolve-context.outputs.branch-suffix }} + run: git push -u origin "backport/${MATRIX_BRANCH}-${HEAD_BRANCH_SUFFIX}" + + - name: Save results + if: always() + env: + MATRIX_BRANCH: ${{ matrix.branch }} + MERGE_RESULTS: ${{ steps.merge-prs.outputs.results }} + run: | + mkdir -p apply-results + SAFE_BRANCH=$(echo "${MATRIX_BRANCH}" | tr '.' '-') + { + echo "branch=${MATRIX_BRANCH}" + echo "merge_results=${MERGE_RESULTS}" + } > "apply-results/${SAFE_BRANCH}.txt" + + - name: Upload results + if: always() + uses: actions/upload-artifact@v4 + with: + name: apply-results-${{ matrix.branch }} + path: apply-results/ + retention-days: 1 + + # Creates a draft pull request for each successfully applied backport branch. + # Requires only write access to pull requests, keeping git operations separate. + # + # Performs the following steps: + # - Downloads the apply result artifact to confirm changes were applied successfully. + # - Creates a draft pull request targeting the specified repository. + # - Adds the `Auto-backport` label to the pull request if it exists. + # - Formats and uploads a pre-rendered summary row artifact for the report job. + create-pr: + name: 'Create PR for ${{ matrix.branch }}' + needs: [ 'validate-inputs', 'get-branches', 'resolve-context', 'backport' ] + if: ${{ always() && !cancelled() && needs.resolve-context.result == 'success' }} + runs-on: ubuntu-24.04 + permissions: + contents: read + pull-requests: write + strategy: + fail-fast: false + matrix: + branch: ${{ fromJson( needs.get-branches.outputs.branches ) }} + steps: + - name: Download apply result + id: apply-result + uses: actions/download-artifact@v4 + with: + name: apply-results-${{ matrix.branch }} + path: apply-result/ + continue-on-error: true + + - name: Create pull request + id: create-pr + if: ${{ steps.apply-result.outcome == 'success' }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + UPSTREAM_REPO: ${{ needs.resolve-context.outputs.upstream-repo }} + HEAD_BRANCH_SUFFIX: ${{ needs.resolve-context.outputs.branch-suffix }} + PR_NAME: ${{ inputs.pr-name }} + MATRIX_BRANCH: ${{ matrix.branch }} + COMMIT_SHA: ${{ inputs.commit-sha }} + PR_NUMBERS: ${{ inputs.pr_numbers }} + REPO_SOURCE: ${{ inputs.repo-source }} + PR_TARGET: ${{ inputs.pr-target }} + run: | + BACKPORT_BRANCH="backport/${MATRIX_BRANCH}-${HEAD_BRANCH_SUFFIX}" + + if [ "${REPO_SOURCE}" = "upstream" ]; then + SOURCE_REPO="${UPSTREAM_REPO}" + else + SOURCE_REPO="${GITHUB_REPOSITORY}" + fi + + if [ -n "${PR_NAME}" ]; then + PR_TITLE="${PR_NAME} - ${MATRIX_BRANCH} branch" + else + PR_TITLE="Backport to ${MATRIX_BRANCH}" + fi + + if [ -n "${COMMIT_SHA}" ]; then + BODY="This pull request backports \`${COMMIT_SHA}\` (https://github.com/${UPSTREAM_REPO}/commit/${COMMIT_SHA}) to the \`${MATRIX_BRANCH}\` branch." + else + BODY="Backports to the \`${MATRIX_BRANCH}\` branch." + fi + + BODY="${BODY}\n\n## Changes Included\n" + + if [ -n "${COMMIT_SHA}" ]; then + COMMIT_MESSAGE=$(gh api "repos/${UPSTREAM_REPO}/commits/${COMMIT_SHA}" --jq '.commit.message') + BLOCKQUOTE=$(echo "${COMMIT_MESSAGE}" | sed 's/^/> /') + BODY="${BODY}\n${BLOCKQUOTE}" + fi + + if [ -n "${PR_NUMBERS}" ] && [ -z "${COMMIT_SHA}" ]; then + IFS=',' read -ra PR_LIST <<< "${PR_NUMBERS}" + for PR_NUMBER in "${PR_LIST[@]}"; do + PR_NUMBER=$(echo "$PR_NUMBER" | tr -d ' ') + BODY="${BODY}\n- ${SOURCE_REPO}#${PR_NUMBER}" + done + fi + + if [ "${PR_TARGET}" = "upstream" ]; then + PR_REPO="${UPSTREAM_REPO}" + PR_HEAD="${GITHUB_REPOSITORY_OWNER}:${BACKPORT_BRANCH}" + else + PR_REPO="${GITHUB_REPOSITORY}" + PR_HEAD="${BACKPORT_BRANCH}" + fi + + PR_URL=$(gh pr create \ + --repo "${PR_REPO}" \ + --base "${MATRIX_BRANCH}" \ + --head "${PR_HEAD}" \ + --title "$PR_TITLE" \ + --assignee "${GITHUB_ACTOR}" \ + --draft \ + --body "$(echo -e "$BODY")") + + if gh label list --repo "${PR_REPO}" --json name --jq '[.[].name] | contains(["Auto-backport"])' | grep -q 'true'; then + gh pr edit "$PR_URL" --repo "${PR_REPO}" --add-label 'Auto-backport' + else + echo "::notice::The 'Auto-backport' label does not exist on ${PR_REPO}. Consider adding it so that backport pull requests can be identified easily." + fi + + echo "url=${PR_URL}" >> "$GITHUB_OUTPUT" + + - name: Save summary row + if: always() + env: + MATRIX_BRANCH: ${{ matrix.branch }} + PR_URL: ${{ steps.create-pr.outputs.url }} + COMMIT_SHA: ${{ inputs.commit-sha }} + PR_NUMBERS: ${{ inputs.pr_numbers }} + run: | + mkdir -p summary-row + SAFE_BRANCH=$(echo "${MATRIX_BRANCH}" | tr '.' '-') + MERGE_RESULTS="" + + if [ -f "apply-result/${SAFE_BRANCH}.txt" ]; then + MERGE_RESULTS=$(grep '^merge_results=' "apply-result/${SAFE_BRANCH}.txt" | cut -d= -f2-) + fi + + PR_DISPLAY="${PR_URL:-N/A}" + + if [ -n "${PR_NUMBERS}" ] && [ -z "${COMMIT_SHA}" ]; then + IFS=',' read -ra PR_LIST <<< "${PR_NUMBERS}" + ROW="| \`${MATRIX_BRANCH}\` |" + for PR_NUM in "${PR_LIST[@]}"; do + PR_NUM=$(echo "$PR_NUM" | tr -d ' ') + STATUS=$(echo "${MERGE_RESULTS}" | tr ' ' '\n' | grep "^${PR_NUM}=" | cut -d= -f2) + ROW="${ROW} ${STATUS:-❌} |" + done + ROW="${ROW} ${PR_DISPLAY} |" + else + ROW="| \`${MATRIX_BRANCH}\` | ${PR_DISPLAY} |" + fi + + printf '%s\n' "$ROW" > "summary-row/${SAFE_BRANCH}.txt" + + - name: Upload summary row + if: always() + uses: actions/upload-artifact@v4 + with: + name: summary-row-${{ matrix.branch }} + path: summary-row/ + retention-days: 1 + + # Aggregates results from all matrix jobs into a single workflow summary. + # + # Performs the following steps: + # - Downloads pre-rendered summary row artifacts from all create-pr jobs. + # - Writes the table header and appends all rows to the workflow summary. + report: + name: Backport report + needs: [ 'backport', 'create-pr' ] + runs-on: ubuntu-24.04 + if: always() + steps: + - name: Download summary rows + uses: actions/download-artifact@v4 + with: + pattern: summary-row-* + path: summary-rows/ + merge-multiple: true + continue-on-error: true + + - name: Write summary + env: + COMMIT_SHA: ${{ inputs.commit-sha }} + PR_NUMBERS: ${{ inputs.pr_numbers }} + run: | + if [ -n "${PR_NUMBERS}" ] && [ -z "${COMMIT_SHA}" ]; then + IFS=',' read -ra PR_LIST <<< "${PR_NUMBERS}" + + HEADER="| Branch |" + SEPARATOR="| :--- |" + for PR_NUM in "${PR_LIST[@]}"; do + PR_NUM=$(echo "$PR_NUM" | tr -d ' ') + HEADER="${HEADER} #${PR_NUM} |" + SEPARATOR="${SEPARATOR} :---: |" + done + HEADER="${HEADER} Pull Request |" + SEPARATOR="${SEPARATOR} :--- |" + + printf '%s\n%s\n' "$HEADER" "$SEPARATOR" >> "$GITHUB_STEP_SUMMARY" + else + printf '| Branch | Pull Request |\n| :--- | :--- |\n' >> "$GITHUB_STEP_SUMMARY" + fi + + for ROW_FILE in $(ls summary-rows/*.txt 2>/dev/null | sort); do + cat "${ROW_FILE}" >> "$GITHUB_STEP_SUMMARY" + done From f4f934bd1c4a40386aa5f9df2593daef160ab536 Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 17 Apr 2026 19:47:05 -0400 Subject: [PATCH 4/9] Bug fixes and improvements. --- .github/workflows/create-backport-prs.yml | 39 ++++++++++++++++------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/.github/workflows/create-backport-prs.yml b/.github/workflows/create-backport-prs.yml index 6329761f79ba9..f0ad03ecc9636 100644 --- a/.github/workflows/create-backport-prs.yml +++ b/.github/workflows/create-backport-prs.yml @@ -200,7 +200,7 @@ jobs: MATRIX_BRANCH: ${{ matrix.branch }} HEAD_BRANCH_SUFFIX: ${{ needs.resolve-context.outputs.branch-suffix }} run: | - BRANCH_NAME="backport/${MATRIX_BRANCH}-${HEAD_BRANCH_SUFFIX}" + BRANCH_NAME="backport-${MATRIX_BRANCH}/${HEAD_BRANCH_SUFFIX}" if git ls-remote --exit-code --heads origin "${BRANCH_NAME}" > /dev/null 2>&1; then echo "::error::Branch '${BRANCH_NAME}' already exists on origin." @@ -286,7 +286,6 @@ jobs: git cherry-pick --abort 2>/dev/null || git reset --hard HEAD RESULTS="${RESULTS}${PR_NUMBER}=❌ " FAILED=true - break fi done @@ -300,7 +299,7 @@ jobs: env: MATRIX_BRANCH: ${{ matrix.branch }} HEAD_BRANCH_SUFFIX: ${{ needs.resolve-context.outputs.branch-suffix }} - run: git push -u origin "backport/${MATRIX_BRANCH}-${HEAD_BRANCH_SUFFIX}" + run: git push -u origin "backport-${MATRIX_BRANCH}/${HEAD_BRANCH_SUFFIX}" - name: Save results if: always() @@ -317,7 +316,7 @@ jobs: - name: Upload results if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: apply-results-${{ matrix.branch }} path: apply-results/ @@ -346,7 +345,7 @@ jobs: steps: - name: Download apply result id: apply-result - uses: actions/download-artifact@v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: apply-results-${{ matrix.branch }} path: apply-result/ @@ -366,7 +365,7 @@ jobs: REPO_SOURCE: ${{ inputs.repo-source }} PR_TARGET: ${{ inputs.pr-target }} run: | - BACKPORT_BRANCH="backport/${MATRIX_BRANCH}-${HEAD_BRANCH_SUFFIX}" + BACKPORT_BRANCH="backport-${MATRIX_BRANCH}/${HEAD_BRANCH_SUFFIX}" if [ "${REPO_SOURCE}" = "upstream" ]; then SOURCE_REPO="${UPSTREAM_REPO}" @@ -402,6 +401,8 @@ jobs: done fi + BODY="${BODY}\n\n---\n\nThis PR was generated from a [GitHub Actions workflow run](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}) triggered by @${GITHUB_ACTOR}." + if [ "${PR_TARGET}" = "upstream" ]; then PR_REPO="${UPSTREAM_REPO}" PR_HEAD="${GITHUB_REPOSITORY_OWNER}:${BACKPORT_BRANCH}" @@ -434,6 +435,7 @@ jobs: PR_URL: ${{ steps.create-pr.outputs.url }} COMMIT_SHA: ${{ inputs.commit-sha }} PR_NUMBERS: ${{ inputs.pr_numbers }} + PR_NAME: ${{ inputs.pr-name }} run: | mkdir -p summary-row SAFE_BRANCH=$(echo "${MATRIX_BRANCH}" | tr '.' '-') @@ -443,7 +445,22 @@ jobs: MERGE_RESULTS=$(grep '^merge_results=' "apply-result/${SAFE_BRANCH}.txt" | cut -d= -f2-) fi - PR_DISPLAY="${PR_URL:-N/A}" + RUN_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + + if [ -n "${PR_URL}" ]; then + if [ -n "${PR_NUMBERS}" ] && [ -z "${COMMIT_SHA}" ]; then + PR_DISPLAY="${PR_URL}" + else + if [ -n "${PR_NAME}" ]; then + PR_TITLE="${PR_NAME} - ${MATRIX_BRANCH} branch" + else + PR_TITLE="Backport to ${MATRIX_BRANCH}" + fi + PR_DISPLAY="[${PR_TITLE}](${PR_URL})" + fi + else + PR_DISPLAY="[View log](${RUN_URL})" + fi if [ -n "${PR_NUMBERS}" ] && [ -z "${COMMIT_SHA}" ]; then IFS=',' read -ra PR_LIST <<< "${PR_NUMBERS}" @@ -462,7 +479,7 @@ jobs: - name: Upload summary row if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: summary-row-${{ matrix.branch }} path: summary-row/ @@ -475,12 +492,12 @@ jobs: # - Writes the table header and appends all rows to the workflow summary. report: name: Backport report - needs: [ 'backport', 'create-pr' ] + needs: [ 'validate-inputs', 'backport', 'create-pr' ] runs-on: ubuntu-24.04 - if: always() + if: ${{ always() && needs.validate-inputs.result != 'failure' }} steps: - name: Download summary rows - uses: actions/download-artifact@v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: summary-row-* path: summary-rows/ From eacedb6d0edb6b72c79955411b7aa136dd9eaf7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:41:14 +0000 Subject: [PATCH 5/9] Bump the tests-qunit group across 1 directory with 2 updates Bumps the tests-qunit group with 2 updates in the / directory: [grunt-contrib-qunit](https://github.com/gruntjs/grunt-contrib-qunit) and [sinon](https://github.com/sinonjs/sinon). Updates `grunt-contrib-qunit` from 10.1.1 to 10.2.0 - [Release notes](https://github.com/gruntjs/grunt-contrib-qunit/releases) - [Changelog](https://github.com/gruntjs/grunt-contrib-qunit/blob/main/CHANGELOG.md) - [Commits](https://github.com/gruntjs/grunt-contrib-qunit/compare/v10.1.1...v10.2.0) Updates `sinon` from 16.1.3 to 21.1.2 - [Release notes](https://github.com/sinonjs/sinon/releases) - [Changelog](https://github.com/sinonjs/sinon/blob/main/docs/changelog.md) - [Commits](https://github.com/sinonjs/sinon/compare/v16.1.3...v21.1.2) --- updated-dependencies: - dependency-name: grunt-contrib-qunit dependency-version: 10.2.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: tests-qunit - dependency-name: sinon dependency-version: 21.1.2 dependency-type: direct:development update-type: version-update:semver-major dependency-group: tests-qunit ... Signed-off-by: dependabot[bot] --- package-lock.json | 239 ++++++++++++++++++++-------------------------- package.json | 4 +- 2 files changed, 107 insertions(+), 136 deletions(-) diff --git a/package-lock.json b/package-lock.json index a48bff6270b72..102f87ecd36bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "grunt-contrib-cssmin": "~5.0.0", "grunt-contrib-imagemin": "~4.0.0", "grunt-contrib-jshint": "3.2.0", - "grunt-contrib-qunit": "~10.1.1", + "grunt-contrib-qunit": "~10.2.0", "grunt-contrib-uglify": "~5.2.2", "grunt-contrib-watch": "~1.1.0", "grunt-file-append": "0.0.7", @@ -85,7 +85,7 @@ "qunit": "~2.25.0", "react-refresh": "0.14.0", "sass": "1.98.0", - "sinon": "16.1.3", + "sinon": "21.1.2", "sinon-test": "~3.1.6", "source-map-loader": "5.0.0", "terser-webpack-plugin": "5.4.0", @@ -4562,19 +4562,18 @@ "dev": true }, "node_modules/@puppeteer/browsers": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.0.tgz", - "integrity": "sha512-ioXoq9gPxkss4MYhD+SFaU9p1IHFUX0ILAWFPyjGaBdjLsYAlZw6j1iLA0N/m12uVHLFDfSYNF7EQccjinIMDA==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz", + "integrity": "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "debug": "^4.3.5", + "debug": "^4.4.3", "extract-zip": "^2.0.1", "progress": "^2.0.3", - "proxy-agent": "^6.4.0", - "semver": "^7.6.3", - "tar-fs": "^3.0.6", - "unbzip2-stream": "^1.4.3", + "proxy-agent": "^6.5.0", + "semver": "^7.7.4", + "tar-fs": "^3.1.1", "yargs": "^17.7.2" }, "bin": { @@ -4707,31 +4706,26 @@ } }, "node_modules/@sinonjs/samsam": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", - "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-10.0.2.tgz", + "integrity": "sha512-8lVwD1Df1BmzoaOLhMcGGcz/Jyr5QY2KSB75/YK1QgKzoabTeLdIVyhXNZK9ojfSKSdirbXqdbsXXqP9/Ve8+A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@sinonjs/commons": "^2.0.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" } }, - "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, - "dependencies": { - "type-detect": "4.0.8" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", - "dev": true - }, "node_modules/@standard-schema/spec": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", @@ -10618,20 +10612,29 @@ } }, "node_modules/chromium-bidi": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.3.tgz", - "integrity": "sha512-qXlsCmpCZJAnoTYI83Iu6EdYQpMYdVkCfq08KDh2pmlVqK5t5IA9mGs4/LwCwp4fqisSOMXZxP3HIh8w8aRn0A==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz", + "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0", - "zod": "3.23.8" + "mitt": "^3.0.1", + "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, + "node_modules/chromium-bidi/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -17004,14 +17007,14 @@ } }, "node_modules/grunt-contrib-qunit": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-qunit/-/grunt-contrib-qunit-10.1.1.tgz", - "integrity": "sha512-qSzY/aWl4xn8dQc2eAwKrXNB0171WHgb4aA3ZdKkN88csxS3tCD3Eh8ljfsscFAKIKZkhjierRgQypep/aV4NA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-qunit/-/grunt-contrib-qunit-10.2.0.tgz", + "integrity": "sha512-pOvHgMgDfwUFWvnd2FIksQ+jfEI7lZ1voabDiE5GtU+3Bi2Zuq70vRCSeiKSIbI2vZpVHK0zXpUKzFT4LbNIKw==", "dev": true, "license": "MIT", "dependencies": { "eventemitter2": "^6.4.9", - "puppeteer": "^22.0.0" + "puppeteer": "^24.0.0" }, "engines": { "node": ">=18" @@ -22728,12 +22731,6 @@ "node": ">=4.0" } }, - "node_modules/just-extend": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", - "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", - "dev": true - }, "node_modules/keyv": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", @@ -23283,12 +23280,6 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true - }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -24510,29 +24501,6 @@ "dev": true, "optional": true }, - "node_modules/nise": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", - "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^11.2.2", - "@sinonjs/text-encoding": "^0.7.2", - "just-extend": "^6.2.0", - "path-to-regexp": "^6.2.1" - } - }, - "node_modules/nise/node_modules/@sinonjs/fake-timers": { - "version": "11.3.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz", - "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -26110,13 +26078,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true, - "license": "MIT" - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -27511,21 +27472,22 @@ } }, "node_modules/puppeteer": { - "version": "22.15.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.15.0.tgz", - "integrity": "sha512-XjCY1SiSEi1T7iSYuxS82ft85kwDJUS7wj1Z0eGVXKdtr5g4xnVcbjwxhq5xBnpK/E7x1VZZoJDxpjAOasHT4Q==", - "deprecated": "< 24.15.0 is no longer supported", + "version": "24.42.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.42.0.tgz", + "integrity": "sha512-94MoPfFp2eY3eYIMdINkez4IOP5TMHntlZbVx06fHlQTtiQiYgaY0L2Zzfod8PVUkPqP7m3Qlre2v8YS8cudPA==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.3.0", + "@puppeteer/browsers": "2.13.0", + "chromium-bidi": "14.0.0", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1312386", - "puppeteer-core": "22.15.0" + "devtools-protocol": "0.0.1595872", + "puppeteer-core": "24.42.0", + "typed-query-selector": "^2.12.1" }, "bin": { - "puppeteer": "lib/esm/puppeteer/node/cli.js" + "puppeteer": "lib/cjs/puppeteer/node/cli.js" }, "engines": { "node": ">=18" @@ -27615,7 +27577,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/puppeteer/node_modules/cosmiconfig": { "version": "9.0.1", @@ -27644,6 +27607,13 @@ } } }, + "node_modules/puppeteer/node_modules/devtools-protocol": { + "version": "0.0.1595872", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1595872.tgz", + "integrity": "sha512-kRfgp8vWVjBu/fbYCiVFiOqsCk3CrMKEo3WbgGT2NXK2dG7vawWPBljixajVgGK9II8rDO9G0oD0zLt3I1daRg==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/puppeteer/node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -27658,26 +27628,28 @@ } }, "node_modules/puppeteer/node_modules/puppeteer-core": { - "version": "22.15.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.15.0.tgz", - "integrity": "sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA==", + "version": "24.42.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.42.0.tgz", + "integrity": "sha512-T4zXokk/izH01fYPhyyev1A4piWiOKrYq7CUFpdoYQxmOnXoV6YjUabmfIjCYkNspSoAXIxRid3Tw+Vg0fthYg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.3.0", - "chromium-bidi": "0.6.3", - "debug": "^4.3.6", - "devtools-protocol": "0.0.1312386", - "ws": "^8.18.0" + "@puppeteer/browsers": "2.13.0", + "chromium-bidi": "14.0.0", + "debug": "^4.4.3", + "devtools-protocol": "0.0.1595872", + "typed-query-selector": "^2.12.1", + "webdriver-bidi-protocol": "0.4.1", + "ws": "^8.19.0" }, "engines": { "node": ">=18" } }, "node_modules/puppeteer/node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", "dev": true, "license": "MIT", "engines": { @@ -29439,17 +29411,16 @@ "dev": true }, "node_modules/sinon": { - "version": "16.1.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.1.3.tgz", - "integrity": "sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.1.2.tgz", + "integrity": "sha512-FS6mN+/bx7e2ajpXkEmOcWB6xBzWiuNoAQT18/+a20SS4U7FSYl8Ms7N6VTUxN/1JAjkx7aXp+THMC8xdpp0gA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^10.3.0", - "@sinonjs/samsam": "^8.0.0", - "diff": "^5.1.0", - "nise": "^5.1.4", - "supports-color": "^7.2.0" + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^15.3.2", + "@sinonjs/samsam": "^10.0.2", + "diff": "^8.0.4" }, "funding": { "type": "opencollective", @@ -29465,25 +29436,24 @@ "sinon": ">= 2.x" } }, - "node_modules/sinon/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/sinon/node_modules/@sinonjs/fake-timers": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.2.tgz", + "integrity": "sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==", "dev": true, - "engines": { - "node": ">=8" + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" } }, - "node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/sinon/node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">=0.3.1" } }, "node_modules/sirv": { @@ -32073,10 +32043,11 @@ } }, "node_modules/typed-query-selector": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", - "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", - "dev": true + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", + "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", + "dev": true, + "license": "MIT" }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", @@ -32481,13 +32452,6 @@ "node": ">= 4" } }, - "node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true, - "license": "MIT" - }, "node_modules/use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -32708,6 +32672,13 @@ "integrity": "sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q==", "dev": true }, + "node_modules/webdriver-bidi-protocol": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz", + "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index 6f3cd1fbe6c3e..ae77e3b43af2f 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "grunt-contrib-cssmin": "~5.0.0", "grunt-contrib-imagemin": "~4.0.0", "grunt-contrib-jshint": "3.2.0", - "grunt-contrib-qunit": "~10.1.1", + "grunt-contrib-qunit": "~10.2.0", "grunt-contrib-uglify": "~5.2.2", "grunt-contrib-watch": "~1.1.0", "grunt-file-append": "0.0.7", @@ -72,7 +72,7 @@ "qunit": "~2.25.0", "react-refresh": "0.14.0", "sass": "1.98.0", - "sinon": "16.1.3", + "sinon": "21.1.2", "sinon-test": "~3.1.6", "source-map-loader": "5.0.0", "terser-webpack-plugin": "5.4.0", From cad46d3d3968dac04e94a2d381acb10217d322c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:23:54 +0000 Subject: [PATCH 6/9] Fix dashboard getTimeZone QUnit stubs for sinon 21 compatibility Agent-Logs-Url: https://github.com/desrosj/wordpress-develop/sessions/1c594881-ea0b-4e27-aea7-41affd580f30 Co-authored-by: desrosj <359867+desrosj@users.noreply.github.com> --- tests/qunit/wp-admin/js/dashboard.js | 38 ++++++++++++++++++---------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/tests/qunit/wp-admin/js/dashboard.js b/tests/qunit/wp-admin/js/dashboard.js index 035d6e929f277..26893d12400ac 100644 --- a/tests/qunit/wp-admin/js/dashboard.js +++ b/tests/qunit/wp-admin/js/dashboard.js @@ -144,37 +144,49 @@ jQuery( document ).ready( function () { QUnit.module( 'communityEvents.getTimeZone', function() { QUnit.test( 'modern browsers should return a time zone name', function( assert ) { // Simulate a modern browser. - var stub = sinon.stub( Intl.DateTimeFormat.prototype, 'resolvedOptions' ); - stub.returns( { timeZone: 'America/Chicago' } ); + var originalDateTimeFormat = Intl.DateTimeFormat; + Intl.DateTimeFormat = function() { + return { + resolvedOptions: function() { + return { timeZone: 'America/Chicago' }; + } + }; + }; var actual = getTimeZone( startDate ); - stub.restore(); + Intl.DateTimeFormat = originalDateTimeFormat; assert.strictEqual( actual, 'America/Chicago' ); } ); QUnit.test( 'older browsers should fallback to a raw UTC offset', function( assert ) { // Simulate IE11. - var resolvedOptionsStub = sinon.stub( Intl.DateTimeFormat.prototype, 'resolvedOptions' ); - var getTimezoneOffsetStub = sinon.stub( Date.prototype, 'getTimezoneOffset' ); - - resolvedOptionsStub.returns( { timeZone: undefined } ); - - getTimezoneOffsetStub.returns( 300 ); + var originalDateTimeFormat = Intl.DateTimeFormat; + var getFlippedTimeZoneOffsetStub = sinon.stub( wp.communityEvents, 'getFlippedTimeZoneOffset' ); + + Intl.DateTimeFormat = function() { + return { + resolvedOptions: function() { + return { timeZone: undefined }; + } + }; + }; + + getFlippedTimeZoneOffsetStub.returns( -300 ); var actual = getTimeZone( startDate ); assert.strictEqual( actual, -300, 'negative offset' ); // Intentionally opposite, see `getTimeZone()`. - getTimezoneOffsetStub.returns( 0 ); + getFlippedTimeZoneOffsetStub.returns( 0 ); actual = getTimeZone( startDate ); assert.strictEqual( actual, 0, 'no offset' ); - getTimezoneOffsetStub.returns( -300 ); + getFlippedTimeZoneOffsetStub.returns( 300 ); actual = getTimeZone( startDate ); assert.strictEqual( actual, 300, 'positive offset' ); // Intentionally opposite, see `getTimeZone()`. - resolvedOptionsStub.restore(); - getTimezoneOffsetStub.restore(); + Intl.DateTimeFormat = originalDateTimeFormat; + getFlippedTimeZoneOffsetStub.restore(); } ); } ); From 51e02c6444e493ee9d6ae6b099baeeb13ee97b73 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:24:57 +0000 Subject: [PATCH 7/9] Preserve Intl.DateTimeFormat prototype in dashboard timezone test mocks Agent-Logs-Url: https://github.com/desrosj/wordpress-develop/sessions/1c594881-ea0b-4e27-aea7-41affd580f30 Co-authored-by: desrosj <359867+desrosj@users.noreply.github.com> --- tests/qunit/wp-admin/js/dashboard.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/qunit/wp-admin/js/dashboard.js b/tests/qunit/wp-admin/js/dashboard.js index 26893d12400ac..758c44c63e420 100644 --- a/tests/qunit/wp-admin/js/dashboard.js +++ b/tests/qunit/wp-admin/js/dashboard.js @@ -152,6 +152,7 @@ jQuery( document ).ready( function () { } }; }; + Intl.DateTimeFormat.prototype = originalDateTimeFormat.prototype; var actual = getTimeZone( startDate ); @@ -172,6 +173,7 @@ jQuery( document ).ready( function () { } }; }; + Intl.DateTimeFormat.prototype = originalDateTimeFormat.prototype; getFlippedTimeZoneOffsetStub.returns( -300 ); var actual = getTimeZone( startDate ); From a924470d33999b3c4339966100c258f78a002dd2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:26:09 +0000 Subject: [PATCH 8/9] Align Intl.DateTimeFormat test double signature in dashboard QUnit tests Agent-Logs-Url: https://github.com/desrosj/wordpress-develop/sessions/1c594881-ea0b-4e27-aea7-41affd580f30 Co-authored-by: desrosj <359867+desrosj@users.noreply.github.com> --- tests/qunit/wp-admin/js/dashboard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qunit/wp-admin/js/dashboard.js b/tests/qunit/wp-admin/js/dashboard.js index 758c44c63e420..4c60e93be1a4d 100644 --- a/tests/qunit/wp-admin/js/dashboard.js +++ b/tests/qunit/wp-admin/js/dashboard.js @@ -145,7 +145,7 @@ jQuery( document ).ready( function () { QUnit.test( 'modern browsers should return a time zone name', function( assert ) { // Simulate a modern browser. var originalDateTimeFormat = Intl.DateTimeFormat; - Intl.DateTimeFormat = function() { + Intl.DateTimeFormat = function( locales, options ) { return { resolvedOptions: function() { return { timeZone: 'America/Chicago' }; @@ -166,7 +166,7 @@ jQuery( document ).ready( function () { var originalDateTimeFormat = Intl.DateTimeFormat; var getFlippedTimeZoneOffsetStub = sinon.stub( wp.communityEvents, 'getFlippedTimeZoneOffset' ); - Intl.DateTimeFormat = function() { + Intl.DateTimeFormat = function( locales, options ) { return { resolvedOptions: function() { return { timeZone: undefined }; From a43c418c3ae5e2c48095e6c1e8c80ef32c4238aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:27:21 +0000 Subject: [PATCH 9/9] Use constructor-compatible Intl.DateTimeFormat mocks in dashboard tests Agent-Logs-Url: https://github.com/desrosj/wordpress-develop/sessions/1c594881-ea0b-4e27-aea7-41affd580f30 Co-authored-by: desrosj <359867+desrosj@users.noreply.github.com> --- tests/qunit/wp-admin/js/dashboard.js | 30 +++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/qunit/wp-admin/js/dashboard.js b/tests/qunit/wp-admin/js/dashboard.js index 4c60e93be1a4d..bf51766796af9 100644 --- a/tests/qunit/wp-admin/js/dashboard.js +++ b/tests/qunit/wp-admin/js/dashboard.js @@ -145,14 +145,15 @@ jQuery( document ).ready( function () { QUnit.test( 'modern browsers should return a time zone name', function( assert ) { // Simulate a modern browser. var originalDateTimeFormat = Intl.DateTimeFormat; - Intl.DateTimeFormat = function( locales, options ) { - return { - resolvedOptions: function() { - return { timeZone: 'America/Chicago' }; - } - }; + Intl.DateTimeFormat = function DateTimeFormatMock() { + if ( ! ( this instanceof DateTimeFormatMock ) ) { + return new DateTimeFormatMock(); + } + }; + Intl.DateTimeFormat.prototype = Object.create( originalDateTimeFormat.prototype ); + Intl.DateTimeFormat.prototype.resolvedOptions = function() { + return { timeZone: 'America/Chicago' }; }; - Intl.DateTimeFormat.prototype = originalDateTimeFormat.prototype; var actual = getTimeZone( startDate ); @@ -166,14 +167,15 @@ jQuery( document ).ready( function () { var originalDateTimeFormat = Intl.DateTimeFormat; var getFlippedTimeZoneOffsetStub = sinon.stub( wp.communityEvents, 'getFlippedTimeZoneOffset' ); - Intl.DateTimeFormat = function( locales, options ) { - return { - resolvedOptions: function() { - return { timeZone: undefined }; - } - }; + Intl.DateTimeFormat = function DateTimeFormatMock() { + if ( ! ( this instanceof DateTimeFormatMock ) ) { + return new DateTimeFormatMock(); + } + }; + Intl.DateTimeFormat.prototype = Object.create( originalDateTimeFormat.prototype ); + Intl.DateTimeFormat.prototype.resolvedOptions = function() { + return { timeZone: undefined }; }; - Intl.DateTimeFormat.prototype = originalDateTimeFormat.prototype; getFlippedTimeZoneOffsetStub.returns( -300 ); var actual = getTimeZone( startDate );