@intentius/chant-lexicon-gcp 0.0.15
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/integrity.json +36 -0
- package/dist/manifest.json +12 -0
- package/dist/meta.json +10919 -0
- package/dist/rules/gcp-helpers.ts +117 -0
- package/dist/rules/hardcoded-project.ts +58 -0
- package/dist/rules/hardcoded-region.ts +56 -0
- package/dist/rules/public-iam.ts +43 -0
- package/dist/rules/wgc101.ts +56 -0
- package/dist/rules/wgc102.ts +35 -0
- package/dist/rules/wgc103.ts +45 -0
- package/dist/rules/wgc104.ts +42 -0
- package/dist/rules/wgc105.ts +46 -0
- package/dist/rules/wgc106.ts +36 -0
- package/dist/rules/wgc107.ts +39 -0
- package/dist/rules/wgc108.ts +41 -0
- package/dist/rules/wgc109.ts +39 -0
- package/dist/rules/wgc110.ts +38 -0
- package/dist/rules/wgc111.ts +54 -0
- package/dist/rules/wgc112.ts +56 -0
- package/dist/rules/wgc113.ts +42 -0
- package/dist/rules/wgc201.ts +36 -0
- package/dist/rules/wgc202.ts +39 -0
- package/dist/rules/wgc203.ts +44 -0
- package/dist/rules/wgc204.ts +39 -0
- package/dist/rules/wgc301.ts +34 -0
- package/dist/rules/wgc302.ts +34 -0
- package/dist/rules/wgc303.ts +37 -0
- package/dist/skills/chant-gcp-patterns.md +367 -0
- package/dist/skills/chant-gcp-security.md +276 -0
- package/dist/skills/chant-gcp.md +108 -0
- package/dist/types/index.d.ts +26529 -0
- package/package.json +35 -0
- package/src/actions/index.ts +52 -0
- package/src/codegen/docs-cli.ts +7 -0
- package/src/codegen/docs.ts +820 -0
- package/src/codegen/generate-cli.ts +24 -0
- package/src/codegen/generate.ts +252 -0
- package/src/codegen/naming.test.ts +49 -0
- package/src/codegen/naming.ts +132 -0
- package/src/codegen/package.ts +66 -0
- package/src/composites/cloud-function.ts +117 -0
- package/src/composites/cloud-run-service.ts +124 -0
- package/src/composites/cloud-sql-instance.ts +126 -0
- package/src/composites/composites.test.ts +432 -0
- package/src/composites/gcs-bucket.ts +111 -0
- package/src/composites/gke-cluster.ts +125 -0
- package/src/composites/index.ts +20 -0
- package/src/composites/managed-certificate.ts +79 -0
- package/src/composites/private-service.ts +95 -0
- package/src/composites/pubsub-pipeline.ts +102 -0
- package/src/composites/secure-project.ts +128 -0
- package/src/composites/vpc-network.ts +165 -0
- package/src/coverage.test.ts +27 -0
- package/src/coverage.ts +51 -0
- package/src/default-labels.test.ts +111 -0
- package/src/default-labels.ts +93 -0
- package/src/generated/index.d.ts +26529 -0
- package/src/generated/index.ts +1723 -0
- package/src/generated/lexicon-gcp.json +10919 -0
- package/src/generated/runtime.ts +4 -0
- package/src/import/generator.test.ts +125 -0
- package/src/import/generator.ts +82 -0
- package/src/import/parser.test.ts +167 -0
- package/src/import/parser.ts +80 -0
- package/src/import/roundtrip.test.ts +66 -0
- package/src/index.ts +54 -0
- package/src/lint/post-synth/gcp-helpers.ts +117 -0
- package/src/lint/post-synth/index.ts +20 -0
- package/src/lint/post-synth/post-synth.test.ts +693 -0
- package/src/lint/post-synth/wgc101.ts +56 -0
- package/src/lint/post-synth/wgc102.ts +35 -0
- package/src/lint/post-synth/wgc103.ts +45 -0
- package/src/lint/post-synth/wgc104.ts +42 -0
- package/src/lint/post-synth/wgc105.ts +46 -0
- package/src/lint/post-synth/wgc106.ts +36 -0
- package/src/lint/post-synth/wgc107.ts +39 -0
- package/src/lint/post-synth/wgc108.ts +41 -0
- package/src/lint/post-synth/wgc109.ts +39 -0
- package/src/lint/post-synth/wgc110.ts +38 -0
- package/src/lint/post-synth/wgc111.ts +54 -0
- package/src/lint/post-synth/wgc112.ts +56 -0
- package/src/lint/post-synth/wgc113.ts +42 -0
- package/src/lint/post-synth/wgc201.ts +36 -0
- package/src/lint/post-synth/wgc202.ts +39 -0
- package/src/lint/post-synth/wgc203.ts +44 -0
- package/src/lint/post-synth/wgc204.ts +39 -0
- package/src/lint/post-synth/wgc301.ts +34 -0
- package/src/lint/post-synth/wgc302.ts +34 -0
- package/src/lint/post-synth/wgc303.ts +37 -0
- package/src/lint/rules/hardcoded-project.ts +58 -0
- package/src/lint/rules/hardcoded-region.ts +56 -0
- package/src/lint/rules/index.ts +3 -0
- package/src/lint/rules/public-iam.ts +43 -0
- package/src/lint/rules/rules.test.ts +63 -0
- package/src/lsp/completions.test.ts +67 -0
- package/src/lsp/completions.ts +17 -0
- package/src/lsp/hover.test.ts +66 -0
- package/src/lsp/hover.ts +54 -0
- package/src/package-cli.ts +24 -0
- package/src/plugin.test.ts +250 -0
- package/src/plugin.ts +405 -0
- package/src/pseudo.test.ts +40 -0
- package/src/pseudo.ts +19 -0
- package/src/serializer.test.ts +250 -0
- package/src/serializer.ts +232 -0
- package/src/skills/chant-gcp-patterns.md +367 -0
- package/src/skills/chant-gcp-security.md +276 -0
- package/src/skills/chant-gcp.md +108 -0
- package/src/spec/fetch.test.ts +16 -0
- package/src/spec/fetch.ts +121 -0
- package/src/spec/parse.test.ts +163 -0
- package/src/spec/parse.ts +432 -0
- package/src/testdata/compute-instance.yaml +93 -0
- package/src/testdata/iam-policy-member.yaml +66 -0
- package/src/testdata/manifests/compute-instance.yaml +18 -0
- package/src/testdata/manifests/full-app.yaml +34 -0
- package/src/testdata/manifests/storage-bucket.yaml +12 -0
- package/src/testdata/storage-bucket.yaml +100 -0
- package/src/validate-cli.ts +13 -0
- package/src/validate.test.ts +38 -0
- package/src/validate.ts +30 -0
- package/src/variables.ts +15 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for GCP lexicon generation.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { dirname } from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
import { generate, writeGeneratedFiles } from "./generate";
|
|
9
|
+
|
|
10
|
+
// src/codegen/generate-cli.ts → dirname x3 → package root
|
|
11
|
+
const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
12
|
+
|
|
13
|
+
const verbose = process.argv.includes("--verbose") || !process.argv.includes("--quiet");
|
|
14
|
+
const force = process.argv.includes("--force");
|
|
15
|
+
|
|
16
|
+
const result = await generate({ verbose, force });
|
|
17
|
+
writeGeneratedFiles(result, pkgDir);
|
|
18
|
+
|
|
19
|
+
console.error(
|
|
20
|
+
`Generated ${result.resources} resources, ${result.properties} property types, ${result.enums} enums`,
|
|
21
|
+
);
|
|
22
|
+
if (result.warnings.length > 0) {
|
|
23
|
+
console.error(`${result.warnings.length} warnings`);
|
|
24
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GCP Config Connector generation pipeline — uses core generatePipeline
|
|
3
|
+
* with GCP-specific fetch, parse, naming, and generation callbacks.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
generatePipeline,
|
|
8
|
+
writeGeneratedArtifacts,
|
|
9
|
+
type GenerateOptions,
|
|
10
|
+
type GenerateResult,
|
|
11
|
+
type GeneratePipelineConfig,
|
|
12
|
+
} from "@intentius/chant/codegen/generate";
|
|
13
|
+
import { fetchCRDBundle } from "../spec/fetch";
|
|
14
|
+
import { parseGcpCRD, type GcpParseResult } from "../spec/parse";
|
|
15
|
+
import { NamingStrategy, propertyTypeName } from "./naming";
|
|
16
|
+
import {
|
|
17
|
+
generateRuntimeIndex as coreGenerateRuntimeIndex,
|
|
18
|
+
type RuntimeIndexEntry,
|
|
19
|
+
type RuntimeIndexPropertyEntry,
|
|
20
|
+
} from "@intentius/chant/codegen/generate-runtime-index";
|
|
21
|
+
|
|
22
|
+
export type { GenerateOptions, GenerateResult };
|
|
23
|
+
|
|
24
|
+
const gcpPipelineConfig: GeneratePipelineConfig<GcpParseResult> = {
|
|
25
|
+
fetchSchemas: async (opts) => {
|
|
26
|
+
return fetchCRDBundle(opts.force);
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
parseSchema: (_typeName, data) => {
|
|
30
|
+
const results = parseGcpCRD(data);
|
|
31
|
+
// Each CRD file may contain one or more resources
|
|
32
|
+
// We return the first one; additional ones get collected in augmentResults
|
|
33
|
+
if (results.length === 0) return null;
|
|
34
|
+
return results[0];
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
createNaming: (results) => new NamingStrategy(results),
|
|
38
|
+
|
|
39
|
+
augmentResults: (results, _opts, log) => {
|
|
40
|
+
const warnings: Array<{ file: string; error: string }> = [];
|
|
41
|
+
log(`Total: ${results.length} GCP Config Connector resource schemas`);
|
|
42
|
+
return { results, warnings };
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
generateRegistry: (results, naming) => {
|
|
46
|
+
return generateLexiconJSON(results, naming as NamingStrategy);
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
generateTypes: (results, naming) => {
|
|
50
|
+
return generateTypeScriptDeclarations(results, naming as NamingStrategy);
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
generateRuntimeIndex: (results, naming) => {
|
|
54
|
+
return generateRuntimeIndex(results, naming as NamingStrategy);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Run the full GCP generation pipeline.
|
|
60
|
+
*/
|
|
61
|
+
export async function generate(opts: GenerateOptions = {}): Promise<GenerateResult> {
|
|
62
|
+
return generatePipeline(gcpPipelineConfig, opts);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Write generated artifacts to disk.
|
|
67
|
+
*/
|
|
68
|
+
export function writeGeneratedFiles(result: GenerateResult, baseDir: string): void {
|
|
69
|
+
writeGeneratedArtifacts({
|
|
70
|
+
baseDir,
|
|
71
|
+
files: {
|
|
72
|
+
"lexicon-gcp.json": result.lexiconJSON,
|
|
73
|
+
"index.d.ts": result.typesDTS,
|
|
74
|
+
"index.ts": result.indexTS,
|
|
75
|
+
"runtime.ts": [
|
|
76
|
+
"/**",
|
|
77
|
+
" * Runtime factory constructors — re-exported from core.",
|
|
78
|
+
" */",
|
|
79
|
+
'export { createResource, createProperty } from "@intentius/chant/runtime";',
|
|
80
|
+
"",
|
|
81
|
+
].join("\n"),
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Extract the property-type suffix from a GCP `::` name and flatten to valid TS identifier.
|
|
88
|
+
* "GCP::Alloydb::Backup::EncryptionConfig", "GCP::Alloydb::Backup" → "EncryptionConfig"
|
|
89
|
+
* "GCP::Acm::AccessLevel::Custom::Expr", "GCP::Acm::AccessLevel" → "Custom_Expr"
|
|
90
|
+
*/
|
|
91
|
+
function gcpDefName(ptName: string, resourceType: string): string {
|
|
92
|
+
const prefix = `${resourceType}::`;
|
|
93
|
+
const raw = ptName.startsWith(prefix) ? ptName.slice(prefix.length) : ptName.split("::").pop()!;
|
|
94
|
+
return raw.replace(/::/g, "_");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── Lexicon JSON generation ─────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
function generateLexiconJSON(results: GcpParseResult[], naming: NamingStrategy): string {
|
|
100
|
+
const entries: Record<string, unknown> = {};
|
|
101
|
+
|
|
102
|
+
for (const r of results) {
|
|
103
|
+
const cfnType = r.resource.typeName;
|
|
104
|
+
const tsName = naming.resolve(cfnType);
|
|
105
|
+
if (!tsName) continue;
|
|
106
|
+
|
|
107
|
+
const apiVersion = `${r.gvk.group}/${r.gvk.version}`;
|
|
108
|
+
|
|
109
|
+
entries[tsName] = {
|
|
110
|
+
resourceType: cfnType,
|
|
111
|
+
kind: "resource",
|
|
112
|
+
apiVersion,
|
|
113
|
+
gvkKind: r.gvk.kind,
|
|
114
|
+
group: r.gvk.group,
|
|
115
|
+
properties: r.resource.properties.length,
|
|
116
|
+
attrs: Object.fromEntries(
|
|
117
|
+
r.resource.attributes.map((a) => [a.name, a.tsType]),
|
|
118
|
+
),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Add property types
|
|
122
|
+
for (const pt of r.propertyTypes) {
|
|
123
|
+
const defName = gcpDefName(pt.name, cfnType);
|
|
124
|
+
const ptName = propertyTypeName(tsName, defName);
|
|
125
|
+
entries[ptName] = {
|
|
126
|
+
resourceType: `${cfnType}.${pt.specType}`,
|
|
127
|
+
kind: "property",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return JSON.stringify(entries, null, 2);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ── TypeScript declarations generation ──────────────────────────────
|
|
136
|
+
|
|
137
|
+
function generateTypeScriptDeclarations(results: GcpParseResult[], naming: NamingStrategy): string {
|
|
138
|
+
const lines: string[] = [
|
|
139
|
+
"/**",
|
|
140
|
+
" * GCP Config Connector resource type declarations.",
|
|
141
|
+
" *",
|
|
142
|
+
" * Auto-generated — do not edit.",
|
|
143
|
+
" */",
|
|
144
|
+
"",
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
const emittedTypes = new Set<string>();
|
|
148
|
+
|
|
149
|
+
for (const r of results) {
|
|
150
|
+
const cfnType = r.resource.typeName;
|
|
151
|
+
const tsName = naming.resolve(cfnType);
|
|
152
|
+
if (!tsName) continue;
|
|
153
|
+
|
|
154
|
+
// Resource class interface
|
|
155
|
+
const desc = (r.resource.description ?? cfnType).replace(/\*\//g, "*\\/");
|
|
156
|
+
lines.push(`/** ${desc} */`);
|
|
157
|
+
lines.push(`export declare class ${tsName} {`);
|
|
158
|
+
lines.push(` constructor(props: ${tsName}Props);`);
|
|
159
|
+
|
|
160
|
+
// Attributes
|
|
161
|
+
for (const attr of r.resource.attributes) {
|
|
162
|
+
lines.push(` readonly ${attr.name}: ${attr.tsType};`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
lines.push(`}`);
|
|
166
|
+
lines.push("");
|
|
167
|
+
|
|
168
|
+
// Props interface
|
|
169
|
+
lines.push(`export interface ${tsName}Props {`);
|
|
170
|
+
for (const prop of r.resource.properties) {
|
|
171
|
+
const optional = prop.required ? "" : "?";
|
|
172
|
+
const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(prop.name) ? prop.name : JSON.stringify(prop.name);
|
|
173
|
+
const propDesc = prop.description ? ` /** ${prop.description.replace(/\*\//g, "*\\/")} */\n` : "";
|
|
174
|
+
lines.push(`${propDesc} ${safeName}${optional}: ${prop.tsType};`);
|
|
175
|
+
}
|
|
176
|
+
lines.push(`}`);
|
|
177
|
+
lines.push("");
|
|
178
|
+
|
|
179
|
+
// Aliases
|
|
180
|
+
for (const alias of naming.aliases(cfnType)) {
|
|
181
|
+
lines.push(`/** Alias for ${tsName} */`);
|
|
182
|
+
lines.push(`export declare const ${alias}: typeof ${tsName};`);
|
|
183
|
+
lines.push("");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Property types (skip duplicates from singularization collisions)
|
|
187
|
+
for (const pt of r.propertyTypes) {
|
|
188
|
+
const defName = gcpDefName(pt.name, cfnType);
|
|
189
|
+
const ptName = propertyTypeName(tsName, defName);
|
|
190
|
+
if (emittedTypes.has(ptName)) continue;
|
|
191
|
+
emittedTypes.add(ptName);
|
|
192
|
+
|
|
193
|
+
lines.push(`export declare class ${ptName} {`);
|
|
194
|
+
lines.push(` constructor(props: ${ptName}Props);`);
|
|
195
|
+
lines.push(`}`);
|
|
196
|
+
lines.push("");
|
|
197
|
+
|
|
198
|
+
lines.push(`export interface ${ptName}Props {`);
|
|
199
|
+
for (const prop of pt.properties) {
|
|
200
|
+
const optional = prop.required ? "" : "?";
|
|
201
|
+
const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(prop.name) ? prop.name : JSON.stringify(prop.name);
|
|
202
|
+
lines.push(` ${safeName}${optional}: ${prop.tsType};`);
|
|
203
|
+
}
|
|
204
|
+
lines.push(`}`);
|
|
205
|
+
lines.push("");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return lines.join("\n");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── Runtime index generation ────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
function generateRuntimeIndex(results: GcpParseResult[], naming: NamingStrategy): string {
|
|
215
|
+
const resourceEntries: RuntimeIndexEntry[] = [];
|
|
216
|
+
const propertyEntries: RuntimeIndexPropertyEntry[] = [];
|
|
217
|
+
const emittedProperties = new Set<string>();
|
|
218
|
+
|
|
219
|
+
for (const r of results) {
|
|
220
|
+
const cfnType = r.resource.typeName;
|
|
221
|
+
const tsName = naming.resolve(cfnType);
|
|
222
|
+
if (!tsName) continue;
|
|
223
|
+
|
|
224
|
+
const attrs: Record<string, string> = {};
|
|
225
|
+
for (const a of r.resource.attributes) {
|
|
226
|
+
attrs[a.name] = a.name;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
resourceEntries.push({ tsName, resourceType: cfnType, attrs });
|
|
230
|
+
|
|
231
|
+
for (const alias of naming.aliases(cfnType)) {
|
|
232
|
+
resourceEntries.push({ tsName: alias, resourceType: cfnType, attrs });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const pt of r.propertyTypes) {
|
|
236
|
+
const defName = gcpDefName(pt.name, cfnType);
|
|
237
|
+
const ptName = propertyTypeName(tsName, defName);
|
|
238
|
+
if (emittedProperties.has(ptName)) continue;
|
|
239
|
+
emittedProperties.add(ptName);
|
|
240
|
+
const ptCfnType = `${cfnType}.${pt.specType}`;
|
|
241
|
+
propertyEntries.push({ tsName: ptName, resourceType: ptCfnType });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return coreGenerateRuntimeIndex(resourceEntries, propertyEntries, {
|
|
246
|
+
lexiconName: "gcp",
|
|
247
|
+
pseudoReExports: {
|
|
248
|
+
names: ["GCP", "ProjectId", "Region", "Zone"],
|
|
249
|
+
from: "../pseudo",
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { NamingStrategy } from "./naming";
|
|
3
|
+
import type { GcpParseResult } from "../spec/parse";
|
|
4
|
+
|
|
5
|
+
function makeResult(typeName: string): GcpParseResult {
|
|
6
|
+
return {
|
|
7
|
+
resource: {
|
|
8
|
+
typeName,
|
|
9
|
+
properties: [],
|
|
10
|
+
attributes: [],
|
|
11
|
+
deprecatedProperties: [],
|
|
12
|
+
},
|
|
13
|
+
propertyTypes: [],
|
|
14
|
+
enums: [],
|
|
15
|
+
gvk: { group: "", version: "v1beta1", kind: typeName.split("::").pop()! },
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("NamingStrategy", () => {
|
|
20
|
+
test("resolves priority names", () => {
|
|
21
|
+
const results = [
|
|
22
|
+
makeResult("GCP::Compute::Instance"),
|
|
23
|
+
makeResult("GCP::Storage::Bucket"),
|
|
24
|
+
makeResult("GCP::Container::Cluster"),
|
|
25
|
+
];
|
|
26
|
+
const naming = new NamingStrategy(results);
|
|
27
|
+
|
|
28
|
+
expect(naming.resolve("GCP::Compute::Instance")).toBe("ComputeInstance");
|
|
29
|
+
expect(naming.resolve("GCP::Storage::Bucket")).toBe("StorageBucket");
|
|
30
|
+
expect(naming.resolve("GCP::Container::Cluster")).toBe("GKECluster");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("provides aliases", () => {
|
|
34
|
+
const results = [makeResult("GCP::Container::Cluster")];
|
|
35
|
+
const naming = new NamingStrategy(results);
|
|
36
|
+
|
|
37
|
+
const aliases = naming.aliases("GCP::Container::Cluster");
|
|
38
|
+
expect(aliases).toContain("GKE");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("resolves non-priority types by short name", () => {
|
|
42
|
+
const results = [makeResult("GCP::Accesscontextmanager::AccessLevel")];
|
|
43
|
+
const naming = new NamingStrategy(results);
|
|
44
|
+
|
|
45
|
+
const name = naming.resolve("GCP::Accesscontextmanager::AccessLevel");
|
|
46
|
+
expect(name).toBeDefined();
|
|
47
|
+
expect(name!.length).toBeGreaterThan(0);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GCP-specific naming configuration for the core NamingStrategy.
|
|
3
|
+
*
|
|
4
|
+
* Maps Config Connector resource types to concise TypeScript class names.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
NamingStrategy as CoreNamingStrategy,
|
|
9
|
+
type NamingConfig,
|
|
10
|
+
type NamingInput,
|
|
11
|
+
} from "@intentius/chant/codegen/naming";
|
|
12
|
+
|
|
13
|
+
export { propertyTypeName, extractDefName } from "@intentius/chant/codegen/naming";
|
|
14
|
+
|
|
15
|
+
import type { GcpParseResult } from "../spec/parse";
|
|
16
|
+
import { gcpShortName, gcpServiceName } from "../spec/parse";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Fixed TypeScript class names for key GCP resources.
|
|
20
|
+
*/
|
|
21
|
+
const priorityNames: Record<string, string> = {
|
|
22
|
+
"GCP::Compute::Instance": "ComputeInstance",
|
|
23
|
+
"GCP::Compute::Network": "VPCNetwork",
|
|
24
|
+
"GCP::Compute::Subnetwork": "Subnetwork",
|
|
25
|
+
"GCP::Compute::Firewall": "Firewall",
|
|
26
|
+
"GCP::Compute::Address": "ComputeAddress",
|
|
27
|
+
"GCP::Compute::Disk": "ComputeDisk",
|
|
28
|
+
"GCP::Compute::ForwardingRule": "ForwardingRule",
|
|
29
|
+
"GCP::Compute::HealthCheck": "ComputeHealthCheck",
|
|
30
|
+
"GCP::Compute::Router": "Router",
|
|
31
|
+
"GCP::Compute::RouterNAT": "RouterNAT",
|
|
32
|
+
"GCP::Compute::BackendService": "BackendService",
|
|
33
|
+
"GCP::Compute::TargetHTTPProxy": "TargetHTTPProxy",
|
|
34
|
+
"GCP::Compute::URLMap": "URLMap",
|
|
35
|
+
"GCP::Compute::SSLCertificate": "SSLCertificate",
|
|
36
|
+
"GCP::Storage::Bucket": "StorageBucket",
|
|
37
|
+
"GCP::Sql::Instance": "SQLInstance",
|
|
38
|
+
"GCP::Sql::Database": "SQLDatabase",
|
|
39
|
+
"GCP::Sql::User": "SQLUser",
|
|
40
|
+
"GCP::Container::Cluster": "GKECluster",
|
|
41
|
+
"GCP::Container::NodePool": "NodePool",
|
|
42
|
+
"GCP::Iam::ServiceAccount": "GCPServiceAccount",
|
|
43
|
+
"GCP::Iam::PolicyMember": "IAMPolicyMember",
|
|
44
|
+
"GCP::Iam::Policy": "IAMPolicy",
|
|
45
|
+
"GCP::Iam::AuditConfig": "IAMAuditConfig",
|
|
46
|
+
"GCP::Run::Service": "CloudRunService",
|
|
47
|
+
"GCP::Run::Job": "CloudRunJob",
|
|
48
|
+
"GCP::Pubsub::Topic": "PubSubTopic",
|
|
49
|
+
"GCP::Pubsub::Subscription": "PubSubSubscription",
|
|
50
|
+
"GCP::Bigquery::Dataset": "BigQueryDataset",
|
|
51
|
+
"GCP::Bigquery::Table": "BigQueryTable",
|
|
52
|
+
"GCP::Dns::ManagedZone": "DNSManagedZone",
|
|
53
|
+
"GCP::Dns::RecordSet": "DNSRecordSet",
|
|
54
|
+
"GCP::Logging::Metric": "LoggingMetric",
|
|
55
|
+
"GCP::Monitoring::AlertPolicy": "AlertPolicy",
|
|
56
|
+
"GCP::Secretmanager::Secret": "SecretManagerSecret",
|
|
57
|
+
"GCP::Secretmanager::SecretVersion": "SecretManagerSecretVersion",
|
|
58
|
+
"GCP::Artifactregistry::Repository": "ArtifactRegistryRepository",
|
|
59
|
+
"GCP::Cloudfunctions::Function": "CloudFunction",
|
|
60
|
+
"GCP::Kms::KeyRing": "KMSKeyRing",
|
|
61
|
+
"GCP::Kms::CryptoKey": "KMSCryptoKey",
|
|
62
|
+
"GCP::Redis::Instance": "RedisInstance",
|
|
63
|
+
"GCP::Memcache::Instance": "MemcacheInstance",
|
|
64
|
+
"GCP::Spanner::Instance": "SpannerInstance",
|
|
65
|
+
"GCP::Spanner::Database": "SpannerDatabase",
|
|
66
|
+
"GCP::Dataflow::Job": "DataflowJob",
|
|
67
|
+
"GCP::Bigtable::Instance": "BigtableInstance",
|
|
68
|
+
"GCP::Bigtable::Table": "BigtableTable",
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Additional TypeScript names beyond the primary priority name.
|
|
73
|
+
*/
|
|
74
|
+
const priorityAliases: Record<string, string[]> = {
|
|
75
|
+
"GCP::Compute::Network": ["VPC"],
|
|
76
|
+
"GCP::Container::Cluster": ["GKE"],
|
|
77
|
+
"GCP::Storage::Bucket": ["GCSBucket"],
|
|
78
|
+
"GCP::Sql::Instance": ["CloudSQL"],
|
|
79
|
+
"GCP::Iam::ServiceAccount": ["GSA"],
|
|
80
|
+
"GCP::Run::Service": ["CloudRun"],
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Service name abbreviations for collision-resolved names.
|
|
85
|
+
*/
|
|
86
|
+
const serviceAbbreviations: Record<string, string> = {
|
|
87
|
+
Compute: "Compute",
|
|
88
|
+
Storage: "Gcs",
|
|
89
|
+
Container: "Gke",
|
|
90
|
+
Sql: "Sql",
|
|
91
|
+
Iam: "Iam",
|
|
92
|
+
Bigquery: "BQ",
|
|
93
|
+
Pubsub: "PubSub",
|
|
94
|
+
Dns: "Dns",
|
|
95
|
+
Logging: "Log",
|
|
96
|
+
Monitoring: "Mon",
|
|
97
|
+
Secretmanager: "Sm",
|
|
98
|
+
Artifactregistry: "Ar",
|
|
99
|
+
Cloudfunctions: "Gcf",
|
|
100
|
+
Kms: "Kms",
|
|
101
|
+
Redis: "Redis",
|
|
102
|
+
Run: "Run",
|
|
103
|
+
Spanner: "Spanner",
|
|
104
|
+
Dataflow: "Df",
|
|
105
|
+
Bigtable: "Bt",
|
|
106
|
+
Memcache: "Mc",
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const gcpNamingConfig: NamingConfig = {
|
|
110
|
+
priorityNames,
|
|
111
|
+
priorityAliases,
|
|
112
|
+
priorityPropertyAliases: {},
|
|
113
|
+
serviceAbbreviations,
|
|
114
|
+
shortName: gcpShortName,
|
|
115
|
+
serviceName: (typeName: string) => {
|
|
116
|
+
const parts = typeName.split("::");
|
|
117
|
+
return parts.length >= 2 ? parts[1] : "Other";
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* GCP-specific NamingStrategy — wraps the core algorithm with GCP data tables.
|
|
123
|
+
*/
|
|
124
|
+
export class NamingStrategy extends CoreNamingStrategy {
|
|
125
|
+
constructor(results: GcpParseResult[]) {
|
|
126
|
+
const inputs: NamingInput[] = results.map((r) => ({
|
|
127
|
+
typeName: r.resource.typeName,
|
|
128
|
+
propertyTypes: r.propertyTypes,
|
|
129
|
+
}));
|
|
130
|
+
super(inputs, gcpNamingConfig);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GCP lexicon packaging — delegates to core packagePipeline
|
|
3
|
+
* with GCP-specific manifest building and skill collection.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createRequire } from "module";
|
|
7
|
+
import { readFileSync } from "fs";
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
import { dirname } from "path";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
import {
|
|
12
|
+
packagePipeline,
|
|
13
|
+
collectSkills,
|
|
14
|
+
type PackageOptions,
|
|
15
|
+
type PackageResult,
|
|
16
|
+
} from "@intentius/chant/codegen/package";
|
|
17
|
+
import { generate } from "./generate";
|
|
18
|
+
|
|
19
|
+
export type { PackageOptions, PackageResult };
|
|
20
|
+
|
|
21
|
+
// src/codegen/package.ts → dirname x2 → src/ (srcDir for rule collection)
|
|
22
|
+
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Package the GCP lexicon into a distributable BundleSpec.
|
|
26
|
+
*/
|
|
27
|
+
export async function packageLexicon(opts: PackageOptions = {}): Promise<PackageResult> {
|
|
28
|
+
const pkgJson = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf-8"));
|
|
29
|
+
|
|
30
|
+
return packagePipeline(
|
|
31
|
+
{
|
|
32
|
+
generate: (genOpts) => generate({ verbose: genOpts.verbose, force: genOpts.force }),
|
|
33
|
+
|
|
34
|
+
buildManifest: (_genResult) => {
|
|
35
|
+
const { gcpPlugin } = require("../plugin");
|
|
36
|
+
|
|
37
|
+
const pseudoParams: string[] = gcpPlugin.pseudoParameters?.() ?? [];
|
|
38
|
+
const pseudoParameters: Record<string, string> = {};
|
|
39
|
+
for (const p of pseudoParams) {
|
|
40
|
+
const shortName = p.split("::").pop()!;
|
|
41
|
+
pseudoParameters[shortName] = p;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
name: "gcp",
|
|
46
|
+
version: pkgJson.version ?? "0.0.0",
|
|
47
|
+
chantVersion: ">=0.1.0",
|
|
48
|
+
namespace: "GCP",
|
|
49
|
+
intrinsics: [],
|
|
50
|
+
pseudoParameters,
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
srcDir: pkgDir,
|
|
55
|
+
|
|
56
|
+
collectSkills: () => {
|
|
57
|
+
const { gcpPlugin } = require("../plugin");
|
|
58
|
+
const skillDefs = gcpPlugin.skills?.() ?? [];
|
|
59
|
+
return collectSkills(skillDefs);
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
version: pkgJson.version ?? "0.0.0",
|
|
63
|
+
},
|
|
64
|
+
opts,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudFunctionWithTrigger composite — CloudFunction + source bucket + optional PubSub/HTTP trigger + invoker IAM.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface CloudFunctionWithTriggerProps {
|
|
6
|
+
/** Function name. */
|
|
7
|
+
name: string;
|
|
8
|
+
/** Runtime (e.g., "nodejs20", "python312"). */
|
|
9
|
+
runtime: string;
|
|
10
|
+
/** Entry point function name. */
|
|
11
|
+
entryPoint: string;
|
|
12
|
+
/** GCP region. */
|
|
13
|
+
region?: string;
|
|
14
|
+
/** Available memory (default: "256M"). */
|
|
15
|
+
availableMemoryMb?: string;
|
|
16
|
+
/** Timeout in seconds (default: 60). */
|
|
17
|
+
timeout?: number;
|
|
18
|
+
/** Trigger type (default: "http"). */
|
|
19
|
+
triggerType?: "http" | "pubsub";
|
|
20
|
+
/** PubSub topic name (required if triggerType is "pubsub"). */
|
|
21
|
+
triggerTopic?: string;
|
|
22
|
+
/** Allow unauthenticated HTTP invocations (default: false). */
|
|
23
|
+
publicAccess?: boolean;
|
|
24
|
+
/** Environment variables. */
|
|
25
|
+
environmentVariables?: Record<string, string>;
|
|
26
|
+
/** Additional labels. */
|
|
27
|
+
labels?: Record<string, string>;
|
|
28
|
+
/** Namespace for all resources. */
|
|
29
|
+
namespace?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface CloudFunctionWithTriggerResult {
|
|
33
|
+
function: Record<string, unknown>;
|
|
34
|
+
sourceBucket: Record<string, unknown>;
|
|
35
|
+
invokerIam?: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function CloudFunctionWithTrigger(props: CloudFunctionWithTriggerProps): CloudFunctionWithTriggerResult {
|
|
39
|
+
const {
|
|
40
|
+
name,
|
|
41
|
+
runtime,
|
|
42
|
+
entryPoint,
|
|
43
|
+
region,
|
|
44
|
+
availableMemoryMb = "256M",
|
|
45
|
+
timeout = 60,
|
|
46
|
+
triggerType = "http",
|
|
47
|
+
triggerTopic,
|
|
48
|
+
publicAccess = false,
|
|
49
|
+
environmentVariables,
|
|
50
|
+
labels: extraLabels = {},
|
|
51
|
+
namespace,
|
|
52
|
+
} = props;
|
|
53
|
+
|
|
54
|
+
const commonLabels: Record<string, string> = {
|
|
55
|
+
"app.kubernetes.io/name": name,
|
|
56
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
57
|
+
...extraLabels,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const sourceBucket: Record<string, unknown> = {
|
|
61
|
+
metadata: {
|
|
62
|
+
name: `${name}-source`,
|
|
63
|
+
...(namespace && { namespace }),
|
|
64
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "source" },
|
|
65
|
+
},
|
|
66
|
+
location: region ?? "US",
|
|
67
|
+
uniformBucketLevelAccess: true,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const eventTrigger = triggerType === "pubsub" && triggerTopic
|
|
71
|
+
? { eventType: "google.cloud.pubsub.topic.v1.messagePublished", pubsubTopic: triggerTopic }
|
|
72
|
+
: undefined;
|
|
73
|
+
|
|
74
|
+
const fn: Record<string, unknown> = {
|
|
75
|
+
metadata: {
|
|
76
|
+
name,
|
|
77
|
+
...(namespace && { namespace }),
|
|
78
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "function" },
|
|
79
|
+
},
|
|
80
|
+
...(region && { location: region }),
|
|
81
|
+
runtime,
|
|
82
|
+
entryPoint,
|
|
83
|
+
availableMemoryMb,
|
|
84
|
+
timeout: `${timeout}s`,
|
|
85
|
+
buildConfig: {
|
|
86
|
+
sourceRef: {
|
|
87
|
+
storageBucketRef: { name: `${name}-source` },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
...(eventTrigger && { eventTrigger }),
|
|
91
|
+
...(environmentVariables && { environmentVariables }),
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const result: CloudFunctionWithTriggerResult = {
|
|
95
|
+
function: fn,
|
|
96
|
+
sourceBucket,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
if (publicAccess && triggerType === "http") {
|
|
100
|
+
result.invokerIam = {
|
|
101
|
+
metadata: {
|
|
102
|
+
name: `${name}-invoker`,
|
|
103
|
+
...(namespace && { namespace }),
|
|
104
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "iam" },
|
|
105
|
+
},
|
|
106
|
+
member: "allUsers",
|
|
107
|
+
role: "roles/cloudfunctions.invoker",
|
|
108
|
+
resourceRef: {
|
|
109
|
+
apiVersion: "cloudfunctions.cnrm.cloud.google.com/v1beta1",
|
|
110
|
+
kind: "CloudFunctionsFunction",
|
|
111
|
+
name,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return result;
|
|
117
|
+
}
|