├── .circleci ├── config.yml └── test-deploy.yml ├── .github ├── ISSUE_TEMPLATE │ ├── BUG.md │ ├── FEATURE_REQUEST.md │ └── config.yml ├── PULL_REQUEST_TEMPLATE │ └── PULL_REQUEST.md └── assets │ ├── nx-logo.png │ └── nx.png ├── .gitignore ├── .yamllint ├── CHANGELOG.md ├── COMMIT ├── CONTRIBUTING.md ├── LICENSE ├── README.md └── src ├── @orb.yml ├── README.md ├── commands └── set-shas.yml ├── examples ├── custom.yml ├── default.yml ├── private.yml └── tags.yml └── scripts ├── find-successful-workflow.js └── set-shas.sh /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | setup: true 3 | orbs: 4 | orb-tools: circleci/orb-tools@12 5 | shellcheck: circleci/shellcheck@3.2 6 | 7 | filters: &filters 8 | tags: 9 | only: /.*/ 10 | 11 | workflows: 12 | lint-pack: 13 | jobs: 14 | - orb-tools/lint: 15 | filters: *filters 16 | - orb-tools/pack: 17 | filters: *filters 18 | - orb-tools/review: 19 | filters: *filters 20 | exclude: RC010 # This rule would force us to use snake case for command names 21 | - shellcheck/check: 22 | exclude: SC2148,SC2038,SC2086,SC2002,SC2016 23 | filters: *filters 24 | - orb-tools/publish: 25 | orb_name: nrwl/nx 26 | vcs_type: << pipeline.project.type >> 27 | enable_pr_comment: false 28 | requires: 29 | - orb-tools/lint 30 | - orb-tools/review 31 | - orb-tools/pack 32 | - shellcheck/check 33 | context: 34 | - orb-publishing-context 35 | filters: *filters 36 | # Triggers the next workflow in the Orb Development Kit. 37 | - orb-tools/continue: 38 | orb_name: nrwl/nx 39 | pipeline_number: << pipeline.number >> 40 | vcs_type: << pipeline.project.type >> 41 | requires: [orb-tools/publish] 42 | filters: *filters 43 | -------------------------------------------------------------------------------- /.circleci/test-deploy.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | nx: nrwl/nx@dev:<> 4 | orb-tools: circleci/orb-tools@12 5 | jobs: 6 | set-shas-integration-test: 7 | docker: 8 | - image: cimg/node:18.17.1 9 | steps: 10 | - checkout 11 | - nx/set-shas 12 | - run: 13 | name: Check values 14 | shell: "/bin/bash" 15 | command: | 16 | HEAD_SHA=$(git rev-parse HEAD) 17 | if [[ $CIRCLE_BRANCH == "main" ]]; then 18 | BASE_SHA=$(git rev-parse origin/main~1) 19 | else 20 | BASE_SHA=$(git merge-base origin/main HEAD) 21 | fi 22 | echo "Comparing head: $NX_HEAD to $HEAD_SHA" 23 | echo "Comparing base: $NX_BASE to $BASE_SHA" 24 | if [[ $NX_HEAD == $HEAD_SHA && $NX_BASE == $BASE_SHA ]]; then 25 | echo "Test conditions met" 26 | else 27 | echo "Test conditions NOT met" 28 | exit 5 29 | fi 30 | 31 | workflows: 32 | test-deploy: 33 | jobs: 34 | - set-shas-integration-test: 35 | filters: &filters 36 | tags: 37 | ignore: /.*/ 38 | - orb-tools/pack: 39 | filters: &filters 40 | tags: 41 | only: /.*/ 42 | - orb-tools/publish: 43 | orb_name: nrwl/nx 44 | vcs_type: << pipeline.project.type >> 45 | pub_type: production 46 | enable_pr_comment: false 47 | requires: 48 | - set-shas-integration-test 49 | - orb-tools/pack 50 | context: 51 | - orb-publishing-context 52 | filters: 53 | branches: 54 | ignore: /.*/ 55 | tags: 56 | only: /^v[0-9]+\.[0-9]+\.[0-9]+$/ 57 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41E Bug report" 3 | about: Report any bugs encountered while using this orb. 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Orb version: 11 | 12 | 19 | 20 | ## What happened: 21 | 22 | 26 | 27 | ## Expected behavior: 28 | 29 | 30 | 31 | ## Additional Information: 32 | 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature Request" 3 | about: Propose changes to the orb. 4 | title: '' 5 | labels: feature_request 6 | assignees: '' 7 | --- 8 | 9 | ## Describe Request: 10 | 11 | ## Examples: 12 | 13 | ## Supporting Documentation Links: 14 | 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST.md: -------------------------------------------------------------------------------- 1 | 2 | **SEMVER Update Type:** 3 | - [ ] Major 4 | - [ ] Minor 5 | - [ ] Patch 6 | 7 | ## Description: 8 | 9 | 13 | 14 | ## Motivation: 15 | 16 | 19 | 20 | **Closes Issues:** 21 | - ISSUE URL 22 | 23 | ## Checklist: 24 | 25 | 30 | 31 | - [ ] All new jobs, commands, executors, parameters have descriptions. 32 | - [ ] Usage Example version numbers have been updated. 33 | - [ ] Changelog has been updated. -------------------------------------------------------------------------------- /.github/assets/nx-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nrwl/nx-orb/b3949da5ce8dea905f8aa840c269875d7a518076/.github/assets/nx-logo.png -------------------------------------------------------------------------------- /.github/assets/nx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nrwl/nx-orb/b3949da5ce8dea905f8aa840c269875d7a518076/.github/assets/nx.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # orb.yml is "packed" from source, and not published directly from the repository. 2 | orb.yml 3 | .idea -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | extends: relaxed 2 | 3 | rules: 4 | line-length: 5 | max: 200 6 | allow-non-breakable-inline-mappings: true 7 | 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | - **[fix]**: Delete generated script file on Orb run 9 | - **[feat]**: set BASE to empty tree if HEAD~1 does not exist 10 | 11 | ## [1.6.2] - 2023-09-11 12 | - **[fix]**: Provide more information for private repos when missing access 13 | - **[chore]**: Update tooling for orbs 14 | 15 | ## [1.6.1] - 2022-10-14 16 | - **[fix]**: Use fallback on CirclCI API glitches 17 | - **[fix]**: Fallback to main branch when CIRCLE_BRANCH is missing 18 | - **[fix]**: Incorrect API page query param 19 | 20 | ## [1.6.0] - 2022-08-09 21 | - **[fix]**: Use explicitly main branch's previous commit when last successful sha was not found 22 | - **[fix]**: Use CircleCI internal env var to check if run is pull request 23 | - **[feat]**: Optionally disable filtering the past workflow runs by branch 24 | 25 | ## [1.5.1] - 2022-06-27 26 | - **[fix]**: Build url regex broken for pipeline ids < 10 27 | 28 | ## [1.5.0] - 2022-05-27 29 | - **[feat]**: Support CircleCI Enterprise setups 30 | 31 | ## [1.4.1] - 2022-05-11 32 | - **[fix]**: Fix handling of boolean parameters 33 | 34 | ## [1.4.0] - 2022-04-05 35 | ### Added 36 | - **[feat]**: Add support for `on-hold` workflows 37 | 38 | ## [1.3.0] - 2022-03-24 39 | ### Added 40 | - **[feat]**: Add main branch name environment variable support 41 | 42 | ## [1.2.2] - 2022-03-17 43 | ### Added 44 | - **[fix]**: Improve error message on missing or invalid private token 45 | 46 | [1.2.2]: https://github.com/nrwl/nx-orb/releases/tag/v1.2.2 47 | 48 | ## [1.2.1] - 2022-03-17 49 | ### Changed 50 | - **[fix]**: Enable cross browser commit check 51 | 52 | [1.2.1]: https://github.com/nrwl/nx-orb/releases/tag/v1.2.1 53 | 54 | ## [1.2.0] - 2022-03-16 55 | ### Added 56 | - **[feature]**: Add support for targeting specific workflow 57 | ### Changed 58 | - **[docs]**: Update information on `private.yml` example 59 | 60 | [1.2.0]: https://github.com/nrwl/nx-orb/releases/tag/v1.2.0 61 | 62 | ## [1.1.4] - 2022-03-15 63 | ### Added 64 | - **[docs]**: Create `private.yml` example 65 | 66 | [1.1.4]: https://github.com/nrwl/nx-orb/releases/tag/v1.1.4 67 | 68 | ## [1.1.3] - 2021-12-28 69 | ### Added 70 | - **[docs]**: Add private token information to README 71 | 72 | [1.1.3]: https://github.com/nrwl/nx-orb/releases/tag/v1.1.3 73 | 74 | ## [1.1.2] - 2021-10-28 75 | ### Added 76 | - **[docs]**: Add information for private repositories to README 77 | 78 | [1.1.2]: https://github.com/nrwl/nx-orb/releases/tag/v1.1.2 79 | 80 | ## [1.1.0] - 2021-09-20 81 | ### Added 82 | - **[feature]**: Support for private repos 83 | 84 | [1.1.0]: https://github.com/nrwl/nx-orb/releases/tag/v1.1.0 85 | 86 | ## [1.0.0] - 2021-07-23 87 | ### Added 88 | - Initial Release 89 | ### Changed 90 | - Initial Release 91 | ### Removed 92 | - Initial Release 93 | 94 | [1.0.0]: https://github.com/nrwl/nx-orb/releases/tag/v1.0.0 95 | -------------------------------------------------------------------------------- /COMMIT: -------------------------------------------------------------------------------- 1 | File is here to submit commits to trigger releases 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to nx-orb 2 | 3 | We welcome [issues](https://github.com/nrwl/nx-orb/issues) to and [pull requests](https://github.com/nrwl/nx-orb/pulls) against this repository! Read this document to see how to do it. 4 | 5 | ## How to Contribute 6 | * Create and push a branch with your new features. 7 | * When ready to publish a new production version, create a Pull Request from _feature branch_ to `master`. 8 | * The title of the pull request should follow [commit message guideline](#commit-message-guideline) 9 | * Squash and merge. Ensure the semver tag is preserved and entered as a part of the commit message. 10 | * On merge, after manual approval, the orb will automatically be published to the Orb Registry. 11 | 12 | #### Commit Message Guidelines 13 | 14 | The commit message should follow the following format: 15 | 16 | ``` 17 | type: subject 18 | BLANK LINE 19 | body 20 | ``` 21 | 22 | ##### Type 23 | 24 | The type must be one of the following: 25 | 26 | - feat - New or improved behavior being introduced (e.g. adding new command) 27 | - fix - Fixes the current unexpected behavior to match expected behavior (e.g. fixing the wrong commit search logic) 28 | - cleanup - Code Style changes that have little to no effect on the user (e.g. Refactoring some function) 29 | - docs - Changes to the documentation (e.g. Adding more details into the examples) 30 | - chore - Changes that have absolutely no effect on users (e.g. Renaming files) 31 | 32 | Optionally, type can have a scope (e.g. `feat(set-shas)`) 33 | 34 | ##### Subject and Body 35 | 36 | The subject must contain a description of the change, and the body of the message contains any additional details to provide more context about the change. 37 | 38 | Including the issue number that the PR relates to also helps with tracking. 39 | 40 | ##### Example 41 | 42 | ``` 43 | feat(set-shas): add optinal workflow-id 44 | 45 | Add workflow-id to filter successful workflows 46 | 47 | Closes #157 48 | ``` 49 | 50 | ### Publishing 51 | 52 | 53 | #### Test the PR 54 | As a contributor, inspect the PR. When you are satisfied, run the following command to pull in the changes to allow the CI job to run. 55 | 56 | ``` 57 | gh pr checkout 58 | git push origin HEAD 59 | ``` 60 | #### Publish 61 | 1. Draft a new [Release](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release) 62 | 2. Temporarily set a tag for the release. ex: `v1.2.3` 63 | 3. Click the `+auto-generate` notes button 64 | 4. Review the commits added to the release. Use conventional-commits to determine if the tag selected is appropriate for the changes included, and adjust the tag version number if needed. 65 | 5. Publish the release. The new tag will trigger a CI deployment job. 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Narwhal Technologies Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Nx - Smart, Extensible Build Framework

3 | 4 |

NX Orb

5 | 6 | [![CircleCI Build Status](https://circleci.com/gh/nrwl/nx-orb.svg?style=shield "CircleCI Build Status")](https://circleci.com/gh/nrwl/nx-orb) [![CircleCI Orb Version](https://badges.circleci.com/orbs/nrwl/nx.svg)](https://circleci.com/orbs/registry/orb/nrwl/nx) [![GitHub License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://raw.githubusercontent.com/nrwl/nx-orb/master/LICENSE) [![CircleCI Community](https://img.shields.io/badge/community-CircleCI%20Discuss-343434.svg)](https://discuss.circleci.com/c/ecosystem/orbs) 7 | 8 | 9 | > ✨ A CircleCI Orb which includes helpful commands for running Nx commands in the CI 10 | 11 | ## Usage 12 | 13 | ```yaml 14 | version: 2.1 15 | 16 | orbs: 17 | nx: nrwl/nx@1.7.0 18 | 19 | jobs: 20 | checks: 21 | docker: 22 | - image: cimg/node:lts-browsers 23 | steps: 24 | - checkout 25 | - run: 26 | name: Install dependencies 27 | command: yarn install --frozen-lockfile 28 | - nx/set-shas 29 | - run: 30 | name: Run Builds 31 | command: yarn nx affected --target=build --base=$NX_BASE --parallel 32 | - run: 33 | name: Run Unit Tests 34 | command: yarn nx affected --target=test --base=$NX_BASE --parallel 35 | ``` 36 | 37 | ## Background 38 | 39 | When we run [affected](https://nx.dev/using-nx/affected) command on [Nx](https://nx.dev/), we can specify 2 git history positions - base and head, and it calculates [which projects in your repository changed 40 | between those 2 commits](https://nx.dev/node-tutorial/07-test-affected-projects#test-affected-projects). We can then run a set of tasks (like building or linting) only on those affected projects. 41 | 42 | This makes it easy to set-up a CI system that scales well with the continous growth of your repository, as you add more and more projects. 43 | 44 | ### Problem 45 | 46 | Figuring out what these two git commits are might not be as simple as it seems. 47 | 48 | On a CI system that runs on submitted PRs, we determine what commits to include in the **affected** calculation by comparing our `HEAD-commit-of-PR-branch` to the commit in main branch (`master` or `main` usually) from which the PR branch originated. This will ensure the entirety of our PR is always being tested. 49 | 50 | But what if we want to set up a continuous deployment system 51 | that, as changes get pushed to `master`, it builds and deploys 52 | only the affected projects? 53 | 54 | What are the `FROM` and `TO` commits in that case? 55 | 56 | Conceptually, what we want is to use the absolute latest commit on the `master` branch as the HEAD, and the previous _successful_ commit on `master` as the BASE. Note, we want the previous _successful_ one because it is still possible for commits on the `master` branch to fail for a variety of reasons. 57 | 58 | The commits therefore can't just be `HEAD` and `HEAD~1`. If a few deployments fail one after another, that means that we're accumulating a list of affected projects that are not getting deployed. Anytime we retry the deployment, we want to include **every commit since the last time we deployed successfully**. That way we ensure we don't accidentally skip deploying a project that has changed. 59 | 60 | This action enables you to find: 61 | * Commit SHA from which PR originated (in the case of `pull_request`) 62 | * Commit SHA of the last successful CI run 63 | 64 | ## Private repositories 65 | 66 | To use this orb with a private repository on your main branch, you need to grant the orb access to your CircleCI API. You can do this by creating an environment variable called `CIRCLE_API_TOKEN` in the context or the project. 67 | 68 | > Note: It should be a user token, not project token. 69 | 70 | ## License 71 | 72 | [MIT](http://opensource.org/licenses/MIT) 73 | 74 | Copyright (c) 2021-present Narwhal Technologies Inc. 75 | -------------------------------------------------------------------------------- /src/@orb.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | description: > 4 | A Orb which includes helpful commands for running Nx commands in the CI 5 | 6 | display: 7 | home_url: "https://nx.dev/" 8 | source_url: "https://github.com/nrwl/nx-orb" 9 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Orb Source 2 | 3 | Orbs are shipped as individual `orb.yml` files, however, to make development easier, it is possible to author an orb in _unpacked_ form, which can be _packed_ with the CircleCI CLI and published. 4 | 5 | The default `.circleci/config.yml` file contains the configuration code needed to automatically pack, test, and deploy and changes made to the contents of the orb source in this directory. 6 | 7 | ## @orb.yml 8 | 9 | This is the entry point for our orb "tree", which becomes our `orb.yml` file later. 10 | 11 | Within the `@orb.yml` we generally specify 4 configuration keys 12 | 13 | **Keys** 14 | 15 | 1. **version** 16 | Specify version 2.1 for orb-compatible configuration `version: 2.1` 17 | 2. **description** 18 | Give your orb a description. Shown within the CLI and orb registry 19 | 3. **display** 20 | Specify the `home_url` referencing documentation or product URL, and `source_url` linking to the orb's source repository. 21 | 4. **orbs** 22 | (optional) Some orbs may depend on other orbs. Import them here. 23 | 24 | ## See: 25 | 26 | - [Orb Author Intro](https://circleci.com/docs/2.0/orb-author-intro/#section=configuration) 27 | - [Reusable Configuration](https://circleci.com/docs/2.0/reusing-config) 28 | -------------------------------------------------------------------------------- /src/commands/set-shas.yml: -------------------------------------------------------------------------------- 1 | description: > 2 | Derives SHAs for base and head for use in `nx affected` commands in CI 3 | 4 | parameters: 5 | main-branch-name: 6 | type: string 7 | default: "main" 8 | description: | 9 | The name of the main branch in your repo, used as the target of PRs. E.g. main, master etc. 10 | error-on-no-successful-workflow: 11 | type: boolean 12 | default: false 13 | description: | 14 | By default, if no successful workflow is found on the main branch to determine the SHA, 15 | we will log a warning and use HEAD~1. Enable this option to error and exit instead. 16 | workflow-name: 17 | type: string 18 | default: "" 19 | description: | 20 | By default, the script is looking for the last successful job across all workflows. 21 | Set this param to search for the last successful job within a specific workflow. 22 | allow-on-hold-workflow: 23 | type: boolean 24 | default: false 25 | description: | 26 | By default, only workflows with CircleCI status of "success" will be detected for the main branch. 27 | Enable this option to also detect workflows with CircleCI status of "on_hold" in case your workflow requires manual approvals. 28 | allow-not-run-workflow: 29 | type: boolean 30 | default: false 31 | description: | 32 | By default, only workflows with CircleCI status of "success" will be detected for the main branch. 33 | Enable this option to also detect workflows with CircleCI status of "not_run" in case your workflow requires manual approvals. 34 | skip-branch-filter: 35 | type: boolean 36 | default: false 37 | description: | 38 | By default, the workflow runs will be filtered by `main` branch. This works fine with standard `push` event. If you 39 | want to use the orb for non-push events (e.g. tag, label etc.) you need to disable branch filtering. 40 | 41 | steps: 42 | - run: 43 | environment: 44 | PARAM_MAIN_BRANCH: <> 45 | PARAM_ERROR_ON_NO_SUCCESSFUL_WORKFLOW: <> 46 | PARAM_WORKFLOW_NAME: <> 47 | PARAM_ALLOW_ON_HOLD: <> 48 | PARAM_ALLOW_NOT_RUN: <> 49 | PARAM_SKIP_BRANCH_FILTER: <> 50 | PARAM_SCRIPT: <> 51 | name: Derives SHAs for base and head for use in `nx affected` commands 52 | shell: "/bin/bash" 53 | command: <> 54 | -------------------------------------------------------------------------------- /src/examples/custom.yml: -------------------------------------------------------------------------------- 1 | description: > 2 | You need to specify `main-branch-name` if different than `main`. 3 | If last successful workflow run was not found, by default we report warning and fallback to HEAD~1. 4 | You can instead make this a hard error by settting 'error-on-no-successful-workflow' to true. 5 | If you need to find the last successful job within a specific workflow, set the value of 'workflow-name'. 6 | By default we check for `success` state of the workflow. If you would like to include also `on_hold` states, 7 | you can do so by enabling `allow-on-hold-workflow`. 8 | usage: 9 | version: 2.1 10 | orbs: 11 | nx: nrwl/nx@1.7.0 12 | jobs: 13 | build: 14 | docker: 15 | - image: cimg/node:14.17-browsers 16 | environment: 17 | MAIN_BRANCH_NAME: master # environment variable has priority over command variable 18 | steps: 19 | - checkout 20 | - run: 21 | name: Install dependencies 22 | command: yarn install --frozen-lockfile 23 | - nx/set-shas: 24 | main-branch-name: "master" # you can also use the environment variable MAIN_BRANCH_NAME for this purpose 25 | error-on-no-successful-workflow: true 26 | workflow-name: "nx-pipeline" 27 | allow-on-hold-workflow: true 28 | - run: 29 | name: Run Builds 30 | command: yarn nx affected --target=build --base=$NX_BASE 31 | - run: 32 | name: Run Unit Tests 33 | command: yarn nx affected --target=test --base=$NX_BASE 34 | -------------------------------------------------------------------------------- /src/examples/default.yml: -------------------------------------------------------------------------------- 1 | description: > 2 | You can use `set-shas` without parameters using the default values provided. Check API for more information. 3 | usage: 4 | version: 2.1 5 | orbs: 6 | nx: nrwl/nx@1.7.0 7 | jobs: 8 | build: 9 | docker: 10 | - image: cimg/node:14.17-browsers 11 | steps: 12 | - checkout 13 | - run: 14 | name: Install dependencies 15 | command: yarn install --frozen-lockfile 16 | - nx/set-shas 17 | - run: 18 | name: Run Builds 19 | command: yarn nx affected --target=build --base=$NX_BASE --parallel --max-parallel=3 20 | - run: 21 | name: Run Unit Tests 22 | command: yarn nx affected --target=test --base=$NX_BASE --parallel --max-parallel=2 23 | -------------------------------------------------------------------------------- /src/examples/private.yml: -------------------------------------------------------------------------------- 1 | description: > 2 | To use this orb with a private repository on your main branch, you need to grant the orb access to your CircleCI API. 3 | You can do this by creating an environment variable called `CIRCLE_API_TOKEN` in the context or the project. 4 | The remaining usage code is intentionally omitted, since it does not differ from the normal usage. 5 | 6 | Note: It should be a user token, not project token. 7 | usage: 8 | version: 2.1 9 | orbs: 10 | nx: nrwl/nx@1.7.0 11 | -------------------------------------------------------------------------------- /src/examples/tags.yml: -------------------------------------------------------------------------------- 1 | description: > 2 | You can use `set-shas` also for non-push events, but you need to skip the branch check in that case. 3 | usage: 4 | version: 2.1 5 | orbs: 6 | nx: nrwl/nx@1.7.0 7 | jobs: 8 | build: 9 | docker: 10 | - image: cimg/node:14.17-browsers 11 | steps: 12 | - checkout 13 | - run: 14 | name: Install dependencies 15 | command: yarn install --frozen-lockfile 16 | - nx/set-shas: 17 | skip-branch-filter: true 18 | - run: 19 | name: Run Builds 20 | command: yarn nx affected --target=build --base=$NX_BASE --parallel --max-parallel=3 21 | - run: 22 | name: Run Unit Tests 23 | command: yarn nx affected --target=test --base=$NX_BASE --parallel --max-parallel=2 24 | workflows: 25 | my-workflow: 26 | jobs: 27 | - build: 28 | filters: 29 | branches: 30 | ignore: /.*/ # ignore any commit on any branch by default 31 | tags: 32 | only: /^v[0-9]+(\.[0-9]+)*$/ # only act on version tags 33 | -------------------------------------------------------------------------------- /src/scripts/find-successful-workflow.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const { execSync } = require('child_process'); 3 | const https = require('https'); 4 | 5 | const buildUrl = process.argv[2]; 6 | const branchName = process.argv[3]; 7 | const mainBranchName = process.env.MAIN_BRANCH_NAME || process.argv[4]; 8 | const errorOnNoSuccessfulWorkflow = process.argv[5] === '1'; 9 | const allowOnHoldWorkflow = process.argv[6] === '1'; 10 | const allowNotRunWorkflow = process.argv[7] == '1'; 11 | const skipBranchFilter = process.argv[8] === '1'; 12 | const workflowName = process.argv[9]; 13 | const circleToken = process.env.CIRCLE_API_TOKEN; 14 | 15 | const [, host, project] = buildUrl.match(/https?:\/\/([^\/]+)\/(.*)\/\d+/); 16 | 17 | let BASE_SHA; 18 | (async () => { 19 | if (branchName !== mainBranchName) { 20 | BASE_SHA = execSync(`git merge-base origin/${mainBranchName} HEAD`, { encoding: 'utf-8' }); 21 | } else { 22 | try { 23 | BASE_SHA = await findSuccessfulCommit(skipBranchFilter ? undefined : mainBranchName, workflowName); 24 | } catch (e) { 25 | process.stderr.write(e.message); 26 | if (errorOnNoSuccessfulWorkflow) { 27 | process.exit(1); 28 | } else { 29 | // Check if HEAD~1 exists, and if not, set BASE_SHA to the empty tree hash 30 | try { 31 | BASE_SHA = execSync(`git rev-parse origin/${mainBranchName}~1`, { encoding: 'utf-8' }); 32 | process.stdout.write(` 33 | WARNING: Accessing CircleCI API failed on 'origin/${mainBranchName}'. 34 | This might be a temporary issue with their API or a misconfiguration of the CIRCLE_API_TOKEN. 35 | We are therefore defaulting to use HEAD~1 on 'origin/${mainBranchName}'. 36 | 37 | NOTE: You can instead make this a hard error by settting 'error-on-no-successful-workflow' on the step in your workflow.\n\n`); 38 | } catch { 39 | process.stdout.write( 40 | `HEAD~1 does not exist. We are therefore defaulting to use the empty git tree hash as BASE.\n`, 41 | ); 42 | try { 43 | BASE_SHA = execSync(`git hash-object -t tree /dev/null`, { 44 | encoding: "utf-8", 45 | }); 46 | } catch { 47 | // 4b825dc642cb6eb9a060e54bf8d69288fbee4904 is the expected result of hashing the empty tree 48 | BASE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"; 49 | } 50 | } 51 | } 52 | } 53 | 54 | if (!BASE_SHA) { 55 | if (errorOnNoSuccessfulWorkflow) { 56 | process.stdout.write(` 57 | Unable to find a successful workflow run on 'origin/${mainBranchName}' 58 | NOTE: You have set 'error-on-no-successful-workflow' on the step so this is a hard error. 59 | 60 | Is it possible that you have no runs currently on 'origin/${mainBranchName}'? 61 | - If yes, then you should run the workflow without this flag first. 62 | - If no, then you might have changed your git history and those commits no longer exist.`); 63 | process.exit(1); 64 | } else { 65 | 66 | // Check if HEAD~1 exists, and if not, set BASE_SHA to the empty tree hash 67 | try { 68 | BASE_SHA = execSync(`git rev-parse origin/${mainBranchName}~1`, { encoding: 'utf-8' }); 69 | process.stdout.write(` 70 | WARNING: Unable to find a successful workflow run on 'origin/${mainBranchName}'. 71 | We are therefore defaulting to use HEAD~1 on 'origin/${mainBranchName}'. 72 | 73 | NOTE: You can instead make this a hard error by setting 'error-on-no-successful-workflow' on the step in your workflow.\n\n`); 74 | } catch { 75 | process.stdout.write( 76 | `HEAD~1 does not exist. We are therefore defaulting to use the empty git tree hash as BASE.\n`, 77 | ); 78 | try { 79 | BASE_SHA = execSync(`git hash-object -t tree /dev/null`, { 80 | encoding: "utf-8", 81 | }); 82 | } catch { 83 | // 4b825dc642cb6eb9a060e54bf8d69288fbee4904 is the expected result of hashing the empty tree 84 | BASE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"; 85 | } 86 | } 87 | } 88 | } else { 89 | process.stdout.write(` 90 | Found the last successful workflow run on 'origin/${mainBranchName}'.\n\n`); 91 | } 92 | } 93 | 94 | process.stdout.write(`Commit: ${BASE_SHA}\n\n`); 95 | })(); 96 | 97 | async function findSuccessfulCommit(branch, workflowName) { 98 | const url = `https://${host}/api/v2/project/${project}/pipeline?`; 99 | const params = branch ? [`branch=${branch}`] : []; 100 | let nextPage; 101 | let foundSHA; 102 | 103 | do { 104 | const fullParams = params.concat(nextPage ? [`page-token=${nextPage}`] : []).join('&'); 105 | const { next_page_token, sha } = await getJson(`${url}${fullParams}`) 106 | .then(async ({ next_page_token, items }) => { 107 | const pipeline = await findSuccessfulPipeline(items, workflowName); 108 | return { 109 | next_page_token, 110 | sha: pipeline ? pipeline.vcs.revision : void 0 111 | }; 112 | }); 113 | 114 | foundSHA = sha; 115 | nextPage = next_page_token; 116 | } while (!foundSHA && nextPage); 117 | 118 | return foundSHA; 119 | } 120 | 121 | async function findSuccessfulPipeline(pipelines, workflowName) { 122 | for (const pipeline of pipelines) { 123 | if (!pipeline.errors.length 124 | && commitExists(pipeline.vcs.revision) 125 | && await isWorkflowSuccessful(pipeline.id, workflowName)) { 126 | return pipeline; 127 | } 128 | } 129 | return undefined; 130 | } 131 | 132 | function commitExists(commitSha) { 133 | try { 134 | execSync(`git cat-file -e ${commitSha}`, { stdio: ['pipe', 'pipe', null] }); 135 | return true; 136 | } catch { 137 | return false; 138 | } 139 | } 140 | 141 | async function isWorkflowSuccessful(pipelineId, workflowName) { 142 | if (!workflowName) { 143 | return getJson( 144 | `https://${host}/api/v2/pipeline/${pipelineId}/workflow` 145 | ).then(({ items }) => 146 | items.every( 147 | (item) => 148 | item.status === 'success' || 149 | (allowOnHoldWorkflow && item.status === 'on_hold') || 150 | (allowNotRunWorkflow && item.status === 'not_run') 151 | ) 152 | ); 153 | } else { 154 | return getJson( 155 | `https://${host}/api/v2/pipeline/${pipelineId}/workflow` 156 | ).then(({ items }) => 157 | items.some( 158 | (item) => 159 | (item.status === 'success' || 160 | (allowOnHoldWorkflow && item.status === 'on_hold') || 161 | (allowNotRunWorkflow && item.status === 'not_run')) && 162 | item.name === workflowName 163 | ) 164 | ); 165 | } 166 | } 167 | 168 | async function getJson(url) { 169 | return new Promise((resolve, reject) => { 170 | let options = {}; 171 | 172 | if (circleToken) { 173 | options.headers = { 174 | 'Circle-Token': circleToken 175 | } 176 | } 177 | 178 | https.get(url, options, (res) => { 179 | let data = []; 180 | 181 | res.on('data', chunk => { 182 | data.push(chunk); 183 | }); 184 | 185 | res.on('end', () => { 186 | const response = Buffer.concat(data).toString(); 187 | try { 188 | const responseJSON = JSON.parse(response); 189 | resolve(responseJSON); 190 | } catch (e) { 191 | if (response.includes('Project not found')) { 192 | reject(new Error(`Error: Project not found.\nIf you are using a private repo, make sure the CIRCLE_API_TOKEN is set.\n\n${response}`)); 193 | } else { 194 | reject(e) 195 | } 196 | } 197 | }); 198 | }).on('error', error => reject( 199 | circleToken 200 | ? new Error(`Error: Pipeline fetching failed.\nCheck if you set the correct user CIRCLE_API_TOKEN.\n\n${error.toString()}`) 201 | : new Error(`Error: Pipeline fetching failed.\nIf this is private repo you will need to set CIRCLE_API_TOKEN\n\n${error.toString()}`) 202 | )); 203 | }); 204 | } 205 | -------------------------------------------------------------------------------- /src/scripts/set-shas.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "$PARAM_SCRIPT" >>"index.cjs" 3 | if [ -z "$CIRCLE_BRANCH" ]; then 4 | echo "\$CIRCLE_BRANCH not set, falling back to $PARAM_MAIN_BRANCH" 5 | TARGET_BRANCH=$PARAM_MAIN_BRANCH 6 | else 7 | TARGET_BRANCH=$CIRCLE_BRANCH 8 | fi 9 | RESPONSE=$(node index.cjs $CIRCLE_BUILD_URL $TARGET_BRANCH $PARAM_MAIN_BRANCH $PARAM_ERROR_ON_NO_SUCCESSFUL_WORKFLOW $PARAM_ALLOW_ON_HOLD $PARAM_ALLOW_NOT_RUN $PARAM_SKIP_BRANCH_FILTER $PARAM_WORKFLOW_NAME) 10 | echo "$RESPONSE" 11 | BASE_SHA=$(echo "$RESPONSE" | grep 'Commit:' | sed 's/.*Commit: //') 12 | HEAD_SHA=$(git rev-parse HEAD) 13 | echo "Base SHA" 14 | echo $BASE_SHA 15 | echo "" 16 | echo "Head SHA" 17 | echo $HEAD_SHA 18 | echo "" 19 | echo "export NX_BASE=\"$BASE_SHA\";" >>$BASH_ENV 20 | echo "export NX_HEAD=\"$HEAD_SHA\";" >>$BASH_ENV 21 | echo "" 22 | echo "NX_BASE and NX_HEAD environment variables have been set for the current Job" 23 | rm index.cjs --------------------------------------------------------------------------------