diff --git a/docs/deployment/methods/git.md b/docs/deployment/methods/git.md index 061c3c926..564e7425e 100644 --- a/docs/deployment/methods/git.md +++ b/docs/deployment/methods/git.md @@ -109,6 +109,8 @@ dokku git:set node-js-app keep-git-dir false dokku git:set node-js-app keep-git-dir "" ``` +Please keep in mind that setting `keep-git-dir` to `true` may result in unstaged changes shown within the built container due to the build process generating application changes within the built app directory. + ### Initializing an app repository from a remote repository > The application must exist before the repository can be initialized diff --git a/plugins/git/functions b/plugins/git/functions index 450685c1a..114c28971 100755 --- a/plugins/git/functions +++ b/plugins/git/functions @@ -9,19 +9,10 @@ set -eo pipefail use_git_worktree() { declare desc="detects whether to use git worktree" - local GIT_VERSION MAJOR_VERSION MINOR_VERSION + declare deprecated=true + dokku_log_warn "Deprecated: fn-git-use-worktree" - GIT_VERSION=$(git --version | awk '{split($0,a," "); print a[3]}') - MAJOR_VERSION=$(echo "$GIT_VERSION" | awk '{split($0,a,"."); print a[1]}') - MINOR_VERSION=$(echo "$GIT_VERSION" | awk '{split($0,a,"."); print a[2]}') - - if [[ "$MAJOR_VERSION" -ge "3" ]]; then - return 0 - elif [[ "$MAJOR_VERSION" -eq "2" ]] && [[ "$MINOR_VERSION" -ge "11" ]]; then - return 0 - else - return 1 - fi + fn-git-use-worktree } git_build_app_repo() { @@ -35,49 +26,13 @@ git_build_app_repo() { local GIT_BUILD_APP_REPO_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") trap "rm -rf '$GIT_BUILD_APP_REPO_TMP_WORK_DIR' >/dev/null" RETURN INT TERM EXIT - local TMP_TAG="dokku/$REV" chmod 755 "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" - unset GIT_DIR GIT_WORK_TREE + unset GIT_DIR GIT_QUARANTINE_PATH GIT_WORK_TREE ! plugn trigger app-exists "$APP" >/dev/null 2>&1 && plugn trigger app-maybe-create "$APP" - if use_git_worktree; then - # git worktree - this method uses git worktree which was introduced in git 2.5 - pushd "$DOKKU_ROOT/$APP" >/dev/null - # unset the git quarantine path to allow us to use 2.13.0+ - # See this issue for more information: https://github.com/dokku/dokku/issues/2796 - suppress_output env -u GIT_QUARANTINE_PATH git worktree add "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV" - popd >/dev/null 2>&1 || pushd "/tmp" >/dev/null - pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" >/dev/null - else - # git clone - this method creates a new git repository and adds the primary - # repo as a remote, then does a fetch depth=1 to avoid cloning - # the entire repo. - # Not working for git >= 2.11.0 due to changes introduced - # in this merge: - # https://github.com/git/git/commit/25ab004c53cdcfea485e5bf437aeaa74df47196d - pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" >/dev/null - GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &>/dev/null || true - GIT_DIR="$DOKKU_ROOT/$APP" git tag "$TMP_TAG" "$REV" &>/dev/null - git init &>/dev/null - git config advice.detachedHead false - git remote add origin "$DOKKU_ROOT/$APP" &>/dev/null - git fetch --depth=1 origin "refs/tags/$TMP_TAG" &>/dev/null - git reset --hard FETCH_HEAD &>/dev/null - GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &>/dev/null || true - fi - - suppress_output env -u GIT_QUARANTINE_PATH git submodule update --init --recursive - local DOKKU_KEEP_GIT_DIR="$(fn-plugin-property-get "git" "$APP" "keep-git-dir" "")" - if [[ "$DOKKU_KEEP_GIT_DIR" != "true" ]]; then - find . -name .git -prune -exec rm -rf {} \; >/dev/null - fi - - if use_git_worktree; then - pushd "$DOKKU_ROOT/$APP" >/dev/null - git worktree prune - popd >/dev/null - fi + fn-git-setup-build-dir "$APP" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV" + pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" >/dev/null local DOKKU_APP_DISABLE_ANSI_PREFIX_REMOVAL DOKKU_GLOBAL_DISABLE_ANSI_PREFIX_REMOVAL DOKKU_DISABLE_ANSI_PREFIX_REMOVAL DOKKU_APP_DISABLE_ANSI_PREFIX_REMOVAL=$(config_get "$APP" DOKKU_DISABLE_ANSI_PREFIX_REMOVAL || true) diff --git a/plugins/git/internal-functions b/plugins/git/internal-functions index 74df9a476..8c781c8ae 100755 --- a/plugins/git/internal-functions +++ b/plugins/git/internal-functions @@ -257,3 +257,88 @@ fn-git-deploy-branch() { echo "$DEFAULT_BRANCH" fi } + +fn-git-setup-build-dir() { + declare APP="$1" GIT_WORKDIR="$2" REV="$3" + local DOKKU_KEEP_GIT_DIR="$(fn-plugin-property-get "git" "$APP" "keep-git-dir" "")" + + if ! fn-git-use-worktree; then + fn-git-setup-build-dir-old "$APP" "$GIT_WORKDIR" "$REV" + return + fi + + if [[ "$DOKKU_KEEP_GIT_DIR" == "true" ]]; then + fn-git-setup-build-dir-new "$APP" "$GIT_WORKDIR" "$REV" + else + fn-git-setup-build-dir-worktree "$APP" "$GIT_WORKDIR" "$REV" + fi +} + +fn-git-setup-build-dir-new() { + declare APP="$1" GIT_WORKDIR="$2" REV="$3" + local DOKKU_DEPLOY_BRANCH="$(fn-git-deploy-branch "$APP")" + + # repo workaround from https://gitlab.com/gitlab-org/gitlab-foss/-/issues/52249 + # and https://docs.gitlab.com/ee/user/project/repository/repository_mirroring.html#preventing-conflicts-using-a-pre-receive-hook + env -u GIT_QUARANTINE_PATH git -C "$DOKKU_ROOT/$APP" archive "$REV" | tar x --warning=none -C "${GIT_WORKDIR}" + suppress_output env -u GIT_QUARANTINE_PATH git -C "$GIT_WORKDIR" clone --bare "$DOKKU_ROOT/$APP" .git + suppress_output env -u GIT_QUARANTINE_PATH git -C "$GIT_WORKDIR" config --local gc.auto 0 + suppress_output env -u GIT_QUARANTINE_PATH git -C "$GIT_WORKDIR" config --local --bool core.bare false + suppress_output env -u GIT_QUARANTINE_PATH git -C "$GIT_WORKDIR" config --local receive.denyCurrentBranch ignore + suppress_output env -u GIT_QUARANTINE_PATH git -C "$GIT_WORKDIR" symbolic-ref HEAD "refs/heads/$DOKKU_DEPLOY_BRANCH" + suppress_output env -u GIT_QUARANTINE_PATH git -C "$GIT_WORKDIR" reset "$REV" -- . + suppress_output env -u GIT_QUARANTINE_PATH git -C "$GIT_WORKDIR" push $GIT_WORKDIR $REV:refs/heads/$DOKKU_DEPLOY_BRANCH + fn-git-setup-build-dir-submodules "$APP" "$GIT_WORKDIR" +} + +fn-git-setup-build-dir-old() { + declare APP="$1" GIT_WORKDIR="$2" REV="$3" + local TMP_TAG="dokku/$REV" + + suppress_output git -C "$DOKKU_ROOT/$APP" tag -d "$TMP_TAG" &>/dev/null || true + suppress_output git -C "$DOKKU_ROOT/$APP" tag "$TMP_TAG" "$REV" + suppress_output git -C "$GIT_WORKDIR" init + suppress_output git -C "$GIT_WORKDIR" config advice.detachedHead false + suppress_output git -C "$GIT_WORKDIR" remote add origin "$DOKKU_ROOT/$APP" + suppress_output git -C "$GIT_WORKDIR" fetch --depth=1 origin "refs/tags/$TMP_TAG" + suppress_output git -C "$GIT_WORKDIR" reset --hard FETCH_HEAD + suppress_output git -C "$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &>/dev/null || true + fn-git-setup-build-dir-submodules "$APP" "$GIT_WORKDIR" +} + +fn-git-setup-build-dir-worktree() { + declare APP="$1" GIT_WORKDIR="$2" REV="$3" + + # unset the git quarantine path to allow us to use 2.13.0+ + # See this issue for more information: https://github.com/dokku/dokku/issues/2796 + suppress_output env -u GIT_QUARANTINE_PATH git -C "$DOKKU_ROOT/$APP" worktree add "$GIT_WORKDIR" "$REV" + fn-git-setup-build-dir-submodules "$APP" "$GIT_WORKDIR" + suppress_output git -C "$DOKKU_ROOT/$APP" worktree prune +} + +fn-git-setup-build-dir-submodules() { + declare APP="$1" GIT_WORKDIR="$2" + local DOKKU_KEEP_GIT_DIR="$(fn-plugin-property-get "git" "$APP" "keep-git-dir" "")" + + suppress_output env -u GIT_QUARANTINE_PATH git -C "$GIT_WORKDIR" submodule update --init --recursive + if [[ "$DOKKU_KEEP_GIT_DIR" != "true" ]]; then + find "$GIT_WORKDIR" -name .git -prune -exec rm -rf {} \; >/dev/null + fi +} + +fn-git-use-worktree() { + declare desc="detects whether to use git worktree" + local GIT_VERSION MAJOR_VERSION MINOR_VERSION + + GIT_VERSION=$(git --version | awk '{split($0,a," "); print a[3]}') + MAJOR_VERSION=$(echo "$GIT_VERSION" | awk '{split($0,a,"."); print a[1]}') + MINOR_VERSION=$(echo "$GIT_VERSION" | awk '{split($0,a,"."); print a[2]}') + + if [[ "$MAJOR_VERSION" -ge "3" ]]; then + return 0 + elif [[ "$MAJOR_VERSION" -eq "2" ]] && [[ "$MINOR_VERSION" -ge "11" ]]; then + return 0 + else + return 1 + fi +} diff --git a/tests/unit/git_1.bats b/tests/unit/git_1.bats index cb49ea9a1..760b90f6f 100644 --- a/tests/unit/git_1.bats +++ b/tests/unit/git_1.bats @@ -71,3 +71,35 @@ teardown() { echo "status: $status" assert_output_exists } + +@test "(git) keep-git-dir" { + run /bin/bash -c "dokku git:set $TEST_APP keep-git-dir true" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku enter $TEST_APP web ls .git" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "branches" + assert_output_contains "config" + assert_output_contains "description" + assert_output_contains "HEAD" + assert_output_contains "hooks" + assert_output_contains "index" + assert_output_contains "info" + assert_output_contains "logs" + assert_output_contains "objects" + assert_output_contains "refs" + + run /bin/bash -c "dokku enter $TEST_APP web test -d .git" + echo "output: $output" + echo "status: $status" + assert_success +}