import crypto from 'node:crypto' import path from 'node:path' import { fileURLToPath } from 'node:url' import { options, packageRelease, inspectPackage, deploymentTarget, shellQuote, run, fail } from './release.mjs' export const installScript = `set -euo pipefail BASE_DIR=$(cd -- "$1" && pwd -P) ARCHIVE=$2 EXPECTED=$3 TAG=$4 MODE=$5 RELEASE="$BASE_DIR/frontend-releases/$TAG" STAGING='' cleanup() { rm -f -- "$ARCHIVE" if [[ -n "$STAGING" && "$STAGING" == "$BASE_DIR/frontend-releases/.incoming-"* ]]; then rm -rf -- "$STAGING"; fi } trap cleanup EXIT exec 8>"$BASE_DIR/frontend-releases/.install-$TAG.lock" flock -x 8 printf '%s %s\\n' "$EXPECTED" "$ARCHIVE" | sha256sum -c - if [[ -e "$RELEASE" ]]; then [[ -f "$RELEASE/package.sha256" && "$(cat "$RELEASE/package.sha256")" == "$EXPECTED" ]] || { echo 'Existing release has a different checksum' >&2; exit 1; } else STAGING=$(mktemp -d "$BASE_DIR/frontend-releases/.incoming-$TAG.XXXXXX") tar --no-same-owner -xzf "$ARCHIVE" -C "$STAGING" printf '%s\\n' "$EXPECTED" > "$STAGING/package.sha256" mv -T -- "$STAGING" "$RELEASE" STAGING='' fi SW_DEPLOY_BASE="$BASE_DIR" bash "$RELEASE/deploy.sh" "$TAG" "$MODE" ` function main() { const args = options({ archive: { type: 'string' }, tag: { type: 'string' }, 'source-ref': { type: 'string' }, 'skip-install': { type: 'boolean' }, host: { type: 'string' }, user: { type: 'string' }, port: { type: 'string' }, identity: { type: 'string' }, directory: { type: 'string' }, 'build-only': { type: 'boolean' } }) if (args.help) { console.log('pnpm deploy:test [--archive FILE.tar.gz] [--tag VERSION] [--build-only] [--host 192.168.2.226] [--user root] [--port 22] [--identity SSH_KEY] [--directory /www/wwwroot/sw2.fuhua91.com]\n不指定 --archive 时先独立构建前端;--build-only 仅在服务器构建并预检,不替换当前容器。') return } if (args.archive && (args.tag || args['source-ref'] || args['skip-install'])) throw new Error('--archive 复用现有包,不能再指定构建参数') const target = deploymentTarget(args) const release = args.archive ? inspectPackage(args.archive) : inspectPackage(packageRelease(args).archive) const identity = args.identity ? ['-i', path.resolve(args.identity)] : [] const ssh = ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=10', '-p', target.port, ...identity, target.remote] const uploads = `${target.base}/frontend-releases` const uploaded = `${uploads}/.upload-${release.info.version}-${crypto.randomBytes(6).toString('hex')}.tar.gz` run('ssh', [...ssh, `test -d ${shellQuote(target.base)} && test -w ${shellQuote(target.base)} && mkdir -p ${shellQuote(uploads)}`]) run('scp', ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=10', '-P', target.port, ...identity, release.archive, `${target.remote}:${uploaded}`]) const parameters = [target.base, uploaded, release.checksum, release.info.version, args['build-only'] ? '--build-only' : ''] run('ssh', [...ssh, `bash -s -- ${parameters.map(shellQuote).join(' ')}`], { stdio: ['pipe', 'inherit', 'inherit'], input: installScript }) console.log(args['build-only'] ? `预检通过:${release.info.version},当前前端未替换` : `前端发布完成:${release.info.version};http://sw2.fuhua91.com`) } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { try { main() } catch (error) { fail(error) } }