Compare commits

...

20 commits
v1.2.0 ... main

Author SHA1 Message Date
9f9fcb8534 Merge branch 'feat/vm-tests' into 'main'
feat: add support for running NixOS VM tests more easily

See merge request TECHNOFAB/nixtest!10
2026-01-26 16:24:15 +01:00
c64e0cce0c
feat: add support for running NixOS VM tests more easily 2026-01-20 20:11:18 +01:00
f8fa0846ce Merge branch 'renovate/lock-file-maintenance' into 'main'
chore(deps): lock file maintenance

See merge request TECHNOFAB/nixtest!9
2026-01-01 17:52:46 +01:00
Renovate Bot
e25fb0c609 chore(deps): lock file maintenance 2026-01-01 14:47:37 +00:00
b87e38847d Merge branch 'renovate/github.com-jedib0t-go-pretty-v6-6.x' into 'main'
fix(deps): update module github.com/jedib0t/go-pretty/v6 to v6.7.8

See merge request TECHNOFAB/nixtest!8
2025-12-23 19:23:24 +01:00
Renovate Bot
98250aa7e7 fix(deps): update module github.com/jedib0t/go-pretty/v6 to v6.7.8 2025-12-23 14:09:03 +00:00
fc2b64839c Merge branch 'renovate/github.com-sergi-go-diff-1.x' into 'main'
fix(deps): update module github.com/sergi/go-diff to v1.4.0

See merge request TECHNOFAB/nixtest!6
2025-12-15 09:46:30 +01:00
Renovate Bot
b0988954c7 fix(deps): update module github.com/sergi/go-diff to v1.4.0 2025-12-15 08:09:53 +00:00
a71f897a65 Merge branch 'renovate/github.com-jedib0t-go-pretty-v6-6.x' into 'main'
fix(deps): update module github.com/jedib0t/go-pretty/v6 to v6.7.7

See merge request TECHNOFAB/nixtest!5
2025-12-15 08:35:52 +01:00
Renovate Bot
056851d6c6 fix(deps): update module github.com/jedib0t/go-pretty/v6 to v6.7.7 2025-12-15 07:08:30 +00:00
54d3b534d6 Merge branch 'renovate/github.com-spf13-pflag-1.x' into 'main'
fix(deps): update module github.com/spf13/pflag to v1.0.10

See merge request TECHNOFAB/nixtest!4
2025-12-15 07:17:57 +01:00
Renovate Bot
be32005bc7 fix(deps): update module github.com/spf13/pflag to v1.0.10 2025-12-15 00:07:32 +00:00
c202e41681 Merge branch 'renovate/github.com-stretchr-testify-1.x' into 'main'
fix(deps): update module github.com/stretchr/testify to v1.11.1

See merge request TECHNOFAB/nixtest!7
2025-12-15 00:47:37 +01:00
Renovate Bot
dab1e2ef6f fix(deps): update module github.com/stretchr/testify to v1.11.1 2025-12-14 23:15:53 +00:00
2477ad31ae Merge branch 'feat/rensa-rewrite' into 'main'
refactor: replace flake-parts, devenv etc. with rensa

See merge request TECHNOFAB/nixtest!3
2025-12-14 17:00:58 +01:00
318b903d12
chore: rename module to uppercase 2025-12-14 15:23:19 +01:00
0414493963
refactor: replace flake-parts, devenv etc. with rensa 2025-12-14 14:56:40 +01:00
5a7053afcb
docs: set _file so "declared in" works correctly 2025-10-01 19:42:30 +02:00
56d22f4aa1
test: set SSL_CERT_FILE and NIX_SSL_CERT_FILE so test works in pure mode 2025-10-01 17:08:49 +02:00
c9618a4d9b
docs: update nixmkdocs, use svg for logo and favicon, add module docs 2025-10-01 16:43:30 +02:00
40 changed files with 734 additions and 707 deletions

3
.envrc
View file

@ -1 +1,2 @@
use flake . --impure --accept-flake-config
source $(fetchurl https://gitlab.com/rensa-nix/direnv/-/raw/v0.3.0/direnvrc "sha256-u7+KEz684NnIZ+Vh5x5qLrt8rKdnUNexewBoeTcEVHQ=")
use ren //repo/devShells/default

3
.gitignore vendored
View file

@ -1,6 +1,3 @@
.direnv/
.devenv/
result
.pre-commit-config.yaml
*.xml
cover.*

View file

@ -1,4 +1,5 @@
# Generated by soonix, DO NOT EDIT
include:
- component: gitlab.com/TECHNOFAB/nix-gitlab-ci/nix-gitlab-ci@2.0.0
inputs:
version: 2.0.0
- component: gitlab.com/TECHNOFAB/nix-gitlab-ci/nix-gitlab-ci@3.1.2
inputs:
version: 3.1.2

23
.gitlab/renovate.json5 Normal file
View file

@ -0,0 +1,23 @@
{
"extends": [
"config:recommended"
],
"gitlabci": {
"enabled": false
},
"lockFileMaintenance": {
"enabled": true,
"extends": [
"schedule:monthly"
]
},
"nix": {
"enabled": true
},
"postUpgradeTasks": {
"commands": [
"nix-portable nix run .#update-package",
"nix-portable nix run .#soonix:update"
]
}
}

View file

@ -3,14 +3,14 @@ package main
import (
"os"
"gitlab.com/technofab/nixtest/internal/config"
appnix "gitlab.com/technofab/nixtest/internal/nix"
"gitlab.com/technofab/nixtest/internal/report/console"
"gitlab.com/technofab/nixtest/internal/report/junit"
"gitlab.com/technofab/nixtest/internal/runner"
appsnap "gitlab.com/technofab/nixtest/internal/snapshot"
"gitlab.com/technofab/nixtest/internal/types"
"gitlab.com/technofab/nixtest/internal/util"
"gitlab.com/TECHNOFAB/nixtest/internal/config"
appnix "gitlab.com/TECHNOFAB/nixtest/internal/nix"
"gitlab.com/TECHNOFAB/nixtest/internal/report/console"
"gitlab.com/TECHNOFAB/nixtest/internal/report/junit"
"gitlab.com/TECHNOFAB/nixtest/internal/runner"
appsnap "gitlab.com/TECHNOFAB/nixtest/internal/snapshot"
"gitlab.com/TECHNOFAB/nixtest/internal/types"
"gitlab.com/TECHNOFAB/nixtest/internal/util"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

1
docs/images/logo.svg Executable file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" fill="none"><rect width="100" height="100" fill="#222" rx="12"/><rect width="100" height="100" fill="url(#a)" rx="12"/><path fill="#3FB950" d="M18 54.7a3 3 0 0 1 4.3 0l17 17c.5.4 1 .4 1.5 0L82.5 30a3 3 0 0 1 4.2 0l2 1.9a3 3 0 0 1 0 4.2L46.2 78.7l-4 4a3 3 0 0 1-4.2 0L16 60.9a3 3 0 0 1 0-4.3l2-1.9Z"/><path fill="url(#b)" d="m89 66.6-5.3 9.6H71l6.3 11-2.7 4.8h-5.5l-9-15.8H53l9.6-9.6H89Zm-76-4.8 1 1.2 6 5.9-3.5 6-5.5-9.4 2-3.7Zm3-5 .1-.2-.1.2Zm11.4-1.1-3-3.1a6 6 0 0 0-8.5 0l-1.8 1.8H4.7L2 49.6l2.7-4.8h18l6.5-11.2H40l-12.6 22ZM62.8 18l6.4-11h5.4l2.7 4.8-9 15.7 4.4 7.9-6.9 6.9L45.8 7 56.5 7l6.2 11.1Z"/><path fill="url(#c)" d="m54.9 91.9-10.7.1-3.1-5.5a6 6 0 0 0 3.2-1.7l4-4 .2-.2L55 92Zm-19.1-7.2L31.7 92h-5.3l-2.8-4.8L29 78l6.9 6.8ZM47 78l-.8.8-4 4-.1.1.1-.1 4-4 .8-.8ZM40.2 72Zm.5-.2Zm.3-4.5-1 .9-11.7-11.7 3.3-6L41 67.3Zm54.3-22.6 2.7 4.7-2.7 4.8H77.5l-6.3 11.2h-7.6l20.8-20.7h11Zm-54.8-22 12.7.1 5.4 9.5H12l5.3-9.5h12.5l-6.2-11L26.2 7h5.5l8.8 15.8Zm44.3 3.4a6 6 0 0 0-2.4.4l1.3-2.4 1.1 2Z"/><defs><linearGradient id="b" x1="76.7" x2="59.1" y1="47.2" y2="17.1" gradientUnits="userSpaceOnUse"><stop offset=".2" stop-color="#7EB1DD"/></linearGradient><linearGradient id="c" x1="63.4" x2="81.3" y1="70.3" y2="40.6" gradientUnits="userSpaceOnUse"><stop offset="1" stop-color="#5277C3"/></linearGradient><radialGradient id="a" cx="0" cy="0" r="1" gradientTransform="matrix(0 50 -50 0 50 50)" gradientUnits="userSpaceOnUse"><stop offset=".5" stop-color="#091A3D"/><stop offset="1" stop-color="#000819"/></radialGradient></defs></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

3
docs/options.md Normal file
View file

@ -0,0 +1,3 @@
# Options
{% include 'options.md' %}

15
docs/style.css Normal file
View file

@ -0,0 +1,15 @@
.md-header__button.md-logo {
margin: 0;
padding-top: .2rem;
padding-bottom: .2rem;
}
[dir="ltr"] .md-header__title {
margin-left: 0;
}
.md-header__button.md-logo img,
.md-header__button.md-logo svg {
height: 2rem;
}

View file

@ -64,6 +64,7 @@ There are currently 3 types of tests:
- `snapshot` -> snapshot testing, only needs `actual` and compares that to the snapshot
- `unit` -> equality checking, needs `expected` and `actual` or `actualDrv`
- `script` -> shell script test, needs `script`
- `vm` -> NixOS VM test, needs `vmConfig`
Examples:
@ -126,6 +127,23 @@ Examples:
expected = pkgs.hello;
actual = pkgs.hello;
}
{
name = "vm-test";
type = "vm";
# gets passed to pkgs.testers.nixosTest, so same params apply
# name gets automatically set, so thats not required
vmConfig = {
nodes.machine = {
services.nginx.enable = true;
};
testScript =
# py
''
machine.wait_for_unit("nginx.service")
machine.wait_for_open_port(80)
'';
};
}
]
```

399
flake.lock generated
View file

@ -1,281 +1,12 @@
{
"nodes": {
"cachix": {
"inputs": {
"devenv": [
"devenv"
],
"flake-compat": [
"devenv"
],
"git-hooks": [
"devenv"
],
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1742042642,
"narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=",
"owner": "cachix",
"repo": "cachix",
"rev": "a624d3eaf4b1d225f918de8543ed739f2f574203",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "latest",
"repo": "cachix",
"type": "github"
}
},
"devenv": {
"inputs": {
"cachix": "cachix",
"flake-compat": "flake-compat",
"git-hooks": "git-hooks",
"nix": "nix",
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1746189866,
"narHash": "sha256-3sTvuSVBFcXbqg26Qcw/ENJ1s36jtzEcZ0mHqLqvWRA=",
"owner": "cachix",
"repo": "devenv",
"rev": "5fc592d45dd056035e0fd5000893a21609c35526",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1733328505,
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"devenv",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1712014858,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1743550720,
"narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "c621e8422220273271f52058f618c94e405bb0f5",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"devenv"
],
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
]
},
"locked": {
"lastModified": 1742649964,
"narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"devenv",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"libgit2": {
"flake": false,
"locked": {
"lastModified": 1697646580,
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
}
},
"mkdocs-material-umami": {
"locked": {
"lastModified": 1745840856,
"narHash": "sha256-1Ad1JTMQMP6YsoIKAA+SBCE15qWrYkGue9/lXOLnu9I=",
"owner": "technofab",
"repo": "mkdocs-material-umami",
"rev": "3ac9b194450f6b779c37b8d16fec640198e5cd0a",
"type": "gitlab"
},
"original": {
"owner": "technofab",
"repo": "mkdocs-material-umami",
"type": "gitlab"
}
},
"nix": {
"inputs": {
"flake-compat": [
"devenv"
],
"flake-parts": "flake-parts",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs_2",
"nixpkgs-23-11": [
"devenv"
],
"nixpkgs-regression": [
"devenv"
],
"pre-commit-hooks": [
"devenv"
]
},
"locked": {
"lastModified": 1745930071,
"narHash": "sha256-bYyjarS3qSNqxfgc89IoVz8cAFDkF9yPE63EJr+h50s=",
"owner": "domenkozar",
"repo": "nix",
"rev": "b455edf3505f1bf0172b39a735caef94687d0d9c",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.24",
"repo": "nix",
"type": "github"
}
},
"nix-devtools": {
"locked": {
"dir": "lib",
"lastModified": 1739971859,
"narHash": "sha256-DaY11jX7Lraw7mRUIsgPsO+aSkkewQe2D+WMZORTNPE=",
"owner": "technofab",
"repo": "nix-devtools",
"rev": "b4f059657de5ac2569afd69a8f042614d309e6bb",
"type": "gitlab"
},
"original": {
"dir": "lib",
"owner": "technofab",
"repo": "nix-devtools",
"type": "gitlab"
}
},
"nix-gitlab-ci": {
"locked": {
"dir": "lib",
"lastModified": 1746973171,
"narHash": "sha256-q/LhPZlhJB2gXZ5BfgU1Wep/1x1y9Sct3/JU8A2fzjg=",
"owner": "technofab",
"repo": "nix-gitlab-ci",
"rev": "dca2d724c155799e537a898cb9f948f8afae4921",
"type": "gitlab"
},
"original": {
"dir": "lib",
"owner": "technofab",
"ref": "2.0.1",
"repo": "nix-gitlab-ci",
"type": "gitlab"
}
},
"nix-mkdocs": {
"locked": {
"dir": "lib",
"lastModified": 1745841841,
"narHash": "sha256-297zPQbUlc7ZAYDoaD6mCmQxCC3Tr4YOKekRF1ArZ7g=",
"owner": "technofab",
"repo": "nixmkdocs",
"rev": "c7e3c3b13ded25818e9789938387bba6f2cde690",
"type": "gitlab"
},
"original": {
"dir": "lib",
"owner": "technofab",
"repo": "nixmkdocs",
"type": "gitlab"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1733212471,
"narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=",
"lastModified": 1767116409,
"narHash": "sha256-5vKw92l1GyTnjoLzEagJy5V5mDFck72LiQWZSOnSicw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "55d15ad12a74eb7d4646254e13638ad0c4128776",
"rev": "cad22e7d996aea55ecab064e84834289143e44a0",
"type": "github"
},
"original": {
@ -287,11 +18,11 @@
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1743296961,
"narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
"lastModified": 1754184128,
"narHash": "sha256-AjhoyBL4eSyXf01Bmc6DiuaMrJRNdWopmdnMY0Pa/M0=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
"rev": "02e72200e6d56494f4a7c0da8118760736e41b60",
"type": "github"
},
"original": {
@ -300,114 +31,30 @@
"type": "github"
}
},
"nixpkgs_2": {
"ren": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1717432640,
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
"type": "github"
"dir": "lib",
"lastModified": 1766497301,
"narHash": "sha256-W7WeOXMUZROMtbU1qQNWy/yai+k8gG09YACFQ7ImpsQ=",
"owner": "rensa-nix",
"repo": "core",
"rev": "e08c48b5db1052bfb8b8dad764e05decc1af893e",
"type": "gitlab"
},
"original": {
"owner": "NixOS",
"ref": "release-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1733477122,
"narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1746152631,
"narHash": "sha256-zBuvmL6+CUsk2J8GINpyy8Hs1Zp4PP6iBWSmZ4SCQ/s=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "032bc6539bd5f14e9d0c51bd79cfe9a055b094c3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_5": {
"locked": {
"lastModified": 1745377448,
"narHash": "sha256-jhZDfXVKdD7TSEGgzFJQvEEZ2K65UMiqW5YJ2aIqxMA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "507b63021ada5fee621b6ca371c4fca9ca46f52c",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
"dir": "lib",
"owner": "rensa-nix",
"repo": "core",
"type": "gitlab"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"flake-parts": "flake-parts_2",
"mkdocs-material-umami": "mkdocs-material-umami",
"nix-devtools": "nix-devtools",
"nix-gitlab-ci": "nix-gitlab-ci",
"nix-mkdocs": "nix-mkdocs",
"nixpkgs": "nixpkgs_4",
"systems": "systems",
"treefmt-nix": "treefmt-nix"
}
},
"systems": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
"repo": "default-linux",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-linux",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_5"
},
"locked": {
"lastModified": 1746216483,
"narHash": "sha256-4h3s1L/kKqt3gMDcVfN8/4v2jqHrgLIe4qok4ApH5x4=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "29ec5026372e0dec56f890e50dbe4f45930320fd",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
"nixpkgs": "nixpkgs",
"ren": "ren"
}
}
},

279
flake.nix
View file

@ -1,254 +1,39 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
ren.url = "gitlab:rensa-nix/core?dir=lib";
};
outputs = {
flake-parts,
systems,
ren,
self,
...
} @ inputs:
flake-parts.lib.mkFlake {inherit inputs;} {
imports = [
inputs.devenv.flakeModule
inputs.treefmt-nix.flakeModule
inputs.nix-gitlab-ci.flakeModule
inputs.nix-devtools.flakeModule
inputs.nix-mkdocs.flakeModule
ren.buildWith
{
inherit inputs;
cellsFrom = ./nix;
transformInputs = system: i:
i
// {
pkgs = import i.nixpkgs {inherit system;};
};
cellBlocks = with ren.blocks; [
(simple "devShells")
(simple "ci")
(simple "tests")
(simple "packages")
(simple "docs")
(simple "soonix")
];
}
{
packages = ren.select self [
["repo" "ci" "packages"]
["repo" "tests"]
["packages" "packages"]
["repo" "docs"]
["repo" "soonix" "packages"]
];
systems = import systems;
flake = {};
perSystem = {
lib,
pkgs,
config,
...
}: {
treefmt = {
projectRootFile = "flake.nix";
programs = {
alejandra.enable = true;
mdformat.enable = true;
gofmt.enable = true;
};
settings.formatter.mdformat.command = let
pkg = pkgs.python3.withPackages (p: [
p.mdformat
p.mdformat-mkdocs
]);
in "${pkg}/bin/mdformat";
};
devenv.shells.default = {
containers = pkgs.lib.mkForce {};
packages = with pkgs; [gore go-junit-report];
languages.go = {
enable = true;
enableHardeningWorkaround = true;
};
pre-commit.hooks = {
treefmt = {
enable = true;
packageOverrides.treefmt = config.treefmt.build.wrapper;
};
convco.enable = true;
};
task = {
enable = true;
alias = ",";
tasks = {
"test" = {
cmds = [
"go test -v -coverprofile cover.out ./..."
"go tool cover -html cover.out -o cover.html"
];
};
};
};
};
doc = {
path = ./docs;
deps = pp: [
pp.mkdocs-material
(pp.callPackage inputs.mkdocs-material-umami {})
];
config = {
site_name = "Nixtest";
repo_name = "TECHNOFAB/nixtest";
repo_url = "https://gitlab.com/TECHNOFAB/nixtest";
edit_uri = "edit/main/docs/";
theme = {
name = "material";
features = ["content.code.copy" "content.action.edit"];
icon.repo = "simple/gitlab";
logo = "images/logo.png";
favicon = "images/favicon.png";
palette = [
{
scheme = "default";
media = "(prefers-color-scheme: light)";
primary = "green";
accent = "light green";
toggle = {
icon = "material/brightness-7";
name = "Switch to dark mode";
};
}
{
scheme = "slate";
media = "(prefers-color-scheme: dark)";
primary = "green";
accent = "light green";
toggle = {
icon = "material/brightness-4";
name = "Switch to light mode";
};
}
];
};
plugins = ["search" "material-umami"];
nav = [
{"Introduction" = "index.md";}
{"Usage" = "usage.md";}
{"Reference" = "reference.md";}
{"CLI" = "cli.md";}
{"Example Configs" = "examples.md";}
];
markdown_extensions = [
"pymdownx.superfences"
"admonition"
];
extra.analytics = {
provider = "umami";
site_id = "716d1869-9342-4b62-a770-e15d2d5c807d";
src = "https://analytics.tf/umami";
domains = "nixtest.projects.tf";
feedback = {
title = "Was this page helpful?";
ratings = [
{
icon = "material/thumb-up-outline";
name = "This page is helpful";
data = "good";
note = "Thanks for your feedback!";
}
{
icon = "material/thumb-down-outline";
name = "This page could be improved";
data = "bad";
note = "Thanks for your feedback! Please leave feedback by creating an issue :)";
}
];
};
};
};
};
ci = {
stages = ["test" "build" "deploy"];
jobs = {
"test:lib" = {
stage = "test";
script = [
"nix run .#tests -- --junit=junit.xml"
];
allow_failure = true;
artifacts = {
when = "always";
reports.junit = "junit.xml";
};
};
"test:go" = {
stage = "test";
nix.deps = with pkgs; [go go-junit-report gocover-cobertura];
variables = {
GOPATH = "$CI_PROJECT_DIR/.go";
GOCACHE = "$CI_PROJECT_DIR/.go/pkg/mod";
};
script = [
"go test -coverprofile=coverage.out -v 2>&1 ./... | go-junit-report -set-exit-code > report.xml"
"go tool cover -func coverage.out"
"gocover-cobertura < coverage.out > coverage.xml"
];
allow_failure = true;
coverage = "/\(statements\)(?:\s+)?(\d+(?:\.\d+)?%)/";
cache.paths = [".go/pkg/mod/"];
artifacts = {
when = "always";
reports = {
junit = "report.xml";
coverage_report = {
coverage_format = "cobertura";
path = "coverage.xml";
};
};
};
};
"docs" = {
stage = "build";
script = [
# sh
''
nix build .#docs:default
mkdir -p public
cp -r result/. public/
''
];
artifacts.paths = ["public"];
};
"pages" = {
nix.enable = false;
image = "alpine:latest";
stage = "deploy";
script = ["true"];
artifacts.paths = ["public"];
rules = [
{
"if" = "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH";
}
];
};
};
};
packages = let
ntlib = import ./lib {inherit pkgs lib;};
in {
default = pkgs.callPackage ./package.nix {};
tests = ntlib.mkNixtest {
modules = ntlib.autodiscover {dir = ./tests;};
args = {
inherit pkgs ntlib;
};
};
};
};
};
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# flake & devenv related
flake-parts.url = "github:hercules-ci/flake-parts";
systems.url = "github:nix-systems/default-linux";
devenv.url = "github:cachix/devenv";
treefmt-nix.url = "github:numtide/treefmt-nix";
nix-gitlab-ci.url = "gitlab:technofab/nix-gitlab-ci/2.0.1?dir=lib";
nix-devtools.url = "gitlab:technofab/nix-devtools?dir=lib";
nix-mkdocs.url = "gitlab:technofab/nixmkdocs?dir=lib";
mkdocs-material-umami.url = "gitlab:technofab/mkdocs-material-umami";
};
nixConfig = {
extra-substituters = [
"https://cache.nixos.org/"
"https://nix-community.cachix.org"
"https://devenv.cachix.org"
];
extra-trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
"devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="
];
};
}

10
go.mod
View file

@ -1,12 +1,12 @@
module gitlab.com/technofab/nixtest
module gitlab.com/TECHNOFAB/nixtest
go 1.23.0
require (
github.com/akedrou/textdiff v0.1.0
github.com/rs/zerolog v1.34.0
github.com/spf13/pflag v1.0.6
github.com/stretchr/testify v1.10.0
github.com/spf13/pflag v1.0.10
github.com/stretchr/testify v1.11.1
)
require (
@ -19,9 +19,9 @@ require (
)
require (
github.com/jedib0t/go-pretty/v6 v6.6.7
github.com/jedib0t/go-pretty/v6 v6.7.8
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/sergi/go-diff v1.3.1
github.com/sergi/go-diff v1.4.0
golang.org/x/sys v0.33.0 // indirect
)

10
go.sum
View file

@ -7,6 +7,10 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo=
github.com/jedib0t/go-pretty/v6 v6.6.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/jedib0t/go-pretty/v6 v6.7.7 h1:Y1Id3lJ3k4UB8uwWWy3l8EVFnUlx5chR5+VbsofPNX0=
github.com/jedib0t/go-pretty/v6 v6.7.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/jedib0t/go-pretty/v6 v6.7.8 h1:BVYrDy5DPBA3Qn9ICT+PokP9cvCv1KaHv2i+Hc8sr5o=
github.com/jedib0t/go-pretty/v6 v6.7.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -32,12 +36,18 @@ github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -8,7 +8,7 @@ import (
"os/exec"
"strings"
apperrors "gitlab.com/technofab/nixtest/internal/errors"
apperrors "gitlab.com/TECHNOFAB/nixtest/internal/errors"
)
// Service defines operations related to Nix

View file

@ -10,7 +10,7 @@ import (
"strings"
"testing"
apperrors "gitlab.com/technofab/nixtest/internal/errors"
apperrors "gitlab.com/TECHNOFAB/nixtest/internal/errors"
)
func TestHelperProcess(t *testing.T) {

View file

@ -11,8 +11,8 @@ import (
"github.com/jedib0t/go-pretty/v6/text"
"github.com/rs/zerolog/log"
"github.com/sergi/go-diff/diffmatchpatch"
"gitlab.com/technofab/nixtest/internal/types"
"gitlab.com/technofab/nixtest/internal/util"
"gitlab.com/TECHNOFAB/nixtest/internal/types"
"gitlab.com/TECHNOFAB/nixtest/internal/util"
)
// PrintErrors prints error messages for failed tests

View file

@ -12,7 +12,7 @@ import (
"time"
"github.com/jedib0t/go-pretty/v6/text"
"gitlab.com/technofab/nixtest/internal/types"
"gitlab.com/TECHNOFAB/nixtest/internal/types"
)
// captureOutput captures stdout and stderr during the execution of a function

View file

@ -7,8 +7,8 @@ import (
"strings"
"time"
"gitlab.com/technofab/nixtest/internal/types"
"gitlab.com/technofab/nixtest/internal/util"
"gitlab.com/TECHNOFAB/nixtest/internal/types"
"gitlab.com/TECHNOFAB/nixtest/internal/util"
)
type JUnitReport struct {

View file

@ -9,7 +9,7 @@ import (
"testing"
"time"
"gitlab.com/technofab/nixtest/internal/types"
"gitlab.com/TECHNOFAB/nixtest/internal/types"
)
func formatDurationSeconds(d time.Duration) string {

View file

@ -9,10 +9,10 @@ import (
"time"
"github.com/rs/zerolog/log"
"gitlab.com/technofab/nixtest/internal/nix"
"gitlab.com/technofab/nixtest/internal/snapshot"
"gitlab.com/technofab/nixtest/internal/types"
"gitlab.com/technofab/nixtest/internal/util"
"gitlab.com/TECHNOFAB/nixtest/internal/nix"
"gitlab.com/TECHNOFAB/nixtest/internal/snapshot"
"gitlab.com/TECHNOFAB/nixtest/internal/types"
"gitlab.com/TECHNOFAB/nixtest/internal/util"
)
// Runner executes tests based on provided specifications and configuration

View file

@ -9,8 +9,8 @@ import (
"testing"
"time"
apperrors "gitlab.com/technofab/nixtest/internal/errors"
"gitlab.com/technofab/nixtest/internal/types"
apperrors "gitlab.com/TECHNOFAB/nixtest/internal/errors"
"gitlab.com/TECHNOFAB/nixtest/internal/types"
)
// --- Mock Service Implementations ---

View file

@ -7,8 +7,8 @@ import (
"path/filepath"
"strings"
apperrors "gitlab.com/technofab/nixtest/internal/errors"
"gitlab.com/technofab/nixtest/internal/util"
apperrors "gitlab.com/TECHNOFAB/nixtest/internal/errors"
"gitlab.com/TECHNOFAB/nixtest/internal/util"
)
// Service defines operations related to test snapshots

View file

@ -9,7 +9,7 @@ import (
"strings"
"testing"
apperrors "gitlab.com/technofab/nixtest/internal/errors"
apperrors "gitlab.com/TECHNOFAB/nixtest/internal/errors"
)
func TestDefaultService_GetPath(t *testing.T) {

View file

@ -8,7 +8,7 @@ import (
"github.com/akedrou/textdiff"
"github.com/akedrou/textdiff/myers"
apperrors "gitlab.com/technofab/nixtest/internal/errors"
apperrors "gitlab.com/TECHNOFAB/nixtest/internal/errors"
)
func ComputeDiff(expected, actual string) (string, error) {

View file

@ -8,7 +8,7 @@ import (
"strings"
"testing"
apperrors "gitlab.com/technofab/nixtest/internal/errors"
apperrors "gitlab.com/TECHNOFAB/nixtest/internal/errors"
)
func TestComputeDiff(t *testing.T) {

View file

@ -13,6 +13,8 @@
removePrefix
assertMsg
generators
literalExpression
xor
;
nixtest-lib = import ./default.nix {inherit pkgs lib;};
@ -27,6 +29,18 @@
_type = "unset";
};
isUnset = isType "unset";
unsetOr = typ:
(types.either unsetType typ)
// {
inherit (typ) description getSubOptions;
};
mkUnsetOption = opts:
mkOption (opts
// {
type = unsetOr opts.type;
default = opts.default or unset;
defaultText = literalExpression "unset";
});
filterUnset = value:
if builtins.isAttrs value && !builtins.hasAttr "_type" value
@ -45,8 +59,11 @@
...
}: {
options = {
pos = mkOption {
type = types.either types.attrs unsetType;
pos = mkUnsetOption {
type = types.attrs;
description = ''
Position of test, use `__curPos` for automatic insertion of current position.
'';
default = pos;
apply = val:
if isUnset val
@ -56,63 +73,81 @@
in "${fileRelative}:${toString val.line}";
};
type = mkOption {
type = types.enum ["unit" "snapshot" "script"];
type = types.enum ["unit" "snapshot" "script" "vm"];
description = ''
Type of test, has to be one of "unit", "snapshot", "script", or "vm".
'';
default = "unit";
apply = value:
assert assertMsg (value != "script" || !isUnset config.script)
assert assertMsg (value == "script" -> !isUnset config.script)
"test '${config.name}' as type 'script' requires 'script' to be set";
assert assertMsg (value != "unit" || !isUnset config.expected)
assert assertMsg (value == "unit" -> !isUnset config.expected)
"test '${config.name}' as type 'unit' requires 'expected' to be set";
assert assertMsg (
let
actualIsUnset = isUnset config.actual;
actualDrvIsUnset = isUnset config.actualDrv;
in
(value != "unit")
|| (!actualIsUnset && actualDrvIsUnset)
|| (actualIsUnset && !actualDrvIsUnset)
)
assert assertMsg (value == "unit" -> (xor (isUnset config.actual) (isUnset config.actualDrv)))
"test '${config.name}' as type 'unit' requires only 'actual' OR 'actualDrv' to be set"; value;
};
name = mkOption {
type = types.str;
description = ''
Name of this test.
'';
};
description = mkOption {
type = types.either types.str unsetType;
default = unset;
description = mkUnsetOption {
type = types.str;
description = ''
Short description of the test.
'';
};
format = mkOption {
type = types.enum ["json" "pretty"];
description = ''
Which format to use for serializing arbitrary values.
Required since this config is serialized to JSON for passing it to Nixtest, so no Nix-values can be used directly.
- `json`: serializes the data to json using `builtins.toJSON`
- `pretty`: serializes the data to a "pretty" format using `lib.generators.toPretty`
'';
default = "json";
};
expected = mkOption {
expected = mkUnsetOption {
type = types.anything;
default = unset;
description = ''
Expected value of the test. Remember, the values are serialized (see [here](#suitesnametestsformat)).
'';
apply = val:
if isUnset val || config.format == "json"
then val
else generators.toPretty {} val;
};
actual = mkOption {
actual = mkUnsetOption {
type = types.anything;
default = unset;
description = ''
Actual value of the test. Remember, the values are serialized (see [here](#suitesnametestsformat)).
'';
apply = val:
if isUnset val || config.format == "json"
then val
else generators.toPretty {} val;
};
actualDrv = mkOption {
type = types.either types.package unsetType;
default = unset;
actualDrv = mkUnsetOption {
type = types.package;
description = ''
Actual value of the test, but as a derivation.
Nixtest will build this derivation when running the test, then compare the contents of the
resulting file to the [`expected`](#suitesnametestsexpected) value.
'';
apply = val:
# keep unset value
if isUnset val
then val
else builtins.unsafeDiscardStringContext (val.drvPath or "");
};
script = mkOption {
type = types.either types.str unsetType;
default = unset;
script = mkUnsetOption {
type = types.str;
description = ''
Script to run for the test.
Nixtest will run this, failing the test if it exits with a non-zero exit code.
'';
apply = val:
if isUnset val
then val
@ -120,6 +155,59 @@
builtins.unsafeDiscardStringContext
(pkgs.writeShellScript "nixtest-${config.name}" val).drvPath;
};
vmConfig = mkUnsetOption {
type = types.attrs;
description = ''
Configuration for `pkgs.testers.nixosText`.
'';
example = {
nodes.machine = {
services.nginx.enable = true;
};
testScript =
# py
''
machine.wait_for_unit("nginx.service")
machine.wait_for_open_port(80)
'';
};
};
finalConfig = mkOption {
internal = true;
type = types.attrs;
};
};
config = {
finalConfig = builtins.addErrorContext "[nixtest] while processing test ${config.name}" {
inherit (config) name expected actual actualDrv;
type =
if config.type == "vm"
then "script"
else config.type;
script =
if config.type == "vm"
then
assert assertMsg ((!isUnset config.vmConfig) && (config.vmConfig ? nodes) && (config.vmConfig ? testScript))
"test '${config.name}' as type 'vm' requires 'vmConfig' to be set and contain 'nodes' & 'testScript'"; let
inherit
(pkgs.testers.nixosTest (
{
name = "nixtest-vm-${config.name}";
}
// config.vmConfig
))
driver
;
in
builtins.unsafeDiscardStringContext
(pkgs.writeShellScript "nixtest-vm-${config.name}" ''
# use different TMPDIR to prevent race conditions:
# vde_switch: Could not bind to socket '/tmp/vde1.ctl/ctl': Address already in use
TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d) ${driver}/bin/nixos-test-driver
'').drvPath
else config.script;
};
};
};
@ -132,11 +220,19 @@
options = {
name = mkOption {
type = types.str;
description = ''
Name of the suite, uses attrset name by default.
'';
default = name;
defaultText = literalExpression name;
};
pos = mkOption {
type = types.either types.attrs unsetType;
default = unset;
pos = mkUnsetOption {
type = types.attrs;
description = ''
Position for tests, use `__curPos` for automatic insertion of current position.
This will set `pos` for every test of this suite, useful if the suite's tests are all in a single file.
'';
example = literalExpression "__curPos";
};
tests = mkOption {
type = types.listOf (types.submoduleWith {
@ -146,20 +242,41 @@
inherit testsBase;
};
});
description = ''
Define tests of this suite here.
'';
default = [];
};
finalConfig = mkOption {
internal = true;
type = types.attrs;
};
};
config = {
finalConfig = builtins.addErrorContext "[nixtest] while processing suite ${config.name}" {
inherit (config) name;
tests = map (test: test.finalConfig) config.tests;
};
};
};
nixtestSubmodule = {config, ...}: {
_file = ./module.nix;
options = {
base = mkOption {
description = "Base directory of the tests, will be removed from the test file path";
type = types.str;
description = ''
Base directory of the tests, will be removed from the test file path.
This makes it possible to show the relative path from the git repo, instead of ugly Nix store paths.
'';
default = "";
};
skip = mkOption {
type = types.str;
description = ''
Tests to skip, is passed to Nixtest's `--skip` param.
'';
default = "";
};
suites = mkOption {
@ -169,14 +286,23 @@
testsBase = config.base;
};
});
description = ''
Define your test suites here, every test belongs to a suite.
'';
default = {};
apply = suites:
map (
n: filterUnset (builtins.removeAttrs suites.${n} ["pos"])
)
(builtins.attrNames suites);
example = {
"Suite A".tests = [
{
name = "Some Test";
}
];
};
};
finalConfig = mkOption {
internal = true;
type = types.listOf types.attrs;
};
finalConfigJson = mkOption {
internal = true;
type = types.package;
@ -187,11 +313,18 @@
};
};
config = {
finalConfigJson = nixtest-lib.exportSuites config.suites;
app = nixtest-lib.mkBinary {
nixtests = config.finalConfigJson;
extraParams = ''--skip="${config.skip}"'';
};
finalConfig = map (suite: filterUnset suite.finalConfig) (builtins.attrValues config.suites);
finalConfigJson =
builtins.addErrorContext "[nixtest] while exporting suites"
(nixtest-lib.exportSuites config.finalConfig);
app =
(nixtest-lib.mkBinary {
nixtests = config.finalConfigJson;
extraParams = ''--skip="${config.skip}"'';
})
// {
rawTests = config.finalConfig;
};
};
};
in

View file

@ -0,0 +1,8 @@
{inputs, ...}: let
inherit (inputs) self pkgs;
in {
nixtest = pkgs.callPackage "${self}/package.nix" {};
update-package = pkgs.writeShellScriptBin "update-package" ''
${pkgs.nix-update}/bin/nix-update nixtest --flake --version skip
'';
}

85
nix/repo/ci.nix Normal file
View file

@ -0,0 +1,85 @@
{inputs, ...}: let
inherit (inputs) pkgs cilib;
in
cilib.mkCI {
pipelines."default" = {
stages = ["test" "build" "deploy"];
jobs = {
"test:lib" = {
stage = "test";
script = [
"nix run .#tests -- --junit=junit.xml"
];
allow_failure = true;
artifacts = {
when = "always";
reports.junit = "junit.xml";
};
};
"test:go" = {
stage = "test";
nix.deps = with pkgs; [gcc go go-junit-report gocover-cobertura];
variables = {
GOPATH = "$CI_PROJECT_DIR/.go";
GOCACHE = "$CI_PROJECT_DIR/.go/pkg/mod";
};
script = [
# sh
''
set +e
go test -coverprofile=coverage.out -v 2>&1 ./... | go-junit-report -set-exit-code > report.xml
TEST_EXIT_CODE=$?
go tool cover -func coverage.out
gocover-cobertura < coverage.out > coverage.xml
exit $TEST_EXIT_CODE
''
];
allow_failure = true;
coverage = "/\(statements\)(?:\s+)?(\d+(?:\.\d+)?%)/";
cache.paths = [".go/pkg/mod/"];
artifacts = {
when = "always";
reports = {
junit = "report.xml";
coverage_report = {
coverage_format = "cobertura";
path = "coverage.xml";
};
};
};
};
"build" = {
stage = "build";
script = [
# sh
"nix build .#nixtest"
];
};
"docs" = {
stage = "build";
script = [
# sh
''
nix build .#docs:default
mkdir -p public
cp -r result/. public/
''
];
artifacts.paths = ["public"];
};
"pages" = {
nix.enable = false;
image = "alpine:latest";
stage = "deploy";
script = ["true"];
artifacts.paths = ["public"];
rules = [
{
"if" = "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH";
}
];
};
};
};
}

33
nix/repo/devShells.nix Normal file
View file

@ -0,0 +1,33 @@
{
inputs,
cell,
...
}: let
inherit (inputs) pkgs devshell treefmt;
inherit (cell) soonix;
in {
default = devshell.mkShell {
imports = [soonix.devshellModule];
packages = with pkgs; [
(treefmt.mkWrapper pkgs {
programs = {
alejandra.enable = true;
mdformat.enable = true;
gofmt.enable = true;
};
settings.formatter.mdformat.command = let
pkg = pkgs.python3.withPackages (p: [
p.mdformat
p.mdformat-mkdocs
]);
in "${pkg}/bin/mdformat";
})
gcc
go
gopls
delve
go-junit-report
gocover-cobertura
];
};
}

64
nix/repo/docs.nix Normal file
View file

@ -0,0 +1,64 @@
{inputs, ...}: let
inherit (inputs) pkgs doclib ntlib;
optionsDoc = doclib.mkOptionDocs {
module = ntlib.module;
roots = [
{
url = "https://gitlab.com/TECHNOFAB/nixtest/-/blob/main/lib";
path = "${inputs.self}/lib";
}
];
};
optionsDocs = pkgs.runCommand "options-docs" {} ''
mkdir -p $out
ln -s ${optionsDoc} $out/options.md
'';
in
(doclib.mkDocs {
docs."default" = {
base = "${inputs.self}";
path = "${inputs.self}/docs";
material = {
enable = true;
colors = {
primary = "green";
accent = "light green";
};
umami = {
enable = true;
src = "https://analytics.tf/umami";
siteId = "716d1869-9342-4b62-a770-e15d2d5c807d";
domains = ["nixtest.projects.tf"];
};
};
macros = {
enable = true;
includeDir = toString optionsDocs;
};
config = {
site_name = "Nixtest";
site_url = "https://nixtest.projects.tf";
repo_name = "TECHNOFAB/nixtest";
repo_url = "https://gitlab.com/TECHNOFAB/nixtest";
extra_css = ["style.css"];
theme = {
logo = "images/logo.svg";
icon.repo = "simple/gitlab";
favicon = "images/logo.svg";
};
nav = [
{"Introduction" = "index.md";}
{"Usage" = "usage.md";}
{"Reference" = "reference.md";}
{"CLI" = "cli.md";}
{"Example Configs" = "examples.md";}
{"Options" = "options.md";}
];
markdown_extensions = [
"pymdownx.superfences"
"admonition"
];
};
};
}).packages

100
nix/repo/flake.lock generated Normal file
View file

@ -0,0 +1,100 @@
{
"nodes": {
"devshell-lib": {
"locked": {
"dir": "lib",
"lastModified": 1767274074,
"narHash": "sha256-h2grM9qoSnYdqN7K8+taeMuWC2umaN/c2FCBu48frlo=",
"owner": "rensa-nix",
"repo": "devshell",
"rev": "5508ced269ee40ff7f5261ee3b5bf5597f7cad5d",
"type": "gitlab"
},
"original": {
"dir": "lib",
"owner": "rensa-nix",
"repo": "devshell",
"type": "gitlab"
}
},
"nix-gitlab-ci-lib": {
"locked": {
"dir": "lib",
"lastModified": 1765444672,
"narHash": "sha256-B0cMjRs9P50ym9Le0VUcRN69Yy6tbV13MXq81tTTEus=",
"owner": "TECHNOFAB",
"repo": "nix-gitlab-ci",
"rev": "8f88a53b5479773cd626420362631bc1da99e677",
"type": "gitlab"
},
"original": {
"dir": "lib",
"owner": "TECHNOFAB",
"ref": "3.1.2",
"repo": "nix-gitlab-ci",
"type": "gitlab"
}
},
"nixmkdocs-lib": {
"locked": {
"dir": "lib",
"lastModified": 1766404754,
"narHash": "sha256-EjBe6x6BT8ckPirMWhSf1GfaFxORYxR/Uu71FvSAm60=",
"owner": "TECHNOFAB",
"repo": "nixmkdocs",
"rev": "cfa9606eeeb9288e2799896d7d42b3d3860f9ccb",
"type": "gitlab"
},
"original": {
"dir": "lib",
"owner": "TECHNOFAB",
"repo": "nixmkdocs",
"type": "gitlab"
}
},
"root": {
"inputs": {
"devshell-lib": "devshell-lib",
"nix-gitlab-ci-lib": "nix-gitlab-ci-lib",
"nixmkdocs-lib": "nixmkdocs-lib",
"soonix-lib": "soonix-lib",
"treefmt-nix": "treefmt-nix"
}
},
"soonix-lib": {
"locked": {
"dir": "lib",
"lastModified": 1767274116,
"narHash": "sha256-8+VeMokZHjOLs6fRUTj/9uxbMlHKDl384Tk6K8Qjm4k=",
"owner": "TECHNOFAB",
"repo": "soonix",
"rev": "56f281eea45bdcf29674adfa7962f14e490a6051",
"type": "gitlab"
},
"original": {
"dir": "lib",
"owner": "TECHNOFAB",
"repo": "soonix",
"type": "gitlab"
}
},
"treefmt-nix": {
"flake": false,
"locked": {
"lastModified": 1767122417,
"narHash": "sha256-yOt/FTB7oSEKQH9EZMFMeuldK1HGpQs2eAzdS9hNS/o=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "dec15f37015ac2e774c84d0952d57fcdf169b54d",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

22
nix/repo/flake.nix Normal file
View file

@ -0,0 +1,22 @@
{
inputs = {
devshell-lib.url = "gitlab:rensa-nix/devshell?dir=lib";
soonix-lib.url = "gitlab:TECHNOFAB/soonix?dir=lib";
nix-gitlab-ci-lib.url = "gitlab:TECHNOFAB/nix-gitlab-ci/3.1.2?dir=lib";
nixmkdocs-lib.url = "gitlab:TECHNOFAB/nixmkdocs?dir=lib";
treefmt-nix = {
url = "github:numtide/treefmt-nix";
flake = false;
};
};
outputs = i:
i
// {
devshell = i.devshell-lib.lib {inherit (i.parent) pkgs;};
soonix = i.soonix-lib.lib {inherit (i.parent) pkgs;};
cilib = i.nix-gitlab-ci-lib.lib {inherit (i.parent) pkgs;};
doclib = i.nixmkdocs-lib.lib {inherit (i.parent) pkgs;};
ntlib = import "${i.parent.self}/lib" {inherit (i.parent) pkgs;};
treefmt = import i.treefmt-nix;
};
}

34
nix/repo/soonix.nix Normal file
View file

@ -0,0 +1,34 @@
{
inputs,
cell,
...
}: let
inherit (inputs) soonix;
inherit (cell) ci;
in
(soonix.make {
hooks = {
ci = ci.soonix;
renovate = {
output = ".gitlab/renovate.json5";
data = {
extends = ["config:recommended"];
postUpgradeTasks.commands = [
"nix-portable nix run .#update-package"
"nix-portable nix run .#soonix:update"
];
lockFileMaintenance = {
enabled = true;
extends = ["schedule:monthly"];
};
nix.enabled = true;
gitlabci.enabled = false;
};
hook = {
mode = "copy";
gitignore = false;
};
opts.format = "json";
};
};
}).config

10
nix/repo/tests.nix Normal file
View file

@ -0,0 +1,10 @@
{inputs, ...}: let
inherit (inputs) pkgs ntlib;
in {
tests = ntlib.mkNixtest {
modules = ntlib.autodiscover {dir = "${inputs.self}/tests";};
args = {
inherit pkgs ntlib;
};
};
}

View file

@ -4,7 +4,8 @@
...
}:
buildGoModule {
name = "nixtest";
pname = "nixtest";
version = "latest";
src =
# filter everything except for cmd/ and go.mod, go.sum
with lib.fileset;
@ -18,6 +19,6 @@ buildGoModule {
];
};
subPackages = ["cmd/nixtest"];
vendorHash = "sha256-6kARJgngmXielUoXukYdAA0QHk1mwLRvgKJhx+v1iSo=";
vendorHash = "sha256-WF/lzu9lt9SR3WiA8LLWVT1OwpE3sIOtSqf4HMIMmE8=";
meta.mainProgram = "nixtest";
}

View file

@ -1 +1 @@
"{\n example = \u003cfunction\u003e;\n example2 = {\n drv = \u003cderivation hello-2.12.1\u003e;\n };\n}"
"{\n example = \u003cfunction\u003e;\n example2 = {\n drv = \u003cderivation hello-2.12.2\u003e;\n };\n}"

View file

@ -49,6 +49,39 @@
grep -q "test" ${builtins.toFile "test" "test"}
'';
}
{
name = "test-vm";
type = "vm";
vmConfig = {
nodes.machine = {pkgs, ...}: {
services.nginx = {
enable = true;
virtualHosts."localhost" = {
root = pkgs.writeTextDir "index.html" "Hello from nixtest VM!";
};
};
};
testScript =
# py
''
machine.wait_for_unit("nginx.service")
machine.wait_for_open_port(80)
machine.succeed("curl -f http://localhost | grep 'Hello from nixtest VM!'")
'';
};
}
{
name = "vm-fail";
type = "vm";
vmConfig = {
nodes.machine = {};
testScript =
# py
''
machine.succeed("curl -f http://localhost | grep 'Hello from nixtest VM!'")
'';
};
}
];
};
"other-suite".tests = [

View file

@ -72,6 +72,8 @@
''
${ntlib.helpers.path [pkgs.gnugrep pkgs.mktemp pkgs.coreutils]}
${ntlib.helpers.scriptHelpers}
export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
export NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
cp -r ${./../snapshots} snapshots
# start without nix & env binaries to expect errors
@ -87,9 +89,10 @@
assert "-f junit2.xml" "should create junit2.xml"
assert_not_contains "$output" "executable file not found" "nix should now exist"
assert_contains "$output" "suite-one" "should contain suite-one"
assert_contains "$output" "8/11 (1 SKIPPED)" "should be 8/11 total"
assert_contains "$output" "9/13 (1 SKIPPED)" "should be 9/13 total"
assert_contains "$output" "ERROR" "should contain an error"
assert_contains "$output" "SKIP" "should contain a skip"
assert_contains "$output" "RequestedAssertionFailed" "vm-fail test should fail"
'';
}
];