Gathered from phi4 llm To achieve your goal of creating a Git bare repository on a Linux server with a
post-receivehook that triggers a CI build, follow these steps:
Step 1: Create the Bare Repository
-
SSH into your Linux server.
-
Create the directory for the bare repository:
mkdir -p /path/to/repo.git
cd /path/to/repo.git
- Initialize the bare repository:
git init --bare
Step 2: Create the post-receive Hook
- Navigate to the hooks directory:
cd hooks
- Create a new
post-receivehook script:
touch post-receive
chmod +x post-receive
-
Edit the
post-receivefile with your desired script:Here's an example script that reads the
new_ref, exports a variable for the CI archive, and executesmake ciwithnew_refas 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:
- On each developer's machine, navigate to their local Git repository:
cd /path/to/local/repo
- 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.
- 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
makeand any other dependencies required by yourmake cicommand are installed on the server. - The script assumes you have a Makefile with a target named
ciwhich accepts aREFargument. 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