Skip to content

Commit

Permalink
feat: #479 Introduce CI mode (#482)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkleszcz authored Feb 26, 2024
1 parent db0a868 commit d4d5d04
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 16 deletions.
17 changes: 17 additions & 0 deletions packages/infra/infra-core/src/lib/env-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ declare const process: {
SB_TOOLS_HOSTED_ZONE_NAME: string;
SB_TOOLS_HOSTED_ZONE_ID: string;
SB_TOOLS_DOMAIN_VERSION_MATRIX: string;
SB_CI_MODE: string;
};
};

Expand Down Expand Up @@ -64,6 +65,15 @@ interface WebAppConfig {
envVariables: EnvironmentVariables;
}

export enum CI_MODE {
PARALLEL = 'parallel',
SIMPLE = 'simple',
}

interface CIConfig {
mode: CI_MODE;
}

export interface EnvironmentSettings {
appBasicAuth: string | null | undefined;
deployBranches: Array<string>;
Expand All @@ -77,11 +87,13 @@ export interface EnvironmentSettings {
version: string;
webAppEnvVariables: EnvironmentVariables;
certificates: CertificatesConfig;
CIConfig: CIConfig;
}

interface ConfigFileContent {
toolsConfig: ToolsConfig;
webAppConfig: WebAppConfig;
CIConfig: CIConfig;
}

export interface EnvConfigFileContent {
Expand Down Expand Up @@ -109,6 +121,10 @@ async function readConfig(): Promise<ConfigFileContent> {
versionMatrix: process.env.SB_TOOLS_DOMAIN_VERSION_MATRIX,
},
},
CIConfig: {
mode: process.env.SB_CI_MODE === CI_MODE.SIMPLE
? CI_MODE.SIMPLE : CI_MODE.PARALLEL,
}
};
}

Expand Down Expand Up @@ -176,5 +192,6 @@ export async function loadEnvSettings(): Promise<EnvironmentSettings> {
...(envConfig?.webAppConfig?.envVariables || {}),
},
certificates: envConfig.certificates,
CIConfig: config.CIConfig,
};
}
11 changes: 11 additions & 0 deletions packages/infra/infra-core/src/lib/patterns/serviceCiConfig.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Construct } from 'constructs';
import * as codebuild from 'aws-cdk-lib/aws-codebuild';
import { EnvConstructProps } from '../constructs';
import { CI_MODE } from '../env-config';
import { IStage } from 'aws-cdk-lib/aws-codepipeline';

export interface IServiceCiConfig {
defaultEnvVariables: {
Expand All @@ -22,9 +24,11 @@ export enum PnpmWorkspaceFilters {
export class ServiceCiConfig extends Construct implements IServiceCiConfig {
defaultEnvVariables: { [p: string]: codebuild.BuildEnvironmentVariable };
defaultCachePaths: string[];
props: EnvConstructProps;

constructor(scope: Construct, id: string, props: EnvConstructProps) {
super(scope, id);
this.props = props;

this.defaultEnvVariables = {
CI: {
Expand Down Expand Up @@ -69,4 +73,11 @@ export class ServiceCiConfig extends Construct implements IServiceCiConfig {
'export AWS_SESSION_TOKEN=$(echo "${TEMP_ROLE}" | jq -r \'.Credentials.SessionToken\')',
];
}

protected getRunOrder(stage: IStage, defaultRunOrder?: number) {
if (this.props.envSettings.CIConfig.mode === CI_MODE.PARALLEL) {
return defaultRunOrder;
}
return stage.actions.length + 1;
}
}
17 changes: 9 additions & 8 deletions packages/infra/infra-shared/src/stacks/ci/ciBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,25 @@ export class BackendCiConfig extends ServiceCiConfig {
),
);

const apiDeployProject = this.createApiDeployProject(props);
const migrationsDeployProject = this.createMigrationsDeployProject(props);
props.deployStage.addAction(
this.createDeployAction(
'api',
'migrations',
{
project: apiDeployProject,
runOrder: 2,
project: migrationsDeployProject,
runOrder: this.getRunOrder(props.deployStage, 2),
},
props,
),
);

const migrationsDeployProject = this.createMigrationsDeployProject(props);
const apiDeployProject = this.createApiDeployProject(props);
props.deployStage.addAction(
this.createDeployAction(
'migrations',
'api',
{
project: migrationsDeployProject,
runOrder: 2,
project: apiDeployProject,
runOrder: this.getRunOrder(props.deployStage, 2),
},
props,
),
Expand All @@ -72,6 +72,7 @@ export class BackendCiConfig extends ServiceCiConfig {
actionName: `${props.envSettings.projectEnvName}-build-${name}`,
project: actionProps.project,
input: props.inputArtifact,
runOrder: this.getRunOrder(props.buildStage),
});
}

Expand Down
2 changes: 1 addition & 1 deletion packages/infra/infra-shared/src/stacks/ci/ciComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class ComponentsCiConfig extends ServiceCiConfig {
project: actionProps.project,
actionName: `${props.envSettings.projectEnvName}-deploy-components`,
input: props.inputArtifact,
runOrder: 1,
runOrder: this.getRunOrder(props.deployStage, 1),
});
}

Expand Down
3 changes: 2 additions & 1 deletion packages/infra/infra-shared/src/stacks/ci/ciDocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class DocsCiConfig extends ServiceCiConfig {
{
project: deployProject,
input: buildArtifact,
runOrder: 2,
runOrder: this.getRunOrder(props.deployStage, 2),
},
props,
),
Expand All @@ -61,6 +61,7 @@ export class DocsCiConfig extends ServiceCiConfig {
>{
...actionProps,
actionName: `${props.envSettings.projectEnvName}-build-docs`,
runOrder: this.getRunOrder(props.buildStage)
});
}

