Skip to content

Commit 0715e88

Browse files
committed
add new debug mode
1 parent 0c83162 commit 0715e88

5 files changed

Lines changed: 440 additions & 2 deletions

File tree

.github/workflows/cleanup.yml

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
name: Repository Cleanup
2+
3+
on:
4+
schedule:
5+
# Run every Sunday at 3 AM UTC
6+
- cron: '0 3 * * 0'
7+
workflow_dispatch:
8+
inputs:
9+
dry_run:
10+
description: 'Dry run (show what would be deleted)'
11+
required: false
12+
type: boolean
13+
default: false
14+
max_age_days:
15+
description: 'Delete workflow runs older than X days'
16+
required: false
17+
type: number
18+
default: 30
19+
20+
permissions:
21+
contents: write
22+
actions: write
23+
24+
jobs:
25+
cleanup_workflow_runs:
26+
name: Cleanup Old Workflow Runs
27+
runs-on: ubuntu-latest
28+
steps:
29+
- name: Delete old workflow runs
30+
uses: Mattraks/delete-workflow-runs@v2
31+
with:
32+
token: ${{ secrets.GITHUB_TOKEN }}
33+
repository: ${{ github.repository }}
34+
retain_days: ${{ inputs.max_age_days || 30 }}
35+
keep_minimum_runs: 3
36+
delete_workflow_pattern: "all"
37+
38+
cleanup_caches:
39+
name: Cleanup Old Caches
40+
runs-on: ubuntu-latest
41+
steps:
42+
- name: Cleanup old caches
43+
uses: actions/github-script@v7
44+
with:
45+
script: |
46+
const caches = await github.rest.actions.getActionsCacheList({
47+
owner: context.repo.owner,
48+
repo: context.repo.repo,
49+
per_page: 100
50+
});
51+
52+
const now = new Date();
53+
const maxAge = 14 * 24 * 60 * 60 * 1000; // 14 days in ms
54+
let deleted = 0;
55+
56+
for (const cache of caches.data.actions_caches) {
57+
const cacheAge = now - new Date(cache.last_accessed_at);
58+
if (cacheAge > maxAge) {
59+
if (${{ inputs.dry_run || false }}) {
60+
console.log(`[DRY RUN] Would delete cache: ${cache.key} (${cache.size_in_bytes} bytes)`);
61+
} else {
62+
await github.rest.actions.deleteActionsCacheById({
63+
owner: context.repo.owner,
64+
repo: context.repo.repo,
65+
cache_id: cache.id
66+
});
67+
console.log(`Deleted cache: ${cache.key}`);
68+
deleted++;
69+
}
70+
}
71+
}
72+
console.log(`Deleted ${deleted} old caches`);
73+
74+
cleanup_stale_branches:
75+
name: Cleanup Stale Branches
76+
runs-on: ubuntu-latest
77+
steps:
78+
- uses: actions/checkout@v6
79+
with:
80+
fetch-depth: 0
81+
82+
- name: Delete merged branches
83+
run: |
84+
echo "Checking for merged branches to delete..."
85+
86+
# Get all remote branches except main, master, develop
87+
BRANCHES=$(git branch -r --merged origin/main | grep -v 'main\|master\|develop\|HEAD' | sed 's/origin\///')
88+
89+
if [ -z "$BRANCHES" ]; then
90+
echo "No stale branches found."
91+
exit 0
92+
fi
93+
94+
for branch in $BRANCHES; do
95+
if [ "${{ inputs.dry_run }}" = "true" ]; then
96+
echo "[DRY RUN] Would delete branch: $branch"
97+
else
98+
echo "Deleting merged branch: $branch"
99+
git push origin --delete "$branch" || echo "Could not delete $branch"
100+
fi
101+
done
102+
103+
cleanup_draft_releases:
104+
name: Cleanup Old Draft Releases
105+
runs-on: ubuntu-latest
106+
steps:
107+
- name: Delete old draft releases
108+
uses: actions/github-script@v7
109+
with:
110+
script: |
111+
const releases = await github.rest.repos.listReleases({
112+
owner: context.repo.owner,
113+
repo: context.repo.repo,
114+
per_page: 100
115+
});
116+
117+
const now = new Date();
118+
const maxAge = 7 * 24 * 60 * 60 * 1000; // 7 days
119+
let deleted = 0;
120+
121+
for (const release of releases.data) {
122+
if (release.draft) {
123+
const releaseAge = now - new Date(release.created_at);
124+
if (releaseAge > maxAge) {
125+
if (${{ inputs.dry_run || false }}) {
126+
console.log(`[DRY RUN] Would delete draft release: ${release.name || release.tag_name}`);
127+
} else {
128+
await github.rest.repos.deleteRelease({
129+
owner: context.repo.owner,
130+
repo: context.repo.repo,
131+
release_id: release.id
132+
});
133+
console.log(`Deleted draft release: ${release.name || release.tag_name}`);
134+
deleted++;
135+
}
136+
}
137+
}
138+
}
139+
console.log(`Deleted ${deleted} old draft releases`);
140+
141+
summary:
142+
name: Cleanup Summary
143+
runs-on: ubuntu-latest
144+
needs: [cleanup_workflow_runs, cleanup_caches, cleanup_stale_branches, cleanup_draft_releases]
145+
if: always()
146+
steps:
147+
- name: Summary
148+
run: |
149+
echo "## 🧹 Repository Cleanup Complete" >> $GITHUB_STEP_SUMMARY
150+
echo "" >> $GITHUB_STEP_SUMMARY
151+
echo "| Task | Status |" >> $GITHUB_STEP_SUMMARY
152+
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
153+
echo "| Workflow Runs | ${{ needs.cleanup_workflow_runs.result }} |" >> $GITHUB_STEP_SUMMARY
154+
echo "| Caches | ${{ needs.cleanup_caches.result }} |" >> $GITHUB_STEP_SUMMARY
155+
echo "| Stale Branches | ${{ needs.cleanup_stale_branches.result }} |" >> $GITHUB_STEP_SUMMARY
156+
echo "| Draft Releases | ${{ needs.cleanup_draft_releases.result }} |" >> $GITHUB_STEP_SUMMARY
157+
echo "" >> $GITHUB_STEP_SUMMARY
158+
echo "Dry run mode: ${{ inputs.dry_run || false }}" >> $GITHUB_STEP_SUMMARY

