Skip to content

Update dependencies in our NPM package#2035

Merged
lionello merged 3 commits intomainfrom
lio/update-npm-deps
Apr 9, 2026
Merged

Update dependencies in our NPM package#2035
lionello merged 3 commits intomainfrom
lio/update-npm-deps

Conversation

@lionello
Copy link
Copy Markdown
Member

@lionello lionello commented Apr 8, 2026

Description

Linked Issues

Checklist

  • I have performed a self-review of my code
  • I have added appropriate tests
  • I have updated the Defang CLI docs and/or README to reflect my changes, if necessary

Summary by CodeRabbit

  • Chores

    • Removed axios and ts-node; migrated to native fetch and cleaned ESM test config
    • Added Node engine requirement (>=18.0.0) and bumped dev tool versions
    • Improved download/version handling, permission setting, and null/error handling for more robust runtime behavior
  • Tests

    • Updated tests to stub global fetch and adjusted assertions for version parsing and download failure/success paths

@lionello lionello requested a review from jordanstephens as a code owner April 8, 2026 21:31
@lionello lionello added the security Security related issue label Apr 8, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

📝 Walkthrough

Walkthrough

Replaced axios with native fetch for GitHub API requests and binary downloads, updated related return types to allow missing versions, removed ts-node configuration and axios from package.json, bumped dev dependencies, and updated tests to stub fetch instead of axios.

Changes

Cohort / File(s) Summary
Configuration & Dependencies
pkgs/npm/package.json
Removed ts-node ESM and custom mocha config, added engines.node: ">=18.0.0", removed axios from dependencies, and bumped dev dependency versions (@types/node, @types/sinon, chai, mocha, sinon, typescript).
HTTP Client & CLI Logic
pkgs/npm/src/clilib.ts
Replaced axios calls with fetch (checked response.ok, use response.json()/response.arrayBuffer()), changed getLatestVersion() to return Promise<string | undefined>, made extractCLIVersions() fields string | null, switched execexecFile, used fs.promises.rm(..., { force: true }) for cleanup, set executable mode with octal 0o755, and adjusted __dirname via path.dirname(__filename).
Tests
pkgs/npm/test/clilib.spec.ts
Replaced Axios stubs with global fetch stubs for version and download tests, updated mocks to return { ok, json() } and { ok, arrayBuffer() }, adjusted assertions to expect undefined for malformed/empty tags and to verify fs.promises.rm on failures.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as Local CLI Code
  participant GitHub as GitHub API
  participant FS as Filesystem
  participant Shell as Child Process

  CLI->>GitHub: GET /repos/.../releases/latest (fetch)
  GitHub-->>CLI: 200 { tag_name } / 4xx/5xx
  alt tag_name valid
    CLI->>CLI: parse semver (may strip leading "v")
    CLI->>Shell: execFile("version") to get local version
    Shell-->>CLI: local version stdout
  else tag_name missing/invalid
    CLI-->>CLI: latestVersion = undefined
  end

  CLI->>GitHub: GET archive_url (fetch)
  GitHub-->>CLI: 200 arrayBuffer (binary)
  CLI->>FS: writeFile(temp)
  CLI->>FS: rm(temp, { force: true }) on failure
  CLI->>FS: extract zip and chmod 0o755
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped from Axios to Fetch today,
Native calls now lead the way.
Buffers, nulls, and versions checked,
Tests updated, no steps wrecked.
🥕 A tidy bounce toward a cleaner play.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Update dependencies in our NPM package' is vague and generic, referring to a general activity without specifying the main technical changes (axios→fetch migration, ts-node→tsx replacement, or semver handling improvements). Consider a more specific title that highlights the primary technical change, such as 'Replace axios with fetch in NPM CLI utilities' or 'Migrate NPM package from axios to native fetch'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lio/update-npm-deps

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkgs/npm/src/clilib.ts`:
- Around line 29-30: The code currently returns data.tag_name?.replace("v",
"").trim() which can produce empty or non-semver strings; update the logic in
clilib.ts so you first read data.tag_name, trim whitespace, strip only a single
leading "v" (not every "v" in the string), then validate the resulting value
against SEMVER_REGEX and return it only if it matches; otherwise return
undefined — this prevents run() from treating malformed values as installable
and avoids downstream errors in getAppArchiveFilename().
- Around line 22-23: The package now uses the native global fetch (seen in
pkgs/npm/src/clilib.ts where fetch is called for URL_LATEST_RELEASE), which
requires Node >=18; update pkgs/npm/package.json to include an "engines" entry
like "node": ">=18.0.0" to prevent installation on older Node versions, or
alternatively implement a runtime fallback in clilib.ts that detects
global.fetch and loads a polyfill (e.g., node-fetch) when absent; modify either
package.json or the fetch call fallback logic so the CLI won't fail with "fetch
is not defined" on Node <18.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9db523ba-8506-4e54-bc37-89779369b7f0

📥 Commits

Reviewing files that changed from the base of the PR and between 6a020f0 and c80e71d.

⛔ Files ignored due to path filters (1)
  • pkgs/npm/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • pkgs/npm/package.json
  • pkgs/npm/src/clilib.ts
  • pkgs/npm/test/clilib.spec.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
pkgs/npm/test/clilib.spec.ts (2)

97-99: Variable name unlinkStub is misleading since it stubs fs.promises.rm.

Consider renaming to rmStub to match the actual method being stubbed.

♻️ Suggested rename
-  let unlinkStub: sinon.SinonStub;
+  let rmStub: sinon.SinonStub;
   ...
-    unlinkStub = sandbox
+    rmStub = sandbox
       .stub(fs.promises, "rm")
       .callsFake(() => Promise.resolve());

And update all references from unlinkStub to rmStub in the assertions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkgs/npm/test/clilib.spec.ts` around lines 97 - 99, The test uses a
misleading variable name unlinkStub while stubbing fs.promises.rm; rename the
variable to rmStub and update all references to it in this spec (the
sandbox.stub call that currently assigns unlinkStub and any assertions/uses
elsewhere) so the identifier matches the actual method being stubbed
(fs.promises.rm), ensuring assertions reference rmStub instead of unlinkStub.

