From 9f027b22b1ec86d8123a53c3663d943e3854c503 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 4 Apr 2013 17:30:07 +0200 Subject: [PATCH] Allow users to reproduce builds on their own systems You can now do: bash <(curl http://hydra-server/build/1238757/reproduce) to download and execute a script that reproduces a Hydra build locally. This script fetches all inputs (e.g. Git repositories) and then invokes nix-build. The downloaded sources are stored in /tmp/build- and reused between invocations of the script. Any additional command line options are passed to nix-build. So bash <(curl http://hydra-server/build/1238757/reproduce) --run-env will drop you in a shell where you can interactively hack on the build, e.g. $ source $stdenv/setup $ set +e $ unpackPhase $ cd $sourceRoot $ configurePhase $ emacs foo.c & $ make and so on. --- src/lib/Hydra/Controller/Build.pm | 8 +++ src/root/build.tt | 7 ++ src/root/reproduce.tt | 107 ++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 src/root/reproduce.tt diff --git a/src/lib/Hydra/Controller/Build.pm b/src/lib/Hydra/Controller/Build.pm index ebf83d4f..4a05edd6 100644 --- a/src/lib/Hydra/Controller/Build.pm +++ b/src/lib/Hydra/Controller/Build.pm @@ -593,4 +593,12 @@ sub evals : Chained('build') PathPart('evals') Args(0) { } +sub reproduce : Chained('build') PathPart('reproduce') Args(0) { + my ($self, $c) = @_; + $c->response->content_type('text/x-shellscript'); + $c->response->header('Content-Disposition', 'attachment; filename="reproduce-build-' . $c->stash->{build}->id . '.sh"'); + $c->stash->{template} = 'reproduce.tt'; +} + + 1; diff --git a/src/root/build.tt b/src/root/build.tt index 49a41d25..5ed635ca 100644 --- a/src/root/build.tt +++ b/src/root/build.tt @@ -77,6 +77,13 @@
+ [% IF build.nixexprinput %] + + Reproduce this build locally + + [% END %] +
diff --git a/src/root/reproduce.tt b/src/root/reproduce.tt new file mode 100644 index 00000000..0d394ca8 --- /dev/null +++ b/src/root/reproduce.tt @@ -0,0 +1,107 @@ +#! /bin/sh + +# This script has been generated automatically by Hydra from the build +# at [% c.uri_for('/build' build.id) %]. + +set -e + +export NIX_PATH= + +srcDir=${TMPDIR:-/tmp}/build-[% build.id +%] +mkdir -p "$srcDir"/build +cd "$srcDir"/build +echo "storing sources in $srcDir" >&2 + +declare -a args + + +# Fetch the inputs. + +[%+ FOREACH input IN build.inputs %] +inputDir= + +[%+ IF input.type == "git" %] + +inputDir="$srcDir/[% input.name %]/git-export" + +if ! [ -d "$inputDir" ]; then + echo "fetching Git input ‘[% input.name %]’ from ‘[% input.uri %]’ (commit [% input.revision %])..." >&2 + inputDirTmp="$inputDir.tmp" + rm -rf "$inputDirTmp" + mkdir -p "$inputDirTmp" + git clone '[% input.uri %]' "$inputDirTmp" + (cd "$inputDirTmp" && git checkout '[% input.revision %]') + revCount="$(cd "$inputDirTmp" && (git rev-list '[% input.revision %]' | wc -l))" + rm -rf "$inputDirTmp/.git" + mv "$inputDirTmp" "$inputDir" +fi + +args+=(--arg '[% input.name %]' "{ outPath = $inputDir; rev = \"[% input.revision %]\"; shortRev = \"[% input.revision.substr(0, 7) %]\"; revCount = \"$revCount\"; }") + +[%+ ELSIF input.type == "hg" %] + +inputDir="$srcDir/[% input.name %]/hg-archive" + +if ! [ -d "$inputDir" ]; then + echo "fetching Mercurial input ‘[% input.name %]’ from ‘[% input.uri %]’ (commit [% input.revision %])..." >&2 + inputDirTmp="$inputDir.tmp" + rm -rf "$inputDirTmp" + hg clone '[% input.uri %]' "$inputDirTmp" -r '[% input.revision %]' + rm -rf "$inputDirTmp/.hg" + mv "$inputDirTmp" "$inputDir" +fi + +args+=(--arg '[% input.name %]' "{ outPath = $inputDir; rev = \"[% input.revision %]\"; revCount = \"$revCount\"; }") + +[%+ ELSIF input.type == "svn" %] + +inputDir="$srcDir/[% input.name %]/svn-export" + +if ! [ -d "$inputDir" ]; then + echo "fetching Subversion input ‘[% input.name %]’ from ‘[% input.uri %]’ (commit [% input.revision %])..." >&2 + rm -rf "$inputDir.tmp" + svn export '[% input.uri %]@[% input.revision %]' "$inputDir.tmp" + mv "$inputDir.tmp" "$inputDir" +fi + +args+=(--arg '[% input.name %]' "{ outPath = $inputDir; rev = \"[% input.revision %]\"; }") + +[% ELSIF input.type == "string" %] +args+=(--arg '[% input.name %]' '"[% input.value %]"') + +[% ELSIF input.type == "boolean" %] +args+=(--arg '[% input.name %]' '[% input.value %]') + +[% ELSE %] +echo "$0: input ‘[% input.name %]’ has unsupported type ‘[% input.type %]’" >&2 +exit 1 +[% END %] + +[% IF input.name == build.nixexprinput +%] +nixExprInputDir="$inputDir" +[%+ END %] + +if [ -n "$inputDir" ]; then + NIX_PATH="$NIX_PATH${NIX_PATH:+:}[% input.name %]=$inputDir" +fi + +[%+ END %] + + +# Run nix-build. + +if [ -z "$nixExprInputDir" ]; then + echo "$0: don't know the path to the Nix expression!" >&2 + exit 1 +fi + +#args+=(--option binary-caches '[% c.uri_for('/') %]') + +# Since Hydra runs on x86_64-linux, pretend we're one. This matters +# when evaluating jobs that rely on builtins.currentSystem. +args+=(--option system x86_64-linux) + +echo "Nix path is $NIX_PATH" >&2 +echo "Nix args are ${args[@]}" >&2 + +nix-build "$nixExprInputDir/[% build.nixexprpath %]" -A '[% build.job.name %]' "${args[@]}" "$@"