-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: switch to tagless workflow success check #2
Changes from 4 commits
ac7c55b
813443a
ee7951c
af2740f
96c4ba2
c0d1652
79cefc8
38197b9
d1f0d5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
|
||
# Unplanned lock files | ||
yarn.lock | ||
pnpm-lock.yml | ||
|
||
# Runtime data | ||
pids | ||
*.pid | ||
*.seed | ||
*.pid.lock | ||
|
||
# Dependency directories | ||
node_modules/ | ||
jspm_packages/ | ||
|
||
# Compiled files | ||
dist | ||
tmp | ||
|
||
# Optional npm cache directory | ||
.npm | ||
|
||
# Optional eslint cache | ||
.eslintcache | ||
|
||
# Yarn Integrity file | ||
.yarn-integrity | ||
|
||
# yarn v2 | ||
.yarn/cache | ||
.yarn/unplugged | ||
.yarn/build-state.yml | ||
.yarn/install-state.gz | ||
.pnp.* | ||
|
||
# dotenv environment variables file | ||
.env | ||
.env.test | ||
|
||
# parcel-bundler cache (https://parceljs.org/) | ||
.cache | ||
.parcel-cache | ||
|
||
# IDE | ||
.idea | ||
.vscode | ||
|
||
.DS_Store |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
(The MIT License) | ||
|
||
Copyright (c) 2021-present Narwhal Technologies Inc. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining | ||
a copy of this software and associated documentation files (the | ||
'Software'), to deal in the Software without restriction, including | ||
without limitation the rights to use, copy, modify, merge, publish, | ||
distribute, sublicense, and/or sell copies of the Software, and to | ||
permit persons to whom the Software is furnished to do so, subject to | ||
the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be | ||
included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,16 @@ | ||
# nx-set-shas | ||
<p style="text-align: center;"><img src=".github/assets/nx.png" | ||
width="100%" alt="Nx - Smart, Extensible Build Framework"></p> | ||
|
||
<h1 align="center">Set SHAs Action</h2> | ||
|
||
> ✨ A Github Action which sets the base and head SHAs required for `nx affected` commands in CI | ||
|
||
- [Example Usage](#example-usage) | ||
- [Configuration Options](#configuration-options) | ||
- [Background](#background) | ||
- [License](#license) | ||
|
||
> This documentation is for version 2.x.x. If you are using version 1.x.x you also need to include accomplanying [nx-tag-successful-ci-run](https://github.com/nrwl/nx-tag-successful-ci-run) as the last step of your job, since version 1.x.x depends on the existance of git tags that mark successful runs. | ||
|
||
## Example Usage | ||
|
||
|
@@ -25,7 +37,9 @@ jobs: | |
# OPTION 1) Environment variables | ||
# =========================================================================== | ||
- name: Derive appropriate SHAs for base and head for `nx affected` commands | ||
uses: nrwl/nx-set-shas@v1 | ||
uses: nrwl/nx-set-shas@v2 | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- run: | | ||
echo "BASE: ${{ env.NX_BASE }}" | ||
|
@@ -36,12 +50,33 @@ jobs: | |
# =========================================================================== | ||
- name: Derive appropriate SHAs for base and head for `nx affected` commands | ||
id: setSHAs | ||
uses: nrwl/nx-set-shas@v1 | ||
uses: nrwl/nx-set-shas@v2 | ||
with: | ||
set-environment-variables-for-job: 'false' | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- run: | | ||
echo "BASE: ${{ steps.setSHAs.outputs.base }}" | ||
echo "HEAD: ${{ steps.setSHAs.outputs.head }}" | ||
|
||
# =========================================================================== | ||
# OPTION 3) Specify all parameters | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not really what was intended by OPTIONs here. There are two fundamentally different ways of using the SHAs - via environment variables, or via github job outputs. This is not a third OPTION when it comes to fundamental usage of the SHAS, so please remove. The configuration options section below was intended to exhaustively cover all the options available |
||
# =========================================================================== | ||
- name: Derive appropriate SHAs for base and head for `nx affected` commands | ||
uses: nrwl/nx-set-shas@v2 | ||
with: | ||
main-branch-name: 'main' | ||
set-environment-variables-for-job: 'true' | ||
error-on-no-successful-workflow: 'false' | ||
workflow-id: 'ci.yml' | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- run: | | ||
echo "BASE: ${{ env.NX_BASE }}" | ||
echo "HEAD: ${{ env.NX_HEAD }}" | ||
|
||
# ... more CI config ... | ||
``` | ||
<!-- end example-usage --> | ||
|
@@ -51,29 +86,66 @@ jobs: | |
<!-- start configuration-options --> | ||
```yaml | ||
- uses: nrwl/nx-set-shas@v1 | ||
env: | ||
# The github token needed to provide access to APIs. | ||
# Use ${{ secrets.GITHUB_TOKEN }} as a value | ||
# | ||
# Required: true | ||
GITHUB_TOKEN: '' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should use One of the great things about github actions is the fact you get a token provisioned automatically for you There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See examples on the official org of actions: https://github.com/actions There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. User does not need to set up new secret in their repo, the https://github.com/octokit/action.js/blob/master/README.md |
||
|
||
with: | ||
# The "main" branch of your repository (the base branch which you target with PRs). | ||
# Common names for this branch include main and master. | ||
# | ||
# Default: main | ||
main-branch-name: '' | ||
|
||
# The glob(7) pattern to be provided to `git describe --match` in order to match against | ||
# the latest relevant tag on the specified "main" branch. | ||
# | ||
# The default pattern aligns with the default behavior of the complementary `nrwl/nx-tag-successful-ci-run` action. | ||
# | ||
# Default: nx_successful_ci_run* | ||
tag-match-pattern: '' | ||
|
||
# Applies the derived SHAs for base and head as NX_BASE and NX_HEAD environment variables within the current Job. | ||
# | ||
# Default: true | ||
set-environment-variables-for-job: '' | ||
|
||
# By default, if no matching tags are found on the main branch to determine the SHA, we will log a warning and use HEAD~1. Enable this option to error and exit instead. | ||
# By default, if no successful workflow run is found on the main branch to determine the SHA, we will log a warning and use HEAD~1. Enable this option to error and exit instead. | ||
# | ||
# Default: false | ||
error-on-no-matching-tags: '' | ||
error-on-no-successful-workflow: '' | ||
|
||
# The ID of the github action workflow to check for successful run or the name of the file name containing the workflow. | ||
# E.g. 'ci.yml'. If not provided, current workflow id will be used | ||
# | ||
workflow-id: '' | ||
``` | ||
<!-- end configuration-options --> | ||
<!-- end configuration-options --> | ||
|
||
## Background | ||
|
||
When we run `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 | ||
between those 2 commits](https://nx.dev/latest/angular/tutorial/11-test-affected-projects#step-11-test-affected-projects | ||
). We can then run a set of tasks (like building or linting) only on those **affected** projects. | ||
|
||
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. | ||
|
||
|
||
### Problem | ||
|
||
Figuring out what these two git commits are might not be as simple as it seems. | ||
|
||
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 our PR in whole is always being tested. | ||
meeroslav marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
But what if we want to set up a continuous deployment system | ||
that, as changes get pushed to `master`, it builds and deploys | ||
only the affected projects? | ||
|
||
What are the `FROM` and `TO` commits in that case? | ||
|
||
They can't be just `HEAD` and `HEAD~1` as some of those run might fail. 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. | ||
meeroslav marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This action enables you to find: | ||
* Commit SHA from which PR originated (in the case of `pull_request`) | ||
* Commit SHA of the last successful CI run | ||
|
||
## License | ||
|
||
[MIT](http://opensource.org/licenses/MIT) | ||
|
||
Copyright (c) 2021-present Narwhal Technologies Inc. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
const { Octokit } = require("@octokit/action"); | ||
const core = require("@actions/core"); | ||
const { execSync } = require('child_process'); | ||
|
||
const branch = process.argv[2]; | ||
const workflowId = process.argv[3]; | ||
const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/"); | ||
const run_id = process.env.GITHUB_RUN_ID; | ||
|
||
(async () => { | ||
try { | ||
const octokit = new Octokit(); | ||
let workflow_id = workflowId; | ||
if (!workflow_id) { | ||
// retrieve workflow-id | ||
workflow_id = await octokit.request(`GET /repos/${owner}/${repo}/actions/runs/${run_id}`, { | ||
owner, | ||
repo, | ||
branch, | ||
run_id | ||
}).then(({ data: { workflow_id } }) => workflow_id); | ||
} | ||
// fetch all workflow runs on a given repo/branch/workflow with push and success | ||
const shas = await octokit.request(`GET /repos/${owner}/${repo}/actions/workflows/${workflow_id}/runs`, { | ||
owner, | ||
repo, | ||
branch, | ||
workflow_id, | ||
event: 'push', | ||
status: 'success' | ||
}).then(({ data: { workflow_runs } }) => workflow_runs.map(run => run.head_sha)); | ||
|
||
const sha = await findExistingCommit(shas); | ||
console.log(sha); | ||
} catch (e) { | ||
core.setFailed(e.message); | ||
process.exit(1); | ||
} | ||
})(); | ||
|
||
/** | ||
* Get first existing commit | ||
* @param {string[]} commit_shas | ||
* @returns {string?} | ||
*/ | ||
async function findExistingCommit(shas) { | ||
for (const commitSha of shas) { | ||
if (await commitExists(commitSha)) { | ||
return commitSha; | ||
} | ||
} | ||
return undefined; | ||
} | ||
|
||
/** | ||
* Check if given commit is valid | ||
* @param {string} commitSha | ||
* @returns {boolean} | ||
*/ | ||
async function commitExists(commitSha) { | ||
try { | ||
execSync(`git cat-file -e ${commitSha} 2> /dev/null`); | ||
return true; | ||
} catch { | ||
return false; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a couple of typos in here (
accomplanying
,existance
) so it needs to be changed anyway, so may I suggest instead of this paragraph we instead simply link to the README.md at the latest v1 tag?