This commit is contained in:
EthanShoeDev
2025-12-01 01:34:43 -05:00
parent ff4e7c1676
commit 58b8ee5807
9 changed files with 174 additions and 65 deletions

View File

@@ -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

View File

@@ -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=="],

3
docs/TODO.md Normal file
View File

@@ -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.

View File

@@ -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",

View File

@@ -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<string>;
});
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,
),
);

View File

@@ -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 = <E, R>(
stream: Stream.Stream<Uint8Array, E, R>,
): Effect.Effect<string, E, R> =>
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...');

View File

@@ -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 = <E, R>(
stream: Stream.Stream<Uint8Array, E, R>,
): Effect.Effect<string, E, R> =>
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;
});

View File

@@ -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');