Skip to content

Commit 627184f

Browse files
committed
ci: add a one-shot signing setup script
Generating the release key and loading the five prod secrets was a manual sequence of keytool, base64 and gh commands that is easy to get subtly wrong — a mistyped alias or a fingerprint copied with colons fails only later, inside a release build. scripts/setup-signing.sh does the whole thing on the maintainer's machine: creates the keystore outside the repository, derives and validates the certificate SHA-256, and uploads all five secrets to the prod environment. Passwords are read with echo disabled and never printed, logged, or passed as command arguments; the base64 of the keystore goes to a umask-077 temp file that is removed on exit. Re-running with an existing keystore reuses it and only refreshes the secrets.
1 parent 32ab72e commit 627184f

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

scripts/setup-signing.sh

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env bash
2+
# One-shot signing setup for iLINK releases.
3+
#
4+
# Generates the release keystore, derives its certificate fingerprint, and
5+
# uploads the five secrets the release workflow needs to the `prod` GitHub
6+
# environment. Run it yourself: passwords are read from your terminal with
7+
# echo disabled and are never printed, logged, or passed as arguments.
8+
#
9+
# bash scripts/setup-signing.sh
10+
#
11+
# Re-running with an existing keystore is safe — it will reuse that file and
12+
# only refresh the secrets.
13+
set -euo pipefail
14+
15+
REPO="${REPO:-i99dev/ilink}"
16+
ALIAS="${ALIAS:-dash}"
17+
# Deliberately outside the repository so the key can never be committed.
18+
KEYSTORE="${KEYSTORE:-$HOME/ilink-release.jks}"
19+
20+
command -v keytool >/dev/null || { echo "keytool not found — install a JDK or add Android Studio's jbr/bin to PATH" >&2; exit 1; }
21+
command -v gh >/dev/null || { echo "gh not found — install the GitHub CLI" >&2; exit 1; }
22+
gh auth status >/dev/null 2>&1 || { echo "gh is not authenticated — run: gh auth login" >&2; exit 1; }
23+
24+
echo "Repository : $REPO"
25+
echo "Keystore : $KEYSTORE"
26+
echo "Alias : $ALIAS"
27+
echo
28+
29+
if [ -f "$KEYSTORE" ]; then
30+
echo "Keystore already exists — reusing it (no new key is generated)."
31+
read -r -s -p "Store password: " STORE_PW; echo
32+
read -r -s -p "Key password : " KEY_PW; echo
33+
else
34+
echo "Creating a new keystore. Choose a strong password and store it in a"
35+
echo "password manager — losing it means no future build can ever update an"
36+
echo "installed iLINK."
37+
echo
38+
read -r -s -p "Store password : " STORE_PW; echo
39+
read -r -s -p "Confirm store password: " STORE_PW2; echo
40+
[ "$STORE_PW" = "$STORE_PW2" ] || { echo "Passwords do not match." >&2; exit 1; }
41+
[ ${#STORE_PW} -ge 12 ] || { echo "Use at least 12 characters." >&2; exit 1; }
42+
# One password for both is normal for a release keystore and keeps Gradle simple.
43+
KEY_PW="$STORE_PW"
44+
45+
keytool -genkeypair -v \
46+
-keystore "$KEYSTORE" \
47+
-alias "$ALIAS" \
48+
-keyalg RSA -keysize 4096 -validity 10000 \
49+
-storepass:env STORE_PW -keypass:env KEY_PW \
50+
-dname "CN=iLINK, OU=iLINK, O=iLINK, L=, ST=, C=" \
51+
>/dev/null
52+
chmod 600 "$KEYSTORE" 2>/dev/null || true
53+
echo "Created $KEYSTORE"
54+
fi
55+
export STORE_PW KEY_PW
56+
57+
SIGNER=$(keytool -list -v -keystore "$KEYSTORE" -alias "$ALIAS" \
58+
-storepass:env STORE_PW 2>/dev/null \
59+
| awk '/SHA256:/ {gsub(":", ""); print tolower($2); exit}')
60+
printf '%s' "$SIGNER" | grep -Eq '^[0-9a-f]{64}$' || {
61+
echo "Could not read the certificate — wrong password or alias?" >&2; exit 1; }
62+
echo "Certificate SHA-256: $SIGNER"
63+
64+
# base64 of the keystore, written with a restrictive umask and removed after upload.
65+
B64=$(mktemp)
66+
trap 'rm -f "$B64"' EXIT
67+
( umask 077; base64 -w0 "$KEYSTORE" > "$B64" 2>/dev/null || base64 -i "$KEYSTORE" | tr -d '\n' > "$B64" )
68+
69+
echo
70+
echo "Uploading secrets to the '$REPO' prod environment..."
71+
gh secret set DASH_RELEASE_KEYSTORE_B64 --env prod --repo "$REPO" < "$B64"
72+
printf '%s' "$STORE_PW" | gh secret set DASH_KEYSTORE_PASSWORD --env prod --repo "$REPO"
73+
printf '%s' "$KEY_PW" | gh secret set DASH_KEY_PASSWORD --env prod --repo "$REPO"
74+
printf '%s' "$ALIAS" | gh secret set DASH_KEY_ALIAS --env prod --repo "$REPO"
75+
printf '%s' "$SIGNER" | gh secret set DASH_EXPECTED_SIGNER_SHA --env prod --repo "$REPO"
76+
77+
echo
78+
echo "Done. Secrets now set (names only):"
79+
gh secret list --env prod --repo "$REPO"
80+
echo
81+
echo "Back up $KEYSTORE somewhere encrypted and off this machine before releasing."

0 commit comments

Comments
 (0)