Created
April 9, 2025 07:57
-
-
Save chmouel/d0a3da28d6e8a006f4f5bbdea6f6f7a0 to your computer and use it in GitHub Desktop.
git clone with merge
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
apiVersion: tekton.dev/v1beta1 | |
kind: Task | |
metadata: | |
# NOTE: Changed name to avoid conflict with the original | |
name: git-clone | |
labels: | |
app.kubernetes.io/version: "0.10" | |
annotations: | |
tekton.dev/pipelines.minVersion: "0.38.0" | |
tekton.dev/categories: Git | |
tekton.dev/tags: git | |
tekton.dev/displayName: "git clone" | |
tekton.dev/platforms: "linux/amd64,linux/s390x,linux/ppc64le,linux/arm64" | |
spec: | |
description: >- | |
Clones a Git repository and optionally merges a specified target branch | |
(e.g., 'main') into the checked-out revision before finishing. | |
workspaces: | |
- name: output | |
description: The git repo will be cloned and potentially merged onto the volume backing this Workspace. | |
- name: ssh-directory | |
optional: true | |
description: | | |
A .ssh directory with private key, known_hosts, config, etc. Copied to | |
the user's home before git commands are executed. Used to authenticate | |
with the git remote when performing the clone. Binding a Secret to this | |
Workspace is strongly recommended over other volume types. | |
- name: basic-auth | |
optional: true | |
description: | | |
A Workspace containing a .gitconfig and .git-credentials file. These | |
will be copied to the user's home before any git commands are run. Any | |
other files in this Workspace are ignored. It is strongly recommended | |
to use ssh-directory over basic-auth whenever possible and to bind a | |
Secret to this Workspace over other volume types. | |
- name: ssl-ca-directory | |
optional: true | |
description: | | |
A workspace containing CA certificates, this will be used by Git to | |
verify the peer with when fetching or pushing over HTTPS. | |
params: | |
- name: url | |
description: Repository URL to clone from. | |
type: string | |
- name: revision | |
description: Revision to checkout initially (branch, tag, sha, ref, etc...). | |
type: string | |
default: "" | |
- name: refspec | |
description: Refspec to fetch before checking out revision. | |
default: "" | |
- name: submodules | |
description: Initialize and fetch git submodules. | |
type: string | |
default: "true" | |
- name: depth | |
description: Perform a shallow clone initially. Merging requires fetching more history later if enabled. | |
type: string | |
# Depth 1 is fast for initial checkout, but merge might need more history. | |
# The task attempts to fetch the target branch specifically if merging. | |
default: "1" | |
- name: sslVerify | |
description: Set the `http.sslVerify` global git config. Setting this to `false` is not advised unless you are sure that you trust your git remote. | |
type: string | |
default: "true" | |
- name: crtFileName | |
description: file name of mounted crt using ssl-ca-directory workspace. default value is ca-bundle.crt. | |
type: string | |
default: "ca-bundle.crt" | |
- name: subdirectory | |
description: Subdirectory inside the `output` Workspace to clone the repo into. | |
type: string | |
default: "" | |
- name: sparseCheckoutDirectories | |
description: Define the directory patterns to match or exclude when performing a sparse checkout. | |
type: string | |
default: "" | |
- name: deleteExisting | |
description: Clean out the contents of the destination directory if it already exists before cloning. | |
type: string | |
default: "true" | |
- name: httpProxy | |
description: HTTP proxy server for non-SSL requests. | |
type: string | |
default: "" | |
- name: httpsProxy | |
description: HTTPS proxy server for SSL requests. | |
type: string | |
default: "" | |
- name: noProxy | |
description: Opt out of proxying HTTP/HTTPS requests. | |
type: string | |
default: "" | |
- name: verbose | |
description: Log the commands that are executed during this Task's operation. | |
type: string | |
default: "true" | |
- name: gitInitImage | |
description: The image providing the git-init binary and git command that this Task runs. | |
type: string | |
# Ensure this image contains a standard 'git' binary as well | |
default: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:v0.40.2" | |
- name: userHome | |
description: | | |
Absolute path to the user's home directory. | |
type: string | |
default: "/home/git" | |
- name: mergeTargetBranch | |
description: Set to "true" to merge the targetBranch into the checked-out revision. | |
type: string | |
default: "false" | |
- name: targetBranch | |
description: The target branch to merge into the revision (if mergeTargetBranch is true). | |
type: string | |
default: "main" | |
results: | |
- name: commit | |
description: The precise commit SHA of the revision that was initially checked out by this Task (before optional merge). | |
- name: url | |
description: The precise URL that was fetched by this Task. | |
- name: committer-date # Date of the original commit *before* any merge | |
description: The epoch timestamp of the original commit that was fetched by this Task (before optional merge). | |
- name: merged_sha | |
description: The SHA of the commit after merging the target branch (if mergeTargetBranch is true). | |
steps: | |
- name: clone | |
image: "$(params.gitInitImage)" | |
env: | |
- name: HOME | |
value: "$(params.userHome)" | |
- name: PARAM_URL | |
value: $(params.url) | |
- name: PARAM_REVISION | |
value: $(params.revision) | |
- name: PARAM_REFSPEC | |
value: $(params.refspec) | |
- name: PARAM_SUBMODULES | |
value: $(params.submodules) | |
- name: PARAM_DEPTH | |
value: $(params.depth) | |
- name: PARAM_SSL_VERIFY | |
value: $(params.sslVerify) | |
- name: PARAM_CRT_FILENAME | |
value: $(params.crtFileName) | |
- name: PARAM_SUBDIRECTORY | |
value: $(params.subdirectory) | |
- name: PARAM_DELETE_EXISTING | |
value: $(params.deleteExisting) | |
- name: PARAM_HTTP_PROXY | |
value: $(params.httpProxy) | |
- name: PARAM_HTTPS_PROXY | |
value: $(params.httpsProxy) | |
- name: PARAM_NO_PROXY | |
value: $(params.noProxy) | |
- name: PARAM_VERBOSE | |
value: $(params.verbose) | |
- name: PARAM_SPARSE_CHECKOUT_DIRECTORIES | |
value: $(params.sparseCheckoutDirectories) | |
- name: PARAM_USER_HOME | |
value: $(params.userHome) | |
- name: PARAM_MERGE_TARGET_BRANCH | |
value: $(params.mergeTargetBranch) | |
- name: PARAM_TARGET_BRANCH | |
value: $(params.targetBranch) | |
- name: WORKSPACE_OUTPUT_PATH | |
value: $(workspaces.output.path) | |
- name: WORKSPACE_SSH_DIRECTORY_BOUND | |
value: $(workspaces.ssh-directory.bound) | |
- name: WORKSPACE_SSH_DIRECTORY_PATH | |
value: $(workspaces.ssh-directory.path) | |
- name: WORKSPACE_BASIC_AUTH_DIRECTORY_BOUND | |
value: $(workspaces.basic-auth.bound) | |
- name: WORKSPACE_BASIC_AUTH_DIRECTORY_PATH | |
value: $(workspaces.basic-auth.path) | |
- name: WORKSPACE_SSL_CA_DIRECTORY_BOUND | |
value: $(workspaces.ssl-ca-directory.bound) | |
- name: WORKSPACE_SSL_CA_DIRECTORY_PATH | |
value: $(workspaces.ssl-ca-directory.path) | |
securityContext: | |
runAsNonRoot: true | |
runAsUser: 65532 | |
script: | | |
#!/usr/bin/env sh | |
set -eu # Ensure script fails on error | |
if [ "${PARAM_VERBOSE}" = "true" ] ; then | |
set -x # Print commands for debugging | |
fi | |
# --- Setup Authentication and SSL (Same as original task) --- | |
if [ "${WORKSPACE_BASIC_AUTH_DIRECTORY_BOUND}" = "true" ] ; then | |
cp "${WORKSPACE_BASIC_AUTH_DIRECTORY_PATH}/.git-credentials" "${PARAM_USER_HOME}/.git-credentials" | |
cp "${WORKSPACE_BASIC_AUTH_DIRECTORY_PATH}/.gitconfig" "${PARAM_USER_HOME}/.gitconfig" | |
chmod 400 "${PARAM_USER_HOME}/.git-credentials" | |
chmod 400 "${PARAM_USER_HOME}/.gitconfig" | |
fi | |
if [ "${WORKSPACE_SSH_DIRECTORY_BOUND}" = "true" ] ; then | |
cp -R "${WORKSPACE_SSH_DIRECTORY_PATH}" "${PARAM_USER_HOME}"/.ssh | |
chmod 700 "${PARAM_USER_HOME}"/.ssh | |
chmod -R 400 "${PARAM_USER_HOME}"/.ssh/* | |
fi | |
if [ "${WORKSPACE_SSL_CA_DIRECTORY_BOUND}" = "true" ] ; then | |
export GIT_SSL_CAPATH="${WORKSPACE_SSL_CA_DIRECTORY_PATH}" | |
if [ "${PARAM_CRT_FILENAME}" != "" ] ; then | |
export GIT_SSL_CAINFO="${WORKSPACE_SSL_CA_DIRECTORY_PATH}/${PARAM_CRT_FILENAME}" | |
fi | |
fi | |
# --- Prepare Checkout Directory (Same as original task) --- | |
CHECKOUT_DIR="${WORKSPACE_OUTPUT_PATH}/${PARAM_SUBDIRECTORY}" | |
cleandir() { | |
if [ -d "${CHECKOUT_DIR}" ] ; then | |
rm -rf "${CHECKOUT_DIR:?}"/* | |
rm -rf "${CHECKOUT_DIR}"/.[!.]* | |
rm -rf "${CHECKOUT_DIR}"/..?* | |
fi | |
} | |
if [ "${PARAM_DELETE_EXISTING}" = "true" ] ; then | |
cleandir || true | |
fi | |
# --- Set Proxy (Same as original task) --- | |
test -z "${PARAM_HTTP_PROXY}" || export HTTP_PROXY="${PARAM_HTTP_PROXY}" | |
test -z "${PARAM_HTTPS_PROXY}" || export HTTPS_PROXY="${PARAM_HTTPS_PROXY}" | |
test -z "${PARAM_NO_PROXY}" || export NO_PROXY="${PARAM_NO_PROXY}" | |
# --- Initial Clone & Checkout using git-init (Same as original task) --- | |
git config --global --add safe.directory "${WORKSPACE_OUTPUT_PATH}" # Needed for git commands below | |
/ko-app/git-init \ | |
-url="${PARAM_URL}" \ | |
-revision="${PARAM_REVISION}" \ | |
-refspec="${PARAM_REFSPEC}" \ | |
-path="${CHECKOUT_DIR}" \ | |
-sslVerify="${PARAM_SSL_VERIFY}" \ | |
-submodules="${PARAM_SUBMODULES}" \ | |
-depth="${PARAM_DEPTH}" \ | |
-sparseCheckoutDirectories="${PARAM_SPARSE_CHECKOUT_DIRECTORIES}" | |
cd "${CHECKOUT_DIR}" | |
INITIAL_SHA="$(git rev-parse HEAD)" # Capture the originally checked-out commit | |
EXIT_CODE="$?" | |
if [ "${EXIT_CODE}" != 0 ] ; then | |
echo "ERROR: Initial git checkout failed." >&2 | |
exit "${EXIT_CODE}" | |
fi | |
INITIAL_COMMITTER_DATE="$(git log -1 --pretty=%ct)" | |
if [ "${PARAM_MERGE_TARGET_BRANCH}" = "true" ]; then | |
echo "Merge option enabled. Attempting to merge target branch '${PARAM_TARGET_BRANCH}' into HEAD (${INITIAL_SHA})." | |
# Ensure we have the target branch history. Fetch it specifically. | |
# This might fetch more than strictly necessary if depth was > 1, but ensures it's available. | |
# Use standard 'git' command - assumes it's available in the image. | |
echo "Fetching target branch '${PARAM_TARGET_BRANCH}'..." | |
git fetch origin "${PARAM_TARGET_BRANCH}" | |
FETCH_EXIT_CODE="$?" | |
if [ "${FETCH_EXIT_CODE}" != "0" ]; then | |
echo "ERROR: Failed to fetch target branch '${PARAM_TARGET_BRANCH}'." >&2 | |
exit "${FETCH_EXIT_CODE}" | |
fi | |
# Perform the merge using the fetched remote tracking branch | |
echo "Merging origin/${PARAM_TARGET_BRANCH} into current HEAD..." | |
# Use --no-ff to create a merge commit even if it could be fast-forwarded, | |
# which can make history clearer, but is optional. | |
git merge "origin/${PARAM_TARGET_BRANCH}" --no-commit --no-ff # Let's try --no-commit first to check for conflicts before committing | |
MERGE_CHECK_EXIT_CODE="$?" | |
if [ "${MERGE_CHECK_EXIT_CODE}" != "0" ] ; then | |
echo "ERROR: Merge conflict detected or merge failed before commit." >&2 | |
echo "--- Git Status ---" | |
git status | |
echo "------------------" | |
# Optional: Attempt to abort the merge? | |
# git merge --abort | |
exit "${MERGE_CHECK_EXIT_CODE}" | |
else | |
# If merge is successful (no conflicts), commit the merge | |
echo "Merge successful (no conflicts found), committing..." | |
# Set committer info - adjust as needed | |
git config --global user.email "[email protected]" | |
git config --global user.name "Tekton Git Clone Task" | |
git commit -m "Merge branch '${PARAM_TARGET_BRANCH}' into ${INITIAL_SHA}" | |
COMMIT_EXIT_CODE="$?" | |
if [ "${COMMIT_EXIT_CODE}" != "0" ]; then | |
echo "ERROR: Failed to commit merge." >&2 | |
exit "${COMMIT_EXIT_CODE}" | |
fi | |
echo "Successfully merged and committed branch '${PARAM_TARGET_BRANCH}' into initial revision." | |
MERGED_SHA=$(git rev-parse HEAD) | |
echo "New HEAD after merge: ${MERGED_SHA}" | |
echo "${MERGED_SHA}" > "$(results.commit.merged_sha)" | |
fi | |
else | |
echo "Merge option disabled. Using checked-out revision ${INITIAL_SHA} directly." | |
fi | |
# --- Write Results (Using the initial commit info) --- | |
# Note: Results reflect the state *before* the merge, identifying the PR commit. | |
# The workspace 'output' contains the potentially merged code. | |
printf "%s" "${INITIAL_COMMITTER_DATE}" > "$(results.committer-date.path)" | |
printf "%s" "${INITIAL_SHA}" > "$(results.commit.path)" | |
printf "%s" "${PARAM_URL}" > "$(results.url.path)" | |
echo "Task completed. Workspace contains code at initial revision ${INITIAL_SHA} (potentially merged with ${PARAM_TARGET_BRANCH})." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment