Run workspace-side mirror git commands with the checkout's git environment; adopt mirrors owned by another user #181
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
| 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))" |