diff --git a/.drone.yml b/.drone.yml index 903be5b0..0c8a9221 100644 --- a/.drone.yml +++ b/.drone.yml @@ -2,68 +2,26 @@ kind: pipeline name: default -workspace: - base: /drone/garage - -volumes: -- name: nix_store - host: - path: /var/lib/drone/nix -- name: nix_config - temp: {} - -environment: - HOME: /drone/garage +node: + nix-daemon: 1 steps: - - name: setup nix - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + - name: check formatting + image: nixpkgs/nix:nixos-22.05 commands: - - cp nix/nix.conf /etc/nix/nix.conf - - nix-build --no-build-output --no-out-link shell.nix --arg release false -A inputDerivation - - - name: code quality - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix - commands: - - nix-shell --arg release false --run "cargo fmt -- --check" - - nix-shell --arg release false --run "cargo clippy -- --deny warnings" + - nix-shell --attr rust --run "cargo fmt -- --check" - name: build - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 commands: - - nix-build --no-build-output --option log-lines 100 --argstr target x86_64-unknown-linux-musl --arg release false --argstr git_version $DRONE_COMMIT + - nix-build --no-build-output --attr clippy.amd64 --argstr git_version $DRONE_COMMIT - name: unit + func tests - image: nixpkgs/nix:nixos-21.05 + image: nixpkgs/nix:nixos-22.05 environment: GARAGE_TEST_INTEGRATION_EXE: result/bin/garage - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix commands: - - | - nix-build \ - --no-build-output \ - --option log-lines 100 \ - --argstr target x86_64-unknown-linux-musl \ - --argstr compileMode test + - nix-build --no-build-output --attr test.amd64 - ./result/bin/garage_api-* - ./result/bin/garage_model-* - ./result/bin/garage_rpc-* @@ -73,16 +31,11 @@ steps: - ./result/bin/garage-* - ./result/bin/integration-* - - name: smoke-test - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + - name: integration tests + image: nixpkgs/nix:nixos-22.05 commands: - - nix-build --no-build-output --argstr target x86_64-unknown-linux-musl --arg release false --argstr git_version $DRONE_COMMIT - - nix-shell --arg release false --run ./script/test-smoke.sh || (cat /tmp/garage.log; false) + - nix-build --no-build-output --attr clippy.amd64 --argstr git_version $DRONE_COMMIT + - nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false) trigger: event: @@ -92,78 +45,38 @@ trigger: - tag - cron -node: - nix: 1 - --- kind: pipeline type: docker -name: release-linux-x86_64 +name: release-linux-amd64 -volumes: -- name: nix_store - host: - path: /var/lib/drone/nix -- name: nix_config - temp: {} - -environment: - TARGET: x86_64-unknown-linux-musl +node: + nix-daemon: 1 steps: - - name: setup nix - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix - commands: - - cp nix/nix.conf /etc/nix/nix.conf - - nix-build --no-build-output --no-out-link shell.nix -A inputDerivation - - name: build - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 commands: - - nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT + - nix-build --no-build-output --attr pkgs.amd64.release --argstr git_version $DRONE_COMMIT + - nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage" - name: integration - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 commands: - - nix-shell --run ./script/test-smoke.sh || (cat /tmp/garage.log; false) + - nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false) - name: push static binary - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 environment: AWS_ACCESS_KEY_ID: from_secret: garagehq_aws_access_key_id AWS_SECRET_ACCESS_KEY: from_secret: garagehq_aws_secret_access_key commands: - - nix-shell --arg rust false --arg integration false --run "to_s3" + - nix-shell --attr release --run "to_s3" - name: docker build and publish - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 environment: DOCKER_AUTH: from_secret: docker_auth @@ -174,7 +87,7 @@ steps: - mkdir -p /kaniko/.docker - echo $DOCKER_AUTH > /kaniko/.docker/config.json - export CONTAINER_TAG=${DRONE_TAG:-$DRONE_COMMIT} - - nix-shell --arg rust false --arg integration false --run "to_docker" + - nix-shell --attr release --run "to_docker" trigger: @@ -182,78 +95,38 @@ trigger: - promote - cron -node: - nix: 1 - --- kind: pipeline type: docker -name: release-linux-i686 +name: release-linux-i386 -volumes: -- name: nix_store - host: - path: /var/lib/drone/nix -- name: nix_config - temp: {} - -environment: - TARGET: i686-unknown-linux-musl +node: + nix-daemon: 1 steps: - - name: setup nix - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix - commands: - - cp nix/nix.conf /etc/nix/nix.conf - - nix-build --no-build-output --no-out-link shell.nix -A inputDerivation - - name: build - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 commands: - - nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT + - nix-build --no-build-output --attr pkgs.i386.release --argstr git_version $DRONE_COMMIT + - nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage" - name: integration - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 commands: - - nix-shell --run ./script/test-smoke.sh || (cat /tmp/garage.log; false) + - nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false) - name: push static binary - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 environment: AWS_ACCESS_KEY_ID: from_secret: garagehq_aws_access_key_id AWS_SECRET_ACCESS_KEY: from_secret: garagehq_aws_secret_access_key commands: - - nix-shell --arg rust false --arg integration false --run "to_s3" + - nix-shell --attr release --run "to_s3" - name: docker build and publish - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 environment: DOCKER_AUTH: from_secret: docker_auth @@ -264,75 +137,40 @@ steps: - mkdir -p /kaniko/.docker - echo $DOCKER_AUTH > /kaniko/.docker/config.json - export CONTAINER_TAG=${DRONE_TAG:-$DRONE_COMMIT} - - nix-shell --arg rust false --arg integration false --run "to_docker" + - nix-shell --attr release --run "to_docker" trigger: event: - promote - cron -node: - nix: 1 - --- kind: pipeline type: docker -name: release-linux-aarch64 +name: release-linux-arm64 -volumes: -- name: nix_store - host: - path: /var/lib/drone/nix -- name: nix_config - temp: {} - -environment: - TARGET: aarch64-unknown-linux-musl +node: + nix-daemon: 1 steps: - - name: setup nix - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix - commands: - - cp nix/nix.conf /etc/nix/nix.conf - - nix-build --no-build-output --no-out-link ./shell.nix --arg rust false --arg integration false -A inputDerivation - - name: build - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 commands: - - nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT + - nix-build --no-build-output --attr pkgs.arm64.release --argstr git_version $DRONE_COMMIT + - nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage" - name: push static binary - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 environment: AWS_ACCESS_KEY_ID: from_secret: garagehq_aws_access_key_id AWS_SECRET_ACCESS_KEY: from_secret: garagehq_aws_secret_access_key commands: - - nix-shell --arg rust false --arg integration false --run "to_s3" + - nix-shell --attr release --run "to_s3" - name: docker build and publish - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 environment: DOCKER_AUTH: from_secret: docker_auth @@ -343,75 +181,40 @@ steps: - mkdir -p /kaniko/.docker - echo $DOCKER_AUTH > /kaniko/.docker/config.json - export CONTAINER_TAG=${DRONE_TAG:-$DRONE_COMMIT} - - nix-shell --arg rust false --arg integration false --run "to_docker" + - nix-shell --attr release --run "to_docker" trigger: event: - promote - cron -node: - nix: 1 - --- kind: pipeline type: docker -name: release-linux-armv6l +name: release-linux-arm -volumes: -- name: nix_store - host: - path: /var/lib/drone/nix -- name: nix_config - temp: {} - -environment: - TARGET: armv6l-unknown-linux-musleabihf +node: + nix-daemon: 1 steps: - - name: setup nix - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix - commands: - - cp nix/nix.conf /etc/nix/nix.conf - - nix-build --no-build-output --no-out-link --arg rust false --arg integration false -A inputDerivation - - name: build - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 commands: - - nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT + - nix-build --no-build-output --attr pkgs.arm.release --argstr git_version $DRONE_COMMIT + - nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage" - name: push static binary - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 environment: AWS_ACCESS_KEY_ID: from_secret: garagehq_aws_access_key_id AWS_SECRET_ACCESS_KEY: from_secret: garagehq_aws_secret_access_key commands: - - nix-shell --arg integration false --arg rust false --run "to_s3" + - nix-shell --attr release --run "to_s3" - name: docker build and publish - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix - - name: nix_config - path: /etc/nix + image: nixpkgs/nix:nixos-22.05 environment: DOCKER_AUTH: from_secret: docker_auth @@ -422,32 +225,24 @@ steps: - mkdir -p /kaniko/.docker - echo $DOCKER_AUTH > /kaniko/.docker/config.json - export CONTAINER_TAG=${DRONE_TAG:-$DRONE_COMMIT} - - nix-shell --arg rust false --arg integration false --run "to_docker" + - nix-shell --attr release --run "to_docker" trigger: event: - promote - cron -node: - nix: 1 - --- kind: pipeline type: docker name: refresh-release-page -volumes: -- name: nix_store - host: - path: /var/lib/drone/nix +node: + nix-daemon: 1 steps: - name: refresh-index - image: nixpkgs/nix:nixos-21.05 - volumes: - - name: nix_store - path: /nix + image: nixpkgs/nix:nixos-22.05 environment: AWS_ACCESS_KEY_ID: from_secret: garagehq_aws_access_key_id @@ -455,24 +250,21 @@ steps: from_secret: garagehq_aws_secret_access_key commands: - mkdir -p /etc/nix && cp nix/nix.conf /etc/nix/nix.conf - - nix-shell --arg integration false --arg rust false --run "refresh_index" + - nix-shell --attr release --run "refresh_index" depends_on: - - release-linux-x86_64 - - release-linux-i686 - - release-linux-aarch64 - - release-linux-armv6l + - release-linux-amd64 + - release-linux-i386 + - release-linux-arm64 + - release-linux-arm trigger: event: - promote - cron -node: - nix: 1 - --- kind: signature -hmac: 3fc19d6f9a3555519c8405e3281b2e74289bb802f644740d5481d53df3a01fa4 +hmac: 8495114848396ebb492831fc9bd37b353e1a4add9d72c0a123d109490a5b0db0 ... diff --git a/default.nix b/default.nix index de996ac1..4d7558c5 100644 --- a/default.nix +++ b/default.nix @@ -1,147 +1,33 @@ { system ? builtins.currentSystem, - release ? false, - target ? "x86_64-unknown-linux-musl", - compileMode ? null, git_version ? null, }: with import ./nix/common.nix; -let - crossSystem = { config = target; }; -in let - log = v: builtins.trace v v; +let + pkgs = import pkgsSrc { }; + compile = import ./nix/compile.nix; + build_debug_and_release = (target: { + debug = (compile { inherit target; release = false; }).workspace.garage { compileMode = "build"; }; + release = (compile { inherit target; release = true; }).workspace.garage { compileMode = "build"; }; + }); + test = (rustPkgs: pkgs.symlinkJoin { + name ="garage-tests"; + paths = builtins.map (key: rustPkgs.workspace.${key} { compileMode = "test"; }) (builtins.attrNames rustPkgs.workspace); + }); - pkgs = import pkgsSrc { - inherit system crossSystem; - overlays = [ cargo2nixOverlay ]; +in { + pkgs = { + amd64 = build_debug_and_release "x86_64-unknown-linux-musl"; + i386 = build_debug_and_release "i686-unknown-linux-musl"; + arm64 = build_debug_and_release "aarch64-unknown-linux-musl"; + arm = build_debug_and_release "armv6l-unknown-linux-musleabihf"; }; - - - /* - Rust and Nix triples are not the same. Cargo2nix has a dedicated library - to convert Nix triples to Rust ones. We need this conversion as we want to - set later options linked to our (rust) target in a generic way. Not only - the triple terminology is different, but also the "roles" are named differently. - Nix uses a build/host/target terminology where Nix's "host" maps to Cargo's "target". - */ - rustTarget = log (pkgs.rustBuilder.rustLib.rustTriple pkgs.stdenv.hostPlatform); - - /* - Cargo2nix is built for rustOverlay which installs Rust from Mozilla releases. - We want our own Rust to avoid incompatibilities, like we had with musl 1.2.0. - rustc was built with musl < 1.2.0 and nix shipped musl >= 1.2.0 which lead to compilation breakage. - So we want a Rust release that is bound to our Nix repository to avoid these problems. - See here for more info: https://musl.libc.org/time64.html - Because Cargo2nix does not support the Rust environment shipped by NixOS, - we emulate the structure of the Rust object created by rustOverlay. - In practise, rustOverlay ships rustc+cargo in a single derivation while - NixOS ships them in separate ones. We reunite them with symlinkJoin. - */ - rustChannel = pkgs.symlinkJoin { - name ="rust-channel"; - paths = [ - pkgs.rustPlatform.rust.rustc - pkgs.rustPlatform.rust.cargo - ]; + test = { + amd64 = test (compile { target = "x86_64-unknown-linux-musl"; }); }; - - /* - Cargo2nix provides many overrides by default, you can take inspiration from them: - https://github.com/cargo2nix/cargo2nix/blob/master/overlay/overrides.nix - - You can have a complete list of the available options by looking at the overriden object, mkcrate: - https://github.com/cargo2nix/cargo2nix/blob/master/overlay/mkcrate.nix - */ - overrides = pkgs.rustBuilder.overrides.all ++ [ - /* - [1] We need to alter Nix hardening to be able to statically compile: PIE, - Position Independent Executables seems to be supported only on amd64. Having - this flags set either make our executables crash or compile as dynamic on many platforms. - In the following section codegenOpts, we reactive it for the supported targets - (only amd64 curently) through the `-static-pie` flag. PIE is a feature used - by ASLR, which helps mitigate security issues. - Learn more about Nix Hardening: https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/cc-wrapper/add-hardening.sh - - [2] We want to inject the git version while keeping the build deterministic. - As we do not want to consider the .git folder as part of the input source, - we ask the user (the CI often) to pass the value to Nix. - */ - (pkgs.rustBuilder.rustLib.makeOverride { - name = "garage_rpc"; - overrideAttrs = drv: - /* [1] */ { hardeningDisable = [ "pie" ]; } - // - /* [2] */ (if git_version != null then { - preConfigure = '' - ${drv.preConfigure or ""} - export GIT_VERSION="${git_version}" - ''; - } else {}); - }) - - /* - We ship some parts of the code disabled by default by putting them behind a flag. - It speeds up the compilation (when the feature is not required) and released crates have less dependency by default (less attack surface, disk space, etc.). - But we want to ship these additional features when we release Garage. - In the end, we chose to exclude all features from debug builds while putting (all of) them in the release builds. - Currently, the only feature of Garage is kubernetes-discovery from the garage_rpc crate. - */ - (pkgs.rustBuilder.rustLib.makeOverride { - name = "garage_rpc"; - overrideArgs = old: - { - features = if release then [ "kubernetes-discovery" ] else []; - }; - }) - ]; - - packageFun = import ./Cargo.nix; - - /* - We compile fully static binaries with musl to simplify deployment on most systems. - When possible, we reactivate PIE hardening (see above). - - Also, if you set the RUSTFLAGS environment variable, the following parameters will - be ignored. - - For more information on static builds, please refer to Rust's RFC 1721. - https://rust-lang.github.io/rfcs/1721-crt-static.html#specifying-dynamicstatic-c-runtime-linkage - */ - - codegenOpts = { - "armv6l-unknown-linux-musleabihf" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* compile as dynamic with static-pie */ - "aarch64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */ - "i686-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */ - "x86_64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static-pie" ]; + clippy = { + amd64 = (compile { compiler = "clippy"; }).workspace.garage { compileMode = "build"; } ; }; - - /* - The following definition is not elegant as we use a low level function of Cargo2nix - that enables us to pass our custom rustChannel object. We need this low level definition - to pass Nix's Rust toolchains instead of Mozilla's one. - - target is mandatory but must be kept to null to allow cargo2nix to set it to the appropriate value - for each crate. - */ - rustPkgs = pkgs.rustBuilder.makePackageSet { - inherit packageFun rustChannel release codegenOpts; - packageOverrides = overrides; - target = null; - - buildRustPackages = pkgs.buildPackages.rustBuilder.makePackageSet { - inherit rustChannel packageFun codegenOpts; - packageOverrides = overrides; - target = null; - }; - }; - - -in - if compileMode == "test" - then pkgs.symlinkJoin { - name ="garage-tests"; - paths = builtins.map (key: rustPkgs.workspace.${key} { inherit compileMode; }) (builtins.attrNames rustPkgs.workspace); - } - else rustPkgs.workspace.garage { inherit compileMode; } +} diff --git a/nix/common.nix b/nix/common.nix index 5cd15c8a..8396a6de 100644 --- a/nix/common.nix +++ b/nix/common.nix @@ -4,18 +4,16 @@ rec { */ pkgsSrc = fetchTarball { # As of 2021-10-04 - url ="https://github.com/NixOS/nixpkgs/archive/b27d18a412b071f5d7991d1648cfe78ee7afe68a.tar.gz"; + url = "https://github.com/NixOS/nixpkgs/archive/b27d18a412b071f5d7991d1648cfe78ee7afe68a.tar.gz"; sha256 = "1xy9zpypqfxs5gcq5dcla4bfkhxmh5nzn9dyqkr03lqycm9wg5cr"; }; cargo2nixSrc = fetchGit { # As of 2022-03-17 url = "https://github.com/superboum/cargo2nix"; - ref = "main"; - rev = "bcbf3ba99e9e01a61eb83a24624419c2dd9dec64"; + ref = "dedup_propagate"; + rev = "486675c67249e735dd7eb68e1b9feac9db102be7"; }; - - /* * Shared objects */ diff --git a/nix/compile.nix b/nix/compile.nix new file mode 100644 index 00000000..49f7c1d6 --- /dev/null +++ b/nix/compile.nix @@ -0,0 +1,227 @@ +{ + system ? builtins.currentSystem, + target ? null, + compiler ? "rustc", + release ? false, + git_version ? null, +}: + +with import ./common.nix; + +let + log = v: builtins.trace v v; + + pkgs = import pkgsSrc { + inherit system; + ${ if target == null then null else "crossSystem" } = { config = target; }; + overlays = [ cargo2nixOverlay ]; + }; + + /* + Rust and Nix triples are not the same. Cargo2nix has a dedicated library + to convert Nix triples to Rust ones. We need this conversion as we want to + set later options linked to our (rust) target in a generic way. Not only + the triple terminology is different, but also the "roles" are named differently. + Nix uses a build/host/target terminology where Nix's "host" maps to Cargo's "target". + */ + rustTarget = log (pkgs.rustBuilder.rustLib.rustTriple pkgs.stdenv.hostPlatform); + + /* + Cargo2nix is built for rustOverlay which installs Rust from Mozilla releases. + We want our own Rust to avoid incompatibilities, like we had with musl 1.2.0. + rustc was built with musl < 1.2.0 and nix shipped musl >= 1.2.0 which lead to compilation breakage. + So we want a Rust release that is bound to our Nix repository to avoid these problems. + See here for more info: https://musl.libc.org/time64.html + Because Cargo2nix does not support the Rust environment shipped by NixOS, + we emulate the structure of the Rust object created by rustOverlay. + In practise, rustOverlay ships rustc+cargo in a single derivation while + NixOS ships them in separate ones. We reunite them with symlinkJoin. + */ + rustChannel = { + rustc = pkgs.symlinkJoin { + name = "rust-channel"; + paths = [ + pkgs.rustPlatform.rust.cargo + pkgs.rustPlatform.rust.rustc + ]; + }; + clippy = pkgs.symlinkJoin { + name = "clippy-channel"; + paths = [ + pkgs.rustPlatform.rust.cargo + pkgs.rustPlatform.rust.rustc + pkgs.clippy + ]; + }; + }.${compiler}; + + clippyBuilder = pkgs.writeScriptBin "clippy" '' + #!${pkgs.stdenv.shell} + . ${cargo2nixSrc + "/overlay/utils.sh"} + isBuildScript= + args=("$@") + for i in "''${!args[@]}"; do + if [ "xmetadata=" = "x''${args[$i]::9}" ]; then + args[$i]=metadata=$NIX_RUST_METADATA + elif [ "x--crate-name" = "x''${args[$i]}" ] && [ "xbuild_script_" = "x''${args[$i+1]::13}" ]; then + isBuildScript=1 + fi + done + if [ "$isBuildScript" ]; then + args+=($NIX_RUST_BUILD_LINK_FLAGS) + else + args+=($NIX_RUST_LINK_FLAGS) + fi + touch invoke.log + echo "''${args[@]}" >>invoke.log + + exec ${rustChannel}/bin/clippy-driver --deny warnings "''${args[@]}" + ''; + + buildEnv = (drv: { + rustc = drv.setBuildEnv; + clippy = '' + ${drv.setBuildEnv or "" } + echo + echo --- BUILDING WITH CLIPPY --- + echo + + export RUSTC=${clippyBuilder}/bin/clippy + ''; + }.${compiler}); + + /* + Cargo2nix provides many overrides by default, you can take inspiration from them: + https://github.com/cargo2nix/cargo2nix/blob/master/overlay/overrides.nix + + You can have a complete list of the available options by looking at the overriden object, mkcrate: + https://github.com/cargo2nix/cargo2nix/blob/master/overlay/mkcrate.nix + */ + overrides = pkgs.rustBuilder.overrides.all ++ [ + /* + [1] We add some logic to compile our crates with clippy, it provides us many additional lints + + [2] We need to alter Nix hardening to make static binaries: PIE, + Position Independent Executables seems to be supported only on amd64. Having + this flag set either 1. make our executables crash or 2. compile as dynamic on some platforms. + Here, we deactivate it. Later (find `codegenOpts`), we reactivate it for supported targets + (only amd64 curently) through the `-static-pie` flag. + PIE is a feature used by ASLR, which helps mitigate security issues. + Learn more about Nix Hardening at: https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/cc-wrapper/add-hardening.sh + + [3] We want to inject the git version while keeping the build deterministic. + As we do not want to consider the .git folder as part of the input source, + we ask the user (the CI often) to pass the value to Nix. + + [4] We ship some parts of the code disabled by default by putting them behind a flag. + It speeds up the compilation (when the feature is not required) and released crates have less dependency by default (less attack surface, disk space, etc.). + But we want to ship these additional features when we release Garage. + In the end, we chose to exclude all features from debug builds while putting (all of) them in the release builds. + Currently, the only feature of Garage is kubernetes-discovery from the garage_rpc crate. + */ + (pkgs.rustBuilder.rustLib.makeOverride { + name = "garage"; + overrideAttrs = drv: { + /* [1] */ setBuildEnv = (buildEnv drv); + /* [2] */ hardeningDisable = [ "pie" ]; + }; + }) + + (pkgs.rustBuilder.rustLib.makeOverride { + name = "garage_rpc"; + overrideAttrs = drv: + (if git_version != null then { + /* [3] */ preConfigure = '' + ${drv.preConfigure or ""} + export GIT_VERSION="${git_version}" + ''; + } else {}) + // { + /* [1] */ setBuildEnv = (buildEnv drv); + }; + overrideArgs = old: { + /* [4] */ features = if release then [ "kubernetes-discovery" ] else []; + }; + }) + + (pkgs.rustBuilder.rustLib.makeOverride { + name = "garage_db"; + overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); }; + }) + + (pkgs.rustBuilder.rustLib.makeOverride { + name = "garage_util"; + overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); }; + }) + + (pkgs.rustBuilder.rustLib.makeOverride { + name = "garage_table"; + overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); }; + }) + + (pkgs.rustBuilder.rustLib.makeOverride { + name = "garage_block"; + overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); }; + }) + + (pkgs.rustBuilder.rustLib.makeOverride { + name = "garage_model"; + overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); }; + }) + + (pkgs.rustBuilder.rustLib.makeOverride { + name = "garage_api"; + overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); }; + }) + + (pkgs.rustBuilder.rustLib.makeOverride { + name = "garage_web"; + overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); }; + }) + + (pkgs.rustBuilder.rustLib.makeOverride { + name = "k2v-client"; + overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); }; + }) + ]; + + packageFun = import ../Cargo.nix; + + /* + We compile fully static binaries with musl to simplify deployment on most systems. + When possible, we reactivate PIE hardening (see above). + + Also, if you set the RUSTFLAGS environment variable, the following parameters will + be ignored. + + For more information on static builds, please refer to Rust's RFC 1721. + https://rust-lang.github.io/rfcs/1721-crt-static.html#specifying-dynamicstatic-c-runtime-linkage + */ + + codegenOpts = { + "armv6l-unknown-linux-musleabihf" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* compile as dynamic with static-pie */ + "aarch64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */ + "i686-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */ + "x86_64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static-pie" ]; + }; + +in + /* + The following definition is not elegant as we use a low level function of Cargo2nix + that enables us to pass our custom rustChannel object. We need this low level definition + to pass Nix's Rust toolchains instead of Mozilla's one. + + target is mandatory but must be kept to null to allow cargo2nix to set it to the appropriate value + for each crate. + */ + pkgs.rustBuilder.makePackageSet { + inherit packageFun rustChannel release codegenOpts; + packageOverrides = overrides; + target = null; + + buildRustPackages = pkgs.buildPackages.rustBuilder.makePackageSet { + inherit rustChannel packageFun codegenOpts; + packageOverrides = overrides; + target = null; + }; + } diff --git a/nix/nix.conf b/nix/nix.conf index 871efb10..6abf96b3 100644 --- a/nix/nix.conf +++ b/nix/nix.conf @@ -1,4 +1,9 @@ substituters = https://cache.nixos.org https://nix.web.deuxfleurs.fr trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix.web.deuxfleurs.fr:eTGL6kvaQn6cDR/F9lDYUIP9nCVR/kkshYfLDJf1yKs= max-jobs = auto -cores = 4 +cores = 0 +log-lines = 200 +filter-syscalls = false +sandbox = false +keep-outputs = true +keep-derivations = true diff --git a/script/not-dynamic.sh b/script/not-dynamic.sh new file mode 100755 index 00000000..b9a13070 --- /dev/null +++ b/script/not-dynamic.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -e + +if [ "$#" -ne 1 ]; then + echo "[fail] usage: $0 binary" + exit 2 +fi + +if file $1 | grep 'dynamically linked' 2>&1; then + echo "[fail] $1 is dynamic" + exit 1 +fi +echo "[ok] $1 is probably static" diff --git a/shell.nix b/shell.nix index 13ea4a0e..ea5d6356 100644 --- a/shell.nix +++ b/shell.nix @@ -1,8 +1,5 @@ { system ? builtins.currentSystem, - rust ? true, - integration ? true, - release ? true, }: with import ./nix/common.nix; @@ -16,9 +13,59 @@ let winscp = (import ./nix/winscp.nix) pkgs; in + { -pkgs.mkShell { - shellHook = '' + /* --- Rust Shell --- + * Use it to compile Garage + */ + rust = pkgs.mkShell { + shellHook = '' +function refresh_toolchain { + nix copy \ + --to 's3://nix?endpoint=garage.deuxfleurs.fr®ion=garage&secret-key=/etc/nix/signing-key.sec' \ + $(nix-store -qR \ + $(nix-build --quiet --no-build-output --no-out-link nix/toolchain.nix)) +} + ''; + + nativeBuildInputs = [ + #pkgs.rustPlatform.rust.rustc + pkgs.rustPlatform.rust.cargo + #pkgs.clippy + pkgs.rustfmt + #pkgs.perl + #pkgs.protobuf + #pkgs.pkg-config + #pkgs.openssl + pkgs.file + #cargo2nix.packages.x86_64-linux.cargo2nix + ]; + }; + + /* --- Integration shell --- + * Use it to test Garage with common S3 clients + */ + integration = pkgs.mkShell { + nativeBuildInputs = [ + winscp + pkgs.s3cmd + pkgs.awscli2 + pkgs.minio-client + pkgs.rclone + pkgs.socat + pkgs.psmisc + pkgs.which + pkgs.openssl + pkgs.curl + pkgs.jq + ]; + }; + + /* --- Release shell --- + * A shell built to make releasing easier + */ + release = pkgs.mkShell { + shellHook = '' function to_s3 { aws \ --endpoint-url https://garage.deuxfleurs.fr \ @@ -62,45 +109,12 @@ function refresh_index { result/share/_releases.html \ s3://garagehq.deuxfleurs.fr/ } + ''; + nativeBuildInputs = [ + pkgs.awscli2 + kaniko + ]; + }; + } -function refresh_toolchain { - nix copy \ - --to 's3://nix?endpoint=garage.deuxfleurs.fr®ion=garage&secret-key=/etc/nix/signing-key.sec' \ - $(nix-store -qR \ - $(nix-build --quiet --no-build-output --no-out-link nix/toolchain.nix)) -} - ''; - nativeBuildInputs = - (if rust then [ - pkgs.rustPlatform.rust.rustc - pkgs.rustPlatform.rust.cargo - pkgs.clippy - pkgs.rustfmt - pkgs.perl - pkgs.protobuf - pkgs.pkg-config - pkgs.openssl - cargo2nix.packages.x86_64-linux.cargo2nix - ] else []) - ++ - (if integration then [ - winscp - pkgs.s3cmd - pkgs.awscli2 - pkgs.minio-client - pkgs.rclone - pkgs.socat - pkgs.psmisc - pkgs.which - pkgs.openssl - pkgs.curl - pkgs.jq - ] else []) - ++ - (if release then [ - pkgs.awscli2 - kaniko - ] else []) - ; -}