diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..5c914707 --- /dev/null +++ b/.envrc @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2023 Steffen Vogel +# SPDX-License-Identifier: Apache-2.0 + +use flake ./nix/ --impure + +if [ -n "$CI" -o "$USER" = "stv0g" ]; then + export SOPS_AGE_RECIPIENTS=age18xgq8h2mu3a4hkcdwqzzs0gnmjvy8aas7zy9s8m8w7tvl5uvc9cq4tn7ul,age1yubikey1qfxe3zpuh45y44lup6ymvlpf2n9ngjta935cd8ytdecyd3n4chgeglx7ysc + [ -z "$SOPS_AGE_KEY" ] && export SOPS_AGE_KEY=AGE-PLUGIN-YUBIKEY-1KDR7KQYZWL0G4QQ7C76V4 + + eval "$(sops decrypt --output-type dotenv .secrets.yaml | direnv dotenv bash /dev/stdin)" + watch_file .secrets.yaml +fi diff --git a/.github/workflows/build.yaml b/.forgejo/workflows/build.yaml similarity index 99% rename from .github/workflows/build.yaml rename to .forgejo/workflows/build.yaml index 406e4926..8670016b 100644 --- a/.github/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -57,7 +57,7 @@ jobs: run: | nix develop .#ci --command sh <<'EOF' | tee "$GITHUB_OUTPUT" # We always increment the patch version to ensure hashes of Nix derivations are updated - # by our GitHub actions tag workflow which runs nix-update + # by our Forgejo actions tag workflow which runs nix-update echo next=$(svu next) echo current=$(svu current) EOF diff --git a/.github/workflows/check.yaml b/.forgejo/workflows/check.yaml similarity index 100% rename from .github/workflows/check.yaml rename to .forgejo/workflows/check.yaml diff --git a/.github/workflows/release.yaml b/.forgejo/workflows/release.yaml similarity index 100% rename from .github/workflows/release.yaml rename to .forgejo/workflows/release.yaml diff --git a/.forgejo/workflows/website.yaml b/.forgejo/workflows/website.yaml new file mode 100644 index 00000000..bf6f42bc --- /dev/null +++ b/.forgejo/workflows/website.yaml @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2023-2025 Steffen Vogel +# SPDX-License-Identifier: Apache-2.0 + +# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json +--- +name: Build website + +on: + push: + branches: + - main + workflow_call: + pull_request: + +jobs: + build: + name: Build website + runs-on: nix + + steps: + + - name: Checkout Repository + uses: https://code.forgejo.org/actions/checkout@v5 + + - name: Load Environment + env: + SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }} + run: | + nix run nixpkgs#direnv -- allow . + nix shell nixpkgs#age nixpkgs#sops nixpkgs#direnv --command direnv export gha > "$GITHUB_ENV" + + - name: Build Website + run: | + nix build .#website + + - name: Build Website + run: nix build . + + - name: Publish Website + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: nix run .#website.publish diff --git a/.github/workflows/mirror.yaml b/.github/workflows/mirror.yaml deleted file mode 100644 index 79f42d0d..00000000 --- a/.github/workflows/mirror.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-FileCopyrightText: 2023-2025 Steffen Vogel -# SPDX-License-Identifier: Apache-2.0 - -# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json ---- -name: Mirror to Codeberg - -on: - push: - delete: - -jobs: - mirror: - uses: cunicu/.github/.github/workflows/mirror.yaml@v0.3.0 - secrets: - CODEBERG_SSH_KEY: ${{ secrets.CODEBERG_SSH_KEY }} diff --git a/.github/workflows/website.yaml b/.github/workflows/website.yaml deleted file mode 100644 index e2b012f5..00000000 --- a/.github/workflows/website.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# SPDX-FileCopyrightText: 2023-2025 Steffen Vogel -# SPDX-License-Identifier: Apache-2.0 - -# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json ---- -name: Build website - -on: - push: - tags: - - 'v*' - workflow_call: - -jobs: - build: - name: Build website - runs-on: ubuntu-24.04 - environment: release - steps: - - name: Checkout - uses: actions/checkout@v5 - - - name: Setup Nix - uses: cachix/install-nix-action@v31 - with: - nix_path: nixpkgs=channel:nixos-unstable - - - name: Setup Nix cache - uses: cachix/cachix-action@v16 - with: - name: cunicu - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - - name: Update Go modules - run: | - nix run .#scripts -- -modules-fetch -modules-file nix/modules.json - git add nix/modules.json - - - name: Build website - run: | - nix build --print-build-logs .#website - - - name: Deploy to GitHub Pages - if: startsWith(github.ref, 'refs/tags/v') && github.event_name == 'push' - uses: peaceiris/actions-gh-pages@v4 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./result/ - user_name: github-actions[bot] - user_email: 41898282+github-actions[bot]@users.noreply.github.com diff --git a/.secrets.yaml b/.secrets.yaml new file mode 100644 index 00000000..af9fb3ef --- /dev/null +++ b/.secrets.yaml @@ -0,0 +1,27 @@ +BUNNY_API_KEY: ENC[AES256_GCM,data:0arrb9UmnUGAQa7rjFVbvXf00EBvxGzVyBtacKlZXdSyrjjE,iv:JxtezI/yRpLGu8jMYJT1sU4L1haueGfOKKXKxCOB3c0=,tag:t3bwUy7VDyUIF1h084UwdA==,type:str] +BUNNY_STORAGE_API_KEY: ENC[AES256_GCM,data:f6JaJ9DEMYi4lz7lXopwG2/subyLWf3/AHL71EqZUZh7x//UBL9DTU4=,iv:7hGjgJTWSxLzC41XHD9tAVHqIMZr8KjOvSFtTH4ClrU=,tag:YqpVMIWv0tRfRPZ3gC03lA==,type:str] +sops: + age: + - recipient: age18xgq8h2mu3a4hkcdwqzzs0gnmjvy8aas7zy9s8m8w7tvl5uvc9cq4tn7ul + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzZmFXUzRmZWdtaGI3bm9U + L2tVdFpKemkwcGJvenFmaFlnbitwSnViNGlFCi9IbGQ4WElQNFpFeHdTTnR3NDky + VXNabHgzTDZmZjQvYmJxamd5Zmc4VzAKLS0tIC9BZWUzQ2dWS2hLV2FaejVpdUFL + WU9uNytISDl2R0E5NUlzQTNMbkNtdWcKK1vzE6lMUPXIzTcOFxieGqkjouOOVh9B + X1PJXQofKlNNsYOqFxT+pwN0qnC1aYAlfF5d6wuYO4OB/HNBrC5uqw== + -----END AGE ENCRYPTED FILE----- + - recipient: age1yubikey1qfxe3zpuh45y44lup6ymvlpf2n9ngjta935cd8ytdecyd3n4chgeglx7ysc + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IGQ5NktnQSBBeWFvUWxT + ZG1RNjByaTdDY2NrSGJ4MGVqR3ZSODZGdHV0WnRUSm81ZDQ3agpuT2huRmNKVG5U + YWQrUTZFZko1WlhHSTJoNUN4Q3M3dGVKTkg2NVg5WmlZCi0tLSB3SXBveGR2amZZ + TkVWNnh0VlkvRzNrY1M4LzBUUzdrNFNKMitQeGRXN2E0CrraqiG7ig16AZCuVqP0 + 74eKgSWSX8F19cZKK5Tode7jQYvyBgZdFpI1vcDiQJZNOKJDYFHYMrN9xcOSYwE5 + D+M= + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-12-17T16:00:17Z" + mac: ENC[AES256_GCM,data:jibavwJfxVoiBsCp2L9dBqNr6nnF8svxP9tNI32+8s0DF12nkMSLn8vO7U/Hpa1QeZT7ahqYqAYt521nhcek/R7ZyaeC0vFaY/+mezSCOHya3qznLQz9bQxz4BLNWvSMHQ9fE4jkTwzlWGBUNHS2JXu5RwZzgakwIt7FQdvV1OE=,iv:+A91TioKobgH/+vHPnZXoMjmqY9i7h2fVzgmjr4UeL4=,tag:39VeVKylu44bgwYh23bs0Q==,type:str] + unencrypted_suffix: _unencrypted + version: 3.11.0 diff --git a/etc/cunicu.yaml b/etc/cunicu.yaml index 3e8e3057..c02a9956 100644 --- a/etc/cunicu.yaml +++ b/etc/cunicu.yaml @@ -1,5 +1,7 @@ # SPDX-FileCopyrightText: 2023-2025 Steffen Vogel # SPDX-License-Identifier: Apache-2.0 +# yaml-language-server: $schema=https://cunicu.li/schemas/config.yaml +--- # This is an example of a simple cunicu configuration file. # For a full example please look at cunicu.advanced.yaml diff --git a/flake.lock b/flake.lock index a9bdcaae..75322b4f 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,50 @@ { "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "thumper", + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1763188655, + "narHash": "sha256-6fkf0JP1OCVxmyBZRdEVQu9wBwLyTI+idTISoijxpQE=", + "owner": "nix-community", + "repo": "fenix", + "rev": "c684a289edf43cfc5cd1bea7c1b61ab9a0fba99c", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "fenix_2": { + "inputs": { + "nixpkgs": [ + "thumper", + "naersk", + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src_2" + }, + "locked": { + "lastModified": 1752475459, + "narHash": "sha256-z6QEu4ZFuHiqdOPbYss4/Q8B0BFhacR8ts6jO/F/aOU=", + "owner": "nix-community", + "repo": "fenix", + "rev": "bf0d6f70f4c9a9cf8845f992105652173f4b617f", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" @@ -18,6 +63,43 @@ "type": "github" } }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "naersk": { + "inputs": { + "fenix": "fenix_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1752689277, + "narHash": "sha256-uldUBFkZe/E7qbvxa3mH1ItrWZyT6w1dBKJQF/3ZSsc=", + "owner": "nix-community", + "repo": "naersk", + "rev": "0e72363d0938b0208d6c646d10649164c43f4d64", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "naersk", + "type": "github" + } + }, "nix-update": { "inputs": { "flake-parts": [ @@ -73,11 +155,101 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1752077645, + "narHash": "sha256-HM791ZQtXV93xtCY+ZxG1REzhQenSQO020cu6rHtAPk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "be9e214982e20b8310878ac2baa063a961c1bdf6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "flake-parts": "flake-parts", "nix-update": "nix-update", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "thumper": "thumper" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1762860488, + "narHash": "sha256-rMfWMCOo/pPefM2We0iMBLi2kLBAnYoB9thi4qS7uk4=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "2efc80078029894eec0699f62ec8d5c1a56af763", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "rust-analyzer-src_2": { + "flake": false, + "locked": { + "lastModified": 1752428706, + "narHash": "sha256-EJcdxw3aXfP8Ex1Nm3s0awyH9egQvB2Gu+QEnJn2Sfg=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "591e3b7624be97e4443ea7b5542c191311aa141d", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "thumper": { + "inputs": { + "fenix": "fenix", + "flake-utils": "flake-utils", + "naersk": "naersk", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1764286856, + "narHash": "sha256-vhI0hLk+d73wdt8Vplmn9VSHbDzTreea8sgIlLw5Lj8=", + "owner": "stv0g", + "repo": "thumper", + "rev": "dd4fb4863497028a8069a91ef8ac91c31d7efc93", + "type": "github" + }, + "original": { + "owner": "stv0g", + "ref": "add-nix-flake", + "repo": "thumper", + "type": "github" } }, "treefmt-nix": { diff --git a/flake.nix b/flake.nix index 01352525..892bd105 100644 --- a/flake.nix +++ b/flake.nix @@ -12,10 +12,18 @@ flake-parts.follows = "flake-parts"; }; }; + thumper = { + url = "github:stv0g/thumper/add-nix-flake"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = - inputs@{ self, ... }: + inputs@{ + self, + thumper, + ... + }: inputs.flake-parts.lib.mkFlake { inherit inputs; } { flake = { nixosModules = { @@ -26,8 +34,8 @@ overlays = { default = final: prev: { cunicu = final.callPackage ./nix/cunicu.nix { src = self; }; - cunicu-website = final.callPackage ./nix/website.nix { }; - cunicu-scripts = final.callPackage ./nix/scripts.nix { }; + website = final.callPackage ./nix/website.nix { }; + scripts = final.callPackage ./nix/scripts.nix { }; gocov-merger = final.callPackage ./nix/gocov-merger.nix { }; }; }; @@ -47,7 +55,10 @@ let pkgs = import inputs.nixpkgs { inherit system; - overlays = [ self.overlays.default ]; + overlays = [ + self.overlays.default + thumper.overlays.default + ]; }; lib = inputs.nixpkgs.lib; in @@ -67,8 +78,8 @@ inherit (pkgs) cunicu gocov-merger; default = pkgs.cunicu; - website = pkgs.cunicu-website; - scripts = pkgs.cunicu-scripts; + website = pkgs.website; + scripts = pkgs.scripts; nixosTest = import ./nix/test.nix { inherit self pkgs lib; }; }; }; diff --git a/nix/shell.nix b/nix/shell.nix index 5a973dc0..d5dff753 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -8,8 +8,6 @@ act, coturn, - cunicu-website, - cunicu, evans, ginkgo, gnumake, @@ -22,35 +20,36 @@ reuse, svu, - cunicu-scripts, + cunicu, + website, + scripts, ... }: mkShell { - packages = - [ - act - evans - ginkgo - gnumake - gocov-merger - golangci-lint - goreleaser - libpcap - nix-update - reuse - svu + packages = [ + act + evans + ginkgo + gnumake + gocov-merger + golangci-lint + goreleaser + libpcap + nix-update + reuse + svu - cunicu-scripts - ] - ++ lib.optional stdenv.isLinux [ - coturn - inotify-tools - ]; + scripts + ] + ++ lib.optional stdenv.isLinux [ + coturn + inotify-tools + ]; inputsFrom = [ cunicu - cunicu-website + website ]; hardeningDisable = [ "fortify" ]; diff --git a/nix/website.nix b/nix/website.nix index 6577bff1..547bc8f3 100644 --- a/nix/website.nix +++ b/nix/website.nix @@ -1,97 +1,87 @@ # SPDX-FileCopyrightText: 2025 Steffen Vogel # SPDX-License-Identifier: Apache-2.0 + +let + inherit (builtins) + toFile + readFile + toJSON + fromJSON + ; +in { lib, stdenv, - stdenvNoCC, - yarn-berry, - cacert, - nodejs-slim, + yarn-berry_4, + writeShellApplication, + thumper, + nodejs, + cunicu, - cunicu-scripts, + scripts, # Options - goModules ? builtins.fromJSON (builtins.readFile ./modules.json), + goModules ? fromJSON (readFile ./modules.json), ... }: let - yarnOfflineCache = stdenvNoCC.mkDerivation { - pname = "yarn-deps"; - - inherit (cunicu) version src; - - nativeBuildInputs = [ yarn-berry ]; - - dontInstall = true; - - NODE_EXTRA_CA_CERTS = "${cacert}/etc/ssl/certs/ca-bundle.crt"; - YARN_ENABLE_TELEMETRY = "0"; - - buildPhase = '' - runHook preBuild - - cd website - - export HOME=$(mktemp -d) - - YARN_CACHE="$(yarn config get cacheFolder)" - yarn install --immutable --mode skip-build - - mkdir -p $out - cp -r $YARN_CACHE/* $out/ - - runHook postBuild - ''; - - outputHash = "sha256-0+km0FIvC08noQOVOlrqf79Ocuq8p5DyURZFPGEaLWs="; - outputHashMode = "recursive"; - }; + yarn-berry = yarn-berry_4; in stdenv.mkDerivation (finalAttrs: { - pname = "cunicu-docs"; - inherit (cunicu) version src; - inherit yarnOfflineCache; + pname = "cunicu-website"; + inherit (cunicu) version; + + src = "${cunicu.src}/website"; nativeBuildInputs = [ - nodejs-slim + nodejs yarn-berry + yarn-berry.yarnBerryConfigHook ]; - NODE_ENV = "production"; - YARN_ENABLE_TELEMETRY = "0"; + offlineCache = yarn-berry.fetchYarnBerryDeps { + inherit (finalAttrs) src; + hash = "sha256-B8rsgN8XR8JxR9yPfuQohfOMOOT40lABHBrCOLAILQA="; + }; + + patchPhase = '' + runHook prePatch + + rm -rf docs + cp -r ${../docs} docs + + rm -rf openapi + cp -r ${../openapi} openapi + + rm -rf static/schemas + cp -r ${../openapi} static/schemas + + chmod -R u+w docs + + sed -i 's|!!raw-loader!../../../../etc|!!raw-loader!${../etc}|' src/components/ExampleConfig/index.js + + runHook postPatch + ''; buildPhase = '' runHook preBuild - shopt -s globstar - - cd website - - export HOME=$(mktemp -d) - export npm_config_nodedir=${nodejs-slim} - - mkdir -p ~/.yarn/berry - ln -s $yarnOfflineCache ~/.yarn/berry/cache - echo "== Generate redirects" - ${cunicu-scripts}/bin/vanity_redirects -static-dir static -modules-file ${builtins.toFile "modules.json" (builtins.toJSON goModules)} + ${scripts}/bin/vanity_redirects -static-dir static -modules-file ${toFile "modules.json" (toJSON goModules)} echo "== Generate usage docs" ${lib.getExe cunicu} docs --with-frontmatter --output-dir docs/usage echo "-- Fix generated docs" + shopt -s globstar substituteInPlace docs/usage/md/**/*.md \ --replace-quiet '<' '\<' \ --replace-quiet '{' '\{' - echo "== Yarn install" - yarn install --immutable --immutable-cache - - patchShebangs ~/node_modules - echo "== Yarn build" + ls -l yarn build runHook postBuild @@ -100,8 +90,25 @@ stdenv.mkDerivation (finalAttrs: { installPhase = '' runHook preInstall - cp -r build $out + mv build $out runHook postInstall ''; + + passthru.publish = + let + storageZone = "stv0g-cunicu-li"; + pullZone = 4878258; + in + writeShellApplication { + name = "publish"; + runtimeInputs = [ + thumper + ]; + text = '' + out=$(nix build --print-out-paths .#website) + thumper --api-key "$BUNNY_STORAGE_API_KEY" sync "$out" ${storageZone} + thumper --api-key "$BUNNY_API_KEY" purge-zone ${builtins.toString pullZone} + ''; + }; }) diff --git a/etc/cunicu.schema.yaml b/openapi/config.yaml similarity index 100% rename from etc/cunicu.schema.yaml rename to openapi/config.yaml diff --git a/website/openapi/openapi.yaml b/openapi/openapi.yaml similarity index 100% rename from website/openapi/openapi.yaml rename to openapi/openapi.yaml diff --git a/website/openapi b/website/openapi new file mode 120000 index 00000000..307b83e0 --- /dev/null +++ b/website/openapi @@ -0,0 +1 @@ +../openapi \ No newline at end of file diff --git a/website/openapi/config.yaml b/website/openapi/config.yaml deleted file mode 120000 index a41d7783..00000000 --- a/website/openapi/config.yaml +++ /dev/null @@ -1 +0,0 @@ -../../etc/cunicu.schema.yaml \ No newline at end of file