@@ -67353,6 +67353,12 @@ const token_1 = __nccwpck_require__(94606);
6735367353async function run() {
6735467354 let credentials = null;
6735567355 try {
67356+ // Validate: at least one of files or commits must be provided
67357+ const filePatterns = core.getMultilineInput('files').filter(p => p.trim());
67358+ const commitsRange = core.getInput('commits').trim();
67359+ if (filePatterns.length === 0 && !commitsRange) {
67360+ throw new Error('At least one of `files` or `commits` must be provided');
67361+ }
6735667362 // 1. Resolve credentials
6735767363 credentials = await (0, token_1.resolveCredentials)();
6735867364 core.info('Credentials resolved');
@@ -67362,91 +67368,173 @@ async function run() {
6736267368 if (!authsPath) {
6736367369 throw new Error('Failed to find or install auths CLI');
6736467370 }
67365- // 3. Glob match files
67366- const filePatterns = core.getMultilineInput('files', { required: true });
67367- const patterns = filePatterns.join('\n');
67368- const globber = await glob.create(patterns, { followSymbolicLinks: false });
67369- let files = await globber.glob();
67370- // Workspace containment check
67371- const workspace = path.resolve(process.env.GITHUB_WORKSPACE || process.cwd());
67372- files = files.filter(f => {
67373- const resolved = path.resolve(f);
67374- if (!resolved.startsWith(workspace + path.sep) && resolved !== workspace) {
67375- core.warning(`Skipping path outside workspace: ${f}`);
67376- return false;
67377- }
67378- return true;
67379- });
67380- files = [...new Set(files)];
67381- if (files.length === 0) {
67382- throw new Error('No files matched the provided glob patterns');
67383- }
67384- core.info(`Found ${files.length} file(s) to sign`);
67385- // 4. Sign each file
6738667371 const deviceKey = core.getInput('device-key') || 'ci-release-device';
6738767372 const note = core.getInput('note') || '';
67373+ const shouldVerify = core.getInput('verify') === 'true';
67374+ let allVerified = true;
6738867375 const signedFiles = [];
6738967376 const attestationFiles = [];
67390- for (const file of files) {
67391- core.info(`Signing: ${path.basename(file)}`);
67392- const cliArgs = [
67393- 'artifact', 'sign', file,
67394- '--device-key', deviceKey,
67395- '--repo', credentials.identityRepoPath,
67396- ];
67397- if (note)
67398- cliArgs.push('--note', note);
67399- const exitCode = await exec.exec(authsPath, cliArgs, {
67400- env: {
67401- ...process.env,
67402- AUTHS_PASSPHRASE: credentials.passphrase,
67403- AUTHS_KEYCHAIN_BACKEND: 'file',
67404- AUTHS_KEYCHAIN_FILE: credentials.keychainPath,
67405- },
67406- ignoreReturnCode: true,
67377+ const signedCommits = [];
67378+ const authsEnv = {
67379+ ...process.env,
67380+ AUTHS_PASSPHRASE: credentials.passphrase,
67381+ AUTHS_KEYCHAIN_BACKEND: 'file',
67382+ AUTHS_KEYCHAIN_FILE: credentials.keychainPath,
67383+ };
67384+ // 3. Sign artifacts (if files provided)
67385+ if (filePatterns.length > 0) {
67386+ const patterns = filePatterns.join('\n');
67387+ const globber = await glob.create(patterns, { followSymbolicLinks: false });
67388+ let files = await globber.glob();
67389+ // Workspace containment check
67390+ const workspace = path.resolve(process.env.GITHUB_WORKSPACE || process.cwd());
67391+ files = files.filter(f => {
67392+ const resolved = path.resolve(f);
67393+ if (!resolved.startsWith(workspace + path.sep) && resolved !== workspace) {
67394+ core.warning(`Skipping path outside workspace: ${f}`);
67395+ return false;
67396+ }
67397+ return true;
6740767398 });
67408- if (exitCode !== 0) {
67409- throw new Error(`Failed to sign ${path.basename(file)} (exit code ${exitCode})`);
67399+ files = [...new Set(files)];
67400+ if (files.length === 0) {
67401+ core.warning('No files matched the provided glob patterns');
6741067402 }
67411- const attestationPath = `${file}.auths.json`;
67412- if (!fs.existsSync(attestationPath)) {
67413- throw new Error(`Signing succeeded but attestation file not found: ${attestationPath}`);
67403+ else {
67404+ core.info(`Found ${files.length} file(s) to sign`);
67405+ for (const file of files) {
67406+ core.info(`Signing: ${path.basename(file)}`);
67407+ const cliArgs = [
67408+ 'artifact', 'sign', file,
67409+ '--device-key', deviceKey,
67410+ '--repo', credentials.identityRepoPath,
67411+ ];
67412+ if (note)
67413+ cliArgs.push('--note', note);
67414+ const exitCode = await exec.exec(authsPath, cliArgs, {
67415+ env: authsEnv,
67416+ ignoreReturnCode: true,
67417+ });
67418+ if (exitCode !== 0) {
67419+ throw new Error(`Failed to sign ${path.basename(file)} (exit code ${exitCode})`);
67420+ }
67421+ const attestationPath = `${file}.auths.json`;
67422+ if (!fs.existsSync(attestationPath)) {
67423+ throw new Error(`Signing succeeded but attestation file not found: ${attestationPath}`);
67424+ }
67425+ signedFiles.push(file);
67426+ attestationFiles.push(attestationPath);
67427+ core.info(`\u2713 ${path.basename(file)} -> ${path.basename(attestationPath)}`);
67428+ }
6741467429 }
67415- signedFiles.push(file);
67416- attestationFiles.push(attestationPath);
67417- core.info(`\u2713 ${path.basename(file)} -> ${path.basename(attestationPath)}`);
6741867430 }
67419- // 5. Optionally verify
67420- const shouldVerify = core.getInput('verify') === 'true';
67421- let allVerified = true;
67422- if (shouldVerify) {
67423- if (!credentials.verifyBundlePath) {
67424- core.warning('verify: true requested but no verify bundle available. Provide verify_bundle in the CiToken or set the verify-bundle input.');
67425- allVerified = false;
67431+ // 4. Sign commits (if commits range provided)
67432+ if (commitsRange) {
67433+ core.info('');
67434+ core.info('=== Commit Signing ===');
67435+ // Enumerate commits in range (skip merges)
67436+ const revListResult = await exec.getExecOutput('git', ['rev-list', '--no-merges', commitsRange], { ignoreReturnCode: true, silent: true });
67437+ if (revListResult.exitCode !== 0) {
67438+ throw new Error(`Failed to enumerate commits in range '${commitsRange}': ${revListResult.stderr.trim()}`);
67439+ }
67440+ const commitShas = revListResult.stdout.trim().split('\n').filter(s => s.length > 0);
67441+ if (commitShas.length === 0) {
67442+ core.info('No commits found in range (or all are merge commits)');
6742667443 }
6742767444 else {
67428- core.info('');
67429- core.info('=== Post-Sign Verification ===');
67430- for (const file of signedFiles) {
67431- const result = await exec.getExecOutput(authsPath, ['artifact', 'verify', file, '--identity-bundle', credentials.verifyBundlePath, '--json'], { ignoreReturnCode: true, silent: true });
67432- if (result.stdout.trim()) {
67433- try {
67434- const parsed = JSON.parse(result.stdout.trim());
67435- if (parsed.valid === true) {
67436- core.info(`\u2713 Verified ${path.basename(file)}${parsed.issuer ? ` (issuer: ${parsed.issuer})` : ''}`);
67445+ core.info(`Found ${commitShas.length} commit(s) to sign`);
67446+ for (const sha of commitShas) {
67447+ core.info(`Signing commit: ${sha.substring(0, 8)}`);
67448+ const cliArgs = [
67449+ 'signcommit', sha,
67450+ '--device-key', deviceKey,
67451+ '--repo', credentials.identityRepoPath,
67452+ ];
67453+ const exitCode = await exec.exec(authsPath, cliArgs, {
67454+ env: authsEnv,
67455+ ignoreReturnCode: true,
67456+ });
67457+ if (exitCode !== 0) {
67458+ core.warning(`Failed to sign commit ${sha.substring(0, 8)} (exit code ${exitCode})`);
67459+ continue;
67460+ }
67461+ signedCommits.push(sha);
67462+ core.info(`\u2713 Signed commit ${sha.substring(0, 8)}`);
67463+ }
67464+ // Push attestation refs
67465+ if (signedCommits.length > 0) {
67466+ core.info('Pushing attestation refs...');
67467+ const pushResult = await exec.exec('git', ['push', 'origin', 'refs/auths/commits/*:refs/auths/commits/*'], { ignoreReturnCode: true });
67468+ if (pushResult !== 0) {
67469+ core.warning('Failed to push attestation refs (may not have contents: write permission)');
67470+ }
67471+ else {
67472+ core.info(`\u2713 Pushed attestation refs for ${signedCommits.length} commit(s)`);
67473+ }
67474+ // Also push KERI refs if they exist
67475+ const keriCheck = await exec.getExecOutput('git', ['show-ref', '--heads', '--tags'], { ignoreReturnCode: true, silent: true });
67476+ if (keriCheck.stdout.includes('refs/keri')) {
67477+ await exec.exec('git', ['push', 'origin', 'refs/keri/*:refs/keri/*'], {
67478+ ignoreReturnCode: true,
67479+ });
67480+ }
67481+ }
67482+ }
67483+ }
67484+ // 5. Optionally verify
67485+ if (shouldVerify) {
67486+ // Verify artifacts
67487+ if (signedFiles.length > 0) {
67488+ if (!credentials.verifyBundlePath) {
67489+ core.warning('verify: true requested but no verify bundle available for artifacts.');
67490+ allVerified = false;
67491+ }
67492+ else {
67493+ core.info('');
67494+ core.info('=== Post-Sign Artifact Verification ===');
67495+ for (const file of signedFiles) {
67496+ const result = await exec.getExecOutput(authsPath, ['artifact', 'verify', file, '--identity-bundle', credentials.verifyBundlePath, '--json'], { ignoreReturnCode: true, silent: true });
67497+ if (result.stdout.trim()) {
67498+ try {
67499+ const parsed = JSON.parse(result.stdout.trim());
67500+ if (parsed.valid === true) {
67501+ core.info(`\u2713 Verified ${path.basename(file)}${parsed.issuer ? ` (issuer: ${parsed.issuer})` : ''}`);
67502+ }
67503+ else {
67504+ core.warning(`\u2717 ${path.basename(file)}: ${parsed.error || 'verification returned valid=false'}`);
67505+ allVerified = false;
67506+ }
6743767507 }
67438- else {
67439- core.warning(`\u2717 ${path.basename(file)}: ${parsed.error || ' verification returned valid=false'} `);
67508+ catch {
67509+ core.warning(`\u2717 ${path.basename(file)}: could not parse verification output `);
6744067510 allVerified = false;
6744167511 }
6744267512 }
67443- catch {
67444- core.warning(`\u2717 ${path.basename(file)}: could not parse verification output `);
67513+ else {
67514+ core.warning(`\u2717 ${path.basename(file)}: ${result.stderr.trim() || `exit code ${result.exitCode}`} `);
6744567515 allVerified = false;
6744667516 }
6744767517 }
67518+ }
67519+ }
67520+ // Verify commits
67521+ if (signedCommits.length > 0) {
67522+ core.info('');
67523+ core.info('=== Post-Sign Commit Verification ===');
67524+ for (const sha of signedCommits) {
67525+ const verifyArgs = ['verify-commit', sha];
67526+ if (credentials.verifyBundlePath) {
67527+ verifyArgs.push('--identity-bundle', credentials.verifyBundlePath);
67528+ }
67529+ const result = await exec.exec(authsPath, verifyArgs, {
67530+ env: authsEnv,
67531+ ignoreReturnCode: true,
67532+ });
67533+ if (result === 0) {
67534+ core.info(`\u2713 Verified commit ${sha.substring(0, 8)}`);
67535+ }
6744867536 else {
67449- core.warning(`\u2717 ${path.basename(file)}: ${result.stderr.trim() || `exit code ${result.exitCode}`} `);
67537+ core.warning(`\u2717 Commit ${sha.substring(0, 8)} verification failed `);
6745067538 allVerified = false;
6745167539 }
6745267540 }
@@ -67455,12 +67543,13 @@ async function run() {
6745567543 // 6. Set outputs
6745667544 core.setOutput('signed-files', JSON.stringify(signedFiles));
6745767545 core.setOutput('attestation-files', JSON.stringify(attestationFiles));
67546+ core.setOutput('signed-commits', JSON.stringify(signedCommits));
6745867547 core.setOutput('verified', shouldVerify ? allVerified.toString() : '');
6745967548 // 7. Step summary
67460- await writeStepSummary(signedFiles, attestationFiles, shouldVerify, allVerified);
67549+ await writeStepSummary(signedFiles, attestationFiles, signedCommits, shouldVerify, allVerified);
6746167550 // 8. Fail if verification was requested and failed
6746267551 if (shouldVerify && !allVerified) {
67463- core.setFailed('Post-sign verification failed for one or more files ');
67552+ core.setFailed('Post-sign verification failed for one or more artifacts/commits ');
6746467553 }
6746567554 }
6746667555 catch (error) {
@@ -67477,24 +67566,42 @@ async function run() {
6747767566 }
6747867567 }
6747967568}
67480- async function writeStepSummary(signedFiles, attestationFiles, verified, allVerified) {
67481- if (signedFiles.length === 0)
67569+ async function writeStepSummary(signedFiles, attestationFiles, signedCommits, verified, allVerified) {
67570+ if (signedFiles.length === 0 && signedCommits.length === 0 )
6748267571 return;
6748367572 const lines = [];
67484- lines.push('## Auths Artifact Signing');
67485- lines.push('');
67486- lines.push('| Artifact | Attestation | Status |');
67487- lines.push('|----------|-------------|--------|');
67488- for (let i = 0; i < signedFiles.length; i++) {
67489- const file = path.basename(signedFiles[i]);
67490- const attest = path.basename(attestationFiles[i]);
67491- const status = verified
67492- ? (allVerified ? '\u2705 Signed + Verified' : '\u26a0\ufe0f Signed (verify failed)')
67493- : '\u2705 Signed';
67494- lines.push(`| \`${file}\` | \`${attest}\` | ${status} |`);
67495- }
67573+ lines.push('## Auths Signing');
6749667574 lines.push('');
67497- lines.push(`**${signedFiles.length}** artifact(s) signed`);
67575+ if (signedFiles.length > 0) {
67576+ lines.push('### Artifacts');
67577+ lines.push('');
67578+ lines.push('| Artifact | Attestation | Status |');
67579+ lines.push('|----------|-------------|--------|');
67580+ for (let i = 0; i < signedFiles.length; i++) {
67581+ const file = path.basename(signedFiles[i]);
67582+ const attest = path.basename(attestationFiles[i]);
67583+ const status = verified
67584+ ? (allVerified ? '\u2705 Signed + Verified' : '\u26a0\ufe0f Signed (verify failed)')
67585+ : '\u2705 Signed';
67586+ lines.push(`| \`${file}\` | \`${attest}\` | ${status} |`);
67587+ }
67588+ lines.push('');
67589+ }
67590+ if (signedCommits.length > 0) {
67591+ lines.push('### Commits');
67592+ lines.push('');
67593+ lines.push('| Commit | Status |');
67594+ lines.push('|--------|--------|');
67595+ for (const sha of signedCommits) {
67596+ const status = verified
67597+ ? (allVerified ? '\u2705 Signed + Verified' : '\u26a0\ufe0f Signed')
67598+ : '\u2705 Signed';
67599+ lines.push(`| \`${sha.substring(0, 8)}\` | ${status} |`);
67600+ }
67601+ lines.push('');
67602+ }
67603+ const totalCount = signedFiles.length + signedCommits.length;
67604+ lines.push(`**${totalCount}** item(s) signed`);
6749867605 if (verified) {
6749967606 lines.push(allVerified ? '**Verification:** All passed' : '**Verification:** Failed');
6750067607 }
0 commit comments