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 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude committed 2026-10-19 19:19:04 -04:00
1 parent 0d991e1c71
commit 41321fd05f
3 files changed
+38 -27

No files matched your search

@@ -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 }}"
+1 -1
View File
@@ -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
+22 -17
View File
@@ -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"