Makefile: add OPEN_RETURNS_EINTR knob
[git] / t / t5534-push-signed.sh
1 #!/bin/sh
2
3 test_description='signed push'
4
5 . ./test-lib.sh
6 . "$TEST_DIRECTORY"/lib-gpg.sh
7
8 prepare_dst () {
9         rm -fr dst &&
10         test_create_repo dst &&
11
12         git push dst master:noop master:ff master:noff
13 }
14
15 test_expect_success setup '
16         # master, ff and noff branches pointing at the same commit
17         test_tick &&
18         git commit --allow-empty -m initial &&
19
20         git checkout -b noop &&
21         git checkout -b ff &&
22         git checkout -b noff &&
23
24         # noop stays the same, ff advances, noff rewrites
25         test_tick &&
26         git commit --allow-empty --amend -m rewritten &&
27         git checkout ff &&
28
29         test_tick &&
30         git commit --allow-empty -m second
31 '
32
33 test_expect_success 'unsigned push does not send push certificate' '
34         prepare_dst &&
35         mkdir -p dst/.git/hooks &&
36         write_script dst/.git/hooks/post-receive <<-\EOF &&
37         # discard the update list
38         cat >/dev/null
39         # record the push certificate
40         if test -n "${GIT_PUSH_CERT-}"
41         then
42                 git cat-file blob $GIT_PUSH_CERT >../push-cert
43         fi
44         EOF
45
46         git push dst noop ff +noff &&
47         ! test -f dst/push-cert
48 '
49
50 test_expect_success 'talking with a receiver without push certificate support' '
51         prepare_dst &&
52         mkdir -p dst/.git/hooks &&
53         write_script dst/.git/hooks/post-receive <<-\EOF &&
54         # discard the update list
55         cat >/dev/null
56         # record the push certificate
57         if test -n "${GIT_PUSH_CERT-}"
58         then
59                 git cat-file blob $GIT_PUSH_CERT >../push-cert
60         fi
61         EOF
62
63         git push dst noop ff +noff &&
64         ! test -f dst/push-cert
65 '
66
67 test_expect_success 'push --signed fails with a receiver without push certificate support' '
68         prepare_dst &&
69         mkdir -p dst/.git/hooks &&
70         test_must_fail git push --signed dst noop ff +noff 2>err &&
71         test_i18ngrep "the receiving end does not support" err
72 '
73
74 test_expect_success 'push --signed=1 is accepted' '
75         prepare_dst &&
76         mkdir -p dst/.git/hooks &&
77         test_must_fail git push --signed=1 dst noop ff +noff 2>err &&
78         test_i18ngrep "the receiving end does not support" err
79 '
80
81 test_expect_success GPG 'no certificate for a signed push with no update' '
82         prepare_dst &&
83         mkdir -p dst/.git/hooks &&
84         write_script dst/.git/hooks/post-receive <<-\EOF &&
85         if test -n "${GIT_PUSH_CERT-}"
86         then
87                 git cat-file blob $GIT_PUSH_CERT >../push-cert
88         fi
89         EOF
90         git push dst noop &&
91         ! test -f dst/push-cert
92 '
93
94 test_expect_success GPG 'signed push sends push certificate' '
95         prepare_dst &&
96         mkdir -p dst/.git/hooks &&
97         git -C dst config receive.certnonceseed sekrit &&
98         write_script dst/.git/hooks/post-receive <<-\EOF &&
99         # discard the update list
100         cat >/dev/null
101         # record the push certificate
102         if test -n "${GIT_PUSH_CERT-}"
103         then
104                 git cat-file blob $GIT_PUSH_CERT >../push-cert
105         fi &&
106
107         cat >../push-cert-status <<E_O_F
108         SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
109         KEY=${GIT_PUSH_CERT_KEY-nokey}
110         STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
111         NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
112         NONCE=${GIT_PUSH_CERT_NONCE-nononce}
113         E_O_F
114
115         EOF
116
117         git push --signed dst noop ff +noff &&
118
119         (
120                 cat <<-\EOF &&
121                 SIGNER=C O Mitter <committer@example.com>
122                 KEY=13B6F51ECDDE430D
123                 STATUS=G
124                 NONCE_STATUS=OK
125                 EOF
126                 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
127         ) >expect &&
128
129         noop=$(git rev-parse noop) &&
130         ff=$(git rev-parse ff) &&
131         noff=$(git rev-parse noff) &&
132         grep "$noop $ff refs/heads/ff" dst/push-cert &&
133         grep "$noop $noff refs/heads/noff" dst/push-cert &&
134         test_cmp expect dst/push-cert-status
135 '
136
137 test_expect_success GPG 'inconsistent push options in signed push not allowed' '
138         # First, invoke receive-pack with dummy input to obtain its preamble.
139         prepare_dst &&
140         git -C dst config receive.certnonceseed sekrit &&
141         git -C dst config receive.advertisepushoptions 1 &&
142         printf xxxx | test_might_fail git receive-pack dst >preamble &&
143
144         # Then, invoke push. Simulate a receive-pack that sends the preamble we
145         # obtained, followed by a dummy packet.
146         write_script myscript <<-\EOF &&
147                 cat preamble &&
148                 printf xxxx &&
149                 cat >push
150         EOF
151         test_might_fail git push --push-option="foo" --push-option="bar" \
152                 --receive-pack="\"$(pwd)/myscript\"" --signed dst --delete ff &&
153
154         # Replay the push output on a fresh dst, checking that ff is truly
155         # deleted.
156         prepare_dst &&
157         git -C dst config receive.certnonceseed sekrit &&
158         git -C dst config receive.advertisepushoptions 1 &&
159         git receive-pack dst <push &&
160         test_must_fail git -C dst rev-parse ff &&
161
162         # Tweak the push output to make the push option outside the cert
163         # different, then replay it on a fresh dst, checking that ff is not
164         # deleted.
165         perl -pe "s/([^ ])bar/\$1baz/" push >push.tweak &&
166         prepare_dst &&
167         git -C dst config receive.certnonceseed sekrit &&
168         git -C dst config receive.advertisepushoptions 1 &&
169         git receive-pack dst <push.tweak >out &&
170         git -C dst rev-parse ff &&
171         grep "inconsistent push options" out
172 '
173
174 test_expect_success GPG 'fail without key and heed user.signingkey' '
175         prepare_dst &&
176         mkdir -p dst/.git/hooks &&
177         git -C dst config receive.certnonceseed sekrit &&
178         write_script dst/.git/hooks/post-receive <<-\EOF &&
179         # discard the update list
180         cat >/dev/null
181         # record the push certificate
182         if test -n "${GIT_PUSH_CERT-}"
183         then
184                 git cat-file blob $GIT_PUSH_CERT >../push-cert
185         fi &&
186
187         cat >../push-cert-status <<E_O_F
188         SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
189         KEY=${GIT_PUSH_CERT_KEY-nokey}
190         STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
191         NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
192         NONCE=${GIT_PUSH_CERT_NONCE-nononce}
193         E_O_F
194
195         EOF
196
197         test_config user.email hasnokey@nowhere.com &&
198         (
199                 sane_unset GIT_COMMITTER_EMAIL &&
200                 test_must_fail git push --signed dst noop ff +noff
201         ) &&
202         test_config user.signingkey $GIT_COMMITTER_EMAIL &&
203         git push --signed dst noop ff +noff &&
204
205         (
206                 cat <<-\EOF &&
207                 SIGNER=C O Mitter <committer@example.com>
208                 KEY=13B6F51ECDDE430D
209                 STATUS=G
210                 NONCE_STATUS=OK
211                 EOF
212                 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
213         ) >expect &&
214
215         noop=$(git rev-parse noop) &&
216         ff=$(git rev-parse ff) &&
217         noff=$(git rev-parse noff) &&
218         grep "$noop $ff refs/heads/ff" dst/push-cert &&
219         grep "$noop $noff refs/heads/noff" dst/push-cert &&
220         test_cmp expect dst/push-cert-status
221 '
222
223 test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
224         test_config gpg.format x509 &&
225         prepare_dst &&
226         mkdir -p dst/.git/hooks &&
227         git -C dst config receive.certnonceseed sekrit &&
228         write_script dst/.git/hooks/post-receive <<-\EOF &&
229         # discard the update list
230         cat >/dev/null
231         # record the push certificate
232         if test -n "${GIT_PUSH_CERT-}"
233         then
234                 git cat-file blob $GIT_PUSH_CERT >../push-cert
235         fi &&
236
237         cat >../push-cert-status <<E_O_F
238         SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
239         KEY=${GIT_PUSH_CERT_KEY-nokey}
240         STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
241         NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
242         NONCE=${GIT_PUSH_CERT_NONCE-nononce}
243         E_O_F
244
245         EOF
246
247         test_config user.email hasnokey@nowhere.com &&
248         test_config user.signingkey "" &&
249         (
250                 sane_unset GIT_COMMITTER_EMAIL &&
251                 test_must_fail git push --signed dst noop ff +noff
252         ) &&
253         test_config user.signingkey $GIT_COMMITTER_EMAIL &&
254         git push --signed dst noop ff +noff &&
255
256         (
257                 cat <<-\EOF &&
258                 SIGNER=/CN=C O Mitter/O=Example/SN=C O/GN=Mitter
259                 KEY=
260                 STATUS=G
261                 NONCE_STATUS=OK
262                 EOF
263                 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
264         ) >expect.in &&
265         key=$(cat "${GNUPGHOME}/trustlist.txt" | cut -d" " -f1 | tr -d ":") &&
266         sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
267
268         noop=$(git rev-parse noop) &&
269         ff=$(git rev-parse ff) &&
270         noff=$(git rev-parse noff) &&
271         grep "$noop $ff refs/heads/ff" dst/push-cert &&
272         grep "$noop $noff refs/heads/noff" dst/push-cert &&
273         test_cmp expect dst/push-cert-status
274 '
275
276 test_expect_success GPG 'failed atomic push does not execute GPG' '
277         prepare_dst &&
278         git -C dst config receive.certnonceseed sekrit &&
279         write_script gpg <<-EOF &&
280         # should check atomic push locally before running GPG.
281         exit 1
282         EOF
283         test_must_fail env PATH="$TRASH_DIRECTORY:$PATH" git push \
284                         --signed --atomic --porcelain \
285                         dst noop ff noff >out 2>err &&
286
287         test_i18ngrep ! "gpg failed to sign" err &&
288         cat >expect <<-EOF &&
289         To dst
290         =       refs/heads/noop:refs/heads/noop [up to date]
291         !       refs/heads/ff:refs/heads/ff     [rejected] (atomic push failed)
292         !       refs/heads/noff:refs/heads/noff [rejected] (non-fast-forward)
293         Done
294         EOF
295         test_cmp expect out
296 '
297
298 test_done