@devramps/cli 0.1.23 → 0.1.24

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.
Files changed (2) hide show
  1. package/dist/index.js +91 -67
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1556,10 +1556,11 @@ function addOidcProviderResource(template, conditional = true, oidcProviderUrl)
1556
1556
  }
1557
1557
  };
1558
1558
  }
1559
- function buildOidcTrustPolicy(accountId, subject, oidcProviderUrl, additionalTrustedAccounts) {
1559
+ function buildOidcTrustPolicy(accountId, subject, oidcProviderUrl, additionalTrustedAccounts, skipOidc) {
1560
1560
  const providerUrl = oidcProviderUrl || OIDC_PROVIDER_URL;
1561
- const statements = [
1562
- {
1561
+ const statements = [];
1562
+ if (!skipOidc) {
1563
+ statements.push({
1563
1564
  Effect: "Allow",
1564
1565
  Principal: {
1565
1566
  Federated: `arn:aws:iam::${accountId}:oidc-provider/${providerUrl}`
@@ -1571,8 +1572,8 @@ function buildOidcTrustPolicy(accountId, subject, oidcProviderUrl, additionalTru
1571
1572
  [`${providerUrl}:aud`]: "sts.amazonaws.com"
1572
1573
  }
1573
1574
  }
1574
- }
1575
- ];
1575
+ });
1576
+ }
1576
1577
  if (additionalTrustedAccounts && additionalTrustedAccounts.length > 0) {
1577
1578
  statements.push({
1578
1579
  Effect: "Allow",
@@ -1948,7 +1949,7 @@ function createTerraformStateBucketPolicy(bucketName, cicdAccountId, allowedAcco
1948
1949
 
1949
1950
  // src/templates/org-stack.ts
1950
1951
  function generateOrgStackTemplate(options) {
1951
- const { orgSlug, cicdAccountId, targetAccountIds, oidcProviderUrl, additionalTrustedAccounts } = options;
1952
+ const { orgSlug, cicdAccountId, targetAccountIds, oidcProviderUrl, additionalTrustedAccounts, skipOidc } = options;
1952
1953
  const template = createBaseTemplate(`DevRamps Org Stack for ${orgSlug}`);
1953
1954
  const kmsKeyPolicy = buildKmsKeyPolicy(cicdAccountId, targetAccountIds);
1954
1955
  template.Resources.DevRampsKMSKey = createKmsKeyResource(
@@ -1978,7 +1979,7 @@ function generateOrgStackTemplate(options) {
1978
1979
  PolicyDocument: bucketPolicy
1979
1980
  }
1980
1981
  };
1981
- const trustPolicy = buildOidcTrustPolicy(cicdAccountId, `org:${orgSlug}/cicd`, oidcProviderUrl, additionalTrustedAccounts);
1982
+ const trustPolicy = buildOidcTrustPolicy(cicdAccountId, `org:${orgSlug}/cicd`, oidcProviderUrl, additionalTrustedAccounts, skipOidc);
1982
1983
  const orgRolePolicies = buildOrgRolePolicies(orgSlug);
1983
1984
  template.Resources.DevRampsCICDDeploymentRole = createIamRoleResource(
1984
1985
  getOrgRoleName(),
@@ -2444,13 +2445,14 @@ function generateStageStackTemplate(options) {
2444
2445
  dockerArtifacts,
2445
2446
  bundleArtifacts,
2446
2447
  oidcProviderUrl,
2447
- additionalTrustedAccounts
2448
+ additionalTrustedAccounts,
2449
+ skipOidc
2448
2450
  } = options;
2449
2451
  const template = createBaseTemplate(
2450
2452
  `DevRamps Stage Stack for ${pipelineSlug}/${stageName}`
2451
2453
  );
2452
2454
  const roleName = generateStageRoleName(pipelineSlug, stageName);
2453
- const trustPolicy = buildStageTrustPolicy(accountId, orgSlug, pipelineSlug, oidcProviderUrl, additionalTrustedAccounts);
2455
+ const trustPolicy = buildStageTrustPolicy(accountId, orgSlug, pipelineSlug, oidcProviderUrl, additionalTrustedAccounts, skipOidc);
2454
2456
  const policies = buildStagePolicies(steps, additionalPolicies);
2455
2457
  template.Resources.StageDeploymentRole = createIamRoleResource(
2456
2458
  roleName,
@@ -2494,8 +2496,6 @@ function generateStageStackTemplate(options) {
2494
2496
  );
2495
2497
  s3Outputs[artifact.name] = { resourceId };
2496
2498
  }
2497
- const providerUrl = oidcProviderUrl || OIDC_PROVIDER_URL;
2498
- const oidcProviderArn = `arn:aws:iam::${accountId}:oidc-provider/${providerUrl}`;
2499
2499
  template.Outputs = {
2500
2500
  StageRoleArn: {
2501
2501
  Description: "ARN of the stage deployment role",
@@ -2506,10 +2506,6 @@ function generateStageStackTemplate(options) {
2506
2506
  Description: "Name of the stage deployment role",
2507
2507
  Value: { Ref: "StageDeploymentRole" }
2508
2508
  },
2509
- OIDCProviderArn: {
2510
- Description: "ARN of the OIDC provider (created by Account Bootstrap stack)",
2511
- Value: oidcProviderArn
2512
- },
2513
2509
  PipelineSlug: {
2514
2510
  Description: "Pipeline slug",
2515
2511
  Value: pipelineSlug
@@ -2519,6 +2515,13 @@ function generateStageStackTemplate(options) {
2519
2515
  Value: stageName
2520
2516
  }
2521
2517
  };
2518
+ if (!skipOidc) {
2519
+ const providerUrl = oidcProviderUrl || OIDC_PROVIDER_URL;
2520
+ template.Outputs.OIDCProviderArn = {
2521
+ Description: "ARN of the OIDC provider (created by Account Bootstrap stack)",
2522
+ Value: `arn:aws:iam::${accountId}:oidc-provider/${providerUrl}`
2523
+ };
2524
+ }
2522
2525
  for (const [artifactName, { resourceId }] of Object.entries(ecrOutputs)) {
2523
2526
  const safeName = sanitizeResourceId(artifactName);
2524
2527
  template.Outputs[`${safeName}RepoUri`] = {
@@ -2535,9 +2538,9 @@ function generateStageStackTemplate(options) {
2535
2538
  }
2536
2539
  return template;
2537
2540
  }
2538
- function buildStageTrustPolicy(accountId, orgSlug, pipelineSlug, oidcProviderUrl, additionalTrustedAccounts) {
2541
+ function buildStageTrustPolicy(accountId, orgSlug, pipelineSlug, oidcProviderUrl, additionalTrustedAccounts, skipOidc) {
2539
2542
  const subject = `org:${orgSlug}/pipeline:${pipelineSlug}`;
2540
- return buildOidcTrustPolicy(accountId, subject, oidcProviderUrl, additionalTrustedAccounts);
2543
+ return buildOidcTrustPolicy(accountId, subject, oidcProviderUrl, additionalTrustedAccounts, skipOidc);
2541
2544
  }
2542
2545
  function buildStagePolicies(steps, additionalPolicies) {
2543
2546
  const policies = [];
@@ -2606,12 +2609,12 @@ function buildStagePolicies(steps, additionalPolicies) {
2606
2609
 
2607
2610
  // src/templates/import-stack.ts
2608
2611
  function generateImportStackTemplate(options) {
2609
- const { pipelineSlug, orgSlug, accountId, oidcProviderUrl, additionalTrustedAccounts } = options;
2612
+ const { pipelineSlug, orgSlug, accountId, oidcProviderUrl, additionalTrustedAccounts, skipOidc } = options;
2610
2613
  const template = createBaseTemplate(
2611
2614
  `DevRamps Import Stack for ${pipelineSlug} - grants read access for artifact imports`
2612
2615
  );
2613
2616
  const roleName = generateImportRoleName(pipelineSlug);
2614
- const trustPolicy = buildOidcTrustPolicy(accountId, `org:${orgSlug}/cicd`, oidcProviderUrl, additionalTrustedAccounts);
2617
+ const trustPolicy = buildOidcTrustPolicy(accountId, `org:${orgSlug}/cicd`, oidcProviderUrl, additionalTrustedAccounts, skipOidc);
2615
2618
  const policies = buildImportRolePolicies();
2616
2619
  template.Resources.ImportRole = createIamRoleResource(
2617
2620
  roleName,
@@ -2739,6 +2742,15 @@ function getOidcProviderUrlFromEndpoint(endpointOverride) {
2739
2742
  return void 0;
2740
2743
  }
2741
2744
  }
2745
+ function isLocalhostEndpoint(endpointOverride) {
2746
+ if (!endpointOverride) return false;
2747
+ try {
2748
+ const url = new URL(endpointOverride);
2749
+ return url.hostname === "localhost" || url.hostname === "127.0.0.1";
2750
+ } catch {
2751
+ return false;
2752
+ }
2753
+ }
2742
2754
  async function bootstrapCommand(options) {
2743
2755
  try {
2744
2756
  if (options.verbose) {
@@ -2790,7 +2802,11 @@ async function bootstrapCommand(options) {
2790
2802
  }
2791
2803
  const oidcProviderUrl = getOidcProviderUrlFromEndpoint(options.endpointOverride);
2792
2804
  const additionalTrustedAccounts = options.additionalTrustedAccounts ? options.additionalTrustedAccounts.split(",").map((s) => s.trim()) : void 0;
2793
- await executeDeployment(plan, pipelines, pipelineArtifacts, authData, identity.accountId, options, oidcProviderUrl, additionalTrustedAccounts);
2805
+ const skipOidc = isLocalhostEndpoint(options.endpointOverride);
2806
+ if (skipOidc) {
2807
+ info("Localhost endpoint detected \u2014 OIDC provider creation will be skipped");
2808
+ }
2809
+ await executeDeployment(plan, pipelines, pipelineArtifacts, authData, identity.accountId, options, oidcProviderUrl, additionalTrustedAccounts, skipOidc);
2794
2810
  } catch (error2) {
2795
2811
  if (error2 instanceof DevRampsError) {
2796
2812
  error(error2.message);
@@ -3063,48 +3079,53 @@ async function confirmDeploymentPlan(plan) {
3063
3079
  ]
3064
3080
  });
3065
3081
  }
3066
- async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts) {
3082
+ async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts, skipOidc) {
3067
3083
  const results = { success: 0, failed: 0 };
3068
3084
  const remainingStacks = 1 + plan.pipelineStacks.length + plan.stageStacks.length + plan.importStacks.length;
3069
- newline();
3070
- header("Phase 1: Deploying Account Bootstrap Stacks");
3071
- info(`Deploying ${plan.accountStacks.length} account stack(s) in parallel...`);
3072
- newline();
3073
- const accountProgress = getMultiStackProgress();
3074
- for (const stack of plan.accountStacks) {
3075
- accountProgress.addStack(stack.stackName, "account", stack.accountId, stack.region, 1);
3076
- }
3077
- accountProgress.start();
3078
- const accountResults = await Promise.all(
3079
- plan.accountStacks.map(async (stack) => {
3080
- try {
3081
- await deployAccountStack(stack, currentAccountId, options, oidcProviderUrl);
3082
- return { stack: `${stack.stackName} (${stack.accountId})`, success: true };
3083
- } catch (error2) {
3084
- return {
3085
- stack: `${stack.stackName} (${stack.accountId})`,
3086
- success: false,
3087
- error: error2 instanceof Error ? error2.message : String(error2)
3088
- };
3085
+ if (skipOidc) {
3086
+ newline();
3087
+ header("Phase 1: Skipping Account Bootstrap Stacks (localhost endpoint, OIDC not needed)");
3088
+ } else {
3089
+ newline();
3090
+ header("Phase 1: Deploying Account Bootstrap Stacks");
3091
+ info(`Deploying ${plan.accountStacks.length} account stack(s) in parallel...`);
3092
+ newline();
3093
+ const accountProgress = getMultiStackProgress();
3094
+ for (const stack of plan.accountStacks) {
3095
+ accountProgress.addStack(stack.stackName, "account", stack.accountId, stack.region, 1);
3096
+ }
3097
+ accountProgress.start();
3098
+ const accountResults = await Promise.all(
3099
+ plan.accountStacks.map(async (stack) => {
3100
+ try {
3101
+ await deployAccountStack(stack, currentAccountId, options, oidcProviderUrl);
3102
+ return { stack: `${stack.stackName} (${stack.accountId})`, success: true };
3103
+ } catch (error2) {
3104
+ return {
3105
+ stack: `${stack.stackName} (${stack.accountId})`,
3106
+ success: false,
3107
+ error: error2 instanceof Error ? error2.message : String(error2)
3108
+ };
3109
+ }
3110
+ })
3111
+ );
3112
+ clearMultiStackProgress();
3113
+ newline();
3114
+ for (const result of accountResults) {
3115
+ if (result.success) {
3116
+ success(`${result.stack} deployed`);
3117
+ results.success++;
3118
+ } else {
3119
+ error(`${result.stack} failed: ${result.error}`);
3120
+ results.failed++;
3089
3121
  }
3090
- })
3091
- );
3092
- clearMultiStackProgress();
3093
- newline();
3094
- for (const result of accountResults) {
3095
- if (result.success) {
3096
- success(`${result.stack} deployed`);
3097
- results.success++;
3098
- } else {
3099
- error(`${result.stack} failed: ${result.error}`);
3100
- results.failed++;
3101
3122
  }
3102
- }
3103
- if (results.failed > 0) {
3104
- newline();
3105
- header("Deployment Summary");
3106
- error(`${results.failed} account stack(s) failed. Skipping remaining ${remainingStacks} stack(s).`);
3107
- process.exit(1);
3123
+ if (results.failed > 0) {
3124
+ newline();
3125
+ header("Deployment Summary");
3126
+ error(`${results.failed} account stack(s) failed. Skipping remaining ${remainingStacks} stack(s).`);
3127
+ process.exit(1);
3128
+ }
3108
3129
  }
3109
3130
  newline();
3110
3131
  header("Phase 2: Deploying Org, Pipeline, Stage, and Import Stacks");
@@ -3126,7 +3147,7 @@ async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, c
3126
3147
  mainProgress.start();
3127
3148
  const orgPromise = (async () => {
3128
3149
  try {
3129
- await deployOrgStack(plan, pipelines, authData, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts);
3150
+ await deployOrgStack(plan, pipelines, authData, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts, skipOidc);
3130
3151
  return { stack: plan.orgStack.stackName, success: true };
3131
3152
  } catch (error2) {
3132
3153
  return {
@@ -3150,7 +3171,7 @@ async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, c
3150
3171
  });
3151
3172
  const stagePromises = plan.stageStacks.map(async (stack) => {
3152
3173
  try {
3153
- await deployStageStack(stack, authData, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts);
3174
+ await deployStageStack(stack, authData, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts, skipOidc);
3154
3175
  return { stack: stack.stackName, success: true };
3155
3176
  } catch (error2) {
3156
3177
  return {
@@ -3162,7 +3183,7 @@ async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, c
3162
3183
  });
3163
3184
  const importPromises = plan.importStacks.map(async (stack) => {
3164
3185
  try {
3165
- await deployImportStack(stack, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts);
3186
+ await deployImportStack(stack, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts, skipOidc);
3166
3187
  return { stack: `${stack.stackName} (${stack.accountId})`, success: true };
3167
3188
  } catch (error2) {
3168
3189
  return {
@@ -3199,7 +3220,7 @@ async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, c
3199
3220
  process.exit(1);
3200
3221
  }
3201
3222
  }
3202
- async function deployOrgStack(plan, pipelines, authData, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts) {
3223
+ async function deployOrgStack(plan, pipelines, authData, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts, skipOidc) {
3203
3224
  const { orgSlug, cicdAccountId, cicdRegion } = authData;
3204
3225
  const credentials = cicdAccountId !== currentAccountId ? (await assumeRoleForAccount({
3205
3226
  targetAccountId: cicdAccountId,
@@ -3231,7 +3252,8 @@ async function deployOrgStack(plan, pipelines, authData, currentAccountId, optio
3231
3252
  cicdAccountId,
3232
3253
  targetAccountIds,
3233
3254
  oidcProviderUrl,
3234
- additionalTrustedAccounts
3255
+ additionalTrustedAccounts,
3256
+ skipOidc
3235
3257
  });
3236
3258
  const deployOptions = {
3237
3259
  stackName: plan.orgStack.stackName,
@@ -3283,7 +3305,7 @@ async function deployAccountStack(stack, currentAccountId, options, oidcProvider
3283
3305
  await previewStackChanges(deployOptions);
3284
3306
  await deployStack(deployOptions);
3285
3307
  }
3286
- async function deployStageStack(stack, authData, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts) {
3308
+ async function deployStageStack(stack, authData, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts, skipOidc) {
3287
3309
  const credentials = stack.accountId !== currentAccountId ? (await assumeRoleForAccount({
3288
3310
  targetAccountId: stack.accountId,
3289
3311
  currentAccountId,
@@ -3299,7 +3321,8 @@ async function deployStageStack(stack, authData, currentAccountId, options, oidc
3299
3321
  dockerArtifacts: stack.dockerArtifacts,
3300
3322
  bundleArtifacts: stack.bundleArtifacts,
3301
3323
  oidcProviderUrl,
3302
- additionalTrustedAccounts
3324
+ additionalTrustedAccounts,
3325
+ skipOidc
3303
3326
  });
3304
3327
  const deployOptions = {
3305
3328
  stackName: stack.stackName,
@@ -3311,7 +3334,7 @@ async function deployStageStack(stack, authData, currentAccountId, options, oidc
3311
3334
  await previewStackChanges(deployOptions);
3312
3335
  await deployStack(deployOptions);
3313
3336
  }
3314
- async function deployImportStack(stack, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts) {
3337
+ async function deployImportStack(stack, currentAccountId, options, oidcProviderUrl, additionalTrustedAccounts, skipOidc) {
3315
3338
  const credentials = stack.accountId !== currentAccountId ? (await assumeRoleForAccount({
3316
3339
  targetAccountId: stack.accountId,
3317
3340
  currentAccountId,
@@ -3322,7 +3345,8 @@ async function deployImportStack(stack, currentAccountId, options, oidcProviderU
3322
3345
  orgSlug: stack.orgSlug,
3323
3346
  accountId: stack.accountId,
3324
3347
  oidcProviderUrl,
3325
- additionalTrustedAccounts
3348
+ additionalTrustedAccounts,
3349
+ skipOidc
3326
3350
  });
3327
3351
  const deployOptions = {
3328
3352
  stackName: stack.stackName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devramps/cli",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "DevRamps CLI - Bootstrap AWS infrastructure for CI/CD pipelines",
5
5
  "main": "dist/index.js",
6
6
  "bin": {