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
7 changes: 7 additions & 0 deletions .auths/allowed_signers
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# auths:managed — do not edit manually
# Current identity (E6IXlw5-lnX88r3WZCt3u1qyN_Xlq7nQjtoTmuOfMIjI)
z6MktnihicwetvA16FtHFynaJTn9eDZw51eizUEA1yGJCR4o@auths.local namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINT/yz5N7+GkzsRTHiyaueZbDy+fovwYUXyJ9uwD67tk
# Previous identities
z6MkipUqayiDZWM8j4YktjiEFZcCGw51YDVvLM7SrYPqLLyZ@auths.local namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEDeaOmUEcUjzChUedAsPyDO4mnjIa8j92fD9rGpuZd0
z6MkhfnUUc2UJJ5C9sQQ7GvXmSbQJsdtNKV6HNYcQtTjc7xE@auths.local namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC/Ib83sxXogDnEVzLjFBkyC+DhP+cssbPzZAmQhB+Lz
z6Mkio7WpoPy5EfeMJwhiZzePFch7xxuDeF9tpAf9q15nnHf@auths.local namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIECEt+3NgK9ws6M65lPSqW1FgWFjCYQVj1fsDedIvkRi
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,15 @@ jobs:
echo "::error::dist/ is out of date. Run 'just build' (or 'npm run build') and commit the result."
exit 1
}

verify-commits:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Verify commit signatures
uses: ./
with:
fail-on-unsigned: true
28 changes: 18 additions & 10 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39487,7 +39487,7 @@ const fs = __importStar(__nccwpck_require__(9896));
const path = __importStar(__nccwpck_require__(6928));
const os = __importStar(__nccwpck_require__(857));
// Repository that hosts the public auths CLI releases
const CLI_RELEASE_REPO = 'auths-dev/auths-releases';
const CLI_RELEASE_REPO = 'auths-dev/auths';
/**
* Classify a verification error string into a structured failure type.
*/
Expand Down Expand Up @@ -39529,11 +39529,11 @@ async function runPreflightChecks() {
}
// git command failed, not necessarily a problem
}
// Check for ssh-keygen (required by auths verify-commit)
// Check for ssh-keygen (required by auths verify)
try {
const sshKeygenPath = await io.which('ssh-keygen', false);
if (!sshKeygenPath) {
core.warning('ssh-keygen not found in PATH. The auths verify-commit command requires OpenSSH 8.0+.\n' +
core.warning('ssh-keygen not found in PATH. The auths verify command requires OpenSSH 8.0+.\n' +
'GitHub-hosted runners include it by default. Self-hosted runners may need to install openssh-client.');
}
}
Expand All @@ -39542,7 +39542,7 @@ async function runPreflightChecks() {
}
}
/**
* Verify commits in the given range using auths verify-commit
* Verify commits in the given range using auths verify
*/
async function verifyCommits(commitRange, options) {
const { allowedSignersPath, identityBundlePath, skipMergeCommits } = options;
Expand All @@ -39551,7 +39551,15 @@ async function verifyCommits(commitRange, options) {
// Validate inputs
if (!useIdentityBundle && !fs.existsSync(allowedSignersPath)) {
core.warning(`Allowed signers file not found: ${allowedSignersPath}`);
core.warning('Create one with: echo "user@example.com ssh-ed25519 AAAA..." > .auths/allowed_signers');
core.warning('To set up commit verification:\n' +
' 1. auths init # create identity\n' +
' 2. auths git allowed-signers -o .auths/allowed_signers # generate file\n' +
' 3. git add .auths/allowed_signers && git commit # commit it\n' +
'\n' +
'Or use an identity bundle for stateless CI (no file needed):\n' +
' auths id export-bundle --alias <ALIAS> --output bundle.json\n' +
'\n' +
'Docs: https://docs.auths.dev/cli/commands/advanced/#auths-git-allowed-signers');
const commits = await getCommitsInRange(commitRange, skipMergeCommits);
return commits.map(commit => ({
commit,
Expand Down Expand Up @@ -39583,15 +39591,15 @@ async function verifyCommits(commitRange, options) {
return mergeResults;
}
// Build CLI arguments
const cliArgs = ['verify-commit'];
const cliArgs = ['verify'];
if (useIdentityBundle) {
cliArgs.push('--identity-bundle', identityBundlePath);
}
else {
cliArgs.push('--allowed-signers', allowedSignersPath);
}
cliArgs.push('--json', commitRange);
// Run auths verify-commit with --json flag
// Run auths verify with --json flag
let stdout = '';
let stderr = '';
try {
Expand All @@ -39608,8 +39616,8 @@ async function verifyCommits(commitRange, options) {
});
}
catch (error) {
core.debug(`auths verify-commit stderr: ${stderr}`);
core.debug(`auths verify-commit stdout: ${stdout}`);
core.debug(`auths verify stderr: ${stderr}`);
core.debug(`auths verify stdout: ${stdout}`);
}
// Parse JSON output
let verifyResults = [];
Expand Down Expand Up @@ -39668,7 +39676,7 @@ async function verifyCommitsOneByOne(authsPath, commits, options) {
for (const commit of commits) {
let stdout = '';
let exitCode = 0;
const cliArgs = ['verify-commit'];
const cliArgs = ['verify'];
if (useIdentityBundle) {
cliArgs.push('--identity-bundle', identityBundlePath);
}
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/verifier.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface VerifyOptions {
*/
export declare function runPreflightChecks(): Promise<void>;
/**
* Verify commits in the given range using auths verify-commit
* Verify commits in the given range using auths verify
*/
export declare function verifyCommits(commitRange: string, options: VerifyOptions): Promise<VerificationResult[]>;
/**
Expand Down
172 changes: 172 additions & 0 deletions docs/release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#!/usr/bin/env python3
"""
Tag and push a GitHub release from the version in package.json.

Usage:
python docs/release.py # dry-run (shows what would happen)
python docs/release.py --push # create tag and push to trigger release workflow

What it does:
1. Reads the version from package.json
2. Checks that the npm package version has been bumped (if published)
3. Checks that the git tag doesn't already exist on GitHub
4. Ensures dist/ is built and committed
5. Creates a git tag v{version} and pushes it to origin
6. The release workflow creates a GitHub Release and updates the floating v1 tag

Requires:
- python3 (no external dependencies)
- git on PATH
- network access to npmjs.org
"""

import json
import subprocess
import sys
import urllib.request
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[1]
PACKAGE_JSON = REPO_ROOT / "package.json"
NPM_PACKAGE = "@auths/verify-action"
GITHUB_REPO = "auths-dev/auths-verify-github-action"


def get_package_version() -> str:
data = json.loads(PACKAGE_JSON.read_text())
version = data.get("version")
if not version:
print("ERROR: No version field in package.json", file=sys.stderr)
sys.exit(1)
return version


def get_npm_version() -> str | None:
url = f"https://registry.npmjs.org/{NPM_PACKAGE}/latest"
req = urllib.request.Request(url, headers={"User-Agent": "auths-release-script/1.0"})
try:
with urllib.request.urlopen(req, timeout=10) as resp:
data = json.loads(resp.read())
return data.get("version")
except Exception:
return None


def git(*args: str) -> str:
result = subprocess.run(
["git", *args],
capture_output=True,
text=True,
cwd=REPO_ROOT,
)
if result.returncode != 0:
print(f"ERROR: git {' '.join(args)} failed:\n{result.stderr.strip()}", file=sys.stderr)
sys.exit(1)
return result.stdout.strip()


def local_tag_exists(tag: str) -> bool:
result = subprocess.run(
["git", "tag", "-l", tag],
capture_output=True,
text=True,
cwd=REPO_ROOT,
)
return bool(result.stdout.strip())


def remote_tag_exists(tag: str) -> bool:
result = subprocess.run(
["git", "ls-remote", "--tags", "origin", f"refs/tags/{tag}"],
capture_output=True,
text=True,
cwd=REPO_ROOT,
)
return bool(result.stdout.strip())


def delete_local_tag(tag: str) -> None:
subprocess.run(
["git", "tag", "-d", tag],
capture_output=True,
cwd=REPO_ROOT,
)


def main() -> None:
push = "--push" in sys.argv

version = get_package_version()
tag = f"v{version}"
print(f"package.json version: {version}")
print(f"Git tag: {tag}")

# Check npm for version collision (this action isn't published to npm,
# but check anyway in case it ever is)
published = get_npm_version()
if published:
print(f"npm version: {published}")
if published == version:
print(f"\nWARNING: Version {version} matches npm. Consider bumping.", file=sys.stderr)
else:
print("npm version: (not published)")

# GitHub is the source of truth for tags.
if remote_tag_exists(tag):
print(f"\nERROR: Git tag {tag} already exists on origin.", file=sys.stderr)
print("Bump the version in package.json or delete the remote tag/release first.", file=sys.stderr)
sys.exit(1)

if local_tag_exists(tag):
print(f"Local tag {tag} exists but not on origin — deleting stale local tag.")
delete_local_tag(tag)

# Check we're on a clean working tree
status = git("status", "--porcelain")
if status:
print(f"\nERROR: Working tree is not clean:\n{status}", file=sys.stderr)
print("Commit or stash changes before releasing.", file=sys.stderr)
sys.exit(1)

# Check dist/ is committed (the release workflow validates this too)
diff = subprocess.run(
["git", "diff", "--name-only", "--", "dist/"],
capture_output=True,
text=True,
cwd=REPO_ROOT,
)
if diff.stdout.strip():
print(f"\nERROR: dist/ has uncommitted changes:\n{diff.stdout.strip()}", file=sys.stderr)
print("Run `npm run build` and commit dist/ before releasing.", file=sys.stderr)
sys.exit(1)

if not push:
print(f"\nDry run: would create and push tag {tag}")
print("Run with --push to execute.")
return

print(f"\nCreating tag {tag}...", flush=True)
result = subprocess.run(
["git", "tag", "-a", tag, "-m", f"release: {version}"],
cwd=REPO_ROOT,
)
if result.returncode != 0:
print(f"\nERROR: git tag failed (exit {result.returncode})", file=sys.stderr)
sys.exit(1)

print(f"Pushing tag {tag} to origin...", flush=True)
result = subprocess.run(
["git", "push", "origin", tag],
cwd=REPO_ROOT,
)
if result.returncode != 0:
print(f"\nERROR: git push failed (exit {result.returncode})", file=sys.stderr)
sys.exit(1)

print(f"\nDone. Release workflow will run at:")
print(f" https://github.com/{GITHUB_REPO}/actions")
print(f"\nThe workflow will also update the floating v1 tag.")


if __name__ == "__main__":
main()
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.0.0",
"version": "1.1.0",
"description": "GitHub Action to verify commit signatures using Auths identity keys",
"main": "dist/index.js",
"scripts": {
Expand Down
12 changes: 6 additions & 6 deletions src/__tests__/verifier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('getAuthsDownloadUrl', () => {

const url = getAuthsDownloadUrl('');
expect(url).toBe(
'https://github.com/auths-dev/auths-releases/releases/latest/download/auths-linux-x86_64.tar.gz'
'https://github.com/auths-dev/auths/releases/latest/download/auths-linux-x86_64.tar.gz'
);
});

Expand All @@ -70,7 +70,7 @@ describe('getAuthsDownloadUrl', () => {

const url = getAuthsDownloadUrl('');
expect(url).toBe(
'https://github.com/auths-dev/auths-releases/releases/latest/download/auths-macos-aarch64.tar.gz'
'https://github.com/auths-dev/auths/releases/latest/download/auths-macos-aarch64.tar.gz'
);
});

Expand All @@ -80,7 +80,7 @@ describe('getAuthsDownloadUrl', () => {

const url = getAuthsDownloadUrl('');
expect(url).toBe(
'https://github.com/auths-dev/auths-releases/releases/latest/download/auths-windows-x86_64.zip'
'https://github.com/auths-dev/auths/releases/latest/download/auths-windows-x86_64.zip'
);
});

Expand All @@ -90,7 +90,7 @@ describe('getAuthsDownloadUrl', () => {

const url = getAuthsDownloadUrl('0.5.0');
expect(url).toBe(
'https://github.com/auths-dev/auths-releases/releases/download/v0.5.0/auths-linux-x86_64.tar.gz'
'https://github.com/auths-dev/auths/releases/download/v0.5.0/auths-linux-x86_64.tar.gz'
);
});

Expand All @@ -116,7 +116,7 @@ describe('getAuthsDownloadUrl', () => {

const url = getAuthsDownloadUrl('');
expect(url).toBe(
'https://github.com/auths-dev/auths-releases/releases/latest/download/auths-macos-x86_64.tar.gz'
'https://github.com/auths-dev/auths/releases/latest/download/auths-macos-x86_64.tar.gz'
);
});

Expand All @@ -126,7 +126,7 @@ describe('getAuthsDownloadUrl', () => {

const url = getAuthsDownloadUrl('');
expect(url).toBe(
'https://github.com/auths-dev/auths-releases/releases/latest/download/auths-linux-aarch64.tar.gz'
'https://github.com/auths-dev/auths/releases/latest/download/auths-linux-aarch64.tar.gz'
);
});
});
Expand Down
Loading
Loading