This commit is contained in:
EthanShoeDev
2025-11-30 17:52:15 -05:00
commit 01a35296a9
12 changed files with 1309 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
---
description: Use Bun instead of Node.js, npm, pnpm, or vite.
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
alwaysApply: false
---
Default to using Bun instead of Node.js.
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
- Use `bun test` instead of `jest` or `vitest`
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
- Bun automatically loads .env, so don't use dotenv.
## APIs
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
- `Bun.redis` for Redis. Don't use `ioredis`.
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
- `WebSocket` is built-in. Don't use `ws`.
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
- Bun.$`ls` instead of execa.
## Testing
Use `bun test` to run tests.
```ts#index.test.ts
import { test, expect } from "bun:test";
test("hello world", () => {
expect(1).toBe(1);
});
```
## Frontend
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
Server:
```ts#index.ts
import index from "./index.html"
Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})
```
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
```html#index.html
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>
```
With the following `frontend.tsx`:
```tsx#frontend.tsx
import React from "react";
// import .css files directly and it works
import './index.css';
import { createRoot } from "react-dom/client";
const root = createRoot(document.body);
export default function Frontend() {
return <h1>Hello, world!</h1>;
}
root.render(<Frontend />);
```
Then, run index.ts
```sh
bun --hot ./index.ts
```
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.

34
.gitignore vendored Normal file
View File

@@ -0,0 +1,34 @@
# dependencies (bun install)
node_modules
# output
out
dist
*.tgz
# code coverage
coverage
*.lcov
# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# caches
.eslintcache
.cache
*.tsbuildinfo
# IntelliJ based IDEs
.idea
# Finder (MacOS) folder config
.DS_Store

4
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"typescript.tsdk": "./node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}

317
README.md Normal file
View File

@@ -0,0 +1,317 @@
I really love my nixos config. I install it on all my linux boxes and it makes it dead simple to configure them exactly how I want them. I want a taste of that experience for my windows boxes. I am trying to evaluate different approaches. Can you help me evaluate a few different approaches?
- I do not like python so ansible sounds not great but I would like to know what all features they have configured out of the box
- I really like typescript and projects like sst and pulumi. They seem to have a lot of overlap with what I want to do, given they have a `diff` and `apply` command.
I cloned my nixos-config to docs\nixos-config
and I cloned several of the tools I am evaluating to docs\cloned-repos-as-docs
I think it would be very useful for you to grep through all of that and get an understanding of what each tool can do what.
The main things I want:
- Be able to declare what programs are installed on the box
- Declare that wsl2 be enabled
- Declare that there should be a specific wsl distro installed.
- Declare the `"C:\Users\ethan\.wslconfig"` settings
- Declare the windows theme (dark)
- Have the system be modular so I can enable/disable some programs for different hosts.
# WinOS Config
A declarative configuration system for Windows 11, inspired by NixOS. The goal is to describe the complete state of a Windows machine in version-controlled configuration files.
## Project Goals
- **Declarative**: Define _what_ the system should look like, not _how_ to get there
- **Reproducible**: Apply the same config to multiple machines with identical results
- **Version controlled**: Track all configuration changes in git
- **Modular**: Compose configurations for different hosts (gaming-pc, work-laptop, htpc)
## Current Status: Research & Planning
This project is in the early research phase. We are evaluating technologies and documenting what is/isn't possible on Windows.
---
## Technology Options Under Consideration
### 1. DSC v3 (Microsoft Desired State Configuration)
**Pros:**
- Actively maintained (v3.1.2 released Nov 2025, v3.2.0-preview.8 in progress)
- Complete rewrite in Rust - no PowerShell dependency
- Cross-platform (Windows, macOS, Linux)
- JSON/YAML configuration files
- JSON Schema available for validation/type generation
- Resources can be written in any language
- Adapters for legacy PowerShell DSC resources
- WinGet integration via `microsoft/winget-dsc`
**Cons:**
- No official TypeScript/Python/Go SDKs or language bindings
- Configuration is YAML/JSON (not a real programming language)
- **Does NOT auto-uninstall packages when removed from config**
- Limited built-in resources - most functionality requires adapters
- No native WSL configuration resource
- No Start Menu/taskbar pin configuration
**Resources:**
- Repo: https://github.com/PowerShell/DSC
- Docs: https://learn.microsoft.com/en-us/powershell/dsc/overview
- Schemas: https://github.com/PowerShell/DSC/tree/main/schemas
- WinGet DSC: https://github.com/microsoft/winget-dsc
- Samples: https://aka.ms/dsc.yaml
### 2. Ansible
**Pros:**
- Mature ecosystem with large community
- YAML-based with inventory system for multi-host
- Idempotent task execution
- Good Windows support via WinRM
**Cons:**
- Requires control node (Linux/WSL) to run FROM
- YAML configuration (not a real language)
- **Does NOT auto-uninstall packages when removed from config** - requires explicit `state: absent`
- Windows modules less mature than Linux
### 3. PowerShell DSC v2 (Legacy)
**Pros:**
- Full PowerShell language features
- Native Windows integration
- Can compose configurations programmatically
**Cons:**
- Being superseded by DSC v3
- MOF-based, more complex
- **Does NOT auto-uninstall when removed from config**
### 4. Custom TypeScript Tooling
**Pros:**
- Full language features (types, functions, loops, conditionals)
- Can generate DSC v3 YAML from TypeScript
- Type safety via JSON Schema → TypeScript generation
- Familiar tooling (Bun, npm ecosystem)
**Cons:**
- Must build/maintain the tooling ourselves
- Thin wrapper around DSC v3 CLI
### 5. WinGet Configuration (standalone)
**Pros:**
- Native Windows, simple YAML
- Built on DSC v3 under the hood
- `winget configure` command
**Cons:**
- Single file per configuration (limited modularity)
- **Does NOT auto-uninstall when removed from config**
---
## The Uninstall Problem
### NixOS Behavior (What We Want)
In NixOS, removing a package from `configuration.nix` and running `nixos-rebuild switch` will:
1. Build a new system profile without that package
2. Switch to the new profile
3. The package is no longer available
### Windows Reality (What We Get)
**No Windows configuration tool provides true "remove on deletion" behavior.**
| Tool | Behavior When Package Removed From Config |
|------|-------------------------------------------|
| DSC v3 | Stops managing it - package remains installed |
| Ansible | Nothing happens - requires explicit `state: absent` |
| WinGet Config | Nothing happens - package remains installed |
| Chocolatey DSC | Nothing happens - package remains installed |
### Workarounds
1. **Explicit "absent" lists**: Maintain a separate list of packages that should NOT be present
```yaml
packages_present:
- Google.Chrome
- Git.Git
packages_absent:
- Microsoft.Edge # Explicitly remove
```
2. **State diffing**: Build tooling that compares current state vs desired state and generates removal tasks
3. **Full reset approach**: Periodically wipe and reapply (not practical for daily use)
---
## What Can Be Configured
### Fully Supported
| Category | Method | Notes |
|----------|--------|-------|
| **Package Installation** | WinGet DSC | Searches winget-pkgs repository |
| **Registry Settings** | `Microsoft.Windows/Registry` | Theme, Explorer options, etc. |
| **Environment Variables** | PSDscResources adapter | System and user variables |
| **Windows Services** | PSDscResources adapter | Start/stop, startup type |
| **Windows Features** | PSDscResources adapter | Enable/disable optional features |
### Partially Supported
| Category | Method | Limitations |
|----------|--------|-------------|
| **WSL Enable** | WindowsOptionalFeature | Works via adapter |
| **WSL Distro Install** | WinGet | Install only, no config |
| **File Creation** | Script resource | Not declarative, imperative script |
### Not Supported (Manual/Script Required)
| Category | Why |
|----------|-----|
| **`.wslconfig` file** | No file content resource in DSC v3 |
| **Start Menu Layout** | Complex binary format, export/import only |
| **Taskbar Pinned Apps** | Complex, no resource exists |
| **Default Browser** | Requires user interaction |
| **File Associations** | Registry-based but complex |
| **Microsoft Account Sync** | Cloud-based, not local config |
---
## Package Availability (WinGet Repository)
WinGet uses the community-maintained [winget-pkgs](https://github.com/microsoft/winget-pkgs) repository. You can search at https://winget.run
### Common Developer Tools
| App | WinGet ID | Available |
|-----|-----------|-----------|
| Chrome | `Google.Chrome` | Yes |
| Firefox | `Mozilla.Firefox` | Yes |
| Cursor | `Anysphere.Cursor` | Yes |
| VS Code | `Microsoft.VisualStudioCode` | Yes |
| Git | `Git.Git` | Yes |
| Bun | `Oven-sh.Bun` | Yes |
| Node.js | `OpenJS.NodeJS` | Yes |
| Python | `Python.Python.3.12` | Yes |
| Rust | `Rustlang.Rust.MSVC` | Yes |
### Entertainment
| App | WinGet ID | Available |
|-----|-----------|-----------|
| Spotify | `Spotify.Spotify` | Yes |
| Discord | `Discord.Discord` | Yes |
| Steam | `Valve.Steam` | Yes |
| Epic Games | `EpicGames.EpicGamesLauncher` | Yes |
### Utilities
| App | WinGet ID | Available |
|-----|-----------|-----------|
| 7-Zip | `7zip.7zip` | Yes |
| PowerToys | `Microsoft.PowerToys` | Yes |
| Windows Terminal | `Microsoft.WindowsTerminal` | Yes |
| WinRAR | `RARLab.WinRAR` | Yes |
---
## Registry Paths for Common Settings
### Theme & Appearance
```
# Dark mode (apps)
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize
AppsUseLightTheme = 0 (dark) | 1 (light)
# Dark mode (system)
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize
SystemUsesLightTheme = 0 (dark) | 1 (light)
# Taskbar alignment (Windows 11)
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
TaskbarAl = 0 (left) | 1 (center)
# Accent color on title bars
HKCU\SOFTWARE\Microsoft\Windows\DWM
ColorPrevalence = 0 (off) | 1 (on)
```
### Explorer Settings
```
# Show file extensions
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
HideFileExt = 0 (show) | 1 (hide)
# Show hidden files
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
Hidden = 1 (show) | 2 (hide)
# Show full path in title bar
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\CabinetState
FullPath = 1 (show) | 0 (hide)
```
### Privacy & Telemetry
```
# Disable Cortana
HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search
AllowCortana = 0 (disabled) | 1 (enabled)
# Telemetry level (requires admin)
HKLM\SOFTWARE\Policies\Microsoft\Windows\DataCollection
AllowTelemetry = 0 (security) | 1 (basic) | 2 (enhanced) | 3 (full)
```
---
## DSC v3 JSON Schema (For TypeScript Generation)
DSC v3 publishes JSON schemas that can be used to generate TypeScript types:
```bash
# Download bundled schema
curl -o schemas/dsc-config.schema.json \
https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/config/document.json
# Generate TypeScript types
bunx json-schema-to-typescript schemas/dsc-config.schema.json > src/types/dsc-config.d.ts
```
---
---
## What This Project Will NOT Track
Some things are better left out of declarative config:
| Category | Reason |
|----------|--------|
| **User documents/files** | Use cloud sync (OneDrive, git) |
| **Browser bookmarks/extensions** | Use browser sync |
| **Application-specific settings** | Often stored in AppData, use app's sync |
---
## Next Steps
1. [ ] Decide on primary technology (DSC v3 vs custom TypeScript wrapper)
2. [ ] Set up JSON schema → TypeScript type generation
3. [ ] Create base configuration module
4. [ ] Create first host configuration (gaming-pc)
5. [ ] Document the "absent packages" pattern for uninstalls
6. [ ] Test on fresh Windows 11 VM
---
## References
- [NixOS Config (inspiration)](https://github.com/ethan/nixos-config)
- [DSC v3 Repository](https://github.com/PowerShell/DSC)
- [WinGet DSC Resources](https://github.com/microsoft/winget-dsc)
- [WinGet Package Search](https://winget.run)
- [DSC v3 Documentation](https://learn.microsoft.com/en-us/powershell/dsc/overview)

10
bootstrap.ps1 Normal file
View File

@@ -0,0 +1,10 @@
# Given I plan to use this repo to setup brand new fresh installs of windows.
# and the repo will be using typescript and dsc, I need to install the following packages
# to 'bootstrap' the system.
# Install DSC
winget install DesiredStateConfiguration
# Install Bun
# TODO: Do we want the scripted install or the winget install?
winget install -e --id Oven-sh.Bun
# powershell -c "irm bun.sh/install.ps1|iex"

149
bun.lock Normal file
View File

@@ -0,0 +1,149 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "winos-config",
"dependencies": {
"@effect/cli": "^0.72.1",
"@effect/platform": "^0.93.5",
"@effect/platform-bun": "^0.85.0",
"effect": "^3.19.8",
},
"devDependencies": {
"@effect/language-service": "^0.57.1",
"@types/bun": "latest",
"typescript": "^5.9.3",
},
},
},
"packages": {
"@effect/cli": ["@effect/cli@0.72.1", "", { "dependencies": { "ini": "^4.1.3", "toml": "^3.0.0", "yaml": "^2.5.0" }, "peerDependencies": { "@effect/platform": "^0.93.0", "@effect/printer": "^0.47.0", "@effect/printer-ansi": "^0.47.0", "effect": "^3.19.3" } }, "sha512-HGDMGD23TxFW9tCSX6g+M2u0robikMA0mP0SqeJMj7FWXTdcQ+cQsJE99bxi9iu+5YID7MIrVJMs8TUwXUV2sg=="],
"@effect/cluster": ["@effect/cluster@0.54.0", "", { "dependencies": { "kubernetes-types": "^1.30.0" }, "peerDependencies": { "@effect/platform": "^0.93.5", "@effect/rpc": "^0.72.2", "@effect/sql": "^0.48.5", "@effect/workflow": "^0.14.0", "effect": "^3.19.8" } }, "sha512-Hpxn+z1NaDIN+USM3ml7lp3AQtQHN/VRUfiTzyRJp6nVdUVZRylz8PggEq+O9E1TIeG+TXHtv7/Jq7ME2Ahbdg=="],
"@effect/experimental": ["@effect/experimental@0.57.8", "", { "dependencies": { "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.93.5", "effect": "^3.19.8", "ioredis": "^5", "lmdb": "^3" }, "optionalPeers": ["ioredis", "lmdb"] }, "sha512-3NYIH+ujSR+aBiQ5KqG8RviIdSWORjQUVQbLq8KSTT/DExRgsa8Sw+8IWiGeaaneVQd/PBpNnNqvncxhv+JGHw=="],
"@effect/language-service": ["@effect/language-service@0.57.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-uWzYN+aHl4KfZHGmDxa3+OjV/mk9gMUycIyB8SYvyDiny3lXBtmRFdRVnw6TYL1P5EBfg4N09+lO/0ECRREVXQ=="],
"@effect/platform": ["@effect/platform@0.93.5", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.19.8" } }, "sha512-31ikgJRAG8py26SEIjyrgaPlEJ+JYqOsGP7g4WHFRIYxGFwFX/OrlBrW0+mRISCfeDR1BUztaFyIZyRPfBRvcw=="],
"@effect/platform-bun": ["@effect/platform-bun@0.85.0", "", { "dependencies": { "@effect/platform-node-shared": "^0.55.0", "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/cluster": "^0.54.0", "@effect/platform": "^0.93.5", "@effect/rpc": "^0.72.2", "@effect/sql": "^0.48.5", "effect": "^3.19.8" } }, "sha512-olIFcq2tNow4jRhLNQuuTgcts7qFICvL4z7kI0YoFzvHvXwH+R87b+iTa/5jR7h6KuckapqgIyf1LX5IN2Jj6w=="],
"@effect/platform-node-shared": ["@effect/platform-node-shared@0.55.0", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "multipasta": "^0.2.7", "ws": "^8.18.2" }, "peerDependencies": { "@effect/cluster": "^0.54.0", "@effect/platform": "^0.93.5", "@effect/rpc": "^0.72.2", "@effect/sql": "^0.48.5", "effect": "^3.19.8" } }, "sha512-rmeb3bnt6CKIC+QSyDY0DBa46XQMWRzY3RAsr9MUbxGbPmk1fY8kX0R9rZ8wd1ymi7RlAYXfwE8KvJG2WI2NXA=="],
"@effect/printer": ["@effect/printer@0.47.0", "", { "peerDependencies": { "@effect/typeclass": "^0.38.0", "effect": "^3.19.0" } }, "sha512-VgR8e+YWWhMEAh9qFOjwiZ3OXluAbcVLIOtvp2S5di1nSrPOZxj78g8LE77JSvyfp5y5bS2gmFW+G7xD5uU+2Q=="],
"@effect/printer-ansi": ["@effect/printer-ansi@0.47.0", "", { "dependencies": { "@effect/printer": "^0.47.0" }, "peerDependencies": { "@effect/typeclass": "^0.38.0", "effect": "^3.19.0" } }, "sha512-tDEQ9XJpXDNYoWMQJHFRMxKGmEOu6z32x3Kb8YLOV5nkauEKnKmWNs7NBp8iio/pqoJbaSwqDwUg9jXVquxfWQ=="],
"@effect/rpc": ["@effect/rpc@0.72.2", "", { "dependencies": { "msgpackr": "^1.11.4" }, "peerDependencies": { "@effect/platform": "^0.93.3", "effect": "^3.19.5" } }, "sha512-BmTXybXCOq96D2r9mvSW/YdiTQs5CStnd4II+lfVKrMr3pMNERKLZ2LG37Tfm4Sy3Q8ire6IVVKO/CN+VR0uQQ=="],
"@effect/sql": ["@effect/sql@0.48.5", "", { "dependencies": { "uuid": "^11.0.3" }, "peerDependencies": { "@effect/experimental": "^0.57.7", "@effect/platform": "^0.93.5", "effect": "^3.19.8" } }, "sha512-OhmqA4zaajZc5F4M30W/5UjnjnhW+Z3MIbdTn6KihYOywXG+uSD2RVDhW4HiMWlysD4jv2mapbYSzi+IowYqOg=="],
"@effect/typeclass": ["@effect/typeclass@0.38.0", "", { "peerDependencies": { "effect": "^3.19.0" } }, "sha512-lMUcJTRtG8KXhXoczapZDxbLK5os7M6rn0zkvOgncJW++A0UyelZfMVMKdT5R+fgpZcsAU/1diaqw3uqLJwGxA=="],
"@effect/workflow": ["@effect/workflow@0.14.0", "", { "peerDependencies": { "@effect/experimental": "^0.57.7", "@effect/platform": "^0.93.5", "@effect/rpc": "^0.72.2", "effect": "^3.19.8" } }, "sha512-NPJqScwqe0szAl9IOYNIYnTR7cN00UiMWgOlLD6500ejnjgsaXYnZIWSQZHK54zkOMfsAVVyfuZ0wgVq/K2zMw=="],
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
"@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="],
"@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="],
"@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="],
"@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="],
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
"@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
"@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="],
"@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="],
"@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="],
"@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="],
"@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="],
"@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="],
"@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="],
"@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="],
"@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="],
"@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="],
"@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="],
"@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="],
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
"@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="],
"@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="],
"detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
"effect": ["effect@3.19.8", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-OmLw8EfH02vdmyU2fO4uY9He/wepwKI5E/JNpE2pseaWWUbaYOK9UlxIiKP20ZEqQr+S/jSqRDGmpiqD/2DeCQ=="],
"fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
"find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="],
"ini": ["ini@4.1.3", "", {}, "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="],
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"msgpackr": ["msgpackr@1.11.5", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA=="],
"msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="],
"multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="],
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
"node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="],
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
"yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
"node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
}
}

