Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 4 additions & 2 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
[target.wasm32-unknown-unknown]
rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""]
[env]
TS_RS_EXPORT_DIR = { value = "ui/src/generated", relative = true }
TS_RS_IMPORT_EXTENSION = "ts"
TS_RS_LARGE_INT = "number"
37 changes: 37 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Set up Gate
description: Check out the pinned Hellas workspace and load Gate's locked development tools
runs:
using: composite
steps:
- name: Read Hellas revision
id: hellas
shell: bash
working-directory: ${{ github.workspace }}/gate
run: |
revision=$(<.hellas-revision)
if [[ ! "$revision" =~ ^[0-9a-f]{40}$ ]]; then
echo "::error::.hellas-revision must contain one full commit SHA"
exit 1
fi
printf 'revision=%s\n' "$revision" >> "$GITHUB_OUTPUT"

- name: Check out Hellas
uses: actions/checkout@v5
with:
repository: hellas-ai/hellas
ref: ${{ steps.hellas.outputs.revision }}
path: hellas
persist-credentials: false

- name: Install Nix
uses: cachix/install-nix-action@v31
with:
github_access_token: ${{ github.token }}

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: gate -> target
key: ${{ runner.arch }}-${{ hashFiles('gate/flake.lock', 'gate/.hellas-revision') }}
cache-bin: false
cmd-format: nix develop --no-update-lock-file "${{ github.workspace }}/gate" --command {0}
12 changes: 0 additions & 12 deletions .github/dependabot.yml

This file was deleted.

55 changes: 55 additions & 0 deletions .github/scripts/macos-keychain.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
set -euo pipefail

: "${RUNNER_TEMP:?This script requires a GitHub Actions runner}"
keychain="$RUNNER_TEMP/gate-signing.keychain-db"
certificate="$RUNNER_TEMP/gate-signing.p12"
search_list="$RUNNER_TEMP/gate-keychain-search-list.txt"

case "${1:-}" in
setup)
: "${APPLE_CERTIFICATE:?Missing APPLE_CERTIFICATE}"
: "${APPLE_CERTIFICATE_PASSWORD:?Missing APPLE_CERTIFICATE_PASSWORD}"
: "${APPLE_SIGNING_IDENTITY:?Missing APPLE_SIGNING_IDENTITY}"
: "${KEYCHAIN_PASSWORD:?Missing KEYCHAIN_PASSWORD}"
if [[ "$APPLE_SIGNING_IDENTITY" != "Developer ID Application:"* &&
! "$APPLE_SIGNING_IDENTITY" =~ ^[0-9A-Fa-f]{40}$ ]]; then
echo "APPLE_SIGNING_IDENTITY must be a Developer ID Application name or certificate SHA-1" >&2
exit 1
fi
umask 077
/usr/bin/security list-keychains -d user > "$search_list"
printf '%s' "$APPLE_CERTIFICATE" | /usr/bin/base64 --decode > "$certificate"
/usr/bin/security create-keychain -p "$KEYCHAIN_PASSWORD" "$keychain"
/usr/bin/security set-keychain-settings -lut 21600 "$keychain"
/usr/bin/security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain"
/usr/bin/security import "$certificate" -P "$APPLE_CERTIFICATE_PASSWORD" \
-k "$keychain" -t cert -f pkcs12 -T /usr/bin/codesign
/usr/bin/security set-key-partition-list -S apple-tool:,apple:,codesign: \
-s -k "$KEYCHAIN_PASSWORD" "$keychain"
keychains=("$keychain")
while IFS= read -r previous; do
keychains+=("$previous")
done < <(sed -E 's/^[[:space:]]*"//; s/"[[:space:]]*$//' "$search_list")
/usr/bin/security list-keychains -d user -s "${keychains[@]}"
/usr/bin/security find-identity -v -p codesigning "$keychain"
rm -f "$certificate"
;;
cleanup)
if [[ -f "$search_list" ]]; then
keychains=()
while IFS= read -r previous; do
keychains+=("$previous")
done < <(sed -E 's/^[[:space:]]*"//; s/"[[:space:]]*$//' "$search_list")
/usr/bin/security list-keychains -d user -s "${keychains[@]}"
fi
if [[ -f "$keychain" ]]; then
/usr/bin/security delete-keychain "$keychain"
fi
rm -f "$certificate"
;;
*)
echo "Usage: macos-keychain.sh setup|cleanup" >&2
exit 1
;;
esac
77 changes: 77 additions & 0 deletions .github/scripts/package-macos-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
set -euo pipefail

: "${CARGO_TARGET_DIR:?Missing CARGO_TARGET_DIR}"
: "${RUNNER_TEMP:?This script requires a GitHub Actions runner}"
: "${APPLE_SIGNING_IDENTITY:?Missing APPLE_SIGNING_IDENTITY}"
: "${APPLE_ID:?Missing APPLE_ID}"
: "${APPLE_PASSWORD:?Missing APPLE_PASSWORD}"
: "${APPLE_TEAM_ID:?Missing APPLE_TEAM_ID}"
: "${GITHUB_REF_NAME:?Missing release tag}"

version=$(python3 .github/scripts/release-version.py "$GITHUB_REF_NAME")
app="$CARGO_TARGET_DIR/aarch64-apple-darwin/release/bundle/macos/Hellas Gate.app"
release_dir=$(mktemp -d "$RUNNER_TEMP/gate-release.XXXXXX")
staging=$(mktemp -d "$RUNNER_TEMP/gate-dmg.XXXXXX")
mountpoint=$(mktemp -d "$RUNNER_TEMP/gate-mount.XXXXXX")
extraction=$(mktemp -d "$RUNNER_TEMP/gate-zip.XXXXXX")
archive="$release_dir/Hellas-Gate_${version}_aarch64.app.zip"
dmg="$release_dir/Hellas-Gate_${version}_aarch64.dmg"
mounted=false

