Skip to content

fix: layer publish support code is http url#143

Closed
mozhou52 wants to merge 1 commit intomasterfrom
support-http
Closed

fix: layer publish support code is http url#143
mozhou52 wants to merge 1 commit intomasterfrom
support-http

Conversation

@mozhou52
Copy link
Collaborator

@mozhou52 mozhou52 commented Mar 4, 2026

Summary by CodeRabbit

  • Tests

    • Added mocks for an external downloads module in test setup.
  • Bug Fixes

    • Improved cleanup of temporary download and zip artifacts during deployments and layer publishing.
    • Guarded optional NAS configuration handling to avoid runtime errors.
  • Refactor

    • Centralized URL download handling into a shared utility and propagated temporary-download handling through publish flows.
    • Renamed a helper parameter for consistency.
  • Chores

    • Updated a dev dependency version.

@mozhou52 mozhou52 requested a review from rsonghuster March 4, 2026 05:19
@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Extracted URL-download logic into a shared _downloadFromUrl in src/utils; deploy and layer flows now call this util and clean up downloaded temp files. Tests add Jest mocks for @serverless-devs/downloads. checkFcDir parameter renamed to inputPath.

Changes

Cohort / File(s) Summary
Test Module Mocks
__tests__/ut/commands/invoke_test.ts, __tests__/ut/utils/utils_test.ts
Added Jest mocks for @serverless-devs/downloads to avoid import errors in tests.
Shared Utilities
src/utils/index.ts
Added exported async _downloadFromUrl(url: string): Promise<string> (creates temp dir, downloads file, returns local path); renamed checkFcDir(path...)checkFcDir(inputPath...) and tightened path validation.
Deploy Implementation
src/subCommands/deploy/impl/function.ts
Replaced local download method with imported _downloadFromUrl, removed @serverless-devs/downloads and os imports, and added cleanup (fs.rmSync) of downloaded temp file on skip/post-upload paths.
Layer Subcommand
src/subCommands/layer/index.ts
Detects HTTP(S) codeUri, downloads via _downloadFromUrl, threads downloadedTempFile into safe_publish_layer (signature updated), and removes temp artifacts when skipping or after upload.
Model Guard
src/subCommands/model/model.ts
Guarded NAS path derivation to avoid runtime errors when nasMountPoints is falsy.
Dependencies & Types
package.json
Bumped dev dependency @types/lodash (4.17.23 → 4.17.24).

Sequence Diagram(s)

sequenceDiagram
  participant CLI as "CLI/Command"
  participant Layer as "Layer.publish()"
  participant Deploy as "Deploy/function flow"
  participant Utils as "utils._downloadFromUrl"
  participant FS as "Filesystem (temp dirs)"

  CLI->>Layer: publish(codeUri)
  alt codeUri is http(s)
    Layer->>Utils: _downloadFromUrl(codeUri)
    Utils->>FS: create temp dir & download file
    Utils-->>Layer: local downloadedTempFile
    Layer->>Layer: safe_publish_layer(..., downloadedTempFile)
    Layer->>FS: remove temp zip & downloadedTempFile
  else non-URL
    Layer->>Layer: proceed with local path
  end

  CLI->>Deploy: deploy/function(codeUri)
  alt codeUri is http(s)
    Deploy->>Utils: _downloadFromUrl(codeUri)
    Utils->>FS: create temp dir & download file
    Utils-->>Deploy: local downloadedTempFile
    Deploy->>FS: remove downloadedTempFile on skip/after upload
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • rsonghuster

Poem

🐰

I hopped to fetch a distant file,
I tucked it safe within a pile.
I cleaned my tracks, I zipped the trail,
Shared tunnels make the build set sail.
A tidy hop, a lighter mile.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main change: adding HTTP URL support to the layer publish functionality, which aligns with the primary modifications in src/subCommands/layer/index.ts and related utility functions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch support-http

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/subCommands/layer/index.ts (1)

34-100: ⚠️ Potential issue | 🟠 Major

Use try/finally in safe_publish_layer for guaranteed temp cleanup.

downloadedTempDir and generated zip cleanup run only on success/skip branches. If an exception is thrown during checksum, OSS upload, or create-layer call, temp artifacts are left behind.

