diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c81d96..689070f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,46 @@ env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' jobs: + versions: + name: Version declarations agree + # The README badge sat at 1.4.0 while this skill shipped 1.5.0, 1.6.0 and + # 1.6.1 (wordpress-api-pro's at 3.8.2 through 3.9.5): nothing compared + # them. Every place that states the version has to say the same thing, + # on pull requests too, so the bump that forgets one is caught before merge. + runs-on: ubuntu-latest + env: + SKILL: wordpress-api-pro/SKILL.md + steps: + - uses: actions/checkout@v4 + - name: SKILL.md, README badge, package.json and "Current version" lines agree + run: | + set -euo pipefail + skill_v=$(grep -m1 '^version:' "$SKILL" | sed 's/^version:[[:space:]]*//' | tr -d '"' | tr -d "'" | xargs) + [ -n "$skill_v" ] || { echo "::error::no version: in $SKILL"; exit 1; } + mismatch=0 + # Color-agnostic, and the WHOLE semver: after the x.y.z core take + # every character a prerelease or build-metadata suffix can carry - + # shields.io writes a dash inside the value as "--" and a "+" as + # itself (measured: version-1.2.0--rc.1-blue renders "1.2.0-rc.1", + # version-1.2.0+build.7-blue "1.2.0+build.7", and + # version-1.2.0+build--2-blue "1.2.0+build-2") - and stop at the first + # single dash, which is the colour separator. Then fold "--" back. A + # prerelease is a legitimate version here - release-drift.yml accepts + # one - and the check must not reject a consistent bump to it. + badge_v=$(grep -oE 'badge/version-[0-9]+\.[0-9]+\.[0-9]+(--|[0-9A-Za-z.+])*' README.md | head -1 | sed 's|badge/version-||; s|--|-|g' || true) + [ "$badge_v" = "$skill_v" ] || { echo "::error::README badge says ${badge_v:-}, $SKILL says $skill_v"; mismatch=1; } + pkg_v="" + if [ -f package.json ]; then + pkg_v=$(jq -r .version package.json) + [ "$pkg_v" = "$skill_v" ] || { echo "::error::package.json says $pkg_v, $SKILL says $skill_v"; mismatch=1; } + fi + # Every "Current version: **x.y.z**" line in the README, if any. + for v in $(grep -oE 'Current version: \*\*[0-9]+\.[0-9]+\.[0-9]+[0-9A-Za-z.+-]*\*\*' README.md | grep -oE '[0-9]+\.[0-9]+\.[0-9]+[0-9A-Za-z.+-]*' || true); do + [ "$v" = "$skill_v" ] || { echo "::error::README 'Current version' says $v, $SKILL says $skill_v"; mismatch=1; } + done + [ "$mismatch" = 0 ] || { echo "::error::bump every declaration in the same commit"; exit 1; } + echo "SKILL.md, README badge${pkg_v:+, package.json} agree on $skill_v" + test: name: Test (Python ${{ matrix.python-version }}) runs-on: ubuntu-latest diff --git a/.github/workflows/release-drift.yml b/.github/workflows/release-drift.yml index 2702d8a..f04d653 100644 --- a/.github/workflows/release-drift.yml +++ b/.github/workflows/release-drift.yml @@ -94,20 +94,52 @@ jobs: # Collapsing both into "missing" would report "cut the release" after # a transient API blip, sending someone to create a release that # already exists. - ERR=$(mktemp) - set +e - DRAFT=$(gh api "repos/$GITHUB_REPOSITORY/releases/tags/v$VERSION" \ - --jq '.draft' 2>"$ERR") - RC=$? - set -e - if [ "$RC" -eq 0 ]; then - STATE=$DRAFT - elif grep -qiE '404|not found' "$ERR"; then - STATE=missing - else - echo "::error::Could not query releases - this is a lookup failure, not a missing release:" - cat "$ERR" >&2 - exit 1 + lookup() { + ERR=$(mktemp) + set +e + DRAFT=$(gh api "repos/$GITHUB_REPOSITORY/releases/tags/v$VERSION" \ + --jq '.draft' 2>"$ERR") + RC=$? + set -e + if [ "$RC" -eq 0 ]; then + STATE=$DRAFT + elif grep -qiE '404|not found' "$ERR"; then + STATE=missing + else + echo "::error::Could not query releases - this is a lookup failure, not a missing release:" + cat "$ERR" >&2 + exit 1 + fi + } + lookup + # On a push to main the release is cut AFTER the merge - seconds later + # when the release flow is scripted, longer by hand - and this run + # starts the moment the push lands. Failing on the first lookup turned + # every ordinary release into a red check on main's head that no later + # run could replace (measured: hostinger-mcp v1.2.1 was cut 19 s after + # the merge and the push run had already failed; wordpress-api-pro + # v3.9.5, 52 min after). So on push, wait for the release: up to + # 20 x 30 s. A bump still unreleased after ten minutes is the drift + # this check exists for and fails exactly as before. Schedule and + # dispatch runs do not wait - nothing is in flight for them. + if [ "$GITHUB_EVENT_NAME" = "push" ]; then + for attempt in $(seq 1 20); do + # Wait through a DRAFT as well as a miss: a release flow that + # creates the release as a draft and publishes it only after its + # ClawHub publish succeeded (cloudways-mcp's release.yml) shows a + # draft for the whole publish, and a draft cut by hand before the + # merge is published moments after it. Both are the interval this + # wait is for. A draft still standing after the window fails + # below, as before. + case "$STATE" in + missing) what="no v$VERSION release yet" ;; + true) what="v$VERSION is still a draft" ;; + *) break ;; + esac + echo "$what (attempt $attempt/20) - waiting 30 s for the release flow" + sleep 30 + lookup + done fi case "$STATE" in false) diff --git a/README.md b/README.md index 3e3f855..ab38b39 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ![OpenClaw Skill](https://img.shields.io/badge/OpenClaw-Skill-purple) ![WordPress](https://img.shields.io/badge/WordPress-REST_API-21759b) ![License: MIT--0](https://img.shields.io/badge/License-MIT--0-green) -![Version](https://img.shields.io/badge/version-3.8.2-blue) +![Version](https://img.shields.io/badge/version-3.9.5-blue) A production-grade **Claude Code & OpenClaw skill** for managing WordPress content via the REST API — posts, pages, media, WooCommerce, Elementor, SEO meta, ACF, JetEngine — with explicit safety boundaries for agentic use.