From 1ce76c53c2fc36e2a0de12525a06ddf7adac6377 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 23 May 2026 17:49:46 -0500 Subject: [PATCH 1/2] feat(ci): add release artifact verification step (Step 8c) Verifies the built release ZIP after upload by: - Downloading the artifact from the release URL - Extracting and checking manifest version matches release version - Comparing SHA256 against updates.xml - Scanning for disallowed files (.claude, .env, TODO.md, node_modules) - Checking for non-vendor .min files that should be runtime-generated Fails the pipeline if any critical check fails. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/workflows/auto-release.yml | 136 ++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 46ce4b2..1598d8f 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -889,6 +889,142 @@ jobs: " 2>/dev/null && echo "Release body updated with changelog + SHA" >> $GITHUB_STEP_SUMMARY fi + # -- STEP 8c: Verify release artifact ---------------------------------------- + - name: "Step 8c: Verify release artifact" + if: steps.version.outputs.skip != 'true' + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + RELEASE_TAG="${{ steps.version.outputs.release_tag }}" + PLATFORM="${{ steps.platform.outputs.platform }}" + EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}" + EXT_TYPE="${{ steps.updates.outputs.ext_type }}" + EXT_FOLDER="${{ steps.updates.outputs.ext_folder }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.GA_TOKEN }}" + + # Build ZIP name (same logic as Step 8) + TYPE_PREFIX="" + case "${EXT_TYPE}" in + plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;; + module) TYPE_PREFIX="mod_" ;; + component) TYPE_PREFIX="com_" ;; + template) TYPE_PREFIX="tpl_" ;; + library) TYPE_PREFIX="lib_" ;; + package) TYPE_PREFIX="pkg_" ;; + esac + ZIP_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip" + + VERIFY_DIR="/tmp/release-verify" + rm -rf "${VERIFY_DIR}" && mkdir -p "${VERIFY_DIR}" + PASS=0 + FAIL=0 + WARN=0 + + echo "### Release Verification" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Check | Result | Details |" >> $GITHUB_STEP_SUMMARY + echo "|-------|--------|---------|" >> $GITHUB_STEP_SUMMARY + + # 1. Download the release ZIP from the release asset + DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}" + HTTP_CODE=$(curl -sf -o "${VERIFY_DIR}/release.zip" -w '%{http_code}' \ + -H "Authorization: token ${TOKEN}" \ + "${DOWNLOAD_URL}" 2>/dev/null || echo "000") + + if [ "${HTTP_CODE}" != "200" ] || [ ! -s "${VERIFY_DIR}/release.zip" ]; then + echo "| Download | FAIL | HTTP ${HTTP_CODE} — could not download \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY + FAIL=$((FAIL + 1)) + else + echo "| Download | PASS | \`${ZIP_NAME}\` downloaded |" >> $GITHUB_STEP_SUMMARY + PASS=$((PASS + 1)) + + # 2. Unzip and verify contents + unzip -qo "${VERIFY_DIR}/release.zip" -d "${VERIFY_DIR}/contents" 2>/dev/null + if [ $? -ne 0 ]; then + echo "| Unzip | FAIL | ZIP extraction failed |" >> $GITHUB_STEP_SUMMARY + FAIL=$((FAIL + 1)) + else + echo "| Unzip | PASS | Extracted successfully |" >> $GITHUB_STEP_SUMMARY + PASS=$((PASS + 1)) + + # 3. Version check — manifest version matches release version (Joomla) + if [ "${PLATFORM}" = "joomla" ]; then + MANIFEST_FILE=$(find "${VERIFY_DIR}/contents" -maxdepth 1 -name '*.xml' \ + -exec grep -l '/dev/null | head -1) + if [ -n "${MANIFEST_FILE}" ]; then + MANIFEST_VER=$(sed -n 's/.*\([^<]*\)<\/version>.*/\1/p' "${MANIFEST_FILE}" | head -1) + if [ "${MANIFEST_VER}" = "${VERSION}" ]; then + echo "| Manifest version | PASS | \`${MANIFEST_VER}\` matches release |" >> $GITHUB_STEP_SUMMARY + PASS=$((PASS + 1)) + else + echo "| Manifest version | FAIL | \`${MANIFEST_VER}\` != \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + FAIL=$((FAIL + 1)) + fi + else + echo "| Manifest version | WARN | No XML manifest found in ZIP |" >> $GITHUB_STEP_SUMMARY + WARN=$((WARN + 1)) + fi + fi + + # 4. SHA256 check — ZIP hash matches updates.xml + ZIP_SHA=$(sha256sum "${VERIFY_DIR}/release.zip" | cut -d' ' -f1) + if [ "${PLATFORM}" = "joomla" ] && [ -f "updates.xml" ]; then + UPDATES_SHA=$(sed -n 's/.*\([^<]*\)<\/sha256>.*/\1/p' updates.xml | head -1) + if [ -n "${UPDATES_SHA}" ]; then + if [ "${ZIP_SHA}" = "${UPDATES_SHA}" ]; then + echo "| SHA256 vs updates.xml | PASS | \`${ZIP_SHA:0:16}...\` |" >> $GITHUB_STEP_SUMMARY + PASS=$((PASS + 1)) + else + echo "| SHA256 vs updates.xml | FAIL | ZIP=\`${ZIP_SHA:0:16}...\` updates.xml=\`${UPDATES_SHA:0:16}...\` |" >> $GITHUB_STEP_SUMMARY + FAIL=$((FAIL + 1)) + fi + else + echo "| SHA256 vs updates.xml | WARN | No sha256 in updates.xml |" >> $GITHUB_STEP_SUMMARY + WARN=$((WARN + 1)) + fi + fi + + # 5. Disallowed files check + DISALLOWED="" + for pattern in ".claude" ".mcp.json" "TODO.md" "todo.md" ".git" "node_modules" ".env"; do + FOUND=$(find "${VERIFY_DIR}/contents" -name "${pattern}" 2>/dev/null) + [ -n "${FOUND}" ] && DISALLOWED="${DISALLOWED} ${pattern}" + done + # Check for minified source files (should be generated at runtime, not shipped) + MIN_FILES=$(find "${VERIFY_DIR}/contents" \( -name '*.min.css' -o -name '*.min.js' \) \ + -not -path '*/vendor/*' 2>/dev/null | wc -l) + + if [ -n "${DISALLOWED}" ]; then + echo "| Disallowed files | FAIL | Found:${DISALLOWED} |" >> $GITHUB_STEP_SUMMARY + FAIL=$((FAIL + 1)) + else + echo "| Disallowed files | PASS | None found |" >> $GITHUB_STEP_SUMMARY + PASS=$((PASS + 1)) + fi + + if [ "${MIN_FILES}" -gt 0 ]; then + echo "| Non-vendor .min files | WARN | ${MIN_FILES} file(s) — should be generated at runtime |" >> $GITHUB_STEP_SUMMARY + WARN=$((WARN + 1)) + else + echo "| Non-vendor .min files | PASS | None shipped |" >> $GITHUB_STEP_SUMMARY + PASS=$((PASS + 1)) + fi + fi + fi + + # Summary + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Verification: ${PASS} passed, ${FAIL} failed, ${WARN} warnings**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Fail the step if any critical checks failed + if [ "${FAIL}" -gt 0 ]; then + echo "::error::Release verification failed with ${FAIL} failure(s)" + exit 1 + fi + + rm -rf "${VERIFY_DIR}" + # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - name: "Step 9: Mirror release to GitHub" if: >- -- 2.52.0 From 8a4e23973a10b709a8f598640262ad1eb4090b3a Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 23 May 2026 17:52:14 -0500 Subject: [PATCH 2/2] Revert "feat(ci): add release artifact verification step (Step 8c)" This reverts commit 1ce76c53c2fc36e2a0de12525a06ddf7adac6377. --- .mokogitea/workflows/auto-release.yml | 136 -------------------------- 1 file changed, 136 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 1598d8f..46ce4b2 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -889,142 +889,6 @@ jobs: " 2>/dev/null && echo "Release body updated with changelog + SHA" >> $GITHUB_STEP_SUMMARY fi - # -- STEP 8c: Verify release artifact ---------------------------------------- - - name: "Step 8c: Verify release artifact" - if: steps.version.outputs.skip != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - PLATFORM="${{ steps.platform.outputs.platform }}" - EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}" - EXT_TYPE="${{ steps.updates.outputs.ext_type }}" - EXT_FOLDER="${{ steps.updates.outputs.ext_folder }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.GA_TOKEN }}" - - # Build ZIP name (same logic as Step 8) - TYPE_PREFIX="" - case "${EXT_TYPE}" in - plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;; - module) TYPE_PREFIX="mod_" ;; - component) TYPE_PREFIX="com_" ;; - template) TYPE_PREFIX="tpl_" ;; - library) TYPE_PREFIX="lib_" ;; - package) TYPE_PREFIX="pkg_" ;; - esac - ZIP_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip" - - VERIFY_DIR="/tmp/release-verify" - rm -rf "${VERIFY_DIR}" && mkdir -p "${VERIFY_DIR}" - PASS=0 - FAIL=0 - WARN=0 - - echo "### Release Verification" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Check | Result | Details |" >> $GITHUB_STEP_SUMMARY - echo "|-------|--------|---------|" >> $GITHUB_STEP_SUMMARY - - # 1. Download the release ZIP from the release asset - DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}" - HTTP_CODE=$(curl -sf -o "${VERIFY_DIR}/release.zip" -w '%{http_code}' \ - -H "Authorization: token ${TOKEN}" \ - "${DOWNLOAD_URL}" 2>/dev/null || echo "000") - - if [ "${HTTP_CODE}" != "200" ] || [ ! -s "${VERIFY_DIR}/release.zip" ]; then - echo "| Download | FAIL | HTTP ${HTTP_CODE} — could not download \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY - FAIL=$((FAIL + 1)) - else - echo "| Download | PASS | \`${ZIP_NAME}\` downloaded |" >> $GITHUB_STEP_SUMMARY - PASS=$((PASS + 1)) - - # 2. Unzip and verify contents - unzip -qo "${VERIFY_DIR}/release.zip" -d "${VERIFY_DIR}/contents" 2>/dev/null - if [ $? -ne 0 ]; then - echo "| Unzip | FAIL | ZIP extraction failed |" >> $GITHUB_STEP_SUMMARY - FAIL=$((FAIL + 1)) - else - echo "| Unzip | PASS | Extracted successfully |" >> $GITHUB_STEP_SUMMARY - PASS=$((PASS + 1)) - - # 3. Version check — manifest version matches release version (Joomla) - if [ "${PLATFORM}" = "joomla" ]; then - MANIFEST_FILE=$(find "${VERIFY_DIR}/contents" -maxdepth 1 -name '*.xml' \ - -exec grep -l '/dev/null | head -1) - if [ -n "${MANIFEST_FILE}" ]; then - MANIFEST_VER=$(sed -n 's/.*\([^<]*\)<\/version>.*/\1/p' "${MANIFEST_FILE}" | head -1) - if [ "${MANIFEST_VER}" = "${VERSION}" ]; then - echo "| Manifest version | PASS | \`${MANIFEST_VER}\` matches release |" >> $GITHUB_STEP_SUMMARY - PASS=$((PASS + 1)) - else - echo "| Manifest version | FAIL | \`${MANIFEST_VER}\` != \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY - FAIL=$((FAIL + 1)) - fi - else - echo "| Manifest version | WARN | No XML manifest found in ZIP |" >> $GITHUB_STEP_SUMMARY - WARN=$((WARN + 1)) - fi - fi - - # 4. SHA256 check — ZIP hash matches updates.xml - ZIP_SHA=$(sha256sum "${VERIFY_DIR}/release.zip" | cut -d' ' -f1) - if [ "${PLATFORM}" = "joomla" ] && [ -f "updates.xml" ]; then - UPDATES_SHA=$(sed -n 's/.*\([^<]*\)<\/sha256>.*/\1/p' updates.xml | head -1) - if [ -n "${UPDATES_SHA}" ]; then - if [ "${ZIP_SHA}" = "${UPDATES_SHA}" ]; then - echo "| SHA256 vs updates.xml | PASS | \`${ZIP_SHA:0:16}...\` |" >> $GITHUB_STEP_SUMMARY - PASS=$((PASS + 1)) - else - echo "| SHA256 vs updates.xml | FAIL | ZIP=\`${ZIP_SHA:0:16}...\` updates.xml=\`${UPDATES_SHA:0:16}...\` |" >> $GITHUB_STEP_SUMMARY - FAIL=$((FAIL + 1)) - fi - else - echo "| SHA256 vs updates.xml | WARN | No sha256 in updates.xml |" >> $GITHUB_STEP_SUMMARY - WARN=$((WARN + 1)) - fi - fi - - # 5. Disallowed files check - DISALLOWED="" - for pattern in ".claude" ".mcp.json" "TODO.md" "todo.md" ".git" "node_modules" ".env"; do - FOUND=$(find "${VERIFY_DIR}/contents" -name "${pattern}" 2>/dev/null) - [ -n "${FOUND}" ] && DISALLOWED="${DISALLOWED} ${pattern}" - done - # Check for minified source files (should be generated at runtime, not shipped) - MIN_FILES=$(find "${VERIFY_DIR}/contents" \( -name '*.min.css' -o -name '*.min.js' \) \ - -not -path '*/vendor/*' 2>/dev/null | wc -l) - - if [ -n "${DISALLOWED}" ]; then - echo "| Disallowed files | FAIL | Found:${DISALLOWED} |" >> $GITHUB_STEP_SUMMARY - FAIL=$((FAIL + 1)) - else - echo "| Disallowed files | PASS | None found |" >> $GITHUB_STEP_SUMMARY - PASS=$((PASS + 1)) - fi - - if [ "${MIN_FILES}" -gt 0 ]; then - echo "| Non-vendor .min files | WARN | ${MIN_FILES} file(s) — should be generated at runtime |" >> $GITHUB_STEP_SUMMARY - WARN=$((WARN + 1)) - else - echo "| Non-vendor .min files | PASS | None shipped |" >> $GITHUB_STEP_SUMMARY - PASS=$((PASS + 1)) - fi - fi - fi - - # Summary - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Verification: ${PASS} passed, ${FAIL} failed, ${WARN} warnings**" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - - # Fail the step if any critical checks failed - if [ "${FAIL}" -gt 0 ]; then - echo "::error::Release verification failed with ${FAIL} failure(s)" - exit 1 - fi - - rm -rf "${VERIFY_DIR}" - # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - name: "Step 9: Mirror release to GitHub" if: >- -- 2.52.0