mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-24 05:15:16 +02:00
154 lines
5.2 KiB
TypeScript
154 lines
5.2 KiB
TypeScript
#!/usr/bin/env tsx
|
||
import { writeFileSync } from 'node:fs';
|
||
import { parseArgs } from 'node:util';
|
||
|
||
import { createHelmStack, type HelmStackMode } from './helm-stack';
|
||
|
||
const colors = {
|
||
reset: '\x1b[0m',
|
||
bright: '\x1b[1m',
|
||
green: '\x1b[32m',
|
||
yellow: '\x1b[33m',
|
||
blue: '\x1b[34m',
|
||
red: '\x1b[31m',
|
||
cyan: '\x1b[36m',
|
||
};
|
||
|
||
const log = {
|
||
info: (msg: string) => console.log(`${colors.blue}ℹ${colors.reset} ${msg}`),
|
||
success: (msg: string) => console.log(`${colors.green}✓${colors.reset} ${msg}`),
|
||
error: (msg: string) => console.error(`${colors.red}✗${colors.reset} ${msg}`),
|
||
header: (msg: string) => console.log(`\n${colors.bright}${colors.cyan}${msg}${colors.reset}\n`),
|
||
};
|
||
|
||
function showHelp() {
|
||
console.log(`
|
||
${colors.bright}n8n Helm Stack${colors.reset}
|
||
|
||
Start n8n via Helm chart in a K3s (lightweight Kubernetes) container.
|
||
|
||
${colors.yellow}Usage:${colors.reset}
|
||
pnpm stack:helm [options]
|
||
|
||
${colors.yellow}Options:${colors.reset}
|
||
--mode <mode> standalone (SQLite, default) or queue (PostgreSQL + Redis + workers)
|
||
--image <image> n8n Docker image (default: n8nio/n8n:local)
|
||
--chart-ref <ref> Git branch/tag for n8n-hosting repo (default: main)
|
||
--chart-repo <url> Git repo URL (default: https://github.com/n8n-io/n8n-hosting.git)
|
||
--k3s-image <image> K3s image (default: rancher/k3s:v1.32.2-k3s1)
|
||
--env <KEY=VALUE> Set environment variable in n8n pods (repeatable)
|
||
--url-file <path> Write URL to file when ready (for CI)
|
||
--help, -h Show this help
|
||
|
||
${colors.yellow}Examples:${colors.reset}
|
||
${colors.bright}# Start with default local image${colors.reset}
|
||
pnpm stack:helm
|
||
|
||
${colors.bright}# Test specific n8n version against specific chart${colors.reset}
|
||
pnpm stack:helm --image n8nio/n8n:1.80.0 --chart-ref v1.2.0
|
||
|
||
${colors.bright}# Queue mode (PostgreSQL + Redis + workers)${colors.reset}
|
||
pnpm stack:helm --mode queue --image n8nio/n8n:latest
|
||
|
||
${colors.bright}# E2E test mode (requires INCLUDE_TEST_CONTROLLER image)${colors.reset}
|
||
pnpm stack:helm --env E2E_TESTS=true --env NODE_ENV=development
|
||
|
||
${colors.bright}# CI mode (writes URL to file)${colors.reset}
|
||
pnpm stack:helm --url-file /tmp/n8n-url.txt &
|
||
|
||
${colors.yellow}Prerequisites:${colors.reset}
|
||
• Docker with privileged container support
|
||
• helm and kubectl CLIs installed locally
|
||
• n8n Docker image built locally (pnpm build:docker) or available on Docker Hub
|
||
• See HELM-TESTING.md for full requirements
|
||
|
||
${colors.yellow}Notes:${colors.reset}
|
||
• helm and kubectl run on your host (not inside K3s)
|
||
• Startup takes ~60-120s (K3s boot + image load + Helm install)
|
||
• After startup, use kubectl with KUBECONFIG printed below
|
||
• Press Ctrl+C to stop
|
||
`);
|
||
}
|
||
|
||
async function main() {
|
||
const { values } = parseArgs({
|
||
args: process.argv.slice(2),
|
||
options: {
|
||
help: { type: 'boolean', short: 'h' },
|
||
mode: { type: 'string' },
|
||
image: { type: 'string' },
|
||
'chart-ref': { type: 'string' },
|
||
'chart-repo': { type: 'string' },
|
||
'k3s-image': { type: 'string' },
|
||
env: { type: 'string', multiple: true },
|
||
'url-file': { type: 'string' },
|
||
'kubeconfig-file': { type: 'string' },
|
||
},
|
||
allowPositionals: false,
|
||
});
|
||
|
||
if (values.help) {
|
||
showHelp();
|
||
process.exit(0);
|
||
}
|
||
|
||
log.header('Starting n8n Helm Stack');
|
||
|
||
const mode = (values.mode as HelmStackMode) || undefined;
|
||
|
||
// Parse --env KEY=VALUE pairs into a record
|
||
const envOverrides: Record<string, string> = {};
|
||
for (const entry of values.env ?? []) {
|
||
const eqIndex = entry.indexOf('=');
|
||
if (eqIndex === -1) {
|
||
log.error(`Invalid --env format: "${entry}" (expected KEY=VALUE)`);
|
||
process.exit(1);
|
||
}
|
||
envOverrides[entry.slice(0, eqIndex)] = entry.slice(eqIndex + 1);
|
||
}
|
||
|
||
const stack = await createHelmStack({
|
||
n8nImage: values.image,
|
||
helmChartRef: values['chart-ref'],
|
||
helmChartRepo: values['chart-repo'],
|
||
k3sImage: values['k3s-image'],
|
||
mode,
|
||
env: Object.keys(envOverrides).length > 0 ? envOverrides : undefined,
|
||
});
|
||
|
||
log.header('Stack Ready');
|
||
log.success(`n8n URL: ${colors.bright}${colors.green}${stack.baseUrl}${colors.reset}`);
|
||
log.info(`Kubeconfig: ${colors.bright}${stack.kubeConfigPath}${colors.reset}`);
|
||
|
||
if (values['url-file']) {
|
||
writeFileSync(values['url-file'], stack.baseUrl);
|
||
log.info(`URL written to ${values['url-file']}`);
|
||
}
|
||
|
||
if (values['kubeconfig-file']) {
|
||
writeFileSync(values['kubeconfig-file'], stack.kubeConfigPath);
|
||
log.info(`Kubeconfig path written to ${values['kubeconfig-file']}`);
|
||
}
|
||
|
||
console.log('');
|
||
log.info('Debug with kubectl (context already active):');
|
||
log.info(` ${colors.bright}kubectl get pods${colors.reset}`);
|
||
log.info(` ${colors.bright}kubectl logs -l app.kubernetes.io/name=n8n${colors.reset}`);
|
||
console.log('');
|
||
log.info(`Cleanup: ${colors.bright}pnpm --filter n8n-containers stack:helm:clean${colors.reset}`);
|
||
console.log('');
|
||
if (envOverrides.E2E_TESTS === 'true') {
|
||
log.info('Run tests against this instance:');
|
||
log.info(
|
||
` ${colors.bright}N8N_BASE_URL=${stack.baseUrl} RESET_E2E_DB=true npx playwright test tests/e2e/building-blocks/ --workers=1${colors.reset}`,
|
||
);
|
||
}
|
||
|
||
// Container stays alive in Docker (Ryuk disabled) — clean up via stack:helm:clean.
|
||
}
|
||
|
||
main().catch((error) => {
|
||
log.error(`Failed to start: ${error instanceof Error ? error.message : String(error)}`);
|
||
process.exit(1);
|
||
});
|