mirror of https://github.com/garrytan/gstack.git
fix(slug): terminate the marker walk-up on dirname's fixed point — hung every bin on Windows
Under git-bash on Windows a mixed-form path walks C:/Users -> C: -> . -> . forever: dirname's fixed point there is never "/", so the walk-up loop spun and every bin that evals gstack-slug (learnings-log first among them) hung until spawn timeout. Caught by windows-free-tests CI on the wave PR. Break on the fixed point itself with a depth cap for exotic forms; regression tests drive the extracted function with hostile path shapes under a hard timeout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4fc3731a50
commit
daeec2c017
|
|
@ -68,7 +68,12 @@ _outermost_project_root() {
|
|||
local dir="$1"
|
||||
local outermost_strong=""
|
||||
local outermost_weak=""
|
||||
while [[ -n "$dir" && "$dir" != "/" ]]; do
|
||||
local parent="" depth=0
|
||||
# Terminate on dirname's FIXED POINT, not on a literal "/": under git-bash
|
||||
# on Windows a mixed-form path walks C:/Users -> C: -> . -> . forever, which
|
||||
# hung every bin that evals gstack-slug (caught by windows-free-tests CI).
|
||||
# The depth cap is belt-and-braces for exotic path forms (UNC, //server).
|
||||
while [[ -n "$dir" && "$dir" != "/" && $depth -lt 64 ]]; do
|
||||
if [[ -e "$dir/.git" \
|
||||
|| -f "$dir/.project.yaml" \
|
||||
|| -f "$dir/package.json" \
|
||||
|
|
@ -84,7 +89,10 @@ _outermost_project_root() {
|
|||
|| -f "$dir/LICENSE.md" ]]; then
|
||||
outermost_weak="$dir"
|
||||
fi
|
||||
dir=$(dirname "$dir")
|
||||
parent=$(dirname "$dir")
|
||||
[[ "$parent" == "$dir" ]] && break # dirname fixed point (C:/, ., //srv)
|
||||
dir="$parent"
|
||||
depth=$((depth + 1))
|
||||
done
|
||||
# Strong markers win over weak; either wins over nothing.
|
||||
if [[ -n "$outermost_strong" ]]; then
|
||||
|
|
|
|||
|
|
@ -289,3 +289,23 @@ describe('gstack-slug — outermost project-root resolution', () => {
|
|||
expect(slug).toBe('custom-override');
|
||||
});
|
||||
});
|
||||
|
||||
describe('_outermost_project_root termination (windows-free-tests regression)', () => {
|
||||
// Under git-bash on Windows a mixed-form path walks C:/Users -> C: -> . -> .
|
||||
// forever: dirname's fixed point there is never "/". The loop must break on
|
||||
// the fixed point itself. Extract the function and drive it with hostile
|
||||
// path forms under a hard timeout — a hang fails the spawn, not the suite.
|
||||
const script = fs.readFileSync(path.join(ROOT, 'bin', 'gstack-slug'), 'utf-8');
|
||||
const fnMatch = script.match(/_outermost_project_root\(\) \{[\s\S]*?\n\}/);
|
||||
|
||||
test.each(['C:/Users/nobody/project', '.', '//server/share/dir'])(
|
||||
'terminates on hostile path form: %s',
|
||||
(hostile) => {
|
||||
expect(fnMatch).not.toBeNull();
|
||||
const r = Bun.spawnSync(['bash', '-c', `${fnMatch![0]}\n_outermost_project_root "$1"; echo TERMINATED`, '_', hostile], {
|
||||
timeout: 5000,
|
||||
});
|
||||
expect(r.stdout.toString()).toContain('TERMINATED');
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue