From a4aab417baa5c71dee248f89438e3a217f358ae9 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Tue, 7 Jul 2026 11:02:16 -0400 Subject: [PATCH 1/6] ci: attach info.json update manifest to plugin releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On each published release, download the current WPE plugin-update manifest, refresh the release-time fields (version, download_link, last_updated, and tested/requires/requires_php/changelog synced from readme.txt), and upload the resulting info.json as a GitHub Release asset — mirroring the faustjs release flow. --- .github/scripts/add-wpe-version-info-file.sh | 78 ++++++++++++++++++++ .github/workflows/release.yml | 13 ++++ 2 files changed, 91 insertions(+) create mode 100755 .github/scripts/add-wpe-version-info-file.sh diff --git a/.github/scripts/add-wpe-version-info-file.sh b/.github/scripts/add-wpe-version-info-file.sh new file mode 100755 index 00000000..9282bdb0 --- /dev/null +++ b/.github/scripts/add-wpe-version-info-file.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# +# Refreshes the release-time fields in the plugin's info.json manifest. +# +# Downloads the currently published manifest from the WP Engine plugin update +# service, refreshes the release-time fields from readme.txt (the source of +# truth), and writes the result to ./info.json in the current working directory +# for upload as a GitHub Release asset. +# +# Environment overrides (with defaults for this repo): +# SLUG plugin slug (default: wp-graphql-content-blocks) +# README path to readme.txt (default: readme.txt) +# UPDATE_URL base WPE updates URL (default: https://wpe-plugin-updates.wpengine.com/${SLUG}) +# +# Usage: bash add-wpe-version-info-file.sh + +set -euo pipefail + +VERSION="${1:?usage: $0 }" +SLUG="${SLUG:-wp-graphql-content-blocks}" +README="${README:-readme.txt}" +UPDATE_URL="${UPDATE_URL:-https://wpe-plugin-updates.wpengine.com/${SLUG}}" + +if [ ! -f "$README" ]; then + echo "::error::readme.txt not found at $README" >&2 + exit 1 +fi + +curl --fail --silent --show-error --location \ + "${UPDATE_URL}/info.json" \ + --output info.json + +# date %p emits uppercase AM/PM on Linux; the manifest convention is lowercase. +current_time=$(LC_ALL=C date -u +"%Y-%m-%d %-I:%M%p GMT" | sed -e 's/AM/am/' -e 's/PM/pm/') +download_link="${UPDATE_URL}/${SLUG}.${VERSION}.zip" + +# Sync compatibility headers from readme.txt — empty value skips the update. +tested=$(sed -nE 's/^Tested up to:[[:space:]]+([^[:space:]]+).*/\1/p' "$README" | head -1 | tr -d '\r') +requires=$(sed -nE 's/^Requires at least:[[:space:]]+([^[:space:]]+).*/\1/p' "$README" | head -1 | tr -d '\r') +requires_php=$(sed -nE 's/^Requires PHP:[[:space:]]+([^[:space:]]+).*/\1/p' "$README" | head -1 | tr -d '\r') + +# Sync the changelog from readme.txt so the manifest reflects the current +# release notes. readme.txt is the source of truth and is already trimmed to +# the recent versions with a "View the full changelog" link. Capture the body +# of the "== Changelog ==" section, then trim surrounding whitespace. Empty +# value skips the update. +changelog=$(awk ' + /^== Changelog ==/ { capture = 1; next } + capture && /^== / { capture = 0 } + capture { body = body $0 "\n" } + END { + gsub(/^[ \t\r\n]+/, "", body) + gsub(/[ \t\r\n]+$/, "", body) + printf "%s", body + } +' "$README") + +jq \ + --arg version "$VERSION" \ + --arg last_updated "$current_time" \ + --arg download_link "$download_link" \ + --arg new_version "$VERSION" \ + --arg new_download_link "$download_link" \ + --arg tested "$tested" \ + --arg requires "$requires" \ + --arg requires_php "$requires_php" \ + --arg changelog "$changelog" \ + ' + .versions = {($new_version): $new_download_link} + .versions + | .version = $version + | .last_updated = $last_updated + | .download_link = $download_link + | if $tested != "" then .tested = $tested else . end + | if $requires != "" then .requires = $requires else . end + | if $requires_php != "" then .requires_php = $requires_php else . end + | if $changelog != "" then .sections.changelog = $changelog else . end + ' \ + info.json > info.json.tmp && mv info.json.tmp info.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8adcea1..b4dd27f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,6 +69,19 @@ jobs: tag: ${{ format('v{0}', fromJSON(steps.changesets.outputs.publishedPackages)[0].version) }} overwrite: true + - name: Generate WPE info.json manifest + if: steps.changesets.outputs.published == 'true' + run: bash .github/scripts/add-wpe-version-info-file.sh ${{ fromJSON(steps.changesets.outputs.publishedPackages)[0].version }} + + - name: Upload info.json to the release + if: steps.changesets.outputs.published == 'true' + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ format('v{0}', fromJSON(steps.changesets.outputs.publishedPackages)[0].version) }} + files: info.json + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Setup WordPress to generate static schema if: steps.changesets.outputs.published == 'true' uses: ./.github/actions/setup-wordpress From c03ecb65e883752cedfc34724e71e14e2e8a63ea Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Tue, 7 Jul 2026 11:26:38 -0400 Subject: [PATCH 2/6] ci: add workflow_dispatch dry run to generate info.json as an artifact --- .github/workflows/info-json-dry-run.yml | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/info-json-dry-run.yml diff --git a/.github/workflows/info-json-dry-run.yml b/.github/workflows/info-json-dry-run.yml new file mode 100644 index 00000000..19d293c1 --- /dev/null +++ b/.github/workflows/info-json-dry-run.yml @@ -0,0 +1,34 @@ +name: info.json dry run + +# Manually generate the plugin-update info.json manifest for a given version +# without publishing a release. Produces the manifest as a workflow artifact so +# it can be inspected on any branch (e.g. an open PR) before a real release. + +on: + workflow_dispatch: + inputs: + version: + description: 'Version to generate info.json for (e.g. 4.8.6)' + required: true + +jobs: + dry-run: + name: Generate info.json (dry run) + runs-on: ubuntu-22.04 + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Generate info.json + run: bash .github/scripts/add-wpe-version-info-file.sh "${{ inputs.version }}" + + - name: Validate manifest + run: | + jq empty info.json + jq '{version, last_updated, download_link, tested, requires, requires_php, versions: (.versions | keys)}' info.json + + - name: Upload info.json artifact + uses: actions/upload-artifact@v4 + with: + name: info-json + path: info.json From c94b7e64d437cd57d8163f96d8039ae2856988b0 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Wed, 8 Jul 2026 13:24:29 -0400 Subject: [PATCH 3/6] ci: extract generate-info-json composite action --- .github/actions/generate-info-json/action.yml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/actions/generate-info-json/action.yml diff --git a/.github/actions/generate-info-json/action.yml b/.github/actions/generate-info-json/action.yml new file mode 100644 index 00000000..ae9475a6 --- /dev/null +++ b/.github/actions/generate-info-json/action.yml @@ -0,0 +1,20 @@ +name: 'Generate info.json' +description: 'Generate and validate the plugin-update info.json manifest for a given version.' + +inputs: + version: + description: 'Plugin version to generate the manifest for (e.g. 4.8.6).' + required: true + +runs: + using: 'composite' + steps: + - name: Generate info.json + shell: bash + run: bash .github/scripts/add-wpe-version-info-file.sh "${{ inputs.version }}" + + - name: Validate manifest + shell: bash + run: | + jq empty info.json + jq '{version, last_updated, download_link, tested, requires, requires_php, versions: (.versions | keys)}' info.json From c6d8518dc0bec24c96c9a514550679cc0e31a9dc Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Wed, 8 Jul 2026 13:28:58 -0400 Subject: [PATCH 4/6] ci: use generate-info-json action in release and dry-run workflows --- .github/workflows/info-json-dry-run.yml | 9 +++------ .github/workflows/release.yml | 4 +++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/info-json-dry-run.yml b/.github/workflows/info-json-dry-run.yml index 19d293c1..462304c7 100644 --- a/.github/workflows/info-json-dry-run.yml +++ b/.github/workflows/info-json-dry-run.yml @@ -20,12 +20,9 @@ jobs: uses: actions/checkout@v4 - name: Generate info.json - run: bash .github/scripts/add-wpe-version-info-file.sh "${{ inputs.version }}" - - - name: Validate manifest - run: | - jq empty info.json - jq '{version, last_updated, download_link, tested, requires, requires_php, versions: (.versions | keys)}' info.json + uses: ./.github/actions/generate-info-json + with: + version: ${{ inputs.version }} - name: Upload info.json artifact uses: actions/upload-artifact@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b4dd27f4..16deaafa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,7 +71,9 @@ jobs: - name: Generate WPE info.json manifest if: steps.changesets.outputs.published == 'true' - run: bash .github/scripts/add-wpe-version-info-file.sh ${{ fromJSON(steps.changesets.outputs.publishedPackages)[0].version }} + uses: ./.github/actions/generate-info-json + with: + version: ${{ fromJSON(steps.changesets.outputs.publishedPackages)[0].version }} - name: Upload info.json to the release if: steps.changesets.outputs.published == 'true' From 0e90e85afee0cbc089c1a3764d190f286f0a7556 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Wed, 8 Jul 2026 13:37:34 -0400 Subject: [PATCH 5/6] ci: remove WPE branding, rename script to generate-info-json.sh --- .github/actions/generate-info-json/action.yml | 2 +- ...-version-info-file.sh => generate-info-json.sh} | 14 +++++++------- .github/workflows/release.yml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) rename .github/scripts/{add-wpe-version-info-file.sh => generate-info-json.sh} (83%) diff --git a/.github/actions/generate-info-json/action.yml b/.github/actions/generate-info-json/action.yml index ae9475a6..0e345f51 100644 --- a/.github/actions/generate-info-json/action.yml +++ b/.github/actions/generate-info-json/action.yml @@ -11,7 +11,7 @@ runs: steps: - name: Generate info.json shell: bash - run: bash .github/scripts/add-wpe-version-info-file.sh "${{ inputs.version }}" + run: bash .github/scripts/generate-info-json.sh "${{ inputs.version }}" - name: Validate manifest shell: bash diff --git a/.github/scripts/add-wpe-version-info-file.sh b/.github/scripts/generate-info-json.sh similarity index 83% rename from .github/scripts/add-wpe-version-info-file.sh rename to .github/scripts/generate-info-json.sh index 9282bdb0..bb46783b 100755 --- a/.github/scripts/add-wpe-version-info-file.sh +++ b/.github/scripts/generate-info-json.sh @@ -2,17 +2,17 @@ # # Refreshes the release-time fields in the plugin's info.json manifest. # -# Downloads the currently published manifest from the WP Engine plugin update -# service, refreshes the release-time fields from readme.txt (the source of -# truth), and writes the result to ./info.json in the current working directory +# Downloads the currently published manifest from the plugin update service, +# refreshes the release-time fields from readme.txt (the source of truth), +# and writes the result to ./info.json in the current working directory # for upload as a GitHub Release asset. # # Environment overrides (with defaults for this repo): -# SLUG plugin slug (default: wp-graphql-content-blocks) -# README path to readme.txt (default: readme.txt) -# UPDATE_URL base WPE updates URL (default: https://wpe-plugin-updates.wpengine.com/${SLUG}) +# SLUG plugin slug (default: wp-graphql-content-blocks) +# README path to readme.txt (default: readme.txt) +# UPDATE_URL base plugin updates URL (default: https://wpe-plugin-updates.wpengine.com/${SLUG}) # -# Usage: bash add-wpe-version-info-file.sh +# Usage: bash generate-info-json.sh set -euo pipefail diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 16deaafa..fcae2fd4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,7 +69,7 @@ jobs: tag: ${{ format('v{0}', fromJSON(steps.changesets.outputs.publishedPackages)[0].version) }} overwrite: true - - name: Generate WPE info.json manifest + - name: Generate info.json manifest if: steps.changesets.outputs.published == 'true' uses: ./.github/actions/generate-info-json with: From 119734595e6fe14bc2aa067cbb1bcdb3111011fc Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Wed, 8 Jul 2026 13:58:48 -0400 Subject: [PATCH 6/6] ci: decouple download URL, expose action inputs, bootstrap on 404, fix versions idempotency --- .github/actions/generate-info-json/action.yml | 17 ++++++++++ .github/scripts/generate-info-json.sh | 31 ++++++++++++++----- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/.github/actions/generate-info-json/action.yml b/.github/actions/generate-info-json/action.yml index 0e345f51..452c9459 100644 --- a/.github/actions/generate-info-json/action.yml +++ b/.github/actions/generate-info-json/action.yml @@ -5,12 +5,29 @@ inputs: version: description: 'Plugin version to generate the manifest for (e.g. 4.8.6).' required: true + slug: + description: 'Plugin slug.' + default: 'wp-graphql-content-blocks' + readme: + description: 'Path to readme.txt.' + default: 'readme.txt' + update_url: + description: 'Base URL to fetch the seed manifest from.' + default: '' + download_base: + description: 'Base URL written into download_link and versions entries. Defaults to update_url if not set.' + default: '' runs: using: 'composite' steps: - name: Generate info.json shell: bash + env: + SLUG: ${{ inputs.slug }} + README: ${{ inputs.readme }} + UPDATE_URL: ${{ inputs.update_url }} + DOWNLOAD_BASE_URL: ${{ inputs.download_base }} run: bash .github/scripts/generate-info-json.sh "${{ inputs.version }}" - name: Validate manifest diff --git a/.github/scripts/generate-info-json.sh b/.github/scripts/generate-info-json.sh index bb46783b..5a5a6b72 100755 --- a/.github/scripts/generate-info-json.sh +++ b/.github/scripts/generate-info-json.sh @@ -7,10 +7,16 @@ # and writes the result to ./info.json in the current working directory # for upload as a GitHub Release asset. # +# If no manifest exists at UPDATE_URL yet (first release / new slug), a minimal +# skeleton is bootstrapped automatically. +# # Environment overrides (with defaults for this repo): -# SLUG plugin slug (default: wp-graphql-content-blocks) -# README path to readme.txt (default: readme.txt) -# UPDATE_URL base plugin updates URL (default: https://wpe-plugin-updates.wpengine.com/${SLUG}) +# SLUG plugin slug (default: wp-graphql-content-blocks) +# README path to readme.txt (default: readme.txt) +# UPDATE_URL base URL to fetch the seed manifest from +# (default: https://wpe-plugin-updates.wpengine.com/${SLUG}) +# DOWNLOAD_BASE_URL base URL written into download_link / versions entries +# (default: same as UPDATE_URL) # # Usage: bash generate-info-json.sh @@ -20,19 +26,30 @@ VERSION="${1:?usage: $0 }" SLUG="${SLUG:-wp-graphql-content-blocks}" README="${README:-readme.txt}" UPDATE_URL="${UPDATE_URL:-https://wpe-plugin-updates.wpengine.com/${SLUG}}" +DOWNLOAD_BASE_URL="${DOWNLOAD_BASE_URL:-${UPDATE_URL}}" if [ ! -f "$README" ]; then echo "::error::readme.txt not found at $README" >&2 exit 1 fi -curl --fail --silent --show-error --location \ +# Fetch the current manifest; bootstrap a skeleton on first release (404). +http_status=$(curl --silent --show-error --location --write-out "%{http_code}" \ "${UPDATE_URL}/info.json" \ - --output info.json + --output info.json) + +if [ "$http_status" = "404" ] || [ "$http_status" = "000" ]; then + echo "No existing manifest found (HTTP ${http_status}); bootstrapping skeleton." + printf '{"name":"%s","slug":"%s","version":"","download_link":"","sections":{},"versions":{}}' \ + "$SLUG" "$SLUG" > info.json +elif [ "$http_status" != "200" ]; then + echo "::error::Failed to fetch manifest (HTTP ${http_status})" >&2 + exit 1 +fi # date %p emits uppercase AM/PM on Linux; the manifest convention is lowercase. current_time=$(LC_ALL=C date -u +"%Y-%m-%d %-I:%M%p GMT" | sed -e 's/AM/am/' -e 's/PM/pm/') -download_link="${UPDATE_URL}/${SLUG}.${VERSION}.zip" +download_link="${DOWNLOAD_BASE_URL}/${SLUG}.${VERSION}.zip" # Sync compatibility headers from readme.txt — empty value skips the update. tested=$(sed -nE 's/^Tested up to:[[:space:]]+([^[:space:]]+).*/\1/p' "$README" | head -1 | tr -d '\r') @@ -66,7 +83,7 @@ jq \ --arg requires_php "$requires_php" \ --arg changelog "$changelog" \ ' - .versions = {($new_version): $new_download_link} + .versions + .versions[$new_version] = $new_download_link | .version = $version | .last_updated = $last_updated | .download_link = $download_link