Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions scripts/selfhost-update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ fi
cd "$SCRIPT_DIR/.."

current_branch="$(git rev-parse --abbrev-ref HEAD)"
if [ "$current_branch" = "HEAD" ]; then
echo "error: checkout is in a detached HEAD state, expected to be on '$BRANCH' -- checkout" \
"$BRANCH first (this script only updates a branch-tracking checkout)" >&2
exit 1
fi
if [ "$current_branch" != "$BRANCH" ]; then
echo "error: currently on '$current_branch', expected '$BRANCH' -- checkout $BRANCH first, or" \
"set SELFHOST_UPDATE_BRANCH=$current_branch if that is deliberate" >&2
Expand All @@ -61,6 +66,13 @@ fi
echo "selfhost update: fetching $REMOTE"
git fetch "$REMOTE"

if ! git rev-parse --verify --quiet "$REMOTE/$BRANCH" >/dev/null; then
echo "error: $REMOTE/$BRANCH does not exist after fetching $REMOTE -- check" \
"SELFHOST_UPDATE_REMOTE/SELFHOST_UPDATE_BRANCH for a typo, or confirm $REMOTE actually has a" \
"'$BRANCH' branch" >&2
exit 1
fi

echo "selfhost update: fast-forwarding $BRANCH to $REMOTE/$BRANCH"
if ! git merge --ff-only "$REMOTE/$BRANCH"; then
echo "error: $BRANCH could not be fast-forwarded to $REMOTE/$BRANCH -- local history has" \
Expand Down
13 changes: 9 additions & 4 deletions test/unit/docs-selfhost-git-deploy-hygiene.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ describe("self-host git-deploy hygiene (#1660)", () => {
});

it("does not shadow any file actually tracked in the repo", () => {
// The real regression concern: a future PR could add a legitimately-tracked file whose name
// happens to match `*.bak-*` or `*.backup-*`, which would silently untrack it the moment
// someone re-clones. Ask git itself, rather than approximating the glob in JS, since git's
// own matcher is the one that actually enforces these patterns.
// The real regression concern: .gitignore has no effect on a file git already tracks -- it
// keeps being tracked, staged, and diffed normally forever, ignore pattern or not (verified:
// `git add -A` still stages a modification to an already-tracked-but-now-ignored file). The
// actual risk is the opposite direction -- a future PR that genuinely intends to add a NEW
// tracked file whose name happens to match `*.bak-*` or `*.backup-*` would have that file
// silently excluded from `git status`'s untracked list and from `git add -A`/`git add .`, so
// it could go uncommitted without anyone noticing (an explicit `git add <path>` at least warns
// and needs `-f`; a broad add just skips it quietly). Ask git itself, rather than approximating
// the glob in JS, since git's own matcher is the one that actually enforces these patterns.
const result = spawnSync("git", ["ls-files"], { encoding: "utf8" });
expect(result.status).toBe(0);
const trackedFiles = result.stdout.split("\n").filter(Boolean);
Expand Down
28 changes: 28 additions & 0 deletions test/unit/selfhost-update-script.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,34 @@ describe("selfhost-update.sh", () => {
expect(readCallLog(callLog)).toBe("");
});

it("refuses a detached HEAD with a distinct message instead of the generic branch mismatch", () => {
const { seedDir, checkoutDir, callLog } = createSandbox();
advanceOrigin(seedDir, "advance for detached-head");
git(["checkout", "-q", "--detach", "HEAD"], checkoutDir);

const result = run(checkoutDir, callLog);

expect(result.status).not.toBe(0);
expect(result.stderr).toContain("detached HEAD state");
expect(result.stderr).not.toContain("currently on 'HEAD'");
expect(readCallLog(callLog)).toBe("");
});

it("gives a distinct error when SELFHOST_UPDATE_BRANCH names a branch the remote doesn't have", () => {
const { checkoutDir, callLog } = createSandbox();
// The local checkout must actually be on the named branch for the branch-mismatch check to
// pass, so this exercises the *next* guard: the branch exists locally but has no upstream
// counterpart to fast-forward from.
git(["checkout", "-q", "-b", "no-such-branch-upstream"], checkoutDir);

const result = run(checkoutDir, callLog, { SELFHOST_UPDATE_BRANCH: "no-such-branch-upstream" });

expect(result.status).not.toBe(0);
expect(result.stderr).toContain("origin/no-such-branch-upstream does not exist");
expect(result.stderr).not.toContain("could not be fast-forwarded");
expect(readCallLog(callLog)).toBe("");
});

it("accepts a non-default branch when SELFHOST_UPDATE_BRANCH names it explicitly", () => {
const { seedDir, checkoutDir, callLog } = createSandbox();
git(["checkout", "-q", "-b", "release"], seedDir);
Expand Down
Loading