@el-j/google-sheet-translations 2.2.0-beta.4 → 2.2.0-beta.6
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/README.md +17 -0
- package/dist/esm/index.js +238 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +241 -0
- package/dist/setup/cli.d.ts +13 -0
- package/dist/setup/cli.d.ts.map +1 -0
- package/dist/setup/wifSetup.d.ts +95 -0
- package/dist/setup/wifSetup.d.ts.map +1 -0
- package/dist/utils/docIngester.d.ts.map +1 -1
- package/dist-cli/setup-wif.mjs +19007 -0
- package/package.json +32 -27
package/README.md
CHANGED
|
@@ -378,6 +378,23 @@ jobs:
|
|
|
378
378
|
| `spreadsheet-title` | ❌ | `google-sheet-translations` | Title for the auto-created spreadsheet |
|
|
379
379
|
| `source-locale` | ❌ | `en` | Source locale used as the base for auto-translate formulas |
|
|
380
380
|
| `target-locales` | ❌ | `de,fr,es,it,pt,ja,zh` | Comma-separated target locales added to the auto-created spreadsheet |
|
|
381
|
+
| `drive-folder-id` | ❌ | `''` | Drive folder ID used to auto-discover spreadsheets, docs, and images |
|
|
382
|
+
| `scan-for-spreadsheets` | ❌ | `true` | When `drive-folder-id` is set, scan the folder recursively for spreadsheets |
|
|
383
|
+
| `spreadsheet-ids` | ❌ | `''` | Optional extra spreadsheet IDs to merge with any IDs discovered in Drive |
|
|
384
|
+
| `sync-images` | ❌ | `false` | Download Drive images alongside translation sync |
|
|
385
|
+
| `image-output-path` | ❌ | `./public/remote-images` | Output directory for downloaded Drive images when `sync-images` is enabled |
|
|
386
|
+
|
|
387
|
+
### Drive Folder Mode
|
|
388
|
+
|
|
389
|
+
When `drive-folder-id` is set, the action switches from single-spreadsheet mode to `manageDriveTranslations()`. That enables recursive spreadsheet discovery, optional image sync, and optional explicit `spreadsheet-ids` on the same run.
|
|
390
|
+
|
|
391
|
+
Operational notes:
|
|
392
|
+
|
|
393
|
+
- If the Drive folder is empty and `auto-create` remains enabled, the package creates a spreadsheet and then tries to move it into the target folder.
|
|
394
|
+
- `spreadsheetNameFilter` only applies to discovered sheets in the programmatic API. Explicit `spreadsheet-ids` are never filtered out.
|
|
395
|
+
- `sync-images` supports incremental freshness checks and optional `cleanSync` deletion in the programmatic API. See the Drive Folder guide for those semantics before enabling destructive cleanup.
|
|
396
|
+
|
|
397
|
+
See the full Drive Folder guide in `website/guide/drive-folder.md` for the complete API surface, image sync semantics, and Google Doc ingestion details.
|
|
381
398
|
|
|
382
399
|
### Outputs
|
|
383
400
|
|
package/dist/esm/index.js
CHANGED
|
@@ -2070,6 +2070,9 @@ function entriesToSeedKeys(entries) {
|
|
|
2070
2070
|
return keys;
|
|
2071
2071
|
}
|
|
2072
2072
|
function entriesToTranslationData(entries, locale) {
|
|
2073
|
+
if (locale === "__proto__" || locale === "constructor" || locale === "prototype") {
|
|
2074
|
+
return {};
|
|
2075
|
+
}
|
|
2073
2076
|
const data = {};
|
|
2074
2077
|
data[locale] = {};
|
|
2075
2078
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -2398,11 +2401,244 @@ async function manageDriveTranslations(options) {
|
|
|
2398
2401
|
return { translations, spreadsheetIds: filteredIds, imageSync, manifest, docIngestResults };
|
|
2399
2402
|
}
|
|
2400
2403
|
|
|
2404
|
+
// src/setup/wifSetup.ts
|
|
2405
|
+
import { GoogleAuth as GoogleAuth4 } from "google-auth-library";
|
|
2406
|
+
var GcpApiError = class extends Error {
|
|
2407
|
+
constructor(message, status) {
|
|
2408
|
+
super(message);
|
|
2409
|
+
this.status = status;
|
|
2410
|
+
this.name = "GcpApiError";
|
|
2411
|
+
}
|
|
2412
|
+
};
|
|
2413
|
+
async function getAccessToken4(keyFilePath) {
|
|
2414
|
+
const auth = new GoogleAuth4({
|
|
2415
|
+
...keyFilePath ? { keyFilename: keyFilePath } : {},
|
|
2416
|
+
scopes: ["https://www.googleapis.com/auth/cloud-platform"]
|
|
2417
|
+
});
|
|
2418
|
+
const client = await auth.getClient();
|
|
2419
|
+
const tokenResponse = await client.getAccessToken();
|
|
2420
|
+
if (!tokenResponse.token) {
|
|
2421
|
+
throw new Error(
|
|
2422
|
+
"Failed to obtain a Google Cloud access token. Ensure you are authenticated via Application Default Credentials (run: gcloud auth application-default login) or provide --key-file pointing to a service account JSON key."
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
return tokenResponse.token;
|
|
2426
|
+
}
|
|
2427
|
+
async function gcpFetch(url, token, method = "GET", body) {
|
|
2428
|
+
const response = await fetch(url, {
|
|
2429
|
+
method,
|
|
2430
|
+
headers: {
|
|
2431
|
+
Authorization: `Bearer ${token}`,
|
|
2432
|
+
"Content-Type": "application/json"
|
|
2433
|
+
},
|
|
2434
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
2435
|
+
});
|
|
2436
|
+
const data = await response.json();
|
|
2437
|
+
if (!response.ok) {
|
|
2438
|
+
const errData = data;
|
|
2439
|
+
const message = errData.error?.message ?? `HTTP ${response.status}`;
|
|
2440
|
+
throw new GcpApiError(message, response.status);
|
|
2441
|
+
}
|
|
2442
|
+
return data;
|
|
2443
|
+
}
|
|
2444
|
+
async function pollOperation(operationName, token, maxWaitMs = 6e4) {
|
|
2445
|
+
let opUrl;
|
|
2446
|
+
if (operationName.startsWith("http")) {
|
|
2447
|
+
let isGoogleHost;
|
|
2448
|
+
try {
|
|
2449
|
+
const parsedUrl = new URL(operationName);
|
|
2450
|
+
const hostname = (parsedUrl.hostname || "").toLowerCase();
|
|
2451
|
+
isGoogleHost = hostname === "iam.googleapis.com" || hostname.endsWith(".iam.googleapis.com") || hostname === "googleapis.com" || hostname.endsWith(".googleapis.com");
|
|
2452
|
+
} catch {
|
|
2453
|
+
isGoogleHost = false;
|
|
2454
|
+
}
|
|
2455
|
+
if (!isGoogleHost) {
|
|
2456
|
+
throw new Error(
|
|
2457
|
+
`Invalid operation URL: hostname must be a Google API endpoint (*.googleapis.com), got: ${operationName}`
|
|
2458
|
+
);
|
|
2459
|
+
}
|
|
2460
|
+
opUrl = operationName;
|
|
2461
|
+
} else {
|
|
2462
|
+
opUrl = `https://iam.googleapis.com/v1/${operationName}`;
|
|
2463
|
+
}
|
|
2464
|
+
const deadline = Date.now() + maxWaitMs;
|
|
2465
|
+
const maxWaitSecs = Math.round(maxWaitMs / 1e3);
|
|
2466
|
+
while (Date.now() < deadline) {
|
|
2467
|
+
const op = await gcpFetch(opUrl, token);
|
|
2468
|
+
if (op.done) {
|
|
2469
|
+
if (op.error) {
|
|
2470
|
+
throw new Error(`Operation failed: ${op.error.message}`);
|
|
2471
|
+
}
|
|
2472
|
+
return;
|
|
2473
|
+
}
|
|
2474
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
2475
|
+
}
|
|
2476
|
+
if (Date.now() >= deadline) {
|
|
2477
|
+
throw new Error(
|
|
2478
|
+
`Operation timed out after ${maxWaitSecs} s. The resources may still be provisioning in the background \u2013 re-running the command is safe (existing resources are reused).`
|
|
2479
|
+
);
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
async function getProjectNumber(projectId, token) {
|
|
2483
|
+
const data = await gcpFetch(
|
|
2484
|
+
`https://cloudresourcemanager.googleapis.com/v1/projects/${encodeURIComponent(projectId)}`,
|
|
2485
|
+
token
|
|
2486
|
+
);
|
|
2487
|
+
return data.projectNumber;
|
|
2488
|
+
}
|
|
2489
|
+
async function createOrGetWifPool(projectId, poolId, token) {
|
|
2490
|
+
try {
|
|
2491
|
+
const op = await gcpFetch(
|
|
2492
|
+
`https://iam.googleapis.com/v1/projects/${encodeURIComponent(projectId)}/locations/global/workloadIdentityPools?workloadIdentityPoolId=${encodeURIComponent(poolId)}`,
|
|
2493
|
+
token,
|
|
2494
|
+
"POST",
|
|
2495
|
+
{
|
|
2496
|
+
displayName: "GitHub Actions Pool",
|
|
2497
|
+
description: "Pool for GitHub Actions OIDC authentication",
|
|
2498
|
+
disabled: false
|
|
2499
|
+
}
|
|
2500
|
+
);
|
|
2501
|
+
if (!op.done) {
|
|
2502
|
+
await pollOperation(op.name, token);
|
|
2503
|
+
} else if (op.error) {
|
|
2504
|
+
throw new Error(`Operation failed: ${op.error.message}`);
|
|
2505
|
+
}
|
|
2506
|
+
} catch (err) {
|
|
2507
|
+
if (err instanceof GcpApiError && err.status === 409) {
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
throw err;
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
async function createOrGetWifProvider(projectId, poolId, providerId, githubRepo, token) {
|
|
2514
|
+
try {
|
|
2515
|
+
const op = await gcpFetch(
|
|
2516
|
+
`https://iam.googleapis.com/v1/projects/${encodeURIComponent(projectId)}/locations/global/workloadIdentityPools/${encodeURIComponent(poolId)}/providers?workloadIdentityPoolProviderId=${encodeURIComponent(providerId)}`,
|
|
2517
|
+
token,
|
|
2518
|
+
"POST",
|
|
2519
|
+
{
|
|
2520
|
+
displayName: "GitHub OIDC Provider",
|
|
2521
|
+
disabled: false,
|
|
2522
|
+
attributeMapping: {
|
|
2523
|
+
"google.subject": "assertion.sub",
|
|
2524
|
+
"attribute.actor": "assertion.actor",
|
|
2525
|
+
"attribute.repository": "assertion.repository"
|
|
2526
|
+
},
|
|
2527
|
+
// Scope the provider to this exact repository for security
|
|
2528
|
+
attributeCondition: `assertion.repository=='${githubRepo}'`,
|
|
2529
|
+
oidc: {
|
|
2530
|
+
issuerUri: "https://token.actions.githubusercontent.com"
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
);
|
|
2534
|
+
if (!op.done) {
|
|
2535
|
+
await pollOperation(op.name, token);
|
|
2536
|
+
} else if (op.error) {
|
|
2537
|
+
throw new Error(`Operation failed: ${op.error.message}`);
|
|
2538
|
+
}
|
|
2539
|
+
} catch (err) {
|
|
2540
|
+
if (err instanceof GcpApiError && err.status === 409) {
|
|
2541
|
+
return;
|
|
2542
|
+
}
|
|
2543
|
+
throw err;
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
async function bindServiceAccount(projectId, serviceAccountEmail, projectNumber, poolId, githubRepo, token) {
|
|
2547
|
+
const saResource = `projects/${encodeURIComponent(projectId)}/serviceAccounts/${encodeURIComponent(serviceAccountEmail)}`;
|
|
2548
|
+
const principal = `principalSet://iam.googleapis.com/projects/${projectNumber}/locations/global/workloadIdentityPools/${poolId}/attribute.repository/${githubRepo}`;
|
|
2549
|
+
const policy = await gcpFetch(
|
|
2550
|
+
`https://iam.googleapis.com/v1/${saResource}:getIamPolicy`,
|
|
2551
|
+
token,
|
|
2552
|
+
"POST",
|
|
2553
|
+
{}
|
|
2554
|
+
);
|
|
2555
|
+
const bindings = policy.bindings ?? [];
|
|
2556
|
+
const role = "roles/iam.workloadIdentityUser";
|
|
2557
|
+
const existing = bindings.find((b) => b.role === role);
|
|
2558
|
+
if (existing) {
|
|
2559
|
+
if (existing.members.includes(principal)) {
|
|
2560
|
+
return;
|
|
2561
|
+
}
|
|
2562
|
+
existing.members.push(principal);
|
|
2563
|
+
} else {
|
|
2564
|
+
bindings.push({ role, members: [principal] });
|
|
2565
|
+
}
|
|
2566
|
+
await gcpFetch(
|
|
2567
|
+
`https://iam.googleapis.com/v1/${saResource}:setIamPolicy`,
|
|
2568
|
+
token,
|
|
2569
|
+
"POST",
|
|
2570
|
+
{ policy: { ...policy, bindings } }
|
|
2571
|
+
);
|
|
2572
|
+
}
|
|
2573
|
+
async function setupWIF(options) {
|
|
2574
|
+
const poolId = options.poolId ?? "github-actions";
|
|
2575
|
+
const providerId = options.providerId ?? "github-oidc";
|
|
2576
|
+
const log = options.onProgress ?? (() => void 0);
|
|
2577
|
+
if (!options.githubRepo.includes("/")) {
|
|
2578
|
+
throw new Error(
|
|
2579
|
+
`githubRepo must be in "owner/repo" format, got: "${options.githubRepo}"`
|
|
2580
|
+
);
|
|
2581
|
+
}
|
|
2582
|
+
log("Authenticating with Google Cloud...");
|
|
2583
|
+
const token = await getAccessToken4(options.keyFilePath);
|
|
2584
|
+
log("Fetching project number...");
|
|
2585
|
+
const projectNumber = await getProjectNumber(options.projectId, token);
|
|
2586
|
+
log(`Creating Workload Identity Pool "${poolId}"...`);
|
|
2587
|
+
await createOrGetWifPool(options.projectId, poolId, token);
|
|
2588
|
+
log(`Creating OIDC Provider "${providerId}"...`);
|
|
2589
|
+
await createOrGetWifProvider(
|
|
2590
|
+
options.projectId,
|
|
2591
|
+
poolId,
|
|
2592
|
+
providerId,
|
|
2593
|
+
options.githubRepo,
|
|
2594
|
+
token
|
|
2595
|
+
);
|
|
2596
|
+
log("Binding service account permissions...");
|
|
2597
|
+
await bindServiceAccount(
|
|
2598
|
+
options.projectId,
|
|
2599
|
+
options.serviceAccountEmail,
|
|
2600
|
+
projectNumber,
|
|
2601
|
+
poolId,
|
|
2602
|
+
options.githubRepo,
|
|
2603
|
+
token
|
|
2604
|
+
);
|
|
2605
|
+
const wifProvider = `projects/${projectNumber}/locations/global/workloadIdentityPools/${poolId}/providers/${providerId}`;
|
|
2606
|
+
return { wifProvider, projectNumber, poolId, providerId };
|
|
2607
|
+
}
|
|
2608
|
+
async function grantDrivePermissions(options) {
|
|
2609
|
+
const token = await getAccessToken4(options.keyFilePath);
|
|
2610
|
+
const policy = await gcpFetch(
|
|
2611
|
+
`https://cloudresourcemanager.googleapis.com/v1/projects/${encodeURIComponent(options.projectId)}:getIamPolicy`,
|
|
2612
|
+
token,
|
|
2613
|
+
"POST",
|
|
2614
|
+
{}
|
|
2615
|
+
);
|
|
2616
|
+
const bindings = policy.bindings ?? [];
|
|
2617
|
+
const role = "roles/drive.file";
|
|
2618
|
+
const member = `serviceAccount:${options.serviceAccountEmail}`;
|
|
2619
|
+
const existing = bindings.find((b) => b.role === role);
|
|
2620
|
+
if (existing) {
|
|
2621
|
+
if (existing.members.includes(member)) {
|
|
2622
|
+
return;
|
|
2623
|
+
}
|
|
2624
|
+
existing.members.push(member);
|
|
2625
|
+
} else {
|
|
2626
|
+
bindings.push({ role, members: [member] });
|
|
2627
|
+
}
|
|
2628
|
+
await gcpFetch(
|
|
2629
|
+
`https://cloudresourcemanager.googleapis.com/v1/projects/${encodeURIComponent(options.projectId)}:setIamPolicy`,
|
|
2630
|
+
token,
|
|
2631
|
+
"POST",
|
|
2632
|
+
{ policy: { ...policy, bindings } }
|
|
2633
|
+
);
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2401
2636
|
// src/index.ts
|
|
2402
2637
|
var index_default = getSpreadSheetData;
|
|
2403
2638
|
export {
|
|
2404
2639
|
DEFAULT_IMAGE_EXTENSIONS,
|
|
2405
2640
|
DEFAULT_WAIT_SECONDS,
|
|
2641
|
+
GcpApiError,
|
|
2406
2642
|
buildGoogleAuth,
|
|
2407
2643
|
buildManifest,
|
|
2408
2644
|
convertFromDataJsonFormat,
|
|
@@ -2424,6 +2660,7 @@ export {
|
|
|
2424
2660
|
getOriginalHeaderForLocale,
|
|
2425
2661
|
getSpreadSheetData,
|
|
2426
2662
|
getTranslationSummary,
|
|
2663
|
+
grantDrivePermissions,
|
|
2427
2664
|
handleBidirectionalSync,
|
|
2428
2665
|
inferLocaleFromDocName,
|
|
2429
2666
|
ingestDoc,
|
|
@@ -2441,6 +2678,7 @@ export {
|
|
|
2441
2678
|
resolveLocaleWithFallback,
|
|
2442
2679
|
scanDriveFolderForDocs,
|
|
2443
2680
|
scanDriveFolderForSpreadsheets,
|
|
2681
|
+
setupWIF,
|
|
2444
2682
|
slugifyKey,
|
|
2445
2683
|
syncDriveImages,
|
|
2446
2684
|
updateSpreadsheetWithLocalChanges,
|
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ export { parseDocContent, slugifyKey } from './utils/docParser';
|
|
|
44
44
|
export type { ParsedDocEntry, DocKeyStrategy, ParseDocOptions } from './utils/docParser';
|
|
45
45
|
export { ingestDoc, exportDoc, entriesToSeedKeys, entriesToTranslationData } from './utils/docIngester';
|
|
46
46
|
export type { DocIngesterOptions, DocIngestResult, DocUpdateMode } from './utils/docIngester';
|
|
47
|
+
export { setupWIF, grantDrivePermissions, GcpApiError } from './setup/wifSetup';
|
|
48
|
+
export type { WifSetupOptions, WifSetupResult, GrantDrivePermissionsOptions } from './setup/wifSetup';
|
|
47
49
|
import { getSpreadSheetData } from './getSpreadSheetData';
|
|
48
50
|
export default getSpreadSheetData;
|
|
49
51
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
DEFAULT_IMAGE_EXTENSIONS: () => DEFAULT_IMAGE_EXTENSIONS,
|
|
34
34
|
DEFAULT_WAIT_SECONDS: () => DEFAULT_WAIT_SECONDS,
|
|
35
|
+
GcpApiError: () => GcpApiError,
|
|
35
36
|
buildGoogleAuth: () => buildGoogleAuth,
|
|
36
37
|
buildManifest: () => buildManifest,
|
|
37
38
|
convertFromDataJsonFormat: () => convertFromDataJsonFormat,
|
|
@@ -53,6 +54,7 @@ __export(index_exports, {
|
|
|
53
54
|
getOriginalHeaderForLocale: () => getOriginalHeaderForLocale,
|
|
54
55
|
getSpreadSheetData: () => getSpreadSheetData,
|
|
55
56
|
getTranslationSummary: () => getTranslationSummary,
|
|
57
|
+
grantDrivePermissions: () => grantDrivePermissions,
|
|
56
58
|
handleBidirectionalSync: () => handleBidirectionalSync,
|
|
57
59
|
inferLocaleFromDocName: () => inferLocaleFromDocName,
|
|
58
60
|
ingestDoc: () => ingestDoc,
|
|
@@ -70,6 +72,7 @@ __export(index_exports, {
|
|
|
70
72
|
resolveLocaleWithFallback: () => resolveLocaleWithFallback,
|
|
71
73
|
scanDriveFolderForDocs: () => scanDriveFolderForDocs,
|
|
72
74
|
scanDriveFolderForSpreadsheets: () => scanDriveFolderForSpreadsheets,
|
|
75
|
+
setupWIF: () => setupWIF,
|
|
73
76
|
slugifyKey: () => slugifyKey,
|
|
74
77
|
syncDriveImages: () => syncDriveImages,
|
|
75
78
|
updateSpreadsheetWithLocalChanges: () => updateSpreadsheetWithLocalChanges,
|
|
@@ -2158,6 +2161,9 @@ function entriesToSeedKeys(entries) {
|
|
|
2158
2161
|
return keys;
|
|
2159
2162
|
}
|
|
2160
2163
|
function entriesToTranslationData(entries, locale) {
|
|
2164
|
+
if (locale === "__proto__" || locale === "constructor" || locale === "prototype") {
|
|
2165
|
+
return {};
|
|
2166
|
+
}
|
|
2161
2167
|
const data = {};
|
|
2162
2168
|
data[locale] = {};
|
|
2163
2169
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -2486,12 +2492,245 @@ async function manageDriveTranslations(options) {
|
|
|
2486
2492
|
return { translations, spreadsheetIds: filteredIds, imageSync, manifest, docIngestResults };
|
|
2487
2493
|
}
|
|
2488
2494
|
|
|
2495
|
+
// src/setup/wifSetup.ts
|
|
2496
|
+
var import_google_auth_library4 = require("google-auth-library");
|
|
2497
|
+
var GcpApiError = class extends Error {
|
|
2498
|
+
constructor(message, status) {
|
|
2499
|
+
super(message);
|
|
2500
|
+
this.status = status;
|
|
2501
|
+
this.name = "GcpApiError";
|
|
2502
|
+
}
|
|
2503
|
+
};
|
|
2504
|
+
async function getAccessToken4(keyFilePath) {
|
|
2505
|
+
const auth = new import_google_auth_library4.GoogleAuth({
|
|
2506
|
+
...keyFilePath ? { keyFilename: keyFilePath } : {},
|
|
2507
|
+
scopes: ["https://www.googleapis.com/auth/cloud-platform"]
|
|
2508
|
+
});
|
|
2509
|
+
const client = await auth.getClient();
|
|
2510
|
+
const tokenResponse = await client.getAccessToken();
|
|
2511
|
+
if (!tokenResponse.token) {
|
|
2512
|
+
throw new Error(
|
|
2513
|
+
"Failed to obtain a Google Cloud access token. Ensure you are authenticated via Application Default Credentials (run: gcloud auth application-default login) or provide --key-file pointing to a service account JSON key."
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
2516
|
+
return tokenResponse.token;
|
|
2517
|
+
}
|
|
2518
|
+
async function gcpFetch(url, token, method = "GET", body) {
|
|
2519
|
+
const response = await fetch(url, {
|
|
2520
|
+
method,
|
|
2521
|
+
headers: {
|
|
2522
|
+
Authorization: `Bearer ${token}`,
|
|
2523
|
+
"Content-Type": "application/json"
|
|
2524
|
+
},
|
|
2525
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
2526
|
+
});
|
|
2527
|
+
const data = await response.json();
|
|
2528
|
+
if (!response.ok) {
|
|
2529
|
+
const errData = data;
|
|
2530
|
+
const message = errData.error?.message ?? `HTTP ${response.status}`;
|
|
2531
|
+
throw new GcpApiError(message, response.status);
|
|
2532
|
+
}
|
|
2533
|
+
return data;
|
|
2534
|
+
}
|
|
2535
|
+
async function pollOperation(operationName, token, maxWaitMs = 6e4) {
|
|
2536
|
+
let opUrl;
|
|
2537
|
+
if (operationName.startsWith("http")) {
|
|
2538
|
+
let isGoogleHost;
|
|
2539
|
+
try {
|
|
2540
|
+
const parsedUrl = new URL(operationName);
|
|
2541
|
+
const hostname = (parsedUrl.hostname || "").toLowerCase();
|
|
2542
|
+
isGoogleHost = hostname === "iam.googleapis.com" || hostname.endsWith(".iam.googleapis.com") || hostname === "googleapis.com" || hostname.endsWith(".googleapis.com");
|
|
2543
|
+
} catch {
|
|
2544
|
+
isGoogleHost = false;
|
|
2545
|
+
}
|
|
2546
|
+
if (!isGoogleHost) {
|
|
2547
|
+
throw new Error(
|
|
2548
|
+
`Invalid operation URL: hostname must be a Google API endpoint (*.googleapis.com), got: ${operationName}`
|
|
2549
|
+
);
|
|
2550
|
+
}
|
|
2551
|
+
opUrl = operationName;
|
|
2552
|
+
} else {
|
|
2553
|
+
opUrl = `https://iam.googleapis.com/v1/${operationName}`;
|
|
2554
|
+
}
|
|
2555
|
+
const deadline = Date.now() + maxWaitMs;
|
|
2556
|
+
const maxWaitSecs = Math.round(maxWaitMs / 1e3);
|
|
2557
|
+
while (Date.now() < deadline) {
|
|
2558
|
+
const op = await gcpFetch(opUrl, token);
|
|
2559
|
+
if (op.done) {
|
|
2560
|
+
if (op.error) {
|
|
2561
|
+
throw new Error(`Operation failed: ${op.error.message}`);
|
|
2562
|
+
}
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
2566
|
+
}
|
|
2567
|
+
if (Date.now() >= deadline) {
|
|
2568
|
+
throw new Error(
|
|
2569
|
+
`Operation timed out after ${maxWaitSecs} s. The resources may still be provisioning in the background \u2013 re-running the command is safe (existing resources are reused).`
|
|
2570
|
+
);
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
async function getProjectNumber(projectId, token) {
|
|
2574
|
+
const data = await gcpFetch(
|
|
2575
|
+
`https://cloudresourcemanager.googleapis.com/v1/projects/${encodeURIComponent(projectId)}`,
|
|
2576
|
+
token
|
|
2577
|
+
);
|
|
2578
|
+
return data.projectNumber;
|
|
2579
|
+
}
|
|
2580
|
+
async function createOrGetWifPool(projectId, poolId, token) {
|
|
2581
|
+
try {
|
|
2582
|
+
const op = await gcpFetch(
|
|
2583
|
+
`https://iam.googleapis.com/v1/projects/${encodeURIComponent(projectId)}/locations/global/workloadIdentityPools?workloadIdentityPoolId=${encodeURIComponent(poolId)}`,
|
|
2584
|
+
token,
|
|
2585
|
+
"POST",
|
|
2586
|
+
{
|
|
2587
|
+
displayName: "GitHub Actions Pool",
|
|
2588
|
+
description: "Pool for GitHub Actions OIDC authentication",
|
|
2589
|
+
disabled: false
|
|
2590
|
+
}
|
|
2591
|
+
);
|
|
2592
|
+
if (!op.done) {
|
|
2593
|
+
await pollOperation(op.name, token);
|
|
2594
|
+
} else if (op.error) {
|
|
2595
|
+
throw new Error(`Operation failed: ${op.error.message}`);
|
|
2596
|
+
}
|
|
2597
|
+
} catch (err) {
|
|
2598
|
+
if (err instanceof GcpApiError && err.status === 409) {
|
|
2599
|
+
return;
|
|
2600
|
+
}
|
|
2601
|
+
throw err;
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
async function createOrGetWifProvider(projectId, poolId, providerId, githubRepo, token) {
|
|
2605
|
+
try {
|
|
2606
|
+
const op = await gcpFetch(
|
|
2607
|
+
`https://iam.googleapis.com/v1/projects/${encodeURIComponent(projectId)}/locations/global/workloadIdentityPools/${encodeURIComponent(poolId)}/providers?workloadIdentityPoolProviderId=${encodeURIComponent(providerId)}`,
|
|
2608
|
+
token,
|
|
2609
|
+
"POST",
|
|
2610
|
+
{
|
|
2611
|
+
displayName: "GitHub OIDC Provider",
|
|
2612
|
+
disabled: false,
|
|
2613
|
+
attributeMapping: {
|
|
2614
|
+
"google.subject": "assertion.sub",
|
|
2615
|
+
"attribute.actor": "assertion.actor",
|
|
2616
|
+
"attribute.repository": "assertion.repository"
|
|
2617
|
+
},
|
|
2618
|
+
// Scope the provider to this exact repository for security
|
|
2619
|
+
attributeCondition: `assertion.repository=='${githubRepo}'`,
|
|
2620
|
+
oidc: {
|
|
2621
|
+
issuerUri: "https://token.actions.githubusercontent.com"
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
);
|
|
2625
|
+
if (!op.done) {
|
|
2626
|
+
await pollOperation(op.name, token);
|
|
2627
|
+
} else if (op.error) {
|
|
2628
|
+
throw new Error(`Operation failed: ${op.error.message}`);
|
|
2629
|
+
}
|
|
2630
|
+
} catch (err) {
|
|
2631
|
+
if (err instanceof GcpApiError && err.status === 409) {
|
|
2632
|
+
return;
|
|
2633
|
+
}
|
|
2634
|
+
throw err;
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
async function bindServiceAccount(projectId, serviceAccountEmail, projectNumber, poolId, githubRepo, token) {
|
|
2638
|
+
const saResource = `projects/${encodeURIComponent(projectId)}/serviceAccounts/${encodeURIComponent(serviceAccountEmail)}`;
|
|
2639
|
+
const principal = `principalSet://iam.googleapis.com/projects/${projectNumber}/locations/global/workloadIdentityPools/${poolId}/attribute.repository/${githubRepo}`;
|
|
2640
|
+
const policy = await gcpFetch(
|
|
2641
|
+
`https://iam.googleapis.com/v1/${saResource}:getIamPolicy`,
|
|
2642
|
+
token,
|
|
2643
|
+
"POST",
|
|
2644
|
+
{}
|
|
2645
|
+
);
|
|
2646
|
+
const bindings = policy.bindings ?? [];
|
|
2647
|
+
const role = "roles/iam.workloadIdentityUser";
|
|
2648
|
+
const existing = bindings.find((b) => b.role === role);
|
|
2649
|
+
if (existing) {
|
|
2650
|
+
if (existing.members.includes(principal)) {
|
|
2651
|
+
return;
|
|
2652
|
+
}
|
|
2653
|
+
existing.members.push(principal);
|
|
2654
|
+
} else {
|
|
2655
|
+
bindings.push({ role, members: [principal] });
|
|
2656
|
+
}
|
|
2657
|
+
await gcpFetch(
|
|
2658
|
+
`https://iam.googleapis.com/v1/${saResource}:setIamPolicy`,
|
|
2659
|
+
token,
|
|
2660
|
+
"POST",
|
|
2661
|
+
{ policy: { ...policy, bindings } }
|
|
2662
|
+
);
|
|
2663
|
+
}
|
|
2664
|
+
async function setupWIF(options) {
|
|
2665
|
+
const poolId = options.poolId ?? "github-actions";
|
|
2666
|
+
const providerId = options.providerId ?? "github-oidc";
|
|
2667
|
+
const log = options.onProgress ?? (() => void 0);
|
|
2668
|
+
if (!options.githubRepo.includes("/")) {
|
|
2669
|
+
throw new Error(
|
|
2670
|
+
`githubRepo must be in "owner/repo" format, got: "${options.githubRepo}"`
|
|
2671
|
+
);
|
|
2672
|
+
}
|
|
2673
|
+
log("Authenticating with Google Cloud...");
|
|
2674
|
+
const token = await getAccessToken4(options.keyFilePath);
|
|
2675
|
+
log("Fetching project number...");
|
|
2676
|
+
const projectNumber = await getProjectNumber(options.projectId, token);
|
|
2677
|
+
log(`Creating Workload Identity Pool "${poolId}"...`);
|
|
2678
|
+
await createOrGetWifPool(options.projectId, poolId, token);
|
|
2679
|
+
log(`Creating OIDC Provider "${providerId}"...`);
|
|
2680
|
+
await createOrGetWifProvider(
|
|
2681
|
+
options.projectId,
|
|
2682
|
+
poolId,
|
|
2683
|
+
providerId,
|
|
2684
|
+
options.githubRepo,
|
|
2685
|
+
token
|
|
2686
|
+
);
|
|
2687
|
+
log("Binding service account permissions...");
|
|
2688
|
+
await bindServiceAccount(
|
|
2689
|
+
options.projectId,
|
|
2690
|
+
options.serviceAccountEmail,
|
|
2691
|
+
projectNumber,
|
|
2692
|
+
poolId,
|
|
2693
|
+
options.githubRepo,
|
|
2694
|
+
token
|
|
2695
|
+
);
|
|
2696
|
+
const wifProvider = `projects/${projectNumber}/locations/global/workloadIdentityPools/${poolId}/providers/${providerId}`;
|
|
2697
|
+
return { wifProvider, projectNumber, poolId, providerId };
|
|
2698
|
+
}
|
|
2699
|
+
async function grantDrivePermissions(options) {
|
|
2700
|
+
const token = await getAccessToken4(options.keyFilePath);
|
|
2701
|
+
const policy = await gcpFetch(
|
|
2702
|
+
`https://cloudresourcemanager.googleapis.com/v1/projects/${encodeURIComponent(options.projectId)}:getIamPolicy`,
|
|
2703
|
+
token,
|
|
2704
|
+
"POST",
|
|
2705
|
+
{}
|
|
2706
|
+
);
|
|
2707
|
+
const bindings = policy.bindings ?? [];
|
|
2708
|
+
const role = "roles/drive.file";
|
|
2709
|
+
const member = `serviceAccount:${options.serviceAccountEmail}`;
|
|
2710
|
+
const existing = bindings.find((b) => b.role === role);
|
|
2711
|
+
if (existing) {
|
|
2712
|
+
if (existing.members.includes(member)) {
|
|
2713
|
+
return;
|
|
2714
|
+
}
|
|
2715
|
+
existing.members.push(member);
|
|
2716
|
+
} else {
|
|
2717
|
+
bindings.push({ role, members: [member] });
|
|
2718
|
+
}
|
|
2719
|
+
await gcpFetch(
|
|
2720
|
+
`https://cloudresourcemanager.googleapis.com/v1/projects/${encodeURIComponent(options.projectId)}:setIamPolicy`,
|
|
2721
|
+
token,
|
|
2722
|
+
"POST",
|
|
2723
|
+
{ policy: { ...policy, bindings } }
|
|
2724
|
+
);
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2489
2727
|
// src/index.ts
|
|
2490
2728
|
var index_default = getSpreadSheetData;
|
|
2491
2729
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2492
2730
|
0 && (module.exports = {
|
|
2493
2731
|
DEFAULT_IMAGE_EXTENSIONS,
|
|
2494
2732
|
DEFAULT_WAIT_SECONDS,
|
|
2733
|
+
GcpApiError,
|
|
2495
2734
|
buildGoogleAuth,
|
|
2496
2735
|
buildManifest,
|
|
2497
2736
|
convertFromDataJsonFormat,
|
|
@@ -2512,6 +2751,7 @@ var index_default = getSpreadSheetData;
|
|
|
2512
2751
|
getOriginalHeaderForLocale,
|
|
2513
2752
|
getSpreadSheetData,
|
|
2514
2753
|
getTranslationSummary,
|
|
2754
|
+
grantDrivePermissions,
|
|
2515
2755
|
handleBidirectionalSync,
|
|
2516
2756
|
inferLocaleFromDocName,
|
|
2517
2757
|
ingestDoc,
|
|
@@ -2529,6 +2769,7 @@ var index_default = getSpreadSheetData;
|
|
|
2529
2769
|
resolveLocaleWithFallback,
|
|
2530
2770
|
scanDriveFolderForDocs,
|
|
2531
2771
|
scanDriveFolderForSpreadsheets,
|
|
2772
|
+
setupWIF,
|
|
2532
2773
|
slugifyKey,
|
|
2533
2774
|
syncDriveImages,
|
|
2534
2775
|
updateSpreadsheetWithLocalChanges,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gst-setup-wif – Interactive CLI for configuring Workload Identity Federation.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* npx -p @el-j/google-sheet-translations gst-setup-wif
|
|
6
|
+
* npx -p @el-j/google-sheet-translations gst-setup-wif \
|
|
7
|
+
* --project=my-gcp-project \
|
|
8
|
+
* --service-account=deploy@my-gcp-project.iam.gserviceaccount.com \
|
|
9
|
+
* --repo=myorg/myrepo \
|
|
10
|
+
* --key-file=./service-account-key.json
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/setup/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|