63 Commits

Author SHA1 Message Date
35168619ff Fix conflicts with main 2024-06-06 10:54:15 -07:00
4e3e886d7a Add missing env vars to inputs 2024-06-06 10:51:46 -07:00
ed0fe829d8 Merge pull request #115 from detsys-pr-bot/detsys-ts-update-bc45b6c0a6318ae30192c4bf23a73dc879bdb632
Update `detsys-ts`:
2024-06-05 16:19:34 -07:00
278b2c0e02 Merge pull request #114 from detsys-pr-bot/detsys-ts-update-515d00bc192ae4460e2122572ebc24020c58ea95
Update detsys-ts: Merge pull request #51 from DeterminateSystems/add-missing-actions-to-update-matrix

Add missing Actions to update matrix
2024-06-05 15:48:14 -07:00
1b00b724a5 Regenerate dist 2024-06-05 15:46:52 -07:00
23e86b41e2 Merge pull request #116 from detsys-pr-bot/detsys-ts-update-856a75af22949b76e23f6e54a1b4d27d8816cea4
Update `detsys-ts`: Merge pull request #113 from DeterminateSystems/update-detsys-ts-status-page
2024-06-05 18:06:07 -04:00
b8b46eb7bb Update detsys-ts for: Merge pull request #113 from DeterminateSystems/update-detsys-ts-status-page (856a75af22949b76e23f6e54a1b4d27d8816cea4) 2024-06-05 21:59:53 +00:00
50c97e1435 Update detsys-ts for: ` (bc45b6c0a6318ae30192c4bf23a73dc879bdb632`) 2024-06-05 21:32:11 +00:00
d777dd2ba4 Update detsys-ts for: Merge pull request #51 from DeterminateSystems/add-missing-actions-to-update-matrix
Add missing Actions to update matrix (515d00bc192ae4460e2122572ebc24020c58ea95)
2024-06-05 21:06:52 +00:00
0829421b88 Initial version of PR body rendering 2024-06-04 09:19:35 -07:00
8c5e8043f8 More test cases: 2024-06-04 08:44:31 -07:00
09b0ac8cd3 Enable supplying a commit message template 2024-06-04 08:28:12 -07:00
d3aa136776 Provide pr-body as output from step 2024-06-03 14:32:30 -07:00
5d674d8347 Fix formatting 2024-06-03 14:20:34 -07:00
3f84616103 Remove test subflake 2024-06-03 14:18:58 -07:00
cbee267f6f Fix merge conflicts with main 2024-06-03 13:44:53 -07:00
bba6a453b7 Merge pull request #113 from DeterminateSystems/update-detsys-ts-status-page
Update detsys-ts (status page changes)
2024-06-03 09:18:41 -07:00
aecc58b9ac Update detsys-ts (status page changes) 2024-06-03 09:12:51 -07:00
41c8f7e1b5 Merge pull request #112 from DeterminateSystems/srv
Update detsys-ts for srv
2024-05-31 12:05:22 -04:00
be77a56766 eslint stricter 2024-05-31 11:40:33 -04:00
d98ea6576b Update detsys-ts for srv 2024-05-31 11:34:46 -04:00
2b0d863d87 Merge pull request #111 from DeterminateSystems/update-detsys-ts
Update detsys-ts
2024-05-29 15:20:23 -03:00
559c3e249c Update detsys-ts 2024-05-29 15:18:32 -03:00
5d30a7794a Make a single dir a special case 2024-05-23 23:39:14 -03:00
dccc3175bf Update detsys-ts 2024-05-23 19:19:07 -03:00
c16b76233e Provide improved input handling 2024-05-23 16:09:06 -03:00
b5a9000c3f Check for flake-dirs clash with inputs 2024-05-23 15:55:25 -03:00
b6aab91cde Improve flake-dirs handling logic 2024-05-23 15:48:19 -03:00
c7eb3f32c9 Fix input validation 2024-05-23 15:42:03 -03:00
0c1dd1090d Add CI test 2024-05-23 15:37:34 -03:00
eb897bb16b Add README.md example 2024-05-23 15:34:32 -03:00
1127ba41bd Check that each directory is a valid flake 2024-05-23 15:33:00 -03:00
f5dab0ead5 Rework input handling 2024-05-23 15:19:56 -03:00
6a1287939f Add flake-dirs input 2024-05-23 15:16:12 -03:00
222f041780 Merge pull request #108 from DeterminateSystems/strict-mode-env-var
Add environment variable for strict mode input
2024-05-23 12:26:35 -03:00
0e2a61b1f3 Add environment variable for strict mode input 2024-05-23 12:23:56 -03:00
ede634b2c8 Merge pull request #107 from DeterminateSystems/strict-mode-not-required
Make strict mode input not required
2024-05-23 12:05:47 -03:00
7a7f13f9b5 Make strict mode input not required 2024-05-23 12:03:54 -03:00
e0fe1f8e46 Merge pull request #105 from DeterminateSystems/detsys-ts-update
Update detsys-ts
2024-05-22 20:27:21 -03:00
7ce3b51a1d Update detsys-ts 2024-05-22 15:40:01 -03:00
4f21d96ab3 Merge pull request #103 from DeterminateSystems/space-sep
Split flake inputs on spaces instead of commas
2024-05-09 18:14:58 -04:00
165ae6e270 space-sep'd inputs 2024-05-09 18:13:24 -04:00
bcabaab2f1 Merge pull request #101 from DeterminateSystems/detsys-ts
Convert the Action into TypeScript
2024-05-09 15:50:34 -04:00
406a429015 Regenerate 2024-05-09 15:48:13 -04:00
28eac596e7 nits on the nits 2024-05-09 15:47:03 -04:00
da64c8c904 nits 2024-05-09 15:45:38 -04:00
3fa85bcf4c nit: run line 2024-05-09 15:44:43 -04:00
d021735a89 space separated options 2024-05-09 15:35:58 -04:00
d978837d43 Expose all inputs 2024-05-09 15:35:53 -04:00
fc5dacd10b Use nix flake update if no inputs are specified 2024-05-09 14:15:38 -04:00
7352b7f36b lockfile-summary, d'oh 2024-05-07 23:03:21 -04:00
6d82bce8ec Update detsys-ts 2024-05-07 23:03:12 -04:00
8363f28293 Call the node action instead directly 2024-05-07 23:02:56 -04:00
1752965d0b Don't cd separately 2024-05-06 16:13:34 -04:00
21663d562d Rename workflow 2024-04-29 10:15:25 -03:00
6318aa12c1 Remove now-unnecessary shellcheck check 2024-04-26 14:23:24 -03:00
239b4c9810 Add JS-specific bits to Actions 2024-04-26 14:19:53 -03:00
539b7a6481 Remove Bash script and do more TS streamlining 2024-04-26 12:10:07 -03:00
dde5487502 Finish initial rework into TS 2024-04-26 11:55:19 -03:00
502daa7e5e Construct Nix command 2024-04-21 19:50:32 -03:00
b1f8684b21 Update Nix shell and add envrc 2024-04-21 19:42:23 -03:00
cf6776dfd1 Add initial JS setup 2024-04-21 19:17:03 -03:00
cc5f064749 Update README.md to use actions/checkout@v4 2024-04-08 09:21:10 -07:00
27 changed files with 108828 additions and 127 deletions

