diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 685824c..f6e59ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,5 +38,5 @@ jobs: - name: Lint run: bun run lint - - name: Contract tests (workflow generator) - run: bun run test -- packages/core/src/workflow-generator.test.ts + - name: Core contract tests + run: bun run test -- packages/core/src/workflow-generator.test.ts packages/core/src/context.test.ts packages/core/src/config.test.ts diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 4bf0ffe..443cb10 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as yaml from 'yaml'; import type { ConstructConfig, StackConfig } from './plugins/types.js'; -import type { DotGithubContext } from './context.js'; +import { DotGithubContext } from './context.js'; export interface DotGithubAction { /** GitHub repository in org/repo format */ @@ -11,6 +11,8 @@ export interface DotGithubAction { ref: string; /** Original version reference that was requested (e.g., v4, main) */ versionRef: string; + /** Legacy function name (backward-compat) */ + functionName?: string; /** Action name for type names and function names (overrides default from YAML) */ actionName?: string; /** Output file path where the TypeScript was generated - required when generateCode is true */ @@ -138,9 +140,12 @@ module.exports = ${JSON.stringify(config, null, 2)}; /** * Sets a custom config file path to override the default discovery */ +let customConfigPathOverride: string | undefined; + export function setConfigPath(configPath: string): void { - // This function is kept for compatibility but doesn't need to do anything - // since we're using DotGithubContext now + customConfigPathOverride = configPath + ? path.resolve(configPath) + : undefined; } /** @@ -189,6 +194,25 @@ export function getConfigPath(): string { 'dotgithub.yml', ]; + if (customConfigPathOverride) { + const stat = fs.existsSync(customConfigPathOverride) + ? fs.statSync(customConfigPathOverride) + : undefined; + + if (stat?.isDirectory()) { + const githubDir = path.join(customConfigPathOverride, '.github'); + for (const fileName of CONFIG_FILE_NAMES) { + const configPath = path.join(githubDir, fileName); + if (fs.existsSync(configPath)) { + return configPath; + } + } + return path.join(githubDir, CONFIG_FILE_NAMES[0]!); + } + + return customConfigPathOverride; + } + let currentDir = process.cwd(); while (currentDir !== path.dirname(currentDir)) { @@ -310,8 +334,23 @@ export function writeConfig( */ export function addActionToConfig( actionInfo: DotGithubAction, - context: DotGithubContext + contextOrRootDir: DotGithubContext | string ): void { + const context: DotGithubContext = + typeof contextOrRootDir === 'string' + ? new DotGithubContext({ + config: { ...readConfig(), rootDir: contextOrRootDir }, + configPath: getConfigPath(), + }) + : contextOrRootDir; + + if (!context.config) { + context.config = createDefaultConfig(); + } + if (!context.config.actions) { + context.config.actions = []; + } + // Check if action already exists (check orgRepo AND actionPath for uniqueness) const existingIndex = context.config.actions.findIndex( (action) => @@ -323,6 +362,10 @@ export function addActionToConfig( ...actionInfo, }; + if (actionWithRelativePath.outputPath && path.isAbsolute(actionWithRelativePath.outputPath)) { + actionWithRelativePath.outputPath = context.relativePath(actionWithRelativePath.outputPath); + } + if (existingIndex >= 0) { // Update existing action context.config.actions[existingIndex] = actionWithRelativePath; diff --git a/packages/core/src/constructs/workflow.ts b/packages/core/src/constructs/workflow.ts index bac23a3..b009c33 100644 --- a/packages/core/src/constructs/workflow.ts +++ b/packages/core/src/constructs/workflow.ts @@ -12,7 +12,9 @@ export class WorkflowConstruct extends Construct { this._workflow = workflow; - scope.addWorkflow(id, this._workflow); + if (typeof (scope as any).addWorkflow === 'function') { + (scope as any).addWorkflow(id, this._workflow); + } } addJob(id: string, jobConstruct: JobConstruct): JobConstruct { diff --git a/packages/core/src/context.ts b/packages/core/src/context.ts index fb8d116..35ff762 100644 --- a/packages/core/src/context.ts +++ b/packages/core/src/context.ts @@ -11,11 +11,14 @@ export class DotGithubContext { config: DotGithubConfig; configPath: string; rootPath: string; + outputPath: string; constructor({ config, configPath }: DotGithubContextOptions) { this.config = config; this.configPath = configPath; - this.rootPath = path.join(path.dirname(configPath), this.config.rootDir); + const rootDir = this.config.rootDir ?? this.config.outputDir ?? 'src'; + this.rootPath = path.join(path.dirname(configPath), rootDir); + this.outputPath = this.rootPath; } resolvePath(relativePath: string): string { diff --git a/packages/core/src/plugin-generator.ts b/packages/core/src/plugin-generator.ts index e67425e..33286f7 100644 --- a/packages/core/src/plugin-generator.ts +++ b/packages/core/src/plugin-generator.ts @@ -13,6 +13,7 @@ import { PropertyDeclaration, } from 'ts-morph'; import { generateActionFiles } from './actions-manager.js'; +import { generateFunctionName } from './utils.js'; // Enhanced TypeScript interfaces for better type safety interface WorkflowSchema { @@ -207,7 +208,7 @@ export async function createConstructFromFiles( } // Check if there's a local dotgithub.json config in the source directory - let configToUse = context.config; + let configToUse: any = context.config || {}; const localConfigPath = path.join(githubFilesPath, 'dotgithub.json'); if (fs.existsSync(localConfigPath)) { try { @@ -226,13 +227,10 @@ export async function createConstructFromFiles( // Generate construct content (now with updated config that includes auto-added actions) // Filter actions to only include those with functionName for code generation const configForConstruct: Config = { - actions: configToUse.actions - .filter((action) => action.generateCode !== false && action.outputPath) - .map((action) => { - const { generateFunctionName } = require('./utils'); - const functionName = action.actionName - ? generateFunctionName(action.actionName) - : action.orgRepo; + actions: (configToUse.actions || []) + .filter((action: any) => action.generateCode !== false && action.outputPath) + .map((action: any) => { + const functionName = generateFunctionName(action.actionName || action.orgRepo); return { orgRepo: action.orgRepo, ref: action.ref, @@ -299,13 +297,13 @@ export async function generateConstructFromGitHubFiles( const { content, filename } = await downloadGitHubFile(source); // Filter actions to only include those with outputPath for code generation const configForConstruct: Config = { - actions: context.config.actions - .filter((action) => action.generateCode !== false && action.outputPath) - .map((action) => { + actions: (context.config.actions || []) + .filter((action: any) => action.generateCode !== false && action.outputPath) + .map((action: any) => { const { generateFunctionName } = require('./utils'); const functionName = action.actionName ? generateFunctionName(action.actionName) - : action.orgRepo; + : generateFunctionName(action.actionName || action.orgRepo); return { orgRepo: action.orgRepo, ref: action.ref,