sha256:110c30c90a971fd2ca660b616a915de28d1dc9b6041a3c1c8b37a2fc526b65e3
Last pushed
about 4 hours by cdupuis
Type
Sandbox Kit
Manifest digest
sha256:110c30c90a971fd2ca660b616a915de28d1dc9b6041a3c1c8b37a2fc526b65e3
schemaVersion: "2"
kind: mixin
name: github-clone
displayName: GitHub Clone
description: Clones a GitHub repository into the sandbox at create time, using the gh CLI with a proxy-managed GH_TOKEN sentinel. Auto-installs gh if the base image doesn't have it.
args:
dir:
default: /project
description: |
Absolute directory inside the sandbox to clone into. If the directory
already exists and is non-empty, the clone is skipped (the mixin
re-runs cleanly on `sbx create` retries).
pattern: ^/[A-Za-z0-9._/-]+$
pr:
default: ""
description: |
Optional pull request number to check out with `gh pr checkout` after
the clone. The PR lands on a local branch, so `git push` from the
sandbox updates it. Empty (the default) leaves the working copy on
`ref` or the repo's default branch. Mutually exclusive with `ref`.
pattern: ^[0-9]*$
ref:
default: ""
description: |
Optional git ref (branch, tag, or 7–40 hex commit SHA) to check out.
Empty (the default) clones the repo's default branch. Mutually
exclusive with `pr`.
pattern: ^[A-Za-z0-9._/-]*$
repo:
required: true
description: |
GitHub repository to clone. Accepts the `owner/name` shorthand, a full
`https://github.com/owner/name[.git]` URL, or an
`[email protected]:owner/name[.git]` SSH URL. Any other form is rejected
before the clone shells out.
pattern: ^(?:[A-Za-z0-9._-]+/[A-Za-z0-9._-]+(?:\.git)?|https://github\.com/[A-Za-z0-9._-]+/[A-Za-z0-9._-]+(?:\.git)?|git@github\.com:[A-Za-z0-9._-]+/[A-Za-z0-9._-]+(?:\.git)?)$
agentInstructions:
content: |
## github-clone
A GitHub repository (`${{ kit.args.repo }}`) has been cloned into
`${{ kit.args.dir }}` at sandbox-create time. Treat that directory as
the working copy for this task rather than `~/workspace`.
The kit's `pr` arg is `${{ kit.args.pr }}` — empty when no pull request
was requested. When it holds a number, that PR is already checked out on
a local branch via `gh pr checkout` and the clone has full history, so
`git diff <base>...HEAD` shows the PR's changes and `git push` updates
the PR itself.
Authentication is proxy-mediated: `GH_TOKEN` in the container is a
sentinel — the sandbox proxy swaps in the real token bound on the host
(via `sbx secret set -g github`) when talking to `api.github.com` and
`github.com`. `gh` and `git` HTTPS operations both use this path, so
`gh api …`, `gh pr create`, `git push`, and friends work without any
extra setup.
Public repos work with no host-side credential bound; only writes and
private-repo access need one.
permissions:
network:
allow:
- api.github.com
- github.com
- codeload.github.com
- raw.githubusercontent.com
- cli.github.com
- archive.ubuntu.com
- security.ubuntu.com
- ports.ubuntu.com
- download.docker.com
credentials:
- service: github
description: Token used to clone the repository and for later gh/git operations.
apiKey:
name: GH_TOKEN
proxyManaged: true
inject:
- domain: api.github.com
header: Authorization
format: Bearer %s
- domain: github.com
header: Authorization
format: '%s'
username: x-access-token
setup:
install:
- command: |
set -euo pipefail
NEED=""
command -v gh >/dev/null 2>&1 || NEED="$NEED gh"
command -v git >/dev/null 2>&1 || NEED="$NEED git"
if [ -z "$NEED" ]; then
exit 0
fi
if ! command -v apt-get >/dev/null 2>&1; then
echo "github-clone: missing:$NEED and apt-get is not available — this kit only knows how to auto-install on Debian/Ubuntu bases. Layer a mixin that installs the missing tools, or pick a base agent that ships them." >&2
exit 1
fi
export DEBIAN_FRONTEND=noninteractive
# Add GitHub's official apt source only when gh is what's missing.
# Recipe follows https://github.com/cli/cli/blob/trunk/docs/install_linux.md.
case " $NEED " in
*' gh '*)
install -m 0755 -d /etc/apt/keyrings
if command -v curl >/dev/null 2>&1; then
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
-o /etc/apt/keyrings/githubcli-archive-keyring.gpg
elif command -v wget >/dev/null 2>&1; then
wget -qO /etc/apt/keyrings/githubcli-archive-keyring.gpg \
https://cli.github.com/packages/githubcli-archive-keyring.gpg
else
# Fall back to installing curl first via apt so we can fetch
# the keyring; the base at least has ca-certificates for the
# https handshake (every *-docker base does).
apt-get update -qq
apt-get install -y -qq --no-install-recommends curl ca-certificates
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
-o /etc/apt/keyrings/githubcli-archive-keyring.gpg
fi
chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
> /etc/apt/sources.list.d/github-cli.list
;;
esac
apt-get update -qq
# `$NEED` is intentionally unquoted — it's a space-separated list
# of package names constructed from the two `command -v` checks
# above, not user input.
apt-get install -y -qq --no-install-recommends $NEED
rm -rf /var/lib/apt/lists/*
user: "0"
description: Install gh (and git) via apt if the base image doesn't already ship them
- command: |
set -euo pipefail
REF='${{ kit.args.ref }}'
PR='${{ kit.args.pr }}'
DIR='${{ kit.args.dir }}'
# Both args pick the checked-out commit, so honouring both would mean
# silently letting one win. Fail at create time instead.
if [ -n "$REF" ] && [ -n "$PR" ]; then
echo "github-clone: 'ref' and 'pr' are mutually exclusive — pass one or the other." >&2
exit 1
fi
# Route git HTTPS auth for github.com through gh's credential helper
# for both the clone below and later agent-run git operations.
git config --system --unset-all credential."https://github.com".helper 2>/dev/null || true
git config --system --add credential."https://github.com".helper ''
git config --system --add credential."https://github.com".helper '!gh auth git-credential'
# Idempotent re-run: don't change ownership of an existing working
# tree. The agent hook below performs the same check and skips it.
if [ -d "$DIR" ] && [ -n "$(ls -A "$DIR" 2>/dev/null || true)" ]; then
exit 0
fi
mkdir -p "$DIR"
chown agent:agent "$DIR"
user: "0"
description: Prepare ${{ kit.args.dir }} for an agent-owned clone
- command: |
set -euo pipefail
REPO='${{ kit.args.repo }}'
REF='${{ kit.args.ref }}'
PR='${{ kit.args.pr }}'
DIR='${{ kit.args.dir }}'
if [ -d "$DIR" ] && [ -n "$(ls -A "$DIR" 2>/dev/null || true)" ]; then
echo "github-clone: '$DIR' already exists and is non-empty; skipping clone." >&2
exit 0
fi
# gh reads GH_TOKEN itself, so no extra plumbing is needed for
# the clone. Extra flags after `--` are passed straight to
# `git clone`.
if [ -n "$PR" ]; then
# Full history on purpose. `gh pr checkout` fetches the PR head
# into the clone, and in a --depth 1 repo that head shares no
# ancestor with the base branch, so `git diff base...HEAD` and
# `git log base..HEAD` — the first things anyone reviewing a PR
# reaches for — fail with "no merge base".
gh repo clone "$REPO" "$DIR"
elif [ -n "$REF" ]; then
# Fast path: shallow-clone a branch or tag directly. Fallback:
# if REF is a commit SHA (not a branch/tag), --branch fails, so
# do a full clone and check the SHA out.
if ! gh repo clone "$REPO" "$DIR" -- --depth 1 --branch "$REF" 2>/dev/null; then
# Keep the root-created destination itself: uid 1000 may not be
# able to recreate it beneath a root-owned parent such as `/`.
find "$DIR" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
gh repo clone "$REPO" "$DIR"
git -C "$DIR" checkout "$REF"
fi
else
gh repo clone "$REPO" "$DIR" -- --depth 1
fi
if [ -n "$PR" ]; then
# Run from inside the clone so gh resolves the repo from origin —
# no --repo, which would have to re-parse $REPO's URL forms. gh
# looks the PR up over api.github.com, fetches its head, and
# leaves a local branch behind, including for PRs from forks.
( cd "$DIR" && gh pr checkout "$PR" )
fi
user: "1000"
description: Clone ${{ kit.args.repo }} into ${{ kit.args.dir }} via gh