hereya-cli 0.2.1
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/README.md +392 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +6 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/dist/backend/common.d.ts +126 -0
- package/dist/backend/common.js +1 -0
- package/dist/backend/index.d.ts +4 -0
- package/dist/backend/index.js +5 -0
- package/dist/backend/local.d.ts +16 -0
- package/dist/backend/local.js +231 -0
- package/dist/commands/add/index.d.ts +13 -0
- package/dist/commands/add/index.js +96 -0
- package/dist/commands/bootstrap/index.d.ts +12 -0
- package/dist/commands/bootstrap/index.js +28 -0
- package/dist/commands/down/index.d.ts +10 -0
- package/dist/commands/down/index.js +78 -0
- package/dist/commands/env/index.d.ts +10 -0
- package/dist/commands/env/index.js +46 -0
- package/dist/commands/init/index.d.ts +13 -0
- package/dist/commands/init/index.js +46 -0
- package/dist/commands/remote/exec/index.d.ts +12 -0
- package/dist/commands/remote/exec/index.js +65 -0
- package/dist/commands/remove/index.d.ts +12 -0
- package/dist/commands/remove/index.js +80 -0
- package/dist/commands/run/index.d.ts +14 -0
- package/dist/commands/run/index.js +60 -0
- package/dist/commands/up/index.d.ts +10 -0
- package/dist/commands/up/index.js +121 -0
- package/dist/commands/workspace/create/index.d.ts +9 -0
- package/dist/commands/workspace/create/index.js +25 -0
- package/dist/commands/workspace/delete/index.d.ts +9 -0
- package/dist/commands/workspace/delete/index.js +24 -0
- package/dist/commands/workspace/env/index.d.ts +9 -0
- package/dist/commands/workspace/env/index.js +28 -0
- package/dist/commands/workspace/install/index.d.ts +14 -0
- package/dist/commands/workspace/install/index.js +74 -0
- package/dist/commands/workspace/uninstall/index.d.ts +14 -0
- package/dist/commands/workspace/uninstall/index.js +85 -0
- package/dist/iac/cdk.d.ts +8 -0
- package/dist/iac/cdk.js +83 -0
- package/dist/iac/common.d.ts +29 -0
- package/dist/iac/common.js +5 -0
- package/dist/iac/index.d.ts +16 -0
- package/dist/iac/index.js +18 -0
- package/dist/iac/terraform.d.ts +6 -0
- package/dist/iac/terraform.js +67 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/infrastructure/aws.d.ts +8 -0
- package/dist/infrastructure/aws.js +28 -0
- package/dist/infrastructure/common.d.ts +59 -0
- package/dist/infrastructure/common.js +7 -0
- package/dist/infrastructure/index.d.ts +43 -0
- package/dist/infrastructure/index.js +82 -0
- package/dist/infrastructure/local.d.ts +14 -0
- package/dist/infrastructure/local.js +81 -0
- package/dist/lib/config/common.d.ts +34 -0
- package/dist/lib/config/common.js +1 -0
- package/dist/lib/config/index.d.ts +3 -0
- package/dist/lib/config/index.js +5 -0
- package/dist/lib/config/simple.d.ts +8 -0
- package/dist/lib/config/simple.js +41 -0
- package/dist/lib/env/index.d.ts +27 -0
- package/dist/lib/env/index.js +42 -0
- package/dist/lib/env-utils.d.ts +8 -0
- package/dist/lib/env-utils.js +20 -0
- package/dist/lib/filesystem.d.ts +1 -0
- package/dist/lib/filesystem.js +15 -0
- package/dist/lib/object-utils.d.ts +4 -0
- package/dist/lib/object-utils.js +9 -0
- package/dist/lib/package/common.d.ts +15 -0
- package/dist/lib/package/common.js +1 -0
- package/dist/lib/package/github.d.ts +4 -0
- package/dist/lib/package/github.js +32 -0
- package/dist/lib/package/index.d.ts +30 -0
- package/dist/lib/package/index.js +45 -0
- package/dist/lib/parameter/index.d.ts +34 -0
- package/dist/lib/parameter/index.js +47 -0
- package/dist/lib/shell.d.ts +9 -0
- package/dist/lib/shell.js +16 -0
- package/dist/lib/yaml-utils.d.ts +5 -0
- package/dist/lib/yaml-utils.js +27 -0
- package/oclif.manifest.json +610 -0
- package/package.json +86 -0
package/dist/iac/cdk.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { CloudFormationClient, DescribeStacksCommand } from '@aws-sdk/client-cloudformation';
|
|
2
|
+
import { writeFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { runShell } from '../lib/shell.js';
|
|
5
|
+
import { load } from '../lib/yaml-utils.js';
|
|
6
|
+
export class Cdk {
|
|
7
|
+
async apply(input) {
|
|
8
|
+
try {
|
|
9
|
+
const parameterNames = await this.getParameterNames(input.pkgPath);
|
|
10
|
+
const serializedWorkspaceEnv = Object.entries(input.env)
|
|
11
|
+
.filter(([key]) => parameterNames.includes(key))
|
|
12
|
+
.flatMap(([key, value]) => ['--parameters', `${key}=${value}`]);
|
|
13
|
+
const serializedParameters = Object.entries(input.parameters ?? {})
|
|
14
|
+
.filter(([key]) => parameterNames.includes(key))
|
|
15
|
+
.flatMap(([key, value]) => ['--parameters', `${key}=${value}`]);
|
|
16
|
+
runShell('npx', [
|
|
17
|
+
'cdk', 'deploy', '--require-approval', 'never', ...serializedWorkspaceEnv, ...serializedParameters,
|
|
18
|
+
], {
|
|
19
|
+
directory: input.pkgPath,
|
|
20
|
+
env: { STACK_NAME: input.id },
|
|
21
|
+
});
|
|
22
|
+
const env = await this.getEnv(input.id);
|
|
23
|
+
return { env, success: true };
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return { reason: error.message, success: false };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async destroy(input) {
|
|
30
|
+
try {
|
|
31
|
+
const env = await this.getEnv(input.id);
|
|
32
|
+
const parameterNames = await this.getParameterNames(input.pkgPath);
|
|
33
|
+
const serializedWorkspaceEnv = Object.entries(input.env)
|
|
34
|
+
.filter(([key]) => parameterNames.includes(key))
|
|
35
|
+
.flatMap(([key, value]) => ['--parameters', `${key}=${value}`]);
|
|
36
|
+
const serializedParameters = Object.entries(input.parameters ?? {})
|
|
37
|
+
.filter(([key]) => parameterNames.includes(key))
|
|
38
|
+
.flatMap(([key, value]) => ['--parameters', `${key}=${value}`]);
|
|
39
|
+
runShell('npx', [
|
|
40
|
+
'cdk', 'destroy', '--force', ...serializedWorkspaceEnv, ...serializedParameters,
|
|
41
|
+
], {
|
|
42
|
+
directory: input.pkgPath,
|
|
43
|
+
env: { STACK_NAME: input.id }
|
|
44
|
+
});
|
|
45
|
+
return { env, success: true };
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
return { reason: error.message, success: false };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async getEnv(stackName) {
|
|
52
|
+
const stack = await this.getStack(stackName);
|
|
53
|
+
const env = {};
|
|
54
|
+
for (const output of stack?.Outputs?.filter(output => output.OutputKey) ?? []) {
|
|
55
|
+
env[output.OutputKey] = output.OutputValue;
|
|
56
|
+
}
|
|
57
|
+
return env;
|
|
58
|
+
}
|
|
59
|
+
async getParameterNames(workDir) {
|
|
60
|
+
runShell('npm', ['install'], { directory: workDir });
|
|
61
|
+
const result = runShell('npx', [
|
|
62
|
+
'cdk', 'synth',
|
|
63
|
+
], {
|
|
64
|
+
directory: workDir,
|
|
65
|
+
stdio: 'pipe'
|
|
66
|
+
});
|
|
67
|
+
const stackYamlPath = path.join(workDir, '.stack.yaml');
|
|
68
|
+
writeFileSync(stackYamlPath, result.stdout);
|
|
69
|
+
const { data: synthedStack } = await load(stackYamlPath);
|
|
70
|
+
return Object.keys(synthedStack.Parameters);
|
|
71
|
+
}
|
|
72
|
+
async getStack(stackName) {
|
|
73
|
+
const cfnClient = new CloudFormationClient({});
|
|
74
|
+
const response = await cfnClient.send(new DescribeStacksCommand({
|
|
75
|
+
StackName: stackName,
|
|
76
|
+
}));
|
|
77
|
+
const stack = response.Stacks?.[0];
|
|
78
|
+
if (!stack) {
|
|
79
|
+
throw new Error(`Stack ${stackName} not found`);
|
|
80
|
+
}
|
|
81
|
+
return stack;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface Iac {
|
|
2
|
+
apply(input: ApplyInput): Promise<ApplyOutput>;
|
|
3
|
+
destroy(input: DestroyInput): Promise<DestroyOutput>;
|
|
4
|
+
}
|
|
5
|
+
export declare enum IacType {
|
|
6
|
+
cdk = "cdk",
|
|
7
|
+
terraform = "terraform"
|
|
8
|
+
}
|
|
9
|
+
export type ApplyInput = {
|
|
10
|
+
env: {
|
|
11
|
+
[key: string]: string;
|
|
12
|
+
};
|
|
13
|
+
id: string;
|
|
14
|
+
parameters?: {
|
|
15
|
+
[key: string]: string;
|
|
16
|
+
};
|
|
17
|
+
pkgPath: string;
|
|
18
|
+
};
|
|
19
|
+
export type ApplyOutput = {
|
|
20
|
+
env: {
|
|
21
|
+
[key: string]: string;
|
|
22
|
+
};
|
|
23
|
+
success: true;
|
|
24
|
+
} | {
|
|
25
|
+
reason: string;
|
|
26
|
+
success: false;
|
|
27
|
+
};
|
|
28
|
+
export type DestroyInput = ApplyInput;
|
|
29
|
+
export type DestroyOutput = ApplyOutput;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Cdk } from './cdk.js';
|
|
2
|
+
import { Iac, IacType } from './common.js';
|
|
3
|
+
import { Terraform } from './terraform.js';
|
|
4
|
+
export declare const terraform: Terraform;
|
|
5
|
+
export declare const cdk: Cdk;
|
|
6
|
+
export declare function getIac({ type }: GetIacInput): GetIacOutput;
|
|
7
|
+
export type GetIacInput = {
|
|
8
|
+
type: IacType;
|
|
9
|
+
};
|
|
10
|
+
export type GetIacOutput = {
|
|
11
|
+
iac: Iac;
|
|
12
|
+
supported: true;
|
|
13
|
+
} | {
|
|
14
|
+
reason: string;
|
|
15
|
+
supported: false;
|
|
16
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Cdk } from './cdk.js';
|
|
2
|
+
import { IacType } from './common.js';
|
|
3
|
+
import { Terraform } from './terraform.js';
|
|
4
|
+
export const terraform = new Terraform();
|
|
5
|
+
export const cdk = new Cdk();
|
|
6
|
+
export function getIac({ type }) {
|
|
7
|
+
switch (type) {
|
|
8
|
+
case IacType.terraform: {
|
|
9
|
+
return { iac: terraform, supported: true };
|
|
10
|
+
}
|
|
11
|
+
case IacType.cdk: {
|
|
12
|
+
return { iac: cdk, supported: true };
|
|
13
|
+
}
|
|
14
|
+
default: {
|
|
15
|
+
return { reason: `Iac type ${type} is not supported yet!`, supported: false };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { mapObject } from '../lib/object-utils.js';
|
|
2
|
+
import { runShell } from '../lib/shell.js';
|
|
3
|
+
export class Terraform {
|
|
4
|
+
async apply(input) {
|
|
5
|
+
try {
|
|
6
|
+
runShell('terraform', ['init'], {
|
|
7
|
+
directory: input.pkgPath,
|
|
8
|
+
env: Object.assign(input.env, mapObject(input.parameters ?? {}, (key, value) => [`TF_VAR_${key}`, value]))
|
|
9
|
+
});
|
|
10
|
+
runShell('terraform', ['apply', '-auto-approve'], {
|
|
11
|
+
directory: input.pkgPath,
|
|
12
|
+
env: Object.assign(input.env, mapObject(input.parameters ?? {}, (key, value) => [`TF_VAR_${key}`, value]))
|
|
13
|
+
});
|
|
14
|
+
const env = await this.getEnv(input.pkgPath);
|
|
15
|
+
return {
|
|
16
|
+
env,
|
|
17
|
+
success: true
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
return {
|
|
22
|
+
reason: error.message,
|
|
23
|
+
success: false
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async destroy(input) {
|
|
28
|
+
const applyOutput = await this.apply(input);
|
|
29
|
+
if (!applyOutput.success) {
|
|
30
|
+
return applyOutput;
|
|
31
|
+
}
|
|
32
|
+
const { env } = applyOutput;
|
|
33
|
+
try {
|
|
34
|
+
runShell('terraform', ['destroy', '-auto-approve'], {
|
|
35
|
+
directory: input.pkgPath,
|
|
36
|
+
env: Object.assign(input.env, mapObject(input.parameters ?? {}, (key, value) => [`TF_VAR_${key}`, value]))
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
env,
|
|
40
|
+
success: true
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return {
|
|
45
|
+
reason: error.message,
|
|
46
|
+
success: false
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async getEnv(pkgPath) {
|
|
51
|
+
const resourceOut = runShell('terraform', ['output', '--json'], {
|
|
52
|
+
directory: pkgPath,
|
|
53
|
+
stdio: 'pipe'
|
|
54
|
+
});
|
|
55
|
+
let outStr = resourceOut.output.toString().trim();
|
|
56
|
+
const start = outStr.indexOf('{');
|
|
57
|
+
const end = outStr.lastIndexOf('}');
|
|
58
|
+
outStr = outStr.slice(start, end + 1);
|
|
59
|
+
const tfEnvObj = JSON.parse(outStr);
|
|
60
|
+
const tfEnv = {};
|
|
61
|
+
// eslint-disable-next-line guard-for-in
|
|
62
|
+
for (const key in tfEnvObj) {
|
|
63
|
+
tfEnv[key] = tfEnvObj[key].value;
|
|
64
|
+
}
|
|
65
|
+
return tfEnv;
|
|
66
|
+
}
|
|
67
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BootstrapInput, DestroyInput, DestroyOutput, Infrastructure, ProvisionInput, ProvisionOutput, ResolveEnvInput, ResolveEnvOutput, SaveEnvInput, SaveEnvOutput } from './common.js';
|
|
2
|
+
export declare class AwsInfrastructure implements Infrastructure {
|
|
3
|
+
bootstrap(_: BootstrapInput): Promise<void>;
|
|
4
|
+
destroy(_: DestroyInput): Promise<DestroyOutput>;
|
|
5
|
+
provision(_: ProvisionInput): Promise<ProvisionOutput>;
|
|
6
|
+
resolveEnv(_: ResolveEnvInput): Promise<ResolveEnvOutput>;
|
|
7
|
+
saveEnv(_: SaveEnvInput): Promise<SaveEnvOutput>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';
|
|
2
|
+
import { runShell } from '../lib/shell.js';
|
|
3
|
+
import { provisionPackage } from './index.js';
|
|
4
|
+
export class AwsInfrastructure {
|
|
5
|
+
async bootstrap(_) {
|
|
6
|
+
const stsClient = new STSClient({});
|
|
7
|
+
const { Account: accountId } = await stsClient.send(new GetCallerIdentityCommand({}));
|
|
8
|
+
const region = process.env.AWS_REGION;
|
|
9
|
+
runShell('npx', ['cdk', 'bootstrap', `aws://${accountId}/${region}`]);
|
|
10
|
+
const bootstrapPackage = 'hereya/bootstrap-aws-stack';
|
|
11
|
+
const output = await provisionPackage({ package: bootstrapPackage });
|
|
12
|
+
if (!output.success) {
|
|
13
|
+
throw new Error(output.reason);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async destroy(_) {
|
|
17
|
+
throw new Error('Method not implemented.');
|
|
18
|
+
}
|
|
19
|
+
async provision(_) {
|
|
20
|
+
throw new Error('Method not implemented.');
|
|
21
|
+
}
|
|
22
|
+
async resolveEnv(_) {
|
|
23
|
+
throw new Error('Method not implemented.');
|
|
24
|
+
}
|
|
25
|
+
async saveEnv(_) {
|
|
26
|
+
throw new Error('Method not implemented.');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { IacType } from '../iac/common.js';
|
|
2
|
+
export declare enum InfrastructureType {
|
|
3
|
+
aws = "aws",
|
|
4
|
+
azure = "azure",
|
|
5
|
+
gcp = "gcp",
|
|
6
|
+
local = "local"
|
|
7
|
+
}
|
|
8
|
+
export interface Infrastructure {
|
|
9
|
+
bootstrap(input: BootstrapInput): Promise<void>;
|
|
10
|
+
destroy(input: DestroyInput): Promise<DestroyOutput>;
|
|
11
|
+
provision(input: ProvisionInput): Promise<ProvisionOutput>;
|
|
12
|
+
resolveEnv(input: ResolveEnvInput): Promise<ResolveEnvOutput>;
|
|
13
|
+
saveEnv(input: SaveEnvInput): Promise<SaveEnvOutput>;
|
|
14
|
+
}
|
|
15
|
+
export type BootstrapInput = {
|
|
16
|
+
force?: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type ProvisionInput = {
|
|
19
|
+
canonicalName: string;
|
|
20
|
+
env?: {
|
|
21
|
+
[key: string]: string;
|
|
22
|
+
};
|
|
23
|
+
iacType: IacType;
|
|
24
|
+
id: string;
|
|
25
|
+
parameters?: {
|
|
26
|
+
[key: string]: string;
|
|
27
|
+
};
|
|
28
|
+
pkgName: string;
|
|
29
|
+
pkgUrl: string;
|
|
30
|
+
};
|
|
31
|
+
export type ProvisionOutput = {
|
|
32
|
+
env: {
|
|
33
|
+
[key: string]: string;
|
|
34
|
+
};
|
|
35
|
+
success: true;
|
|
36
|
+
} | {
|
|
37
|
+
reason: string;
|
|
38
|
+
success: false;
|
|
39
|
+
};
|
|
40
|
+
export type DestroyInput = ProvisionInput;
|
|
41
|
+
export type DestroyOutput = ProvisionOutput;
|
|
42
|
+
export type ResolveEnvInput = {
|
|
43
|
+
value: string;
|
|
44
|
+
};
|
|
45
|
+
export type ResolveEnvOutput = {
|
|
46
|
+
value: string;
|
|
47
|
+
};
|
|
48
|
+
export type SaveEnvInput = {
|
|
49
|
+
env: {
|
|
50
|
+
[key: string]: string;
|
|
51
|
+
};
|
|
52
|
+
id: string;
|
|
53
|
+
};
|
|
54
|
+
export type SaveEnvOutput = {
|
|
55
|
+
reason: string;
|
|
56
|
+
success: false;
|
|
57
|
+
} | {
|
|
58
|
+
success: true;
|
|
59
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export var InfrastructureType;
|
|
2
|
+
(function (InfrastructureType) {
|
|
3
|
+
InfrastructureType["aws"] = "aws";
|
|
4
|
+
InfrastructureType["azure"] = "azure";
|
|
5
|
+
InfrastructureType["gcp"] = "gcp";
|
|
6
|
+
InfrastructureType["local"] = "local";
|
|
7
|
+
})(InfrastructureType || (InfrastructureType = {}));
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PackageMetadata } from '../lib/package/index.js';
|
|
3
|
+
import { AwsInfrastructure } from './aws.js';
|
|
4
|
+
import { Infrastructure, InfrastructureType } from './common.js';
|
|
5
|
+
import { LocalInfrastructure } from './local.js';
|
|
6
|
+
export declare const localInfrastructure: LocalInfrastructure;
|
|
7
|
+
export declare const awsInfrastructure: AwsInfrastructure;
|
|
8
|
+
export declare function getInfrastructure(input: GetInfrastructureInput): GetInfrastructureOutput;
|
|
9
|
+
export declare function destroyPackage(input: DestroyPackageInput): Promise<DestroyPackageOutput>;
|
|
10
|
+
export declare function provisionPackage(input: ProvisionPackageInput): Promise<ProvisionPackageOutput>;
|
|
11
|
+
export type DestroyPackageInput = ProvisionPackageInput;
|
|
12
|
+
export type DestroyPackageOutput = ProvisionPackageOutput;
|
|
13
|
+
export type ProvisionPackageInput = {
|
|
14
|
+
env?: {
|
|
15
|
+
[key: string]: string;
|
|
16
|
+
};
|
|
17
|
+
package: string;
|
|
18
|
+
parameters?: {
|
|
19
|
+
[key: string]: string;
|
|
20
|
+
};
|
|
21
|
+
project?: string;
|
|
22
|
+
workspace?: string;
|
|
23
|
+
};
|
|
24
|
+
export type ProvisionPackageOutput = {
|
|
25
|
+
env: {
|
|
26
|
+
[key: string]: string;
|
|
27
|
+
};
|
|
28
|
+
metadata: z.infer<typeof PackageMetadata>;
|
|
29
|
+
success: true;
|
|
30
|
+
} | {
|
|
31
|
+
reason: string;
|
|
32
|
+
success: false;
|
|
33
|
+
};
|
|
34
|
+
export type GetInfrastructureInput = {
|
|
35
|
+
type: InfrastructureType;
|
|
36
|
+
};
|
|
37
|
+
export type GetInfrastructureOutput = {
|
|
38
|
+
reason: string;
|
|
39
|
+
supported: false;
|
|
40
|
+
} | {
|
|
41
|
+
infrastructure: Infrastructure;
|
|
42
|
+
supported: true;
|
|
43
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { resolvePackage } from '../lib/package/index.js';
|
|
2
|
+
import { AwsInfrastructure } from './aws.js';
|
|
3
|
+
import { InfrastructureType } from './common.js';
|
|
4
|
+
import { LocalInfrastructure } from './local.js';
|
|
5
|
+
export const localInfrastructure = new LocalInfrastructure();
|
|
6
|
+
export const awsInfrastructure = new AwsInfrastructure();
|
|
7
|
+
export function getInfrastructure(input) {
|
|
8
|
+
switch (input.type) {
|
|
9
|
+
case InfrastructureType.local: {
|
|
10
|
+
return {
|
|
11
|
+
infrastructure: localInfrastructure,
|
|
12
|
+
supported: true
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
case InfrastructureType.aws: {
|
|
16
|
+
return {
|
|
17
|
+
infrastructure: awsInfrastructure,
|
|
18
|
+
supported: true
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
default: {
|
|
22
|
+
return {
|
|
23
|
+
reason: `Infrastructure type ${input.type} is not supported yet!`,
|
|
24
|
+
supported: false
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function destroyPackage(input) {
|
|
30
|
+
const resolvePackageOutput = await resolvePackage({ package: input.package });
|
|
31
|
+
if (!resolvePackageOutput.found) {
|
|
32
|
+
return { reason: resolvePackageOutput.reason, success: false };
|
|
33
|
+
}
|
|
34
|
+
const { canonicalName, metadata, packageUri } = resolvePackageOutput;
|
|
35
|
+
const infrastructure$ = getInfrastructure({ type: metadata.infra });
|
|
36
|
+
if (!infrastructure$.supported) {
|
|
37
|
+
return { reason: infrastructure$.reason, success: false };
|
|
38
|
+
}
|
|
39
|
+
const { infrastructure } = infrastructure$;
|
|
40
|
+
const destroyOutput = await infrastructure.destroy({
|
|
41
|
+
canonicalName,
|
|
42
|
+
env: input.env,
|
|
43
|
+
iacType: metadata.iac,
|
|
44
|
+
id: ((input.project || input.workspace) ? [input.project, input.workspace, canonicalName] : [canonicalName])
|
|
45
|
+
.filter(Boolean).join('')
|
|
46
|
+
.replaceAll(/[^\dA-Za-z]/g, ''),
|
|
47
|
+
parameters: input.parameters,
|
|
48
|
+
pkgName: input.package,
|
|
49
|
+
pkgUrl: packageUri,
|
|
50
|
+
});
|
|
51
|
+
if (!destroyOutput.success) {
|
|
52
|
+
return { reason: destroyOutput.reason, success: false };
|
|
53
|
+
}
|
|
54
|
+
return { env: destroyOutput.env, metadata, success: true };
|
|
55
|
+
}
|
|
56
|
+
export async function provisionPackage(input) {
|
|
57
|
+
const resolvePackageOutput = await resolvePackage({ package: input.package });
|
|
58
|
+
if (!resolvePackageOutput.found) {
|
|
59
|
+
return { reason: resolvePackageOutput.reason, success: false };
|
|
60
|
+
}
|
|
61
|
+
const { canonicalName, metadata, packageUri } = resolvePackageOutput;
|
|
62
|
+
const infrastructure$ = getInfrastructure({ type: metadata.infra });
|
|
63
|
+
if (!infrastructure$.supported) {
|
|
64
|
+
return { reason: infrastructure$.reason, success: false };
|
|
65
|
+
}
|
|
66
|
+
const { infrastructure } = infrastructure$;
|
|
67
|
+
const provisionOutput = await infrastructure.provision({
|
|
68
|
+
canonicalName,
|
|
69
|
+
env: input.env,
|
|
70
|
+
iacType: metadata.iac,
|
|
71
|
+
id: ((input.project || input.workspace) ? [input.project, input.workspace, canonicalName] : [canonicalName])
|
|
72
|
+
.filter(Boolean).join('')
|
|
73
|
+
.replaceAll(/[^\dA-Za-z]/g, ''),
|
|
74
|
+
parameters: input.parameters,
|
|
75
|
+
pkgName: input.package,
|
|
76
|
+
pkgUrl: packageUri,
|
|
77
|
+
});
|
|
78
|
+
if (!provisionOutput.success) {
|
|
79
|
+
return { reason: provisionOutput.reason, success: false };
|
|
80
|
+
}
|
|
81
|
+
return { env: provisionOutput.env, metadata, success: true };
|
|
82
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Infrastructure, ProvisionInput, ProvisionOutput, SaveEnvInput, SaveEnvOutput } from './common.js';
|
|
2
|
+
export declare class LocalInfrastructure implements Infrastructure {
|
|
3
|
+
bootstrap(): Promise<void>;
|
|
4
|
+
destroy(input: ProvisionInput): Promise<ProvisionOutput>;
|
|
5
|
+
provision(input: ProvisionInput): Promise<ProvisionOutput>;
|
|
6
|
+
resolveEnv(input: {
|
|
7
|
+
value: string;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
value: string;
|
|
10
|
+
}>;
|
|
11
|
+
saveEnv(input: SaveEnvInput): Promise<SaveEnvOutput>;
|
|
12
|
+
private download;
|
|
13
|
+
private isNotEmpty;
|
|
14
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { simpleGit } from 'simple-git';
|
|
5
|
+
import { getIac } from '../iac/index.js';
|
|
6
|
+
export class LocalInfrastructure {
|
|
7
|
+
async bootstrap() {
|
|
8
|
+
console.log('Bootstrapping local infrastructure');
|
|
9
|
+
}
|
|
10
|
+
async destroy(input) {
|
|
11
|
+
// noinspection DuplicatedCode
|
|
12
|
+
const destPath = path.join(os.homedir(), '.hereya', input.id, input.canonicalName);
|
|
13
|
+
const downloadPath = await this.download(input.pkgUrl, destPath);
|
|
14
|
+
const iac$ = getIac({ type: input.iacType });
|
|
15
|
+
if (!iac$.supported) {
|
|
16
|
+
return { reason: iac$.reason, success: false };
|
|
17
|
+
}
|
|
18
|
+
const { iac } = iac$;
|
|
19
|
+
const output = await iac.destroy({
|
|
20
|
+
env: input.env ?? {},
|
|
21
|
+
id: input.id,
|
|
22
|
+
parameters: input.parameters,
|
|
23
|
+
pkgPath: downloadPath
|
|
24
|
+
});
|
|
25
|
+
if (!output.success) {
|
|
26
|
+
return { reason: output.reason, success: false };
|
|
27
|
+
}
|
|
28
|
+
// Remove downloaded package
|
|
29
|
+
await fs.rm(downloadPath, { recursive: true });
|
|
30
|
+
return { env: output.env, success: true };
|
|
31
|
+
}
|
|
32
|
+
async provision(input) {
|
|
33
|
+
// noinspection DuplicatedCode
|
|
34
|
+
const destPath = path.join(os.homedir(), '.hereya', input.id, input.canonicalName);
|
|
35
|
+
const downloadPath = await this.download(input.pkgUrl, destPath);
|
|
36
|
+
const iac$ = getIac({ type: input.iacType });
|
|
37
|
+
if (!iac$.supported) {
|
|
38
|
+
return { reason: iac$.reason, success: false };
|
|
39
|
+
}
|
|
40
|
+
const { iac } = iac$;
|
|
41
|
+
const output = await iac.apply({
|
|
42
|
+
env: input.env ?? {},
|
|
43
|
+
id: input.id,
|
|
44
|
+
parameters: input.parameters,
|
|
45
|
+
pkgPath: downloadPath
|
|
46
|
+
});
|
|
47
|
+
if (!output.success) {
|
|
48
|
+
return { reason: output.reason, success: false };
|
|
49
|
+
}
|
|
50
|
+
return { env: output.env, success: true };
|
|
51
|
+
}
|
|
52
|
+
async resolveEnv(input) {
|
|
53
|
+
return { value: input.value };
|
|
54
|
+
}
|
|
55
|
+
async saveEnv(input) {
|
|
56
|
+
console.log(`Saving env to ${input.id}`);
|
|
57
|
+
return { success: true };
|
|
58
|
+
}
|
|
59
|
+
async download(pkgUrl, destPath) {
|
|
60
|
+
if (await this.isNotEmpty(destPath)) {
|
|
61
|
+
console.log(`Package already downloaded at ${destPath}`);
|
|
62
|
+
return destPath;
|
|
63
|
+
}
|
|
64
|
+
await fs.mkdir(destPath, { recursive: true });
|
|
65
|
+
console.log(`Downloading package from ${pkgUrl}`);
|
|
66
|
+
// Initialize simple-git
|
|
67
|
+
const git = simpleGit();
|
|
68
|
+
// Clone repository into temp directory
|
|
69
|
+
await git.clone(pkgUrl, destPath, ['--depth=1']);
|
|
70
|
+
return destPath;
|
|
71
|
+
}
|
|
72
|
+
async isNotEmpty(directoryPath) {
|
|
73
|
+
try {
|
|
74
|
+
const files = await fs.readdir(directoryPath);
|
|
75
|
+
return files.length > 0;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false; // or you can handle the error as needed
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface ConfigManager {
|
|
2
|
+
addPackage: (input: AddPackageInput) => Promise<void>;
|
|
3
|
+
loadConfig: (input: LoadConfigInput) => Promise<LoadConfigOutput>;
|
|
4
|
+
removePackage: (input: RemovePackageInput) => Promise<void>;
|
|
5
|
+
saveConfig: (input: SaveConfigInput) => Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export interface LoadConfigInput {
|
|
8
|
+
projectRootDir?: string;
|
|
9
|
+
}
|
|
10
|
+
export type LoadConfigOutput = {
|
|
11
|
+
config: Config;
|
|
12
|
+
found: false;
|
|
13
|
+
} | {
|
|
14
|
+
config: Config;
|
|
15
|
+
found: true;
|
|
16
|
+
};
|
|
17
|
+
export interface Config {
|
|
18
|
+
packages?: {
|
|
19
|
+
[key: string]: {
|
|
20
|
+
version: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
project: string;
|
|
24
|
+
workspace: string;
|
|
25
|
+
}
|
|
26
|
+
export interface SaveConfigInput {
|
|
27
|
+
config: Config;
|
|
28
|
+
projectRootDir?: string;
|
|
29
|
+
}
|
|
30
|
+
export type AddPackageInput = {
|
|
31
|
+
package: string;
|
|
32
|
+
projectRootDir?: string;
|
|
33
|
+
};
|
|
34
|
+
export type RemovePackageInput = AddPackageInput;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AddPackageInput, ConfigManager, LoadConfigInput, LoadConfigOutput, RemovePackageInput, SaveConfigInput } from './common.js';
|
|
2
|
+
export declare class SimpleConfigManager implements ConfigManager {
|
|
3
|
+
addPackage(input: AddPackageInput): Promise<void>;
|
|
4
|
+
loadConfig(input: LoadConfigInput): Promise<LoadConfigOutput>;
|
|
5
|
+
removePackage(input: RemovePackageInput): Promise<void>;
|
|
6
|
+
saveConfig(input: SaveConfigInput): Promise<void>;
|
|
7
|
+
private getConfigPath;
|
|
8
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { getAnyPath } from '../filesystem.js';
|
|
3
|
+
import * as yaml from '../yaml-utils.js';
|
|
4
|
+
export class SimpleConfigManager {
|
|
5
|
+
async addPackage(input) {
|
|
6
|
+
const { config } = await this.loadConfig({ projectRootDir: input.projectRootDir });
|
|
7
|
+
await yaml.save({
|
|
8
|
+
...config,
|
|
9
|
+
packages: {
|
|
10
|
+
...config.packages,
|
|
11
|
+
[input.package]: {
|
|
12
|
+
version: ''
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
}, await this.getConfigPath(input.projectRootDir));
|
|
16
|
+
}
|
|
17
|
+
async loadConfig(input) {
|
|
18
|
+
const configFilePath = await this.getConfigPath(input.projectRootDir);
|
|
19
|
+
const { data: config, found } = await yaml.load(configFilePath);
|
|
20
|
+
return {
|
|
21
|
+
config,
|
|
22
|
+
found,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async removePackage(input) {
|
|
26
|
+
const { config } = await this.loadConfig({ projectRootDir: input.projectRootDir });
|
|
27
|
+
const newPackages = { ...config.packages };
|
|
28
|
+
delete newPackages[input.package];
|
|
29
|
+
await yaml.save({
|
|
30
|
+
...config,
|
|
31
|
+
packages: newPackages,
|
|
32
|
+
}, await this.getConfigPath(input.projectRootDir));
|
|
33
|
+
}
|
|
34
|
+
async saveConfig(input) {
|
|
35
|
+
const configFilePath = await this.getConfigPath(input.projectRootDir);
|
|
36
|
+
await yaml.save(input.config, configFilePath);
|
|
37
|
+
}
|
|
38
|
+
async getConfigPath(rootDir) {
|
|
39
|
+
return getAnyPath(path.join(rootDir ?? process.cwd(), 'hereya.yaml'), path.join(rootDir ?? process.cwd(), 'hereya.yml'));
|
|
40
|
+
}
|
|
41
|
+
}
|