mirror of
https://github.com/project-dorado/.github.git
synced 2026-10-12 02:21:10 +00:00
feat(signing): unified GPG release key, verification guide, and org secrets tooling
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
h2eyLaSysuCo47WOvwuN
|
||||
=7J/k
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
@@ -0,0 +1,93 @@
|
||||
# Release Signing & Cryptographic Verification
|
||||
|
||||
This document describes how to verify official releases, binaries, and packages published by:
|
||||
- [project-dorado](https://github.com/project-dorado)
|
||||
- [Heretek-Games](https://github.com/Heretek-Games)
|
||||
- [Heretek-AI](https://github.com/Heretek-AI)
|
||||
|
||||
All official release artifacts are signed using **Heretek Release Signing** GPG key and authenticated with **GitHub Artifact Attestations (Sigstore SLSA Provenance)**.
|
||||
|
||||
---
|
||||
|
||||
## 1. GPG Public Key
|
||||
|
||||
The ASCII-armored public key is available at:
|
||||
- Repository file: [`HERETEK_RELEASE_SIGNING_KEY.asc`](./HERETEK_RELEASE_SIGNING_KEY.asc)
|
||||
- Direct URL: `https://raw.githubusercontent.com/project-dorado/.github/main/HERETEK_RELEASE_SIGNING_KEY.asc`
|
||||
|
||||
### Key Details
|
||||
- **User ID**: `Heretek Release Signing (Heretek & Dorado Unified Release Signing Key) <[email protected]>`
|
||||
- **Key ID**: `BCBC85E3B15DC872`
|
||||
- **Algorithm**: `RSA 4096-bit`
|
||||
- **Fingerprint**:
|
||||
```text
|
||||
11EE 4B06 9B95 02EB E5EF 6531 BCBC 85E3 B15D C872
|
||||
```
|
||||
|
||||
### Importing the Public Key
|
||||
|
||||
```bash
|
||||
# Direct import from GitHub:
|
||||
curl -sSL https://raw.githubusercontent.com/project-dorado/.github/main/HERETEK_RELEASE_SIGNING_KEY.asc | gpg --import
|
||||
|
||||
# Or via keyserver (once synchronized):
|
||||
gpg --keyserver keys.openpgp.org --recv-keys 11EE4B069B9502EBE5EF6531BCBC85E3B15DC872
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Verifying Release Assets with GPG
|
||||
|
||||
Every release publishes a `SHA256SUMS` file and an ASCII-armored detached signature `SHA256SUMS.asc`.
|
||||
|
||||
### Step-by-Step Verification
|
||||
|
||||
```bash
|
||||
# 1. Download the release asset(s), SHA256SUMS, and SHA256SUMS.asc
|
||||
curl -LO https://github.com/project-dorado/dorado/releases/download/<tag>/Dorado-Linux-x64.tar.gz
|
||||
curl -LO https://github.com/project-dorado/dorado/releases/download/<tag>/SHA256SUMS
|
||||
curl -LO https://github.com/project-dorado/dorado/releases/download/<tag>/SHA256SUMS.asc
|
||||
|
||||
# 2. Verify the GPG detached signature on the checksum file
|
||||
gpg --verify SHA256SUMS.asc SHA256SUMS
|
||||
|
||||
# Expected output includes:
|
||||
# gpg: Good signature from "Heretek Release Signing ... <[email protected]>"
|
||||
# Primary key fingerprint: 11EE 4B06 9B95 02EB E5EF 6531 BCBC 85E3 B15D C872
|
||||
|
||||
# 3. Verify the checksum of the downloaded file
|
||||
sha256sum --check --ignore-missing SHA256SUMS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Verifying with GitHub Artifact Attestations (Sigstore)
|
||||
|
||||
Releases are also signed with GitHub's native Sigstore infrastructure using OpenID Connect (OIDC). This provides cryptographically verifiable build provenance linking the artifact directly to the exact workflow run, repository, and git commit.
|
||||
|
||||
### Verification using GitHub CLI (`gh`):
|
||||
|
||||
```bash
|
||||
# Verify binary against project-dorado repository
|
||||
gh attestation verify Dorado-Linux-x64.tar.gz --owner project-dorado
|
||||
|
||||
# Or verify against Heretek-Games or Heretek-AI
|
||||
gh attestation verify <game-package.zip> --owner Heretek-Games
|
||||
gh attestation verify <package.tar.gz> --owner Heretek-AI
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Verifying Android APK Signatures
|
||||
|
||||
Android application packages (`.apk`) are signed with the official Heretek release keystore (APK Signature Scheme v1, v2, v3, and v4).
|
||||
|
||||
To verify on any machine with the Android SDK installed:
|
||||
|
||||
```bash
|
||||
apksigner verify --verbose --print-certs dorado-hd-<sha>.apk
|
||||
```
|
||||
|
||||
Expected signer:
|
||||
- **Owner**: `CN=Heretek Release Signing, OU=Engineering, O=Heretek, C=US`
|
||||
- **Key Algorithm**: `4096-bit RSA`
|
||||
@@ -1,13 +1,15 @@
|
||||
# Dorado — Program Status
|
||||
|
||||
**Last updated:** 2026-09-11
|
||||
**Last updated:** 2026-09-13
|
||||
|
||||
A consolidated view of what has been completed and what remains across the
|
||||
organization. Legend: ✅ done · 🚧 in progress · ⏳ pending
|
||||
|
||||
> **Single source of truth.** This file is the canonical program status for the
|
||||
> organization; repository READMEs and the website link here. Figures are as of
|
||||
> the date above.
|
||||
> organization; repository READMEs, in-repo working notes (e.g.
|
||||
> `dorado-hd/docs/parity-roadmap.md`, `docs/ui-parity-audit.md`) and the website
|
||||
> defer to it. On any conflict of figures or status, this file wins — update it
|
||||
> when a change moves counts or state, and date the change above.
|
||||
|
||||
All figures below were **re-verified against `origin/main` and executed test
|
||||
suites on 2026-09-10** (commands in [Verification](#verification)). Every
|
||||
@@ -18,14 +20,22 @@ repository has a clean working tree and is **0 ahead / 0 behind** its remote.
|
||||
| Repository | Purpose | State |
|
||||
|---|---|---|
|
||||
| [dorado](https://github.com/project-dorado/dorado) | Zune 4.8 desktop re-creation (.NET 8 / Avalonia) | ✅ pushed · 433/433 tests (incl. golden-image gate) · **~88%** weight parity ([audited 2026-09-11](https://github.com/project-dorado/dorado/blob/main/docs/parity/audit-2026-09-11.md), remediated M1–M3 + follow-on) |
|
||||
| [dorado-hd](https://github.com/project-dorado/dorado-hd) | Zune HD Android client (Kotlin / Compose) | ✅ pushed · **1,602/1,602 tests** (JDK 21) · **M16: all 62 official apps implemented** + engine3d core · **UI-parity program complete** (audit + gap register, 63-app smoke/layout/back/golden suites, emulator crawl 63/63) · M4, M7–M9, M12–M14 done; **next-steps Track B/C shipped**: fade-through crossfade, live FFT visualizer, sleep timer, richer lock-screen, Smart DJ, podcast search, artist photo grid, radio/picture Now Playing, **audiobooks (D4)**; M15 social inbox wired; M10 widget · UI/UX deep audit done ([audit](https://github.com/project-dorado/dorado-hd/blob/main/docs/ui-ux-audit.md)) |
|
||||
| [dorado-cloud](https://github.com/project-dorado/dorado-cloud) | Community cloud services (.NET 8) | ✅ pushed · M0–M12 done · 195/195 tests · M6 media legal-gated (model ready) |
|
||||
| [dorado-hd](https://github.com/project-dorado/dorado-hd) | Zune HD Android client (Kotlin / Compose) | ✅ pushed · **1,603/1,603 tests** (JDK 21) · **M16: all 62 official apps implemented** + engine3d core · **UI-parity program complete** (audit + gap register, 63-app smoke/layout/back/golden suites, emulator crawl 63/63) · M4, M7–M9, M12–M14 done; **next-steps Track B/C shipped**: fade-through crossfade, live FFT visualizer, sleep timer, richer lock-screen, Smart DJ, podcast search, artist photo grid, radio/picture Now Playing, **audiobooks (D4)**; M15 social inbox wired; M10 widget · UI/UX deep audit done ([audit](https://github.com/project-dorado/dorado-hd/blob/main/docs/ui-ux-audit.md)) |
|
||||
| [dorado-cloud](https://github.com/project-dorado/dorado-cloud) | Community cloud services (.NET 8) | ✅ pushed · M0–M12 done · 200/200 tests · M6 media legal-gated (model ready) |
|
||||
| [dorado-emu](https://github.com/project-dorado/dorado-emu) | Zune HD `.zcp`/`.ccgame` XNA emulator core | ✅ pushed · M0–M1 + ZCSTFS volume reader + **official-app runtime** (XNB pipeline, ZDK bridge) · **79/79 tests** · **50/54 framework-path titles run headless** · DRM key seam (no keys shipped) |
|
||||
| [project-dorado.github.io](https://github.com/project-dorado/project-dorado.github.io) | Organization website (dorado.org.uk) | ✅ published |
|
||||
| [.github](https://github.com/project-dorado/.github) | Org profile + community health files | ✅ published |
|
||||
| [.github](https://github.com/project-dorado/.github) | Org profile + community health files | ✅ published · Release signing keys & org secrets provisioned |
|
||||
|
||||
## Completed
|
||||
|
||||
### Release signing & cryptographic provenance ✅ complete
|
||||
- Cross-organization release signing infrastructure established across `project-dorado`, `Heretek-Games`, and `Heretek-AI`.
|
||||
- 4096-bit RSA GPG release signing key and PKCS12 Android release keystore provisioned across organization secrets (`GPG_PRIVATE_KEY`, `GPG_PASSPHRASE`, `GPG_KEY_ID`, `ANDROID_KEYSTORE_BASE64`, `ANDROID_KEYSTORE_PASSWORD`, `ANDROID_KEY_ALIAS`, `ANDROID_KEY_PASSWORD`).
|
||||
- Desktop (`dorado`) workflow enhanced with `SHA256SUMS` generation, detached GPG signatures (`SHA256SUMS.asc`), and Sigstore SLSA build provenance attestations (`actions/attest-build-provenance@v2`).
|
||||
- Android client (`dorado-hd`) Gradle build wired with production release signing configuration, and workflow enhanced with signed APK packaging, detached GPG checksum signatures, and Sigstore build provenance attestations.
|
||||
- Public key documentation published in `.github/KEYS.md` with ASCII-armored key in `.github/HERETEK_RELEASE_SIGNING_KEY.asc`.
|
||||
|
||||
|
||||
### Repository migration & rebrand
|
||||
- `Heretek-AI/not-zune` → **project-dorado/dorado** and
|
||||
`Heretek-AI/xune-HD` → **project-dorado/dorado-hd**; histories preserved,
|
||||
@@ -73,7 +83,7 @@ repository has a clean working tree and is **0 ahead / 0 behind** its remote.
|
||||
stream only from external, untracked corpora and fail closed (`404`) when
|
||||
unset; `commerce.zune.net` purchase and Zune-Pass DRM/license endpoints are
|
||||
**not** implemented (no DRM circumvention); login is disabled by default.
|
||||
- 195/195 tests; Release build 0 warnings (`/warnaserror`).
|
||||
- 200/200 tests; Release build 0 warnings (`/warnaserror`).
|
||||
|
||||
### Legacy client enablement + hardening (M8–M12) ✅
|
||||
- **M8 — Legacy client enablement:** gated `login.zune.net` bridge now issues the
|
||||
@@ -139,7 +149,7 @@ repository has a clean working tree and is **0 ahead / 0 behind** its remote.
|
||||
51.4 GB, per-file MD5); decompiled app tree verifies **7,843/7,844** files
|
||||
(one upstream gap); `color-spill` remains corpus-blocked (GUID collision
|
||||
with Reversi) with provisional genre constants.
|
||||
- Gates: `assembleDebug` + `testDebugUnitTest` (1,602) + `lintDebug` green;
|
||||
- Gates: `assembleDebug` + `testDebugUnitTest` (1,603) + `lintDebug` green;
|
||||
`DesignInvariantTest` 0 violations.
|
||||
|
||||
### Dorado-HD modern listening + always-on (M9/M10) ✅
|
||||
@@ -201,13 +211,13 @@ done
|
||||
|
||||
# .NET suites
|
||||
dotnet test # dorado → 433 passed (14 Domain + 418 Application + 1 visual golden gate)
|
||||
dotnet test DoradoCloud.sln # cloud → 195 passed (178 integration + 17 client)
|
||||
dotnet test DoradoCloud.sln # cloud → 200 passed (182 integration + 18 client)
|
||||
dotnet test Dorado.sln # dorado-emu → 79 passed (incl. official-app smoke)
|
||||
native/zdk-bridge/build.sh # dorado-emu → libZDK.so (image/font/ZDKGL bridge)
|
||||
python3 tools/smoke_official.py # dorado-emu → 50/54 framework-path titles ran
|
||||
|
||||
# Android suite (JDK 21 required; JDK 26 breaks Robolectric)
|
||||
JAVA_HOME=/home/linuxbrew/.linuxbrew/opt/openjdk@21/libexec ./gradlew testDebugUnitTest # → 1,602 passed (unit + UI parity suites)
|
||||
JAVA_HOME=/home/linuxbrew/.linuxbrew/opt/openjdk@21/libexec ./gradlew testDebugUnitTest # → 1,603 passed (unit + UI parity suites)
|
||||
JAVA_HOME=/home/linuxbrew/.linuxbrew/opt/openjdk@21/libexec ./gradlew assembleDebug lintDebug # → green
|
||||
|
||||
# official-app corpus (external, untracked; never committed)
|
||||
@@ -244,10 +254,9 @@ python3 dorado-hd/tools/zune_app_corpus.py --verify-only # 7,843/7,844 MD5 ver
|
||||
redirect, CSRF coverage, pgvector fallback); desktop **MPRIS2** shipped
|
||||
(i18n at 20 locales; remaining locales deferred per
|
||||
`docs/parity/deferred_registry.md`).
|
||||
- **Gates:** HD 1,602/1,602 + lintDebug + ui_lint; emulator entry crawl
|
||||
- **Gates:** HD 1,603/1,603 + lintDebug + ui_lint; emulator entry crawl
|
||||
63/63 and interactive flow crawl 63/63 (a launch-thread P0 was caught and
|
||||
fixed by the crawl).
|
||||
- **Documented N/A:** live-radio ±5-min rewind (no time-shifting server);
|
||||
cloud inbox mark-read (server read-only); org repository pinning (manual
|
||||
profile step — descriptions/topics are set).
|
||||
org repository pinning (manual profile step — descriptions/topics are set).
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# ==============================================================================
|
||||
# Reusable Template: Sign & Attest GitHub Release Assets
|
||||
#
|
||||
# Usage in repositories across Heretek-Games, Heretek-AI, and project-dorado:
|
||||
#
|
||||
# Include this pattern in your release or publish job. It uses the organization-level
|
||||
# secrets (GPG_PRIVATE_KEY, GPG_PASSPHRASE) automatically available to all repos.
|
||||
# ==============================================================================
|
||||
|
||||
name: Sign & Attest Release Assets Example
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags: ['v*']
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
|
||||
jobs:
|
||||
publish-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# (Insert build/download artifact steps here to produce distribution files)
|
||||
# e.g., dist/myapp-linux.tar.gz, dist/myapp-windows.zip, etc.
|
||||
|
||||
- name: Generate Checksums
|
||||
working-directory: dist
|
||||
run: |
|
||||
sha256sum * > SHA256SUMS
|
||||
|
||||
- name: Import GPG Key
|
||||
if: env.HAS_GPG == 'true'
|
||||
uses: crazy-max/ghaction-import-gpg@v6
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
env:
|
||||
HAS_GPG: ${{ secrets.GPG_PRIVATE_KEY != '' }}
|
||||
|
||||
- name: Sign Checksums (Detached ASCII-armored signature)
|
||||
if: env.HAS_GPG == 'true'
|
||||
working-directory: dist
|
||||
run: |
|
||||
gpg --batch --yes --detach-sign --armor SHA256SUMS
|
||||
|
||||
- name: Attest Build Provenance (Sigstore)
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: |
|
||||
dist/*
|
||||
dist/SHA256SUMS
|
||||
dist/SHA256SUMS.asc
|
||||
|
||||
- name: Publish Release to GitHub
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ github.ref_name }}
|
||||
name: Release ${{ github.ref_name }}
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
dist/*
|
||||
dist/SHA256SUMS
|
||||
dist/SHA256SUMS.asc
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
Executable
+327
@@ -0,0 +1,327 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# setup-org-signing-secrets.sh
|
||||
# Unified Release Signing Key Generation & GitHub Org Secrets Provisioning
|
||||
#
|
||||
# Target Organizations:
|
||||
# - project-dorado (https://github.com/project-dorado)
|
||||
# - Heretek-Games (https://github.com/Heretek-Games)
|
||||
# - Heretek-AI (https://github.com/Heretek-AI)
|
||||
#
|
||||
# Generated Credentials:
|
||||
# - GPG Signing Key (RSA 4096, 3-year validity)
|
||||
# - Android Keystore (PKCS12, RSA 4096, 10,000-day validity)
|
||||
#
|
||||
# Organizational Secrets Created:
|
||||
# - GPG_PRIVATE_KEY
|
||||
# - GPG_PASSPHRASE
|
||||
# - GPG_KEY_ID
|
||||
# - ANDROID_KEYSTORE_BASE64
|
||||
# - ANDROID_KEYSTORE_PASSWORD
|
||||
# - ANDROID_KEY_ALIAS
|
||||
# - ANDROID_KEY_PASSWORD
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
GITHUB_REPO_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
BACKUP_DIR="${HERETEK_BACKUP_DIR:-${HOME}/.heretek-signing-backup}"
|
||||
|
||||
TARGET_ORGS=("project-dorado" "Heretek-Games" "Heretek-AI")
|
||||
|
||||
KEY_NAME="${HERETEK_KEY_NAME:-Heretek Release Signing}"
|
||||
KEY_EMAIL="${HERETEK_KEY_EMAIL:-releases@heretek.net}"
|
||||
KEY_COMMENT="${HERETEK_KEY_COMMENT:-Heretek & Dorado Unified Release Signing Key}"
|
||||
KEY_VALIDITY="${HERETEK_KEY_VALIDITY:-3y}"
|
||||
|
||||
KEYSTORE_ALIAS="${HERETEK_KEYSTORE_ALIAS:-heretek-release}"
|
||||
KEYSTORE_VALIDITY="${HERETEK_KEYSTORE_VALIDITY:-10000}"
|
||||
KEYSTORE_DNAME="${HERETEK_KEYSTORE_DNAME:-CN=Heretek Release Signing, OU=Engineering, O=Heretek, C=US}"
|
||||
|
||||
# Color output helpers
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
}
|
||||
|
||||
check_prerequisites() {
|
||||
log_info "Checking prerequisites..."
|
||||
local missing=()
|
||||
for cmd in gpg keytool openssl base64 gh; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
missing+=("$cmd")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
log_error "Missing required utilities: ${missing[*]}"
|
||||
log_error "Please install them before continuing."
|
||||
exit 1
|
||||
fi
|
||||
log_success "All prerequisites are installed."
|
||||
}
|
||||
|
||||
init_backup_dir() {
|
||||
if [ ! -d "$BACKUP_DIR" ]; then
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
chmod 700 "$BACKUP_DIR"
|
||||
log_info "Created secure backup directory: $BACKUP_DIR (permissions 0700)"
|
||||
else
|
||||
chmod 700 "$BACKUP_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
generate_credentials() {
|
||||
init_backup_dir
|
||||
local gpg_key_file="${BACKUP_DIR}/heretek-gpg-private.asc"
|
||||
local jks_file="${BACKUP_DIR}/heretek-release.jks"
|
||||
local env_file="${BACKUP_DIR}/secrets.env"
|
||||
|
||||
if [ -f "$gpg_key_file" ] && [ -f "$jks_file" ] && [ -f "$env_file" ]; then
|
||||
log_warn "Credentials already exist in $BACKUP_DIR."
|
||||
log_warn "Skipping generation. Using existing credentials."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Generating new cryptographic keys..."
|
||||
|
||||
# 1. Passphrases
|
||||
local gpg_passphrase="${HERETEK_GPG_PASSPHRASE:-$(openssl rand -base64 24)}"
|
||||
local keystore_pass="${HERETEK_KEYSTORE_PASSWORD:-$(openssl rand -base64 24)}"
|
||||
local key_pass="$keystore_pass"
|
||||
|
||||
# 2. GPG Key Generation in isolated GNUPGHOME
|
||||
log_info "Generating 4096-bit RSA GPG release signing key..."
|
||||
local gpg_temp_home
|
||||
gpg_temp_home="$(mktemp -d)"
|
||||
chmod 700 "$gpg_temp_home"
|
||||
|
||||
cat <<EOF > "$gpg_temp_home/gen-key.conf"
|
||||
Key-Type: RSA
|
||||
Key-Length: 4096
|
||||
Subkey-Type: RSA
|
||||
Subkey-Length: 4096
|
||||
Name-Real: ${KEY_NAME}
|
||||
Name-Email: ${KEY_EMAIL}
|
||||
Name-Comment: ${KEY_COMMENT}
|
||||
Expire-Date: ${KEY_VALIDITY}
|
||||
Passphrase: ${gpg_passphrase}
|
||||
%commit
|
||||
EOF
|
||||
|
||||
GNUPGHOME="$gpg_temp_home" gpg --batch --generate-key "$gpg_temp_home/gen-key.conf" >/dev/null 2>&1
|
||||
|
||||
local gpg_key_id
|
||||
gpg_key_id="$(GNUPGHOME="$gpg_temp_home" gpg --with-colons --list-secret-keys "$KEY_EMAIL" | awk -F: '$1 == "sec" {print $5; exit}')"
|
||||
local gpg_fingerprint
|
||||
gpg_fingerprint="$(GNUPGHOME="$gpg_temp_home" gpg --with-colons --fingerprint "$KEY_EMAIL" | awk -F: '$1 == "fpr" {print $10; exit}')"
|
||||
|
||||
log_success "Generated GPG Key: $gpg_key_id (Fingerprint: $gpg_fingerprint)"
|
||||
|
||||
# Export ASCII-armored private and public keys
|
||||
GNUPGHOME="$gpg_temp_home" gpg --armor --batch --pinentry-mode loopback --passphrase "$gpg_passphrase" --export-secret-keys "$gpg_key_id" > "$gpg_key_file"
|
||||
GNUPGHOME="$gpg_temp_home" gpg --armor --export "$gpg_key_id" > "${BACKUP_DIR}/heretek-gpg-public.asc"
|
||||
chmod 600 "$gpg_key_file" "${BACKUP_DIR}/heretek-gpg-public.asc"
|
||||
rm -rf "$gpg_temp_home"
|
||||
|
||||
# Also place public key into repo for distribution
|
||||
cp "${BACKUP_DIR}/heretek-gpg-public.asc" "${GITHUB_REPO_DIR}/HERETEK_RELEASE_SIGNING_KEY.asc"
|
||||
log_success "Published public key to ${GITHUB_REPO_DIR}/HERETEK_RELEASE_SIGNING_KEY.asc"
|
||||
|
||||
# 3. Android Keystore Generation
|
||||
log_info "Generating 4096-bit RSA PKCS12 Android release keystore..."
|
||||
keytool -genkeypair -v \
|
||||
-keystore "$jks_file" \
|
||||
-storetype PKCS12 \
|
||||
-alias "$KEYSTORE_ALIAS" \
|
||||
-keyalg RSA \
|
||||
-keysize 4096 \
|
||||
-validity "$KEYSTORE_VALIDITY" \
|
||||
-storepass "$keystore_pass" \
|
||||
-keypass "$key_pass" \
|
||||
-dname "$KEYSTORE_DNAME" >/dev/null 2>&1
|
||||
|
||||
chmod 600 "$jks_file"
|
||||
local keystore_base64
|
||||
keystore_base64="$(base64 -w 0 "$jks_file")"
|
||||
echo "$keystore_base64" > "${BACKUP_DIR}/android-keystore.base64"
|
||||
chmod 600 "${BACKUP_DIR}/android-keystore.base64"
|
||||
log_success "Generated Android Keystore: $jks_file (Alias: $KEYSTORE_ALIAS)"
|
||||
|
||||
# 4. Write secrets dotenv file for backup and gh secret import
|
||||
cat <<EOF > "$env_file"
|
||||
GPG_KEY_ID=${gpg_key_id}
|
||||
GPG_FINGERPRINT=${gpg_fingerprint}
|
||||
GPG_PASSPHRASE=${gpg_passphrase}
|
||||
ANDROID_KEYSTORE_PASSWORD=${keystore_pass}
|
||||
ANDROID_KEY_ALIAS=${KEYSTORE_ALIAS}
|
||||
ANDROID_KEY_PASSWORD=${key_pass}
|
||||
EOF
|
||||
chmod 600 "$env_file"
|
||||
|
||||
log_success "All credentials generated and safely stored in $BACKUP_DIR"
|
||||
}
|
||||
|
||||
check_gh_org_permissions() {
|
||||
log_info "Verifying GitHub CLI authentication and organization access..."
|
||||
if ! gh auth status >/dev/null 2>&1; then
|
||||
log_error "GitHub CLI is not authenticated. Please run 'gh auth login'."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Test access to org secrets API on project-dorado
|
||||
local test_res
|
||||
if ! test_res="$(gh secret list --org project-dorado 2>&1)"; then
|
||||
if echo "$test_res" | grep -q "403"; then
|
||||
log_warn "GitHub token does not currently have 'admin:org' permission to manage org secrets."
|
||||
echo ""
|
||||
echo -e "${YELLOW}Please run the following command in your terminal to grant org secret permissions:${NC}"
|
||||
echo -e " ${BOLD}gh auth refresh -s admin:org${NC}"
|
||||
echo ""
|
||||
return 2
|
||||
fi
|
||||
log_error "Error checking organization secrets: $test_res"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "GitHub CLI has required 'admin:org' permissions."
|
||||
return 0
|
||||
}
|
||||
|
||||
deploy_org_secrets() {
|
||||
local gpg_key_file="${BACKUP_DIR}/heretek-gpg-private.asc"
|
||||
local jks_file="${BACKUP_DIR}/heretek-release.jks"
|
||||
local env_file="${BACKUP_DIR}/secrets.env"
|
||||
|
||||
if [ ! -f "$gpg_key_file" ] || [ ! -f "$jks_file" ] || [ ! -f "$env_file" ]; then
|
||||
log_error "Missing credentials in $BACKUP_DIR. Run '$0 generate' first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Source parameters
|
||||
# shellcheck disable=SC1090
|
||||
source "$env_file"
|
||||
local gpg_private_key
|
||||
gpg_private_key="$(cat "$gpg_key_file")"
|
||||
local keystore_base64
|
||||
keystore_base64="$(cat "${BACKUP_DIR}/android-keystore.base64")"
|
||||
|
||||
log_info "Deploying secrets across organizations: ${TARGET_ORGS[*]}"
|
||||
|
||||
for org in "${TARGET_ORGS[@]}"; do
|
||||
echo ""
|
||||
log_info "=== Setting secrets for organization: $org ==="
|
||||
|
||||
# GPG Secrets
|
||||
echo -n "$gpg_private_key" | gh secret set GPG_PRIVATE_KEY --org "$org" --visibility all
|
||||
gh secret set GPG_PASSPHRASE --org "$org" --visibility all --body "$GPG_PASSPHRASE"
|
||||
gh secret set GPG_KEY_ID --org "$org" --visibility all --body "$GPG_KEY_ID"
|
||||
|
||||
# Android Keystore Secrets
|
||||
gh secret set ANDROID_KEYSTORE_BASE64 --org "$org" --visibility all --body "$keystore_base64"
|
||||
gh secret set ANDROID_KEYSTORE_PASSWORD --org "$org" --visibility all --body "$ANDROID_KEYSTORE_PASSWORD"
|
||||
gh secret set ANDROID_KEY_ALIAS --org "$org" --visibility all --body "$ANDROID_KEY_ALIAS"
|
||||
gh secret set ANDROID_KEY_PASSWORD --org "$org" --visibility all --body "$ANDROID_KEY_PASSWORD"
|
||||
|
||||
log_success "Successfully configured 7 release secrets for organization: $org"
|
||||
gh secret list --org "$org"
|
||||
done
|
||||
}
|
||||
|
||||
show_status() {
|
||||
echo ""
|
||||
echo -e "${BOLD}Heretek & Dorado Release Signing Status${NC}"
|
||||
echo "--------------------------------------------------"
|
||||
if [ -d "$BACKUP_DIR" ] && [ -f "${BACKUP_DIR}/secrets.env" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "${BACKUP_DIR}/secrets.env"
|
||||
echo -e "Backup Directory: ${GREEN}${BACKUP_DIR}${NC} (chmod 0700)"
|
||||
echo -e "GPG Key ID: ${BOLD}${GPG_KEY_ID}${NC}"
|
||||
echo -e "GPG Fingerprint: ${BOLD}${GPG_FINGERPRINT}${NC}"
|
||||
echo -e "Android Key Alias: ${BOLD}${ANDROID_KEY_ALIAS}${NC}"
|
||||
echo -e "Public Key Artifact: ${GREEN}${GITHUB_REPO_DIR}/HERETEK_RELEASE_SIGNING_KEY.asc${NC}"
|
||||
else
|
||||
echo -e "Status: ${YELLOW}No credentials generated yet.${NC}"
|
||||
echo "Run '$0 generate' to create them."
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
print_manual_instructions() {
|
||||
local env_file="${BACKUP_DIR}/secrets.env"
|
||||
if [ ! -f "$env_file" ]; then
|
||||
return
|
||||
fi
|
||||
# shellcheck disable=SC1090
|
||||
source "$env_file"
|
||||
echo ""
|
||||
echo -e "${BOLD}Manual GitHub Settings Web UI Links:${NC}"
|
||||
for org in "${TARGET_ORGS[@]}"; do
|
||||
echo " - https://github.com/organizations/${org}/settings/secrets/actions"
|
||||
done
|
||||
echo ""
|
||||
echo -e "Required Secrets to enter if not using CLI:"
|
||||
echo " 1. GPG_PRIVATE_KEY -> File content of ${BACKUP_DIR}/heretek-gpg-private.asc"
|
||||
echo " 2. GPG_PASSPHRASE -> From ${BACKUP_DIR}/secrets.env"
|
||||
echo " 3. GPG_KEY_ID -> ${GPG_KEY_ID}"
|
||||
echo " 4. ANDROID_KEYSTORE_BASE64 -> File content of ${BACKUP_DIR}/android-keystore.base64"
|
||||
echo " 5. ANDROID_KEYSTORE_PASSWORD -> From ${BACKUP_DIR}/secrets.env"
|
||||
echo " 6. ANDROID_KEY_ALIAS -> ${ANDROID_KEY_ALIAS}"
|
||||
echo " 7. ANDROID_KEY_PASSWORD -> From ${BACKUP_DIR}/secrets.env"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main Command Dispatch
|
||||
cmd="${1:-all}"
|
||||
|
||||
case "$cmd" in
|
||||
generate)
|
||||
check_prerequisites
|
||||
generate_credentials
|
||||
show_status
|
||||
;;
|
||||
deploy)
|
||||
check_prerequisites
|
||||
if check_gh_org_permissions; then
|
||||
deploy_org_secrets
|
||||
else
|
||||
print_manual_instructions
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
all)
|
||||
check_prerequisites
|
||||
generate_credentials
|
||||
show_status
|
||||
if check_gh_org_permissions; then
|
||||
deploy_org_secrets
|
||||
else
|
||||
print_manual_instructions
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [generate|deploy|status|all]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user