Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions .github/workflows/auto-merge.yml

This file was deleted.

60 changes: 60 additions & 0 deletions .github/workflows/ci-orchestrator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ jobs:
fi

lint:
permissions:
contents: write
uses: ./.github/workflows/lint.yml

test-backend:
Expand All @@ -58,3 +60,61 @@ jobs:
build:
needs: [test-backend]
uses: ./.github/workflows/build.yml

auto-merge:
needs: [build]
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
permissions:
contents: write
pull-requests: write
checks: read
issues: read
steps:
- uses: actions/checkout@v6

- name: Check Criteria and Merge
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
echo "Processing PR #$PR_NUMBER for Auto-Merge..."

# 1. Check CI Status
# Implicitly passed because this job 'needs' build/test-backend.
echo "CI Orchestrator pipeline passed."

# 2. Check PR Author Permissions (Security Restriction)
PR_AUTHOR=$(gh pr view "$PR_NUMBER" --json author --jq '.author.login')
echo "PR Author: $PR_AUTHOR"

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
Comment on lines +92 to +103
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.


# 3. Check Coderabbit Status
echo "Checking Coderabbit status..."
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
Comment on lines +107 to +117
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.


# 4. Attempt Merge
gh pr merge "$PR_NUMBER" --merge --auto --delete-branch
15 changes: 14 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.head_ref || github.ref }}

- name: Set up Python
uses: actions/setup-python@v6
Expand All @@ -18,4 +20,15 @@ jobs:
run: pip install ruff

- name: Run Ruff (Backend)
run: ruff check src/
run: ruff check --fix --exit-zero src/

- 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
Comment on lines +25 to +33
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

fi
68 changes: 68 additions & 0 deletions .github/workflows/review-auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Auto Merge (Review Listener)

on:
pull_request_review:
types: [submitted]

permissions:
contents: write
pull-requests: write
checks: read
issues: read
actions: read

jobs:
auto-merge-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Check Criteria and Merge
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
echo "Processing PR #$PR_NUMBER for Auto-Merge (Trigger: Review)..."

# 1. Check CI Status (Must manually verify CI Orchestrator passed)
echo "Checking CI Status for PR #$PR_NUMBER..."
PR_HEAD_SHA=$(gh pr view "$PR_NUMBER" --json headRefOid --jq '.headRefOid')
CI_CONCLUSION=$(gh run list --commit "$PR_HEAD_SHA" --workflow "CI Orchestrator" --json conclusion --jq '.[0].conclusion')

if [[ "$CI_CONCLUSION" != "success" ]]; then
echo "::notice::CI Orchestrator is not 'success' (current: $CI_CONCLUSION). Skipping merge."
exit 0
fi
echo "CI Orchestrator passed ($CI_CONCLUSION)."

# 2. Check PR Author Permissions
PR_AUTHOR=$(gh pr view "$PR_NUMBER" --json author --jq '.author.login')
if [[ "$PR_AUTHOR" == *"[bot]" ]]; then
echo "PR Author is a Bot. Allowed."
else
PERM=$(gh api "repos/$REPO/collaborators/$PR_AUTHOR/permission" --jq '.permission')
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. Skipping."
exit 0
fi
fi

# 3. Check Coderabbit Status
echo "Checking Coderabbit status..."
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

# 4. Attempt Merge
gh pr merge "$PR_NUMBER" --merge --auto --delete-branch
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
> **Active development is for Windows only.** Linux and macOS builds are untested but available. Bug reports for other platforms are welcome!

## 📚 Documentation
## 📚 Documentation

- [**✨ Features & Analysis**](docs/FEATURES.md): Detailed breakdown of supported installers and analysis capabilities.
- [**🤖 CLI Reference**](docs/CLI_Reference.md): Command-line usage, JSON output, and headless operation.
- [**🏗️ CI Architecture**](docs/CI_Architecture.md): Build process, pip structure, and testing guide.
Expand Down Expand Up @@ -120,6 +120,8 @@ switchcraft --install-addons=advanced,winget
- **Analysis History**: Keep track of your last 100 analyzed installers.
- **Winget Toggle**: Easily enable/disable store integration to suit your workflow.
- **Enhanced AI**: Support for local AI, Gemini (Free tier), and OpenAI.
- **Cloud Sync**: Sync your configuration and settings across devices using GitHub Gists.
- **Script Signing**: Automatically sign generated PowerShell scripts with your Code Signing Certificate.

