From 8f04e6eee9c53b8d0003ba1eee9042d488b0b8ee Mon Sep 17 00:00:00 2001 From: div72 Date: Fri, 16 Aug 2024 17:25:42 +0300 Subject: [PATCH 1/3] build.nix: fix config Updates the default value for the port and changes the WEBHOOK_URL to not contain the api key as the config.py does it now. --- flake.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index c1b5f2b..056628e 100644 --- a/flake.nix +++ b/flake.nix @@ -113,12 +113,12 @@ }; PORT = lib.mkOption { type = lib.types.port; - default = 48879; - description = "Port to listen the webhook on. Defaults to 48879."; + default = 51413; + description = "Port to listen the webhook on. Defaults to 51413."; }; WEBHOOK_URL = lib.mkOption { type = lib.types.str; - default = "https://${cfg.hostname}/${cfg.settings.TELEGRAM_API_KEY}"; + default = "https://${cfg.hostname}"; description = "The URL for the webhook. Defaults to hostname + api key."; }; BACKGROUND_COLORS = lib.mkOption { From 3b2c7a6eb0751c5dfcf9ae8ab603752b32b0f15b Mon Sep 17 00:00:00 2001 From: div72 Date: Fri, 16 Aug 2024 17:31:39 +0300 Subject: [PATCH 2/3] lint: add script for config checks --- lint/lint_config.py | 126 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 lint/lint_config.py diff --git a/lint/lint_config.py b/lint/lint_config.py new file mode 100644 index 0000000..04654dd --- /dev/null +++ b/lint/lint_config.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 + +"""Ensures config defined config parameters in config.py are identical to the ones in the NixOS module.""" + +import ast +import json +import subprocess +import sys +from dataclasses import dataclass +from pathlib import Path +from typing import Any + + +class NoDefault: + def __eq__(self, o: Any) -> bool: + return isinstance(o, NoDefault) + + def __repr__(self) -> str: + return "@NO_DEFAULT@" + + +class ComplicatedDefault: + def __eq__(self, o: Any) -> bool: + return isinstance(o, ComplicatedDefault) + + def __repr__(self) -> str: + return "@COMPLICATED_DEFAULT@" + + +@dataclass(eq=True, order=True) +class ConfigKey: + key: str + default: Any + + +def parse_config_keys(module: ast.Module) -> list[ConfigKey]: + keys: list[ConfigKey] = [] + + for child in ast.walk(module): + if isinstance(child, ast.Subscript): + # config[key] + if not isinstance(child.value, ast.Name) or child.value.id != "config": + continue + + assert isinstance(child.slice, ast.Constant) and isinstance(child.slice.value, str) + keys.append(ConfigKey(child.slice.value, NoDefault())) + elif isinstance(child, ast.Call): + # config.get(key, ...) + if not isinstance(child.func, ast.Attribute) or child.func.attr != "get" or not isinstance(child.func.value, ast.Name) or child.func.value.id != "config": + continue + + assert isinstance(child.args[0], ast.Constant) and isinstance(child.args[0].value, str) + if len(child.args) == 2: + # We have a default if we're here. + keys.append(ConfigKey(child.args[0].value, ast.literal_eval(child.args[1]))) + else: + keys.append(ConfigKey(child.args[0].value, NoDefault())) + + return keys + + +def parse_nix_keys(flake_path: Path) -> list[ConfigKey]: + obj: dict[str, Any] = json.loads(subprocess.check_output([ + "nix", "eval", "--impure", "--json", "--expr", f'builtins.getFlake "{flake_path}"', "--apply", + """flake: ( + builtins.mapAttrs (name: value: + let eval = builtins.tryEval (value.default or { nodefault = true; }); + in if eval.success then eval.value else { complicateddefault = true; } + ) ( + builtins.elemAt ( + flake.outputs.nixosModules { lib = flake.inputs.nixpkgs.lib; config = throw "not full eval"; pkgs = {}; }).options.services.hu-cafeteria-bot.settings.type.getSubModules + 0 + ).options + )""" + ], encoding="utf-8")) + + keys: list[ConfigKey] = [] + for key, default in obj.items(): + if isinstance(default, dict): + if default.get("nodefault"): + keys.append(ConfigKey(key, NoDefault())) + continue + elif default.get("complicateddefault"): + keys.append(ConfigKey(key, ComplicatedDefault())) + continue + keys.append(ConfigKey(key, default)) + + return keys + + +def main() -> bool: + config_path: Path = Path(__file__).parents[1] / "src" / "config.py" + + config_keys: list[ConfigKey] = parse_config_keys(ast.parse(config_path.read_text())) + config_keys.sort(reverse=True) + config_key_set: set[str] = set(map(lambda c: c.key, config_keys)) + + nix_keys: list[ConfigKey] = parse_nix_keys(Path(__file__).parents[1]) + nix_keys.sort(reverse=True) + nix_key_set: set[str] = set(map(lambda c: c.key, nix_keys)) + + if config_key_set != nix_key_set: + print("ERROR: Either config and nix keys are not same.") + + if nix_key_set - config_key_set: + print(" Following keys are present in flake.nix but not in config.py:") + print(" - ", end="") + print(*(nix_key_set - config_key_set), sep=', ') + + if config_key_set - nix_key_set: + print(" Following keys are present in config.py but not in flake.nix:") + print(" - ", end="") + print(*(config_key_set - nix_key_set), sep=', ') + + return False + + for (config_key, nix_key) in zip(config_keys, nix_keys): + if config_key.default != nix_key.default and nix_key.default != ComplicatedDefault(): + print("ERROR: Default mismatch:", "key:", config_key.key, "config.py:", config_key.default, "flake.nix:", nix_key.default) + return False + + return True + + +if __name__ == "__main__": + sys.exit(int(not main())) From 901bfde7f54e3ed5de51c74c91fc8f2f65d1b575 Mon Sep 17 00:00:00 2001 From: div72 Date: Fri, 16 Aug 2024 17:31:55 +0300 Subject: [PATCH 3/3] workflows: add lint workflow --- .github/workflows/lint.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..76305bd --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,26 @@ +--- +name: Lint + +on: + pull_request: + push: + branches: + - 'master' + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v25 + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: run lint scripts + run: | + for f in lint/lint_*.py; do + if ! python $f; then + echo "errors from $f ^^^^" + exit 1 + fi + done