fix: generate changelog via Python subprocess for reliability
Local Build and Deploy / deploy (push) Successful in 1m26s

Previous approaches using temp files or env vars failed in Gitea runner.
Python now runs git commands directly via subprocess, reads stdout,
and builds the JSON payload — no intermediate files or shell escaping.
This commit is contained in:
openhands committed 2026-07-20 15:21:12 +02:00
1 parent 80bf3bb0d5
commit 57ef6289ff
1 file changed
+75 -51
+75 -51
View File
@@ -7,7 +7,7 @@ jobs:
release:
runs-on: shell
steps:
- name: Checkout, build & release
- name: Build & release
env:
VERSION: ${{ gitea.ref_name }}
GITEA_API: ${{ gitea.api_url }}
@@ -15,90 +15,114 @@ jobs:
run: |
set -e
# Use the local checkout (has full history + HEAD)
LOCAL="/var/www/atom-nexst"
# ── Generate changelog ────────────────────────────────────────
PREV_TAG="$(git -C "$LOCAL" tag --sort=-creatordate | head -2 | tail -1 || echo '')"
echo "=== Debug: VERSION=$VERSION PREV_TAG=$PREV_TAG ==="
if [ -n "$PREV_TAG" ] && [ "$PREV_TAG" != "$VERSION" ]; then
RANGE="${PREV_TAG}..${VERSION}"
echo "=== Range: $RANGE ==="
git -C "$LOCAL" log --oneline --no-decorate "${RANGE}" > /tmp/changelog.txt
else
# First release or only one tag — show all commits up to this tag
echo "=== First release or only one tag, showing all commits ==="
git -C "$LOCAL" log --oneline --no-decorate "${VERSION}" > /tmp/changelog.txt
fi
echo "=== Changelog ==="
cat /tmp/changelog.txt
echo "=== End changelog ==="
# ── Build ─────────────────────────────────────────────────────
# Shallow clone for build
SRC="/tmp/epicnext-release"
rm -rf "$SRC"
git clone --branch "${VERSION}" --depth 1 \
/docker/gitea/gitea/git/repositories/remco/epicnext-cms.git \
"$SRC"
cd "$SRC"
if [ -f /var/www/atom-nexst/.env ]; then
cp /var/www/atom-nexst/.env "$SRC/.env"
fi
cd "$SRC"
pnpm install --frozen-lockfile
pnpm prisma:generate
export SKIP_ENV_VALIDATION=1
pnpm build
# ── Create release (Python handles JSON properly) ─────────────
# Create release — Python reads changelog directly from git
python3 << 'PYEOF'
import json, os, urllib.request
import json, os, subprocess, urllib.request, urllib.error
version = os.environ['VERSION']
api = os.environ['GITEA_API']
repo = os.environ['GITEA_REPO']
version = os.environ["VERSION"]
api = os.environ["GITEA_API"]
repo = os.environ["GITEA_REPO"]
token = "${{ secrets.GITEA_TOKEN }}"
local = "/var/www/atom-nexst"
with open('/tmp/changelog.txt') as f:
notes = f.read().strip()
# Get previous tag
tags = subprocess.run(
["git", "-C", local, "tag", "--sort=-creatordate"],
capture_output=True, text=True, check=False,
).stdout.strip().split("\n")
tags = [t for t in tags if t]
if not notes:
notes = "Initial release"
prev_tag = tags[1] if len(tags) > 1 else ""
print(f"Tags: {tags}")
print(f"Version: {version}, Previous: {prev_tag}")
if prev_tag and prev_tag != version:
r = subprocess.run(
["git", "-C", local, "log", "--oneline", "--no-decorate", f"{prev_tag}..{version}"],
capture_output=True, text=True, check=False,
)
notes_raw = r.stdout.strip()
print(f"Range: {prev_tag}..{version}, stderr: {r.stderr.strip()}")
else:
r = subprocess.run(
["git", "-C", local, "log", "--oneline", "--no-decorate", version],
capture_output=True, text=True, check=False,
)
notes_raw = r.stdout.strip()
print(f"Single tag {version}, stderr: {r.stderr.strip()}")
if not notes_raw:
notes_raw = "Initial release"
print(f"Notes ({len(notes_raw)} chars):")
print(notes_raw)
print("=== End notes ===")
body = f"""## EpicNext-CMS {version}
### Changes
{notes}
{notes_raw}
---
*Automated release from Gitea Actions*
"""
payload = json.dumps({
'tag_name': version,
'name': version,
'body': body,
'draft': False,
'prerelease': False,
}).encode('utf-8')
"tag_name": version,
"name": version,
"body": body,
"draft": False,
"prerelease": False,
}).encode("utf-8")
req = urllib.request.Request(
f'{api}/repos/{repo}/releases',
data=payload,
headers={
'Authorization': f'token {token}',
'Content-Type': 'application/json',
},
method='POST',
)
def api_call(method, path, data=None):
url = f"{api}/repos/{repo}/{path}"
print(f"API {method} {url}")
req = urllib.request.Request(
url, data=data,
headers={
"Authorization": f"token {token}",
"Content-Type": "application/json",
},
method=method,
)
resp = urllib.request.urlopen(req)
return json.loads(resp.read().decode("utf-8"))
try:
resp = urllib.request.urlopen(req)
result = json.loads(resp.read().decode('utf-8'))
result = api_call("POST", "releases", payload)
print(f"Release created: {result.get('html_url', result.get('id', '?'))}")
except urllib.error.HTTPError as e:
print(f"API error {e.code}: {e.read().decode('utf-8')}")
raise
err_body = e.read().decode("utf-8")
print(f"POST error {e.code}: {err_body}")
if e.code == 409:
releases = api_call("GET", "releases")
for rel in releases:
if rel["tag_name"] == version:
result = api_call("PATCH", f"releases/{rel['id']}", payload)
print(f"Release updated: {result.get('html_url', result.get('id', '?'))}")
break
else:
print(f"ERROR: no existing release found for {version}")
raise
else:
raise
PYEOF