create-op-node 0.10.15 → 0.10.17
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/dist/cli.js +61 -35
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import pc2 from 'picocolors';
|
|
|
4
4
|
import * as p3 from '@clack/prompts';
|
|
5
5
|
import { execa } from 'execa';
|
|
6
6
|
import { Octokit } from '@octokit/rest';
|
|
7
|
+
import _sodium from 'libsodium-wrappers';
|
|
7
8
|
import { randomBytes, createHmac } from 'crypto';
|
|
8
9
|
import { join, resolve, dirname } from 'path';
|
|
9
10
|
import { mkdir, writeFile, access, chmod, rename, rm } from 'fs/promises';
|
|
@@ -443,25 +444,54 @@ async function createRepoFromTemplate(input) {
|
|
|
443
444
|
defaultBranch: res.data.default_branch ?? "main"
|
|
444
445
|
};
|
|
445
446
|
}
|
|
446
|
-
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
447
|
+
function parseRepoSlug(repo) {
|
|
448
|
+
const slash = repo.indexOf("/");
|
|
449
|
+
if (slash <= 0 || slash === repo.length - 1 || repo.indexOf("/", slash + 1) !== -1) return null;
|
|
450
|
+
return { owner: repo.slice(0, slash), repo: repo.slice(slash + 1) };
|
|
451
|
+
}
|
|
452
|
+
async function setRepoSecrets(input) {
|
|
453
|
+
const firstName = input.secrets[0]?.name ?? "(none)";
|
|
454
|
+
const parsed = parseRepoSlug(input.repo);
|
|
455
|
+
if (!parsed) {
|
|
453
456
|
return {
|
|
454
|
-
|
|
455
|
-
|
|
457
|
+
seeded: [],
|
|
458
|
+
failed: { name: firstName, reason: `invalid repo "${input.repo}" (expected <owner>/<repo>)` }
|
|
456
459
|
};
|
|
457
460
|
}
|
|
458
|
-
|
|
461
|
+
const { owner, repo } = parsed;
|
|
462
|
+
const octokit = client(input.token);
|
|
463
|
+
let key;
|
|
464
|
+
try {
|
|
465
|
+
key = await octokit.actions.getRepoPublicKey({ owner, repo });
|
|
466
|
+
} catch (err) {
|
|
459
467
|
return {
|
|
460
|
-
|
|
461
|
-
reason: `
|
|
468
|
+
seeded: [],
|
|
469
|
+
failed: { name: firstName, reason: `fetching the repo public key failed: ${err.message}` }
|
|
462
470
|
};
|
|
463
471
|
}
|
|
464
|
-
|
|
472
|
+
await _sodium.ready;
|
|
473
|
+
const sodium = _sodium;
|
|
474
|
+
const publicKey = sodium.from_base64(key.data.key, sodium.base64_variants.ORIGINAL);
|
|
475
|
+
const seeded = [];
|
|
476
|
+
for (const s of input.secrets) {
|
|
477
|
+
const encrypted_value = sodium.to_base64(
|
|
478
|
+
sodium.crypto_box_seal(sodium.from_string(s.value), publicKey),
|
|
479
|
+
sodium.base64_variants.ORIGINAL
|
|
480
|
+
);
|
|
481
|
+
try {
|
|
482
|
+
await octokit.actions.createOrUpdateRepoSecret({
|
|
483
|
+
owner,
|
|
484
|
+
repo,
|
|
485
|
+
secret_name: s.name,
|
|
486
|
+
encrypted_value,
|
|
487
|
+
key_id: key.data.key_id
|
|
488
|
+
});
|
|
489
|
+
} catch (err) {
|
|
490
|
+
return { seeded, failed: { name: s.name, reason: `setting secret ${s.name} failed: ${err.message}` } };
|
|
491
|
+
}
|
|
492
|
+
seeded.push(s.name);
|
|
493
|
+
}
|
|
494
|
+
return { seeded };
|
|
465
495
|
}
|
|
466
496
|
async function commitFile(input) {
|
|
467
497
|
const [owner, repo] = input.repo.split("/");
|
|
@@ -728,7 +758,7 @@ var initCommand = new Command("init").description(
|
|
|
728
758
|
await confirmPlan({ opts, region, owner, newRepoFull, publicConfig });
|
|
729
759
|
const created = await createRegionRepo({ opts, ghToken, owner, newRepoName, newRepoFull, region });
|
|
730
760
|
if (publicConfig) {
|
|
731
|
-
await seedRepoSecrets({ publicConfig, newRepoFull });
|
|
761
|
+
await seedRepoSecrets({ ghToken, publicConfig, newRepoFull });
|
|
732
762
|
}
|
|
733
763
|
const pr = publicConfig ? await openInfraPr({ opts, ghToken, region, newRepoFull, publicConfig, created }) : null;
|
|
734
764
|
await persistPgsodiumKey({ keychain, opts, region });
|
|
@@ -889,7 +919,7 @@ async function createRegionRepo(args) {
|
|
|
889
919
|
}
|
|
890
920
|
}
|
|
891
921
|
async function seedRepoSecrets(args) {
|
|
892
|
-
const { publicConfig, newRepoFull } = args;
|
|
922
|
+
const { ghToken, publicConfig, newRepoFull } = args;
|
|
893
923
|
const secrets = [
|
|
894
924
|
{ name: "CLOUDFLARE_API_TOKEN", value: publicConfig.cfToken },
|
|
895
925
|
{ name: "CLOUDFLARE_ACCOUNT_ID", value: publicConfig.cfAccount },
|
|
@@ -899,24 +929,20 @@ async function seedRepoSecrets(args) {
|
|
|
899
929
|
];
|
|
900
930
|
const secSpin = p3.spinner();
|
|
901
931
|
secSpin.start(`Seeding ${secrets.length} repo secrets\u2026`);
|
|
902
|
-
const seeded =
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
);
|
|
917
|
-
process.exit(1);
|
|
918
|
-
}
|
|
919
|
-
seeded.push(s.name);
|
|
932
|
+
const { seeded, failed } = await setRepoSecrets({ token: ghToken, repo: newRepoFull, secrets });
|
|
933
|
+
if (failed) {
|
|
934
|
+
secSpin.stop(pc2.red(`\u2717 Failed to set ${failed.name}: ${failed.reason}`));
|
|
935
|
+
const remaining = secrets.slice(seeded.length).map((x) => x.name);
|
|
936
|
+
p3.cancel(
|
|
937
|
+
[
|
|
938
|
+
seeded.length > 0 ? `Already seeded on ${newRepoFull}: ${seeded.join(", ")}.` : `Nothing seeded yet on ${newRepoFull}.`,
|
|
939
|
+
`Still pending: ${remaining.join(", ")}.`,
|
|
940
|
+
"",
|
|
941
|
+
`Make sure your --gh-token PAT has "Actions Secrets: write" (fine-grained) or \`repo\` scope (classic).`,
|
|
942
|
+
`Re-run with --use-existing-repo to retry (GitHub will overwrite the already-set secrets idempotently).`
|
|
943
|
+
].join("\n")
|
|
944
|
+
);
|
|
945
|
+
process.exit(1);
|
|
920
946
|
}
|
|
921
947
|
secSpin.stop(pc2.green(`\u2713 Seeded ${secrets.length} secrets on ${newRepoFull}`));
|
|
922
948
|
}
|
|
@@ -3331,7 +3357,7 @@ function reportResetOutcome(args) {
|
|
|
3331
3357
|
|
|
3332
3358
|
// src/lib/cosign.ts
|
|
3333
3359
|
var COSIGN_OIDC_ISSUER = "https://token.actions.githubusercontent.com";
|
|
3334
|
-
var DEFAULT_IDENTITY_REGEXP = "^https://github\\.com/OpusPopuli/opuspopuli/\\.github/workflows/.*$";
|
|
3360
|
+
var DEFAULT_IDENTITY_REGEXP = "^https://github\\.com/OpusPopuli/opuspopuli/\\.github/workflows/release\\.yml@refs/heads/.*$";
|
|
3335
3361
|
async function cosignVerifyImage(input) {
|
|
3336
3362
|
const idRegex = input.certificateIdentityRegexp ?? DEFAULT_IDENTITY_REGEXP;
|
|
3337
3363
|
const issuer = input.certificateOidcIssuer ?? COSIGN_OIDC_ISSUER;
|
|
@@ -4860,7 +4886,7 @@ async function looksLikeRegionsRepo(dir) {
|
|
|
4860
4886
|
}
|
|
4861
4887
|
|
|
4862
4888
|
// src/cli.ts
|
|
4863
|
-
var VERSION = "0.10.
|
|
4889
|
+
var VERSION = "0.10.17";
|
|
4864
4890
|
var program = new Command();
|
|
4865
4891
|
program.name("create-op-node").description(
|
|
4866
4892
|
"Interactive bootstrap for an Opus Populi federation node.\nCloudflare infrastructure \u2192 Mac Studio \u2192 live public API."
|