mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-10-08 11:39:10 +11:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e912126b74 |
@@ -1,212 +0,0 @@
|
||||
name: Repo Contributors' Issue Triage Tools
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
jobs:
|
||||
triage-tools:
|
||||
if: ${{ !github.event.issue.pull_request }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: none
|
||||
contents: none
|
||||
steps:
|
||||
- name: Process issue command
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const trustedUsers = [
|
||||
7896101, // ChrisTitusTech
|
||||
57459428, // FluffyPunk
|
||||
125669256, // FallenGME
|
||||
90123670, // mewclouds
|
||||
121827219, // MyDrift-user
|
||||
17331812, // Callum
|
||||
101426328, // CodingWonders
|
||||
70659536 // og-mrk
|
||||
];
|
||||
|
||||
if (context.payload.issue.pull_request) {
|
||||
console.log("Pull request comments are not supported. Exiting.");
|
||||
return;
|
||||
}
|
||||
|
||||
const comment = context.payload.comment;
|
||||
const commentAuthor = comment.user;
|
||||
|
||||
if (!trustedUsers.includes(commentAuthor.id)) {
|
||||
console.log(`Comment author ${commentAuthor.login} is not a trusted user. Exiting.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const {owner, repo} = context.repo;
|
||||
const issueNumber = context.issue.number;
|
||||
const command = comment.body.trim().toLowerCase();
|
||||
const triageMatch = command.match(/^\/triage$/);
|
||||
const triageOffMatch = command.match(/^\/triageoff$/);
|
||||
const notPlannedMatch = command.match(/^\/np(?:\s+(\w+))?$/);
|
||||
const duplicateMatch = command.match(/^\/duplicate\s+#?(\d+)$/);
|
||||
|
||||
if (triageMatch) {
|
||||
const issue = await github.rest.issues.get({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber
|
||||
});
|
||||
|
||||
if (issue.data.labels.some(label => label.name === "needs-triage")) {
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body: "This issue already has the 'needs-triage' label. Use /triageoff to remove it."
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
labels: ["needs-triage"]
|
||||
});
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body: `${commentAuthor.login} requested maintainer triage for this issue.`
|
||||
});
|
||||
}
|
||||
} else if (triageOffMatch) {
|
||||
const issue = await github.rest.issues.get({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber
|
||||
});
|
||||
|
||||
if (issue.data.labels.some(label => label.name === "needs-triage")) {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
name: "needs-triage"
|
||||
});
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body: `${commentAuthor.login} removed the triage request from this issue.`
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body: "This issue is not currently marked for triage."
|
||||
});
|
||||
}
|
||||
} else if (notPlannedMatch) {
|
||||
const reason = notPlannedMatch[1];
|
||||
|
||||
if (reason === "wontfix") {
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
labels: ["wontfix"]
|
||||
});
|
||||
} else if (reason === "notrelated") {
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
labels: ["not-related"]
|
||||
});
|
||||
}
|
||||
|
||||
await github.rest.issues.update({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
state: "closed",
|
||||
state_reason: "not_planned"
|
||||
});
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body: `${commentAuthor.login} closed this issue as not planned.`
|
||||
});
|
||||
} else if (duplicateMatch) {
|
||||
const duplicateIssueNumber = Number(duplicateMatch[1]);
|
||||
|
||||
if (!Number.isSafeInteger(duplicateIssueNumber) || duplicateIssueNumber < 1) {
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body: "The duplicate target must be a positive issue number."
|
||||
});
|
||||
} else if (duplicateIssueNumber === issueNumber) {
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body: "An issue cannot be marked as a duplicate of itself."
|
||||
});
|
||||
} else {
|
||||
let duplicateIssue;
|
||||
try {
|
||||
const response = await github.rest.issues.get({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: duplicateIssueNumber
|
||||
});
|
||||
duplicateIssue = response.data;
|
||||
} catch (error) {
|
||||
if (error.status !== 404) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (duplicateIssue?.pull_request) {
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body: `#${duplicateIssueNumber} is a pull request, not an issue.`
|
||||
});
|
||||
} else if (duplicateIssue) {
|
||||
await github.rest.issues.update({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
state: "closed",
|
||||
state_reason: "duplicate",
|
||||
duplicate_issue_id: duplicateIssue.id
|
||||
});
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body: `${commentAuthor.login} closed this issue as a duplicate of #${duplicateIssueNumber}.`
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body: `Issue #${duplicateIssueNumber} does not exist.`
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("Comment does not contain a supported issue command. Exiting.");
|
||||
return;
|
||||
}
|
||||
|
||||
await github.rest.issues.deleteComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: comment.id
|
||||
});
|
||||
@@ -78,7 +78,7 @@ See https://github.com/ChrisTitusTech/winutil/blob/main/.github/CONTRIBUTING.md
|
||||
|
||||
These are the sponsors that help keep this project alive with monthly contributions.
|
||||
|
||||
<!-- sponsors --><a href="https://github.com/ysaito8015"><img src="https://github.com/ysaito8015.png" width="60px" alt="User avatar: Yusuke Saito" /></a><a href="https://github.com/dwelfusius"><img src="https://github.com/dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https://github.com/mews-se.png" width="60px" alt="User avatar: Martin" /></a><a href="https://github.com/jdiegmueller"><img src="https://github.com/jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https://github.com/robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/paulsheets"><img src="https://github.com/paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https://github.com/djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https://github.com/anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https://github.com/FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https://github.com/DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https://github.com/DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https://github.com/KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/seanh1995"><img src="https://github.com/seanh1995.png" width="60px" alt="User avatar: Sean (ANGRYxScotsman)" /></a><a href="https://github.com/Abs313a"><img src="https://github.com/Abs313a.png" width="60px" alt="User avatar: Abs" /></a><a href="https://github.com/kydoa"><img src="https://github.com/kydoa.png" width="60px" alt="User avatar: Daniel Scheuermann" /></a><a href="https://github.com/hafiz-muhammad"><img src="https://github.com/hafiz-muhammad.png" width="60px" alt="User avatar: Abdul Hafiz Muhammad" /></a><!-- sponsors -->
|
||||
<!-- sponsors --><a href="https://github.com/ysaito8015"><img src="https://github.com/ysaito8015.png" width="60px" alt="User avatar: Yusuke Saito" /></a><a href="https://github.com/dwelfusius"><img src="https://github.com/dwelfusius.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/mews-se"><img src="https://github.com/mews-se.png" width="60px" alt="User avatar: Martin" /></a><a href="https://github.com/jdiegmueller"><img src="https://github.com/jdiegmueller.png" width="60px" alt="User avatar: Jason A. Diegmueller" /></a><a href="https://github.com/robertsandrock"><img src="https://github.com/robertsandrock.png" width="60px" alt="User avatar: RMS" /></a><a href="https://github.com/paulsheets"><img src="https://github.com/paulsheets.png" width="60px" alt="User avatar: Paul" /></a><a href="https://github.com/djones369"><img src="https://github.com/djones369.png" width="60px" alt="User avatar: Dave J (WhamGeek)" /></a><a href="https://github.com/anthonymendez"><img src="https://github.com/anthonymendez.png" width="60px" alt="User avatar: Anthony Mendez" /></a><a href="https://github.com/FatBastard0"><img src="https://github.com/FatBastard0.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/DursleyGuy"><img src="https://github.com/DursleyGuy.png" width="60px" alt="User avatar: DursleyGuy" /></a><a href="https://github.com/DwayneTheRockLobster1"><img src="https://github.com/DwayneTheRockLobster1.png" width="60px" alt="User avatar: " /></a><a href="https://github.com/KieraKujisawa"><img src="https://github.com/KieraKujisawa.png" width="60px" alt="User avatar: Kiera Meredith" /></a><a href="https://github.com/andrewpayne68"><img src="https://github.com/andrewpayne68.png" width="60px" alt="User avatar: Andrew P" /></a><a href="https://github.com/seanh1995"><img src="https://github.com/seanh1995.png" width="60px" alt="User avatar: Sean (ANGRYxScotsman)" /></a><a href="https://github.com/Abs313a"><img src="https://github.com/Abs313a.png" width="60px" alt="User avatar: Abs" /></a><!-- sponsors -->
|
||||
|
||||
---
|
||||
|
||||
|
||||
+16
-62
@@ -98,15 +98,6 @@
|
||||
"winget": "AutoHotkey.AutoHotkey",
|
||||
"foss": true
|
||||
},
|
||||
"battlenet": {
|
||||
"category": "Games",
|
||||
"choco": "na",
|
||||
"winget": "Blizzard.BattleNet",
|
||||
"content": "Battle.net",
|
||||
"description": "Battle.net is a launcher for games created and developed by Activision Blizzard",
|
||||
"link": "https://battle.net",
|
||||
"foss": false
|
||||
},
|
||||
"bitwarden": {
|
||||
"category": "Utilities",
|
||||
"choco": "bitwarden",
|
||||
@@ -184,7 +175,7 @@
|
||||
"choco": "na",
|
||||
"content": "ChatGPT Desktop",
|
||||
"description": "The official ChatGPT desktop app for Windows, distributed through the Microsoft Store.",
|
||||
"link": "https://openai.com/chatgpt/download/",
|
||||
"link": "https://apps.microsoft.com/detail/9nt1r1c2hh7j",
|
||||
"winget": "msstore:9NT1R1C2HH7J",
|
||||
"foss": false
|
||||
},
|
||||
@@ -211,7 +202,7 @@
|
||||
"choco": "chromium",
|
||||
"content": "Chromium",
|
||||
"description": "Chromium is the open-source project that serves as the foundation for various web browsers, including Chrome.",
|
||||
"link": "https://www.chromium.org/",
|
||||
"link": "https://github.com/Hibbiki/chromium-win64",
|
||||
"winget": "Hibbiki.Chromium",
|
||||
"foss": true
|
||||
},
|
||||
@@ -337,7 +328,7 @@
|
||||
"choco": "dorion",
|
||||
"content": "Dorion",
|
||||
"description": "Tiny alternative Discord client with a smaller footprint, snappier startup, themes, plugins and more!",
|
||||
"link": "https://spikehd.dev/projects/dorion/",
|
||||
"link": "https://github.com/SpikeHD/Dorion",
|
||||
"winget": "SpikeHD.Dorion",
|
||||
"foss": true
|
||||
},
|
||||
@@ -422,16 +413,6 @@
|
||||
"winget": "Microsoft.Edge",
|
||||
"foss": false
|
||||
},
|
||||
"es-de": {
|
||||
"category": "Games",
|
||||
"choco": "",
|
||||
"content": "EmulationStation Desktop Edition",
|
||||
"_comment": "This and emulationstation are two completely different things. ES-DE is your frontend for everything and has its own set of emulators. Emulationstation is a graphical frontend for RetroArch.",
|
||||
"description": "EmulationStation Desktop Edition is a frontend for browsing and launching games from your multi-platform game collection.",
|
||||
"link": "https://es-de.org/",
|
||||
"winget": "ES-DE.EmulationStation-DE",
|
||||
"foss": true
|
||||
},
|
||||
"enteauth": {
|
||||
"category": "Utilities",
|
||||
"choco": "ente-auth",
|
||||
@@ -455,7 +436,7 @@
|
||||
"choco": "files",
|
||||
"content": "Files",
|
||||
"description": "Alternative file explorer.",
|
||||
"link": "https://files.community",
|
||||
"link": "https://github.com/files-community/Files",
|
||||
"winget": "FilesCommunity.Files",
|
||||
"foss": true
|
||||
},
|
||||
@@ -626,7 +607,7 @@
|
||||
"choco": "helium",
|
||||
"content": "Helium",
|
||||
"description": "Private, fast, and honest web browser.",
|
||||
"link": "https://helium.computer",
|
||||
"link": "https://github.com/imputnet/helium/",
|
||||
"winget": "ImputNet.Helium",
|
||||
"foss": true
|
||||
},
|
||||
@@ -635,7 +616,7 @@
|
||||
"choco": "hugo-extended",
|
||||
"content": "Hugo",
|
||||
"description": "The world's fastest framework for building websites.",
|
||||
"link": "https://gohugo.io",
|
||||
"link": "https://github.com/gohugoio/hugo/",
|
||||
"winget": "Hugo.Hugo.Extended",
|
||||
"foss": true
|
||||
},
|
||||
@@ -752,7 +733,7 @@
|
||||
"choco": "jellyfin-media-player",
|
||||
"content": "Jellyfin Media Player",
|
||||
"description": "Jellyfin Media Player is a client application for the Jellyfin media server, providing access to your media library.",
|
||||
"link": "https://jellyfin.org/",
|
||||
"link": "https://github.com/jellyfin/jellyfin-media-player",
|
||||
"winget": "Jellyfin.JellyfinMediaPlayer",
|
||||
"foss": true
|
||||
},
|
||||
@@ -842,7 +823,7 @@
|
||||
"choco": "librewolf",
|
||||
"content": "LibreWolf",
|
||||
"description": "LibreWolf is a privacy-focused web browser based on Firefox, with additional privacy and security enhancements.",
|
||||
"link": "https://librewolf.net/",
|
||||
"link": "https://librewolf-community.gitlab.io/",
|
||||
"winget": "LibreWolf.LibreWolf",
|
||||
"foss": true
|
||||
},
|
||||
@@ -860,7 +841,7 @@
|
||||
"choco": "mediainfo",
|
||||
"content": "mpc-qt",
|
||||
"description": "Media Player Classic Qute Theater",
|
||||
"link": "https://mpc-qt.github.io",
|
||||
"link": "https://github.com/mpc-qt/mpc-qt",
|
||||
"winget": "mpc-qt.mpc-qt",
|
||||
"foss": true
|
||||
},
|
||||
@@ -913,7 +894,7 @@
|
||||
"choco": "mpc-hc-clsid2",
|
||||
"content": "Media Player Classic - Home Cinema",
|
||||
"description": "Media Player Classic - Home Cinema (MPC-HC) is a free and open-source video and audio player for Windows. MPC-HC is based on the original Guliverkli project and contains many additional features and bug fixes.",
|
||||
"link": "https://mpc-hc.org/",
|
||||
"link": "https://github.com/clsid2/mpc-hc/",
|
||||
"winget": "clsid2.mpc-hc",
|
||||
"foss": true
|
||||
},
|
||||
@@ -940,7 +921,7 @@
|
||||
"choco": "mullvad-app",
|
||||
"content": "Mullvad VPN",
|
||||
"description": "This is the VPN client software for the Mullvad VPN service.",
|
||||
"link": "https://mullvad.net/",
|
||||
"link": "https://github.com/mullvad/mullvadvpn-app",
|
||||
"winget": "MullvadVPN.MullvadVPN",
|
||||
"foss": true
|
||||
},
|
||||
@@ -967,7 +948,7 @@
|
||||
"choco": "nanazip",
|
||||
"content": "NanaZip",
|
||||
"description": "NanaZip is a fast and efficient file compression and decompression tool.",
|
||||
"link": "https://nanazip.org",
|
||||
"link": "https://github.com/M2Team/NanaZip",
|
||||
"winget": "M2Team.NanaZip",
|
||||
"foss": true
|
||||
},
|
||||
@@ -980,15 +961,6 @@
|
||||
"winget": "Netbird.Netbird",
|
||||
"foss": true
|
||||
},
|
||||
"tailscale": {
|
||||
"category": "Utilities",
|
||||
"choco": "tailscale",
|
||||
"content": "Tailscale",
|
||||
"description": "The Tailscale client allows you to connect all your devices using WireGuard®, without the hassle. Tailscale makes it as easy as installing an app and signing in.",
|
||||
"link": "https://tailscale.com/",
|
||||
"winget": "Tailscale.Tailscale",
|
||||
"foss": false
|
||||
},
|
||||
"naps2": {
|
||||
"category": "Document",
|
||||
"choco": "naps2",
|
||||
@@ -1402,15 +1374,6 @@
|
||||
"winget": "qBittorrent.qBittorrent",
|
||||
"foss": true
|
||||
},
|
||||
"qownnotes": {
|
||||
"category": "Document",
|
||||
"choco": "qownnotes",
|
||||
"content": "QOwnNotes",
|
||||
"description": "QOwnNotes is a free open-source note taking app with Nextcloud/ownCloud integration.",
|
||||
"link": "https://www.qownnotes.org/",
|
||||
"winget": "pbek.QOwnNotes",
|
||||
"foss": true
|
||||
},
|
||||
"qtox": {
|
||||
"category": "Communications",
|
||||
"choco": "qtox",
|
||||
@@ -1596,7 +1559,7 @@
|
||||
"choco": "sunshine",
|
||||
"content": "Sunshine/GameStream Server",
|
||||
"description": "Sunshine is a GameStream server that allows you to remotely play PC games on Android devices, offering low-latency streaming.",
|
||||
"link": "https://app.lizardbyte.dev/Sunshine/",
|
||||
"link": "https://github.com/LizardByte/Sunshine",
|
||||
"winget": "LizardByte.Sunshine",
|
||||
"foss": true
|
||||
},
|
||||
@@ -1713,7 +1676,7 @@
|
||||
"choco": "translucenttb",
|
||||
"content": "TranslucentTB",
|
||||
"description": "TranslucentTB is a tool that allows you to customize the transparency of the Windows Taskbar.",
|
||||
"link": "https://translucenttb.github.io",
|
||||
"link": "https://github.com/TranslucentTB/TranslucentTB",
|
||||
"winget": "CharlesMilette.TranslucentTB",
|
||||
"foss": true
|
||||
},
|
||||
@@ -1794,7 +1757,7 @@
|
||||
"choco": "na",
|
||||
"content": "Vesktop",
|
||||
"description": "A cross platform electron-based desktop app aiming to give you a snappier Discord experience with Vencord pre-installed.",
|
||||
"link": "https://vesktop.dev",
|
||||
"link": "https://github.com/Vencord/Vesktop",
|
||||
"winget": "Vencord.Vesktop",
|
||||
"foss": true
|
||||
},
|
||||
@@ -1884,7 +1847,7 @@
|
||||
"choco": "na",
|
||||
"content": "WhatsApp Desktop",
|
||||
"description": "WhatsApp Desktop is the official Windows desktop messaging app from Meta, distributed through the Microsoft Store.",
|
||||
"link": "https://www.whatsapp.com/download",
|
||||
"link": "https://apps.microsoft.com/detail/9nksqgp7f2nh",
|
||||
"winget": "msstore:9NKSQGP7F2NH",
|
||||
"foss": false
|
||||
},
|
||||
@@ -2076,14 +2039,5 @@
|
||||
"content": "Lua",
|
||||
"link": "https://github.com/rjpcomputing/luaforwindows",
|
||||
"foss": true
|
||||
},
|
||||
"CloudflareWARP": {
|
||||
"category": "Utilities",
|
||||
"choco": "warp",
|
||||
"winget": "Cloudflare.Warp",
|
||||
"description": "WARP is a freemium VPN service provided by Cloudflare. Includes usage of Cloudflare's DNS",
|
||||
"content": "Cloudflare WARP",
|
||||
"link": "https://one.one.one.one",
|
||||
"foss": false
|
||||
}
|
||||
}
|
||||
|
||||
+17
-1
@@ -114,7 +114,7 @@
|
||||
"service": [
|
||||
{
|
||||
"Name": "lfsvc",
|
||||
"StartupType": "Disabled",
|
||||
"StartupType": "Disable",
|
||||
"OriginalType": "Manual"
|
||||
}
|
||||
],
|
||||
@@ -1168,6 +1168,22 @@
|
||||
],
|
||||
"link": "https://winutil.christitus.com/code-reference/tweaks/z--advanced-tweaks---caution/disablebgapps"
|
||||
},
|
||||
"WPFTweaksDisableFSO": {
|
||||
"Content": "Fullscreen Optimizations - Disable",
|
||||
"Description": "Disables FSO in all applications. NOTE: This will disable Color Management in Exclusive Fullscreen.",
|
||||
"category": "z__Advanced Tweaks - CAUTION",
|
||||
"panel": "1",
|
||||
"registry": [
|
||||
{
|
||||
"Path": "HKCU:\\System\\GameConfigStore",
|
||||
"Name": "GameDVR_DXGIHonorFSEWindowsCompatible",
|
||||
"Value": "1",
|
||||
"Type": "DWord",
|
||||
"OriginalValue": "0"
|
||||
}
|
||||
],
|
||||
"link": "https://winutil.christitus.com/code-reference/tweaks/z--advanced-tweaks---caution/disablefso"
|
||||
},
|
||||
"WPFTweaksDisableExplorerAutoDiscovery": {
|
||||
"Content": "File Explorer Automatic Folder Discovery - Disable",
|
||||
"Description": "Windows Explorer automatically tries to guess the type of the folder based on its contents, slowing down the browsing experience. WARNING! Will disable File Explorer grouping.",
|
||||
|
||||
@@ -56,7 +56,6 @@ export default defineConfig({
|
||||
label: 'Code Reference',
|
||||
items: [
|
||||
{ label: 'Architecture & Design', slug: 'code-reference/architecture' },
|
||||
{ label: 'Issue Triage Commands', slug: 'code-reference/issue-triage' },
|
||||
{ label: 'Tweaks Reference', items: [{ autogenerate: { directory: 'code-reference/tweaks' } }] },
|
||||
{ label: 'Features Reference', items: [{ autogenerate: { directory: 'code-reference/features' } }] },
|
||||
],
|
||||
|
||||
@@ -125,9 +125,9 @@ function Invoke-WPFFixesUpdate {
|
||||
if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate") {
|
||||
Write-Progress -Id 0 -Activity "Repairing Windows Update" -Status "Removing WSUS client settings..." -PercentComplete 60
|
||||
Write-Progress -Id 6 -ParentId 0 -Activity "Removing WSUS client settings" -PercentComplete 0
|
||||
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" -Name "AccountDomainSid" -ErrorAction SilentlyContinue
|
||||
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" -Name "PingID" -ErrorAction SilentlyContinue
|
||||
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" -Name "SusClientId" -ErrorAction SilentlyContinue
|
||||
Start-Process -NoNewWindow -FilePath "REG" -ArgumentList "DELETE", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate", "/v", "AccountDomainSid", "/f" -RedirectStandardError "NUL"
|
||||
Start-Process -NoNewWindow -FilePath "REG" -ArgumentList "DELETE", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate", "/v", "PingID", "/f" -RedirectStandardError "NUL"
|
||||
Start-Process -NoNewWindow -FilePath "REG" -ArgumentList "DELETE", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate", "/v", "SusClientId", "/f" -RedirectStandardError "NUL"
|
||||
Write-Progress -Id 6 -ParentId 0 -Activity "Removing WSUS client settings" -Status "Completed" -PercentComplete 100
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
title: Issue Triage Commands
|
||||
description: Commands trusted maintainers can use to label and close GitHub issues.
|
||||
---
|
||||
|
||||
Trusted repository members whose numeric GitHub user IDs are listed in the issue triage workflow can run the commands below. These commands work only on issues. Comments on pull requests are ignored, and the workflow token has no pull-request write permission.
|
||||
|
||||
## Commands
|
||||
|
||||
- `/triage` adds the `needs-triage` label.
|
||||
- `/triageoff` removes the `needs-triage` label when it is present.
|
||||
- `/np` closes the issue as not planned.
|
||||
- `/np wontfix` adds the `wontfix` label without replacing existing labels, then closes the issue as not planned.
|
||||
- `/np notrelated` adds the `not-related` label without replacing existing labels, then closes the issue as not planned.
|
||||
- `/np <reason>` closes the issue as not planned without adding a label for unrecognized reasons.
|
||||
- `/duplicate <issue number>` closes the issue as a duplicate of another issue. The positive issue number may optionally start with `#`; pull request numbers are rejected.
|
||||
|
||||
## Command handling
|
||||
|
||||
Put exactly one command in the comment. Leading and trailing whitespace is allowed, but text before or after the command is not. Command matching is case-insensitive, and malformed issue numbers are rejected.
|
||||
|
||||
After a command is handled, the workflow deletes the command comment and leaves an audit comment describing the result. A missing duplicate target or a request to duplicate an issue into itself is reported without closing the issue. Unexpected GitHub API failures stop the workflow and leave the command comment available for retry.
|
||||
|
||||
## Access
|
||||
|
||||
Access is granted by adding the trusted member's immutable numeric GitHub user ID to `.github/workflows/triage-tools.yaml` through a pull request. Abuse can result in removal from the allowlist or other repository moderation action.
|
||||
+10
-22
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "Multiplane Overlay"
|
||||
description: "Multiplane Overlay composes multiple image layers, which can sometimes cause issues with graphics cards. Changes to this preference are applied immediately."
|
||||
description: "Multiplane Overlay compose multiple image layers, which can sometimes cause issues with graphics cards."
|
||||
editUrl: false
|
||||
---
|
||||
|
||||
@@ -9,40 +9,28 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
:::
|
||||
|
||||
```json title="config/tweaks.json"
|
||||
"WPFMultiplaneOverlay": {
|
||||
"WPFToggleMultiplaneOverlay": {
|
||||
"Content": "Multiplane Overlay",
|
||||
"Description": "Multiplane Overlay composes multiple image layers, which can sometimes cause issues with graphics cards. Changes to this preference are applied immediately.",
|
||||
"Description": "Multiplane Overlay compose multiple image layers, which can sometimes cause issues with graphics cards.",
|
||||
"category": "Customize Preferences",
|
||||
"panel": "2",
|
||||
"Type": "Combobox",
|
||||
"ComboItems": "Enabled|Disabled (Compatibility)|Fully Disabled",
|
||||
"ComboDescriptions": {
|
||||
"Enabled": "Uses Windows' default overlay behavior.",
|
||||
"Disabled (Compatibility)": "Disables MPO using OverlayTestMode=5, the less aggressive compatibility method.",
|
||||
"Fully Disabled": "Disables MPO using OverlayTestMode=5 and DisableOverlays=1, the more aggressive method."
|
||||
},
|
||||
"Type": "Toggle",
|
||||
"registry": [
|
||||
{
|
||||
"Path": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\Dwm",
|
||||
"Name": "OverlayTestMode",
|
||||
"Value": "0",
|
||||
"Type": "DWord",
|
||||
"DefaultValue": "0",
|
||||
"Values": {
|
||||
"Enabled": "<RemoveEntry>",
|
||||
"Disabled (Compatibility)": "5",
|
||||
"Fully Disabled": "5"
|
||||
}
|
||||
"OriginalValue": "5",
|
||||
"DefaultState": "true"
|
||||
},
|
||||
{
|
||||
"Path": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers",
|
||||
"Name": "DisableOverlays",
|
||||
"Value": "0",
|
||||
"Type": "DWord",
|
||||
"DefaultValue": "0",
|
||||
"Values": {
|
||||
"Enabled": "<RemoveEntry>",
|
||||
"Disabled (Compatibility)": "<RemoveEntry>",
|
||||
"Fully Disabled": "1"
|
||||
}
|
||||
"OriginalValue": "1",
|
||||
"DefaultState": "true"
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
"service": [
|
||||
{
|
||||
"Name": "lfsvc",
|
||||
"StartupType": "Disabled",
|
||||
"StartupType": "Disable",
|
||||
"OriginalType": "Manual"
|
||||
}
|
||||
],
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: "Fullscreen Optimizations - Disable"
|
||||
description: "Disables FSO in all applications. NOTE: This will disable Color Management in Exclusive Fullscreen."
|
||||
editUrl: false
|
||||
---
|
||||
|
||||
:::note
|
||||
This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitusTech/winutil/blob/main/config/tweaks.json). Do not edit this page directly.
|
||||
:::
|
||||
|
||||
```json title="config/tweaks.json"
|
||||
"WPFTweaksDisableFSO": {
|
||||
"Content": "Fullscreen Optimizations - Disable",
|
||||
"Description": "Disables FSO in all applications. NOTE: This will disable Color Management in Exclusive Fullscreen.",
|
||||
"category": "z__Advanced Tweaks - CAUTION",
|
||||
"panel": "1",
|
||||
"registry": [
|
||||
{
|
||||
"Path": "HKCU:\\System\\GameConfigStore",
|
||||
"Name": "GameDVR_DXGIHonorFSEWindowsCompatible",
|
||||
"Value": "1",
|
||||
"Type": "DWord",
|
||||
"OriginalValue": "0"
|
||||
}
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Registry Changes
|
||||
|
||||
Applications and System Components store and retrieve configuration data to modify Windows settings, so we can use the registry to change many settings in one place.
|
||||
|
||||
You can find information about the registry on [Wikipedia](https://en.wikipedia.org/wiki/Windows_Registry) and [Microsoft's Website](https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry).
|
||||
+1
-1
@@ -14,6 +14,6 @@ This page is generated from [`config/tweaks.json`](https://github.com/ChrisTitus
|
||||
"category": "z__Advanced Tweaks - CAUTION",
|
||||
"panel": "1",
|
||||
"Type": "Combobox",
|
||||
"ComboItems": "Default DHCP Google Cloudflare Cloudflare_Malware Cloudflare_Malware_Adult Open_DNS Quad9 AdGuard_Ads_Trackers AdGuard_Ads_Trackers_Malware_Adult Mullvad Mullvad_Ads_Trackers Mullvad_Ads_Trackers_Malware Mullvad_Ads_Trackers_Malware_Social Mullvad_Ads_Trackers_Malware_Adult_Gambling Mullvad_Ads_Trackers_Malware_Adult_Gambling_Social",
|
||||
"ComboItems": "Default DHCP Google Cloudflare Cloudflare_Malware Cloudflare_Malware_Adult Open_DNS Quad9 AdGuard_Ads_Trackers AdGuard_Ads_Trackers_Malware_Adult",
|
||||
}
|
||||
```
|
||||
|
||||
@@ -22,8 +22,6 @@ Example:
|
||||
To view exactly what each preset does, see:
|
||||
https://github.com/ChrisTitusTech/winutil/blob/main/config/preset.json
|
||||
|
||||
To find a key for a custom configuration, hover over a supported application, tweak, feature, or AppX entry in WinUtil. Its tooltip shows `Preset key: <key>`. Application keys are searchable on the Install tab, while tweak and AppX keys are searchable on their respective tabs. Feature keys are available from their tooltips because the Config/Features tab does not have search. Controls that cannot be applied from a preset, such as toggle switches, drop-downs, and package-manager choices, intentionally do not advertise a preset key.
|
||||
|
||||
To create your own config file:
|
||||
|
||||
1. Open WinUtil.
|
||||
@@ -42,14 +40,6 @@ This is useful for:
|
||||
- Reusing a known-good baseline after reinstalling Windows
|
||||
- Standardizing deployments for labs, workstations, or personal setups
|
||||
|
||||
:::caution[Keep exported configurations current]
|
||||
Exported configurations contain the WinUtil catalog keys that existed when the file was created. Current exports use a flat JSON format: one string when a single setting is selected, or an array of strings when several settings are selected. If a later WinUtil version removes or renames any imported key, the entire import is rejected before current selections are changed. PowerShell reports the stale entry as `Unknown selection key '<key>'`.
|
||||
|
||||
Older WinUtil versions exported a JSON object with `Install` package metadata and grouped `WPFInstall`, `WPFTweaks`, `WPFToggle`, and `WPFFeature` selections. When importing one of these legacy files, WinUtil restores the keys that still exist and skips retired keys, recording them as a warning in the WinUtil log. If the file contains no supported selections, the import makes no changes.
|
||||
|
||||
To recover, compare the reported key with the current files in the [WinUtil configuration catalog](https://github.com/ChrisTitusTech/winutil/tree/main/config). Remove or replace the stale key in your JSON file, or create and export a new configuration with the current WinUtil version, then run the import again. Re-export long-lived baselines after catalog changes so they remain compatible.
|
||||
:::
|
||||
|
||||
:::note
|
||||
Run the command in an elevated PowerShell session so WinUtil can apply system-level changes.
|
||||
:::
|
||||
|
||||
@@ -5,9 +5,9 @@ function Find-AppsByNameOrDescription {
|
||||
|
||||
.DESCRIPTION
|
||||
Search text and categories are independent filters that both have to pass. An entry is
|
||||
shown when its name, description, or application preset key matches the search text, and
|
||||
when its category is in the selected set. An empty search matches everything, and an empty
|
||||
category set matches every category.
|
||||
shown when its name or description matches the search text, and when its category is in
|
||||
the selected set. An empty search matches everything, and an empty category set matches
|
||||
every category.
|
||||
|
||||
While either filter is active the matching categories are expanded, since a collapsed
|
||||
category would otherwise hide the very results that were asked for. With no filter at
|
||||
@@ -115,8 +115,7 @@ function Find-AppsByNameOrDescription {
|
||||
$categoryMatch = -not $hasCategories -or $activeCategories -contains $appEntry.Category
|
||||
$textMatch = -not $hasSearch -or
|
||||
$appEntry.Content -like "*$escapedSearchString*" -or
|
||||
$appEntry.Description -like "*$escapedSearchString*" -or
|
||||
$appTag -like "*$escapedSearchString*"
|
||||
$appEntry.Description -like "*$escapedSearchString*"
|
||||
|
||||
if ($categoryMatch -and $textMatch) {
|
||||
$appControl.Visibility = [Windows.Visibility]::Visible
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
function Get-WinUtilEntryToolTip {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds the tooltip string for an app/tweak/feature entry: its description plus its preset JSON key
|
||||
|
||||
.PARAMETER Description
|
||||
The entry's description from the config JSON. May be null or empty.
|
||||
|
||||
.PARAMETER Key
|
||||
The entry's JSON key as used in preset files (e.g. WPFInstallbrave, WPFTweaksTele).
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Description,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Key
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Description)) {
|
||||
return "Preset key: $Key"
|
||||
}
|
||||
|
||||
return "$Description`n`nPreset key: $Key"
|
||||
}
|
||||
@@ -19,7 +19,7 @@ function Initialize-InstallAppEntry {
|
||||
$border = New-Object Windows.Controls.Border
|
||||
$border.Style = $sync.Form.Resources.AppEntryBorderStyle
|
||||
$border.Tag = $appKey
|
||||
$border.ToolTip = Get-WinUtilEntryToolTip -Description $app.description -Key $appKey
|
||||
$border.ToolTip = $app.description
|
||||
$border.Add_MouseLeftButtonUp({
|
||||
# Resolve through $sync because the border's child is a layout Grid for FOSS entries
|
||||
$childCheckbox = $sync.$($this.Tag)
|
||||
|
||||
@@ -14,6 +14,7 @@ function Initialize-WinUtilTabContent {
|
||||
|
||||
switch ($TabName) {
|
||||
"Install" {
|
||||
Invoke-WPFUIElements -configVariable $sync.configs.appnavigation -targetGridName "appscategory" -columncount 1
|
||||
Initialize-WPFUI -targetGridName "appscategory"
|
||||
|
||||
Initialize-WPFUI -targetGridName "appspanel"
|
||||
|
||||
@@ -529,34 +529,6 @@ function Invoke-WinUtilISOCleanAndReset {
|
||||
$script.BeginInvoke()
|
||||
}
|
||||
|
||||
function Get-WinUtilOSCDImgPath {
|
||||
# Windows ADK installation
|
||||
$oscdimg = Get-ChildItem "C:\Program Files (x86)\Windows Kits" -Recurse -Filter "oscdimg.exe" -ErrorAction SilentlyContinue |
|
||||
Select-Object -First 1 -ExpandProperty FullName
|
||||
if (-not $oscdimg) {
|
||||
# Per-user winget installation
|
||||
$oscdimg = Get-ChildItem "$env:LOCALAPPDATA\Microsoft\WinGet\Packages" -Recurse -Filter "oscdimg.exe" -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.FullName -match 'Microsoft\.OSCDIMG' } |
|
||||
Select-Object -First 1 -ExpandProperty FullName
|
||||
}
|
||||
|
||||
if (-not $oscdimg) {
|
||||
# Installation available through the current process PATH
|
||||
$oscdimg = Get-Command oscdimg.exe -CommandType Application -ErrorAction SilentlyContinue |
|
||||
Select-Object -First 1 -ExpandProperty Source
|
||||
}
|
||||
|
||||
if (-not $oscdimg) {
|
||||
# WinGet links that may not yet be available through the current process PATH
|
||||
$oscdimg = @(
|
||||
"$env:LOCALAPPDATA\Microsoft\WinGet\Links\oscdimg.exe"
|
||||
"$env:ProgramFiles\WinGet\Links\oscdimg.exe"
|
||||
) | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1
|
||||
}
|
||||
|
||||
return $oscdimg
|
||||
}
|
||||
|
||||
function Invoke-WinUtilISOExport {
|
||||
$contentsDir = $sync["Win11ISOContentsDir"]
|
||||
|
||||
@@ -579,7 +551,14 @@ function Invoke-WinUtilISOExport {
|
||||
|
||||
$outputISO = $dlg.FileName
|
||||
|
||||
$oscdimg = Get-WinUtilOSCDImgPath
|
||||
# Locate oscdimg.exe (Windows ADK or winget per-user install)
|
||||
$oscdimg = Get-ChildItem "C:\Program Files (x86)\Windows Kits" -Recurse -Filter "oscdimg.exe" |
|
||||
Select-Object -First 1 -ExpandProperty FullName
|
||||
if (-not $oscdimg) {
|
||||
$oscdimg = Get-ChildItem "$env:LOCALAPPDATA\Microsoft\WinGet\Packages" -Recurse -Filter "oscdimg.exe" |
|
||||
Where-Object { $_.FullName -match 'Microsoft\.OSCDIMG' } |
|
||||
Select-Object -First 1 -ExpandProperty FullName
|
||||
}
|
||||
|
||||
if (-not $oscdimg) {
|
||||
Write-WinUtilISOLog "oscdimg.exe not found. Attempting to install via winget..."
|
||||
@@ -590,7 +569,9 @@ function Invoke-WinUtilISOExport {
|
||||
$winget = Get-Command winget
|
||||
$result = & $winget install -e --id Microsoft.OSCDIMG --accept-package-agreements --accept-source-agreements
|
||||
Write-WinUtilISOLog "winget output: $result"
|
||||
$oscdimg = Get-WinUtilOSCDImgPath
|
||||
$oscdimg = Get-ChildItem "$env:LOCALAPPDATA\Microsoft\WinGet\Packages" -Recurse -Filter "oscdimg.exe" |
|
||||
Where-Object { $_.FullName -match 'Microsoft\.OSCDIMG' } |
|
||||
Select-Object -First 1 -ExpandProperty FullName
|
||||
} catch {
|
||||
Write-WinUtilISOLog "winget not available or install failed: $_"
|
||||
}
|
||||
|
||||
@@ -33,10 +33,7 @@ Function Set-WinUtilService {
|
||||
|
||||
# Service exists, proceed with changing properties -- while handling auto delayed start for PWSH 5
|
||||
if (($PSVersionTable.PSVersion.Major -lt 7) -and ($StartupType -eq "AutomaticDelayedStart")) {
|
||||
sc.exe config $Name start= delayed-auto
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "sc.exe config failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
sc.exe config $Name start=delayed-auto
|
||||
} else {
|
||||
$service | Set-Service -StartupType $StartupType -ErrorAction Stop
|
||||
}
|
||||
|
||||
@@ -1,21 +1,4 @@
|
||||
function Update-WinUtilSelections {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string[]]$flatJson,
|
||||
|
||||
[switch]$Replace,
|
||||
|
||||
[switch]$SkipUnknown
|
||||
)
|
||||
|
||||
$nextSelections = @{
|
||||
selectedApps = [System.Collections.Generic.List[string]]::new()
|
||||
selectedTweaks = [System.Collections.Generic.List[string]]::new()
|
||||
selectedToggles = [System.Collections.Generic.List[string]]::new()
|
||||
selectedFeatures = [System.Collections.Generic.List[string]]::new()
|
||||
selectedAppx = [System.Collections.Generic.List[string]]::new()
|
||||
}
|
||||
|
||||
function Update-WinUtilSelections ($flatJson) {
|
||||
foreach ($cbkey in $flatJson) {
|
||||
|
||||
$listName = switch -Regex ($cbkey) {
|
||||
@@ -26,58 +9,6 @@ function Update-WinUtilSelections {
|
||||
'^WPFAppx' { 'selectedAppx' }
|
||||
}
|
||||
|
||||
if (-not $listName) {
|
||||
if ($SkipUnknown) {
|
||||
$cbkey
|
||||
continue
|
||||
}
|
||||
throw "Unsupported selection key '$cbkey'."
|
||||
}
|
||||
|
||||
$isKnownSelection = switch ($listName) {
|
||||
'selectedApps' {
|
||||
$sync.configs.applicationsHashtable.ContainsKey($cbkey)
|
||||
}
|
||||
'selectedTweaks' {
|
||||
$null -ne $sync.configs.tweaks.PSObject.Properties[$cbkey]
|
||||
}
|
||||
'selectedToggles' {
|
||||
$null -ne $sync.configs.tweaks.PSObject.Properties[$cbkey]
|
||||
}
|
||||
'selectedFeatures' {
|
||||
$null -ne $sync.configs.feature.PSObject.Properties[$cbkey]
|
||||
}
|
||||
'selectedAppx' {
|
||||
$sync.configs.appxHashtable.ContainsKey($cbkey)
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $isKnownSelection) {
|
||||
if ($SkipUnknown) {
|
||||
$cbkey
|
||||
continue
|
||||
}
|
||||
throw "Unknown selection key '$cbkey'."
|
||||
}
|
||||
|
||||
$nextSelections[$listName].Add($cbkey)
|
||||
}
|
||||
|
||||
$validSelectionCount = ($nextSelections.Values | ForEach-Object { $_.Count } | Measure-Object -Sum).Sum
|
||||
if ($SkipUnknown -and $validSelectionCount -eq 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if ($Replace) {
|
||||
foreach ($listName in $nextSelections.Keys) {
|
||||
$sync[$listName] = $nextSelections[$listName]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
foreach ($listName in $nextSelections.Keys) {
|
||||
foreach ($cbkey in $nextSelections[$listName]) {
|
||||
$sync.$listName.Add($cbkey)
|
||||
}
|
||||
$sync.$listName.Add($cbkey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ function Initialize-WPFUI {
|
||||
|
||||
switch ($TargetGridName) {
|
||||
"appscategory"{
|
||||
Invoke-WPFUIElements -configVariable $sync.configs.appnavigation -targetGridName "appscategory" -columncount 1
|
||||
# TODO
|
||||
# Switch UI generation of the sidebar to this function
|
||||
# $sync.ItemsControl = Initialize-InstallAppArea -TargetElement $TargetGridName
|
||||
# ...
|
||||
|
||||
# Create and configure a popup for displaying selected apps
|
||||
$selectedAppsPopup = New-Object Windows.Controls.Primitives.Popup
|
||||
|
||||
@@ -114,9 +114,9 @@ function Invoke-WPFFixesUpdate {
|
||||
if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate") {
|
||||
Write-Progress -Id 0 -Activity "Repairing Windows Update" -Status "Removing WSUS client settings..." -PercentComplete 60
|
||||
Write-Progress -Id 6 -ParentId 0 -Activity "Removing WSUS client settings" -PercentComplete 0
|
||||
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" -Name "AccountDomainSid" -ErrorAction SilentlyContinue
|
||||
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" -Name "PingID" -ErrorAction SilentlyContinue
|
||||
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" -Name "SusClientId" -ErrorAction SilentlyContinue
|
||||
Start-Process -NoNewWindow -FilePath "REG" -ArgumentList "DELETE", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate", "/v", "AccountDomainSid", "/f" -RedirectStandardError "NUL"
|
||||
Start-Process -NoNewWindow -FilePath "REG" -ArgumentList "DELETE", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate", "/v", "PingID", "/f" -RedirectStandardError "NUL"
|
||||
Start-Process -NoNewWindow -FilePath "REG" -ArgumentList "DELETE", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate", "/v", "SusClientId", "/f" -RedirectStandardError "NUL"
|
||||
Write-Progress -Id 6 -ParentId 0 -Activity "Removing WSUS client settings" -Status "Completed" -PercentComplete 100
|
||||
}
|
||||
|
||||
|
||||
@@ -73,31 +73,9 @@ function Invoke-WPFImpex {
|
||||
Write-Error "Failed to load the JSON file from the specified path or URL: $_"
|
||||
return
|
||||
}
|
||||
$isLegacyConfig = $jsonFile -is [System.Management.Automation.PSCustomObject] -and
|
||||
$null -ne $jsonFile.PSObject.Properties["Install"] -and
|
||||
$null -ne $jsonFile.PSObject.Properties["WPFInstall"]
|
||||
if ($isLegacyConfig) {
|
||||
Write-WinUtilLog -Component "Impex" -Message "Detected legacy WinUtil config structure; flattening import object."
|
||||
# Legacy exports stored checkbox keys in WPFInstall and duplicated package
|
||||
# source metadata in Install. Current package IDs come from the app catalog,
|
||||
# so only the selection-key properties are restored.
|
||||
$flattenedJson = @(
|
||||
foreach ($property in $jsonFile.PSObject.Properties) {
|
||||
if ($property.Name -notmatch '^WPF(?:Install|Tweaks|Toggle|Feature|Appx)') {
|
||||
continue
|
||||
}
|
||||
|
||||
foreach ($selection in @($property.Value)) {
|
||||
if ($selection -is [string] -and -not [string]::IsNullOrWhiteSpace($selection)) {
|
||||
$selection
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
# New style config: flat array of strings
|
||||
$flattenedJson = $jsonFile
|
||||
}
|
||||
# TODO how to handle old style? detected json type then flatten it in a func?
|
||||
# $flattenedJson = $jsonFile.PSObject.Properties.Where({ $_.Name -ne "Install" }).ForEach({ $_.Value })
|
||||
$flattenedJson = $jsonFile
|
||||
|
||||
if (-not $flattenedJson) {
|
||||
[System.Windows.MessageBox]::Show(
|
||||
@@ -106,37 +84,15 @@ function Invoke-WPFImpex {
|
||||
return
|
||||
}
|
||||
|
||||
# Modern configs stay strict. Legacy configs can reference entries that no
|
||||
# longer exist, so restore supported selections and report the retired keys.
|
||||
if ($isLegacyConfig) {
|
||||
$skippedSelections = @(Update-WinUtilSelections -flatJson $flattenedJson -Replace -SkipUnknown)
|
||||
# Clear all existing selections before importing so the import replaces
|
||||
# the current state rather than merging with it
|
||||
$sync.selectedAppx = [System.Collections.Generic.List[string]]::new()
|
||||
$sync.selectedApps = [System.Collections.Generic.List[string]]::new()
|
||||
$sync.selectedTweaks = [System.Collections.Generic.List[string]]::new()
|
||||
$sync.selectedToggles = [System.Collections.Generic.List[string]]::new()
|
||||
$sync.selectedFeatures = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
if ($skippedSelections.Count -gt 0) {
|
||||
$skippedSummary = $skippedSelections -join ", "
|
||||
Write-WinUtilLog -Component "Impex" -Level "WARN" -Message "Skipped unsupported legacy selections: $skippedSummary"
|
||||
}
|
||||
|
||||
if ($skippedSelections.Count -eq @($flattenedJson).Count) {
|
||||
if ($sync.Form) {
|
||||
Show-WinUtilMessage -Message "This legacy configuration contains no settings supported by this version of WinUtil. No changes have been made." -Title "Unsupported Legacy Configuration" -Icon "Warning" | Out-Null
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if ($skippedSelections.Count -gt 0) {
|
||||
$skippedDisplay = @($skippedSelections | Select-Object -First 10) -join ", "
|
||||
if ($skippedSelections.Count -gt 10) {
|
||||
$skippedDisplay += "`n...and $($skippedSelections.Count - 10) more. See the WinUtil log for details."
|
||||
}
|
||||
if ($sync.Form) {
|
||||
Show-WinUtilMessage -Message "Supported settings were imported. The following retired settings were skipped:`n`n$skippedDisplay" -Title "Legacy Configuration Partially Imported" -Icon "Warning" | Out-Null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# Build and validate every imported selection before replacing the current
|
||||
# state. This keeps a malformed config from leaving partial selections behind.
|
||||
Update-WinUtilSelections -flatJson $flattenedJson -Replace
|
||||
}
|
||||
Update-WinUtilSelections -flatJson $flattenedJson
|
||||
|
||||
if ($sync.Form) {
|
||||
Reset-WPFCheckBoxes -doToggles $true
|
||||
|
||||
@@ -224,7 +224,7 @@ function Invoke-WPFUIElements {
|
||||
$toggleButton = New-Object Windows.Controls.Primitives.ToggleButton
|
||||
$toggleButton.Name = $entryInfo.Name
|
||||
$toggleButton.Content = $entryInfo.Content[1]
|
||||
$toggleButton.ToolTip = Get-WinUtilEntryToolTip -Description $entryInfo.Description -Key $entryInfo.Name
|
||||
$toggleButton.ToolTip = $entryInfo.Description
|
||||
$toggleButton.HorizontalAlignment = "Left"
|
||||
$toggleButton.Style = $ToggleButtonStyle
|
||||
[System.Windows.Automation.AutomationProperties]::SetName($toggleButton, $entryInfo.Content[0])
|
||||
@@ -490,7 +490,7 @@ function Invoke-WPFUIElements {
|
||||
$checkBox.Name = $entryInfo.Name
|
||||
$checkBox.Content = $entryInfo.Content
|
||||
$checkBox.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "FontSize")
|
||||
$checkBox.ToolTip = Get-WinUtilEntryToolTip -Description $entryInfo.Description -Key $entryInfo.Name
|
||||
$checkBox.ToolTip = $entryInfo.Description
|
||||
$checkBox.SetResourceReference([Windows.Controls.Control]::MarginProperty, "CheckBoxMargin")
|
||||
$checkBox.UseLayoutRounding = $true
|
||||
[System.Windows.Automation.AutomationProperties]::SetName($checkBox, $entryInfo.Content)
|
||||
|
||||
@@ -217,16 +217,6 @@ Describe "Tweaks config" {
|
||||
throw ($invalidTweaks -join "`n")
|
||||
}
|
||||
}
|
||||
|
||||
It "keeps the location service disabled" -TestCases $testCase {
|
||||
param([string]$Path)
|
||||
|
||||
$tweaks = Get-Content -Path $Path -Raw | ConvertFrom-Json
|
||||
$locationServices = @($tweaks.WPFTweaksLocation.service | Where-Object Name -eq "lfsvc")
|
||||
|
||||
$locationServices | Should -HaveCount 1
|
||||
$locationServices[0].StartupType | Should -Be "Disabled"
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Preset config" {
|
||||
@@ -264,9 +254,8 @@ Describe "App navigation config" {
|
||||
It "is wired to an existing XAML target grid" {
|
||||
$mainScript = Get-Content -Path $script:mainScriptPath -Raw
|
||||
$tabInitializerScript = Get-Content -Path (Join-Path $script:repoRoot "functions/private/Initialize-WinUtilTabContent.ps1") -Raw
|
||||
$uiInitializerScript = Get-Content -Path (Join-Path $script:repoRoot "functions/public/Initialize-WPFUI.ps1") -Raw
|
||||
$targetGridMatch = [regex]::Match(
|
||||
"$mainScript`n$tabInitializerScript`n$uiInitializerScript",
|
||||
"$mainScript`n$tabInitializerScript",
|
||||
'Invoke-WPFUIElements\s+-configVariable\s+\$sync\.configs\.appnavigation\s+-targetGridName\s+"([^"]+)"'
|
||||
)
|
||||
|
||||
@@ -439,7 +428,6 @@ Describe "UI-rendered config entries" {
|
||||
$tweaks = Get-WinUtilConfigObject -Name "tweaks"
|
||||
$requiredFields = @("Content", "category", "panel", "link")
|
||||
$allowedTypes = @("Button", "Combobox", "Toggle", "ToggleButton")
|
||||
$allowedServiceStartupTypes = @("Automatic", "AutomaticDelayedStart", "Disabled", "Manual")
|
||||
$supportedButtons = Get-WinUtilButtonSwitchNames
|
||||
$invalidEntries = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
@@ -510,13 +498,6 @@ Describe "UI-rendered config entries" {
|
||||
foreach ($missingField in (Get-WinUtilMissingRequiredFields -EntryName "$($entry.Name),service" -Entry $serviceEntry -RequiredFields @("Name", "StartupType", "OriginalType"))) {
|
||||
$invalidEntries.Add($missingField)
|
||||
}
|
||||
|
||||
foreach ($startupTypeField in @("StartupType", "OriginalType")) {
|
||||
if ((Test-WinUtilHasNonEmptyProperty -Object $serviceEntry -Name $startupTypeField) -and
|
||||
$allowedServiceStartupTypes -notcontains $serviceEntry.$startupTypeField) {
|
||||
$invalidEntries.Add("$($entry.Name),service has unsupported $startupTypeField '$($serviceEntry.$startupTypeField)'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"Install": [
|
||||
{
|
||||
"winget": "Git.Git",
|
||||
"choco": "git"
|
||||
},
|
||||
{
|
||||
"winget": "Retired.App",
|
||||
"choco": "retired-app"
|
||||
}
|
||||
],
|
||||
"WPFTweaks": [
|
||||
"WPFTweaksTelemetry"
|
||||
],
|
||||
"WPFFeature": [
|
||||
"WPFFeatureSandbox"
|
||||
],
|
||||
"WPFInstall": [
|
||||
"WPFInstallGit",
|
||||
"WPFInstallRetired"
|
||||
]
|
||||
}
|
||||
@@ -8,10 +8,12 @@ BeforeAll {
|
||||
function Invoke-WPFUIElements {
|
||||
param($configVariable, [string]$targetGridName, [int]$columncount)
|
||||
}
|
||||
function Initialize-WPFUI {
|
||||
param([string]$TargetGridName)
|
||||
}
|
||||
function Invoke-WinUtilISOCheckExistingWork { }
|
||||
function Reset-WPFCheckBoxes { param([bool]$doToggles) }
|
||||
|
||||
. (Join-Path $script:repoRoot "functions\public\Initialize-WPFUI.ps1")
|
||||
. (Join-Path $script:repoRoot "functions\private\Initialize-WinUtilTabContent.ps1")
|
||||
}
|
||||
|
||||
@@ -39,7 +41,9 @@ Describe "Initialize-WinUtilTabContent" {
|
||||
Initialize-WinUtilTabContent -TabName "Install"
|
||||
Initialize-WinUtilTabContent -TabName "Install"
|
||||
|
||||
|
||||
Should -Invoke -CommandName Invoke-WPFUIElements -Times 1 -Exactly -ParameterFilter {
|
||||
$targetGridName -eq "appscategory" -and $columncount -eq 1
|
||||
}
|
||||
Should -Invoke -CommandName Initialize-WPFUI -Times 1 -Exactly -ParameterFilter {
|
||||
$TargetGridName -eq "appscategory"
|
||||
}
|
||||
@@ -101,32 +105,6 @@ Describe "Initialize-WinUtilTabContent" {
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Initialize-WPFUI" {
|
||||
BeforeEach {
|
||||
$script:sync = [Hashtable]::Synchronized(@{
|
||||
configs = @{
|
||||
appnavigation = [pscustomobject]@{}
|
||||
}
|
||||
})
|
||||
|
||||
Mock Invoke-WPFUIElements { throw "App category rendered" }
|
||||
}
|
||||
|
||||
AfterEach {
|
||||
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
It "renders app navigation through the app category target" {
|
||||
{ Initialize-WPFUI -TargetGridName "appscategory" } | Should -Throw "App category rendered"
|
||||
|
||||
Should -Invoke -CommandName Invoke-WPFUIElements -Times 1 -Exactly -ParameterFilter {
|
||||
$configVariable -eq $script:sync.configs.appnavigation -and
|
||||
$targetGridName -eq "appscategory" -and
|
||||
$columncount -eq 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Startup lazy tab wiring" {
|
||||
It "builds only install tab content before first paint" {
|
||||
$mainScript = Get-Content -Path (Join-Path $script:repoRoot "scripts\main.ps1") -Raw
|
||||
|
||||
@@ -360,19 +360,6 @@ Describe "Find-AppsByNameOrDescription" {
|
||||
$editorItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
|
||||
}
|
||||
|
||||
It "matches apps by preset key" {
|
||||
$browserItem = New-WinUtilAppSearchItem -Tag "WPFInstallBrowser"
|
||||
$mediaItem = New-WinUtilAppSearchItem -Tag "WPFInstallMedia"
|
||||
$category = New-WinUtilAppCategory -Label "- Browsers" -Items @($browserItem, $mediaItem)
|
||||
New-WinUtilAppSearchContext -Categories @($category)
|
||||
|
||||
Find-AppsByNameOrDescription -SearchString "WPFInstallBrowser"
|
||||
|
||||
$browserItem.Visibility | Should -Be ([Windows.Visibility]::Visible)
|
||||
$mediaItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
|
||||
$category.Visibility | Should -Be ([Windows.Visibility]::Visible)
|
||||
}
|
||||
|
||||
It "treats wildcard characters as literal app search text" {
|
||||
$literalItem = New-WinUtilAppSearchItem -Tag "WPFInstallLiteral"
|
||||
$mediaItem = New-WinUtilAppSearchItem -Tag "WPFInstallMedia"
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
#===========================================================================
|
||||
# Tests - Entry ToolTip Helper
|
||||
#===========================================================================
|
||||
|
||||
BeforeAll {
|
||||
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
||||
. (Join-Path $script:repoRoot "functions\private\Get-WinUtilEntryToolTip.ps1")
|
||||
. (Join-Path $script:repoRoot "functions\private\Update-WinUtilSelections.ps1")
|
||||
|
||||
$applications = Get-Content (Join-Path $script:repoRoot "config\applications.json") -Raw | ConvertFrom-Json
|
||||
$applicationsHashtable = @{}
|
||||
foreach ($property in $applications.PSObject.Properties) {
|
||||
$applicationsHashtable["WPFInstall$($property.Name)"] = $property.Value
|
||||
}
|
||||
$appx = Get-Content (Join-Path $script:repoRoot "config\appx.json") -Raw | ConvertFrom-Json
|
||||
$appxHashtable = @{}
|
||||
foreach ($property in $appx.PSObject.Properties) {
|
||||
$appxHashtable[$property.Name] = $property.Value
|
||||
}
|
||||
$script:selectionConfigs = @{
|
||||
applicationsHashtable = $applicationsHashtable
|
||||
tweaks = Get-Content (Join-Path $script:repoRoot "config\tweaks.json") -Raw | ConvertFrom-Json
|
||||
feature = Get-Content (Join-Path $script:repoRoot "config\feature.json") -Raw | ConvertFrom-Json
|
||||
appxHashtable = $appxHashtable
|
||||
}
|
||||
|
||||
# Map each control type the renderer handles to whether its branch adds the preset key.
|
||||
# Read from the source AST so re-adding the helper to an unsupported branch fails the test.
|
||||
$script:rendererClauses = @{}
|
||||
$rendererPath = Join-Path $script:repoRoot "functions\public\Invoke-WPFUIElements.ps1"
|
||||
$rendererAst = [System.Management.Automation.Language.Parser]::ParseFile($rendererPath, [ref]$null, [ref]$null)
|
||||
$typeSwitch = $rendererAst.FindAll({
|
||||
$args[0] -is [System.Management.Automation.Language.SwitchStatementAst]
|
||||
}, $true) | Where-Object { $_.Clauses.Item1.Extent.Text -contains '"Combobox"' }
|
||||
|
||||
foreach ($clause in $typeSwitch.Clauses) {
|
||||
$clauseName = $clause.Item1.Extent.Text.Trim('"')
|
||||
$script:rendererClauses[$clauseName] = [bool]($clause.Item2.Extent.Text -match 'Get-WinUtilEntryToolTip')
|
||||
}
|
||||
$script:rendererClauses["default"] = [bool]($typeSwitch.Default.Extent.Text -match 'Get-WinUtilEntryToolTip')
|
||||
|
||||
$appRendererPath = Join-Path $script:repoRoot "functions\private\Initialize-InstallAppEntry.ps1"
|
||||
$script:appRenderer = Get-Content $appRendererPath -Raw
|
||||
|
||||
# Every entry the renderer draws, with the branch that draws it
|
||||
$script:renderedEntries = foreach ($configName in "appnavigation", "tweaks", "feature", "appx") {
|
||||
$config = Get-Content (Join-Path $script:repoRoot "config\$configName.json") -Raw | ConvertFrom-Json
|
||||
foreach ($property in $config.PSObject.Properties) {
|
||||
$clauseName = "default"
|
||||
if ($property.Value.Type -and $script:rendererClauses.ContainsKey($property.Value.Type)) {
|
||||
$clauseName = $property.Value.Type
|
||||
}
|
||||
[pscustomobject]@{
|
||||
Config = $configName
|
||||
Key = $property.Name
|
||||
Clause = $clauseName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Get-WinUtilEntryToolTip" {
|
||||
It "appends the preset key after the description" {
|
||||
Get-WinUtilEntryToolTip -Description "Fast private browser" -Key "WPFInstallBrave" |
|
||||
Should -Be "Fast private browser`n`nPreset key: WPFInstallBrave"
|
||||
}
|
||||
|
||||
It "returns only the key line when description is null" {
|
||||
Get-WinUtilEntryToolTip -Description $null -Key "WPFTweaksTele" |
|
||||
Should -Be "Preset key: WPFTweaksTele"
|
||||
}
|
||||
|
||||
It "returns only the key line when description is whitespace" {
|
||||
Get-WinUtilEntryToolTip -Description " " -Key "WPFTweaksTele" |
|
||||
Should -Be "Preset key: WPFTweaksTele"
|
||||
}
|
||||
|
||||
It "returns a plain string, not a UI object" {
|
||||
(Get-WinUtilEntryToolTip -Description "x" -Key "WPFTweaksTele").GetType().Name |
|
||||
Should -Be "String"
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Preset key tooltips" {
|
||||
It "leaves unsupported controls unlabelled" {
|
||||
# Comboboxes and radio buttons are not representable in the flat preset format.
|
||||
# Toggle keys can be imported, but preset execution does not apply their state.
|
||||
$script:rendererClauses["Toggle"] | Should -BeFalse
|
||||
$script:rendererClauses["Combobox"] | Should -BeFalse
|
||||
$script:rendererClauses["RadioButton"] | Should -BeFalse
|
||||
}
|
||||
|
||||
It "labels entries created by the application renderer" {
|
||||
$script:appRenderer | Should -Match '\$border\.ToolTip\s*=\s*Get-WinUtilEntryToolTip\s+-Description\s+\$app\.description\s+-Key\s+\$appKey'
|
||||
}
|
||||
|
||||
It "labels every entry with a key the preset importer accepts" {
|
||||
$labelled = @($script:renderedEntries | Where-Object { $script:rendererClauses[$_.Clause] })
|
||||
$labelled.Count | Should -BeGreaterThan 0
|
||||
|
||||
foreach ($entry in $labelled) {
|
||||
$sync = @{
|
||||
selectedApps = [System.Collections.Generic.List[string]]::new()
|
||||
selectedTweaks = [System.Collections.Generic.List[string]]::new()
|
||||
selectedToggles = [System.Collections.Generic.List[string]]::new()
|
||||
selectedFeatures = [System.Collections.Generic.List[string]]::new()
|
||||
selectedAppx = [System.Collections.Generic.List[string]]::new()
|
||||
configs = $script:selectionConfigs
|
||||
}
|
||||
|
||||
{ Update-WinUtilSelections -flatJson @($entry.Key) } |
|
||||
Should -Not -Throw -Because "$($entry.Config).json key '$($entry.Key)' is shown as a preset key"
|
||||
|
||||
$imported = @($sync.Values | ForEach-Object { $_ })
|
||||
$imported | Should -Contain $entry.Key -Because "importing '$($entry.Key)' must restore that selection"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
#===========================================================================
|
||||
# Tests - GitHub Issue Triage Workflow
|
||||
#===========================================================================
|
||||
|
||||
BeforeAll {
|
||||
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
||||
$script:workflowPath = Join-Path $script:repoRoot ".github\workflows\triage-tools.yaml"
|
||||
$script:guidePath = Join-Path $script:repoRoot "docs\src\content\docs\code-reference\issue-triage.mdx"
|
||||
$script:workflow = Get-Content -Path $script:workflowPath -Raw
|
||||
$script:guide = Get-Content -Path $script:guidePath -Raw
|
||||
}
|
||||
|
||||
Describe "GitHub issue triage workflow" {
|
||||
It "cannot run against pull requests" {
|
||||
$script:workflow | Should -Match '(?m)^\s+if:\s+\$\{\{\s*!github\.event\.issue\.pull_request\s*\}\}\s*$'
|
||||
$script:workflow | Should -Match 'if \(context\.payload\.issue\.pull_request\)'
|
||||
$script:workflow | Should -Match '(?m)^\s+issues:\s+write\s*$'
|
||||
$script:workflow | Should -Match '(?m)^\s+pull-requests:\s+none\s*$'
|
||||
$script:workflow | Should -Match '(?m)^\s+contents:\s+none\s*$'
|
||||
$script:workflow | Should -Not -Match '(?m)^\s+pull-requests:\s+write\s*$'
|
||||
}
|
||||
|
||||
It "authorizes trusted users by immutable numeric ID" {
|
||||
$script:workflow | Should -Match 'trustedUsers\.includes\(commentAuthor\.id\)'
|
||||
$script:workflow | Should -Not -Match 'trustedUsers\.includes\(commentAuthor\.login\)'
|
||||
}
|
||||
|
||||
It "matches one complete command at a time" {
|
||||
$script:workflow.Contains('const command = comment.body.trim().toLowerCase();') | Should -BeTrue
|
||||
$script:workflow.Contains('const triageMatch = command.match(/^\/triage$/);') | Should -BeTrue
|
||||
$script:workflow.Contains('const triageOffMatch = command.match(/^\/triageoff$/);') | Should -BeTrue
|
||||
$script:workflow.Contains('const notPlannedMatch = command.match(/^\/np(?:\s+(\w+))?$/);') | Should -BeTrue
|
||||
$script:workflow.Contains('const duplicateMatch = command.match(/^\/duplicate\s+#?(\d+)$/);') | Should -BeTrue
|
||||
$script:workflow.Contains('} else if (triageOffMatch) {') | Should -BeTrue
|
||||
$script:workflow.Contains('} else if (notPlannedMatch) {') | Should -BeTrue
|
||||
$script:workflow.Contains('} else if (duplicateMatch) {') | Should -BeTrue
|
||||
}
|
||||
|
||||
It "preserves existing labels for not-related closures" {
|
||||
$notRelatedBlock = [regex]::Match(
|
||||
$script:workflow,
|
||||
'(?s)reason === "notrelated".*?github\.rest\.issues\.addLabels\(\{.*?labels: \["not-related"\].*?github\.rest\.issues\.update'
|
||||
)
|
||||
|
||||
$notRelatedBlock.Success | Should -BeTrue
|
||||
$notRelatedBlock.Value | Should -Not -Match 'issues\.update\(\{.*?labels:'
|
||||
}
|
||||
|
||||
It "validates duplicate targets before closing the issue" {
|
||||
$script:workflow.Contains('const duplicateIssueNumber = Number(duplicateMatch[1]);') | Should -BeTrue
|
||||
$script:workflow | Should -Match 'Number\.isSafeInteger\(duplicateIssueNumber\)'
|
||||
$script:workflow | Should -Match 'duplicateIssueNumber === issueNumber'
|
||||
$script:workflow | Should -Match 'duplicateIssue = response\.data'
|
||||
$script:workflow | Should -Match 'duplicateIssue\?\.pull_request'
|
||||
$script:workflow | Should -Match 'error\.status !== 404'
|
||||
$script:workflow | Should -Match 'throw error'
|
||||
$script:workflow | Should -Match 'duplicate_issue_id: duplicateIssue\.id'
|
||||
}
|
||||
|
||||
It "keeps the maintainer guide in the documentation site" {
|
||||
Test-Path -Path (Join-Path $script:repoRoot ".github\TRIAGE_TOOLS.md") | Should -BeFalse
|
||||
$script:guide | Should -Match 'These commands work only on issues\.'
|
||||
$script:guide | Should -Match '`not-related` label without replacing existing labels'
|
||||
|
||||
$astroConfig = Get-Content -Path (Join-Path $script:repoRoot "docs\astro.config.mjs") -Raw
|
||||
$astroConfig | Should -Match "slug: 'code-reference/issue-triage'"
|
||||
}
|
||||
}
|
||||
+2
-213
@@ -48,17 +48,12 @@ namespace System.Windows.Controls
|
||||
|
||||
public class WrapPanel
|
||||
{
|
||||
public object Visibility { get; set; }
|
||||
public global::Windows.Visibility Visibility { get; set; }
|
||||
}
|
||||
|
||||
public class StackPanel
|
||||
{
|
||||
public System.Collections.ArrayList Children { get; private set; }
|
||||
|
||||
public StackPanel()
|
||||
{
|
||||
Children = new System.Collections.ArrayList();
|
||||
}
|
||||
public System.Collections.ArrayList Children { get; } = new System.Collections.ArrayList();
|
||||
}
|
||||
}
|
||||
"@
|
||||
@@ -66,7 +61,6 @@ namespace System.Windows.Controls
|
||||
|
||||
. (Join-Path $script:repoRoot "functions\private\Update-WinUtilSelections.ps1")
|
||||
. (Join-Path $script:repoRoot "functions\private\Reset-WPFCheckBoxes.ps1")
|
||||
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFImpex.ps1")
|
||||
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFGetInstalled.ps1")
|
||||
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFSelectedCheckboxesUpdate.ps1")
|
||||
. (Join-Path $script:repoRoot "functions\public\Invoke-WPFButton.ps1")
|
||||
@@ -93,9 +87,6 @@ namespace System.Windows.Controls
|
||||
function Write-WinUtilLog {
|
||||
param($Message, $Level, $Component)
|
||||
}
|
||||
function Show-WinUtilMessage {
|
||||
param($Message, $Title, $Button, $Icon)
|
||||
}
|
||||
|
||||
function script:New-WinUtilFakeCheckBox {
|
||||
param([bool]$IsChecked = $false)
|
||||
@@ -136,16 +127,6 @@ namespace System.Windows.Controls
|
||||
Content = "Git"
|
||||
}
|
||||
}
|
||||
appxHashtable = @{
|
||||
WPFAppxExample = [pscustomobject]@{}
|
||||
}
|
||||
tweaks = [pscustomobject]@{
|
||||
WPFTweaksTelemetry = [pscustomobject]@{}
|
||||
WPFToggleDarkMode = [pscustomobject]@{}
|
||||
}
|
||||
feature = [pscustomobject]@{
|
||||
WPFFeatureSandbox = [pscustomobject]@{}
|
||||
}
|
||||
}
|
||||
WPFselectedAppsButton = [pscustomobject]@{
|
||||
Content = ""
|
||||
@@ -193,198 +174,6 @@ Describe "Update-WinUtilSelections" {
|
||||
@($script:sync.selectedFeatures) | Should -Be @("WPFFeatureSandbox")
|
||||
@($script:sync.selectedAppx) | Should -Be @("WPFAppxExample")
|
||||
}
|
||||
|
||||
It "replaces selections only after every imported key is validated" {
|
||||
$script:sync.selectedApps.Add("WPFInstallExisting")
|
||||
$script:sync.selectedTweaks.Add("WPFTweaksExisting")
|
||||
|
||||
Update-WinUtilSelections -flatJson @(
|
||||
"WPFInstallGit",
|
||||
"WPFFeatureSandbox"
|
||||
) -Replace
|
||||
|
||||
@($script:sync.selectedApps) | Should -Be @("WPFInstallGit")
|
||||
@($script:sync.selectedTweaks) | Should -Be @()
|
||||
@($script:sync.selectedFeatures) | Should -Be @("WPFFeatureSandbox")
|
||||
}
|
||||
|
||||
It "preserves existing selections when an imported key is unsupported" {
|
||||
$script:sync.selectedApps.Add("WPFInstallExisting")
|
||||
$script:sync.selectedTweaks.Add("WPFTweaksExisting")
|
||||
|
||||
{
|
||||
Update-WinUtilSelections -flatJson @(
|
||||
"WPFInstallGit",
|
||||
"NotAWinUtilKey"
|
||||
) -Replace
|
||||
} | Should -Throw "Unsupported selection key 'NotAWinUtilKey'."
|
||||
|
||||
@($script:sync.selectedApps) | Should -Be @("WPFInstallExisting")
|
||||
@($script:sync.selectedTweaks) | Should -Be @("WPFTweaksExisting")
|
||||
@($script:sync.selectedFeatures) | Should -Be @()
|
||||
}
|
||||
|
||||
It "preserves existing selections when an imported key is not in the current catalog" {
|
||||
$script:sync.selectedApps.Add("WPFInstallExisting")
|
||||
|
||||
{
|
||||
Update-WinUtilSelections -flatJson @(
|
||||
"WPFInstallGit",
|
||||
"WPFInstallUnknown"
|
||||
) -Replace
|
||||
} | Should -Throw "Unknown selection key 'WPFInstallUnknown'."
|
||||
|
||||
@($script:sync.selectedApps) | Should -Be @("WPFInstallExisting")
|
||||
@($script:sync.selectedTweaks) | Should -Be @()
|
||||
@($script:sync.selectedFeatures) | Should -Be @()
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Invoke-WPFImpex import selection state" {
|
||||
BeforeEach {
|
||||
New-WinUtilUiStateTestContext
|
||||
$script:sync.Form = [pscustomobject]@{}
|
||||
|
||||
Mock Reset-WPFCheckBoxes { }
|
||||
Mock Write-Error { }
|
||||
Mock Write-WinUtilLog { }
|
||||
Mock Show-WinUtilMessage { }
|
||||
}
|
||||
|
||||
AfterEach {
|
||||
Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
|
||||
Remove-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
It "replaces selections and resets the UI after a valid import" {
|
||||
$script:sync.selectedApps.Add("WPFInstallExisting")
|
||||
$script:sync.selectedTweaks.Add("WPFTweaksExisting")
|
||||
$configPath = Join-Path $TestDrive "valid-config.json"
|
||||
@(
|
||||
"WPFInstallGit",
|
||||
"WPFFeatureSandbox"
|
||||
) | ConvertTo-Json | Set-Content -LiteralPath $configPath
|
||||
|
||||
Invoke-WPFImpex -type "import" -Config $configPath
|
||||
|
||||
@($script:sync.selectedApps) | Should -Be @("WPFInstallGit")
|
||||
@($script:sync.selectedTweaks) | Should -Be @()
|
||||
@($script:sync.selectedFeatures) | Should -Be @("WPFFeatureSandbox")
|
||||
Should -Invoke -CommandName Reset-WPFCheckBoxes -Times 1 -Exactly -ParameterFilter {
|
||||
$doToggles -eq $true
|
||||
}
|
||||
Should -Invoke -CommandName Write-Error -Times 0 -Exactly
|
||||
}
|
||||
|
||||
It "imports supported legacy selections and reports retired entries" {
|
||||
$configPath = Join-Path $script:repoRoot "pester\fixtures\legacy-config.json"
|
||||
|
||||
Invoke-WPFImpex -type "import" -Config $configPath
|
||||
|
||||
@($script:sync.selectedApps) | Should -Be @("WPFInstallGit")
|
||||
@($script:sync.selectedTweaks) | Should -Be @("WPFTweaksTelemetry")
|
||||
@($script:sync.selectedFeatures) | Should -Be @("WPFFeatureSandbox")
|
||||
Should -Invoke -CommandName Reset-WPFCheckBoxes -Times 1 -Exactly -ParameterFilter {
|
||||
$doToggles -eq $true
|
||||
}
|
||||
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
|
||||
$Component -eq "Impex" -and
|
||||
$Level -eq "WARN" -and
|
||||
$Message -eq "Skipped unsupported legacy selections: WPFInstallRetired"
|
||||
}
|
||||
Should -Invoke -CommandName Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter {
|
||||
$Title -eq "Legacy Configuration Partially Imported" -and
|
||||
$Message -like "*WPFInstallRetired*"
|
||||
}
|
||||
Should -Invoke -CommandName Write-Error -Times 0 -Exactly
|
||||
}
|
||||
|
||||
It "ignores unrelated string metadata in a legacy configuration" {
|
||||
$configPath = Join-Path $TestDrive "legacy-config-with-metadata.json"
|
||||
[pscustomobject]@{
|
||||
Install = @([pscustomobject]@{ winget = "Git.Git"; choco = "git" })
|
||||
WPFInstall = @("WPFInstallGit")
|
||||
ExportVersion = "1.0"
|
||||
} | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath $configPath
|
||||
|
||||
Invoke-WPFImpex -type "import" -Config $configPath
|
||||
|
||||
@($script:sync.selectedApps) | Should -Be @("WPFInstallGit")
|
||||
Should -Invoke -CommandName Reset-WPFCheckBoxes -Times 1 -Exactly
|
||||
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
|
||||
$Component -eq "Impex" -and
|
||||
$Message -eq "Detected legacy WinUtil config structure; flattening import object."
|
||||
}
|
||||
Should -Invoke -CommandName Show-WinUtilMessage -Times 0 -Exactly
|
||||
Should -Invoke -CommandName Write-Error -Times 0 -Exactly
|
||||
}
|
||||
|
||||
It "preserves selections when every legacy entry is retired" {
|
||||
$script:sync.selectedApps.Add("WPFInstallExisting")
|
||||
$configPath = Join-Path $TestDrive "retired-legacy-config.json"
|
||||
[pscustomobject]@{
|
||||
Install = @([pscustomobject]@{ winget = "Retired.App"; choco = "retired-app" })
|
||||
WPFTweaks = @()
|
||||
WPFFeature = @()
|
||||
WPFInstall = @("WPFInstallRetired")
|
||||
} | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath $configPath
|
||||
|
||||
Invoke-WPFImpex -type "import" -Config $configPath
|
||||
|
||||
@($script:sync.selectedApps) | Should -Be @("WPFInstallExisting")
|
||||
Should -Invoke -CommandName Reset-WPFCheckBoxes -Times 0 -Exactly
|
||||
Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
|
||||
$Message -eq "Skipped unsupported legacy selections: WPFInstallRetired"
|
||||
}
|
||||
Should -Invoke -CommandName Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter {
|
||||
$Title -eq "Unsupported Legacy Configuration"
|
||||
}
|
||||
Should -Invoke -CommandName Write-Error -Times 0 -Exactly
|
||||
}
|
||||
|
||||
It "imports legacy selection groups without treating Install metadata as a selection" {
|
||||
$legacyConfigPath = Join-Path $TestDrive "legacy-config.json"
|
||||
[ordered]@{
|
||||
Install = @(
|
||||
[pscustomobject]@{
|
||||
winget = "Git.Git"
|
||||
choco = "git"
|
||||
}
|
||||
)
|
||||
WPFInstall = @("WPFInstallGit")
|
||||
WPFTweaks = @("WPFTweaksTelemetry")
|
||||
WPFToggle = @("WPFToggleDarkMode")
|
||||
WPFFeature = @("WPFFeatureSandbox")
|
||||
} | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $legacyConfigPath
|
||||
|
||||
Invoke-WPFImpex -type "import" -Config $legacyConfigPath
|
||||
|
||||
@($script:sync.selectedApps) | Should -Be @("WPFInstallGit")
|
||||
@($script:sync.selectedTweaks) | Should -Be @("WPFTweaksTelemetry")
|
||||
@($script:sync.selectedToggles) | Should -Be @("WPFToggleDarkMode")
|
||||
@($script:sync.selectedFeatures) | Should -Be @("WPFFeatureSandbox")
|
||||
Should -Invoke -CommandName Write-Error -Times 0 -Exactly
|
||||
}
|
||||
|
||||
It "preserves selections and does not reset the UI after an invalid import" {
|
||||
$script:sync.selectedApps.Add("WPFInstallExisting")
|
||||
$script:sync.selectedTweaks.Add("WPFTweaksExisting")
|
||||
$configPath = Join-Path $TestDrive "invalid-config.json"
|
||||
@(
|
||||
"WPFInstallGit",
|
||||
"WPFInstallUnknown"
|
||||
) | ConvertTo-Json | Set-Content -LiteralPath $configPath
|
||||
|
||||
Invoke-WPFImpex -type "import" -Config $configPath
|
||||
|
||||
@($script:sync.selectedApps) | Should -Be @("WPFInstallExisting")
|
||||
@($script:sync.selectedTweaks) | Should -Be @("WPFTweaksExisting")
|
||||
@($script:sync.selectedFeatures) | Should -Be @()
|
||||
Should -Invoke -CommandName Reset-WPFCheckBoxes -Times 0 -Exactly
|
||||
Should -Invoke -CommandName Write-Error -Times 1 -Exactly -ParameterFilter {
|
||||
$Message -like "An error occurred while importing: *Unknown selection key 'WPFInstallUnknown'.*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Invoke-WPFSelectedCheckboxesUpdate" {
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
#===========================================================================
|
||||
# Tests - Get-WinUtilVariables
|
||||
#===========================================================================
|
||||
|
||||
BeforeAll {
|
||||
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
||||
$script:originalSyncVariable = Get-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue
|
||||
if ($script:originalSyncVariable) {
|
||||
$script:originalSyncValue = $script:originalSyncVariable.Value
|
||||
}
|
||||
$global:sync = [Hashtable]::Synchronized(@{})
|
||||
|
||||
# Setup some test variables
|
||||
$global:sync["WPFTestString"] = "I am a string"
|
||||
$global:sync["WPFTestObj1"] = [PSCustomObject]@{ Name = "Test1" }
|
||||
$global:sync["WPFTestObj2"] = [PSCustomObject]@{ Name = "Test2" }
|
||||
$global:sync["WPFTestButton"] = [System.Version]::new("1.0.0.0")
|
||||
$global:sync["OtherVar"] = "Not a WPF variable"
|
||||
|
||||
. (Join-Path $script:repoRoot "functions\private\Get-WinUtilVariables.ps1")
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
if ($script:originalSyncVariable) {
|
||||
Set-Variable -Name sync -Value $script:originalSyncValue -Scope Global -Force
|
||||
} else {
|
||||
Remove-Variable -Name sync -Scope Global -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Get-WinUtilVariables" {
|
||||
|
||||
It "returns all WPF-prefixed keys when no type is provided" {
|
||||
$result = Get-WinUtilVariables
|
||||
$result.Count | Should -Be 4
|
||||
$result | Should -Contain "WPFTestString"
|
||||
$result | Should -Contain "WPFTestObj1"
|
||||
$result | Should -Contain "WPFTestObj2"
|
||||
$result | Should -Contain "WPFTestButton"
|
||||
$result | Should -Not -Contain "OtherVar"
|
||||
}
|
||||
|
||||
It "returns only WPF keys matching the specified exact type" {
|
||||
$result = Get-WinUtilVariables -Type "String"
|
||||
$result.Count | Should -Be 1
|
||||
$result | Should -Contain "WPFTestString"
|
||||
}
|
||||
|
||||
It "returns multiple objects matching PSCustomObject" {
|
||||
$result = Get-WinUtilVariables -Type "PSCustomObject"
|
||||
$result.Count | Should -Be 2
|
||||
$result | Should -Contain "WPFTestObj1"
|
||||
$result | Should -Contain "WPFTestObj2"
|
||||
}
|
||||
|
||||
It "returns an empty list when no matching type is found" {
|
||||
$result = Get-WinUtilVariables -Type "Int32"
|
||||
$result | Should -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
+2
-7
@@ -1120,8 +1120,7 @@
|
||||
Name="SearchBar"
|
||||
Foreground="{DynamicResource MainForegroundColor}" Background="{DynamicResource MainBackgroundColor}"
|
||||
Padding="3,3,30,0"
|
||||
ToolTip="Press Ctrl-F and type app name to filter application list below. Press Esc to reset the filter"
|
||||
AutomationProperties.Name="Search">
|
||||
ToolTip="Press Ctrl-F and type app name to filter application list below. Press Esc to reset the filter">
|
||||
</TextBox>
|
||||
<TextBlock
|
||||
Name="SearchBarIcon"
|
||||
@@ -1137,7 +1136,6 @@
|
||||
VerticalAlignment="Center" HorizontalAlignment="Right"
|
||||
Name="SearchBarClearButton"
|
||||
Style="{StaticResource SearchBarClearButtonStyle}"
|
||||
AutomationProperties.Name="Clear Search"
|
||||
Margin="0,0,20,0" Visibility="Collapsed">
|
||||
</Button>
|
||||
|
||||
@@ -1155,7 +1153,6 @@
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Content="N/A"
|
||||
ToolTip="Change the WinUtil UI Theme"
|
||||
AutomationProperties.Name="Theme"
|
||||
/>
|
||||
<Popup Name="ThemePopup"
|
||||
IsOpen="False"
|
||||
@@ -1194,7 +1191,6 @@
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Content=""
|
||||
ToolTip="Adjust Font Scaling for Accessibility"
|
||||
AutomationProperties.Name="Font Scaling"
|
||||
/>
|
||||
<Popup Name="FontScalingPopup"
|
||||
IsOpen="False"
|
||||
@@ -1222,8 +1218,7 @@
|
||||
TickPlacement="BottomRight"
|
||||
IsSnapToTickEnabled="True"
|
||||
Width="120"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="Font Scaling"/>
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBlock Text="Large"
|
||||
FontSize="{DynamicResource ButtonFontSize}"
|
||||
Foreground="{DynamicResource MainForegroundColor}"
|
||||
|
||||
Reference in New Issue
Block a user