Skip to content

Commit 921a6a8

Browse files
authored
refactor: build scripts as standalone packages (#36)
1 parent 8b88af5 commit 921a6a8

File tree

15 files changed

+382
-134
lines changed

15 files changed

+382
-134
lines changed

demo.tape

Lines changed: 0 additions & 26 deletions
This file was deleted.

nix/default.nix

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
{inputs, ...}: {
22
imports = [
3+
./scripts
4+
35
./devenv.nix
4-
./package.nix
6+
./overlay.nix
7+
./packages.nix
58
];
69
perSystem = {pkgs, ...}: {
710
formatter = pkgs.alejandra;

nix/devenv.nix

Lines changed: 11 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,16 @@
1717
modules = [
1818
inputs.env-help.devenvModule
1919
];
20-
shells.default = let
21-
buildInputs = config.packages.snekcheck.nativeBuildInputs;
22-
goPkg = lib.findFirst (pkg: builtins.match "go" pkg.pname != null) pkgs.go buildInputs;
23-
in {
20+
shells.default = {
2421
enterShell = "${pkgs.writeShellApplication {
2522
name = "splashScreen";
2623
runtimeInputs = [
24+
self.scripts.${system}.splash
2725
pkgs.lolcat
2826
pkgs.uutils-coreutils-noprefix
2927
];
3028
text = ''
31-
printf " ▄▄▄▄▄ ▄ ▄███▄ █ █▀ ▄█▄ ▄ █ ▄███▄ ▄█▄ █ █▀
32-
█ ▀▄ █ █▀ ▀ █▄█ █▀ ▀▄ █ █ █▀ ▀ █▀ ▀▄ █▄█
33-
▄ ▀▀▀▀▄ ██ █ ██▄▄ █▀▄ █ ▀ ██▀▀█ ██▄▄ █ ▀ █▀▄
34-
▀▄▄▄▄▀ █ █ █ █▄ ▄▀ █ █ █▄ ▄▀ █ █ █▄ ▄▀ █▄ ▄▀ █ █
35-
█ █ █ ▀███▀ █ ▀███▀ █ ▀███▀ ▀███▀ █
36-
█ ██ ▀ ▀ ▀\n" | lolcat
29+
splash
3730
printf "\033[0;1;36mDEVSHELL ACTIVATED\033[0m\n"
3831
'';
3932
}}/bin/splashScreen";
@@ -42,17 +35,13 @@
4235
PROJECT_ROOT = config.devenv.shells.default.env.DEVENV_ROOT;
4336
in {
4437
inherit PROJECT_ROOT;
45-
GO_ROOT = "${PROJECT_ROOT}/go";
46-
NIX_ROOT = "${PROJECT_ROOT}/nix";
38+
SOURCE_ROOT = "${PROJECT_ROOT}/go";
4739
};
4840

4941
env-help.enable = true;
5042

5143
languages = {
52-
go = {
53-
enable = true;
54-
package = goPkg;
55-
};
44+
go.enable = true;
5645
nix.enable = true;
5746
};
5847

@@ -93,84 +82,12 @@
9382
};
9483
};
9584

96-
scripts = {
97-
bench = {
98-
description = "Runs all benchmark tests.";
99-
exec = ''
100-
cd "$GO_ROOT" && \
101-
${goPkg}/bin/go test ./... -bench=.
102-
'';
103-
};
104-
build = {
105-
description = "Builds the project binary.";
106-
exec = "${pkgs.writeShellApplication {
107-
name = "build";
108-
runtimeInputs = [
109-
goPkg
110-
inputs.gomod2nix.legacyPackages.${system}.gomod2nix
111-
];
112-
text = ''
113-
(cd "$GO_ROOT" && go mod tidy && gomod2nix) && \
114-
nix build "$PROJECT_ROOT"#snekcheck
115-
'';
116-
}}/bin/build";
117-
};
118-
demo = {
119-
description = "Generates a demo GIF.";
120-
exec = ''
121-
PATH="$PROJECT_ROOT/result/bin:${pkgs.bashInteractive}/bin:$PATH"
122-
123-
mkdir --parents "$PROJECT_ROOT"/demo
124-
for i in $(seq 1 3); do touch "$PROJECT_ROOT"/demo/"$i"valid; done;
125-
for i in $(seq 1 3); do touch "$PROJECT_ROOT"/demo/"$i"InVaLiD; done;
126-
127-
build && \
128-
${pkgs.vhs}/bin/vhs "$PROJECT_ROOT"/demo.tape
129-
130-
rm --force --recursive "$PROJECT_ROOT"/demo
131-
'';
132-
};
133-
e2e = {
134-
description = "Runs all end-to-end tests.";
135-
exec = ''
136-
build && \
137-
${pkgs.shellspec}/bin/shellspec --no-warning-as-failure "$PROJECT_ROOT"
138-
'';
139-
};
140-
lint = {
141-
description = "Lints the project.";
142-
exec = "${pkgs.writeShellApplication {
143-
name = "lint";
144-
runtimeInputs = [
145-
goPkg
146-
pkgs.golangci-lint
147-
self.packages.${system}.snekcheck
148-
];
149-
text = ''
150-
snekcheck --fix "$PROJECT_ROOT" && \
151-
nix fmt "$PROJECT_ROOT" -- --quiet && \
152-
(cd "$GO_ROOT" && go mod tidy && go fmt ./... && go vet ./... && \
153-
golangci-lint run ./...)
154-
'';
155-
}}/bin/lint";
156-
};
157-
run = {
158-
description = "Runs the project.";
159-
exec = ''
160-
(cd "$GO_ROOT" && \
161-
${goPkg}/bin/go mod tidy && \
162-
${inputs.gomod2nix.legacyPackages.${system}.gomod2nix}/bin/gomod2nix) && \
163-
nix run "$PROJECT_ROOT"#snekcheck -- "$@"
164-
'';
165-
};
166-
unit = {
167-
description = "Runs all unit tests.";
168-
exec = ''
169-
cd "$GO_ROOT" && \
170-
${goPkg}/bin/go test --cover ./...
171-
'';
172-
};
173-
};
85+
scripts =
86+
lib.mapAttrs (_: pkg: {
87+
inherit (pkg.meta) description;
88+
exec = "${pkg}/bin/${pkg.name} $@";
89+
})
90+
self.scripts.${system};
17491
};
17592
};
17693
};

nix/overlay.nix

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
inputs,
3+
self,
4+
...
5+
}: {
6+
flake.overlay = _: prev: {
7+
writePythonApplication = {
8+
description,
9+
libraries ? [],
10+
name,
11+
python ? prev.pkgs.python3,
12+
ruffIgnoredRules ? [
13+
"D100"
14+
"D203"
15+
"D212"
16+
"FBT"
17+
"I001"
18+
"S101"
19+
"S603"
20+
"S607"
21+
"T201"
22+
],
23+
runtimeInputs ? [],
24+
script,
25+
}:
26+
assert builtins.isString description;
27+
assert builtins.isList libraries && builtins.all (pkg: builtins.isAttrs pkg && pkg ? outPath) libraries;
28+
assert builtins.isString name;
29+
assert builtins.isAttrs python && python ? withPackages;
30+
assert builtins.isList ruffIgnoredRules && builtins.all (rule: builtins.isString rule) ruffIgnoredRules;
31+
assert builtins.isList runtimeInputs && builtins.all (pkg: builtins.isAttrs pkg && pkg ? outPath) runtimeInputs;
32+
assert builtins.isString script;
33+
prev.pkgs.runCommandLocal name {
34+
buildInputs = [
35+
prev.pkgs.makeWrapper
36+
prev.pkgs.ruff
37+
prev.pkgs.uutils-coreutils-noprefix
38+
];
39+
meta.description = description;
40+
} ''
41+
mkdir -p "$out/bin"
42+
43+
cat > "$out/bin/${name}.py" <<EOF
44+
#!${python.withPackages (_: libraries)}/bin/python
45+
${script}
46+
EOF
47+
48+
chmod +x "$out/bin/${name}.py"
49+
50+
ruff check "$out/bin/${name}.py" \
51+
--select "ALL" \
52+
${builtins.concatStringsSep " " (builtins.map (rule: "--ignore ${rule}") ruffIgnoredRules)}
53+
54+
makeWrapper "$out/bin/${name}.py" "$out/bin/${name}" \
55+
--set "PATH" "${prev.lib.makeBinPath runtimeInputs}"
56+
'';
57+
};
58+
59+
perSystem = {system, ...}: {
60+
_module.args.pkgs = import inputs.nixpkgs {
61+
inherit system;
62+
overlays = [self.overlay];
63+
};
64+
};
65+
}

nix/package.nix

Lines changed: 0 additions & 13 deletions
This file was deleted.

nix/packages.nix

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
inputs,
3+
self,
4+
...
5+
}: {
6+
perSystem = {
7+
lib,
8+
pkgs,
9+
system,
10+
...
11+
}: {
12+
packages = {
13+
default = self.packages.${system}.snekcheck;
14+
snekcheck = inputs.gomod2nix.legacyPackages.${system}.buildGoApplication {
15+
inherit (pkgs) go;
16+
meta = {
17+
description = "An opinionated filename linter that loves snake case.";
18+
homepage = "https://github.com/jtrrll/snekcheck";
19+
license = lib.licenses.mit;
20+
};
21+
modules = ../go/gomod2nix.toml;
22+
pname = "snekcheck";
23+
src = ../go;
24+
version = "0.1.0";
25+
};
26+
};
27+
};
28+
}

nix/scripts/bench.nix

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
perSystem = {pkgs, ...}: {
3+
scripts.bench = pkgs.writeShellApplication {
4+
meta.description = "Runs all benchmark tests.";
5+
name = "bench";
6+
runtimeInputs = [
7+
pkgs.go
8+
];
9+
text = ''
10+
cd "$SOURCE_ROOT"
11+
go test ./... -bench=.
12+
'';
13+
};
14+
};
15+
}

nix/scripts/build.nix

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{inputs, ...}: {
2+
perSystem = {
3+
pkgs,
4+
system,
5+
...
6+
}: {
7+
scripts.build = pkgs.writePythonApplication {
8+
description = "Builds the snekcheck package.";
9+
name = "build";
10+
libraries = [pkgs.python3.pkgs.typer];
11+
runtimeInputs = [
12+
inputs.gomod2nix.legacyPackages.${system}.gomod2nix
13+
pkgs.go
14+
pkgs.nix
15+
];
16+
script = ''
17+
from pathlib import Path
18+
import datetime
19+
import os
20+
import subprocess
21+
import typer
22+
23+
app = typer.Typer()
24+
25+
PROJECT_ROOT: Path = Path(os.environ.get("PROJECT_ROOT", "."))
26+
SOURCE_ROOT: Path = Path(os.environ.get("SOURCE_ROOT"))
27+
RESULT_DIR: Path = PROJECT_ROOT / "result"
28+
29+
def format_time(epoch: float) -> str:
30+
"""Format a timestamp as a string."""
31+
return datetime.datetime.fromtimestamp(
32+
epoch, tz=datetime.UTC,
33+
).strftime("%Y-%m-%dT%H:%M:%S")
34+
35+
def should_rebuild() -> bool:
36+
"""Determine if the project should be rebuilt."""
37+
output_binary = RESULT_DIR / "bin" / "snekcheck"
38+
39+
if not output_binary.exists():
40+
return True
41+
42+
go_files: list[Path] = list(SOURCE_ROOT.rglob("*.go"))
43+
if not go_files:
44+
return False
45+
46+
latest_src_time: float = max(path.stat().st_mtime for path in go_files)
47+
latest_build_time: float = RESULT_DIR.lstat().st_mtime
48+
49+
print(f"{"":20} | {"Human Time":20} | Epoch Time")
50+
print(f"{"-"*20} | {"-"*20} | {"-"*11}")
51+
print(
52+
f"{"Source changed":20} | "
53+
f"{format_time(latest_src_time):20} | "
54+
f"{int(latest_src_time)}",
55+
)
56+
print(
57+
f"{"Last build":20} | "
58+
f"{format_time(latest_build_time):20} | "
59+
f"{int(latest_build_time)}",
60+
)
61+
62+
return latest_src_time > latest_build_time
63+
64+
@app.command()
65+
def main(
66+
force: bool = typer.Option(False, "--force", "-f",
67+
help="Force rebuild regardless of timestamps"),
68+
) -> None:
69+
"""Rebuild the snekcheck binary if the source has changed."""
70+
if force or should_rebuild():
71+
if RESULT_DIR.exists():
72+
RESULT_DIR.unlink()
73+
74+
print("Rebuilding...")
75+
subprocess.run(["go", "mod", "tidy"], cwd=SOURCE_ROOT, check=True)
76+
subprocess.run(["gomod2nix"], cwd=SOURCE_ROOT, check=True)
77+
subprocess.run(["nix", "build", f"{PROJECT_ROOT}#snekcheck"], check=True)
78+
else:
79+
print("Build is up to date.")
80+
81+
if __name__ == "__main__":
82+
app()
83+
'';
84+
};
85+
};
86+
}

0 commit comments

Comments
 (0)