From e468723051e73ff771fd7fee30f4fbbd672ddcd8 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Thu, 07 May 2026 04:40:45 +0000 Subject: [PATCH] ci: post results as a comment --- --- a/flake.nix +++ b/flake.nix @@ -39,12 +39,104 @@ mkForce optionals optionalAttrs + concatMapStringsSep ; + rel = system: inputs.self.legacyPackages.${system}.release; + releaseSections = [ + { + name = "Linux (glibc)"; + artifacts = [ + { + label = "jrsonnet-x86_64-linux-glibc"; + drv = (rel "x86_64-linux").jrsonnet-linux-glibc; + } + { + label = "jrsonnet-experimental-x86_64-linux-glibc"; + drv = (rel "x86_64-linux").jrsonnet-experimental-linux-glibc; + } + { + label = "jrsonnet-aarch64-linux-glibc"; + drv = (rel "aarch64-linux").jrsonnet-linux-glibc; + } + { + label = "jrsonnet-experimental-aarch64-linux-glibc"; + drv = (rel "aarch64-linux").jrsonnet-experimental-linux-glibc; + } + { + label = "jrsonnet-i686-linux-glibc"; + drv = (rel "i686-linux").jrsonnet-linux-glibc; + } + { + label = "jrsonnet-experimental-i686-linux-glibc"; + drv = (rel "i686-linux").jrsonnet-experimental-linux-glibc; + } + { + label = "jrsonnet-armv7l-linux-glibc"; + drv = (rel "armv7l-linux").jrsonnet-linux-glibc; + } + { + label = "jrsonnet-experimental-armv7l-linux-glibc"; + drv = (rel "armv7l-linux").jrsonnet-experimental-linux-glibc; + } + ]; + } + { + name = "Linux (musl)"; + artifacts = [ + { + label = "jrsonnet-x86_64-linux-musl"; + drv = (rel "x86_64-linux").jrsonnet-linux-musl; + } + { + label = "jrsonnet-experimental-x86_64-linux-musl"; + drv = (rel "x86_64-linux").jrsonnet-experimental-linux-musl; + } + { + label = "jrsonnet-aarch64-linux-musl"; + drv = (rel "aarch64-linux").jrsonnet-linux-musl; + } + { + label = "jrsonnet-experimental-aarch64-linux-musl"; + drv = (rel "aarch64-linux").jrsonnet-experimental-linux-musl; + } + ]; + } + { + name = "macOS"; + artifacts = [ + { + label = "jrsonnet-aarch64-darwin"; + drv = (rel "aarch64-linux").jrsonnet-darwin; + } + { + label = "jrsonnet-experimental-aarch64-darwin"; + drv = (rel "aarch64-linux").jrsonnet-experimental-darwin; + } + ]; + } + { + name = "Windows"; + artifacts = [ + { + label = "jrsonnet-x86_64-windows"; + drv = (rel "x86_64-linux").jrsonnet-windows; + windows = true; + } + { + label = "jrsonnet-experimental-x86_64-windows"; + drv = (rel "x86_64-linux").jrsonnet-experimental-windows; + windows = true; + } + ]; + } + ]; + releaseArtifacts = builtins.concatMap (s: s.artifacts) releaseSections; in inputs.flake-parts.lib.mkFlake { inherit inputs; } { imports = [ inputs.shelly.flakeModule inputs.hercules-ci-effects.flakeModule + ./nix/post-comment.nix ]; systems = [ "x86_64-linux" @@ -310,80 +402,10 @@ ]); }; }; - hercules-ci.github-releases.files = - let - rel = system: inputs.self.legacyPackages.${system}.release; - bin = drv: "${drv}/bin/jrsonnet"; - exe = drv: "${drv}/bin/jrsonnet.exe"; - in - [ - { - label = "jrsonnet-x86_64-linux-musl"; - path = bin (rel "x86_64-linux").jrsonnet-linux-musl; - } - { - label = "jrsonnet-experimental-x86_64-linux-musl"; - path = bin (rel "x86_64-linux").jrsonnet-experimental-linux-musl; - } - { - label = "jrsonnet-aarch64-darwin"; - path = bin (rel "aarch64-linux").jrsonnet-darwin; - } - { - label = "jrsonnet-experimental-aarch64-darwin"; - path = bin (rel "aarch64-linux").jrsonnet-experimental-darwin; - } - { - label = "jrsonnet-x86_64-windows.exe"; - path = exe (rel "x86_64-linux").jrsonnet-windows; - } - { - label = "jrsonnet-experimental-x86_64-windows.exe"; - path = exe (rel "x86_64-linux").jrsonnet-experimental-windows; - } - - { - label = "jrsonnet-aarch64-linux-musl"; - path = bin (rel "aarch64-linux").jrsonnet-linux-musl; - } - { - label = "jrsonnet-experimental-aarch64-linux-musl"; - path = bin (rel "aarch64-linux").jrsonnet-experimental-linux-musl; - } - - { - label = "jrsonnet-x86_64-linux-glibc"; - path = bin (rel "x86_64-linux").jrsonnet-linux-glibc; - } - { - label = "jrsonnet-experimental-x86_64-linux-glibc"; - path = bin (rel "x86_64-linux").jrsonnet-experimental-linux-glibc; - } - { - label = "jrsonnet-aarch64-linux-glibc"; - path = bin (rel "aarch64-linux").jrsonnet-linux-glibc; - } - { - label = "jrsonnet-experimental-aarch64-linux-glibc"; - path = bin (rel "aarch64-linux").jrsonnet-experimental-linux-glibc; - } - { - label = "jrsonnet-i686-linux-glibc"; - path = bin (rel "i686-linux").jrsonnet-linux-glibc; - } - { - label = "jrsonnet-experimental-i686-linux-glibc"; - path = bin (rel "i686-linux").jrsonnet-experimental-linux-glibc; - } - { - label = "jrsonnet-armv7l-linux-glibc"; - path = bin (rel "armv7l-linux").jrsonnet-linux-glibc; - } - { - label = "jrsonnet-experimental-armv7l-linux-glibc"; - path = bin (rel "armv7l-linux").jrsonnet-experimental-linux-glibc; - } - ]; + hercules-ci.github-releases.files = map (a: { + label = a.label + (if a.windows or false then ".exe" else ""); + path = "${a.drv}/bin/jrsonnet${if a.windows or false then ".exe" else ""}"; + }) releaseArtifacts; hercules-ci.cargo-publish = { enable = true; secretName = "crates-io"; @@ -398,6 +420,30 @@ dayOfWeek = [ "Sat" ]; }; }; + hercules-ci.post-comment = { + enable = true; + caches = [ "jrsonnet.cachix.org" ]; + script = + let + benchmarks = inputs.self.legacyPackages.x86_64-linux.benchmarks.default; + renderSection = s: '' + echo + echo "### ${s.name}" + echo + ${concatMapStringsSep "\n" (a: ''echo "- [${a.label}]($(nixTar ${a.drv}))"'') s.artifacts} + ''; + in + '' + { + echo "## Benchmark results" + echo + echo "[View rendered]($(nixRender ${benchmarks}))" + echo + echo "## Downloads" + ${concatMapStringsSep "\n" renderSection releaseSections} + } > $out + ''; + }; herculesCI = { lib, config, ... }: { --- /dev/null +++ b/nix/post-comment.nix @@ -0,0 +1,150 @@ +{ + config, + lib, + withSystem, + ... +}: +let + inherit (lib) + mkOption + mkIf + types + concatStringsSep + ; + cfg = config.hercules-ci.post-comment; +in +{ + options.hercules-ci.post-comment = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to post a GitHub commit comment for every commit Hercules CI runs on. + ''; + }; + script = mkOption { + type = types.lines; + description = '' + Bash snippet that writes the comment body to `$out`. Runs as part of the effect + (after secrets are loaded), so the helpers below are in scope: + + - `nixTar ` — prints a signed deltarocks URL that streams the path + as a tar.zst, realised through the configured caches. + - `nixRender ` — prints a signed deltarocks URL that renders the + path's AsciiDoc content as HTML. + ''; + example = lib.literalExpression '' + ''' + { + echo "Render: $(nixRender ''${benchmarks})" + echo "Tar: $(nixTar ''${binary})" + } > $out + ''' + ''; + }; + system = mkOption { + type = types.str; + default = "x86_64-linux"; + description = '' + System on which the effect runs. + ''; + }; + baseUrl = mkOption { + type = types.str; + default = "https://delta.rocks"; + description = '' + Base URL of the deltarocks signing service. + ''; + }; + caches = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "jrsonnet.cachix.org" ]; + description = '' + Cache hosts the signing service should use as substituters when realising the + signed store path. + ''; + }; + signSecret = mkOption { + type = types.str; + default = "deltarocks-nix-sign"; + description = '' + Name of the Hercules CI agent secret that holds the deltarocks signing key. + Its `data` must have a field named `ogSecret`. + ''; + }; + }; + + config = mkIf cfg.enable { + herculesCI = + { config, ... }: + { + onPush.default.outputs.effects.post-comment = withSystem cfg.system ( + { pkgs, hci-effects, ... }: + hci-effects.mkEffect { + name = "post-comment"; + inputs = [ pkgs.openssl ]; + secretsMap = { + token = { + type = "GitToken"; + }; + ogSecret = cfg.signSecret; + }; + owner = config.repo.owner; + repoName = config.repo.name; + rev = config.repo.rev; + baseUrl = cfg.baseUrl; + caches = concatStringsSep " " cfg.caches; + effectScript = '' + set -euo pipefail + + token=$(readSecretString token .token) + ogSecret=$(readSecretString ogSecret .ogSecret) + read -ra cacheArr <<<"$caches" + if [[ ''${#cacheArr[@]} -eq 0 ]]; then + echo "hercules-ci.post-comment: at least one cache host is required" >&2 + exit 1 + fi + sortedCaches=$(printf '%s\n' "''${cacheArr[@]}" | LC_ALL=C sort | paste -sd,) + + _hmacHex() { + printf '%s' "$1" \ + | openssl dgst -sha256 -hmac "$ogSecret" -hex \ + | sed 's/^.*= //' + } + + _uri() { + jq -nj --arg s "$1" '$s|@uri' + } + + _signedUrl() { + local endpoint=$1 drv=$2 + local sig + sig=$(_hmacHex "''${endpoint}:''${sortedCaches}:''${drv}") + local query="" + for c in "''${cacheArr[@]}"; do + query+="cache=$(_uri "$c")&" + done + query+="drv=$(_uri "$drv")&sig=''${sig}" + printf '%s/%s?%s' "$baseUrl" "$endpoint" "$query" + } + + nixTar() { _signedUrl nixTar "$1"; } + nixRender() { _signedUrl nixRender "$1"; } + + out=$(mktemp) + ${cfg.script} + + jq -n --rawfile content "$out" '{body: $content}' \ + | curl -fsSL -X POST \ + -H "Authorization: Bearer $token" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + --data-binary @- \ + "https://api.github.com/repos/$owner/$repoName/commits/$rev/comments" + ''; + } + ); + }; + }; +} -- gitstuff