View File

@ -1,15 +1,10 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
# https://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
[*.{yml,yaml}]
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

74
.eslintrc.json Normal file
View File

@ -0,0 +1,74 @@
{
"plugins": ["@typescript-eslint"],
"extends": ["plugin:github/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "module",
"project": "./tsconfig.json"
},
"settings": {
"import/resolver": {
"typescript": {}
}
},
"rules": {
"i18n-text/no-en": "off",
"eslint-comments/no-use": "off",
"import/no-namespace": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_"
}
],
"@typescript-eslint/explicit-member-accessibility": [
"error",
{
"accessibility": "no-public"
}
],
"@typescript-eslint/no-base-to-string": "error",
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/ban-ts-comment": "error",
"camelcase": "error",
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/explicit-function-return-type": [
"error",
{
"allowExpressions": true
}
],
"@typescript-eslint/func-call-spacing": ["error", "never"],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/restrict-plus-operands": "error",
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unbound-method": "error"
},
"env": {
"node": true,
"es6": true
}
}

View File

@ -5,8 +5,8 @@ on:
branches: [main]
jobs:
shellcheck:
runs-on: ubuntu-latest
typescript-action:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
@ -15,5 +15,19 @@ jobs:
uses: DeterminateSystems/nix-installer-action@main
- name: Enable magic Nix cache
uses: DeterminateSystems/magic-nix-cache-action@main
- name: Shellcheck
run: nix-shell --run 'shellcheck $(find . -type f -name "*.sh" -executable)'
- name: Install pnpm dependencies
run: nix develop --command pnpm install
- name: Check formatting
run: nix develop --command pnpm run check-fmt
- name: Lint
run: nix develop --command pnpm run lint
- name: Build
run: nix develop --command pnpm run build
- name: Run test suite
run: nix develop --command pnpm run test
- name: Package
run: nix develop --command pnpm run package
- name: Check git status
run: git status --porcelain=v1
- name: Ensure no staged changes
run: git diff --exit-code

View File

@ -2,7 +2,7 @@ name: update-flake-lock
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0'
- cron: "0 0 * * 0"
jobs:
lockfile:
@ -18,3 +18,5 @@ jobs:
uses: DeterminateSystems/flake-checker-action@main
- name: Update flake.lock
uses: ./.
with:
_internal-strict-mode: true

View File

@ -6,7 +6,7 @@ on:
jobs:
validate:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
# JS dependencies
node_modules/

