diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 36165da..28ace14 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -3,7 +3,15 @@ # to 'bootstrap' the system. # Install DSC -winget install DesiredStateConfiguration +# https://learn.microsoft.com/en-us/powershell/dsc/overview?view=dsc-3.0&preserveView=true#install-dsc-on-windows-with-winget + +# winget search DesiredStateConfiguration --source msstore +# Name Id Version Source +# --------------------------------------------------------------- +# DesiredStateConfiguration 9NVTPZWRC6KQ Unknown msstore +# DesiredStateConfiguration-Preview 9PCX3HX4HZ0Z Unknown msstore + +winget install --id 9NVTPZWRC6KQ --source msstore # Install Bun # TODO: Do we want the scripted install or the winget install? winget install -e --id Oven-sh.Bun diff --git a/bun.lock b/bun.lock index ef22116..2f63955 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,6 @@ "@biomejs/biome": "2.3.8", "@effect/language-service": "^0.57.1", "@types/bun": "latest", - "@typescript/native-preview": "^7.0.0-dev.20251130.1", "quicktype": "^23.2.6", "turbo": "^2.6.1", "typescript": "^5.9.3", @@ -145,22 +144,6 @@ "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], - "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20251130.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20251130.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20251130.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20251130.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20251130.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20251130.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20251130.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20251130.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-0DcbBJM5xMvbUWl5ZNprFno6ZlGmkI1RHN4hW1jJza7D5Um0kSs4CU0fx2Z4uNxhY7a37Y6px5LkM3WF6gePQg=="], - - "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20251130.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-j0h3dgtd/te0r6CKiJWxkb/hrtudWjEbTpuxF3Iki1E87oPtig0mNJyzTgzZFLLWaXM16Iuwb/6Y79j/dWwWmw=="], - - "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20251130.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZjKkYNjzw8XJtai/R7eUIlRA1MK3xleNEHn+lrI5WJjZqzmOeKHc0xhbzW3E9aIDTd1/9cyt9AcPLvrx5FFvMw=="], - - "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20251130.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ovlaIVqiJqtuXaP4R/o+ljcFA3pIIMHJ1LN2bqEQYhJJR3nTbx/xvmGtXG2EC3xzrZCgEMY974izascjaziqEg=="], - - "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20251130.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-FLsbHH4ZqjdZ9t6zawbvt0zx/4YO+XB3+eGBkorTY6uhK66UKkXn4ar9k7ovLjRf1C6Ozjb6qvptf0k9mEJl0Q=="], - - "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20251130.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Z7qOyRxiyQUPaLPb6VA4FtXLkIj/AOSIgaP1zguaIYXJAkfMkQbhMaViLwFziMIxlH3ysr6jbZsBmsdYhVa9cQ=="], - - "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20251130.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-a3xBzkixBqg3a2KnYZLPvGlPEbxNXFdfFimiYXx4CJVeiXqJX5U9zhZ2uAxh3oMMW6pDzQmn87AfM4F3FyxDAA=="], - - "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20251130.1", "", { "os": "win32", "cpu": "x64" }, "sha512-1rJCAv76ScP7inMJPWTN28BuDe55po3BcjQR0/j07hViJPVMn4bI+VlgLV0oZpYOuaM9Uk0ex0e14mr0fwcgQA=="], - "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], diff --git a/docs/TODO.md b/docs/TODO.md new file mode 100644 index 0000000..8b47f7d --- /dev/null +++ b/docs/TODO.md @@ -0,0 +1,3 @@ +Based on https://learn.microsoft.com/en-us/powershell/dsc/reference/schemas/schema-uris?view=dsc-3.0#pinning-to-a-version-folder we should probably rework how we are doing type gen. + +The cli does not expose all the schemas we need. \ No newline at end of file diff --git a/package.json b/package.json index ed9f790..0b6f4e4 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ "typecheck": "tsc --noEmit", "lint:biome": "biome check . --write", "lint:biome:unsafe": "biome check . --unsafe --write", - "lint:biome:check": "biome check ." + "lint:biome:check": "biome check .", + "gen:dsc-types": "bun run scripts/gen-dsc-types.ts", + "gen:dsc-resources-types": "bun run scripts/gen-dsc-resources-types.ts" }, "dependencies": { "@effect/cli": "^0.72.1", diff --git a/scripts/gen-dsc-resources-types.ts b/scripts/gen-dsc-resources-types.ts new file mode 100644 index 0000000..3f4e5bb --- /dev/null +++ b/scripts/gen-dsc-resources-types.ts @@ -0,0 +1,91 @@ +import { Command, FileSystem, Path } from '@effect/platform'; +import { BunContext, BunRuntime } from '@effect/platform-bun'; +import { Effect, Logger, LogLevel } from 'effect'; +import { jsonSchemaToEffectSchema, runCommand } from './lib/script-utils'; + +const getDscResourceJsonSchema = (resourceType: string) => + Effect.gen(function* () { + const commandParts = [ + 'dsc', + 'resource', + 'schema', + '--resource', + resourceType, + ] as const; + yield* Effect.logDebug(`Running command: ${commandParts.join(' ')}`); + const schemaString = yield* Command.make(...commandParts).pipe( + Command.string, + ); + return schemaString.trim(); + }); + +const getAvailableDscResources = () => + Effect.gen(function* () { + yield* Effect.logDebug('Getting available DSC resources...'); + + const dscrResourcesListOutput = yield* Command.make( + 'dsc', + 'resource', + 'list', + '-o', + 'json', + ).pipe(Command.string); + + yield* Effect.logDebug( + `DSC resources list output: ${dscrResourcesListOutput}`, + ); + + return [] as Array; + }); + +const genDscResourcesTypes = Effect.gen(function* () { + yield* Effect.log('Starting DSC resources types generation...'); + + const availableResourceTypes = yield* getAvailableDscResources(); + yield* Effect.log( + `Found available resource types: ${availableResourceTypes.join(', ')}`, + ); + + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + + const outputDir = path.join( + process.cwd(), + 'src', + 'dsc-resource-schema-types', + ); + + yield* Effect.logDebug(`Output directory: ${outputDir}`); + + if (yield* fs.exists(outputDir)) { + yield* Effect.log('Removing existing output directory...'); + yield* fs.remove(outputDir, { recursive: true, force: true }); + } + + yield* fs.makeDirectory(outputDir, { recursive: true }); + + for (const schemaType of availableResourceTypes) { + yield* Effect.logDebug(`Processing: ${schemaType}`); + + const jsonSchema = yield* getDscResourceJsonSchema(schemaType); + const effectSchema = yield* jsonSchemaToEffectSchema({ + jsonSchema, + name: schemaType, + }); + + const outputPath = path.join(outputDir, `${schemaType}.gen.ts`); + yield* fs.writeFileString(outputPath, effectSchema); + + yield* Effect.log(`Generated: ${schemaType}.gen.ts`); + } + + yield* Effect.log('DSC types generation complete!'); +}); + +BunRuntime.runMain( + genDscResourcesTypes.pipe( + Effect.provide(BunContext.layer), + Logger.withMinimumLogLevel(LogLevel.Debug), + Effect.scoped, + ), +); diff --git a/scripts/gen-dsc-types.ts b/scripts/gen-dsc-types.ts index 73ae410..cdbf181 100644 --- a/scripts/gen-dsc-types.ts +++ b/scripts/gen-dsc-types.ts @@ -1,29 +1,7 @@ import { Command, FileSystem, Path } from '@effect/platform'; import { BunContext, BunRuntime } from '@effect/platform-bun'; -import { Effect, Logger, LogLevel, pipe, Stream, String } from 'effect'; -import { convertFromString } from './json-schema-to-effect'; - -// Helper function to collect stream output as a string -const runString = ( - stream: Stream.Stream, -): Effect.Effect => - stream.pipe(Stream.decodeText(), Stream.runFold(String.empty, String.concat)); - -const runCommand = (command: Command.Command) => - pipe( - Command.start(command), - Effect.flatMap((process) => - Effect.all( - [ - process.exitCode, - runString(process.stdout), - runString(process.stderr), - ], - { concurrency: 3 }, - ), - ), - Effect.map(([exitCode, stdout, stderr]) => ({ exitCode, stdout, stderr })), - ); +import { Effect, Logger, LogLevel } from 'effect'; +import { jsonSchemaToEffectSchema, runCommand } from './lib/script-utils'; const getDscJsonSchema = (schemaType: string) => Effect.gen(function* () { @@ -62,28 +40,6 @@ const getPossibleDscSchemaTypes = () => return possibleTypes; }); -const jsonSchemaToEffectSchema = (params: { - jsonSchema: string; - name: string; -}) => - Effect.gen(function* () { - yield* Effect.logDebug(`Converting schema: ${params.name}`); - - const result = convertFromString(params.jsonSchema, params.name); - - yield* Effect.logDebug( - `Converted "${params.name}" - types: ${result.typeNames.join(', ')}`, - ); - - if (result.recursiveTypes.length > 0) { - yield* Effect.logInfo( - `Recursive types in ${params.name}: ${result.recursiveTypes.join(', ')}`, - ); - } - - return result.code; - }); - const genDscTypes = Effect.gen(function* () { yield* Effect.log('Starting DSC types generation...'); diff --git a/scripts/json-schema-to-effect.ts b/scripts/lib/json-schema-to-effect.ts similarity index 100% rename from scripts/json-schema-to-effect.ts rename to scripts/lib/json-schema-to-effect.ts diff --git a/scripts/lib/script-utils.ts b/scripts/lib/script-utils.ts new file mode 100644 index 0000000..84722d5 --- /dev/null +++ b/scripts/lib/script-utils.ts @@ -0,0 +1,47 @@ +import { Command } from '@effect/platform'; +import { Effect, pipe, Stream, String } from 'effect'; +import { convertFromString } from './json-schema-to-effect'; + +// Helper function to collect stream output as a string +const runString = ( + stream: Stream.Stream, +): Effect.Effect => + stream.pipe(Stream.decodeText(), Stream.runFold(String.empty, String.concat)); + +export const runCommand = (command: Command.Command) => + pipe( + Command.start(command), + Effect.flatMap((process) => + Effect.all( + [ + process.exitCode, + runString(process.stdout), + runString(process.stderr), + ], + { concurrency: 3 }, + ), + ), + Effect.map(([exitCode, stdout, stderr]) => ({ exitCode, stdout, stderr })), + ); + +export const jsonSchemaToEffectSchema = (params: { + jsonSchema: string; + name: string; +}) => + Effect.gen(function* () { + yield* Effect.logDebug(`Converting schema: ${params.name}`); + + const result = convertFromString(params.jsonSchema, params.name); + + yield* Effect.logDebug( + `Converted "${params.name}" - types: ${result.typeNames.join(', ')}`, + ); + + if (result.recursiveTypes.length > 0) { + yield* Effect.logInfo( + `Recursive types in ${params.name}: ${result.recursiveTypes.join(', ')}`, + ); + } + + return result.code; + }); diff --git a/src/bin.ts b/src/bin.ts index ee3b926..68b5bbc 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -3,6 +3,25 @@ import { BunContext, BunRuntime } from '@effect/platform-bun'; import { Effect } from 'effect'; import pkg from '../package.json' with { type: 'json' }; +// import type { Configuration } from './dsc-schema-types/configuration.gen'; + +// const machineConfig: typeof Configuration.Type = { +// $schema: 'https://aka.ms/dsc/schemas/v3/config/document.json', +// resources: [ +// { +// name: 'must be unique', +// type: 'Microsoft.Windows/Registry', +// properties: { +// keyPath: 'HKCU\\example\\key', +// valueName: 'Example', +// valueData: { +// String: 'Example Value', +// }, +// }, +// }, +// ], +// }; + const diffCommand = Command.make('diff', {}, () => Effect.gen(function* () { yield* Effect.log('diff');