Compare commits

..

20 Commits

Author SHA1 Message Date
Luc Perkins
35168619ff
Fix conflicts with main 2024-06-06 10:54:15 -07:00
Luc Perkins
4e3e886d7a
Add missing env vars to inputs 2024-06-06 10:51:46 -07:00
Luc Perkins
0829421b88
Initial version of PR body rendering 2024-06-04 09:19:35 -07:00
Luc Perkins
8c5e8043f8
More test cases: 2024-06-04 08:44:31 -07:00
Luc Perkins
09b0ac8cd3
Enable supplying a commit message template 2024-06-04 08:28:12 -07:00
Luc Perkins
d3aa136776
Provide pr-body as output from step 2024-06-03 14:32:30 -07:00
Luc Perkins
5d674d8347
Fix formatting 2024-06-03 14:20:34 -07:00
Luc Perkins
3f84616103
Remove test subflake 2024-06-03 14:18:58 -07:00
Luc Perkins
cbee267f6f
Fix merge conflicts with main 2024-06-03 13:44:53 -07:00
Luc Perkins
5d30a7794a
Make a single dir a special case 2024-05-23 23:39:14 -03:00
Luc Perkins
dccc3175bf
Update detsys-ts 2024-05-23 19:19:07 -03:00
Luc Perkins
c16b76233e
Provide improved input handling 2024-05-23 16:09:06 -03:00
Luc Perkins
b5a9000c3f
Check for flake-dirs clash with inputs 2024-05-23 15:55:25 -03:00
Luc Perkins
b6aab91cde
Improve flake-dirs handling logic 2024-05-23 15:48:19 -03:00
Luc Perkins
c7eb3f32c9
Fix input validation 2024-05-23 15:42:03 -03:00
Luc Perkins
0c1dd1090d
Add CI test 2024-05-23 15:37:34 -03:00
Luc Perkins
eb897bb16b
Add README.md example 2024-05-23 15:34:32 -03:00
Luc Perkins
1127ba41bd
Check that each directory is a valid flake 2024-05-23 15:33:00 -03:00
Luc Perkins
f5dab0ead5
Rework input handling 2024-05-23 15:19:56 -03:00
Luc Perkins
6a1287939f
Add flake-dirs input 2024-05-23 15:16:12 -03:00
16 changed files with 51137 additions and 39042 deletions

View File

@ -1,5 +1,4 @@
name: CI name: CI
on: on:
pull_request: pull_request:
push: push:
@ -7,22 +6,15 @@ on:
jobs: jobs:
typescript-action: typescript-action:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
permissions:
id-token: write
contents: read
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Check Nixpkgs - name: Install Nix
uses: DeterminateSystems/flake-checker-action@main uses: DeterminateSystems/nix-installer-action@main
with: - name: Enable magic Nix cache
fail-mode: true uses: DeterminateSystems/magic-nix-cache-action@main
- name: Install Determinate Nix
uses: DeterminateSystems/determinate-nix-action@v3
- name: Enable FlakeHub Cache
uses: DeterminateSystems/flakehub-cache-action@main
- name: Install pnpm dependencies - name: Install pnpm dependencies
run: nix develop --command pnpm install run: nix develop --command pnpm install
- name: Check formatting - name: Check formatting

View File

@ -1,5 +1,4 @@
name: update-flake-lock name: update-flake-lock
on: on:
workflow_dispatch: workflow_dispatch:
schedule: schedule:
@ -7,17 +6,16 @@ on:
jobs: jobs:
lockfile: lockfile:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
permissions:
id-token: write
contents: read
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Determinate Nix - name: Install Nix
uses: DeterminateSystems/determinate-nix-action@v3 uses: DeterminateSystems/nix-installer-action@main
- name: Enable FlakeHub Cache - name: Enable magic Nix cache
uses: DeterminateSystems/flakehub-cache-action@main uses: DeterminateSystems/magic-nix-cache-action@main
- name: Check flake
uses: DeterminateSystems/flake-checker-action@main
- name: Update flake.lock - name: Update flake.lock
uses: ./. uses: ./.
with: with:

View File

@ -1,18 +0,0 @@
# .github/workflows/sync.yml
name: Rebase Upstream
on:
schedule:
- cron: "0 0 * * 0" # run once a week
workflow_dispatch: # run manually
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
with:
fetch-depth: 10 # greater than the number of commits you made
- uses: imba-tjd/rebase-upstream-action@master
with: # all args are optional
upstream: DeterminateSystems/update-flake-lock
branch: main

View File

@ -1,5 +1,4 @@
name: CI name: CI
on: on:
pull_request: pull_request:
push: push:
@ -7,7 +6,7 @@ on:
jobs: jobs:
validate: validate:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:

172
README.md
View File

@ -1,37 +1,32 @@
# update-flake-lock # update-flake-lock
This is a GitHub Action that updates the [`flake.lock`][lockfile] file for your [Nix flake][flakes] whenever it is run. This is a GitHub Action that will update your flake.lock file whenever it is run.
> [!NOTE] > **NOTE:** As of v3, this action will no longer automatically install Nix to the action runner. You **MUST** set up a Nix with flakes support enabled prior to running this action, or your workflow will not function as expected.
> As of v3, this action no longer automatically installs [Determinate Nix][det-nix] to the action runner.
> You **must** set up Nix with flakes support enabled prior to running this action or your workflow will not function as expected.
## Example ## Example
Here's an example GitHub Action workflow using this Action: An example GitHub Action workflow using this action would look like the following:
```yaml ```yaml
name: "Flake.lock: update Nix dependencies" name: update-flake-lock
on: on:
workflow_dispatch: # allows manual triggering workflow_dispatch: # allows manual triggering
schedule: schedule:
- cron: '0 0 * * 0' # runs weekly on Sunday at 00:00 - cron: '0 0 * * 0' # runs weekly on Sunday at 00:00
jobs: jobs:
nix-flake-update: lockfile:
permissions:
contents: write
id-token: write
issues: write
pull-requests: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - name: Checkout repository
- uses: DeterminateSystems/determinate-nix-action@v3 uses: actions/checkout@v4
- uses: DeterminateSystems/update-flake-lock@main - name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@main
with: with:
pr-title: "Update Nix flake inputs" # Title of PR to be created pr-title: "Update flake.lock" # Title of PR to be created
pr-labels: | # Labels to be set on the PR pr-labels: | # Labels to be set on the PR
dependencies dependencies
automated automated
@ -39,14 +34,12 @@ jobs:
## Example updating specific input(s) ## Example updating specific input(s)
> [!NOTE] > **NOTE**: If any inputs have a stale reference (e.g. the lockfile thinks a git input wants its "ref" to be "nixos-unstable", but the flake.nix specifies "nixos-unstable-small"), they will also be updated. At this time, there is no known workaround.
> If any inputs have a stale reference (e.g. the lockfile thinks a git input wants its "ref" to be "nixos-unstable", but the flake.nix specifies "nixos-unstable-small"), they are also updated. At this time, there is no known workaround.
It's also possible to update specific [flake inputs][inputs] by specifying them in a space-separated list: It is also possible to update specific inputs by specifying them in a space-separated list:
```yaml ```yaml
name: update-flake-lock name: update-flake-lock
on: on:
workflow_dispatch: # allows manual triggering workflow_dispatch: # allows manual triggering
schedule: schedule:
@ -58,17 +51,17 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Determinate Nix - name: Install Nix
uses: DeterminateSystems/determinate-nix-action@v3 uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock - name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@main uses: DeterminateSystems/update-flake-lock@vX
with: with:
inputs: input1 input2 input3 inputs: input1 input2 input3
``` ```
## Example adding options to nix command ## Example adding options to nix command
It's also possible to use specific options to the `nix` command in a space-separated list: It is also possible to use specific options to the nix command in a space separated list:
```yaml ```yaml
name: update-flake-lock name: update-flake-lock
@ -83,10 +76,10 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Determinate Nix - name: Install Nix
uses: DeterminateSystems/determinate-nix-action@v3 uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock - name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@main uses: DeterminateSystems/update-flake-lock@vX
with: with:
nix-options: --debug --log-format raw nix-options: --debug --log-format raw
``` ```
@ -106,11 +99,11 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Determinate Nix - name: Install Nix
uses: DeterminateSystems/determinate-nix-action@v3 uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock - name: Update flake.lock
id: update id: update
uses: DeterminateSystems/update-flake-lock@main uses: DeterminateSystems/update-flake-lock@vX
with: with:
inputs: input1 input2 input3 inputs: input1 input2 input3
- name: Print PR number - name: Print PR number
@ -135,16 +128,33 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Determinate Nix - name: Install Nix
uses: DeterminateSystems/determinate-nix-action@v3 uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock - name: Update flake.lock
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
uses: DeterminateSystems/update-flake-lock@main uses: DeterminateSystems/update-flake-lock@vX
with: with:
inputs: input1 input2 input3 inputs: input1 input2 input3
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' 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 ## 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: 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:
@ -162,30 +172,37 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Determinate Nix - name: Install Nix
uses: DeterminateSystems/determinate-nix-action@v3 uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock - name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@main uses: DeterminateSystems/update-flake-lock@vX
with: with:
git-author-name: Jane Author git-author-name: 'Jane Author'
git-author-email: github-actions[bot]@users.noreply.github.com git-author-email: 'github-actions[bot]@users.noreply.github.com'
git-committer-name: John Committer git-committer-name: 'John Committer'
git-committer-email: github-actions[bot]@users.noreply.github.com git-committer-email: 'github-actions[bot]@users.noreply.github.com'
``` ```
## Running GitHub Actions CI ## Running GitHub Actions CI
GitHub Actions doesn't run workflows when a branch is pushed by or a PR is opened by a GitHub Action. GitHub Actions will not run workflows when a branch is pushed by or a PR is opened by a GitHub Action. There are two ways to have GitHub Actions CI run on a PR submitted by this action.
There are two ways to have GitHub Actions CI run on a PR submitted by this action.
### Without a Personal Authentication Token ### Without a Personal Authentication Token
Without using a Personal Authentication Token, close and reopen the pull request manually to kick off CI. Without using a Personal Authentication Token, you can manually run the following to kick off a CI run:
```
git branch -D update_flake_lock_action
git fetch origin
git checkout update_flake_lock_action
git commit --amend --no-edit
git push origin update_flake_lock_action --force
```
### With a Personal Authentication Token ### With a Personal Authentication Token
By providing a Personal Authentication Token, the PR is submitted in a way that bypasses this limitation (GitHub essentially thinks it's the owner of the PAT submitting the PR, and not an Action). By providing a Personal Authentication Token, the PR will be submitted in a way that bypasses this limitation (GitHub will essentially think it is the owner of the PAT submitting the PR, and not an Action).
You can create a token by visiting https://github.com/settings/tokens and select at least the `repo` scope. For the new fine-grained tokens, you need to enable read and write access for "Contents" and "Pull Requests" permissions. Then, store this token in your repository secrets (i.e. `https://github.com/<USER>/<REPO>/settings/secrets/actions`) as `GH_TOKEN_FOR_UPDATES` and set up your workflow file like the following: You can create a token by visiting https://github.com/settings/tokens and select at least the `repo` scope. Then, store this token in your repository secrets (i.e. `https://github.com/<USER>/<REPO>/settings/secrets/actions`) as `GH_TOKEN_FOR_UPDATES` and set up your workflow file like the following:
```yaml ```yaml
name: update-flake-lock name: update-flake-lock
@ -200,34 +217,30 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Determinate Nix - name: Install Nix
uses: DeterminateSystems/determinate-nix-action@v3 uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock - name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@main uses: DeterminateSystems/update-flake-lock@vX
with: with:
token: ${{ secrets.GH_TOKEN_FOR_UPDATES }} token: ${{ secrets.GH_TOKEN_FOR_UPDATES }}
``` ```
## With GPG commit signing ## With GPG commit signing
It's possible for the bot to produce GPG-signed commits. It's possible for the bot to produce GPG signed commits. Associating a GPG public key to a github user account is not required but it is necessary if you want the signed commits to appear as verified in Github. This can be a compliance requirement in some cases.
Associating a GPG public key to a GitHub user account isn't required but it *is* necessary if you want the signed commits to appear as verified in Github.
This can be a compliance requirement in some cases.
You can follow [GitHub's guide to creating and/or adding a new GPG key to an user account](https://docs.github.com/en/authentication/managing-commit-signature-verification/adding-a-new-gpg-key-to-your-github-account). You can follow [Github's guide on creating and/or adding a new GPG key to an user account](https://docs.github.com/en/authentication/managing-commit-signature-verification/adding-a-new-gpg-key-to-your-github-account). Using a specific github user account for the bot can be a good security measure to dissociate this bot's actions and commits from your personal github account.
Using a specific GitHub user account for the bot can be a good security measure to dissociate this bot's actions and commits from your personal GitHub account.
For the bot to produce signed commits, you need to provide the GPG private keys to this action's input parameters. You can safely do that with [Github secrets as explained here](https://github.com/crazy-max/ghaction-import-gpg#prerequisites). For the bot to produce signed commits, you will have to provide the GPG private keys to this action's input parameters. You can safely do that with [Github secrets as explained here](https://github.com/crazy-max/ghaction-import-gpg#prerequisites).
When using commit signing, the commit author name and email for the commits produced by this bot would correspond to the ones associated to the GPG Public Key. When using commit signing, the commit author name and email for the commits produced by this bot would correspond to the ones associated to the GPG Public Key.
If you want to sign using a subkey, you must specify the subkey fingerprint using the `gpg-fingerprint` input parameter. If you want to sign using a subkey, you must specify the subkey fingerprint using the `gpg-fingerprint` input parameter.
Here's an example of how to using this action with commit signing: You can find an example of how to using this action with commit signing below:
```yaml ```yaml
name: update-flake-lock name: update-flake-lock
on: on:
workflow_dispatch: # allows manual triggering workflow_dispatch: # allows manual triggering
schedule: schedule:
@ -239,10 +252,10 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Determinate Nix - name: Install Nix
uses: DeterminateSystems/determinate-nix-action@v3 uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock - name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@main uses: DeterminateSystems/update-flake-lock@vX
with: with:
sign-commits: true sign-commits: true
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
@ -252,19 +265,18 @@ jobs:
## Custom PR Body ## Custom PR Body
By default, the generated PR body uses this template: By default the generated PR body is set to be the following template:
````handlebars ````handlebars
Automated changes by the [update-flake-lock](https://github.com/DeterminateSystems/update-flake-lock) GitHub Action. Automated changes by the [update-flake-lock](https://github.com/DeterminateSystems/update-flake-lock) GitHub Action.
```` ```
{{ env.GIT_COMMIT_MESSAGE }} {{ env.GIT_COMMIT_MESSAGE }}
````
``` ```
### Running GitHub Actions on this PR ### Running GitHub Actions on this PR
GitHub Actions doesn't run workflows on pull requests that are opened by a GitHub Action. GitHub Actions will not run workflows on pull requests which are opened by a GitHub Action.
To run GitHub Actions workflows on this PR, run: To run GitHub Actions workflows on this PR, run:
@ -277,14 +289,12 @@ git push origin update_flake_lock_action --force
``` ```
```` ````
You can customize it, however, using variable interpolation performed with [Handlebars]. However you can customize it, with variable interpolation performed with [Handlebars](https://handlebarsjs.com/). This allows you to customize the template with the following variables:
This enables you to customize the template with these variables: - env.GIT_AUTHOR_NAME
- env.GIT_AUTHOR_EMAIL
- `env.GIT_AUTHOR_NAME` - env.GIT_COMMITTER_NAME
- `env.GIT_AUTHOR_EMAIL` - env.GIT_COMMITTER_EMAIL
- `env.GIT_COMMITTER_NAME` - env.GIT_COMMIT_MESSAGE
- `env.GIT_COMMITTER_EMAIL`
- `env.GIT_COMMIT_MESSAGE`
## Add assignees or reviewers ## Add assignees or reviewers
@ -304,10 +314,10 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Determinate Nix - name: Install Nix
uses: DeterminateSystems/determinate-nix-action@v3 uses: DeterminateSystems/nix-installer-action@v1
- name: Update flake.lock - name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@main uses: DeterminateSystems/update-flake-lock@vX
with: with:
pr-assignees: SomeGitHubUsername pr-assignees: SomeGitHubUsername
pr-reviewers: SomeOtherGitHubUsername,SomeThirdGitHubUsername pr-reviewers: SomeOtherGitHubUsername,SomeThirdGitHubUsername
@ -315,16 +325,8 @@ jobs:
## Contributing ## Contributing
Feel free to send a PR or open an issue if you find that something functions unexpectedly! Feel free to send a PR or open an issue if you find something functions unexpectedly! Please make sure to test your changes and update any related documentation before submitting your PR.
Please make sure to test your changes and update any related documentation before submitting your PR.
### How to test changes ### How to test changes
In order to more easily test your changes to this action, we have created a template repository that should point you in the right direction: https://github.com/DeterminateSystems/update-flake-lock-test-template. In order to more easily test your changes to this action, we have created a template repository that should point you in the right direction: https://github.com/DeterminateSystems/update-flake-lock-test-template. Please see the README in that repository for instructions on testing your changes.
Please see the README in that repository for instructions on testing your changes.
[det-nix]: https://docs.determinate.systems/determinate-nix
[flakes]: https://zero-to-nix.com/concepts/flakes
[handlebars]: https://handlebarsjs.com
[inputs]: https://zero-to-nix.com/concepts/flakes/#inputs
[lockfile]: https://zero-to-nix.com/concepts/flakes/#lockfile

View File

@ -9,10 +9,21 @@ inputs:
description: "GITHUB_TOKEN or a `repo` scoped Personal Access Token (PAT)" description: "GITHUB_TOKEN or a `repo` scoped Personal Access Token (PAT)"
required: false required: false
default: ${{ github.token }} default: ${{ github.token }}
commit-msg: commit-msg-template:
description: "The message provided with the commit" 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 required: false
default: "flake.lock: Update"
base: base:
description: "Sets the pull request base branch. Defaults to the branch checked out in the workflow." description: "Sets the pull request base branch. Defaults to the branch checked out in the workflow."
required: false required: false
@ -21,12 +32,32 @@ inputs:
required: false required: false
default: "update_flake_lock_action" default: "update_flake_lock_action"
path-to-flake-dir: 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 required: false
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: pr-title:
description: "The title of the PR to be created" description: "The title of the PR to be created"
required: false required: false
default: "flake.lock: Update" 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: pr-body:
description: "The body of the PR to be created" description: "The body of the PR to be created"
required: false required: false
@ -41,7 +72,15 @@ inputs:
GitHub Actions will not run workflows on pull requests which are opened by a GitHub Action. GitHub Actions will not run workflows on pull requests which are opened by a GitHub Action.
**To run GitHub Actions workflows on this PR, close and re-open this pull request.** To run GitHub Actions workflows on this PR, run:
```sh
git branch -D update_flake_lock_action
git fetch origin
git checkout update_flake_lock_action
git commit --amend --no-edit
git push origin update_flake_lock_action --force
```
pr-labels: 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"
@ -98,9 +137,6 @@ outputs:
pull-request-number: 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 }} value: ${{ steps.create-pr.outputs.pull-request-number }}
pull-request-url:
description: "The The URL of the opened pull request."
value: ${{ steps.create-pr.outputs.pull-request-url }}
pull-request-operation: 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 }} value: ${{ steps.create-pr.outputs.pull-request-operation }}
@ -110,7 +146,7 @@ runs:
- name: Import bot's GPG key for signing commits - name: Import bot's GPG key for signing commits
if: ${{ inputs.sign-commits == 'true' }} if: ${{ inputs.sign-commits == 'true' }}
id: import-gpg id: import-gpg
uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0 uses: crazy-max/ghaction-import-gpg@v6
with: with:
gpg_private_key: ${{ inputs.gpg-private-key }} gpg_private_key: ${{ inputs.gpg-private-key }}
fingerprint: ${{ inputs.gpg-fingerprint }} fingerprint: ${{ inputs.gpg-fingerprint }}
@ -141,6 +177,7 @@ runs:
echo "GIT_COMMITTER_NAME=${{ inputs.git-committer-name }}" >> $GITHUB_ENV echo "GIT_COMMITTER_NAME=${{ inputs.git-committer-name }}" >> $GITHUB_ENV
echo "GIT_COMMITTER_EMAIL=<${{ inputs.git-committer-email }}>" >> $GITHUB_ENV echo "GIT_COMMITTER_EMAIL=<${{ inputs.git-committer-email }}>" >> $GITHUB_ENV
- name: Run update-flake-lock - name: Run update-flake-lock
id: update-flake-lock
shell: bash shell: bash
run: node "$GITHUB_ACTION_PATH/dist/index.js" run: node "$GITHUB_ACTION_PATH/dist/index.js"
env: env:
@ -149,6 +186,7 @@ runs:
INPUT_BASE: ${{ inputs.base }} INPUT_BASE: ${{ inputs.base }}
INPUT_BRANCH: ${{ inputs.branch }} INPUT_BRANCH: ${{ inputs.branch }}
INPUT_COMMIT-MSG: ${{ inputs.commit-msg }} 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-EMAIL: ${{ inputs.git-author-email }}
INPUT_GIT-AUTHOR-NAME: ${{ inputs.git-author-name }} INPUT_GIT-AUTHOR-NAME: ${{ inputs.git-author-name }}
INPUT_GIT-COMMITTER-EMAIL: ${{ inputs.git-committer-email }} INPUT_GIT-COMMITTER-EMAIL: ${{ inputs.git-committer-email }}
@ -159,8 +197,10 @@ runs:
INPUT_INPUTS: ${{ inputs.inputs }} INPUT_INPUTS: ${{ inputs.inputs }}
INPUT_NIX-OPTIONS: ${{ inputs.nix-options }} INPUT_NIX-OPTIONS: ${{ inputs.nix-options }}
INPUT_PATH-TO-FLAKE-DIR: ${{ inputs.path-to-flake-dir }} INPUT_PATH-TO-FLAKE-DIR: ${{ inputs.path-to-flake-dir }}
INPUT_FLAKE-DIRS: ${{ inputs.flake-dirs }}
INPUT_PR-ASSIGNEES: ${{ inputs.pr-assignees }} INPUT_PR-ASSIGNEES: ${{ inputs.pr-assignees }}
INPUT_PR-BODY: ${{ inputs.pr-body }} INPUT_PR-BODY: ${{ inputs.pr-body }}
INPUT_PR-BODY-TEMPLATE: ${{ inputs.pr-body-template }}
INPUT_PR-LABELS: ${{ inputs.pr-labels }} INPUT_PR-LABELS: ${{ inputs.pr-labels }}
INPUT_PR-REVIEWERS: ${{ inputs.pr-reviewers }} INPUT_PR-REVIEWERS: ${{ inputs.pr-reviewers }}
INPUT_PR-TITLE: ${{ inputs.pr-title }} INPUT_PR-TITLE: ${{ inputs.pr-title }}
@ -173,7 +213,7 @@ runs:
uses: DamianReeves/write-file-action@v1.3 uses: DamianReeves/write-file-action@v1.3
with: with:
path: pr_body.template path: pr_body.template
contents: ${{ inputs.pr-body }} contents: ${{ steps.update-flake-lock.outputs.pr-body }}
env: {} env: {}
- name: Set additional env variables (GIT_COMMIT_MESSAGE) - name: Set additional env variables (GIT_COMMIT_MESSAGE)
shell: bash shell: bash
@ -185,7 +225,7 @@ runs:
echo "$DELIMITER" >> $GITHUB_ENV echo "$DELIMITER" >> $GITHUB_ENV
echo "GIT_COMMIT_MESSAGE is: ${COMMIT_MESSAGE}" echo "GIT_COMMIT_MESSAGE is: ${COMMIT_MESSAGE}"
- name: Interpolate PR Body - name: Interpolate PR Body
uses: pedrolamas/handlebars-action@2995d7eadacbc8f2f6ab8431a01d84a5fa3b8bb4 # v2.4.0 uses: pedrolamas/handlebars-action@v2.4.0
with: with:
files: "pr_body.template" files: "pr_body.template"
output-filename: "pr_body.txt" output-filename: "pr_body.txt"
@ -202,7 +242,7 @@ runs:
run: rm -f pr_body.txt pr_body.template run: rm -f pr_body.txt pr_body.template
- name: Create PR - name: Create PR
id: create-pr id: create-pr
uses: https://nayeonie.com/ahuston-0/create-pull-request@6b3a86bf8bfe10eb458b00968a8fefe2f5f5a6c1 # v6.0.5 uses: peter-evans/create-pull-request@v6
with: with:
base: ${{ inputs.base }} base: ${{ inputs.base }}
branch: ${{ inputs.branch }} branch: ${{ inputs.branch }}

82327
dist/index.js vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

10
flake.lock generated
View File

@ -2,12 +2,12 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1746663147, "lastModified": 1713537308,
"narHash": "sha256-Ua0drDHawlzNqJnclTJGf87dBmaO/tn7iZ+TCkTRpRc=", "narHash": "sha256-XtTSSIB2DA6tOv+l0FhvfDMiyCmhoRbNB+0SeInZkbk=",
"rev": "dda3dcd3fe03e991015e9a74b22d35950f264a54", "rev": "5c24cf2f0a12ad855f444c30b2421d044120c66f",
"revCount": 796699, "revCount": 614481,
"type": "tarball", "type": "tarball",
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.796699%2Brev-dda3dcd3fe03e991015e9a74b22d35950f264a54/0196b263-02b0-7dec-8aca-c2506ed2485f/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": { "original": {
"type": "tarball", "type": "tarball",

View File

@ -12,6 +12,7 @@
"lint": "eslint src/**/*.ts --ignore-pattern *.test.ts", "lint": "eslint src/**/*.ts --ignore-pattern *.test.ts",
"package": "ncc build", "package": "ncc build",
"test": "vitest --watch false", "test": "vitest --watch false",
"test-dev": "vitest",
"all": "pnpm run format && pnpm run lint && pnpm run build && pnpm run package" "all": "pnpm run format && pnpm run lint && pnpm run build && pnpm run package"
}, },
"repository": { "repository": {
@ -26,22 +27,23 @@
}, },
"homepage": "https://github.com/DeterminateSystems/update-flake-lock#readme", "homepage": "https://github.com/DeterminateSystems/update-flake-lock#readme",
"dependencies": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"detsys-ts": "github:DeterminateSystems/detsys-ts" "detsys-ts": "github:DeterminateSystems/detsys-ts",
"handlebars": "^4.7.8"
}, },
"devDependencies": { "devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.3.0", "@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/eslint-plugin": "^7.11.0",
"@vercel/ncc": "^0.38.3", "@vercel/ncc": "^0.38.1",
"eslint": "^8.57.1", "eslint": "^8.57.0",
"eslint-import-resolver-typescript": "^3.10.1", "eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-github": "^4.10.2", "eslint-plugin-github": "^4.10.2",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.4.0", "eslint-plugin-prettier": "^5.1.3",
"prettier": "^3.5.3", "prettier": "^3.2.5",
"tsup": "^8.4.0", "tsup": "^8.0.2",
"typescript": "^5.8.3", "typescript": "^5.4.5",
"vitest": "^3.1.3" "vitest": "^1.6.0"
} }
} }

6896
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,26 @@
import { makeNixCommandArgs } from "./nix.js"; import { makeNixCommandArgs } from "./nix.js";
import { renderCommitMessage, renderPullRequestBody } from "./template.js";
import * as actionsCore from "@actions/core"; import * as actionsCore from "@actions/core";
import * as actionsExec from "@actions/exec"; import * as actionsExec from "@actions/exec";
import { DetSysAction, inputs } from "detsys-ts"; 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"; const EVENT_EXECUTION_FAILURE = "execution_failure";
class UpdateFlakeLockAction extends DetSysAction { class UpdateFlakeLockAction extends DetSysAction {
private commitMessage: string; private commitMessage: string;
private commitMessageTemplate: string;
private prBody: string;
private prBodyTemplate: string;
private nixOptions: string[]; private nixOptions: string[];
private flakeInputs: string[]; private flakeInputs: string[];
private pathToFlakeDir: string | null; private pathToFlakeDir: string | null;
private flakeDirsInput: string[] | null;
private flakeDirs: string[];
constructor() { constructor() {
super({ super({
@ -19,19 +30,55 @@ class UpdateFlakeLockAction extends DetSysAction {
}); });
this.commitMessage = inputs.getString("commit-msg"); 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.flakeInputs = inputs.getArrayOfStrings("inputs", "space");
this.nixOptions = inputs.getArrayOfStrings("nix-options", "space"); this.nixOptions = inputs.getArrayOfStrings("nix-options", "space");
this.pathToFlakeDir = inputs.getStringOrNull("path-to-flake-dir"); 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> { async main(): Promise<void> {
await this.update(); 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 // No post phase
async post(): Promise<void> {} async post(): Promise<void> {}
async update(): 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 command of this form:
// nix ${maybe nix options} flake ${"update" or "lock"} ${maybe --update-input flags} --commit-lock-file --commit-lockfile-summary ${commit message} // nix ${maybe nix options} flake ${"update" or "lock"} ${maybe --update-input flags} --commit-lock-file --commit-lockfile-summary ${commit message}
// Example commands: // Example commands:
@ -40,11 +87,12 @@ class UpdateFlakeLockAction extends DetSysAction {
const nixCommandArgs: string[] = makeNixCommandArgs( const nixCommandArgs: string[] = makeNixCommandArgs(
this.nixOptions, this.nixOptions,
this.flakeInputs, this.flakeInputs,
this.commitMessage, commitMessage,
); );
actionsCore.debug( actionsCore.debug(
JSON.stringify({ JSON.stringify({
directory: flakeDir,
options: this.nixOptions, options: this.nixOptions,
inputs: this.flakeInputs, inputs: this.flakeInputs,
message: this.commitMessage, message: this.commitMessage,
@ -53,8 +101,7 @@ class UpdateFlakeLockAction extends DetSysAction {
); );
const execOptions: actionsExec.ExecOptions = { const execOptions: actionsExec.ExecOptions = {
cwd: this.pathToFlakeDir !== null ? this.pathToFlakeDir : undefined, cwd: flakeDir,
ignoreReturnCode: true,
}; };
const exitCode = await actionsExec.exec("nix", nixCommandArgs, execOptions); const exitCode = await actionsExec.exec("nix", nixCommandArgs, execOptions);
@ -63,9 +110,69 @@ class UpdateFlakeLockAction extends DetSysAction {
this.recordEvent(EVENT_EXECUTION_FAILURE, { this.recordEvent(EVENT_EXECUTION_FAILURE, {
exitCode, exitCode,
}); });
actionsCore.setFailed(`non-zero exit code of ${exitCode} detected`); actionsCore.setFailed(
`non-zero exit code of ${exitCode} detected while updating directory \`${flakeDir}\``,
);
} else { } else {
actionsCore.info(`flake.lock file was successfully updated`); 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`);
} }
} }
} }

View File

@ -9,23 +9,10 @@ export function makeNixCommandArgs(
input, input,
]); ]);
// NOTE(cole-h): In Nix versions 2.23.0 and later, `commit-lockfile-summary` became an alias to
// the setting `commit-lock-file-summary` (https://github.com/NixOS/nix/pull/10691), and Nix does
// not treat aliases the same as their "real" setting by requiring setting aliases to be
// configured via `--option <alias name> <option value>`
// (https://github.com/NixOS/nix/issues/10989).
// So, we go the long way so that we can support versions both before and after Nix 2.23.0.
const lockfileSummaryFlags = [
"--option",
"commit-lockfile-summary",
commitMessage,
];
const updateLockMechanism = flakeInputFlags.length === 0 ? "update" : "lock"; const updateLockMechanism = flakeInputFlags.length === 0 ? "update" : "lock";
return nixOptions return nixOptions
.concat(["flake", updateLockMechanism]) .concat(["flake", updateLockMechanism])
.concat(flakeInputFlags) .concat(flakeInputFlags)
.concat(["--commit-lock-file"]) .concat(["--commit-lock-file", "--commit-lockfile-summary", commitMessage]);
.concat(lockfileSummaryFlags);
} }

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

@ -1,16 +1,16 @@
import { makeNixCommandArgs } from "./nix.js"; import { makeNixCommandArgs } from "./nix.js";
import { expect, test } from "vitest"; import { expect, test } from "vitest";
type TestCase = { test("Nix command arguments", () => {
type TestCase = {
inputs: { inputs: {
nixOptions: string[]; nixOptions: string[];
flakeInputs: string[]; flakeInputs: string[];
commitMessage: string; commitMessage: string;
}; };
expected: string[]; expected: string[];
}; };
test("Nix command arguments", () => {
const testCases: TestCase[] = [ const testCases: TestCase[] = [
{ {
inputs: { inputs: {
@ -24,8 +24,7 @@ test("Nix command arguments", () => {
"flake", "flake",
"update", "update",
"--commit-lock-file", "--commit-lock-file",
"--option", "--commit-lockfile-summary",
"commit-lockfile-summary",
"just testing", "just testing",
], ],
}, },
@ -43,8 +42,7 @@ test("Nix command arguments", () => {
"--update-input", "--update-input",
"rust-overlay", "rust-overlay",
"--commit-lock-file", "--commit-lock-file",
"--option", "--commit-lockfile-summary",
"commit-lockfile-summary",
"just testing", "just testing",
], ],
}, },
@ -59,8 +57,7 @@ test("Nix command arguments", () => {
"flake", "flake",
"update", "update",
"--commit-lock-file", "--commit-lock-file",
"--option", "--commit-lockfile-summary",
"commit-lockfile-summary",
"just testing", "just testing",
], ],
}, },