.github/workflows/release.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,39 @@ jobs:
143143
mv dist/${{ matrix.artifact_name }} dist/${{ matrix.asset_name }}
144144
shell: bash
145145

146+
- name: Build Windows Installer
147+
if: matrix.os == 'windows-latest'
148+
run: |
149+
# Install Inno Setup
150+
choco install innosetup -y
151+
152+
# Install ImageMagick for PNG to ICO conversion
153+
choco install imagemagick -y
154+
155+
# Convert PNG to ICO with multiple sizes
156+
magick switchcraft_logo.png -define icon:auto-resize=256,128,64,48,32,16 switchcraft_logo.ico
157+
158+
# Update version in ISS file
159+
$version = "${{ needs.prepare_release.outputs.version }}"
160+
(Get-Content switchcraft.iss) -replace '#define MyAppVersion ".*"', "#define MyAppVersion `"$version`"" | Set-Content switchcraft.iss
161+
162+
# Build installer
163+
iscc switchcraft.iss
164+
shell: pwsh
165+
146166
- name: Upload Release Asset
147167
uses: softprops/action-gh-release@v2
148168
with:
149169
tag_name: v${{ needs.prepare_release.outputs.version }}
150170
files: dist/${{ matrix.asset_name }}
151171
env:
152172
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
173+
174+
- name: Upload Windows Installer
175+
if: matrix.os == 'windows-latest'
176+
uses: softprops/action-gh-release@v2
177+
with:
178+
tag_name: v${{ needs.prepare_release.outputs.version }}
179+
files: dist/SwitchCraft-Setup.exe
180+
env:
181+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,45 @@
5959
## 🚀 Installation
6060

6161
### Pre-built Binaries
62-
Download the latest standalone executable from the [Releases](https://github.com/FaserF/SwitchCraft/releases) page:
62+
Download from the [Releases](https://github.com/FaserF/SwitchCraft/releases) page:
63+
64+
#### Windows Installer (Recommended)
65+
- **`SwitchCraft-Setup.exe`** – Full installer with Start Menu, Desktop shortcuts, and Add/Remove Programs entry
66+
67+
| Install Mode | Default Location | How to trigger |
68+
|-------------|------------------|----------------|
69+
| **User** | `%LOCALAPPDATA%\FaserF\SwitchCraft` | Just run the installer |
70+
| **Admin** | `C:\Program Files\FaserF\SwitchCraft` | Run as Administrator |
71+
72+
**Silent Install:**
73+
```powershell
74+
# Standard silent install
75+
SwitchCraft-Setup.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART
76+
77+
# Silent install with debug logging enabled
78+
SwitchCraft-Setup.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /DEBUGMODE
79+
```
80+
81+
**Silent Uninstall:** Use the same switches with the uninstaller from Add/Remove Programs.
82+
83+
#### Debug Logging Mode
84+
Enable verbose structured logging for troubleshooting or log analysis:
85+
86+
| Method | How to enable |
87+
|--------|---------------|
88+
| **Installer checkbox** | Select "Enable Debug Logging" during installation |
89+
| **Silent install** | Add `/DEBUGMODE` or `/DEBUGMODE=1` parameter |
90+
| **Environment variable** | Set `SWITCHCRAFT_DEBUG=1` |
91+
| **Command line** | Run with `--debug` or `-d` flag |
92+
93+
Debug output format:
94+
```
95+
[2025-12-16 23:00:00] [INFO ] [main] SwitchCraft v2025.12.5 - Debug Log
96+
[2025-12-16 23:00:00] [DEBUG ] [exe] Analyzing: C:\path\installer.exe
97+
[2025-12-16 23:00:00] [DEBUG ] [exe] Detected: NSIS (confidence: 90%)
98+
```
99+
100+
#### Portable Executables (No Install)
63101
- **Windows**: `SwitchCraft-windows.exe`
64102
- **Linux**: `SwitchCraft-linux`
65103
- **macOS**: `SwitchCraft-macos`

src/switchcraft/main.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import click
22
import logging
3+
import sys
4+
import os
35
from pathlib import Path
46
from rich import print
57
from rich.panel import Panel
@@ -10,7 +12,54 @@
1012
from switchcraft.analyzers.exe import ExeAnalyzer
1113
from switchcraft.utils.winget import WingetHelper
1214

13-
logging.basicConfig(level=logging.ERROR)
15+
def is_debug_mode_enabled():
16+
"""Check if debug mode is enabled via registry, environment, or command line."""
17+
# 1. Check command line argument
18+
if '--debug' in sys.argv or '-d' in sys.argv:
19+
return True
20+
21+
# 2. Check environment variable
22+
if os.environ.get('SWITCHCRAFT_DEBUG', '').lower() in ('1', 'true', 'yes'):
23+
return True
24+
25+
# 3. Check Windows registry (set by installer)
26+
if sys.platform == 'win32':
27+
try:
28+
import winreg
29+
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\FaserF\SwitchCraft', 0, winreg.KEY_READ)
30+
value, _ = winreg.QueryValueEx(key, 'DebugMode')
31+
winreg.CloseKey(key)
32+
if value == 1:
33+
return True
34+
except (FileNotFoundError, OSError, WindowsError):
35+
pass # Registry key doesn't exist, ignore
36+
37+
return False
38+
39+
def setup_logging():
40+
"""Setup structured logging format based on debug mode setting."""
41+
debug_enabled = is_debug_mode_enabled()
42+
43+
if debug_enabled:
44+
# Structured debug logging format for easy parsing
45+
logging.basicConfig(
46+
level=logging.DEBUG,
47+
format='[%(asctime)s] [%(levelname)-8s] [%(name)s] %(message)s',
48+
datefmt='%Y-%m-%d %H:%M:%S'
49+
)
50+
logging.info("=" * 60)
51+
logging.info(f"SwitchCraft v{__version__} - Debug Log")
52+
logging.info("=" * 60)
53+
logging.info(f"Python: {sys.version}")
54+
logging.info(f"Platform: {sys.platform}")
55+
logging.info(f"Executable: {sys.executable}")
56+
logging.info(f"Debug enabled via: {'registry' if sys.platform == 'win32' else 'env/cli'}")
57+
logging.info("=" * 60)
58+
else:
59+
logging.basicConfig(level=logging.ERROR)
60+
61+
setup_logging()
62+
logger = logging.getLogger(__name__)
1463

1564
@click.command()
1665
@click.argument('filepath', type=click.Path(exists=True), required=False)

0 commit comments

Comments
 (0)