Skip to content

fix: pass signing key via temp file to tauri signer sign #24

fix: pass signing key via temp file to tauri signer sign

fix: pass signing key via temp file to tauri signer sign #24

Workflow file for this run

name: Release
on:
push:
tags:
- "v*"
env:
CARGO_TERM_COLOR: always
CARGO_HOME: /Users/runner/.cargo
CARGO_INCREMENTAL: 0
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
jobs:
release-macos:
runs-on: macos-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Rust cache
uses: Swatinem/rust-cache@v2
with:
workspaces: ". -> target"
cache-on-failure: true
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install frontend deps
run: npm ci
- name: Install system deps
run: |
# ffmpeg@7 required: mpv 0.40.0 uses FF_PROFILE_* macros removed in ffmpeg 8.x
brew install meson ninja pkg-config ffmpeg@7 libass libplacebo dylibbundler
# Make ffmpeg@7 visible to pkg-config (it is keg-only, not linked by default)
echo "PKG_CONFIG_PATH=$(brew --prefix ffmpeg@7)/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" >> $GITHUB_ENV
- name: Build libmpv
run: ./scripts/build-libmpv.sh macos
- name: Prepare signing key
run: |
# Tauri CLI requires the raw minisign format (starting with "untrusted comment:").
# The secret may be stored as:
# (a) base64-encoded raw key, or
# (b) raw key with actual newlines, or
# (c) raw key with escaped \n (two chars) instead of real newlines.
# We normalize to raw format and always write to GITHUB_ENV so the key
# is guaranteed to have proper newlines when Tauri reads it.
python3 - << 'EOF'
import base64, os, sys
key = os.environ.get("TAURI_SIGNING_PRIVATE_KEY", "")
print(f"[signing] Key length: {len(key)}, has newlines: {'yes' if chr(10) in key else 'no'}")
if not key.strip():
print("[signing] ERROR: no signing key set.")
sys.exit(1)
# Normalize literal \n (two chars) to actual newlines
key = key.replace("\\n", "\n")
def try_b64(s):
try:
return base64.b64decode(s.strip(), validate=True).decode("utf-8")
except Exception:
return None
if "untrusted comment:" in key:
final_key = key
print("[signing] Raw minisign key detected.")
else:
# Try up to 2 levels of base64 decoding to handle both
# single-encoded and accidentally double-encoded secrets.
current = key
final_key = None
for depth in range(1, 3):
decoded = try_b64(current)
if decoded is None:
print(f"[signing] ERROR: base64 decode failed at depth {depth}.")
sys.exit(1)
if "untrusted comment:" in decoded:
final_key = decoded
print(f"[signing] Key decoded at depth {depth}.")
break
current = decoded
if final_key is None:
print("[signing] ERROR: key is not a valid minisign private key.")
sys.exit(1)
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write("TAURI_SIGNING_PRIVATE_KEY<<__ENDKEY__\n")
f.write(final_key.rstrip("\n") + "\n")
f.write("__ENDKEY__\n")
print("[signing] Key written to GITHUB_ENV.")
EOF
- name: Build Tauri app
# Build without updater artifacts so bundle_dmg.sh doesn't race with
# hdiutil over the .app bundle. We create the updater artifacts manually below.
run: cd apps/desktop && npx tauri build --config '{"bundle":{"createUpdaterArtifacts":false}}'
- name: Create updater artifacts
run: |
set -euo pipefail
VERSION=$(python3 -c "import json; print(json.load(open('apps/desktop/src-tauri/tauri.conf.json'))['version'])")
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
BUNDLE_DIR="target/release/bundle/macos"
UPDATER="$BUNDLE_DIR/MaxVideoPlayer.app.tar.gz"
# Create the .tar.gz updater archive from the built .app
cd "$BUNDLE_DIR"
tar czf MaxVideoPlayer.app.tar.gz MaxVideoPlayer.app
cd -
# tauri signer sign cannot parse raw minisign keys from the env var
# (it tries base64 decode and fails). Write the key to a temp file
# and pass it with -k so Tauri reads it as a raw file instead.
printf '%s' "$TAURI_SIGNING_PRIVATE_KEY" > /tmp/skey.key
chmod 600 /tmp/skey.key
if [[ -n "${TAURI_SIGNING_PRIVATE_KEY_PASSWORD:-}" ]]; then
cd apps/desktop && npx tauri signer sign \
-k /tmp/skey.key \
-p "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD" \
"../../$UPDATER"
cd -
else
cd apps/desktop && npx tauri signer sign \
-k /tmp/skey.key \
--no-password \
"../../$UPDATER"
cd -
fi
rm -f /tmp/skey.key
# Build latest.json for the auto-updater endpoint
SIG=$(cat "${UPDATER}.sig")
python3 - "$VERSION" "$SIG" << 'PYEOF'
import json, sys
from datetime import datetime, timezone
version, sig = sys.argv[1], sys.argv[2]
data = {
"version": version,
"notes": "See the CHANGELOG for details.",
"pub_date": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"platforms": {
"darwin-aarch64": {
"signature": sig,
"url": f"https://github.com/MaxMB15/MaxVideoPlayer/releases/download/v{version}/MaxVideoPlayer_{version}_aarch64.app.tar.gz"
}
}
}
with open("latest.json", "w") as f:
json.dump(data, f, indent=2)
print("latest.json written")
PYEOF
- name: Publish GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
VERSION="$VERSION"
DMG="target/release/bundle/dmg/MaxVideoPlayer_${VERSION}_aarch64.dmg"
UPDATER="target/release/bundle/macos/MaxVideoPlayer.app.tar.gz"
gh release create "v${VERSION}" \
--repo "$GITHUB_REPOSITORY" \
--title "MaxVideoPlayer v${VERSION}" \
--notes "See the [CHANGELOG](https://github.com/MaxMB15/MaxVideoPlayer/blob/main/CHANGELOG.md) for details." \
--draft \
"$DMG" \
"${UPDATER}#MaxVideoPlayer_${VERSION}_aarch64.app.tar.gz" \
"${UPDATER}.sig#MaxVideoPlayer_${VERSION}_aarch64.app.tar.gz.sig" \
"latest.json"