Skip to content

Conversation

@myieye
Copy link
Collaborator

@myieye myieye commented Oct 21, 2025

This pull request adds robust support for downloading and syncing full FW Headless project folders from a Kubernetes pod for local debugging and integration. The changes include a new automation script and task for downloading project folders, updates to backend utilities to support syncing downloaded projects, and improvements to dependency management and ignore rules.

Deployment automation and backend integration:

Deployment scripts and tasks:

  • Added deployment/download-fw-headless-project.js, a Node.js script that automates finding the pod, tarring the project folder, serving it via a temporary Python HTTP server, port-forwarding, downloading, extracting locally, and cleaning up. This enables easy retrieval of entire project folders for local use.
  • Added a new Taskfile task download-fw-headless-project to invoke the script with simple arguments, streamlining the workflow for developers.
  • Updated .gitignore to exclude the _downloads/ directory, preventing accidental commits of downloaded project data.

Backend utility enhancements:

  • Added SyncDownloadedProject method to Utils.cs, allowing backend code to open and sync a downloaded project folder by path, including logic to locate the default downloads directory relative to the repo root.
  • Updated Program.cs to register a mock IServerHttpClientProvider for improved testability and added example usage of the new sync method.

And here's a bookmarklet that when used on a project page in Lexbox will put a cli command on your clipboard for downloading the current project:

javascript:(function(){try{const u=new URL(window.location.href),p=u.pathname.split('/').filter(Boolean),c=p[p.length-1],i=u.searchParams.get('id');if(!i||!c){alert('Could not find project code or ID in URL');return}let ctx='docker-desktop',ns='languagedepot';const o=u.origin;if(o.includes('staging.languagedepot.org')||o.includes('lexbox.dev.languagetechnology.org'))ctx='dallas-stage';else if(o.includes('lexbox.org')||o.includes('languagedepot.org'))ctx='aws-prod';if(o.includes('lexbox.dev.languagetechnology.org'))ns='languagedepot-dev';const t=task k8s:download-fw-headless-project id="${i}" code="${c}" context="${ctx}" namespace="${ns}";navigator.clipboard.writeText(t).then(()=>alert('Copied to clipboard:\n'+t)).catch(e=>alert('Failed to copy to clipboard:\n'+e))}catch(e){alert('Error: '+e.message)}})();

@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

The PR adds infrastructure for downloading and syncing fw-headless projects. Changes include adding Moq mock support to LcmDebugger, implementing a SyncDownloadedProject extension method for end-to-end sync operations, creating a Node.js script to orchestrate project downloads from Kubernetes pods via tar.gz, and adding corresponding deployment configurations.

Changes

Cohort / File(s) Summary
LcmDebugger Infrastructure
backend/FwLite/LcmDebugger/LcmDebugger.csproj, backend/FwLite/LcmDebugger/Program.cs, backend/FwLite/LcmDebugger/Utils.cs
Added Moq package dependency to enable mocking. Updated Program.cs to register a mocked IServerHttpClientProvider via Moq DI. Introduced SyncDownloadedProject extension method in Utils.cs to enable end-to-end Crdt-FWData project synchronization with dryRun control, including a private GetDefaultDownloadsPath helper and console logging.
Deployment Configuration
deployment/.gitignore, deployment/Taskfile.yml
Added _downloads/ directory to .gitignore. Added new Taskfile task download-fw-headless-project with aliases (download-project, dp) that downloads project folders from pods as tar.gz and extracts locally, invoking a Node.js script with project id, code, context, and namespace parameters.
Project Download Orchestration
deployment/download-fw-headless-project.js
New Node.js script that orchestrates fw-headless project downloads from Kubernetes pods. Executes kubectl commands to locate pods, creates tar archives inside pods, starts a Python HTTP server, port-forwards to retrieve the tar.gz, decompresses and extracts the archive locally with minimal tar header parsing, and performs cleanup of pod resources. Includes comprehensive error handling and progress logging.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

The changes span configuration (trivial), simple DI/mocking setup (straightforward), and a complex Node.js orchestration script with Kubernetes interaction, tar processing, HTTP server management, and async flow control requiring careful logic verification.

Possibly related PRs

Suggested labels

📦 Lexbox, 💻 FW Lite

Suggested reviewers

  • rmunn
  • hahn-kev

Poem

🐰 A rabbit hops through downloads deep,
Kubernetes pods where secrets sleep,
With tars and streams and servers spun,
Projects sync when tasks are run!
✨ Hop along, the work is done!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.23% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "Utils for downloading and syncing fw-headless projects locally" directly and accurately reflects the main objective of the changeset. The changes across deployment automation (new download script and Taskfile task), backend utilities (SyncDownloadedProject method), and supporting infrastructure (.gitignore updates and Moq dependency) all center on enabling local downloading and syncing of FW Headless projects. The title is concise, specific, and clearly communicates the primary purpose to a developer scanning the repository history.
Description Check ✅ Passed The PR description is directly related to the changeset and provides meaningful context for all the changes made. It clearly explains the purpose (adding support for downloading and syncing FW Headless project folders for local debugging), documents the specific changes across deployment automation (new script and task), backend utilities (SyncDownloadedProject method), and infrastructure updates (.gitignore), and even includes a helpful bookmarklet example. The description is specific and informative without being vague or generic.

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.

@github-actions github-actions bot added 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related 📦 Lexbox issues related to any server side code, fw-headless included labels Oct 21, 2025
@argos-ci
Copy link

argos-ci bot commented Oct 21, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Nov 21, 2025, 11:16 AM

@github-actions
Copy link

github-actions bot commented Oct 21, 2025

UI unit Tests

  1 files  ±0   45 suites  ±0   20s ⏱️ -1s
111 tests ±0  111 ✅ ±0  0 💤 ±0  0 ❌ ±0 
160 runs  ±0  160 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 7d5d285. ± Comparison against base commit 846d917.

♻️ This comment has been updated with latest results.

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: 6

🧹 Nitpick comments (13)
deployment/download-fw-headless-project.js (8)

103-116: fetch requires Node 18+; use http HEAD or guard.

To avoid Node version issues, use http.request({method:"HEAD"}) or dynamically import a polyfill. Also add a timeout.


90-98: Possible port conflict on 8088.

Taskfile also uses 8088 for hgweb forwarding. Pick a free port dynamically or allow override via CLI/env.


81-85: Python dependency in container is brittle.

Many images lack python3. Prefer kubectl cp for the tar or stream kubectl exec ... tar czf - to local. If keeping Python, add a preflight check and a BusyBox httpd fallback.


45-47: Command building lacks escaping.

runKubectl(args.join(" ")) risks shell parsing issues if args contain spaces/specials. Use spawnSync("kubectl", args, ...) or escape args.


32-36: Align downloads path with repo structure.

Current path is CWD‑relative. Resolve relative to this script so artifacts land in deployment/_downloads (which is ignored).

-import path from "path";
+import path from "path";
+import { fileURLToPath } from "url";
@@
-const localExtractDir = path.resolve(`_downloads/${projectCode}-${timestamp}`);
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const localExtractDir = path.resolve(__dirname, `_downloads/${projectCode}-${timestamp}`);

12-13: Remove unused variables.

pipe and localTar aren’t used. Also the timestamp comment shows an underscore not present in the computed value.

Also applies to: 30-33


49-61: Pod selection robustness.

items[0] may pick a non‑Running pod in rolling updates. Filter for Running and consider newest.


215-223: More defensive cleanup.

Guard against missing pid file and ignore errors; you already do that. Also kill pfProcess with SIGINT and wait for exit to avoid orphaned forwards.

backend/FwLite/LcmDebugger/LcmDebugger.csproj (1)

13-13: Pin Moq version or use central package management.

Unpinned packages can cause nondeterministic builds. If you’re not using Directory.Packages.props, add an explicit version. If you are, mark this with Version there and consider PrivateAssets="all" so Moq doesn’t flow transitively.

Example:

<PackageReference Include="Moq" Version="4.20.72" PrivateAssets="all" />
backend/FwLite/LcmDebugger/Program.cs (2)

19-19: Make DI registration explicit and strict.

Register the service type explicitly and use MockBehavior.Strict to fail fast if it’s accidentally used.

-builder.Services.AddScoped((_services) => new Mock<IServerHttpClientProvider>().Object);
+builder.Services.AddScoped<IServerHttpClientProvider>(_ =>
+    new Mock<IServerHttpClientProvider>(MockBehavior.Strict).Object);

