@companyhelm/cli 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli_io_interface.d.ts +12 -0
- package/dist/cli_io_interface.js +2 -0
- package/dist/cli_io_interface.js.map +1 -0
- package/dist/companyhelm_cli.d.ts +21 -0
- package/dist/companyhelm_cli.js +90 -0
- package/dist/companyhelm_cli.js.map +1 -0
- package/dist/console_io.d.ts +10 -0
- package/dist/console_io.js +24 -0
- package/dist/console_io.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/provider/login_client.d.ts +34 -0
- package/dist/provider/login_client.js +41 -0
- package/dist/provider/login_client.js.map +1 -0
- package/dist/provider/login_command.d.ts +19 -0
- package/dist/provider/login_command.js +35 -0
- package/dist/provider/login_command.js.map +1 -0
- package/dist/provider/oauth_login_runner.d.ts +12 -0
- package/dist/provider/oauth_login_runner.js +37 -0
- package/dist/provider/oauth_login_runner.js.map +1 -0
- package/package.json +21 -29
- package/LICENSE +0 -21
- package/README.md +0 -66
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -39
- package/dist/commands/dependencies.d.ts +0 -28
- package/dist/commands/dependencies.js +0 -292
- package/dist/commands/down.d.ts +0 -3
- package/dist/commands/down.js +0 -5
- package/dist/commands/interactive.d.ts +0 -7
- package/dist/commands/interactive.js +0 -25
- package/dist/commands/logs.d.ts +0 -3
- package/dist/commands/logs.js +0 -14
- package/dist/commands/register-commands.d.ts +0 -3
- package/dist/commands/register-commands.js +0 -23
- package/dist/commands/reset.d.ts +0 -7
- package/dist/commands/reset.js +0 -51
- package/dist/commands/set-image-version.d.ts +0 -31
- package/dist/commands/set-image-version.js +0 -87
- package/dist/commands/setup-github-app.d.ts +0 -13
- package/dist/commands/setup-github-app.js +0 -233
- package/dist/commands/startup-preferences.d.ts +0 -3
- package/dist/commands/startup-preferences.js +0 -39
- package/dist/commands/status.d.ts +0 -3
- package/dist/commands/status.js +0 -7
- package/dist/commands/up.d.ts +0 -3
- package/dist/commands/up.js +0 -39
- package/dist/config/image_config.d.ts +0 -4
- package/dist/config/image_config.js +0 -4
- package/dist/core/bootstrap/DeploymentBootstrapper.d.ts +0 -15
- package/dist/core/bootstrap/DeploymentBootstrapper.js +0 -118
- package/dist/core/bootstrap/SeedSqlRenderer.d.ts +0 -12
- package/dist/core/bootstrap/SeedSqlRenderer.js +0 -44
- package/dist/core/config/ApiEnvFileWriter.d.ts +0 -8
- package/dist/core/config/ApiEnvFileWriter.js +0 -33
- package/dist/core/config/GithubAppConfig.d.ts +0 -6
- package/dist/core/config/GithubAppConfig.js +0 -26
- package/dist/core/config/GithubAppConfigStore.d.ts +0 -11
- package/dist/core/config/GithubAppConfigStore.js +0 -57
- package/dist/core/docker/ComposeTemplateRenderer.d.ts +0 -22
- package/dist/core/docker/ComposeTemplateRenderer.js +0 -66
- package/dist/core/docker/DockerStackManager.d.ts +0 -29
- package/dist/core/docker/DockerStackManager.js +0 -163
- package/dist/core/local/ApiLocalService.d.ts +0 -22
- package/dist/core/local/ApiLocalService.js +0 -65
- package/dist/core/local/LocalRepoSourceResolver.d.ts +0 -24
- package/dist/core/local/LocalRepoSourceResolver.js +0 -33
- package/dist/core/local/LocalServiceProcessManager.d.ts +0 -18
- package/dist/core/local/LocalServiceProcessManager.js +0 -83
- package/dist/core/local/WebLocalService.d.ts +0 -23
- package/dist/core/local/WebLocalService.js +0 -101
- package/dist/core/logs/LogsService.d.ts +0 -6
- package/dist/core/logs/LogsService.js +0 -14
- package/dist/core/process/CommandRunner.d.ts +0 -4
- package/dist/core/process/CommandRunner.js +0 -51
- package/dist/core/runner/RunnerSupervisor.d.ts +0 -29
- package/dist/core/runner/RunnerSupervisor.js +0 -90
- package/dist/core/runner/runner-bootstrap.d.ts +0 -2
- package/dist/core/runner/runner-bootstrap.js +0 -48
- package/dist/core/runtime/CliPackageMetadata.d.ts +0 -3
- package/dist/core/runtime/CliPackageMetadata.js +0 -8
- package/dist/core/runtime/CliRoot.d.ts +0 -2
- package/dist/core/runtime/CliRoot.js +0 -23
- package/dist/core/runtime/ImageCatalog.d.ts +0 -11
- package/dist/core/runtime/ImageCatalog.js +0 -16
- package/dist/core/runtime/ImageConfigStore.d.ts +0 -15
- package/dist/core/runtime/ImageConfigStore.js +0 -51
- package/dist/core/runtime/LocalConfigStore.d.ts +0 -16
- package/dist/core/runtime/LocalConfigStore.js +0 -50
- package/dist/core/runtime/ManagedImages.d.ts +0 -10
- package/dist/core/runtime/ManagedImages.js +0 -27
- package/dist/core/runtime/PortAllocator.d.ts +0 -9
- package/dist/core/runtime/PortAllocator.js +0 -20
- package/dist/core/runtime/PublicImageTagRegistry.d.ts +0 -17
- package/dist/core/runtime/PublicImageTagRegistry.js +0 -168
- package/dist/core/runtime/RuntimePaths.d.ts +0 -16
- package/dist/core/runtime/RuntimePaths.js +0 -43
- package/dist/core/runtime/RuntimeState.d.ts +0 -38
- package/dist/core/runtime/RuntimeState.js +0 -1
- package/dist/core/runtime/RuntimeStateStore.d.ts +0 -11
- package/dist/core/runtime/RuntimeStateStore.js +0 -81
- package/dist/core/runtime/Secrets.d.ts +0 -10
- package/dist/core/runtime/Secrets.js +0 -26
- package/dist/core/runtime/VersionCatalog.d.ts +0 -10
- package/dist/core/runtime/VersionCatalog.js +0 -21
- package/dist/core/services/ManagedServiceNames.d.ts +0 -5
- package/dist/core/services/ManagedServiceNames.js +0 -12
- package/dist/core/status/StatusService.d.ts +0 -19
- package/dist/core/status/StatusService.js +0 -30
- package/dist/core/ui/TerminalRenderer.d.ts +0 -17
- package/dist/core/ui/TerminalRenderer.js +0 -71
- package/dist/preflight/ApiPortPreflightCheck.d.ts +0 -6
- package/dist/preflight/ApiPortPreflightCheck.js +0 -10
- package/dist/preflight/DockerInstalledPreflightCheck.d.ts +0 -7
- package/dist/preflight/DockerInstalledPreflightCheck.js +0 -15
- package/dist/preflight/PortAvailabilityPreflightCheck.d.ts +0 -7
- package/dist/preflight/PortAvailabilityPreflightCheck.js +0 -31
- package/dist/preflight/PostgresPortPreflightCheck.d.ts +0 -6
- package/dist/preflight/PostgresPortPreflightCheck.js +0 -10
- package/dist/preflight/PreflightCheck.d.ts +0 -3
- package/dist/preflight/PreflightCheck.js +0 -1
- package/dist/preflight/WebPortPreflightCheck.d.ts +0 -6
- package/dist/preflight/WebPortPreflightCheck.js +0 -10
- package/dist/preflight/runStartupPreflightChecks.d.ts +0 -18
- package/dist/preflight/runStartupPreflightChecks.js +0 -42
- package/dist/templates/api.env.tpl +0 -3
- package/dist/templates/docker-compose.yaml.tpl +0 -24
- package/dist/templates/seed.sql.tpl +0 -76
- package/src/config/image_config.ts +0 -4
- package/src/templates/api.env.tpl +0 -3
- package/src/templates/docker-compose.yaml.tpl +0 -24
- package/src/templates/seed.sql.tpl +0 -76
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { parse, stringify } from "yaml";
|
|
4
|
-
import { normalizeGithubAppConfig } from "./GithubAppConfig.js";
|
|
5
|
-
import { defaultCliConfigRoot } from "../runtime/CliRoot.js";
|
|
6
|
-
function defaultConfigRoot() {
|
|
7
|
-
return defaultCliConfigRoot();
|
|
8
|
-
}
|
|
9
|
-
export class GithubAppConfigStore {
|
|
10
|
-
configRoot;
|
|
11
|
-
constructor(configRoot = defaultConfigRoot()) {
|
|
12
|
-
this.configRoot = configRoot;
|
|
13
|
-
}
|
|
14
|
-
configPath() {
|
|
15
|
-
return path.join(this.configRoot, "github-app.yaml");
|
|
16
|
-
}
|
|
17
|
-
hasConfig() {
|
|
18
|
-
return fs.existsSync(this.configPath());
|
|
19
|
-
}
|
|
20
|
-
save(config) {
|
|
21
|
-
const normalized = normalizeGithubAppConfig(config);
|
|
22
|
-
fs.mkdirSync(this.configRoot, { recursive: true });
|
|
23
|
-
const tempPath = `${this.configPath()}.tmp`;
|
|
24
|
-
const yaml = stringify({
|
|
25
|
-
app_url: normalized.appUrl,
|
|
26
|
-
app_client_id: normalized.appClientId,
|
|
27
|
-
app_private_key_pem: normalized.appPrivateKeyPem,
|
|
28
|
-
});
|
|
29
|
-
fs.writeFileSync(tempPath, yaml, { encoding: "utf8", mode: 0o600 });
|
|
30
|
-
fs.renameSync(tempPath, this.configPath());
|
|
31
|
-
return this.configPath();
|
|
32
|
-
}
|
|
33
|
-
load() {
|
|
34
|
-
if (!this.hasConfig()) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
const parsed = parse(fs.readFileSync(this.configPath(), "utf8"));
|
|
38
|
-
if (!parsed || typeof parsed !== "object") {
|
|
39
|
-
throw new Error(`Machine GitHub App config at ${this.configPath()} is invalid.`);
|
|
40
|
-
}
|
|
41
|
-
return normalizeGithubAppConfig({
|
|
42
|
-
appUrl: String(parsed.app_url || ""),
|
|
43
|
-
appClientId: String(parsed.app_client_id || ""),
|
|
44
|
-
appPrivateKeyPem: String(parsed.app_private_key_pem || ""),
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
loadOrThrow() {
|
|
48
|
-
const config = this.load();
|
|
49
|
-
if (!config) {
|
|
50
|
-
throw new Error(`GitHub App config is not set up. Run \`companyhelm setup-github-app\` to create ${this.configPath()}.`);
|
|
51
|
-
}
|
|
52
|
-
return config;
|
|
53
|
-
}
|
|
54
|
-
delete() {
|
|
55
|
-
fs.rmSync(this.configPath(), { force: true });
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { LogLevel } from "../../commands/dependencies.js";
|
|
2
|
-
export interface ComposePorts {
|
|
3
|
-
apiHttpPort: number;
|
|
4
|
-
uiPort: number;
|
|
5
|
-
runnerGrpcPort: number;
|
|
6
|
-
agentCliGrpcPort: number;
|
|
7
|
-
}
|
|
8
|
-
export interface ComposePaths {
|
|
9
|
-
apiConfigPath: string;
|
|
10
|
-
apiEnvPath: string;
|
|
11
|
-
frontendConfigPath: string;
|
|
12
|
-
seedFilePath: string;
|
|
13
|
-
}
|
|
14
|
-
export interface ComposeRenderOptions {
|
|
15
|
-
frontendLogLevel?: LogLevel;
|
|
16
|
-
includeApi?: boolean;
|
|
17
|
-
includeFrontend?: boolean;
|
|
18
|
-
exposePostgresPort?: boolean;
|
|
19
|
-
}
|
|
20
|
-
export declare class ComposeTemplateRenderer {
|
|
21
|
-
render(ports: ComposePorts, paths: ComposePaths, options?: ComposeRenderOptions): string;
|
|
22
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { ImageCatalog } from "../runtime/ImageCatalog.js";
|
|
5
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
-
const __dirname = path.dirname(__filename);
|
|
7
|
-
export class ComposeTemplateRenderer {
|
|
8
|
-
render(ports, paths, options = {}) {
|
|
9
|
-
const templatePath = path.resolve(__dirname, "../../templates/docker-compose.yaml.tpl");
|
|
10
|
-
const template = fs.readFileSync(templatePath, "utf8");
|
|
11
|
-
const images = new ImageCatalog().resolve();
|
|
12
|
-
const frontendLogLevel = options.frontendLogLevel ?? "info";
|
|
13
|
-
const includeApi = options.includeApi ?? true;
|
|
14
|
-
const includeFrontend = options.includeFrontend ?? true;
|
|
15
|
-
const postgresPortsBlock = options.exposePostgresPort ? [
|
|
16
|
-
" ports:",
|
|
17
|
-
' - "5432:5432"'
|
|
18
|
-
].join("\n") : "";
|
|
19
|
-
const apiBlock = includeApi ? [
|
|
20
|
-
" api:",
|
|
21
|
-
` image: ${images.api}`,
|
|
22
|
-
" platform: linux/amd64",
|
|
23
|
-
" depends_on:",
|
|
24
|
-
" - postgres",
|
|
25
|
-
" env_file:",
|
|
26
|
-
` - "${paths.apiEnvPath}"`,
|
|
27
|
-
" environment:",
|
|
28
|
-
" COMPANYHELM_CONFIG_PATH: /run/companyhelm/config.yaml",
|
|
29
|
-
" ports:",
|
|
30
|
-
` - "${ports.apiHttpPort}:4000"`,
|
|
31
|
-
` - "${ports.runnerGrpcPort}:${ports.runnerGrpcPort}"`,
|
|
32
|
-
" volumes:",
|
|
33
|
-
` - "${paths.apiConfigPath}:/run/companyhelm/config.yaml:ro"`,
|
|
34
|
-
" networks:",
|
|
35
|
-
" - companyhelm"
|
|
36
|
-
].join("\n") : "";
|
|
37
|
-
const frontendBlock = includeFrontend ? [
|
|
38
|
-
" frontend:",
|
|
39
|
-
` image: ${images.frontend}`,
|
|
40
|
-
...(includeApi ? [" depends_on:", " - api"] : []),
|
|
41
|
-
" environment:",
|
|
42
|
-
" COMPANYHELM_CONFIG_PATH: /run/companyhelm/config.yaml",
|
|
43
|
-
` COMPANYHELM_LOG_LEVEL: "${frontendLogLevel}"`,
|
|
44
|
-
` PORT: "${ports.uiPort}"`,
|
|
45
|
-
` npm_config_loglevel: "${frontendLogLevel}"`,
|
|
46
|
-
" ports:",
|
|
47
|
-
` - "${ports.uiPort}:${ports.uiPort}"`,
|
|
48
|
-
" volumes:",
|
|
49
|
-
` - "${paths.frontendConfigPath}:/run/companyhelm/config.yaml:ro"`,
|
|
50
|
-
" networks:",
|
|
51
|
-
" - companyhelm"
|
|
52
|
-
].join("\n") : "";
|
|
53
|
-
return template
|
|
54
|
-
.replaceAll("{{POSTGRES_IMAGE}}", images.postgres)
|
|
55
|
-
.replace("{{POSTGRES_PORTS_BLOCK}}", postgresPortsBlock)
|
|
56
|
-
.replace("{{API_SERVICE_BLOCK}}", apiBlock)
|
|
57
|
-
.replaceAll("{{API_CONFIG_PATH}}", paths.apiConfigPath)
|
|
58
|
-
.replaceAll("{{API_ENV_PATH}}", paths.apiEnvPath)
|
|
59
|
-
.replaceAll("{{SEED_FILE_PATH}}", paths.seedFilePath)
|
|
60
|
-
.replaceAll("{{API_HTTP_PORT}}", String(ports.apiHttpPort))
|
|
61
|
-
.replaceAll("{{UI_PORT}}", String(ports.uiPort))
|
|
62
|
-
.replaceAll("{{RUNNER_GRPC_PORT}}", String(ports.runnerGrpcPort))
|
|
63
|
-
.replaceAll("{{AGENT_CLI_GRPC_PORT}}", String(ports.agentCliGrpcPort))
|
|
64
|
-
.replace("{{FRONTEND_SERVICE_BLOCK}}", frontendBlock);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { LogLevel } from "../../commands/dependencies.js";
|
|
2
|
-
import { CommandRunner } from "../process/CommandRunner.js";
|
|
3
|
-
import type { RuntimeState } from "../runtime/RuntimeState.js";
|
|
4
|
-
import { ComposeTemplateRenderer } from "./ComposeTemplateRenderer.js";
|
|
5
|
-
export interface DockerStackUpOptions {
|
|
6
|
-
frontendLogLevel?: LogLevel;
|
|
7
|
-
includeApi?: boolean;
|
|
8
|
-
includeFrontend?: boolean;
|
|
9
|
-
exposePostgresPort?: boolean;
|
|
10
|
-
}
|
|
11
|
-
export interface DockerStackDownOptions {
|
|
12
|
-
removeVolumes?: boolean;
|
|
13
|
-
}
|
|
14
|
-
export declare class DockerStackManager {
|
|
15
|
-
private readonly commandRunner;
|
|
16
|
-
private readonly composeRenderer;
|
|
17
|
-
private static readonly BOOTSTRAP_RETRY_COUNT;
|
|
18
|
-
private static readonly BOOTSTRAP_RETRY_DELAY_MS;
|
|
19
|
-
private readonly runtimePaths;
|
|
20
|
-
constructor(root: string, commandRunner?: CommandRunner, composeRenderer?: ComposeTemplateRenderer);
|
|
21
|
-
up(state: RuntimeState, options?: DockerStackUpOptions): Promise<void>;
|
|
22
|
-
applySeedSql(seedEmail: string): Promise<void>;
|
|
23
|
-
private seedSchemaReady;
|
|
24
|
-
private seedAlreadyApplied;
|
|
25
|
-
private waitForNextBootstrapAttempt;
|
|
26
|
-
down(options?: DockerStackDownOptions): Promise<void>;
|
|
27
|
-
logs(service: "postgres" | "api" | "frontend"): Promise<void>;
|
|
28
|
-
runningServices(): Promise<string>;
|
|
29
|
-
}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import { CommandRunner } from "../process/CommandRunner.js";
|
|
3
|
-
import { RuntimePaths } from "../runtime/RuntimePaths.js";
|
|
4
|
-
import { ComposeTemplateRenderer } from "./ComposeTemplateRenderer.js";
|
|
5
|
-
export class DockerStackManager {
|
|
6
|
-
commandRunner;
|
|
7
|
-
composeRenderer;
|
|
8
|
-
static BOOTSTRAP_RETRY_COUNT = 60;
|
|
9
|
-
static BOOTSTRAP_RETRY_DELAY_MS = 1000;
|
|
10
|
-
runtimePaths;
|
|
11
|
-
constructor(root, commandRunner = new CommandRunner(), composeRenderer = new ComposeTemplateRenderer()) {
|
|
12
|
-
this.commandRunner = commandRunner;
|
|
13
|
-
this.composeRenderer = composeRenderer;
|
|
14
|
-
this.runtimePaths = new RuntimePaths(root);
|
|
15
|
-
}
|
|
16
|
-
async up(state, options = {}) {
|
|
17
|
-
fs.mkdirSync(this.runtimePaths.runnerConfigPath(), { recursive: true });
|
|
18
|
-
fs.writeFileSync(this.runtimePaths.composeFilePath(), this.composeRenderer.render({
|
|
19
|
-
apiHttpPort: state.ports.apiHttp,
|
|
20
|
-
uiPort: state.ports.ui,
|
|
21
|
-
runnerGrpcPort: state.ports.runnerGrpc,
|
|
22
|
-
agentCliGrpcPort: state.ports.agentCliGrpc
|
|
23
|
-
}, {
|
|
24
|
-
apiConfigPath: this.runtimePaths.apiConfigPath(),
|
|
25
|
-
apiEnvPath: this.runtimePaths.apiEnvPath(),
|
|
26
|
-
frontendConfigPath: this.runtimePaths.frontendConfigPath(),
|
|
27
|
-
seedFilePath: this.runtimePaths.seedFilePath()
|
|
28
|
-
}, {
|
|
29
|
-
frontendLogLevel: options.frontendLogLevel,
|
|
30
|
-
includeApi: options.includeApi,
|
|
31
|
-
includeFrontend: options.includeFrontend,
|
|
32
|
-
exposePostgresPort: options.exposePostgresPort
|
|
33
|
-
}), "utf8");
|
|
34
|
-
await this.commandRunner.run("docker", [
|
|
35
|
-
"compose",
|
|
36
|
-
"-f",
|
|
37
|
-
this.runtimePaths.composeFilePath(),
|
|
38
|
-
"up",
|
|
39
|
-
"-d"
|
|
40
|
-
]);
|
|
41
|
-
}
|
|
42
|
-
async applySeedSql(seedEmail) {
|
|
43
|
-
if (!fs.existsSync(this.runtimePaths.seedFilePath())) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
let lastError = null;
|
|
47
|
-
for (let attempt = 0; attempt < DockerStackManager.BOOTSTRAP_RETRY_COUNT; attempt += 1) {
|
|
48
|
-
try {
|
|
49
|
-
if (!(await this.seedSchemaReady())) {
|
|
50
|
-
await this.waitForNextBootstrapAttempt();
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
if (await this.seedAlreadyApplied(seedEmail)) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
await this.commandRunner.run("docker", [
|
|
57
|
-
"compose",
|
|
58
|
-
"-f",
|
|
59
|
-
this.runtimePaths.composeFilePath(),
|
|
60
|
-
"exec",
|
|
61
|
-
"-T",
|
|
62
|
-
"postgres",
|
|
63
|
-
"psql",
|
|
64
|
-
"-U",
|
|
65
|
-
"postgres",
|
|
66
|
-
"-d",
|
|
67
|
-
"companyhelm",
|
|
68
|
-
"-f",
|
|
69
|
-
"/run/companyhelm/seed.sql"
|
|
70
|
-
]);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
lastError = error;
|
|
75
|
-
await this.waitForNextBootstrapAttempt();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
throw lastError ?? new Error("Failed to apply seed SQL.");
|
|
79
|
-
}
|
|
80
|
-
async seedSchemaReady() {
|
|
81
|
-
const output = await this.commandRunner.capture("docker", [
|
|
82
|
-
"compose",
|
|
83
|
-
"-f",
|
|
84
|
-
this.runtimePaths.composeFilePath(),
|
|
85
|
-
"exec",
|
|
86
|
-
"-T",
|
|
87
|
-
"postgres",
|
|
88
|
-
"psql",
|
|
89
|
-
"-U",
|
|
90
|
-
"postgres",
|
|
91
|
-
"-d",
|
|
92
|
-
"companyhelm",
|
|
93
|
-
"-tAc",
|
|
94
|
-
"SELECT to_regclass('public.user_auths') IS NOT NULL"
|
|
95
|
-
]);
|
|
96
|
-
return output.trim() === "t";
|
|
97
|
-
}
|
|
98
|
-
async seedAlreadyApplied(seedEmail) {
|
|
99
|
-
const escapedEmail = seedEmail.replaceAll("'", "''");
|
|
100
|
-
const output = await this.commandRunner.capture("docker", [
|
|
101
|
-
"compose",
|
|
102
|
-
"-f",
|
|
103
|
-
this.runtimePaths.composeFilePath(),
|
|
104
|
-
"exec",
|
|
105
|
-
"-T",
|
|
106
|
-
"postgres",
|
|
107
|
-
"psql",
|
|
108
|
-
"-U",
|
|
109
|
-
"postgres",
|
|
110
|
-
"-d",
|
|
111
|
-
"companyhelm",
|
|
112
|
-
"-tAc",
|
|
113
|
-
`SELECT 1 FROM user_auths WHERE email = '${escapedEmail}' LIMIT 1`
|
|
114
|
-
]);
|
|
115
|
-
return output.trim() === "1";
|
|
116
|
-
}
|
|
117
|
-
async waitForNextBootstrapAttempt() {
|
|
118
|
-
await new Promise((resolve) => {
|
|
119
|
-
setTimeout(resolve, DockerStackManager.BOOTSTRAP_RETRY_DELAY_MS);
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
async down(options = {}) {
|
|
123
|
-
if (!fs.existsSync(this.runtimePaths.composeFilePath())) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
const args = [
|
|
127
|
-
"compose",
|
|
128
|
-
"-f",
|
|
129
|
-
this.runtimePaths.composeFilePath(),
|
|
130
|
-
"down",
|
|
131
|
-
"--remove-orphans"
|
|
132
|
-
];
|
|
133
|
-
if (options.removeVolumes) {
|
|
134
|
-
args.push("--volumes");
|
|
135
|
-
}
|
|
136
|
-
await this.commandRunner.run("docker", [
|
|
137
|
-
...args
|
|
138
|
-
]);
|
|
139
|
-
}
|
|
140
|
-
async logs(service) {
|
|
141
|
-
await this.commandRunner.run("docker", [
|
|
142
|
-
"compose",
|
|
143
|
-
"-f",
|
|
144
|
-
this.runtimePaths.composeFilePath(),
|
|
145
|
-
"logs",
|
|
146
|
-
service
|
|
147
|
-
]);
|
|
148
|
-
}
|
|
149
|
-
async runningServices() {
|
|
150
|
-
if (!fs.existsSync(this.runtimePaths.composeFilePath())) {
|
|
151
|
-
return "";
|
|
152
|
-
}
|
|
153
|
-
return this.commandRunner.capture("docker", [
|
|
154
|
-
"compose",
|
|
155
|
-
"-f",
|
|
156
|
-
this.runtimePaths.composeFilePath(),
|
|
157
|
-
"ps",
|
|
158
|
-
"--services",
|
|
159
|
-
"--status",
|
|
160
|
-
"running"
|
|
161
|
-
]);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { LocalServiceProcessManager } from "./LocalServiceProcessManager.js";
|
|
2
|
-
import { CommandRunner } from "../process/CommandRunner.js";
|
|
3
|
-
import type { LogLevel } from "../../commands/dependencies.js";
|
|
4
|
-
import type { GithubAppConfig } from "../config/GithubAppConfig.js";
|
|
5
|
-
import type { LocalManagedServiceRuntime, RuntimeState } from "../runtime/RuntimeState.js";
|
|
6
|
-
export interface ApiLocalServiceStartInput {
|
|
7
|
-
repoPath: string;
|
|
8
|
-
configPath: string;
|
|
9
|
-
graphqlUrl: string;
|
|
10
|
-
logPath: string;
|
|
11
|
-
githubAppConfig: GithubAppConfig | null;
|
|
12
|
-
state: RuntimeState;
|
|
13
|
-
logLevel: LogLevel;
|
|
14
|
-
}
|
|
15
|
-
export declare class ApiLocalService {
|
|
16
|
-
private readonly processManager;
|
|
17
|
-
private readonly commandRunner;
|
|
18
|
-
constructor(processManager?: LocalServiceProcessManager, commandRunner?: CommandRunner);
|
|
19
|
-
start(input: ApiLocalServiceStartInput): Promise<LocalManagedServiceRuntime>;
|
|
20
|
-
private ensureNodeModules;
|
|
21
|
-
private waitForReadiness;
|
|
22
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import { LocalServiceProcessManager } from "./LocalServiceProcessManager.js";
|
|
3
|
-
import { CommandRunner } from "../process/CommandRunner.js";
|
|
4
|
-
export class ApiLocalService {
|
|
5
|
-
processManager;
|
|
6
|
-
commandRunner;
|
|
7
|
-
constructor(processManager = new LocalServiceProcessManager(), commandRunner = new CommandRunner()) {
|
|
8
|
-
this.processManager = processManager;
|
|
9
|
-
this.commandRunner = commandRunner;
|
|
10
|
-
}
|
|
11
|
-
async start(input) {
|
|
12
|
-
await this.ensureNodeModules(input.repoPath);
|
|
13
|
-
const runtime = this.processManager.start({
|
|
14
|
-
serviceName: "api",
|
|
15
|
-
repoPath: input.repoPath,
|
|
16
|
-
command: process.execPath,
|
|
17
|
-
args: [
|
|
18
|
-
"./node_modules/tsx/dist/cli.mjs",
|
|
19
|
-
"watch",
|
|
20
|
-
"src/server.ts",
|
|
21
|
-
"--config-path",
|
|
22
|
-
input.configPath
|
|
23
|
-
],
|
|
24
|
-
logPath: input.logPath,
|
|
25
|
-
env: {
|
|
26
|
-
APP_ENV: "local",
|
|
27
|
-
GITHUB_APP_CLIENT_ID: input.githubAppConfig?.appClientId ?? "",
|
|
28
|
-
GITHUB_APP_URL: input.githubAppConfig?.appUrl ?? "",
|
|
29
|
-
GITHUB_APP_PRIVATE_KEY_PEM: input.githubAppConfig?.appPrivateKeyPem ?? "",
|
|
30
|
-
COMPANYHELM_JWT_PRIVATE_KEY_PEM: input.state.auth.jwtPrivateKeyPem,
|
|
31
|
-
COMPANYHELM_JWT_PUBLIC_KEY_PEM: input.state.auth.jwtPublicKeyPem,
|
|
32
|
-
COMPANYHELM_LOG_LEVEL: input.logLevel
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
await this.waitForReadiness(input.graphqlUrl, runtime, "API");
|
|
36
|
-
return runtime;
|
|
37
|
-
}
|
|
38
|
-
async ensureNodeModules(repoPath) {
|
|
39
|
-
if (fs.existsSync(`${repoPath}/node_modules`)) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
await this.commandRunner.run("npm", ["install"], repoPath);
|
|
43
|
-
}
|
|
44
|
-
async waitForReadiness(url, runtime, serviceName) {
|
|
45
|
-
const deadline = Date.now() + 60_000;
|
|
46
|
-
while (Date.now() < deadline) {
|
|
47
|
-
if (!this.processManager.isRunning(runtime)) {
|
|
48
|
-
throw new Error(`${serviceName} exited before becoming ready.`);
|
|
49
|
-
}
|
|
50
|
-
try {
|
|
51
|
-
const response = await fetch(url, { method: "OPTIONS" });
|
|
52
|
-
if (response.ok) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
// Retry until the deadline.
|
|
58
|
-
}
|
|
59
|
-
await new Promise((resolve) => {
|
|
60
|
-
setTimeout(resolve, 1000);
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
throw new Error(`${serviceName} did not become ready: ${url}`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { LocalRepoOptionValue } from "../../commands/dependencies.js";
|
|
2
|
-
export interface DockerServiceSource {
|
|
3
|
-
source: "docker";
|
|
4
|
-
}
|
|
5
|
-
export interface LocalRepoServiceSource {
|
|
6
|
-
source: "local";
|
|
7
|
-
repoPath: string;
|
|
8
|
-
}
|
|
9
|
-
export type ResolvedServiceSource = DockerServiceSource | LocalRepoServiceSource;
|
|
10
|
-
export interface ResolvedServiceSources {
|
|
11
|
-
api: ResolvedServiceSource;
|
|
12
|
-
frontend: ResolvedServiceSource;
|
|
13
|
-
}
|
|
14
|
-
export interface LocalRepoSourceOptions {
|
|
15
|
-
apiRepoPath?: LocalRepoOptionValue;
|
|
16
|
-
webRepoPath?: LocalRepoOptionValue;
|
|
17
|
-
}
|
|
18
|
-
export declare class LocalRepoSourceResolver {
|
|
19
|
-
private readonly companyhelmRoot;
|
|
20
|
-
constructor(companyhelmRoot?: string);
|
|
21
|
-
resolve(options: LocalRepoSourceOptions): ResolvedServiceSources;
|
|
22
|
-
private resolveService;
|
|
23
|
-
private assertRepoPathExists;
|
|
24
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
export class LocalRepoSourceResolver {
|
|
4
|
-
companyhelmRoot;
|
|
5
|
-
constructor(companyhelmRoot = process.cwd()) {
|
|
6
|
-
this.companyhelmRoot = companyhelmRoot;
|
|
7
|
-
}
|
|
8
|
-
resolve(options) {
|
|
9
|
-
return {
|
|
10
|
-
api: this.resolveService("api", options.apiRepoPath, "../companyhelm-api"),
|
|
11
|
-
frontend: this.resolveService("frontend", options.webRepoPath, "../companyhelm-web")
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
resolveService(service, option, defaultRelativePath) {
|
|
15
|
-
if (option === undefined) {
|
|
16
|
-
return {
|
|
17
|
-
source: "docker"
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
const repoPath = path.resolve(this.companyhelmRoot, option === true ? defaultRelativePath : option);
|
|
21
|
-
this.assertRepoPathExists(service, repoPath);
|
|
22
|
-
return {
|
|
23
|
-
source: "local",
|
|
24
|
-
repoPath
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
assertRepoPathExists(service, repoPath) {
|
|
28
|
-
if (fs.existsSync(repoPath) && fs.statSync(repoPath).isDirectory()) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
throw new Error(`Local ${service} repo path does not exist: ${repoPath}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { LocalManagedServiceRuntime } from "../runtime/RuntimeState.js";
|
|
2
|
-
export interface LocalProcessStartInput {
|
|
3
|
-
serviceName: string;
|
|
4
|
-
repoPath: string;
|
|
5
|
-
command: string;
|
|
6
|
-
args: string[];
|
|
7
|
-
logPath: string;
|
|
8
|
-
env?: NodeJS.ProcessEnv;
|
|
9
|
-
}
|
|
10
|
-
export declare class LocalServiceProcessManager {
|
|
11
|
-
start(input: LocalProcessStartInput): LocalManagedServiceRuntime;
|
|
12
|
-
isRunning(runtime: LocalManagedServiceRuntime): boolean;
|
|
13
|
-
stop(runtime: LocalManagedServiceRuntime): Promise<void>;
|
|
14
|
-
printLogs(runtime: LocalManagedServiceRuntime): void;
|
|
15
|
-
private kill;
|
|
16
|
-
private isPidRunning;
|
|
17
|
-
private waitForExit;
|
|
18
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
export class LocalServiceProcessManager {
|
|
5
|
-
start(input) {
|
|
6
|
-
fs.mkdirSync(path.dirname(input.logPath), { recursive: true });
|
|
7
|
-
fs.writeFileSync(input.logPath, `\n[companyhelm] starting ${input.serviceName}: ${input.command} ${input.args.join(" ")}\n`, { flag: "a" });
|
|
8
|
-
const logFd = fs.openSync(input.logPath, "a");
|
|
9
|
-
const child = spawn(input.command, input.args, {
|
|
10
|
-
cwd: input.repoPath,
|
|
11
|
-
env: {
|
|
12
|
-
...process.env,
|
|
13
|
-
...input.env
|
|
14
|
-
},
|
|
15
|
-
stdio: ["ignore", logFd, logFd],
|
|
16
|
-
detached: true
|
|
17
|
-
});
|
|
18
|
-
child.unref();
|
|
19
|
-
fs.closeSync(logFd);
|
|
20
|
-
return {
|
|
21
|
-
source: "local",
|
|
22
|
-
repoPath: input.repoPath,
|
|
23
|
-
logPath: input.logPath,
|
|
24
|
-
pid: child.pid ?? 0
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
isRunning(runtime) {
|
|
28
|
-
return runtime.pid > 0 && this.isPidRunning(runtime.pid);
|
|
29
|
-
}
|
|
30
|
-
async stop(runtime) {
|
|
31
|
-
if (!this.isRunning(runtime)) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
this.kill(runtime.pid, "SIGTERM");
|
|
35
|
-
const exitedAfterSigTerm = await this.waitForExit(runtime.pid, 5000);
|
|
36
|
-
if (!exitedAfterSigTerm) {
|
|
37
|
-
this.kill(runtime.pid, "SIGKILL");
|
|
38
|
-
await this.waitForExit(runtime.pid, 2000);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
printLogs(runtime) {
|
|
42
|
-
if (!fs.existsSync(runtime.logPath)) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
process.stdout.write(fs.readFileSync(runtime.logPath, "utf8"));
|
|
46
|
-
}
|
|
47
|
-
kill(pid, signal) {
|
|
48
|
-
try {
|
|
49
|
-
process.kill(-pid, signal);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
// Fall through to direct child kill.
|
|
54
|
-
}
|
|
55
|
-
try {
|
|
56
|
-
process.kill(pid, signal);
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
// Ignore stale pid files.
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
isPidRunning(pid) {
|
|
63
|
-
try {
|
|
64
|
-
process.kill(pid, 0);
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
async waitForExit(pid, timeoutMs) {
|
|
72
|
-
const deadline = Date.now() + timeoutMs;
|
|
73
|
-
while (Date.now() < deadline) {
|
|
74
|
-
if (!this.isPidRunning(pid)) {
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
await new Promise((resolve) => {
|
|
78
|
-
setTimeout(resolve, 100);
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
return !this.isPidRunning(pid);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { LocalServiceProcessManager } from "./LocalServiceProcessManager.js";
|
|
2
|
-
import { CommandRunner } from "../process/CommandRunner.js";
|
|
3
|
-
import type { LogLevel } from "../../commands/dependencies.js";
|
|
4
|
-
import type { LocalManagedServiceRuntime } from "../runtime/RuntimeState.js";
|
|
5
|
-
export interface WebLocalServiceStartInput {
|
|
6
|
-
repoPath: string;
|
|
7
|
-
configPath: string;
|
|
8
|
-
url: string;
|
|
9
|
-
uiPort: number;
|
|
10
|
-
logPath: string;
|
|
11
|
-
logLevel: LogLevel;
|
|
12
|
-
}
|
|
13
|
-
export declare class WebLocalService {
|
|
14
|
-
private readonly processManager;
|
|
15
|
-
private readonly commandRunner;
|
|
16
|
-
private static readonly SERVICE_NAME;
|
|
17
|
-
constructor(processManager?: LocalServiceProcessManager, commandRunner?: CommandRunner);
|
|
18
|
-
start(input: WebLocalServiceStartInput): Promise<LocalManagedServiceRuntime>;
|
|
19
|
-
private ensureNodeModules;
|
|
20
|
-
private waitForReadiness;
|
|
21
|
-
private assertUiPortAvailable;
|
|
22
|
-
private buildStartupFailureMessage;
|
|
23
|
-
}
|