Skip to content

nialov/actions.nix

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

79 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

actions.nix - generate GitHub/Gitea actions using nix

Features

  • Contains a nix module in nix/flake-modules/actions-nix/ that converts nix configuration into GitHub/Gitea action syntax yaml
  • Module contains definition of a pre-commit hook, using git-hooks.nix, that converts actions-nix configuration into respective workflow files in the path defined in configuration

Why

  • Write your action logic in nix and transform that into yaml actions

  • Use the full capabilities of nix to generate logic rather than writing it in plain yaml

    • nix is a programming language rather than just a configuration language like yaml
    • Consequently, it can be used to generate configuration more succinctly using functions
    • Examples and reusable definitions will be added to this project
  • Reuse action definitions you have written in nix across repositories by using flakes rather than copy-pasting yaml across repositories

Example

See nix/ci/default.nix for action configuration in nix. This is turned by the pre-commit hook, or by running nix run .#render-workflows, into the workflow file in .github/workflows/main.yaml.

Installation

With flake-parts

Add the module exposed by this repository and configure your workflows.

inputs.flake-parts.lib.mkFlake { inherit inputs; }
  ({ ... }: {
    imports = [
      inputs.actions-nix.flakeModules.default
      # Module config for your repository (replace with your own below)
      # ./ci
    ];
  });

Without flake-parts

You can also use the plain library API: write an actions-nix config module and evaluate it yourself.

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    actions-nix.url = "github:nialov/actions.nix";
  };

  outputs = { self, nixpkgs, actions-nix }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};
      actionsEval = actions-nix.lib.evalModule pkgs ./ci.nix;
    in
    {
      packages.${system}.render-workflows = actionsEval.config.build.renderWorkflows;
      checks.${system}.actions = actionsEval.config.build.check self;
    };
}

config.build.check self verifies that the workflow files already committed in the flake source match what actions-nix would generate.

Usage

Example ci.nix:

{
  defaultValues.jobs.runs-on = "ubuntu-latest";

  workflows.".github/workflows/main.yaml".jobs.nix-flake-check.steps = [
    { uses = "actions/checkout@v4"; }
    { uses = "cachix/install-nix-action@v31"; }
    {
      name = "Check flake";
      run = "nix -Lv flake check";
    }
  ];
}

Then run:

nix run .#render-workflows

render-workflows then creates or overwrites files in the git root according to ci.nix.

Documentation

The flake.parts website hosts the module option documentation:

Advanced

Note on git-hooks import collisions with flake-parts

The actions-nix module automatically imports git-hooks. If you also explicitly import git-hooks in your downstream project, and the versions differ, this can cause an import collision due to the module options being defined twice.

Example of collision potential in your flake:

imports = [
  inputs.actions-nix.flakeModules.default
  inputs.git-hooks.flakeModule  # Import may conflict as it is already implemented by actions-nix
];

How to avoid collisions:

You do not have to import git-hooks in your flake if you imported the actions-nix module. Alternatively, configure your flake inputs so that both your project and actions-nix use the same version of git-hooks. For example, you can set your git-hooks input to follow the one used by actions-nix, or vice versa:

actions-nix = {
  url = "github:nialov/actions.nix";
};
git-hooks.follows = "actions-nix/git-hooks";

or

git-hooks = {
  url = "github:cachix/git-hooks.nix";
};

actions-nix = {
  url = "github:nialov/actions.nix";
  inputs.git-hooks.follows = "nix-extra/pre-commit-hooks";
};

Recommended:

Let actions-nix handle the import, and avoid importing git-hooks directly.

Setting of default values

A convenience is provided in the form of flake.actions-nix.defaultValues. Setting these options sets the defaults for those value options across workflows and jobs. For example, by setting flake.actions-nix.defaultValues.jobs.runs-on = "ubuntu-latest", all workflows (flake.actions-nix.workflows.<name>) will have the runs-on property set to that default. Overriding individually will still work normally without, e.g., lib.mkForce as defaultValues only sets the default option value. Note that defaultValues have themselves opinionated default values which you should override to fit your needs.

Secrets

GitHub/Gitea Actions secret references (e.g. ${{ secrets.MY_SECRET }}) must be escaped in Nix strings to prevent Nix from interpreting the ${{...}} syntax. Prefix the $ with a backslash. E.g.

env = {
  API_TOKEN_GITHUB = "\${{ secrets.WEBSITE_DEPLOY }}";
};

This produces the correct literal string ${{ secrets.WEBSITE_DEPLOY }} in the rendered yaml. See nix/lib/steps.nix for examples of this escaping in use.

Control relative path with --no-prepend-git-root

By default, workflow files will be rendered relative to the git repo root. To write workflow files relative to the process working directory, run:

nix run .#render-workflows -- --no-prepend-git-root

This is useful when you want files output somewhere other than the git root (e.g., when scripting or testing in a subdirectory).

Passing the flag in pre-commit configuration

If you need to add arguments (such as --no-prepend-git-root) to the pre-commit hook invocation, you can do so, e.g., using the raw.args option in your Nix flake configuration:

pre-commit.settings.hooks.render-actions = {
  raw.args = [
    "--no-prepend-git-root"
  ];
};

Using Jujutsu (jj) to find the repo root

There is built-in, optional support for Jujutsu VCS via the option:

flake.actions-nix.useJJ = true # default: `false`

If enabled, locating the repo root will be tried with jj first and fallback to the usual approach git.

This is useful if you use jj workspaces where there isn't a git root available or if you don't use git-backed repos.

The --no-prepend-git-root flag also affects this option.

About

This is a work-in-progress project. My plan is to implement all functionality I need for minimizing action code repetition across my own various projects.

The aim is to be minimal and allow free-form configuration by users instead of trying to generate it using logic in this project. Main purpose of this project is to enable writing action logic in nix rather than yaml. The opportunities opened by using nix are then mainly left for users to exploit.

Development

Format code:

nix develop -c pre-commit run --all-files

Check flake and evaluate tests:

nix flake check

Test workflow rendering locally:

nix run .#render-workflows

Check past commit conventions:

git log --oneline

About

generate GitHub/Gitea actions using nix

Topics

Resources

License

Stars

50 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors