#!/usr/bin/env node import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; import * as NodeServices from "@effect/platform-node/NodeServices"; import * as Config from "effect/Config"; import * as Console from "effect/Console"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Option from "effect/Option"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; import { Argument, Command, Flag } from "effect/unstable/cli"; import { fromJsonStringPretty } from "@t3tools/shared/schemaJson"; export const releasePackageFiles = [ "apps/server/package.json", "apps/desktop/package.json", "apps/web/package.json", "packages/contracts/package.json", ] as const; interface UpdateReleasePackageVersionsOptions { readonly rootDir?: string | undefined; } const PackageJsonSchema = Schema.Record(Schema.String, Schema.Unknown); const PackageJsonPrettyJson = fromJsonStringPretty(PackageJsonSchema); const decodePackageJson = Schema.decodeUnknownEffect(PackageJsonPrettyJson); const encodePackageJson = Schema.encodeEffect(PackageJsonPrettyJson); export const updateReleasePackageVersions = Effect.fn("updateReleasePackageVersions")(function* ( version: string, options: UpdateReleasePackageVersionsOptions = {}, ) { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; const rootDir = path.resolve(options.rootDir ?? process.cwd()); let changed = false; for (const relativePath of releasePackageFiles) { const filePath = path.join(rootDir, relativePath); const packageJson = yield* fs.readFileString(filePath).pipe(Effect.flatMap(decodePackageJson)); if (packageJson.version === version) { continue; } const packageJsonString = yield* encodePackageJson({ ...packageJson, version }); yield* fs.writeFileString(filePath, `${packageJsonString}\n`); changed = true; } return { changed }; }); const writeGithubOutput = Effect.fn("writeGithubOutput")(function* (changed: boolean) { const fs = yield* FileSystem.FileSystem; const githubOutputPath = yield* Config.nonEmptyString("GITHUB_OUTPUT"); yield* fs.writeFileString(githubOutputPath, `changed=${changed}\n`, { flag: "a" }); }); export const updateReleasePackageVersionsCommand = Command.make( "update-release-package-versions", { version: Argument.string("version").pipe( Argument.withDescription("Release version to write into each releasable package.json."), ), root: Flag.string("root").pipe( Flag.withDescription("Workspace root used to resolve the release package manifests."), Flag.optional, ), githubOutput: Flag.boolean("github-output").pipe( Flag.withDescription("Append changed= to GITHUB_OUTPUT."), Flag.withDefault(false), ), }, ({ version, root, githubOutput }) => updateReleasePackageVersions(version, { rootDir: Option.getOrUndefined(root), }).pipe( Effect.tap(({ changed }) => changed ? Effect.void : Console.log("All package.json versions already match release version."), ), Effect.tap(({ changed }) => (githubOutput ? writeGithubOutput(changed) : Effect.void)), ), ).pipe(Command.withDescription("Update release package versions across the workspace.")); if (import.meta.main) { Command.run(updateReleasePackageVersionsCommand, { version: "0.0.0" }).pipe( Effect.provide(NodeServices.layer), NodeRuntime.runMain, ); }