Skip to content

Commit 2e6c448

Browse files
ci(treefmt): stop treefmt.toml from drifting out of sync with the flake (#5545)
* ci(treefmt): stop treefmt.toml from drifting out of sync with the flake treefmt.toml and nix/flake-modules/formatter.nix had to be kept in sync by hand, and had already drifted: the former declared nixfmt while the latter uses nixpkgs-fmt. Contributors without Nix run a bare treefmt against treefmt.toml, so any drift makes them format against a different config than CI enforces, and their supposedly formatted code fails linting. Generate treefmt.toml from the treefmt settings instead, reducing the pinned store paths to bare binary names so the result stays usable without Nix. The same derivation is exposed as checks.treefmt-toml so nix flake check covers it, and as packages.check-treefmt-toml so the linting workflow can build it without hardcoding a system, since CI does not run nix flake check. update-treefmt-toml anchors on the projectRootFile marker treefmt already declares rather than deriving a second notion of the project root. Regenerate with: nix run .#update-treefmt-toml This only makes the two configs agree; it does not change any formatter. Note that the generated fallback resolves formatters from PATH, so their versions remain unpinned for contributors without Nix. * style(nix): use nixfmt instead of nixpkgs-fmt nixpkgs-fmt is archived upstream; nixfmt is the formatter standardised in RFC 166. treefmt.toml is regenerated accordingly. * style(nix): reformat all Nix files with nixfmt
1 parent 054a715 commit 2e6c448

11 files changed

Lines changed: 190 additions & 77 deletions

File tree

.github/workflows/ensure_linting.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ jobs:
3838
# except any version with the key that is the same as the `primary-key`
3939
purge-primary-key: never
4040

41+
- name: Ensure treefmt.toml matches the Nix formatter config
42+
# treefmt.toml is generated from nix/flake-modules/formatter.nix and only
43+
# consumed by contributors without Nix. If it goes stale, those
44+
# contributors format against a different config than this job enforces.
45+
run: nix build .#check-treefmt-toml --no-link
46+
4147
- name: Run treefmt in CI mode
4248
# Use the lean `.#lint` app instead of the full devenv shell: it only
4349
# provides the treefmt wrapper + Elixir, so CI does not have to realize

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
- build(deps): bump body-parser from 1.20.5 to 1.20.6 in /website (#5539)
6767
- build(deps): bump fast-uri from 3.1.2 to 3.1.4 in /website (#5540)
6868
- build(deps): bump immutable from 5.1.5 to 5.1.9 in /assets (#5541)
69+
- ci(treefmt): stop treefmt.toml from drifting out of sync with the flake (#5545 - @JakobLichterfeld)
70+
- style(nix): format Nix code with nixfmt instead of the archived nixpkgs-fmt (#5545 - @JakobLichterfeld)
6971

7072
#### Dashboards
7173

flake.nix

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
1313
};
1414

15-
outputs = inputs@{ self, flake-parts, ... }:
15+
outputs =
16+
inputs@{ self, flake-parts, ... }:
1617
flake-parts.lib.mkFlake { inherit inputs; } {
1718
flake.nixosModules.default = import ./nix/module.nix { inherit self; };
1819

nix/backup_and_restore.nix

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
{ stdenv
2-
, lib
3-
, pkgs
4-
, writeShellScript
5-
, databaseUser
6-
, databaseName
7-
, ...
1+
{
2+
stdenv,
3+
lib,
4+
pkgs,
5+
writeShellScript,
6+
databaseUser,
7+
databaseName,
8+
...
89
}:
910
let
1011
backup = writeShellScript "teslamate-backup" ''

nix/flake-modules/checks.nix

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{ self, inputs, ... }:
22
{
33
perSystem =
4-
{ self'
5-
, pkgs
6-
, lib
7-
, ...
4+
{
5+
self',
6+
pkgs,
7+
lib,
8+
...
89
}:
910
let
1011
inherit (inputs) nixpkgs;
@@ -41,8 +42,11 @@
4142
in
4243
{
4344
checks =
44-
if pkgs.stdenv.isLinux then {
45-
default = moduleTest;
46-
} else { };
45+
if pkgs.stdenv.isLinux then
46+
{
47+
default = moduleTest;
48+
}
49+
else
50+
{ };
4751
};
4852
}

nix/flake-modules/devenv.nix

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
];
66

77
perSystem =
8-
{ config
9-
, pkgs
10-
, lib
11-
, ...
8+
{
9+
config,
10+
pkgs,
11+
lib,
12+
...
1213
}:
1314
# legacy
1415
let
@@ -88,7 +89,7 @@
8889
package = pkgs.postgresql;
8990
listen_addresses = "127.0.0.1";
9091
port = postgres_port;
91-
initialDatabases = [{ name = "teslamate"; }];
92+
initialDatabases = [ { name = "teslamate"; } ];
9293
initialScript = ''
9394
CREATE USER teslamate with encrypted password 'your_secure_password_here';
9495
GRANT ALL PRIVILEGES ON DATABASE teslamate TO teslamate;

nix/flake-modules/formatter.nix

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,45 @@
55
];
66
perSystem =
77
{ config, pkgs, ... }:
8+
let
9+
# treefmt.toml is generated from the treefmt settings below so that the
10+
# two can not drift apart (see checks.treefmt-toml). The only difference
11+
# is the `command` of each formatter: the Nix config pins absolute store
12+
# paths, while contributors without Nix need the bare binary name from
13+
# their PATH.
14+
treefmtTomlHeader = ''
15+
# One CLI to format the code tree - https://git.numtide.com/numtide/treefmt
16+
#
17+
# GENERATED FILE - DO NOT EDIT.
18+
# Source of truth: nix/flake-modules/formatter.nix
19+
# Regenerate with: nix run .#update-treefmt-toml
20+
#
21+
# This copy exists for contributors without Nix, who run a bare `treefmt`.
22+
# Nix users (`nix fmt`, `nix run .#lint`) and CI use the generated config
23+
# directly and never read this file. Note that it resolves formatters from
24+
# PATH, so their versions are not pinned the way the Nix config pins them.
25+
'';
26+
27+
generatedTreefmtToml = pkgs.runCommand "treefmt.toml" { } ''
28+
cat ${pkgs.writeText "treefmt-toml-header" treefmtTomlHeader} > $out
29+
echo >> $out
30+
# Reduce the pinned store paths to bare binary names, resolved from PATH.
31+
sed -E 's|"/nix/store/[^"]*/|"|' ${config.treefmt.build.configFile} >> $out
32+
'';
33+
34+
# treefmt.toml must stay byte-identical to what the treefmt settings
35+
# generate, otherwise contributors without Nix format against a stale
36+
# config and CI rejects their result.
37+
treefmtTomlInSync = pkgs.runCommand "treefmt-toml-in-sync" { } ''
38+
if ! diff -u ${../../treefmt.toml} ${generatedTreefmtToml}; then
39+
echo >&2
40+
echo "treefmt.toml is out of sync with nix/flake-modules/formatter.nix." >&2
41+
echo "Regenerate it with: nix run .#update-treefmt-toml" >&2
42+
exit 1
43+
fi
44+
touch $out
45+
'';
46+
in
847
{
948
# Auto formatters. This also adds a flake check to ensure that the
1049
# source tree was auto formatted.
@@ -13,7 +52,6 @@
1352
flakeCheck = false; # Add a flake check to run treefmt, disabled, as mix format does need the dependencies fetched beforehand
1453
projectRootFile = "VERSION"; # File used to identity repo root
1554

16-
# we really need to mirror the treefmt.toml as we can't use it directly
1755
settings.global.excludes = [
1856
"*.gitignore"
1957
"*.dockerignore"
@@ -62,7 +100,27 @@
62100

63101
programs.prettier.enable = true;
64102

65-
programs.nixpkgs-fmt.enable = true;
103+
programs.nixfmt.enable = true;
104+
};
105+
106+
# Exposed twice on purpose: as a check so `nix flake check` covers it, and
107+
# as a package so CI can build it as `.#check-treefmt-toml`, which resolves
108+
# the current system instead of hardcoding one.
109+
checks.treefmt-toml = treefmtTomlInSync;
110+
packages.check-treefmt-toml = treefmtTomlInSync;
111+
112+
# Writes next to the root marker treefmt itself anchors on, rather than
113+
# deriving a second notion of the project root.
114+
packages.update-treefmt-toml = pkgs.writeShellApplication {
115+
name = "update-treefmt-toml";
116+
text = ''
117+
if [ ! -f ${config.treefmt.projectRootFile} ]; then
118+
echo "run this from the project root (no ${config.treefmt.projectRootFile} here)" >&2
119+
exit 1
120+
fi
121+
install -m 644 ${generatedTreefmtToml} treefmt.toml
122+
echo "wrote treefmt.toml"
123+
'';
66124
};
67125

68126
# Lean treefmt entrypoint for CI: `nix run .#lint -- --ci`

nix/flake-modules/package.nix

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{ ... }:
22
{
33
perSystem =
4-
{ lib
5-
, pkgs
6-
, system
7-
, ...
4+
{
5+
lib,
6+
pkgs,
7+
system,
8+
...
89
}:
910
let
1011
beamPackages = pkgs.beam.packagesWith pkgs.beam.interpreters.erlang_28;

nix/maintenance.nix

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
{ stdenv
2-
, lib
3-
, pkgs
4-
, writeShellScript
5-
, databaseUser
6-
, databaseName
7-
, getExe
8-
, teslamate
9-
, environmentFilePath
10-
, ...
1+
{
2+
stdenv,
3+
lib,
4+
pkgs,
5+
writeShellScript,
6+
databaseUser,
7+
databaseName,
8+
getExe,
9+
teslamate,
10+
environmentFilePath,
11+
...
1112
}:
1213
let
1314
# Extract a single KEY=value from the environment file literally, without

nix/module.nix

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{ self }:
2-
{ config
3-
, lib
4-
, pkgs
5-
, ...
2+
{
3+
config,
4+
lib,
5+
pkgs,
6+
...
67
}:
78
let
89
teslamate = self.packages.${pkgs.stdenv.hostPlatform.system}.default;
@@ -256,7 +257,9 @@ in
256257
# so they are allowed here as well. This also covers the psql
257258
# \getenv used in postStart, which needs PostgreSQL >= 14.
258259
assertion =
259-
let v = cfg.postgres.package.version; in
260+
let
261+
v = cfg.postgres.package.version;
262+
in
260263
lib.versionAtLeast v "18"
261264
|| (lib.versionAtLeast v "17.3" && lib.versionOlder v "18")
262265
|| (lib.versionAtLeast v "16.7" && lib.versionOlder v "17");
@@ -336,8 +339,9 @@ in
336339
};
337340
})
338341
(mkIf cfg.grafana.enable {
339-
warnings = lib.optional (cfg.grafana.secretKeyFile == /dev/null)
340-
"teslamate: grafana.secretKeyFile is not set. Using the insecure default secret_key. Set grafana.secretKeyFile to a file containing a secure random key.";
342+
warnings =
343+
lib.optional (cfg.grafana.secretKeyFile == /dev/null)
344+
"teslamate: grafana.secretKeyFile is not set. Using the insecure default secret_key. Set grafana.secretKeyFile to a file containing a secure random key.";
341345
services.grafana = {
342346
enable = true;
343347
settings = {
@@ -352,9 +356,10 @@ in
352356
allow_embedding = true;
353357
disable_gravatar = true;
354358
secret_key =
355-
if cfg.grafana.secretKeyFile == /dev/null
356-
then "SW2YcwTIb9zpOOhoPsMm" # old default value, see https://github.com/grafana/grafana/blob/0920e8bcc69f555a34462d0d2029a882272a0184/conf/defaults.ini#L334
357-
else "$__file{${cfg.grafana.secretKeyFile}}";
359+
if cfg.grafana.secretKeyFile == /dev/null then
360+
"SW2YcwTIb9zpOOhoPsMm" # old default value, see https://github.com/grafana/grafana/blob/0920e8bcc69f555a34462d0d2029a882272a0184/conf/defaults.ini#L334
361+
else
362+
"$__file{${cfg.grafana.secretKeyFile}}";
358363
};
359364
users = {
360365
allow_sign_up = false;
@@ -368,7 +373,9 @@ in
368373
# Plugins only ever change through nixpkgs here, so the 10-minute check
369374
# is pure log noise and an unnecessary call to grafana.com.
370375
analytics.check_for_plugin_updates = false;
371-
dashboards.default_home_dashboard_path = mkIf cfg.grafana.setDefaultDashboard "${pkgs.lib.sources.sourceFilesBySuffices ../grafana/dashboards/internal [".json"]}/home.json";
376+
dashboards.default_home_dashboard_path = mkIf cfg.grafana.setDefaultDashboard "${
377+
pkgs.lib.sources.sourceFilesBySuffices ../grafana/dashboards/internal [ ".json" ]
378+
}/home.json";
372379
date_formats.use_browser_locale = true;
373380
plugins.preinstall_disabled = true;
374381
unified_alerting.enabled = false;
@@ -410,9 +417,7 @@ in
410417
disableDeletion = false;
411418
allowUiUpdates = true;
412419
updateIntervalSeconds = 86400;
413-
options.path = lib.sources.sourceByRegex
414-
../grafana/dashboards
415-
[ "^[^\/]*\.json$" ];
420+
options.path = lib.sources.sourceByRegex ../grafana/dashboards [ "^[^\/]*\.json$" ];
416421
}
417422
{
418423
name = "teslamate_internal";
@@ -423,9 +428,7 @@ in
423428
disableDeletion = false;
424429
allowUiUpdates = true;
425430
updateIntervalSeconds = 86400;
426-
options.path = lib.sources.sourceFilesBySuffices
427-
../grafana/dashboards/internal
428-
[ ".json" ];
431+
options.path = lib.sources.sourceFilesBySuffices ../grafana/dashboards/internal [ ".json" ];
429432
}
430433
{
431434
name = "teslamate_reports";
@@ -436,9 +439,7 @@ in
436439
disableDeletion = false;
437440
allowUiUpdates = true;
438441
updateIntervalSeconds = 86400;
439-
options.path = lib.sources.sourceFilesBySuffices
440-
../grafana/dashboards/reports
441-
[ ".json" ];
442+
options.path = lib.sources.sourceFilesBySuffices ../grafana/dashboards/reports [ ".json" ];
442443
}
443444
];
444445
};

0 commit comments

Comments
 (0)