5
.prettierignore Normal file
View File

@ -0,0 +1,5 @@
dist/
lib/
node_modules/
pnpm-lock.yaml
README.md

View File

@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Update flake.lock
@ -50,7 +50,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock
@ -75,7 +75,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock
@ -98,7 +98,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock
@ -127,7 +127,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock
@ -138,6 +138,23 @@ jobs:
path-to-flake-dir: 'nix/' # in this example our flake doesn't sit at the root of the repository, it sits under 'nix/flake.nix'
```
You can also run the update operation in multiple directories, provided that each directory is a valid flake:
```yaml
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@vX
with:
flake-dirs: |
flake1
flake2
flake3
```
> **Warning**: If you choose multiple directories, `update-flake-lock` can only update all flake inputs,
> meaning that you can't set the `inputs` parameter. This is due to limitations in input handling in
> GitHub Actions, which only allows for strings, numbers, Booleans, and arrays but not objects, which
> would be the much preferred data type for expressing per-directory inputs.
## Example using a different Git user
If you want to change the author and / or committer of the flake.lock update commit, you can tweak the `git-{author,committer}-{name,email}` options:
@ -154,7 +171,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock
@ -199,7 +216,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock
@ -234,7 +251,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock
@ -296,7 +313,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock

View File

@ -1,35 +1,65 @@
name: 'Update Nix Flake Lock'
description: 'Update your Nix flake.lock and send a PR'
name: "Update Nix Flake Lock"
description: "Update your Nix flake.lock and send a PR"
inputs:
inputs:
description: 'A space-separated list of inputs to update. Leave empty to update all inputs.'
description: "A space-separated list of inputs to update. Leave empty to update all inputs."
required: false
default: ''
default: ""
token:
description: 'GITHUB_TOKEN or a `repo` scoped Personal Access Token (PAT)'
description: "GITHUB_TOKEN or a `repo` scoped Personal Access Token (PAT)"
required: false
default: ${{ github.token }}
commit-msg:
description: 'The message provided with the commit'
commit-msg-template:
description: |
The commit message template to use. You can use these variables in your template:
* `{{ flake_dot_lock }}` is the path to the `flake.lock` file being updated
* `{{ flake_dot_lock_dir }}` is the `flake.lock` file's directory
If you set both this and `commit-msg`, the `commit-msg` setting is used (it does not support templating).
required: false
default: |
flake.lock: Updated in {{ flake_dot_lock_dir }}
commit-msg:
description: |
The message provided with the commit.
required: false
default: "flake.lock: Update"
base:
description: "Sets the pull request base branch. Defaults to the branch checked out in the workflow."
required: false
branch:
description: 'The branch of the PR to be created'
description: "The branch of the PR to be created"
required: false
default: "update_flake_lock_action"
path-to-flake-dir:
description: 'The path of the directory containing `flake.nix` file within your repository. Useful when `flake.nix` cannot reside at the root of your repository.'
description: |
The path of the directory containing `flake.nix` file within your repository.
Useful when `flake.nix` cannot reside at the root of your repository.
required: false
default: ''
flake-dirs:
description: |
A space-separated list of directories containing `flake.nix` files within your repository.
Useful when you have multiple flakes in your repository.
required: false
default: ""
pr-title:
description: 'The title of the PR to be created'
description: "The title of the PR to be created"
required: false
default: "flake.lock: Update"
pr-body-template:
description: |
The pull request body template to use. You can use these variables in your template:
* `{{ comma_separated_dirs }}` is the flake directories that were updated separated by comma
* `{{ space_separated_dirs }}` is the flake directories that were updated separated by space
* `{{ updated_dirs_list }}` is the flake directories that were updated as a Markdown list
If you set both this and `pr-body`, the `pr-body` setting is used (it does not support templating).
required: false
default: |
Just testing.
pr-body:
description: 'The body of the PR to be created'
description: "The body of the PR to be created"
required: false
default: |
Automated changes by the [update-flake-lock](https://github.com/DeterminateSystems/update-flake-lock) GitHub Action.
@ -53,58 +83,62 @@ inputs:
```
pr-labels:
description: 'A comma or newline separated list of labels to set on the Pull Request to be created'
description: "A comma or newline separated list of labels to set on the Pull Request to be created"
required: false
default: ''
default: ""
pr-assignees:
description: 'A comma or newline separated list of assignees (GitHub usernames).'
description: "A comma or newline separated list of assignees (GitHub usernames)."
required: false
default: ''
default: ""
pr-reviewers:
description: 'A comma or newline separated list of reviewers (GitHub usernames) to request a review from.'
description: "A comma or newline separated list of reviewers (GitHub usernames) to request a review from."
required: false
default: ''
default: ""
git-author-name:
description: 'Author name used for commit. Only used if sign-commits is false.'
description: "Author name used for commit. Only used if sign-commits is false."
required: false
default: 'github-actions[bot]'
default: "github-actions[bot]"
git-author-email:
description: 'Author email used for commit. Only used if sign-commits is false.'
description: "Author email used for commit. Only used if sign-commits is false."
required: false
default: 'github-actions[bot]@users.noreply.github.com'
default: "github-actions[bot]@users.noreply.github.com"
git-committer-name:
description: 'Committer name used for commit. Only used if sign-commits is false.'
description: "Committer name used for commit. Only used if sign-commits is false."
required: false
default: 'github-actions[bot]'
default: "github-actions[bot]"
git-committer-email:
description: 'Committer email used for commit. Only used if sign-commits is false.'
description: "Committer email used for commit. Only used if sign-commits is false."
required: false
default: 'github-actions[bot]@users.noreply.github.com'
default: "github-actions[bot]@users.noreply.github.com"
sign-commits:
description: 'Set to true if the action should sign the commit with GPG'
description: "Set to true if the action should sign the commit with GPG"
required: false
default: 'false'
default: "false"
gpg-private-key:
description: 'GPG Private Key with which to sign the commits in the PR to be created'
description: "GPG Private Key with which to sign the commits in the PR to be created"
required: false
default: ''
default: ""
gpg-fingerprint:
description: 'Fingerprint of specific GPG subkey to use'
description: "Fingerprint of specific GPG subkey to use"
required: false
gpg-passphrase:
description: 'GPG Private Key Passphrase for the GPG Private Key with which to sign the commits in the PR to be created'
description: "GPG Private Key Passphrase for the GPG Private Key with which to sign the commits in the PR to be created"
required: false
default: ''
default: ""
nix-options:
description: 'A space-separated list of options to pass to the nix command'
description: "A space-separated list of options to pass to the nix command"
required: false
default: ''
default: ""
_internal-strict-mode:
description: Whether to fail when any errors are thrown. Used only to test the Action; do not set this in your own workflows.
required: false
default: false
outputs:
pull-request-number:
description: 'The number of the opened pull request'
description: "The number of the opened pull request"
value: ${{ steps.create-pr.outputs.pull-request-number }}
pull-request-operation:
description: 'The pull request operation performed by the action, `created`, `updated` or `closed`.'
description: "The pull request operation performed by the action, `created`, `updated` or `closed`."
value: ${{ steps.create-pr.outputs.pull-request-operation }}
runs:
using: "composite"
@ -115,7 +149,7 @@ runs:
uses: crazy-max/ghaction-import-gpg@v6
with:
gpg_private_key: ${{ inputs.gpg-private-key }}
fingerprint: ${{ inputs.gpg-fingerprint }}
fingerprint: ${{ inputs.gpg-fingerprint }}
passphrase: ${{ inputs.gpg-passphrase }}
git_config_global: true
git_user_signingkey: true
@ -142,23 +176,44 @@ runs:
echo "GIT_AUTHOR_EMAIL=<${{ inputs.git-author-email }}>" >> $GITHUB_ENV
echo "GIT_COMMITTER_NAME=${{ inputs.git-committer-name }}" >> $GITHUB_ENV
echo "GIT_COMMITTER_EMAIL=<${{ inputs.git-committer-email }}>" >> $GITHUB_ENV
- name: Run update-flake-lock.sh
run: $GITHUB_ACTION_PATH/update-flake-lock.sh
- name: Run update-flake-lock
id: update-flake-lock
shell: bash
run: node "$GITHUB_ACTION_PATH/dist/index.js"
env:
GIT_AUTHOR_NAME: ${{ env.GIT_AUTHOR_NAME }}
GIT_AUTHOR_EMAIL: ${{ env.GIT_AUTHOR_EMAIL }}
GIT_COMMITTER_NAME: ${{ env.GIT_COMMITTER_NAME }}
GIT_COMMITTER_EMAIL: ${{ env.GIT_COMMITTER_EMAIL }}
NIX_OPTIONS: ${{ inputs.nix-options }}
TARGETS: ${{ inputs.inputs }}
COMMIT_MSG: ${{ inputs.commit-msg }}
PATH_TO_FLAKE_DIR: ${{ inputs.path-to-flake-dir }}
# The following manually exposes all of the action inputs into INPUT_ environment variables so actionsCore.getInput works:
# https://github.com/actions/toolkit/blob/ae38557bb0dba824cdda26ce787bd6b66cf07a83/packages/core/src/core.ts#L126
INPUT_BASE: ${{ inputs.base }}
INPUT_BRANCH: ${{ inputs.branch }}
INPUT_COMMIT-MSG: ${{ inputs.commit-msg }}
INPUT_COMMIT-MSG-TEMPLATE: ${{ inputs.commit-msg-template }}
INPUT_GIT-AUTHOR-EMAIL: ${{ inputs.git-author-email }}
INPUT_GIT-AUTHOR-NAME: ${{ inputs.git-author-name }}
INPUT_GIT-COMMITTER-EMAIL: ${{ inputs.git-committer-email }}
INPUT_GIT-COMMITTER-NAME: ${{ inputs.git-committer-name }}
INPUT_GPG-FINGERPRINT: ${{ inputs.gpg-fingerprint }}
INPUT_GPG-PASSPHRASE: ${{ inputs.gpg-passphrase }}
INPUT_GPG-PRIVATE-KEY: ${{ inputs.gpg-private-key }}
INPUT_INPUTS: ${{ inputs.inputs }}
INPUT_NIX-OPTIONS: ${{ inputs.nix-options }}
INPUT_PATH-TO-FLAKE-DIR: ${{ inputs.path-to-flake-dir }}
INPUT_FLAKE-DIRS: ${{ inputs.flake-dirs }}
INPUT_PR-ASSIGNEES: ${{ inputs.pr-assignees }}
INPUT_PR-BODY: ${{ inputs.pr-body }}
INPUT_PR-BODY-TEMPLATE: ${{ inputs.pr-body-template }}
INPUT_PR-LABELS: ${{ inputs.pr-labels }}
INPUT_PR-REVIEWERS: ${{ inputs.pr-reviewers }}
INPUT_PR-TITLE: ${{ inputs.pr-title }}
INPUT_PULL-REQUEST-NUMBER: ${{ inputs.pull-request-number }}
INPUT_PULL-REQUEST-OPERATION: ${{ inputs.pull-request-operation }}
INPUT_SIGN-COMMITS: ${{ inputs.sign-commits }}
INPUT_TOKEN: ${{ inputs.token }}
INPUT__INTERNAL-STRICT-MODE: ${{ inputs._internal-strict-mode }}
- name: Save PR Body as file
uses: DamianReeves/write-file-action@v1.3
with:
path: pr_body.template
contents: ${{ inputs.pr-body }}
contents: ${{ steps.update-flake-lock.outputs.pr-body }}
env: {}
- name: Set additional env variables (GIT_COMMIT_MESSAGE)
shell: bash
@ -172,8 +227,8 @@ runs:
- name: Interpolate PR Body
uses: pedrolamas/handlebars-action@v2.4.0
with:
files: 'pr_body.template'
output-filename: 'pr_body.txt'
files: "pr_body.template"
output-filename: "pr_body.txt"
- name: Read pr_body.txt
id: pr_body
uses: juliangruber/read-file-action@v1

