hydra/src/root/reproduce.tt
Josh McSavaney e0d3a1c1a5
Make nix-build args copy-pastable via set -x
A reproduce script includes a logline that may resemble:

> using these flags: --arg nixpkgs { outPath = /tmp/build-137689173/nixpkgs/source; rev = "fdc872fa200a32456f12cc849d33b1fdbd6a933c"; shortRev = "fdc872f"; revCount = 273100; } -I nixpkgs=/tmp/build-137689173/nixpkgs/source --arg officialRelease false --option extra-binary-caches https://hydra.nixos.org/ --option system x86_64-linux /tmp/build-137689173/nixpkgs/source/pkgs/top-level/release.nix -A 

These are passed along to nix-build and that's fine and dandy, but you can't just copy-paste this as is, as the `{}` introduces a syntax error and the value accompanying `-A` is `''`.

A very naive approach is to just `printf "%q"` the individual args, which makes them safe to copy-paste. Unfortunately, this looks awful due to the liberal usage of slashes:

```
$ printf "%q" '{ outPath = /tmp/build-137689173/nixpkgs/source; rev = "fdc872fa200a32456f12cc849d33b1fdbd6a933c"; shortRev = "fdc872f"; revCount = 273100; }'
\{\ outPath\ =\ /tmp/build-137689173/nixpkgs/source\;\ rev\ =\ \"fdc872fa200a32456f12cc849d33b1fdbd6a933c\"\;\ shortRev\ =\ \"fdc872f\"\;\ revCount\ =\ 273100\;\ \}
```

Alternatively, if we just use `set -x` before we execute nix-build, we'll get the whole invocation in a friendly, copy-pastable format that nicely displays `{}`-enclosed content and preserves the empty arg following `-A`:

```
running nix-build...
using this invocation: 
+ nix-build --arg nixpkgs '{ outPath = /tmp/build-138165173/nixpkgs/source; rev = "e0e4484f2c028d2269f5ebad0660a51bbe46caa4"; shortRev = "e0e4484"; revCount = 274008; }' -I nixpkgs=/tmp/build-138165173/nixpkgs/source --arg officialRelease false --option extra-binary-caches https://hydra.nixos.org/ --option system x86_64-linux /tmp/build-138165173/nixpkgs/source/pkgs/top-level/release.nix -A ''
```
2021-03-06 23:25:26 -05:00

218 lines
5.8 KiB
Bash
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#! /usr/bin/env bash
main() {
# This script has been generated automatically by Hydra from the build
# at [% c.uri_for('/build' build.id) %].
set -e
tmpDir=${TMPDIR:-/tmp}/build-[% build.id +%]
declare -a args extraArgs
info() {
echo "$1" >&2
}
# Process the command line.
fetchOnly=
printFlags=
while [ $# -gt 0 ]; do
arg="$1"
shift
if [ "$arg" = --help ]; then
cat <<EOF
Usage: $0 [--dir PATH] [--run-env]
This script will reproduce Hydra build [% build.id %] of job [%
build.project.name %]:[% build.jobset.name %]:[% build.job.name +%]
(available at [%+ c.uri_for('/build' build.id) +%]). It will fetch
all inputs of the Hydra build, then invoke Nix to build the job and
all its dependencies.
The inputs will be stored in $tmpDir. This can be overriden using the
--dir flag. After the build, the result of the build is available via
the symlink $tmpDir/result.
Flags:
--dir PATH
Override the location where the inputs and result symlink are stored.
--run-env
Fetch the inputs and build the dependencies, then start an
interactive shell in which the environment is equal to that used
to perform the build. See the description of the --run-env flag
in the nix-build(1) manpage for more details.
--fetch
Fetch the inputs and then exit.
--print-flags
Fetch the inputs, then print the argument to nix-build on stdout
and exit.
Any additional flags are passed to nix-build. See the nix-build(1)
manpage for details.
EOF
exit 0
elif [ "$arg" = --dir ]; then
tmpDir="$1"
if [ -z "$tmpDir" ]; then
echo "$0: --dir requires an argument" >&2
exit 1
fi
shift
elif [ "$arg" = --fetch ]; then
fetchOnly=1
elif [ "$arg" = --print-flags ]; then
printFlags=1
else
extraArgs+=("$arg")
fi
done
mkdir -p "$tmpDir"
cd "$tmpDir"
info "storing inputs and results in $tmpDir..."
requireCommand() {
local cmd="$1"
if ! type -P "$cmd" > /dev/null; then
echo "$0: command $cmd is not installed; please install it and try again" >&2
exit 1
fi
return 0
}
# Fetch the inputs.
[% inputs = build.inputs ? build.inputs : eval.jobsetevalinputs %]
[%+ FOREACH input IN inputs %]
inputDir=
[%+ IF input.type == "git" %]
inputDir="$tmpDir/[% input.name %]/source"
if ! [ -d "$inputDir" ]; then
info "fetching Git input [% input.name %] from [% input.uri %] (commit [% input.revision %])..."
requireCommand git
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"
echo -n $revCount > "$tmpDir/[% input.name %]/rev-count"
else
revCount="$(cat "$tmpDir/[% input.name %]/rev-count")"
fi
args+=(--arg '[% input.name %]' "{ outPath = $inputDir; rev = \"[% input.revision %]\"; shortRev = \"[% input.revision.substr(0, 7) %]\"; revCount = $revCount; }")
[%+ ELSIF input.type == "hg" %]
inputDir="$tmpDir/[% input.name %]/source"
if ! [ -d "$inputDir" ]; then
info "fetching Mercurial input [% input.name %] from [% input.uri %] (commit [% input.revision %])..."
requireCommand hg
inputDirTmp="$inputDir.tmp"
rm -rf "$inputDirTmp"
mkdir -p "$inputDirTmp"
hg clone '[% input.uri %]' "$inputDirTmp"
(cd "$inputDirTmp" && hg update '[% input.revision %]')
revCount="$(cd "$inputDirTmp" && (hg log -r '[% input.revision %]' --template "{rev}"))"
rm -rf "$inputDirTmp/.hg"
mv "$inputDirTmp" "$inputDir"
echo -n $revCount > "$tmpDir/[% input.name %]/rev-count"
else
revCount="$(cat "$tmpDir/[% input.name %]/rev-count")"
fi
args+=(--arg '[% input.name %]' "{ outPath = $inputDir; rev = \"[% input.revision %]\"; revCount = $revCount; }")
[%+ ELSIF input.type == "svn" %]
inputDir="$tmpDir/[% input.name %]/source"
if ! [ -d "$inputDir" ]; then
info "fetching Subversion input [% input.name %] from [% input.uri %] (commit [% input.revision %])..."
requireCommand svn
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 %]"') # FIXME: escape
[% ELSIF input.type == "boolean" %]
args+=(--arg '[% input.name %]' '[% input.value %]')
[% ELSIF input.type == "nix" %]
args+=(--arg '[% input.name %]' '[% input.value %]') # FIXME: escape
[% ELSE %]
echo "$0: input [% input.name %] has unsupported type [% input.type %]" >&2
exit 1
[% END %]
[% IF input.name == eval.nixexprinput +%]
nixExprInputDir="$inputDir"
[%+ END %]
if [ -n "$inputDir" ]; then
args+=(-I [% input.name %]=$inputDir)
fi
[%+ END %]
if [ -n "$fetchOnly" ]; then exit 0; fi
# Run nix-build.
requireCommand nix-build
if [ -z "$nixExprInputDir" ]; then
echo "$0: don't know the path to the Nix expression!" >&2
exit 1
fi
args+=(--option extra-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)
args+=("$nixExprInputDir/[% eval.nixexprpath %]" -A '[% build.job.name %]')
if [ -n "$printFlags" ]; then
first=1
for i in "${args[@]}"; do
if [ -z "$first" ]; then printf " "; fi
first=
printf "%q" "$i"
done
exit 0
fi
info "running nix-build..."
echo "using the following invocation:" >&2
set -x
nix-build "${args[@]}" "${extraArgs[@]}"
}
main "$@"