Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
name: Nix flake checks (per-template)

on:
push:
pull_request:
types: [opened, synchronize, reopened]
schedule:
# once a day (midnight UTC)
- cron: '0 0 * * *'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: nix-flake-check-${{ github.ref }}
cancel-in-progress: true

jobs:
prepare:
name: Prepare matrix of templates
runs-on: ubuntu-latest
outputs:
dirs: ${{ steps.set-matrix.outputs.dirs }}
has_templates: ${{ steps.set-matrix.outputs.has_templates }}
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Detect changed template directories
id: set-matrix
shell: bash
run: |
set -euo pipefail

# Find all top-level directories that contain a flake.nix
mapfile -t ALL_TEMPLATES < <(find . -maxdepth 2 -name flake.nix -print0 | xargs -0 -n1 dirname | sed 's|^\./||' | grep -v '^\.$' || true)
# Ensure unique and sorted
if [ ${#ALL_TEMPLATES[@]} -gt 0 ]; then
ALL_TEMPLATES=( $(printf "%s\n" "${ALL_TEMPLATES[@]}" | sort -u) )
fi
echo "All templates: ${ALL_TEMPLATES[*]}" >&2

# If scheduled run or manual dispatch, operate on all templates
if [ "${GITHUB_EVENT_NAME}" = "schedule" ] || [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then
# For manual runs, allow an optional `dirs` input (comma/space/newline-separated) to limit templates.
if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then
if jq -e '.inputs and .inputs.dirs' "$GITHUB_EVENT_PATH" >/dev/null 2>&1; then
INPUT_DIRS=$(jq -r '.inputs.dirs // ""' "$GITHUB_EVENT_PATH")
if [ -n "$INPUT_DIRS" ]; then
dirs_json=$(printf "%s\n" "$INPUT_DIRS" | tr ',' '\n' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' | jq -R -s -c 'split("\n")[:-1]')
echo "dirs=$dirs_json" >> $GITHUB_OUTPUT
echo "has_templates=true" >> $GITHUB_OUTPUT
exit 0
fi
fi
fi

dirs_json=$(printf "%s\n" "${ALL_TEMPLATES[@]}" | jq -R -s -c 'split("\n")[:-1]')
echo "dirs=$dirs_json" >> $GITHUB_OUTPUT
echo "has_templates=true" >> $GITHUB_OUTPUT
exit 0
fi

# Determine base/head SHAs
if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then
BASE_SHA=$(jq -r .pull_request.base.sha "$GITHUB_EVENT_PATH")
HEAD_SHA=$(jq -r .pull_request.head.sha "$GITHUB_EVENT_PATH")
else
BASE_SHA=$(jq -r .before "$GITHUB_EVENT_PATH")
HEAD_SHA=$GITHUB_SHA
fi

echo "Base: $BASE_SHA Head: $HEAD_SHA" >&2

# If base is all zeros (new branch), compare against default branch
if [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
DEFAULT_BRANCH=$(jq -r .repository.default_branch "$GITHUB_EVENT_PATH")
git fetch origin "$DEFAULT_BRANCH" --depth=1 || true
BASE_SHA=$(git rev-parse origin/"$DEFAULT_BRANCH")
fi

# Ensure refs are available for diff
git fetch --no-tags --prune --depth=1 origin || true

CHANGED_FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" || true)
echo "Changed files:\n$CHANGED_FILES" >&2

# If the repo-level flake changed, run all templates
if printf "%s\n" "$CHANGED_FILES" | grep -qx "flake.nix"; then
echo "Root flake.nix changed: running all templates" >&2
dirs_json=$(printf "%s\n" "${ALL_TEMPLATES[@]}" | jq -R -s -c 'split("\n")[:-1]')
echo "dirs=$dirs_json" >> $GITHUB_OUTPUT
echo "has_templates=true" >> $GITHUB_OUTPUT
exit 0
fi

# Map changed files to top-level dirs and keep only those that are templates
mapfile -t TOP_DIRS < <(printf "%s\n" "$CHANGED_FILES" | awk -F/ '{print $1}' | sort -u | sed '/^\s*$/d' || true)

SELECTED=()
for d in "${TOP_DIRS[@]:-}"; do
if [ -n "$d" ] && [ -d "$d" ] && [ -f "$d/flake.nix" ]; then
SELECTED+=("$d")
fi
done

# If nothing selected, return empty
if [ ${#SELECTED[@]} -eq 0 ]; then
echo "dirs=[]" >> $GITHUB_OUTPUT
echo "has_templates=false" >> $GITHUB_OUTPUT
exit 0
fi

# Build JSON array of selected template dirs
dirs_json=$(printf "%s\n" "${SELECTED[@]}" | jq -R -s -c 'split("\n")[:-1]')
echo "Detected templates: $dirs_json" >&2
echo "dirs=$dirs_json" >> $GITHUB_OUTPUT
echo "has_templates=true" >> $GITHUB_OUTPUT

check:
name: Run nix flake check and show
needs: prepare
if: ${{ needs.prepare.outputs.has_templates == 'true' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 4
matrix:
dir: ${{ fromJson(needs.prepare.outputs.dirs) }}
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Install Nix (non-daemon) and enable flakes
uses: cachix/install-nix-action@v31.8.1
with:
nix_path: nixpkgs=channel:nixpkgs-unstable
extra_nix_config: |
experimental-features = nix-command flakes

- name: Run nix flake check
shell: bash
run: |
echo "=== nix flake check -- ${{ matrix.dir }} ==="
cd "${{ matrix.dir }}"
# run checks; will fail the job if tests fail
nix flake check

- name: Run nix flake show
shell: bash
run: |
echo "=== nix flake show -- ${{ matrix.dir }} ==="
cd "${{ matrix.dir }}"
nix flake show


lint:
name: Lint all nix file with treefmt
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Install Nix (non-daemon) and enable flakes
uses: cachix/install-nix-action@v31.8.1
with:
nix_path: nixpkgs=channel:nixpkgs-unstable
extra_nix_config: |
experimental-features = nix-command flakes

- name: Run nixfmt
run: |
nix run nixpkgs#nixfmt-tree -- --ci
114 changes: 63 additions & 51 deletions bash-hello/flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
# Nixpkgs / NixOS version to use.
inputs.nixpkgs.url = "nixpkgs/nixos-21.05";

outputs = { self, nixpkgs }:
outputs =
{ self, nixpkgs }:
let

# to work with older version of flakes
Expand All @@ -14,13 +15,24 @@
version = builtins.substring 0 8 lastModifiedDate;

# System types to support.
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
supportedSystems = [
"x86_64-linux"
"x86_64-darwin"
"aarch64-linux"
"aarch64-darwin"
];

# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;

# Nixpkgs instantiated for supported system types.
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; overlays = [ self.overlay ]; });
nixpkgsFor = forAllSystems (
system:
import nixpkgs {
inherit system;
overlays = [ self.overlay ];
}
);

in

Expand All @@ -29,34 +41,33 @@
# A Nixpkgs overlay.
overlay = final: prev: {

hello = with final; stdenv.mkDerivation rec {
name = "hello-${version}";
hello =
with final;
stdenv.mkDerivation rec {
name = "hello-${version}";

unpackPhase = ":";
unpackPhase = ":";

buildPhase =
''
buildPhase = ''
cat > hello <<EOF
#! $SHELL
echo "Hello Nixers!"
EOF
chmod +x hello
'';

installPhase =
''
installPhase = ''
mkdir -p $out/bin
cp hello $out/bin/
'';
};
};

};

# Provide some binary packages for selected system types.
packages = forAllSystems (system:
{
inherit (nixpkgsFor.${system}) hello;
});
packages = forAllSystems (system: {
inherit (nixpkgsFor.${system}) hello;
});

# The default package for 'nix build'. This makes sense if the
# flake provides only one package or there is a clear "main"
Expand All @@ -75,53 +86,54 @@
};

# Tests run by 'nix flake check' and by Hydra.
checks = forAllSystems
(system:
with nixpkgsFor.${system};
checks = forAllSystems (
system:
with nixpkgsFor.${system};

{
inherit (self.packages.${system}) hello;
{
inherit (self.packages.${system}) hello;

# Additional tests, if applicable.
test = stdenv.mkDerivation {
name = "hello-test-${version}";
# Additional tests, if applicable.
test = stdenv.mkDerivation {
name = "hello-test-${version}";

buildInputs = [ hello ];
buildInputs = [ hello ];

unpackPhase = "true";
unpackPhase = "true";

buildPhase = ''
echo 'running some integration tests'
[[ $(hello) = 'Hello Nixers!' ]]
'';
buildPhase = ''
echo 'running some integration tests'
[[ $(hello) = 'Hello Nixers!' ]]
'';

installPhase = "mkdir -p $out";
};
}
installPhase = "mkdir -p $out";
};
}

// lib.optionalAttrs stdenv.isLinux {
# A VM test of the NixOS module.
vmTest =
with import (nixpkgs + "/nixos/lib/testing-python.nix") {
inherit system;
};
// lib.optionalAttrs stdenv.isLinux {
# A VM test of the NixOS module.
vmTest =
with import (nixpkgs + "/nixos/lib/testing-python.nix") {
inherit system;
};

makeTest {
nodes = {
client = { ... }: {
makeTest {
nodes = {
client =
{ ... }:
{
imports = [ self.nixosModules.hello ];
};
};

testScript =
''
start_all()
client.wait_for_unit("multi-user.target")
client.succeed("hello")
'';
};
}
);

testScript = ''
start_all()
client.wait_for_unit("multi-user.target")
client.succeed("hello")
'';
};
}
);

};
}
Loading