2
dist/index.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
export { }

103570
dist/index.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/index.js.map vendored Normal file

File diff suppressed because one or more lines are too long

3
dist/package.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"type": "module"
}

12
flake.lock generated
View File

@ -2,16 +2,16 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1696604326,
"narHash": "sha256-YXUNI0kLEcI5g8lqGMb0nh67fY9f2YoJsILafh6zlMo=",
"rev": "87828a0e03d1418e848d3dd3f3014a632e4a4f64",
"revCount": 533189,
"lastModified": 1713537308,
"narHash": "sha256-XtTSSIB2DA6tOv+l0FhvfDMiyCmhoRbNB+0SeInZkbk=",
"rev": "5c24cf2f0a12ad855f444c30b2421d044120c66f",
"revCount": 614481,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.533189%2Brev-87828a0e03d1418e848d3dd3f3014a632e4a4f64/018b0dc8-e84f-7c59-b5d6-16849c3b2074/source.tar.gz"
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.614481%2Brev-5c24cf2f0a12ad855f444c30b2421d044120c66f/018efa00-a443-7f41-b371-ce568b5c7e9f/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/NixOS/nixpkgs/0.1.533189.tar.gz"
"url": "https://flakehub.com/f/NixOS/nixpkgs/0.1.%2A.tar.gz"
}
},
"root": {

View File

@ -1,30 +1,23 @@
{
description = "update-flake-lock";
inputs.nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1.533189.tar.gz";
inputs.nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1.*.tar.gz";
outputs =
{ self
, nixpkgs
}:
outputs = { self, nixpkgs }:
let
nameValuePair = name: value: { inherit name value; };
genAttrs = names: f: builtins.listToAttrs (map (n: nameValuePair n (f n)) names);
allSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
forAllSystems = f: genAttrs allSystems
(system: f {
inherit system;
pkgs = import nixpkgs { inherit system; };
});
supportedSystems = [ "x86_64-linux" "aarch64-darwin" "aarch64-linux" "x86_64-darwin" ];
forEachSupportedSystem = f: nixpkgs.lib.genAttrs supportedSystems (system: f {
pkgs = import nixpkgs { inherit system; };
});
in
{
devShell = forAllSystems
({ system, pkgs, ... }:
pkgs.stdenv.mkDerivation {
name = "update-flake-lock-devshell";
buildInputs = [ pkgs.shellcheck ];
src = self;
});
devShells = forEachSupportedSystem ({ pkgs }: {
default = pkgs.mkShell {
packages = with pkgs; [
nodejs_latest
nodePackages_latest.pnpm
];
};
});
};
}

49
package.json Normal file
View File

@ -0,0 +1,49 @@
{
"name": "update-flake-lock",
"version": "1.0.0",
"description": "",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"type": "module",
"scripts": {
"build": "tsup",
"format": "prettier --write .",
"check-fmt": "prettier --check .",
"lint": "eslint src/**/*.ts --ignore-pattern *.test.ts",
"package": "ncc build",
"test": "vitest --watch false",
"test-dev": "vitest",
"all": "pnpm run format && pnpm run lint && pnpm run build && pnpm run package"
},
"repository": {
"type": "git",
"url": "git+https://github.com/DeterminateSystems/update-flake-lock.git"
},
"keywords": [],
"author": "",
"license": "MIT",
"bugs": {
"url": "https://github.com/DeterminateSystems/update-flake-lock/issues"
},
"homepage": "https://github.com/DeterminateSystems/update-flake-lock#readme",
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1",
"detsys-ts": "github:DeterminateSystems/detsys-ts",
"handlebars": "^4.7.8"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@typescript-eslint/eslint-plugin": "^7.11.0",
"@vercel/ncc": "^0.38.1",
"eslint": "^8.57.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-github": "^4.10.2",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"prettier": "^3.2.5",
"tsup": "^8.0.2",
"typescript": "^5.4.5",
"vitest": "^1.6.0"
}
}

4508
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

12
prettier.config.cjs Normal file
View File

@ -0,0 +1,12 @@
/** @type {import('prettier').Config} */
module.exports = {
plugins: [require.resolve("@trivago/prettier-plugin-sort-imports")],
semi: true,
singleQuote: false,
tabWidth: 2,
trailingComma: "all",
useTabs: false,
// Import sorting
importOrderSeparation: true,
importOrderSortSpecifiers: true,
};

184
src/index.ts Normal file
View File

@ -0,0 +1,184 @@
import { makeNixCommandArgs } from "./nix.js";
import { renderCommitMessage, renderPullRequestBody } from "./template.js";
import * as actionsCore from "@actions/core";
import * as actionsExec from "@actions/exec";
import { DetSysAction, inputs } from "detsys-ts";
import * as fs from "fs";
const DEFAULT_FLAKE_DIR = ".";
const PR_BODY_OUTPUT_KEY = "pr-body";
const EVENT_EXECUTION_FAILURE = "execution_failure";
class UpdateFlakeLockAction extends DetSysAction {
private commitMessage: string;
private commitMessageTemplate: string;
private prBody: string;
private prBodyTemplate: string;
private nixOptions: string[];
private flakeInputs: string[];
private pathToFlakeDir: string | null;
private flakeDirsInput: string[] | null;
private flakeDirs: string[];
constructor() {
super({
name: "update-flake-lock",
fetchStyle: "universal",
requireNix: "fail",
});
this.commitMessage = inputs.getString("commit-msg");
this.commitMessageTemplate = inputs.getString("commit-msg-template");
this.prBody = inputs.getString("pr-body");
this.prBodyTemplate = inputs.getString("pr-body-template");
this.flakeInputs = inputs.getArrayOfStrings("inputs", "space");
this.nixOptions = inputs.getArrayOfStrings("nix-options", "space");
this.pathToFlakeDir = inputs.getStringOrNull("path-to-flake-dir");
this.flakeDirsInput = inputs.getArrayOfStringsOrNull("flake-dirs", "space");
this.validateInputs();
if (this.flakeDirsInput !== null && this.flakeDirsInput.length > 0) {
this.flakeDirs = this.flakeDirsInput;
} else {
this.flakeDirs = [this.pathToFlakeDir ?? DEFAULT_FLAKE_DIR];
}
}
async main(): Promise<void> {
for (const directory of this.flakeDirs) {
await this.updateFlakeInDirectory(directory);
}
const prBody =
this.prBody !== ""
? this.prBody
: renderPullRequestBody(this.prBodyTemplate, this.flakeDirs);
actionsCore.setOutput(PR_BODY_OUTPUT_KEY, prBody);
}
// No post phase
async post(): Promise<void> {}
private async updateFlakeInDirectory(flakeDir: string): Promise<void> {
this.ensureDirectoryExists(flakeDir);
this.ensureDirectoryIsFlake(flakeDir);
actionsCore.debug(`Running flake lock update in directory \`${flakeDir}\``);
const flakeDotLock = `${flakeDir}/flake.lock`;
const commitMessage =
this.commitMessage !== ""
? this.commitMessage
: renderCommitMessage(
this.commitMessageTemplate,
flakeDir,
flakeDotLock,
);
// Nix command of this form:
// nix ${maybe nix options} flake ${"update" or "lock"} ${maybe --update-input flags} --commit-lock-file --commit-lockfile-summary ${commit message}
// Example commands:
// nix --extra-substituters https://example.com flake lock --update-input nixpkgs --commit-lock-file --commit-lockfile-summary "updated flake.lock"
// nix flake update --commit-lock-file --commit-lockfile-summary "updated flake.lock"
const nixCommandArgs: string[] = makeNixCommandArgs(
this.nixOptions,
this.flakeInputs,
commitMessage,
);
actionsCore.debug(
JSON.stringify({
directory: flakeDir,
options: this.nixOptions,
inputs: this.flakeInputs,
message: this.commitMessage,
args: nixCommandArgs,
}),
);
const execOptions: actionsExec.ExecOptions = {
cwd: flakeDir,
};
const exitCode = await actionsExec.exec("nix", nixCommandArgs, execOptions);
if (exitCode !== 0) {
this.recordEvent(EVENT_EXECUTION_FAILURE, {
exitCode,
});
actionsCore.setFailed(
`non-zero exit code of ${exitCode} detected while updating directory \`${flakeDir}\``,
);
} else {
actionsCore.info(
`flake.lock file in \`${flakeDir}\` was successfully updated`,
);
}
}
private validateInputs(): void {
// Ensure that either `path-to-flake-dir` or `flake-dirs` is set to a meaningful value but not both
if (
this.flakeDirsInput !== null &&
this.flakeDirsInput.length > 0 &&
this.pathToFlakeDir !== null &&
this.pathToFlakeDir !== ""
) {
throw new Error(
"Both `path-to-flake-dir` and `flake-dirs` are set, whereas only one can be",
);
}
// Ensure that `flake-dirs` isn't an empty array if set
if (this.flakeDirsInput !== null && this.flakeDirsInput.length === 0) {
throw new Error(
"The `flake-dirs` input is set to an empty array; it must contain at least one directory",
);
}
// Ensure that both `flake-dirs` and `inputs` aren't set at the same time
if (
this.flakeDirsInput !== null &&
this.flakeDirsInput.length > 0 &&
this.flakeInputs.length > 0
) {
throw new Error(
`You've set both \`flake-dirs\` and \`inputs\` but you can only set one`,
);
}
}
private ensureDirectoryExists(flakeDir: string): void {
actionsCore.debug(`Checking that flake directory \`${flakeDir}\` exists`);
// Ensure the directory exists
fs.access(flakeDir, fs.constants.F_OK, (err) => {
if (err !== null) {
throw new Error(`Directory \`${flakeDir}\` doesn't exist`);
} else {
actionsCore.debug(`Flake directory \`${flakeDir}\` exists`);
}
});
}
private ensureDirectoryIsFlake(flakeDir: string): void {
const flakeDotNix = `${flakeDir}/flake.nix`;
if (!fs.existsSync(flakeDotNix)) {
throw new Error(
`Directory \`${flakeDir}\` is not a valid flake as it doesn't contain a \`flake.nix\``,
);
} else {
actionsCore.debug(`Directory \`${flakeDir}\` is a valid flake`);
}
}
}
function main(): void {
new UpdateFlakeLockAction().execute();
}
main();

18
src/nix.ts Normal file
View File

@ -0,0 +1,18 @@
// Build the Nix args out of inputs from the Actions environment
export function makeNixCommandArgs(
nixOptions: string[],
flakeInputs: string[],
commitMessage: string,
): string[] {
const flakeInputFlags = flakeInputs.flatMap((input) => [
"--update-input",
input,
]);
const updateLockMechanism = flakeInputFlags.length === 0 ? "update" : "lock";
return nixOptions
.concat(["flake", updateLockMechanism])
.concat(flakeInputFlags)
.concat(["--commit-lock-file", "--commit-lockfile-summary", commitMessage]);
}

75
src/template.test.ts Normal file
View File

@ -0,0 +1,75 @@
import { renderCommitMessage, renderPullRequestBody } from "./template.js";
import { template } from "handlebars";
import { Test, describe, expect, test } from "vitest";
describe("templating", () => {
test("commit message", () => {
type TestCase = {
template: string;
flakeDotLockDir: string;
flakeDotLock: string;
expected: string;
};
const testCases: TestCase[] = [
{
template: "Updating flake.lock in dir {{ flake_dot_lock_dir }}",
flakeDotLockDir: ".",
flakeDotLock: "./flake.lock",
expected: "Updating flake.lock in dir .",
},
{
template:
"Here I go doing some updating of my pristine flake.lock at {{ flake_dot_lock }}",
flakeDotLockDir: "subflake",
flakeDotLock: "subflake/flake.lock",
expected:
"Here I go doing some updating of my pristine flake.lock at subflake/flake.lock",
},
{
template: "This variable doesn't exist: {{ foo }}",
flakeDotLockDir: ".",
flakeDotLock: "./flake.lock",
expected: "This variable doesn't exist: ",
},
];
testCases.forEach(
({ template, flakeDotLockDir, flakeDotLock, expected }) => {
expect(
renderCommitMessage(template, flakeDotLockDir, flakeDotLock),
).toEqual(expected);
},
);
});
test("pull request body", () => {
type TestCase = {
template: string;
dirs: string[];
expected: string;
};
const testCases: TestCase[] = [
{
template: "Updated inputs: {{ comma_separated_dirs }}",
dirs: ["."],
expected: "Updated inputs: .",
},
{
template: "Updated inputs: {{ space_separated_dirs }}",
dirs: ["subflake", "subflake2"],
expected: "Updated inputs: subflake subflake2",
},
{
template: "Updated inputs:\n{{ updated_dirs_list }}",
dirs: ["flake1", "flake2"],
expected: `Updated inputs:\n* flake1\n* flake2`,
},
];
testCases.forEach(({ template, dirs, expected }) => {
expect(renderPullRequestBody(template, dirs)).toEqual(expected);
});
});
});

39
src/template.ts Normal file
View File

@ -0,0 +1,39 @@
import Handlebars from "handlebars";
export function renderPullRequestBody(
template: string,
dirs: string[],
): string {
const commaSeparated = dirs.join(", ");
const spaceSeparated = dirs.join(" ");
const dirsList = dirs.map((d: string) => `* ${d}`).join("\n");
const tpl = Handlebars.compile(template);
return tpl({
// eslint-disable-next-line camelcase
comma_separated_dirs: commaSeparated,
// eslint-disable-next-line camelcase
space_separated_dirs: spaceSeparated,
// eslint-disable-next-line camelcase
updated_dirs_list: dirsList,
});
}
export function renderCommitMessage(
template: string,
flakeDotLockDir: string,
flakeDotLock: string,
): string {
return render(template, {
// eslint-disable-next-line camelcase
flake_dot_lock_dir: flakeDotLockDir,
// eslint-disable-next-line camelcase
flake_dot_lock: flakeDotLock,
});
}
function render(template: string, inputs: Record<string, string>): string {
const tpl = Handlebars.compile(template);
return tpl(inputs);
}

View File

@ -0,0 +1,74 @@
import { makeNixCommandArgs } from "./nix.js";
import { expect, test } from "vitest";
test("Nix command arguments", () => {
type TestCase = {
inputs: {
nixOptions: string[];
flakeInputs: string[];
commitMessage: string;
};
expected: string[];
};
const testCases: TestCase[] = [
{
inputs: {
nixOptions: ["--log-format", "raw"],
flakeInputs: [],
commitMessage: "just testing",
},
expected: [
"--log-format",
"raw",
"flake",
"update",
"--commit-lock-file",
"--commit-lockfile-summary",
"just testing",
],
},
{
inputs: {
nixOptions: [],
flakeInputs: ["nixpkgs", "rust-overlay"],
commitMessage: "just testing",
},
expected: [
"flake",
"lock",
"--update-input",
"nixpkgs",
"--update-input",
"rust-overlay",
"--commit-lock-file",
"--commit-lockfile-summary",
"just testing",
],
},
{
inputs: {
nixOptions: ["--debug"],
flakeInputs: [],
commitMessage: "just testing",
},
expected: [
"--debug",
"flake",
"update",
"--commit-lock-file",
"--commit-lockfile-summary",
"just testing",
],
},
];
testCases.forEach(({ inputs, expected }) => {
const args = makeNixCommandArgs(
inputs.nixOptions,
inputs.flakeInputs,
inputs.commitMessage,
);
expect(args).toStrictEqual(expected);
});
});

15
tsconfig.json Normal file
View File

@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
"module": "Node16",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
"resolveJsonModule": true,
"declaration": true
},
"exclude": ["node_modules", "**/*.test.ts", "dist"]
}

16
tsup.config.ts Normal file
View File

@ -0,0 +1,16 @@
import { name } from "./package.json";
import { defineConfig } from "tsup";
export default defineConfig({
name,
entry: ["src/index.ts"],
format: ["esm"],
target: "node20",
bundle: true,
splitting: false,
sourcemap: true,
clean: true,
dts: {
resolve: true,
},
});

View File

@ -1,23 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "$PATH_TO_FLAKE_DIR" ]]; then
cd "$PATH_TO_FLAKE_DIR"
fi
options=()
if [[ -n "$NIX_OPTIONS" ]]; then
for option in $NIX_OPTIONS; do
options+=("${option}")
done
fi
if [[ -n "$TARGETS" ]]; then
inputs=()
for input in $TARGETS; do
inputs+=("--update-input" "$input")
done
nix "${options[@]}" flake lock "${inputs[@]}" --commit-lock-file --commit-lockfile-summary "$COMMIT_MSG"
else
nix "${options[@]}" flake update --commit-lock-file --commit-lockfile-summary "$COMMIT_MSG"
fi