## 🛠️ Building from Source
SwitchCraft includes helper scripts to easily build release executables for your platform.
Expand Down
61 changes: 39 additions & 22 deletions docs/PolicyDefinitions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@ Administrative templates for managing SwitchCraft settings via Group Policy (GPO

## Available Policies

| Policy | Category | Description |
|--------|----------|-------------|
| **Enable Debug Logging** | SwitchCraft | Enable/disable verbose debug logging |
| **Update Channel** | Updates | Configure update channel (Stable/Beta/Dev) |
| Policy | Category | Description | Registry Value | Type |
|--------|----------|-------------|----------------|------|
| **Enable Debug Logging** | SwitchCraft | Enable/disable verbose debug logging | `DebugMode` | DWORD (0/1) |
| **Update Channel** | Updates | Configure update channel (Stable/Beta/Dev) | `UpdateChannel` | String |
| **Enable Winget** | General | Enable/disable Winget Store integration | `EnableWinget` | DWORD (0/1) |
| **Language** | General | Set application language (en/de) | `Language` | String |
| **Git Repo Path** | General | Path to local Git repository | `GitRepoPath` | String |
| **AI Provider** | AI | Backend provider (local, openai, gemini) | `AIProvider` | String |
| **Sign Scripts** | Security | Enable automatic script signing | `SignScripts` | DWORD (0/1) |
| **Cert Thumbprint** | Security | Thumbprint of Code Signing Certificate | `CodeSigningCertThumbprint` | String |
| **Tenant ID** | Intune | Azure AD Tenant ID | `GraphTenantId` | String |
| **Client ID** | Intune | Azure App Client ID | `GraphClientId` | String |
| **Client Secret** | Intune | Azure App Client Secret (Use with caution) | `GraphClientSecret` | String |

## Installation

Expand All @@ -29,26 +38,34 @@ Administrative templates for managing SwitchCraft settings via Group Policy (GPO
Method 1: **Settings Catalog** (Recommended for new policies)
1. Create a new Configuration Profile → Settings Catalog
2. Search for the registry path: `HKCU\Software\FaserF\SwitchCraft`
3. Add values manually:
- `DebugMode` (Integer): `1` = enabled, `0` = disabled
- `UpdateChannel` (String): `stable`, `beta`, or `dev`
3. Add values manually matching the table above.

Method 2: **Custom OMA-URI** (Preferred for Intune)

SwitchCraft fully supports Intune's custom OMA-URI policies that target the `Software\Policies` keys.

**Debug Mode Example:**
```
OMA-URI: ./User/Vendor/MSFT/Policy/Config/FaserF~Policy~SwitchCraft/DebugMode
Data type: Integer
Value: 1
```

**Update Channel Example:**
```
OMA-URI: ./User/Vendor/MSFT/Policy/Config/FaserF~Policy~SwitchCraft/UpdateChannel
Data type: String
Value: stable
The Base URI is: `./User/Vendor/MSFT/Policy/Config/FaserF~Policy~SwitchCraft`

| Setting | OMA-URI Path | Data Type | Value Example |
|---------|--------------|-----------|---------------|
| **Debug Mode** | `.../DebugMode` | Integer | `1` |
| **Update Channel** | `.../UpdateChannel` | String | `stable` |
| **Enable Winget** | `.../EnableWinget` | Integer | `1` |
| **Language** | `.../Language` | String | `en` |
| **Git Repo Path** | `.../GitRepoPath` | String | `C:\Repo\MyConfig` |
| **AI Provider** | `.../AIProvider` | String | `local` |
| **Sign Scripts** | `.../SignScripts` | Integer | `1` |
| **Cert Thumbprint** | `.../CodeSigningCertThumbprint` | String | `A1B2C3D4...` |
| **Tenant ID** | `.../GraphTenantId` | String | `00000000-0000...` |
| **Client ID** | `.../GraphClientId` | String | `00000000-0000...` |
| **Graph Client Secret** | `.../GraphClientSecret` | String | `*****` |

**Example XML for Bulk Import:**
```xml
<Row>
<OMAURI>./User/Vendor/MSFT/Policy/Config/FaserF~Policy~SwitchCraft/EnableWinget</OMAURI>
<DataType>Integer</DataType>
<Value>1</Value>
</Row>
```

Method 3: **ADMX Ingestion**
Expand All @@ -65,8 +82,8 @@ SwitchCraft reads configuration with the following precedence order:

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 Setting (Overrides Machine Default)*
4. **Machine Preference** (`HKLM\Software\FaserF\SwitchCraft`) - *Admin Default*

This ensures that Policies managed by your organization (via Intune/GPO) always override local user settings.

Expand Down
Loading