Skip to content

Conversation

@cdrage
Copy link
Contributor

@cdrage cdrage commented Dec 19, 2025

feat(macadam): only install macadam when vm features are used

What does this PR do?

Macadam is used to run VM's, this was previously activated on boot and would
install macadam immediately by showing a sudo prompt. This PR adds
"lazy" installing functionality where we only prompt if VM features are
clicked / used.

This PR:

  • Moves the init functionality to only happen when creating a VM
  • The VM monitoring loop is triggered either at the beginning (if
    macadam is already installed) or after create VM is used which would
    be the first point a VM is accessed / used
  • Adds documentation as well as screenshot

Screenshot / video of UI

Video shows functionality of it installed and not installed:

Screen.Recording.2025-12-19.at.3.40.09.PM.mov

What issues does this PR fix or reference?

Closes #2171

How to test this PR?

  1. REMOVE MACADAM (macOS):
~ $ whereis macadam
macadam: /opt/macadam/bin/macadam
~ $ sudo rm -rf /opt/macadam
Password:

If you are on Linux, it must be removed at: /usr/local/bin/macadam

  1. Start bootc extension, you should NO LONGER receive the sudo prompt.

  2. Go to Disk Images > Create VM

  3. Expect the sudo prompt to appear to install

  4. Be able to create virtual machine / list VM functionality like usual.

Signed-off-by: Charlie Drage [email protected]

@cdrage cdrage requested a review from a team as a code owner December 19, 2025 20:55
@cdrage cdrage requested review from SoniaSandler, deboer-tim, feloy and jeffmaury and removed request for a team December 19, 2025 20:55
@coderabbitai
Copy link

coderabbitai bot commented Dec 19, 2025

📝 Walkthrough

Walkthrough

Lazy-initialize Macadam: activation no longer forces macadam install or monitoring. The extension defers initialization until the macadam binary is present or a VM operation runs; new API and tests added, and README updated with macOS/Linux install notes and a Windows support note.

Changes

Cohort / File(s) Summary
Documentation
README.md
Added one-time macadam install note for macOS/Linux (expected macOS path, manual Linux option), escalated-privileges image, and a Windows note about Hyper-V/WSL2 constraints; duplicated VM-usage notes near VM sections.
Extension logic
packages/backend/src/extension.ts
Added OS-specific macadam path constants, macadamInitialized flag, macadamEvents EventEmitter and MACADAM_INITIALIZED_EVENT; deferred initialization flow, exported ensureMacadamInitialized(): Promise<void>, and guarded VM ops (getJSONMachineListByProvider, startMachine, stopMachine, remove) to ensure init before use.
VM lifecycle integration
packages/backend/src/macadam.ts
After successful VM creation, calls ensureMacadamInitialized() to trigger initialization/monitoring so new VMs appear in Resources.
Tests & Mocks
packages/backend/src/extension.spec.ts, packages/backend/src/macadam.spec.ts
Added tests for lazy-init, init propagation/errors, OS-specific activation flows, and no double-init; updated mocks: centralized fs.existsSync mock, Macadam represented as class with instance init/listVms, and ensureMacadamInitialized mocked in macadam.spec.ts.

Sequence Diagram(s)

sequenceDiagram
  participant User as User / VM Operation
  participant Ext as Extension
  participant FS as Filesystem
  participant Mac as Macadam (binary/API)
  participant Monitor as Monitor/EventEmitter

  Note over Ext,FS: Extension activation — do not force install
  Ext->>FS: check macadam binary present?
  alt binary present
    Ext->>Mac: init()
    Mac-->>Ext: init success
    Ext->>Monitor: start monitoring
  else binary missing
    Ext-->>Ext: defer init (no monitoring)
  end

  Note over User,Ext: User triggers VM operation
  User->>Ext: call VM operation (list/create/start/stop/remove)
  Ext->>FS: check macadam binary present?
  alt binary present
    Ext->>Mac: ensureMacadamInitialized() -> init()
    Mac-->>Ext: init success
    Ext->>Monitor: start monitoring
    Ext->>Mac: perform VM operation
    Mac-->>Ext: operation result
  else binary missing
    Ext-->>User: proceed without monitoring or surface install prompt when appropriate
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: lazy initialization of macadam only when VM features are used instead of on extension activation.
Description check ✅ Passed The description clearly explains the PR purpose, implementation approach, testing steps, and references the related issue #2171.
Linked Issues check ✅ Passed The PR implementation fully addresses issue #2171 by deferring macadam initialization until VM features are used, implementing lazy initialization, and preventing sudo prompts on extension activation.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing lazy macadam initialization. The README updates, test enhancements, and API exports support the core objective of deferring installation to VM feature usage.

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

🧹 Nitpick comments (2)
packages/backend/src/macadam.spec.ts (1)

79-105: Consider adding a test to verify ensureMacadamInitialized is called after VM creation.

The createVm method in macadam.ts now calls ensureMacadamInitialized() after successful VM creation (line 73 in macadam.ts). However, there's no test verifying this integration point. Adding such a test would ensure the monitoring loop trigger behavior is preserved.

🔎 Example test addition
import { ensureMacadamInitialized } from './extension';

test('Test createVm calls ensureMacadamInitialized after success', async () => {
  const macadamVm = new MacadamHandler(TELEMETRY_LOGGER_MOCK);
  const macadamInstance = vi.mocked(macadam.Macadam).mock.results[0].value;
  macadamInstance.createVm.mockResolvedValue({ stdout: '', stderr: '' });

  vi.spyOn(extensionApi.window, 'withProgress').mockImplementation((_options, task) => {
    return task(progress, {} as unknown as extensionApi.CancellationToken);
  });
  vi.mocked(extensionApi.env).isMac = true;

  await macadamVm.createVm({
    name: 'test',
    imagePath: '/test.qcow2',
    sshIdentityPath: '/key',
  });

  expect(ensureMacadamInitialized).toHaveBeenCalled();
});
packages/backend/src/extension.spec.ts (1)

136-193: Test isolation concern: module-level macadamInitialized state may leak between tests.

The extension.ts module uses a module-level macadamInitialized flag. Since vi.clearAllMocks() in beforeEach doesn't reset module state, tests that set this flag to true (like "should not re-initialize macadam if already initialized") could affect subsequent tests.

Consider using vi.resetModules() before each test in this describe block, or re-importing the module dynamically to ensure clean state.

🔎 Proposed fix for test isolation
 describe('test ensureMacadamInitialized doesnt double init', () => {
   beforeEach(() => {
+    vi.resetModules();
     vi.spyOn(fs.promises, 'readFile').mockImplementation(() => {
       return Promise.resolve('<html></html>');
     });
     (podmanDesktopApi.version as string) = '1.8.0';
   });

Note: This may require adjusting how mocks are set up, as resetModules clears the module cache.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a69de4a and 6b38132.

⛔ Files ignored due to path filters (1)
  • docs/img/escalated_privileges.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • README.md (1 hunks)
  • packages/backend/src/extension.spec.ts (7 hunks)
  • packages/backend/src/extension.ts (8 hunks)
  • packages/backend/src/macadam.spec.ts (1 hunks)
  • packages/backend/src/macadam.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/backend/src/macadam.ts (1)
packages/backend/src/extension.ts (1)
  • ensureMacadamInitialized (262-281)
packages/backend/src/extension.spec.ts (1)
packages/backend/src/extension.ts (2)
  • activate (102-214)
  • getJSONMachineListByProvider (329-342)
🪛 markdownlint-cli2 (0.18.1)
README.md

192-192: Link fragments should be valid

(MD051, link-fragments)

⏰ 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). (3)
  • GitHub Check: linter, formatter, and tests / macos-14
  • GitHub Check: linter, formatter, and tests / windows-2022
  • GitHub Check: e2e tests
🔇 Additional comments (11)
README.md (1)

192-196: Documentation content aligns well with PR objectives.

The new documentation clearly communicates the lazy initialization behavior: the password prompt now occurs during first-time VM operations (not on extension activation), it's a one-time installation, and platform-specific paths are documented. The Windows support note on line 196 is also valuable context.

packages/backend/src/macadam.spec.ts (1)

24-27: Mock setup looks correct for test isolation.

The mock for ensureMacadamInitialized properly isolates the MacadamHandler tests from the actual initialization logic in extension.ts.

packages/backend/src/macadam.ts (2)

69-73: Calling ensureMacadamInitialized after local init is correct for triggering monitoring.

The code correctly calls ensureMacadamInitialized() after VM creation to start the monitoring loop in extension.ts. Since MacadamHandler uses its own Macadam instance (this.macadam) while extension.ts maintains a separate module-level instance, calling ensureMacadamInitialized() here properly initializes the extension's instance and triggers monitorMachines().


95-105: listVms does not call ensureMacadamInitialized but other VM operations do.

The listVms method calls this.macadam.init() locally but does not trigger ensureMacadamInitialized() from extension.ts. This differs from the pattern established in createVm. While getJSONMachineListByProvider in extension.ts handles initialization for the extension's instance, if listVms in MacadamHandler is called directly, the monitoring loop won't start.

Is this intentional? If listVms can be invoked independently of getJSONMachineListByProvider, consider adding ensureMacadamInitialized() here as well for consistency.

packages/backend/src/extension.ts (4)

51-57: Binary path constants and state management are well-structured.

The constants for binary paths clearly document the source, and the module-level state variables (macadamInitialized, macadamProvider, macadamExtensionContext) properly track initialization state for the lazy init pattern.


197-212: Lazy initialization logic correctly defers sudo prompt.

The activation now checks for binary existence before calling init(), achieving the PR objective of avoiding the sudo prompt on extension activation when macadam isn't installed. The error handling logs failures without throwing, allowing the extension to continue loading.


258-281: Consider whether repeated init retries on failure are desired.

If macadam.init() fails (e.g., user cancels sudo prompt), macadamInitialized remains false, causing subsequent VM operations to retry initialization. This could lead to repeated sudo prompts.

If this retry behavior is intentional (giving users another chance), this is fine. If you want to avoid repeated prompts after a failure, consider adding a macadamInitFailed flag to short-circuit future attempts.


329-342: getJSONMachineListByProvider properly guards with lazy init.

The function correctly calls ensureMacadamInitialized() before listing VMs and captures any initialization errors in the return object. This ensures the sudo prompt only appears when users actually invoke VM operations.

packages/backend/src/extension.spec.ts (3)

45-56: Clean fs mock setup for controlling binary existence.

The mock correctly exports existsSync both as a default export property and as a named export, ensuring compatibility with both import styles.


109-116: Class-based Macadam mock aligns with actual library structure.

The mock properly creates a class with init and listVms instance methods, matching how new Macadam(name) is used in the production code.


283-355: Comprehensive test coverage for lazy initialization across platforms.

The tests thoroughly cover:

  • macOS and Linux: binary existence checks controlling init behavior
  • Windows: correctly skipping macadam (not yet supported)
  • Error handling during activation

This test suite effectively validates the PR objectives.

![](https://raw.githubusercontent.com/podman-desktop/podman-desktop-extension-bootc/main/docs/img/vm.gif)

> **Note (macOS and Linux):** When performing virtual machine operations (create, start, stop, delete) for the first time, you will be prompted for your password to install the [macadam](https://github.com/crc-org/macadam) binary. This is a one-time installation that enables VM management. On macOS, the binary is installed to `/opt/macadam/bin/macadam`. On Linux, the binary will be installed automatically, but if issues occur it can be [installed manually](#linux-only-unable-to-create-virtual-machine).
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Invalid link fragment: reference target is not a markdown heading.

Line 192 contains a link to #linux-only-unable-to-create-virtual-machine, but the referenced section on line 247 (**(Linux only) Unable to create virtual machine:**) is formatted as bold text, not a markdown heading. Markdown processors won't generate an anchor for non-heading text, making this link invalid. This also triggered the markdownlint MD051 warning.

🔎 Proposed fix: Convert line 247 to a proper markdown heading

Convert the bold text to a level 3 markdown heading so it generates a valid anchor:

-**(Linux only) Unable to create virtual machine:**
+### (Linux only) Unable to create virtual machine

This will create the anchor #linux-only-unable-to-create-virtual-machine that the link on line 192 references.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

192-192: Link fragments should be valid

(MD051, link-fragments)

🤖 Prompt for AI Agents
README.md around lines 192 and 247: the link at line 192 targets
#linux-only-unable-to-create-virtual-machine but the target at line 247 is plain
bold text, not a markdown heading, so no anchor is generated; fix by converting
the bold text at line 247 into a proper markdown heading (e.g., "### (Linux
only) Unable to create virtual machine" or similar) so the generated anchor
matches the existing link, or alternatively update the link to exactly match the
final heading text; after change, run a markdown linter to ensure MD051 is
resolved.

console.error('Error initializing macadam', error);
}

monitorMachines(provider, extensionContext).catch((error: unknown) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would better separate concerns to fire an event initialized here, and have an initialized.on(() => monitorMachines) at the place of the original monitorMachines()

@cdrage cdrage force-pushed the install-on-using-vms branch from 6b38132 to 674df49 Compare December 24, 2025 15:53
@cdrage
Copy link
Contributor Author

cdrage commented Dec 24, 2025

Updated! Great suggestion @feloy I switched it to use events emitter instead to retrigger the monitoring.

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)
packages/backend/src/macadam.ts (1)

96-105: Consider triggering monitoring in listVms as well.

The listVms method initializes macadam but doesn't call ensureMacadamInitialized(), unlike createVm (Line 73). This creates an inconsistency:

  • If a user lists VMs before creating one, monitoring won't be triggered
  • Per PR objectives, both createVM and listVMs should "lazily activate monitoring"

Consider adding await ensureMacadamInitialized(); after Line 98 to ensure consistent behavior across VM operations, or document why monitoring should only start after VM creation.

🔎 Proposed fix to trigger monitoring in listVms
 async listVms(): Promise<VmDetails[]> {
   try {
     await this.macadam.init();
+    await ensureMacadamInitialized();
     const vms = await this.macadam.listVms({});
     return vms;
   } catch (err) {
     console.error('Failed to list VMs:', err);
     throw new Error(`Error listing VMs: ${err instanceof Error ? err.message : String(err)}`);
   }
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b38132 and 674df49.

⛔ Files ignored due to path filters (1)
  • docs/img/escalated_privileges.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • README.md
  • packages/backend/src/extension.spec.ts
  • packages/backend/src/extension.ts
  • packages/backend/src/macadam.spec.ts
  • packages/backend/src/macadam.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/backend/src/extension.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/backend/src/extension.spec.ts (1)
packages/backend/src/extension.ts (2)
  • activate (105-218)
  • getJSONMachineListByProvider (329-342)
packages/backend/src/macadam.ts (1)
packages/backend/src/extension.ts (1)
  • ensureMacadamInitialized (266-281)
🪛 markdownlint-cli2 (0.18.1)
README.md

192-192: Link fragments should be valid

(MD051, link-fragments)

⏰ 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). (1)
  • GitHub Check: linter, formatter, and tests / windows-2022
🔇 Additional comments (8)
README.md (1)

192-196: Documentation accurately reflects lazy-initialization behavior.

The new notes clearly explain the deferred macadam installation and when users encounter the password prompt (during first VM operations rather than on extension activation). This correctly documents the PR's objectives.

packages/backend/src/macadam.spec.ts (1)

24-27: LGTM!

The mock correctly prevents actual macadam initialization during tests while allowing the test to verify the MacadamHandler behavior.

packages/backend/src/extension.spec.ts (5)

21-21: LGTM!

The test infrastructure correctly supports lazy initialization testing:

  • getJSONMachineListByProvider import enables direct testing of the lazy init pathway
  • Mock additions (existsSyncMock, macadamInitMock, macadamListVmsMock) allow simulation of various initialization scenarios
  • fs.existsSync mock enables testing binary presence/absence conditions

Also applies to: 33-36, 46-56


67-69: LGTM!

Mock enhancements properly support platform-specific and error handling test scenarios:

  • Platform flags (isMac, isWindows, isLinux) enable OS-specific activation tests
  • Class-based Macadam mock with observable methods allows verification of initialization patterns
  • consoleErrorMock enables error logging assertions

Also applies to: 109-116, 122-122


136-193: LGTM!

The test suite thoroughly validates the lazy initialization guard and error propagation:

  • Error propagation test correctly verifies that init failures surface through getJSONMachineListByProvider and prevent VM listing
  • Lazy initialization test confirms macadam is not initialized at activation when the binary is missing, then initializes on first use
  • Double initialization test verifies the guard flag prevents redundant init calls

283-344: LGTM!

Platform-specific activation tests comprehensively cover lazy initialization behavior:

  • macOS and Linux tests verify binary presence determines whether initialization occurs at activation
  • Windows test correctly expects no macadam initialization (feature not yet implemented)
  • Test scenarios align with the PR's lazy initialization objectives

345-354: LGTM!

The error handling test correctly verifies that macadam initialization failures during activation are caught and logged without crashing the extension.

packages/backend/src/macadam.ts (1)

64-73: The concern about double initialization is incorrect—these are two separate Macadam instances with different purposes.

MacadamHandler creates its own instance (this.macadam) on line 35 for VM operations, while extension.ts maintains a separate global instance for monitoring and lifecycle management. Line 64 initializes the MacadamHandler's instance for the current VM operation, and line 73 calls ensureMacadamInitialized() to initialize the global instance and trigger the monitoring loop. The guard flag in ensureMacadamInitialized() prevents redundant initialization of the global instance, not the MacadamHandler instance. This separation is intentional and correct—no changes are needed.

Likely an incorrect or invalid review comment.

Comment on lines +192 to +204
> **Note (macOS and Linux):** When performing virtual machine operations (create, start, stop, delete) for the first time, you will be prompted for your password to install the [macadam](https://github.com/crc-org/macadam) binary. This is a one-time installation that enables VM management. On macOS, the binary is installed to `/opt/macadam/bin/macadam`. On Linux, the binary will be installed automatically, but if issues occur it can be [installed manually](#linux-only-unable-to-create-virtual-machine).
![Escalated privileges prompt](https://raw.githubusercontent.com/podman-desktop/podman-desktop-extension-bootc/main/docs/img/escalated_privileges.png)

> **Note (Windows):** Windows support is currently _not supported_ due to Hyper-V limitations (must run as a privileged user), WSL2 support is _not supported_ due to Windows using a custom kernel for Virtual Machine usage which is incompatible with bootc-based images.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix broken link fragment by converting target to a markdown heading.

Line 192 contains a link to #linux-only-unable-to-create-virtual-machine, but line 247 is formatted as bold text, not a heading, so no anchor is generated. This breaks the link and triggers the MD051 linter error.

🔎 Proposed fix: Convert line 247 to a proper markdown heading
-**(Linux only) Unable to create virtual machine:**
+### (Linux only) Unable to create virtual machine

This will generate the anchor #linux-only-unable-to-create-virtual-machine that the link on line 192 expects.

Also applies to: 247-247

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

192-192: Link fragments should be valid

(MD051, link-fragments)

🤖 Prompt for AI Agents
README.md lines 192-196 (link) and line 247 (target) — the link to
#linux-only-unable-to-create-virtual-machine is broken because the target is
bold text, not a markdown heading; replace the bold text at line 247 with a
proper markdown heading whose text exactly matches the expected slug, e.g. "##
Linux only: Unable to create virtual machine" (or another heading level) so the
anchor linux-only-unable-to-create-virtual-machine is generated and the link on
line 192 resolves.

### What does this PR do?

Macadam is used to run VM's, this was previously activated on boot and would
install macadam immediately by showing a sudo prompt. This PR adds
"lazy" installing functionality where we only prompt if VM features are
clicked / used.

This PR:
* Moves the init functionality to only happen when creating a VM
* The VM monitoring loop is triggered either at the beginning (if
  macadam is already installed) or after create VM is used which would
  be the first point a VM is accessed / used
* Adds documentation as well as screenshot

### Screenshot / video of UI

<!-- If this PR is changing UI, please include
screenshots or screencasts showing the difference -->

### What issues does this PR fix or reference?

<!-- Include any related issues from Podman Desktop
repository (or from another issue tracker). -->

Closes podman-desktop#2171

### How to test this PR?

<!-- Please explain steps to reproduce -->

1. REMOVE MACADAM (macOS):

```sh
~ $ whereis macadam
macadam: /opt/macadam/bin/macadam
~ $ sudo rm -rf /opt/macadam
Password:
```

If you are on Linux, it must be removed at: `/usr/local/bin/macadam`

2. Start bootc extension, you should NO LONGER receive the sudo prompt.

3. Go to **Disk Images > Create VM**

4. Expect the sudo prompt to appear to install

5. Be able to create virtual machine / list VM functionality like usual.

Signed-off-by: Charlie Drage <[email protected]>
@cdrage cdrage force-pushed the install-on-using-vms branch from 674df49 to d4f0c9f Compare December 29, 2025 14:14
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: 2

♻️ Duplicate comments (1)
README.md (1)

200-200: Broken link fragment already flagged in previous reviews.

The link to #linux-only-unable-to-create-virtual-machine points to a target (line 247) that is formatted as bold text rather than a markdown heading, preventing anchor generation. This issue has been identified in prior reviews.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 674df49 and d4f0c9f.

⛔ Files ignored due to path filters (1)
  • docs/img/escalated_privileges.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • README.md
  • packages/backend/src/extension.spec.ts
  • packages/backend/src/extension.ts
  • packages/backend/src/macadam.spec.ts
  • packages/backend/src/macadam.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/backend/src/macadam.ts (1)
packages/backend/src/extension.ts (1)
  • ensureMacadamInitialized (266-281)
packages/backend/src/extension.spec.ts (1)
packages/backend/src/extension.ts (2)
  • activate (105-218)
  • getJSONMachineListByProvider (329-342)
🪛 markdownlint-cli2 (0.18.1)
README.md

200-200: Link fragments should be valid

(MD051, link-fragments)

⏰ 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). (3)
  • GitHub Check: linter, formatter, and tests / windows-2022
  • GitHub Check: e2e tests
  • GitHub Check: linter, formatter, and tests / macos-14
🔇 Additional comments (11)
README.md (1)

200-204: Clear documentation of the lazy installation behavior.

The added notes effectively communicate the one-time macadam installation requirement and privilege escalation to users, aligning well with the PR's objective to defer sudo prompts until VM features are used.

packages/backend/src/macadam.spec.ts (1)

24-27: LGTM: Mock setup enables clean testing of MacadamHandler.

The mock for ensureMacadamInitialized appropriately simulates a successful initialization without triggering actual side effects, allowing tests to focus on MacadamHandler behavior.

packages/backend/src/macadam.ts (2)

23-23: LGTM: Import supports lazy initialization pattern.

The import of ensureMacadamInitialized enables the MacadamHandler to trigger monitoring after VM creation, aligning with the PR's lazy initialization strategy.


70-73: Well-documented trigger point for monitoring initialization.

The call to ensureMacadamInitialized() after VM creation correctly ensures the monitoring loop starts only after a user-initiated VM operation, with a clear comment explaining the behavior.

packages/backend/src/extension.ts (3)

203-216: Clean separation of initialization from monitoring startup.

The conditional initialization based on binary existence successfully defers the sudo prompt to the first VM operation when the binary is not pre-installed, meeting the PR's core objective.


262-281: LGTM: Clear API for lazy initialization.

The exported ensureMacadamInitialized() function provides a straightforward gate for VM operations, with appropriate error handling and event emission to trigger monitoring.


333-333: Consistent initialization guards across all VM operations.

All VM operation entry points (getJSONMachineListByProvider, startMachine, stopMachine, and the delete lifecycle method) correctly ensure macadam is initialized before proceeding.

Also applies to: 355-355, 385-385, 417-417

packages/backend/src/extension.spec.ts (4)

45-56: Well-structured fs module mock for binary existence simulation.

The mock setup for fs.existsSync enables precise control over binary existence checks in tests, supporting validation of both initialization paths (binary present vs. absent).


109-116: LGTM: Macadam mock structure aligns with production usage.

The class-based mock with init and listVms methods accurately reflects the Macadam API surface used in the extension, enabling realistic test scenarios.


136-193: Comprehensive test coverage for initialization lifecycle.

The test suite thoroughly validates:

  • Error propagation through the lazy initialization path
  • Deferred initialization when binary is absent at activation
  • Prevention of re-initialization

These tests provide strong confidence in the lazy initialization behavior.


283-355: Excellent OS-specific test coverage.

The test suite validates platform-specific behavior (macOS, Linux, Windows) for both binary-present and binary-absent scenarios, including error handling during activation. This comprehensive coverage aligns well with the cross-platform nature of the extension.

Comment on lines +52 to +60
// Macadam binary paths - used to check if already installed
// MACADAM_MACOS_PATH is exported from macadam.js, Linux path is hardcoded in the library
const MACADAM_BINARY_PATH_MACOS = `${MACADAM_MACOS_PATH}/macadam`;
const MACADAM_BINARY_PATH_LINUX = '/usr/local/bin/macadam';
let macadamInitialized = false;

// Event emitter for macadam lifecycle events - separates initialization from monitoring
const macadamEvents = new EventEmitter();
const MACADAM_INITIALIZED_EVENT = 'initialized';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/backend/src/extension.ts | head -100

Repository: podman-desktop/extension-bootc

Length of output: 4300


🏁 Script executed:

sed -n '200,300p' packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 3178


🏁 Script executed:

rg -n "ensureMacadamInitialized" packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 313


🏁 Script executed:

sed -n '330,340p' packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 391


🏁 Script executed:

sed -n '350,360p' packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 460


🏁 Script executed:

sed -n '380,390p' packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 477


🏁 Script executed:

sed -n '410,425p' packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 604


🏁 Script executed:

sed -n '266,282p' packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 453


🏁 Script executed:

rg -n "macadamInitialized\|macadamInitPromise" packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 56


🏁 Script executed:

rg -n "macadamInitialized" packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 214


Race condition in concurrent initialization must be fixed.

The macadamInitialized flag lacks synchronization. When multiple VM operations call ensureMacadamInitialized() concurrently—for example, if a user starts two VMs simultaneously—they can both read macadamInitialized === false before the first initialization completes, causing multiple concurrent macadam.init() calls. This can trigger multiple sudo prompts or installation attempts.

Fix by introducing a Promise to serialize initialization:

 let macadamInitialized = false;
+let macadamInitPromise: Promise<void> | undefined;

 // Event emitter for macadam lifecycle events - separates initialization from monitoring
 const macadamEvents = new EventEmitter();
 const MACADAM_INITIALIZED_EVENT = 'initialized';

Then update ensureMacadamInitialized():

 export async function ensureMacadamInitialized(): Promise<void> {
   if (macadamInitialized) {
     return;
   }
+
+  // If initialization is already in progress, wait for it
+  if (macadamInitPromise) {
+    return macadamInitPromise;
+  }

-  try {
-    await macadam.init();
-    macadamInitialized = true;
-
-    // Emit initialized event to trigger monitoring
-    macadamEvents.emit(MACADAM_INITIALIZED_EVENT);
-  } catch (error) {
-    console.error('Error initializing macadam', error);
-    throw error;
-  }
+  macadamInitPromise = (async () => {
+    try {
+      await macadam.init();
+      macadamInitialized = true;
+
+      // Emit initialized event to trigger monitoring
+      macadamEvents.emit(MACADAM_INITIALIZED_EVENT);
+    } catch (error) {
+      console.error('Error initializing macadam', error);
+      throw error;
+    } finally {
+      macadamInitPromise = undefined;
+    }
+  })();
+
+  return macadamInitPromise;
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +196 to +216
// Register listener to start monitoring when macadam is initialized
macadamEvents.on(MACADAM_INITIALIZED_EVENT, () => {
monitorMachines(provider, extensionContext).catch((error: unknown) => {
console.error('Error while monitoring machines', error);
});
});

// Only initialize and start monitoring if macadam binary is already installed.
// This avoids prompting for sudo on extension activation (macOS).
// If not installed, init() will be called lazily when user performs a VM operation.
const macadamBinaryPath = extensionApi.env.isMac ? MACADAM_BINARY_PATH_MACOS : MACADAM_BINARY_PATH_LINUX;
if (fs.existsSync(macadamBinaryPath)) {
try {
await macadam.init();
macadamInitialized = true;
} catch (error) {
console.error('Error initializing macadam', error);
}

macadamEvents.emit(MACADAM_INITIALIZED_EVENT);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check the monitorMachines function implementation
rg -nP -A20 'async function monitorMachines' packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 892


🏁 Script executed:

# Search for stopLoop variable declaration and usage
rg -nP 'stopLoop' packages/backend/src/extension.ts | head -20

Repository: podman-desktop/extension-bootc

Length of output: 144


🏁 Script executed:

# Search for ensureMacadamInitialized function
rg -nP -B3 -A15 'function ensureMacadamInitialized' packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 741


🏁 Script executed:

# Check the event listener registration to see if it uses .on() or .once()
rg -nP -B5 -A5 'macadamEvents.on.*MACADAM_INITIALIZED_EVENT' packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 528


🏁 Script executed:

# Check where monitorMachines is called and how many times it could be invoked
rg -nP 'monitorMachines' packages/backend/src/extension.ts

Repository: podman-desktop/extension-bootc

Length of output: 351


Improve event listener registration to prevent duplicate monitoring loops.

The MACADAM_INITIALIZED_EVENT listener at line 197 uses .on(), which means it will trigger every time the event is emitted. While the macadamInitialized guard in ensureMacadamInitialized() (line 267) currently prevents a second emission in normal flow, this design is fragile. If the event is emitted multiple times—whether intentionally or through future code changes—multiple monitorMachines() invocations would run concurrently, each spawning independent recursive loops checking the same stopLoop flag.

Use .once() instead of .on() to ensure the listener fires only once, or add a guard flag to track whether monitoring is already active.

🤖 Prompt for AI Agents
packages/backend/src/extension.ts lines 196-216: the MACADAM_INITIALIZED_EVENT
listener is registered with .on(), which can allow multiple monitorMachines()
loops to start if the event fires more than once; change the listener to .once()
so it only fires a single time, or alternatively add a simple guard (e.g., let
monitoringActive = false; check it before calling monitorMachines and set it
true once started) to ensure monitorMachines() is invoked at most once; keep the
existing error handling wrapper when calling monitorMachines().

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.

macadam: Only install when trying to use VM features.

2 participants