mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-07-01 19:26:53 +02:00
353 lines
14 KiB
YAML
353 lines
14 KiB
YAML
name: Main Release
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: release-main
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
CI_GIT_EMAIL: 'git-ci@hiabuto.net'
|
|
CI_GIT_NAME: 'Git-CI'
|
|
GITEA_API_BASE: 'https://git.hiabuto.net/api/v1'
|
|
GITEA_REPO: ${{ github.repository }}
|
|
|
|
jobs:
|
|
compute:
|
|
runs-on: linux-amd64
|
|
if: ${{ github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' }}
|
|
outputs:
|
|
program_version: ${{ steps.ver.outputs.program_version }}
|
|
did_release: ${{ steps.ver.outputs.did_release }}
|
|
release_tag: ${{ steps.ver.outputs.release_tag }}
|
|
release_kind: ${{ steps.ver.outputs.release_kind }}
|
|
head_sha: ${{ steps.meta.outputs.head_sha }}
|
|
head_msg: ${{ steps.meta.outputs.head_msg }}
|
|
steps:
|
|
- name: Checkout repository
|
|
shell: bash
|
|
env:
|
|
CI_MATCHA_GITEA_TOKEN: ${{ secrets.CI_MATCHA_GITEA_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
git init .
|
|
git remote add origin "https://git.hiabuto.net/${{ github.repository }}.git"
|
|
if [ -n "${CI_MATCHA_GITEA_TOKEN:-}" ]; then
|
|
basic=$(python3 -c 'import base64, os; print(base64.b64encode(("matcha:" + os.environ["CI_MATCHA_GITEA_TOKEN"]).encode()).decode())')
|
|
git config --global http.https://git.hiabuto.net/.extraheader "AUTHORIZATION: basic ${basic}"
|
|
else
|
|
echo "CI_MATCHA_GITEA_TOKEN not set; trying unauthenticated checkout."
|
|
fi
|
|
git fetch --depth=50 origin "${{ github.ref_name }}"
|
|
git checkout --force "${{ github.sha }}"
|
|
|
|
- name: Load central CI templates
|
|
shell: bash
|
|
env:
|
|
CI_MATCHA_GITEA_TOKEN: ${{ secrets.CI_MATCHA_GITEA_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -z "${CI_MATCHA_GITEA_TOKEN:-}" ]; then
|
|
echo "Missing secrets.CI_MATCHA_GITEA_TOKEN" >&2
|
|
exit 1
|
|
fi
|
|
basic=$(python3 -c 'import base64, os; print(base64.b64encode(("matcha:" + os.environ["CI_MATCHA_GITEA_TOKEN"]).encode()).decode())')
|
|
git config --global http.https://git.hiabuto.net/.extraheader "AUTHORIZATION: basic ${basic}"
|
|
git submodule sync --recursive
|
|
git submodule update --init --remote ci-templates
|
|
git -C ci-templates rev-parse --short HEAD
|
|
|
|
- id: meta
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
echo "head_sha=${{ github.sha }}" >> "$GITHUB_OUTPUT"
|
|
echo "head_msg=$(git log -1 --pretty=format:%s)" >> "$GITHUB_OUTPUT"
|
|
|
|
- id: ver
|
|
name: Compute version
|
|
uses: ./ci-templates/.gitea/actions/actions-versioning
|
|
with:
|
|
config_file: ci-config.yaml
|
|
ref_name: ${{ github.ref_name }}
|
|
|
|
package:
|
|
needs: [compute]
|
|
if: ${{ needs.compute.outputs.did_release == 'true' && !contains(needs.compute.outputs.head_msg, '[nobuild]') }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- os_label: linux-amd64
|
|
os: linux
|
|
arch: amd64
|
|
- os_label: linux-arm64
|
|
os: linux
|
|
arch: arm64
|
|
- os_label: windows-amd64
|
|
os: windows
|
|
arch: amd64
|
|
runs-on: ${{ matrix.os_label }}
|
|
steps:
|
|
- name: Checkout repository
|
|
shell: bash
|
|
env:
|
|
CI_MATCHA_GITEA_TOKEN: ${{ secrets.CI_MATCHA_GITEA_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
git init .
|
|
git remote add origin "https://git.hiabuto.net/${{ github.repository }}.git"
|
|
if [ -n "${CI_MATCHA_GITEA_TOKEN:-}" ]; then
|
|
basic=$(python3 -c 'import base64, os; print(base64.b64encode(("matcha:" + os.environ["CI_MATCHA_GITEA_TOKEN"]).encode()).decode())')
|
|
git config --global http.https://git.hiabuto.net/.extraheader "AUTHORIZATION: basic ${basic}"
|
|
else
|
|
echo "CI_MATCHA_GITEA_TOKEN not set; trying unauthenticated checkout."
|
|
fi
|
|
git fetch --depth=50 origin "${{ github.ref_name }}"
|
|
git checkout --force "${{ needs.compute.outputs.head_sha }}"
|
|
|
|
- name: Load central CI templates
|
|
shell: bash
|
|
env:
|
|
CI_MATCHA_GITEA_TOKEN: ${{ secrets.CI_MATCHA_GITEA_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -z "${CI_MATCHA_GITEA_TOKEN:-}" ]; then
|
|
echo "Missing secrets.CI_MATCHA_GITEA_TOKEN" >&2
|
|
exit 1
|
|
fi
|
|
basic=$(python3 -c 'import base64, os; print(base64.b64encode(("matcha:" + os.environ["CI_MATCHA_GITEA_TOKEN"]).encode()).decode())')
|
|
git config --global http.https://git.hiabuto.net/.extraheader "AUTHORIZATION: basic ${basic}"
|
|
git submodule sync --recursive
|
|
git submodule update --init --remote ci-templates
|
|
git -C ci-templates rev-parse --short HEAD
|
|
|
|
- name: Package release via central CI templates
|
|
uses: ./ci-templates/actions/ci-runner
|
|
env:
|
|
CI_ARCH: ${{ matrix.arch }}
|
|
with:
|
|
command: package-release
|
|
config: ci-config.yaml
|
|
version: ${{ needs.compute.outputs.program_version }}
|
|
|
|
- name: Upload dist
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: dist-${{ matrix.os }}-${{ matrix.arch }}
|
|
path: dist/
|
|
|
|
finalize_and_publish:
|
|
needs: [compute, package]
|
|
if: ${{ needs.compute.outputs.did_release == 'true' && needs.package.result == 'success' }}
|
|
runs-on: linux-amd64
|
|
steps:
|
|
- name: Checkout repository
|
|
shell: bash
|
|
env:
|
|
CI_MATCHA_GITEA_TOKEN: ${{ secrets.CI_MATCHA_GITEA_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
git init .
|
|
git remote add origin "https://git.hiabuto.net/${{ github.repository }}.git"
|
|
if [ -n "${CI_MATCHA_GITEA_TOKEN:-}" ]; then
|
|
basic=$(python3 -c 'import base64, os; print(base64.b64encode(("matcha:" + os.environ["CI_MATCHA_GITEA_TOKEN"]).encode()).decode())')
|
|
git config --global http.https://git.hiabuto.net/.extraheader "AUTHORIZATION: basic ${basic}"
|
|
else
|
|
echo "CI_MATCHA_GITEA_TOKEN not set; trying unauthenticated checkout."
|
|
fi
|
|
git fetch --depth=50 origin "${{ github.ref_name }}"
|
|
git checkout --force "${{ needs.compute.outputs.head_sha }}"
|
|
|
|
- name: Download artifacts
|
|
uses: actions/download-artifact@v3
|
|
with:
|
|
path: release-artifacts
|
|
|
|
- name: Prepare upload dir
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p upload
|
|
find release-artifacts -maxdepth 4 -type f -print -exec cp -f {} upload/ \;
|
|
ls -la upload
|
|
|
|
- name: Commit VERSION + tag + push (only now)
|
|
shell: bash
|
|
env:
|
|
CI_MATCHA_GITEA_TOKEN: ${{ secrets.CI_MATCHA_GITEA_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -z "${CI_MATCHA_GITEA_TOKEN:-}" ]; then
|
|
echo "Missing secrets.CI_MATCHA_GITEA_TOKEN" >&2
|
|
exit 1
|
|
fi
|
|
|
|
version="${{ needs.compute.outputs.program_version }}"
|
|
tag="${{ needs.compute.outputs.release_tag }}"
|
|
version_file=$(python3 - <<'PY'
|
|
import re
|
|
p='VERSION'
|
|
for line in open('ci-config.yaml', encoding='utf-8'):
|
|
if re.match(r'^\s*file\s*:', line):
|
|
p=line.split(':',1)[1].strip().strip('"\'')
|
|
break
|
|
print(p)
|
|
PY
|
|
)
|
|
|
|
git config user.email "${CI_GIT_EMAIL}"
|
|
git config user.name "${CI_GIT_NAME}"
|
|
|
|
echo "$version" > "$version_file"
|
|
git add "$version_file"
|
|
if git diff --cached --quiet; then
|
|
echo "VERSION already set in repo; continuing"
|
|
else
|
|
git commit -m "ci: release version ${version} [skip ci]"
|
|
fi
|
|
|
|
git config --local --unset-all http.https://git.hiabuto.net/.extraheader || true
|
|
git config --global --unset-all http.https://git.hiabuto.net/.extraheader || true
|
|
git remote set-url origin "https://matcha:${CI_MATCHA_GITEA_TOKEN}@git.hiabuto.net/${GITEA_REPO}.git"
|
|
git fetch --tags origin
|
|
git push origin "HEAD:main"
|
|
|
|
if git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null 2>&1; then
|
|
echo "Tag already exists on origin: ${tag} (will reuse)"
|
|
else
|
|
git tag "${tag}"
|
|
git push origin "${tag}"
|
|
fi
|
|
|
|
- name: Publish release + upload assets
|
|
shell: bash
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.CI_MATCHA_GITEA_TOKEN }}
|
|
TAG: ${{ needs.compute.outputs.release_tag }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -z "${GITEA_TOKEN:-}" ]; then
|
|
echo "Missing secrets.CI_MATCHA_GITEA_TOKEN" >&2
|
|
exit 1
|
|
fi
|
|
|
|
python3 - <<'PY'
|
|
import json, os, pathlib, urllib.parse, urllib.request, urllib.error
|
|
api_base = os.environ['GITEA_API_BASE'].rstrip('/')
|
|
repo = os.environ['GITEA_REPO']
|
|
token = os.environ['GITEA_TOKEN'].strip()
|
|
tag = os.environ['TAG'].strip()
|
|
artifacts = sorted(p for p in pathlib.Path('upload').glob('*') if p.is_file())
|
|
headers = {'Authorization': f'token {token}', 'Accept': 'application/json'}
|
|
def request_json(method, url, data=None):
|
|
payload=None
|
|
h=dict(headers)
|
|
if data is not None:
|
|
payload=json.dumps(data).encode('utf-8'); h['Content-Type']='application/json'
|
|
req=urllib.request.Request(url, data=payload, method=method, headers=h)
|
|
try:
|
|
with urllib.request.urlopen(req) as resp:
|
|
txt=resp.read().decode('utf-8'); return resp.getcode(), json.loads(txt) if txt else None
|
|
except urllib.error.HTTPError as exc:
|
|
return exc.code, {'error': exc.read().decode('utf-8', errors='replace')}
|
|
def request_binary_post(url, payload, content_type):
|
|
h=dict(headers); h['Content-Type']=content_type
|
|
req=urllib.request.Request(url, data=payload, method='POST', headers=h)
|
|
with urllib.request.urlopen(req) as resp:
|
|
txt=resp.read().decode('utf-8'); return resp.getcode(), json.loads(txt) if txt else None
|
|
tag_url=f"{api_base}/repos/{repo}/releases/tags/{urllib.parse.quote(tag, safe='')}"
|
|
status, release = request_json('GET', tag_url)
|
|
if status==200 and isinstance(release, dict):
|
|
release_id=release.get('id')
|
|
else:
|
|
status, release = request_json('POST', f"{api_base}/repos/{repo}/releases", {
|
|
'tag_name': tag, 'name': tag, 'body': f'Automated release for {tag}', 'draft': False, 'prerelease': False,
|
|
})
|
|
if status not in (200,201):
|
|
raise SystemExit(f"create release failed {status} {release}")
|
|
release_id=release.get('id')
|
|
assets_url=f"{api_base}/repos/{repo}/releases/{release_id}/assets"
|
|
status, existing = request_json('GET', assets_url)
|
|
existing = existing if isinstance(existing, list) else []
|
|
by_name={e.get('name'):e for e in existing if isinstance(e, dict) and e.get('name')}
|
|
for a in artifacts:
|
|
name=a.name
|
|
prev=by_name.get(name)
|
|
if prev and prev.get('id'):
|
|
ds,_=request_json('DELETE', f"{assets_url}/{prev['id']}")
|
|
if ds!=204: raise SystemExit(f"delete asset failed {name} {ds}")
|
|
code,_=request_binary_post(f"{assets_url}?name={urllib.parse.quote(name, safe='')}", a.read_bytes(), 'application/octet-stream')
|
|
if code not in (200,201): raise SystemExit(f"upload failed {name} {code}")
|
|
print('Uploaded', name)
|
|
print('Release done', tag)
|
|
PY
|
|
|
|
sync_main_to_dev:
|
|
needs: [finalize_and_publish]
|
|
if: ${{ needs.finalize_and_publish.result == 'success' }}
|
|
runs-on: linux-amd64
|
|
steps:
|
|
- name: Checkout repository
|
|
shell: bash
|
|
env:
|
|
CI_MATCHA_GITEA_TOKEN: ${{ secrets.CI_MATCHA_GITEA_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
git init .
|
|
git remote add origin "https://git.hiabuto.net/${{ github.repository }}.git"
|
|
if [ -n "${CI_MATCHA_GITEA_TOKEN:-}" ]; then
|
|
basic=$(python3 -c 'import base64, os; print(base64.b64encode(("matcha:" + os.environ["CI_MATCHA_GITEA_TOKEN"]).encode()).decode())')
|
|
git config --global http.https://git.hiabuto.net/.extraheader "AUTHORIZATION: basic ${basic}"
|
|
else
|
|
echo "CI_MATCHA_GITEA_TOKEN not set; trying unauthenticated checkout."
|
|
fi
|
|
git fetch --depth=50 origin "${{ github.ref_name }}"
|
|
git checkout --force "${{ github.sha }}"
|
|
|
|
- name: Merge main into dev + bump dev0
|
|
shell: bash
|
|
env:
|
|
CI_MATCHA_GITEA_TOKEN: ${{ secrets.CI_MATCHA_GITEA_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -z "${CI_MATCHA_GITEA_TOKEN:-}" ]; then
|
|
echo "Missing secrets.CI_MATCHA_GITEA_TOKEN" >&2
|
|
exit 1
|
|
fi
|
|
|
|
git config user.email "${CI_GIT_EMAIL}"
|
|
git config user.name "${CI_GIT_NAME}"
|
|
|
|
git config --local --unset-all http.https://git.hiabuto.net/.extraheader || true
|
|
git config --global --unset-all http.https://git.hiabuto.net/.extraheader || true
|
|
git remote set-url origin "https://matcha:${CI_MATCHA_GITEA_TOKEN}@git.hiabuto.net/${GITEA_REPO}.git"
|
|
git fetch origin main dev
|
|
git checkout -B dev origin/dev
|
|
git merge origin/main -m "ci: merge main into dev [skip ci]"
|
|
|
|
version_file=$(python3 - <<'PY'
|
|
import re
|
|
p='VERSION'
|
|
for line in open('ci-config.yaml', encoding='utf-8'):
|
|
if re.match(r'^\s*file\s*:', line):
|
|
p=line.split(':',1)[1].strip().strip('"\'')
|
|
break
|
|
print(p)
|
|
PY
|
|
)
|
|
main_version=$(git show origin/main:"$version_file" | head -n1 | tr -d '\r\n' | xargs)
|
|
python3 -c "import re,sys; v=sys.argv[1].strip(); m=re.search(r'(\\d+)\\.(\\d+)\\.(\\d+)', v); (m is not None) or (_ for _ in ()).throw(SystemExit(f'Bad VERSION on main: {v!r}')); print(f'{m.group(1)}.{m.group(2)}.{m.group(3)}-dev0')" "$main_version" > "$version_file"
|
|
|
|
git add "$version_file"
|
|
if git diff --cached --quiet; then
|
|
echo "No VERSION changes after sync.";
|
|
else
|
|
git commit -m "ci: bump dev version to $(cat "$version_file") [skip ci]"
|
|
fi
|
|
|
|
git push origin HEAD:dev
|