116-126: Test calls downloadFile twice unnecessarily.

The test invokes downloadFile twice (lines 118 and 120), both returning null on failure. The assertion effectively checks null === null, which isn't meaningful. Consider simplifying:

♻️ Simplified test
 it("download fails path", async () => {
   fetchStub.returns(Promise.reject("failed"));
-  const targetFile = await clilib.downloadFile(url, downloadFileName);
-  await expect(
-    clilib.downloadFile(url, downloadFileName)
-  ).to.eventually.equal(targetFile);
+  await expect(clilib.downloadFile(url, downloadFileName)).to.eventually.be.null;

   sinon.assert.calledWith(fetchStub, url);
   sinon.assert.notCalled(writeStub);
   sinon.assert.calledWith(unlinkStub, downloadFileName);
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkgs/npm/test/clilib.spec.ts` around lines 116 - 126, The test calls
clilib.downloadFile(url, downloadFileName) twice which is redundant and makes
the assertion meaningless; replace the two calls with a single awaited call
(assigning to targetFile), then assert the returned value is null or equals the
expected failure value, and keep the existing verifications that fetchStub was
called with url, writeStub was not called, and unlinkStub was called with
downloadFileName; reference symbols: clilib.downloadFile, fetchStub, writeStub,
unlinkStub, url, downloadFileName.
pkgs/npm/src/clilib.ts (1)

44-70: Consider adding a timeout to prevent hanging on unresponsive servers.

The native fetch API has no default timeout, so if the download server becomes unresponsive, the CLI could hang indefinitely.

💡 Optional: Add fetch timeout using AbortSignal
 async function downloadFile(
   downloadUrl: string,
   downloadTargetFile: string
 ): Promise<string | null> {
   try {
-    const response = await fetch(downloadUrl);
+    const response = await fetch(downloadUrl, {
+      signal: AbortSignal.timeout(60_000), // 60 second timeout
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkgs/npm/src/clilib.ts` around lines 44 - 70, The downloadFile function can
hang because fetch has no timeout; add an AbortController and pass
controller.signal to fetch, start a setTimeout (e.g., 30s or configurable) that
calls controller.abort(), and clear the timer when the response is received or
on success before returning; ensure the catch still deletes downloadTargetFile
and returns null, and handle abort errors the same way (or log them distinctly).
Update references: downloadFile and the fetch call to use the
AbortController/signal and a timeout timer that is cleared on success.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkgs/npm/src/clilib.ts`:
- Around line 44-70: The downloadFile function can hang because fetch has no
timeout; add an AbortController and pass controller.signal to fetch, start a
setTimeout (e.g., 30s or configurable) that calls controller.abort(), and clear
the timer when the response is received or on success before returning; ensure
the catch still deletes downloadTargetFile and returns null, and handle abort
errors the same way (or log them distinctly). Update references: downloadFile
and the fetch call to use the AbortController/signal and a timeout timer that is
cleared on success.

In `@pkgs/npm/test/clilib.spec.ts`:
- Around line 97-99: The test uses a misleading variable name unlinkStub while
stubbing fs.promises.rm; rename the variable to rmStub and update all references
to it in this spec (the sandbox.stub call that currently assigns unlinkStub and
any assertions/uses elsewhere) so the identifier matches the actual method being
stubbed (fs.promises.rm), ensuring assertions reference rmStub instead of
unlinkStub.
- Around line 116-126: The test calls clilib.downloadFile(url, downloadFileName)
twice which is redundant and makes the assertion meaningless; replace the two
calls with a single awaited call (assigning to targetFile), then assert the
returned value is null or equals the expected failure value, and keep the
existing verifications that fetchStub was called with url, writeStub was not
called, and unlinkStub was called with downloadFileName; reference symbols:
clilib.downloadFile, fetchStub, writeStub, unlinkStub, url, downloadFileName.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 529f9a81-0722-47f5-a9ed-8b31d6fd8a0c

📥 Commits

Reviewing files that changed from the base of the PR and between c80e71d and acb99ea.

📒 Files selected for processing (3)
  • pkgs/npm/package.json
  • pkgs/npm/src/clilib.ts
  • pkgs/npm/test/clilib.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkgs/npm/package.json

@lionello lionello merged commit 2043c75 into main Apr 9, 2026
14 checks passed
@lionello lionello deleted the lio/update-npm-deps branch April 9, 2026 20:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

security Security related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants