Skip to content

cmdtools install leaves CMD_PATH in a partial state after a failed install, corrupting the result on retry #16

Description

@bbanas16

Summary

installCmdTools in packages/core/src/cmdTools/install.ts does not roll back or clean up the install target (CMD_PATH, default ~/command-line-tools) when the install fails partway through. Its catch/finally only clean up the temporary scratch directory, never the actual install target:

https://github.com/eclipse-oniro4openharmony/oniro-app-builder/blob/main/packages/core/src/cmdTools/install.ts#L114-L150

try {
  ...
  await extractZipWithProgress({ zipPath, dest: extractPath, ... });

  fs.mkdirSync(CMD_PATH, { recursive: true });
  const srcDir = findCmdToolsSourceDir(extractPath);

  for (const entry of fs.readdirSync(srcDir)) {
    const src = path.join(srcDir, entry);
    const dest = path.join(CMD_PATH, entry);
    if (fs.statSync(src).isDirectory()) {
      if (fs.existsSync(dest)) fs.rmSync(dest, { recursive: true, force: true });
      movePath(src, dest);       // <- can throw partway through a large dir (e.g. ENOSPC)
    } else {
      fs.copyFileSync(src, dest);
    }
  }
  ...
} catch (err) {
  throw toSpaceError(err, tmpRoot, what);
} finally {
  removeTempWorkDir(tmpDir, tmpRoot);   // only removes the scratch dir, never CMD_PATH
}

If an error (e.g. running out of disk space) interrupts the copy loop partway through, whatever had already been written into CMD_PATH — some entries fully copied, the one in progress possibly truncated/partial, later entries never reached at all — is left behind. movePath's cross-device fallback (fs.cpSync + fs.rmSync, in sdk/move.ts) is itself non-atomic per directory, so even a single top-level entry (e.g. sdk/) can be left half-copied.

On retry, the loop does replace CMD_PATH entries that share a name with something in the fresh archive (rmSync + movePath), so a full, uninterrupted second run can self-heal — but any inconsistency introduced by the first partial run is not guaranteed to be reconciled cleanly, and the resulting tree can end up different from what you'd get extracting the same archive fresh into an empty directory.

Steps to reproduce
Run oniro-app cmdtools install --from-zip with insufficient free disk space on the drive backing the install target / scratch directory. The install fails partway through with a disk-space error.
Free up space on the drive.
Run the same install command again. It completes without error.
Run oniro-app build on a project.
Expected behavior

A failed install should leave CMD_PATH clean (either untouched or fully rolled back), and a subsequent successful install should always produce a directory tree identical to a clean extraction of the archive, regardless of a prior failed attempt. oniro-app build should find all the directories it depends on under CMD_PATH.

Actual behavior

A subfolder under CMD_PATH is missing even though the second install reported success. oniro-app build fails with an error about a non-existent directory, because it can't find a path it expects to exist inside the command-line-tools install.

Suggested fix

On failure, also remove any partial content written to CMD_PATH during this run (or write into a temporary staging directory under the scratch dir and only move/rename the whole finished tree into CMD_PATH once the copy loop completes successfully, so a failure anywhere leaves CMD_PATH untouched instead of partially written).

Environment

OS: macOS
oniro-app-builder version: 0.10.0
Install method: --from-zip with a manually downloaded archive

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions