From b6b8dec6b70db73f8b75749a532661c15f3dd23f Mon Sep 17 00:00:00 2001 From: Daniel Clayton Date: Tue, 3 Mar 2026 07:10:30 -0700 Subject: [PATCH 1/2] docs: clarify dotgithub add generates TypeScript wrappers in root README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9a597e8..b410f04 100644 --- a/README.md +++ b/README.md @@ -33,13 +33,13 @@ dotgithub init dotgithub init --output ./my-workflows ``` -### Add GitHub Actions +### Add GitHub Actions (and generate TypeScript wrappers) ```bash -# Add a specific action +# Add a specific action and generate its TypeScript wrapper/types dotgithub add actions/checkout@v4 -# Add multiple actions +# Add multiple actions and generate wrappers for each dotgithub add actions/setup-node@v4 actions/setup-python@v5 ``` @@ -53,7 +53,7 @@ dotgithub synth ## Basic Usage 1. **Initialize** your project with `dotgithub init` -2. **Configure** actions in `dotgithub.json` +2. **Add actions** with `dotgithub add ...` (this generates TypeScript action wrappers and updates config) 3. **Write** your workflow logic in TypeScript 4. **Synthesize** workflows with `dotgithub synth` From ef1168cf7ea065c7c861d943dfb633e07ac7de42 Mon Sep 17 00:00:00 2001 From: Daniel Clayton Date: Tue, 3 Mar 2026 07:15:30 -0700 Subject: [PATCH 2/2] feat(cli): add init starter templates for node, bun, and monorepo --- .changeset/little-gorillas-accept.md | 5 ++ docs/command-init.md | 21 ++++- packages/cli/src/commands/init.ts | 119 +++++++++++++++++++++++---- 3 files changed, 124 insertions(+), 21 deletions(-) create mode 100644 .changeset/little-gorillas-accept.md diff --git a/.changeset/little-gorillas-accept.md b/.changeset/little-gorillas-accept.md new file mode 100644 index 0000000..5cbf88c --- /dev/null +++ b/.changeset/little-gorillas-accept.md @@ -0,0 +1,5 @@ +--- +"@dotgithub/cli": patch +--- + +Add starter template support to `dotgithub init` (`node-library`, `bun-app`, `monorepo`) and update init command docs. diff --git a/docs/command-init.md b/docs/command-init.md index 77bc0f8..7d03888 100644 --- a/docs/command-init.md +++ b/docs/command-init.md @@ -15,7 +15,8 @@ The `init` command sets up a new DotGitHub project by creating the necessary dir ## Options - `--force` - Overwrite existing files if they exist -- `--output ` - Output directory for the workspace (default: `src`) +- `--output ` - Output directory for the workspace (default: `.github`) +- `--template ` - Starter template (`node-library`, `bun-app`, `monorepo`) ## What it creates @@ -35,7 +36,7 @@ The `init` command creates the following files and directories: dotgithub init ``` -This creates a `src/` directory with all necessary files. +This creates a `.github/` workspace with all necessary files. ### Custom output directory @@ -43,7 +44,19 @@ This creates a `src/` directory with all necessary files. dotgithub init --output ./my-workflows ``` -This creates a `my-workflows/` directory instead of `src/`. +This creates a `my-workflows/` directory instead of `.github/`. + +### Choose a starter template + +```bash +dotgithub init --template bun-app +``` + +Available templates: + +- `node-library` (default) +- `bun-app` +- `monorepo` ### Force overwrite existing files @@ -89,7 +102,7 @@ A basic entry point that imports the DotGitHub core library. After running `init`, you typically: -1. Navigate to the workspace directory: `cd src` +1. Navigate to the workspace directory: `cd .github/src` 2. Install dependencies: `npm install` 3. Add GitHub Actions: `dotgithub add actions/checkout@v4` 4. Write your workflow logic in TypeScript diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 65a634c..cf6fac6 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -2,16 +2,18 @@ import { Command } from 'commander'; import * as fs from 'fs'; import * as path from 'path'; import { - createConfigFile, - writeConfig, createDefaultConfig, type DotGithubContext, logger, } from '@dotgithub/core'; +const SUPPORTED_TEMPLATES = ['node-library', 'bun-app', 'monorepo'] as const; +type StarterTemplate = (typeof SUPPORTED_TEMPLATES)[number]; + export interface InitCommandOptions { force?: boolean; output?: string; + template?: StarterTemplate; } export function createInitCommand( @@ -19,7 +21,7 @@ export function createInitCommand( ): Command { return new Command('init') .description( - 'Initialize a new GitHub Actions workspace with TypeScript and ESM support' + 'Initialize a new GitHub Actions TypeScript workspace with synth-ready starter templates' ) .option('--force', 'Overwrite existing files if they exist', false) .option( @@ -27,6 +29,11 @@ export function createInitCommand( 'Output directory for the workspace (default: .github)', '.github' ) + .option( + '--template ', + `Starter template: ${SUPPORTED_TEMPLATES.join(', ')}`, + 'node-library' + ) .action(async (options: InitCommandOptions) => { try { await initializeWorkspace(options, createContext); @@ -48,6 +55,8 @@ async function initializeWorkspace( const outputDir = options.output || '.github'; const outputDirPath = path.resolve(outputDir); + const template = normalizeTemplate(options.template); + // Create output directory if it doesn't exist if (!fs.existsSync(outputDirPath)) { fs.mkdirSync(outputDirPath, { recursive: true }); @@ -65,9 +74,9 @@ async function initializeWorkspace( // Create default config with workspace output directory const defaultConfig = createDefaultConfig(); - defaultConfig.rootDir = 'src'; // This will be the workspace directory inside the output directory - defaultConfig.outputDir = '.'; // Set output directory to current directory - + defaultConfig.rootDir = 'src'; // Workspace directory inside the output directory + defaultConfig.outputDir = '.'; + // Add a local construct definition defaultConfig.constructs = [ { @@ -113,7 +122,7 @@ async function initializeWorkspace( } // Generate package.json - const packageJson = generatePackageJson(); + const packageJson = generatePackageJson(template); fs.writeFileSync( packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n' @@ -124,17 +133,18 @@ async function initializeWorkspace( if (fs.existsSync(tsconfigPath) && !options.force) { logger.info('✓ tsconfig.json already exists, skipping'); } else { - const tsconfig = generateTsConfig(); + const tsconfig = generateTsConfig(template); fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + '\n'); } // Create basic index.ts file const indexPath = path.join(workspaceDir, 'index.ts'); if (!fs.existsSync(indexPath) || options.force) { - const indexContent = generateIndexFile(); + const indexContent = generateIndexFile(template); fs.writeFileSync(indexPath, indexContent); } + logger.info(`Template: ${template}`); logger.info('Generated files:'); logger.debug(` ${outputDir}/dotgithub.json`); logger.debug(` ${workspaceDir}/package.json`); @@ -145,11 +155,74 @@ async function initializeWorkspace( logger.info(' npm install'); } -function generatePackageJson(): object { +function normalizeTemplate(value?: string): StarterTemplate { + const candidate = (value || 'node-library') as StarterTemplate; + if (SUPPORTED_TEMPLATES.includes(candidate)) { + return candidate; + } + throw new Error( + `Unsupported template "${value}". Valid templates: ${SUPPORTED_TEMPLATES.join(', ')}` + ); +} + +function generatePackageJson(template: StarterTemplate): object { + if (template === 'bun-app') { + return { + name: 'github-actions-bun-workspace', + version: '1.0.0', + description: 'DotGitHub Bun app workspace with TypeScript support', + type: 'module', + main: 'dist/index.js', + scripts: { + build: 'tsc', + dev: 'bun --watch src/index.ts', + clean: 'rm -rf dist', + }, + dependencies: { + '@dotgithub/core': '*', + '@dotgithub/cli': '*', + }, + devDependencies: { + '@types/node': '^20.0.0', + typescript: '^5.0.0', + }, + engines: { + bun: '>=1.2.22', + }, + packageManager: 'bun@1.2.22', + }; + } + + if (template === 'monorepo') { + return { + name: 'github-actions-monorepo-workspace', + version: '1.0.0', + private: true, + description: 'DotGitHub monorepo starter with TypeScript support', + type: 'module', + scripts: { + build: 'tsc', + clean: 'rm -rf dist', + }, + workspaces: ['packages/*'], + dependencies: { + '@dotgithub/core': '*', + '@dotgithub/cli': '*', + }, + devDependencies: { + '@types/node': '^20.0.0', + typescript: '^5.0.0', + }, + engines: { + node: '>=18.0.0', + }, + }; + } + return { name: 'github-actions-workspace', version: '1.0.0', - description: 'GitHub Actions workspace with TypeScript support', + description: 'DotGitHub Node library workspace with TypeScript support', type: 'module', main: 'dist/index.js', module: 'dist/index.js', @@ -173,12 +246,13 @@ function generatePackageJson(): object { }; } -function generateTsConfig(): object { +function generateTsConfig(template: StarterTemplate): object { + const moduleResolution = template === 'bun-app' ? 'bundler' : 'bundler'; return { compilerOptions: { target: 'ES2022', module: 'ESNext', - moduleResolution: 'bundler', + moduleResolution, allowSyntheticDefaultImports: true, esModuleInterop: true, forceConsistentCasingInFileNames: true, @@ -197,9 +271,20 @@ function generateTsConfig(): object { }; } -function generateIndexFile(): string { - return `// GitHub Actions workspace entry point -import { +function generateIndexFile(template: StarterTemplate): string { + if (template === 'bun-app') { + return `// Bun-oriented DotGitHub starter entry point\n// Use this to author CI/CD workflow constructs in TypeScript and synthesize YAML.\n\n${baseIndexContent()}`; + } + + if (template === 'monorepo') { + return `// Monorepo-oriented DotGitHub starter entry point\n// Tip: split constructs by package and export a composed default construct.\n\n${baseIndexContent()}`; + } + + return `// Node library-oriented DotGitHub starter entry point\n// Author workflow logic in TypeScript and synthesize to GitHub Actions YAML.\n\n${baseIndexContent()}`; +} + +function baseIndexContent(): string { + return `import { GitHubConstruct, GitHubStack, JobConstruct, @@ -279,7 +364,7 @@ export class MyConstruct extends GitHubConstruct { }); // Create a job - const job = new JobConstruct(workflow, 'test', { + new JobConstruct(workflow, 'test', { name: 'Test', 'runs-on': 'ubuntu-latest', steps: [