Skip to content

Conversation

@FaserF
Copy link
Owner

@FaserF FaserF commented Dec 23, 2025

Summary by CodeRabbit

  • New Features

    • Cloud Sync (GitHub Gists); Script signing with certificate thumbprint support; English/German UI; AI provider choice (Local, OpenAI, Gemini); Winget toggle; Git repo path; Intune/Graph credential settings; UI now respects managed/enforced policies.
  • Documentation

    • Expanded policy docs, consolidated settings/OMA‑URI tables, XML bulk import example, ADMX/ADML localization, and upgrade notes for signing configuration.
  • Tests

    • Added unit tests for secure-value precedence, keyring fallback, and legacy secret migration.

✏️ Tip: You can customize this high-level summary in your review settings.

@FaserF FaserF self-assigned this Dec 23, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 23, 2025

Walkthrough

Adds Enforced/Defaults policy branches and many new policies and localizations; implements managed-setting detection in the Settings UI; renames Intune keys to Graph-*, adds secure secret handling and keyring migration; implements policy-driven script signing via certificate thumbprint with path and auto-detect fallbacks; updates CI workflows and docs.

Changes

Cohort / File(s) Summary
Top-level docs
README.md
Documented Cloud Sync and Script Signing features.
Policy docs
docs/PolicyDefinitions/README.md, docs/PolicyDefinitions/SwitchCraft.admx
Added Enforced/Defaults hierarchy, Registry Value/Type info, OMA-URI base/table approach, XML bulk import example, and many new policies (EnableWinget, Language, GitRepoPath, AIProvider, SignScripts, CodeSigningCertThumbprint, GraphTenantId/ClientId/Secret, plus Enforced/Defaults variants).
Localization (ADML)
docs/PolicyDefinitions/de-DE/SwitchCraft.adml, docs/PolicyDefinitions/en-US/SwitchCraft.adml
Added category/policy strings and presentation entries for Enforced/Defaults and new groups (General, AI, Security, Intune) and UI bindings for new fields.
GUI — Settings view
src/switchcraft/gui/views/settings_view.py
Added SettingsView._check_managed_settings() and tkraise() override; schedule initial check; store widget refs to disable/preload managed values; use get_secure_value and set_secret for secrets.
GUI — Intune/Graph view
src/switchcraft/gui/views/intune_view.py
Renamed Intune keys to GraphTenantId, GraphClientId, GraphClientSecret; use secure retrieval for secret; removed duplicate Log Toggle frame.
Backend — Signing service
src/switchcraft/services/signing_service.py
Added thumbprint-based signing using CodeSigningCertThumbprint (searches CurrentUser then LocalMachine), fallback to CodeSigningCertPath/legacy CertPath, auto-detect path retained; improved logging and error details.
Backend — Config utilities
src/switchcraft/utils/config.py
Added SwitchCraftConfig.is_managed() and get_secure_value() (policy-first, keyring migration from legacy registry, secure fallback precedence); updated secret handling (set_secret deletes empty values) and precedence comments.
Tests
tests/test_config_secure.py
New unit tests for get_secure_value covering policy precedence, keyring fallback, and registry→keyring migration.
CI workflows
.github/workflows/*
Removed auto-merge.yml; added/updated orchestrator, review auto-merge, lint (auto-fix + commit) workflows; introduced auto-merge gating and checks.
Upgrade docs
docs/upgrade.md
Added upgrade notes for code signing certificate precedence and migration guidance.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SettingsView as SettingsView GUI
    participant Config as SwitchCraftConfig
    participant Registry as Windows Registry
    participant Widget as UI Widget

    User->>SettingsView: open / raise Settings view
    SettingsView->>SettingsView: schedule _check_managed_settings (500ms)

    rect `#EBF5FF`
    Note over SettingsView,Config: Managed settings resolution
    SettingsView->>Config: is_managed(key) for each mapped widget
    Config->>Registry: query HKLM/HKCU Policy paths
    Registry-->>Config: policy value present / not present
    Config-->>SettingsView: True / False
    alt managed == True
        SettingsView->>Widget: disable widget and set displayed value from registry
    else
        SettingsView->>Widget: leave enabled (use preference/keyring)
    end
    end
Loading
sequenceDiagram
    participant Caller as Signing Caller
    participant Service as SigningService
    participant Config as SwitchCraftConfig
    participant PowerShell as PowerShell Executor
    participant Registry as Windows Registry

    Caller->>Service: request sign(script)
    Service->>Config: get_value("SignScripts")
    Config->>Registry: check policy/preference precedence
    Registry-->>Config: value
    Config-->>Service: signing enabled?

    alt SignScripts == True
        Service->>Config: get_value("CodeSigningCertThumbprint")
        Config->>Registry: read thumbprint (policy/preference)
        Registry-->>Config: thumbprint / none
        alt thumbprint present
            Service->>PowerShell: locate cert in Cert:\CurrentUser\My by thumbprint
            PowerShell-->>Service: cert found / not found
            alt cert found
                Service->>PowerShell: Sign-File with found cert
                PowerShell-->>Service: success
            else
                Service-->>Caller: error (certificate not found)
            end
        else if cert_path provided
            Service->>PowerShell: Sign using provided .pfx path (may require password)
            PowerShell-->>Service: success / failure
        else
            Service->>PowerShell: auto-detect code-signing cert in stores and sign
            PowerShell-->>Service: success / failure
        end
    else
        Service-->>Caller: skip signing
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.67% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'improved GPO & intune templates' accurately reflects the primary changes: enhancements to Group Policy Object (GPO) and Intune policy definitions across multiple ADMX/ADML files, README documentation, and configuration handling.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch improved-gpo

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 documentation Improvements or additions to documentation backend labels Dec 23, 2025
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: 8

Caution

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

⚠️ Outside diff range comments (2)
README.md (1)

16-17: Duplicate heading detected.

Line 16 and 17 both contain ## 📚 Documentation. Remove the duplicate on line 17.

🔎 Proposed fix
 ## 📚 Documentation
-## 📚 Documentation
 - [**✨ Features & Analysis**](docs/FEATURES.md): Detailed breakdown of supported installers and analysis capabilities.
docs/PolicyDefinitions/README.md (1)

82-85: Precedence documentation inconsistency.

The precedence order here (lines 82-85) shows HKLM Preference before HKCU Preference, but the updated config.py now checks HKCU Preference (User) before HKLM Preference (Machine). Update to match the code:

🔎 Proposed fix
 1. **Machine Policy** (`HKLM\Software\Policies\FaserF\SwitchCraft`) - *Highest Priority (Intune/GPO)*
 2. **User Policy** (`HKCU\Software\Policies\FaserF\SwitchCraft`) - *High Priority (Intune/GPO)*
-3. **Machine Preference** (`HKLM\Software\FaserF\SwitchCraft`)
-4. **User Preference** (`HKCU\Software\FaserF\SwitchCraft`) - *Default User Settings*
+3. **User Preference** (`HKCU\Software\FaserF\SwitchCraft`) - *User Settings*
+4. **Machine Preference** (`HKLM\Software\FaserF\SwitchCraft`) - *Admin Default*
🧹 Nitpick comments (2)
src/switchcraft/services/signing_service.py (1)

85-103: Consider capturing both stdout and stderr for debugging.

When signing fails, line 98 logs stdout as a warning, but stderr (line 97) is only logged if non-empty. The condition completed.returncode == 0 and "Signed Successfully" is good, but failed signing may have useful info in stdout even when stderr is empty.

🔎 Suggested improvement
             if completed.returncode == 0 and "Signed Successfully" in completed.stdout:
                 logger.info(f"Successfully signed {script_path_obj.name}")
                 return True
             else:
-                if completed.stderr:
-                    logger.error(f"Signing Error: {completed.stderr.strip()}")
-                logger.warning(f"Signing Output: {completed.stdout.strip()}")
+                logger.error(f"Signing failed for {script_path_obj.name}")
+                if completed.stdout.strip():
+                    logger.warning(f"Signing stdout: {completed.stdout.strip()}")
+                if completed.stderr.strip():
+                    logger.error(f"Signing stderr: {completed.stderr.strip()}")
                 return False
docs/PolicyDefinitions/de-DE/SwitchCraft.adml (1)

1-137: Consider native speaker review for translation quality.

While the German translations appear technically accurate and the overall structure is excellent, consider having a native German speaker review the localization to ensure the terminology and phrasing align with German IT/enterprise conventions.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d43740b and 78bd164.

📒 Files selected for processing (9)
  • README.md
  • docs/PolicyDefinitions/README.md
  • docs/PolicyDefinitions/SwitchCraft.admx
  • docs/PolicyDefinitions/de-DE/SwitchCraft.adml
  • docs/PolicyDefinitions/en-US/SwitchCraft.adml
  • src/switchcraft/gui/views/intune_view.py
  • src/switchcraft/gui/views/settings_view.py
  • src/switchcraft/services/signing_service.py
  • src/switchcraft/utils/config.py
🧰 Additional context used
🧬 Code graph analysis (3)
src/switchcraft/services/signing_service.py (1)
src/switchcraft/utils/config.py (1)
  • get_value (23-60)
src/switchcraft/gui/views/settings_view.py (1)
src/switchcraft/utils/config.py (2)
  • is_managed (63-82)
  • get_value (23-60)
src/switchcraft/gui/views/intune_view.py (1)
src/switchcraft/utils/config.py (1)
  • get_value (23-60)
🪛 GitHub Actions: CI Orchestrator
src/switchcraft/gui/views/settings_view.py

[error] 1046-1046: E701: Multiple statements on one line (colon).

🪛 markdownlint-cli2 (0.18.1)
docs/PolicyDefinitions/README.md

17-17: Multiple headings with the same content

(MD024, no-duplicate-heading)

🔇 Additional comments (14)
README.md (1)

123-124: New feature documentation looks good.

The Cloud Sync and Script Signing features are documented concisely and align with the implementation changes in this PR.

src/switchcraft/utils/config.py (1)

71-82: is_managed implementation is sound.

The method correctly checks only the Policy paths (not Preference paths) to determine if a setting is enforced. The broad exception handler on line 79 silently ignores errors, which is acceptable here since the method returns a safe default (False).

docs/PolicyDefinitions/SwitchCraft.admx (2)

15-63: Category hierarchy is well-structured.

The Enforced/Defaults branching with subcategories (Updates, General, AI, Security, Intune) provides clear organization for both mandatory policies and default settings.


158-164: Security consideration: Client Secret stored in registry.

GraphClientSecret is stored as plain text in the registry. While the ADML includes a warning, consider:

  1. Documenting that the secret should have minimal permissions (read-only app registration)
  2. Recommending certificate-based authentication for production environments

This is noted in the help text, so no action required, but worth highlighting for enterprise deployments.

src/switchcraft/gui/views/settings_view.py (1)

1067-1069: tkraise override is a good pattern for refreshing managed state.

Re-checking managed settings when the view is raised ensures the UI reflects policy changes if they occurred while another tab was active.

src/switchcraft/services/signing_service.py (1)

33-44: Thumbprint-based signing implementation is solid.

The logic correctly:

  1. Prioritizes policy-configured thumbprint
  2. Searches CurrentUser store first, then LocalMachine
  3. Provides clear error messaging if certificate not found
docs/PolicyDefinitions/en-US/SwitchCraft.adml (2)

92-95: Good security warning for GraphClientSecret.

The help text appropriately warns about registry storage security considerations.


99-133: Presentation elements are correctly defined.

All presentation IDs match the corresponding ADMX policy references, and the UI element bindings (dropdowns, textboxes) are properly configured.

src/switchcraft/gui/views/intune_view.py (1)

113-117: Credential key renaming is consistent.

The keys now match the policy definitions (GraphTenantId, GraphClientId, GraphClientSecret) and are used consistently in both _show_intune_form (lines 113-115) and _run_upload (lines 256-258).

Consider documenting the migration path for users who had previously configured IntuneTenantID/IntuneClientId/IntuneClientSecret. Existing configurations will silently stop working.

#!/bin/bash
# Check if old key names are still referenced anywhere
rg -n "IntuneTenantID|IntuneClientId|IntuneClientSecret" --type py
docs/PolicyDefinitions/de-DE/SwitchCraft.adml (5)

14-15: LGTM: Well-organized category structure.

The new category hierarchy (Enforced/Defaults/General/AI/Security/Intune) provides clear organization for policy management. The German translations are appropriate, and special characters are properly escaped.

Also applies to: 31-34


52-63: LGTM: General settings are well-defined.

The Winget integration, language selection, and Git repository path configurations are clearly documented with appropriate help text. The German translations are technically accurate.


66-74: LGTM: AI provider configuration is well-documented.

The AI provider options are clearly explained with privacy and API key requirements noted. The distinction between local (private) and cloud-based providers is appropriate.


77-83: LGTM: Script signing configuration is secure and well-documented.

The certificate thumbprint approach is appropriate for code signing, and the help text correctly references the certificate store location. The override behavior is clearly explained.


103-133: LGTM: Presentation bindings are complete and correct.

All new policy strings have corresponding presentation entries with appropriate control types (dropdowns for selections, text boxes for input). The German labels are contextually appropriate, and the bindings maintain consistency with the string table.

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 (2)
src/switchcraft/gui/views/settings_view.py (2)

1032-1043: Widget attribute names still don't match actual widget references.

This is the same issue flagged in the previous review. The managed_widgets dictionary references attributes that don't exist:

  • chk_winget → actual widget is winget_var (BooleanVar at line 62); the CTkSwitch at line 69 has no stored reference
  • combo_lang → no stored reference; OptionMenu created inline at line 83
  • combo_theme → no stored reference; OptionMenu created inline at line 95
  • combo_ai_provider → should be ai_provider (correct name at line 549)
  • combo_channel → should be channel_opt (correct name at line 394)

Since getattr(..., None) returns None for missing attributes, policy enforcement silently fails for most settings.

Based on learnings from past reviews, this issue must be addressed to enable policy enforcement.


1045-1046: Fix E701 style violation: multiple statements on one line.

This is the same formatting issue flagged in the previous review.

🔎 Proposed fix
         for key, widget in managed_widgets.items():
-            if not widget: continue
+            if not widget:
+                continue
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78bd164 and 6d67d03.

📒 Files selected for processing (1)
  • src/switchcraft/gui/views/settings_view.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/switchcraft/gui/views/settings_view.py (1)
src/switchcraft/utils/config.py (3)
  • SwitchCraftConfig (9-239)
  • is_managed (63-82)
  • get_value (23-60)
⏰ 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: build / Build Installer (windows-latest)
🔇 Additional comments (2)
src/switchcraft/gui/views/settings_view.py (2)

48-49: LGTM!

The initial scheduled check for managed settings is appropriately placed after UI setup, allowing widgets to initialize before enforcement.


1068-1070: LGTM!

The tkraise override correctly preserves base behavior and re-checks managed settings when the view is brought to the foreground, ensuring policy enforcement stays current.

@github-actions github-actions bot added the ci-cd label Dec 23, 2025
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

🧹 Nitpick comments (2)
tests/test_config_secure.py (1)

10-13: Test silently skips if SwitchCraftConfig import fails.

The bare except ImportError: pass means that if the module fails to import, the test class will be defined but all test methods will silently pass without actually testing anything. This can mask real import/dependency issues.

🔎 Recommended fix
 try:
     from switchcraft.utils.config import SwitchCraftConfig
 except ImportError:
-    pass
+    import unittest
+    raise unittest.SkipTest("SwitchCraftConfig not available")

Or use unittest.skipIf decorator on the test class:

try:
    from switchcraft.utils.config import SwitchCraftConfig
    SWITCHCRAFT_AVAILABLE = True
except ImportError:
    SWITCHCRAFT_AVAILABLE = False

@unittest.skipIf(not SWITCHCRAFT_AVAILABLE, "SwitchCraftConfig not available")
class TestSecureConfig(unittest.TestCase):
    ...
src/switchcraft/gui/views/settings_view.py (1)

1055-1077: Consider narrowing exception handling for better error visibility.

The broad except Exception: pass at line 1076 will silently catch all errors when configuring managed widgets, including AttributeErrors if widget APIs change or TypeErrors from unexpected values. This can make debugging difficult.

🔎 Suggested refinement
             if SwitchCraftConfig.is_managed(key):
                 try:
                     widget.configure(state="disabled")
                     if isinstance(widget, ctk.CTkEntry):
                          val = SwitchCraftConfig.get_value(key, "")
                          widget.delete(0, "end")
                          widget.insert(0, str(val))
                     elif isinstance(widget, ctk.CTkSwitch):
                          val = SwitchCraftConfig.get_value(key, 0)
                          # Ensure integer/bool
                          if str(val).lower() in ("true", "1", "yes"):
                              widget.select()
                          else:
                              widget.deselect()
                     elif isinstance(widget, (ctk.CTkOptionMenu, ctk.CTkComboBox, ctk.CTkSegmentedButton)):
                          val = SwitchCraftConfig.get_value(key, "")
                          widget.set(str(val))
-                except Exception:
-                    pass
+                except (AttributeError, TypeError) as e:
+                    logger.warning(f"Failed to configure managed widget for '{key}': {e}")

This logs issues while preventing crashes, making it easier to diagnose widget API changes or configuration problems.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d67d03 and 58e53ab.

📒 Files selected for processing (7)
  • docs/PolicyDefinitions/README.md
  • docs/PolicyDefinitions/en-US/SwitchCraft.adml
  • src/switchcraft/gui/views/intune_view.py
  • src/switchcraft/gui/views/settings_view.py
  • src/switchcraft/services/signing_service.py
  • src/switchcraft/utils/config.py
  • tests/test_config_secure.py
🧰 Additional context used
🧬 Code graph analysis (4)
src/switchcraft/services/signing_service.py (1)
src/switchcraft/utils/config.py (1)
  • get_value (24-61)
src/switchcraft/gui/views/intune_view.py (1)
src/switchcraft/utils/config.py (2)
  • get_value (24-61)
  • get_secure_value (173-227)
tests/test_config_secure.py (1)
src/switchcraft/utils/config.py (1)
  • get_secure_value (173-227)
src/switchcraft/gui/views/settings_view.py (1)
src/switchcraft/utils/config.py (4)
  • get_secure_value (173-227)
  • get_value (24-61)
  • set_secret (240-254)
  • is_managed (64-83)
🔇 Additional comments (7)
src/switchcraft/gui/views/intune_view.py (1)

113-115: LGTM! Credential handling updated correctly.

The migration from IntuneTenantID/IntuneClientId/IntuneClientSecret to GraphTenantId/GraphClientId/GraphClientSecret is consistent with the broader policy changes. Using get_secure_value for the client secret properly implements secure credential retrieval with policy precedence and keyring fallback.

docs/PolicyDefinitions/README.md (1)

46-61: Comprehensive OMA-URI configuration documentation.

The consolidated base URI approach with the detailed settings table makes Intune configuration much clearer. The inclusion of GraphClientSecret in the table addresses the previous gap, and the example XML for bulk import is helpful for administrators.

src/switchcraft/gui/views/settings_view.py (1)

789-796: Secure credential loading implemented correctly.

The conditional handling for GraphClientSecret using get_secure_value versus standard get_value for other fields properly implements the secure credential retrieval pattern. This ensures secrets are loaded from the keyring (or policy) with appropriate precedence.

src/switchcraft/services/signing_service.py (1)

36-47: Thumbprint-based signing provides robust certificate management.

The policy-driven thumbprint approach properly searches both CurrentUser\My and LocalMachine\My stores, providing clear fallback behavior. The explicit error message when the certificate isn't found (line 46) helps administrators diagnose deployment issues quickly.

src/switchcraft/utils/config.py (1)

208-220: Migration logic safely transitions secrets to keyring.

The migration path (lines 210-220) properly:

  • Logs the migration event
  • Stores the secret in the keyring
  • Attempts to delete the legacy registry entry
  • Gracefully handles deletion failures with a warning
  • Returns the migrated value

This ensures secrets are moved to more secure storage while maintaining availability during the transition.

docs/PolicyDefinitions/en-US/SwitchCraft.adml (2)

86-95: Important security warning for GraphClientSecret deployment.

The help text appropriately warns administrators that GraphClientSecret is stored in the registry when deployed via policy. This is crucial information since registry values are less secure than keyring storage. Consider also documenting recommended ACL restrictions in the main README.


103-133: UI presentation bindings are complete and well-structured.

All new policies have corresponding presentation definitions with appropriate control types (dropdownList for selections, textBox for free-form input). The refId attributes correctly map to their policy definitions, ensuring proper rendering in the Group Policy Editor.

@github-actions github-actions bot merged commit b81f64e into main Dec 23, 2025
9 of 10 checks passed
@github-actions github-actions bot deleted the improved-gpo branch December 23, 2025 10:00
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: 4

♻️ Duplicate comments (2)
.github/workflows/review-auto-merge.yml (2)

41-51: Same bot check pattern issue as ci-orchestrator.yml.

The *[bot] pattern matching has the same issue here. See the comment on ci-orchestrator.yml for the fix.


55-56: Same overly broad skip detection issue.

The test("skip"; "i") regex is too permissive. See the comment on ci-orchestrator.yml for the recommended fix.

🧹 Nitpick comments (3)
.github/workflows/ci-orchestrator.yml (1)

64-120: Consider extracting shared auto-merge logic.

The auto-merge logic here duplicates nearly identical code in review-auto-merge.yml. Consider extracting this into a reusable composite action or shared script to maintain DRY principles.

.github/workflows/review-auto-merge.yml (1)

3-5: Workflow triggers on all review types.

This workflow runs on any review submission, including "REQUEST_CHANGES" and "COMMENT" reviews which will never lead to a merge. Consider filtering to only run on approving reviews to save CI minutes.

🔎 Proposed fix
 on:
   pull_request_review:
     types: [submitted]
+
+jobs:
+  auto-merge-review:
+    runs-on: ubuntu-latest
+    if: github.event.review.state == 'approved'
docs/PolicyDefinitions/README.md (1)

19-19: Security warning for GraphClientSecret could be stronger.

While "(Use with caution)" is noted, consider adding explicit guidance that secrets in GPO/registry are stored in plaintext and recommending secure alternatives (like Azure Key Vault, Managed Identity, or the application's keyring storage) for production environments.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58e53ab and 4cfa4d3.

📒 Files selected for processing (10)
  • .github/workflows/auto-merge.yml
  • .github/workflows/ci-orchestrator.yml
  • .github/workflows/lint.yml
  • .github/workflows/review-auto-merge.yml
  • README.md
  • docs/PolicyDefinitions/README.md
  • docs/upgrade.md
  • src/switchcraft/gui/views/settings_view.py
  • src/switchcraft/utils/config.py
  • tests/test_config_secure.py
💤 Files with no reviewable changes (1)
  • .github/workflows/auto-merge.yml
✅ Files skipped from review due to trivial changes (1)
  • docs/upgrade.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/switchcraft/utils/config.py
🧰 Additional context used
🧬 Code graph analysis (2)
src/switchcraft/gui/views/settings_view.py (1)
src/switchcraft/utils/config.py (4)
  • get_secure_value (173-227)
  • get_value (24-61)
  • set_secret (240-254)
  • is_managed (64-83)
tests/test_config_secure.py (1)
src/switchcraft/utils/config.py (1)
  • get_secure_value (173-227)
⏰ 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: build / Build Installer (windows-latest)
🔇 Additional comments (7)
README.md (1)

123-124: LGTM!

New feature documentation for Cloud Sync and Script Signing accurately reflects the functionality added in this PR.

tests/test_config_secure.py (2)

14-46: Test setup is well-structured.

Good use of patching for keyring and winreg to enable cross-platform testing of Windows-specific functionality. The setUp/tearDown pattern ensures proper cleanup.


83-101: The winreg mocking is correct and the test passes.

The mock setup via sys.modules patching correctly intercepts the winreg import inside get_secure_value(), and the HKEY_CURRENT_USER constant (value 2) is properly defined in setUp() and used consistently in the side_effect comparison. The test successfully validates that the migration logic works as expected. No changes needed.

docs/PolicyDefinitions/README.md (1)

7-19: Documentation updates look good.

The policy table is comprehensive, OMA-URI paths are correctly documented, and the GraphClientSecret entry (line 60) addresses the previous review comment. The masked value example (*****) is appropriate for secrets.

Also applies to: 48-61

src/switchcraft/gui/views/settings_view.py (3)

789-796: Good implementation of secure credential handling.

The differentiation between GraphClientSecret (using get_secure_value) and other fields (using get_value) correctly implements the secure value precedence from config.py.


1042-1053: Widget mapping addresses previous review feedback.

The managed_widgets dictionary now correctly references the stored widget attributes (winget_switch, lang_menu, theme_menu, ai_provider, channel_opt) instead of the non-existent names flagged in the previous review.


1079-1081: Good approach for re-checking managed settings on view raise.

Calling _check_managed_settings when the view is raised ensures policy enforcement is re-evaluated when navigating back to settings, which handles cases where policies might change during app runtime.

Comment on lines +92 to +103
if [[ "$PR_AUTHOR" == *"[bot]" ]]; then
echo "PR Author is a Bot. Allowed."
else
PERM=$(gh api "repos/$REPO/collaborators/$PR_AUTHOR/permission" --jq '.permission')
echo "Author Permission: $PERM"
if [[ "$PERM" == "admin" || "$PERM" == "maintain" || "$PERM" == "write" ]]; then
echo "Author has trusted permission ($PERM). Allowed."
else
echo "::notice::PR Author ($PR_AUTHOR) does not have sufficient permissions ($PERM) for auto-merge. Skipping."
exit 0
fi
fi
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

Bot check pattern matching is incorrect.

The pattern *[bot] in bash [[ ]] is a glob pattern, but it's case-sensitive and might not match all bot formats (e.g., dependabot[bot]). The * before [bot] should work, but consider using a suffix check for clarity.

🔎 Proposed fix
-          if [[ "$PR_AUTHOR" == *"[bot]" ]]; then
+          if [[ "$PR_AUTHOR" == *\[bot\] ]]; then

Or for more robust matching:

-          if [[ "$PR_AUTHOR" == *"[bot]" ]]; then
+          if [[ "$PR_AUTHOR" =~ \[bot\]$ ]]; then
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [[ "$PR_AUTHOR" == *"[bot]" ]]; then
echo "PR Author is a Bot. Allowed."
else
PERM=$(gh api "repos/$REPO/collaborators/$PR_AUTHOR/permission" --jq '.permission')
echo "Author Permission: $PERM"
if [[ "$PERM" == "admin" || "$PERM" == "maintain" || "$PERM" == "write" ]]; then
echo "Author has trusted permission ($PERM). Allowed."
else
echo "::notice::PR Author ($PR_AUTHOR) does not have sufficient permissions ($PERM) for auto-merge. Skipping."
exit 0
fi
fi
if [[ "$PR_AUTHOR" == *\[bot\] ]]; then
echo "PR Author is a Bot. Allowed."
else
PERM=$(gh api "repos/$REPO/collaborators/$PR_AUTHOR/permission" --jq '.permission')
echo "Author Permission: $PERM"
if [[ "$PERM" == "admin" || "$PERM" == "maintain" || "$PERM" == "write" ]]; then
echo "Author has trusted permission ($PERM). Allowed."
else
echo "::notice::PR Author ($PR_AUTHOR) does not have sufficient permissions ($PERM) for auto-merge. Skipping."
exit 0
fi
fi
🤖 Prompt for AI Agents
.github/workflows/ci-orchestrator.yml around lines 92 to 103: the bot author
check uses a case-sensitive glob `*"[bot]"` which can miss variants; convert
PR_AUTHOR to lowercase and perform a suffix match against "[bot]" (e.g., use
bash parameter expansion `${PR_AUTHOR,,}` then `[[ "${PR_AUTHOR,,}" == *"[bot]"
]]`) so matching is robust and clear; ensure the variable is quoted to avoid
word-splitting and preserve existing logic for bot authors.

Comment on lines +107 to +117
IS_APPROVED=$(gh api "repos/$REPO/pulls/$PR_NUMBER/reviews" --jq '.[] | select(.user.login == "coderabbitai[bot]" and .state == "APPROVED") | .state' | head -n 1)
HAS_SKIP_COMMENT=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" --jq '.[] | select(.user.login == "coderabbitai[bot]" and (.body | test("skip"; "i"))) | .id' | head -n 1)
if [[ -n "$IS_APPROVED" ]]; then
echo "Coderabbit APPROVED."
elif [[ -n "$HAS_SKIP_COMMENT" ]]; then
echo "Coderabbit SKIPPED review."
else
echo "::notice::Coderabbit has not approved or skipped yet. Waiting."
exit 0
fi
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

Overly broad "skip" detection.

The regex test("skip"; "i") matches any comment containing "skip" anywhere (e.g., "I'll skip this review", "Please don't skip tests"). This could cause unintended auto-merges.

🔎 Proposed fix

Consider matching a specific pattern like @coderabbitai skip or a dedicated label:

-          HAS_SKIP_COMMENT=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" --jq '.[] | select(.user.login == "coderabbitai[bot]" and (.body | test("skip"; "i"))) | .id' | head -n 1)
+          HAS_SKIP_COMMENT=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" --jq '.[] | select(.user.login == "coderabbitai[bot]" and (.body | test("<!-- skip_review -->|\\[skip\\]"; "i"))) | .id' | head -n 1)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
IS_APPROVED=$(gh api "repos/$REPO/pulls/$PR_NUMBER/reviews" --jq '.[] | select(.user.login == "coderabbitai[bot]" and .state == "APPROVED") | .state' | head -n 1)
HAS_SKIP_COMMENT=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" --jq '.[] | select(.user.login == "coderabbitai[bot]" and (.body | test("skip"; "i"))) | .id' | head -n 1)
if [[ -n "$IS_APPROVED" ]]; then
echo "Coderabbit APPROVED."
elif [[ -n "$HAS_SKIP_COMMENT" ]]; then
echo "Coderabbit SKIPPED review."
else
echo "::notice::Coderabbit has not approved or skipped yet. Waiting."
exit 0
fi
IS_APPROVED=$(gh api "repos/$REPO/pulls/$PR_NUMBER/reviews" --jq '.[] | select(.user.login == "coderabbitai[bot]" and .state == "APPROVED") | .state' | head -n 1)
HAS_SKIP_COMMENT=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" --jq '.[] | select(.user.login == "coderabbitai[bot]" and (.body | test("<!-- skip_review -->|\\[skip\\]"; "i"))) | .id' | head -n 1)
if [[ -n "$IS_APPROVED" ]]; then
echo "Coderabbit APPROVED."
elif [[ -n "$HAS_SKIP_COMMENT" ]]; then
echo "Coderabbit SKIPPED review."
else
echo "::notice::Coderabbit has not approved or skipped yet. Waiting."
exit 0
fi
🤖 Prompt for AI Agents
.github/workflows/ci-orchestrator.yml lines 107-117: the current skip detection
uses a broad regex that matches any occurrence of "skip"; change it to detect a
specific, unambiguous signal such as the exact phrase "@coderabbitai skip"
(case-insensitive) or a dedicated PR label instead of any "skip" substring.
Update the GH comments query to only consider comments where the body matches
that exact mention/phrase (or alternatively call the labels endpoint and check
for a specific label name) so accidental uses of the word "skip" don't trigger
auto-merge.

Comment on lines +25 to +33
- name: Commit and Push Fixes
if: always()
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add src/
if ! git diff --cached --quiet; then
git commit -m "style: auto-fix ruff linting issues"
git push
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

Auto-commit step has potential issues.

  1. if: always() runs even when prior steps fail (e.g., Python setup fails), which would cause the git commands to fail or behave unexpectedly.

  2. Risk of infinite commit loop: pushing a commit triggers a new workflow run, which could auto-fix and push again. Consider adding [skip ci] to the commit message or checking github.actor to skip bot commits.

  3. For PRs from forks, this step will fail since GITHUB_TOKEN lacks write access to the fork's branch.

🔎 Proposed fix
       - name: Commit and Push Fixes
-        if: always()
+        if: success() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
         run: |
           git config --global user.name "github-actions[bot]"
           git config --global user.email "github-actions[bot]@users.noreply.github.com"
           git add src/
           if ! git diff --cached --quiet; then
-            git commit -m "style: auto-fix ruff linting issues"
+            git commit -m "style: auto-fix ruff linting issues [skip ci]"
             git push
           fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Commit and Push Fixes
if: always()
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add src/
if ! git diff --cached --quiet; then
git commit -m "style: auto-fix ruff linting issues"
git push
- name: Commit and Push Fixes
if: success() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add src/
if ! git diff --cached --quiet; then
git commit -m "style: auto-fix ruff linting issues [skip ci]"
git push

Comment on lines +1059 to +1077
if SwitchCraftConfig.is_managed(key):
try:
widget.configure(state="disabled")
if isinstance(widget, ctk.CTkEntry):
val = SwitchCraftConfig.get_value(key, "")
widget.delete(0, "end")
widget.insert(0, str(val))
elif isinstance(widget, ctk.CTkSwitch):
val = SwitchCraftConfig.get_value(key, 0)
# Ensure integer/bool
if str(val).lower() in ("true", "1", "yes"):
widget.select()
else:
widget.deselect()
elif isinstance(widget, (ctk.CTkOptionMenu, ctk.CTkComboBox, ctk.CTkSegmentedButton)):
val = SwitchCraftConfig.get_value(key, "")
widget.set(str(val))
except Exception as e:
logger.error(f"Failed to apply managed setting for {key}: {e}")
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

Potential issue with modifying disabled CTkEntry widgets.

On lines 1064-1065, widget.delete() and widget.insert() are called after widget.configure(state="disabled") on line 1061. CustomTkinter's CTkEntry may not allow text modifications when disabled, which could silently fail or raise an exception.

🔎 Proposed fix

Modify the widget content before disabling:

         if SwitchCraftConfig.is_managed(key):
             try:
-                widget.configure(state="disabled")
                 if isinstance(widget, ctk.CTkEntry):
+                     widget.configure(state="normal")
                      val = SwitchCraftConfig.get_value(key, "")
                      widget.delete(0, "end")
                      widget.insert(0, str(val))
+                     widget.configure(state="disabled")
                 elif isinstance(widget, ctk.CTkSwitch):
+                     widget.configure(state="disabled")
                      val = SwitchCraftConfig.get_value(key, 0)
                      # Ensure integer/bool
                      if str(val).lower() in ("true", "1", "yes"):
                          widget.select()
                      else:
                          widget.deselect()
                 elif isinstance(widget, (ctk.CTkOptionMenu, ctk.CTkComboBox, ctk.CTkSegmentedButton)):
                      val = SwitchCraftConfig.get_value(key, "")
                      widget.set(str(val))
+                     widget.configure(state="disabled")
+                else:
+                     widget.configure(state="disabled")
             except Exception as e:
                 logger.error(f"Failed to apply managed setting for {key}: {e}")
🤖 Prompt for AI Agents
In src/switchcraft/gui/views/settings_view.py around lines 1059 to 1077, the
code disables widgets before updating CTkEntry contents which can prevent or
error on text modifications; change the order so you update the CTkEntry's value
first (delete/insert) and then call widget.configure(state="disabled"), or
alternatively set the entry to normal state, perform delete/insert, and then
disable it; keep the existing handling for CTkSwitch and option widgets as-is,
and ensure the try/except remains to log any remaining failures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend ci-cd documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants