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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ jobs:
# --- Artifact signing (mirrors auths/auths release workflow) ---
- name: Install auths CLI
run: |
curl -sL https://github.com/auths-dev/auths/releases/latest/download/auths-linux-x86_64.tar.gz | tar xz -C /usr/local/bin
mkdir -p /tmp/auths-install
curl -sL https://github.com/auths-dev/auths/releases/latest/download/auths-linux-x86_64.tar.gz | tar xz -C /tmp/auths-install
sudo cp /tmp/auths-install/auths /usr/local/bin/auths
rm -rf /tmp/auths-install

- name: Sign dist/index.js
env:
Expand Down
19 changes: 15 additions & 4 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,26 @@ check-dist:
# Run the full CI suite locally: test + build + verify dist
ci: test build check-dist

# Set up CI secrets for release artifact signing (one-time)
ci-setup:
bash scripts/ci-setup.sh

# Sign the dist/index.js artifact locally (creates dist/index.js.auths.json)
sign-dist:
auths artifact sign dist/index.js

# Cut a release: bump version, commit, then tag+push via release script
# Cut a release: bump version (if needed), commit, then tag+push via release script
# The release workflow handles build verification, artifact signing, and GitHub release creation.
# Usage: just release 1.0.3
release VERSION: test build
npm version {{VERSION}} --no-git-tag-version
git add package.json package-lock.json dist/ src/ .github/ justfile
git commit -m "build: bump version to {{VERSION}}"
#!/usr/bin/env bash
set -euo pipefail
CURRENT=$(node -p "require('./package.json').version")
if [ "$CURRENT" != "{{VERSION}}" ]; then
npm version {{VERSION}} --no-git-tag-version
git add package.json package-lock.json dist/ src/ .github/ justfile
git commit -m "build: bump version to {{VERSION}}"
else
echo "Version already {{VERSION}}, skipping bump"
fi
python scripts/release.py --push
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@auths/verify-action",
"version": "1.1.0",
"version": "1.2.0",
"description": "GitHub Action to verify commit signatures using Auths identity keys",
"main": "dist/index.js",
"scripts": {
Expand Down
211 changes: 211 additions & 0 deletions scripts/ci-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#!/usr/bin/env bash
#
# CI Release Signing Setup (One-Time)
#
# Creates a limited-capability device key for GitHub Actions to sign
# release artifacts. Your root identity stays on your machine.
#
# Prerequisites:
# - auths CLI installed and initialized (auths init)
# - gh CLI installed and authenticated (gh auth login)
#
# Usage:
# bash scripts/ci-setup.sh # interactive setup
# just ci-setup # same thing via justfile
#
set -euo pipefail

CYAN='\033[0;36m'
GREEN='\033[0;32m'
BOLD='\033[1m'
DIM='\033[2m'
YELLOW='\033[1;33m'
RESET='\033[0m'

echo ""
echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${RESET}"
echo -e "${CYAN}║${RESET}${BOLD} CI Release Signing Setup (One-Time) ${RESET}${CYAN}║${RESET}"
echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${RESET}"
echo ""
echo "This creates a limited-capability device for GitHub Actions to sign"
echo "release artifacts. Your root identity stays on your machine."
echo ""

# --- Step 1: Verify identity exists ---
if ! auths status > /dev/null 2>&1; then
echo "ERROR: No auths identity found. Run 'auths init' first." >&2
exit 1
fi

# --- Step 2: Read identity info ---
ID_OUTPUT=$(auths id show)
IDENTITY_DID=$(echo "$ID_OUTPUT" | grep "Controller DID:" | awk '{print $3}')
if [ -z "$IDENTITY_DID" ]; then
echo "ERROR: Could not parse Controller DID from 'auths id show'" >&2
exit 1
fi

KEY_OUTPUT=$(auths key list)
IDENTITY_KEY_ALIAS=$(echo "$KEY_OUTPUT" | grep '^-' | head -1 | awk '{print $2}')
if [ -z "$IDENTITY_KEY_ALIAS" ]; then
echo "ERROR: Could not parse key alias from 'auths key list'" >&2
exit 1
fi

echo -e "${BOLD}Identity:${RESET} ${CYAN}${IDENTITY_DID}${RESET}"
echo -e "${BOLD}Key alias:${RESET} ${CYAN}${IDENTITY_KEY_ALIAS}${RESET}"
echo ""

# --- Step 3: Check for existing CI device key ---
REUSE=0
if echo "$KEY_OUTPUT" | grep -q "ci-release-device"; then
echo -e "${DIM}Found existing ci-release-device key — will reuse it.${RESET}"
REUSE=1
fi

# --- Step 4: Prompt for passphrase ---
echo -e "${BOLD}Choose a passphrase for the CI device key.${RESET}"
echo -e "${DIM}This will be stored as AUTHS_CI_PASSPHRASE in GitHub Secrets.${RESET}"
echo ""

read -rsp "CI device passphrase: " CI_PASS
echo ""
read -rsp "Confirm passphrase: " CI_PASS_CONFIRM
echo ""

if [ "$CI_PASS" != "$CI_PASS_CONFIRM" ]; then
echo "ERROR: Passphrases do not match" >&2
exit 1
fi

# --- Step 5: Generate seed + import key (or reuse existing) ---
TMPDIR_WORK=$(mktemp -d)
trap 'rm -rf "$TMPDIR_WORK"' EXIT

if [ "$REUSE" -eq 0 ]; then
echo ""
echo -e "${DIM}Generating CI device key...${RESET}"

# Generate a fresh 32-byte Ed25519 seed
SEED_PATH="$TMPDIR_WORK/ci-device-seed.bin"
dd if=/dev/urandom of="$SEED_PATH" bs=32 count=1 2>/dev/null
chmod 600 "$SEED_PATH"

# Import the seed into platform keychain
echo -e "${DIM}Importing key into platform keychain:${RESET}"
auths key import \
--alias ci-release-device \
--seed-file "$SEED_PATH" \
--controller-did "$IDENTITY_DID"

echo -e "${GREEN}✓${RESET} CI device key imported into platform keychain"
fi

# Create CI file keychain
KEYCHAIN_PATH="$TMPDIR_WORK/ci-keychain.enc"
if [ "$REUSE" -eq 0 ]; then
echo -e "${DIM}Creating CI file keychain...${RESET}"
else
echo -e "${DIM}Reusing existing ci-release-device key — regenerating CI file keychain...${RESET}"
fi

AUTHS_PASSPHRASE="$CI_PASS" auths key copy-backend \
--alias ci-release-device \
--dst-backend file \
--dst-file "$KEYCHAIN_PATH"

KEYCHAIN_B64=$(base64 < "$KEYCHAIN_PATH" | tr -d '\n')
echo -e "${GREEN}✓${RESET} CI file keychain created"

# --- Step 6: Derive device DID ---
DEVICE_PUB=$(auths key export \
--alias ci-release-device \
--passphrase "$CI_PASS" \
--format pub)

DEVICE_DID=$(auths debug util pubkey-to-did "$DEVICE_PUB")
echo -e "${GREEN}✓${RESET} Device DID: ${CYAN}${DEVICE_DID}${RESET}"

# --- Step 7: Link device (if not already linked) ---
DEVICES_OUTPUT=$(auths device list 2>/dev/null || echo "")
if echo "$DEVICES_OUTPUT" | grep -q "$DEVICE_DID"; then
echo -e "${GREEN}✓${RESET} CI device already linked — skipping"
else
echo ""
echo -e "${DIM}Linking CI device to identity...${RESET}"
AUTHS_PASSPHRASE="$CI_PASS" auths device link \
--key "$IDENTITY_KEY_ALIAS" \
--device-key ci-release-device \
--device-did "$DEVICE_DID" \
--note "GitHub Actions release signer" \
--capabilities sign_release
echo -e "${GREEN}✓${RESET} CI device linked"
fi

# --- Step 8: Package identity repo ---
AUTHS_DIR="${HOME}/.auths"
echo -e "${DIM}Packaging identity repo...${RESET}"

if ! git -C "$AUTHS_DIR" rev-parse --git-dir > /dev/null 2>&1; then
echo "ERROR: ~/.auths does not appear to be a git repository. Run 'auths init' first." >&2
exit 1
fi
echo -e " ${GREEN}✓${RESET} ~/.auths is a valid git repo"

# Build tar.gz of ~/.auths, excluding *.sock files
BUNDLE_PATH="$TMPDIR_WORK/identity-bundle.tar.gz"
tar -czf "$BUNDLE_PATH" \
--exclude='*.sock' \
-C "$(dirname "$AUTHS_DIR")" \
"$(basename "$AUTHS_DIR")"

IDENTITY_BUNDLE_B64=$(base64 < "$BUNDLE_PATH" | tr -d '\n')

# --- Step 9: Set GitHub secrets ---
echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo -e "${BOLD} Setting GitHub Secrets:${RESET}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo ""

REPO=$(git remote get-url origin | sed 's/.*github\.com[:/]//' | sed 's/\.git$//')
if [ -z "$REPO" ] || ! echo "$REPO" | grep -q '/'; then
echo "ERROR: Could not extract owner/repo from git remote URL" >&2
exit 1
fi

GH_OK=1

if ! gh auth status > /dev/null 2>&1; then
GH_OK=0
fi

if [ "$GH_OK" -eq 1 ]; then
echo -e "${DIM}Setting secrets via gh CLI...${RESET}"

echo -n "$CI_PASS" | gh secret set AUTHS_CI_PASSPHRASE --repo "$REPO" || GH_OK=0
echo -n "$KEYCHAIN_B64" | gh secret set AUTHS_CI_KEYCHAIN --repo "$REPO" || GH_OK=0
echo -n "$IDENTITY_BUNDLE_B64" | gh secret set AUTHS_CI_IDENTITY_BUNDLE --repo "$REPO" || GH_OK=0
fi

if [ "$GH_OK" -eq 1 ]; then
echo -e "${GREEN}✓${RESET} All 3 secrets set on ${CYAN}${REPO}${RESET}"
else
echo -e "${YELLOW}Could not set secrets automatically.${RESET}"
echo -e "${DIM}Try: gh auth login then re-run, or add manually:${RESET}"
echo -e "${DIM} Repository → Settings → Secrets → Actions → New secret${RESET}"
echo ""
echo -e "${BOLD}AUTHS_CI_PASSPHRASE${RESET}"
echo "$CI_PASS"
echo ""
echo -e "${BOLD}AUTHS_CI_KEYCHAIN${RESET}"
echo "$KEYCHAIN_B64"
echo ""
echo -e "${BOLD}AUTHS_CI_IDENTITY_BUNDLE${RESET}"
echo "$IDENTITY_BUNDLE_B64"
fi

echo ""
echo -e "${BOLD}To revoke CI access at any time:${RESET}"
echo -e " ${CYAN}auths device revoke --device-did ${DEVICE_DID} --key ${IDENTITY_KEY_ALIAS}${RESET}"
echo ""
Loading