cleanup() {
if [[ "$mounted" == true ]]; then
/usr/bin/hdiutil detach "$mountpoint" || true
fi
}
trap cleanup EXIT

notarize() {
local artifact=$1
local report=$2
if ! /usr/bin/xcrun notarytool submit "$artifact" \
--apple-id "$APPLE_ID" --password "$APPLE_PASSWORD" --team-id "$APPLE_TEAM_ID" \
--wait --timeout 30m --output-format json > "$report"; then
cat "$report"
return 1
fi
cat "$report"
if [[ "$(/usr/bin/plutil -extract status raw -o - "$report")" != Accepted ]]; then
local submission
submission=$(/usr/bin/plutil -extract id raw -o - "$report")
/usr/bin/xcrun notarytool log "$submission" \
--apple-id "$APPLE_ID" --password "$APPLE_PASSWORD" --team-id "$APPLE_TEAM_ID" || true
return 1
fi
}

bash .github/scripts/verify-macos-app.sh "$app"
/usr/bin/ditto -c -k --sequesterRsrc --keepParent "$app" "$archive"
notarize "$archive" "$release_dir/app-notarization.json"
/usr/bin/xcrun stapler staple "$app"
bash .github/scripts/verify-macos-app.sh "$app" --notarized

# Build the disk image from the stapled app so both delivery formats work offline.
/usr/bin/ditto "$app" "$staging/Hellas Gate.app"
ln -s /Applications "$staging/Applications"
/usr/bin/hdiutil create -volname 'Hellas Gate' -srcfolder "$staging" -format UDZO "$dmg"
/usr/bin/codesign --force --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$dmg"
/usr/bin/codesign --verify --strict --verbose=2 "$dmg"
notarize "$dmg" "$release_dir/dmg-notarization.json"
/usr/bin/xcrun stapler staple "$dmg"
/usr/bin/xcrun stapler validate "$dmg"
/usr/sbin/spctl --assess --type open --context context:primary-signature --verbose=2 "$dmg"
/usr/bin/hdiutil verify "$dmg"
/usr/bin/hdiutil attach "$dmg" -readonly -nobrowse -mountpoint "$mountpoint"
mounted=true
bash .github/scripts/verify-macos-app.sh "$mountpoint/Hellas Gate.app" --notarized
/usr/bin/hdiutil detach "$mountpoint"
mounted=false

# Recreate the zip after stapling; ditto preserves executable modes and symlinks.
/usr/bin/ditto -c -k --sequesterRsrc --keepParent "$app" "$archive"
/usr/bin/ditto -x -k "$archive" "$extraction"
bash .github/scripts/verify-macos-app.sh "$extraction/Hellas Gate.app" --notarized
test -s "$dmg"
test -s "$archive"
printf 'GATE_RELEASE_DIR=%s\n' "$release_dir" >> "$GITHUB_ENV"
26 changes: 26 additions & 0 deletions .github/scripts/release-version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Validate Cargo/Tauri version agreement, and optionally the release tag."""

import json
import re
import sys
import tomllib
from pathlib import Path

root = Path(__file__).resolve().parents[2]
workspace = tomllib.loads((root / "Cargo.toml").read_text())
package = tomllib.loads((root / "src-tauri/Cargo.toml").read_text())["package"]
config = json.loads((root / "src-tauri/tauri.conf.json").read_text())
version = workspace["workspace"]["package"]["version"]
package_version = package["version"]
if package_version == {"workspace": True}:
package_version = version

if not re.fullmatch(r"\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?", version):
sys.exit(f"Invalid release version: {version}")
if config["version"] != version or package_version != version:
sys.exit("Cargo workspace, desktop package, and Tauri versions must agree")
if len(sys.argv) > 2:
sys.exit("Usage: release-version.py [vVERSION]")
if len(sys.argv) == 2 and sys.argv[1] != f"v{version}":
sys.exit(f"Release tag must be v{version}, got {sys.argv[1]}")
print(version)
24 changes: 24 additions & 0 deletions .github/scripts/verify-macos-app.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail

app=${1:?Usage: verify-macos-app.sh APP [--notarized]}
version=$(python3 .github/scripts/release-version.py)
plist="$app/Contents/Info.plist"
test -f "$plist"
[[ "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$plist")" == ai.hellas.gate ]]
[[ "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$plist")" == "$version" ]]
executable=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$plist")
test -x "$app/Contents/MacOS/$executable"
[[ "$(/usr/bin/lipo -archs "$app/Contents/MacOS/$executable")" == arm64 ]]
/usr/bin/codesign --verify --deep --strict --verbose=2 "$app"

if [[ "${2:-}" == --notarized ]]; then
: "${APPLE_TEAM_ID:?APPLE_TEAM_ID is required to verify a release}"
details=$(/usr/bin/codesign --display --verbose=4 "$app" 2>&1)
printf '%s\n' "$details"
printf '%s\n' "$details" | grep -q '^Authority=Developer ID Application:'
printf '%s\n' "$details" | grep -Fqx "TeamIdentifier=$APPLE_TEAM_ID"
printf '%s\n' "$details" | grep -Eq '^CodeDirectory .*flags=.*runtime'
/usr/bin/xcrun stapler validate "$app"
/usr/sbin/spctl --assess --type execute --verbose=2 "$app"
fi
Loading