From 1127b23d1ecc62b390c416ad8ab3a43383eb18d5 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Mon, 13 Jul 2026 16:22:30 +0000 Subject: [PATCH] chore: sync auto-release.yml from Template-Generic [skip ci] --- .mokogitea/workflows/auto-release.yml | 76 +++++++++++++++++++-------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 1b273e45..ee87c563 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -8,14 +8,13 @@ # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic # PATH: /.mokogitea/workflows/auto-release.yml # VERSION: 05.01.00 -# BRIEF: Universal build & release � detects platform from the MokoGitea Metadata field (API) +# BRIEF: Universal build & release � detects platform from metadata API # # +=======================================================================+ # | UNIVERSAL BUILD & RELEASE PIPELINE | # +=======================================================================+ # | | -# | Reads platform (joomla|dolibarr|generic) from the MokoGitea | -# | Metadata field (API) to branch logic. | +# | Reads metadata API (joomla|dolibarr|generic) to branch logic. | # | | # | Platform-specific: | # | joomla: XML manifest, type-prefixed packages | @@ -116,20 +115,17 @@ jobs: SRC_SHA=$(printf '%s' "$SRC_JSON" | python3 -c "import sys, json; print(json.load(sys.stdin)['commit']['id'])" 2>/dev/null || true) [ -n "$SRC_SHA" ] || { echo "::error::Could not resolve HEAD of ${FROM}"; exit 1; } - # Point rc at the source commit. If rc already exists (a protected branch that - # cannot be deleted), force-update its ref in place instead of delete+recreate: - # deleting a protected branch fails, which then makes the recreate return HTTP 409. - if curl -sf -o /dev/null -H "$AUTH" "${API_BASE}/branches/rc"; then - echo "rc exists - force-updating to ${FROM} (${SRC_SHA})" - curl -sf -X PATCH -H "$AUTH" -H "Content-Type: application/json" \ - "${API_BASE}/git/refs/heads/rc" -d "{\"sha\":\"${SRC_SHA}\",\"force\":true}" \ - || { echo "::error::Failed to force-update rc (CI token needs force-push on the protected rc branch)"; exit 1; } - else - echo "Creating rc from ${FROM}" - curl -sf -X POST -H "$AUTH" -H "Content-Type: application/json" \ - "${API_BASE}/branches" -d "{\"new_branch_name\":\"rc\",\"old_branch_name\":\"${FROM}\"}" \ - || { echo "::error::Failed to create rc from ${FROM}"; exit 1; } - fi + # Point rc at the source commit via git push. Gitea's git/refs PATCH API + # returns HTTP 405 on ANY protected branch (force or not, even for a user in + # the force-push allowlist), so it cannot move a protected rc. git push honors + # the push + force-push allowlists and creates rc if it is absent. + PUSH_URL="https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@${MOKOGITEA_URL#https://}/${GITEA_ORG}/${GITEA_REPO}.git" + git config --global user.name "mokogitea-actions[bot]" + git config --global user.email "actions@mokoconsulting.tech" + git fetch --no-tags "$PUSH_URL" "${FROM}" + git push --force "$PUSH_URL" "FETCH_HEAD:refs/heads/rc" \ + || { echo "::error::Failed to point rc at ${FROM} (${SRC_SHA}) via git push"; exit 1; } + echo "rc set to ${FROM} (${SRC_SHA})" # Repoint the PR at rc, then delete the old source branch (non-fatal). if [ -n "$PR" ]; then @@ -378,13 +374,47 @@ jobs: if [ -n "$VERSION" ] && [ -f "CHANGELOG.md" ]; then DATE=$(date +%Y-%m-%d) python3 -c " - import sys + import sys, re version, date = sys.argv[1], sys.argv[2] - content = open('CHANGELOG.md').read() - old = '## [Unreleased]' - new = f'## [Unreleased]\n\n## [{version}] --- {date}' - content = content if ('## [' + version + ']') in content else content.replace(old, new, 1) - open('CHANGELOG.md', 'w').write(content) + lines = open('CHANGELOG.md').read().split('\n') + h2 = re.compile(r'^##\s+\[([^\]]+)\]') + header, sections, cur = [], [], None + for ln in lines: + m = h2.match(ln) + if m: + if cur: sections.append(cur) + cur = {'label': m.group(1).strip(), 'head': ln, 'body': []} + elif cur is None: + header.append(ln) + else: + cur['body'].append(ln) + if cur: sections.append(cur) + def nonempty(b): return any(x.strip() for x in b) + def trim(b): + b = b[:] + while b and not b[0].strip(): b.pop(0) + while b and not b[-1].strip(): b.pop() + return b + unreleased, order, bykey = [], [], {} + for s in sections: + key = s['label'].lower() + if key == 'unreleased': + if nonempty(s['body']): unreleased += s['body'] + continue + if not nonempty(s['body']): continue # drop blank release sections + if key in bykey: bykey[key]['body'] += [''] + s['body'] # merge duplicate heading (never drop content) + else: bykey[key] = s; order.append(key) + rest, seen = [bykey[k] for k in order], set(order) + out = [] + htxt = '\n'.join(header).rstrip() + if htxt: out += [htxt, ''] + out += ['## [Unreleased]', ''] + promote = bool(unreleased) and version.lower() not in seen + if unreleased and not promote: out += trim(unreleased) + [''] + if promote: out += ['## [%s] --- %s' % (version, date), ''] + trim(unreleased) + [''] + for s in rest: out += [s['head'], ''] + trim(s['body']) + [''] + res = re.sub(r'\n{3,}', '\n\n', '\n'.join(out)).rstrip('\n') + '\n' + open('CHANGELOG.md', 'w').write(res) " "$VERSION" "$DATE" git add CHANGELOG.md git commit -m "chore: promote changelog [Unreleased] → [${VERSION}]" || true