summaryrefslogtreecommitdiffstats
path: root/pkgs/build-support/fetchdarcs/nix-prefetch-darcs
blob: b379aa2b23c3d041f66f1d088cf0941af45aa7d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/bin/sh
set -eu

quiet=
name="fetchdarcs"
repository=
tag=
context=
darcs_hash=
exp_hash=

usage() {
	echo "Usage: nix-prefetch-darcs [options] [REPOSITORY] [FILENAME [EXPECTED-HASH]]"
	echo
	echo "Options:"
	echo "	--quiet               Suppress most error messages."
	echo "	--name <NAME>         Symbolic store path name to use for the result."
	echo "	--repo <REPOSITORY>   URL for the Darcs repository."
	echo "	--tag <REGEXP>        Clone specified by tag matching a regular expression."
	echo "	--context <FILENAME>  Clone specified by context file."
	echo "	--darcs-hash <HASH>   Clone specified by hash. WARN: hash order is fickle by design."
	echo "	--hash <HASH>         Expected hash."
	echo "	--help                Show this help message."
}

# Argument parsing
while [ $# -gt 0 ]; do
	case "$1" in
		--quiet)
			quiet=1; shift 1 ;;
		--name)
			name="$2"; shift 2 ;;
		--repo)
			repository="$2"; shift 2 ;;
		--tag)
			tag="$2"; shift 2 ;;
		--context)
			context="$2"; shift 2 ;;
		--darcs-hash)
			darcs_hash="$2"; shift 2 ;;
		--hash)
			exp_hash="$2"; shift 2 ;;
		--help)
			usage; exit 0 ;;
		*)
			# Positional arguments
			if [ -z "$repository" ]; then
				repository="$1"
				shift
			elif [ -z "$context" ]; then
				context="$1"
				shift
			elif [ -z "$exp_hash" ]; then
				exp_hash="$1"
				shift
			else
				echo "Error: Too many arguments" >&2
				usage
				exit 1
			fi
			;;
	esac
done

if [ -z "$repository" ]; then
	echo "Error: URL for repository is required." >&2
	echo >&2
	usage
	exit 1
fi

state_flag_count=0
[ -n "$tag" ] && state_flag_count=$(( state_flag_count + 1 ))
[ -n "$context" ] && state_flag_count=$(( state_flag_count + 1 ))
[ -n "$darcs_hash" ] && state_flag_count=$(( state_flag_count + 1 ))

if [ "$state_flag_count" -gt 1 ]; then
	echo "Error: no more than 1 of --tag, --context, --darcs-hash flags can be set. $state_flag_count were set." >&2
	echo >&2
	usage
	exit 1
elif [ -n "$context" ]; then
	if [ ! -s "$context" ]; then
		echo "Error: context file must be readable & non-empty @ “$context”" >&2
		echo >&2
		usage
		exit 1
	else
		context="$(realpath "$context")"
	fi
fi

weak_hash=
hash=
hash_algo="${NIX_HASH_ALGO:-"sha256"}"
hash_format="${hashFormat:-"--base32"}"
final_path=
final_context=

# If the hash was given, a file with that hash may already be in the
# store.
if [ -n "$exp_hash" ]; then
	final_path=$(nix-store --print-fixed-path --recursive "$hash_algo" "$exp_hash" "$name")
	if ! nix-store --check-validity "$final_path" 2> /dev/null; then
		final_path=""
	fi
	hash="$exp_hash"
fi

# If we don’t know the hash or a path with that hash doesn’t exist,
# download the file and add it to the store.
if [ -z "$final_path" ]; then
	tmp_clone="$(realpath ${quiet:+--quiet} "$(mktemp ${quiet:+--quiet} -d --tmpdir darcs-clone-tmp-XXXXXXXX)")"
	trap "rm -rf \"$tmp_clone\"" EXIT

	clone_args="--lazy"
	if [ -n "$quiet" ]; then
		clone_args="$clone_args --quiet"
	fi
	if [ -n "$tag" ]; then
		clone_args="$clone_args --tag=$tag"
	elif [ -n "$context" ]; then
		clone_args="$clone_args --context=$context"
	elif [ -n "$darcs_hash" ]; then
		clone_args="$clone_args --to-hash=$darcs_hash"
	fi

	cd "$tmp_clone"
	# Do not print Darcs progress to stdout (else stdout isn’t parsable JSON)
	darcs clone "$clone_args" "$repository" "$name" 1>&2
	# Will put the current Darcs context into the store.
	new_context="$tmp_clone/${name}-context.txt"
	darcs log --repodir="$tmp_clone/$name" --context > "$new_context"
	final_context="$(nix-store --add-fixed "$hash_algo" "$new_context")"
	# Darcs has a weak hash using the XOR of the patch hashes which is useful
	# for other scripts
	# https://darcs.net/Internals/Hashes
	weak_hash="$(darcs show repo --repodir="$tmp_clone/$name" | awk '/^[[:space:]]*Weak Hash:/ { sub(/^[[:space:]]*Weak Hash:[[:space:]]*/, "", $0); printf $0 }')"
	date="$(darcs log --repodir="$tmp_clone/$name" --max-count 1 | awk '/^Date:/ { sub(/^Date:[[:space:]]*/, "", $0); printf $0 }')"
	date="$(date -u -d "$date" "+%Y-%m-%dT%H:%M:%SZ")"
	rm -rf "$tmp_clone/$name/_darcs"

	hash="$(nix-hash --type "$hash_algo" "$hash_format" "$tmp_clone/$name")"
	final_path=$(nix-store --add-fixed --recursive "$hash_algo" "$tmp_clone/$name")

	if [ -n "$exp_hash" ] && [ "$exp_hash" != "$hash" ]; then
		echo "Hash mismatch for “$repository”" >&2
		echo "Expected: $exp_hash" >&2
		echo "Got:      $hash" >&2
		exit 1
	fi
fi

json_escape() {
	printf '%s' "$1" | jq -Rs .
}

cat <<EOF
{
	"repository": $(json_escape "$repository"),
EOF
if [ -n "$tag" ]; then cat <<EOF
	"tag": $(json_escape "$tag"),
EOF
elif [ -n "$darcs_hash" ]; then cat <<EOF
	"darcs-hash": $(json_escape "$darcs_hash"),
EOF
fi; if [ -n "$weak_hash" ]; then cat <<EOF
	"weak-hash": $(json_escape "$weak_hash"),
EOF
fi; if [ -s "$final_context" ]; then cat <<EOF
	"context": $(json_escape "$final_context"),
EOF
fi; cat <<EOF
	"date": "$date",
	"path": "$final_path",
	$(json_escape "$hash_algo"): $(json_escape "$hash"),
	"hash": "$(nix-hash --to-sri --type "$hash_algo" "$hash")"
}
EOF
# vim: noet ci pi sts=0