commit 01a35296a9f7866e0049b3902b00cda33763ef58 Author: EthanShoeDev <13422990+EthanShoeDev@users.noreply.github.com> Date: Sun Nov 30 17:52:15 2025 -0500 init diff --git a/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc b/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc new file mode 100644 index 0000000..b8100b7 --- /dev/null +++ b/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc @@ -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 ` instead of `node ` or `ts-node ` +- Use `bun test` instead of `jest` or `vitest` +- Use `bun build ` instead of `webpack` or `esbuild` +- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install` +- Use `bun run + + +``` + +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

Hello, world!

; +} + +root.render(); +``` + +Then, run index.ts + +```sh +bun --hot ./index.ts +``` + +For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/.gitignore @@ -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 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..08044fe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "typescript.tsdk": "./node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true + } \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2972477 --- /dev/null +++ b/README.md @@ -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) diff --git a/bootstrap.ps1 b/bootstrap.ps1 new file mode 100644 index 0000000..36165da --- /dev/null +++ b/bootstrap.ps1 @@ -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" \ No newline at end of file diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..a830475 --- /dev/null +++ b/bun.lock @@ -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=="], + } +} diff --git a/docs/winget-configure-vs-dsc.md b/docs/winget-configure-vs-dsc.md new file mode 100644 index 0000000..98a89c2 --- /dev/null +++ b/docs/winget-configure-vs-dsc.md @@ -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 | + diff --git a/docs/winget-notes.md b/docs/winget-notes.md new file mode 100644 index 0000000..eb345d7 --- /dev/null +++ b/docs/winget-notes.md @@ -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 + +# 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 + +# 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" + } + } +} +``` + diff --git a/package.json b/package.json new file mode 100644 index 0000000..3131036 --- /dev/null +++ b/package.json @@ -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" + } +} diff --git a/src/bin.ts b/src/bin.ts new file mode 100644 index 0000000..b32f3d1 --- /dev/null +++ b/src/bin.ts @@ -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) \ No newline at end of file diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..e69de29 diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c6cb0ec --- /dev/null +++ b/tsconfig.json @@ -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 + } +} \ No newline at end of file