#!/usr/bin/env bash # lib-forgejo.sh — shared CI helpers for the three release pipelines # (client / site / server). `source` this from release-*.sh and deploy-*.sh. # # Provides: # ver_from_tag -> strips client-v|site-v|server-v|v prefix # extract_release_notes -> prints the latest "## [x]" section body # create_release -> POSTs a Forgejo Release, sets RELEASE_ID # upload_asset -> uploads one asset (needs RELEASE_ID) # download_release_assets -> downloads all assets of a release into dist/ # setup_ssh / teardown_ssh -> prepare/clean the EC2 deploy key (sets SSH/SCP) # # Requires env (where relevant): FORGEJO_URL, FORGEJO_TOKEN, GITEA_REPOSITORY, # EC2_SSH_KEY, EC2_HOST, EC2_USER. # Strip the part-specific tag prefix, leaving a bare semver (1.2.3). ver_from_tag() { local tag="$1" tag="${tag#client-v}" tag="${tag#site-v}" tag="${tag#server-v}" tag="${tag#v}" printf '%s' "$tag" } # Print the body (lines) of the most recent "## [ver] - date" section. extract_release_notes() { python3 - "$1" <<'PYEOF' import re, sys fn = sys.argv[1] try: with open(fn, encoding='utf-8') as f: content = f.read() parts = re.split(r'\n## ', '\n' + content) if len(parts) > 1: lines = parts[1].strip().split('\n') notes = [] for line in lines[1:]: s = line.strip() if s.startswith('## '): break if s: notes.append(s) print('\n'.join(notes) if notes else lines[0]) else: print('') except Exception: print('') PYEOF } # create_release — sets global RELEASE_ID on success. create_release() { local tag="$1" body="$2" http_code resp echo "==> release: creating Forgejo Release ${tag}" http_code=$(curl -k -w "%{http_code}" -o /tmp/release_resp.json \ -X POST "${FORGEJO_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases" \ -H "Authorization: token ${FORGEJO_TOKEN}" \ -H "Content-Type: application/json" \ -d "{\"tag_name\":\"${tag}\",\"name\":\"Release ${tag}\",\"body\":${body},\"draft\":false,\"prerelease\":false}") resp=$(cat /tmp/release_resp.json) echo "==> release: API response (HTTP ${http_code}): ${resp}" if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then echo "==> release: FAILED — HTTP ${http_code}" >&2 return 1 fi RELEASE_ID=$(python3 -c "import sys,json; print(json.load(sys.stdin)['id'])" <<< "${resp}") echo "==> release: release_id=${RELEASE_ID}" } # upload_asset — requires RELEASE_ID. upload_asset() { local file="$1" code code=$(curl -k -w "%{http_code}" -o /tmp/upload_resp.json \ -X POST "${FORGEJO_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/${RELEASE_ID}/assets" \ -H "Authorization: token ${FORGEJO_TOKEN}" \ -F "attachment=@${file}") echo "==> release: uploaded ${file} (HTTP ${code}): $(cat /tmp/upload_resp.json)" if [ "$code" -lt 200 ] || [ "$code" -ge 300 ]; then return 1; fi } # download_release_assets — pull every asset of the release into dist/. download_release_assets() { local tag="$1" echo "==> deploy: downloading assets for ${tag} from Forgejo" mkdir -p dist curl -kf -H "Authorization: token ${FORGEJO_TOKEN}" \ "${FORGEJO_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/tags/${tag}" \ -o /tmp/release-info.json python3 - <<'PYEOF' import json, urllib.request, ssl, os ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE rel = json.load(open('/tmp/release-info.json')) token = os.environ['FORGEJO_TOKEN'] url = os.environ['FORGEJO_URL'] repo = os.environ['GITEA_REPOSITORY'] rid = rel['id'] for a in rel.get('assets', []): u = f"{url}/api/v1/repos/{repo}/releases/{rid}/assets/{a['id']}" req = urllib.request.Request(u, headers={'Authorization': f'token {token}'}) with urllib.request.urlopen(req, context=ctx) as r, open(f"dist/{a['name']}", 'wb') as o: o.write(r.read()) print('Downloaded:', a['name']) PYEOF } # setup_ssh — write the deploy key and export SSH / SCP commands. # Target is the EC2 host by default; set DEPLOY_HOST / DEPLOY_USER / DEPLOY_SSH_KEY # to retarget another host (e.g. the Alibaba Cloud box during the EC2→Ali migration). # Backward-compatible: with no DEPLOY_* set, behaves exactly as before (EC2). setup_ssh() { local host="${DEPLOY_HOST:-$EC2_HOST}" local key="${DEPLOY_SSH_KEY:-$EC2_SSH_KEY}" mkdir -p ~/.ssh # `printf '%s\n'` 末尾补一个换行:Forgejo 存 secret 会去掉结尾换行,而 # OpenSSH 格式私钥(-----BEGIN OPENSSH PRIVATE KEY-----,如阿里 ed25519 部署 key) # 缺结尾换行会被判为 "invalid format" 而拒绝加载,退化成无密钥 → Permission denied。 # 多补的换行对已含结尾换行的 PEM(如 EC2 key)无害。 printf '%s\n' "${key}" > ~/.ssh/ec2_deploy.pem chmod 600 ~/.ssh/ec2_deploy.pem ssh-keyscan -H "${host}" >> ~/.ssh/known_hosts 2>/dev/null SSH="ssh -i ~/.ssh/ec2_deploy.pem -o StrictHostKeyChecking=no" SCP="scp -O -i ~/.ssh/ec2_deploy.pem" } teardown_ssh() { rm -f ~/.ssh/ec2_deploy.pem }