8000 refactor: centralized experimental features by gastoner · Pull Request #13227 · podman-desktop/podman-desktop · GitHub
[go: up one dir, main page]

Skip to content

Conversation

gastoner
Copy link
Contributor
@gastoner gastoner commented Jul 16, 2025

What does this PR do?

Changes logic from storing boolean value to object/undefined in order to make work
#13223
#13224
#13225
#13226

Uses new experimental configuration manager #13316

More info in #13095

Screenshot / video of UI

What issues does this PR fix or reference?

Closes #13096
Needs rebase after #13316

THOSE PRs MUST be merged together:
#13225
#13224
#13223
#13226
#13227

How to test this PR?

  • Tests are covering the bug fix or the new feature

Copy link
Contributor
coderabbitai bot commented Jul 16, 2025
📝 Walkthrough

Walkthrough

This change updates the handling of experimental configuration features in the renderer's preferences UI and related tests. It shifts the logic from treating experimental configurations as booleans to treating them as objects, updating the UI and test logic accordingly. The update and toggle operations now use new APIs (window.isExperimentalConfigurationEnabled and window.updateExperimentalConfigurationValue) and avoid redundant updates by checking the current state before applying changes. The PreferencesRenderingItemFormat component is also updated to support object-typed configuration properties, treating them as boolean toggles with special serialization logic.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~18 minutes

Assessment against linked issues

Objective Addressed Explanation
Implement changes discussed in centralize experimental configuration (#13096)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes were identified based on the objectives from the linked issues.

Possibly related PRs

Suggested reviewers

  • benoitf
  • MarsKubeX
  • cdrage

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c717fd5 and 4401a69.

📒 Files selected for processing (3)
  • packages/renderer/src/lib/preferences/ExperimentalPage.spec.ts (3 hunks)
  • packages/renderer/src/lib/preferences/ExperimentalPage.svelte (2 hunks)
  • packages/renderer/src/lib/preferences/PreferencesRenderingItemFormat.svelte (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/renderer/src/lib/preferences/ExperimentalPage.svelte
  • packages/renderer/src/lib/preferences/ExperimentalPage.spec.ts
⏰ 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). (10)
  • GitHub Check: smoke e2e tests (production)
  • GitHub Check: smoke e2e tests (development)
  • GitHub Check: unit tests / windows-2025
  • GitHub Check: unit tests / ubuntu-24.04
  • GitHub Check: typecheck
  • GitHub Check: build website
  • GitHub Check: linter, formatters
  • GitHub Check: macOS
  • GitHub Check: Linux
  • GitHub Check: Windows
🔇 Additional comments (5)
packages/renderer/src/lib/preferences/PreferencesRenderingItemFormat.svelte (5)

61-61: LGTM: Direct assignment is appropriate here.

The direct assignment of the configuration value in the change callback is correct and aligns with the simplified handling approach.


91-93: LGTM: Object type coercion to boolean is appropriate.

The coercion of 'object' type configurations to boolean values aligns with the feature toggle pattern where objects represent enabled state and undefined represents disabled state.


106-111: LGTM: Serialization workaround is necessary but clearly documented.

The JSON stringify/parse approach for deep cloning Svelte state is a valid workaround, and the HACK comment appropriately documents the limitation. For configuration objects containing plain data, this approach should work correctly.


138-138: LGTM: Type validation correctly accepts booleans for object types.

The validation logic appropriately allows boolean values for 'object' type records since they're presented as boolean toggles in the UI.


160-172: LGTM: Object toggle logic correctly implements feature flag pattern.

The conversion from boolean UI interactions to object/undefined storage correctly implements the centralized experimental feature pattern. The comments clearly document the intended behavior.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them 8000 as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@gastoner gastoner force-pushed the centralized_experimenal_features branch from 1a38edb to 32fd0f6 Compare July 23, 2025 08:56
@gastoner gastoner marked this pull request as ready for review July 23, 2025 08:58
@gastoner gastoner requested review from benoitf and a team as code owners July 23, 2025 08:58
@gastoner gastoner requested review from dgolovin, simonrey1 and jeffmaury and removed request for a team July 23, 2025 08:58
@gastoner gastoner marked this pull request as draft July 23, 2025 08:58
Copy link
Contributor
@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @gastoner - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `packages/renderer/src/lib/preferences/PreferencesRenderingItemFormat.svelte:204` </location>
<code_context>
       record={record}
       checked={typeof givenValue === 'boolean' ? givenValue : !!recordValue}
       onChange={onChange} />
+  {:else if record.type === 'object'}
+    <BooleanItem
+      record={record}
+      checked={!!recordValue}
+      onChange={onChange} />
   {:else if record.type === 'number' || record.type === 'integer'}
     {#if enableSlider && typeof record.maximum === 'number'}
</code_context>

<issue_to_address>
Rendering BooleanItem for 'object' types may be misleading.

If this is an intentional convention (e.g., for feature toggles), please document it clearly in the codebase to avoid confusion.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
  {:else if record.type === 'object'}
    <BooleanItem
      record={record}
      checked={!!recordValue}
      onChange={onChange} />
=======
  {:else if record.type === 'object'}
    {/*
      NOTE: Rendering BooleanItem for 'object' types is intentional.
      This is used as a convention for feature toggles or similar boolean-like objects.
      If you change this behavior, ensure you understand all usages of 'object' type in the preferences schema.
    */}
    <BooleanItem
      record={record}
      checked={!!recordValue}
      onChange={onChange} />
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +204 to +208
{:else if record.type === 'object'}
<BooleanItem
record={record}
checked={!!recordValue}
onChange={onChange} />
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Rendering BooleanItem for 'object' types may be misleading.

If this is an intentional convention (e.g., for feature toggles), please document it clearly in the codebase to avoid confusion.

Suggested change
{:else if record.type === 'object'}
<BooleanItem
record={record}
checked={!!recordValue}
onChange={onChange} />
{:else if record.type === 'object'}
{/*
NOTE: Rendering BooleanItem for 'object' types is intentional.
This is used as a convention for feature toggles or similar boolean-like objects.
If you change this behavior, ensure you understand all usages of 'object' type in the preferences schema.
*/}
<BooleanItem
record={record}
checked={!!recordValue}
onChange={onChange} />

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/renderer/src/lib/preferences/PreferencesRenderingItemFormat.svelte (1)

102-118: Consider the implications of the JSON serialization hack.

The JSON.parse(JSON.stringify(recordValue)) approach on lines 107-110 works for simple objects but could fail for:

  • Objects with circular references
  • Objects with functions or undefined values
  • Objects with Date, RegExp, or other special types

Since experimental features are represented as empty objects {}, this should work fine for now. However, consider documenting this limitation or using a more robust deep clone method if the object structure becomes more complex in the future.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c87c96 and 32fd0f6.

📒 Files selected for processing (4)
  • packages/main/src/plugin/configuration-impl.ts (1 hunks)
  • packages/renderer/src/lib/preferences/ExperimentalPage.spec.ts (3 hunks)
  • packages/renderer/src/lib/preferences/ExperimentalPage.svelte (2 hunks)
  • packages/renderer/src/lib/preferences/PreferencesRenderingItemFormat.svelte (6 hunks)
🧠 Learnings (2)
📓 Common learnings
Learnt from: benoitf
PR: podman-desktop/podman-desktop#13043
File: packages/main/src/plugin/image-checker.ts:39-46
Timestamp: 2025-07-02T08:30:22.649Z
Learning: In Podman Desktop, benoitf prefers to add dependency injection annotations (@injectable, @inject) in separate PRs before implementing the actual container bindings and DI activation. This allows for incremental adoption of dependency injection.
packages/renderer/src/lib/preferences/ExperimentalPage.spec.ts (1)

Learnt from: benoitf
PR: #13043
File: packages/main/src/plugin/color-registry-inject.spec.ts:33-35
Timestamp: 2025-07-02T08:26:44.201Z
Learning: In test files for Podman Desktop, benoitf prefers partial mocking over complete mocking. Partial mocks that only implement the methods actually used in the test are sufficient and preferred over creating complete mock objects.

🧬 Code Graph Analysis (1)
packages/renderer/src/lib/preferences/ExperimentalPage.spec.ts (2)
packages/api/src/configuration/models.ts (1)
  • IConfigurationPropertyRecordedSchema (49-53)
packages/api/src/configuration/constants.ts (1)
  • CONFIGURATION_DEFAULT_SCOPE (19-19)
🪛 GitHub Check: typecheck
packages/renderer/src/lib/preferences/ExperimentalPage.spec.ts

[failure] 31-31:
Property 'isExperimentalConfigurationEnabled' does not exist on type 'Window & typeof globalThis'.


[failure] 103-103:
Property 'isExperimentalConfigurationEnabled' does not exist on type 'Window & typeof globalThis'.


[failure] 93-93:
Property 'updateExperimentalConfigurationValue' does not exist on type 'Window & typeof globalThis'. Did you mean 'updateConfigurationValue'?


[failure] 116-116:
Property 'updateExperimentalConfigurationValue' does not exist on type 'Window & typeof globalThis'. Did you mean 'updateConfigurationValue'?

🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: benoitf
PR: podman-desktop/podman-desktop#13043
File: packages/main/src/plugin/image-checker.ts:39-46
Timestamp: 2025-07-02T08:30:22.649Z
Learning: In Podman Desktop, benoitf prefers to add dependency injection annotations (@injectable, @inject) in separate PRs before implementing the actual container bindings and DI activation. This allows for incremental adoption of dependency injection.
packages/renderer/src/lib/preferences/ExperimentalPage.spec.ts (1)

Learnt from: benoitf
PR: #13043
File: packages/main/src/plugin/color-registry-inject.spec.ts:33-35
Timestamp: 2025-07-02T08:26:44.201Z
Learning: In test files for Podman Desktop, benoitf prefers partial mocking over complete mocking. Partial mocks that only implement the methods actually used in the test are sufficient and preferred over creating complete mock objects.

🧬 Code Graph Analysis (1)
packages/renderer/src/lib/preferences/ExperimentalPage.spec.ts (2)
packages/api/src/configuration/models.ts (1)
  • IConfigurationPropertyRecordedSchema (49-53)
packages/api/src/configuration/constants.ts (1)
  • CONFIGURATION_DEFAULT_SCOPE (19-19)
🪛 GitHub Check: typecheck
packages/renderer/src/lib/preferences/ExperimentalPage.spec.ts

[failure] 31-31:
Property 'isExperimentalConfigurationEnabled' does not exist on type 'Window & typeof globalThis'.


[failure] 103-103:
Property 'isExperimentalConfigurationEnabled' does not exist on type 'Window & typeof globalThis'.


[failure] 93-93:
Property 'updateExperimentalConfigurationValue' does not exist on type 'Window & typeof globalThis'. Did you mean 'updateConfigurationValue'?


[failure] 116-116:
Property 'updateExperimentalConfigurationValue' does not exist on type 'Window & typeof globalThis'. Did you mean 'updateConfigurationValue'?

⏰ 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). (11)
  • GitHub Check: smoke e2e tests (production)
  • GitHub Check: smoke e2e tests (development)
  • GitHub Check: linter, formatters
  • GitHub Check: unit tests / windows-2025
  • GitHub Check: unit tests / ubuntu-24.04
  • GitHub Check: typecheck
  • GitHub Check: unit tests / macos-15
  • GitHub Check: macOS
  • GitHub Check: Linux
  • GitHub Check: Windows
  • GitHub Check: build website
🔇 Additional comments (7)
packages/main/src/plugin/configuration-impl.ts (1)

70-94: Good catch! The deletion now correctly uses the computed key.

The fix properly deletes localKey instead of section from the local view. This ensures that configuration keys with global section prefixes are correctly removed when setting values to undefined.

packages/renderer/src/lib/preferences/ExperimentalPage.svelte (2)

23-29: LGTM! The update function correctly handles object-typed experimental configurations.

The logic properly interprets object values as enabled states, aligning with the new experimental feature representation.


41-59: Good optimization to avoid redundant updates.

The changes correctly:

  1. Check the current state before updating to avoid unnecessary API calls
  2. Use the new experimental configuration APIs
  3. Set empty object {} to enable and undefined to disable features
packages/renderer/src/lib/preferences/ExperimentalPage.spec.ts (1)

28-51: Test changes correctly reflect the new experimental configuration handling.

The updates properly:

  • Mock the new isExperimentalConfigurationEnabled method
  • Change configuration types from 'boolean' to 'object'
  • Remove the default values (as experimental features don't have defaults)
packages/renderer/src/lib/preferences/PreferencesRenderingItemFormat.svelte (3)

86-100: Good handling of object-typed configurations as boolean toggles.

The logic correctly converts object values to booleans for UI display while preserving the original value structure.


160-173: Well-structured boolean to object conversion for experimental features.

The logic clearly handles the conversion:

  • true{} (enable feature)
  • falseundefined (disable feature)

This aligns perfectly with the experimental feature representation.


204-208: Consistent UI representation for object-typed configurations.

Using BooleanItem for object types provides a consistent toggle interface for experimental features.

// mock false by default
vi.mocked(window.getConfigurationValue).mockResolvedValue(false);
// mock undefined by default (not enabled)
vi.mocked(window.isExperimentalConfigurationEnabled).mockResolvedValue(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Address TypeScript errors for the new window methods.

The static analysis shows that isExperimentalConfigurationEnabled and updateExperimentalConfigurationValue are not defined on the Window interface. These methods need to be properly mocked in the test setup.

Also applies to: 93-93, 103-103, 116-116


🏁 Script executed:

#!/bin/bash
# Description: Check if these window methods are defined in the test setup files

# Search for test setup files that might define window mocks
fd -e ts -e js "vitest-setup|test-setup|setup" packages/renderer/src

# Search for existing window method definitions
rg -A 5 "window\.(getConfigurationValue|updateConfigurationValue)" packages/renderer/src

Length of output: 87264


Define and mock the new experimental window methods

TypeScript errors occur because isExperimentalConfigurationEnabled and updateExperimentalConfigurationValue aren’t declared on Window. Please:

  • Add a global type declaration (e.g. packages/renderer/src/global.d.ts):
    export {};
    declare global {
      interface Window {
        /** Returns whether a given experimental flag (optional) is enabled */
        isExperimentalConfigurationEnabled?(flag?: string): Promise<boolean>;
        /** Updates the value of an experimental flag */
        updateExperimentalConfigurationValue?(flag: string, enabled: boolean): Promise<void>;
      }
    }
  • In your Vitest setup file (e.g. packages/renderer/vitest-setup.ts), mock both methods:
    import { vi } from 'vitest';
    
    Object.defineProperty(window, 'isExperimentalConfigurationEnabled', { value: vi.fn().mockResolvedValue(false) });
    Object.defineProperty(window, 'updateExperimentalConfigurationValue', { value: vi.fn().mockResolvedValue(undefined) });
  • Remove the inline mocks in ExperimentalPage.spec.ts now that the globals exist (lines 31, 93, 103, 116).

This ensures both methods are recognized by TypeScript and properly faked in tests.

🧰 Tools
🪛 GitHub Check: typecheck

[failure] 31-31:
Property 'isExperimentalConfigurationEnabled' does not exist on type 'Window & typeof globalThis'.

🤖 Prompt for AI Agents
In packages/renderer/src/lib/preferences/ExperimentalPage.spec.ts at line 31,
TypeScript errors occur because the window methods
isExperimentalConfigurationEnabled and updateExperimentalConfigurationValue are
not declared on the Window interface. To fix this, add a global type declaration
for these methods in packages/renderer/src/global.d.ts, then mock both methods
in the Vitest setup file (e.g., packages/renderer/vitest-setup.ts) using
Object.defineProperty with vi.fn().mockResolvedValue. Finally, remove the inline
mocks from ExperimentalPage.spec.ts at lines 31, 93, 103, and 116 since the
global mocks now exist.

@gastoner gastoner force-pushed the centralized_experimenal_features branch from 32fd0f6 to 9ce9f52 Compare July 29, 2025 10:20
Copy link
codecov bot commented Jul 29, 2025

Codecov Report

❌ Patch coverage is 48.14815% with 14 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
.../preferences/PreferencesRenderingItemFormat.svelte 45.00% 11 Missing ⚠️
...nderer/src/lib/preferences/ExperimentalPage.svelte 57.14% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Contributor
@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @gastoner - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

if (value === undefined) {
if (localView[localKey]) {
delete localView[section];
delete localView[localKey];
Copy link
Contributor

Choose a reason for hiding this comment

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

Why this change? I cannot see where the affectation changed from section to localkey

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When seting some configuration to undefined e.g. kubernetes.statesExperimental the localKey === kubernetes.statesExperimentalbut thesection === statesExperimental` due to this this configuration is not found so we are "deleting" not existing config :(

So setting config value with undewined will do nothing.

I've didn't found any other way how to do this, maybe some bug or typo? Other config should work as expected

Copy link
Contributor
@feloy feloy left a comment

Choose a reason for hiding this comment

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

LGTM codewise

recordValue = value;
// HACK: when updating experimental features (representated in settings.json by object)
// disabling this feature will set undefined as a value
// enabking will set empty object
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// enabking will set empty object
// enabling will set empty object

Signed-off-by: Evzen Gasta <evzen.ml@seznam.cz>
@gastoner gastoner force-pushed the centralized_experimenal_features branch from c717fd5 to 4401a69 Compare August 1, 2025 08:11
@gastoner gastoner merged commit 17c4d2d into podman-desktop:main Aug 1, 2025
20 of 21 checks passed
@podman-desktop-bot podman-desktop-bot added this to the 1.21.0 milestone Aug 1, 2025
@coderabbitai coderabbitai bot mentioned this pull request Aug 21, 2025
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement one of the options discussed in centralize experimental configuration
4 participants
0