name: Check Dotorg Sync # A green dotorg-deploy run only means the commit reached SVN. WordPress.org # then decides separately whether to serve it, and when it does not the commit # sits in SVN while the directory keeps handing users the old version. Nothing # in the deploy notices. This runs daily against the published result and posts # what disagrees. on: schedule: # 09:00 UTC, well clear of the deploys that follow a merge to trunk. - cron: '0 9 * * *' workflow_dispatch: inputs: grace-hours: description: 'Hours to wait on WordPress.org before reporting an unpublished SVN commit' type: string default: '48' env: # apt hosts are for installing subversion; nodejs.org and the github asset # hosts are what setup-node fetches from. The check itself only talks to # api.wordpress.org and themes.svn.wordpress.org, and the notification to # slack.com. No registry.npmjs.org: nothing here installs npm packages. SECEX_EGRESS_RULES: deb.debian.org,security.debian.org,archive.ubuntu.com,security.ubuntu.com,nodejs.org,api.github.com,github.com,objects.githubusercontent.com,release-assets.githubusercontent.com,raw.githubusercontent.com,api.wordpress.org,themes.svn.wordpress.org,slack.com permissions: contents: read concurrency: group: check-dotorg-sync cancel-in-progress: false jobs: check: name: 'Compare themes against WordPress.org' runs-on: [self-hosted, secex, gha-runner-2cpu-2gb] timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 # Needed only for `svn info`, which dates the SVN commit a theme has been # waiting on. Reading versions out of SVN uses plain HTTP. - name: Install SVN run: | if command -v svn >/dev/null 2>&1; then echo "svn already present" else sudo apt-get update sudo apt-get install -y subversion fi - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version-file: '.nvmrc' # Called directly rather than through `npm run check:dotorg-sync`. The npm # script goes via index.mjs, which imports dotenv and would need `npm ci` # first; the check itself has no dependencies, so entering through its own # file keeps this job install-free. # # grace-hours arrives through the environment rather than being expanded # into the script. A `${{ }}` expansion is spliced into the shell source # before bash parses it, so a dispatch input containing shell syntax would # run as part of this step. - name: Check themes against WordPress.org id: check env: GRACE_HOURS: ${{ inputs.grace-hours || '48' }} run: | set +e node .theme-utils/check-dotorg-sync.mjs \ --grace-hours="$GRACE_HOURS" \ --slack-output=slack-message.txt \ | tee report.txt status=${PIPESTATUS[0]} set -e # Written before the exit below, so a run that broke partway still # shows how far it got. The summary is the only durable copy of the # report now, so nothing may return early and skip it. { echo '### WordPress.org sync check' echo '```' cat report.txt echo '```' } >> "$GITHUB_STEP_SUMMARY" # 0 clean, 2 drift found, anything else means the check itself broke. case "$status" in 0) echo "drift=false" >> "$GITHUB_OUTPUT" ;; 2) echo "drift=true" >> "$GITHUB_OUTPUT" ;; *) echo "::error::check-dotorg-sync failed with exit code $status"; exit "$status" ;; esac # slack-post.mjs rather than curl and jq: jq is not part of the secex # image, and building the payload in Node keeps the report's backticks and # newlines out of the shell entirely. It handles a missing token and # Slack's {"ok": false} responses itself. - name: Post to Slack if: steps.check.outputs.drift == 'true' env: SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} SLACK_CHANNEL: ${{ secrets.SLACK_TEAM_CHANNEL }} run: node .theme-utils/slack-post.mjs slack-message.txt # Deliberately the last step, so Slack is notified and the job summary is # written before the run goes red. Themes out of sync with WordPress.org # are a real problem, and a green scheduled run would imply otherwise. - name: Fail if any theme is out of sync if: steps.check.outputs.drift == 'true' run: | echo "::error::Themes are out of sync with WordPress.org. See the job summary for the list." exit 1