A fork of attic a self-hostable Nix Binary Cache server
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #173 from zhaofengli/multi-arch-image

Build and push multi-arch images

authored by

Zhaofeng Li and committed by
GitHub
54bbd7d5 aec90814

+327 -112
+60
.ci/build-and-push-images.sh
··· 1 + #!/usr/bin/env bash 2 + set -euo pipefail 3 + 4 + if [[ "$#" -lt "2" ]]; then 5 + >&2 echo "Usage: $0 <image name> <tag1> ..." 6 + >&2 echo "Example: $0 ghcr.io/zhaofengli/attic main abcd123" 7 + exit 1 8 + fi 9 + 10 + cleanup() { 11 + if [[ -f "${manifest_spec}" ]]; then 12 + rm "${manifest_spec}" 13 + fi 14 + } 15 + trap cleanup EXIT 16 + 17 + image_name="$1" 18 + tags=("${@:2}") 19 + 20 + manifest_spec="$(mktemp -t attic-manifest-spec.XXXXXXXXXX)" 21 + 22 + declare -a digests 23 + 24 + emit_header() { 25 + echo "image: ${image_name}" 26 + echo "tags:" 27 + for tag in "${tags[@]}"; do 28 + echo "- ${tag}" 29 + done 30 + echo "manifests:" 31 + } 32 + 33 + push_digest() { 34 + source_image="docker-archive:$1" 35 + digest="$(skopeo inspect "${source_image}" | jq -r .Digest)" 36 + target_image="docker://${image_name}@${digest}" 37 + 38 + >&2 echo "${source_image} ▸ ${target_image}" 39 + >&2 skopeo copy --insecure-policy "${source_image}" "${target_image}" 40 + 41 + echo -n "- " 42 + skopeo inspect "${source_image}" | \ 43 + jq '{platform: {architecture: .Architecture, os: .Os}, image: ($image_name + "@" + .Digest)}' \ 44 + --arg image_name "${image_name}" 45 + } 46 + 47 + >>"${manifest_spec}" emit_header 48 + 49 + nix build .#attic-server-image .#attic-server-image-aarch64 -L --print-out-paths | \ 50 + while read -r output; do 51 + >>"${manifest_spec}" push_digest "${output}" 52 + done 53 + 54 + >&2 echo "----------" 55 + >&2 echo "Generated manifest-tool spec:" 56 + >&2 echo "----------" 57 + cat "${manifest_spec}" 58 + >&2 echo "----------" 59 + 60 + manifest-tool push from-spec "${manifest_spec}"
+4 -4
.github/workflows/book.yml
··· 16 16 if: github.repository == 'zhaofengli/attic' 17 17 18 18 steps: 19 - - uses: actions/checkout@v4.1.1 19 + - uses: actions/checkout@v4.1.7 20 20 21 - - uses: DeterminateSystems/nix-installer-action@v9 21 + - uses: DeterminateSystems/nix-installer-action@v14 22 22 continue-on-error: true # Self-hosted runners already have Nix installed 23 23 24 24 - name: Install Attic ··· 40 40 cp --recursive --dereference --no-preserve=mode,ownership result public 41 41 42 42 - name: Upload book artifact 43 - uses: actions/upload-pages-artifact@v2.0.0 43 + uses: actions/upload-pages-artifact@v3.0.1 44 44 with: 45 45 path: public 46 46 47 47 - name: Deploy book 48 - uses: actions/deploy-pages@v3.0.1 48 + uses: actions/deploy-pages@v4.0.5 49 49 50 50 # TODO: Just take a diff of the list of store paths, also abstract all of this out 51 51 - name: Push build artifacts
+80 -24
.github/workflows/build.yml
··· 4 4 push: 5 5 env: 6 6 REGISTRY: ghcr.io 7 - IMAGE_NAME: ${{ github.repository }} 7 + IMAGE_NAME: ghcr.io/${{ github.repository }} 8 8 jobs: 9 9 tests: 10 10 strategy: ··· 17 17 - "2.24" 18 18 - "default" 19 19 runs-on: ${{ matrix.os }} 20 - permissions: 21 - contents: read 22 - packages: write 23 20 steps: 24 - - uses: actions/checkout@v4.1.1 21 + - uses: actions/checkout@v4.1.7 25 22 26 23 - name: Install current Bash on macOS 27 24 if: runner.os == 'macOS' 28 25 run: | 29 26 command -v brew && brew install bash || true 30 27 31 - - uses: DeterminateSystems/nix-installer-action@v9 28 + - uses: DeterminateSystems/nix-installer-action@v14 32 29 continue-on-error: true # Self-hosted runners already have Nix installed 33 30 34 31 - name: Install Attic ··· 38 35 fi 39 36 40 37 - name: Configure Attic 38 + continue-on-error: true 41 39 run: | 42 40 : "${ATTIC_SERVER:=https://staging.attic.rs/}" 43 41 : "${ATTIC_CACHE:=attic-ci}" ··· 75 73 .#internalMatrix."$system".\"${{ matrix.nix }}\".cargoArtifacts \ 76 74 | xargs attic push "ci:$ATTIC_CACHE" 77 75 fi 76 + 77 + image: 78 + runs-on: ubuntu-latest 79 + if: github.event_name == 'push' 80 + needs: 81 + - tests 82 + permissions: 83 + contents: read 84 + packages: write 85 + steps: 86 + - uses: actions/checkout@v4.1.7 87 + 88 + - name: Install current Bash on macOS 89 + if: runner.os == 'macOS' 90 + run: | 91 + command -v brew && brew install bash || true 92 + 93 + - uses: DeterminateSystems/nix-installer-action@v14 94 + continue-on-error: true # Self-hosted runners already have Nix installed 95 + 96 + - name: Install Attic 97 + run: | 98 + if ! command -v attic &> /dev/null; then 99 + ./.github/install-attic-ci.sh 100 + fi 101 + 102 + - name: Configure Attic 103 + continue-on-error: true 104 + run: | 105 + : "${ATTIC_SERVER:=https://staging.attic.rs/}" 106 + : "${ATTIC_CACHE:=attic-ci}" 107 + echo ATTIC_CACHE=$ATTIC_CACHE >>$GITHUB_ENV 108 + export PATH=$HOME/.nix-profile/bin:$PATH # FIXME 109 + attic login --set-default ci "$ATTIC_SERVER" "$ATTIC_TOKEN" 110 + attic use "$ATTIC_CACHE" 111 + env: 112 + ATTIC_SERVER: ${{ secrets.ATTIC_SERVER }} 113 + ATTIC_CACHE: ${{ secrets.ATTIC_CACHE }} 114 + ATTIC_TOKEN: ${{ secrets.ATTIC_TOKEN }} 115 + 116 + - name: Cache dev shell 117 + run: | 118 + .ci/cache-shell.sh 119 + system=$(nix-instantiate --eval -E 'builtins.currentSystem') 120 + echo system=$system >>$GITHUB_ENV 121 + 78 122 - name: Log in to the Container registry 79 - uses: docker/login-action@v3.0.0 80 - if: runner.os == 'Linux' && github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) 123 + uses: docker/login-action@v3.3.0 81 124 with: 82 125 registry: ${{ env.REGISTRY }} 83 126 username: ${{ github.actor }} 84 127 password: ${{ secrets.GITHUB_TOKEN }} 85 128 86 - - name: Push build container image 87 - if: runner.os == 'Linux' && github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) 129 + - name: Build and push container images 88 130 continue-on-error: true 89 131 run: | 90 - IMAGE_ID=ghcr.io/${IMAGE_NAME} 91 - TARBALL=$(nix build --json .#attic-server-image | jq -r '.[].outputs.out') 92 - BRANCH=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') 93 - TAG="${{ github.sha }}" 94 - [[ "${{ github.ref }}" == "refs/tags/"* ]] && TAG=$(echo $BRANCH | sed -e 's/^v//') 95 - docker load < ${TARBALL} 96 - echo IMAGE_ID=$IMAGE_ID 97 - echo TAG=$TAG 98 - docker tag attic-server:main "${IMAGE_ID}:${TAG}" 99 - docker push ${IMAGE_ID}:${TAG} 100 - if [ "$BRANCH" == "main" ]; then 101 - TAG="latest" 102 - docker tag attic-server:main "${IMAGE_ID}:${TAG}" 103 - docker push ${IMAGE_ID}:${TAG} 132 + declare -a tags 133 + tags+=("${{ github.sha }}") 134 + 135 + branch=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') 136 + if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then 137 + tags+=("$(echo $branch | sed -e 's/^v//')") 138 + else 139 + tags+=("${branch}") 140 + fi 141 + 142 + if [ "$branch" == "${{ github.event.repository.default_branch }}" ]; then 143 + tags+=("latest") 144 + fi 145 + 146 + >&2 echo "Image: ${IMAGE_NAME}" 147 + >&2 echo "Tags: ${tags[@]}" 148 + 149 + .ci/run just ci-build-and-push-images "${IMAGE_NAME}" "${tags[@]}" 150 + 151 + # TODO: Just take a diff of the list of store paths, also abstract all of this out 152 + - name: Push build artifacts 153 + run: | 154 + export PATH=$HOME/.nix-profile/bin:$PATH # FIXME 155 + if [ -n "$ATTIC_TOKEN" ]; then 156 + nix build --no-link --print-out-paths -L \ 157 + .#attic-server-image \ 158 + .#attic-server-image-aarch64 \ 159 + | xargs attic push "ci:$ATTIC_CACHE" 104 160 fi
+7 -2
.github/workflows/lint.yml
··· 10 10 runs-on: ubuntu-latest 11 11 12 12 steps: 13 - - uses: actions/checkout@v4.1.1 13 + - uses: actions/checkout@v4.1.7 14 + 15 + - name: Install current Bash on macOS 16 + if: runner.os == 'macOS' 17 + run: | 18 + command -v brew && brew install bash || true 14 19 15 - - uses: DeterminateSystems/nix-installer-action@v9 20 + - uses: DeterminateSystems/nix-installer-action@v14 16 21 continue-on-error: true # Self-hosted runners already have Nix installed 17 22 18 23 - name: Install Attic
+32 -9
crane.nix
··· 7 7 8 8 { stdenv 9 9 , lib 10 + , buildPackages 10 11 , craneLib 11 - , rustPlatform 12 + , rust 12 13 , runCommand 13 14 , writeReferencesToFile 14 15 , pkg-config ··· 19 20 , boost 20 21 , darwin 21 22 , libiconv 23 + 24 + , extraPackageArgs ? {} 22 25 }: 23 26 24 27 let 25 28 version = "0.1.0"; 26 29 27 - ignoredPaths = [ ".github" "target" "book" "nixos" "integration-tests" ]; 30 + ignoredPaths = [ 31 + ".ci" 32 + ".github" 33 + "book" 34 + "flake" 35 + "integration-tests" 36 + "nixos" 37 + "target" 38 + ]; 28 39 29 40 src = lib.cleanSourceWith { 30 41 filter = name: type: !(type == "directory" && builtins.elem (baseNameOf name) ignoredPaths); ··· 43 54 libiconv 44 55 ]; 45 56 46 - cargoArtifacts = craneLib.buildDepsOnly { 57 + crossArgs = let 58 + rustTargetSpec = rust.toRustTargetSpec stdenv.hostPlatform; 59 + rustTargetSpecEnv = lib.toUpper (builtins.replaceStrings [ "-" ] [ "_" ] rustTargetSpec); 60 + in lib.optionalAttrs (stdenv.hostPlatform != stdenv.buildPlatform) { 61 + depsBuildBuild = [ buildPackages.stdenv.cc ]; 62 + 63 + CARGO_BUILD_TARGET = rustTargetSpec; 64 + "CARGO_TARGET_${rustTargetSpecEnv}_LINKER" = "${stdenv.cc.targetPrefix}cc"; 65 + }; 66 + 67 + extraArgs = crossArgs // extraPackageArgs; 68 + 69 + cargoArtifacts = craneLib.buildDepsOnly ({ 47 70 pname = "attic"; 48 71 inherit src version nativeBuildInputs buildInputs; 49 72 ··· 54 77 # With `use-zstd`, the cargo artifacts are archived in a `tar.zstd`. This is 55 78 # actually set if you use `buildPackage` without passing `cargoArtifacts`. 56 79 installCargoArtifactsMode = "use-zstd"; 57 - }; 80 + } // extraArgs); 58 81 59 82 mkAttic = args: craneLib.buildPackage ({ 60 83 pname = "attic"; ··· 86 109 maintainers = with maintainers; [ zhaofengli ]; 87 110 platforms = platforms.linux ++ platforms.darwin; 88 111 }; 89 - } // args); 112 + } // args // extraArgs); 90 113 91 114 attic = mkAttic { 92 115 cargoExtraArgs = "-p attic-client -p attic-server"; ··· 106 129 # 107 130 # We don't enable fat LTO in the default `attic` package since it 108 131 # dramatically increases build time. 109 - attic-server = craneLib.buildPackage { 132 + attic-server = craneLib.buildPackage ({ 110 133 pname = "attic-server"; 111 134 112 135 # We don't pull in the common cargoArtifacts because the feature flags ··· 120 143 121 144 CARGO_PROFILE_RELEASE_LTO = "fat"; 122 145 CARGO_PROFILE_RELEASE_CODEGEN_UNITS = "1"; 123 - }; 146 + } // extraArgs); 124 147 125 148 # Attic interacts with Nix directly and its tests require trusted-user access 126 149 # to nix-daemon to import NARs, which is not possible in the build sandbox. 127 150 # In the CI pipeline, we build the test executable inside the sandbox, then 128 151 # run it outside. 129 - attic-tests = craneLib.mkCargoDerivation { 152 + attic-tests = craneLib.mkCargoDerivation ({ 130 153 pname = "attic-tests"; 131 154 132 155 inherit src version buildInputs cargoArtifacts; ··· 151 174 152 175 runHook postInstall 153 176 ''; 154 - }; 177 + } // extraArgs); 155 178 in { 156 179 inherit cargoArtifacts attic attic-client attic-server attic-tests; 157 180 }
+3 -1
flake/devshells.nix
··· 35 35 cfg = config.attic.devshell; 36 36 in { 37 37 attic.devshell.packageSets = with pkgs; { 38 - rustc = [ 38 + rustc = lib.optionals (config.attic.toolchain == null) [ 39 39 rustc 40 40 ]; 41 41 ··· 63 63 sqlite-interactive 64 64 65 65 flyctl 66 + skopeo 67 + manifest-tool 66 68 ] ++ lib.optionals pkgs.stdenv.isLinux [ 67 69 wrangler 68 70 ];
+137 -72
flake/packages.nix
··· 1 - { self, inputs, lib, makeCranePkgs, ... }: 1 + { self 2 + , lib 3 + , flake-parts-lib 4 + , inputs 5 + , config 6 + , makeCranePkgs 7 + , getSystem 8 + , ... 9 + }: 10 + 2 11 let 3 - defaultMakeCranePkgs = pkgs: let 4 - craneLib = inputs.crane.mkLib pkgs; 5 - in pkgs.callPackage ../crane.nix { inherit craneLib; }; 12 + inherit (lib) 13 + mkOption 14 + types 15 + ; 16 + inherit (flake-parts-lib) 17 + mkPerSystemOption 18 + ; 19 + 20 + # Re-evaluate perSystem with cross nixpkgs 21 + # HACK before https://github.com/hercules-ci/flake-parts/issues/95 is solved 22 + evalCross = { system, pkgs }: config.allSystems.${system}.debug.extendModules { 23 + modules = [ 24 + ({ config, lib, ... }: { 25 + _module.args.pkgs = pkgs; 26 + _module.args.self' = lib.mkForce config; 27 + }) 28 + ]; 29 + }; 6 30 in 7 31 { 8 - _module.args.makeCranePkgs = lib.mkDefault defaultMakeCranePkgs; 32 + options = { 33 + perSystem = mkPerSystemOption { 34 + options.attic = { 35 + toolchain = mkOption { 36 + type = types.nullOr types.package; 37 + default = null; 38 + }; 39 + extraPackageArgs = mkOption { 40 + type = types.attrsOf types.anything; 41 + default = {}; 42 + }; 43 + }; 44 + }; 45 + }; 46 + 47 + config = { 48 + _module.args.makeCranePkgs = lib.mkDefault (pkgs: let 49 + perSystemConfig = getSystem pkgs.system; 50 + craneLib = builtins.foldl' (acc: f: f acc) pkgs [ 51 + inputs.crane.mkLib 52 + (craneLib: 53 + if perSystemConfig.attic.toolchain == null then craneLib 54 + else craneLib.overrideToolchain config.attic.toolchain 55 + ) 56 + ]; 57 + in pkgs.callPackage ../crane.nix { 58 + inherit craneLib; 59 + inherit (perSystemConfig.attic) extraPackageArgs; 60 + }); 9 61 10 - perSystem = { self', pkgs, cranePkgs, ... }: (lib.mkMerge [ 11 - { 12 - _module.args.cranePkgs = makeCranePkgs pkgs; 62 + perSystem = { self', pkgs, config, cranePkgs, ... }: (lib.mkMerge [ 63 + { 64 + _module.args.cranePkgs = makeCranePkgs pkgs; 13 65 14 - packages = { 15 - default = self'.packages.attic; 66 + packages = { 67 + default = self'.packages.attic; 16 68 17 - inherit (cranePkgs) 18 - attic 19 - attic-client 20 - attic-server 21 - ; 69 + inherit (cranePkgs) 70 + attic 71 + attic-client 72 + attic-server 73 + ; 22 74 23 - attic-nixpkgs = pkgs.callPackage ../package.nix { }; 75 + attic-nixpkgs = pkgs.callPackage ../package.nix { }; 24 76 25 - attic-ci-installer = pkgs.callPackage ../ci-installer.nix { 26 - inherit self; 27 - }; 77 + attic-ci-installer = pkgs.callPackage ../ci-installer.nix { 78 + inherit self; 79 + }; 28 80 29 - book = pkgs.callPackage ../book { 30 - attic = self'.packages.attic; 81 + book = pkgs.callPackage ../book { 82 + attic = self'.packages.attic; 83 + }; 31 84 }; 32 - }; 33 - } 85 + } 34 86 35 - (lib.mkIf pkgs.stdenv.isLinux { 36 - packages = { 37 - attic-server-image = pkgs.dockerTools.buildImage { 38 - name = "attic-server"; 39 - tag = "main"; 40 - copyToRoot = [ 41 - self'.packages.attic-server 87 + (lib.mkIf pkgs.stdenv.isLinux { 88 + packages = { 89 + attic-server-image = pkgs.dockerTools.buildImage { 90 + name = "attic-server"; 91 + tag = "main"; 92 + copyToRoot = [ 93 + self'.packages.attic-server 42 94 43 - # Debugging utilities for `fly ssh console` 44 - pkgs.busybox 95 + # Debugging utilities for `fly ssh console` 96 + pkgs.busybox 45 97 46 - # Now required by the fly.io sshd 47 - pkgs.dockerTools.fakeNss 48 - ]; 49 - config = { 50 - Entrypoint = [ "${self'.packages.attic-server}/bin/atticd" ]; 51 - Env = [ 52 - "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" 98 + # Now required by the fly.io sshd 99 + pkgs.dockerTools.fakeNss 53 100 ]; 101 + config = { 102 + Entrypoint = [ "${self'.packages.attic-server}/bin/atticd" ]; 103 + Env = [ 104 + "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" 105 + ]; 106 + }; 54 107 }; 55 108 }; 56 - }; 57 - }) 109 + }) 58 110 59 - # Unfortunately, x86_64-darwin fails to evaluate static builds 60 - (lib.mkIf (pkgs.system != "x86_64-darwin") { 61 - packages = { 62 - # TODO: Make this work with Crane 63 - attic-static = (pkgs.pkgsStatic.callPackage ../package.nix { 64 - nix = pkgs.pkgsStatic.nix.overrideAttrs (old: { 65 - patches = (old.patches or []) ++ [ 66 - # To be submitted 67 - (pkgs.fetchpatch { 68 - url = "https://github.com/NixOS/nix/compare/3172c51baff5c81362fcdafa2e28773c2949c660...6b09a02536d5946458b537dfc36b7d268c9ce823.diff"; 69 - hash = "sha256-LFLq++J2XitEWQ0o57ihuuUlYk2PgUr11h7mMMAEe3c="; 70 - }) 111 + (lib.mkIf (pkgs.system == "x86_64-linux") { 112 + packages = { 113 + attic-server-image-aarch64 = let 114 + eval = evalCross { 115 + system = "aarch64-linux"; 116 + pkgs = pkgs.pkgsCross.aarch64-multiplatform; 117 + }; 118 + 119 + in eval.config.packages.attic-server-image; 120 + }; 121 + }) 122 + 123 + # Unfortunately, x86_64-darwin fails to evaluate static builds 124 + (lib.mkIf (pkgs.system != "x86_64-darwin") { 125 + packages = { 126 + # TODO: Make this work with Crane 127 + attic-static = (pkgs.pkgsStatic.callPackage ../package.nix { 128 + nix = pkgs.pkgsStatic.nix.overrideAttrs (old: { 129 + patches = (old.patches or []) ++ [ 130 + # To be submitted 131 + (pkgs.fetchpatch { 132 + url = "https://github.com/NixOS/nix/compare/3172c51baff5c81362fcdafa2e28773c2949c660...6b09a02536d5946458b537dfc36b7d268c9ce823.diff"; 133 + hash = "sha256-LFLq++J2XitEWQ0o57ihuuUlYk2PgUr11h7mMMAEe3c="; 134 + }) 135 + ]; 136 + }); 137 + }).overrideAttrs (old: { 138 + nativeBuildInputs = (old.nativeBuildInputs or []) ++ [ 139 + pkgs.nukeReferences 71 140 ]; 72 - }); 73 - }).overrideAttrs (old: { 74 - nativeBuildInputs = (old.nativeBuildInputs or []) ++ [ 75 - pkgs.nukeReferences 76 - ]; 77 141 78 - # Read by pkg_config crate (do some autodetection in build.rs?) 79 - PKG_CONFIG_ALL_STATIC = "1"; 142 + # Read by pkg_config crate (do some autodetection in build.rs?) 143 + PKG_CONFIG_ALL_STATIC = "1"; 80 144 81 - "NIX_CFLAGS_LINK_${pkgs.pkgsStatic.stdenv.cc.suffixSalt}" = "-lc"; 82 - RUSTFLAGS = "-C relocation-model=static"; 145 + "NIX_CFLAGS_LINK_${pkgs.pkgsStatic.stdenv.cc.suffixSalt}" = "-lc"; 146 + RUSTFLAGS = "-C relocation-model=static"; 83 147 84 - postFixup = (old.postFixup or "") + '' 85 - rm -f $out/nix-support/propagated-build-inputs 86 - nuke-refs $out/bin/attic 87 - ''; 88 - }); 148 + postFixup = (old.postFixup or "") + '' 149 + rm -f $out/nix-support/propagated-build-inputs 150 + nuke-refs $out/bin/attic 151 + ''; 152 + }); 89 153 90 - attic-client-static = self'.packages.attic-static.override { 91 - clientOnly = true; 154 + attic-client-static = self'.packages.attic-static.override { 155 + clientOnly = true; 156 + }; 92 157 }; 93 - }; 94 - }) 95 - ]); 158 + }) 159 + ]); 160 + }; 96 161 }
+4
justfile
··· 45 45 # (CI) Run rustfmt check 46 46 ci-rustfmt: 47 47 cargo fmt --check 48 + 49 + # (CI) Build and push images 50 + ci-build-and-push-images *args: 51 + .ci/build-and-push-images.sh {{ args }}