difftreelog
ci post results as a comment
in: master
2 files changed
flake.nixdiffbeforeafterboth39 mkForce39 mkForce40 optionals40 optionals41 optionalAttrs41 optionalAttrs42 concatMapStringsSep42 ;43 ;44 rel = system: inputs.self.legacyPackages.${system}.release;45 releaseSections = [46 {47 name = "Linux (glibc)";48 artifacts = [49 {50 label = "jrsonnet-x86_64-linux-glibc";51 drv = (rel "x86_64-linux").jrsonnet-linux-glibc;52 }53 {54 label = "jrsonnet-experimental-x86_64-linux-glibc";55 drv = (rel "x86_64-linux").jrsonnet-experimental-linux-glibc;56 }57 {58 label = "jrsonnet-aarch64-linux-glibc";59 drv = (rel "aarch64-linux").jrsonnet-linux-glibc;60 }61 {62 label = "jrsonnet-experimental-aarch64-linux-glibc";63 drv = (rel "aarch64-linux").jrsonnet-experimental-linux-glibc;64 }65 {66 label = "jrsonnet-i686-linux-glibc";67 drv = (rel "i686-linux").jrsonnet-linux-glibc;68 }69 {70 label = "jrsonnet-experimental-i686-linux-glibc";71 drv = (rel "i686-linux").jrsonnet-experimental-linux-glibc;72 }73 {74 label = "jrsonnet-armv7l-linux-glibc";75 drv = (rel "armv7l-linux").jrsonnet-linux-glibc;76 }77 {78 label = "jrsonnet-experimental-armv7l-linux-glibc";79 drv = (rel "armv7l-linux").jrsonnet-experimental-linux-glibc;80 }81 ];82 }83 {84 name = "Linux (musl)";85 artifacts = [86 {87 label = "jrsonnet-x86_64-linux-musl";88 drv = (rel "x86_64-linux").jrsonnet-linux-musl;89 }90 {91 label = "jrsonnet-experimental-x86_64-linux-musl";92 drv = (rel "x86_64-linux").jrsonnet-experimental-linux-musl;93 }94 {95 label = "jrsonnet-aarch64-linux-musl";96 drv = (rel "aarch64-linux").jrsonnet-linux-musl;97 }98 {99 label = "jrsonnet-experimental-aarch64-linux-musl";100 drv = (rel "aarch64-linux").jrsonnet-experimental-linux-musl;101 }102 ];103 }104 {105 name = "macOS";106 artifacts = [107 {108 label = "jrsonnet-aarch64-darwin";109 drv = (rel "aarch64-linux").jrsonnet-darwin;110 }111 {112 label = "jrsonnet-experimental-aarch64-darwin";113 drv = (rel "aarch64-linux").jrsonnet-experimental-darwin;114 }115 ];116 }117 {118 name = "Windows";119 artifacts = [120 {121 label = "jrsonnet-x86_64-windows";122 drv = (rel "x86_64-linux").jrsonnet-windows;123 windows = true;124 }125 {126 label = "jrsonnet-experimental-x86_64-windows";127 drv = (rel "x86_64-linux").jrsonnet-experimental-windows;128 windows = true;129 }130 ];131 }132 ];133 releaseArtifacts = builtins.concatMap (s: s.artifacts) releaseSections;43 in134 in44 inputs.flake-parts.lib.mkFlake { inherit inputs; } {135 inputs.flake-parts.lib.mkFlake { inherit inputs; } {45 imports = [136 imports = [46 inputs.shelly.flakeModule137 inputs.shelly.flakeModule47 inputs.hercules-ci-effects.flakeModule138 inputs.hercules-ci-effects.flakeModule139 ./nix/post-comment.nix48 ];140 ];49 systems = [141 systems = [50 "x86_64-linux"142 "x86_64-linux"310 ]);402 ]);311 };403 };312 };404 };313 hercules-ci.github-releases.files =405 hercules-ci.github-releases.files = map (a: {314 let315 rel = system: inputs.self.legacyPackages.${system}.release;316 bin = drv: "${drv}/bin/jrsonnet";317 exe = drv: "${drv}/bin/jrsonnet.exe";318 in319 [320 {321 label = "jrsonnet-x86_64-linux-musl";322 path = bin (rel "x86_64-linux").jrsonnet-linux-musl;323 }324 {325 label = "jrsonnet-experimental-x86_64-linux-musl";326 path = bin (rel "x86_64-linux").jrsonnet-experimental-linux-musl;327 }328 {329 label = "jrsonnet-aarch64-darwin";330 path = bin (rel "aarch64-linux").jrsonnet-darwin;331 }332 {333 label = "jrsonnet-experimental-aarch64-darwin";334 path = bin (rel "aarch64-linux").jrsonnet-experimental-darwin;335 }336 {337 label = "jrsonnet-x86_64-windows.exe";406 label = a.label + (if a.windows or false then ".exe" else "");338 path = exe (rel "x86_64-linux").jrsonnet-windows;407 path = "${a.drv}/bin/jrsonnet${if a.windows or false then ".exe" else ""}";339 }408 }) releaseArtifacts;340 {341 label = "jrsonnet-experimental-x86_64-windows.exe";342 path = exe (rel "x86_64-linux").jrsonnet-experimental-windows;343 }344345 {346 label = "jrsonnet-aarch64-linux-musl";347 path = bin (rel "aarch64-linux").jrsonnet-linux-musl;348 }349 {350 label = "jrsonnet-experimental-aarch64-linux-musl";351 path = bin (rel "aarch64-linux").jrsonnet-experimental-linux-musl;352 }353354 {355 label = "jrsonnet-x86_64-linux-glibc";356 path = bin (rel "x86_64-linux").jrsonnet-linux-glibc;357 }358 {359 label = "jrsonnet-experimental-x86_64-linux-glibc";360 path = bin (rel "x86_64-linux").jrsonnet-experimental-linux-glibc;361 }362 {363 label = "jrsonnet-aarch64-linux-glibc";364 path = bin (rel "aarch64-linux").jrsonnet-linux-glibc;365 }366 {367 label = "jrsonnet-experimental-aarch64-linux-glibc";368 path = bin (rel "aarch64-linux").jrsonnet-experimental-linux-glibc;369 }370 {371 label = "jrsonnet-i686-linux-glibc";372 path = bin (rel "i686-linux").jrsonnet-linux-glibc;373 }374 {375 label = "jrsonnet-experimental-i686-linux-glibc";376 path = bin (rel "i686-linux").jrsonnet-experimental-linux-glibc;377 }378 {379 label = "jrsonnet-armv7l-linux-glibc";380 path = bin (rel "armv7l-linux").jrsonnet-linux-glibc;381 }382 {383 label = "jrsonnet-experimental-armv7l-linux-glibc";384 path = bin (rel "armv7l-linux").jrsonnet-experimental-linux-glibc;385 }386 ];387 hercules-ci.cargo-publish = {409 hercules-ci.cargo-publish = {388 enable = true;410 enable = true;389 secretName = "crates-io";411 secretName = "crates-io";398 dayOfWeek = [ "Sat" ];420 dayOfWeek = [ "Sat" ];399 };421 };400 };422 };423 hercules-ci.post-comment = {424 enable = true;425 caches = [ "jrsonnet.cachix.org" ];426 script =427 let428 benchmarks = inputs.self.legacyPackages.x86_64-linux.benchmarks.default;429 renderSection = s: ''430 echo431 echo "### ${s.name}"432 echo433 ${concatMapStringsSep "\n" (a: ''echo "- [${a.label}]($(nixTar ${a.drv}))"'') s.artifacts}434 '';435 in436 ''437 {438 echo "## Benchmark results"439 echo440 echo "[View rendered]($(nixRender ${benchmarks}))"441 echo442 echo "## Downloads"443 ${concatMapStringsSep "\n" renderSection releaseSections}444 } > $out445 '';446 };401 herculesCI =447 herculesCI =402 { lib, config, ... }:448 { lib, config, ... }:403 {449 {nix/post-comment.nixdiffbeforeafterboth--- /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 <store-path>` — prints a signed deltarocks URL that streams the path
+ as a tar.zst, realised through the configured caches.
+ - `nixRender <store-path>` — 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"
+ '';
+ }
+ );
+ };
+ };
+}