Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Gathered from phi4 llm To achieve your goal of creating a Git bare repository on a Linux server with a post-receive hook that triggers a CI build, follow these steps:

Step 1: Create the Bare Repository

  1. SSH into your Linux server.

  2. Create the directory for the bare repository:

mkdir -p /path/to/repo.git
cd /path/to/repo.git
  1. Initialize the bare repository:
git init --bare

Step 2: Create the post-receive Hook

  1. Navigate to the hooks directory:
cd hooks
  1. Create a new post-receive hook script:
touch post-receive
chmod +x post-receive
  1. Edit the post-receive file with your desired script:

    Here's an example script that reads the new_ref, exports a variable for the CI archive, and executes make ci with new_ref as an argument:

#!/bin/bash
prj=basename $(pwd) | sed 's?.git??'
while read oldrev newrev refname; do
      # Extract branch name from refname (e.g., refs/heads/main -> main)
      branch=$(echo $refname | sed 's|refs/heads/||')

      # Define the filename for CI archive based on the branch and commit hash
      ci_filename="$prj-$branch-${newrev:0:7}.tar.gz"

      echo "Branch '$branch' updated to ${newrev:0:7}."

      # Export the filename variable (for use in other scripts orcommands)
      export CI_FILENAME=$ci_filename

      # Execute 'make ci' with the new ref as an argument
      echo "Running make ci for branch '$branch' at commit '${newrev:0:7}'..."
      if ! make ci REF=$refname; then
         echo "CI build failed for branch '$branch'."
         exit 1
      fi

      echo "CI build succeeded for branch '$branch'. Archive: $ci_filename"
done

Step 3: Configure Developer Repositories to Use the CI Remote

Each developer needs to add a new remote called ci pointing to your bare repository. This can be done with the following steps:

  1. On each developer's machine, navigate to their local Git repository:
cd /path/to/local/repo
  1. Add the CI remote:
git remote add ci ssh://username@server_ip:/path/to/repo.git

Replace username, server_ip, and /path/to/repo.git with your server's SSH username, IP address, and the path to the bare repository.

  1. Push changes to trigger CI: When a developer wants to push their changes and trigger the CI process, they can use:
git push ci main  # or replace 'main' with any branch name as needed

Notes

  • Ensure that make and any other dependencies required by your make ci command are installed on the server.
  • The script assumes you have a Makefile with a target named ci which accepts a REF argument. Adjust this part if necessary.
  • Consider adding error handling or logging to better track CI builds in production environments.

By following these steps, you should be able to set up your Git bare repository on the server and configure it to trigger CI builds using a post-receive hook script.

#!/bin/bash
prj=$(basename $(pwd) | sed 's?.git??')
bare_dir="$(pwd)"

function ci() {
	make ci REF=$refname CI_FILENAME=$1
}

while read oldrev newrev refname; do
	# Extract branch name from refname (e.g., refs/heads/main -> main)
	branch=$(echo $refname | sed 's|refs/heads/||')
	echo "Branch '$branch' updated to ${newrev:0:7}."

	# Define the filename for CI archive based on the branch and commit hash
	ci_filename="$prj-$branch-${newrev:0:7}.tar.gz"

	# Define directory for the worktree
	worktree_dir="$HOME/$prj-$branch-worktree"

	# Check if worktree exists
	if git --git-dir="$bare_dir" worktree list | grep -q "$worktree_dir"; then
		echo "Worktree for '$branch' already exists at $worktree_dir"
	else
		echo "Creating new worktree for '$branch'"
		git --git-dir="$bare_dir" worktree add -f "$worktree_dir" "$branch" || {
			echo "Failed to create worktree for branch '$branch'."
			exit 1
		}
	fi

	# Change to the worktree directory
	cd "$worktree_dir" || {
		echo "Failed to change to worktree directory."
		exit 1
	}

	# Update the worktree directly to the pushed commit
	echo "Updating worktree to latest commit ${newrev:0:7}"

	# Stash any local changes
	echo "Stashing local changes if any exist"
	git --git-dir="$bare_dir" --work-tree="$worktree_dir" stash push -m "Auto-stashed before updating to ${newrev:0:7}" || {
		echo "Note: No local changes to stash or stash failed."
		# Continue even if stash fails (could be no changes)
	}

	# Skip checkout and directly reset to the new commit
	# Using -f to force the update even if the branch is already checked out
	echo "Resetting to commit ${newrev:0:7}"
	git --git-dir="$bare_dir" --work-tree="$worktree_dir" reset --hard "$newrev" || {
		echo "Failed to reset to commit ${newrev:0:7}."
		exit 1
	}

	# Pop the stash if there are any stashed changes
	if git --git-dir="$bare_dir" --work-tree="$worktree_dir" stash list | grep -q "Auto-stashed before updating to ${newrev:0:7}"; then
		echo "Restoring local changes from stash"
		git --git-dir="$bare_dir" --work-tree="$worktree_dir" stash pop || {
			echo "Warning: Stash pop had conflicts. Please check the worktree."
			# Continue execution even if there are conflicts
		}
	fi

	# Export the filename variable (for use in other scripts or commands)
	export CI_FILENAME=$ci_filename

	if ! ci "$CI_FILENAME"; then
		echo "CI build failed for branch '$branch'."
		cd - >/dev/null || cd "$HOME"
		exit 1
	else
		echo "make ci was successful for branch '$branch'."
	fi

	# Return to the original directory
	cd "$bare_dir" || cd "$HOME"
done