@rathnasgala/cli 1.1.14 → 1.1.15
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
|
@@ -2,7 +2,8 @@ import { readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
import { UsageError } from '../cli/args.js';
|
|
5
|
-
import {
|
|
5
|
+
import { readPublication } from '../publication.js';
|
|
6
|
+
import { listProfiles, requireProfileName } from './profiles.js';
|
|
6
7
|
|
|
7
8
|
const FILENAME = 'gala-account-profile';
|
|
8
9
|
|
|
@@ -34,18 +35,71 @@ export async function checkoutProfile(root) {
|
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
export async function accountForCommand(options, root) {
|
|
38
|
+
export async function accountForCommand(options, root, { terminal, profilesRoot } = {}) {
|
|
38
39
|
const explicit = options.value('account');
|
|
39
40
|
const bound = await checkoutProfile(root);
|
|
40
41
|
if (explicit != null) {
|
|
41
|
-
const selected =
|
|
42
|
+
const selected = await resolveProfileSelector(explicit, profilesRoot);
|
|
42
43
|
if (bound != null && bound !== selected) {
|
|
43
44
|
throw new UsageError(
|
|
44
45
|
`This checkout belongs to account profile ${bound}; --account ${selected} cannot override it.`
|
|
45
46
|
);
|
|
46
47
|
}
|
|
48
|
+
if (bound == null) await bindIfCheckout(root, selected);
|
|
47
49
|
return selected;
|
|
48
50
|
}
|
|
49
51
|
if (bound != null) return bound;
|
|
50
|
-
|
|
52
|
+
|
|
53
|
+
const profiles = await listProfiles(profilesRoot == null ? {} : { root: profilesRoot });
|
|
54
|
+
if (profiles.length === 0) {
|
|
55
|
+
throw new UsageError('No account profile exists. Run `npx --yes @rathnasgala/cli@latest auth add`.');
|
|
56
|
+
}
|
|
57
|
+
const publication = await readPublication(root);
|
|
58
|
+
const repositoryOwner = typeof publication?.repository === 'string'
|
|
59
|
+
? publication.repository.split('/', 1)[0]?.toLowerCase()
|
|
60
|
+
: null;
|
|
61
|
+
const ownerMatches = repositoryOwner == null ? [] : profiles.filter((profile) =>
|
|
62
|
+
profile.githubLogin.toLowerCase() === repositoryOwner);
|
|
63
|
+
let selected;
|
|
64
|
+
if (ownerMatches.length === 1) selected = ownerMatches[0].name;
|
|
65
|
+
else if (profiles.length === 1) selected = profiles[0].name;
|
|
66
|
+
else {
|
|
67
|
+
if (!terminal?.interactive) {
|
|
68
|
+
throw new UsageError(
|
|
69
|
+
`This repository could use more than one account profile (${profileChoices(profiles)}); pass --account <profile>.`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
const answer = await terminal.ask(`Which account owns this repository? (${profileChoices(profiles)})`);
|
|
73
|
+
selected = resolveFromProfiles(answer, profiles);
|
|
74
|
+
}
|
|
75
|
+
await bindIfCheckout(root, selected);
|
|
76
|
+
terminal?.note(`Using account profile ${selected} for this repository.`);
|
|
77
|
+
return selected;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function resolveProfileSelector(value, profilesRoot) {
|
|
81
|
+
const profiles = await listProfiles(profilesRoot == null ? {} : { root: profilesRoot });
|
|
82
|
+
return resolveFromProfiles(value, profiles);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resolveFromProfiles(value, profiles) {
|
|
86
|
+
const selector = String(value ?? '').trim().replace(/^@/, '').toLowerCase();
|
|
87
|
+
const matches = profiles.filter(({ name, githubLogin, gala: identity }) => name === selector
|
|
88
|
+
|| githubLogin.toLowerCase() === selector
|
|
89
|
+
|| identity.email.toLowerCase() === selector);
|
|
90
|
+
if (matches.length === 1) return matches[0].name;
|
|
91
|
+
if (matches.length > 1) {
|
|
92
|
+
throw new UsageError(`Account ${value} matches more than one profile; use one of: ${profileChoices(matches)}.`);
|
|
93
|
+
}
|
|
94
|
+
throw new UsageError(`Account ${value} is not configured; use one of: ${profileChoices(profiles)}.`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function profileChoices(profiles) {
|
|
98
|
+
return profiles.map(({ name, githubLogin, gala: identity }) =>
|
|
99
|
+
`${name} (GitHub @${githubLogin}, Gala ${identity.email})`).join(', ');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function bindIfCheckout(root, selected) {
|
|
103
|
+
const metadata = await stat(path.join(path.resolve(root), '.git')).catch(() => null);
|
|
104
|
+
if (metadata?.isDirectory()) await bindCheckoutProfile(root, selected);
|
|
51
105
|
}
|
package/src/auth/profiles.js
CHANGED
|
@@ -145,7 +145,9 @@ async function readProfile(name, { root }) {
|
|
|
145
145
|
}
|
|
146
146
|
const [gala, github] = await Promise.all([readCredential(paths.gala), readCredential(paths.github)]);
|
|
147
147
|
if (gala == null || github == null) {
|
|
148
|
-
throw new UsageError(
|
|
148
|
+
throw new UsageError(
|
|
149
|
+
`Account profile ${name} has expired; run \`npx --yes @rathnasgala/cli@latest auth add\` to sign in again.`
|
|
150
|
+
);
|
|
149
151
|
}
|
|
150
152
|
return { metadata, gala, github };
|
|
151
153
|
}
|
package/src/commands/doctor.js
CHANGED
|
@@ -21,7 +21,7 @@ export async function doctor({ terminal, options, cwd = process.cwd() }) {
|
|
|
21
21
|
|
|
22
22
|
let selected;
|
|
23
23
|
checks.push(await checkCredential('Account profile', async () => {
|
|
24
|
-
const account = await accountForCommand(options, root);
|
|
24
|
+
const account = await accountForCommand(options, root, { terminal });
|
|
25
25
|
selected = await selectedProfile({ name: account });
|
|
26
26
|
return ok(`${account}: Gala ${selected.metadata.gala.email} + GitHub @${selected.metadata.githubLogin}`);
|
|
27
27
|
}));
|
package/src/commands/domain.js
CHANGED
|
@@ -23,7 +23,7 @@ export async function domain({ terminal, options, cwd = process.cwd() }) {
|
|
|
23
23
|
if (action === 'set' && value == null) throw new UsageError('domain set needs a hostname');
|
|
24
24
|
if (action !== 'set' && value != null) throw new UsageError(`domain ${action} takes no hostname`);
|
|
25
25
|
|
|
26
|
-
const account = await accountForCommand(options, root);
|
|
26
|
+
const account = await accountForCommand(options, root, { terminal });
|
|
27
27
|
const credential = (await selectedProfile({ name: account })).gala;
|
|
28
28
|
const api = galaApi({ baseUrl: credential.apiBaseUrl, token: credential.accessToken });
|
|
29
29
|
|
package/src/commands/prism.js
CHANGED
|
@@ -22,7 +22,7 @@ export async function prism({ terminal, options, cwd = process.cwd() }) {
|
|
|
22
22
|
if (!publication || !ULID.test(publication.siteId ?? '')) {
|
|
23
23
|
throw new UsageError('Run this inside a registered Gala publication, or pass --root.');
|
|
24
24
|
}
|
|
25
|
-
const account = await accountForCommand(options, root);
|
|
25
|
+
const account = await accountForCommand(options, root, { terminal });
|
|
26
26
|
const credential = (await selectedProfile({ name: account })).gala;
|
|
27
27
|
const api = galaApi({ baseUrl: credential.apiBaseUrl, token: credential.accessToken });
|
|
28
28
|
const [action = 'status', ...args] = options.positional;
|
package/src/commands/publish.js
CHANGED
|
@@ -26,7 +26,7 @@ export async function publish({ terminal, options, cwd = process.cwd(), regenera
|
|
|
26
26
|
const root = path.resolve(options.value('root') ?? cwd);
|
|
27
27
|
const today = options.value('today');
|
|
28
28
|
|
|
29
|
-
const account = await accountForCommand(options, root);
|
|
29
|
+
const account = await accountForCommand(options, root, { terminal });
|
|
30
30
|
const github = (await selectedProfile({ name: account })).github;
|
|
31
31
|
const git = createGit({ root, token: github.accessToken });
|
|
32
32
|
|