feat(release): add FTP deploy script and env vars; add npm run release:push
This commit is contained in:
parent
77b5f8fb5a
commit
7348316409
3 changed files with 129 additions and 1 deletions
10
.env.example
10
.env.example
|
|
@ -3,3 +3,13 @@
|
||||||
AMO_JWT_ISSUER=your-amo-jwt-issuer
|
AMO_JWT_ISSUER=your-amo-jwt-issuer
|
||||||
AMO_JWT_SECRET=your-amo-jwt-secret
|
AMO_JWT_SECRET=your-amo-jwt-secret
|
||||||
|
|
||||||
|
# FTP deploy (used by npm run release:push)
|
||||||
|
# Protocol: ftp (default) or ftps
|
||||||
|
FTP_PROTOCOL=ftp
|
||||||
|
FTP_HOST=your-ftp-host.example.com
|
||||||
|
# Port for ftp/ftps (21 for FTP/explicit FTPS, 990 for implicit FTPS)
|
||||||
|
FTP_PORT=21
|
||||||
|
FTP_USER=your-ftp-username
|
||||||
|
FTP_PASS=your-ftp-password
|
||||||
|
# Remote directory to upload signed artifacts (e.g., /addons/archive-org-link-grabber/)
|
||||||
|
FTP_REMOTE_DIR=/path/on/server
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@
|
||||||
"release:prepare:patch": "npm version patch && node scripts/sync-version.js",
|
"release:prepare:patch": "npm version patch && node scripts/sync-version.js",
|
||||||
"release:prepare:minor": "npm version minor && node scripts/sync-version.js",
|
"release:prepare:minor": "npm version minor && node scripts/sync-version.js",
|
||||||
"release:prepare:major": "npm version major && node scripts/sync-version.js",
|
"release:prepare:major": "npm version major && node scripts/sync-version.js",
|
||||||
"release:sign": "node scripts/release-sign.js"
|
"release:sign": "node scripts/release-sign.js",
|
||||||
|
"release:push": "node scripts/release-push.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"web-ext": "^8.3.0"
|
"web-ext": "^8.3.0"
|
||||||
|
|
|
||||||
117
scripts/release-push.js
Normal file
117
scripts/release-push.js
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
/*
|
||||||
|
* Uploads the latest signed release artifacts in releases/<version>/ via FTP/FTPS using curl.
|
||||||
|
* Reads configuration from .env (auto-loaded) or process.env.
|
||||||
|
* Env vars:
|
||||||
|
* FTP_PROTOCOL=ftp|ftps (default: ftp)
|
||||||
|
* FTP_HOST=example.com (required)
|
||||||
|
* FTP_PORT=21 (optional)
|
||||||
|
* FTP_USER=username (required)
|
||||||
|
* FTP_PASS=password (required)
|
||||||
|
* FTP_REMOTE_DIR=/remote/path (required)
|
||||||
|
*/
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
|
const root = path.join(__dirname, '..');
|
||||||
|
|
||||||
|
// Auto-load .env if present
|
||||||
|
(() => {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
console.log('Loaded environment from .env');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Warning: Failed to load .env:', e.message);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
function mask(str) {
|
||||||
|
if (!str) return str;
|
||||||
|
return '***';
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(cmd, secrets = []) {
|
||||||
|
let shown = cmd;
|
||||||
|
for (const s of secrets) {
|
||||||
|
if (s) shown = shown.split(String(s)).join('***');
|
||||||
|
}
|
||||||
|
console.log('> ' + shown);
|
||||||
|
execSync(cmd, { stdio: 'inherit' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const pkg = require(path.join(root, 'package.json'));
|
||||||
|
const version = pkg.version;
|
||||||
|
if (!version) {
|
||||||
|
console.error('package.json is missing version');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const artifactsDir = path.join(root, 'releases', version);
|
||||||
|
if (!fs.existsSync(artifactsDir) || !fs.statSync(artifactsDir).isDirectory()) {
|
||||||
|
console.error(`Artifacts directory not found: ${artifactsDir}. Run npm run release:sign first.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = fs.readdirSync(artifactsDir).filter(f => fs.statSync(path.join(artifactsDir, f)).isFile());
|
||||||
|
if (files.length === 0) {
|
||||||
|
console.error(`No files found in ${artifactsDir}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const protocol = (process.env.FTP_PROTOCOL || 'ftp').toLowerCase();
|
||||||
|
const host = process.env.FTP_HOST;
|
||||||
|
const port = process.env.FTP_PORT;
|
||||||
|
const user = process.env.FTP_USER;
|
||||||
|
const pass = process.env.FTP_PASS;
|
||||||
|
let remoteDir = process.env.FTP_REMOTE_DIR || '/';
|
||||||
|
|
||||||
|
if (!host || !user || !pass || !remoteDir) {
|
||||||
|
console.error('Missing FTP config. Required: FTP_HOST, FTP_USER, FTP_PASS, FTP_REMOTE_DIR');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize remoteDir
|
||||||
|
if (!remoteDir.startsWith('/')) remoteDir = '/' + remoteDir;
|
||||||
|
if (remoteDir.endsWith('/')) remoteDir = remoteDir.slice(0, -1);
|
||||||
|
|
||||||
|
// Construct base URL
|
||||||
|
const baseUrl = `${protocol}://${host}${port ? `:${port}` : ''}${remoteDir}`;
|
||||||
|
|
||||||
|
console.log(`Uploading ${files.length} file(s) from ${artifactsDir} to ${protocol}://${host}${port ? `:${port}` : ''}${remoteDir}/`);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const localPath = path.join(artifactsDir, file);
|
||||||
|
const url = `${baseUrl}/${encodeURIComponent(file)}`;
|
||||||
|
// --ftp-create-dirs ensures remote dirs are created; --fail fails on server errors.
|
||||||
|
const cmd = [
|
||||||
|
'curl',
|
||||||
|
'--fail',
|
||||||
|
'--ftp-create-dirs',
|
||||||
|
`--user`, `${user}:${pass}`,
|
||||||
|
'--upload-file', JSON.stringify(localPath),
|
||||||
|
JSON.stringify(url),
|
||||||
|
].join(' ');
|
||||||
|
run(cmd, [user, pass]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Upload complete.');
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue