Skip to content

Instantly share code, notes, and snippets.

@billyvg
Last active January 6, 2026 20:36
Show Gist options
  • Select an option

  • Save billyvg/eb67f9051ed404c1908b79847e4cf19f to your computer and use it in GitHub Desktop.

Select an option

Save billyvg/eb67f9051ed404c1908b79847e4cf19f to your computer and use it in GitHub Desktop.
Komodo Action: Create a new stack on git push
/**
* Create a new Komodo Action and paste the following in. Take the webhook URL and connect
* it to your git host so that the webhook is called on pushes.
*
* You may need to modify it to your liking but the code detects when files are added and assumes
* the format: `<stackName>/compose.yaml`
*/
// I used to use a template to create a new stack, but
// templates cause my update procedure to fail, so I just define my template here instead
// I created an test action and called `komodo.read('GetStack', {stack});` to my stack template
// to get the following values
const STACK_CONFIG_TEMPLATE: Partial<Types.StackConfig> = {
server_id: <server_id>,
linked_repo: <linked_repo_id>,
auto_pull: true,
poll_for_updates: true,
environment: "TZ=America/New_York",
// ... additional config
};
// Access arguments using the 'ARGS' object.
const {commits}: {commits: Array<{added: null|string[]}>} = ARGS.WEBHOOK_BODY;
const newStacks: Record<string, {compose: string; env: string[]}> = commits
.filter(commit => Array.isArray(commit?.added))
.reduce((acc, commit) => {
// Returns dict of {stackName, {compose: string, env: string[]}},
// which needs to be merged for each commit
commit.added
.forEach(file => {
// needs to be in format of `<stackName>/compose.yaml`
const [stackName, fileName] = file.split('/');
const stackValue = acc[stackName] ?? {};
const envFiles = stackValue.env ?? [];
if (fileName.endsWith('compose.yaml') || fileName.endsWith('compose.yml')) {
stackValue.compose = fileName;
}
if (fileName.endsWith('.env')) {
envFiles.push(fileName);
}
acc[stackName] = {
compose: stackValue.compose,
env: envFiles,
};
});
return acc;
}, {})
if (!Object.keys(newStacks).length) {
return;
}
for (const [stackName, stackObject] of Object.entries(newStacks)) {
if (stackObject.compose) {
createStack(stackName, stackObject.env);
} else if (stackObject.env?.length) {
// TODO, !compose && env.length (env file was later added) --> get existing stack and update the additional_env_files
try {
const stack = await komodo.read('GetStack', {stack: stackName});
console.log({stack});
await komodo.write('UpdateStack', {
id: stack.id,
config: {
additional_env_files: [
...stack.config.additional_env_files,
...stackObject.env,
],
}
});
} catch {
// stack doesn't exist... can't do anything
}
}
}
async function createStack(stackName: string, envFiles: string[] = []) {
/*
const STACK_TEMPLATE_NAME = 'git-template';
let stackTemplate: Types.GetStackResponse|null;
try {
stackTemplate = await komodo.read('GetStack', {stack: STACK_TEMPLATE_NAME});
} catch {}
*/
try {
const stack = await komodo.read('GetStack', {stack: stackName});
// This shouldn't happen because above will throw if stack doesn't exist
if (stack) {
return stack;
}
} catch {
// Create stack
const result = await komodo.write('CreateStack', {
name: stackName,
config: {
//...stackTemplate?.config,
...STACK_CONFIG_TEMPLATE,
additional_env_files: envFiles,
run_directory: stackName,
}
});
if (result) {
console.log(`Created new stack: ${result.name}`);
await komodo.execute('DeployStack', {stack: result.name});
}
return result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment