main
1{
2 lib,
3 pkgs,
4 self,
5 skopeo,
6}: let
7 systems = [
8 "x86_64-linux"
9 "aarch64-linux"
10 ];
11
12 channels = {
13 version = "default";
14 latest = "latest";
15 };
16
17 registries = [
18 "docker.io/hpcesia/nix-act-image"
19 "git.trin.one/goi/nix-act-image"
20 ];
21
22 # git.trin.one's CDN rejects large request bodies, so its blobs go up in
23 # chunks.
24 chunkedHosts = ["git.trin.one"];
25 chunkSize = 64 * 1024 * 1024;
26 chunkMax = 8 * 1024 * 1024;
27
28 channelNames = lib.attrNames channels;
29
30 imageOf = system: self.packages.${system}.default;
31 perArchTag = channel: system: self.packages.${system}.${channels.${channel}}.imageTag;
32
33 images = lib.genAttrs systems imageOf;
34
35 perArch = lib.listToAttrs (
36 lib.concatMap (
37 channel:
38 map (system: lib.nameValuePair "${channel}/${system}" (perArchTag channel system)) systems
39 )
40 channelNames
41 );
42
43 indexTag = channel: let
44 stripped = map (system: lib.removeSuffix "-${system}" (perArchTag channel system)) systems;
45 in
46 assert lib.all (tag: tag == lib.head stripped) stripped;
47 lib.head stripped;
48
49 indexTags = lib.genAttrs channelNames indexTag;
50
51 pairs = attrs:
52 lib.concatStringsSep " " (lib.mapAttrsToList (name: value: "[${name}]=${value}") attrs);
53
54 data = ''
55 systems=(${lib.concatStringsSep " " systems})
56 channels=(${lib.concatStringsSep " " channelNames})
57 registries=(${lib.concatStringsSep " " registries})
58 chunked=(${lib.concatStringsSep " " chunkedHosts})
59 declare -A images=(${pairs images})
60 declare -A per_arch=(${pairs perArch})
61 declare -A index_tag=(${pairs indexTags})
62 '';
63
64 text = ''
65 ${data}
66
67 log() { printf 'publish: %s\n' "$*" >&2; }
68 run() {
69 if [ "''${DRY_RUN:-0}" = 1 ]; then
70 printf 'publish: would run: %s\n' "$*" >&2
71 else
72 "$@"
73 fi
74 }
75
76 workdir=$(mktemp -d)
77 trap 'rm -rf "$workdir"' EXIT
78
79 if [ "''${#chunked[@]}" -gt 0 ]; then
80 export REGCTL_CONFIG=$workdir/regctl.json
81 for host in "''${chunked[@]}"; do
82 log "chunking uploads to $host at ${toString chunkSize} bytes"
83 run regctl registry set "$host" \
84 --blob-chunk ${toString chunkSize} --blob-max ${toString chunkMax} \
85 --skip-check
86 done
87 fi
88
89 # regctl cannot read the nix: transport, so each arch is staged as an OCI
90 # layout first.
91 for system in "''${systems[@]}"; do
92 run skopeo --insecure-policy copy \
93 "nix:''${images[$system]}" "oci:$workdir/$system:image"
94 done
95
96 for registry in "''${registries[@]}"; do
97 for channel in "''${channels[@]}"; do
98 refs=()
99 for system in "''${systems[@]}"; do
100 ref="$registry:''${per_arch[$channel/$system]}"
101 log "pushing $ref"
102 run regctl image copy "ocidir://$workdir/$system:image" "$ref"
103 refs+=(--ref "$ref")
104 done
105 log "publishing $registry:''${index_tag[$channel]}"
106 run regctl index create "$registry:''${index_tag[$channel]}" "''${refs[@]}"
107 done
108 done
109 '';
110in
111 pkgs.writeShellApplication {
112 name = "publish";
113 runtimeInputs = [
114 pkgs.coreutils
115 pkgs.regclient
116 skopeo
117 ];
118 inherit text;
119 }