View File

@@ -0,0 +1,449 @@
# Winget Configure vs DSC CLI: In-Depth Comparison
Both `winget configure` and the `dsc` CLI can apply declarative configurations that include winget packages. This doc explains when to use each, their differences, and practical examples.
---
## Quick Comparison
| Aspect | `winget configure` | `dsc` CLI |
|--------|-------------------|-----------|
| **Focus** | Winget-centric configurations | General-purpose DSC (any resource) |
| **Output** | Human-friendly, interactive | JSON output, scriptable |
| **State checking** | Limited | Full (`get`, `test`, `set`) |
| **Diffing** | No built-in | Yes (`dsc config test`) |
| **Complexity** | Simpler | More powerful |
| **Install** | Built into winget | Separate install (`winget install Microsoft.DSC`) |
---
## Entry Points
### Winget Configure
```powershell
# Apply a configuration
winget configure ./config.dsc.yaml
# Apply with auto-accept (for scripts)
winget configure ./config.dsc.yaml --accept-configuration-agreements
# Show what would happen (dry run) — limited support
winget configure show ./config.dsc.yaml
```
### DSC CLI
```powershell
# Check current state (what IS installed)
dsc config get --file ./config.dsc.yaml
# Test if current state matches desired (diffing)
dsc config test --file ./config.dsc.yaml
# Apply the configuration
dsc config set --file ./config.dsc.yaml
# Export current state to a config
dsc config export --resource Microsoft.WinGet.DSC/WinGetPackage
```
---
## Configuration File Format
Both use the same YAML format (DSCv3 schema):
```yaml
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
properties:
configurationVersion: 0.2.0
resources:
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: install-vscode
directives:
description: Install VS Code
allowPrerelease: true
settings:
id: Microsoft.VisualStudioCode
source: winget
```
---
## Example Project Structures
### Option A: Winget Configure (Simple Setup)
Best for: Personal machine setup, quick provisioning
```
my-windows-config/
├── config.dsc.yaml # Main configuration
├── README.md
└── install.ps1 # Optional wrapper script
```
**config.dsc.yaml**
```yaml
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
properties:
configurationVersion: 0.2.0
resources:
# Developer Tools
- resource: Microsoft.WinGet.DSC/WinGetPackage
directives:
description: Visual Studio Code
settings:
id: Microsoft.VisualStudioCode
source: winget
- resource: Microsoft.WinGet.DSC/WinGetPackage
directives:
description: Git
settings:
id: Git.Git
source: winget
- resource: Microsoft.WinGet.DSC/WinGetPackage
directives:
description: Node.js LTS
settings:
id: OpenJS.NodeJS.LTS
source: winget
version: "20.10.0" # Pinned version
```
**install.ps1**
```powershell
#!/usr/bin/env pwsh
Write-Host "Applying Windows configuration..." -ForegroundColor Cyan
winget configure ./config.dsc.yaml --accept-configuration-agreements
Write-Host "Done!" -ForegroundColor Green
```
**Usage:**
```powershell
.\install.ps1
# or directly:
winget configure ./config.dsc.yaml
```
---
### Option B: DSC CLI (Advanced/Enterprise)
Best for: CI/CD, drift detection, mixed resource types, automation
```
my-windows-config/
├── configs/
│ ├── packages.dsc.yaml # Winget packages
│ ├── settings.dsc.yaml # Windows settings/registry
│ └── full.dsc.yaml # Combined config
├── scripts/
│ ├── apply.ps1
│ ├── test.ps1 # Drift detection
│ └── export.ps1 # Generate config from current state
├── lockfiles/
│ └── packages.lock.yaml # Version-locked config
└── README.md
```
**configs/packages.dsc.yaml**
```yaml
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
properties:
configurationVersion: 0.2.0
resources:
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: vscode
directives:
description: Visual Studio Code
settings:
id: Microsoft.VisualStudioCode
source: winget
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: git
directives:
description: Git
settings:
id: Git.Git
source: winget
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: nodejs
directives:
description: Node.js LTS
settings:
id: OpenJS.NodeJS.LTS
source: winget
```
**scripts/apply.ps1**
```powershell
#!/usr/bin/env pwsh
param(
[string]$Config = "./configs/packages.dsc.yaml"
)
Write-Host "Testing current state..." -ForegroundColor Cyan
$testResult = dsc config test --file $Config | ConvertFrom-Json
if ($testResult.results | Where-Object { $_.result.inDesiredState -eq $false }) {
Write-Host "Drift detected. Applying configuration..." -ForegroundColor Yellow
dsc config set --file $Config
} else {
Write-Host "System is already in desired state." -ForegroundColor Green
}
```
**scripts/test.ps1** (Drift Detection)
```powershell
#!/usr/bin/env pwsh
param(
[string]$Config = "./configs/packages.dsc.yaml"
)
Write-Host "Checking for configuration drift..." -ForegroundColor Cyan
$result = dsc config test --file $Config | ConvertFrom-Json
$drifted = $result.results | Where-Object { $_.result.inDesiredState -eq $false }
if ($drifted) {
Write-Host "`nDrift detected in:" -ForegroundColor Red
$drifted | ForEach-Object {
Write-Host " - $($_.resource.type): $($_.resource.id)" -ForegroundColor Yellow
}
exit 1
} else {
Write-Host "No drift detected. System matches desired state." -ForegroundColor Green
exit 0
}
```
**scripts/export.ps1** (Generate Config from Current State)
```powershell
#!/usr/bin/env pwsh
# Export currently installed winget packages to a DSC config
$packages = winget list --source winget | Select-Object -Skip 2 | ForEach-Object {
$parts = $_ -split '\s{2,}'
if ($parts.Length -ge 2) {
@{
name = $parts[0]
id = $parts[1]
version = if ($parts.Length -ge 3) { $parts[2] } else { $null }
}
}
} | Where-Object { $_ -ne $null }
# Output as DSC YAML format
Write-Output "# Auto-generated from current system state"
Write-Output "# yaml-language-server: `$schema=https://aka.ms/configuration-dsc-schema/0.2"
Write-Output "properties:"
Write-Output " configurationVersion: 0.2.0"
Write-Output " resources:"
foreach ($pkg in $packages) {
Write-Output " - resource: Microsoft.WinGet.DSC/WinGetPackage"
Write-Output " id: $($pkg.id.ToLower() -replace '[^a-z0-9]', '-')"
Write-Output " settings:"
Write-Output " id: $($pkg.id)"
Write-Output " source: winget"
if ($pkg.version) {
Write-Output " version: `"$($pkg.version)`""
}
Write-Output ""
}
```
---
## Version Locking
### With Winget Configure
Pin versions directly in the YAML:
```yaml
resources:
- resource: Microsoft.WinGet.DSC/WinGetPackage
settings:
id: OpenJS.NodeJS.LTS
source: winget
version: "20.10.0" # Exact version lock
```
### With DSC CLI
Same approach, but you can also generate a locked config:
```powershell
# Get current installed versions
dsc config get --file ./config.dsc.yaml > current-state.json
# Or use winget export with versions
winget export -o winget-lock.json --include-versions
```
**Lockfile Pattern:**
Create two files:
- `packages.dsc.yaml` — Desired packages (may float versions)
- `packages.lock.dsc.yaml` — Locked versions for reproducibility
```yaml
# packages.lock.dsc.yaml - Generated/maintained with exact versions
properties:
configurationVersion: 0.2.0
resources:
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: nodejs
settings:
id: OpenJS.NodeJS.LTS
source: winget
version: "20.10.0"
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: git
settings:
id: Git.Git
source: winget
version: "2.43.0"
```
---
## Diffing / Drift Detection
### Winget Configure
Limited support — `winget configure show` gives some info but no true diff:
```powershell
winget configure show ./config.dsc.yaml
```
### DSC CLI (Full Diffing)
```powershell
# Test returns JSON with inDesiredState boolean for each resource
dsc config test --file ./config.dsc.yaml
```
**Example output:**
```json
{
"results": [
{
"resource": {
"type": "Microsoft.WinGet.DSC/WinGetPackage",
"id": "vscode"
},
"result": {
"inDesiredState": true
}
},
{
"resource": {
"type": "Microsoft.WinGet.DSC/WinGetPackage",
"id": "nodejs"
},
"result": {
"inDesiredState": false,
"diff": {
"version": {
"desired": "20.10.0",
"actual": "18.19.0"
}
}
}
}
]
}
```
**CI/CD Integration:**
```powershell
# In a CI pipeline - fail if drift detected
$result = dsc config test --file ./config.dsc.yaml | ConvertFrom-Json
$hasDrift = $result.results | Where-Object { -not $_.result.inDesiredState }
if ($hasDrift) {
Write-Error "Configuration drift detected!"
exit 1
}
```
---
## When to Use Which
### Use `winget configure` when:
- ✅ You want a simple, interactive setup experience
- ✅ Your config is mostly/entirely winget packages
- ✅ You're doing one-time machine provisioning
- ✅ You don't need drift detection
- ✅ You want minimal dependencies (built into winget)
### Use `dsc` CLI when:
- ✅ You need drift detection / compliance checking
- ✅ You're building CI/CD pipelines
- ✅ You want JSON output for automation
- ✅ You're mixing winget with other DSC resources (registry, files, etc.)
- ✅ You need the full `get`/`test`/`set` lifecycle
- ✅ Enterprise configuration management
---
## Mixed Resource Example (DSC Only)
DSC can combine winget packages with other configuration types:
```yaml
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
properties:
configurationVersion: 0.2.0
resources:
# Install VS Code
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: vscode
settings:
id: Microsoft.VisualStudioCode
source: winget
# Configure VS Code settings (after install)
- resource: Microsoft.Windows.Developer/DeveloperMode
id: enable-dev-mode
settings:
Ensure: Present
# Set environment variable
- resource: Microsoft.Windows.Developer/EnvironmentVariable
id: set-editor
settings:
Name: EDITOR
Value: code
Target: User
```
This is where DSC really shines — you can configure the entire system state, not just packages.
---
## Summary
| Task | Winget Configure | DSC CLI |
|------|-----------------|---------|
| Apply config | `winget configure ./config.yaml` | `dsc config set --file ./config.yaml` |
| Check current state | ❌ | `dsc config get --file ./config.yaml` |
| Diff/test | Limited | `dsc config test --file ./config.yaml` |
| Output format | Human text | JSON (scriptable) |
| Version locking | Manual in YAML | Manual in YAML + export tools |
| Multiple resource types | Winget only | Any DSC resource |

159
docs/winget-notes.md Normal file
View File

@@ -0,0 +1,159 @@
# Winget Quick Reference
Windows Package Manager (`winget`) is Microsoft's CLI tool for installing, updating, and managing software.
---
## Search for Packages
```powershell
# Search by name
winget search vscode
# Web-based search UI
# https://solrevdev.com/winget-search/
```
---
## Install
```powershell
# Basic install
winget install <package-name>
# Install with exact ID match (-e = --exact)
winget install -e --id Microsoft.VisualStudioCode
# Install specific version
winget install -e --id Oven-sh.Bun --version 1.1.0
# Accept all agreements automatically (useful for scripts)
winget install -e --id Oven-sh.Bun --accept-package-agreements --accept-source-agreements
```
### Installation Scope
```powershell
# Install for current user only (no admin required)
winget install -e --id Oven-sh.Bun --scope user
# Install system-wide (requires admin)
winget install -e --id Oven-sh.Bun --scope machine
```
---
## Uninstall
```powershell
# Basic uninstall
winget uninstall <package-name>
# Uninstall with exact ID
winget uninstall -e --id Oven-sh.Bun
# Uninstall and remove app data (if supported)
winget uninstall -e --id Oven-sh.Bun --purge
```
---
## Update / Upgrade
```powershell
# List packages with available updates
winget upgrade
# Update a specific package
winget upgrade -e --id Oven-sh.Bun
# Update ALL packages
winget upgrade --all
# Update all, accepting agreements (for scripts)
winget upgrade --all --accept-package-agreements --accept-source-agreements
```
---
## View Package Info
```powershell
# Show package details (version, publisher, license, install URL)
winget show Oven-sh.Bun
# Show all available versions
winget show Oven-sh.Bun --versions
```
---
## List Installed Packages
```powershell
# List all installed packages (from all sources)
winget list
# Filter by name
winget list bun
```
---
## Export / Import (Lockfile-like System)
Export your installed packages to a JSON file for reproducible setups:
```powershell
# Export all packages
winget export -o winget-pkgs.json
# Export with pinned versions
winget export -o winget-pkgs-lock.json --include-versions
```
Import on a new machine:
```powershell
# Install all packages from export file
winget import -i winget-pkgs-lock.json --accept-package-agreements --accept-source-agreements
```
---
## Common Flags Reference
| Flag | Long Form | Description |
|------|-----------|-------------|
| `-e` | `--exact` | Match package ID exactly |
| `-h` | `--silent` | Silent install (no UI) |
| `-i` | `--interactive` | Interactive install (show installer UI) |
| `-o` | `--output` | Output file path |
| `-v` | `--version` | Specify version |
| | `--scope` | `user` or `machine` |
| | `--accept-package-agreements` | Auto-accept package license |
| | `--accept-source-agreements` | Auto-accept source agreements |
---
## Settings
Open winget settings file:
```powershell
winget settings
```
Example settings (JSON):
```json
{
"installBehavior": {
"preferences": {
"scope": "user"
}
}
}
```

20
package.json Normal file
View File

@@ -0,0 +1,20 @@
{
"name": "winos-config",
"module": "index.ts",
"type": "module",
"private": true,
"scripts": {
"prepare": "effect-language-service patch"
},
"dependencies": {
"@effect/cli": "^0.72.1",
"@effect/platform": "^0.93.5",
"@effect/platform-bun": "^0.85.0",
"effect": "^3.19.8"
},
"devDependencies": {
"@effect/language-service": "^0.57.1",
"@types/bun": "latest",
"typescript": "^5.9.3"
}
}

17
src/bin.ts Normal file
View File

@@ -0,0 +1,17 @@
import { Command } from "@effect/cli"
import { BunContext, BunRuntime } from "@effect/platform-bun"
import { Console, Effect } from "effect"
// Define the top-level command
const command = Command.make("hello-world", {}, () =>
Console.log("Hello World")
)
// Set up the CLI application
const cli = Command.run(command, {
name: "Hello World CLI",
version: "v1.0.0"
})
// Prepare and run the CLI application
cli(process.argv).pipe(Effect.provide(BunContext.layer), BunRuntime.runMain)

0
src/cli.ts Normal file
View File

39
tsconfig.json Normal file
View File

@@ -0,0 +1,39 @@
{
"compilerOptions": {
// Environment setup & latest features
"lib": [
"ESNext"
],
"target": "ESNext",
"module": "Preserve",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
// Some stricter flags
"noUnusedParameters": true,
"noPropertyAccessFromIndexSignature": true,
// Effect-ts
// https://www.effect.solutions/tsconfig
"plugins": [
{
"name": "@effect/language-service"
}
],
"rewriteRelativeImportExtensions": true,
"exactOptionalPropertyTypes": true,
"noUnusedLocals": true,
"sourceMap": true,
"resolveJsonModule": true
}
}