@devramps/cli 0.1.11 → 0.1.13
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/index.js +107 -55
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -933,6 +933,11 @@ async function authenticateViaBrowser(options = {}) {
|
|
|
933
933
|
const state = generateState();
|
|
934
934
|
verbose("Generated PKCE code_challenge and state");
|
|
935
935
|
const { server, port, callbackPromise } = await startCallbackServer(state);
|
|
936
|
+
const sigintHandler = () => {
|
|
937
|
+
server.close();
|
|
938
|
+
process.exit(130);
|
|
939
|
+
};
|
|
940
|
+
process.on("SIGINT", sigintHandler);
|
|
936
941
|
try {
|
|
937
942
|
const redirectUri = `http://localhost:${port}`;
|
|
938
943
|
const authParams = new URLSearchParams({
|
|
@@ -1005,6 +1010,7 @@ async function authenticateViaBrowser(options = {}) {
|
|
|
1005
1010
|
cicdRegion: awsConfig.defaultRegion
|
|
1006
1011
|
};
|
|
1007
1012
|
} finally {
|
|
1013
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
1008
1014
|
await closeServer(server);
|
|
1009
1015
|
}
|
|
1010
1016
|
}
|
|
@@ -1296,8 +1302,11 @@ async function parseAdditionalPolicies(pipelineDir) {
|
|
|
1296
1302
|
if (!policy || typeof policy !== "object") {
|
|
1297
1303
|
throw new Error(`Policy at index ${i} is not an object`);
|
|
1298
1304
|
}
|
|
1299
|
-
if (!("Statement" in policy) || !
|
|
1300
|
-
throw new Error(`Policy at index ${i} is missing Statement
|
|
1305
|
+
if (!("Statement" in policy) || !policy.Statement || typeof policy.Statement !== "object") {
|
|
1306
|
+
throw new Error(`Policy at index ${i} is missing Statement`);
|
|
1307
|
+
}
|
|
1308
|
+
if (!Array.isArray(policy.Statement)) {
|
|
1309
|
+
policy.Statement = [policy.Statement];
|
|
1301
1310
|
}
|
|
1302
1311
|
validatedPolicies.push(policy);
|
|
1303
1312
|
}
|
|
@@ -1501,7 +1510,8 @@ function createBaseTemplate(description) {
|
|
|
1501
1510
|
function sanitizeResourceId(name) {
|
|
1502
1511
|
return name.replace(/[^a-zA-Z0-9]/g, "").substring(0, 64);
|
|
1503
1512
|
}
|
|
1504
|
-
function addOidcProviderResource(template, conditional = true) {
|
|
1513
|
+
function addOidcProviderResource(template, conditional = true, oidcProviderUrl) {
|
|
1514
|
+
const providerUrl = oidcProviderUrl || OIDC_PROVIDER_URL;
|
|
1505
1515
|
if (conditional) {
|
|
1506
1516
|
template.Parameters.OIDCProviderExists = {
|
|
1507
1517
|
Type: "String",
|
|
@@ -1517,27 +1527,28 @@ function addOidcProviderResource(template, conditional = true) {
|
|
|
1517
1527
|
Type: "AWS::IAM::OIDCProvider",
|
|
1518
1528
|
...conditional ? { Condition: "CreateOIDCProvider" } : {},
|
|
1519
1529
|
Properties: {
|
|
1520
|
-
Url: `https://${
|
|
1521
|
-
ClientIdList: [
|
|
1530
|
+
Url: `https://${providerUrl}`,
|
|
1531
|
+
ClientIdList: [providerUrl],
|
|
1522
1532
|
ThumbprintList: [getOidcThumbprint()],
|
|
1523
1533
|
Tags: STANDARD_TAGS
|
|
1524
1534
|
}
|
|
1525
1535
|
};
|
|
1526
1536
|
}
|
|
1527
|
-
function buildOidcTrustPolicy(accountId, subject) {
|
|
1537
|
+
function buildOidcTrustPolicy(accountId, subject, oidcProviderUrl) {
|
|
1538
|
+
const providerUrl = oidcProviderUrl || OIDC_PROVIDER_URL;
|
|
1528
1539
|
return {
|
|
1529
1540
|
Version: "2012-10-17",
|
|
1530
1541
|
Statement: [
|
|
1531
1542
|
{
|
|
1532
1543
|
Effect: "Allow",
|
|
1533
1544
|
Principal: {
|
|
1534
|
-
Federated: `arn:aws:iam::${accountId}:oidc-provider/${
|
|
1545
|
+
Federated: `arn:aws:iam::${accountId}:oidc-provider/${providerUrl}`
|
|
1535
1546
|
},
|
|
1536
1547
|
Action: "sts:AssumeRoleWithWebIdentity",
|
|
1537
1548
|
Condition: {
|
|
1538
1549
|
StringEquals: {
|
|
1539
|
-
[`${
|
|
1540
|
-
[`${
|
|
1550
|
+
[`${providerUrl}:sub`]: subject,
|
|
1551
|
+
[`${providerUrl}:aud`]: providerUrl
|
|
1541
1552
|
}
|
|
1542
1553
|
}
|
|
1543
1554
|
}
|
|
@@ -1899,7 +1910,7 @@ function createTerraformStateBucketPolicy(bucketName, cicdAccountId, allowedAcco
|
|
|
1899
1910
|
|
|
1900
1911
|
// src/templates/org-stack.ts
|
|
1901
1912
|
function generateOrgStackTemplate(options) {
|
|
1902
|
-
const { orgSlug, cicdAccountId, targetAccountIds } = options;
|
|
1913
|
+
const { orgSlug, cicdAccountId, targetAccountIds, oidcProviderUrl } = options;
|
|
1903
1914
|
const template = createBaseTemplate(`DevRamps Org Stack for ${orgSlug}`);
|
|
1904
1915
|
const kmsKeyPolicy = buildKmsKeyPolicy(cicdAccountId, targetAccountIds);
|
|
1905
1916
|
template.Resources.DevRampsKMSKey = createKmsKeyResource(
|
|
@@ -1929,7 +1940,7 @@ function generateOrgStackTemplate(options) {
|
|
|
1929
1940
|
PolicyDocument: bucketPolicy
|
|
1930
1941
|
}
|
|
1931
1942
|
};
|
|
1932
|
-
const trustPolicy = buildOidcTrustPolicy(cicdAccountId, `org:${orgSlug}
|
|
1943
|
+
const trustPolicy = buildOidcTrustPolicy(cicdAccountId, `org:${orgSlug}/cicd`, oidcProviderUrl);
|
|
1933
1944
|
const orgRolePolicies = buildOrgRolePolicies(orgSlug);
|
|
1934
1945
|
template.Resources.DevRampsCICDDeploymentRole = createIamRoleResource(
|
|
1935
1946
|
getOrgRoleName(),
|
|
@@ -2137,11 +2148,11 @@ function generatePipelineStackTemplate(options) {
|
|
|
2137
2148
|
}
|
|
2138
2149
|
|
|
2139
2150
|
// src/templates/account-stack.ts
|
|
2140
|
-
function generateAccountStackTemplate() {
|
|
2151
|
+
function generateAccountStackTemplate(options) {
|
|
2141
2152
|
const template = createBaseTemplate(
|
|
2142
2153
|
"DevRamps Account Bootstrap Stack - Creates OIDC provider for the account"
|
|
2143
2154
|
);
|
|
2144
|
-
addOidcProviderResource(template, false);
|
|
2155
|
+
addOidcProviderResource(template, false, options?.oidcProviderUrl);
|
|
2145
2156
|
template.Outputs = {
|
|
2146
2157
|
OIDCProviderArn: {
|
|
2147
2158
|
Description: "ARN of the OIDC provider",
|
|
@@ -2333,13 +2344,14 @@ function generateStageStackTemplate(options) {
|
|
|
2333
2344
|
steps,
|
|
2334
2345
|
additionalPolicies,
|
|
2335
2346
|
dockerArtifacts,
|
|
2336
|
-
bundleArtifacts
|
|
2347
|
+
bundleArtifacts,
|
|
2348
|
+
oidcProviderUrl
|
|
2337
2349
|
} = options;
|
|
2338
2350
|
const template = createBaseTemplate(
|
|
2339
2351
|
`DevRamps Stage Stack for ${pipelineSlug}/${stageName}`
|
|
2340
2352
|
);
|
|
2341
2353
|
const roleName = generateStageRoleName(pipelineSlug, stageName);
|
|
2342
|
-
const trustPolicy = buildStageTrustPolicy(accountId, orgSlug, pipelineSlug,
|
|
2354
|
+
const trustPolicy = buildStageTrustPolicy(accountId, orgSlug, pipelineSlug, oidcProviderUrl);
|
|
2343
2355
|
const policies = buildStagePolicies(steps, additionalPolicies);
|
|
2344
2356
|
template.Resources.StageDeploymentRole = createIamRoleResource(
|
|
2345
2357
|
roleName,
|
|
@@ -2383,7 +2395,8 @@ function generateStageStackTemplate(options) {
|
|
|
2383
2395
|
);
|
|
2384
2396
|
s3Outputs[artifact.name] = { resourceId };
|
|
2385
2397
|
}
|
|
2386
|
-
const
|
|
2398
|
+
const providerUrl = oidcProviderUrl || OIDC_PROVIDER_URL;
|
|
2399
|
+
const oidcProviderArn = `arn:aws:iam::${accountId}:oidc-provider/${providerUrl}`;
|
|
2387
2400
|
template.Outputs = {
|
|
2388
2401
|
StageRoleArn: {
|
|
2389
2402
|
Description: "ARN of the stage deployment role",
|
|
@@ -2423,9 +2436,9 @@ function generateStageStackTemplate(options) {
|
|
|
2423
2436
|
}
|
|
2424
2437
|
return template;
|
|
2425
2438
|
}
|
|
2426
|
-
function buildStageTrustPolicy(accountId, orgSlug, pipelineSlug,
|
|
2427
|
-
const subject = `org:${orgSlug}/pipeline:${pipelineSlug}
|
|
2428
|
-
return buildOidcTrustPolicy(accountId, subject);
|
|
2439
|
+
function buildStageTrustPolicy(accountId, orgSlug, pipelineSlug, oidcProviderUrl) {
|
|
2440
|
+
const subject = `org:${orgSlug}/pipeline:${pipelineSlug}`;
|
|
2441
|
+
return buildOidcTrustPolicy(accountId, subject, oidcProviderUrl);
|
|
2429
2442
|
}
|
|
2430
2443
|
function buildStagePolicies(steps, additionalPolicies) {
|
|
2431
2444
|
const policies = [];
|
|
@@ -2531,6 +2544,15 @@ async function confirmDeployment(plan) {
|
|
|
2531
2544
|
}
|
|
2532
2545
|
|
|
2533
2546
|
// src/commands/bootstrap.ts
|
|
2547
|
+
function getOidcProviderUrlFromEndpoint(endpointOverride) {
|
|
2548
|
+
if (!endpointOverride) return void 0;
|
|
2549
|
+
try {
|
|
2550
|
+
const url = new URL(endpointOverride);
|
|
2551
|
+
return url.hostname;
|
|
2552
|
+
} catch {
|
|
2553
|
+
return void 0;
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2534
2556
|
async function bootstrapCommand(options) {
|
|
2535
2557
|
try {
|
|
2536
2558
|
if (options.verbose) {
|
|
@@ -2580,7 +2602,8 @@ async function bootstrapCommand(options) {
|
|
|
2580
2602
|
info("Deployment cancelled by user.");
|
|
2581
2603
|
return;
|
|
2582
2604
|
}
|
|
2583
|
-
|
|
2605
|
+
const oidcProviderUrl = getOidcProviderUrlFromEndpoint(options.endpointOverride);
|
|
2606
|
+
await executeDeployment(plan, pipelines, pipelineArtifacts, authData, identity.accountId, options, oidcProviderUrl);
|
|
2584
2607
|
} catch (error2) {
|
|
2585
2608
|
if (error2 instanceof DevRampsError) {
|
|
2586
2609
|
error(error2.message);
|
|
@@ -2771,7 +2794,9 @@ async function showDryRunPlan(plan) {
|
|
|
2771
2794
|
}
|
|
2772
2795
|
const totalStacks = 1 + plan.pipelineStacks.length + plan.accountStacks.length + plan.stageStacks.length;
|
|
2773
2796
|
newline();
|
|
2774
|
-
info(`Total stacks to deploy
|
|
2797
|
+
info(`Total stacks to deploy: ${totalStacks}`);
|
|
2798
|
+
info(` Phase 1: ${plan.accountStacks.length} Account stack(s) (deployed first)`);
|
|
2799
|
+
info(` Phase 2: ${1 + plan.pipelineStacks.length + plan.stageStacks.length} Org/Pipeline/Stage stack(s) (deployed in parallel after Phase 1)`);
|
|
2775
2800
|
}
|
|
2776
2801
|
async function confirmDeploymentPlan(plan) {
|
|
2777
2802
|
const totalStacks = 1 + plan.pipelineStacks.length + plan.accountStacks.length + plan.stageStacks.length;
|
|
@@ -2791,30 +2816,68 @@ async function confirmDeploymentPlan(plan) {
|
|
|
2791
2816
|
]
|
|
2792
2817
|
});
|
|
2793
2818
|
}
|
|
2794
|
-
async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, currentAccountId, options) {
|
|
2819
|
+
async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, currentAccountId, options, oidcProviderUrl) {
|
|
2795
2820
|
const results = { success: 0, failed: 0 };
|
|
2796
2821
|
const totalStacks = 1 + plan.pipelineStacks.length + plan.accountStacks.length + plan.stageStacks.length;
|
|
2822
|
+
const remainingStacks = 1 + plan.pipelineStacks.length + plan.stageStacks.length;
|
|
2797
2823
|
newline();
|
|
2798
|
-
header("Deploying
|
|
2799
|
-
info(`Deploying ${
|
|
2824
|
+
header("Phase 1: Deploying Account Bootstrap Stacks");
|
|
2825
|
+
info(`Deploying ${plan.accountStacks.length} account stack(s) in parallel...`);
|
|
2800
2826
|
newline();
|
|
2801
|
-
const
|
|
2802
|
-
|
|
2827
|
+
const accountProgress = getMultiStackProgress();
|
|
2828
|
+
for (const stack of plan.accountStacks) {
|
|
2829
|
+
accountProgress.addStack(stack.stackName, "account", stack.accountId, stack.region, 1);
|
|
2830
|
+
}
|
|
2831
|
+
accountProgress.start();
|
|
2832
|
+
const accountResults = await Promise.all(
|
|
2833
|
+
plan.accountStacks.map(async (stack) => {
|
|
2834
|
+
try {
|
|
2835
|
+
await deployAccountStack(stack, currentAccountId, options, oidcProviderUrl);
|
|
2836
|
+
return { stack: `${stack.stackName} (${stack.accountId})`, success: true };
|
|
2837
|
+
} catch (error2) {
|
|
2838
|
+
return {
|
|
2839
|
+
stack: `${stack.stackName} (${stack.accountId})`,
|
|
2840
|
+
success: false,
|
|
2841
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2844
|
+
})
|
|
2845
|
+
);
|
|
2846
|
+
clearMultiStackProgress();
|
|
2847
|
+
newline();
|
|
2848
|
+
for (const result of accountResults) {
|
|
2849
|
+
if (result.success) {
|
|
2850
|
+
success(`${result.stack} deployed`);
|
|
2851
|
+
results.success++;
|
|
2852
|
+
} else {
|
|
2853
|
+
error(`${result.stack} failed: ${result.error}`);
|
|
2854
|
+
results.failed++;
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
if (results.failed > 0) {
|
|
2858
|
+
newline();
|
|
2859
|
+
header("Deployment Summary");
|
|
2860
|
+
error(`${results.failed} account stack(s) failed. Skipping remaining ${remainingStacks} stack(s).`);
|
|
2861
|
+
process.exit(1);
|
|
2862
|
+
}
|
|
2863
|
+
newline();
|
|
2864
|
+
header("Phase 2: Deploying Org, Pipeline, and Stage Stacks");
|
|
2865
|
+
info(`Deploying ${remainingStacks} stack(s) in parallel...`);
|
|
2866
|
+
newline();
|
|
2867
|
+
const mainProgress = getMultiStackProgress();
|
|
2868
|
+
mainProgress.addStack(plan.orgStack.stackName, "org", plan.orgStack.accountId, plan.orgStack.region, 5);
|
|
2803
2869
|
for (const stack of plan.pipelineStacks) {
|
|
2804
2870
|
const resourceCount = stack.dockerArtifacts.length + stack.bundleArtifacts.length;
|
|
2805
|
-
|
|
2806
|
-
}
|
|
2807
|
-
for (const stack of plan.accountStacks) {
|
|
2808
|
-
progress.addStack(stack.stackName, "account", stack.accountId, stack.region, 1);
|
|
2871
|
+
mainProgress.addStack(stack.stackName, "pipeline", stack.accountId, stack.region, Math.max(resourceCount, 1));
|
|
2809
2872
|
}
|
|
2810
2873
|
for (const stack of plan.stageStacks) {
|
|
2811
2874
|
const resourceCount = stack.dockerArtifacts.length + stack.bundleArtifacts.length + 2;
|
|
2812
|
-
|
|
2875
|
+
mainProgress.addStack(stack.stackName, "stage", stack.accountId, stack.region, resourceCount);
|
|
2813
2876
|
}
|
|
2814
|
-
|
|
2877
|
+
mainProgress.start();
|
|
2815
2878
|
const orgPromise = (async () => {
|
|
2816
2879
|
try {
|
|
2817
|
-
await deployOrgStack(plan, pipelines, authData, currentAccountId, options);
|
|
2880
|
+
await deployOrgStack(plan, pipelines, authData, currentAccountId, options, oidcProviderUrl);
|
|
2818
2881
|
return { stack: plan.orgStack.stackName, success: true };
|
|
2819
2882
|
} catch (error2) {
|
|
2820
2883
|
return {
|
|
@@ -2836,21 +2899,9 @@ async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, c
|
|
|
2836
2899
|
};
|
|
2837
2900
|
}
|
|
2838
2901
|
});
|
|
2839
|
-
const accountPromises = plan.accountStacks.map(async (stack) => {
|
|
2840
|
-
try {
|
|
2841
|
-
await deployAccountStack(stack, currentAccountId, options);
|
|
2842
|
-
return { stack: stack.stackName, success: true };
|
|
2843
|
-
} catch (error2) {
|
|
2844
|
-
return {
|
|
2845
|
-
stack: stack.stackName,
|
|
2846
|
-
success: false,
|
|
2847
|
-
error: error2 instanceof Error ? error2.message : String(error2)
|
|
2848
|
-
};
|
|
2849
|
-
}
|
|
2850
|
-
});
|
|
2851
2902
|
const stagePromises = plan.stageStacks.map(async (stack) => {
|
|
2852
2903
|
try {
|
|
2853
|
-
await deployStageStack(stack, authData, currentAccountId, options);
|
|
2904
|
+
await deployStageStack(stack, authData, currentAccountId, options, oidcProviderUrl);
|
|
2854
2905
|
return { stack: stack.stackName, success: true };
|
|
2855
2906
|
} catch (error2) {
|
|
2856
2907
|
return {
|
|
@@ -2860,15 +2911,14 @@ async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, c
|
|
|
2860
2911
|
};
|
|
2861
2912
|
}
|
|
2862
2913
|
});
|
|
2863
|
-
const
|
|
2914
|
+
const mainResults = await Promise.all([
|
|
2864
2915
|
orgPromise,
|
|
2865
2916
|
...pipelinePromises,
|
|
2866
|
-
...accountPromises,
|
|
2867
2917
|
...stagePromises
|
|
2868
2918
|
]);
|
|
2869
2919
|
clearMultiStackProgress();
|
|
2870
2920
|
newline();
|
|
2871
|
-
for (const result of
|
|
2921
|
+
for (const result of mainResults) {
|
|
2872
2922
|
if (result.success) {
|
|
2873
2923
|
success(`${result.stack} deployed`);
|
|
2874
2924
|
results.success++;
|
|
@@ -2887,7 +2937,7 @@ async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, c
|
|
|
2887
2937
|
process.exit(1);
|
|
2888
2938
|
}
|
|
2889
2939
|
}
|
|
2890
|
-
async function deployOrgStack(plan, pipelines, authData, currentAccountId, options) {
|
|
2940
|
+
async function deployOrgStack(plan, pipelines, authData, currentAccountId, options, oidcProviderUrl) {
|
|
2891
2941
|
const { orgSlug, cicdAccountId, cicdRegion } = authData;
|
|
2892
2942
|
const credentials = cicdAccountId !== currentAccountId ? (await assumeRoleForAccount({
|
|
2893
2943
|
targetAccountId: cicdAccountId,
|
|
@@ -2917,7 +2967,8 @@ async function deployOrgStack(plan, pipelines, authData, currentAccountId, optio
|
|
|
2917
2967
|
const template = generateOrgStackTemplate({
|
|
2918
2968
|
orgSlug,
|
|
2919
2969
|
cicdAccountId,
|
|
2920
|
-
targetAccountIds
|
|
2970
|
+
targetAccountIds,
|
|
2971
|
+
oidcProviderUrl
|
|
2921
2972
|
});
|
|
2922
2973
|
const deployOptions = {
|
|
2923
2974
|
stackName: plan.orgStack.stackName,
|
|
@@ -2952,13 +3003,13 @@ async function deployPipelineStack(stack, authData, currentAccountId, options) {
|
|
|
2952
3003
|
await previewStackChanges(deployOptions);
|
|
2953
3004
|
await deployStack(deployOptions);
|
|
2954
3005
|
}
|
|
2955
|
-
async function deployAccountStack(stack, currentAccountId, options) {
|
|
3006
|
+
async function deployAccountStack(stack, currentAccountId, options, oidcProviderUrl) {
|
|
2956
3007
|
const credentials = stack.accountId !== currentAccountId ? (await assumeRoleForAccount({
|
|
2957
3008
|
targetAccountId: stack.accountId,
|
|
2958
3009
|
currentAccountId,
|
|
2959
3010
|
targetRoleName: options.targetAccountRoleName
|
|
2960
3011
|
}))?.credentials : void 0;
|
|
2961
|
-
const template = generateAccountStackTemplate();
|
|
3012
|
+
const template = generateAccountStackTemplate({ oidcProviderUrl });
|
|
2962
3013
|
const deployOptions = {
|
|
2963
3014
|
stackName: stack.stackName,
|
|
2964
3015
|
template,
|
|
@@ -2969,7 +3020,7 @@ async function deployAccountStack(stack, currentAccountId, options) {
|
|
|
2969
3020
|
await previewStackChanges(deployOptions);
|
|
2970
3021
|
await deployStack(deployOptions);
|
|
2971
3022
|
}
|
|
2972
|
-
async function deployStageStack(stack, authData, currentAccountId, options) {
|
|
3023
|
+
async function deployStageStack(stack, authData, currentAccountId, options, oidcProviderUrl) {
|
|
2973
3024
|
const credentials = stack.accountId !== currentAccountId ? (await assumeRoleForAccount({
|
|
2974
3025
|
targetAccountId: stack.accountId,
|
|
2975
3026
|
currentAccountId,
|
|
@@ -2983,7 +3034,8 @@ async function deployStageStack(stack, authData, currentAccountId, options) {
|
|
|
2983
3034
|
steps: stack.steps,
|
|
2984
3035
|
additionalPolicies: stack.additionalPolicies,
|
|
2985
3036
|
dockerArtifacts: stack.dockerArtifacts,
|
|
2986
|
-
bundleArtifacts: stack.bundleArtifacts
|
|
3037
|
+
bundleArtifacts: stack.bundleArtifacts,
|
|
3038
|
+
oidcProviderUrl
|
|
2987
3039
|
});
|
|
2988
3040
|
const deployOptions = {
|
|
2989
3041
|
stackName: stack.stackName,
|