From a31b64b579cc4be9a8a0213b0a84e5ae0cd4b8d5 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 17 Jul 2024 00:57:16 +0200 Subject: [PATCH] scripts/checkout.sh: prevent ls-remote from matching multiple tags git ls-remote's argument [1] is a glob [2], and matches anything ending with the given string. This is problematic if multiple tags or branches end with the given pattern. In containerd's case, this returns both tags for the main module ("refs/tags/v1.7.19") and # the API module ("refs/tags/api/v1.7.19"). To prevent both of those being found, we check if the given reference starts with a "v"; if it does, we can assume it's a tag, and prefix the pattern with "refs/tags/" to make it less ambiguous. We're using a case statement here to avoid introducing Bashisms. [1]: https://git-scm.com/docs/git-ls-remote#Documentation/git-ls-remote.txt-ltpatternsgt82308203 [2]: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-glob Before this patch: make REF=v1.7.19 checkout # ... Initialized empty Git repository in /Users/thajeztah/go/src/github.com/docker/containerd-packaging/src/github.com/containerd/containerd/.git/ git -C src/github.com/containerd/containerd remote add origin "https://github.com/containerd/containerd.git" ./scripts/checkout.sh src/github.com/containerd/containerd "v1.7.19" + SRC=src/github.com/containerd/containerd + REF=v1.7.19 + REF_FETCH=v1.7.19 ++ git -C src/github.com/containerd/containerd ls-remote --refs --heads --tags origin v1.7.19 ++ awk '{print $2}' + REF='refs/tags/api/v1.7.19 refs/tags/v1.7.19' + '[' -n 'refs/tags/api/v1.7.19 refs/tags/v1.7.19' ']' + REF_FETCH='refs/tags/api/v1.7.19 refs/tags/v1.7.19:refs/tags/api/v1.7.19 refs/tags/v1.7.19' + git -C src/github.com/containerd/containerd fetch --update-head-ok --depth 1 origin 'refs/tags/api/v1.7.19 refs/tags/v1.7.19:refs/tags/api/v1.7.19 refs/tags/v1.7.19' fatal: invalid refspec 'refs/tags/api/v1.7.19 refs/tags/v1.7.19:refs/tags/api/v1.7.19 refs/tags/v1.7.19' make: *** [checkout] Error 128 With this patch: make REF=v1.7.19 checkout # ... Initialized empty Git repository in /Users/thajeztah/go/src/github.com/docker/containerd-packaging/src/github.com/containerd/containerd/.git/ git -C src/github.com/containerd/containerd remote add origin "https://github.com/containerd/containerd.git" ./scripts/checkout.sh src/github.com/containerd/containerd "v1.7.19" + SRC=src/github.com/containerd/containerd + REF=v1.7.19 + REF_FETCH=v1.7.19 + REF_GLOB=v1.7.19 + case $REF_GLOB in + REF_GLOB=refs/tags/v1.7.19 ++ git -C src/github.com/containerd/containerd ls-remote --refs --heads --tags origin refs/tags/v1.7.19 ++ awk '{print $2}' + REF=refs/tags/v1.7.19 + '[' -n refs/tags/v1.7.19 ']' + REF_FETCH=refs/tags/v1.7.19:refs/tags/v1.7.19 + git -C src/github.com/containerd/containerd fetch --update-head-ok --depth 1 origin refs/tags/v1.7.19:refs/tags/v1.7.19 remote: Enumerating objects: 6397, done. remote: Counting objects: 100% (6397/6397), done. remote: Compressing objects: 100% (5114/5114), done. Receiving objects: 100% (6397/6397), 10.09 MiB | 13.94 MiB/s, done. remote: Total 6397 (delta 1376), reused 3349 (delta 816), pack-reused 0 Resolving deltas: 100% (1376/1376), done. From https://github.com/containerd/containerd * [new tag] v1.7.19 -> v1.7.19 + git -C src/github.com/containerd/containerd checkout -q refs/tags/v1.7.19 Signed-off-by: Sebastiaan van Stijn --- scripts/checkout.sh | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/scripts/checkout.sh b/scripts/checkout.sh index e53a9e4b..baa8f593 100755 --- a/scripts/checkout.sh +++ b/scripts/checkout.sh @@ -19,8 +19,30 @@ checkout() ( SRC="$1" REF="$2" REF_FETCH="$REF" + + # git ls-remote's argument [1] is a glob [2], and matches anything + # ending with the given string. This is problematic if multiple tags or + # branches end with the given pattern. In containerd's case, this returns + # both tags for the main module ("refs/tags/v1.7.19") and # the API module + # ("refs/tags/api/v1.7.19"). + # + # To prevent both of those being found, we check if the given reference starts + # with a "v"; if it does, we can assume it's a tag, and prefix the pattern with + # "refs/tags/" to make it less ambiguous. + # + # We're using a case statement here to avoid introducing Bashisms. + # + # [1]: https://git-scm.com/docs/git-ls-remote#Documentation/git-ls-remote.txt-ltpatternsgt82308203 + # [2]: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-glob + ref_glob="$REF" + case $ref_glob in + "v"*) + ref_glob="refs/tags/$ref_glob" + ;; + esac + # if ref is branch or tag, retrieve its canonical form - REF=$(git -C "$SRC" ls-remote --refs --heads --tags origin "$REF" | awk '{print $2}') + REF=$(git -C "$SRC" ls-remote --refs --heads --tags origin "$ref_glob" | awk '{print $2}') if [ -n "$REF" ]; then # if branch or tag then create it locally too REF_FETCH="$REF:$REF" @@ -31,7 +53,6 @@ checkout() ( git -C "$SRC" checkout -q "$REF" ) - # Only execute checkout function above if this file is executed, not sourced from another script prog=checkout.sh # needs to be in sync with this file's name if [ "$(basename -- $0)" = "$prog" ]; then