Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
51 changes: 47 additions & 4 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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 */
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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) =>
Expand All @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/constructs/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
22 changes: 10 additions & 12 deletions packages/core/src/plugin-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down