Suggested fix
   static async safe_publish_layer(
@@
-    let zipPath = toZipDir;
-    let generateZipFilePath = '';
-    if (!toZipDir.endsWith('.zip')) {
+    let zipPath = toZipDir;
+    let generateZipFilePath = '';
+    try {
+      if (!toZipDir.endsWith('.zip')) {
         const zipConfig = {
@@
-      generateZipFilePath = (await zip(zipConfig)).outputFile;
-      zipPath = generateZipFilePath;
-    }
-    const latestLayer = await fcSdk.getLayerLatestVersion(layerName);
-    if (latestLayer) {
+        generateZipFilePath = (await zip(zipConfig)).outputFile;
+        zipPath = generateZipFilePath;
+      }
+      const latestLayer = await fcSdk.getLayerLatestVersion(layerName);
+      if (latestLayer) {
@@
-        return latestLayer;
+          return latestLayer;
+        }
       }
-    }
-    logger.debug(`Zip file: ${zipPath}`);
-    getFileSize(zipPath);
-    const ossConfig = await fcSdk.uploadCodeToTmpOss(zipPath);
-    logger.debug('ossConfig: ', ossConfig);
+      logger.debug(`Zip file: ${zipPath}`);
+      getFileSize(zipPath);
+      const ossConfig = await fcSdk.uploadCodeToTmpOss(zipPath);
+      logger.debug('ossConfig: ', ossConfig);
@@
-    console.log(JSON.stringify(result));
-    return result;
+      console.log(JSON.stringify(result));
+      return result;
+    } finally {
+      if (generateZipFilePath) {
+        try {
+          fs.rmSync(generateZipFilePath);
+        } catch (ex) {
+          logger.debug(`Unable to remove zip file: ${zipPath}`);
+        }
+      }
+      if (downloadedTempDir) {
+        try {
+          logger.debug(`Removing temp download dir: ${downloadedTempDir}`);
+          fs.rmSync(downloadedTempDir, { recursive: true, force: true });
+        } catch (ex) {
+          logger.debug(`Unable to remove temp download dir: ${downloadedTempDir}`);
+        }
+      }
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/subCommands/layer/index.ts` around lines 34 - 100, The temp artifacts
(downloadedTempDir and generateZipFilePath) are only deleted on the normal/skip
paths, leaving leftovers on exceptions; update the function (safe_publish_layer
/ the block using zipPath, generateZipFilePath, downloadedTempDir,
calculateCRC64, uploadCodeToTmpOss, createLayerVersion) to wrap the main work in
a try block and move all cleanup logic into a finally block that always attempts
to fs.rmSync(generateZipFilePath) and fs.rmSync(downloadedTempDir, { recursive:
true, force: true }), logging any removal errors with logger.debug; keep
existing conditional checks so you only remove files that were actually created
and preserve existing debug messages in the finally.
src/utils/index.ts (1)

301-304: ⚠️ Potential issue | 🟠 Major

Prefix validation in checkFcDir is over-broad and rejects valid paths.

Using startsWith(prefix) blocks unrelated paths like /various/... when checking /var. Match exact prefix directory boundaries instead.

Suggested fix
   for (const prefix of forbiddenPrefixes) {
-    if (normalizedPath.startsWith(prefix)) {
+    if (normalizedPath === prefix || normalizedPath.startsWith(`${prefix}/`)) {
       throw new Error(`Invalid ${paramName}, ${prefix} and its subdirectories are not allowed`);
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/index.ts` around lines 301 - 304, The check in checkFcDir currently
uses normalizedPath.startsWith(prefix) which falsely rejects paths that only
share a leading segment (e.g., "/various" vs "/var"); update the logic to only
reject when the normalizedPath equals the forbidden prefix or lies inside it by
checking boundary-aware matches: i.e., treat a forbidden prefix as matched only
if normalizedPath === prefix or normalizedPath.startsWith(prefix + path.sep)
(use the appropriate path.sep/posix separator for your environment) for each
entry in forbiddenPrefixes; update any references to normalizedPath and
forbiddenPrefixes in checkFcDir accordingly.
src/subCommands/deploy/impl/function.ts (1)

286-360: ⚠️ Potential issue | 🟠 Major

Make temp file cleanup exception-safe with finally.

downloadedTempDir cleanup currently runs only on normal branches. If an exception occurs after download (for example at Line 341), the temp dir can leak.

Suggested fix
   private async _uploadCode(): Promise<boolean> {
@@
-    let generateZipFilePath = '';
-    if (needZip) {
+    let generateZipFilePath = '';
+    try {
+      if (needZip) {
         setNodeModulesBinPermissions(zipPath);
@@
-      zipPath = generateZipFilePath;
-    }
+        zipPath = generateZipFilePath;
+      }
@@
-    const ossConfig = await this.fcSdk.uploadCodeToTmpOss(zipPath);
-    logger.debug('ossConfig: ', ossConfig);
-    _.set(this.local, 'code', ossConfig);
-
-    if (generateZipFilePath) {
-      try {
-        fs.rmSync(generateZipFilePath);
-      } catch (ex) {
-        logger.debug(`Unable to remove zip file: ${zipPath}`);
-      }
-    }
-
-    if (downloadedTempDir) {
-      try {
-        logger.debug(`Removing temp download dir: ${downloadedTempDir}`);
-        fs.rmSync(downloadedTempDir, { recursive: true, force: true });
-      } catch (ex) {
-        logger.debug(`Unable to remove temp download dir: ${downloadedTempDir}`);
-      }
-    }
-
-    return true;
+      const ossConfig = await this.fcSdk.uploadCodeToTmpOss(zipPath);
+      logger.debug('ossConfig: ', ossConfig);
+      _.set(this.local, 'code', ossConfig);
+      return true;
+    } finally {
+      if (generateZipFilePath) {
+        try {
+          fs.rmSync(generateZipFilePath);
+        } catch (ex) {
+          logger.debug(`Unable to remove zip file: ${zipPath}`);
+        }
+      }
+      if (downloadedTempDir) {
+        try {
+          logger.debug(`Removing temp download dir: ${downloadedTempDir}`);
+          fs.rmSync(downloadedTempDir, { recursive: true, force: true });
+        } catch (ex) {
+          logger.debug(`Unable to remove temp download dir: ${downloadedTempDir}`);
+        }
+      }
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/subCommands/deploy/impl/function.ts` around lines 286 - 360, The temp
download and generated zip cleanup can leak if an exception occurs; move the
deletion of downloadedTempDir and generateZipFilePath into a finally block so
they always run after processing (wrap the code between obtaining zipPath and
the return/oss upload in try { ... } finally { try {
fs.rmSync(generateZipFilePath) } catch{}; try { fs.rmSync(downloadedTempDir, {
recursive: true, force: true }) } catch{} }). Update the block that calls
calculateCRC64, this.fcSdk.uploadCodeToTmpOss, and _.set(this.local, 'code',
ossConfig) to be inside the try, and ensure you only attempt to remove
generateZipFilePath when it was created and not remove zipPath if it pointed to
a local codeUri; reference variables downloadedTempDir, generateZipFilePath,
zipPath, calculateCRC64, and uploadCodeToTmpOss when making the changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/utils/index.ts`:
- Around line 322-323: The current fixed temp directory (const tempDir =
path.join(os.tmpdir(), 'fc_code_download')) in _downloadFromUrl risks
collisions; replace it with a unique per-invocation temp directory (e.g., create
via fs.mkdtemp or fs.promises.mkdtemp using os.tmpdir() + 'fc_code_download-')
and use that new directory as the working downloadPath so each call gets its own
folder; ensure cleanup only removes the specific per-call directory (not a
shared parent) and update any logic that assumes a single shared tempDir.

---

Outside diff comments:
In `@src/subCommands/deploy/impl/function.ts`:
- Around line 286-360: The temp download and generated zip cleanup can leak if
an exception occurs; move the deletion of downloadedTempDir and
generateZipFilePath into a finally block so they always run after processing
(wrap the code between obtaining zipPath and the return/oss upload in try { ...
} finally { try { fs.rmSync(generateZipFilePath) } catch{}; try {
fs.rmSync(downloadedTempDir, { recursive: true, force: true }) } catch{} }).
Update the block that calls calculateCRC64, this.fcSdk.uploadCodeToTmpOss, and
_.set(this.local, 'code', ossConfig) to be inside the try, and ensure you only
attempt to remove generateZipFilePath when it was created and not remove zipPath
if it pointed to a local codeUri; reference variables downloadedTempDir,
generateZipFilePath, zipPath, calculateCRC64, and uploadCodeToTmpOss when making
the changes.

In `@src/subCommands/layer/index.ts`:
- Around line 34-100: The temp artifacts (downloadedTempDir and
generateZipFilePath) are only deleted on the normal/skip paths, leaving
leftovers on exceptions; update the function (safe_publish_layer / the block
using zipPath, generateZipFilePath, downloadedTempDir, calculateCRC64,
uploadCodeToTmpOss, createLayerVersion) to wrap the main work in a try block and
move all cleanup logic into a finally block that always attempts to
fs.rmSync(generateZipFilePath) and fs.rmSync(downloadedTempDir, { recursive:
true, force: true }), logging any removal errors with logger.debug; keep
existing conditional checks so you only remove files that were actually created
and preserve existing debug messages in the finally.

In `@src/utils/index.ts`:
- Around line 301-304: The check in checkFcDir currently uses
normalizedPath.startsWith(prefix) which falsely rejects paths that only share a
leading segment (e.g., "/various" vs "/var"); update the logic to only reject
when the normalizedPath equals the forbidden prefix or lies inside it by
checking boundary-aware matches: i.e., treat a forbidden prefix as matched only
if normalizedPath === prefix or normalizedPath.startsWith(prefix + path.sep)
(use the appropriate path.sep/posix separator for your environment) for each
entry in forbiddenPrefixes; update any references to normalizedPath and
forbiddenPrefixes in checkFcDir accordingly.

ℹ️ Review info
Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 72143392-0abe-4488-9c7f-04c710e13482

📥 Commits

Reviewing files that changed from the base of the PR and between 35a061a and c0b858b.

📒 Files selected for processing (5)
  • __tests__/ut/commands/invoke_test.ts
  • __tests__/ut/utils/utils_test.ts
  • src/subCommands/deploy/impl/function.ts
  • src/subCommands/layer/index.ts
  • src/utils/index.ts

logger.debug(
yellow(`skip uploadCode because code is no changed, codeChecksum=${crc64Value}`),
);
if (downloadedTempDir) {
Copy link
Contributor

Choose a reason for hiding this comment

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

为什么是删除那个文件夹, 不是那个 zip 文件

Copy link

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/subCommands/layer/index.ts (1)

52-97: ⚠️ Potential issue | 🟠 Major

Cleanup is not guaranteed on failures and some early returns.

downloadedTempDir/generated zip cleanup only runs in selected branches. If upload/create throws, temp artifacts are left behind. Also, checksum-skip return does not clean generateZipFilePath.

Suggested refactor (single cleanup point)
   static async safe_publish_layer(...) {
     let zipPath = toZipDir;
     let generateZipFilePath = '';
-    if (!toZipDir.endsWith('.zip')) {
+    try {
+      if (!toZipDir.endsWith('.zip')) {
         ...
-    }
-    const latestLayer = await fcSdk.getLayerLatestVersion(layerName);
-    if (latestLayer) {
+      }
+      const latestLayer = await fcSdk.getLayerLatestVersion(layerName);
+      if (latestLayer) {
         ...
-      if (latestLayer.codeChecksum === crc64Value) {
+        if (latestLayer.codeChecksum === crc64Value) {
           ...
-        if (downloadedTempDir) {
-          ...
-        }
-        return latestLayer;
+          return latestLayer;
+        }
       }
-    }
-    ...
-    const result = await fcSdk.createLayerVersion(...);
-    ...
-    if (generateZipFilePath) { ... }
-    if (downloadedTempDir) { ... }
-    console.log(JSON.stringify(result));
-    return result;
+      ...
+      const result = await fcSdk.createLayerVersion(...);
+      console.log(JSON.stringify(result));
+      return result;
+    } finally {
+      if (generateZipFilePath) {
+        try { fs.rmSync(generateZipFilePath); } catch (ex) { logger.debug(`Unable to remove zip file: ${zipPath}`); }
+      }
+      if (downloadedTempDir) {
+        try { fs.rmSync(downloadedTempDir, { recursive: true, force: true }); } catch (ex) { logger.debug(`Unable to remove temp download dir: ${downloadedTempDir}`); }
+      }
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/subCommands/layer/index.ts` around lines 52 - 97, The code leaves temp
artifacts when errors or early-returns occur (e.g., the checksum-skip branch and
failures in fcSdk.uploadCodeToTmpOss / fcSdk.createLayerVersion); refactor the
flow in src/subCommands/layer/index.ts so all temporary cleanup is done in one
place: wrap the main work (checksum check, getFileSize,
fcSdk.uploadCodeToTmpOss, fcSdk.createLayerVersion) in a try block and perform
removal of generateZipFilePath and downloadedTempDir in a single finally block
(or extract a cleanup function and call it from finally), ensuring the checksum
early-return does not bypass cleanup by returning after the finally (e.g., set
latestLayer/skip flag and return after finally), and keep the existing guarded
fs.rmSync(... { recursive:true, force:true }) calls with try/catch and
logger.debug for any removal errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/utils/index.ts`:
- Line 319: The current logger.info call logs the full download URL
(logger.info(`Downloading code from URL: ${url}`)), which can leak sensitive
query parameters; update the logging to redact query strings by parsing the url
variable with the URL class and logging only the origin + pathname (e.g., new
URL(url).origin + new URL(url).pathname) or otherwise strip/redact url.search
before passing to logger.info so only non-sensitive parts are emitted.

---

Outside diff comments:
In `@src/subCommands/layer/index.ts`:
- Around line 52-97: The code leaves temp artifacts when errors or early-returns
occur (e.g., the checksum-skip branch and failures in fcSdk.uploadCodeToTmpOss /
fcSdk.createLayerVersion); refactor the flow in src/subCommands/layer/index.ts
so all temporary cleanup is done in one place: wrap the main work (checksum
check, getFileSize, fcSdk.uploadCodeToTmpOss, fcSdk.createLayerVersion) in a try
block and perform removal of generateZipFilePath and downloadedTempDir in a
single finally block (or extract a cleanup function and call it from finally),
ensuring the checksum early-return does not bypass cleanup by returning after
the finally (e.g., set latestLayer/skip flag and return after finally), and keep
the existing guarded fs.rmSync(... { recursive:true, force:true }) calls with
try/catch and logger.debug for any removal errors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f23cd7fb-32fc-47ab-bdae-69d0d1d0d303

📥 Commits

Reviewing files that changed from the base of the PR and between c0b858b and 9063c57.

📒 Files selected for processing (5)
  • __tests__/ut/commands/invoke_test.ts
  • __tests__/ut/utils/utils_test.ts
  • src/subCommands/deploy/impl/function.ts
  • src/subCommands/layer/index.ts
  • src/utils/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/ut/utils/utils_test.ts
  • tests/ut/commands/invoke_test.ts
  • src/subCommands/deploy/impl/function.ts

Copy link

@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: 1

♻️ Duplicate comments (1)
src/utils/index.ts (1)

320-321: ⚠️ Potential issue | 🟠 Major

Use a per-invocation temp directory to avoid collisions.

A fixed fc_code_download folder can race across parallel runs and cross-cleanup each other.

Suggested fix
-  const tempDir = path.join(os.tmpdir(), 'fc_code_download');
+  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'fc_code_download-'));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/index.ts` around lines 320 - 321, The temp directory is currently a
fixed path (tempDir = path.join(os.tmpdir(), 'fc_code_download')) which can
collide across concurrent runs; change it to create a per-invocation temp dir
(e.g. use fs.mkdtemp or fs.mkdtempSync with a prefix like path.join(os.tmpdir(),
'fc_code_download-')) and assign that result to tempDir/downloadPath so each
invocation gets a unique folder; update any code that references tempDir or
downloadPath accordingly and keep existing cleanup logic to remove the created
temp dir when finished.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/utils/index.ts`:
- Around line 326-333: The download filename sent to downloads() is missing the
extension (parsedPathName uses path.parse(...).name) causing downloadPath to
mismatch the actual file; fix by using the full basename (use
path.parse(...).base or path.basename(urlPath)) when constructing the name
passed to downloads() and ensure downloadPath is built using that same full
filename variable (update parsedPathName/filename usage so downloads(url, {
filename: fullFilename }) and downloadPath = path.join(tempDir, fullFilename)
are consistent).

---

Duplicate comments:
In `@src/utils/index.ts`:
- Around line 320-321: The temp directory is currently a fixed path (tempDir =
path.join(os.tmpdir(), 'fc_code_download')) which can collide across concurrent
runs; change it to create a per-invocation temp dir (e.g. use fs.mkdtemp or
fs.mkdtempSync with a prefix like path.join(os.tmpdir(), 'fc_code_download-'))
and assign that result to tempDir/downloadPath so each invocation gets a unique
folder; update any code that references tempDir or downloadPath accordingly and
keep existing cleanup logic to remove the created temp dir when finished.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a7d0287-b6d2-4a4f-a504-86adb154e7a8

📥 Commits

Reviewing files that changed from the base of the PR and between 9063c57 and 7bfc4a0.

📒 Files selected for processing (5)
  • __tests__/ut/commands/invoke_test.ts
  • __tests__/ut/utils/utils_test.ts
  • src/subCommands/deploy/impl/function.ts
  • src/subCommands/layer/index.ts
  • src/utils/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/ut/utils/utils_test.ts

Copy link

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/subCommands/deploy/impl/function.ts (1)

323-359: ⚠️ Potential issue | 🟠 Major

Move temp-artifact cleanup into a finally.

This still leaks files in two cases: the early return false path never removes generateZipFilePath, and any exception after download/zip creation skips both cleanup blocks entirely. Repeated failed or no-op deploys will keep accumulating temp artifacts.

Suggested fix
-    const crc64Value = await calculateCRC64(zipPath);
-    logger.debug(`code zip crc64=${crc64Value}; codeChecksum=${this.codeChecksum}`);
-    if (this.codeChecksum) {
-      if (this.codeChecksum === crc64Value) {
-        logger.debug(
-          yellow(`skip uploadCode because code is no changed, codeChecksum=${crc64Value}`),
-        );
-        if (downloadedTempFile) {
-          try {
-            logger.debug(`Removing temp download dir: ${downloadedTempFile}`);
-            fs.rmSync(downloadedTempFile, { recursive: true, force: true });
-          } catch (ex) {
-            logger.debug(`Unable to remove temp download dir: ${downloadedTempFile}`);
-          }
-        }
-        return false;
-      } else {
-        logger.debug(`\x1b[33mcodeChecksum from ${this.codeChecksum} to ${crc64Value}\x1b[0m`);
-      }
-    }
-    const ossConfig = await this.fcSdk.uploadCodeToTmpOss(zipPath);
-    logger.debug('ossConfig: ', ossConfig);
-    _.set(this.local, 'code', ossConfig);
-
-    if (generateZipFilePath) {
-      try {
-        fs.rmSync(generateZipFilePath);
-      } catch (ex) {
-        logger.debug(`Unable to remove zip file: ${zipPath}`);
-      }
-    }
-
-    if (downloadedTempFile) {
-      try {
-        logger.debug(`Removing temp download dir: ${downloadedTempFile}`);
-        fs.rmSync(downloadedTempFile, { recursive: true, force: true });
-      } catch (ex) {
-        logger.debug(`Unable to remove temp download dir: ${downloadedTempFile}`);
-      }
-    }
-
-    return true;
+    try {
+      const crc64Value = await calculateCRC64(zipPath);
+      logger.debug(`code zip crc64=${crc64Value}; codeChecksum=${this.codeChecksum}`);
+      if (this.codeChecksum) {
+        if (this.codeChecksum === crc64Value) {
+          logger.debug(
+            yellow(`skip uploadCode because code is no changed, codeChecksum=${crc64Value}`),
+          );
+          return false;
+        }
+        logger.debug(`\x1b[33mcodeChecksum from ${this.codeChecksum} to ${crc64Value}\x1b[0m`);
+      }
+
+      const ossConfig = await this.fcSdk.uploadCodeToTmpOss(zipPath);
+      logger.debug('ossConfig: ', ossConfig);
+      _.set(this.local, 'code', ossConfig);
+      return true;
+    } finally {
+      for (const tempPath of [generateZipFilePath, downloadedTempFile]) {
+        if (!tempPath) continue;
+        try {
+          fs.rmSync(tempPath, { recursive: true, force: true });
+        } catch (ex) {
+          logger.debug(`Unable to remove temp artifact: ${tempPath}`);
+        }
+      }
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/subCommands/deploy/impl/function.ts` around lines 323 - 359, The
temp-artifact cleanup must be moved into a finally block so generateZipFilePath
and downloadedTempFile are always removed (including the early return when
this.codeChecksum === crc64Value and on any exception). Refactor the section
around the crc64 comparison and the call to
this.fcSdk.uploadCodeToTmpOss(zipPath): perform checksum check but defer all
removals of generateZipFilePath and downloadedTempFile to a single finally block
that runs before returning (so the early return returns after cleanup), and
eliminate the duplicate cleanup blocks; keep the _.set(this.local, 'code',
ossConfig) assignment inside the try so upload errors propagate but cleanup
still runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/subCommands/model/model.ts`:
- Around line 120-122: The NAS branch dereferences nasMountPoints[0] without
consistently guarding its existence: ensure any access to nasMountPoints[0] is
protected by a check that nasMountPoints exists and has at least one entry
(e.g., nasMountPoints && nasMountPoints.length > 0 or nasMountPoints?.[0])
before using nasPath, filepath or other properties; update the conditional that
currently reads storage === 'nas' && nasMountPoints[0] ... to include the
nasMountPoints guard, and when using filepath later, either use optional
chaining (nasMountPoints?.[0]?.filepath) or assign const mount =
nasMountPoints[0] after the guard and reference mount.filepath to avoid crashes
when mount points are missing.

---

Outside diff comments:
In `@src/subCommands/deploy/impl/function.ts`:
- Around line 323-359: The temp-artifact cleanup must be moved into a finally
block so generateZipFilePath and downloadedTempFile are always removed
(including the early return when this.codeChecksum === crc64Value and on any
exception). Refactor the section around the crc64 comparison and the call to
this.fcSdk.uploadCodeToTmpOss(zipPath): perform checksum check but defer all
removals of generateZipFilePath and downloadedTempFile to a single finally block
that runs before returning (so the early return returns after cleanup), and
eliminate the duplicate cleanup blocks; keep the _.set(this.local, 'code',
ossConfig) assignment inside the try so upload errors propagate but cleanup
still runs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e908f691-2483-4fd4-abf3-165f75ba7058

📥 Commits

Reviewing files that changed from the base of the PR and between 7bfc4a0 and f18f52d.

📒 Files selected for processing (7)
  • __tests__/ut/commands/invoke_test.ts
  • __tests__/ut/utils/utils_test.ts
  • package.json
  • src/subCommands/deploy/impl/function.ts
  • src/subCommands/layer/index.ts
  • src/subCommands/model/model.ts
  • src/utils/index.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/subCommands/layer/index.ts
  • tests/ut/commands/invoke_test.ts
  • src/utils/index.ts
  • tests/ut/utils/utils_test.ts

@mozhou52 mozhou52 force-pushed the support-http branch 7 times, most recently from 3832ccb to f4d27a4 Compare March 6, 2026 07:10
Copy link

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/utils/index.ts (1)

267-309: ⚠️ Potential issue | 🟠 Major

Guard and normalize inputPath before validating it.

_deployAuto() passes params.mountDir from parseAutoConfig() straight into this helper, so inputPath.trim() can throw a raw TypeError when the value deserializes to a boolean/number. Even for strings, validating the unnormalized path lets /mnt/../var/log through and wrongly rejects sibling names like /various/app.

Suggested fix
 export function checkFcDir(inputPath: string, paramName = 'path'): string {
-  const normalizedPath = inputPath.trim();
+  if (typeof inputPath !== 'string') {
+    throw new Error(`${paramName} must be a string`);
+  }
+  const normalizedPath = path.posix.normalize(inputPath.trim());
@@
   for (const prefix of forbiddenPrefixes) {
-    if (normalizedPath.startsWith(prefix)) {
+    if (normalizedPath === prefix || normalizedPath.startsWith(`${prefix}/`)) {
       throw new Error(`Invalid ${paramName}, ${prefix} and its subdirectories are not allowed`);
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/index.ts` around lines 267 - 309, Guard and normalize the input
before validating in checkFcDir: validate that inputPath is not null/undefined
and coerce it to a string (e.g., String(inputPath)) then trim; use a POSIX path
normalization (path.posix.normalize) to resolve "."/".." and collapse duplicate
slashes while ensuring the normalized result still begins with "/" (prepend "/"
if normalization removed it for inputs like ""). After normalization apply
length checks and replace the current forbiddenPrefixes test so it only rejects
exact prefix matches or true subdirectories (use equality or startsWith(prefix +
"/") rather than startsWith(prefix) to avoid rejecting "/various"); update
references to forbiddenPrefixes and normalizedPath in the function accordingly.
♻️ Duplicate comments (2)
src/utils/index.ts (2)

325-333: ⚠️ Potential issue | 🔴 Critical

Return the file that downloads() actually writes.

Line 326 strips the extension before passing the name to downloads() at Line 332, while downloadPath still points to the full basename. For code.zip, this helper returns .../code.zip but writes .../code, so the HTTP deploy/layer flow will look up a file that does not exist.

Suggested fix
-    const parsedPathName = path.parse(urlPath).name;
     const filename = path.basename(urlPath) || `downloaded_code_${Date.now()}`;
     downloadPath = path.join(tempDir, filename);

     await downloads(url, {
       dest: tempDir,
-      filename: parsedPathName,
+      filename,
       extract: false,
     });
Find the official README/docs for `@serverless-devs/downloads` 0.0.7 and confirm whether the `filename` option sets the exact saved filename on disk, including its extension.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/index.ts` around lines 325 - 333, The helper currently strips the
extension when calling downloads() by using parsedPathName but sets downloadPath
using the full basename, causing a mismatch; update the call-site in this
function so the filename passed to downloads() matches the actual saved filename
(use filename (basename with extension) instead of parsedPathName) or, if
downloads() always strips/changes extensions per its docs, compute downloadPath
from the name downloads() will write (e.g., derive from parsedPathName) after
confirming behavior from `@serverless-devs/downloads` 0.0.7 README; adjust the
downloads(...) invocation and the downloadPath assignment so both reference the
same final filename (identify symbols: urlPath, parsedPathName, filename,
downloadPath, and downloads()).

320-320: ⚠️ Potential issue | 🟠 Major

Don't reuse one global temp directory for every download.

fc_code_download is still shared across all invocations, and the catch path deletes that whole directory. Parallel deploy/layer publishes can clobber each other here.

Also applies to: 340-344

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

In `@src/utils/index.ts` at line 320, The temp directory is currently a single
global path stored in tempDir (path.join(os.tmpdir(), 'fc_code_download')) which
causes concurrent runs to collide and the catch-path cleanup to delete others'
data; change creation to a unique per-invocation directory (e.g., use fs.mkdtemp
or append a UUID/timestamp/pid to the base name) so each call gets its own
folder, ensure any cleanup logic in the catch block and functions that reference
tempDir only remove that specific per-invocation directory, and update all
references (the tempDir variable and the cleanup block around lines 340-344)
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/utils/index.ts`:
- Around line 267-309: Guard and normalize the input before validating in
checkFcDir: validate that inputPath is not null/undefined and coerce it to a
string (e.g., String(inputPath)) then trim; use a POSIX path normalization
(path.posix.normalize) to resolve "."/".." and collapse duplicate slashes while
ensuring the normalized result still begins with "/" (prepend "/" if
normalization removed it for inputs like ""). After normalization apply length
checks and replace the current forbiddenPrefixes test so it only rejects exact
prefix matches or true subdirectories (use equality or startsWith(prefix + "/")
rather than startsWith(prefix) to avoid rejecting "/various"); update references
to forbiddenPrefixes and normalizedPath in the function accordingly.

---

Duplicate comments:
In `@src/utils/index.ts`:
- Around line 325-333: The helper currently strips the extension when calling
downloads() by using parsedPathName but sets downloadPath using the full
basename, causing a mismatch; update the call-site in this function so the
filename passed to downloads() matches the actual saved filename (use filename
(basename with extension) instead of parsedPathName) or, if downloads() always
strips/changes extensions per its docs, compute downloadPath from the name
downloads() will write (e.g., derive from parsedPathName) after confirming
behavior from `@serverless-devs/downloads` 0.0.7 README; adjust the downloads(...)
invocation and the downloadPath assignment so both reference the same final
filename (identify symbols: urlPath, parsedPathName, filename, downloadPath, and
downloads()).
- Line 320: The temp directory is currently a single global path stored in
tempDir (path.join(os.tmpdir(), 'fc_code_download')) which causes concurrent
runs to collide and the catch-path cleanup to delete others' data; change
creation to a unique per-invocation directory (e.g., use fs.mkdtemp or append a
UUID/timestamp/pid to the base name) so each call gets its own folder, ensure
any cleanup logic in the catch block and functions that reference tempDir only
remove that specific per-invocation directory, and update all references (the
tempDir variable and the cleanup block around lines 340-344) accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c698e1b7-a0f2-40ac-945b-6bb66303ed20

📥 Commits

Reviewing files that changed from the base of the PR and between f18f52d and 3832ccb.

📒 Files selected for processing (7)
  • __tests__/ut/commands/invoke_test.ts
  • __tests__/ut/utils/utils_test.ts
  • package.json
  • src/subCommands/deploy/impl/function.ts
  • src/subCommands/layer/index.ts
  • src/subCommands/model/model.ts
  • src/utils/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/subCommands/model/model.ts
  • tests/ut/commands/invoke_test.ts
  • tests/ut/utils/utils_test.ts

@mozhou52 mozhou52 force-pushed the support-http branch 2 times, most recently from c3425b2 to a91c9e0 Compare March 6, 2026 07:18
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.

2 participants