diff --git a/.github/actions/generate-info-json/action.yml b/.github/actions/generate-info-json/action.yml new file mode 100644 index 00000000..452c9459 --- /dev/null +++ b/.github/actions/generate-info-json/action.yml @@ -0,0 +1,37 @@ +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 + 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 + shell: bash + run: | + jq empty info.json + jq '{version, last_updated, download_link, tested, requires, requires_php, versions: (.versions | keys)}' info.json diff --git a/.github/scripts/generate-info-json.sh b/.github/scripts/generate-info-json.sh new file mode 100755 index 00000000..5a5a6b72 --- /dev/null +++ b/.github/scripts/generate-info-json.sh @@ -0,0 +1,95 @@ +#!/bin/bash +# +# Refreshes the release-time fields in the plugin's info.json manifest. +# +# 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. +# +# 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 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 + +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}}" +DOWNLOAD_BASE_URL="${DOWNLOAD_BASE_URL:-${UPDATE_URL}}" + +if [ ! -f "$README" ]; then + echo "::error::readme.txt not found at $README" >&2 + exit 1 +fi + +# 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) + +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="${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') +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 + | .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/info-json-dry-run.yml b/.github/workflows/info-json-dry-run.yml new file mode 100644 index 00000000..462304c7 --- /dev/null +++ b/.github/workflows/info-json-dry-run.yml @@ -0,0 +1,31 @@ +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 + uses: ./.github/actions/generate-info-json + with: + version: ${{ inputs.version }} + + - name: Upload info.json artifact + uses: actions/upload-artifact@v4 + with: + name: info-json + path: info.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8adcea1..fcae2fd4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,6 +69,21 @@ jobs: tag: ${{ format('v{0}', fromJSON(steps.changesets.outputs.publishedPackages)[0].version) }} overwrite: true + - name: Generate info.json manifest + if: steps.changesets.outputs.published == 'true' + 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' + 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