|
| 1 | +### CI/CD integration |
| 2 | + |
| 3 | +> info **Hint** This chapter covers the Nest Devtools integration with the Nest framework. If you are looking for the Devtools application, please visit the [Devtools](https://devtools.nestjs.com) website. |
| 4 | +
|
| 5 | +CI/CD integration is available for users with the **[Enterprise](/settings)** plan. |
| 6 | + |
| 7 | +#### Publishing graphs |
| 8 | + |
| 9 | +Let's first configure the application bootstrap file (`main.ts`) to use the `GraphPublisher` class (exported from the `@nestjs/devtools-integration` - see previous chapter for more details), as follows: |
| 10 | + |
| 11 | +```typescript |
| 12 | +async function bootstrap() { |
| 13 | + const shouldPublishGraph = process.env.PUBLISH_GRAPH === "true"; |
| 14 | + |
| 15 | + const app = await NestFactory.create(AppModule, { |
| 16 | + snapshot: true, |
| 17 | + preview: shouldPublishGraph, |
| 18 | + }); |
| 19 | + |
| 20 | + if (shouldPublishGraph) { |
| 21 | + await app.init(); |
| 22 | + |
| 23 | + const publishOptions = { ... } // NOTE: this options object will vary depending on the CI/CD provider you're using |
| 24 | + const graphPublisher = new GraphPublisher(app); |
| 25 | + await graphPublisher.publish(publishOptions); |
| 26 | + |
| 27 | + await app.close(); |
| 28 | + } else { |
| 29 | + await app.listen(3000); |
| 30 | + } |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +As we can see, we're using the `GraphPublisher` here to publish our serialized graph to the centralized registry. The `PUBLISH_GRAPH` is a custom environment variable that will let us control whether the graph should be published (CI/CD workflow), or not (regular application bootstrap). Also, we set the `preview` attribute here to `true`. With this flag enabled, our application will bootstrap in the preview mode - which basically means that constructors (and lifecycle hooks) of all controllers, enhancers, and providers in our application will not be executed. Note - this isn't **required**, but makes things simpler for us since in this case we won't really have to connect to the database etc. when running our application in the CI/CD pipeline. |
| 35 | + |
| 36 | +The `publishOptions` object will vary depending on the CI/CD provider you're using. We will provide you with instructions for the most popular CI/CD providers below, in later sections. |
| 37 | + |
| 38 | +Once the graph is successfully published, you'll see the following output in your workflow view: |
| 39 | + |
| 40 | +<figure><img src="/assets/devtools/graph-published-terminal.png" /></figure> |
| 41 | + |
| 42 | +Every time our graph is published, we should see a new entry in the project's corresponding page: |
| 43 | + |
| 44 | +<figure><img src="/assets/devtools/project.png" /></figure> |
| 45 | + |
| 46 | +#### Reports |
| 47 | + |
| 48 | +Devtools generate a report for every build **IF** there's a corresponding snapshot already stored in the centralized registry. So for example, if you create a PR against the `master` branch for which the graph was already published - then the application will be able to detect differences and generate a report. Otherwise, the report will not be generated. |
| 49 | + |
| 50 | +To see reports, navigate to the project's corresponding page (see organizations). |
| 51 | + |
| 52 | +<figure><img src="/assets/devtools/report.png" /></figure> |
| 53 | + |
| 54 | +#### Build preview |
| 55 | + |
| 56 | +For every published graph we can go back in time and preview how it looked before by clicking at the **Preview** button. Furthermore, if the report was generated, we should see the differences higlighted on our graph: |
| 57 | + |
| 58 | +- green nodes represent added elements |
| 59 | +- light white nodes represent updated elements |
| 60 | +- red nodes represent deleted elements |
| 61 | + |
| 62 | +See screenshot below: |
| 63 | + |
| 64 | +<figure><img src="/assets/devtools/nodes-selection.png" /></figure> |
| 65 | + |
| 66 | +#### Integrations: Github Actions |
| 67 | + |
| 68 | +First let's start from creating a new Github workflow in the `.github/workflows` directory in our project and call it, for example, `publish-graph.yml`. Inside this file, let's use the following definition: |
| 69 | + |
| 70 | +```yaml |
| 71 | +name: Devtools |
| 72 | + |
| 73 | +on: |
| 74 | + push: |
| 75 | + branches: |
| 76 | + - master |
| 77 | + pull_request: |
| 78 | + branches: |
| 79 | + - '*' |
| 80 | + |
| 81 | +jobs: |
| 82 | + publish: |
| 83 | + if: github.actor!= 'dependabot[bot]' |
| 84 | + name: Publish graph |
| 85 | + runs-on: ubuntu-latest |
| 86 | + steps: |
| 87 | + - uses: actions/checkout@v3 |
| 88 | + - uses: actions/setup-node@v3 |
| 89 | + with: |
| 90 | + node-version: '16' |
| 91 | + cache: 'npm' |
| 92 | + - name: Install dependencies |
| 93 | + run: npm ci |
| 94 | + - name: Setup Environment (PR) |
| 95 | + if: {{ '${{' }} github.event_name == 'pull_request' {{ '}}' }} |
| 96 | + shell: bash |
| 97 | + run: | |
| 98 | + echo "COMMIT_SHA={{ '${{' }} github.event.pull_request.head.sha {{ '}}' }}" >>\${GITHUB_ENV} |
| 99 | + - name: Setup Environment (Push) |
| 100 | + if: {{ '${{' }} github.event_name == 'push' {{ '}}' }} |
| 101 | + shell: bash |
| 102 | + run: | |
| 103 | + echo "COMMIT_SHA=\${GITHUB_SHA}" >> \${GITHUB_ENV} |
| 104 | + - name: Publish |
| 105 | + run: PUBLISH_GRAPH=true npm run start |
| 106 | + env: |
| 107 | + DEVTOOLS_API_KEY: CHANGE_THIS_TO_YOUR_API_KEY |
| 108 | + REPOSITORY_NAME: {{ '${{' }} github.event.repository.name {{ '}}' }} |
| 109 | + BRANCH_NAME: {{ '${{' }} github.head_ref || github.ref_name {{ '}}' }} |
| 110 | + TARGET_SHA: {{ '${{' }} github.event.pull_request.base.sha {{ '}}' }} |
| 111 | +``` |
| 112 | +
|
| 113 | +Ideally, `DEVTOOLS_API_KEY` environment variable should be retrieved from Github Secrets, read more [here](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) . |
| 114 | + |
| 115 | +This workflow will run per each pull request that's targeting the `master` branch OR in case there's a direct commit to the `master` branch. Feel free to align this configuration to whatever your project needs. What's essential here is that we provide necessary environment varaiables for our `GraphPublisher` class (to run). |
| 116 | + |
| 117 | +However, there's one variable that needs to be updated before we can start using this workflow - `DEVTOOLS_API_KEY`. We can generate an API key dedicated for our project on this **page** . |
| 118 | + |
| 119 | +Lastly, let's navigate to the `main.ts` file again and update the `publishOptions` object we previously left empty. |
| 120 | + |
| 121 | +```typescript |
| 122 | +const publishOptions = { |
| 123 | + apiKey: process.env.DEVTOOLS_API_KEY, |
| 124 | + repository: process.env.REPOSITORY_NAME, |
| 125 | + owner: process.env.GITHUB_REPOSITORY_OWNER, |
| 126 | + sha: process.env.COMMIT_SHA, |
| 127 | + target: process.env.TARGET_SHA, |
| 128 | + trigger: process.env.GITHUB_BASE_REF ? 'pull' : 'push', |
| 129 | + branch: process.env.BRANCH_NAME, |
| 130 | +}; |
| 131 | +``` |
| 132 | + |
| 133 | +For the best developer experience, make sure to integrate the **Github application** for your project by clicking on the "Integrate Github app" button (see screenshot below). Note - this isn't required. |
| 134 | + |
| 135 | +<figure><img src="/assets/devtools/integrate-github-app.png" /></figure> |
| 136 | + |
| 137 | +With this integration, you'll be able to see the status of the preview/report generation process right in your pull request: |
| 138 | + |
| 139 | +<figure><img src="/assets/devtools/actions-preview.png" /></figure> |
| 140 | + |
| 141 | +#### Integrations: Gitlab Pipelines |
| 142 | + |
| 143 | +First let's start from creating a new Gitlab CI configuration file in the root directory of our project and call it, for example, `.gitlab-ci.yml`. Inside this file, let's use the following definition: |
| 144 | + |
| 145 | +```typescript |
| 146 | +const publishOptions = { |
| 147 | + apiKey: process.env.DEVTOOLS_API_KEY, |
| 148 | + repository: process.env.REPOSITORY_NAME, |
| 149 | + owner: process.env.GITHUB_REPOSITORY_OWNER, |
| 150 | + sha: process.env.COMMIT_SHA, |
| 151 | + target: process.env.TARGET_SHA, |
| 152 | + trigger: process.env.GITHUB_BASE_REF ? 'pull' : 'push', |
| 153 | + branch: process.env.BRANCH_NAME, |
| 154 | +}; |
| 155 | +``` |
| 156 | + |
| 157 | +> info **Hint** Ideally, `DEVTOOLS_API_KEY` environment variable should be retrieved from secrets. |
| 158 | + |
| 159 | +This workflow will run per each pull request that's targeting the `master` branch OR in case there's a direct commit to the `master` branch. Feel free to align this configuration to whatever your project needs. What's essential here is that we provide necessary environment variables for our `GraphPublisher` class (to run). |
| 160 | + |
| 161 | +However, there's one variable (in this workflow definition) that needs to be updated before we can start using this workflow - `DEVTOOLS_API_KEY`. We can generate an API key dedicated for our project on this **page** . |
| 162 | + |
| 163 | +Lastly, let's navigate to the `main.ts` file again and update the `publishOptions` object we previously left empty. |
| 164 | + |
| 165 | +```yaml |
| 166 | +image: node:16 |
| 167 | +
|
| 168 | +stages: |
| 169 | + - build |
| 170 | +
|
| 171 | +cache: |
| 172 | + key: |
| 173 | + files: |
| 174 | + - package-lock.json |
| 175 | + paths: |
| 176 | + - node_modules/ |
| 177 | +
|
| 178 | +workflow: |
| 179 | + rules: |
| 180 | + - if: $CI_PIPELINE_SOURCE == "merge_request_event" |
| 181 | + when: always |
| 182 | + - if: $CI_COMMIT_BRANCH == "master" && $CI_PIPELINE_SOURCE == "push" |
| 183 | + when: always |
| 184 | + - when: never |
| 185 | +
|
| 186 | +install_dependencies: |
| 187 | + stage: build |
| 188 | + script: |
| 189 | + - npm ci |
| 190 | +
|
| 191 | +publish_graph: |
| 192 | + stage: build |
| 193 | + needs: |
| 194 | + - install_dependencies |
| 195 | + script: npm run start |
| 196 | + variables: |
| 197 | + PUBLISH_GRAPH: 'true' |
| 198 | + DEVTOOLS_API_KEY: 'CHANGE_THIS_TO_YOUR_API_KEY' |
| 199 | +``` |
| 200 | + |
| 201 | +#### Other CI/CD tools |
| 202 | + |
| 203 | +Nest Devtools CI/CD integration can be used with any CI/CD tool of your choice (e.g., [Bitbucket Pipelines](https://bitbucket.org/product/features/pipelines) , [CircleCI](https://circleci.com/), etc) so don't feel limited to providers we described here. |
| 204 | + |
| 205 | +Look at the following `publishOptions` object configuration to understand what information is required to publish the graph for a given commit/build/PR. |
| 206 | + |
| 207 | +```typescript |
| 208 | +const publishOptions = { |
| 209 | + apiKey: process.env.DEVTOOLS_API_KEY, |
| 210 | + repository: process.env.CI_PROJECT_NAME, |
| 211 | + owner: process.env.CI_PROJECT_ROOT_NAMESPACE, |
| 212 | + sha: process.env.CI_COMMIT_SHA, |
| 213 | + target: process.env.CI_MERGE_REQUEST_DIFF_BASE_SHA, |
| 214 | + trigger: process.env.CI_MERGE_REQUEST_DIFF_BASE_SHA ? 'pull' : 'push', |
| 215 | + branch: |
| 216 | + process.env.CI_COMMIT_BRANCH ?? |
| 217 | + process.env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME, |
| 218 | +}; |
| 219 | +``` |
| 220 | + |
| 221 | +Most of this information is provided through CI/CD built-in environment variables (see [CircleCI built-in environment list](https://circleci.com/docs/variables/#built-in-environment-variables) and [Bitbucket variables](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/) ). |
| 222 | + |
| 223 | +When it comes to the pipeline configuration for publishing graphs, we recommend using the following triggers: |
| 224 | + |
| 225 | +- `push` event - only if the current branch represents a deployment environment, for example `master`, `main`, `staging`, `production`, etc. |
| 226 | +- `pull request` event - always, or when the **target branch** represents a deployment environment (see above) |
0 commit comments