@synergenius/flow-weaver 0.13.3 → 0.14.0
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/api/validate.js +8 -2
- package/dist/ast/types.d.ts +120 -0
- package/dist/chevrotain-parser/node-parser.d.ts +4 -0
- package/dist/chevrotain-parser/node-parser.js +41 -1
- package/dist/chevrotain-parser/port-parser.d.ts +1 -0
- package/dist/chevrotain-parser/port-parser.js +22 -2
- package/dist/chevrotain-parser/tokens.d.ts +3 -0
- package/dist/chevrotain-parser/tokens.js +15 -0
- package/dist/cli/commands/export.js +25 -38
- package/dist/cli/flow-weaver.mjs +63703 -54297
- package/dist/cli/templates/index.js +9 -0
- package/dist/cli/templates/workflows/cicd-docker.d.ts +9 -0
- package/dist/cli/templates/workflows/cicd-docker.js +110 -0
- package/dist/cli/templates/workflows/cicd-matrix.d.ts +9 -0
- package/dist/cli/templates/workflows/cicd-matrix.js +112 -0
- package/dist/cli/templates/workflows/cicd-multi-env.d.ts +9 -0
- package/dist/cli/templates/workflows/cicd-multi-env.js +118 -0
- package/dist/cli/templates/workflows/cicd-test-deploy.d.ts +11 -0
- package/dist/cli/templates/workflows/cicd-test-deploy.js +149 -0
- package/dist/constants.js +7 -0
- package/dist/deployment/index.d.ts +14 -7
- package/dist/deployment/index.js +29 -17
- package/dist/deployment/targets/base.d.ts +27 -2
- package/dist/deployment/targets/base.js +38 -6
- package/dist/deployment/targets/cicd-base.d.ts +111 -0
- package/dist/deployment/targets/cicd-base.js +357 -0
- package/dist/deployment/targets/cloudflare.d.ts +6 -0
- package/dist/deployment/targets/cloudflare.js +3 -0
- package/dist/deployment/targets/github-actions.d.ts +54 -0
- package/dist/deployment/targets/github-actions.js +366 -0
- package/dist/deployment/targets/gitlab-ci.d.ts +65 -0
- package/dist/deployment/targets/gitlab-ci.js +374 -0
- package/dist/deployment/targets/inngest.d.ts +25 -0
- package/dist/deployment/targets/inngest.js +10 -1
- package/dist/deployment/targets/lambda.d.ts +17 -0
- package/dist/deployment/targets/lambda.js +5 -0
- package/dist/deployment/targets/vercel.d.ts +16 -0
- package/dist/deployment/targets/vercel.js +5 -0
- package/dist/diagram/geometry.js +13 -5
- package/dist/export/index.d.ts +13 -9
- package/dist/export/index.js +129 -997
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/dist/jsdoc-parser.d.ts +130 -0
- package/dist/jsdoc-parser.js +408 -4
- package/dist/marketplace/index.d.ts +1 -1
- package/dist/marketplace/types.d.ts +13 -0
- package/dist/marketplace/validator.js +21 -2
- package/dist/mcp/tools-export.js +56 -14
- package/dist/parser.js +28 -1
- package/dist/validation/cicd-detection.d.ts +33 -0
- package/dist/validation/cicd-detection.js +76 -0
- package/dist/validation/cicd-rules.d.ts +62 -0
- package/dist/validation/cicd-rules.js +284 -0
- package/docs/reference/scaffold.md +4 -0
- package/package.json +4 -3
|
@@ -169,6 +169,20 @@ export interface BundleArtifacts extends ExportArtifacts {
|
|
|
169
169
|
/** OpenAPI spec if generated */
|
|
170
170
|
openApiSpec?: object;
|
|
171
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Deploy schema field definition.
|
|
174
|
+
* Describes a single key that a target accepts via @deploy annotations.
|
|
175
|
+
*/
|
|
176
|
+
export interface DeploySchemaField {
|
|
177
|
+
type: 'string' | 'number' | 'boolean' | 'string[]';
|
|
178
|
+
description: string;
|
|
179
|
+
default?: unknown;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Deploy schema — describes all @deploy keys a target accepts.
|
|
183
|
+
* Used for validation, Studio autocomplete, and documentation.
|
|
184
|
+
*/
|
|
185
|
+
export type DeploySchema = Record<string, DeploySchemaField>;
|
|
172
186
|
/**
|
|
173
187
|
* Export target interface
|
|
174
188
|
*/
|
|
@@ -177,6 +191,10 @@ export interface ExportTarget {
|
|
|
177
191
|
readonly name: string;
|
|
178
192
|
/** Human-readable description */
|
|
179
193
|
readonly description: string;
|
|
194
|
+
/** Schema for @deploy keys this target accepts on workflows */
|
|
195
|
+
readonly deploySchema?: DeploySchema;
|
|
196
|
+
/** Schema for @deploy keys this target accepts on node types */
|
|
197
|
+
readonly nodeTypeDeploySchema?: DeploySchema;
|
|
180
198
|
/**
|
|
181
199
|
* Generate deployment artifacts for single workflow
|
|
182
200
|
*/
|
|
@@ -307,10 +325,17 @@ export declare abstract class BaseExportTarget implements ExportTarget {
|
|
|
307
325
|
* Registry of available export targets
|
|
308
326
|
*/
|
|
309
327
|
export declare class ExportTargetRegistry {
|
|
310
|
-
private
|
|
311
|
-
|
|
328
|
+
private factories;
|
|
329
|
+
private instances;
|
|
330
|
+
/**
|
|
331
|
+
* Register a target factory. The target is only instantiated on first use.
|
|
332
|
+
* Also accepts a pre-instantiated target for backwards compatibility.
|
|
333
|
+
*/
|
|
334
|
+
register(nameOrTarget: string | ExportTarget, factory?: () => ExportTarget): void;
|
|
312
335
|
get(name: string): ExportTarget | undefined;
|
|
313
336
|
getAll(): ExportTarget[];
|
|
314
337
|
getNames(): string[];
|
|
338
|
+
/** Get deploy schemas from all registered targets (for validation/autocomplete) */
|
|
339
|
+
getDeploySchemas(): Record<string, DeploySchema>;
|
|
315
340
|
}
|
|
316
341
|
//# sourceMappingURL=base.d.ts.map
|
|
@@ -806,18 +806,50 @@ export const functionRegistry = {
|
|
|
806
806
|
* Registry of available export targets
|
|
807
807
|
*/
|
|
808
808
|
export class ExportTargetRegistry {
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
809
|
+
factories = new Map();
|
|
810
|
+
instances = new Map();
|
|
811
|
+
/**
|
|
812
|
+
* Register a target factory. The target is only instantiated on first use.
|
|
813
|
+
* Also accepts a pre-instantiated target for backwards compatibility.
|
|
814
|
+
*/
|
|
815
|
+
register(nameOrTarget, factory) {
|
|
816
|
+
if (typeof nameOrTarget === 'string') {
|
|
817
|
+
// New lazy factory API: register(name, factory)
|
|
818
|
+
this.factories.set(nameOrTarget, factory);
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
// Legacy API: register(target) — wrap in factory
|
|
822
|
+
const target = nameOrTarget;
|
|
823
|
+
this.instances.set(target.name, target);
|
|
824
|
+
this.factories.set(target.name, () => target);
|
|
825
|
+
}
|
|
812
826
|
}
|
|
813
827
|
get(name) {
|
|
814
|
-
|
|
828
|
+
if (!this.instances.has(name)) {
|
|
829
|
+
const factory = this.factories.get(name);
|
|
830
|
+
if (factory)
|
|
831
|
+
this.instances.set(name, factory());
|
|
832
|
+
}
|
|
833
|
+
return this.instances.get(name);
|
|
815
834
|
}
|
|
816
835
|
getAll() {
|
|
817
|
-
|
|
836
|
+
for (const [name, factory] of this.factories) {
|
|
837
|
+
if (!this.instances.has(name))
|
|
838
|
+
this.instances.set(name, factory());
|
|
839
|
+
}
|
|
840
|
+
return Array.from(this.instances.values());
|
|
818
841
|
}
|
|
819
842
|
getNames() {
|
|
820
|
-
return Array.from(this.
|
|
843
|
+
return Array.from(this.factories.keys());
|
|
844
|
+
}
|
|
845
|
+
/** Get deploy schemas from all registered targets (for validation/autocomplete) */
|
|
846
|
+
getDeploySchemas() {
|
|
847
|
+
const schemas = {};
|
|
848
|
+
for (const target of this.getAll()) {
|
|
849
|
+
if (target.deploySchema)
|
|
850
|
+
schemas[target.name] = target.deploySchema;
|
|
851
|
+
}
|
|
852
|
+
return schemas;
|
|
821
853
|
}
|
|
822
854
|
}
|
|
823
855
|
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base CI/CD Export Target
|
|
3
|
+
*
|
|
4
|
+
* Shared logic for generating CI/CD pipeline files (GitHub Actions, GitLab CI).
|
|
5
|
+
* Unlike serverless targets, CI/CD targets produce native YAML that runs without
|
|
6
|
+
* any FW runtime dependency.
|
|
7
|
+
*
|
|
8
|
+
* Key responsibilities:
|
|
9
|
+
* - Build job graph from AST (group nodes by @job, compute dependencies)
|
|
10
|
+
* - Resolve secret wiring (secret: pseudo-node connections → job env vars)
|
|
11
|
+
* - Inject artifact upload/download steps between jobs
|
|
12
|
+
* - Generate SECRETS_SETUP.md documentation
|
|
13
|
+
*/
|
|
14
|
+
import type { TWorkflowAST, TCICDSecret, TCICDCache, TCICDArtifact, TCICDService, TCICDMatrix } from '../../ast/types.js';
|
|
15
|
+
import { BaseExportTarget, type ExportOptions, type MultiWorkflowArtifacts, type CompiledWorkflow, type NodeTypeArtifacts, type NodeTypeInfo, type NodeTypeExportOptions, type BundleArtifacts, type BundleWorkflow, type BundleNodeType } from './base.js';
|
|
16
|
+
export interface CICDStep {
|
|
17
|
+
/** Node instance ID */
|
|
18
|
+
id: string;
|
|
19
|
+
/** Human-readable step name */
|
|
20
|
+
name: string;
|
|
21
|
+
/** Node type (used for action mapping) */
|
|
22
|
+
nodeType: string;
|
|
23
|
+
/** Environment variables for this step */
|
|
24
|
+
env?: Record<string, string>;
|
|
25
|
+
/** Per-target deploy config from @deploy annotations on the node type */
|
|
26
|
+
nodeTypeDeploy?: Record<string, Record<string, unknown>>;
|
|
27
|
+
}
|
|
28
|
+
export interface CICDJob {
|
|
29
|
+
/** Job identifier (from [job: "name"]) */
|
|
30
|
+
id: string;
|
|
31
|
+
/** Human-readable job name */
|
|
32
|
+
name: string;
|
|
33
|
+
/** Runner label (from @runner or job-level override) */
|
|
34
|
+
runner?: string;
|
|
35
|
+
/** Jobs that must complete before this one */
|
|
36
|
+
needs: string[];
|
|
37
|
+
/** Steps in execution order */
|
|
38
|
+
steps: CICDStep[];
|
|
39
|
+
/** Deployment environment (from [environment: "name"]) */
|
|
40
|
+
environment?: string;
|
|
41
|
+
/** Secret names used by this job */
|
|
42
|
+
secrets: string[];
|
|
43
|
+
/** Matrix strategy */
|
|
44
|
+
matrix?: TCICDMatrix;
|
|
45
|
+
/** Sidecar services */
|
|
46
|
+
services?: TCICDService[];
|
|
47
|
+
/** Cache configuration */
|
|
48
|
+
cache?: TCICDCache;
|
|
49
|
+
/** Artifacts to upload after this job */
|
|
50
|
+
uploadArtifacts?: TCICDArtifact[];
|
|
51
|
+
/** Artifact names to download before this job */
|
|
52
|
+
downloadArtifacts?: string[];
|
|
53
|
+
}
|
|
54
|
+
export interface ActionMapping {
|
|
55
|
+
/** GitHub Actions `uses:` value */
|
|
56
|
+
githubAction?: string;
|
|
57
|
+
/** GitHub Actions `with:` defaults */
|
|
58
|
+
githubWith?: Record<string, string>;
|
|
59
|
+
/** GitLab CI script commands */
|
|
60
|
+
gitlabScript?: string[];
|
|
61
|
+
/** GitLab CI image override */
|
|
62
|
+
gitlabImage?: string;
|
|
63
|
+
/** Human-readable step name */
|
|
64
|
+
label?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Default mapping from FW node types to CI/CD platform actions.
|
|
68
|
+
* Unknown node types fall back to a TODO placeholder.
|
|
69
|
+
*/
|
|
70
|
+
export declare const NODE_ACTION_MAP: Record<string, ActionMapping>;
|
|
71
|
+
export declare abstract class BaseCICDTarget extends BaseExportTarget {
|
|
72
|
+
/**
|
|
73
|
+
* CI/CD targets don't compile workflows — they read the AST and generate YAML.
|
|
74
|
+
* The generate() method must be implemented by each platform target.
|
|
75
|
+
*/
|
|
76
|
+
generateMultiWorkflow(_workflows: CompiledWorkflow[], _options: ExportOptions): Promise<MultiWorkflowArtifacts>;
|
|
77
|
+
generateNodeTypeService(_nodeTypes: NodeTypeInfo[], _options: NodeTypeExportOptions): Promise<NodeTypeArtifacts>;
|
|
78
|
+
generateBundle(_workflows: BundleWorkflow[], _nodeTypes: BundleNodeType[], _options: ExportOptions): Promise<BundleArtifacts>;
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the action mapping for a node type.
|
|
81
|
+
* Priority: @deploy annotations on the node type → NODE_ACTION_MAP fallback → undefined.
|
|
82
|
+
*
|
|
83
|
+
* This makes marketplace packages self-describing: a node type with
|
|
84
|
+
* `@deploy github-actions action="actions/checkout@v4"` contributes its own mapping.
|
|
85
|
+
*/
|
|
86
|
+
protected resolveActionMapping(step: CICDStep, targetName: string): ActionMapping | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* Build the job graph from the workflow AST.
|
|
89
|
+
* Groups nodes by their [job: "name"] attribute and computes dependencies
|
|
90
|
+
* from connections between nodes in different jobs.
|
|
91
|
+
*/
|
|
92
|
+
protected buildJobGraph(ast: TWorkflowAST): CICDJob[];
|
|
93
|
+
/**
|
|
94
|
+
* Topologically sort jobs so that dependencies come first.
|
|
95
|
+
*/
|
|
96
|
+
private topoSortJobs;
|
|
97
|
+
/**
|
|
98
|
+
* Map secret:NAME connections to the jobs that need them.
|
|
99
|
+
* Populates job.secrets and step.env for each secret connection.
|
|
100
|
+
*/
|
|
101
|
+
protected resolveJobSecrets(jobs: CICDJob[], ast: TWorkflowAST, renderSecretRef: (name: string) => string): void;
|
|
102
|
+
/**
|
|
103
|
+
* Add artifact upload/download steps between jobs.
|
|
104
|
+
*/
|
|
105
|
+
protected injectArtifactSteps(jobs: CICDJob[], artifacts: TCICDArtifact[]): void;
|
|
106
|
+
/**
|
|
107
|
+
* Generate SECRETS_SETUP.md with per-secret setup instructions.
|
|
108
|
+
*/
|
|
109
|
+
protected generateSecretsDoc(secrets: TCICDSecret[], platform: string): string;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=cicd-base.d.ts.map
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base CI/CD Export Target
|
|
3
|
+
*
|
|
4
|
+
* Shared logic for generating CI/CD pipeline files (GitHub Actions, GitLab CI).
|
|
5
|
+
* Unlike serverless targets, CI/CD targets produce native YAML that runs without
|
|
6
|
+
* any FW runtime dependency.
|
|
7
|
+
*
|
|
8
|
+
* Key responsibilities:
|
|
9
|
+
* - Build job graph from AST (group nodes by @job, compute dependencies)
|
|
10
|
+
* - Resolve secret wiring (secret: pseudo-node connections → job env vars)
|
|
11
|
+
* - Inject artifact upload/download steps between jobs
|
|
12
|
+
* - Generate SECRETS_SETUP.md documentation
|
|
13
|
+
*/
|
|
14
|
+
import { BaseExportTarget, } from './base.js';
|
|
15
|
+
/**
|
|
16
|
+
* Default mapping from FW node types to CI/CD platform actions.
|
|
17
|
+
* Unknown node types fall back to a TODO placeholder.
|
|
18
|
+
*/
|
|
19
|
+
export const NODE_ACTION_MAP = {
|
|
20
|
+
checkout: {
|
|
21
|
+
githubAction: 'actions/checkout@v4',
|
|
22
|
+
gitlabScript: ['echo "Checkout handled by GitLab CI runner"'],
|
|
23
|
+
label: 'Checkout code',
|
|
24
|
+
},
|
|
25
|
+
'setup-node': {
|
|
26
|
+
githubAction: 'actions/setup-node@v4',
|
|
27
|
+
githubWith: { 'node-version': '20' },
|
|
28
|
+
gitlabImage: 'node:20',
|
|
29
|
+
label: 'Setup Node.js',
|
|
30
|
+
},
|
|
31
|
+
'setup-python': {
|
|
32
|
+
githubAction: 'actions/setup-python@v5',
|
|
33
|
+
githubWith: { 'python-version': '3.12' },
|
|
34
|
+
gitlabImage: 'python:3.12',
|
|
35
|
+
label: 'Setup Python',
|
|
36
|
+
},
|
|
37
|
+
'npm-install': {
|
|
38
|
+
githubAction: undefined,
|
|
39
|
+
gitlabScript: ['npm ci'],
|
|
40
|
+
label: 'Install dependencies',
|
|
41
|
+
},
|
|
42
|
+
'npm-test': {
|
|
43
|
+
githubAction: undefined,
|
|
44
|
+
gitlabScript: ['npm test'],
|
|
45
|
+
label: 'Run tests',
|
|
46
|
+
},
|
|
47
|
+
'npm-build': {
|
|
48
|
+
githubAction: undefined,
|
|
49
|
+
gitlabScript: ['npm run build'],
|
|
50
|
+
label: 'Build',
|
|
51
|
+
},
|
|
52
|
+
'docker-build': {
|
|
53
|
+
githubAction: 'docker/build-push-action@v6',
|
|
54
|
+
githubWith: { push: 'false' },
|
|
55
|
+
gitlabScript: ['docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .'],
|
|
56
|
+
label: 'Build Docker image',
|
|
57
|
+
},
|
|
58
|
+
'docker-push': {
|
|
59
|
+
githubAction: 'docker/build-push-action@v6',
|
|
60
|
+
githubWith: { push: 'true' },
|
|
61
|
+
gitlabScript: ['docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA'],
|
|
62
|
+
label: 'Push Docker image',
|
|
63
|
+
},
|
|
64
|
+
'docker-login': {
|
|
65
|
+
githubAction: 'docker/login-action@v3',
|
|
66
|
+
gitlabScript: ['echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY'],
|
|
67
|
+
label: 'Docker login',
|
|
68
|
+
},
|
|
69
|
+
'shell-command': {
|
|
70
|
+
githubAction: undefined,
|
|
71
|
+
gitlabScript: ['echo "TODO: Add shell command"'],
|
|
72
|
+
label: 'Run command',
|
|
73
|
+
},
|
|
74
|
+
'deploy-ssh': {
|
|
75
|
+
githubAction: undefined,
|
|
76
|
+
gitlabScript: ['echo "TODO: Configure SSH deployment"'],
|
|
77
|
+
label: 'Deploy via SSH',
|
|
78
|
+
},
|
|
79
|
+
'deploy-s3': {
|
|
80
|
+
githubAction: 'aws-actions/configure-aws-credentials@v4',
|
|
81
|
+
gitlabScript: ['aws s3 sync dist/ s3://$S3_BUCKET/'],
|
|
82
|
+
label: 'Deploy to S3',
|
|
83
|
+
},
|
|
84
|
+
'slack-notify': {
|
|
85
|
+
githubAction: 'slackapi/slack-github-action@v1',
|
|
86
|
+
gitlabScript: ['curl -X POST -H "Content-type: application/json" --data "{\"text\":\"Pipeline complete\"}" $SLACK_WEBHOOK_URL'],
|
|
87
|
+
label: 'Send Slack notification',
|
|
88
|
+
},
|
|
89
|
+
'health-check': {
|
|
90
|
+
githubAction: undefined,
|
|
91
|
+
gitlabScript: ['curl --retry 10 --retry-delay 5 --retry-all-errors $HEALTH_CHECK_URL'],
|
|
92
|
+
label: 'Health check',
|
|
93
|
+
},
|
|
94
|
+
'wait-for-url': {
|
|
95
|
+
githubAction: undefined,
|
|
96
|
+
gitlabScript: ['for i in $(seq 1 30); do curl -sf $WAIT_URL && exit 0; sleep 10; done; exit 1'],
|
|
97
|
+
label: 'Wait for URL',
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Base CI/CD Target
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
export class BaseCICDTarget extends BaseExportTarget {
|
|
104
|
+
/**
|
|
105
|
+
* CI/CD targets don't compile workflows — they read the AST and generate YAML.
|
|
106
|
+
* The generate() method must be implemented by each platform target.
|
|
107
|
+
*/
|
|
108
|
+
// Not used by CI/CD targets (they don't produce serverless handlers)
|
|
109
|
+
async generateMultiWorkflow(_workflows, _options) {
|
|
110
|
+
throw new Error('CI/CD targets use generate() with AST, not generateMultiWorkflow()');
|
|
111
|
+
}
|
|
112
|
+
async generateNodeTypeService(_nodeTypes, _options) {
|
|
113
|
+
throw new Error('CI/CD targets do not export node types as services');
|
|
114
|
+
}
|
|
115
|
+
async generateBundle(_workflows, _nodeTypes, _options) {
|
|
116
|
+
throw new Error('CI/CD targets use generate() with AST, not generateBundle()');
|
|
117
|
+
}
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Shared CI/CD Logic
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
/**
|
|
122
|
+
* Resolve the action mapping for a node type.
|
|
123
|
+
* Priority: @deploy annotations on the node type → NODE_ACTION_MAP fallback → undefined.
|
|
124
|
+
*
|
|
125
|
+
* This makes marketplace packages self-describing: a node type with
|
|
126
|
+
* `@deploy github-actions action="actions/checkout@v4"` contributes its own mapping.
|
|
127
|
+
*/
|
|
128
|
+
resolveActionMapping(step, targetName) {
|
|
129
|
+
// 1. Check @deploy annotations on the node type
|
|
130
|
+
const deployConfig = step.nodeTypeDeploy?.[targetName];
|
|
131
|
+
if (deployConfig) {
|
|
132
|
+
return {
|
|
133
|
+
githubAction: deployConfig.action,
|
|
134
|
+
githubWith: deployConfig.with
|
|
135
|
+
? (typeof deployConfig.with === 'string'
|
|
136
|
+
? JSON.parse(deployConfig.with)
|
|
137
|
+
: deployConfig.with)
|
|
138
|
+
: undefined,
|
|
139
|
+
gitlabScript: deployConfig.script
|
|
140
|
+
? (Array.isArray(deployConfig.script)
|
|
141
|
+
? deployConfig.script
|
|
142
|
+
: [deployConfig.script])
|
|
143
|
+
: undefined,
|
|
144
|
+
gitlabImage: deployConfig.image,
|
|
145
|
+
label: deployConfig.label || step.name,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
// 2. Fall back to built-in NODE_ACTION_MAP
|
|
149
|
+
return NODE_ACTION_MAP[step.nodeType];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Build the job graph from the workflow AST.
|
|
153
|
+
* Groups nodes by their [job: "name"] attribute and computes dependencies
|
|
154
|
+
* from connections between nodes in different jobs.
|
|
155
|
+
*/
|
|
156
|
+
buildJobGraph(ast) {
|
|
157
|
+
// Build node type lookup for @deploy annotations
|
|
158
|
+
const nodeTypeLookup = new Map();
|
|
159
|
+
for (const nt of ast.nodeTypes) {
|
|
160
|
+
nodeTypeLookup.set(nt.name, nt);
|
|
161
|
+
if (nt.functionName !== nt.name)
|
|
162
|
+
nodeTypeLookup.set(nt.functionName, nt);
|
|
163
|
+
}
|
|
164
|
+
// Group instances by job
|
|
165
|
+
const jobMap = new Map();
|
|
166
|
+
const defaultRunner = ast.options?.cicd?.runner;
|
|
167
|
+
for (const inst of ast.instances) {
|
|
168
|
+
const jobName = inst.job || 'default';
|
|
169
|
+
if (!jobMap.has(jobName))
|
|
170
|
+
jobMap.set(jobName, []);
|
|
171
|
+
jobMap.get(jobName).push(inst);
|
|
172
|
+
}
|
|
173
|
+
// Build node -> job lookup
|
|
174
|
+
const nodeJob = new Map();
|
|
175
|
+
for (const inst of ast.instances) {
|
|
176
|
+
nodeJob.set(inst.id, inst.job || 'default');
|
|
177
|
+
}
|
|
178
|
+
// Build job dependency graph from connections
|
|
179
|
+
const jobDeps = new Map();
|
|
180
|
+
for (const conn of ast.connections) {
|
|
181
|
+
if (conn.from.node.startsWith('secret:'))
|
|
182
|
+
continue;
|
|
183
|
+
if (conn.from.node === 'Start' || conn.to.node === 'Exit')
|
|
184
|
+
continue;
|
|
185
|
+
const fromJob = nodeJob.get(conn.from.node);
|
|
186
|
+
const toJob = nodeJob.get(conn.to.node);
|
|
187
|
+
if (fromJob && toJob && fromJob !== toJob) {
|
|
188
|
+
if (!jobDeps.has(toJob))
|
|
189
|
+
jobDeps.set(toJob, new Set());
|
|
190
|
+
jobDeps.get(toJob).add(fromJob);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Convert to CICDJob array
|
|
194
|
+
const jobs = [];
|
|
195
|
+
for (const [jobId, instances] of jobMap) {
|
|
196
|
+
// Determine environment from first instance with one
|
|
197
|
+
const environment = instances.find((i) => i.environment)?.environment;
|
|
198
|
+
const steps = instances.map((inst) => {
|
|
199
|
+
const nt = nodeTypeLookup.get(inst.nodeType);
|
|
200
|
+
return {
|
|
201
|
+
id: inst.id,
|
|
202
|
+
name: inst.config?.label || inst.id,
|
|
203
|
+
nodeType: inst.nodeType,
|
|
204
|
+
...(nt?.deploy && { nodeTypeDeploy: nt.deploy }),
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
const needs = jobDeps.get(jobId)
|
|
208
|
+
? Array.from(jobDeps.get(jobId))
|
|
209
|
+
: [];
|
|
210
|
+
jobs.push({
|
|
211
|
+
id: jobId,
|
|
212
|
+
name: jobId,
|
|
213
|
+
runner: defaultRunner,
|
|
214
|
+
needs,
|
|
215
|
+
steps,
|
|
216
|
+
environment,
|
|
217
|
+
secrets: [],
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
// Topologically sort jobs so dependencies come first
|
|
221
|
+
return this.topoSortJobs(jobs);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Topologically sort jobs so that dependencies come first.
|
|
225
|
+
*/
|
|
226
|
+
topoSortJobs(jobs) {
|
|
227
|
+
const jobMap = new Map(jobs.map((j) => [j.id, j]));
|
|
228
|
+
const visited = new Set();
|
|
229
|
+
const sorted = [];
|
|
230
|
+
function visit(id) {
|
|
231
|
+
if (visited.has(id))
|
|
232
|
+
return;
|
|
233
|
+
visited.add(id);
|
|
234
|
+
const job = jobMap.get(id);
|
|
235
|
+
if (!job)
|
|
236
|
+
return;
|
|
237
|
+
for (const dep of job.needs) {
|
|
238
|
+
visit(dep);
|
|
239
|
+
}
|
|
240
|
+
sorted.push(job);
|
|
241
|
+
}
|
|
242
|
+
for (const job of jobs) {
|
|
243
|
+
visit(job.id);
|
|
244
|
+
}
|
|
245
|
+
return sorted;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Map secret:NAME connections to the jobs that need them.
|
|
249
|
+
* Populates job.secrets and step.env for each secret connection.
|
|
250
|
+
*/
|
|
251
|
+
resolveJobSecrets(jobs, ast, renderSecretRef) {
|
|
252
|
+
// Build node -> job lookup
|
|
253
|
+
const nodeJob = new Map();
|
|
254
|
+
for (const inst of ast.instances) {
|
|
255
|
+
nodeJob.set(inst.id, inst.job || 'default');
|
|
256
|
+
}
|
|
257
|
+
// Build step lookup
|
|
258
|
+
const stepMap = new Map();
|
|
259
|
+
for (const job of jobs) {
|
|
260
|
+
for (const step of job.steps) {
|
|
261
|
+
stepMap.set(step.id, step);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Process secret connections
|
|
265
|
+
for (const conn of ast.connections) {
|
|
266
|
+
if (!conn.from.node.startsWith('secret:'))
|
|
267
|
+
continue;
|
|
268
|
+
const secretName = conn.from.node.substring(7);
|
|
269
|
+
const targetNode = conn.to.node;
|
|
270
|
+
const targetPort = conn.to.port;
|
|
271
|
+
const jobId = nodeJob.get(targetNode);
|
|
272
|
+
if (!jobId)
|
|
273
|
+
continue;
|
|
274
|
+
const job = jobs.find((j) => j.id === jobId);
|
|
275
|
+
if (!job)
|
|
276
|
+
continue;
|
|
277
|
+
// Add secret to job
|
|
278
|
+
if (!job.secrets.includes(secretName)) {
|
|
279
|
+
job.secrets.push(secretName);
|
|
280
|
+
}
|
|
281
|
+
// Add env var to the step
|
|
282
|
+
const step = stepMap.get(targetNode);
|
|
283
|
+
if (step) {
|
|
284
|
+
step.env = step.env || {};
|
|
285
|
+
// Convert port name to env var (e.g., npmToken -> NPM_TOKEN or use secret name directly)
|
|
286
|
+
step.env[targetPort.replace(/([A-Z])/g, '_$1').toUpperCase().replace(/^_/, '')] =
|
|
287
|
+
renderSecretRef(secretName);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Add artifact upload/download steps between jobs.
|
|
293
|
+
*/
|
|
294
|
+
injectArtifactSteps(jobs, artifacts) {
|
|
295
|
+
if (artifacts.length === 0)
|
|
296
|
+
return;
|
|
297
|
+
// For each job that has dependencies, check if any dependency produces artifacts
|
|
298
|
+
for (const job of jobs) {
|
|
299
|
+
if (job.needs.length === 0)
|
|
300
|
+
continue;
|
|
301
|
+
// Check if any needed job produces artifacts
|
|
302
|
+
const neededJobs = jobs.filter((j) => job.needs.includes(j.id));
|
|
303
|
+
for (const needed of neededJobs) {
|
|
304
|
+
// Find artifacts that match the producing job
|
|
305
|
+
const jobArtifacts = artifacts.filter((a) => !a.name || needed.steps.some((s) => s.nodeType === a.name));
|
|
306
|
+
if (jobArtifacts.length > 0) {
|
|
307
|
+
needed.uploadArtifacts = (needed.uploadArtifacts || []).concat(jobArtifacts);
|
|
308
|
+
job.downloadArtifacts = (job.downloadArtifacts || []).concat(jobArtifacts.map((a) => a.name));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Generate SECRETS_SETUP.md with per-secret setup instructions.
|
|
315
|
+
*/
|
|
316
|
+
generateSecretsDoc(secrets, platform) {
|
|
317
|
+
if (secrets.length === 0)
|
|
318
|
+
return '';
|
|
319
|
+
const lines = [
|
|
320
|
+
'# Secrets Setup Guide',
|
|
321
|
+
'',
|
|
322
|
+
`This workflow requires ${secrets.length} secret(s) to be configured.`,
|
|
323
|
+
'',
|
|
324
|
+
];
|
|
325
|
+
for (const secret of secrets) {
|
|
326
|
+
lines.push(`## ${secret.name}`);
|
|
327
|
+
if (secret.description) {
|
|
328
|
+
lines.push(`> ${secret.description}`);
|
|
329
|
+
}
|
|
330
|
+
lines.push('');
|
|
331
|
+
if (platform === 'github-actions' || secret.platform === 'all' || secret.platform === 'github' || !secret.platform) {
|
|
332
|
+
lines.push('**GitHub Actions:**');
|
|
333
|
+
lines.push('1. Go to your repository on GitHub');
|
|
334
|
+
lines.push('2. Navigate to Settings > Secrets and variables > Actions');
|
|
335
|
+
lines.push('3. Click "New repository secret"');
|
|
336
|
+
lines.push(`4. Name: \`${secret.name}\``);
|
|
337
|
+
lines.push('5. Paste your secret value and click "Add secret"');
|
|
338
|
+
lines.push('');
|
|
339
|
+
}
|
|
340
|
+
if (platform === 'gitlab-ci' || secret.platform === 'all' || secret.platform === 'gitlab' || !secret.platform) {
|
|
341
|
+
lines.push('**GitLab CI:**');
|
|
342
|
+
lines.push('1. Go to your project on GitLab');
|
|
343
|
+
lines.push('2. Navigate to Settings > CI/CD > Variables');
|
|
344
|
+
lines.push('3. Click "Add variable"');
|
|
345
|
+
lines.push(`4. Key: \`${secret.name}\``);
|
|
346
|
+
lines.push('5. Paste your secret value');
|
|
347
|
+
lines.push('6. Check "Mask variable" and optionally "Protect variable"');
|
|
348
|
+
lines.push('7. Click "Add variable"');
|
|
349
|
+
lines.push('');
|
|
350
|
+
}
|
|
351
|
+
lines.push('---');
|
|
352
|
+
lines.push('');
|
|
353
|
+
}
|
|
354
|
+
return lines.join('\n');
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
//# sourceMappingURL=cicd-base.js.map
|
|
@@ -10,6 +10,12 @@ import { BaseExportTarget, type ExportOptions, type ExportArtifacts, type Deploy
|
|
|
10
10
|
export declare class CloudflareTarget extends BaseExportTarget {
|
|
11
11
|
readonly name = "cloudflare";
|
|
12
12
|
readonly description = "Cloudflare Workers";
|
|
13
|
+
readonly deploySchema: {
|
|
14
|
+
compatDate: {
|
|
15
|
+
type: "string";
|
|
16
|
+
description: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
13
19
|
generate(options: ExportOptions): Promise<ExportArtifacts>;
|
|
14
20
|
/**
|
|
15
21
|
* Generate OpenAPI specification for the workflow
|
|
@@ -687,6 +687,9 @@ export default {
|
|
|
687
687
|
export class CloudflareTarget extends BaseExportTarget {
|
|
688
688
|
name = 'cloudflare';
|
|
689
689
|
description = 'Cloudflare Workers';
|
|
690
|
+
deploySchema = {
|
|
691
|
+
compatDate: { type: 'string', description: 'Compatibility date' },
|
|
692
|
+
};
|
|
690
693
|
async generate(options) {
|
|
691
694
|
const files = [];
|
|
692
695
|
const includeDocs = options.includeDocs ?? false;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Actions Export Target
|
|
3
|
+
*
|
|
4
|
+
* Generates .github/workflows/<name>.yml from a Flow Weaver CI/CD workflow.
|
|
5
|
+
* No FW runtime dependency — outputs native GitHub Actions YAML.
|
|
6
|
+
*
|
|
7
|
+
* Mapping:
|
|
8
|
+
* - FW Node → GitHub Actions step (uses: or run:)
|
|
9
|
+
* - FW [job: "name"] → GitHub Actions job
|
|
10
|
+
* - FW @path → job `needs:` dependencies
|
|
11
|
+
* - FW @secret → ${{ secrets.NAME }}
|
|
12
|
+
* - FW @cache → actions/cache@v4
|
|
13
|
+
* - FW @artifact → actions/upload-artifact@v4 / actions/download-artifact@v4
|
|
14
|
+
* - FW @trigger → `on:` event configuration
|
|
15
|
+
*/
|
|
16
|
+
import { BaseCICDTarget } from './cicd-base.js';
|
|
17
|
+
import type { ExportOptions, ExportArtifacts, DeployInstructions } from './base.js';
|
|
18
|
+
export declare class GitHubActionsTarget extends BaseCICDTarget {
|
|
19
|
+
readonly name = "github-actions";
|
|
20
|
+
readonly description = "GitHub Actions workflow YAML (.github/workflows/)";
|
|
21
|
+
readonly deploySchema: {
|
|
22
|
+
runner: {
|
|
23
|
+
type: "string";
|
|
24
|
+
description: string;
|
|
25
|
+
default: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
readonly nodeTypeDeploySchema: {
|
|
29
|
+
action: {
|
|
30
|
+
type: "string";
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
with: {
|
|
34
|
+
type: "string";
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
label: {
|
|
38
|
+
type: "string";
|
|
39
|
+
description: string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
generate(options: ExportOptions): Promise<ExportArtifacts>;
|
|
43
|
+
getDeployInstructions(_artifacts: ExportArtifacts): DeployInstructions;
|
|
44
|
+
private renderWorkflowYAML;
|
|
45
|
+
private renderTriggers;
|
|
46
|
+
private renderJob;
|
|
47
|
+
private renderStep;
|
|
48
|
+
private renderCacheStep;
|
|
49
|
+
/**
|
|
50
|
+
* Apply workflow-level options (cache, services, matrix) to jobs.
|
|
51
|
+
*/
|
|
52
|
+
private applyWorkflowOptions;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=github-actions.d.ts.map
|