@tinybirdco/sdk 0.0.4 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -13
- package/dist/api/branches.d.ts.map +1 -1
- package/dist/api/branches.js +6 -5
- package/dist/api/branches.js.map +1 -1
- package/dist/api/branches.test.js +32 -6
- package/dist/api/branches.test.js.map +1 -1
- package/dist/api/build.d.ts.map +1 -1
- package/dist/api/build.js +2 -1
- package/dist/api/build.js.map +1 -1
- package/dist/api/deploy.d.ts +42 -3
- package/dist/api/deploy.d.ts.map +1 -1
- package/dist/api/deploy.js +162 -19
- package/dist/api/deploy.js.map +1 -1
- package/dist/api/deploy.test.js +83 -31
- package/dist/api/deploy.test.js.map +1 -1
- package/dist/api/fetcher.d.ts +6 -0
- package/dist/api/fetcher.d.ts.map +1 -0
- package/dist/api/fetcher.js +13 -0
- package/dist/api/fetcher.js.map +1 -0
- package/dist/api/local.d.ts.map +1 -1
- package/dist/api/local.js +5 -4
- package/dist/api/local.js.map +1 -1
- package/dist/api/local.test.js.map +1 -1
- package/dist/api/resources.d.ts +178 -0
- package/dist/api/resources.d.ts.map +1 -0
- package/dist/api/resources.js +245 -0
- package/dist/api/resources.js.map +1 -0
- package/dist/api/resources.test.d.ts +2 -0
- package/dist/api/resources.test.d.ts.map +1 -0
- package/dist/api/resources.test.js +255 -0
- package/dist/api/resources.test.js.map +1 -0
- package/dist/api/workspaces.d.ts.map +1 -1
- package/dist/api/workspaces.js +2 -1
- package/dist/api/workspaces.js.map +1 -1
- package/dist/api/workspaces.test.js +9 -1
- package/dist/api/workspaces.test.js.map +1 -1
- package/dist/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +2 -1
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/commands/build.d.ts +3 -4
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +23 -25
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/deploy.d.ts +41 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/deploy.js +92 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +7 -3
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +38 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +434 -23
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/init.test.js +190 -30
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/cli/index.js +80 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/package-manager.d.ts +8 -0
- package/dist/cli/utils/package-manager.d.ts.map +1 -0
- package/dist/cli/utils/package-manager.js +45 -0
- package/dist/cli/utils/package-manager.js.map +1 -0
- package/dist/cli/utils/package-manager.test.d.ts +2 -0
- package/dist/cli/utils/package-manager.test.d.ts.map +1 -0
- package/dist/cli/utils/package-manager.test.js +85 -0
- package/dist/cli/utils/package-manager.test.js.map +1 -0
- package/dist/client/base.d.ts.map +1 -1
- package/dist/client/base.js +2 -1
- package/dist/client/base.js.map +1 -1
- package/dist/codegen/index.d.ts +39 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/index.js +300 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/codegen/index.test.d.ts +2 -0
- package/dist/codegen/index.test.d.ts.map +1 -0
- package/dist/codegen/index.test.js +310 -0
- package/dist/codegen/index.test.js.map +1 -0
- package/dist/codegen/type-mapper.d.ts +20 -0
- package/dist/codegen/type-mapper.d.ts.map +1 -0
- package/dist/codegen/type-mapper.js +238 -0
- package/dist/codegen/type-mapper.js.map +1 -0
- package/dist/codegen/type-mapper.test.d.ts +2 -0
- package/dist/codegen/type-mapper.test.d.ts.map +1 -0
- package/dist/codegen/type-mapper.test.js +167 -0
- package/dist/codegen/type-mapper.test.js.map +1 -0
- package/dist/codegen/utils.d.ts +46 -0
- package/dist/codegen/utils.d.ts.map +1 -0
- package/dist/codegen/utils.js +141 -0
- package/dist/codegen/utils.js.map +1 -0
- package/dist/codegen/utils.test.d.ts +2 -0
- package/dist/codegen/utils.test.d.ts.map +1 -0
- package/dist/codegen/utils.test.js +178 -0
- package/dist/codegen/utils.test.js.map +1 -0
- package/dist/generator/index.d.ts +3 -0
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +17 -1
- package/dist/generator/index.js.map +1 -1
- package/dist/generator/index.test.js +104 -1
- package/dist/generator/index.test.js.map +1 -1
- package/dist/generator/loader.d.ts +15 -0
- package/dist/generator/loader.d.ts.map +1 -1
- package/dist/generator/loader.js +24 -0
- package/dist/generator/loader.js.map +1 -1
- package/dist/schema/connection.d.ts.map +1 -1
- package/dist/schema/connection.js +3 -2
- package/dist/schema/connection.js.map +1 -1
- package/dist/schema/datasource.d.ts.map +1 -1
- package/dist/schema/datasource.js +3 -2
- package/dist/schema/datasource.js.map +1 -1
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +3 -2
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/pipe.d.ts +2 -2
- package/dist/schema/pipe.d.ts.map +1 -1
- package/dist/schema/pipe.js +4 -4
- package/dist/schema/pipe.js.map +1 -1
- package/dist/schema/project.d.ts.map +1 -1
- package/dist/schema/project.js +3 -2
- package/dist/schema/project.js.map +1 -1
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +3 -2
- package/dist/schema/types.js.map +1 -1
- package/dist/test/handlers.d.ts +49 -0
- package/dist/test/handlers.d.ts.map +1 -1
- package/dist/test/handlers.js +45 -0
- package/dist/test/handlers.js.map +1 -1
- package/package.json +4 -2
- package/src/api/branches.test.ts +65 -57
- package/src/api/branches.ts +7 -5
- package/src/api/build.ts +2 -1
- package/src/api/deploy.test.ts +141 -36
- package/src/api/deploy.ts +231 -23
- package/src/api/fetcher.ts +17 -0
- package/src/api/local.test.ts +43 -31
- package/src/api/local.ts +5 -4
- package/src/api/resources.test.ts +332 -0
- package/src/api/resources.ts +555 -0
- package/src/api/workspaces.test.ts +15 -9
- package/src/api/workspaces.ts +3 -1
- package/src/cli/auth.ts +2 -1
- package/src/cli/commands/build.ts +29 -33
- package/src/cli/commands/deploy.ts +131 -0
- package/src/cli/commands/dev.ts +10 -3
- package/src/cli/commands/init.test.ts +239 -30
- package/src/cli/commands/init.ts +548 -26
- package/src/cli/index.ts +117 -20
- package/src/cli/utils/package-manager.test.ts +118 -0
- package/src/cli/utils/package-manager.ts +44 -0
- package/src/client/base.ts +3 -2
- package/src/codegen/index.test.ts +367 -0
- package/src/codegen/index.ts +379 -0
- package/src/codegen/type-mapper.test.ts +224 -0
- package/src/codegen/type-mapper.ts +265 -0
- package/src/codegen/utils.test.ts +221 -0
- package/src/codegen/utils.ts +174 -0
- package/src/generator/index.test.ts +121 -1
- package/src/generator/index.ts +19 -1
- package/src/generator/loader.ts +43 -0
- package/src/schema/connection.ts +3 -2
- package/src/schema/datasource.ts +3 -2
- package/src/schema/params.ts +3 -2
- package/src/schema/pipe.ts +4 -4
- package/src/schema/project.ts +3 -2
- package/src/schema/types.ts +3 -2
- package/src/test/handlers.ts +58 -0
package/src/cli/commands/init.ts
CHANGED
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import * as fs from "fs";
|
|
6
6
|
import * as path from "path";
|
|
7
|
+
import * as p from "@clack/prompts";
|
|
8
|
+
import pc from "picocolors";
|
|
7
9
|
import {
|
|
8
10
|
hasValidToken,
|
|
9
|
-
getTinybirdDir,
|
|
10
11
|
getRelativeTinybirdDir,
|
|
11
12
|
getConfigPath,
|
|
12
13
|
updateConfig,
|
|
14
|
+
type DevMode,
|
|
13
15
|
} from "../config.js";
|
|
14
16
|
import { browserLogin } from "../auth.js";
|
|
15
17
|
import { saveTinybirdToken } from "../env.js";
|
|
@@ -40,9 +42,9 @@ export type PageViewsRow = InferRow<typeof pageViews>;
|
|
|
40
42
|
`;
|
|
41
43
|
|
|
42
44
|
/**
|
|
43
|
-
* Default starter content for
|
|
45
|
+
* Default starter content for endpoints.ts
|
|
44
46
|
*/
|
|
45
|
-
const
|
|
47
|
+
const ENDPOINTS_CONTENT = `import { defineEndpoint, node, t, p, type InferParams, type InferOutputRow } from "@tinybirdco/sdk";
|
|
46
48
|
|
|
47
49
|
/**
|
|
48
50
|
* Top pages endpoint - get the most visited pages
|
|
@@ -88,7 +90,7 @@ const CLIENT_CONTENT = `/**
|
|
|
88
90
|
* Tinybird Client
|
|
89
91
|
*
|
|
90
92
|
* This file defines the typed Tinybird client for your project.
|
|
91
|
-
* Add your datasources and
|
|
93
|
+
* Add your datasources and endpoints here as you create them.
|
|
92
94
|
*/
|
|
93
95
|
|
|
94
96
|
import { createTinybirdClient } from "@tinybirdco/sdk";
|
|
@@ -97,7 +99,7 @@ import { createTinybirdClient } from "@tinybirdco/sdk";
|
|
|
97
99
|
import { pageViews, type PageViewsRow } from "./datasources";
|
|
98
100
|
|
|
99
101
|
// Import endpoints and their types
|
|
100
|
-
import { topPages, type TopPagesParams, type TopPagesOutput } from "./
|
|
102
|
+
import { topPages, type TopPagesParams, type TopPagesOutput } from "./endpoints";
|
|
101
103
|
|
|
102
104
|
// Create the typed Tinybird client
|
|
103
105
|
export const tinybird = createTinybirdClient({
|
|
@@ -112,17 +114,82 @@ export type { PageViewsRow, TopPagesParams, TopPagesOutput };
|
|
|
112
114
|
export { pageViews, topPages };
|
|
113
115
|
`;
|
|
114
116
|
|
|
117
|
+
const TINYBIRD_CI_WORKFLOW = `name: Tinybird CI
|
|
118
|
+
|
|
119
|
+
on:
|
|
120
|
+
pull_request:
|
|
121
|
+
paths:
|
|
122
|
+
- "tinybird.json"
|
|
123
|
+
- "src/tinybird/**"
|
|
124
|
+
- "tinybird/**"
|
|
125
|
+
- "**/*.datasource"
|
|
126
|
+
- "**/*.pipe"
|
|
127
|
+
|
|
128
|
+
jobs:
|
|
129
|
+
tinybird:
|
|
130
|
+
runs-on: ubuntu-latest
|
|
131
|
+
steps:
|
|
132
|
+
- uses: actions/checkout@v4
|
|
133
|
+
- uses: pnpm/action-setup@v4
|
|
134
|
+
- uses: actions/setup-node@v4
|
|
135
|
+
with:
|
|
136
|
+
node-version: "20"
|
|
137
|
+
cache: "pnpm"
|
|
138
|
+
- run: pnpm install --frozen-lockfile
|
|
139
|
+
- run: pnpm run tinybird:deploy -- --check
|
|
140
|
+
env:
|
|
141
|
+
TINYBIRD_TOKEN: \${{ secrets.TINYBIRD_TOKEN }}
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
const TINYBIRD_CD_WORKFLOW = `name: Tinybird CD
|
|
145
|
+
|
|
146
|
+
on:
|
|
147
|
+
push:
|
|
148
|
+
branches:
|
|
149
|
+
- main
|
|
150
|
+
paths:
|
|
151
|
+
- "tinybird.json"
|
|
152
|
+
- "src/tinybird/**"
|
|
153
|
+
- "tinybird/**"
|
|
154
|
+
- "**/*.datasource"
|
|
155
|
+
- "**/*.pipe"
|
|
156
|
+
|
|
157
|
+
jobs:
|
|
158
|
+
tinybird:
|
|
159
|
+
runs-on: ubuntu-latest
|
|
160
|
+
steps:
|
|
161
|
+
- uses: actions/checkout@v4
|
|
162
|
+
- uses: pnpm/action-setup@v4
|
|
163
|
+
- uses: actions/setup-node@v4
|
|
164
|
+
with:
|
|
165
|
+
node-version: "20"
|
|
166
|
+
cache: "pnpm"
|
|
167
|
+
- run: pnpm install --frozen-lockfile
|
|
168
|
+
- run: pnpm run tinybird:deploy
|
|
169
|
+
env:
|
|
170
|
+
TINYBIRD_TOKEN: \${{ secrets.TINYBIRD_TOKEN }}
|
|
171
|
+
`;
|
|
172
|
+
|
|
173
|
+
const TINYBIRD_GITLAB_CI = `stages:\n - tinybird\n\ntinybird_ci:\n stage: tinybird\n image: node:20\n rules:\n - changes:\n - tinybird.json\n - src/tinybird/**\n - tinybird/**\n - **/*.datasource\n - **/*.pipe\n script:\n - corepack enable\n - pnpm install --frozen-lockfile\n - pnpm run tinybird:deploy -- --check\n variables:\n TINYBIRD_TOKEN: \${TINYBIRD_TOKEN}\n\ntinybird_cd:\n stage: tinybird\n image: node:20\n rules:\n - if: '$CI_COMMIT_BRANCH == \"main\"'\n changes:\n - tinybird.json\n - src/tinybird/**\n - tinybird/**\n - **/*.datasource\n - **/*.pipe\n script:\n - corepack enable\n - pnpm install --frozen-lockfile\n - pnpm run tinybird:deploy\n variables:\n TINYBIRD_TOKEN: \${TINYBIRD_TOKEN}\n`;
|
|
174
|
+
|
|
115
175
|
/**
|
|
116
176
|
* Default config content generator
|
|
117
177
|
*/
|
|
118
|
-
function createDefaultConfig(
|
|
178
|
+
function createDefaultConfig(
|
|
179
|
+
tinybirdDir: string,
|
|
180
|
+
devMode: DevMode,
|
|
181
|
+
additionalIncludes: string[] = []
|
|
182
|
+
) {
|
|
183
|
+
const include = [
|
|
184
|
+
`${tinybirdDir}/datasources.ts`,
|
|
185
|
+
`${tinybirdDir}/endpoints.ts`,
|
|
186
|
+
...additionalIncludes,
|
|
187
|
+
];
|
|
119
188
|
return {
|
|
120
|
-
include
|
|
121
|
-
`${tinybirdDir}/datasources.ts`,
|
|
122
|
-
`${tinybirdDir}/pipes.ts`,
|
|
123
|
-
],
|
|
189
|
+
include,
|
|
124
190
|
token: "${TINYBIRD_TOKEN}",
|
|
125
191
|
baseUrl: "https://api.tinybird.co",
|
|
192
|
+
devMode,
|
|
126
193
|
};
|
|
127
194
|
}
|
|
128
195
|
|
|
@@ -136,6 +203,22 @@ export interface InitOptions {
|
|
|
136
203
|
force?: boolean;
|
|
137
204
|
/** Skip the login flow */
|
|
138
205
|
skipLogin?: boolean;
|
|
206
|
+
/** Development mode - if provided, skip interactive prompt */
|
|
207
|
+
devMode?: DevMode;
|
|
208
|
+
/** Client path - if provided, skip interactive prompt */
|
|
209
|
+
clientPath?: string;
|
|
210
|
+
/** Skip prompts for existing datafiles - for testing */
|
|
211
|
+
skipDatafilePrompt?: boolean;
|
|
212
|
+
/** Auto-include existing datafiles without prompting - for testing */
|
|
213
|
+
includeExistingDatafiles?: boolean;
|
|
214
|
+
/** Skip GitHub Actions workflow prompts */
|
|
215
|
+
skipWorkflowPrompt?: boolean;
|
|
216
|
+
/** Include Tinybird CI workflow */
|
|
217
|
+
includeCiWorkflow?: boolean;
|
|
218
|
+
/** Include Tinybird CD workflow */
|
|
219
|
+
includeCdWorkflow?: boolean;
|
|
220
|
+
/** Git provider for workflow templates */
|
|
221
|
+
workflowProvider?: "github" | "gitlab";
|
|
139
222
|
}
|
|
140
223
|
|
|
141
224
|
/**
|
|
@@ -156,6 +239,72 @@ export interface InitResult {
|
|
|
156
239
|
workspaceName?: string;
|
|
157
240
|
/** User email after login */
|
|
158
241
|
userEmail?: string;
|
|
242
|
+
/** Selected development mode */
|
|
243
|
+
devMode?: DevMode;
|
|
244
|
+
/** Selected client path */
|
|
245
|
+
clientPath?: string;
|
|
246
|
+
/** Existing datafiles that were added to config */
|
|
247
|
+
existingDatafiles?: string[];
|
|
248
|
+
/** Whether a Tinybird CI workflow was created */
|
|
249
|
+
ciWorkflowCreated?: boolean;
|
|
250
|
+
/** Whether a Tinybird CD workflow was created */
|
|
251
|
+
cdWorkflowCreated?: boolean;
|
|
252
|
+
/** Git provider used for workflow templates */
|
|
253
|
+
workflowProvider?: "github" | "gitlab";
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Find existing .datasource and .pipe files in the repository
|
|
258
|
+
*
|
|
259
|
+
* @param cwd - Working directory to search from
|
|
260
|
+
* @param maxDepth - Maximum directory depth to search (default: 5)
|
|
261
|
+
* @returns Array of relative file paths
|
|
262
|
+
*/
|
|
263
|
+
export function findExistingDatafiles(
|
|
264
|
+
cwd: string,
|
|
265
|
+
maxDepth: number = 5
|
|
266
|
+
): string[] {
|
|
267
|
+
const files: string[] = [];
|
|
268
|
+
|
|
269
|
+
function searchDir(dir: string, depth: number): void {
|
|
270
|
+
if (depth > maxDepth) return;
|
|
271
|
+
|
|
272
|
+
let entries: fs.Dirent[];
|
|
273
|
+
try {
|
|
274
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
275
|
+
} catch {
|
|
276
|
+
return; // Skip directories we can't read
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
for (const entry of entries) {
|
|
280
|
+
const fullPath = path.join(dir, entry.name);
|
|
281
|
+
|
|
282
|
+
// Skip node_modules and hidden directories
|
|
283
|
+
if (
|
|
284
|
+
entry.isDirectory() &&
|
|
285
|
+
(entry.name === "node_modules" ||
|
|
286
|
+
entry.name.startsWith(".") ||
|
|
287
|
+
entry.name === "dist" ||
|
|
288
|
+
entry.name === "build")
|
|
289
|
+
) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (entry.isDirectory()) {
|
|
294
|
+
searchDir(fullPath, depth + 1);
|
|
295
|
+
} else if (
|
|
296
|
+
entry.isFile() &&
|
|
297
|
+
(entry.name.endsWith(".datasource") || entry.name.endsWith(".pipe"))
|
|
298
|
+
) {
|
|
299
|
+
// Convert to relative path
|
|
300
|
+
const relativePath = path.relative(cwd, fullPath);
|
|
301
|
+
files.push(relativePath);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
searchDir(cwd, 0);
|
|
307
|
+
return files.sort();
|
|
159
308
|
}
|
|
160
309
|
|
|
161
310
|
/**
|
|
@@ -163,7 +312,7 @@ export interface InitResult {
|
|
|
163
312
|
*
|
|
164
313
|
* Creates:
|
|
165
314
|
* - tinybird.json in the project root
|
|
166
|
-
* - src/tinybird/ folder with datasources.ts,
|
|
315
|
+
* - src/tinybird/ folder with datasources.ts, endpoints.ts, and client.ts
|
|
167
316
|
*
|
|
168
317
|
* @param options - Init options
|
|
169
318
|
* @returns Init result
|
|
@@ -175,14 +324,219 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
175
324
|
|
|
176
325
|
const created: string[] = [];
|
|
177
326
|
const skipped: string[] = [];
|
|
327
|
+
let didPrompt = false;
|
|
328
|
+
let existingDatafiles: string[] = [];
|
|
329
|
+
let ciWorkflowCreated = false;
|
|
330
|
+
let cdWorkflowCreated = false;
|
|
331
|
+
let workflowProvider = options.workflowProvider;
|
|
332
|
+
|
|
333
|
+
// Check for existing .datasource and .pipe files
|
|
334
|
+
const foundDatafiles = findExistingDatafiles(cwd);
|
|
335
|
+
|
|
336
|
+
// Determine devMode - prompt if not provided
|
|
337
|
+
let devMode: DevMode = options.devMode ?? "branch";
|
|
338
|
+
|
|
339
|
+
if (!options.devMode) {
|
|
340
|
+
// Show interactive prompt for workflow selection
|
|
341
|
+
p.intro(pc.cyan("tinybird init"));
|
|
342
|
+
|
|
343
|
+
const workflowChoice = await p.select({
|
|
344
|
+
message: "How do you want to develop with Tinybird?",
|
|
345
|
+
options: [
|
|
346
|
+
{
|
|
347
|
+
value: "branch",
|
|
348
|
+
label: "Branches",
|
|
349
|
+
hint: "Use Tinybird Cloud with git-based branching",
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
value: "local",
|
|
353
|
+
label: "Tinybird Local",
|
|
354
|
+
hint: "Run your own Tinybird instance locally",
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
if (p.isCancel(workflowChoice)) {
|
|
360
|
+
p.cancel("Operation cancelled");
|
|
361
|
+
return {
|
|
362
|
+
success: false,
|
|
363
|
+
created: [],
|
|
364
|
+
skipped: [],
|
|
365
|
+
error: "Cancelled by user",
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
didPrompt = true;
|
|
370
|
+
devMode = workflowChoice as DevMode;
|
|
371
|
+
}
|
|
178
372
|
|
|
179
373
|
// Determine tinybird folder path based on project structure
|
|
180
|
-
const
|
|
181
|
-
|
|
374
|
+
const defaultRelativePath = getRelativeTinybirdDir(cwd);
|
|
375
|
+
let relativeTinybirdDir = options.clientPath ?? defaultRelativePath;
|
|
376
|
+
|
|
377
|
+
if (!options.clientPath && !options.devMode) {
|
|
378
|
+
// Ask user to confirm or change the client path
|
|
379
|
+
const clientPathChoice = await p.text({
|
|
380
|
+
message: "Where should we create Tinybird definitions?",
|
|
381
|
+
placeholder: defaultRelativePath,
|
|
382
|
+
defaultValue: defaultRelativePath,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
if (p.isCancel(clientPathChoice)) {
|
|
386
|
+
p.cancel("Operation cancelled");
|
|
387
|
+
return {
|
|
388
|
+
success: false,
|
|
389
|
+
created: [],
|
|
390
|
+
skipped: [],
|
|
391
|
+
error: "Cancelled by user",
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
didPrompt = true;
|
|
396
|
+
relativeTinybirdDir = clientPathChoice || defaultRelativePath;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const skipWorkflowPrompt =
|
|
400
|
+
options.skipWorkflowPrompt ??
|
|
401
|
+
(options.devMode !== undefined || options.clientPath !== undefined);
|
|
402
|
+
let includeCiWorkflow = options.includeCiWorkflow ?? false;
|
|
403
|
+
let includeCdWorkflow = options.includeCdWorkflow ?? false;
|
|
404
|
+
const shouldPromptWorkflows =
|
|
405
|
+
!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
|
+
|
|
431
|
+
if (shouldPromptWorkflows) {
|
|
432
|
+
const confirmWorkflows = await p.confirm({
|
|
433
|
+
message: "Create CI/CD workflows for Tinybird?",
|
|
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?",
|
|
455
|
+
options: [
|
|
456
|
+
{ value: "github", label: "GitHub" },
|
|
457
|
+
{ value: "gitlab", label: "GitLab" },
|
|
458
|
+
],
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
if (p.isCancel(providerChoice)) {
|
|
462
|
+
p.cancel("Operation cancelled");
|
|
463
|
+
return {
|
|
464
|
+
success: false,
|
|
465
|
+
created: [],
|
|
466
|
+
skipped: [],
|
|
467
|
+
error: "Cancelled by user",
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
didPrompt = true;
|
|
472
|
+
workflowProvider = providerChoice as "github" | "gitlab";
|
|
473
|
+
}
|
|
474
|
+
if ((includeCiWorkflow || includeCdWorkflow) && !workflowProvider) {
|
|
475
|
+
workflowProvider = "github";
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Ask about existing datafiles if found
|
|
479
|
+
if (foundDatafiles.length > 0 && !options.skipDatafilePrompt) {
|
|
480
|
+
const includeDatafiles =
|
|
481
|
+
options.includeExistingDatafiles ??
|
|
482
|
+
(await (async () => {
|
|
483
|
+
didPrompt = true;
|
|
484
|
+
return promptForExistingDatafiles(foundDatafiles);
|
|
485
|
+
})());
|
|
486
|
+
|
|
487
|
+
if (includeDatafiles) {
|
|
488
|
+
existingDatafiles = foundDatafiles;
|
|
489
|
+
}
|
|
490
|
+
} else if (options.includeExistingDatafiles && foundDatafiles.length > 0) {
|
|
491
|
+
existingDatafiles = foundDatafiles;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (didPrompt) {
|
|
495
|
+
const devModeLabel = devMode === "local" ? "Tinybird Local" : "Branches";
|
|
496
|
+
const datafileSummary =
|
|
497
|
+
foundDatafiles.length > 0
|
|
498
|
+
? existingDatafiles.length > 0
|
|
499
|
+
? `${existingDatafiles.length} included`
|
|
500
|
+
: "none included"
|
|
501
|
+
: "none found";
|
|
502
|
+
let workflowSummary = "none";
|
|
503
|
+
if (includeCiWorkflow || includeCdWorkflow) {
|
|
504
|
+
workflowSummary =
|
|
505
|
+
workflowProvider === "gitlab"
|
|
506
|
+
? "GitLab (.gitlab-ci.yml)"
|
|
507
|
+
: "GitHub Actions (tinybird-ci.yaml, tinybird-cd.yaml)";
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const summaryLines = [
|
|
511
|
+
`Mode: ${devModeLabel}`,
|
|
512
|
+
`Definitions: ${relativeTinybirdDir}/`,
|
|
513
|
+
`Existing datafiles: ${datafileSummary}`,
|
|
514
|
+
`Workflows: ${workflowSummary}`,
|
|
515
|
+
];
|
|
516
|
+
|
|
517
|
+
p.note(summaryLines.join("\n"), "Installation Summary");
|
|
518
|
+
|
|
519
|
+
const confirmInit = await p.confirm({
|
|
520
|
+
message: "Proceed with initialization?",
|
|
521
|
+
initialValue: true,
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
if (p.isCancel(confirmInit) || !confirmInit) {
|
|
525
|
+
p.cancel("Init cancelled.");
|
|
526
|
+
return {
|
|
527
|
+
success: false,
|
|
528
|
+
created: [],
|
|
529
|
+
skipped: [],
|
|
530
|
+
error: "Cancelled by user",
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const tinybirdDir = path.join(cwd, relativeTinybirdDir);
|
|
182
536
|
|
|
183
537
|
// File paths
|
|
184
538
|
const datasourcesPath = path.join(tinybirdDir, "datasources.ts");
|
|
185
|
-
const
|
|
539
|
+
const endpointsPath = path.join(tinybirdDir, "endpoints.ts");
|
|
186
540
|
const clientPath = path.join(tinybirdDir, "client.ts");
|
|
187
541
|
|
|
188
542
|
// Create config file (tinybird.json)
|
|
@@ -191,7 +545,11 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
191
545
|
skipped.push("tinybird.json");
|
|
192
546
|
} else {
|
|
193
547
|
try {
|
|
194
|
-
const config = createDefaultConfig(
|
|
548
|
+
const config = createDefaultConfig(
|
|
549
|
+
relativeTinybirdDir,
|
|
550
|
+
devMode,
|
|
551
|
+
existingDatafiles
|
|
552
|
+
);
|
|
195
553
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
196
554
|
created.push("tinybird.json");
|
|
197
555
|
} catch (error) {
|
|
@@ -212,7 +570,9 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
212
570
|
success: false,
|
|
213
571
|
created,
|
|
214
572
|
skipped,
|
|
215
|
-
error: `Failed to create ${relativeTinybirdDir} folder: ${
|
|
573
|
+
error: `Failed to create ${relativeTinybirdDir} folder: ${
|
|
574
|
+
(error as Error).message
|
|
575
|
+
}`,
|
|
216
576
|
};
|
|
217
577
|
}
|
|
218
578
|
|
|
@@ -233,19 +593,19 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
233
593
|
}
|
|
234
594
|
}
|
|
235
595
|
|
|
236
|
-
// Create
|
|
237
|
-
if (fs.existsSync(
|
|
238
|
-
skipped.push(`${relativeTinybirdDir}/
|
|
596
|
+
// Create endpoints.ts
|
|
597
|
+
if (fs.existsSync(endpointsPath) && !force) {
|
|
598
|
+
skipped.push(`${relativeTinybirdDir}/endpoints.ts`);
|
|
239
599
|
} else {
|
|
240
600
|
try {
|
|
241
|
-
fs.writeFileSync(
|
|
242
|
-
created.push(`${relativeTinybirdDir}/
|
|
601
|
+
fs.writeFileSync(endpointsPath, ENDPOINTS_CONTENT);
|
|
602
|
+
created.push(`${relativeTinybirdDir}/endpoints.ts`);
|
|
243
603
|
} catch (error) {
|
|
244
604
|
return {
|
|
245
605
|
success: false,
|
|
246
606
|
created,
|
|
247
607
|
skipped,
|
|
248
|
-
error: `Failed to create
|
|
608
|
+
error: `Failed to create endpoints.ts: ${(error as Error).message}`,
|
|
249
609
|
};
|
|
250
610
|
}
|
|
251
611
|
}
|
|
@@ -288,8 +648,16 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
288
648
|
modified = true;
|
|
289
649
|
}
|
|
290
650
|
|
|
651
|
+
if (!packageJson.scripts["tinybird:deploy"]) {
|
|
652
|
+
packageJson.scripts["tinybird:deploy"] = "tinybird deploy";
|
|
653
|
+
modified = true;
|
|
654
|
+
}
|
|
655
|
+
|
|
291
656
|
if (modified) {
|
|
292
|
-
fs.writeFileSync(
|
|
657
|
+
fs.writeFileSync(
|
|
658
|
+
packageJsonPath,
|
|
659
|
+
JSON.stringify(packageJson, null, 2) + "\n"
|
|
660
|
+
);
|
|
293
661
|
created.push("package.json (added tinybird scripts)");
|
|
294
662
|
}
|
|
295
663
|
} catch {
|
|
@@ -297,6 +665,94 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
297
665
|
}
|
|
298
666
|
}
|
|
299
667
|
|
|
668
|
+
const workflowsDir = path.join(cwd, ".github", "workflows");
|
|
669
|
+
const ciWorkflowPath = path.join(workflowsDir, "tinybird-ci.yaml");
|
|
670
|
+
const cdWorkflowPath = path.join(workflowsDir, "tinybird-cd.yaml");
|
|
671
|
+
const gitlabWorkflowPath = path.join(cwd, ".gitlab-ci.yml");
|
|
672
|
+
|
|
673
|
+
if (includeCiWorkflow || includeCdWorkflow) {
|
|
674
|
+
if (workflowProvider === "github") {
|
|
675
|
+
try {
|
|
676
|
+
fs.mkdirSync(workflowsDir, { recursive: true });
|
|
677
|
+
} catch (error) {
|
|
678
|
+
return {
|
|
679
|
+
success: false,
|
|
680
|
+
created,
|
|
681
|
+
skipped,
|
|
682
|
+
error: `Failed to create .github/workflows folder: ${
|
|
683
|
+
(error as Error).message
|
|
684
|
+
}`,
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
if (workflowProvider === "gitlab") {
|
|
690
|
+
if (fs.existsSync(gitlabWorkflowPath) && !force) {
|
|
691
|
+
skipped.push(".gitlab-ci.yml");
|
|
692
|
+
} else {
|
|
693
|
+
try {
|
|
694
|
+
fs.writeFileSync(gitlabWorkflowPath, TINYBIRD_GITLAB_CI);
|
|
695
|
+
created.push(".gitlab-ci.yml");
|
|
696
|
+
ciWorkflowCreated = includeCiWorkflow;
|
|
697
|
+
cdWorkflowCreated = includeCdWorkflow;
|
|
698
|
+
} catch (error) {
|
|
699
|
+
return {
|
|
700
|
+
success: false,
|
|
701
|
+
created,
|
|
702
|
+
skipped,
|
|
703
|
+
error: `Failed to create .gitlab-ci.yml: ${
|
|
704
|
+
(error as Error).message
|
|
705
|
+
}`,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (workflowProvider === "github") {
|
|
713
|
+
if (includeCiWorkflow) {
|
|
714
|
+
if (fs.existsSync(ciWorkflowPath) && !force) {
|
|
715
|
+
skipped.push(".github/workflows/tinybird-ci.yaml");
|
|
716
|
+
} else {
|
|
717
|
+
try {
|
|
718
|
+
fs.writeFileSync(ciWorkflowPath, TINYBIRD_CI_WORKFLOW);
|
|
719
|
+
created.push(".github/workflows/tinybird-ci.yaml");
|
|
720
|
+
ciWorkflowCreated = true;
|
|
721
|
+
} catch (error) {
|
|
722
|
+
return {
|
|
723
|
+
success: false,
|
|
724
|
+
created,
|
|
725
|
+
skipped,
|
|
726
|
+
error: `Failed to create tinybird-ci.yaml: ${
|
|
727
|
+
(error as Error).message
|
|
728
|
+
}`,
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (includeCdWorkflow) {
|
|
735
|
+
if (fs.existsSync(cdWorkflowPath) && !force) {
|
|
736
|
+
skipped.push(".github/workflows/tinybird-cd.yaml");
|
|
737
|
+
} else {
|
|
738
|
+
try {
|
|
739
|
+
fs.writeFileSync(cdWorkflowPath, TINYBIRD_CD_WORKFLOW);
|
|
740
|
+
created.push(".github/workflows/tinybird-cd.yaml");
|
|
741
|
+
cdWorkflowCreated = true;
|
|
742
|
+
} catch (error) {
|
|
743
|
+
return {
|
|
744
|
+
success: false,
|
|
745
|
+
created,
|
|
746
|
+
skipped,
|
|
747
|
+
error: `Failed to create tinybird-cd.yaml: ${
|
|
748
|
+
(error as Error).message
|
|
749
|
+
}`,
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
300
756
|
// Check if login is needed
|
|
301
757
|
if (!skipLogin && !hasValidToken(cwd)) {
|
|
302
758
|
console.log("\nNo authentication found. Starting login flow...\n");
|
|
@@ -312,8 +768,9 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
312
768
|
}
|
|
313
769
|
|
|
314
770
|
// If custom base URL, update tinybird.json
|
|
315
|
-
|
|
316
|
-
|
|
771
|
+
const baseUrl = authResult.baseUrl ?? "https://api.tinybird.co";
|
|
772
|
+
if (baseUrl !== "https://api.tinybird.co") {
|
|
773
|
+
updateConfig(configPath, { baseUrl });
|
|
317
774
|
}
|
|
318
775
|
|
|
319
776
|
return {
|
|
@@ -323,15 +780,31 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
323
780
|
loggedIn: true,
|
|
324
781
|
workspaceName: authResult.workspaceName,
|
|
325
782
|
userEmail: authResult.userEmail,
|
|
783
|
+
devMode,
|
|
784
|
+
clientPath: relativeTinybirdDir,
|
|
785
|
+
existingDatafiles:
|
|
786
|
+
existingDatafiles.length > 0 ? existingDatafiles : undefined,
|
|
787
|
+
ciWorkflowCreated,
|
|
788
|
+
cdWorkflowCreated,
|
|
789
|
+
workflowProvider,
|
|
326
790
|
};
|
|
327
791
|
} catch (error) {
|
|
328
792
|
// Login succeeded but saving credentials failed
|
|
329
|
-
console.error(
|
|
793
|
+
console.error(
|
|
794
|
+
`Warning: Failed to save credentials: ${(error as Error).message}`
|
|
795
|
+
);
|
|
330
796
|
return {
|
|
331
797
|
success: true,
|
|
332
798
|
created,
|
|
333
799
|
skipped,
|
|
334
800
|
loggedIn: false,
|
|
801
|
+
devMode,
|
|
802
|
+
clientPath: relativeTinybirdDir,
|
|
803
|
+
existingDatafiles:
|
|
804
|
+
existingDatafiles.length > 0 ? existingDatafiles : undefined,
|
|
805
|
+
ciWorkflowCreated,
|
|
806
|
+
cdWorkflowCreated,
|
|
807
|
+
workflowProvider,
|
|
335
808
|
};
|
|
336
809
|
}
|
|
337
810
|
} else {
|
|
@@ -341,6 +814,13 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
341
814
|
created,
|
|
342
815
|
skipped,
|
|
343
816
|
loggedIn: false,
|
|
817
|
+
devMode,
|
|
818
|
+
clientPath: relativeTinybirdDir,
|
|
819
|
+
existingDatafiles:
|
|
820
|
+
existingDatafiles.length > 0 ? existingDatafiles : undefined,
|
|
821
|
+
ciWorkflowCreated,
|
|
822
|
+
cdWorkflowCreated,
|
|
823
|
+
workflowProvider,
|
|
344
824
|
};
|
|
345
825
|
}
|
|
346
826
|
}
|
|
@@ -349,5 +829,47 @@ export async function runInit(options: InitOptions = {}): Promise<InitResult> {
|
|
|
349
829
|
success: true,
|
|
350
830
|
created,
|
|
351
831
|
skipped,
|
|
832
|
+
devMode,
|
|
833
|
+
clientPath: relativeTinybirdDir,
|
|
834
|
+
existingDatafiles:
|
|
835
|
+
existingDatafiles.length > 0 ? existingDatafiles : undefined,
|
|
836
|
+
ciWorkflowCreated,
|
|
837
|
+
cdWorkflowCreated,
|
|
838
|
+
workflowProvider,
|
|
352
839
|
};
|
|
353
840
|
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Prompt user about including existing datafiles
|
|
844
|
+
*/
|
|
845
|
+
async function promptForExistingDatafiles(
|
|
846
|
+
datafiles: string[]
|
|
847
|
+
): Promise<boolean> {
|
|
848
|
+
const datasourceCount = datafiles.filter((f) =>
|
|
849
|
+
f.endsWith(".datasource")
|
|
850
|
+
).length;
|
|
851
|
+
const pipeCount = datafiles.filter((f) => f.endsWith(".pipe")).length;
|
|
852
|
+
|
|
853
|
+
const parts: string[] = [];
|
|
854
|
+
if (datasourceCount > 0) {
|
|
855
|
+
parts.push(
|
|
856
|
+
`${datasourceCount} .datasource file${datasourceCount > 1 ? "s" : ""}`
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
if (pipeCount > 0) {
|
|
860
|
+
parts.push(`${pipeCount} .pipe file${pipeCount > 1 ? "s" : ""}`);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const confirmInclude = await p.confirm({
|
|
864
|
+
message: `Found ${parts.join(
|
|
865
|
+
" and "
|
|
866
|
+
)} in your project. Include them in tinybird.json?`,
|
|
867
|
+
initialValue: true,
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
if (p.isCancel(confirmInclude)) {
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
return confirmInclude;
|
|
875
|
+
}
|