aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2022-07-19 16:53:55 +0200
committerQuentin Dufour <quentin@deuxfleurs.fr>2022-07-19 16:53:55 +0200
commitaac8ee19d7ef80f437714f882ec8e32b06d00f41 (patch)
treeb6b022091aa6d20d06c7d343a28756af7faad3c9
parent9ce0d22c99472534838c1afe7c6dffdd0aa659a8 (diff)
downloadbottin-aac8ee19d7ef80f437714f882ec8e32b06d00f41.tar.gz
bottin-aac8ee19d7ef80f437714f882ec8e32b06d00f41.zip
Add Nix packaging
-rw-r--r--README.md11
-rw-r--r--default.nix48
-rw-r--r--gomod2nix.toml153
-rw-r--r--nix/builder/default.nix387
-rw-r--r--nix/builder/fetch.sh13
-rw-r--r--nix/builder/install/install.go57
-rw-r--r--nix/builder/parser.nix141
-rw-r--r--nix/builder/symlink/symlink.go110
8 files changed, 920 insertions, 0 deletions
diff --git a/README.md b/README.md
index 2442040..2fcc22d 100644
--- a/README.md
+++ b/README.md
@@ -44,6 +44,17 @@ Bottin requires go 1.13 or later.
To build Bottin, clone this repository outside of your `$GOPATH`.
Then, run `make` in the root of the repo.
+## Releasing
+
+```bash
+nix-build -A bin
+nix-build -A docker
+```
+
+```bash
+docker load < $(nix-build -A docker)
+docker push ???
+```
## Server initialization
diff --git a/default.nix b/default.nix
new file mode 100644
index 0000000..cc73d86
--- /dev/null
+++ b/default.nix
@@ -0,0 +1,48 @@
+let
+ pkgsSrc = fetchTarball {
+ # As of 2022-07-19
+ url = "https://github.com/NixOS/nixpkgs/archive/d2db10786f27619d5519b12b03fb10dc8ca95e59.tar.gz";
+ sha256 = "0s9gigs3ylnq5b94rfcmxvrmmr3kzhs497gksajf638d5bv7zcl5";
+ };
+ pkgs = import pkgsSrc {
+ overlays = [
+ (self: super: {
+ gomod = super.callPackage ./nix/builder { };
+ })
+ ];
+ };
+in rec {
+ bin = pkgs.gomod.buildGoApplication {
+ pname = "bottin-bin";
+ version = "0.1.0";
+ src = builtins.filterSource
+ (path: type: (builtins.match ".*/test/.*\\.(go|sum|mod)" path) == null)
+ ./.;
+ modules = ./gomod2nix.toml;
+
+ CGO_ENABLED=0;
+
+ meta = with pkgs.lib; {
+ description = "Interface web pour gérer le LDAP: changer son mot de passe, ses infos de profil, inviter des gens, administration";
+ homepage = "https://git.deuxfleurs.fr/Deuxfleurs/guichet";
+ license = licenses.gpl3Plus;
+ platforms = platforms.linux;
+ };
+ };
+ pkg = pkgs.stdenv.mkDerivation {
+ pname = "bottin";
+ version = "0.1.0";
+ unpackPhase = "true";
+
+ installPhase = ''
+ mkdir -p $out/
+ cp ${bin}/bin/bottin $out/bottin
+ '';
+ };
+ docker = pkgs.dockerTools.buildImage {
+ name = "dxflrs/bottin";
+ config = {
+ Cmd = [ "${pkg}/bottin" ];
+ };
+ };
+}
diff --git a/gomod2nix.toml b/gomod2nix.toml
new file mode 100644
index 0000000..62a57bf
--- /dev/null
+++ b/gomod2nix.toml
@@ -0,0 +1,153 @@
+schema = 3
+
+[mod]
+ [mod."github.com/armon/circbuf"]
+ version = "v0.0.0-20150827004946-bbbad097214e"
+ hash = "sha256-klQjllsJZqZ2KPNx1mZT9XP+UAJkuBhmTnZdNlAflEM="
+ [mod."github.com/armon/go-metrics"]
+ version = "v0.0.0-20180917152333-f0300d1749da"
+ hash = "sha256-+zqX1hlJgc+IrXRzBQDMhR8GYQdc0Oj6PiIDfctgh44="
+ [mod."github.com/armon/go-radix"]
+ version = "v0.0.0-20180808171621-7fddfc383310"
+ hash = "sha256-ZHU4pyBqHHRuQJuYr2K+LqeAnLX9peX07cmSYK+GDHk="
+ [mod."github.com/bgentry/speakeasy"]
+ version = "v0.1.0"
+ hash = "sha256-Gt1vj6CFovLnO6wX5u2O4UfecY9V2J9WGw1ez4HMrgk="
+ [mod."github.com/davecgh/go-spew"]
+ version = "v1.1.1"
+ hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI="
+ [mod."github.com/fatih/color"]
+ version = "v1.7.0"
+ hash = "sha256-4In7ef7it7d+6oGUJ3pkD0V+lsL40hVtYdy2KD2ovn0="
+ [mod."github.com/google/btree"]
+ version = "v0.0.0-20180813153112-4030bb1f1f0c"
+ hash = "sha256-5gr0RMnlvrzCke3kwpkf92WvW3x5nnKZesoulyoYRC0="
+ [mod."github.com/google/uuid"]
+ version = "v1.1.1"
+ hash = "sha256-66PXC/RCPUyhS9PhkIPQFR3tbM2zZYDNPGXN7JJj3UE="
+ [mod."github.com/hashicorp/consul/api"]
+ version = "v1.3.0"
+ hash = "sha256-fMORNFAWK2GkRbBl/KzNHrvtCfwz/7P66HzDaE4GnuE="
+ [mod."github.com/hashicorp/consul/sdk"]
+ version = "v0.3.0"
+ hash = "sha256-lF47JPGfmeGjpuQw9VSNs2Mi+G7FQLKrrHteX3iXfpU="
+ [mod."github.com/hashicorp/errwrap"]
+ version = "v1.0.0"
+ hash = "sha256-LGSLrefkABG1kH1i+GUWiD2/ggJxiZEJ+D2YNbhZjmo="
+ [mod."github.com/hashicorp/go-cleanhttp"]
+ version = "v0.5.1"
+ hash = "sha256-c54zcHr9THj3MQk7hrDQcpjOcQi1MvXZ4Kpin6EbfR4="
+ [mod."github.com/hashicorp/go-immutable-radix"]
+ version = "v1.0.0"
+ hash = "sha256-JmNxdGaJG63Ty/sVnPjqvTyA4/k5wkzZ/QvpMK2uduw="
+ [mod."github.com/hashicorp/go-msgpack"]
+ version = "v0.5.3"
+ hash = "sha256-2OUYjD/Jt12TFBrtH0wRqg+lzRljDxSIhk2CqBLUczo="
+ [mod."github.com/hashicorp/go-multierror"]
+ version = "v1.0.0"
+ hash = "sha256-iXzjerl96o7QDiSwQjbak8R/t+YzZeoUqm59TCmy3gI="
+ [mod."github.com/hashicorp/go-rootcerts"]
+ version = "v1.0.0"
+ hash = "sha256-4NZJAT5/vocyto+dv6FmW4kFiYldmNvewowsYK/LiTI="
+ [mod."github.com/hashicorp/go-sockaddr"]
+ version = "v1.0.0"
+ hash = "sha256-orG+SHVsp5lgNRCErmhMLABVFQ3ZWfYIJ/4LTFzlvao="
+ [mod."github.com/hashicorp/go-syslog"]
+ version = "v1.0.0"
+ hash = "sha256-YRuq6oPMwAFVY7mvwpMDvZqGwNnb5CjBYyKI/x5mbCc="
+ [mod."github.com/hashicorp/go-uuid"]
+ version = "v1.0.1"
+ hash = "sha256-s1wIvBu37z4U3qK9sdUR1CtbD39N6RwfX4HgDCpCa0s="
+ [mod."github.com/hashicorp/go.net"]
+ version = "v0.0.1"
+ hash = "sha256-JKal3E+wPO+Hk838opKV4HHKB4O72Xy+77ncXlLkWRk="
+ [mod."github.com/hashicorp/golang-lru"]
+ version = "v0.5.0"
+ hash = "sha256-Lo0UyOZQ89iK0Ui3Hfk5VkWqxEzLw6Lclq4EM8VlYoo="
+ [mod."github.com/hashicorp/logutils"]
+ version = "v1.0.0"
+ hash = "sha256-e8t8Dm8sp/PzKClN1TOmFcrTAWNh4mZHSW7cAjVx3Bw="
+ [mod."github.com/hashicorp/mdns"]
+ version = "v1.0.0"
+ hash = "sha256-ravx4tklQG43OEjPiJn68iJM9ODZ6hgU0idFCEOiJGM="
+ [mod."github.com/hashicorp/memberlist"]
+ version = "v0.1.3"
+ hash = "sha256-IsxqevYulPt+2VGtlq068Jyq1YfIk4Ohh9TgakIGxnc="
+ [mod."github.com/hashicorp/serf"]
+ version = "v0.8.2"
+ hash = "sha256-diRxWOouFLTO75f2E9NlrKgie/qsT+gOOrrbf4tACHw="
+ [mod."github.com/jsimonetti/pwscheme"]
+ version = "v0.0.0-20220125093853-4d9895f5db73"
+ hash = "sha256-YF3RKU/4CWvLPgGzUd7Hk/2+41OUFuRWZgzQuqcsKg0="
+ [mod."github.com/konsorten/go-windows-terminal-sequences"]
+ version = "v1.0.1"
+ hash = "sha256-Nwp+Cza9dIu3ogVGip6wyOjWwwaq+2hU3eYIe4R7kNE="
+ [mod."github.com/mattn/go-colorable"]
+ version = "v0.0.9"
+ hash = "sha256-fVPF8VxbdggLAZnaexMl6Id1WjXKImzVySxKfa+ukts="
+ [mod."github.com/mattn/go-isatty"]
+ version = "v0.0.3"
+ hash = "sha256-9ogEEd8/kl/dImldzdBmTFdepvB0dVXEzN4o8bEqhBs="
+ [mod."github.com/miekg/dns"]
+ version = "v1.0.14"
+ hash = "sha256-OeijUgBaEmDapclTxfvjIqrjh4qZu3+DQpHelGGI4aA="
+ [mod."github.com/mitchellh/cli"]
+ version = "v1.0.0"
+ hash = "sha256-4nG7AhRcjTRCwUCdnaNaFrAKDxEEoiihaCA4lk+uM8U="
+ [mod."github.com/mitchellh/go-homedir"]
+ version = "v1.0.0"
+ hash = "sha256-eGmBNNTuDSMwicjTNgB54IEJuK1tgOLDJ3NHTpQCHzg="
+ [mod."github.com/mitchellh/go-testing-interface"]
+ version = "v1.0.0"
+ hash = "sha256-/Dpv/4i5xuK8hDH+q8YTdF6Jg6NNtfO4Wqig2JCWgrY="
+ [mod."github.com/mitchellh/gox"]
+ version = "v0.4.0"
+ hash = "sha256-GV3LYxzJt8YVbnSac2orlj2QR3MX/YIDrLkSkPhsjuA="
+ [mod."github.com/mitchellh/iochan"]
+ version = "v1.0.0"
+ hash = "sha256-b5Tp7cw/e8mL++IjsebbmKWXtb9Hrzu4Fc6M4tZKFhU="
+ [mod."github.com/mitchellh/mapstructure"]
+ version = "v1.1.2"
+ hash = "sha256-OU9HZYHtl0qaqMFd84w7snkkRuRY6UMSsfCnL5HYdw0="
+ [mod."github.com/pascaldekloe/goe"]
+ version = "v0.0.0-20180627143212-57f6aae5913c"
+ hash = "sha256-2KUjqrEC/BwkTZRxImazcI/C3H7QmXfNrlt8slwdDbc="
+ [mod."github.com/pkg/errors"]
+ version = "v0.8.1"
+ hash = "sha256-oe3iddfoLRwpC3ki5fifHf2ZFprtg99iNak50shiuDw="
+ [mod."github.com/pmezard/go-difflib"]
+ version = "v1.0.0"
+ hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA="
+ [mod."github.com/posener/complete"]
+ version = "v1.1.1"
+ hash = "sha256-heyPMSBzVlx7ZKgTyzl/xmUfZw3EZCcvGFGrRMIbIr8="
+ [mod."github.com/ryanuber/columnize"]
+ version = "v0.0.0-20160712163229-9b3edd62028f"
+ hash = "sha256-RLUQcU6Z03upKe08v6rjn9/tkyrQsgmpdEmBtWaLQfk="
+ [mod."github.com/sean-/seed"]
+ version = "v0.0.0-20170313163322-e2103e2c3529"
+ hash = "sha256-RQQTjvf8Y91jP5FGOyEnGMFw7zCrcSnUU4eH2CXKkT4="
+ [mod."github.com/sirupsen/logrus"]
+ version = "v1.4.2"
+ hash = "sha256-3QzWUsapCmg3F7JqUuINT3/UG097uzLff6iCcCgQ43o="
+ [mod."github.com/stretchr/objx"]
+ version = "v0.1.1"
+ hash = "sha256-HdGVZCuy7VprC5W9UxGbDmXqsKADMjpEDht7ilGVLco="
+ [mod."github.com/stretchr/testify"]
+ version = "v1.3.0"
+ hash = "sha256-+mSebBNccNcxbY462iKTNTWmd5ZuUkUqFebccn3EtIA="
+ [mod."golang.org/x/crypto"]
+ version = "v0.0.0-20200604202706-70a84ac30bf9"
+ hash = "sha256-I5ov2lV6ubB3H3pn6DFN1VPLyxeLfPUaOx2PJ9K1KH8="
+ [mod."golang.org/x/net"]
+ version = "v0.0.0-20190404232315-eb5bcb51f2a3"
+ hash = "sha256-kwP+BDfPsZEeeceyg4H5LgdTDT8O8YWbkpCkpyuPJJo="
+ [mod."golang.org/x/sync"]
+ version = "v0.0.0-20181221193216-37e7f081c4d4"
+ hash = "sha256-FUpv9bmGLDEjCxXdvR0CRDqOKRY0xrHRPzOsyQyvYK0="
+ [mod."golang.org/x/sys"]
+ version = "v0.0.0-20190422165155-953cdadca894"
+ hash = "sha256-YkQjSZaiVFxrmXYSKmqrGeIO3RZ95fc3dWyCaR+SosY="
+ [mod."golang.org/x/text"]
+ version = "v0.3.0"
+ hash = "sha256-0FFbaxF1ZuAQF3sCcA85e8MO6prFeHint36inija4NY="
diff --git a/nix/builder/default.nix b/nix/builder/default.nix
new file mode 100644
index 0000000..8b8ae19
--- /dev/null
+++ b/nix/builder/default.nix
@@ -0,0 +1,387 @@
+{ stdenv
+, stdenvNoCC
+, runCommand
+, buildEnv
+, lib
+, fetchgit
+, removeReferencesTo
+, jq
+, cacert
+, pkgs
+, pkgsBuildBuild
+}:
+let
+
+ inherit (builtins) substring toJSON hasAttr trace split readFile elemAt;
+ inherit (lib)
+ concatStringsSep replaceStrings removePrefix optionalString pathExists
+ optional concatMapStrings fetchers filterAttrs mapAttrs mapAttrsToList
+ warnIf optionalAttrs platforms
+ ;
+
+ parseGoMod = import ./parser.nix;
+
+ removeExpr = refs: ''remove-references-to ${concatMapStrings (ref: " -t ${ref}") refs}'';
+
+ # Internal only build-time attributes
+ internal =
+ let
+ mkInternalPkg = name: src: pkgsBuildBuild.runCommand "gomod2nix-${name}"
+ {
+ inherit (pkgsBuildBuild.go) GOOS GOARCH;
+ nativeBuildInputs = [ pkgsBuildBuild.go ];
+ } ''
+ export HOME=$(mktemp -d)
+ cp ${src} src.go
+ go build -o $out src.go
+ '';
+ in
+ {
+
+ # Create a symlink tree of vendored sources
+ symlink = mkInternalPkg "symlink" ./symlink/symlink.go;
+
+ # Install development dependencies from tools.go
+ install = mkInternalPkg "symlink" ./install/install.go;
+
+ };
+
+ fetchGoModule =
+ { hash
+ , goPackagePath
+ , version
+ , go ? pkgs.go
+ }:
+ stdenvNoCC.mkDerivation {
+ name = "${baseNameOf goPackagePath}_${version}";
+ builder = ./fetch.sh;
+ inherit goPackagePath version;
+ nativeBuildInputs = [ go jq ];
+ outputHashMode = "recursive";
+ outputHashAlgo = null;
+ outputHash = hash;
+ SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt";
+ impureEnvVars = fetchers.proxyImpureEnvVars ++ [ "GOPROXY" ];
+ };
+
+ mkVendorEnv =
+ { go
+ , modulesStruct
+ , localReplaceCommands ? [ ]
+ , defaultPackage ? ""
+ , goMod
+ , pwd
+ }:
+ let
+ localReplaceCommands =
+ let
+ localReplaceAttrs = filterAttrs (n: v: hasAttr "path" v) goMod.replace;
+ commands = (
+ mapAttrsToList
+ (name: value: (
+ ''
+ mkdir -p $(dirname vendor/${name})
+ ln -s ${pwd + "/${value.path}"} vendor/${name}
+ ''
+ ))
+ localReplaceAttrs);
+ in
+ if goMod != null then commands else [ ];
+
+ sources = mapAttrs
+ (goPackagePath: meta: fetchGoModule {
+ goPackagePath = meta.replaced or goPackagePath;
+ inherit (meta) version hash;
+ inherit go;
+ })
+ modulesStruct.mod;
+ in
+ runCommand "vendor-env"
+ {
+ nativeBuildInputs = [ go ];
+ json = toJSON (filterAttrs (n: _: n != defaultPackage) modulesStruct.mod);
+
+ sources = toJSON (filterAttrs (n: _: n != defaultPackage) sources);
+
+ passthru = {
+ inherit sources;
+ };
+
+ passAsFile = [ "json" "sources" ];
+ }
+ (
+ ''
+ mkdir vendor
+
+ export GOCACHE=$TMPDIR/go-cache
+ export GOPATH="$TMPDIR/go"
+
+ ${internal.symlink}
+ ${concatStringsSep "\n" localReplaceCommands}
+
+ mv vendor $out
+ ''
+ );
+
+ # Select Go attribute based on version specified in go.mod
+ selectGo = attrs: goMod: attrs.go or (if goMod == null then pkgs.go else
+ (
+ let
+ goVersion = goMod.go;
+ goAttr = "go_" + (replaceStrings [ "." ] [ "_" ] goVersion);
+ in
+ (
+ if hasAttr goAttr pkgs then pkgs.${goAttr}
+ else trace "go.mod specified Go version ${goVersion} but doesn't exist. Falling back to ${pkgs.go.version}." pkgs.go
+ )
+ ));
+
+ # Strip the rubbish that Go adds to versions, and fall back to a version based on the date if it's a placeholder value
+ stripVersion = version:
+ let
+ parts = elemAt (split "(\\+|-)" (removePrefix "v" version));
+ v = parts 0;
+ d = parts 2;
+ in
+ if v != "0.0.0" then v else "unstable-" + (concatStringsSep "-" [
+ (substring 0 4 d)
+ (substring 4 2 d)
+ (substring 6 2 d)
+ ]);
+
+ mkGoEnv =
+ { pwd
+ }@attrs:
+ let
+ goMod = parseGoMod (readFile "${toString pwd}/go.mod");
+ modulesStruct = fromTOML (readFile "${toString pwd}/gomod2nix.toml");
+
+ go = selectGo attrs goMod;
+
+ vendorEnv = mkVendorEnv {
+ inherit go modulesStruct pwd goMod;
+ };
+
+ in
+ stdenv.mkDerivation (removeAttrs attrs [ "pwd" ] // {
+ name = "${baseNameOf goMod.module}-env";
+
+ dontUnpack = true;
+ dontConfigure = true;
+ dontInstall = true;
+
+ propagatedNativeBuildInputs = [ go ];
+
+ GO_NO_VENDOR_CHECKS = "1";
+
+ GO111MODULE = "on";
+ GOFLAGS = "-mod=vendor";
+
+ preferLocalBuild = true;
+
+ buildPhase = ''
+ mkdir $out
+
+ export GOCACHE=$TMPDIR/go-cache
+ export GOPATH="$out"
+ export GOSUMDB=off
+ export GOPROXY=off
+
+ '' + optionalString (pathExists (pwd + "/tools.go")) ''
+ mkdir source
+ cp ${pwd + "/go.mod"} source/go.mod
+ cp ${pwd + "/go.sum"} source/go.sum
+ cp ${pwd + "/tools.go"} source/tools.go
+ cd source
+ ln -s ${vendorEnv} vendor
+
+ ${internal.install}
+ '';
+ });
+
+ buildGoApplication =
+ { modules ? pwd + "/gomod2nix.toml"
+ , src ? pwd
+ , pwd ? null
+ , nativeBuildInputs ? [ ]
+ , allowGoReference ? false
+ , meta ? { }
+ , passthru ? { }
+ , tags ? [ ]
+
+ # needed for buildFlags{,Array} warning
+ , buildFlags ? ""
+ , buildFlagsArray ? ""
+
+ , ...
+ }@attrs:
+ let
+ modulesStruct = fromTOML (readFile modules);
+
+ goModPath = "${toString pwd}/go.mod";
+
+ goMod =
+ if pwd != null && pathExists goModPath
+ then parseGoMod (readFile goModPath)
+ else null;
+
+ go = selectGo attrs goMod;
+
+ removeReferences = [ ] ++ optional (!allowGoReference) go;
+
+ defaultPackage = modulesStruct.goPackagePath or "";
+
+ vendorEnv = mkVendorEnv {
+ inherit go modulesStruct defaultPackage goMod pwd;
+ };
+
+ in
+ warnIf (buildFlags != "" || buildFlagsArray != "")
+ "Use the `ldflags` and/or `tags` attributes instead of `buildFlags`/`buildFlagsArray`"
+ stdenv.mkDerivation
+ (optionalAttrs (defaultPackage != "")
+ {
+ pname = attrs.pname or baseNameOf defaultPackage;
+ version = stripVersion (modulesStruct.mod.${defaultPackage}).version;
+ src = vendorEnv.passthru.sources.${defaultPackage};
+ } // optionalAttrs (hasAttr "subPackages" modulesStruct) {
+ subPackages = modulesStruct.subPackages;
+ } // attrs // {
+ nativeBuildInputs = [ removeReferencesTo go ] ++ nativeBuildInputs;
+
+ inherit (go) GOOS GOARCH;
+
+ GO_NO_VENDOR_CHECKS = "1";
+
+ GO111MODULE = "on";
+ GOFLAGS = "-mod=vendor";
+
+ configurePhase = attrs.configurePhase or ''
+ runHook preConfigure
+
+ export GOCACHE=$TMPDIR/go-cache
+ export GOPATH="$TMPDIR/go"
+ export GOSUMDB=off
+ export GOPROXY=off
+ cd "$modRoot"
+ if [ -n "${vendorEnv}" ]; then
+ rm -rf vendor
+ ln -s ${vendorEnv} vendor
+ fi
+
+ runHook postConfigure
+ '';
+
+ buildPhase = attrs.buildPhase or ''
+ runHook preBuild
+
+ exclude='\(/_\|examples\|Godeps\|testdata'
+ if [[ -n "$excludedPackages" ]]; then
+ IFS=' ' read -r -a excludedArr <<<$excludedPackages
+ printf -v excludedAlternates '%s\\|' "''${excludedArr[@]}"
+ excludedAlternates=''${excludedAlternates%\\|} # drop final \| added by printf
+ exclude+='\|'"$excludedAlternates"
+ fi
+ exclude+='\)'
+
+ buildGoDir() {
+ local d; local cmd;
+ cmd="$1"
+ d="$2"
+ . $TMPDIR/buildFlagsArray
+ local OUT
+ if ! OUT="$(go $cmd $buildFlags "''${buildFlagsArray[@]}" ''${tags:+-tags=${concatStringsSep "," tags}} ''${ldflags:+-ldflags="$ldflags"} -v -p $NIX_BUILD_CORES $d 2>&1)"; then
+ if echo "$OUT" | grep -qE 'imports .*?: no Go files in'; then
+ echo "$OUT" >&2
+ return 1
+ fi
+ if ! echo "$OUT" | grep -qE '(no( buildable| non-test)?|build constraints exclude all) Go (source )?files'; then
+ echo "$OUT" >&2
+ return 1
+ fi
+ fi
+ if [ -n "$OUT" ]; then
+ echo "$OUT" >&2
+ fi
+ return 0
+ }
+
+ getGoDirs() {
+ local type;
+ type="$1"
+ if [ -n "$subPackages" ]; then
+ echo "$subPackages" | sed "s,\(^\| \),\1./,g"
+ else
+ find . -type f -name \*$type.go -exec dirname {} \; | grep -v "/vendor/" | sort --unique | grep -v "$exclude"
+ fi
+ }
+
+ if (( "''${NIX_DEBUG:-0}" >= 1 )); then
+ buildFlagsArray+=(-x)
+ fi
+
+ if [ ''${#buildFlagsArray[@]} -ne 0 ]; then
+ declare -p buildFlagsArray > $TMPDIR/buildFlagsArray
+ else
+ touch $TMPDIR/buildFlagsArray
+ fi
+ if [ -z "$enableParallelBuilding" ]; then
+ export NIX_BUILD_CORES=1
+ fi
+ for pkg in $(getGoDirs ""); do
+ echo "Building subPackage $pkg"
+ buildGoDir install "$pkg"
+ done
+ '' + optionalString (stdenv.hostPlatform != stdenv.buildPlatform) ''
+ # normalize cross-compiled builds w.r.t. native builds
+ (
+ dir=$GOPATH/bin/${go.GOOS}_${go.GOARCH}
+ if [[ -n "$(shopt -s nullglob; echo $dir/*)" ]]; then
+ mv $dir/* $dir/..
+ fi
+ if [[ -d $dir ]]; then
+ rmdir $dir
+ fi
+ )
+ '' + ''
+ runHook postBuild
+ '';
+
+ doCheck = attrs.doCheck or true;
+ checkPhase = attrs.checkPhase or ''
+ runHook preCheck
+
+ for pkg in $(getGoDirs test); do
+ buildGoDir test $checkFlags "$pkg"
+ done
+
+ runHook postCheck
+ '';
+
+ installPhase = attrs.installPhase or ''
+ runHook preInstall
+
+ mkdir -p $out
+ dir="$GOPATH/bin"
+ [ -e "$dir" ] && cp -r $dir $out
+
+ runHook postInstall
+ '';
+
+ preFixup = (attrs.preFixup or "") + ''
+ find $out/{bin,libexec,lib} -type f 2>/dev/null | xargs -r ${removeExpr removeReferences} || true
+ '';
+
+ strictDeps = true;
+
+ disallowedReferences = optional (!allowGoReference) go;
+
+ passthru = { inherit go vendorEnv; } // passthru;
+
+ meta = { platforms = go.meta.platforms or platforms.all; } // meta;
+ });
+
+in
+{
+ inherit buildGoApplication mkGoEnv;
+}
diff --git a/nix/builder/fetch.sh b/nix/builder/fetch.sh
new file mode 100644
index 0000000..8c6bdb3
--- /dev/null
+++ b/nix/builder/fetch.sh
@@ -0,0 +1,13 @@
+source $stdenv/setup
+
+export HOME=$(mktemp -d)
+
+# Call once first outside of subshell for better error reporting
+go mod download "$goPackagePath@$version"
+
+dir=$(go mod download --json "$goPackagePath@$version" | jq -r .Dir)
+
+chmod -R +w $dir
+find $dir -iname ".ds_store" | xargs -r rm -rf
+
+cp -r $dir $out
diff --git a/nix/builder/install/install.go b/nix/builder/install/install.go
new file mode 100644
index 0000000..4f770b0
--- /dev/null
+++ b/nix/builder/install/install.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+ "fmt"
+ "go/parser"
+ "go/token"
+ "io"
+ "os"
+ "os/exec"
+ "strconv"
+)
+
+const filename = "tools.go"
+
+func main() {
+ fset := token.NewFileSet()
+
+ var src []byte
+ {
+ f, err := os.Open(filename)
+ if err != nil {
+ panic(err)
+ }
+
+ src, err = io.ReadAll(f)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ f, err := parser.ParseFile(fset, filename, src, parser.ImportsOnly)
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ for _, s := range f.Imports {
+ path, err := strconv.Unquote(s.Path.Value)
+ if err != nil {
+ panic(err)
+ }
+
+ cmd := exec.Command("go", "install", path)
+
+ fmt.Printf("Executing '%s'\n", cmd)
+
+ err = cmd.Start()
+ if err != nil {
+ panic(err)
+ }
+
+ err = cmd.Wait()
+ if err != nil {
+ panic(err)
+ }
+ }
+}
diff --git a/nix/builder/parser.nix b/nix/builder/parser.nix
new file mode 100644
index 0000000..eb6f75e
--- /dev/null
+++ b/nix/builder/parser.nix
@@ -0,0 +1,141 @@
+# Parse go.mod in Nix
+# Returns a Nix structure with the contents of the go.mod passed in
+# in normalised form.
+
+let
+ inherit (builtins) elemAt mapAttrs split foldl' match filter typeOf hasAttr length;
+
+ # Strip lines with comments & other junk
+ stripStr = s: elemAt (split "^ *" (elemAt (split " *$" s) 0)) 2;
+ stripLines = initialLines: foldl' (acc: f: f acc) initialLines [
+ # Strip comments
+ (lines: map
+ (l: stripStr (elemAt (splitString "//" l) 0))
+ lines)
+
+ # Strip leading tabs characters
+ (lines: map (l: elemAt (match "(\t)?(.*)" l) 1) lines)
+
+ # Filter empty lines
+ (filter (l: l != ""))
+ ];
+
+ # Parse lines into a structure
+ parseLines = lines: (foldl'
+ (acc: l:
+ let
+ m = match "([^ )]*) *(.*)" l;
+ directive = elemAt m 0;
+ rest = elemAt m 1;
+
+ # Maintain parser state (inside parens or not)
+ inDirective =
+ if rest == "(" then directive
+ else if rest == ")" then null
+ else acc.inDirective
+ ;
+
+ in
+ {
+ data = (acc.data // (
+ if directive == "" && rest == ")" then { }
+ else if inDirective != null && rest == "(" && ! hasAttr inDirective acc.data then {
+ ${inDirective} = { };
+ }
+ else if rest == "(" || rest == ")" then { }
+ else if inDirective != null then {
+ ${inDirective} = acc.data.${inDirective} // { ${directive} = rest; };
+ } else if directive == "replace" then
+ (
+ let
+ segments = split " => " rest;
+ getSegment = elemAt segments;
+ in
+ assert length segments == 3; {
+ replace = acc.data.replace // {
+ ${getSegment 0} = "=> ${getSegment 2}";
+ };
+ }
+ )
+ else {
+ ${directive} = rest;
+ }
+ )
+ );
+ inherit inDirective;
+ })
+ {
+ inDirective = null;
+ data = {
+ require = { };
+ replace = { };
+ exclude = { };
+ };
+ }
+ lines
+ ).data;
+
+ normaliseDirectives = data: (
+ let
+ normaliseString = s:
+ let
+ m = builtins.match "([^ ]+) (.+)" s;
+ in
+ {
+ ${elemAt m 0} = elemAt m 1;
+ };
+ require = data.require or { };
+ replace = data.replace or { };
+ exclude = data.exclude or { };
+ in
+ data // {
+ require =
+ if typeOf require == "string" then normaliseString require
+ else require;
+ replace =
+ if typeOf replace == "string" then normaliseString replace
+ else replace;
+ }
+ );
+
+ parseVersion = ver:
+ let
+ m = elemAt (match "([^-]+)-?([^-]*)-?([^-]*)" ver);
+ v = elemAt (match "([^+]+)\\+?(.*)" (m 0));
+ in
+ {
+ version = v 0;
+ versionSuffix = v 1;
+ date = m 1;
+ rev = m 2;
+ };
+
+ parseReplace = data: (
+ data // {
+ replace =
+ mapAttrs
+ (_: v:
+ let
+ m = match "=> ([^ ]+) (.+)" v;
+ m2 = match "=> (.*+)" v;
+ in
+ if m != null then {
+ goPackagePath = elemAt m 0;
+ version = elemAt m 1;
+ } else {
+ path = elemAt m2 0;
+ })
+ data.replace;
+ }
+ );
+
+ splitString = sep: s: filter (t: t != [ ]) (split sep s);
+
+in
+contents:
+foldl' (acc: f: f acc) (splitString "\n" contents) [
+ stripLines
+ parseLines
+ normaliseDirectives
+ parseReplace
+]
diff --git a/nix/builder/symlink/symlink.go b/nix/builder/symlink/symlink.go
new file mode 100644
index 0000000..3dbb383
--- /dev/null
+++ b/nix/builder/symlink/symlink.go
@@ -0,0 +1,110 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+)
+
+type Package struct {
+ GoPackagePath string `json:"-"`
+ Version string `json:"version"`
+ Hash string `json:"hash"`
+ ReplacedPath string `json:"replaced,omitempty"`
+}
+
+// type Output struct {
+// SchemaVersion int `json:"schema"`
+// Mod map[string]*Package `json:"mod"`
+// }
+
+func main() {
+
+ // var output Output
+ sources := make(map[string]string)
+ pkgs := make(map[string]*Package)
+
+ {
+ b, err := ioutil.ReadFile(os.Getenv("sourcesPath"))
+ if err != nil {
+ panic(err)
+ }
+
+ err = json.Unmarshal(b, &sources)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ {
+ b, err := ioutil.ReadFile(os.Getenv("jsonPath"))
+ if err != nil {
+ panic(err)
+ }
+
+ err = json.Unmarshal(b, &pkgs)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ keys := make([]string, 0, len(pkgs))
+ for key := range pkgs {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+
+ // Iterate, in reverse order
+ for i := len(keys) - 1; i >= 0; i-- {
+ key := keys[i]
+ src := sources[key]
+
+ paths := []string{key}
+
+ for _, path := range paths {
+
+ vendorDir := filepath.Join("vendor", filepath.Dir(path))
+ if err := os.MkdirAll(vendorDir, 0755); err != nil {
+ panic(err)
+ }
+
+ if _, err := os.Stat(filepath.Join("vendor", path)); err == nil {
+ files, err := ioutil.ReadDir(src)
+ if err != nil {
+ panic(err)
+ }
+
+ for _, f := range files {
+ innerSrc := filepath.Join(src, f.Name())
+ dst := filepath.Join("vendor", path, f.Name())
+ if err := os.Symlink(innerSrc, dst); err != nil {
+ // assume it's an existing directory, try to link the directory content instead.
+ // TODO should we do this recursively
+ files, err := ioutil.ReadDir(innerSrc)
+ if err != nil {
+ panic(err)
+ }
+ for _, f := range files {
+ if err := os.Symlink(filepath.Join(innerSrc, f.Name()), filepath.Join(dst, f.Name())); err != nil {
+ fmt.Println("ignore symlink error", filepath.Join(innerSrc, f.Name()), filepath.Join(dst, f.Name()))
+ }
+ }
+ }
+ }
+
+ continue
+ }
+
+ // If the file doesn't already exist, just create a simple symlink
+ err := os.Symlink(src, filepath.Join("vendor", path))
+ if err != nil {
+ panic(err)
+ }
+
+ }
+ }
+
+}