26-27: Avoid commented code in main; gate under DEBUG or move to a sample.

Keeps Program clean and avoids drift.

backend/FwLite/LcmDebugger/Utils.cs (2)

49-50: Validate that crdt.sqlite exists before attempting to open.

If the file doesn't exist, the subsequent OpenProject call will fail with a less helpful error message.

Add a file existence check:

         var crdtDbPath = Path.Combine(currProjRoot, "crdt.sqlite");
+        if (!File.Exists(crdtDbPath))
+            throw new InvalidOperationException($"CRDT database not found at: {crdtDbPath}");
         var crdtProject = new CrdtProject("unused-project-code", crdtDbPath);

51-51: Consider using a safe cast pattern.

The direct cast to CrdtMiniLcmApi will throw InvalidCastException if the implementation type changes. While OpenProject currently returns the correct type, a safer pattern would verify the type or use pattern matching.

Use pattern matching or an explicit type check:

-        var crdtMiniLcmApi = (CrdtMiniLcmApi)await services.GetRequiredService<CrdtProjectsService>().OpenProject(crdtProject, services);
+        var api = await services.GetRequiredService<CrdtProjectsService>().OpenProject(crdtProject, services);
+        if (api is not CrdtMiniLcmApi crdtMiniLcmApi)
+            throw new InvalidOperationException($"Expected CrdtMiniLcmApi but got {api.GetType().Name}");
         Console.WriteLine($"Crdt Project: {crdtMiniLcmApi.ProjectData.Code}");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19cc689 and ac77174.

📒 Files selected for processing (6)
  • backend/FwLite/LcmDebugger/LcmDebugger.csproj (1 hunks)
  • backend/FwLite/LcmDebugger/Program.cs (1 hunks)
  • backend/FwLite/LcmDebugger/Utils.cs (2 hunks)
  • deployment/.gitignore (1 hunks)
  • deployment/Taskfile.yml (1 hunks)
  • deployment/download-fw-headless-project.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/FwLite/LcmDebugger/Utils.cs (3)
backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs (1)
  • CrdtMiniLcmApi (24-809)
backend/FwLite/LcmCrdt/CrdtProjectsService.cs (1)
  • CrdtProjectsService (16-406)
backend/FwLite/FwLiteProjectSync/CrdtFwdataProjectSyncService.cs (1)
  • CrdtFwdataProjectSyncService (16-164)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: frontend-component-unit-tests
  • GitHub Check: Build FW Lite and run tests
  • GitHub Check: frontend
🔇 Additional comments (4)
deployment/.gitignore (1)

1-1: LGTM, but verify CWD assumptions.

This ignores deployment/_downloads. Ensure the download script writes to deployment/_downloads (not repo root), otherwise artifacts may be untracked. Consider resolving the downloads path relative to the script file.

deployment/Taskfile.yml (1)

105-118: Task wiring looks good; confirm working directory.

Command runs node download-fw-headless-project.js ... without an explicit dir. Taskfile lives in deployment/, so this should execute there. If contributors run Task from repo root, confirm Task discovers this Taskfile or document task -d deployment ....

backend/FwLite/LcmDebugger/Utils.cs (2)

1-1: LGTM!

The new using directives are correctly added to support the CallerFilePath attribute and the new sync functionality.

Also applies to: 4-5


60-76: LGTM!

The helper method correctly uses CallerFilePath to locate the repository root and constructs the downloads path. The directory traversal logic properly handles edge cases (null parent directory) and provides clear error messages if the solution root cannot be found.

@myieye myieye force-pushed the script-for-downloading-fw-headless-projects branch from fc8aa7d to de1cc27 Compare November 11, 2025 13:54
@myieye myieye requested a review from rmunn November 19, 2025 14:32
Copy link
Contributor

@rmunn rmunn left a comment

Choose a reason for hiding this comment

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

Nice. Thank you for getting this done (and in Taskfile too); I've been wanting this for a while but never got around to implementing it.

@myieye myieye merged commit 6f98d19 into develop Nov 21, 2025
23 of 24 checks passed
@myieye myieye deleted the script-for-downloading-fw-headless-projects branch November 21, 2025 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related 📦 Lexbox issues related to any server side code, fw-headless included

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants