From 9bbba31438253f49b9a9f9c5dde4caf74db81e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Ho=C3=9F?= Date: Mon, 6 Jul 2026 21:56:22 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20absorb=20the=20shared=20flake=20?= =?UTF-8?q?=E2=80=94=20this=20repo=20is=20now=20the=20metio=20nix=20toolch?= =?UTF-8?q?ain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves the devShell definition and the from-source Go tools out of metio/ci's flake into this repo, so one repo owns both halves of the metio nix toolchain: the flake (lib.mkDevShell + lintTools + lintCommands + arch-go / modernize / helm-schema + kubebuilderAssets) and the Nix-installer action. Consumers now reference one repo for both — `inputs.devshell.url = github:metio/nix-devshell` and `uses: metio/nix-devshell@` — instead of pulling the flake from metio/ci and the action from here. The flake is consumed as a flake input (commit-following via Renovate lock maintenance), so it needs no releases: release.yml stays scoped to `paths: action.yml`, exactly as before, and a flake/tool/nixpkgs bump cuts no tag. The repo drops its `ci` flake input (it owns mkDevShell now) and dogfoods its own devShell — verify.yml runs the lint gate through the ci-* wrappers, and update-flake.yml (relocated here with the tool definitions) keeps their hashes current. shell.nix comes along for `nix-shell` compatibility. metio/ci becomes a thin consumer of this flake in a follow-up, once every repo has repointed its input from `ci` to `devshell`. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Sebastian Hoß --- .github/workflows/update-flake.yml | 45 +++++++ .github/workflows/verify.yml | 8 +- README.md | 31 ++++- flake.lock | 26 +---- flake.nix | 181 +++++++++++++++++++++++++++-- shell.nix | 14 +++ 6 files changed, 260 insertions(+), 45 deletions(-) create mode 100644 .github/workflows/update-flake.yml create mode 100644 shell.nix diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml new file mode 100644 index 0000000..ea87888 --- /dev/null +++ b/.github/workflows/update-flake.yml @@ -0,0 +1,45 @@ +# SPDX-FileCopyrightText: The nix-devshell Authors +# SPDX-License-Identifier: 0BSD + +# Zero-manual maintenance for the from-source packages this repo's flake ships +# (arch-go, modernize, helm-schema) — the Go tools nixpkgs does not package. +# Renovate's nix manager only bumps flake inputs and can't recompute nix hashes, +# so nix-update does both (version + src hash + vendorHash) here in the one place +# they're defined. Consuming repos pick the update up by bumping their `devshell` +# flake input (Renovate lock maintenance); nixpkgs itself stays Renovate's job. +name: Update flake packages +on: + schedule: + - cron: 30 5 * * MON + workflow_dispatch: +permissions: + contents: read +jobs: + update: + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: write # push the update branch + pull-requests: write # open the PR + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + - uses: ./ + - name: Bump the from-source flake packages (version + hashes) + run: | + for pkg in arch-go modernize helm-schema; do + echo "::group::nix-update $pkg" + nix run nixpkgs#nix-update -- --flake --version=stable "$pkg" || echo "::warning::nix-update $pkg made no change or failed" + echo "::endgroup::" + done + - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 + with: + branch: flake-packages-update + title: "chore(deps): update the from-source flake packages" + commit-message: "chore(deps): update the from-source flake packages" + body: | + Automated `nix-update` of the flake packages nixpkgs does not ship + (arch-go, modernize, helm-schema) — version, source hash, and + vendorHash. Consuming repos pick these up by bumping their `devshell` + flake input. Merge once the Verify gate is green. + labels: dependencies + delete-branch: true diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 3aceff4..5d9a76a 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -16,14 +16,14 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - uses: ./ - - run: nix develop --command reuse lint + - run: nix develop --command ci-reuse yaml: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - uses: ./ - - run: nix develop --command yamllint . + - run: nix develop --command ci-yaml github-actions: runs-on: ubuntu-latest timeout-minutes: 10 @@ -31,14 +31,14 @@ jobs: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - uses: ./ # actionlint shells out to shellcheck (in the devShell) for run: blocks. - - run: nix develop --command actionlint + - run: nix develop --command ci-actionlint markdown: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - uses: ./ - - run: nix develop --command markdownlint-cli2 "**/*.md" + - run: nix develop --command ci-markdown policy: # The metio convention policies (SHA-pinned uses, job timeouts, permissions, # …) run against this repo's action and workflows. diff --git a/README.md b/README.md index f8676b0..6506479 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,33 @@ SPDX-License-Identifier: 0BSD --> # nix-devshell -A composite GitHub Action that installs Nix and caches the `/nix` store, so a -repo whose toolchain is a nix flake runs every CI gate through the flake's -devShell and resolves the exact tool versions in `flake.lock` — identical to a -local `nix develop`. +The metio nix toolchain, in two halves: + +- **A flake** (`lib.mkDevShell`) that assembles a repo's devShell from the + shared lint gate (reuse, typos, yamllint, actionlint, shellcheck, + markdownlint) and its `ci-*` command wrappers, plus the from-source Go tools + nixpkgs does not ship (arch-go, modernize, helm-schema). Defined once here so + every repo resolves the same tools from `flake.lock`. +- **A composite GitHub Action** that installs Nix and caches the `/nix` store, + so a repo whose toolchain is that flake runs every CI gate through the + devShell — identical to a local `nix develop`. ## Usage -Check out the repo first, then run each gate with `nix develop --command …`: +Build the devShell from this flake, then in CI check the repo out, set Nix up +with the action, and run each gate through the devShell: + +```nix +# flake.nix +inputs.devshell.url = "github:metio/nix-devshell"; +inputs.nixpkgs.follows = "devshell/nixpkgs"; +outputs = { nixpkgs, devshell, ... }: { + devShells..default = devshell.lib.mkDevShell { + pkgs = nixpkgs.legacyPackages.; + packages = [ /* repo-specific tools + gate commands */ ]; + }; +}; +``` ```yaml jobs: @@ -21,7 +40,7 @@ jobs: steps: - uses: actions/checkout@ - uses: metio/nix-devshell@ - - run: nix develop --command + - run: nix develop --command ci-reuse # or any gate ``` The store is keyed on `flake.nix`/`flake.lock`, so it downloads the devShell diff --git a/flake.lock b/flake.lock index 3cb65db..aa7d2c5 100644 --- a/flake.lock +++ b/flake.lock @@ -1,24 +1,5 @@ { "nodes": { - "ci": { - "inputs": { - "flake-compat": "flake-compat", - "nixpkgs": "nixpkgs" - }, - "locked": { - "lastModified": 1783329208, - "narHash": "sha256-yG3RQLcbs1TN6AlWj47jH6JLxIJDjOTzrCkVhCwtd8c=", - "owner": "metio", - "repo": "ci", - "rev": "59a3cbf82e2797f72e63edce1880a4c8db865a3d", - "type": "github" - }, - "original": { - "owner": "metio", - "repo": "ci", - "type": "github" - } - }, "flake-compat": { "locked": { "lastModified": 1767039857, @@ -52,11 +33,8 @@ }, "root": { "inputs": { - "ci": "ci", - "nixpkgs": [ - "ci", - "nixpkgs" - ] + "flake-compat": "flake-compat", + "nixpkgs": "nixpkgs" } } }, diff --git a/flake.nix b/flake.nix index 643a667..41f9615 100644 --- a/flake.nix +++ b/flake.nix @@ -1,22 +1,42 @@ # SPDX-FileCopyrightText: The nix-devshell Authors # SPDX-License-Identifier: 0BSD -# This repo ships a composite action, not a toolchain — but it dogfoods that -# action in its own CI: verify.yml sets up Nix through `./` (this very action) -# and runs each lint gate with `nix develop --command …`. The devShell is the -# shared metio lint gate (reuse, typos, yamllint, actionlint, shellcheck, -# markdownlint) from ci.lib.mkDevShell, so a change to the action is checked by -# the action, and local and CI resolve the same tools from flake.lock. +# The metio nix toolchain: the shared devShell (`lib.mkDevShell`) plus the from- +# source Go tools nixpkgs does not ship (arch-go, modernize, helm-schema), and — +# alongside this flake — the composite action (action.yml) that installs Nix in +# CI. Every metio repo builds its devShell from `lib.mkDevShell` and sets Nix up +# through this repo's action, so the lint gate (reuse, typos, yamllint, actionlint, +# shellcheck, markdownlint) and its `ci-*` command wrappers are defined once here +# instead of copied into each flake, and local and CI resolve the same tools from +# flake.lock. `update-flake.yml` keeps the from-source tools' versions + hashes +# current; a consuming repo picks the update up by bumping its `devshell` flake +# input (Renovate lock maintenance). +# +# A repo's flake becomes: +# +# inputs.devshell.url = "github:metio/nix-devshell"; +# inputs.nixpkgs.follows = "devshell/nixpkgs"; # one nixpkgs pin, org-wide +# outputs = { nixpkgs, devshell, ... }: { +# devShells..default = devshell.lib.mkDevShell { +# pkgs = nixpkgs.legacyPackages.; +# packages = [ … repo-specific tools + gate commands … ]; +# env.KUBEBUILDER_ASSETS = "${devshell.lib.kubebuilderAssets pkgs}"; # controllers only +# }; +# }; +# +# This repo dogfoods both halves: verify.yml installs Nix through `./` (its own +# action) and runs each lint gate via `nix develop --command ci-*` from the +# devShell this flake defines. { - description = "The metio nix-devshell action, dogfooded through the shared lint gate"; + description = "metio nix toolchain: the shared devShell, the Go tools nixpkgs lacks, and the Nix-installer action"; inputs = { - ci.url = "github:metio/ci"; - nixpkgs.follows = "ci/nixpkgs"; + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + flake-compat.url = "github:edolstra/flake-compat"; }; outputs = - { nixpkgs, ci, ... }: + { nixpkgs, ... }: let systems = [ "x86_64-linux" @@ -25,10 +45,149 @@ "aarch64-darwin" ]; forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); + + # dadav/helm-schema (docs chart-values reference). Tags carry no `v`. + helm-schema = + pkgs: + pkgs.buildGoModule rec { + pname = "helm-schema"; + version = "0.23.4"; + src = pkgs.fetchFromGitHub { + owner = "dadav"; + repo = "helm-schema"; + rev = version; + hash = "sha256-btkkNzye9if4lF/YdhalbwA2/dcZArU6/9Hr0bTJf1M="; + }; + vendorHash = "sha256-jbK+XD5CbjMQJUJCcKbNN8LhYuhuy+Z3XcCmgiYw25Y="; + }; + + # arch-go (architecture rules, arch-go.yml). + arch-go = + pkgs: + pkgs.buildGoModule rec { + pname = "arch-go"; + version = "2.1.2"; + src = pkgs.fetchFromGitHub { + owner = "arch-go"; + repo = "arch-go"; + rev = "v${version}"; + hash = "sha256-clwVZ/5PwUiD1LzRG6jGghQWcWZP3Pj3CzrdZiHUrIQ="; + }; + vendorHash = "sha256-xIf+Ty1Pqa3oqqFLFsOv8Jz2bLOaIF+kjfGao05FhrM="; + }; + + # modernize (newer-Go idiom check), a subpackage of x/tools' gopls module. + modernize = + pkgs: + pkgs.buildGoModule rec { + pname = "modernize"; + version = "0.47.0"; + src = pkgs.fetchFromGitHub { + owner = "golang"; + repo = "tools"; + rev = "v${version}"; + hash = "sha256-JfrmKeIAhHhxMqOfh27w+T9PaBAIzh47wOokXmr1Z5Q="; + }; + modRoot = "gopls"; + subPackages = [ "internal/analysis/modernize/cmd/modernize" ]; + vendorHash = "sha256-GF9KSCr2aMjczVKz9H2t5Gc2kF0wqmKenO7qa8TQw4o="; + }; + + # controller-runtime envtest wants a dir holding etcd, kube-apiserver, and + # kubectl. Assemble it from nixpkgs so a controller's tests run offline + # against the flake-pinned Kubernetes, no setup-envtest download. + kubebuilderAssets = + pkgs: + pkgs.runCommand "kubebuilder-assets" { } '' + mkdir -p $out + ln -s ${pkgs.etcd}/bin/etcd $out/etcd + ln -s ${pkgs.kubernetes}/bin/kube-apiserver $out/kube-apiserver + ln -s ${pkgs.kubectl}/bin/kubectl $out/kubectl + ''; + + # The lint gate every metio repo shares, byte-for-byte. + lintTools = + pkgs: with pkgs; [ + reuse + typos + yamllint + actionlint + shellcheck # actionlint shells out to it for run: blocks + markdownlint-cli2 + ]; + + # One canonical `ci-` command per shared lint tool, wrapping the EXACT + # CI invocation (e.g. `reuse lint`, `markdownlint-cli2 "**/*.md"`). Defined + # once here so every repo inherits them: a workflow runs the gate with + # `nix develop --command ci-reuse`, and a developer runs the same name bare + # inside `nix develop` — the invocation lives in one place instead of being + # copied into each workflow's YAML and each repo's flake. The `ci-` prefix + # leaves the raw tool free for its other modes (e.g. `reuse annotate`); the + # wrapper pins only the CI mode. Tools are referenced by store path so the + # wrapper resolves the flake-pinned binary regardless of PATH. + lintCommands = pkgs: [ + (pkgs.writeShellScriptBin "ci-reuse" ''exec ${pkgs.reuse}/bin/reuse lint "$@"'') + (pkgs.writeShellScriptBin "ci-typos" ''exec ${pkgs.typos}/bin/typos "$@"'') + (pkgs.writeShellScriptBin "ci-yaml" ''exec ${pkgs.yamllint}/bin/yamllint . "$@"'') + # actionlint finds shellcheck on PATH, which the devShell provides. + (pkgs.writeShellScriptBin "ci-actionlint" ''exec ${pkgs.actionlint}/bin/actionlint "$@"'') + (pkgs.writeShellScriptBin "ci-markdown" ''exec ${pkgs.markdownlint-cli2}/bin/markdownlint-cli2 "**/*.md" "$@"'') + ]; + + # Assemble a repo's devShell: the shared lint gate plus the repo's own + # tools and gate commands, any extra env vars, its command menu, and any + # always-run setup. `menu` prints only for an interactive shell — otherwise + # it lands on the stdout that `nix develop --command ` captures and + # reads as tool output (e.g. golang.yml's gofumpt gate captures + # `unformatted="$(… gofumpt -l .)"`). `shellHook` always runs. + mkDevShell = + { + pkgs, + packages ? [ ], + env ? { }, + menu ? "", + shellHook ? "", + }: + pkgs.mkShell ( + env + // { + packages = lintTools pkgs ++ lintCommands pkgs ++ packages; + shellHook = '' + if [ -t 1 ]; then + echo "metio devshell — shared lint gate: reuse, typos, yamllint, actionlint, markdownlint-cli2" + echo " run any the CI way: ci-reuse, ci-typos, ci-yaml, ci-actionlint, ci-markdown" + ${menu} + fi + '' + + shellHook; + } + ); in { + # System-independent building blocks a repo's flake composes. + lib = { + inherit + mkDevShell + lintTools + lintCommands + helm-schema + arch-go + modernize + kubebuilderAssets + ; + }; + + # The from-source packages, buildable for `nix build` and `nix-update`. + packages = forAllSystems (pkgs: { + helm-schema = helm-schema pkgs; + arch-go = arch-go pkgs; + modernize = modernize pkgs; + }); + + # This repo dogfoods its own shared devShell: the lint gate that verify.yml + # runs through `nix develop --command ci-*`. devShells = forAllSystems (pkgs: { - default = ci.lib.mkDevShell { inherit pkgs; }; + default = mkDevShell { inherit pkgs; }; }); formatter = forAllSystems (pkgs: pkgs.nixfmt-rfc-style); diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..c1705cc --- /dev/null +++ b/shell.nix @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: The nix-devshell Authors +# SPDX-License-Identifier: 0BSD + +# nix-shell compatibility: exposes the flake's devShell to plain `nix-shell`. +(import ( + let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + node = lock.nodes.flake-compat.locked; + in + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/${node.rev}.tar.gz"; + sha256 = node.narHash; + } +) { src = ./.; }).shellNix