From 9a5e9a0eff50eaf4a534b65f684ee3435bc4e451 Mon Sep 17 00:00:00 2001 From: gdlol Date: Mon, 10 Nov 2025 14:28:23 +0000 Subject: [PATCH 1/4] Update versions --- .config/dotnet/tools.json | 2 +- .config/eslint/eslint.config.ts | 3 +- .config/eslint/plugins.d.ts | 5 -- .config/git/ignore | 2 - .config/typescript/tsconfig.node.json | 4 +- .devcontainer/Dockerfile | 2 +- Workspace.proj | 2 +- package.json | 76 +++++++++---------- scripts/installPackage.ts | 5 +- scripts/node.ts | 37 --------- scripts/project.ts | 2 + scripts/publish.ts | 7 +- scripts/tasks/build.ts | 5 +- setup/configs/typescript/workspace.ts | 1 + setup/package.json | 28 +++---- .../base/.devcontainer/devcontainer.json | 2 +- .../csharp/.config/dotnet/tools.json | 2 +- .../csharp/Automation/Tasks/CSharpier.cs | 4 +- setup/templates/csharp/Workspace.proj | 2 +- .../.config/eslint/eslint.config.ts | 3 +- .../.config/typescript/tsconfig.node.json | 4 +- .../.devcontainer/devcontainer.json | 3 +- setup/templates/typescript/package.json | 34 ++++----- setup/tsconfig.json | 4 +- vite.config.ts | 5 ++ 25 files changed, 101 insertions(+), 143 deletions(-) delete mode 100644 .config/eslint/plugins.d.ts delete mode 100644 scripts/node.ts diff --git a/.config/dotnet/tools.json b/.config/dotnet/tools.json index 5d702d2..dc65a05 100644 --- a/.config/dotnet/tools.json +++ b/.config/dotnet/tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "csharpier": { - "version": "1.0.0", + "version": "1.2.0", "commands": ["csharpier"], "rollForward": false } diff --git a/.config/eslint/eslint.config.ts b/.config/eslint/eslint.config.ts index acbc76d..804e2f2 100644 --- a/.config/eslint/eslint.config.ts +++ b/.config/eslint/eslint.config.ts @@ -1,5 +1,6 @@ import eslint from "@eslint/js"; import vitest from "@vitest/eslint-plugin"; +import { defineConfig } from "eslint/config"; import prettier from "eslint-config-prettier"; import { gitignore } from "eslint-flat-config-gitignore"; import importPlugin from "eslint-plugin-import"; @@ -9,7 +10,7 @@ import eslintPluginUnicorn from "eslint-plugin-unicorn"; import globals from "globals"; import tsESLint from "typescript-eslint"; -export default tsESLint.config( +export default defineConfig( await gitignore(import.meta.dirname), { linterOptions: { reportUnusedDisableDirectives: true }, diff --git a/.config/eslint/plugins.d.ts b/.config/eslint/plugins.d.ts deleted file mode 100644 index d2e071b..0000000 --- a/.config/eslint/plugins.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module "eslint-plugin-import" { - import type { ESLint } from "eslint"; - const eslintPluginImport: ESLint.Plugin; - export default eslintPluginImport; -} diff --git a/.config/git/ignore b/.config/git/ignore index 41b2feb..1366379 100644 --- a/.config/git/ignore +++ b/.config/git/ignore @@ -7,5 +7,3 @@ _* local.env node_modules/ - -dist/ diff --git a/.config/typescript/tsconfig.node.json b/.config/typescript/tsconfig.node.json index b861403..212989c 100644 --- a/.config/typescript/tsconfig.node.json +++ b/.config/typescript/tsconfig.node.json @@ -1,7 +1,7 @@ { - "extends": "@tsconfig/node22/tsconfig.json", + "extends": "@tsconfig/node24/tsconfig.json", "compilerOptions": { "resolveJsonModule": true, - "module": "Node18" + "module": "node18" } } diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index cc342aa..5a7b8ea 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,3 +1,3 @@ -FROM mcr.microsoft.com/devcontainers/javascript-node:22 +FROM mcr.microsoft.com/devcontainers/javascript-node:24-bookworm RUN npm install --global pnpm@latest-10 diff --git a/Workspace.proj b/Workspace.proj index edd7dcb..f65fdf5 100644 --- a/Workspace.proj +++ b/Workspace.proj @@ -1,5 +1,5 @@ - + =22" + "node": ">=24" } } diff --git a/scripts/installPackage.ts b/scripts/installPackage.ts index 204b524..557d346 100644 --- a/scripts/installPackage.ts +++ b/scripts/installPackage.ts @@ -1,10 +1,9 @@ -// eslint-disable-next-line n/no-unsupported-features/node-builtins import { cp, mkdir, rm, writeFile } from "node:fs/promises"; import path from "node:path"; import { $ } from "execa"; -import { packagePrefix, projectRoot } from "@/scripts/project.js"; +import { packageOutputPath, packagePrefix } from "@/scripts/project.js"; import { build } from "@/scripts/tasks/build.js"; import pkg from "@/setup/package.json" with { type: "json" }; @@ -20,7 +19,7 @@ const packageName = `${packagePrefix}${pkg.name}`; const tempPath = `/tmp/${packageName}`; await rm(tempPath, { recursive: true, force: true }); await mkdir(tempPath, { recursive: true }); -await cp(path.resolve(projectRoot, pkg.name, "dist"), tempPath, { recursive: true }); +await cp(packageOutputPath, tempPath, { recursive: true }); await writeFile(path.resolve(tempPath, "pnpm-workspace.yaml"), ""); await writeFile( path.resolve(tempPath, ".npmrc"), diff --git a/scripts/node.ts b/scripts/node.ts deleted file mode 100644 index c056445..0000000 --- a/scripts/node.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { createServer } from "vite"; -import { ViteNodeRunner } from "vite-node/client"; -import { ViteNodeServer } from "vite-node/server"; -import { installSourcemapsSupport } from "vite-node/source-map"; - -const server = await createServer({ - optimizeDeps: { - noDiscovery: true, - include: [], - }, -}); - -const node = new ViteNodeServer(server, { - deps: { - inline: ["isomorphic-git"], - }, -}); - -// fixes stacktraces in Errors -installSourcemapsSupport({ - getSourceMap: (source) => node.getSourceMap(source), -}); - -const runner = new ViteNodeRunner({ - root: server.config.root, - base: server.config.base, - fetchModule(id) { - return node.fetchModule(id); - }, - resolveId(id, importer) { - return node.resolveId(id, importer); - }, -}); - -await runner.executeFile(process.argv[2]); - -await server.close(); diff --git a/scripts/project.ts b/scripts/project.ts index 37a1b3e..a60d9bd 100644 --- a/scripts/project.ts +++ b/scripts/project.ts @@ -7,3 +7,5 @@ export const workspaces = path.resolve(projectRoot, ".."); export const csProjectRoot = path.resolve(projectRoot, "setup/templates/csharp"); export const packagePrefix = "@devcontainer-config/"; + +export const packageOutputPath = path.resolve(projectRoot, `.local/dist`); diff --git a/scripts/publish.ts b/scripts/publish.ts index 2ed30c4..e101769 100644 --- a/scripts/publish.ts +++ b/scripts/publish.ts @@ -1,8 +1,5 @@ -import path from "node:path"; - -import { projectRoot } from "@/scripts/project.js"; +import { packageOutputPath } from "@/scripts/project.js"; import { $$ } from "@/scripts/shell.js"; -import pkg from "@/setup/package.json" with { type: "json" }; await $$`npm set //registry.npmjs.org/:_authToken=\${NPM_TOKEN}`; -await $$`npm publish --access public ${path.resolve(projectRoot, `${pkg.name}/dist`)}`; +await $$`npm publish --access public ${packageOutputPath}`; diff --git a/scripts/tasks/build.ts b/scripts/tasks/build.ts index 46b94ab..1fe01c1 100644 --- a/scripts/tasks/build.ts +++ b/scripts/tasks/build.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line n/no-unsupported-features/node-builtins import fs, { copyFile, cp } from "node:fs/promises"; import { readFile, rm, writeFile } from "node:fs/promises"; import path from "node:path"; @@ -10,11 +9,11 @@ import type { PackageJson } from "type-fest"; import prettierOptions from "@/.config/prettier/.prettierrc.json" with { type: "json" }; import project from "@/package.json" with { type: "json" }; -import { packagePrefix, projectRoot } from "@/scripts/project.js"; +import { packageOutputPath, packagePrefix, projectRoot } from "@/scripts/project.js"; import { $$ } from "@/scripts/shell.js"; import pkg from "@/setup/package.json" with { type: "json" }; -const dist = path.resolve(projectRoot, pkg.name, "dist"); +const dist = packageOutputPath; const insertShebang = async (path: string) => { const content = await readFile(path, "utf-8"); diff --git a/setup/configs/typescript/workspace.ts b/setup/configs/typescript/workspace.ts index 35ba0d4..f672ea2 100644 --- a/setup/configs/typescript/workspace.ts +++ b/setup/configs/typescript/workspace.ts @@ -27,6 +27,7 @@ export const createTypeScriptWorkspaceConfigs = async ( "@commander-js/extra-typings", "@eslint/js", `@tsconfig/node${nodeVersion}`, + "eslint", "eslint-config-prettier", "eslint-flat-config-gitignore", "execa", diff --git a/setup/package.json b/setup/package.json index 186e967..4e61de5 100644 --- a/setup/package.json +++ b/setup/package.json @@ -14,31 +14,31 @@ }, "dependencies": { "@commander-js/extra-typings": "^14.0.0", - "@prettier/plugin-xml": "^3.4.1", - "commander": "~14.0.0", - "cspell-lib": "^9.0.2", - "dedent": "^1.6.0", + "@prettier/plugin-xml": "^3.4.2", + "commander": "~14.0.2", + "cspell-lib": "^9.3.0", + "dedent": "^1.7.0", "default-composer": "^0.6.0", - "fast-xml-parser": "^5.2.3", + "fast-xml-parser": "^5.3.1", "jsonc-parser": "^3.3.1", "lodash-es": "^4.17.21", "oci-registry-client": "^0.2.0", - "pacote": "^21.0.0", - "prettier": "^3.5.3", + "pacote": "^21.0.3", + "prettier": "^3.6.2", "prettier-plugin-ini": "^1.3.0", - "prettier-plugin-packagejson": "^2.5.15", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "write-file-atomic": "^6.0.0" + "prettier-plugin-packagejson": "^2.5.19", + "semver": "^7.7.3", + "type-fest": "^5.2.0", + "write-file-atomic": "^7.0.0" }, "devDependencies": { "@types/lodash-es": "^4.17.12", - "@types/node": "^22.15.29", + "@types/node": "^24.10.0", "@types/pacote": "^11.1.8", - "@types/semver": "^7.7.0", + "@types/semver": "^7.7.1", "@types/write-file-atomic": "^4.0.3" }, "engines": { - "node": ">=22" + "node": ">=24" } } diff --git a/setup/templates/base/.devcontainer/devcontainer.json b/setup/templates/base/.devcontainer/devcontainer.json index 097a039..db02fcc 100644 --- a/setup/templates/base/.devcontainer/devcontainer.json +++ b/setup/templates/base/.devcontainer/devcontainer.json @@ -26,5 +26,5 @@ } } }, - "onCreateCommand": "pnpm install" + "onCreateCommand": "pnpm install || true" } diff --git a/setup/templates/csharp/.config/dotnet/tools.json b/setup/templates/csharp/.config/dotnet/tools.json index 5d702d2..dc65a05 100644 --- a/setup/templates/csharp/.config/dotnet/tools.json +++ b/setup/templates/csharp/.config/dotnet/tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "csharpier": { - "version": "1.0.0", + "version": "1.2.0", "commands": ["csharpier"], "rollForward": false } diff --git a/setup/templates/csharp/Automation/Tasks/CSharpier.cs b/setup/templates/csharp/Automation/Tasks/CSharpier.cs index b94dffc..b099697 100644 --- a/setup/templates/csharp/Automation/Tasks/CSharpier.cs +++ b/setup/templates/csharp/Automation/Tasks/CSharpier.cs @@ -7,7 +7,7 @@ public class CSharpierCheck : FrostingTask { public override void Run(Context context) { - context.DotNetTool($"csharpier check {Context.ProjectRoot}"); + context.DotNetTool(Context.ProjectRoot, "csharpier", $"check {Context.ProjectRoot}"); } } @@ -15,6 +15,6 @@ public class CSharpierFormat : FrostingTask { public override void Run(Context context) { - context.DotNetTool($"csharpier format {Context.ProjectRoot}"); + context.DotNetTool(Context.ProjectRoot, "csharpier", $"format {Context.ProjectRoot}"); } } diff --git a/setup/templates/csharp/Workspace.proj b/setup/templates/csharp/Workspace.proj index 6906247..204d901 100644 --- a/setup/templates/csharp/Workspace.proj +++ b/setup/templates/csharp/Workspace.proj @@ -1,5 +1,5 @@ - + diff --git a/setup/templates/typescript/.config/eslint/eslint.config.ts b/setup/templates/typescript/.config/eslint/eslint.config.ts index af58c76..6a4a996 100644 --- a/setup/templates/typescript/.config/eslint/eslint.config.ts +++ b/setup/templates/typescript/.config/eslint/eslint.config.ts @@ -1,10 +1,11 @@ import eslint from "@eslint/js"; +import { defineConfig } from "eslint/config"; import prettier from "eslint-config-prettier"; import { gitignore } from "eslint-flat-config-gitignore"; import globals from "globals"; import tsESLint from "typescript-eslint"; -export default tsESLint.config( +export default defineConfig( await gitignore(import.meta.dirname), { linterOptions: { reportUnusedDisableDirectives: true }, diff --git a/setup/templates/typescript/.config/typescript/tsconfig.node.json b/setup/templates/typescript/.config/typescript/tsconfig.node.json index b861403..212989c 100644 --- a/setup/templates/typescript/.config/typescript/tsconfig.node.json +++ b/setup/templates/typescript/.config/typescript/tsconfig.node.json @@ -1,7 +1,7 @@ { - "extends": "@tsconfig/node22/tsconfig.json", + "extends": "@tsconfig/node24/tsconfig.json", "compilerOptions": { "resolveJsonModule": true, - "module": "Node18" + "module": "node18" } } diff --git a/setup/templates/typescript/.devcontainer/devcontainer.json b/setup/templates/typescript/.devcontainer/devcontainer.json index 9bb2078..adf0f50 100644 --- a/setup/templates/typescript/.devcontainer/devcontainer.json +++ b/setup/templates/typescript/.devcontainer/devcontainer.json @@ -14,6 +14,5 @@ "typescript.tsdk": "/workspaces/node_modules/typescript/lib" } } - }, - "onCreateCommand": "pnpm install" + } } diff --git a/setup/templates/typescript/package.json b/setup/templates/typescript/package.json index 44f6bf5..a578f73 100644 --- a/setup/templates/typescript/package.json +++ b/setup/templates/typescript/package.json @@ -3,32 +3,32 @@ "type": "module", "dependencies": { "@commander-js/extra-typings": "^14.0.0", - "@eslint/js": "^9.28.0", - "@tsconfig/node22": "^22.0.2", - "commander": "~14.0.0", - "cspell": "^9.0.2", - "eslint": "^9.28.0", - "eslint-config-prettier": "^10.1.5", + "@eslint/js": "^9.39.1", + "@tsconfig/node24": "^24.0.1", + "commander": "~14.0.2", + "cspell": "^9.3.0", + "eslint": "^9.39.1", + "eslint-config-prettier": "^10.1.8", "eslint-flat-config-gitignore": "^3.0.0", "execa": "^9.6.0", - "glob": "^11.0.2", - "globals": "^16.2.0", - "jiti": "^2.4.2", - "prettier": "^3.5.3", - "prettier-plugin-packagejson": "^2.5.15", + "glob": "^11.0.3", + "globals": "^16.5.0", + "jiti": "^2.6.1", + "prettier": "^3.6.2", + "prettier-plugin-packagejson": "^2.5.19", "syncpack": "^13.0.4", - "typescript": "~5.8.3", - "typescript-eslint": "^8.33.1", - "vite": "^6.3.5", - "vite-node": "^3.2.1", + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.3", + "vite": "^7.2.2", + "vite-node": "^5.0.0", "vite-tsconfig-paths": "^5.1.4" }, "devDependencies": { "@types/eslint": "^9.6.1", "@types/eslint-config-prettier": "^6.11.3", - "@types/node": "^22.15.29" + "@types/node": "^24.10.0" }, "engines": { - "node": ">=22" + "node": ">=24" } } diff --git a/setup/tsconfig.json b/setup/tsconfig.json index 405c3a8..34d642c 100644 --- a/setup/tsconfig.json +++ b/setup/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "typeRoots": ["node_modules/@types"], "declaration": true, - "outDir": "dist/" + "outDir": "../.local/dist/" }, - "exclude": ["templates", "dist"] + "exclude": ["templates"] } diff --git a/vite.config.ts b/vite.config.ts index 8496357..c3e206d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,4 +3,9 @@ import tsconfigPaths from "vite-tsconfig-paths"; export default defineConfig({ plugins: [tsconfigPaths()], + server: { + watch: { + ignored: ["**/.local/**"], + }, + }, }); From 4109b8742193499149b1596ca7bc7ea7716eb93d Mon Sep 17 00:00:00 2001 From: gdlol Date: Mon, 10 Nov 2025 16:04:20 +0000 Subject: [PATCH 2/4] tests --- .config/pnpm/rc | 1 + package.json | 1 + scripts/installPackage.ts | 44 ++------------------------- scripts/tasks/installPackage.ts | 45 ++++++++++++++++++++++++++++ scripts/test.ts | 27 ++++++++++++++++- setup/templates/base/.config/pnpm/rc | 1 + 6 files changed, 76 insertions(+), 43 deletions(-) create mode 100644 scripts/tasks/installPackage.ts diff --git a/.config/pnpm/rc b/.config/pnpm/rc index 863fba9..0a946c3 100644 --- a/.config/pnpm/rc +++ b/.config/pnpm/rc @@ -1,3 +1,4 @@ lockfile=false resolution-mode=time-based store-dir=/home/dev/.local/share/pnpm/store +global-bin-dir=/home/dev/.local/bin diff --git a/package.json b/package.json index 61255be..88a5776 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@commander-js/extra-typings": "^14.0.0", + "@devcontainers/cli": "^0.80.1", "@eslint/js": "^9.39.1", "@prettier/plugin-xml": "^3.4.2", "@tsconfig/node24": "^24.0.1", diff --git a/scripts/installPackage.ts b/scripts/installPackage.ts index 557d346..29790ab 100644 --- a/scripts/installPackage.ts +++ b/scripts/installPackage.ts @@ -1,43 +1,3 @@ -import { cp, mkdir, rm, writeFile } from "node:fs/promises"; -import path from "node:path"; +import { installPackage } from "@/scripts/tasks/installPackage.js"; -import { $ } from "execa"; - -import { packageOutputPath, packagePrefix } from "@/scripts/project.js"; -import { build } from "@/scripts/tasks/build.js"; -import pkg from "@/setup/package.json" with { type: "json" }; - -const XDG_DATA_HOME = process.env.XDG_DATA_HOME; -if (!XDG_DATA_HOME) { - throw new Error("XDG_DATA_HOME is not set."); -} - -await build(); - -// Copy the dist folder to a temporary location -const packageName = `${packagePrefix}${pkg.name}`; -const tempPath = `/tmp/${packageName}`; -await rm(tempPath, { recursive: true, force: true }); -await mkdir(tempPath, { recursive: true }); -await cp(packageOutputPath, tempPath, { recursive: true }); -await writeFile(path.resolve(tempPath, "pnpm-workspace.yaml"), ""); -await writeFile( - path.resolve(tempPath, ".npmrc"), - [ - "lockfile=false", - "resolution-mode=time-based", - "shared-workspace-lockfile=false", - "inject-workspace-packages=true", - ].join("\n"), -); - -// Install the package globally after pnpm deploy. -process.env.NODE_ENV = "production"; -const installPath = path.resolve(XDG_DATA_HOME, pkg.name); -await rm(installPath, { recursive: true, force: true }); -const $$ = $({ stdio: "inherit", verbose: "full", cwd: tempPath }); -await $$`pnpm install`; -await $$`pnpm deploy --filter=${packageName} --prod ${installPath}`; -await $$`npm uninstall --global ${packageName}`; -await $$`npm install --global ${installPath}`; -await $$`rm --recursive ${tempPath}`; +await installPackage(); diff --git a/scripts/tasks/installPackage.ts b/scripts/tasks/installPackage.ts new file mode 100644 index 0000000..497bc87 --- /dev/null +++ b/scripts/tasks/installPackage.ts @@ -0,0 +1,45 @@ +import { cp, mkdir, rm, writeFile } from "node:fs/promises"; +import path from "node:path"; + +import { $ } from "execa"; + +import { packageOutputPath, packagePrefix } from "@/scripts/project.js"; +import { build } from "@/scripts/tasks/build.js"; +import pkg from "@/setup/package.json" with { type: "json" }; + +export const installPackage = async () => { + const XDG_DATA_HOME = process.env.XDG_DATA_HOME; + if (!XDG_DATA_HOME) { + throw new Error("XDG_DATA_HOME is not set."); + } + + await build(); + + // Copy the dist folder to a temporary location + const packageName = `${packagePrefix}${pkg.name}`; + const tempPath = `/tmp/${packageName}`; + await rm(tempPath, { recursive: true, force: true }); + await mkdir(tempPath, { recursive: true }); + await cp(packageOutputPath, tempPath, { recursive: true }); + await writeFile(path.resolve(tempPath, "pnpm-workspace.yaml"), ""); + await writeFile( + path.resolve(tempPath, ".npmrc"), + [ + "lockfile=false", + "resolution-mode=time-based", + "shared-workspace-lockfile=false", + "inject-workspace-packages=true", + ].join("\n"), + ); + + // Install the package globally after pnpm deploy. + process.env.NODE_ENV = "production"; + const installPath = path.resolve(XDG_DATA_HOME, pkg.name); + await rm(installPath, { recursive: true, force: true }); + const $$ = $({ stdio: "inherit", verbose: "full", cwd: tempPath }); + await $$`pnpm install`; + await $$`pnpm deploy --filter=${packageName} --prod ${installPath}`; + await $$({ reject: false })`pnpm uninstall --silent --global ${packageName}`; + await $$`pnpm install --global ${installPath}`; + await $$`rm --recursive ${tempPath}`; +}; diff --git a/scripts/test.ts b/scripts/test.ts index 39f0aed..96c8114 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -1 +1,26 @@ -console.log("TODO"); +import { mkdir, rm } from "node:fs/promises"; + +import { $ } from "execa"; + +import { packagePrefix } from "@/scripts/project.js"; +import { installPackage } from "@/scripts/tasks/installPackage.js"; + +await installPackage(); + +const tempPath = `/tmp/${packagePrefix}/tests`; +const $$ = $({ stdio: "inherit", verbose: "full", cwd: tempPath }); + +const reset = async (path: string) => { + await rm(path, { recursive: true, force: true }); + await mkdir(path, { recursive: true }); +}; + +await reset(tempPath); +await $$`create-devcontainer csharp`; +await $$`devcontainer up --remove-existing-container --workspace-folder ${tempPath}`; +await $$`devcontainer exec --workspace-folder ${tempPath} pnpm lint`; + +await reset(tempPath); +await $$`create-devcontainer typescript`; +await $$`devcontainer up --remove-existing-container --workspace-folder ${tempPath}`; +await $$`devcontainer exec --workspace-folder ${tempPath} pnpm lint`; diff --git a/setup/templates/base/.config/pnpm/rc b/setup/templates/base/.config/pnpm/rc index c19b4b1..27810e9 100644 --- a/setup/templates/base/.config/pnpm/rc +++ b/setup/templates/base/.config/pnpm/rc @@ -1,3 +1,4 @@ lockfile=false resolution-mode=time-based store-dir=/home/${remoteUser}/.local/share/pnpm/store +global-bin-dir=/home/${remoteUser}/.local/bin From e7bfb3f39c3bf5e9d4cd4215f81a84a9a2b5075b Mon Sep 17 00:00:00 2001 From: gdlol Date: Mon, 10 Nov 2025 16:19:36 +0000 Subject: [PATCH 3/4] cleanup --- scripts/test.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/scripts/test.ts b/scripts/test.ts index 96c8114..258490a 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -5,9 +5,11 @@ import { $ } from "execa"; import { packagePrefix } from "@/scripts/project.js"; import { installPackage } from "@/scripts/tasks/installPackage.js"; +// Not to be run in CI. + await installPackage(); -const tempPath = `/tmp/${packagePrefix}/tests`; +const tempPath = `/tmp/${packagePrefix}tests`; const $$ = $({ stdio: "inherit", verbose: "full", cwd: tempPath }); const reset = async (path: string) => { @@ -24,3 +26,12 @@ await reset(tempPath); await $$`create-devcontainer typescript`; await $$`devcontainer up --remove-existing-container --workspace-folder ${tempPath}`; await $$`devcontainer exec --workspace-folder ${tempPath} pnpm lint`; + +// https://github.com/devcontainers/cli/issues/386 +await rm(`${tempPath}/.devcontainer/Dockerfile`); +console.log("cleaning up..."); +await $({ + cwd: tempPath, + reject: false, + stdio: "ignore", +})`devcontainer up --remove-existing-container --workspace-folder ${tempPath}`; From 576efee30c6d7a9a47ee42eafc69efbf892d4d8c Mon Sep 17 00:00:00 2001 From: gdlol Date: Tue, 11 Nov 2025 15:01:14 +0000 Subject: [PATCH 4/4] buildx --- .config/appveyor/publish.yml | 9 +++++ .config/cspell/cspell.json | 2 ++ .devcontainer/compose.yaml | 2 +- ReadMe.md | 6 ++++ docker/Dockerfile | 10 ++++++ docker/Dockerfile.dockerignore | 2 ++ package.json | 1 + scripts/buildx.ts | 48 +++++++++++++++++++++++++ scripts/project.ts | 2 ++ scripts/restore.ts | 11 +++++- scripts/tasks/build.ts | 9 +++-- scripts/tasks/deploy.ts | 43 ++++++++++++++++++++++ scripts/tasks/installPackage.ts | 28 +++------------ setup/configs/typescript/workspace.ts | 13 ++----- setup/templates/typescript/package.json | 2 -- 15 files changed, 147 insertions(+), 41 deletions(-) create mode 100644 docker/Dockerfile create mode 100644 docker/Dockerfile.dockerignore create mode 100644 scripts/buildx.ts create mode 100644 scripts/tasks/deploy.ts diff --git a/.config/appveyor/publish.yml b/.config/appveyor/publish.yml index 3f89ea6..bb0ad10 100644 --- a/.config/appveyor/publish.yml +++ b/.config/appveyor/publish.yml @@ -6,6 +6,9 @@ skip_non_tags: true image: Ubuntu2004 environment: NPM_TOKEN: NPM_TOKEN + REGISTRY: ghcr.io + IMAGE_NAME: devcontainer-config/setup + REGISTRY_TOKEN: REGISTRY_TOKEN build_script: - sh: >- set -e @@ -22,3 +25,9 @@ deploy_script: devcontainer exec --workspace-folder . pnpm build devcontainer exec --workspace-folder . --remote-env NPM_TOKEN=${NPM_TOKEN} pnpm run publish + + devcontainer exec --workspace-folder . \ + --remote-env REGISTRY=${REGISTRY} \ + --remote-env IMAGE_NAME=${IMAGE_NAME} \ + --remote-env REGISTRY_TOKEN=${REGISTRY_TOKEN} \ + pnpm run buildx diff --git a/.config/cspell/cspell.json b/.config/cspell/cspell.json index b4e63f4..74ef99b 100644 --- a/.config/cspell/cspell.json +++ b/.config/cspell/cspell.json @@ -5,10 +5,12 @@ "gitignoreRoot": ".", "ignorePaths": ["LICENSE", "local.env"], "words": [ + "buildx", "csharpierignore", "csharpierrc", "devcontainer", "devcontainers", + "distroless", "execa", "globalconfig", "msbuild", diff --git a/.devcontainer/compose.yaml b/.devcontainer/compose.yaml index d334c28..0defc7a 100644 --- a/.devcontainer/compose.yaml +++ b/.devcontainer/compose.yaml @@ -2,7 +2,7 @@ services: devcontainer: env_file: - .env - - path: local.env + - path: ../.local/.env required: false build: context: . diff --git a/ReadMe.md b/ReadMe.md index 7fa5994..a541c90 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1 +1,7 @@ # 🚧 + +```sh +alias create-devcontainer='docker run --rm -it \ + -u $(id -u):$(id -g) -v ${PWD}:${PWD} -w ${PWD} \ + ghcr.io/devcontainer-config/setup' +``` diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..467939c --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,10 @@ +FROM --platform=linux/amd64 gcr.io/distroless/nodejs24-debian12 AS linux-amd64-base +FROM --platform=linux/arm64 gcr.io/distroless/nodejs24-debian12 AS linux-arm64-base + +ARG TARGETOS +ARG TARGETARCH +FROM ${TARGETOS}-${TARGETARCH}-base + +COPY .local/docker /etc/devcontainer-config-setup + +ENTRYPOINT [ "/nodejs/bin/node", "/etc/devcontainer-config-setup/index.js" ] diff --git a/docker/Dockerfile.dockerignore b/docker/Dockerfile.dockerignore new file mode 100644 index 0000000..59646cf --- /dev/null +++ b/docker/Dockerfile.dockerignore @@ -0,0 +1,2 @@ +* +!.local/docker/ diff --git a/package.json b/package.json index 88a5776..c30fa4f 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "type": "module", "scripts": { "build": "pnpm vite-node scripts/build.ts", + "buildx": "pnpm vite-node scripts/buildx.ts", "fix": "pnpm vite-node scripts/fix.ts", "installPackage": "pnpm vite-node scripts/installPackage.ts", "lint": "pnpm vite-node scripts/lint.ts", diff --git a/scripts/buildx.ts b/scripts/buildx.ts new file mode 100644 index 0000000..4c9a735 --- /dev/null +++ b/scripts/buildx.ts @@ -0,0 +1,48 @@ +import path from "node:path"; + +import { $ } from "execa"; + +import { projectRoot } from "@/scripts/project.js"; +import { $$ } from "@/scripts/shell.js"; +import { getVersionTag } from "@/scripts/tasks/build.js"; +import { deploy } from "@/scripts/tasks/deploy.js"; + +const platforms: string[] = ["linux/amd64", "linux/arm64"]; + +const packagePath = path.resolve(projectRoot, ".local/docker"); + +async function buildImage(registry: string, imageName: string, tags: string[], publish: boolean) { + console.log("Building multi-arch Docker image..."); + + const dockerfilePath = path.join(projectRoot, "docker/Dockerfile"); + const dockerPlatforms = platforms.join(","); + + const args = ["buildx", "build", "--file", dockerfilePath, "--platform", dockerPlatforms]; + + for (const tag of tags) { + args.push("--tag", `${registry}/${imageName}:${tag}`); + } + + if (publish) { + args.push("--push"); + } + + args.push(projectRoot); + + await $$`docker ${args}`; + console.log("Docker image built successfully."); +} + +const registry = process.env.REGISTRY ?? "local"; +const imageName = process.env.IMAGE_NAME ?? "setup"; +const version = await getVersionTag(); +const tags = [version, "latest"]; +const token = process.env.REGISTRY_TOKEN; +const publish = !!token; + +if (publish) { + await $({ verbose: "full", input: token })`docker login ${registry} --username USERNAME --password-stdin`; +} + +await deploy(packagePath); +await buildImage(registry, imageName, tags, publish); diff --git a/scripts/project.ts b/scripts/project.ts index a60d9bd..9a88326 100644 --- a/scripts/project.ts +++ b/scripts/project.ts @@ -1,5 +1,7 @@ import path from "node:path"; +export const projectName = "devcontainer-config-setup"; + export const projectRoot = path.resolve(import.meta.dirname, ".."); export const workspaces = path.resolve(projectRoot, ".."); diff --git a/scripts/restore.ts b/scripts/restore.ts index 32235ba..0c14bdb 100644 --- a/scripts/restore.ts +++ b/scripts/restore.ts @@ -1,3 +1,12 @@ -import { cs$$ } from "@/scripts/shell.js"; +import { $ } from "execa"; + +import { projectName } from "@/scripts/project.js"; +import { $$, cs$$ } from "@/scripts/shell.js"; await cs$$`pnpm restore`; + +// Create buildx builder +const { exitCode } = await $({ stdio: "ignore", reject: false })`docker buildx use ${projectName}`; +if (exitCode !== 0) { + await $$`docker buildx create --name ${projectName} --use --bootstrap --driver-opt network=host`; +} diff --git a/scripts/tasks/build.ts b/scripts/tasks/build.ts index 1fe01c1..5bdee8e 100644 --- a/scripts/tasks/build.ts +++ b/scripts/tasks/build.ts @@ -24,12 +24,17 @@ const clean = () => rm(dist, { recursive: true, force: true }); const compile = () => $$`tsc --project ${path.resolve(projectRoot, pkg.name, "tsconfig.json")}`; -const writePackageJson = async () => { +export const getVersionTag = async () => { const tags = await git.listTags({ fs, dir: projectRoot }); + return semver.rsort(tags).at(0) ?? pkg.version; +}; + +const writePackageJson = async () => { + const version = await getVersionTag(); const url = (await git.listRemotes({ fs, dir: projectRoot })).at(0)?.url; const pkgJson: PackageJson = Object.assign({}, pkg as PackageJson, { name: `${packagePrefix}${pkg.name}`, - version: semver.rsort(tags).at(0) ?? pkg.version, + version, ...(url && { repository: { type: "git", url } }), license: project.license, } satisfies PackageJson); diff --git a/scripts/tasks/deploy.ts b/scripts/tasks/deploy.ts new file mode 100644 index 0000000..b66715a --- /dev/null +++ b/scripts/tasks/deploy.ts @@ -0,0 +1,43 @@ +import { cp, mkdir, rm, writeFile } from "node:fs/promises"; +import path from "node:path"; + +import { $ } from "execa"; + +import { packageOutputPath, packagePrefix } from "@/scripts/project.js"; +import { build } from "@/scripts/tasks/build.js"; +import pkg from "@/setup/package.json" with { type: "json" }; + +export const deploy = async (packagePath: string) => { + await build(); + + // Copy the dist folder to a temporary location + const packageName = `${packagePrefix}${pkg.name}`; + const tempPath = `/tmp/${packageName}`; + await rm(tempPath, { recursive: true, force: true }); + await mkdir(tempPath, { recursive: true }); + await cp(packageOutputPath, tempPath, { recursive: true }); + await writeFile(path.resolve(tempPath, "pnpm-workspace.yaml"), ""); + await writeFile( + path.resolve(tempPath, ".npmrc"), + [ + "lockfile=false", + "resolution-mode=time-based", + "shared-workspace-lockfile=false", + "inject-workspace-packages=true", + ].join("\n"), + ); + + await rm(packagePath, { recursive: true, force: true }); + const $$ = $({ + stdio: "inherit", + verbose: "full", + cwd: tempPath, + env: { + ...process.env, + NODE_ENV: "production", + }, + }); + await $$`pnpm install`; + await $$`pnpm deploy --filter=${packageName} --prod ${packagePath}`; + await $$`rm --recursive ${tempPath}`; +}; diff --git a/scripts/tasks/installPackage.ts b/scripts/tasks/installPackage.ts index 497bc87..d050511 100644 --- a/scripts/tasks/installPackage.ts +++ b/scripts/tasks/installPackage.ts @@ -1,10 +1,9 @@ -import { cp, mkdir, rm, writeFile } from "node:fs/promises"; import path from "node:path"; -import { $ } from "execa"; - -import { packageOutputPath, packagePrefix } from "@/scripts/project.js"; +import { packagePrefix } from "@/scripts/project.js"; +import { $$ } from "@/scripts/shell.js"; import { build } from "@/scripts/tasks/build.js"; +import { deploy } from "@/scripts/tasks/deploy.js"; import pkg from "@/setup/package.json" with { type: "json" }; export const installPackage = async () => { @@ -17,29 +16,10 @@ export const installPackage = async () => { // Copy the dist folder to a temporary location const packageName = `${packagePrefix}${pkg.name}`; - const tempPath = `/tmp/${packageName}`; - await rm(tempPath, { recursive: true, force: true }); - await mkdir(tempPath, { recursive: true }); - await cp(packageOutputPath, tempPath, { recursive: true }); - await writeFile(path.resolve(tempPath, "pnpm-workspace.yaml"), ""); - await writeFile( - path.resolve(tempPath, ".npmrc"), - [ - "lockfile=false", - "resolution-mode=time-based", - "shared-workspace-lockfile=false", - "inject-workspace-packages=true", - ].join("\n"), - ); // Install the package globally after pnpm deploy. - process.env.NODE_ENV = "production"; const installPath = path.resolve(XDG_DATA_HOME, pkg.name); - await rm(installPath, { recursive: true, force: true }); - const $$ = $({ stdio: "inherit", verbose: "full", cwd: tempPath }); - await $$`pnpm install`; - await $$`pnpm deploy --filter=${packageName} --prod ${installPath}`; + await deploy(installPath); await $$({ reject: false })`pnpm uninstall --silent --global ${packageName}`; await $$`pnpm install --global ${installPath}`; - await $$`rm --recursive ${tempPath}`; }; diff --git a/setup/configs/typescript/workspace.ts b/setup/configs/typescript/workspace.ts index f672ea2..45bb292 100644 --- a/setup/configs/typescript/workspace.ts +++ b/setup/configs/typescript/workspace.ts @@ -1,9 +1,8 @@ import { defaultComposer } from "default-composer"; -import semver from "semver"; import type { PackageJson } from "type-fest"; import { getNodeLatestLtsVersion } from "../../versions/node.js"; -import { getNpmLatestDependencies, getNpmPackagePeerDependencies } from "../../versions/npm.js"; +import { getNpmLatestDependencies } from "../../versions/npm.js"; import type { BaseConfigs } from "../base/index.js"; import { loadTemplates } from "../templates.js"; @@ -24,7 +23,6 @@ export const createTypeScriptWorkspaceConfigs = async ( const packageJson = JSON.parse(baseConfig["package.json"]) as PackageJson; const nodeVersion = await getNodeLatestLtsVersion(); const dependencies = await getNpmLatestDependencies([ - "@commander-js/extra-typings", "@eslint/js", `@tsconfig/node${nodeVersion}`, "eslint", @@ -46,10 +44,6 @@ export const createTypeScriptWorkspaceConfigs = async ( "@types/eslint-config-prettier", "@types/node", ]); - const commanderVersion = await getNpmPackagePeerDependencies( - "@commander-js/extra-typings", - semver.coerce(dependencies["@commander-js/extra-typings"])!.version, - ); return { "package.json": JSON.stringify( defaultComposer(packageJson, { @@ -57,10 +51,7 @@ export const createTypeScriptWorkspaceConfigs = async ( fix: "vite-node scripts/fix.ts", lint: "vite-node scripts/lint.ts", }, - dependencies: { - ...dependencies, - ...commanderVersion, - }, + dependencies: dependencies, devDependencies: { ...devDependencies, }, diff --git a/setup/templates/typescript/package.json b/setup/templates/typescript/package.json index a578f73..0e5729f 100644 --- a/setup/templates/typescript/package.json +++ b/setup/templates/typescript/package.json @@ -2,10 +2,8 @@ "private": true, "type": "module", "dependencies": { - "@commander-js/extra-typings": "^14.0.0", "@eslint/js": "^9.39.1", "@tsconfig/node24": "^24.0.1", - "commander": "~14.0.2", "cspell": "^9.3.0", "eslint": "^9.39.1", "eslint-config-prettier": "^10.1.8",