# Continuous Integration

See also [CI/CD Quickstart Guide](../quick-start/README.md#continuous-integration).

## CI/CD Architecture

Mira utilizes cloud-native services for the delivery pipeline.

Key AWS Services used:
* [AWS CodePipeline](https://aws.amazon.com/codepipeline/getting-started/)
* [AWS CodeBuild](https://docs.aws.amazon.com/codebuild/latest/userguide/getting-started.html)
* [AWS CodeCommit](https://docs.aws.amazon.com/codecommit/latest/userguide/getting-started-cc.html)

AWS CodePipeline is used for overall orchestration of the delivery pipeline. It is capable of detecting changes in the
CodeCommit repository and triggers when required. A single CodePipeline is defined for delivery, with the pipeline itself built from one or more stages of pre-configured actions.

For code deployment, Mira uses AWS CodeBuild which a dedicated service for artifacts build up and optionally deployment.
Behind the scenes CodeBuild triggers the same Mira commands as a Developer from local workstation.
In order to provide permissions for CodeBuild to deploy stacks, Mira provision dedicated Role(s) in predefined accounts.

Mira CI/CD can deploy the code into the same AWS account where it is hosted, but also to different accounts as required.
You define deployment stages by configuring the `cicd.accounts` value in the `config/default.json` file.

A typical pipeline would look like this:

`Source -> Test -> Manual Approval -> Staging -> Manual Approval -> Production`

For more information on available configuration options see [config documentation](../config/README.md). 

__Note:__ CI/CD assumes [github actions](https://github.com/features/actions) are used for code mirroring into AWS CodeCommit. See the `.github` directory for more information.
Otherwise, the developer is responsible for mirroring the code into the dedicated AWS CodeCommit or use AWS CodeCommit directly.

## Deploy Role

CodeBuild runs with an internal role generated by the pipeline. However, Mira needs to assume a specific role with all the permissions for the app.

To do this, create a `DeploymentPermissions` subclass in your app like the following:

```
import { DeploymentPermissions, MiraApp } from '@nearform/mira'
export default class CustomPermissions extends DeploymentPermissions {
  constructor(parent: Construct) {
    super(parent)
    const account = MiraConfig.getEnvironment()
    const baseProject = MiraApp.getBaseStackName()
    this.role.addToPolicy(new PolicyStatement({
      actions: [
        's3:CreateBucket',
        ...
      ],
      resources: [
        `arn:aws:s3:::${baseProject.toLowerCase()}-*`
      ]
    }))

    this.role.addToPolicy(new PolicyStatement({
      actions: [
        'iam:CreateRole',
        ...
      ],
      resources: [
        `arn:aws:iam::${account.env.account}:role/${baseProject}-*`
      ]
    }))
  }
}
```

Then, modify your `cicd` section in the config file (`default.json`) and create/update property `permissionsFile` with the path to the previously created file, e.g.:

```
"cicd": {
    "target": "cicd",
    "buildspecFile": "infra/buildspec.yaml",
    "permissionsFile": "infra/src/permissions.js",
    "provider": "codecommit",
    "repositoryUrl": "YOUR_REPOSITORY_URL",
    "branchName": "master",
    "codeCommitUserPublicKey": "ssh-rsa xxx",
    "stages": [
      {
        "target": "staging",
        "withDomain": false,
        "requireManualApproval": false,
        "privileged": true
      },
      {
        "target": "production",
        "withDomain": false,
        "requireManualApproval": true,
        "privileged": true
      }
    ]
  }

```

## Deploy Pipeline

You can deploy the pipeline with the following command:

```
npx mira cicd
```
For each `cicd.accounts` element in the configuration file, a deploy stage and stack with deployment permissions role is created.

For each value it's possible to specify a previous manual `Promote` action if `requireManualApproval: true`.

If you need variables or secrets or both available during your deploy phase, you can use `--envVar` as shown in the example below:

### Example 
Deploy the pipeline with a variable and a secret as follows:
```
npx mira cicd --envVar SECRET_TOKEN=123456789 --envVar FOO=bar
```

Then, you can use the secret in `buildspec.yml` file as follows:
```
...
build:
    commands:
      - ./login-to-service.sh --token=${SECRET_TOKEN}
      - npx mira deploy
```


