@rathnasgala/cli 1.1.8 → 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rathnasgala/cli",
3
- "version": "1.1.8",
3
+ "version": "1.1.9",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src"
@@ -0,0 +1,9 @@
1
+ export const CLI_INVOCATION = 'npx --yes @rathnasgala/cli@latest';
2
+
3
+ export function cliCommand(argumentsText = '') {
4
+ return argumentsText === '' ? CLI_INVOCATION : `${CLI_INVOCATION} ${argumentsText}`;
5
+ }
6
+
7
+ export function shellArgument(value) {
8
+ return /^[A-Za-z0-9_./-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
9
+ }
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import { galaApi } from '../api/gala.js';
5
5
  import { galaCredential } from '../auth/gala.js';
6
6
  import { githubCredential } from '../auth/github.js';
7
+ import { cliCommand } from '../cli/invocation.js';
7
8
  import { createGit } from '../git.js';
8
9
 
9
10
  /**
@@ -23,7 +24,7 @@ export async function doctor({ terminal, options, cwd = process.cwd() }) {
23
24
  const accepted = await galaApi({ baseUrl: gala.apiBaseUrl, token: gala.accessToken }).accepted();
24
25
  return accepted
25
26
  ? ok(`valid until ${new Date(gala.expiresAt).toLocaleString()}`)
26
- : wrong('the API no longer accepts it', 'gala auth');
27
+ : wrong('the API no longer accepts it', cliCommand('auth'));
27
28
  }));
28
29
 
29
30
  checks.push(await checkCredential('GitHub sign-in', async () => {
@@ -45,7 +46,7 @@ export async function doctor({ terminal, options, cwd = process.cwd() }) {
45
46
  const source = await readFile(workflow, 'utf8');
46
47
  const siteId = /site-id:\s*([0-9A-Z]{26})/.exec(source)?.[1];
47
48
  return siteId == null
48
- ? wrong('no site id — this publication may not be registered', 'gala init')
49
+ ? wrong('no site id — this publication may not be registered', cliCommand('init'))
49
50
  : ok(siteId);
50
51
  }, 'the workflow is missing; registration writes it'));
51
52
 
@@ -53,8 +54,8 @@ export async function doctor({ terminal, options, cwd = process.cwd() }) {
53
54
  const git = createGit({ root });
54
55
  const dirty = await git.run(['status', '--porcelain'], { capture: true });
55
56
  const ahead = await git.run(['rev-list', '--count', '@{upstream}..HEAD'], { capture: true, allow: [0, 128] });
56
- if (dirty !== '') return wrong(`${dirty.split('\n').length} file(s) not recorded`, 'gala publish');
57
- if (ahead !== '' && ahead !== '0') return wrong(`${ahead} commit(s) not sent`, 'gala publish');
57
+ if (dirty !== '') return wrong(`${dirty.split('\n').length} file(s) not recorded`, cliCommand('publish'));
58
+ if (ahead !== '' && ahead !== '0') return wrong(`${ahead} commit(s) not sent`, cliCommand('publish'));
58
59
  return ok('everything is on GitHub');
59
60
  }, 'this folder is not a git checkout'));
60
61
 
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import { galaApi } from '../api/gala.js';
4
4
  import { galaCredential } from '../auth/gala.js';
5
5
  import { UsageError } from '../cli/args.js';
6
+ import { cliCommand } from '../cli/invocation.js';
6
7
  import { customDomain } from '../domain.js';
7
8
  import { readPublication } from '../publication.js';
8
9
 
@@ -16,7 +17,7 @@ export async function domain({ terminal, options, cwd = process.cwd() }) {
16
17
  }
17
18
  const [action = 'status', value, ...extra] = options.positional;
18
19
  if (extra.length > 0 || !['status', 'set', 'check', 'cancel', 'remove'].includes(action)) {
19
- throw new UsageError('Use: gala domain [status|set <hostname>|check|cancel|remove]');
20
+ throw new UsageError(`Use: ${cliCommand('domain [status|set <hostname>|check|cancel|remove]')}`);
20
21
  }
21
22
  if (action === 'set' && value == null) throw new UsageError('domain set needs a hostname');
22
23
  if (action !== 'set' && value != null) throw new UsageError(`domain ${action} takes no hostname`);
@@ -45,7 +46,7 @@ export async function domain({ terminal, options, cwd = process.cwd() }) {
45
46
  pathPrefix: '/',
46
47
  });
47
48
  terminal.done(`Reserved ${checked.host}`);
48
- terminal.note('Verify it in the repository owner’s GitHub account, then run: gala domain check');
49
+ terminal.note(`Verify it in the repository owner’s GitHub account, then run: ${cliCommand('domain check')}`);
49
50
  terminal.openUrl('https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/verifying-your-custom-domain-for-github-pages');
50
51
  return change;
51
52
  }
@@ -83,7 +84,7 @@ export async function domain({ terminal, options, cwd = process.cwd() }) {
83
84
  const configured = await api.configureTopologyChange(publication.siteId, pending.changeId);
84
85
  terminal.done(`GitHub verified ${configured.cname}`);
85
86
  terminal.note(dnsInstruction(configured.cname, await providerHost(api, publication.siteId)));
86
- terminal.note('After DNS propagates, run: gala domain check');
87
+ terminal.note(`After DNS propagates, run: ${cliCommand('domain check')}`);
87
88
  return configured;
88
89
  }
89
90
  const committed = await api.commitTopologyChange(publication.siteId, pending.changeId);
@@ -119,6 +120,6 @@ function dnsInstruction(host, target) {
119
120
  function showPending(terminal, pending) {
120
121
  terminal.result(`${pending.canonicalBaseUrl}${pending.pathPrefix}`);
121
122
  terminal.note(`State: ${pending.state}`);
122
- if (pending.state === 'PREPARED') terminal.note('Next: verify ownership, then run gala domain check.');
123
- else terminal.note('Next: configure DNS, then run gala domain check.');
123
+ if (pending.state === 'PREPARED') terminal.note(`Next: verify ownership, then run ${cliCommand('domain check')}.`);
124
+ else terminal.note(`Next: configure DNS, then run ${cliCommand('domain check')}.`);
124
125
  }
@@ -7,6 +7,7 @@ import { galaCredential } from '../auth/gala.js';
7
7
  import { githubCredential } from '../auth/github.js';
8
8
  import { cloneRepository, createGit, populateEmptyRepository } from '../git.js';
9
9
  import { UsageError } from '../cli/args.js';
10
+ import { CLI_INVOCATION, shellArgument } from '../cli/invocation.js';
10
11
  import { customDomain } from '../domain.js';
11
12
 
12
13
  /**
@@ -291,7 +292,7 @@ export function reportCreatedPublication({
291
292
  directoryLabel,
292
293
  hasDomainChange = false
293
294
  }) {
294
- const run = 'npx --yes @rathnasgala/cli@latest';
295
+ const run = CLI_INVOCATION;
295
296
  terminal.done(`Created ${owner}/${name}`);
296
297
  terminal.note('The first deployment is running in GitHub Actions. The public site is not live yet.');
297
298
  terminal.note(`track it at https://github.com/${owner}/${name}/actions`);
@@ -313,10 +314,6 @@ export function reportCreatedPublication({
313
314
  terminal.note('lists every available command');
314
315
  }
315
316
 
316
- function shellArgument(value) {
317
- return /^[A-Za-z0-9_./-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
318
- }
319
-
320
317
  function idempotencyKey(owner, name) {
321
318
  return `init-${createHash('sha256').update(`${owner.toLowerCase()}/${name.toLowerCase()}`).digest('hex')}`;
322
319
  }
@@ -11,6 +11,7 @@ import {
11
11
  import { stringify } from 'yaml';
12
12
 
13
13
  import { UsageError } from '../cli/args.js';
14
+ import { cliCommand } from '../cli/invocation.js';
14
15
  import { postUrl, readPublication } from '../publication.js';
15
16
 
16
17
  /**
@@ -54,7 +55,7 @@ export async function createPost({ terminal, options, cwd = process.cwd(), now =
54
55
  if (address != null) terminal.note(`will appear at ${address}`);
55
56
 
56
57
  terminal.blank();
57
- terminal.note('write below the second --- line, then: npx --yes @rathnasgala/cli@latest preview');
58
+ terminal.note(`write below the second --- line, then: ${cliCommand('preview')}`);
58
59
  return { file, metadata };
59
60
  }
60
61
 
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import { galaApi } from '../api/gala.js';
5
5
  import { galaCredential } from '../auth/gala.js';
6
6
  import { UsageError } from '../cli/args.js';
7
+ import { cliCommand } from '../cli/invocation.js';
7
8
  import { createGit } from '../git.js';
8
9
  import { readPublication } from '../publication.js';
9
10
 
@@ -25,7 +26,7 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
25
26
  const [action = 'status', ...args] = options.positional;
26
27
 
27
28
  if (action === 'status') {
28
- requireArgs(args, 0, 'gala prism status');
29
+ requireArgs(args, 0, cliCommand('prism status'));
29
30
  const state = await api.json(`/v1/sites/${publication.siteId}/prism`, { action: 'Prism status' });
30
31
  terminal.result(`Prism ${state.publishedMode}`);
31
32
  terminal.note(`Mode: requested ${state.requestedMode}; published ${state.publishedMode}`);
@@ -39,7 +40,7 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
39
40
  const expectedRepositoryHeadSha = inventory.headSha;
40
41
 
41
42
  if (action === 'mode') {
42
- requireArgs(args, 1, 'gala prism mode <off|presentation-only|manual|assisted>');
43
+ requireArgs(args, 1, cliCommand('prism mode <off|presentation-only|manual|assisted>'));
43
44
  const mode = MODES.get(args[0]);
44
45
  if (!mode) throw new UsageError('Prism mode must be off, presentation-only, manual, or assisted.');
45
46
  if (mode === 'OFF' || mode === 'PRESENTATION_ONLY') await confirm(terminal, options, `Change Prism mode to ${args[0]}?`);
@@ -52,7 +53,7 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
52
53
  if (action === 'link-policy') {
53
54
  const [scope, target, value] = args;
54
55
  if (scope === 'site') {
55
- requireArgs(args, 2, 'gala prism link-policy site <nofollow|follow>');
56
+ requireArgs(args, 2, cliCommand('prism link-policy site <nofollow|follow>'));
56
57
  const policy = policyValue(target);
57
58
  const result = await mutate(api, `/v1/sites/${publication.siteId}/prism`, 'PUT', {
58
59
  configurationLinkPolicy: policy, expectedRepositoryHeadSha,
@@ -60,7 +61,7 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
60
61
  return settleMutation(api, terminal, publication, result);
61
62
  }
62
63
  if (scope === 'work') {
63
- requireArgs(args, 3, 'gala prism link-policy work <slug> <inherit|nofollow|follow>');
64
+ requireArgs(args, 3, cliCommand('prism link-policy work <slug> <inherit|nofollow|follow>'));
64
65
  const post = resolvePost(inventory, target, options.value('language'), publication.defaultLanguage);
65
66
  if (value === 'inherit') {
66
67
  const result = await mutate(api,
@@ -76,11 +77,14 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
76
77
  }, 'Prism work link policy');
77
78
  return settleMutation(api, terminal, publication, result);
78
79
  }
79
- throw new UsageError('Use: gala prism link-policy site <nofollow|follow> or work <slug> <inherit|nofollow|follow>');
80
+ throw new UsageError(
81
+ `Use: ${cliCommand('prism link-policy site <nofollow|follow>')} or `
82
+ + cliCommand('prism link-policy work <slug> <inherit|nofollow|follow>')
83
+ );
80
84
  }
81
85
 
82
86
  if (action === 'list') {
83
- requireArgs(args, 1, 'gala prism list <slug> [--language en]');
87
+ requireArgs(args, 1, cliCommand('prism list <slug> [--language en]'));
84
88
  const post = resolvePost(inventory, args[0], options.value('language'), publication.defaultLanguage);
85
89
  const result = await configurations(api, publication.siteId, post);
86
90
  terminal.result(`${result.configurations.length} configuration${result.configurations.length === 1 ? '' : 's'}`);
@@ -91,7 +95,7 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
91
95
  }
92
96
 
93
97
  if (action === 'create') {
94
- requireArgs(args, 1, 'gala prism create <slug> --language en --depth brief --intent orientation');
98
+ requireArgs(args, 1, cliCommand('prism create <slug> --language en --depth brief --intent orientation'));
95
99
  const post = resolvePost(inventory, args[0], options.value('language'), publication.defaultLanguage);
96
100
  const current = await configurations(api, publication.siteId, post);
97
101
  const result = await mutate(api,
@@ -108,7 +112,7 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
108
112
  }
109
113
 
110
114
  if (!['edit', 'generate', 'submit', 'approve', 'reject', 'revoke'].includes(action)) {
111
- throw new UsageError('Unknown Prism action. Run gala prism --help.');
115
+ throw new UsageError(`Unknown Prism action. Run ${cliCommand('prism --help')}.`);
112
116
  }
113
117
 
114
118
  const configurationId = args[0];
@@ -119,7 +123,7 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
119
123
  const base = `/v1/sites/${publication.siteId}/articles/${post.articleId}/configurations/${configurationId}`;
120
124
 
121
125
  if (action === 'edit') {
122
- requireArgs(args, 1, 'gala prism edit <configuration-id> --file proposal.md');
126
+ requireArgs(args, 1, cliCommand('prism edit <configuration-id> --file proposal.md'));
123
127
  const filename = options.value('file');
124
128
  if (!filename) throw new UsageError('Prism edit needs --file proposal.md.');
125
129
  const markdown = await readFile(path.resolve(root, filename), 'utf8');
@@ -132,7 +136,7 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
132
136
  }
133
137
 
134
138
  if (action === 'generate') {
135
- requireArgs(args, 1, 'gala prism generate <configuration-id>');
139
+ requireArgs(args, 1, cliCommand('prism generate <configuration-id>'));
136
140
  const result = await mutate(api, `${base}/generation-jobs`, 'POST', {
137
141
  expectedSourceContentHash: collection.sourceRevisionHash,
138
142
  hashContract: collection.hashContract,
@@ -146,7 +150,7 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
146
150
  throw new UsageError(`${action} needs a revision ID when there is no working revision.`);
147
151
  }
148
152
  if (action === 'submit') {
149
- if (args.length > 2) throw new UsageError('Use: gala prism submit <configuration-id> [revision-id]');
153
+ if (args.length > 2) throw new UsageError(`Use: ${cliCommand('prism submit <configuration-id> [revision-id]')}`);
150
154
  if (revisionId !== configuration.workingRevision?.revisionId) {
151
155
  throw new UsageError('Only the current working revision can be submitted. Refresh the configuration and try again.');
152
156
  }
@@ -165,7 +169,7 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
165
169
 
166
170
  const reason = options.value('reason');
167
171
  if (action === 'approve') {
168
- if (args.length > 2) throw new UsageError('Use: gala prism approve <configuration-id> [revision-id] [--yes]');
172
+ if (args.length > 2) throw new UsageError(`Use: ${cliCommand('prism approve <configuration-id> [revision-id] [--yes]')}`);
169
173
  if (revisionId !== configuration.workingRevision?.revisionId) {
170
174
  throw new UsageError('Only the current working revision can be approved. Refresh the configuration and try again.');
171
175
  }
@@ -292,7 +296,7 @@ async function settleMutation(api, terminal, publication, result) {
292
296
  throw new UsageError(`Repository update failed (${state.errorCode ?? 'unknown error'}). Run the command again after correcting the cause.`);
293
297
  }
294
298
  if (state.status !== 'COMMITTED') {
295
- throw new UsageError('Repository update did not finish before the 31-minute tracking deadline. Check gala prism status before retrying.');
299
+ throw new UsageError(`Repository update did not finish before the 31-minute tracking deadline. Check ${cliCommand('prism status')} before retrying.`);
296
300
  }
297
301
  if (!state.publicationAttemptSha) {
298
302
  terminal.result('Repository updated. No publication attempt was returned.');
@@ -4,6 +4,8 @@ import { tmpdir } from 'node:os';
4
4
  import path from 'node:path';
5
5
  import { x as extractTar } from 'tar';
6
6
 
7
+ import { cliCommand } from '../cli/invocation.js';
8
+
7
9
  const PACKAGE = '@rathnasgala/theme';
8
10
  const REGISTRY = 'https://registry.npmjs.org';
9
11
  const PROTECTED = ['.git/', 'content/', 'custom.css', 'site.config.yml'];
@@ -81,7 +83,7 @@ async function assertNoManagedDrift(root, installed) {
81
83
  for (const [managed, expected] of Object.entries(installed.files)) {
82
84
  const file = path.join(root, managed);
83
85
  if (!await exists(file) || (await lstat(file)).isSymbolicLink() || sha256(await readFile(file)) !== expected) {
84
- throw new Error(`${managed} has local changes; restore it with gala doctor before upgrading`);
86
+ throw new Error(`${managed} has local changes; restore it with ${cliCommand('doctor')} before upgrading`);
85
87
  }
86
88
  }
87
89
  }
@@ -168,6 +170,6 @@ export async function upgrade({ terminal, options, cwd = process.cwd(), fetchImp
168
170
  await rm(unpacked.temporary, { recursive: true, force: true });
169
171
  }
170
172
  terminal.done(`Upgraded managed theme to ${release.version}`);
171
- terminal.note('Run gala preview, then gala publish when the result is approved.');
173
+ terminal.note(`Run ${cliCommand('preview')}, then ${cliCommand('publish')} when the result is approved.`);
172
174
  return { changed: true, version: release.version };
173
175
  }
@@ -7,6 +7,7 @@ import { preview } from './commands/preview.js';
7
7
  import { publish } from './commands/publish.js';
8
8
  import { prism } from './commands/prism.js';
9
9
  import { upgrade } from './commands/upgrade.js';
10
+ import { cliCommand } from './cli/invocation.js';
10
11
 
11
12
  /**
12
13
  * Commands in the order a writer meets them.
@@ -31,19 +32,19 @@ export const COMMANDS = {
31
32
  },
32
33
  init: {
33
34
  summary: 'Create a publication in an empty directory',
34
- usage: 'gala init [directory] [--name my-notes] [--domain blog.example.com]',
35
+ usage: cliCommand('init [directory] [--name my-notes] [--domain blog.example.com]'),
35
36
  flags: ['name', 'domain', 'api-base-url'],
36
37
  run: init
37
38
  },
38
39
  domain: {
39
40
  summary: 'Inspect or change this publication’s custom domain',
40
- usage: 'gala domain [status|set <hostname>|check|cancel|remove] [--root path]',
41
+ usage: cliCommand('domain [status|set <hostname>|check|cancel|remove] [--root path]'),
41
42
  flags: ['root', 'api-base-url'],
42
43
  run: domain
43
44
  },
44
45
  new: {
45
46
  summary: 'Start a post',
46
- usage: 'gala new "A durable idea" [--language en]',
47
+ usage: cliCommand('new "A durable idea" [--language en]'),
47
48
  flags: ['root', 'language', 'today'],
48
49
  run: createPost
49
50
  },
@@ -60,14 +61,14 @@ export const COMMANDS = {
60
61
  },
61
62
  prism: {
62
63
  summary: 'Manage author-approved Prism configurations',
63
- usage: 'gala prism <status|mode|link-policy|list|create|edit|generate|submit|approve|reject|revoke> [arguments]',
64
+ usage: cliCommand('prism <status|mode|link-policy|list|create|edit|generate|submit|approve|reject|revoke> [arguments]'),
64
65
  flags: ['root', 'api-base-url', 'language', 'depth', 'intent', 'modality', 'file', 'reason'],
65
66
  switches: ['yes'],
66
67
  run: prism
67
68
  },
68
69
  upgrade: {
69
70
  summary: 'Inspect and apply a verified managed-theme update',
70
- usage: 'gala upgrade [--channel latest|next] [--yes]',
71
+ usage: cliCommand('upgrade [--channel latest|next] [--yes]'),
71
72
  flags: ['root', 'channel'],
72
73
  switches: ['yes'],
73
74
  run: upgrade
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { UsageError, parseArguments } from './cli/args.js';
3
+ import { CLI_INVOCATION, cliCommand } from './cli/invocation.js';
3
4
  import { createTerminal } from './cli/terminal.js';
4
5
  import { COMMANDS } from './commands-manifest.js';
5
6
 
@@ -20,7 +21,7 @@ if (command == null) {
20
21
  }
21
22
 
22
23
  if (argv.includes('--help') || argv.includes('-h')) {
23
- process.stdout.write(`\n ${command.usage ?? `gala ${name}`}\n ${command.summary}\n\n`);
24
+ process.stdout.write(`\n ${command.usage ?? cliCommand(name)}\n ${command.summary}\n\n`);
24
25
  process.exit(0);
25
26
  }
26
27
 
@@ -56,5 +57,6 @@ function usage() {
56
57
  const lines = Object.entries(COMMANDS)
57
58
  .map(([key, { summary }]) => ` ${key.padEnd(9)} ${summary}`)
58
59
  .join('\n');
59
- process.stdout.write(`\n gala <command>\n\n${lines}\n\n Run any command with --help.\n\n`);
60
+ process.stdout.write(`\n ${CLI_INVOCATION} <command>\n\n${lines}\n\n`
61
+ + ` Run ${CLI_INVOCATION} <command> --help for details.\n\n`);
60
62
  }