Skip to content

fix(step): resolve update archive root for flat Windows zips - #271

Merged
ZouR-Ma merged 1 commit into
stepfun-ai:mainfrom
li-xiu-qi:fix/windows-step-update-archive-root
Oct 20, 2026
Merged

ZouR-Ma merged 1 commit into
stepfun-ai:mainfrom
li-xiu-qi:fix/windows-step-update-archive-root

Conversation

@li-xiu-qi

@li-xiu-qi li-xiu-qi commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #269

Problem

step update fails on every Windows install, after the download and
checksum verification have already succeeded:

$ step update
Step update failed: release archive does not contain the Step binary

Reproduced on Windows 11 x64 with the native 0.1.4 install updating to
0.1.5. Fresh installs via install.ps1 are fine — only the self-update
path is broken. Windows users currently have to reinstall manually.

The two platforms ship different archive layouts:

  • Windows zips are flat: step.exe, docs/, node_modules/, … all
    sit at the archive root.
  • Unix tar.gz archives are wrapped: a single top-level step/
    directory holds everything (scripts/build-binaries.sh adds the
    wrapper directory for mise compatibility on Unix only).

findArchiveRoot in packages/coding-agent/src/step/local-update.ts
assumed the wrapped layout and picked the first subdirectory it saw:

const directory = entries.find((entry) => entry.isDirectory());
return directory ? path.join(extractDir, directory.name) : extractDir;

On Windows the first subdirectory is docs, so the subsequent recursive
search for step.exe was confined to docs/ and never found it. Any
archive containing at least one subdirectory trips this, regardless of
sort order.

Changes

Anchor the decision on where the binary sits instead of inferring the
layout from the first subdirectory:

export async function findArchiveRoot(extractDir: string, binaryName: string): Promise<string> {
	const entries = await readdir(extractDir, { withFileTypes: true });
	if (entries.some((entry) => entry.isFile() && entry.name === binaryName)) return extractDir;
	const directories = entries.filter((entry) => entry.isDirectory());
	if (directories.length === 1) return path.join(extractDir, directories[0].name);
	return extractDir;
}
  • Binary at the extract root → the root is the archive root (flat Windows zip).
  • Otherwise a single subdirectory is the wrapper (Unix archive).
  • Ambiguous layouts fall back to the extract root, where the recursive
    findFile still locates the binary.

Behavior and compatibility

  • findArchiveRoot gains a binaryName parameter; its single caller
    replaceInstallation is updated accordingly. No other call sites.
  • No changes to public interfaces, configuration keys, CLI arguments,
    persisted sessions, or provider responses.
  • install.sh (recursive find + dirname) and install.ps1
    ($ExtractDir/step.exe, flat by design) keep working as before — the
    fix aligns findArchiveRoot with the layouts both installers already
    handle, so no packaging change is needed.

Validation

Added packages/coding-agent/src/step/local-update.test.ts covering:

  • flat Windows zip (binary + docs/ + node_modules/ at the root)
  • wrapped Unix archive (single step/ directory)
  • wrapper directory with an extra loose file at the extract root
  • ambiguous layout (multiple directories, no root binary) falling back

Verified on Windows 11 x64:

  • npm run check — biome, static checks, tsgo: all pass
  • packages/coding-agent test suite — pass, including the 4 new tests
  • the real 0.1.5 windows-x64 release artifact now resolves the archive
    root correctly (binary found at the extract root instead of docs/)

Intentional skips: two pre-existing failures on Windows reproduce on a
clean main and are unrelated to this change —
scripts/release-bundle.test.mjs (install.sh PATH persistence) and
packages/config/test/migrations.test.ts (session relocation). Both look
like Windows shell/path-encoding issues; happy to file them separately
if useful.

Limitations

A full real-machine step update (0.1.4 → 0.1.5 end-to-end) has not
been run — the binary installed on the test machine is still the
unfixed 0.1.4. What is verified: archive-root resolution against the
real release artifact, the 4 new unit tests, npm run check, the
package test suite, and the PR's build-check-test CI run. No other
unverified behavior or pending CI at this time.

Windows release zips place step.exe and the resources at the archive
root, while Unix tar.gz archives wrap everything in a single top-level
directory. findArchiveRoot assumed the wrapped layout and descended
into the first subdirectory it saw, so on Windows the search for
step.exe was confined to docs/ and step update always failed after a
successful download and checksum verification.

Anchor the decision on where the binary sits instead: if the binary is
at the extract root, the root is the archive root (flat Windows zip);
otherwise a single subdirectory is the wrapper (Unix archive);
ambiguous layouts fall back to the extract root for the recursive
findFile. This matches how infra/release/install.sh already derives the
root and leaves install.ps1's flat assumption untouched.

Add unit tests covering both archive layouts plus the fallback cases.
@ZouR-Ma
ZouR-Ma merged commit 67a44cd into stepfun-ai:main Oct 20, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] step update fails on Windows with "release archive does not contain the Step binary"

2 participants