archive-org-link-grabber/scripts/release-sign.js

96 lines
3 KiB
JavaScript

#!/usr/bin/env node
/*
* Signs an unlisted release via AMO using web-ext and outputs artifacts to releases/<version>/
* Requires env: AMO_JWT_ISSUER, AMO_JWT_SECRET
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const root = path.join(__dirname, '..');
// Auto-load .env if present (no dependency on dotenv)
(() => {
try {
const envPath = path.join(root, '.env');
if (fs.existsSync(envPath)) {
const content = fs.readFileSync(envPath, 'utf8');
for (const rawLine of content.split(/\r?\n/)) {
const line = rawLine.trim();
if (!line || line.startsWith('#')) continue;
const cleaned = line.startsWith('export ')
? line.slice('export '.length).trim()
: line;
const eq = cleaned.indexOf('=');
if (eq === -1) continue;
const key = cleaned.slice(0, eq).trim();
let val = cleaned.slice(eq + 1).trim();
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
val = val.slice(1, -1);
}
if (!(key in process.env)) process.env[key] = val;
}
// Do not log secrets; just note that .env was processed
console.log('Loaded environment from .env');
}
} catch (e) {
console.warn('Warning: Failed to load .env:', e.message);
}
})();
const pkg = require(path.join(root, 'package.json'));
const manifest = require(path.join(root, 'manifest.json'));
const issuer = process.env.AMO_JWT_ISSUER;
const secret = process.env.AMO_JWT_SECRET;
if (!issuer || !secret) {
console.error('Missing AMO credentials. Set AMO_JWT_ISSUER and AMO_JWT_SECRET.');
process.exit(1);
}
const addonId = manifest?.applications?.gecko?.id;
if (!addonId || addonId.includes('example')) {
console.error('Invalid add-on id. Set applications.gecko.id in manifest.json to your stable ID.');
process.exit(1);
}
const version = pkg.version;
if (!version) {
console.error('package.json is missing version');
process.exit(1);
}
const outDir = path.join(root, 'releases', version);
fs.mkdirSync(outDir, { recursive: true });
function run(cmd) {
// Mask secrets in logs
let shown = cmd;
if (issuer) shown = shown.replaceAll(issuer, '***');
if (secret) shown = shown.replaceAll(secret, '***');
console.log(`> ${shown}`);
execSync(cmd, { stdio: 'inherit' });
}
try {
// Lint before signing (allow self-hosted update_url)
run('npx --yes web-ext lint --source-dir . --self-hosted');
// Sign (unlisted) and place artifacts in releases/<version>
const signCmd = [
'npx --yes web-ext sign',
'--channel=unlisted',
`--api-key="${issuer}"`,
`--api-secret="${secret}"`,
// ID is read from manifest.json; newer web-ext may not support --id
`--artifacts-dir "${outDir}"`,
].join(' ');
run(signCmd);
console.log('\nSigned artifacts written to:', outDir);
console.log('Next: host .xpi and update updates.json (if self-hosting updates).');
} catch (err) {
console.error('Release signing failed:', err.message);
process.exit(1);
}