Skip to content

Run workspace-side mirror git commands with the checkout's git environment; adopt mirrors owned by another user #181

Run workspace-side mirror git commands with the checkout's git environment; adopt mirrors owned by another user

Run workspace-side mirror git commands with the checkout's git environment; adopt mirrors owned by another user #181

name: Test Blacksmith Git Mirror
on:
pull_request:
push:
branches:
- main
jobs:
test-git-mirror:
runs-on: blacksmith
steps:
- name: Checkout action repo
uses: actions/checkout@v4
- name: Assert BLACKSMITH_AGENT_ADDR is advertised
run: test -n "$BLACKSMITH_AGENT_ADDR"
- name: Test checkout with git mirror (self)
uses: ./
with:
path: self-checkout
- name: Verify checkout succeeded
run: |
if [ ! -d "self-checkout/.git" ]; then
echo "Expected .git folder to exist"
exit 1
fi
echo "Checkout successful"
ls -la self-checkout/
- name: Test second checkout (should use cached mirror)
uses: ./
with:
path: second-checkout
- name: Verify second checkout succeeded
run: |
if [ ! -d "second-checkout/.git" ]; then
echo "Expected .git folder to exist"
exit 1
fi
echo "Second checkout successful (used cached mirror)"
- name: Test fetch-depth 0 checkout (fresh workspace, direct mirror ref copy)
uses: ./
with:
path: deep-checkout
fetch-depth: 0
- name: Verify deep checkout refs, tags and connectivity
run: |
set -euo pipefail
cd deep-checkout
# Exact ref parity with the remote: every branch/tag ls-remote
# advertises must exist locally at the same sha (modulo refs that
# moved between the checkout and this re-listing).
git ls-remote --heads --tags origin | grep -v '\^{}' | sort > /tmp/remote-refs
git for-each-ref --format='%(objectname) %(refname)' refs/remotes/origin refs/tags \
| grep -v 'refs/remotes/origin/HEAD' \
| sed 's#refs/remotes/origin/#refs/heads/#' | sort > /tmp/local-refs
if ! diff -u /tmp/remote-refs /tmp/local-refs; then
echo "Local refs do not match the remote advertisement"
exit 1
fi
# Annotated tags must peel through the shared object store
for tag in $(git for-each-ref --format='%(refname:short)' refs/tags | head -5); do
git rev-parse "$tag^{commit}" > /dev/null
done
# Every copied ref must be fully connected
git fsck --no-dangling
git rev-list --objects --all --quiet
echo "Deep checkout verified"
- name: Prepare reused deep workspace (origin/HEAD, stale ref, stale FETCH_HEAD)
run: |
set -euo pipefail
cd deep-checkout
git remote set-head origin -a
git update-ref refs/remotes/origin/zz-stale-test "$(git rev-parse HEAD)"
echo "stale fetch head content" > .git/FETCH_HEAD
- name: Test fetch-depth 0 checkout (reused workspace, ref reconciliation)
uses: ./
with:
path: deep-checkout
fetch-depth: 0
- name: Verify reused deep checkout preserved origin/HEAD and pruned stale refs
run: |
set -euo pipefail
cd deep-checkout
# origin/HEAD must survive reconciliation as a symref whose target exists
head_target=$(git symbolic-ref refs/remotes/origin/HEAD)
git rev-parse --verify "$head_target" > /dev/null
echo "origin/HEAD -> $head_target"
# The ref that no longer exists on the remote must have been pruned
if git rev-parse --verify refs/remotes/origin/zz-stale-test 2>/dev/null; then
echo "Stale ref was not pruned"
exit 1
fi
# Stale FETCH_HEAD from a previous checkout must not survive
if grep -q "stale fetch head content" .git/FETCH_HEAD 2>/dev/null; then
echo "Stale FETCH_HEAD survived the checkout"
exit 1
fi
git fsck --no-dangling
echo "Reused deep checkout verified"
# The shape that bit real container jobs: a privileged job container with the
# runner's devices passed through, checking out as root into a directory
# owned by the runner user (the workspace is bind-mounted from the host),
# then dissociating from the mirror. Without the checkout's git environment
# the dissociate repack fails with "dubious ownership". The target is a
# subdirectory given the workspace's owner rather than the workspace root,
# so the checkout does not wipe the action code out from under the job.
#
# The mirror on the sticky disk was committed by VM jobs, so it arrives owned
# by the runner user and the root checkout has to take it over. (Every
# checkout step mounts its own copy of the last commit, so ownership cannot be
# flipped between two steps of one job; the takeover is only observable
# across jobs, and in the VM direction only after a container job committed.)
test-git-mirror-container:
runs-on: blacksmith
container:
image: ubuntu:24.04
options: --privileged -v /dev:/dev
steps:
- name: Install git, sudo and the sticky disk tooling
run: |
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
bash git sudo util-linux e2fsprogs mount ca-certificates
- name: Checkout action repo
uses: actions/checkout@v4
with:
path: action
- name: Create a checkout directory owned by the runner user
shell: bash
run: |
set -euo pipefail
test "$(id -u)" = 0
mkdir host-owned && chown --reference=. host-owned
test "$(stat -c %u host-owned)" != 0
echo "workspace owned by uid $(stat -c %u .), git runs as uid $(id -u)"
- name: Test checkout with git mirror inside the container (dissociate)
uses: ./action
with:
path: host-owned
allow-inside-container: true
dissociate: true
- name: Verify the mirror was mounted and the workspace dissociated
shell: bash
run: |
set -euo pipefail
test "$(id -u)" = 0
mirror="/blacksmith-git-mirror/${GITHUB_REPOSITORY}/v1/${GITHUB_REPOSITORY_OWNER}-${GITHUB_REPOSITORY#*/}.git"
if ! mountpoint -q "$(dirname "$(dirname "$mirror")")"; then
echo "Sticky disk is not mounted inside the container; the mirror path was not exercised"
exit 1
fi
test -d "$mirror"
if [ "$(stat -c %u "$mirror")" != 0 ]; then
echo "mirror still owned by uid $(stat -c %u "$mirror"); the root checkout did not take it over"
exit 1
fi
cd host-owned
if [ -e .git/objects/info/alternates ]; then
echo "alternates file survived dissociate"
exit 1
fi
git config --global --add safe.directory "$PWD"
test "$(git rev-parse HEAD)" = "$GITHUB_SHA"
git fsck --no-dangling
echo "Container checkout with dissociate verified"
# The other direction: a VM job finding the mirror as a root container job
# left it. Real once a container job on a trusted trigger (push to main) has
# committed a root-owned mirror; the action logs the owner it took over from.
test-git-mirror-after-container:
runs-on: blacksmith
needs: test-git-mirror-container
steps:
- name: Checkout action repo
uses: actions/checkout@v4
- name: Test checkout against the mirror a container job left behind
uses: ./
with:
path: after-container
- name: Verify the mirror is owned by the runner user and was used
run: |
set -euo pipefail
mirror="/blacksmith-git-mirror/${GITHUB_REPOSITORY}/v1/${GITHUB_REPOSITORY_OWNER}-${GITHUB_REPOSITORY#*/}.git"
test -d "$mirror"
test "$(stat -c %u "$mirror")" = "$(id -u)"
grep -qF "$mirror/objects" after-container/.git/objects/info/alternates
git -C after-container fsck --no-dangling
echo "Checkout after a container job verified (mirror owned by uid $(id -u))"