Something went wrong. Try again.
A nicer S3 CLI
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192#!/usr/bin/env node// The file list for `bundle create`: everything s3cli needs at run time.//// s3cli has no runtime dependencies and no build step — the archive carries// the TypeScript sources, which node strips as it loads them, the vendored// ordner.fyi S3 client among them — so the list is computed: package.json,// README and LICENSE, and every module under src/. Then it is checked against what real runs read, through bundle's// recording mount: a read the list misses fails the build, so a file loaded// only on some path cannot be left out silently.import * as FS from 'node:fs';import * as OS from 'node:os';import * as PATH from 'node:path';import { spawnSync } from 'node:child_process';import { createRequire } from 'node:module';
const ROOT = PATH.resolve(import.meta.dirname, '..');const OUTPUT = PATH.join(ROOT, 'build', 's3cli.manifest');
/** Runs to observe. None needs the network or an account. */const RUNS: { args: string[]; input?: string; exit?: number }[] = [ { args: ['--help'] }, { args: ['--version'] }, { args: ['version', '--json'] }, { args: ['help', '--json'] }, { args: ['get', '--help'] }, { args: ['bucket', 'add', '--help'] }, { args: ['config', 'list'] }, { args: ['profile', 'list', '--json'] }, { args: ['bucket', 'list'] }, { args: ['completion', 'bash'] }, { args: ['completion', 'fish'] }, { args: ['completion', 'zsh'] }, { args: ['completion', 'powershell'] }, { args: ['__complete', '--shell', 'fish', '--', 'g'] }, { args: ['__complete', '--shell', 'fish', '--', 'ls', ''] }, { args: ['shell'], input: 'help\npwd\nlpwd\nhelp get\n' }, // Without a profile: the error path, exit 3. { args: ['ls', 'some-bucket/'], exit: 3 },];
function walk(dir: string): string[] { return FS.readdirSync(PATH.join(ROOT, dir), { withFileTypes: true }).flatMap((entry) => { const path = PATH.posix.join(dir, entry.name); return entry.isDirectory() ? walk(path) : [path]; });}
const files = [ 'package.json', ...['README.md', 'LICENSE'].filter((name) => FS.existsSync(PATH.join(ROOT, name))), ...walk('src').filter((path) => path.endsWith('.ts')),].sort();
const record = createRequire(import.meta.url).resolve('@pipobscure/bundle/record');const scratch = FS.mkdtempSync(PATH.join(OS.tmpdir(), 's3cli-observe-'));// The runs get a config of their own: secrets in a file, and no browser.FS.mkdirSync(PATH.join(scratch, 'config'));FS.writeFileSync(PATH.join(scratch, 'config', 'config.json'), JSON.stringify({ profiles: {}, buckets: {}, settings: { 'secret-store': 'file' } }));const seen = new Set<string>();try { for (const [index, { args, input, exit }] of RUNS.entries()) { // The recorder truncates its manifest on start: one per run. const manifest = PATH.join(scratch, `run-${index}.manifest`); const result = spawnSync(process.execPath, [ '--no-warnings', '--experimental-vfs', '-r', record, `--vfs-load=${ROOT}`, '--', ...args, ], { cwd: scratch, encoding: 'utf-8', input: input ?? '', env: { ...process.env, BUNDLE_MANIFEST: manifest, S3CLI_CONFIG_DIR: PATH.join(scratch, 'config'), NO_COLOR: '1' }, }); const expected = exit ?? 0; if (result.status !== expected) fail(`observing 's3cli ${args.join(' ')}' exited ${result.status}, not ${expected}:\n${result.stderr}`); for (const line of FS.readFileSync(manifest, 'utf-8').split('\n')) if (line) seen.add(line); }} finally { FS.rmSync(scratch, { recursive: true, force: true });}
const holes = [...seen].filter((name) => !files.includes(name)).sort();if (holes.length) fail(`runs read files the list does not contain:\n ${holes.join('\n ')}`);
FS.mkdirSync(PATH.dirname(OUTPUT), { recursive: true });FS.writeFileSync(OUTPUT, `${files.join('\n')}\n`);console.error(`* ${files.length} members (${seen.size} observed, all covered) -> ${PATH.relative(ROOT, OUTPUT)}`);console.error("* next: 'npm run pack:nzip' to build the unsigned archive");
function fail(reason: string): never { console.error(`error: ${reason}`); process.exit(1);}