mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-11-14 09:14:05 +01:00
ci(desktop): build macOS, and publish every desktop artifact to the release page
Three desktop targets, three different states, one destination. macOS did not build at all. There was no workflow, only Cake's from-source scripts and an Xcode project. It turns out most of the work was already done: monero_c ships aarch64-apple-darwin and x86_64-apple-darwin, and Runner.xcodeproj already references both dylibs with CodeSignOnCopy. So this fetches the same prebuilt bundle the iOS and Android legs use, lipos the two slices into universal libraries, and builds. One download serves Apple Silicon and Intel, because a single-arch build looks correct on the machine that made it and fails on half of everyone else's. It signs with a Developer ID Application certificate, which is NOT the Apple Distribution certificate the TestFlight workflow uses. They are not interchangeable: an App Store cert on a directly downloaded app is refused by Gatekeeper. Signing is inside-out by hand rather than --deep, which Apple deprecated and which skips nested code it does not recognise. Then notarytool with the App Store Connect key this repository already has, then stapler, so Gatekeeper clears the app without a network round trip. Every credential is optional and the preflight says, before spending forty minutes, exactly which stages the run can reach and what the user will see if it cannot reach them. Absent secrets downgrade the build loudly; they do not fail it and they do not pass silently. Windows and Linux already built. Windows already Authenticode-signs through the Fleet broker. What both lacked was a way to get the artifact to anybody: upload-artifact is the only publication and Gitea Actions artifacts here upload and then list as zero, so the signed Windows zip has never been reachable. Both now publish to the GitHub release, on the smirk pattern: tag-triggered, the release must already exist so a half-populated one never goes public, and a same-named asset from an earlier attempt is replaced rather than duplicated. Windows does its API work in PowerShell because git-bash there has neither python nor node guaranteed and ConvertFrom-Json is native. Tags now build automatically on all three. A tag is the thing a release is cut from, and the artifact that gets signed should come from it rather than from whatever was dispatched by hand. scripts/release/sign-release.sh detach-signs the collected artifacts and signs SHA256SUMS, which is the signature that matters when a download reaches someone from a mirror without its .asc. It verifies even immediately after signing: a signature written but never checked invites trust it has not earned. Not done here, and both need a human: GH_RELEASE_TOKEN does not exist in this repository's Gitea secrets, and the release key's user ID still says Smirk.
This commit is contained in:
@@ -5,10 +5,26 @@ name: Hash Bags Linux build
|
||||
# - on PRs targeting dev/main (gates merges)
|
||||
# - manual via workflow_dispatch ("Run workflow" button in the UI)
|
||||
on:
|
||||
# Manual-only for now — auto-run on every push was too noisy. Trigger
|
||||
# Manual-only for pushes — auto-run on every push was too noisy. Trigger
|
||||
# via Actions → "Hash Bags Linux build" → Run workflow when you want a
|
||||
# build. Add `push:` back here if/when we want pre-merge validation.
|
||||
# build. Add `push: branches:` back here if/when we want pre-merge validation.
|
||||
#
|
||||
# Tags DO build automatically: a tag is a deliberate act and it is the thing
|
||||
# a release is cut from, so the artifact that gets signed and published should
|
||||
# come from the tag rather than from whatever was dispatched by hand.
|
||||
push:
|
||||
tags:
|
||||
- 'hash-bags-*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
description: >-
|
||||
Existing release tag to publish this build to (e.g. hash-bags-1.0.3).
|
||||
Leave empty to build without publishing. The release must already
|
||||
exist: this never creates one, because a release created here would
|
||||
go public half-populated while the other platforms are still building.
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
concurrency:
|
||||
group: linux-${{ github.ref }}
|
||||
@@ -295,3 +311,63 @@ jobs:
|
||||
name: hash-wallet-linux-appimage-${{ github.sha }}
|
||||
path: build/linux/x64/release/Hash_Bags-x86_64.AppImage
|
||||
retention-days: 14
|
||||
|
||||
# ---- Publish to the release pages -------------------------------------
|
||||
# Artifact retrieval through Gitea Actions is unreliable here: uploads
|
||||
# succeed and then list as zero, so a build nobody can download is a build
|
||||
# that did not happen. Release assets are the path that actually returns
|
||||
# bytes, and GitHub is where users are told to look.
|
||||
#
|
||||
# Both steps skip quietly without a token, so a fork or a local runner
|
||||
# does not fail a build over a credential it was never given.
|
||||
- name: Resolve the release tag
|
||||
id: reltag
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ "${GITHUB_REF:-}" != "${GITHUB_REF#refs/tags/}" ]; then
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
else
|
||||
TAG="${{ github.event.inputs.release_tag }}"
|
||||
fi
|
||||
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
|
||||
if [ -n "$TAG" ]; then echo "publishing to $TAG"; else echo "no tag; build only"; fi
|
||||
|
||||
- name: Publish the AppImage to the GitHub release
|
||||
if: steps.reltag.outputs.tag != ''
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_RELEASE_TOKEN }}
|
||||
TAG: ${{ steps.reltag.outputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${GH_TOKEN:-}" ]; then
|
||||
echo "GH_RELEASE_TOKEN not set; skipping the GitHub upload"
|
||||
exit 0
|
||||
fi
|
||||
REPO="Such-Software/hash-wallet"
|
||||
API="https://api.github.com/repos/$REPO"
|
||||
AUTH="Authorization: Bearer $GH_TOKEN"
|
||||
FILE="build/linux/x64/release/Hash_Bags-x86_64.AppImage"
|
||||
[ -f "$FILE" ] || { echo "no AppImage to upload"; exit 1; }
|
||||
NAME="$(basename "$FILE")"
|
||||
code=$(curl -sS -o rel-gh.json -w '%{http_code}' -H "$AUTH" "$API/releases/tags/$TAG" || true)
|
||||
if [ "$code" != "200" ]; then
|
||||
echo "no GitHub release for $TAG (HTTP $code); skipping"
|
||||
exit 0
|
||||
fi
|
||||
# Fail rather than skip if there is no JSON parser. An upload that
|
||||
# quietly did not happen leaves a release page that looks finished
|
||||
# and a user with nothing to download.
|
||||
PY=""
|
||||
for c in python3 python; do command -v "$c" >/dev/null && { PY="$c"; break; }; done
|
||||
[ -n "$PY" ] || { echo "no python available to parse the release JSON"; exit 1; }
|
||||
ID=$($PY -c 'import json;print(json.load(open("rel-gh.json"))["id"])')
|
||||
# Replace a same-named asset from an earlier attempt, so a re-run does
|
||||
# not leave two that differ only by which build produced them.
|
||||
for oid in $($PY -c 'import json,sys;d=json.load(open("rel-gh.json"));print(" ".join(str(a["id"]) for a in d.get("assets",[]) if a["name"]==sys.argv[1]))' "$NAME"); do
|
||||
curl -sS -o /dev/null -X DELETE -H "$AUTH" "$API/releases/assets/$oid" || true
|
||||
done
|
||||
curl -sS --fail-with-body -o /dev/null -X POST -H "$AUTH" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary "@$FILE" \
|
||||
"https://uploads.github.com/repos/$REPO/releases/$ID/assets?name=$NAME"
|
||||
echo "uploaded $NAME to the GitHub release $TAG"
|
||||
|
||||
Reference in New Issue
Block a user