From 41321fd05fd09658269725ef86caaff3e45237d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 19 Oct 2026 19:19:04 -0400 Subject: [PATCH] CI: release from GitHub Actions to GitHub releases The release workflow moves from .gitea to .github: macOS on the macos-signing runner, Windows and Linux (WSL) on ely-windows. The persistent clones fetch from GitHub. ci.sh creates or reuses the tag's release through GitHub's API and replaces a rerun's assets of the same name; the update manifests point at GitHub's download URLs. Co-Authored-By: Claude --- {.gitea => .github}/workflows/release.yml | 24 ++++++++------ packaging/macos/release.sh | 2 +- scripts/ci.sh | 39 +++++++++++++---------- 3 files changed, 38 insertions(+), 27 deletions(-) rename {.gitea => .github}/workflows/release.yml (68%) diff --git a/.gitea/workflows/release.yml b/.github/workflows/release.yml similarity index 68% rename from .gitea/workflows/release.yml rename to .github/workflows/release.yml index 18520ce..d40d166 100644 --- a/.gitea/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,18 +1,21 @@ # A tag builds, packages and uploads all three platforms. +# Tags only: no pull request ever reaches the self-hosted runners. name: release on: push: tags: ['v*'] +permissions: + contents: write jobs: macos: # A GUI-session agent: daemons see no login keychain. - runs-on: macos-signing + runs-on: [self-hosted, macos-signing] timeout-minutes: 720 steps: - name: Build, package and upload for macOS shell: bash env: - ELY_RELEASE_TOKEN: ${{ secrets.GITEA_TOKEN }} + ELY_RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }} # The dev clone's engines: LibreOffice from scratch outlasts the runner. ELY_SHARE: /Users/zacharyzhang/Documents/GitHub/ElyOfficeSuite ELY_PATH: /Users/zacharyzhang/.cargo/bin:/opt/homebrew/bin @@ -21,42 +24,45 @@ jobs: ELY_NOTARY_PROFILE: ${{ vars.ELY_NOTARY_PROFILE }} run: | dir=$HOME/ely-ci/ElyOffice - [ -d "$dir/.git" ] || git clone https://git.zacharyzhang.com/ZacharyZhang-NY/ElyOffice.git "$dir" + [ -d "$dir/.git" ] || git clone https://github.com/ELY-Open-Source/ElyOffice.git "$dir" + git -C "$dir" remote set-url origin https://github.com/ELY-Open-Source/ElyOffice.git git -C "$dir" fetch --force --tags origin git -C "$dir" checkout --force "${{ github.sha }}" "$dir/scripts/ci.sh" macos "${{ github.ref_name }}" windows: - runs-on: windows + runs-on: [self-hosted, ely-windows] timeout-minutes: 360 steps: - name: Build, package and upload for Windows shell: bash env: - ELY_RELEASE_TOKEN: ${{ secrets.GITEA_TOKEN }} + ELY_RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }} ELY_SHARE: /c/Users/zhang/Documents/Github/ElyOffice ELY_PATH: /c/Program Files/CMake/bin:/c/Users/zhang/AppData/Local/Microsoft/WinGet/Packages/Ninja-build.Ninja_Microsoft.Winget.Source_8wekyb3d8bbwe run: | dir=$HOME/ely-ci/ElyOffice - [ -d "$dir/.git" ] || git clone https://git.zacharyzhang.com/ZacharyZhang-NY/ElyOffice.git "$dir" + [ -d "$dir/.git" ] || git clone https://github.com/ELY-Open-Source/ElyOffice.git "$dir" + git -C "$dir" remote set-url origin https://github.com/ELY-Open-Source/ElyOffice.git git -C "$dir" fetch --force --tags origin git -C "$dir" checkout --force "${{ github.sha }}" "$dir/scripts/ci.sh" windows "${{ github.ref_name }}" linux: # WSL on the Windows runner keeps a built kernel. - runs-on: windows + runs-on: [self-hosted, ely-windows] needs: windows timeout-minutes: 360 steps: - name: Build, package and upload for Linux shell: bash env: - ELY_RELEASE_TOKEN: ${{ secrets.GITEA_TOKEN }} + ELY_RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }} WSLENV: ELY_RELEASE_TOKEN run: | wsl -d Ubuntu --exec bash -lc ' set -euo pipefail dir=$HOME/ely-ci/ElyOffice - [ -d "$dir/.git" ] || git clone https://git.zacharyzhang.com/ZacharyZhang-NY/ElyOffice.git "$dir" + [ -d "$dir/.git" ] || git clone https://github.com/ELY-Open-Source/ElyOffice.git "$dir" + git -C "$dir" remote set-url origin https://github.com/ELY-Open-Source/ElyOffice.git git -C "$dir" fetch --force --tags origin git -C "$dir" checkout --force "${{ github.sha }}" ELY_SHARE=$HOME/Documents/Github/ElyOffice "$dir/scripts/ci.sh" linux "${{ github.ref_name }}" diff --git a/packaging/macos/release.sh b/packaging/macos/release.sh index ac7a6db..7016a08 100755 --- a/packaging/macos/release.sh +++ b/packaging/macos/release.sh @@ -7,7 +7,7 @@ batch=${1:-1} dist=${ELY_DIST:-$root/dist} version=$(awk -F'"' '/^version = / { print $2; exit }' "$root/Cargo.toml") arch=$(uname -m) -releases=https://git.zacharyzhang.com/ZacharyZhang-NY/ElyOffice/releases +releases=https://github.com/ELY-Open-Source/ElyOffice/releases [ -z "${ELY_NOTARY_PROFILE:-}" ] || [ -n "${ELY_SIGN_IDENTITY:-}" ] || { echo "release.sh: notarizing needs ELY_SIGN_IDENTITY, a Developer ID" >&2 exit 1 diff --git a/scripts/ci.sh b/scripts/ci.sh index c117394..981cf3a 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -10,7 +10,7 @@ version=$(awk -F'"' '/^version = / { print $2; exit }' "$root/Cargo.toml") echo "ci.sh: tag $tag is not v$version" >&2 exit 1 } -: "${ELY_RELEASE_TOKEN:?the Gitea token that uploads}" +: "${ELY_RELEASE_TOKEN:?the GitHub token that uploads}" if [ "$os" = macos ]; then : "${ELY_SIGN_IDENTITY:?the Developer ID Application certificate}" : "${ELY_NOTARY_PROFILE:?the notarytool keychain profile}" @@ -72,13 +72,14 @@ if [ "$os" = linux ]; then fi # The tag's release, made by whichever platform comes first. -api=https://git.zacharyzhang.com/api/v1/repos/ZacharyZhang-NY/ElyOffice -auth="Authorization: token $ELY_RELEASE_TOKEN" -release_id() { curl -fsS -H "$auth" "$api/releases/tags/$tag" | python3 -c 'import json, sys; print(json.load(sys.stdin)["id"])'; } +repo=ELY-Open-Source/ElyOffice +api=https://api.github.com/repos/$repo +auth=(-H "Authorization: Bearer $ELY_RELEASE_TOKEN" -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28") +release_id() { curl -fsS "${auth[@]}" "$api/releases/tags/$tag" | python3 -c 'import json, sys; print(json.load(sys.stdin)["id"])' | tr -d '\r'; } if ! id=$(release_id 2>/dev/null); then body="{\"tag_name\": \"$tag\", \"name\": \"Ely Office $version\"}" - # Another platform may make it first; the read decides. - curl -fsS -H "$auth" -H "Content-Type: application/json" -d "$body" "$api/releases" >/dev/null || true + # Another platform may make it first (422); the read decides. + curl -fsS "${auth[@]}" -H "Content-Type: application/json" -d "$body" "$api/releases" >/dev/null || true id=$(release_id) fi names=() @@ -92,19 +93,23 @@ for file in "$dist"/*; do fi names+=("$name") done -# Rebuilt bytes differ; never mix them with earlier uploads. -have=$(curl -fsS -H "$auth" "$api/releases/$id/assets" | python3 -c 'import json, sys; print("\n".join(a["name"] for a in json.load(sys.stdin)))') -taken=() -for name in "${names[@]}"; do - if grep -qxF "$name" <<<"$have"; then taken+=("$name"); fi +# Each asset's id and name, a page of 100 at a time; Windows Python writes CRLF. +have= +for ((page = 1; ; page++)); do + got=$(curl -fsS "${auth[@]}" "$api/releases/$id/assets?per_page=100&page=$page" | + python3 -c 'import json, sys; print("\n".join("%s\t%s" % (a["id"], a["name"]) for a in json.load(sys.stdin)))' | tr -d '\r') + [ -n "$got" ] || break + have+=$got$'\n' done -if [ ${#taken[@]} -gt 0 ]; then - echo "ci.sh: already on $tag; delete them, then rerun: ${taken[*]}" >&2 - exit 1 -fi +# Rebuilt bytes replace a rerun's earlier upload; other platforms' files stay. +cd "$dist" for name in "${names[@]}"; do - file=$dist/$name + for old in $(awk -F'\t' -v name="$name" '$2 == name { print $1 }' <<<"$have"); do + echo "ci.sh: replacing $name" + curl -fsS "${auth[@]}" -X DELETE "$api/releases/assets/$old" >/dev/null + done echo "ci.sh: uploading $name" - curl -fsS -H "$auth" -F "attachment=@$file" "$api/releases/$id/assets?name=$name" >/dev/null + curl -fsS "${auth[@]}" -H "Content-Type: application/octet-stream" --data-binary "@$name" \ + "https://uploads.github.com/repos/$repo/releases/$id/assets?name=$name" >/dev/null done echo "ci.sh: $os uploaded to $tag"