From 4447fa156c38259f59a91309a2b85cc5f457d3fb Mon Sep 17 00:00:00 2001 From: "Claus Schneider(Eficode)" Date: Wed, 13 Aug 2025 16:13:16 +0200 Subject: [PATCH 1/8] gh-6 : add add-as-submodule function --- .test/10/git-reference.log | 13 +++++ _tests.sh | 68 ++++++++++++++++--------- git-artifact | 102 +++++++++++++++++++++++++++---------- 3 files changed, 133 insertions(+), 50 deletions(-) create mode 100644 .test/10/git-reference.log diff --git a/.test/10/git-reference.log b/.test/10/git-reference.log new file mode 100644 index 0000000..a9f274c --- /dev/null +++ b/.test/10/git-reference.log @@ -0,0 +1,13 @@ +* (HEAD -> main, origin/main) Added submodule repo +| * (tag: v2.0) v2.0 +|/ +| * (tag: v1.0) v1.0 +|/ +* First commit of git artifact +[submodule "submodule-repo"] + path = submodule-repo + url = ../.remote +On branch main +Your branch is up to date with 'origin/main'. + +nothing to commit, working tree clean diff --git a/_tests.sh b/_tests.sh index a0dbd79..4cddf29 100755 --- a/_tests.sh +++ b/_tests.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# shellcheck disable=SC2317 +# shellcheck disable=SC2329 function usage() { @@ -7,8 +9,9 @@ Usage: $(basename "$0") [options] Options: -h, --help Show this help message and exit - --debug Enable debug mode + -d, --debug Enable debug mode --verbose Enable verbose output + -t|--testcase <#> Specify a single test case to run (e.g., 1, 2, 3.1, etc.) Description: This script runs integration tests for git-artifact. @@ -89,7 +92,6 @@ remote_tester_repo=.remote clone_tester_repo=.clone global_exit_code=0 -# shellcheck disable=SC2317 function testcase_header() { [[ ! -d "${test}" ]] && { echo "Creating test directory: ${test} and empty git-reference.log" @@ -103,13 +105,11 @@ function testcase_header() { echo "--------------------------------------------------------------------------------" } -# shellcheck disable=SC2317 function generate_git_test_log() { rm -rf .git/refs/remotes/origin/HEAD git log --graph --all --oneline --format="%d %s" >> "${root_folder}/${test}/git-test.log" } -# shellcheck disable=SC2317 function eval_testcase() { # expect to be in repo to test against @@ -141,7 +141,6 @@ function eval_testcase() { echo } -# shellcheck disable=SC2317 function generate_base_repo() { rm -rf "${local_tester_repo:?}/" "${remote_tester_repo:?}/" "${clone_tester_repo:?}/" git init --bare -b "${default_branch:-main}" $remote_tester_repo || { @@ -167,7 +166,6 @@ echo " - /ok.log(all good)" echo " - /nok.log(failed tests)" echo -# shellcheck disable=SC2317 function 1 { export test="1" testcase_synopsis="base-repo default-branch; clone" @@ -180,7 +178,6 @@ function 1 { eval_testcase } -# shellcheck disable=SC2317 function 1.1 { export test="1.1" testcase_synopsis="base-repo master-branch; clone" @@ -195,7 +192,6 @@ function 1.1 { eval_testcase } -# shellcheck disable=SC2317 function 2 { test="${FUNCNAME[0]}" testcase_synopsis="base-repo ; clone; fetch-co : the repo has two tags and the latest is checked out" @@ -211,7 +207,6 @@ function 2 { eval_testcase } -# shellcheck disable=SC2317 function 3 { test="${FUNCNAME[0]}" testcase_synopsis="base-repo ; clone - gives a repo without any artifacts" @@ -225,7 +220,6 @@ function 3 { eval_testcase } -# shellcheck disable=SC2317 function 4 { export test="4" testcase_synopsis="base-repo ; clone; add-n-push with branch" @@ -243,7 +237,6 @@ function 4 { eval_testcase } -# shellcheck disable=SC2317 function 5 { test="${FUNCNAME[0]}" testcase_synopsis="base-repo ; clone; fetch-co-latest pattern" @@ -279,7 +272,6 @@ function 5 { eval_testcase } -# shellcheck disable=SC2317 function 5.1 { test="${FUNCNAME[0]}" testcase_synopsis="base-repo ; clone; find-latest pattern" @@ -297,7 +289,6 @@ function 5.1 { } -# shellcheck disable=SC2317 function 6 { test="${FUNCNAME[0]}" testcase_synopsis="base-repo ; clone; fetch-tags" @@ -315,7 +306,6 @@ function 6 { eval_testcase } -# shellcheck disable=SC2317 function 7 { test="${FUNCNAME[0]}" testcase_synopsis="base-repo ; clone; list" @@ -342,7 +332,6 @@ function 7 { eval_testcase } -# shellcheck disable=SC2317 function 8 { test="${FUNCNAME[0]}" testcase_synopsis="base-repo ; clone; summary" @@ -377,7 +366,6 @@ function 8 { eval_testcase } -# shellcheck disable=SC2317 function 9 { test="${FUNCNAME[0]}" @@ -400,26 +388,58 @@ function 9 { sleep 1 done - generate_git_test_log - git artifact prune --glob 'v*.*' --keep 5 --dryrun >> ${root_folder}/${test}/git-test.log - git artifact prune --glob 'v*.*' --keep 5 - git fetch origin -pP + generate_git_test_log + git artifact prune --glob 'v*.*' --keep 5 --dryrun >> ${root_folder}/${test}/git-test.log + git artifact prune --glob 'v*.*' --keep 5 + git fetch origin -pP - generate_git_test_log + generate_git_test_log - git artifact list --glob 'v*.*' >> ${root_folder}/${test}/git-test.log + git artifact list --glob 'v*.*' >> ${root_folder}/${test}/git-test.log + + } > ${root_folder}/${test}/run.log 2>&1 || { pwd && cat ${root_folder}/${test}/run.log; } + eval_testcase +} + +function 10 { + test="${FUNCNAME[0]}" + testcase_synopsis="base-repo ; clone; add-as-submodule" + testcase_header + { + cd $test + generate_base_repo + sleep 1 + git artifact clone --url "$(pwd)/$remote_tester_repo" --path "$clone_tester_repo" + + cd $local_tester_repo + + git artifact add-as-submodule --url "../$remote_tester_repo" --path submodule-repo + + git status + + git commit -m "Added submodule repo" + git push origin HEAD:"${default_branch:-main}" + git fetch origin -apP + generate_git_test_log + cat .gitmodules >> ${root_folder}/${test}/git-test.log + git status >> ${root_folder}/${test}/git-test.log + } > ${root_folder}/${test}/run.log 2>&1 || { pwd && cat ${root_folder}/${test}/run.log; } eval_testcase } + if [[ ${arg_testcase:-} == "" ]]; then # Dynamically list and call test functions mapfile -t test_functions < <(declare -F | awk '{print $3}' | grep -E '^[0-9]+(\.[0-9]+)?$') for fn in "${test_functions[@]}"; do - "$fn" + "$fn" || { + echo "Test case '$fn' failed. Check the logs in .test/$fn/run.log" + global_exit_code=1 + } done else # Run a specific test case if provided @@ -430,6 +450,8 @@ else exit 1 fi fi + + echo echo "########################################" echo "All tests completed. Checking results..." diff --git a/git-artifact b/git-artifact index d9618d3..dc48398 100755 --- a/git-artifact +++ b/git-artifact @@ -69,49 +69,50 @@ function set_opts_spec() { commands: -init inititialize a new repository -clone clone and existing repository from the remote -add-n-tag add and commit files to repo abd leave workspace on tag/untouched -push push a tag -add-n-push add and commit files to repo and then push them -fetch-co fetch tag from remote and checkout it out -reset reset workspace and branches -list list all tags in the repo given a glob pattern -summary print a summary of the tags in the repo -prune prune tags in the repo given a glob pattern and keep amount of tags -find-latest Find the latest tags from reqex and print it -fetch-co-latest Get latest tag form remote using grep reg-ex and reset hard to it -fetch-tags Fetch all tags that points to a sha1 or HEAD - Useful in relation to detached HEAD and submodules +init Inititialize a new repository +clone Clone and existing repository from the remote +add-n-tag Add and commit files to repo abd leave workspace on tag/untouched +push push a tag +add-n-push Add and commit files to repo and then push them +fetch-co Fetch tag from remote and checkout it out +reset Reset workspace and branches +list List all tags in the repo given a glob pattern +summary Print a summary of the tags in the repo +prune Prune tags in the repo given a glob pattern and keep amount of tags +find-latest Find the latest tags from reqex and print it +fetch-co-latest Get latest tag form remote using grep reg-ex and reset hard to it +fetch-tags Fetch all tags that points to a sha1 or HEAD - Useful in relation to detached HEAD and submodules +add-as-submodule Add a submodule in a clever way to the current consuming repository. Special operation to avoid big clones . -- -h show the help -q quiet -v,verbose verbose -d show debug messages +h show the help +q quiet +v,verbose verbose +d show debug messages option for 'prune' -dryrun dry run - do not execute any commands ( optional for prune) +dryrun dry run - do not execute any commands ( optional for prune) - options for 'init' and 'clone' -u,url= the remote url -p,path= the optional path for init and clone + options for 'init', 'clone' and 'add-as-submodule' +u,url= the remote url +p,path= the optional path for init and clone options for 'init', 'clone', 'add-n-tag', 'push', 'add-n-push', 'fetch-co' -b,branch= use branch for local changes and pushes +b,branch= use branch for local changes and pushes options for 'add-n-tag', 'push', 'add-n-push', 'fetch-co' -t,tag= the tag to be created +t,tag= the tag to be created options for 'fetch-co-latest', 'find-latest', 'list', 'prune' -g,glob= the glob pattern search remote for tags +g,glob= the glob pattern search remote for tags options for 'fetch-tags' -s,sha1= The sha1 of which to get tags from from +s,sha1= The sha1 of which to get tags from from options for 'prune' -k,keep= The amount of tags to keep - default is 20 +k,keep= The amount of tags to keep - default is 20 options for 'summary' -delimiter= The delimiter to use for the summary - default is '/' +delimiter= The delimiter to use for the summary - default is '/' " } @@ -393,6 +394,40 @@ cmd_summary() { done } +cmd_add-as-submodule() { + # + # This function is operating on the consuming repository and not the git artifact repo. + # It is adding a submodule to the current repository. + # It is done as a special operation as the submodule is by default cloned with tags and branch and that it can be huge. + # + + if [[ -d ${arg_path} ]]; then + echo "ERROR: Directory '${arg_path}' already exists" + exit 1 + fi + + mkdir -p "./.git/modules/" + echo "Cloning repo into .git/modules/${arg_path} but working tree is: ${arg_path}" + git clone --single-branch --no-tags --separate-git-dir="./.git/modules/${arg_path}" "${arg_remoteurl}" "${arg_path}" + + echo "Adding the repo as submodule" + git submodule add --force "${arg_remoteurl}" "${arg_path}" + echo + + echo "#############################################################################" + echo "# HINT: You can use the ../ notation in submodule path to avoid the full url," + echo "# if your remote server is the same as the submodule server " + echo "# .. to ensure portability of the repositories " + echo "#############################################################################" + git remote -v + echo + echo "${arg_path} : ${arg_remoteurl}" + echo + echo "You are now ready to commit ..." + git status + +} + read_remote_tags_to_map() { local -n _tags=${1} @@ -620,6 +655,19 @@ main () { arg_sha1=$(git rev-parse HEAD) fi ;; + add-as-submodule) + # shellcheck source=/dev/null + . git-sh-setup + require_work_tree + if test -z "${arg_remoteurl:-}" ; then + echo "ERROR: --url required for $arg_command" + exit 1 + fi + if test -z "${arg_path:-}" ; then + echo "ERROR: --arg_path required for $arg_command" + exit 1 + fi + ;; *) printf "Unknown command: %s\n\n" "$arg_command" git artifact -h exit 1 From 10baa80e021c95254cefbce39e630c0959bb6c80 Mon Sep 17 00:00:00 2001 From: "Claus Schneider(Eficode)" Date: Thu, 14 Aug 2025 11:27:18 +0200 Subject: [PATCH 2/8] gh-6 : add add-as-submodule function - fix coderabbitai --- git-artifact | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/git-artifact b/git-artifact index dc48398..475026c 100755 --- a/git-artifact +++ b/git-artifact @@ -94,7 +94,7 @@ dryrun dry run - do not execute any commands ( optional for prune) options for 'init', 'clone' and 'add-as-submodule' u,url= the remote url -p,path= the optional path for init and clone +p,path= the optional path for init and clone, mandatory for add-as-submodule options for 'init', 'clone', 'add-n-tag', 'push', 'add-n-push', 'fetch-co' b,branch= use branch for local changes and pushes @@ -176,7 +176,7 @@ reset_workspace-n-branch() { local _default_remote_branch= set_remote_default_branch _default_remote_branch - git fetch origin ${_default_remote_branch} + git fetch origin "${_default_remote_branch}" git reset --hard origin/"${_default_remote_branch}" git clean -xfd } @@ -467,14 +467,15 @@ cmd_find-latest() { cmd_fetch-co-latest() { latest_tag="" find-latest latest_tag - git fetch origin +refs/tags/$latest_tag:refs/tags/$latest_tag - git checkout $latest_tag + git fetch origin "+refs/tags/$latest_tag:refs/tags/$latest_tag" + git checkout "$latest_tag" } cmd_fetch-tags() { echo "Getting tags from: $arg_sha1" - tags=$(git ls-remote --tags origin | grep '\^{}' | grep -E "^${arg_sha1}" | cut -f2 | cut -d ^ -f 1) || tags="" + # shellcheck disable=SC2178 + tags=$(git ls-remote --tags origin | grep '\^{}' | grep -E "^${arg_sha1}" | cut -f2 | cut -d ^ -f 1) if [ -n "${tags:-}" ]; then for tag in $tags ; do git fetch origin "+$tag:$tag" @@ -664,7 +665,7 @@ main () { exit 1 fi if test -z "${arg_path:-}" ; then - echo "ERROR: --arg_path required for $arg_command" + echo "ERROR: --path required for $arg_command" exit 1 fi ;; From df67af1082cafb2259e8dc89279e9e4a4c3541f7 Mon Sep 17 00:00:00 2001 From: "Claus Schneider(Eficode)" Date: Thu, 14 Aug 2025 13:02:30 +0200 Subject: [PATCH 3/8] gh-6 : add add-as-submodule function - fix coderabbitai --- git-artifact | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-artifact b/git-artifact index 475026c..656dac6 100755 --- a/git-artifact +++ b/git-artifact @@ -74,7 +74,7 @@ clone Clone and existing repository from the remote add-n-tag Add and commit files to repo abd leave workspace on tag/untouched push push a tag add-n-push Add and commit files to repo and then push them -fetch-co Fetch tag from remote and checkout it out +fetch-co Fetch tag from remote and checkout it out reset Reset workspace and branches list List all tags in the repo given a glob pattern summary Print a summary of the tags in the repo From fdbf1c55540336e342ffaea6c88d4b186c6fd9ac Mon Sep 17 00:00:00 2001 From: "Claus Schneider(Eficode)" Date: Thu, 14 Aug 2025 13:05:58 +0200 Subject: [PATCH 4/8] gh-6 : add add-as-submodule function - fix coderabbitai --- git-artifact | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-artifact b/git-artifact index 656dac6..28cf97d 100755 --- a/git-artifact +++ b/git-artifact @@ -94,7 +94,7 @@ dryrun dry run - do not execute any commands ( optional for prune) options for 'init', 'clone' and 'add-as-submodule' u,url= the remote url -p,path= the optional path for init and clone, mandatory for add-as-submodule +p,path= the optional path for init and clone, required for add-as-submodule options for 'init', 'clone', 'add-n-tag', 'push', 'add-n-push', 'fetch-co' b,branch= use branch for local changes and pushes From dc4b7989a034b82a456827d7143c889bef67c546 Mon Sep 17 00:00:00 2001 From: "Claus Schneider(Eficode)" Date: Thu, 14 Aug 2025 13:23:31 +0200 Subject: [PATCH 5/8] gh-6 : add add-as-submodule function - fix coderabbitai --- git-artifact | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/git-artifact b/git-artifact index 28cf97d..7ff4d98 100755 --- a/git-artifact +++ b/git-artifact @@ -406,10 +406,12 @@ cmd_add-as-submodule() { exit 1 fi - mkdir -p "./.git/modules/" - echo "Cloning repo into .git/modules/${arg_path} but working tree is: ${arg_path}" - git clone --single-branch --no-tags --separate-git-dir="./.git/modules/${arg_path}" "${arg_remoteurl}" "${arg_path}" - + # Ensure parent dir for the submodule gitdir exists (supports nested paths) + local modules_gitdir=".git/modules/${arg_path}" + mkdir -p "$(dirname "$modules_gitdir")" + echo "Cloning repo into ${modules_gitdir} with working tree: ${arg_path}" + git clone --single-branch --no-tags --separate-git-dir="${modules_gitdir}" "${arg_remoteurl}" "${arg_path}" + echo "Adding the repo as submodule" git submodule add --force "${arg_remoteurl}" "${arg_path}" echo @@ -419,6 +421,8 @@ cmd_add-as-submodule() { echo "# if your remote server is the same as the submodule server " echo "# .. to ensure portability of the repositories " echo "#############################################################################" + echo + echo "Superproject remotes:" git remote -v echo echo "${arg_path} : ${arg_remoteurl}" From 04f633d0ca02625ba1acdf5eb30ce1113483dcc8 Mon Sep 17 00:00:00 2001 From: "Claus Schneider(Eficode)" Date: Thu, 14 Aug 2025 13:27:11 +0200 Subject: [PATCH 6/8] gh-6 : add add-as-submodule function - remove unneeded clone from test 10 --- _tests.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/_tests.sh b/_tests.sh index 4cddf29..ca3b318 100755 --- a/_tests.sh +++ b/_tests.sh @@ -404,13 +404,11 @@ function 9 { function 10 { test="${FUNCNAME[0]}" - testcase_synopsis="base-repo ; clone; add-as-submodule" + testcase_synopsis="base-repo ; add-as-submodule" testcase_header { cd $test generate_base_repo - sleep 1 - git artifact clone --url "$(pwd)/$remote_tester_repo" --path "$clone_tester_repo" cd $local_tester_repo From 234a042bf3acd85b18b72c8893898204c89a0847 Mon Sep 17 00:00:00 2001 From: "Claus Schneider(Eficode)" Date: Thu, 14 Aug 2025 13:38:11 +0200 Subject: [PATCH 7/8] gh-6 : add add-as-submodule function - fix coderabbitai --- git-artifact | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/git-artifact b/git-artifact index 7ff4d98..bc215d6 100755 --- a/git-artifact +++ b/git-artifact @@ -478,14 +478,20 @@ cmd_fetch-co-latest() { cmd_fetch-tags() { echo "Getting tags from: $arg_sha1" - # shellcheck disable=SC2178 - tags=$(git ls-remote --tags origin | grep '\^{}' | grep -E "^${arg_sha1}" | cut -f2 | cut -d ^ -f 1) + + # Match annotated tags that dereference to the given SHA-1 + local tags + tags="$(git ls-remote --tags origin \ + | grep '\^{}' \ + | grep -E "^${arg_sha1}" \ + | cut -f2 | cut -d ^ -f 1 \ + )" if [ -n "${tags:-}" ]; then for tag in $tags ; do git fetch origin "+$tag:$tag" done else - echo "ERROR: No tags found" + echo "ERROR: No tags found for sha1: ${arg_sha1}" exit 1 fi } From 955ee6d5292b96980f2811e0a23ab0926b7df28f Mon Sep 17 00:00:00 2001 From: "Claus Schneider(Eficode)" Date: Thu, 14 Aug 2025 14:12:45 +0200 Subject: [PATCH 8/8] gh-6 : add add-as-submodule function - fix coderabbitai --- git-artifact | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/git-artifact b/git-artifact index bc215d6..fd03c56 100755 --- a/git-artifact +++ b/git-artifact @@ -90,29 +90,29 @@ v,verbose verbose d show debug messages option for 'prune' -dryrun dry run - do not execute any commands ( optional for prune) +dryrun Dry run - do not execute any commands ( optional for prune) options for 'init', 'clone' and 'add-as-submodule' -u,url= the remote url -p,path= the optional path for init and clone, required for add-as-submodule +u,url= Remote url +p,path= Optional path for init and clone, required for add-as-submodule options for 'init', 'clone', 'add-n-tag', 'push', 'add-n-push', 'fetch-co' -b,branch= use branch for local changes and pushes +b,branch= Branch for local changes and pushes options for 'add-n-tag', 'push', 'add-n-push', 'fetch-co' -t,tag= the tag to be created +t,tag= Tag to be created options for 'fetch-co-latest', 'find-latest', 'list', 'prune' -g,glob= the glob pattern search remote for tags +g,glob= Glob pattern search remote for tags options for 'fetch-tags' -s,sha1= The sha1 of which to get tags from from +s,sha1= Sha1 of which to get tags from from options for 'prune' -k,keep= The amount of tags to keep - default is 20 +k,keep= The amount of tags to keep - default is 20 options for 'summary' -delimiter= The delimiter to use for the summary - default is '/' +delimiter= The delimiter to use for the summary - default is '/' " } @@ -407,8 +407,8 @@ cmd_add-as-submodule() { fi # Ensure parent dir for the submodule gitdir exists (supports nested paths) + mkdir -p ".git/modules/" local modules_gitdir=".git/modules/${arg_path}" - mkdir -p "$(dirname "$modules_gitdir")" echo "Cloning repo into ${modules_gitdir} with working tree: ${arg_path}" git clone --single-branch --no-tags --separate-git-dir="${modules_gitdir}" "${arg_remoteurl}" "${arg_path}"