@tinybirdco/sdk 0.0.7 → 0.0.9
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/resources.d.ts +2 -0
- package/dist/api/resources.d.ts.map +1 -1
- package/dist/api/resources.js +1 -0
- package/dist/api/resources.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +301 -205
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/init.test.js +67 -93
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/cli/config.d.ts +3 -7
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +9 -20
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/config.test.js +11 -29
- package/dist/cli/config.test.js.map +1 -1
- package/dist/cli/git.d.ts +5 -0
- package/dist/cli/git.d.ts.map +1 -1
- package/dist/cli/git.js +15 -0
- package/dist/cli/git.js.map +1 -1
- package/dist/cli/index.js +42 -25
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/package-manager.d.ts +9 -0
- package/dist/cli/utils/package-manager.d.ts.map +1 -1
- package/dist/cli/utils/package-manager.js +130 -35
- package/dist/cli/utils/package-manager.js.map +1 -1
- package/dist/cli/utils/package-manager.test.js +124 -32
- package/dist/cli/utils/package-manager.test.js.map +1 -1
- package/dist/codegen/index.d.ts +4 -0
- package/dist/codegen/index.d.ts.map +1 -1
- package/dist/codegen/index.js +96 -0
- package/dist/codegen/index.js.map +1 -1
- package/dist/codegen/index.test.js +10 -0
- package/dist/codegen/index.test.js.map +1 -1
- package/dist/generator/datasource.d.ts.map +1 -1
- package/dist/generator/datasource.js +20 -0
- package/dist/generator/datasource.js.map +1 -1
- package/dist/generator/datasource.test.js +11 -0
- package/dist/generator/datasource.test.js.map +1 -1
- package/dist/schema/datasource.d.ts +5 -0
- package/dist/schema/datasource.d.ts.map +1 -1
- package/dist/schema/datasource.js.map +1 -1
- package/package.json +1 -1
- package/src/api/resources.ts +4 -0
- package/src/cli/commands/init.test.ts +67 -107
- package/src/cli/commands/init.ts +350 -218
- package/src/cli/config.test.ts +12 -42
- package/src/cli/config.ts +9 -23
- package/src/cli/git.ts +15 -0
- package/src/cli/index.ts +46 -27
- package/src/cli/utils/package-manager.test.ts +165 -33
- package/src/cli/utils/package-manager.ts +133 -30
- package/src/codegen/index.test.ts +12 -0
- package/src/codegen/index.ts +120 -0
- package/src/generator/datasource.test.ts +13 -0
- package/src/generator/datasource.ts +24 -0
- package/src/schema/datasource.ts +5 -0
package/src/cli/commands/init.ts
CHANGED
|
@@ -11,15 +11,40 @@ import {
|
|
|
11
11
|
getRelativeTinybirdDir,
|
|
12
12
|
getConfigPath,
|
|
13
13
|
updateConfig,
|
|
14
|
+
loadConfig,
|
|
14
15
|
type DevMode,
|
|
15
16
|
} from "../config.js";
|
|
16
17
|
import { browserLogin } from "../auth.js";
|
|
17
18
|
import { saveTinybirdToken } from "../env.js";
|
|
19
|
+
import { getGitRoot } from "../git.js";
|
|
20
|
+
import { fetchAllResources } from "../../api/resources.js";
|
|
21
|
+
import { generateCombinedFile } from "../../codegen/index.js";
|
|
18
22
|
|
|
19
23
|
/**
|
|
20
|
-
* Default starter content for
|
|
24
|
+
* Default starter content for tinybird.ts (single file with everything)
|
|
21
25
|
*/
|
|
22
|
-
const
|
|
26
|
+
const TINYBIRD_CONTENT = `/**
|
|
27
|
+
* Tinybird Definitions
|
|
28
|
+
*
|
|
29
|
+
* Define your datasources, endpoints, and client here.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import {
|
|
33
|
+
defineDatasource,
|
|
34
|
+
defineEndpoint,
|
|
35
|
+
createTinybirdClient,
|
|
36
|
+
node,
|
|
37
|
+
t,
|
|
38
|
+
p,
|
|
39
|
+
engine,
|
|
40
|
+
type InferRow,
|
|
41
|
+
type InferParams,
|
|
42
|
+
type InferOutputRow,
|
|
43
|
+
} from "@tinybirdco/sdk";
|
|
44
|
+
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Datasources
|
|
47
|
+
// ============================================================================
|
|
23
48
|
|
|
24
49
|
/**
|
|
25
50
|
* Page views datasource - tracks page view events
|
|
@@ -37,14 +62,11 @@ export const pageViews = defineDatasource("page_views", {
|
|
|
37
62
|
}),
|
|
38
63
|
});
|
|
39
64
|
|
|
40
|
-
// Row type - use this for ingesting data
|
|
41
65
|
export type PageViewsRow = InferRow<typeof pageViews>;
|
|
42
|
-
`;
|
|
43
66
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const ENDPOINTS_CONTENT = `import { defineEndpoint, node, t, p, type InferParams, type InferOutputRow } from "@tinybirdco/sdk";
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// Endpoints
|
|
69
|
+
// ============================================================================
|
|
48
70
|
|
|
49
71
|
/**
|
|
50
72
|
* Top pages endpoint - get the most visited pages
|
|
@@ -78,40 +100,17 @@ export const topPages = defineEndpoint("top_pages", {
|
|
|
78
100
|
},
|
|
79
101
|
});
|
|
80
102
|
|
|
81
|
-
// Endpoint types - use these for calling the API
|
|
82
103
|
export type TopPagesParams = InferParams<typeof topPages>;
|
|
83
104
|
export type TopPagesOutput = InferOutputRow<typeof topPages>;
|
|
84
|
-
`;
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Default starter content for client.ts
|
|
88
|
-
*/
|
|
89
|
-
const CLIENT_CONTENT = `/**
|
|
90
|
-
* Tinybird Client
|
|
91
|
-
*
|
|
92
|
-
* This file defines the typed Tinybird client for your project.
|
|
93
|
-
* Add your datasources and endpoints here as you create them.
|
|
94
|
-
*/
|
|
95
105
|
|
|
96
|
-
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Client
|
|
108
|
+
// ============================================================================
|
|
97
109
|
|
|
98
|
-
// Import datasources and their row types
|
|
99
|
-
import { pageViews, type PageViewsRow } from "./datasources";
|
|
100
|
-
|
|
101
|
-
// Import endpoints and their types
|
|
102
|
-
import { topPages, type TopPagesParams, type TopPagesOutput } from "./endpoints";
|
|
103
|
-
|
|
104
|
-
// Create the typed Tinybird client
|
|
105
110
|
export const tinybird = createTinybirdClient({
|
|
106
111
|
datasources: { pageViews },
|
|
107
112
|
pipes: { topPages },
|
|
108
113
|
});
|
|
109
|
-
|
|
110
|
-
// Re-export types for convenience
|
|
111
|
-
export type { PageViewsRow, TopPagesParams, TopPagesOutput };
|
|
112
|
-
|
|
113
|
-
// Re-export entities
|
|
114
|
-
export { pageViews, topPages };
|
|
115
114
|
`;
|
|
116
115
|
|
|
117
116
|
const TINYBIRD_CI_WORKFLOW = `name: Tinybird CI
|
|
@@ -136,6 +135,7 @@ jobs:
|
|
|
136
135
|
node-version: "20"
|
|
137
136
|
cache: "pnpm"
|
|
138
137
|
- run: pnpm install --frozen-lockfile
|
|
138
|
+
- run: pnpm run tinybird:build
|
|
139
139
|
- run: pnpm run tinybird:deploy -- --check
|
|
140
140
|
env:
|
|
141
141
|
TINYBIRD_TOKEN: \${{ secrets.TINYBIRD_TOKEN }}
|
|
@@ -165,26 +165,66 @@ jobs:
|
|
|
165
165
|
node-version: "20"
|
|
166
166
|
cache: "pnpm"
|
|
167
167
|
- run: pnpm install --frozen-lockfile
|
|
168
|
+
- run: pnpm run tinybird:build
|
|
168
169
|
- run: pnpm run tinybird:deploy
|
|
169
170
|
env:
|
|
170
171
|
TINYBIRD_TOKEN: \${{ secrets.TINYBIRD_TOKEN }}
|
|
171
172
|
`;
|
|
172
173
|
|
|
173
|
-
const
|
|
174
|
+
const TINYBIRD_GITLAB_CI_WORKFLOW = `stages:
|
|
175
|
+
- tinybird
|
|
176
|
+
|
|
177
|
+
tinybird_ci:
|
|
178
|
+
stage: tinybird
|
|
179
|
+
image: node:20
|
|
180
|
+
rules:
|
|
181
|
+
- changes:
|
|
182
|
+
- tinybird.json
|
|
183
|
+
- src/tinybird/**
|
|
184
|
+
- tinybird/**
|
|
185
|
+
- "**/*.datasource"
|
|
186
|
+
- "**/*.pipe"
|
|
187
|
+
script:
|
|
188
|
+
- corepack enable
|
|
189
|
+
- pnpm install --frozen-lockfile
|
|
190
|
+
- pnpm run tinybird:build
|
|
191
|
+
- pnpm run tinybird:deploy -- --check
|
|
192
|
+
variables:
|
|
193
|
+
TINYBIRD_TOKEN: \${TINYBIRD_TOKEN}
|
|
194
|
+
`;
|
|
195
|
+
|
|
196
|
+
const TINYBIRD_GITLAB_CD_WORKFLOW = `stages:
|
|
197
|
+
- tinybird
|
|
198
|
+
|
|
199
|
+
tinybird_cd:
|
|
200
|
+
stage: tinybird
|
|
201
|
+
image: node:20
|
|
202
|
+
rules:
|
|
203
|
+
- if: '$CI_COMMIT_BRANCH == "main"'
|
|
204
|
+
changes:
|
|
205
|
+
- tinybird.json
|
|
206
|
+
- src/tinybird/**
|
|
207
|
+
- tinybird/**
|
|
208
|
+
- "**/*.datasource"
|
|
209
|
+
- "**/*.pipe"
|
|
210
|
+
script:
|
|
211
|
+
- corepack enable
|
|
212
|
+
- pnpm install --frozen-lockfile
|
|
213
|
+
- pnpm run tinybird:build
|
|
214
|
+
- pnpm run tinybird:deploy
|
|
215
|
+
variables:
|
|
216
|
+
TINYBIRD_TOKEN: \${TINYBIRD_TOKEN}
|
|
217
|
+
`;
|
|
174
218
|
|
|
175
219
|
/**
|
|
176
220
|
* Default config content generator
|
|
177
221
|
*/
|
|
178
222
|
function createDefaultConfig(
|
|
179
|
-
|
|
223
|
+
tinybirdFilePath: string,
|
|
180
224
|
devMode: DevMode,
|
|
181
225
|
additionalIncludes: string[] = []
|
|
182
226
|
) {
|
|
183
|
-
const include = [
|
|
184
|
-
`${tinybirdDir}/datasources.ts`,
|
|
185
|
-
`${tinybirdDir}/endpoints.ts`,
|
|
186
|
-
...additionalIncludes,
|
|
187
|
-
];
|
|
227
|
+
const include = [tinybirdFilePath, ...additionalIncludes];
|
|
188
228
|
return {
|
|
189
229
|
include,
|
|
190
230
|
token: "${TINYBIRD_TOKEN}",
|
|
@@ -338,7 +378,7 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
338
378
|
|
|
339
379
|
if (!options.devMode) {
|
|
340
380
|
// Show interactive prompt for workflow selection
|
|
341
|
-
p.intro(pc.cyan("tinybird
|
|
381
|
+
p.intro(pc.cyan("tinybird.json"));
|
|
342
382
|
|
|
343
383
|
const workflowChoice = await p.select({
|
|
344
384
|
message: "How do you want to develop with Tinybird?",
|
|
@@ -377,7 +417,7 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
377
417
|
if (!options.clientPath && !options.devMode) {
|
|
378
418
|
// Ask user to confirm or change the client path
|
|
379
419
|
const clientPathChoice = await p.text({
|
|
380
|
-
message: "Where should we create
|
|
420
|
+
message: "Where should we create initial tinybird.ts file?",
|
|
381
421
|
placeholder: defaultRelativePath,
|
|
382
422
|
defaultValue: defaultRelativePath,
|
|
383
423
|
});
|
|
@@ -403,62 +443,27 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
403
443
|
let includeCdWorkflow = options.includeCdWorkflow ?? false;
|
|
404
444
|
const shouldPromptWorkflows =
|
|
405
445
|
!skipWorkflowPrompt && options.includeCiWorkflow === undefined;
|
|
406
|
-
const shouldPromptProvider = !skipWorkflowPrompt && !workflowProvider;
|
|
407
|
-
|
|
408
|
-
if (shouldPromptProvider && shouldPromptWorkflows) {
|
|
409
|
-
const providerChoice = await p.select({
|
|
410
|
-
message: "Which git provider are you using?",
|
|
411
|
-
options: [
|
|
412
|
-
{ value: "github", label: "GitHub" },
|
|
413
|
-
{ value: "gitlab", label: "GitLab" },
|
|
414
|
-
],
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
if (p.isCancel(providerChoice)) {
|
|
418
|
-
p.cancel("Operation cancelled");
|
|
419
|
-
return {
|
|
420
|
-
success: false,
|
|
421
|
-
created: [],
|
|
422
|
-
skipped: [],
|
|
423
|
-
error: "Cancelled by user",
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
didPrompt = true;
|
|
428
|
-
workflowProvider = providerChoice as "github" | "gitlab";
|
|
429
|
-
}
|
|
430
446
|
|
|
431
447
|
if (shouldPromptWorkflows) {
|
|
432
|
-
const
|
|
433
|
-
message: "
|
|
434
|
-
initialValue: true,
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
if (p.isCancel(confirmWorkflows)) {
|
|
438
|
-
p.cancel("Operation cancelled");
|
|
439
|
-
return {
|
|
440
|
-
success: false,
|
|
441
|
-
created: [],
|
|
442
|
-
skipped: [],
|
|
443
|
-
error: "Cancelled by user",
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
didPrompt = true;
|
|
448
|
-
includeCiWorkflow = confirmWorkflows;
|
|
449
|
-
includeCdWorkflow = confirmWorkflows;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
if (shouldPromptProvider && !shouldPromptWorkflows && !workflowProvider) {
|
|
453
|
-
const providerChoice = await p.select({
|
|
454
|
-
message: "Which git provider are you using?",
|
|
448
|
+
const ciChoice = await p.select({
|
|
449
|
+
message: "Set up CI/CD workflows?",
|
|
455
450
|
options: [
|
|
456
|
-
{
|
|
457
|
-
|
|
451
|
+
{
|
|
452
|
+
value: "github",
|
|
453
|
+
label: "GitHub Actions",
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
value: "gitlab",
|
|
457
|
+
label: "GitLab CI",
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
value: "skip",
|
|
461
|
+
label: "Skip",
|
|
462
|
+
},
|
|
458
463
|
],
|
|
459
464
|
});
|
|
460
465
|
|
|
461
|
-
if (p.isCancel(
|
|
466
|
+
if (p.isCancel(ciChoice)) {
|
|
462
467
|
p.cancel("Operation cancelled");
|
|
463
468
|
return {
|
|
464
469
|
success: false,
|
|
@@ -469,49 +474,56 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
469
474
|
}
|
|
470
475
|
|
|
471
476
|
didPrompt = true;
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
477
|
+
if (ciChoice !== "skip") {
|
|
478
|
+
includeCiWorkflow = true;
|
|
479
|
+
includeCdWorkflow = true;
|
|
480
|
+
workflowProvider = ciChoice as "github" | "gitlab";
|
|
481
|
+
}
|
|
482
|
+
} else if ((includeCiWorkflow || includeCdWorkflow) && !workflowProvider) {
|
|
475
483
|
workflowProvider = "github";
|
|
476
484
|
}
|
|
477
485
|
|
|
478
486
|
// Ask about existing datafiles if found
|
|
487
|
+
let datafileAction: DatafileAction = "skip";
|
|
479
488
|
if (foundDatafiles.length > 0 && !options.skipDatafilePrompt) {
|
|
480
|
-
|
|
481
|
-
options.includeExistingDatafiles
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
if (
|
|
489
|
+
if (options.includeExistingDatafiles !== undefined) {
|
|
490
|
+
datafileAction = options.includeExistingDatafiles ? "include" : "skip";
|
|
491
|
+
} else {
|
|
492
|
+
didPrompt = true;
|
|
493
|
+
datafileAction = await promptForExistingDatafiles(foundDatafiles);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (datafileAction === "include") {
|
|
488
497
|
existingDatafiles = foundDatafiles;
|
|
489
498
|
}
|
|
499
|
+
// Note: "codegen" option is handled after file creation
|
|
490
500
|
} else if (options.includeExistingDatafiles && foundDatafiles.length > 0) {
|
|
491
501
|
existingDatafiles = foundDatafiles;
|
|
502
|
+
datafileAction = "include";
|
|
492
503
|
}
|
|
493
504
|
|
|
494
505
|
if (didPrompt) {
|
|
495
506
|
const devModeLabel = devMode === "local" ? "Tinybird Local" : "Branches";
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
507
|
+
let datafileSummary = "none found";
|
|
508
|
+
if (foundDatafiles.length > 0) {
|
|
509
|
+
if (datafileAction === "include") {
|
|
510
|
+
datafileSummary = `${foundDatafiles.length} included`;
|
|
511
|
+
} else if (datafileAction === "codegen") {
|
|
512
|
+
datafileSummary = `${foundDatafiles.length} will generate .ts`;
|
|
513
|
+
} else {
|
|
514
|
+
datafileSummary = "skipped";
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
let cicdSummary = "skipped";
|
|
503
518
|
if (includeCiWorkflow || includeCdWorkflow) {
|
|
504
|
-
|
|
505
|
-
workflowProvider === "gitlab"
|
|
506
|
-
? "GitLab (.gitlab-ci.yml)"
|
|
507
|
-
: "GitHub Actions (tinybird-ci.yaml, tinybird-cd.yaml)";
|
|
519
|
+
cicdSummary = workflowProvider === "gitlab" ? "GitLab" : "GitHub";
|
|
508
520
|
}
|
|
509
521
|
|
|
510
522
|
const summaryLines = [
|
|
511
523
|
`Mode: ${devModeLabel}`,
|
|
512
|
-
`
|
|
524
|
+
`Folder: ${relativeTinybirdDir}/`,
|
|
513
525
|
`Existing datafiles: ${datafileSummary}`,
|
|
514
|
-
`
|
|
526
|
+
`CI/CD: ${cicdSummary}`,
|
|
515
527
|
];
|
|
516
528
|
|
|
517
529
|
p.note(summaryLines.join("\n"), "Installation Summary");
|
|
@@ -532,17 +544,32 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
532
544
|
}
|
|
533
545
|
}
|
|
534
546
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const datasourcesPath = path.join(tinybirdDir, "datasources.ts");
|
|
539
|
-
const endpointsPath = path.join(tinybirdDir, "endpoints.ts");
|
|
540
|
-
const clientPath = path.join(tinybirdDir, "client.ts");
|
|
547
|
+
// relativeTinybirdDir is now a file path like "src/lib/tinybird.ts"
|
|
548
|
+
const tinybirdFilePath = path.join(cwd, relativeTinybirdDir);
|
|
549
|
+
const tinybirdDir = path.dirname(tinybirdFilePath);
|
|
541
550
|
|
|
542
551
|
// Create config file (tinybird.json)
|
|
543
552
|
const configPath = getConfigPath(cwd);
|
|
544
553
|
if (fs.existsSync(configPath) && !force) {
|
|
545
|
-
|
|
554
|
+
try {
|
|
555
|
+
const config = createDefaultConfig(
|
|
556
|
+
relativeTinybirdDir,
|
|
557
|
+
devMode,
|
|
558
|
+
existingDatafiles
|
|
559
|
+
);
|
|
560
|
+
updateConfig(configPath, {
|
|
561
|
+
include: config.include,
|
|
562
|
+
devMode: config.devMode,
|
|
563
|
+
});
|
|
564
|
+
created.push("tinybird.json (updated)");
|
|
565
|
+
} catch (error) {
|
|
566
|
+
return {
|
|
567
|
+
success: false,
|
|
568
|
+
created,
|
|
569
|
+
skipped,
|
|
570
|
+
error: `Failed to update tinybird.json: ${(error as Error).message}`,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
546
573
|
} else {
|
|
547
574
|
try {
|
|
548
575
|
const config = createDefaultConfig(
|
|
@@ -562,7 +589,7 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
562
589
|
}
|
|
563
590
|
}
|
|
564
591
|
|
|
565
|
-
// Create
|
|
592
|
+
// Create lib directory
|
|
566
593
|
try {
|
|
567
594
|
fs.mkdirSync(tinybirdDir, { recursive: true });
|
|
568
595
|
} catch (error) {
|
|
@@ -570,60 +597,30 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
570
597
|
success: false,
|
|
571
598
|
created,
|
|
572
599
|
skipped,
|
|
573
|
-
error: `Failed to create ${relativeTinybirdDir} folder: ${
|
|
600
|
+
error: `Failed to create ${path.dirname(relativeTinybirdDir)} folder: ${
|
|
574
601
|
(error as Error).message
|
|
575
602
|
}`,
|
|
576
603
|
};
|
|
577
604
|
}
|
|
578
605
|
|
|
579
|
-
// Create
|
|
580
|
-
if (
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
if (fs.existsSync(endpointsPath) && !force) {
|
|
598
|
-
skipped.push(`${relativeTinybirdDir}/endpoints.ts`);
|
|
599
|
-
} else {
|
|
600
|
-
try {
|
|
601
|
-
fs.writeFileSync(endpointsPath, ENDPOINTS_CONTENT);
|
|
602
|
-
created.push(`${relativeTinybirdDir}/endpoints.ts`);
|
|
603
|
-
} catch (error) {
|
|
604
|
-
return {
|
|
605
|
-
success: false,
|
|
606
|
-
created,
|
|
607
|
-
skipped,
|
|
608
|
-
error: `Failed to create endpoints.ts: ${(error as Error).message}`,
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// Create client.ts
|
|
614
|
-
if (fs.existsSync(clientPath) && !force) {
|
|
615
|
-
skipped.push(`${relativeTinybirdDir}/client.ts`);
|
|
616
|
-
} else {
|
|
617
|
-
try {
|
|
618
|
-
fs.writeFileSync(clientPath, CLIENT_CONTENT);
|
|
619
|
-
created.push(`${relativeTinybirdDir}/client.ts`);
|
|
620
|
-
} catch (error) {
|
|
621
|
-
return {
|
|
622
|
-
success: false,
|
|
623
|
-
created,
|
|
624
|
-
skipped,
|
|
625
|
-
error: `Failed to create client.ts: ${(error as Error).message}`,
|
|
626
|
-
};
|
|
606
|
+
// Create tinybird.ts (skip if codegen will generate it)
|
|
607
|
+
if (datafileAction !== "codegen") {
|
|
608
|
+
if (fs.existsSync(tinybirdFilePath) && !force) {
|
|
609
|
+
skipped.push(relativeTinybirdDir);
|
|
610
|
+
} else {
|
|
611
|
+
try {
|
|
612
|
+
fs.writeFileSync(tinybirdFilePath, TINYBIRD_CONTENT);
|
|
613
|
+
created.push(relativeTinybirdDir);
|
|
614
|
+
} catch (error) {
|
|
615
|
+
return {
|
|
616
|
+
success: false,
|
|
617
|
+
created,
|
|
618
|
+
skipped,
|
|
619
|
+
error: `Failed to create ${relativeTinybirdDir}: ${
|
|
620
|
+
(error as Error).message
|
|
621
|
+
}`,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
627
624
|
}
|
|
628
625
|
}
|
|
629
626
|
|
|
@@ -665,42 +662,68 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
665
662
|
}
|
|
666
663
|
}
|
|
667
664
|
|
|
668
|
-
|
|
669
|
-
const
|
|
670
|
-
const
|
|
671
|
-
const
|
|
665
|
+
// Use git root for workflow files, fallback to cwd if not in a git repo
|
|
666
|
+
const projectRoot = getGitRoot() ?? cwd;
|
|
667
|
+
const githubWorkflowsDir = path.join(projectRoot, ".github", "workflows");
|
|
668
|
+
const gitlabWorkflowsDir = path.join(projectRoot, ".gitlab");
|
|
669
|
+
const githubCiPath = path.join(githubWorkflowsDir, "tinybird-ci.yaml");
|
|
670
|
+
const githubCdPath = path.join(githubWorkflowsDir, "tinybird-cd.yaml");
|
|
671
|
+
const gitlabCiPath = path.join(gitlabWorkflowsDir, "tinybird-ci.yaml");
|
|
672
|
+
const gitlabCdPath = path.join(gitlabWorkflowsDir, "tinybird-cd.yaml");
|
|
672
673
|
|
|
673
674
|
if (includeCiWorkflow || includeCdWorkflow) {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
675
|
+
const workflowsDir =
|
|
676
|
+
workflowProvider === "github" ? githubWorkflowsDir : gitlabWorkflowsDir;
|
|
677
|
+
try {
|
|
678
|
+
fs.mkdirSync(workflowsDir, { recursive: true });
|
|
679
|
+
} catch (error) {
|
|
680
|
+
return {
|
|
681
|
+
success: false,
|
|
682
|
+
created,
|
|
683
|
+
skipped,
|
|
684
|
+
error: `Failed to create ${
|
|
685
|
+
workflowProvider === "github" ? ".github/workflows" : ".gitlab"
|
|
686
|
+
} folder: ${(error as Error).message}`,
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (workflowProvider === "github") {
|
|
692
|
+
if (includeCiWorkflow) {
|
|
693
|
+
if (fs.existsSync(githubCiPath) && !force) {
|
|
694
|
+
skipped.push(".github/workflows/tinybird-ci.yaml");
|
|
695
|
+
} else {
|
|
696
|
+
try {
|
|
697
|
+
fs.writeFileSync(githubCiPath, TINYBIRD_CI_WORKFLOW);
|
|
698
|
+
created.push(".github/workflows/tinybird-ci.yaml");
|
|
699
|
+
ciWorkflowCreated = true;
|
|
700
|
+
} catch (error) {
|
|
701
|
+
return {
|
|
702
|
+
success: false,
|
|
703
|
+
created,
|
|
704
|
+
skipped,
|
|
705
|
+
error: `Failed to create .github/workflows/tinybird-ci.yaml: ${
|
|
706
|
+
(error as Error).message
|
|
707
|
+
}`,
|
|
708
|
+
};
|
|
709
|
+
}
|
|
686
710
|
}
|
|
687
711
|
}
|
|
688
712
|
|
|
689
|
-
if (
|
|
690
|
-
if (fs.existsSync(
|
|
691
|
-
skipped.push(".
|
|
713
|
+
if (includeCdWorkflow) {
|
|
714
|
+
if (fs.existsSync(githubCdPath) && !force) {
|
|
715
|
+
skipped.push(".github/workflows/tinybird-cd.yaml");
|
|
692
716
|
} else {
|
|
693
717
|
try {
|
|
694
|
-
fs.writeFileSync(
|
|
695
|
-
created.push(".
|
|
696
|
-
|
|
697
|
-
cdWorkflowCreated = includeCdWorkflow;
|
|
718
|
+
fs.writeFileSync(githubCdPath, TINYBIRD_CD_WORKFLOW);
|
|
719
|
+
created.push(".github/workflows/tinybird-cd.yaml");
|
|
720
|
+
cdWorkflowCreated = true;
|
|
698
721
|
} catch (error) {
|
|
699
722
|
return {
|
|
700
723
|
success: false,
|
|
701
724
|
created,
|
|
702
725
|
skipped,
|
|
703
|
-
error: `Failed to create .
|
|
726
|
+
error: `Failed to create .github/workflows/tinybird-cd.yaml: ${
|
|
704
727
|
(error as Error).message
|
|
705
728
|
}`,
|
|
706
729
|
};
|
|
@@ -709,21 +732,21 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
709
732
|
}
|
|
710
733
|
}
|
|
711
734
|
|
|
712
|
-
if (workflowProvider === "
|
|
735
|
+
if (workflowProvider === "gitlab") {
|
|
713
736
|
if (includeCiWorkflow) {
|
|
714
|
-
if (fs.existsSync(
|
|
715
|
-
skipped.push(".
|
|
737
|
+
if (fs.existsSync(gitlabCiPath) && !force) {
|
|
738
|
+
skipped.push(".gitlab/tinybird-ci.yaml");
|
|
716
739
|
} else {
|
|
717
740
|
try {
|
|
718
|
-
fs.writeFileSync(
|
|
719
|
-
created.push(".
|
|
741
|
+
fs.writeFileSync(gitlabCiPath, TINYBIRD_GITLAB_CI_WORKFLOW);
|
|
742
|
+
created.push(".gitlab/tinybird-ci.yaml");
|
|
720
743
|
ciWorkflowCreated = true;
|
|
721
744
|
} catch (error) {
|
|
722
745
|
return {
|
|
723
746
|
success: false,
|
|
724
747
|
created,
|
|
725
748
|
skipped,
|
|
726
|
-
error: `Failed to create tinybird-ci.yaml: ${
|
|
749
|
+
error: `Failed to create .gitlab/tinybird-ci.yaml: ${
|
|
727
750
|
(error as Error).message
|
|
728
751
|
}`,
|
|
729
752
|
};
|
|
@@ -732,19 +755,19 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
732
755
|
}
|
|
733
756
|
|
|
734
757
|
if (includeCdWorkflow) {
|
|
735
|
-
if (fs.existsSync(
|
|
736
|
-
skipped.push(".
|
|
758
|
+
if (fs.existsSync(gitlabCdPath) && !force) {
|
|
759
|
+
skipped.push(".gitlab/tinybird-cd.yaml");
|
|
737
760
|
} else {
|
|
738
761
|
try {
|
|
739
|
-
fs.writeFileSync(
|
|
740
|
-
created.push(".
|
|
762
|
+
fs.writeFileSync(gitlabCdPath, TINYBIRD_GITLAB_CD_WORKFLOW);
|
|
763
|
+
created.push(".gitlab/tinybird-cd.yaml");
|
|
741
764
|
cdWorkflowCreated = true;
|
|
742
765
|
} catch (error) {
|
|
743
766
|
return {
|
|
744
767
|
success: false,
|
|
745
768
|
created,
|
|
746
769
|
skipped,
|
|
747
|
-
error: `Failed to create tinybird-cd.yaml: ${
|
|
770
|
+
error: `Failed to create .gitlab/tinybird-cd.yaml: ${
|
|
748
771
|
(error as Error).message
|
|
749
772
|
}`,
|
|
750
773
|
};
|
|
@@ -773,6 +796,19 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
773
796
|
updateConfig(configPath, { baseUrl });
|
|
774
797
|
}
|
|
775
798
|
|
|
799
|
+
// Generate TypeScript from existing Tinybird resources if requested
|
|
800
|
+
if (datafileAction === "codegen") {
|
|
801
|
+
const tinybirdDir = path.join(cwd, relativeTinybirdDir);
|
|
802
|
+
await runCodegen(
|
|
803
|
+
baseUrl,
|
|
804
|
+
authResult.token,
|
|
805
|
+
tinybirdDir,
|
|
806
|
+
relativeTinybirdDir,
|
|
807
|
+
created,
|
|
808
|
+
foundDatafiles
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
776
812
|
return {
|
|
777
813
|
success: true,
|
|
778
814
|
created,
|
|
@@ -825,6 +861,31 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
825
861
|
}
|
|
826
862
|
}
|
|
827
863
|
|
|
864
|
+
// Generate TypeScript from existing Tinybird resources if requested
|
|
865
|
+
// (when user is already logged in)
|
|
866
|
+
if (datafileAction === "codegen" && hasValidToken(cwd)) {
|
|
867
|
+
try {
|
|
868
|
+
const config = loadConfig(cwd);
|
|
869
|
+
const tinybirdDir = path.join(cwd, relativeTinybirdDir);
|
|
870
|
+
await runCodegen(
|
|
871
|
+
config.baseUrl,
|
|
872
|
+
config.token,
|
|
873
|
+
tinybirdDir,
|
|
874
|
+
relativeTinybirdDir,
|
|
875
|
+
created,
|
|
876
|
+
foundDatafiles
|
|
877
|
+
);
|
|
878
|
+
} catch (error) {
|
|
879
|
+
console.error(
|
|
880
|
+
`Warning: Failed to generate TypeScript: ${(error as Error).message}`
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
if (didPrompt) {
|
|
886
|
+
p.outro("Done!");
|
|
887
|
+
}
|
|
888
|
+
|
|
828
889
|
return {
|
|
829
890
|
success: true,
|
|
830
891
|
created,
|
|
@@ -839,12 +900,69 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
839
900
|
};
|
|
840
901
|
}
|
|
841
902
|
|
|
903
|
+
type DatafileAction = "include" | "codegen" | "skip";
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Generate TypeScript files from Tinybird workspace resources
|
|
907
|
+
* Only generates for resources that match the local datafiles
|
|
908
|
+
*/
|
|
909
|
+
async function runCodegen(
|
|
910
|
+
baseUrl: string,
|
|
911
|
+
token: string,
|
|
912
|
+
tinybirdFilePath: string,
|
|
913
|
+
relativeTinybirdPath: string,
|
|
914
|
+
created: string[],
|
|
915
|
+
localDatafiles: string[]
|
|
916
|
+
): Promise<void> {
|
|
917
|
+
try {
|
|
918
|
+
// Extract names from local datafiles (without extension)
|
|
919
|
+
const localDatasourceNames = new Set(
|
|
920
|
+
localDatafiles
|
|
921
|
+
.filter((f) => f.endsWith(".datasource"))
|
|
922
|
+
.map((f) => path.basename(f, ".datasource"))
|
|
923
|
+
);
|
|
924
|
+
const localPipeNames = new Set(
|
|
925
|
+
localDatafiles
|
|
926
|
+
.filter((f) => f.endsWith(".pipe"))
|
|
927
|
+
.map((f) => path.basename(f, ".pipe"))
|
|
928
|
+
);
|
|
929
|
+
|
|
930
|
+
const resources = await fetchAllResources({ baseUrl, token });
|
|
931
|
+
|
|
932
|
+
// Filter to only resources matching local files
|
|
933
|
+
const matchedDatasources = resources.datasources.filter((ds) =>
|
|
934
|
+
localDatasourceNames.has(ds.name)
|
|
935
|
+
);
|
|
936
|
+
const matchedPipes = resources.pipes.filter((p) =>
|
|
937
|
+
localPipeNames.has(p.name)
|
|
938
|
+
);
|
|
939
|
+
|
|
940
|
+
if (matchedDatasources.length > 0 || matchedPipes.length > 0) {
|
|
941
|
+
// Generate combined tinybird.ts file
|
|
942
|
+
const content = generateCombinedFile(matchedDatasources, matchedPipes);
|
|
943
|
+
// Ensure directory exists
|
|
944
|
+
const dir = path.dirname(tinybirdFilePath);
|
|
945
|
+
if (!fs.existsSync(dir)) {
|
|
946
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
947
|
+
}
|
|
948
|
+
fs.writeFileSync(tinybirdFilePath, content);
|
|
949
|
+
created.push(`${relativeTinybirdPath} (generated)`);
|
|
950
|
+
}
|
|
951
|
+
} catch (error) {
|
|
952
|
+
console.error(
|
|
953
|
+
`Warning: Failed to generate TypeScript from workspace: ${
|
|
954
|
+
(error as Error).message
|
|
955
|
+
}`
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
842
960
|
/**
|
|
843
961
|
* Prompt user about including existing datafiles
|
|
844
962
|
*/
|
|
845
963
|
async function promptForExistingDatafiles(
|
|
846
964
|
datafiles: string[]
|
|
847
|
-
): Promise<
|
|
965
|
+
): Promise<DatafileAction> {
|
|
848
966
|
const datasourceCount = datafiles.filter((f) =>
|
|
849
967
|
f.endsWith(".datasource")
|
|
850
968
|
).length;
|
|
@@ -860,16 +978,30 @@ async function promptForExistingDatafiles(
|
|
|
860
978
|
parts.push(`${pipeCount} .pipe file${pipeCount > 1 ? "s" : ""}`);
|
|
861
979
|
}
|
|
862
980
|
|
|
863
|
-
const
|
|
864
|
-
message: `Found ${parts.join(
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
981
|
+
const choice = await p.select({
|
|
982
|
+
message: `Found ${parts.join(" and ")} in your project.`,
|
|
983
|
+
options: [
|
|
984
|
+
{
|
|
985
|
+
value: "include",
|
|
986
|
+
label: "Include existing resources",
|
|
987
|
+
hint: "Add to tinybird.json",
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
value: "codegen",
|
|
991
|
+
label: "Define resources in TypeScript",
|
|
992
|
+
hint: "Generate TypeScript definitions from existing resources",
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
value: "skip",
|
|
996
|
+
label: "Skip",
|
|
997
|
+
hint: "Don't include existing resources",
|
|
998
|
+
},
|
|
999
|
+
],
|
|
868
1000
|
});
|
|
869
1001
|
|
|
870
|
-
if (p.isCancel(
|
|
871
|
-
return
|
|
1002
|
+
if (p.isCancel(choice)) {
|
|
1003
|
+
return "skip";
|
|
872
1004
|
}
|
|
873
1005
|
|
|
874
|
-
return
|
|
1006
|
+
return choice as DatafileAction;
|
|
875
1007
|
}
|