Expand Down
4 changes: 2 additions & 2 deletions packages/infra/infra-shared/src/stacks/ci/ciPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ export class CiPipeline extends Construct {
inputArtifact: sourceOutputArtifact,
});

new DocsCiConfig(this, 'DocsConfig', {
new ServerlessCiConfig(this, 'WorkersConfig', {
envSettings: props.envSettings,
buildStage,
deployStage,
inputArtifact: sourceOutputArtifact,
});

new ServerlessCiConfig(this, 'WorkersConfig', {
new DocsCiConfig(this, 'DocsConfig', {
envSettings: props.envSettings,
buildStage,
deployStage,
Expand Down
3 changes: 2 additions & 1 deletion packages/infra/infra-shared/src/stacks/ci/ciServerless.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class ServerlessCiConfig extends ServiceCiConfig {
{
project: deployProject,
input: buildArtifact,
runOrder: 2,
runOrder: this.getRunOrder(props.buildStage, 2),
},
props,
),
Expand All @@ -61,6 +61,7 @@ export class ServerlessCiConfig extends ServiceCiConfig {
>{
...actionProps,
actionName: `${props.envSettings.projectEnvName}-build-workers`,
runOrder: this.getRunOrder(props.deployStage),
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class UploadVersionCiConfig extends ServiceCiConfig {
{
project: deployProject,
input: props.inputArtifact,
runOrder: 3,
runOrder: this.getRunOrder(props.stage, 3),
},
props
)
Expand Down
3 changes: 2 additions & 1 deletion packages/infra/infra-shared/src/stacks/ci/ciWebApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class WebappCiConfig extends ServiceCiConfig {
{
project: deployProject,
input: buildArtifact,
runOrder: 2,
runOrder: this.getRunOrder(props.deployStage, 2),
},
props,
),
Expand All @@ -61,6 +61,7 @@ export class WebappCiConfig extends ServiceCiConfig {
>{
...actionProps,
actionName: `${props.envSettings.projectEnvName}-build-webapp`,
runOrder: this.getRunOrder(props.buildStage)
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ CNAME DNS records pointing to CloudFront distribution and Load Balancer need to

### \[Optional\] Configure production domain

SaaS Boilerplate creates a certificate that supports `*.[ENV_STAGE].example.com` domains, like
<ProjectName /> creates a certificate that supports `*.[ENV_STAGE].example.com` domains, like
`app.production.example.com`. You most likely don't want such domain and need straight up `app.example.com` for your
production environment. Set a following variable to override the default behaviour:

Expand All @@ -101,6 +101,32 @@ Resource handler returned message: "Invalid request provided: AWS::CloudFront::D
attached to your distribution doesn't cover the alternate domain name (CNAME) that you're trying to add.
```

### [Optional] Configuring CI mode

:::caution
Deploying `<ProjectName />` on a **new AWS account** may result in reaching the AWS CodeBuild concurrent build limit.
This is a common issue with newly created AWS accounts. To mitigate potential issues during CI builds, it is advisable
to configure the CI mode to `simple`. For more information about potential issues with new AWS accounts, see
[Configuring AWS Credentials for New Accounts](./configure-aws-credentials#for-users-with-newly-created-aws-accounts).
:::

Changing the CI mode affects the execution behavior of CI processes:
- `parallel` (the default setting): Enables actions in the AWS CodePipeline stages to execute concurrently.
- `simple`: Ensures actions in the AWS CodePipeline stages are executed sequentially, with only one CodeBuild process
running at a time.

We recommend utilizing the `parallel` mode for optimal performance and efficiency, provided that the concurrent build
limits of AWS CodeBuild have been adjusted accordingly.

```shell
pnpm saas aws set-var SB_CI_MODE <parallel/simple>
```

:::info
Note: If you modify the settings for an already deployed environment, it is necessary to
[redeploy the CI stack](./deploy-infrastructure#deploy-the-infrastructure) to ensure that the changes take effect.
:::

### \[Optional\] Protect the website with basic auth

It's a good idea to prevent unauthorized access to staging environments so we suggest to set a basic auth password
Expand Down

0 comments on commit d4d5d04

Please sign in to comment.