@rathnasgala/cli 0.0.19 → 0.0.21
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 +1 -1
- package/src/index.js +3 -0
- package/src/publication-creation-client.js +81 -15
- package/src/scaffold-site.js +3 -1
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -130,6 +130,9 @@ if (command === 'auth') {
|
|
|
130
130
|
ask
|
|
131
131
|
});
|
|
132
132
|
const result = await scaffoldSite({
|
|
133
|
+
notify: (message) => process.stdout.write(`${message}\n`),
|
|
134
|
+
ask,
|
|
135
|
+
openUrl: openInBrowser,
|
|
133
136
|
owner: prepared.owner,
|
|
134
137
|
repository: prepared.repository,
|
|
135
138
|
target: prepared.target,
|
|
@@ -22,7 +22,73 @@ export async function createPublication({
|
|
|
22
22
|
githubAccessToken,
|
|
23
23
|
name,
|
|
24
24
|
fetchImpl = fetch,
|
|
25
|
-
authorize = exchangeGithubAuthorization
|
|
25
|
+
authorize = exchangeGithubAuthorization,
|
|
26
|
+
notify = () => {},
|
|
27
|
+
ask,
|
|
28
|
+
openUrl = () => false,
|
|
29
|
+
shareAttempts = 3,
|
|
30
|
+
selfLogin
|
|
31
|
+
}) {
|
|
32
|
+
let created = false;
|
|
33
|
+
let shareUrl = 'https://github.com/settings/installations';
|
|
34
|
+
let repositoryOwner = selfLogin ?? '';
|
|
35
|
+
let repositoryName = name;
|
|
36
|
+
for (let attempt = 0; attempt < Math.max(1, shareAttempts); attempt += 1) {
|
|
37
|
+
const result = await requestPublication({
|
|
38
|
+
apiBaseUrl, galaAccessToken, githubAccessToken, name, fetchImpl, authorize
|
|
39
|
+
});
|
|
40
|
+
if (result.ready) return result.publication;
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* The repository exists with the right content; the App installation simply cannot see it,
|
|
44
|
+
* because it is scoped to selected repositories rather than all of them. Sharing it is a click,
|
|
45
|
+
* and asking again then returns READY — the server short-circuits on a repository it can
|
|
46
|
+
* already see. The browser editor recovers the same way; without this the CLI dead-ends on a
|
|
47
|
+
* state that is one click from working.
|
|
48
|
+
*
|
|
49
|
+
* After the first attempt the repository exists, so a further refusal reports UNSUPPORTED
|
|
50
|
+
* rather than NEEDS_SHARING — the same situation under a different name.
|
|
51
|
+
*/
|
|
52
|
+
const shareable = result.status === 'NEEDS_SHARING' || created;
|
|
53
|
+
if (result.status === 'NEEDS_SHARING') {
|
|
54
|
+
created = true;
|
|
55
|
+
shareUrl = installationSettingsUrl(result.installationId, result.owner, selfLogin);
|
|
56
|
+
repositoryName = result.repository ?? repositoryName;
|
|
57
|
+
repositoryOwner = result.owner ?? repositoryOwner;
|
|
58
|
+
}
|
|
59
|
+
if (!shareable || typeof ask !== 'function') throw result.failure;
|
|
60
|
+
|
|
61
|
+
notify(`${repositoryOwner}/${repositoryName} was created, but the Gala GitHub App cannot reach `
|
|
62
|
+
+ 'it yet — its installation covers only selected repositories, which is the right way to '
|
|
63
|
+
+ 'have it. Add this one repository to the installation; nothing else needs granting.');
|
|
64
|
+
notify(`${openUrl(shareUrl) ? 'Opened' : 'Open'} ${shareUrl}`);
|
|
65
|
+
await ask('Press enter once the App can access that repository. ');
|
|
66
|
+
}
|
|
67
|
+
throw new Error(
|
|
68
|
+
`The Gala GitHub App still cannot reach ${repositoryOwner}/${repositoryName}. Add that `
|
|
69
|
+
+ `repository to the installation at ${shareUrl}, then run scaffold again.`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The page that grants one repository, rather than the list of every app ever installed.
|
|
75
|
+
*
|
|
76
|
+
* GitHub keeps user and organisation installation settings on different paths, and only the caller
|
|
77
|
+
* knows which this is: the created owner differing from the token's own account means the
|
|
78
|
+
* installation lives on an organisation.
|
|
79
|
+
*/
|
|
80
|
+
export function installationSettingsUrl(installationId, owner, selfLogin) {
|
|
81
|
+
const generic = 'https://github.com/settings/installations';
|
|
82
|
+
if (!Number.isSafeInteger(Number(installationId)) || Number(installationId) <= 0) return generic;
|
|
83
|
+
const isOrganization = typeof owner === 'string' && typeof selfLogin === 'string'
|
|
84
|
+
&& owner.toLowerCase() !== selfLogin.toLowerCase();
|
|
85
|
+
return isOrganization
|
|
86
|
+
? `https://github.com/organizations/${encodeURIComponent(owner)}/settings/installations/${installationId}`
|
|
87
|
+
: `https://github.com/settings/installations/${installationId}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function requestPublication({
|
|
91
|
+
apiBaseUrl, galaAccessToken, githubAccessToken, name, fetchImpl, authorize
|
|
26
92
|
}) {
|
|
27
93
|
const authorization = await authorize({
|
|
28
94
|
apiBaseUrl, galaAccessToken, githubAccessToken, fetchImpl
|
|
@@ -44,19 +110,19 @@ export async function createPublication({
|
|
|
44
110
|
const owner = payload?.owner;
|
|
45
111
|
const repository = payload?.name;
|
|
46
112
|
|
|
47
|
-
if (status === 'NEEDS_SHARING') {
|
|
48
|
-
throw new Error(
|
|
49
|
-
`${owner}/${repository} was created from the template, but the Gala GitHub App cannot reach `
|
|
50
|
-
+ 'it yet. Open https://github.com/settings/installations, give the App access to that '
|
|
51
|
-
+ 'repository, then run scaffold again with --resume.'
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
113
|
if (status !== 'READY') {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
114
|
+
return {
|
|
115
|
+
ready: false, status, owner, repository,
|
|
116
|
+
// Carried even though the repository is not in the installation yet: it is what makes the
|
|
117
|
+
// grant a deep link rather than a hunt.
|
|
118
|
+
installationId: payload?.installationId,
|
|
119
|
+
failure: new Error(
|
|
120
|
+
`Gala could not create the publication repository (${payload?.outcome ?? status}). `
|
|
121
|
+
+ 'Give the Gala GitHub App access to it at https://github.com/settings/installations, or '
|
|
122
|
+
+ 'create it from https://github.com/rathnasgala/site-template yourself and run scaffold '
|
|
123
|
+
+ 'with --empty-existing-repository.'
|
|
124
|
+
)
|
|
125
|
+
};
|
|
60
126
|
}
|
|
61
127
|
if (typeof owner !== 'string' || typeof repository !== 'string') {
|
|
62
128
|
throw new TypeError('Gala publication creation returned no repository identity');
|
|
@@ -66,7 +132,7 @@ export async function createPublication({
|
|
|
66
132
|
throw new TypeError('Gala publication creation returned no installation');
|
|
67
133
|
}
|
|
68
134
|
|
|
69
|
-
return Object.freeze({
|
|
135
|
+
return { ready: true, status, owner, repository, publication: Object.freeze({
|
|
70
136
|
owner,
|
|
71
137
|
repository,
|
|
72
138
|
installationId,
|
|
@@ -74,5 +140,5 @@ export async function createPublication({
|
|
|
74
140
|
// The server names the repository it actually made, which may differ from what was asked for.
|
|
75
141
|
fullName: `${owner}/${repository}`,
|
|
76
142
|
cloneUrl: `https://github.com/${owner}/${repository}.git`
|
|
77
|
-
});
|
|
143
|
+
}) };
|
|
78
144
|
}
|
package/src/scaffold-site.js
CHANGED
|
@@ -48,6 +48,7 @@ function registrationLocation(owner, topology, canonicalBaseUrl) {
|
|
|
48
48
|
|
|
49
49
|
export async function scaffoldSite({
|
|
50
50
|
owner, repository, target, githubInstallationId, siteOptions, emptyExistingRepository = false,
|
|
51
|
+
notify = (message) => process.stdout.write(`${message}\n`), ask, openUrl,
|
|
51
52
|
resumeExistingCheckout = false, topology = 'provider-default', canonicalBaseUrl, actionRef,
|
|
52
53
|
buildMode = 'build-and-deploy', templateOwner = 'rathnasgala',
|
|
53
54
|
templateRepository = 'site-template',
|
|
@@ -110,7 +111,8 @@ export async function scaffoldSite({
|
|
|
110
111
|
*/
|
|
111
112
|
const created = await createRepository({
|
|
112
113
|
apiBaseUrl: gala.apiBaseUrl, galaAccessToken: gala.accessToken,
|
|
113
|
-
githubAccessToken: github.accessToken, name: repositoryName
|
|
114
|
+
githubAccessToken: github.accessToken, name: repositoryName,
|
|
115
|
+
notify, ask, openUrl, selfLogin: requestedOwner
|
|
114
116
|
});
|
|
115
117
|
repositoryOwner = segment(created.owner, 'owner');
|
|
116
118
|
repositoryName = segment(created.repository, 'repository');
|