#!/bin/sh # The snapshot page hands a revision back as an archive. Every format is held # byte for byte to the archive git itself writes, once decompressed where cgit # pipes through a compressor, and each compressed format needs the same tool # on the server and here, so one missing tool skips that format on both # grounds at once. The tags after them check how a snapshot is named. test_description='Verify snapshot' . ./setup.sh check_snapshot_headers() { sed "/^$/q" tmp >headers && grep "^Status: 200 OK$" headers && grep "^Content-Type: $2$" headers && grep "^Content-Disposition: inline; filename=\"master.$1\"$" headers } check_snapshot_content() { ls master/ >output && test_line_count = 5 output && grep "^5$" master/file-5 && test_line_count = 1 master/file-5 } # Lists the members of the archive in the response held in tmp. snapshot_members() { strip_headers snapshot.tar && tar -tf snapshot.tar >members } # The format checks below compare against these, which git's own writer makes # from the commit the snapshots are taken of. test_expect_success 'set up the archives git writes' ' git -C repos/foo archive --format=tar --prefix=master/ master >expect.tar && git -C repos/foo archive --format=zip --prefix=master/ master >expect.zip ' test_expect_success 'a tar snapshot is the archive git writes' ' cgit_ok_url "foo/snapshot/master.tar" >tmp && check_snapshot_headers tar application/x-tar && strip_headers master.tar && test_cmp_bin expect.tar master.tar && rm -rf master && tar -xf master.tar && check_snapshot_content ' # Registers the check for one compressed tar format. test_tar_snapshot() { ext=$1 type=$2 tool=$3 prereq=$4 test_lazy_prereq $prereq "command -v $tool" test_expect_success $prereq "a $ext snapshot unpacks to the tar git writes" " cgit_ok_url 'foo/snapshot/master.$ext' >tmp && check_snapshot_headers $ext $type && strip_headers master.$ext && '$tool' -t 'master.$ext' && '$tool' -dc 'master.$ext' >master.tar && test_cmp_bin expect.tar master.tar " } test_tar_snapshot tar.gz application/gzip gzip GZIP test_tar_snapshot tar.bz2 application/x-bzip2 bzip2 BZIP2 test_tar_snapshot tar.lz application/x-lzip lzip LZIP test_tar_snapshot tar.xz application/x-xz xz XZ test_tar_snapshot tar.zst application/zstd zstd ZSTD test_expect_success 'a zip snapshot is the archive git writes' ' cgit_ok_url "foo/snapshot/master.zip" >tmp && check_snapshot_headers zip application/zip && strip_headers master.zip && test_cmp_bin expect.zip master.zip ' test_expect_success UNZIP 'a zip snapshot unzips' ' cgit_ok_url "foo/snapshot/master.zip" >tmp && strip_headers master.zip && rm -rf master && "$GIT_UNZIP" master.zip && check_snapshot_content ' # A tag named v1.0 sheds its v in the snapshot name unless dropping it would # merge two tags. The first tag below stays a loose ref on purpose, because on # a case-insensitive filesystem a ref lookup for V1.0 finds the loose v1.0 # file, and deciding by lookups called every fresh tag ambiguous and kept its # v until git happened to pack the refs. test_expect_success 'set up a repo with a versioned tag' ' mkrepo repos/vtags 2 && git -C repos/vtags update-ref refs/tags/v1.0 HEAD && cgitrc_repo vtagsrc vtags snapshots=tar.gz ' test_expect_success 'a loose v tag still sheds its v in the snapshot name' ' cgit_rc vtagsrc "url=vtags/refs/" >tmp && grep "vtags-1.0.tar.gz" tmp && ! grep "vtags-v1.0.tar.gz" tmp ' # The second tag goes in only after the first is packed, since two loose files # differing in case alone cannot coexist on a case-insensitive filesystem. test_expect_success 'a real case collision keeps the v on both tags' ' git -C repos/vtags pack-refs --all && git -C repos/vtags update-ref refs/tags/V1.0 HEAD~1 && cgit_rc vtagsrc "url=vtags/refs/" >tmp && grep "vtags-v1.0.tar.gz" tmp && grep "vtags-V1.0.tar.gz" tmp && ! grep "vtags-1.0.tar.gz" tmp ' # A name that is all suffix names no revision, and an empty snapshot-prefix # gives the members no leading directory rather than a leading slash. test_expect_success 'a snapshot name with nothing before its suffix is refused' ' git -C repos/vtags branch v HEAD && sed "s/^snapshots=tar.gz$/snapshots=tar/" vtagsrc >vtagstarrc && cgit_rc vtagstarrc "url=vtags/snapshot/.tar" >tmp && grep "^Status: 404" tmp ' test_expect_success 'an empty snapshot-prefix puts no slash ahead of the members' ' { cat vtagstarrc && echo "repo.snapshot-prefix=" } >noprefixrc && cgit_rc noprefixrc "url=vtags/snapshot/vtags.tar&id=master" >tmp && strip_headers noprefix.tar && tar -tf noprefix.tar >members && grep "^file-1$" members && ! grep "^/" members ' # A snapshot is named after the last component of the url once its .git # suffix is gone, so a url of .git alone leaves the ref as the whole name. test_expect_success 'set up repositories whose urls end in .git' ' mkrepo repos/dotgit 1 && git -C repos/dotgit tag v1.0 && { echo "virtual-root=/" && echo "cache-size=0" && echo "snapshots=tar" && echo "repo.url=.git" && echo "repo.path=$PWD/repos/dotgit/.git" && echo "repo.url=sub/.git" && echo "repo.path=$PWD/repos/dotgit/.git" } >dotgitrc ' test_expect_success 'a url of .git names a snapshot after the ref alone' ' cgit_rc dotgitrc "url=.git/refs/" >tmp && grep ">v1.0.tar<" tmp && cgit_rc dotgitrc "url=.git/snapshot/v1.0.tar" >tmp && strip_headers dotgit.tar && tar -tf dotgit.tar >members && grep "^v1.0/file-1$" members ' test_expect_success 'a url ending in /.git names a snapshot after its directory' ' cgit_rc dotgitrc "url=sub/.git/refs/" >tmp && grep ">sub-1.0.tar<" tmp ' # A server may run cgit with no PATH at all, and the compressor is then looked # for on the default path execvp searches. test_expect_success GZIP 'a compressed snapshot is made without a PATH' ' cgitrc_copy nopathrc && cgit_bin=$(command -v cgit) && ( unset PATH && CGIT_CONFIG="$PWD/nopathrc" QUERY_STRING="url=foo/snapshot/master.tar.gz" "$cgit_bin" >tmp ) && grep "^Status: 200" tmp && strip_headers nopath.tar.gz && gzip -t nopath.tar.gz ' # A link offers the snapshot of a tag, and the name on it has to lead back to # that tag even when a branch is spelled like the tag's stem. test_expect_success GZIP 'a snapshot name leads to the tag ahead of a branch' ' git -C repos/vtags update-ref refs/heads/1.0 $(git -C repos/vtags commit-tree -m shadow HEAD^{tree}) && cgit_rc vtagsrc "url=vtags/snapshot/vtags-1.0.tar.gz" >tmp && strip_headers tag.tar.gz && test "$(gzip -dc tag.tar.gz | git -C repos/vtags get-tar-commit-id)" = "$(git -C repos/vtags rev-parse refs/tags/v1.0^{commit})" ' # A signature is a note a pusher stored, held to max-blob-size like any blob. test_expect_success 'a signature past max-blob-size is refused' ' big=$(test_seq 1 2000 | git -C repos/vtags hash-object -w --stdin) && git -C repos/vtags notes --ref signatures/tar.gz add -C $big v1.0 && { echo "max-blob-size=1" && cat vtagsrc } >sigrc && cgit_rc sigrc "url=vtags/snapshot/vtags-1.0.tar.gz.asc" >tmp && grep "^Status: 413" tmp ' test_expect_success 'a snapshot and its signature are linked as one unit' ' test_create_repo repos/signed && ( cd repos/signed && echo one >file && git add file && git commit -m one && git tag one && sig=$(echo signature | git hash-object -w --stdin) && git notes --ref signatures/tar.gz add -C $sig one ) && cgitrc_repo signedrc signed snapshots=tar.gz && cgit_rc signedrc "url=signed/refs/" >tmp && grep "signed-one.tar.gz (sig)" tmp ' # A snapshot name is never worked back to a ref holding a slash, so such a # ref is offered under a dashed name and passed along with it, even from a # page whose own head is that ref. test_expect_success 'a ref with a slash is linked under a dashed name' ' git -C repos/vtags tag release/2.0 HEAD && git -C repos/vtags branch topic/x HEAD && cgit_rc vtagsrc "url=vtags/refs/" >tmp && grep "vtags-release-2.0.tar.gz" tmp && cgit_rc vtagsrc "url=vtags/commit/&h=topic/x" >tmp && grep "vtags-topic-x.tar.gz" tmp ' test_expect_success GZIP 'the dashed name serves the ref' ' cgit_rc vtagsrc "url=vtags/snapshot/vtags-release-2.0.tar.gz&id=release/2.0" >tmp && grep "^Status: 200" tmp && strip_headers slash.tar.gz && test "$(gzip -dc slash.tar.gz | git -C repos/vtags get-tar-commit-id)" = "$(git -C repos/vtags rev-parse release/2.0^{commit})" ' test_done