@donotdev/cli 0.0.14 → 0.0.16
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/dependencies-matrix.json +372 -88
- package/dist/bin/commands/agent-setup.js +7 -1
- package/dist/bin/commands/build.js +141 -44
- package/dist/bin/commands/bump.js +81 -41
- package/dist/bin/commands/cacheout.js +37 -9
- package/dist/bin/commands/create-app.js +276 -121
- package/dist/bin/commands/create-project.js +506 -217
- package/dist/bin/commands/deploy.js +1785 -694
- package/dist/bin/commands/dev.js +177 -43
- package/dist/bin/commands/doctor.d.ts +6 -0
- package/dist/bin/commands/doctor.d.ts.map +1 -0
- package/dist/bin/commands/{lint.js → doctor.js} +1215 -156
- package/dist/bin/commands/doctor.js.map +1 -0
- package/dist/bin/commands/emu.js +451 -104
- package/dist/bin/commands/format.js +37 -9
- package/dist/bin/commands/make-admin.js +77499 -11
- package/dist/bin/commands/preview.js +181 -43
- package/dist/bin/commands/setup.d.ts +6 -0
- package/dist/bin/commands/setup.d.ts.map +1 -0
- package/dist/bin/commands/setup.js +11733 -0
- package/dist/bin/commands/setup.js.map +1 -0
- package/dist/bin/commands/supabase-setup.d.ts +6 -0
- package/dist/bin/commands/supabase-setup.d.ts.map +1 -0
- package/dist/bin/commands/supabase-setup.js +7 -0
- package/dist/bin/commands/supabase-setup.js.map +1 -0
- package/dist/bin/commands/sync-secrets.js +211 -34
- package/dist/bin/commands/type-check.d.ts +14 -0
- package/dist/bin/commands/type-check.d.ts.map +1 -0
- package/dist/bin/commands/type-check.js +2049 -0
- package/dist/bin/commands/type-check.js.map +1 -0
- package/dist/bin/commands/wai.js +3 -1
- package/dist/bin/dndev.js +73 -52
- package/dist/bin/donotdev.js +54 -45
- package/dist/index.js +4212 -3050
- package/package.json +3 -3
- package/templates/app-demo/src/App.tsx.example +1 -0
- package/templates/app-demo/src/pages/FullPage.tsx.example +2 -2
- package/templates/app-demo/src/pages/components/DemoLayout.tsx.example +2 -2
- package/templates/app-demo/src/themes.css.example +5 -12
- package/templates/app-expo/.env.example +44 -0
- package/templates/app-expo/.expo/README.md.example +5 -0
- package/templates/app-expo/.gitignore.example +36 -0
- package/templates/app-expo/README.md.example +58 -0
- package/templates/app-expo/app/.gitkeep +2 -0
- package/templates/app-expo/app/_layout.tsx.example +41 -0
- package/templates/app-expo/app/form.tsx.example +52 -0
- package/templates/app-expo/app/index.tsx.example +89 -0
- package/templates/app-expo/app/list.tsx.example +32 -0
- package/templates/app-expo/app/profile.tsx.example +76 -0
- package/templates/app-expo/app/signin.tsx.example +53 -0
- package/templates/app-expo/app.json.example +39 -0
- package/templates/app-expo/assets/adaptive-icon.png +0 -0
- package/templates/app-expo/assets/favicon.png +0 -0
- package/templates/app-expo/assets/icon.png +0 -0
- package/templates/app-expo/assets/splash.png +0 -0
- package/templates/app-expo/babel.config.js.example +10 -0
- package/templates/app-expo/eas.json.example +20 -0
- package/templates/app-expo/expo-env.d.ts.example +4 -0
- package/templates/app-expo/metro.config.js.example +20 -0
- package/templates/app-expo/service-account-key.json.example +12 -0
- package/templates/app-expo/src/config/app.ts.example +46 -0
- package/templates/app-expo/src/config/providers.ts.example +7 -0
- package/templates/app-expo/tsconfig.json.example +19 -0
- package/templates/app-next/.env.example +4 -33
- package/templates/app-next/src/app/ClientLayout.tsx.example +2 -0
- package/templates/app-next/src/app/layout.tsx.example +7 -6
- package/templates/app-next/src/config/providers.ts.example +7 -0
- package/templates/app-next/src/globals.css.example +2 -11
- package/templates/app-next/src/pages/HomePage.tsx.example +1 -1
- package/templates/app-next/src/themes.css.example +10 -13
- package/templates/app-vite/.env.example +3 -32
- package/templates/app-vite/index.html.example +2 -24
- package/templates/app-vite/src/App.tsx.example +2 -0
- package/templates/app-vite/src/config/providers.ts.example +7 -0
- package/templates/app-vite/src/globals.css.example +2 -12
- package/templates/app-vite/src/pages/FormPageExample.tsx.example +1 -2
- package/templates/app-vite/src/pages/HomePage.tsx.example +2 -2
- package/templates/app-vite/src/themes.css.example +109 -79
- package/templates/app-vite/vercel.json.example +11 -0
- package/templates/functions-firebase/README.md.example +1 -1
- package/templates/functions-firebase/build.mjs.example +2 -72
- package/templates/functions-firebase/functions-firebase/.env.example.example +24 -26
- package/templates/functions-firebase/functions-firebase/README.md.example +1 -1
- package/templates/functions-firebase/functions-firebase/build.mjs.example +2 -72
- package/templates/functions-firebase/functions-firebase/tsconfig.json.example +1 -1
- package/templates/functions-firebase/functions.config.js.example +1 -1
- package/templates/functions-supabase/supabase/config.toml.example +59 -0
- package/templates/functions-supabase/supabase/functions/.env.example +13 -0
- package/templates/functions-supabase/supabase/functions/cancel-subscription/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/change-plan/index.ts.example +11 -0
- package/templates/functions-supabase/supabase/functions/create-checkout-session/index.ts.example +11 -0
- package/templates/functions-supabase/supabase/functions/create-customer-portal/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/crud/index.ts.example +16 -0
- package/templates/functions-supabase/supabase/functions/delete-account/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/deno.json.example +8 -0
- package/templates/functions-supabase/supabase/functions/get-custom-claims/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/get-user-auth-status/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/refresh-subscription-status/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/remove-custom-claims/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/functions/set-custom-claims/index.ts.example +7 -0
- package/templates/functions-supabase/supabase/migrations/20250101000000_idempotency.sql +24 -0
- package/templates/functions-supabase/supabase/migrations/20250101000001_rate_limits.sql +22 -0
- package/templates/functions-supabase/supabase/migrations/20250101000002_cleanup_jobs.sql +28 -0
- package/templates/functions-supabase/supabase/migrations/20250101000003_operation_metrics.sql +28 -0
- package/templates/functions-vercel/functions-vercel/tsconfig.json.example +1 -1
- package/templates/functions-vercel/functions-vercel/vercel.json.example +1 -1
- package/templates/functions-vercel/vercel.json.example +1 -1
- package/templates/github/github/workflows/firebase-deploy.yml.example +1 -1
- package/templates/github/workflows/firebase-deploy.yml.example +1 -1
- package/templates/overlay-firebase/env.fragment.example +34 -0
- package/templates/overlay-firebase/env.fragment.expo.example +34 -0
- package/templates/overlay-firebase/env.fragment.nextjs.example +34 -0
- package/templates/overlay-firebase/src/config/providers.expo.ts.example +49 -0
- package/templates/overlay-firebase/src/config/providers.ts.example +23 -0
- package/templates/overlay-supabase/env.fragment.example +12 -0
- package/templates/overlay-supabase/env.fragment.expo.example +12 -0
- package/templates/overlay-supabase/env.fragment.nextjs.example +12 -0
- package/templates/overlay-supabase/src/config/providers.expo.ts.example +35 -0
- package/templates/overlay-supabase/src/config/providers.ts.example +33 -0
- package/templates/overlay-supabase/vercel.headers.example +23 -0
- package/templates/overlay-supabase/vercel.json.example +22 -0
- package/templates/overlay-vercel/env.fragment.example +34 -0
- package/templates/overlay-vercel/env.fragment.nextjs.example +34 -0
- package/templates/overlay-vercel/src/config/providers.ts.example +24 -0
- package/templates/root-consumer/.claude/agents/architect.md.example +2 -310
- package/templates/root-consumer/.claude/agents/builder.md.example +2 -326
- package/templates/root-consumer/.claude/agents/coder.md.example +2 -83
- package/templates/root-consumer/.claude/agents/extractor.md.example +2 -231
- package/templates/root-consumer/.claude/agents/polisher.md.example +2 -132
- package/templates/root-consumer/.claude/agents/prompt-engineer.md.example +2 -81
- package/templates/root-consumer/.claude/commands/grill.md.example +30 -0
- package/templates/root-consumer/.claude/commands/techdebt.md.example +28 -0
- package/templates/root-consumer/.clinerules.example +1 -0
- package/templates/root-consumer/.cursor/rules/no-docs.mdc.example +15 -0
- package/templates/root-consumer/.cursorrules.example +1 -0
- package/templates/root-consumer/.github/copilot-instructions.md.example +1 -0
- package/templates/root-consumer/.windsurfrules.example +1 -0
- package/templates/root-consumer/AI.md.example +44 -123
- package/templates/root-consumer/CLAUDE.md.example +1 -134
- package/templates/root-consumer/CONVENTIONS.md.example +1 -0
- package/templates/root-consumer/GEMINI.md.example +1 -0
- package/templates/root-consumer/firebase.json.example +1 -1
- package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +22 -2
- package/templates/root-consumer/guides/dndev/COMPONENTS_ADV.md.example +0 -18
- package/templates/root-consumer/guides/dndev/COMPONENTS_UI.md.example +1 -1
- package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +101 -32
- package/templates/root-consumer/guides/dndev/INDEX.md.example +4 -2
- package/templates/root-consumer/guides/dndev/SETUP_APP_CONFIG.md.example +3 -3
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +241 -12
- package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +13 -7
- package/templates/root-consumer/guides/dndev/SETUP_OAUTH_PROVIDERS.md.example +60 -0
- package/templates/root-consumer/guides/dndev/SETUP_SOC2.md.example +234 -0
- package/templates/root-consumer/guides/dndev/SETUP_STRIPE.md.example +62 -0
- package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +124 -0
- package/templates/root-consumer/guides/dndev/SETUP_THEMES.md.example +6 -2
- package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +176 -0
- package/templates/root-consumer/guides/dndev/USE_ROUTING.md.example +5 -9
- package/templates/root-consumer/guides/dndev/essences_reference.css.example +174 -0
- package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +7 -8
- package/templates/root-consumer/guides/wai-way/agents/builder.md.example +10 -0
- package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +25 -5
- package/templates/root-consumer/guides/wai-way/agents/polisher.md.example +13 -2
- package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +2 -2
- package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +55 -15
- package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +15 -4
- package/templates/root-consumer/guides/wai-way/spec_template.md.example +7 -6
- package/dist/bin/commands/lint.d.ts +0 -11
- package/dist/bin/commands/lint.d.ts.map +0 -1
- package/dist/bin/commands/lint.js.map +0 -1
- package/dist/bin/commands/staging.d.ts +0 -11
- package/dist/bin/commands/staging.d.ts.map +0 -1
- package/dist/bin/commands/staging.js +0 -12
- package/dist/bin/commands/staging.js.map +0 -1
- package/templates/app-payload/.env.example +0 -28
- package/templates/app-payload/README.md.example +0 -233
- package/templates/app-payload/collections/Company.ts.example +0 -125
- package/templates/app-payload/collections/Hero.ts.example +0 -62
- package/templates/app-payload/collections/Media.ts.example +0 -41
- package/templates/app-payload/collections/Products.ts.example +0 -115
- package/templates/app-payload/collections/Services.ts.example +0 -104
- package/templates/app-payload/collections/Testimonials.ts.example +0 -92
- package/templates/app-payload/collections/Users.ts.example +0 -35
- package/templates/app-payload/src/server.ts.example +0 -79
- package/templates/app-payload/tsconfig.json.example +0 -24
|
@@ -16,6 +16,10 @@ var __esm = (fn, res) => function __init() {
|
|
|
16
16
|
var __commonJS = (cb, mod) => function __require2() {
|
|
17
17
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
18
18
|
};
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
22
|
+
};
|
|
19
23
|
var __copyProps = (to, from, except, desc) => {
|
|
20
24
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
21
25
|
for (let key of __getOwnPropNames(from))
|
|
@@ -7687,11 +7691,39 @@ var init_PathResolver = __esm({
|
|
|
7687
7691
|
});
|
|
7688
7692
|
|
|
7689
7693
|
// packages/tooling/src/utils/errors.ts
|
|
7690
|
-
var DoNotDevError;
|
|
7694
|
+
var DO_NOT_DEV_ERROR_CODES, DoNotDevError;
|
|
7691
7695
|
var init_errors = __esm({
|
|
7692
7696
|
"packages/tooling/src/utils/errors.ts"() {
|
|
7693
7697
|
"use strict";
|
|
7694
7698
|
init_utils();
|
|
7699
|
+
DO_NOT_DEV_ERROR_CODES = {
|
|
7700
|
+
CONFIGURATION_ERROR: "configuration-error",
|
|
7701
|
+
CONFIG_NOT_FOUND: "config-not-found",
|
|
7702
|
+
CONFIG_INVALID: "config-invalid",
|
|
7703
|
+
PATH_RESOLUTION_ERROR: "path-resolution-error",
|
|
7704
|
+
FILE_OPERATION_ERROR: "file-operation-error",
|
|
7705
|
+
FILE_NOT_FOUND: "file-not-found",
|
|
7706
|
+
PERMISSION_DENIED: "permission-denied",
|
|
7707
|
+
GENERATION_ERROR: "generation-error",
|
|
7708
|
+
TEMPLATE_ERROR: "template-error",
|
|
7709
|
+
TEMPLATE_NOT_FOUND: "template-not-found",
|
|
7710
|
+
CLI_EXECUTION_ERROR: "cli-execution-error",
|
|
7711
|
+
COMMAND_NOT_FOUND: "command-not-found",
|
|
7712
|
+
COMMAND_FAILED: "command-failed",
|
|
7713
|
+
VALIDATION_ERROR: "validation-error",
|
|
7714
|
+
SCHEMA_ERROR: "schema-error",
|
|
7715
|
+
DEPENDENCY_ERROR: "dependency-error",
|
|
7716
|
+
DEPENDENCY_NOT_FOUND: "dependency-not-found",
|
|
7717
|
+
DEPENDENCY_VERSION_ERROR: "dependency-version-error",
|
|
7718
|
+
INVALID_ARGUMENT: "invalid-argument",
|
|
7719
|
+
MISSING_ARGUMENT: "missing-argument",
|
|
7720
|
+
MISSING_PROJECT_ID: "missing-project-id",
|
|
7721
|
+
FIREBASE_CLI_ERROR: "firebase-cli-error",
|
|
7722
|
+
DEPLOYMENT_FAILED: "deployment-failed",
|
|
7723
|
+
OPERATION_CANCELLED: "operation-cancelled",
|
|
7724
|
+
TIMEOUT_ERROR: "timeout-error",
|
|
7725
|
+
UNKNOWN_ERROR: "unknown-error"
|
|
7726
|
+
};
|
|
7695
7727
|
DoNotDevError = class _DoNotDevError extends Error {
|
|
7696
7728
|
/** The error code categorizing this error */
|
|
7697
7729
|
code;
|
|
@@ -7711,7 +7743,7 @@ var init_errors = __esm({
|
|
|
7711
7743
|
* @param {Record<string, any>} [options.context] - Additional context data
|
|
7712
7744
|
* @param {boolean} [options.displayable=true] - Whether this error should be displayed to the user
|
|
7713
7745
|
*/
|
|
7714
|
-
constructor(message, code =
|
|
7746
|
+
constructor(message, code = DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR, options) {
|
|
7715
7747
|
super(message);
|
|
7716
7748
|
this.name = "DoNotDevError";
|
|
7717
7749
|
this.code = code;
|
|
@@ -7742,7 +7774,7 @@ var init_errors = __esm({
|
|
|
7742
7774
|
* @param {boolean} [options.displayable=true] - Whether this error should be displayed to the user
|
|
7743
7775
|
* @returns {DoNotDevError} New DoNotDev error wrapping the original
|
|
7744
7776
|
*/
|
|
7745
|
-
static from(error2, context, code =
|
|
7777
|
+
static from(error2, context, code = DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR, options) {
|
|
7746
7778
|
if (!(error2 instanceof Error)) {
|
|
7747
7779
|
return new _DoNotDevError(
|
|
7748
7780
|
`Unknown error: ${String(error2)}`,
|
|
@@ -7779,21 +7811,21 @@ var init_errors = __esm({
|
|
|
7779
7811
|
}
|
|
7780
7812
|
const message = error2.message.toLowerCase();
|
|
7781
7813
|
if (error2.name === "ValidationError" || message.includes("validation")) {
|
|
7782
|
-
return
|
|
7814
|
+
return DO_NOT_DEV_ERROR_CODES.VALIDATION_ERROR;
|
|
7783
7815
|
}
|
|
7784
7816
|
if (message.includes("not found") || message.includes("no such file")) {
|
|
7785
|
-
return
|
|
7817
|
+
return DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND;
|
|
7786
7818
|
}
|
|
7787
7819
|
if (message.includes("permission") || message.includes("access denied")) {
|
|
7788
|
-
return
|
|
7820
|
+
return DO_NOT_DEV_ERROR_CODES.PERMISSION_DENIED;
|
|
7789
7821
|
}
|
|
7790
7822
|
if (message.includes("timeout") || message.includes("timed out")) {
|
|
7791
|
-
return
|
|
7823
|
+
return DO_NOT_DEV_ERROR_CODES.TIMEOUT_ERROR;
|
|
7792
7824
|
}
|
|
7793
7825
|
if (message.includes("dependency") || message.includes("module not found")) {
|
|
7794
|
-
return
|
|
7826
|
+
return DO_NOT_DEV_ERROR_CODES.DEPENDENCY_ERROR;
|
|
7795
7827
|
}
|
|
7796
|
-
return
|
|
7828
|
+
return DO_NOT_DEV_ERROR_CODES.UNKNOWN_ERROR;
|
|
7797
7829
|
}
|
|
7798
7830
|
};
|
|
7799
7831
|
}
|
|
@@ -7815,7 +7847,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
|
7815
7847
|
function readdirSync2(dirPath, options) {
|
|
7816
7848
|
return pathResolverInstance.readdirSync(dirPath, options);
|
|
7817
7849
|
}
|
|
7818
|
-
var pathResolverInstance, resolvePackage, normalizePath, pathExists, readSync, writeSync, removeSync, statSync2, ensureDirSync, joinPath;
|
|
7850
|
+
var pathResolverInstance, resolvePackage, normalizePath, pathExists, readSync, write, writeSync, remove, removeSync, statSync2, ensureDir, ensureDirSync, getRelativePathBetween, getDirname, joinPath;
|
|
7819
7851
|
var init_pathResolver = __esm({
|
|
7820
7852
|
"packages/tooling/src/utils/pathResolver.ts"() {
|
|
7821
7853
|
"use strict";
|
|
@@ -7828,16 +7860,111 @@ var init_pathResolver = __esm({
|
|
|
7828
7860
|
};
|
|
7829
7861
|
pathExists = (filePath, silent = false) => pathResolverInstance.pathExists(filePath, silent);
|
|
7830
7862
|
readSync = (filePath, options) => pathResolverInstance.readSync(filePath, options);
|
|
7863
|
+
write = async (filePath, content, options) => pathResolverInstance.write(filePath, content, options);
|
|
7831
7864
|
writeSync = (filePath, content, options) => pathResolverInstance.writeSync(filePath, content, options);
|
|
7865
|
+
remove = async (path) => pathResolverInstance.remove(path);
|
|
7832
7866
|
removeSync = (path) => pathResolverInstance.removeSync(path);
|
|
7833
7867
|
statSync2 = (filePath) => pathResolverInstance.statSync(filePath);
|
|
7868
|
+
ensureDir = async (dirPath) => pathResolverInstance.ensureDir(dirPath);
|
|
7834
7869
|
ensureDirSync = (dirPath) => pathResolverInstance.ensureDirSync(dirPath);
|
|
7870
|
+
getRelativePathBetween = (from, to) => {
|
|
7871
|
+
return normalizePath(relative2(from, to));
|
|
7872
|
+
};
|
|
7873
|
+
getDirname = (filePath) => {
|
|
7874
|
+
return normalizePath(dirname2(filePath));
|
|
7875
|
+
};
|
|
7835
7876
|
joinPath = (...pathSegments) => {
|
|
7836
7877
|
return normalizePath(...pathSegments);
|
|
7837
7878
|
};
|
|
7838
7879
|
}
|
|
7839
7880
|
});
|
|
7840
7881
|
|
|
7882
|
+
// packages/tooling/src/utils/typed-file-operations.ts
|
|
7883
|
+
function readFirebaseJson(filePath) {
|
|
7884
|
+
if (!pathExists(filePath)) {
|
|
7885
|
+
throw new DoNotDevError(
|
|
7886
|
+
`firebase.json not found: ${filePath}`,
|
|
7887
|
+
DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND,
|
|
7888
|
+
{ context: { filePath } }
|
|
7889
|
+
);
|
|
7890
|
+
}
|
|
7891
|
+
const content = readSync(filePath, { format: "json" });
|
|
7892
|
+
if (!content || typeof content !== "object") {
|
|
7893
|
+
throw new DoNotDevError(
|
|
7894
|
+
`Invalid firebase.json: ${filePath}`,
|
|
7895
|
+
DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
|
|
7896
|
+
{ context: { filePath } }
|
|
7897
|
+
);
|
|
7898
|
+
}
|
|
7899
|
+
return content;
|
|
7900
|
+
}
|
|
7901
|
+
function readFirebaserc(filePath) {
|
|
7902
|
+
if (!pathExists(filePath)) {
|
|
7903
|
+
return { projects: {} };
|
|
7904
|
+
}
|
|
7905
|
+
const content = readSync(filePath, { format: "json" });
|
|
7906
|
+
if (!content || typeof content !== "object") {
|
|
7907
|
+
throw new DoNotDevError(
|
|
7908
|
+
`Invalid .firebaserc: ${filePath}`,
|
|
7909
|
+
DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
|
|
7910
|
+
{ context: { filePath } }
|
|
7911
|
+
);
|
|
7912
|
+
}
|
|
7913
|
+
return content;
|
|
7914
|
+
}
|
|
7915
|
+
function readPackageJson(filePath) {
|
|
7916
|
+
if (!pathExists(filePath)) {
|
|
7917
|
+
throw new DoNotDevError(
|
|
7918
|
+
`package.json not found: ${filePath}`,
|
|
7919
|
+
DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND,
|
|
7920
|
+
{ context: { filePath } }
|
|
7921
|
+
);
|
|
7922
|
+
}
|
|
7923
|
+
const content = readSync(filePath, { format: "json" });
|
|
7924
|
+
if (!content || typeof content !== "object" || !("name" in content)) {
|
|
7925
|
+
throw new DoNotDevError(
|
|
7926
|
+
`Invalid package.json: ${filePath}`,
|
|
7927
|
+
DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
|
|
7928
|
+
{ context: { filePath } }
|
|
7929
|
+
);
|
|
7930
|
+
}
|
|
7931
|
+
return content;
|
|
7932
|
+
}
|
|
7933
|
+
function readServiceAccountKey(filePath) {
|
|
7934
|
+
if (!pathExists(filePath)) {
|
|
7935
|
+
throw new DoNotDevError(
|
|
7936
|
+
`Service account key not found: ${filePath}`,
|
|
7937
|
+
DO_NOT_DEV_ERROR_CODES.FILE_NOT_FOUND,
|
|
7938
|
+
{ context: { filePath } }
|
|
7939
|
+
);
|
|
7940
|
+
}
|
|
7941
|
+
const content = readSync(filePath, { format: "json" });
|
|
7942
|
+
if (!content || typeof content !== "object") {
|
|
7943
|
+
throw new DoNotDevError(
|
|
7944
|
+
`Invalid service account key: ${filePath}`,
|
|
7945
|
+
DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
|
|
7946
|
+
{ context: { filePath } }
|
|
7947
|
+
);
|
|
7948
|
+
}
|
|
7949
|
+
const key = content;
|
|
7950
|
+
if (!key.project_id || !key.private_key || !key.client_email) {
|
|
7951
|
+
throw new DoNotDevError(
|
|
7952
|
+
`Invalid service account key: missing required fields`,
|
|
7953
|
+
DO_NOT_DEV_ERROR_CODES.CONFIG_INVALID,
|
|
7954
|
+
{ context: { filePath, missingFields: ["project_id", "private_key", "client_email"].filter((f) => !key[f]) } }
|
|
7955
|
+
);
|
|
7956
|
+
}
|
|
7957
|
+
return content;
|
|
7958
|
+
}
|
|
7959
|
+
var init_typed_file_operations = __esm({
|
|
7960
|
+
"packages/tooling/src/utils/typed-file-operations.ts"() {
|
|
7961
|
+
"use strict";
|
|
7962
|
+
init_utils();
|
|
7963
|
+
init_pathResolver();
|
|
7964
|
+
init_errors();
|
|
7965
|
+
}
|
|
7966
|
+
});
|
|
7967
|
+
|
|
7841
7968
|
// packages/tooling/src/bundler/utils.ts
|
|
7842
7969
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
7843
7970
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
@@ -7865,129 +7992,500 @@ var init_utils = __esm({
|
|
|
7865
7992
|
}
|
|
7866
7993
|
});
|
|
7867
7994
|
|
|
7868
|
-
//
|
|
7869
|
-
|
|
7870
|
-
|
|
7995
|
+
// packages/tooling/src/utils/cli-input.ts
|
|
7996
|
+
async function askForInput(message, defaultValue = "") {
|
|
7997
|
+
const result = await he({
|
|
7998
|
+
message,
|
|
7999
|
+
placeholder: defaultValue || void 0,
|
|
8000
|
+
defaultValue: defaultValue || void 0
|
|
8001
|
+
});
|
|
8002
|
+
if (pD(result)) {
|
|
8003
|
+
xe("Operation cancelled.");
|
|
8004
|
+
process.exit(0);
|
|
8005
|
+
}
|
|
8006
|
+
return result;
|
|
8007
|
+
}
|
|
8008
|
+
async function askForConfirmation(message, defaultValue = false) {
|
|
8009
|
+
const result = await ye({
|
|
8010
|
+
message,
|
|
8011
|
+
initialValue: defaultValue
|
|
8012
|
+
});
|
|
8013
|
+
if (pD(result)) {
|
|
8014
|
+
xe("Operation cancelled.");
|
|
8015
|
+
process.exit(0);
|
|
8016
|
+
}
|
|
8017
|
+
return result;
|
|
8018
|
+
}
|
|
8019
|
+
async function askForSelection(message, choices, defaultValue = 0) {
|
|
8020
|
+
const options = choices.map((choice) => ({
|
|
8021
|
+
value: choice.value,
|
|
8022
|
+
label: choice.title,
|
|
8023
|
+
hint: choice.hint
|
|
8024
|
+
}));
|
|
8025
|
+
const result = await ve({
|
|
8026
|
+
message,
|
|
8027
|
+
options,
|
|
8028
|
+
initialValue: choices[defaultValue]?.value
|
|
8029
|
+
});
|
|
8030
|
+
if (pD(result)) {
|
|
8031
|
+
xe("Operation cancelled.");
|
|
8032
|
+
process.exit(0);
|
|
8033
|
+
}
|
|
8034
|
+
return result;
|
|
8035
|
+
}
|
|
8036
|
+
var init_cli_input = __esm({
|
|
8037
|
+
"packages/tooling/src/utils/cli-input.ts"() {
|
|
7871
8038
|
"use strict";
|
|
7872
8039
|
init_utils();
|
|
7873
|
-
|
|
7874
|
-
var DOC = /* @__PURE__ */ Symbol.for("yaml.document");
|
|
7875
|
-
var MAP = /* @__PURE__ */ Symbol.for("yaml.map");
|
|
7876
|
-
var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair");
|
|
7877
|
-
var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar");
|
|
7878
|
-
var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq");
|
|
7879
|
-
var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type");
|
|
7880
|
-
var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS;
|
|
7881
|
-
var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC;
|
|
7882
|
-
var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP;
|
|
7883
|
-
var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR;
|
|
7884
|
-
var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR;
|
|
7885
|
-
var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ;
|
|
7886
|
-
function isCollection(node) {
|
|
7887
|
-
if (node && typeof node === "object")
|
|
7888
|
-
switch (node[NODE_TYPE]) {
|
|
7889
|
-
case MAP:
|
|
7890
|
-
case SEQ:
|
|
7891
|
-
return true;
|
|
7892
|
-
}
|
|
7893
|
-
return false;
|
|
7894
|
-
}
|
|
7895
|
-
function isNode(node) {
|
|
7896
|
-
if (node && typeof node === "object")
|
|
7897
|
-
switch (node[NODE_TYPE]) {
|
|
7898
|
-
case ALIAS:
|
|
7899
|
-
case MAP:
|
|
7900
|
-
case SCALAR:
|
|
7901
|
-
case SEQ:
|
|
7902
|
-
return true;
|
|
7903
|
-
}
|
|
7904
|
-
return false;
|
|
7905
|
-
}
|
|
7906
|
-
var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor;
|
|
7907
|
-
exports.ALIAS = ALIAS;
|
|
7908
|
-
exports.DOC = DOC;
|
|
7909
|
-
exports.MAP = MAP;
|
|
7910
|
-
exports.NODE_TYPE = NODE_TYPE;
|
|
7911
|
-
exports.PAIR = PAIR;
|
|
7912
|
-
exports.SCALAR = SCALAR;
|
|
7913
|
-
exports.SEQ = SEQ;
|
|
7914
|
-
exports.hasAnchor = hasAnchor;
|
|
7915
|
-
exports.isAlias = isAlias;
|
|
7916
|
-
exports.isCollection = isCollection;
|
|
7917
|
-
exports.isDocument = isDocument;
|
|
7918
|
-
exports.isMap = isMap;
|
|
7919
|
-
exports.isNode = isNode;
|
|
7920
|
-
exports.isPair = isPair;
|
|
7921
|
-
exports.isScalar = isScalar;
|
|
7922
|
-
exports.isSeq = isSeq;
|
|
8040
|
+
init_dist2();
|
|
7923
8041
|
}
|
|
7924
8042
|
});
|
|
7925
8043
|
|
|
7926
|
-
//
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
const
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
node.items.splice(i, 1);
|
|
7964
|
-
i -= 1;
|
|
8044
|
+
// packages/tooling/src/utils/cli-tools.ts
|
|
8045
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
8046
|
+
function checkCLI(command, args = ["--version"], useCache = true) {
|
|
8047
|
+
const cacheKey = `${command}:${args.join(",")}`;
|
|
8048
|
+
if (useCache && cliCache.has(cacheKey)) {
|
|
8049
|
+
return cliCache.get(cacheKey);
|
|
8050
|
+
}
|
|
8051
|
+
try {
|
|
8052
|
+
let result;
|
|
8053
|
+
if (process.platform === "win32") {
|
|
8054
|
+
const username = process.env.USERNAME || process.env.USER || "";
|
|
8055
|
+
const programFiles = process.env.ProgramFiles || "C:\\Program Files";
|
|
8056
|
+
const commonPaths = [
|
|
8057
|
+
`${programFiles}\\nodejs\\${command}.ps1`,
|
|
8058
|
+
`${programFiles}\\nodejs\\${command}.cmd`,
|
|
8059
|
+
`${programFiles}\\nodejs\\${command}`,
|
|
8060
|
+
`C:\\Users\\${username}\\AppData\\Roaming\\npm\\${command}.ps1`,
|
|
8061
|
+
`C:\\Users\\${username}\\AppData\\Roaming\\npm\\${command}.cmd`,
|
|
8062
|
+
`C:\\Users\\${username}\\AppData\\Roaming\\npm\\${command}`,
|
|
8063
|
+
process.env.APPDATA ? `${process.env.APPDATA}\\npm\\${command}.ps1` : null,
|
|
8064
|
+
process.env.APPDATA ? `${process.env.APPDATA}\\npm\\${command}.cmd` : null,
|
|
8065
|
+
process.env.APPDATA ? `${process.env.APPDATA}\\npm\\${command}` : null
|
|
8066
|
+
].filter(Boolean);
|
|
8067
|
+
for (const fullPath of commonPaths) {
|
|
8068
|
+
try {
|
|
8069
|
+
const testResult = spawnSync2(
|
|
8070
|
+
"powershell.exe",
|
|
8071
|
+
[
|
|
8072
|
+
"-NoProfile",
|
|
8073
|
+
"-ExecutionPolicy",
|
|
8074
|
+
"Bypass",
|
|
8075
|
+
"-Command",
|
|
8076
|
+
`& '${fullPath}' ${args.join(" ")}`
|
|
8077
|
+
],
|
|
8078
|
+
{
|
|
8079
|
+
stdio: "pipe",
|
|
8080
|
+
shell: false
|
|
7965
8081
|
}
|
|
8082
|
+
);
|
|
8083
|
+
if (testResult.status === 0) {
|
|
8084
|
+
cliCache.set(cacheKey, true);
|
|
8085
|
+
return true;
|
|
7966
8086
|
}
|
|
7967
|
-
}
|
|
7968
|
-
|
|
7969
|
-
const ck = visit_("key", node.key, visitor, path);
|
|
7970
|
-
if (ck === BREAK)
|
|
7971
|
-
return BREAK;
|
|
7972
|
-
else if (ck === REMOVE)
|
|
7973
|
-
node.key = null;
|
|
7974
|
-
const cv = visit_("value", node.value, visitor, path);
|
|
7975
|
-
if (cv === BREAK)
|
|
7976
|
-
return BREAK;
|
|
7977
|
-
else if (cv === REMOVE)
|
|
7978
|
-
node.value = null;
|
|
8087
|
+
} catch {
|
|
8088
|
+
continue;
|
|
7979
8089
|
}
|
|
7980
8090
|
}
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
8091
|
+
result = spawnSync2(
|
|
8092
|
+
"powershell.exe",
|
|
8093
|
+
[
|
|
8094
|
+
"-NoProfile",
|
|
8095
|
+
"-ExecutionPolicy",
|
|
8096
|
+
"Bypass",
|
|
8097
|
+
"-Command",
|
|
8098
|
+
`${command} ${args.join(" ")}`
|
|
8099
|
+
],
|
|
8100
|
+
{
|
|
8101
|
+
stdio: "pipe",
|
|
8102
|
+
shell: false
|
|
8103
|
+
}
|
|
8104
|
+
);
|
|
8105
|
+
} else {
|
|
8106
|
+
result = spawnSync2(command, args, {
|
|
8107
|
+
stdio: "pipe",
|
|
8108
|
+
shell: true
|
|
8109
|
+
});
|
|
8110
|
+
}
|
|
8111
|
+
const isAvailable = result.status === 0;
|
|
8112
|
+
cliCache.set(cacheKey, isAvailable);
|
|
8113
|
+
return isAvailable;
|
|
8114
|
+
} catch (err) {
|
|
8115
|
+
cliCache.set(cacheKey, false);
|
|
8116
|
+
return false;
|
|
8117
|
+
}
|
|
8118
|
+
}
|
|
8119
|
+
function checkBun() {
|
|
8120
|
+
return checkCLI("bun");
|
|
8121
|
+
}
|
|
8122
|
+
function checkNpm() {
|
|
8123
|
+
return checkCLI("npm");
|
|
8124
|
+
}
|
|
8125
|
+
function checkYarn() {
|
|
8126
|
+
return checkCLI("yarn");
|
|
8127
|
+
}
|
|
8128
|
+
function checkFirebase() {
|
|
8129
|
+
return checkCLI("firebase");
|
|
8130
|
+
}
|
|
8131
|
+
function checkSentry() {
|
|
8132
|
+
return checkCLI("sentry-cli");
|
|
8133
|
+
}
|
|
8134
|
+
function checkGitHub() {
|
|
8135
|
+
return checkCLI("gh");
|
|
8136
|
+
}
|
|
8137
|
+
function checkGit() {
|
|
8138
|
+
return checkCLI("git");
|
|
8139
|
+
}
|
|
8140
|
+
function checkTsx() {
|
|
8141
|
+
return checkCLI("tsx");
|
|
8142
|
+
}
|
|
8143
|
+
function getCLIInstallInstructions(tool) {
|
|
8144
|
+
const platform = process.platform;
|
|
8145
|
+
const instructions = {
|
|
8146
|
+
[CLI_TOOLS.BUN]: {
|
|
8147
|
+
win32: [
|
|
8148
|
+
'powershell -c "irm bun.sh/install.ps1 | iex"',
|
|
8149
|
+
"Or download from: https://bun.sh"
|
|
8150
|
+
],
|
|
8151
|
+
darwin: [
|
|
8152
|
+
"curl -fsSL https://bun.sh/install | bash",
|
|
8153
|
+
"Or: brew install bun"
|
|
8154
|
+
],
|
|
8155
|
+
linux: [
|
|
8156
|
+
"curl -fsSL https://bun.sh/install | bash",
|
|
8157
|
+
"Or check your package manager (apt, dnf, pacman, etc.)"
|
|
8158
|
+
]
|
|
8159
|
+
},
|
|
8160
|
+
[CLI_TOOLS.NPM]: {
|
|
8161
|
+
win32: [
|
|
8162
|
+
"Install Node.js from: https://nodejs.org",
|
|
8163
|
+
"npm comes bundled with Node.js"
|
|
8164
|
+
],
|
|
8165
|
+
darwin: [
|
|
8166
|
+
"Install Node.js from: https://nodejs.org",
|
|
8167
|
+
"Or: brew install node",
|
|
8168
|
+
"npm comes bundled with Node.js"
|
|
8169
|
+
],
|
|
8170
|
+
linux: [
|
|
8171
|
+
"Install Node.js from: https://nodejs.org",
|
|
8172
|
+
"Or use your package manager: dnf install nodejs, apt install nodejs, etc.",
|
|
8173
|
+
"npm comes bundled with Node.js"
|
|
8174
|
+
]
|
|
8175
|
+
},
|
|
8176
|
+
[CLI_TOOLS.YARN]: {
|
|
8177
|
+
win32: ["npm install -g yarn", "Or: choco install yarn"],
|
|
8178
|
+
darwin: ["npm install -g yarn", "Or: brew install yarn"],
|
|
8179
|
+
linux: ["npm install -g yarn", "Or check your package manager"]
|
|
8180
|
+
},
|
|
8181
|
+
[CLI_TOOLS.FIREBASE]: {
|
|
8182
|
+
win32: [
|
|
8183
|
+
"npm install -g firebase-tools",
|
|
8184
|
+
"Or download standalone binary: https://firebase.google.com/docs/cli#windows-standalone-binary"
|
|
8185
|
+
],
|
|
8186
|
+
darwin: [
|
|
8187
|
+
"npm install -g firebase-tools",
|
|
8188
|
+
"Or: brew install firebase-cli"
|
|
8189
|
+
],
|
|
8190
|
+
linux: [
|
|
8191
|
+
"npm install -g firebase-tools",
|
|
8192
|
+
"Or check your package manager (dnf, apt, pacman, etc.)"
|
|
8193
|
+
]
|
|
8194
|
+
},
|
|
8195
|
+
[CLI_TOOLS.SUPABASE]: {
|
|
8196
|
+
win32: [
|
|
8197
|
+
"scoop install supabase",
|
|
8198
|
+
"Or: winget install Supabase.CLI",
|
|
8199
|
+
"Or download from: https://github.com/supabase/cli/releases"
|
|
8200
|
+
],
|
|
8201
|
+
darwin: [
|
|
8202
|
+
"brew install supabase/tap/supabase"
|
|
8203
|
+
],
|
|
8204
|
+
linux: [
|
|
8205
|
+
"brew install supabase/tap/supabase",
|
|
8206
|
+
"Or see: https://supabase.com/docs/guides/cli"
|
|
8207
|
+
]
|
|
8208
|
+
},
|
|
8209
|
+
[CLI_TOOLS.VERCEL]: {
|
|
8210
|
+
win32: ["npm install -g vercel", "Or: npx vercel (no install)"],
|
|
8211
|
+
darwin: ["npm install -g vercel", "Or: brew install vercel", "Or: npx vercel"],
|
|
8212
|
+
linux: ["npm install -g vercel", "Or: npx vercel"]
|
|
8213
|
+
},
|
|
8214
|
+
[CLI_TOOLS.SENTRY_CLI]: {
|
|
8215
|
+
win32: [
|
|
8216
|
+
"npm install -g @sentry/cli",
|
|
8217
|
+
"Or download from: https://github.com/getsentry/sentry-cli/releases"
|
|
8218
|
+
],
|
|
8219
|
+
darwin: [
|
|
8220
|
+
"npm install -g @sentry/cli",
|
|
8221
|
+
"Or: brew install getsentry/tools/sentry-cli"
|
|
8222
|
+
],
|
|
8223
|
+
linux: [
|
|
8224
|
+
"npm install -g @sentry/cli",
|
|
8225
|
+
"Or: curl -sL https://sentry.io/get-cli/ | bash"
|
|
8226
|
+
]
|
|
8227
|
+
},
|
|
8228
|
+
[CLI_TOOLS.GH]: {
|
|
8229
|
+
win32: [
|
|
8230
|
+
"Download from: https://cli.github.com",
|
|
8231
|
+
"Or: winget install --id GitHub.cli",
|
|
8232
|
+
"Or: choco install gh"
|
|
8233
|
+
],
|
|
8234
|
+
darwin: ["brew install gh", "Or download from: https://cli.github.com"],
|
|
8235
|
+
linux: [
|
|
8236
|
+
"See: https://github.com/cli/cli/blob/trunk/docs/install_linux.md",
|
|
8237
|
+
"dnf install gh",
|
|
8238
|
+
"apt install gh",
|
|
8239
|
+
"Or download from: https://cli.github.com"
|
|
8240
|
+
]
|
|
8241
|
+
},
|
|
8242
|
+
[CLI_TOOLS.GIT]: {
|
|
8243
|
+
win32: [
|
|
8244
|
+
"Download from: https://git-scm.com/download/win",
|
|
8245
|
+
"Or: winget install --id Git.Git"
|
|
8246
|
+
],
|
|
8247
|
+
darwin: ["brew install git", "Or: xcode-select --install"],
|
|
8248
|
+
linux: [
|
|
8249
|
+
"Use your package manager: dnf install git, apt install git, etc."
|
|
8250
|
+
]
|
|
8251
|
+
},
|
|
8252
|
+
[CLI_TOOLS.TSX]: {
|
|
8253
|
+
win32: ["npm install -g tsx"],
|
|
8254
|
+
darwin: ["npm install -g tsx"],
|
|
8255
|
+
linux: ["npm install -g tsx"]
|
|
8256
|
+
}
|
|
8257
|
+
};
|
|
8258
|
+
const platformInstructions = instructions[tool]?.[platform] || instructions[tool]?.linux || [];
|
|
8259
|
+
return platformInstructions.join("\n \u2022 ");
|
|
8260
|
+
}
|
|
8261
|
+
function formatCLIMissingError(tool, additionalContext) {
|
|
8262
|
+
const toolName = tool === CLI_TOOLS.SENTRY_CLI ? "Sentry CLI" : tool.toUpperCase();
|
|
8263
|
+
M2.error(`${toolName} is not installed or not in PATH.`);
|
|
8264
|
+
const instructions = getCLIInstallInstructions(tool);
|
|
8265
|
+
if (instructions) {
|
|
8266
|
+
M2.warn("Install it with:");
|
|
8267
|
+
M2.message(` \u2022 ${instructions}`);
|
|
8268
|
+
}
|
|
8269
|
+
if (additionalContext) {
|
|
8270
|
+
M2.info(`
|
|
8271
|
+
${additionalContext}`);
|
|
8272
|
+
}
|
|
8273
|
+
const platform = process.platform;
|
|
8274
|
+
M2.info("\nIf already installed, ensure it's in your PATH:");
|
|
8275
|
+
if (platform === "win32") {
|
|
8276
|
+
M2.message(" \u2022 Check with: where " + tool);
|
|
8277
|
+
M2.message(
|
|
8278
|
+
" \u2022 Common location: C:\\Users\\<username>\\AppData\\Roaming\\npm\\"
|
|
8279
|
+
);
|
|
8280
|
+
} else {
|
|
8281
|
+
M2.message(" \u2022 Check with: which " + tool);
|
|
8282
|
+
M2.message(" \u2022 Common locations:");
|
|
8283
|
+
M2.message(" - ~/.nvm/versions/node/*/bin/" + tool);
|
|
8284
|
+
M2.message(" - ~/.bun/bin/" + tool);
|
|
8285
|
+
M2.message(" - /usr/local/bin/" + tool);
|
|
8286
|
+
M2.message(" - /usr/bin/" + tool);
|
|
8287
|
+
}
|
|
8288
|
+
}
|
|
8289
|
+
function requireCLI(tool, additionalContext) {
|
|
8290
|
+
const checkers = {
|
|
8291
|
+
[CLI_TOOLS.BUN]: checkBun,
|
|
8292
|
+
[CLI_TOOLS.NPM]: checkNpm,
|
|
8293
|
+
[CLI_TOOLS.YARN]: checkYarn,
|
|
8294
|
+
[CLI_TOOLS.FIREBASE]: checkFirebase,
|
|
8295
|
+
[CLI_TOOLS.SUPABASE]: () => checkCLI(CLI_TOOLS.SUPABASE),
|
|
8296
|
+
[CLI_TOOLS.VERCEL]: () => checkCLI(CLI_TOOLS.VERCEL),
|
|
8297
|
+
[CLI_TOOLS.SENTRY_CLI]: checkSentry,
|
|
8298
|
+
[CLI_TOOLS.GH]: checkGitHub,
|
|
8299
|
+
[CLI_TOOLS.GIT]: checkGit,
|
|
8300
|
+
[CLI_TOOLS.TSX]: checkTsx
|
|
8301
|
+
};
|
|
8302
|
+
cliCache.delete(`${tool}:--version`);
|
|
8303
|
+
const isAvailable = checkers[tool]?.();
|
|
8304
|
+
if (!isAvailable) {
|
|
8305
|
+
formatCLIMissingError(tool, additionalContext);
|
|
8306
|
+
process.exit(1);
|
|
8307
|
+
}
|
|
8308
|
+
return true;
|
|
8309
|
+
}
|
|
8310
|
+
var CLI_TOOLS, cliCache;
|
|
8311
|
+
var init_cli_tools = __esm({
|
|
8312
|
+
"packages/tooling/src/utils/cli-tools.ts"() {
|
|
8313
|
+
"use strict";
|
|
8314
|
+
init_utils();
|
|
8315
|
+
init_dist2();
|
|
8316
|
+
CLI_TOOLS = {
|
|
8317
|
+
BUN: "bun",
|
|
8318
|
+
NPM: "npm",
|
|
8319
|
+
YARN: "yarn",
|
|
8320
|
+
FIREBASE: "firebase",
|
|
8321
|
+
SUPABASE: "supabase",
|
|
8322
|
+
VERCEL: "vercel",
|
|
8323
|
+
SENTRY_CLI: "sentry-cli",
|
|
8324
|
+
GH: "gh",
|
|
8325
|
+
GIT: "git",
|
|
8326
|
+
TSX: "tsx"
|
|
8327
|
+
};
|
|
8328
|
+
cliCache = /* @__PURE__ */ new Map();
|
|
8329
|
+
}
|
|
8330
|
+
});
|
|
8331
|
+
|
|
8332
|
+
// packages/tooling/src/cli/setup/vercel-token.ts
|
|
8333
|
+
function readEnvVar(filePath, varName) {
|
|
8334
|
+
if (!pathExists(filePath)) return null;
|
|
8335
|
+
const content = readSync(filePath, { format: "text" });
|
|
8336
|
+
if (typeof content !== "string") return null;
|
|
8337
|
+
for (const line of content.split(/\r?\n/)) {
|
|
8338
|
+
const trimmed = line.trim();
|
|
8339
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
8340
|
+
if (trimmed.startsWith(`${varName}=`)) {
|
|
8341
|
+
const val = trimmed.substring(`${varName}=`.length).trim();
|
|
8342
|
+
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
8343
|
+
return val.slice(1, -1);
|
|
8344
|
+
}
|
|
8345
|
+
return val || null;
|
|
8346
|
+
}
|
|
8347
|
+
}
|
|
8348
|
+
return null;
|
|
8349
|
+
}
|
|
8350
|
+
function resolveVercelToken(appDir) {
|
|
8351
|
+
if (process.env.VERCEL_TOKEN) return process.env.VERCEL_TOKEN;
|
|
8352
|
+
const fromEnv = readEnvVar(joinPath(appDir, ".env"), "VERCEL_TOKEN");
|
|
8353
|
+
if (fromEnv) return fromEnv;
|
|
8354
|
+
const fromLocal = readEnvVar(joinPath(appDir, ".env.local"), "VERCEL_TOKEN");
|
|
8355
|
+
if (fromLocal) return fromLocal;
|
|
8356
|
+
return null;
|
|
8357
|
+
}
|
|
8358
|
+
var init_vercel_token = __esm({
|
|
8359
|
+
"packages/tooling/src/cli/setup/vercel-token.ts"() {
|
|
8360
|
+
"use strict";
|
|
8361
|
+
init_utils();
|
|
8362
|
+
init_pathResolver();
|
|
8363
|
+
}
|
|
8364
|
+
});
|
|
8365
|
+
|
|
8366
|
+
// node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js
|
|
8367
|
+
var require_identity = __commonJS({
|
|
8368
|
+
"node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js"(exports) {
|
|
8369
|
+
"use strict";
|
|
8370
|
+
init_utils();
|
|
8371
|
+
var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias");
|
|
8372
|
+
var DOC = /* @__PURE__ */ Symbol.for("yaml.document");
|
|
8373
|
+
var MAP = /* @__PURE__ */ Symbol.for("yaml.map");
|
|
8374
|
+
var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair");
|
|
8375
|
+
var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar");
|
|
8376
|
+
var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq");
|
|
8377
|
+
var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type");
|
|
8378
|
+
var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS;
|
|
8379
|
+
var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC;
|
|
8380
|
+
var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP;
|
|
8381
|
+
var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR;
|
|
8382
|
+
var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR;
|
|
8383
|
+
var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ;
|
|
8384
|
+
function isCollection(node) {
|
|
8385
|
+
if (node && typeof node === "object")
|
|
8386
|
+
switch (node[NODE_TYPE]) {
|
|
8387
|
+
case MAP:
|
|
8388
|
+
case SEQ:
|
|
8389
|
+
return true;
|
|
8390
|
+
}
|
|
8391
|
+
return false;
|
|
8392
|
+
}
|
|
8393
|
+
function isNode(node) {
|
|
8394
|
+
if (node && typeof node === "object")
|
|
8395
|
+
switch (node[NODE_TYPE]) {
|
|
8396
|
+
case ALIAS:
|
|
8397
|
+
case MAP:
|
|
8398
|
+
case SCALAR:
|
|
8399
|
+
case SEQ:
|
|
8400
|
+
return true;
|
|
8401
|
+
}
|
|
8402
|
+
return false;
|
|
8403
|
+
}
|
|
8404
|
+
var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor;
|
|
8405
|
+
exports.ALIAS = ALIAS;
|
|
8406
|
+
exports.DOC = DOC;
|
|
8407
|
+
exports.MAP = MAP;
|
|
8408
|
+
exports.NODE_TYPE = NODE_TYPE;
|
|
8409
|
+
exports.PAIR = PAIR;
|
|
8410
|
+
exports.SCALAR = SCALAR;
|
|
8411
|
+
exports.SEQ = SEQ;
|
|
8412
|
+
exports.hasAnchor = hasAnchor;
|
|
8413
|
+
exports.isAlias = isAlias;
|
|
8414
|
+
exports.isCollection = isCollection;
|
|
8415
|
+
exports.isDocument = isDocument;
|
|
8416
|
+
exports.isMap = isMap;
|
|
8417
|
+
exports.isNode = isNode;
|
|
8418
|
+
exports.isPair = isPair;
|
|
8419
|
+
exports.isScalar = isScalar;
|
|
8420
|
+
exports.isSeq = isSeq;
|
|
8421
|
+
}
|
|
8422
|
+
});
|
|
8423
|
+
|
|
8424
|
+
// node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/visit.js
|
|
8425
|
+
var require_visit = __commonJS({
|
|
8426
|
+
"node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/visit.js"(exports) {
|
|
8427
|
+
"use strict";
|
|
8428
|
+
init_utils();
|
|
8429
|
+
var identity = require_identity();
|
|
8430
|
+
var BREAK = /* @__PURE__ */ Symbol("break visit");
|
|
8431
|
+
var SKIP = /* @__PURE__ */ Symbol("skip children");
|
|
8432
|
+
var REMOVE = /* @__PURE__ */ Symbol("remove node");
|
|
8433
|
+
function visit(node, visitor) {
|
|
8434
|
+
const visitor_ = initVisitor(visitor);
|
|
8435
|
+
if (identity.isDocument(node)) {
|
|
8436
|
+
const cd = visit_(null, node.contents, visitor_, Object.freeze([node]));
|
|
8437
|
+
if (cd === REMOVE)
|
|
8438
|
+
node.contents = null;
|
|
8439
|
+
} else
|
|
8440
|
+
visit_(null, node, visitor_, Object.freeze([]));
|
|
8441
|
+
}
|
|
8442
|
+
visit.BREAK = BREAK;
|
|
8443
|
+
visit.SKIP = SKIP;
|
|
8444
|
+
visit.REMOVE = REMOVE;
|
|
8445
|
+
function visit_(key, node, visitor, path) {
|
|
8446
|
+
const ctrl = callVisitor(key, node, visitor, path);
|
|
8447
|
+
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
8448
|
+
replaceNode(key, path, ctrl);
|
|
8449
|
+
return visit_(key, ctrl, visitor, path);
|
|
8450
|
+
}
|
|
8451
|
+
if (typeof ctrl !== "symbol") {
|
|
8452
|
+
if (identity.isCollection(node)) {
|
|
8453
|
+
path = Object.freeze(path.concat(node));
|
|
8454
|
+
for (let i = 0; i < node.items.length; ++i) {
|
|
8455
|
+
const ci = visit_(i, node.items[i], visitor, path);
|
|
8456
|
+
if (typeof ci === "number")
|
|
8457
|
+
i = ci - 1;
|
|
8458
|
+
else if (ci === BREAK)
|
|
8459
|
+
return BREAK;
|
|
8460
|
+
else if (ci === REMOVE) {
|
|
8461
|
+
node.items.splice(i, 1);
|
|
8462
|
+
i -= 1;
|
|
8463
|
+
}
|
|
8464
|
+
}
|
|
8465
|
+
} else if (identity.isPair(node)) {
|
|
8466
|
+
path = Object.freeze(path.concat(node));
|
|
8467
|
+
const ck = visit_("key", node.key, visitor, path);
|
|
8468
|
+
if (ck === BREAK)
|
|
8469
|
+
return BREAK;
|
|
8470
|
+
else if (ck === REMOVE)
|
|
8471
|
+
node.key = null;
|
|
8472
|
+
const cv = visit_("value", node.value, visitor, path);
|
|
8473
|
+
if (cv === BREAK)
|
|
8474
|
+
return BREAK;
|
|
8475
|
+
else if (cv === REMOVE)
|
|
8476
|
+
node.value = null;
|
|
8477
|
+
}
|
|
8478
|
+
}
|
|
8479
|
+
return ctrl;
|
|
8480
|
+
}
|
|
8481
|
+
async function visitAsync(node, visitor) {
|
|
8482
|
+
const visitor_ = initVisitor(visitor);
|
|
8483
|
+
if (identity.isDocument(node)) {
|
|
8484
|
+
const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node]));
|
|
8485
|
+
if (cd === REMOVE)
|
|
8486
|
+
node.contents = null;
|
|
8487
|
+
} else
|
|
8488
|
+
await visitAsync_(null, node, visitor_, Object.freeze([]));
|
|
7991
8489
|
}
|
|
7992
8490
|
visitAsync.BREAK = BREAK;
|
|
7993
8491
|
visitAsync.SKIP = SKIP;
|
|
@@ -15221,444 +15719,836 @@ var require_dist = __commonJS({
|
|
|
15221
15719
|
exports.parseAllDocuments = publicApi.parseAllDocuments;
|
|
15222
15720
|
exports.parseDocument = publicApi.parseDocument;
|
|
15223
15721
|
exports.stringify = publicApi.stringify;
|
|
15224
|
-
exports.visit = visit.visit;
|
|
15225
|
-
exports.visitAsync = visit.visitAsync;
|
|
15226
|
-
}
|
|
15227
|
-
});
|
|
15228
|
-
|
|
15229
|
-
// packages/cli/src/bin/commands/deploy.ts
|
|
15230
|
-
init_utils();
|
|
15231
|
-
|
|
15232
|
-
// packages/tooling/src/index.ts
|
|
15233
|
-
init_utils();
|
|
15234
|
-
|
|
15235
|
-
// packages/tooling/src/cli/index.ts
|
|
15236
|
-
init_utils();
|
|
15237
|
-
|
|
15238
|
-
// packages/tooling/src/utils/cli-input.ts
|
|
15239
|
-
init_utils();
|
|
15240
|
-
init_dist2();
|
|
15241
|
-
async function askForInput(message, defaultValue = "") {
|
|
15242
|
-
const result = await he({
|
|
15243
|
-
message,
|
|
15244
|
-
placeholder: defaultValue || void 0,
|
|
15245
|
-
defaultValue: defaultValue || void 0
|
|
15246
|
-
});
|
|
15247
|
-
if (pD(result)) {
|
|
15248
|
-
xe("Operation cancelled.");
|
|
15249
|
-
process.exit(0);
|
|
15250
|
-
}
|
|
15251
|
-
return result;
|
|
15252
|
-
}
|
|
15253
|
-
async function askForConfirmation(message, defaultValue = false) {
|
|
15254
|
-
const result = await ye({
|
|
15255
|
-
message,
|
|
15256
|
-
initialValue: defaultValue
|
|
15257
|
-
});
|
|
15258
|
-
if (pD(result)) {
|
|
15259
|
-
xe("Operation cancelled.");
|
|
15260
|
-
process.exit(0);
|
|
15261
|
-
}
|
|
15262
|
-
return result;
|
|
15263
|
-
}
|
|
15264
|
-
async function askForSelection(message, choices, defaultValue = 0) {
|
|
15265
|
-
const options = choices.map((choice) => ({
|
|
15266
|
-
value: choice.value,
|
|
15267
|
-
label: choice.title,
|
|
15268
|
-
hint: choice.hint
|
|
15269
|
-
}));
|
|
15270
|
-
const result = await ve({
|
|
15271
|
-
message,
|
|
15272
|
-
options,
|
|
15273
|
-
initialValue: choices[defaultValue]?.value
|
|
15274
|
-
});
|
|
15275
|
-
if (pD(result)) {
|
|
15276
|
-
xe("Operation cancelled.");
|
|
15277
|
-
process.exit(0);
|
|
15722
|
+
exports.visit = visit.visit;
|
|
15723
|
+
exports.visitAsync = visit.visitAsync;
|
|
15278
15724
|
}
|
|
15279
|
-
|
|
15280
|
-
}
|
|
15725
|
+
});
|
|
15281
15726
|
|
|
15282
|
-
// packages/tooling/src/utils/
|
|
15283
|
-
|
|
15284
|
-
|
|
15285
|
-
init_errors();
|
|
15286
|
-
init_pathResolver();
|
|
15287
|
-
import { spawnSync, execSync } from "node:child_process";
|
|
15288
|
-
function clearConflictingAuth(env) {
|
|
15289
|
-
const cleaned = { ...env };
|
|
15290
|
-
delete cleaned.FIREBASE_TOKEN;
|
|
15291
|
-
delete cleaned.FIREBASE_AUTH_TOKEN;
|
|
15292
|
-
return cleaned;
|
|
15727
|
+
// packages/tooling/src/utils/error-handling.ts
|
|
15728
|
+
function isError(error2) {
|
|
15729
|
+
return error2 instanceof Error;
|
|
15293
15730
|
}
|
|
15294
|
-
function
|
|
15295
|
-
|
|
15296
|
-
|
|
15297
|
-
GOOGLE_APPLICATION_CREDENTIALS: serviceAccountPath,
|
|
15298
|
-
NODE_OPTIONS: "",
|
|
15299
|
-
NODE_ENV: "production"
|
|
15300
|
-
});
|
|
15301
|
-
}
|
|
15302
|
-
function executeFirebaseCommand(args, options) {
|
|
15303
|
-
const deployEnv = createDeployEnv(options.serviceAccountPath);
|
|
15304
|
-
const spawnOptions = {
|
|
15305
|
-
cwd: options.cwd,
|
|
15306
|
-
stdio: "inherit",
|
|
15307
|
-
env: deployEnv
|
|
15308
|
-
};
|
|
15309
|
-
let result;
|
|
15310
|
-
result = spawnSync("firebase", args, { ...spawnOptions, shell: true });
|
|
15311
|
-
if (result.error) {
|
|
15312
|
-
return {
|
|
15313
|
-
success: false,
|
|
15314
|
-
status: 1,
|
|
15315
|
-
error: result.error
|
|
15316
|
-
};
|
|
15731
|
+
function getErrorMessage(error2) {
|
|
15732
|
+
if (isError(error2)) {
|
|
15733
|
+
return error2.message;
|
|
15317
15734
|
}
|
|
15318
|
-
|
|
15319
|
-
|
|
15320
|
-
|
|
15321
|
-
|
|
15322
|
-
|
|
15323
|
-
}
|
|
15735
|
+
if (typeof error2 === "string") {
|
|
15736
|
+
return error2;
|
|
15737
|
+
}
|
|
15738
|
+
if (error2 && typeof error2 === "object" && "message" in error2) {
|
|
15739
|
+
return String(error2.message);
|
|
15740
|
+
}
|
|
15741
|
+
return String(error2);
|
|
15324
15742
|
}
|
|
15325
|
-
|
|
15326
|
-
|
|
15327
|
-
|
|
15328
|
-
|
|
15329
|
-
if (projectId) {
|
|
15330
|
-
args.push("--project", projectId);
|
|
15743
|
+
var init_error_handling = __esm({
|
|
15744
|
+
"packages/tooling/src/utils/error-handling.ts"() {
|
|
15745
|
+
"use strict";
|
|
15746
|
+
init_utils();
|
|
15331
15747
|
}
|
|
15332
|
-
|
|
15333
|
-
|
|
15748
|
+
});
|
|
15749
|
+
|
|
15750
|
+
// packages/tooling/src/apps/sync-secrets.ts
|
|
15751
|
+
var sync_secrets_exports = {};
|
|
15752
|
+
__export(sync_secrets_exports, {
|
|
15753
|
+
default: () => sync_secrets_default,
|
|
15754
|
+
main: () => main
|
|
15755
|
+
});
|
|
15756
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
15757
|
+
function parseEnvFile(filePath) {
|
|
15758
|
+
if (!pathExists(filePath)) {
|
|
15759
|
+
throw new DoNotDevError(
|
|
15760
|
+
`Environment file not found: ${filePath}`,
|
|
15761
|
+
"file-not-found",
|
|
15762
|
+
{ context: { filePath } }
|
|
15763
|
+
);
|
|
15334
15764
|
}
|
|
15335
|
-
|
|
15336
|
-
|
|
15765
|
+
const contentRaw = readSync(filePath, { format: "text" });
|
|
15766
|
+
const content = typeof contentRaw === "string" ? contentRaw : null;
|
|
15767
|
+
if (!content) {
|
|
15768
|
+
throw new Error(`Failed to read secrets file: ${filePath}`);
|
|
15337
15769
|
}
|
|
15338
|
-
|
|
15770
|
+
const secrets = {};
|
|
15771
|
+
content.split("\n").forEach((line, index) => {
|
|
15772
|
+
const trimmedLine = line.trim();
|
|
15773
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) {
|
|
15774
|
+
return;
|
|
15775
|
+
}
|
|
15776
|
+
const equalIndex = trimmedLine.indexOf("=");
|
|
15777
|
+
if (equalIndex === -1) {
|
|
15778
|
+
log.warn(`Invalid line format at line ${index + 1}: ${trimmedLine}`);
|
|
15779
|
+
return;
|
|
15780
|
+
}
|
|
15781
|
+
const key = trimmedLine.substring(0, equalIndex).trim();
|
|
15782
|
+
const value = trimmedLine.substring(equalIndex + 1).trim();
|
|
15783
|
+
const cleanValue = value.replace(/^["']|["']$/g, "");
|
|
15784
|
+
if (key && cleanValue) {
|
|
15785
|
+
secrets[key] = cleanValue;
|
|
15786
|
+
}
|
|
15787
|
+
});
|
|
15788
|
+
return secrets;
|
|
15339
15789
|
}
|
|
15340
|
-
function
|
|
15341
|
-
const
|
|
15342
|
-
|
|
15343
|
-
|
|
15344
|
-
const tips = [];
|
|
15345
|
-
if (isRetryError || isHashError) {
|
|
15346
|
-
tips.push("Try deploying with --debug flag for more details");
|
|
15347
|
-
tips.push("Disable VPN if active - VPNs can corrupt file uploads");
|
|
15348
|
-
tips.push("Clear Firebase cache: Remove .firebase directory");
|
|
15349
|
-
tips.push(
|
|
15350
|
-
"Ensure no file watchers or antivirus are modifying files during upload"
|
|
15351
|
-
);
|
|
15790
|
+
function detectPlatform() {
|
|
15791
|
+
const currentDir = process.cwd();
|
|
15792
|
+
if (pathExists(joinPath(currentDir, "firebase.json"))) {
|
|
15793
|
+
return "firebase";
|
|
15352
15794
|
}
|
|
15353
|
-
if (
|
|
15354
|
-
|
|
15355
|
-
tips.push("Check service account key file is valid JSON");
|
|
15356
|
-
tips.push("Ensure FIREBASE_TOKEN and FIREBASE_AUTH_TOKEN are cleared");
|
|
15795
|
+
if (pathExists(joinPath(currentDir, "vercel.json")) || pathExists(joinPath(currentDir, ".vercel"))) {
|
|
15796
|
+
return "vercel";
|
|
15357
15797
|
}
|
|
15358
|
-
|
|
15798
|
+
log.warn("No platform detected, defaulting to Firebase");
|
|
15799
|
+
return "firebase";
|
|
15359
15800
|
}
|
|
15360
|
-
function
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
|
|
15801
|
+
function findFirebaseProjectDir(envFilePath) {
|
|
15802
|
+
const currentDir = process.cwd();
|
|
15803
|
+
const resolvedPath = joinPath(currentDir, envFilePath);
|
|
15804
|
+
const envFileDir = getDirname(resolvedPath);
|
|
15805
|
+
const appsMatch = envFileDir.match(/apps[\\/]([^\\/]+)[\\/]functions/);
|
|
15806
|
+
if (appsMatch && appsMatch[1]) {
|
|
15807
|
+
const appDir = joinPath(currentDir, "apps", appsMatch[1]);
|
|
15808
|
+
if (pathExists(joinPath(appDir, "firebase.json"))) {
|
|
15809
|
+
return appDir;
|
|
15810
|
+
}
|
|
15811
|
+
}
|
|
15812
|
+
let searchDir = envFileDir;
|
|
15813
|
+
const rootDir = getDirname(currentDir);
|
|
15814
|
+
while (searchDir !== rootDir && searchDir !== getDirname(searchDir)) {
|
|
15815
|
+
if (pathExists(joinPath(searchDir, "firebase.json"))) {
|
|
15816
|
+
return searchDir;
|
|
15817
|
+
}
|
|
15818
|
+
const parentDir = getDirname(searchDir);
|
|
15819
|
+
if (parentDir === searchDir) break;
|
|
15820
|
+
searchDir = parentDir;
|
|
15365
15821
|
}
|
|
15822
|
+
return currentDir;
|
|
15366
15823
|
}
|
|
15367
|
-
function
|
|
15368
|
-
const
|
|
15369
|
-
|
|
15370
|
-
|
|
15371
|
-
|
|
15372
|
-
"
|
|
15373
|
-
|
|
15374
|
-
|
|
15375
|
-
|
|
15376
|
-
|
|
15377
|
-
|
|
15824
|
+
function isFirebaseReservedKey(key) {
|
|
15825
|
+
const reservedPrefixes = ["X_GOOGLE_", "FIREBASE_", "EXT_", "GCLOUD_"];
|
|
15826
|
+
const configOnlyKeys = [
|
|
15827
|
+
"FIREBASE_REGION",
|
|
15828
|
+
"FIREBASE_PROJECT_ID",
|
|
15829
|
+
"FIREBASE_AUTH_DOMAIN"
|
|
15830
|
+
];
|
|
15831
|
+
if (configOnlyKeys.includes(key)) {
|
|
15832
|
+
return true;
|
|
15833
|
+
}
|
|
15834
|
+
return reservedPrefixes.some((prefix) => key.startsWith(prefix));
|
|
15835
|
+
}
|
|
15836
|
+
function detectFirebaseProjectId(cwd) {
|
|
15837
|
+
const searchDir = cwd || process.cwd();
|
|
15838
|
+
const firebasercPath = joinPath(searchDir, ".firebaserc");
|
|
15839
|
+
if (pathExists(firebasercPath)) {
|
|
15840
|
+
try {
|
|
15841
|
+
const firebasercRaw = readSync(firebasercPath, { format: "json" });
|
|
15842
|
+
const firebaserc = firebasercRaw && typeof firebasercRaw === "object" ? firebasercRaw : null;
|
|
15843
|
+
if (firebaserc && firebaserc.projects?.default) {
|
|
15844
|
+
return firebaserc.projects.default;
|
|
15378
15845
|
}
|
|
15846
|
+
} catch {
|
|
15379
15847
|
}
|
|
15380
|
-
|
|
15848
|
+
}
|
|
15849
|
+
let current = searchDir;
|
|
15850
|
+
const root = getDirname(current);
|
|
15851
|
+
while (current !== root) {
|
|
15852
|
+
const firebasercPath2 = joinPath(current, ".firebaserc");
|
|
15853
|
+
if (pathExists(firebasercPath2)) {
|
|
15854
|
+
try {
|
|
15855
|
+
const firebasercRaw = readSync(firebasercPath2, { format: "json" });
|
|
15856
|
+
const firebaserc = firebasercRaw && typeof firebasercRaw === "object" ? firebasercRaw : null;
|
|
15857
|
+
if (firebaserc && firebaserc.projects?.default) {
|
|
15858
|
+
return firebaserc.projects.default;
|
|
15859
|
+
}
|
|
15860
|
+
} catch {
|
|
15861
|
+
}
|
|
15862
|
+
}
|
|
15863
|
+
const parent = getDirname(current);
|
|
15864
|
+
if (parent === current) break;
|
|
15865
|
+
current = parent;
|
|
15866
|
+
}
|
|
15867
|
+
return void 0;
|
|
15381
15868
|
}
|
|
15382
|
-
function
|
|
15383
|
-
|
|
15384
|
-
|
|
15869
|
+
function detectAppsWithFunctions() {
|
|
15870
|
+
const currentDir = process.cwd();
|
|
15871
|
+
const appsDir = joinPath(currentDir, "apps");
|
|
15872
|
+
if (!pathExists(appsDir)) {
|
|
15873
|
+
return [];
|
|
15385
15874
|
}
|
|
15875
|
+
const apps = [];
|
|
15386
15876
|
try {
|
|
15387
|
-
|
|
15388
|
-
|
|
15389
|
-
|
|
15390
|
-
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
|
|
15394
|
-
|
|
15395
|
-
|
|
15877
|
+
const appsList = readdirSync2(appsDir).filter((item) => {
|
|
15878
|
+
const itemPath = joinPath(appsDir, item);
|
|
15879
|
+
const stat = statSync2(itemPath);
|
|
15880
|
+
return stat?.isDirectory() === true;
|
|
15881
|
+
});
|
|
15882
|
+
for (const app of appsList) {
|
|
15883
|
+
const functionsEnvFile = joinPath(appsDir, app, "functions", ".env");
|
|
15884
|
+
if (pathExists(functionsEnvFile)) {
|
|
15885
|
+
apps.push({
|
|
15886
|
+
name: app,
|
|
15887
|
+
dir: joinPath(appsDir, app),
|
|
15888
|
+
envFile: functionsEnvFile
|
|
15889
|
+
});
|
|
15890
|
+
}
|
|
15396
15891
|
}
|
|
15397
|
-
|
|
15892
|
+
} catch {
|
|
15398
15893
|
}
|
|
15894
|
+
return apps.sort((a, b3) => a.name.localeCompare(b3.name));
|
|
15399
15895
|
}
|
|
15400
|
-
|
|
15401
|
-
|
|
15402
|
-
|
|
15403
|
-
|
|
15404
|
-
|
|
15405
|
-
|
|
15406
|
-
|
|
15407
|
-
|
|
15408
|
-
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
|
|
15412
|
-
|
|
15413
|
-
|
|
15414
|
-
|
|
15415
|
-
|
|
15416
|
-
return
|
|
15896
|
+
async function setFirebaseSecret(key, value, projectId, dryRun = false, cwd) {
|
|
15897
|
+
if (isFirebaseReservedKey(key)) {
|
|
15898
|
+
const configOnlyKeys = [
|
|
15899
|
+
"FIREBASE_REGION",
|
|
15900
|
+
"FIREBASE_PROJECT_ID",
|
|
15901
|
+
"FIREBASE_AUTH_DOMAIN"
|
|
15902
|
+
];
|
|
15903
|
+
if (configOnlyKeys.includes(key)) {
|
|
15904
|
+
log.debug(
|
|
15905
|
+
`\u23ED\uFE0F Skipping ${key}: Configuration variable (stays in .env, not synced as secret)`
|
|
15906
|
+
);
|
|
15907
|
+
} else {
|
|
15908
|
+
log.warn(
|
|
15909
|
+
`\u23ED\uFE0F Skipping ${key}: Reserved Firebase prefix (X_GOOGLE_, FIREBASE_, EXT_, GCLOUD_)`
|
|
15910
|
+
);
|
|
15911
|
+
}
|
|
15912
|
+
return;
|
|
15913
|
+
}
|
|
15914
|
+
const finalProjectId = projectId || detectFirebaseProjectId(cwd);
|
|
15915
|
+
if (dryRun) {
|
|
15916
|
+
log.info(
|
|
15917
|
+
`[DRY RUN] Would set Firebase secret: ${key}${finalProjectId ? ` (project: ${finalProjectId})` : ""}`
|
|
15918
|
+
);
|
|
15919
|
+
return;
|
|
15920
|
+
}
|
|
15921
|
+
if (!finalProjectId) {
|
|
15922
|
+
throw new DoNotDevError(
|
|
15923
|
+
`Firebase project ID required for secret '${key}'. Use --project <project_id> or ensure .firebaserc exists with a default project.`,
|
|
15924
|
+
"missing-project-id",
|
|
15925
|
+
{
|
|
15926
|
+
context: {
|
|
15927
|
+
platform: "firebase",
|
|
15928
|
+
key,
|
|
15929
|
+
cwd: cwd || process.cwd(),
|
|
15930
|
+
suggestion: "Run: dn sync-secrets --project <project_id> or create .firebaserc with default project"
|
|
15931
|
+
}
|
|
15932
|
+
}
|
|
15933
|
+
);
|
|
15417
15934
|
}
|
|
15418
15935
|
try {
|
|
15419
|
-
|
|
15420
|
-
|
|
15421
|
-
|
|
15422
|
-
|
|
15423
|
-
|
|
15424
|
-
|
|
15425
|
-
|
|
15426
|
-
|
|
15427
|
-
|
|
15428
|
-
|
|
15429
|
-
|
|
15430
|
-
|
|
15431
|
-
|
|
15432
|
-
|
|
15433
|
-
|
|
15434
|
-
|
|
15435
|
-
|
|
15436
|
-
|
|
15437
|
-
|
|
15438
|
-
|
|
15439
|
-
|
|
15440
|
-
|
|
15441
|
-
|
|
15442
|
-
|
|
15443
|
-
|
|
15444
|
-
|
|
15936
|
+
log.info(`Setting Firebase secret: ${key} (project: ${finalProjectId})`);
|
|
15937
|
+
const tempDir = joinPath(process.cwd(), ".tmp");
|
|
15938
|
+
await ensureDir(tempDir);
|
|
15939
|
+
const tempFile = joinPath(tempDir, `secret-${key}-${Date.now()}.txt`);
|
|
15940
|
+
await write(tempFile, value);
|
|
15941
|
+
try {
|
|
15942
|
+
const args = ["functions:secrets:set", key, "--project", finalProjectId];
|
|
15943
|
+
const firebaseCmd = process.platform === "win32" ? "firebase.cmd" : "firebase";
|
|
15944
|
+
const deployEnv = {
|
|
15945
|
+
...process.env,
|
|
15946
|
+
NODE_OPTIONS: ""
|
|
15947
|
+
// Clear to avoid conflicts
|
|
15948
|
+
};
|
|
15949
|
+
const result = spawnSync6(firebaseCmd, args, {
|
|
15950
|
+
input: value,
|
|
15951
|
+
encoding: "utf8",
|
|
15952
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
15953
|
+
env: deployEnv,
|
|
15954
|
+
cwd: cwd || process.cwd(),
|
|
15955
|
+
shell: false
|
|
15956
|
+
});
|
|
15957
|
+
if (result.error) {
|
|
15958
|
+
throw result.error;
|
|
15959
|
+
}
|
|
15960
|
+
if (result.status !== 0) {
|
|
15961
|
+
const stderr = result.stderr?.toString() || "";
|
|
15962
|
+
const stdout = result.stdout?.toString() || "";
|
|
15963
|
+
const errorOutput = stderr || stdout;
|
|
15964
|
+
const errorLower = errorOutput.toLowerCase();
|
|
15965
|
+
if (errorLower.includes("not logged in") || errorLower.includes("authentication required") || errorLower.includes("please login") || errorLower.includes("run firebase login") || errorLower.includes("unauthorized") || errorOutput.includes("401")) {
|
|
15966
|
+
throw new DoNotDevError(
|
|
15967
|
+
`Firebase CLI authentication required. Please run 'firebase login' to authenticate.`,
|
|
15968
|
+
"permission-denied",
|
|
15445
15969
|
{
|
|
15446
|
-
|
|
15447
|
-
|
|
15970
|
+
context: {
|
|
15971
|
+
platform: "firebase",
|
|
15972
|
+
key,
|
|
15973
|
+
error: errorOutput,
|
|
15974
|
+
suggestion: "Run: firebase login"
|
|
15975
|
+
}
|
|
15976
|
+
}
|
|
15977
|
+
);
|
|
15978
|
+
}
|
|
15979
|
+
if (errorOutput.includes("403") || errorOutput.includes("Permission denied") || errorOutput.includes("secretmanager.secrets.get") || errorOutput.includes("secretmanager.secrets.create")) {
|
|
15980
|
+
const permissionError = new DoNotDevError(
|
|
15981
|
+
`Permission denied: Missing required Google Cloud permissions for secret '${key}'. Required permissions: secretmanager.secrets.get, secretmanager.secrets.create, secretmanager.versions.add. Ensure you're logged in with 'firebase login' and your account has the 'Secret Manager Admin' role.`,
|
|
15982
|
+
"permission-denied",
|
|
15983
|
+
{
|
|
15984
|
+
context: {
|
|
15985
|
+
platform: "firebase",
|
|
15986
|
+
key,
|
|
15987
|
+
error: errorOutput,
|
|
15988
|
+
suggestion: 'Run: firebase login && gcloud projects add-iam-policy-binding donotdevelopp --member="user:YOUR_EMAIL" --role="roles/secretmanager.admin"'
|
|
15989
|
+
}
|
|
15990
|
+
}
|
|
15991
|
+
);
|
|
15992
|
+
throw permissionError;
|
|
15993
|
+
}
|
|
15994
|
+
if (errorOutput.includes("No currently active project")) {
|
|
15995
|
+
throw new DoNotDevError(
|
|
15996
|
+
`Firebase CLI exited with code ${result.status}: No active project. Please specify the project using '--project <project_id>' or set an active project with 'firebase use --add'.`,
|
|
15997
|
+
"firebase-cli-error",
|
|
15998
|
+
{
|
|
15999
|
+
context: {
|
|
16000
|
+
platform: "firebase",
|
|
16001
|
+
key,
|
|
16002
|
+
error: errorOutput,
|
|
16003
|
+
suggestion: `Run 'dn sync-secrets --project ${finalProjectId}' or 'firebase use --add ${finalProjectId}'`
|
|
16004
|
+
}
|
|
15448
16005
|
}
|
|
15449
16006
|
);
|
|
15450
|
-
|
|
15451
|
-
|
|
15452
|
-
|
|
16007
|
+
}
|
|
16008
|
+
throw new Error(
|
|
16009
|
+
`Firebase CLI exited with code ${result.status}
|
|
16010
|
+
${errorOutput}`
|
|
16011
|
+
);
|
|
16012
|
+
}
|
|
16013
|
+
log.success(`Firebase secret ${key} set successfully`);
|
|
16014
|
+
} finally {
|
|
16015
|
+
if (pathExists(tempFile)) {
|
|
16016
|
+
await remove(tempFile);
|
|
16017
|
+
}
|
|
16018
|
+
}
|
|
16019
|
+
} catch (err) {
|
|
16020
|
+
log.error(`Failed to set Firebase secret ${key}:`, err);
|
|
16021
|
+
throw new DoNotDevError(
|
|
16022
|
+
`Failed to set Firebase secret ${key}`,
|
|
16023
|
+
"command-failed",
|
|
16024
|
+
{
|
|
16025
|
+
context: {
|
|
16026
|
+
platform: "firebase",
|
|
16027
|
+
key,
|
|
16028
|
+
error: err instanceof Error ? err.message : String(err)
|
|
16029
|
+
}
|
|
16030
|
+
}
|
|
16031
|
+
);
|
|
16032
|
+
}
|
|
16033
|
+
}
|
|
16034
|
+
function setVercelSecret(key, value, projectId, dryRun = false) {
|
|
16035
|
+
if (dryRun) {
|
|
16036
|
+
log.info(`[DRY RUN] Would set Vercel environment variable: ${key}`);
|
|
16037
|
+
return;
|
|
16038
|
+
}
|
|
16039
|
+
try {
|
|
16040
|
+
log.info(`Setting Vercel environment variable: ${key}`);
|
|
16041
|
+
const args = ["env", "add", key];
|
|
16042
|
+
if (projectId) {
|
|
16043
|
+
args.push("--project", projectId);
|
|
16044
|
+
}
|
|
16045
|
+
const result = spawnSync6("vercel", args, {
|
|
16046
|
+
input: value,
|
|
16047
|
+
encoding: "utf8",
|
|
16048
|
+
stdio: ["pipe", "inherit", "inherit"]
|
|
16049
|
+
});
|
|
16050
|
+
if (result.error) {
|
|
16051
|
+
throw result.error;
|
|
16052
|
+
}
|
|
16053
|
+
if (result.status !== 0) {
|
|
16054
|
+
throw new Error(`Vercel CLI exited with code ${result.status}`);
|
|
16055
|
+
}
|
|
16056
|
+
log.success(`Vercel environment variable ${key} set successfully`);
|
|
16057
|
+
} catch (err) {
|
|
16058
|
+
log.error(`Failed to set Vercel environment variable ${key}:`, err);
|
|
16059
|
+
throw new DoNotDevError(
|
|
16060
|
+
`Failed to set Vercel environment variable ${key}`,
|
|
16061
|
+
"command-failed",
|
|
16062
|
+
{
|
|
16063
|
+
context: {
|
|
16064
|
+
platform: "vercel",
|
|
16065
|
+
key,
|
|
16066
|
+
error: err instanceof Error ? err.message : String(err)
|
|
16067
|
+
}
|
|
16068
|
+
}
|
|
16069
|
+
);
|
|
16070
|
+
}
|
|
16071
|
+
}
|
|
16072
|
+
function detectGitHubRepo() {
|
|
16073
|
+
try {
|
|
16074
|
+
const result = spawnSync6("git", ["remote", "get-url", "origin"], {
|
|
16075
|
+
encoding: "utf8",
|
|
16076
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
16077
|
+
});
|
|
16078
|
+
if (result.status !== 0 || !result.stdout) return void 0;
|
|
16079
|
+
const url = result.stdout.trim();
|
|
16080
|
+
const sshMatch = url.match(/github\.com[:/]([^/]+\/[^/.]+)/);
|
|
16081
|
+
if (sshMatch) return sshMatch[1];
|
|
16082
|
+
const httpsMatch = url.match(/github\.com\/([^/]+\/[^/.]+)/);
|
|
16083
|
+
if (httpsMatch) return httpsMatch[1];
|
|
16084
|
+
return void 0;
|
|
16085
|
+
} catch {
|
|
16086
|
+
return void 0;
|
|
16087
|
+
}
|
|
16088
|
+
}
|
|
16089
|
+
function setGitHubSecret(key, value, repo, dryRun = false) {
|
|
16090
|
+
if (dryRun) {
|
|
16091
|
+
log.info(
|
|
16092
|
+
`[DRY RUN] Would set GitHub secret: ${key}${repo ? ` (repo: ${repo})` : ""}`
|
|
16093
|
+
);
|
|
16094
|
+
return;
|
|
16095
|
+
}
|
|
16096
|
+
try {
|
|
16097
|
+
log.info(`Setting GitHub secret: ${key}${repo ? ` (repo: ${repo})` : ""}`);
|
|
16098
|
+
const args = ["secret", "set", key];
|
|
16099
|
+
if (repo) {
|
|
16100
|
+
args.push("--repo", repo);
|
|
16101
|
+
}
|
|
16102
|
+
const result = spawnSync6("gh", args, {
|
|
16103
|
+
input: value,
|
|
16104
|
+
encoding: "utf8",
|
|
16105
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
16106
|
+
});
|
|
16107
|
+
if (result.error) {
|
|
16108
|
+
throw result.error;
|
|
16109
|
+
}
|
|
16110
|
+
if (result.status !== 0) {
|
|
16111
|
+
const stderr = result.stderr?.toString() || "";
|
|
16112
|
+
const stdout = result.stdout?.toString() || "";
|
|
16113
|
+
const errorOutput = stderr || stdout;
|
|
16114
|
+
if (errorOutput.includes("not logged in") || errorOutput.includes("authentication")) {
|
|
16115
|
+
throw new DoNotDevError(
|
|
16116
|
+
`GitHub CLI authentication required. Run 'gh auth login' to authenticate.`,
|
|
16117
|
+
"permission-denied",
|
|
16118
|
+
{ context: { key, error: errorOutput } }
|
|
16119
|
+
);
|
|
16120
|
+
}
|
|
16121
|
+
throw new Error(
|
|
16122
|
+
`gh CLI exited with code ${result.status}
|
|
16123
|
+
${errorOutput}`
|
|
16124
|
+
);
|
|
16125
|
+
}
|
|
16126
|
+
log.success(`GitHub secret ${key} set successfully`);
|
|
16127
|
+
} catch (err) {
|
|
16128
|
+
if (err instanceof DoNotDevError) throw err;
|
|
16129
|
+
log.error(`Failed to set GitHub secret ${key}:`, err);
|
|
16130
|
+
throw new DoNotDevError(
|
|
16131
|
+
`Failed to set GitHub secret ${key}`,
|
|
16132
|
+
"command-failed",
|
|
16133
|
+
{
|
|
16134
|
+
context: {
|
|
16135
|
+
target: "github",
|
|
16136
|
+
key,
|
|
16137
|
+
error: err instanceof Error ? err.message : String(err)
|
|
16138
|
+
}
|
|
16139
|
+
}
|
|
16140
|
+
);
|
|
16141
|
+
}
|
|
16142
|
+
}
|
|
16143
|
+
function uploadServiceAccountToGitHub(appDir, repo, dryRun = false, staging = false) {
|
|
16144
|
+
const fileName = staging ? "service-account-key.staging.json" : "service-account-key.json";
|
|
16145
|
+
const secretName = staging ? "FIREBASE_SERVICE_ACCOUNT_STAGING" : "FIREBASE_SERVICE_ACCOUNT";
|
|
16146
|
+
const filePath = joinPath(appDir, fileName);
|
|
16147
|
+
if (!pathExists(filePath)) {
|
|
16148
|
+
log.debug(`No ${fileName} found in ${appDir}, skipping GitHub upload`);
|
|
16149
|
+
return;
|
|
16150
|
+
}
|
|
16151
|
+
const contentRaw = readSync(filePath, { format: "text" });
|
|
16152
|
+
const content = typeof contentRaw === "string" ? contentRaw : null;
|
|
16153
|
+
if (!content) return;
|
|
16154
|
+
const encoded = Buffer2.from(content).toString("base64");
|
|
16155
|
+
setGitHubSecret(secretName, encoded, repo, dryRun);
|
|
16156
|
+
}
|
|
16157
|
+
async function main(options = {}) {
|
|
16158
|
+
const config = {
|
|
16159
|
+
envFile: options.envFile || ".env",
|
|
16160
|
+
platform: options.platform,
|
|
16161
|
+
projectId: options.projectId,
|
|
16162
|
+
vercelProjectId: options.vercelProjectId,
|
|
16163
|
+
target: options.target || "runtime",
|
|
16164
|
+
repo: options.repo,
|
|
16165
|
+
dryRun: options.dryRun ?? false,
|
|
16166
|
+
verbose: options.verbose ?? false
|
|
16167
|
+
};
|
|
16168
|
+
if (process.argv.includes("--help")) {
|
|
16169
|
+
log.info(`
|
|
16170
|
+
Secrets Sync Tool
|
|
16171
|
+
|
|
16172
|
+
Usage: bun run sync-secrets [options]
|
|
16173
|
+
|
|
16174
|
+
Options:
|
|
16175
|
+
--env-file <path> Path to .env file (default: .env)
|
|
16176
|
+
--target <target> Sync target: 'runtime' (default) or 'github'
|
|
16177
|
+
--platform <platform> Platform to sync to: 'firebase' or 'vercel' (runtime target only, auto-detected)
|
|
16178
|
+
--project <id> Firebase project ID (for Firebase platform)
|
|
16179
|
+
--vercel-project <id> Vercel project ID (for Vercel platform)
|
|
16180
|
+
--repo <owner/repo> GitHub repository (github target only, auto-detected from git remote)
|
|
16181
|
+
--dry-run, --dry Show what would be done without actually setting secrets
|
|
16182
|
+
--verbose Show detailed output
|
|
16183
|
+
--help Show this help message
|
|
16184
|
+
|
|
16185
|
+
Examples:
|
|
16186
|
+
dndev sync-secrets # sync to Firebase/Vercel runtime
|
|
16187
|
+
dndev sync-secrets --target github # sync to GitHub Secrets
|
|
16188
|
+
dndev sync-secrets --target github --repo owner/repo # sync to specific repo
|
|
16189
|
+
dndev sync-secrets --platform=firebase
|
|
16190
|
+
dndev sync-secrets --dry-run
|
|
16191
|
+
dndev sync-secrets --env-file .env.production --project my-project
|
|
16192
|
+
`);
|
|
16193
|
+
return 0;
|
|
16194
|
+
}
|
|
16195
|
+
try {
|
|
16196
|
+
const currentDir = process.cwd();
|
|
16197
|
+
let firebaseProjectDir;
|
|
16198
|
+
if (!options.envFile || config.envFile === ".env") {
|
|
16199
|
+
const appsWithFunctions = detectAppsWithFunctions();
|
|
16200
|
+
if (appsWithFunctions.length > 0) {
|
|
16201
|
+
let selectedApp;
|
|
16202
|
+
if (appsWithFunctions.length === 1) {
|
|
16203
|
+
selectedApp = appsWithFunctions[0];
|
|
16204
|
+
Me(`Using app: ${selectedApp.name}`, "Auto-detected");
|
|
16205
|
+
} else {
|
|
16206
|
+
const appName = await askForSelection(
|
|
16207
|
+
"Which app would you like to sync secrets for?",
|
|
16208
|
+
appsWithFunctions.map((app) => ({
|
|
16209
|
+
title: app.name,
|
|
16210
|
+
value: app.name,
|
|
16211
|
+
hint: `functions/.env in ${app.name}`
|
|
16212
|
+
}))
|
|
16213
|
+
);
|
|
16214
|
+
selectedApp = appsWithFunctions.find((a) => a.name === appName);
|
|
16215
|
+
}
|
|
16216
|
+
if (selectedApp) {
|
|
16217
|
+
const envFileAbsolute = normalizePath(selectedApp.envFile);
|
|
16218
|
+
const envFileRelative = getRelativePathBetween(
|
|
16219
|
+
currentDir,
|
|
16220
|
+
envFileAbsolute
|
|
16221
|
+
);
|
|
16222
|
+
config.envFile = envFileRelative;
|
|
16223
|
+
firebaseProjectDir = selectedApp.dir;
|
|
16224
|
+
if (!config.projectId) {
|
|
16225
|
+
config.projectId = detectFirebaseProjectId(selectedApp.dir);
|
|
15453
16226
|
}
|
|
15454
|
-
} catch {
|
|
15455
|
-
continue;
|
|
15456
16227
|
}
|
|
15457
16228
|
}
|
|
15458
|
-
|
|
15459
|
-
|
|
15460
|
-
|
|
15461
|
-
|
|
15462
|
-
|
|
15463
|
-
|
|
15464
|
-
|
|
15465
|
-
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
|
|
16229
|
+
}
|
|
16230
|
+
const envFilePath = joinPath(currentDir, config.envFile);
|
|
16231
|
+
const syncTarget = config.target || "runtime";
|
|
16232
|
+
if (syncTarget === "github") {
|
|
16233
|
+
const repo = config.repo || detectGitHubRepo();
|
|
16234
|
+
if (!repo) {
|
|
16235
|
+
log.error(
|
|
16236
|
+
"Could not detect GitHub repository. Use --repo owner/repo or ensure a GitHub remote is configured."
|
|
16237
|
+
);
|
|
16238
|
+
return 1;
|
|
16239
|
+
}
|
|
16240
|
+
log.info(`Syncing secrets to GitHub repository: ${repo}`);
|
|
16241
|
+
const secrets2 = parseEnvFile(envFilePath);
|
|
16242
|
+
const secretKeys2 = Object.keys(secrets2);
|
|
16243
|
+
if (secretKeys2.length === 0) {
|
|
16244
|
+
log.info("No secrets found in .env file");
|
|
16245
|
+
} else {
|
|
16246
|
+
log.info(`Found ${secretKeys2.length} secrets to sync to GitHub:`);
|
|
16247
|
+
secretKeys2.forEach((key) => log.info(` ${key}`));
|
|
16248
|
+
for (const [key, value] of Object.entries(secrets2)) {
|
|
16249
|
+
setGitHubSecret(key, value, repo, config.dryRun);
|
|
16250
|
+
}
|
|
16251
|
+
}
|
|
16252
|
+
if (firebaseProjectDir) {
|
|
16253
|
+
uploadServiceAccountToGitHub(
|
|
16254
|
+
firebaseProjectDir,
|
|
16255
|
+
repo,
|
|
16256
|
+
config.dryRun,
|
|
16257
|
+
false
|
|
16258
|
+
);
|
|
16259
|
+
uploadServiceAccountToGitHub(
|
|
16260
|
+
firebaseProjectDir,
|
|
16261
|
+
repo,
|
|
16262
|
+
config.dryRun,
|
|
16263
|
+
true
|
|
16264
|
+
);
|
|
16265
|
+
} else {
|
|
16266
|
+
uploadServiceAccountToGitHub(currentDir, repo, config.dryRun, false);
|
|
16267
|
+
uploadServiceAccountToGitHub(currentDir, repo, config.dryRun, true);
|
|
16268
|
+
}
|
|
16269
|
+
if (config.dryRun) {
|
|
16270
|
+
log.success("\nDry run completed. No GitHub secrets were set.");
|
|
16271
|
+
} else {
|
|
16272
|
+
log.success("\nAll secrets synced to GitHub successfully!");
|
|
16273
|
+
}
|
|
16274
|
+
return 0;
|
|
16275
|
+
}
|
|
16276
|
+
const platform = config.platform || detectPlatform();
|
|
16277
|
+
log.info(`Reading secrets from: ${envFilePath}`);
|
|
16278
|
+
if (config.verbose) {
|
|
16279
|
+
log.debug(`Working directory: ${currentDir}`);
|
|
16280
|
+
log.debug(`Environment file: ${envFilePath}`);
|
|
16281
|
+
log.debug(`Platform: ${platform}`);
|
|
16282
|
+
log.debug(`Dry run mode: ${config.dryRun}`);
|
|
16283
|
+
}
|
|
16284
|
+
const secrets = parseEnvFile(envFilePath);
|
|
16285
|
+
const secretKeys = Object.keys(secrets);
|
|
16286
|
+
if (secretKeys.length === 0) {
|
|
16287
|
+
log.info("No secrets found in .env file");
|
|
16288
|
+
return 0;
|
|
16289
|
+
}
|
|
16290
|
+
log.info(`Found ${secretKeys.length} secrets to sync to ${platform}:`);
|
|
16291
|
+
secretKeys.forEach((key) => {
|
|
16292
|
+
if (config.verbose) {
|
|
16293
|
+
const value = secrets[key];
|
|
16294
|
+
log.debug(
|
|
16295
|
+
` ${key}: ${value ? value.substring(0, 10) + "..." : "undefined"}`
|
|
16296
|
+
);
|
|
16297
|
+
} else {
|
|
16298
|
+
log.info(` ${key}`);
|
|
16299
|
+
}
|
|
16300
|
+
});
|
|
16301
|
+
const finalFirebaseProjectDir = platform === "firebase" ? firebaseProjectDir || findFirebaseProjectDir(config.envFile) : void 0;
|
|
16302
|
+
if (config.verbose && finalFirebaseProjectDir) {
|
|
16303
|
+
log.debug(`Firebase project directory: ${finalFirebaseProjectDir}`);
|
|
16304
|
+
}
|
|
16305
|
+
for (const [key, value] of Object.entries(secrets)) {
|
|
16306
|
+
if (platform === "firebase") {
|
|
16307
|
+
await setFirebaseSecret(
|
|
16308
|
+
key,
|
|
16309
|
+
value,
|
|
16310
|
+
config.projectId,
|
|
16311
|
+
config.dryRun,
|
|
16312
|
+
finalFirebaseProjectDir
|
|
16313
|
+
);
|
|
16314
|
+
} else if (platform === "vercel") {
|
|
16315
|
+
setVercelSecret(key, value, config.vercelProjectId, config.dryRun);
|
|
16316
|
+
}
|
|
16317
|
+
}
|
|
16318
|
+
if (config.dryRun) {
|
|
16319
|
+
log.success(
|
|
16320
|
+
`
|
|
16321
|
+
Dry run completed. No secrets were actually set to ${platform}.`
|
|
15471
16322
|
);
|
|
15472
16323
|
} else {
|
|
15473
|
-
|
|
15474
|
-
|
|
15475
|
-
shell: true
|
|
15476
|
-
});
|
|
16324
|
+
log.success(`
|
|
16325
|
+
All secrets synced successfully to ${platform}!`);
|
|
15477
16326
|
}
|
|
15478
|
-
|
|
15479
|
-
cliCache.set(cacheKey, isAvailable);
|
|
15480
|
-
return isAvailable;
|
|
16327
|
+
return 0;
|
|
15481
16328
|
} catch (err) {
|
|
15482
|
-
|
|
15483
|
-
|
|
16329
|
+
log.error(
|
|
16330
|
+
"Error syncing secrets:",
|
|
16331
|
+
DoNotDevError.from(err, "Sync-secrets failed", "cli-execution-error")
|
|
16332
|
+
);
|
|
16333
|
+
return 1;
|
|
15484
16334
|
}
|
|
15485
16335
|
}
|
|
15486
|
-
|
|
15487
|
-
|
|
15488
|
-
|
|
15489
|
-
|
|
15490
|
-
|
|
15491
|
-
|
|
15492
|
-
|
|
15493
|
-
|
|
15494
|
-
|
|
15495
|
-
|
|
15496
|
-
|
|
15497
|
-
}
|
|
15498
|
-
|
|
15499
|
-
|
|
15500
|
-
}
|
|
15501
|
-
|
|
15502
|
-
|
|
16336
|
+
var sync_secrets_default;
|
|
16337
|
+
var init_sync_secrets = __esm({
|
|
16338
|
+
"packages/tooling/src/apps/sync-secrets.ts"() {
|
|
16339
|
+
"use strict";
|
|
16340
|
+
init_utils();
|
|
16341
|
+
init_cli_input();
|
|
16342
|
+
init_cli_output();
|
|
16343
|
+
init_errors();
|
|
16344
|
+
init_pathResolver();
|
|
16345
|
+
sync_secrets_default = main;
|
|
16346
|
+
}
|
|
16347
|
+
});
|
|
16348
|
+
|
|
16349
|
+
// packages/tooling/src/apps/deploy-supabase-functions.ts
|
|
16350
|
+
var deploy_supabase_functions_exports = {};
|
|
16351
|
+
__export(deploy_supabase_functions_exports, {
|
|
16352
|
+
deploySupabaseFunctions: () => deploySupabaseFunctions
|
|
16353
|
+
});
|
|
16354
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
16355
|
+
async function deploySupabaseFunctions(appDir, config) {
|
|
16356
|
+
const supabaseDir = joinPath(appDir, "supabase");
|
|
16357
|
+
const functionsDir = joinPath(supabaseDir, "functions");
|
|
16358
|
+
if (!pathExists(functionsDir)) {
|
|
16359
|
+
log.warn("No supabase/functions/ directory found. Skipping Supabase functions deployment.");
|
|
16360
|
+
return;
|
|
16361
|
+
}
|
|
16362
|
+
requireCLI(
|
|
16363
|
+
CLI_TOOLS.SUPABASE,
|
|
16364
|
+
"Supabase CLI is required to deploy Edge Functions.\nInstall: https://supabase.com/docs/guides/cli"
|
|
16365
|
+
);
|
|
16366
|
+
const functionDirs = readdirSync2(functionsDir).filter((item) => {
|
|
16367
|
+
const itemPath = joinPath(functionsDir, item);
|
|
16368
|
+
const stat = statSync2(itemPath);
|
|
16369
|
+
return stat?.isDirectory() === true;
|
|
16370
|
+
}).filter((dir) => {
|
|
16371
|
+
const indexPath = joinPath(functionsDir, dir, "index.ts");
|
|
16372
|
+
return pathExists(indexPath);
|
|
16373
|
+
});
|
|
16374
|
+
if (functionDirs.length === 0) {
|
|
16375
|
+
log.warn("No Edge Functions found in supabase/functions/. Skipping deployment.");
|
|
16376
|
+
return;
|
|
16377
|
+
}
|
|
16378
|
+
log.info(`Found ${functionDirs.length} Edge Function(s): ${functionDirs.join(", ")}`);
|
|
16379
|
+
const s = Y2();
|
|
16380
|
+
for (const functionName of functionDirs) {
|
|
16381
|
+
s.start(`Deploying Edge Function: ${functionName}...`);
|
|
16382
|
+
try {
|
|
16383
|
+
execSync3(`supabase functions deploy ${functionName}`, {
|
|
16384
|
+
cwd: supabaseDir,
|
|
16385
|
+
stdio: config.verbose ? "inherit" : "pipe",
|
|
16386
|
+
env: {
|
|
16387
|
+
...process.env,
|
|
16388
|
+
DNDEV_APP_ROOT: appDir
|
|
16389
|
+
}
|
|
16390
|
+
});
|
|
16391
|
+
s.stop(`Deployed: ${functionName}`);
|
|
16392
|
+
} catch (error2) {
|
|
16393
|
+
s.stop(`Failed to deploy: ${functionName}`);
|
|
16394
|
+
throw new DoNotDevError(
|
|
16395
|
+
`Failed to deploy Supabase Edge Function: ${functionName}`,
|
|
16396
|
+
"deployment-failed",
|
|
16397
|
+
{ context: { functionName, error: error2 instanceof Error ? error2.message : String(error2) } }
|
|
16398
|
+
);
|
|
16399
|
+
}
|
|
16400
|
+
}
|
|
16401
|
+
log.success(`Successfully deployed ${functionDirs.length} Edge Function(s)`);
|
|
15503
16402
|
}
|
|
15504
|
-
|
|
15505
|
-
|
|
16403
|
+
var init_deploy_supabase_functions = __esm({
|
|
16404
|
+
"packages/tooling/src/apps/deploy-supabase-functions.ts"() {
|
|
16405
|
+
"use strict";
|
|
16406
|
+
init_utils();
|
|
16407
|
+
init_pathResolver();
|
|
16408
|
+
init_cli_output();
|
|
16409
|
+
init_cli_tools();
|
|
16410
|
+
init_errors();
|
|
16411
|
+
}
|
|
16412
|
+
});
|
|
16413
|
+
|
|
16414
|
+
// packages/cli/src/bin/commands/deploy.ts
|
|
16415
|
+
init_utils();
|
|
16416
|
+
|
|
16417
|
+
// packages/tooling/src/index.ts
|
|
16418
|
+
init_utils();
|
|
16419
|
+
|
|
16420
|
+
// packages/tooling/src/cli/index.ts
|
|
16421
|
+
init_utils();
|
|
16422
|
+
|
|
16423
|
+
// packages/tooling/src/utils/spawn-utils.ts
|
|
16424
|
+
init_utils();
|
|
16425
|
+
init_cli_output();
|
|
16426
|
+
init_errors();
|
|
16427
|
+
init_pathResolver();
|
|
16428
|
+
import { spawnSync, execSync } from "node:child_process";
|
|
16429
|
+
function clearConflictingAuth(env) {
|
|
16430
|
+
const cleaned = { ...env };
|
|
16431
|
+
delete cleaned.FIREBASE_TOKEN;
|
|
16432
|
+
delete cleaned.FIREBASE_AUTH_TOKEN;
|
|
16433
|
+
return cleaned;
|
|
15506
16434
|
}
|
|
15507
|
-
function
|
|
15508
|
-
return
|
|
16435
|
+
function createDeployEnv(serviceAccountPath) {
|
|
16436
|
+
return clearConflictingAuth({
|
|
16437
|
+
...process.env,
|
|
16438
|
+
GOOGLE_APPLICATION_CREDENTIALS: serviceAccountPath,
|
|
16439
|
+
NODE_OPTIONS: "",
|
|
16440
|
+
NODE_ENV: "production"
|
|
16441
|
+
});
|
|
15509
16442
|
}
|
|
15510
|
-
function
|
|
15511
|
-
const
|
|
15512
|
-
const
|
|
15513
|
-
|
|
15514
|
-
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15522
|
-
|
|
15523
|
-
|
|
15524
|
-
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
|
|
15530
|
-
|
|
15531
|
-
],
|
|
15532
|
-
darwin: [
|
|
15533
|
-
"Install Node.js from: https://nodejs.org",
|
|
15534
|
-
"Or: brew install node",
|
|
15535
|
-
"npm comes bundled with Node.js"
|
|
15536
|
-
],
|
|
15537
|
-
linux: [
|
|
15538
|
-
"Install Node.js from: https://nodejs.org",
|
|
15539
|
-
"Or use your package manager: dnf install nodejs, apt install nodejs, etc.",
|
|
15540
|
-
"npm comes bundled with Node.js"
|
|
15541
|
-
]
|
|
15542
|
-
},
|
|
15543
|
-
yarn: {
|
|
15544
|
-
win32: ["npm install -g yarn", "Or: choco install yarn"],
|
|
15545
|
-
darwin: ["npm install -g yarn", "Or: brew install yarn"],
|
|
15546
|
-
linux: ["npm install -g yarn", "Or check your package manager"]
|
|
15547
|
-
},
|
|
15548
|
-
firebase: {
|
|
15549
|
-
win32: [
|
|
15550
|
-
"npm install -g firebase-tools",
|
|
15551
|
-
"Or download standalone binary: https://firebase.google.com/docs/cli#windows-standalone-binary"
|
|
15552
|
-
],
|
|
15553
|
-
darwin: [
|
|
15554
|
-
"npm install -g firebase-tools",
|
|
15555
|
-
"Or: brew install firebase-cli"
|
|
15556
|
-
],
|
|
15557
|
-
linux: [
|
|
15558
|
-
"npm install -g firebase-tools",
|
|
15559
|
-
"Or check your package manager (dnf, apt, pacman, etc.)"
|
|
15560
|
-
]
|
|
15561
|
-
},
|
|
15562
|
-
"sentry-cli": {
|
|
15563
|
-
win32: [
|
|
15564
|
-
"npm install -g @sentry/cli",
|
|
15565
|
-
"Or download from: https://github.com/getsentry/sentry-cli/releases"
|
|
15566
|
-
],
|
|
15567
|
-
darwin: [
|
|
15568
|
-
"npm install -g @sentry/cli",
|
|
15569
|
-
"Or: brew install getsentry/tools/sentry-cli"
|
|
15570
|
-
],
|
|
15571
|
-
linux: [
|
|
15572
|
-
"npm install -g @sentry/cli",
|
|
15573
|
-
"Or: curl -sL https://sentry.io/get-cli/ | bash"
|
|
15574
|
-
]
|
|
15575
|
-
},
|
|
15576
|
-
gh: {
|
|
15577
|
-
win32: [
|
|
15578
|
-
"Download from: https://cli.github.com",
|
|
15579
|
-
"Or: winget install --id GitHub.cli",
|
|
15580
|
-
"Or: choco install gh"
|
|
15581
|
-
],
|
|
15582
|
-
darwin: ["brew install gh", "Or download from: https://cli.github.com"],
|
|
15583
|
-
linux: [
|
|
15584
|
-
"See: https://github.com/cli/cli/blob/trunk/docs/install_linux.md",
|
|
15585
|
-
"dnf install gh",
|
|
15586
|
-
"apt install gh",
|
|
15587
|
-
"Or download from: https://cli.github.com"
|
|
15588
|
-
]
|
|
15589
|
-
},
|
|
15590
|
-
git: {
|
|
15591
|
-
win32: [
|
|
15592
|
-
"Download from: https://git-scm.com/download/win",
|
|
15593
|
-
"Or: winget install --id Git.Git"
|
|
15594
|
-
],
|
|
15595
|
-
darwin: ["brew install git", "Or: xcode-select --install"],
|
|
15596
|
-
linux: [
|
|
15597
|
-
"Use your package manager: dnf install git, apt install git, etc."
|
|
15598
|
-
]
|
|
15599
|
-
},
|
|
15600
|
-
tsx: {
|
|
15601
|
-
win32: ["npm install -g tsx"],
|
|
15602
|
-
darwin: ["npm install -g tsx"],
|
|
15603
|
-
linux: ["npm install -g tsx"]
|
|
15604
|
-
}
|
|
16443
|
+
function executeFirebaseCommand(args, options) {
|
|
16444
|
+
const deployEnv = createDeployEnv(options.serviceAccountPath);
|
|
16445
|
+
const spawnOptions = {
|
|
16446
|
+
cwd: options.cwd,
|
|
16447
|
+
stdio: "inherit",
|
|
16448
|
+
env: deployEnv
|
|
16449
|
+
};
|
|
16450
|
+
let result;
|
|
16451
|
+
result = spawnSync("firebase", args, { ...spawnOptions, shell: true });
|
|
16452
|
+
if (result.error) {
|
|
16453
|
+
return {
|
|
16454
|
+
success: false,
|
|
16455
|
+
status: 1,
|
|
16456
|
+
error: result.error
|
|
16457
|
+
};
|
|
16458
|
+
}
|
|
16459
|
+
const errorOutput = result.stderr?.toString() || result.stdout?.toString() || "";
|
|
16460
|
+
return {
|
|
16461
|
+
success: result.status === 0,
|
|
16462
|
+
status: result.status ?? 1,
|
|
16463
|
+
errorOutput
|
|
15605
16464
|
};
|
|
15606
|
-
const platformInstructions = instructions[tool]?.[platform] || instructions[tool]?.linux || [];
|
|
15607
|
-
return platformInstructions.join("\n \u2022 ");
|
|
15608
16465
|
}
|
|
15609
|
-
function
|
|
15610
|
-
const
|
|
15611
|
-
|
|
15612
|
-
const
|
|
15613
|
-
if (
|
|
15614
|
-
|
|
15615
|
-
M2.message(` \u2022 ${instructions}`);
|
|
16466
|
+
function buildFirebaseDeployArgs(deployTargets, projectId, debug, force) {
|
|
16467
|
+
const targets = Array.isArray(deployTargets) ? deployTargets : [deployTargets];
|
|
16468
|
+
const targetString = targets.join(",");
|
|
16469
|
+
const args = ["deploy", "--only", targetString, "--non-interactive"];
|
|
16470
|
+
if (projectId) {
|
|
16471
|
+
args.push("--project", projectId);
|
|
15616
16472
|
}
|
|
15617
|
-
if (
|
|
15618
|
-
|
|
15619
|
-
${additionalContext}`);
|
|
16473
|
+
if (debug) {
|
|
16474
|
+
args.push("--debug");
|
|
15620
16475
|
}
|
|
15621
|
-
|
|
15622
|
-
|
|
15623
|
-
|
|
15624
|
-
|
|
15625
|
-
|
|
15626
|
-
|
|
16476
|
+
if (force) {
|
|
16477
|
+
args.push("--force");
|
|
16478
|
+
}
|
|
16479
|
+
return args;
|
|
16480
|
+
}
|
|
16481
|
+
function analyzeDeploymentError(errorOutput) {
|
|
16482
|
+
const isRetryError = errorOutput.includes("retries exhausted") || errorOutput.includes("retry");
|
|
16483
|
+
const isHashError = errorOutput.includes("hash") || errorOutput.includes("content hash");
|
|
16484
|
+
const isAuthError = errorOutput.includes("401") || errorOutput.includes("authentication") || errorOutput.includes("credentials");
|
|
16485
|
+
const tips = [];
|
|
16486
|
+
if (isRetryError || isHashError) {
|
|
16487
|
+
tips.push("Try deploying with --debug flag for more details");
|
|
16488
|
+
tips.push("Disable VPN if active - VPNs can corrupt file uploads");
|
|
16489
|
+
tips.push("Clear Firebase cache: Remove .firebase directory");
|
|
16490
|
+
tips.push(
|
|
16491
|
+
"Ensure no file watchers or antivirus are modifying files during upload"
|
|
15627
16492
|
);
|
|
15628
|
-
} else {
|
|
15629
|
-
M2.message(" \u2022 Check with: which " + tool);
|
|
15630
|
-
M2.message(" \u2022 Common locations:");
|
|
15631
|
-
M2.message(" - ~/.nvm/versions/node/*/bin/" + tool);
|
|
15632
|
-
M2.message(" - ~/.bun/bin/" + tool);
|
|
15633
|
-
M2.message(" - /usr/local/bin/" + tool);
|
|
15634
|
-
M2.message(" - /usr/bin/" + tool);
|
|
15635
16493
|
}
|
|
16494
|
+
if (isAuthError) {
|
|
16495
|
+
tips.push("Verify service account has required IAM roles");
|
|
16496
|
+
tips.push("Check service account key file is valid JSON");
|
|
16497
|
+
tips.push("Ensure FIREBASE_TOKEN and FIREBASE_AUTH_TOKEN are cleared");
|
|
16498
|
+
}
|
|
16499
|
+
return { isRetryError, isHashError, isAuthError, tips };
|
|
15636
16500
|
}
|
|
15637
|
-
function
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
|
|
15650
|
-
|
|
15651
|
-
|
|
15652
|
-
|
|
16501
|
+
function logTroubleshootingTips(tips) {
|
|
16502
|
+
if (tips.length > 0) {
|
|
16503
|
+
log.warn("\nTroubleshooting Tips:");
|
|
16504
|
+
tips.forEach((tip) => log.warn(` - ${tip}`));
|
|
16505
|
+
log.warn("");
|
|
16506
|
+
}
|
|
16507
|
+
}
|
|
16508
|
+
function handleDeploymentFailure(result, command, serviceAccountPath) {
|
|
16509
|
+
const analysis = analyzeDeploymentError(result.errorOutput || "");
|
|
16510
|
+
logTroubleshootingTips(analysis.tips);
|
|
16511
|
+
throw new DoNotDevError(
|
|
16512
|
+
`Firebase deployment failed with exit code ${result.status}`,
|
|
16513
|
+
"command-failed",
|
|
16514
|
+
{
|
|
16515
|
+
context: {
|
|
16516
|
+
command,
|
|
16517
|
+
serviceAccountPath,
|
|
16518
|
+
troubleshootingTips: analysis.tips.length > 0 ? analysis.tips : void 0
|
|
16519
|
+
}
|
|
16520
|
+
}
|
|
16521
|
+
);
|
|
16522
|
+
}
|
|
16523
|
+
function safeRemove(path, options = {}) {
|
|
16524
|
+
if (!pathExists(path)) {
|
|
16525
|
+
return false;
|
|
16526
|
+
}
|
|
16527
|
+
try {
|
|
16528
|
+
removeSync(path);
|
|
16529
|
+
if (options.logSuccess) {
|
|
16530
|
+
log.debug(options.successMessage ?? `Removed: ${path}`);
|
|
16531
|
+
}
|
|
16532
|
+
return true;
|
|
16533
|
+
} catch (error2) {
|
|
16534
|
+
const message = options.failureMessage ?? `Could not remove ${path}: ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
16535
|
+
if (options.silent !== false) {
|
|
16536
|
+
log.warn(message);
|
|
16537
|
+
}
|
|
16538
|
+
return false;
|
|
15653
16539
|
}
|
|
15654
|
-
return true;
|
|
15655
16540
|
}
|
|
15656
16541
|
|
|
16542
|
+
// packages/tooling/src/apps/deploy.ts
|
|
16543
|
+
init_utils();
|
|
16544
|
+
import { execSync as execSync4 } from "node:child_process";
|
|
16545
|
+
|
|
15657
16546
|
// packages/tooling/src/apps/deploy-frontend.ts
|
|
16547
|
+
init_utils();
|
|
15658
16548
|
async function deployFrontend(appDir, serviceAccountPath, projectId, config) {
|
|
15659
16549
|
const s = Y2();
|
|
15660
16550
|
s.start("Deploying frontend to Firebase Hosting...");
|
|
15661
|
-
const args = buildFirebaseDeployArgs("hosting", projectId, config.debug);
|
|
16551
|
+
const args = buildFirebaseDeployArgs("hosting", projectId, config.debug, config.force ?? true);
|
|
15662
16552
|
const result = executeFirebaseCommand(args, {
|
|
15663
16553
|
cwd: appDir,
|
|
15664
16554
|
serviceAccountPath,
|
|
@@ -15680,11 +16570,47 @@ async function deployFrontend(appDir, serviceAccountPath, projectId, config) {
|
|
|
15680
16570
|
s.stop("Frontend deployed successfully");
|
|
15681
16571
|
}
|
|
15682
16572
|
|
|
16573
|
+
// packages/tooling/src/apps/deploy-vercel-frontend.ts
|
|
16574
|
+
init_utils();
|
|
16575
|
+
init_cli_output();
|
|
16576
|
+
init_vercel_token();
|
|
16577
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
16578
|
+
async function deployVercelFrontend(appDir, _config) {
|
|
16579
|
+
const s = Y2();
|
|
16580
|
+
s.start("Deploying frontend to Vercel...");
|
|
16581
|
+
const token = resolveVercelToken(appDir);
|
|
16582
|
+
if (token) {
|
|
16583
|
+
log.debug("Using VERCEL_TOKEN from .env (token-based auth)");
|
|
16584
|
+
}
|
|
16585
|
+
const args = ["vercel", "--prod", "--yes"];
|
|
16586
|
+
if (token) {
|
|
16587
|
+
args.push("--token", token);
|
|
16588
|
+
}
|
|
16589
|
+
try {
|
|
16590
|
+
const result = spawnSync3("bunx", args, {
|
|
16591
|
+
cwd: appDir,
|
|
16592
|
+
stdio: "pipe",
|
|
16593
|
+
encoding: "utf-8"
|
|
16594
|
+
});
|
|
16595
|
+
if (result.status !== 0) {
|
|
16596
|
+
s.stop("Vercel deployment failed");
|
|
16597
|
+
const errOutput = result.stderr?.trim();
|
|
16598
|
+
throw new Error(errOutput || `Vercel deploy exited with code ${result.status}`);
|
|
16599
|
+
}
|
|
16600
|
+
s.stop("Frontend deployed to Vercel");
|
|
16601
|
+
} catch (err) {
|
|
16602
|
+
s.stop("Vercel deployment failed");
|
|
16603
|
+
throw err;
|
|
16604
|
+
}
|
|
16605
|
+
}
|
|
16606
|
+
|
|
15683
16607
|
// packages/tooling/src/apps/deploy-functions.ts
|
|
15684
16608
|
init_utils();
|
|
15685
16609
|
var import_yaml = __toESM(require_dist(), 1);
|
|
15686
|
-
import { execSync as execSync2, spawnSync as
|
|
16610
|
+
import { execSync as execSync2, spawnSync as spawnSync4 } from "node:child_process";
|
|
15687
16611
|
init_pathResolver();
|
|
16612
|
+
init_cli_tools();
|
|
16613
|
+
init_typed_file_operations();
|
|
15688
16614
|
function backupFiles(functionsDir) {
|
|
15689
16615
|
const backupDir = joinPath(functionsDir, ".deploy-backup");
|
|
15690
16616
|
const packageJsonPath = joinPath(functionsDir, "package.json");
|
|
@@ -15694,14 +16620,16 @@ function backupFiles(functionsDir) {
|
|
|
15694
16620
|
const backupPackageJson = joinPath(backupDir, "package.json");
|
|
15695
16621
|
const backupPackageLockJson = joinPath(backupDir, "package-lock.json");
|
|
15696
16622
|
if (pathExists(packageJsonPath)) {
|
|
15697
|
-
const
|
|
16623
|
+
const contentRaw = readSync(packageJsonPath, { format: "text" });
|
|
16624
|
+
const content = typeof contentRaw === "string" ? contentRaw : null;
|
|
15698
16625
|
if (!content) {
|
|
15699
16626
|
throw new Error(`Failed to read package.json: ${packageJsonPath}`);
|
|
15700
16627
|
}
|
|
15701
16628
|
writeSync(backupPackageJson, content, { overwrite: true });
|
|
15702
16629
|
}
|
|
15703
16630
|
if (pathExists(packageLockJsonPath)) {
|
|
15704
|
-
const
|
|
16631
|
+
const contentRaw = readSync(packageLockJsonPath, { format: "text" });
|
|
16632
|
+
const content = typeof contentRaw === "string" ? contentRaw : null;
|
|
15705
16633
|
if (!content) {
|
|
15706
16634
|
throw new Error(
|
|
15707
16635
|
`Failed to read package-lock.json: ${packageLockJsonPath}`
|
|
@@ -15728,8 +16656,8 @@ function generateCleanPackageJson(functionsDir) {
|
|
|
15728
16656
|
"file-not-found"
|
|
15729
16657
|
);
|
|
15730
16658
|
}
|
|
15731
|
-
const packageJson =
|
|
15732
|
-
if (!packageJson) {
|
|
16659
|
+
const packageJson = readPackageJson(packageJsonPath);
|
|
16660
|
+
if (!packageJson.name) {
|
|
15733
16661
|
throw new Error(
|
|
15734
16662
|
`package.json is empty or invalid JSON at ${packageJsonPath}`
|
|
15735
16663
|
);
|
|
@@ -15821,7 +16749,9 @@ function getDeployedFunctionNames(functionsDir) {
|
|
|
15821
16749
|
}
|
|
15822
16750
|
return Object.keys(parsed.endpoints);
|
|
15823
16751
|
} catch (error2) {
|
|
15824
|
-
log.warn(
|
|
16752
|
+
log.warn(
|
|
16753
|
+
`Failed to parse functions.yaml: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
16754
|
+
);
|
|
15825
16755
|
return [];
|
|
15826
16756
|
}
|
|
15827
16757
|
}
|
|
@@ -15831,18 +16761,22 @@ function updateCloudRunIAM(functionNames, projectId, region, serviceAccountPath,
|
|
|
15831
16761
|
}
|
|
15832
16762
|
if (!checkCLI("gcloud", ["--version"])) {
|
|
15833
16763
|
log.warn("gcloud CLI not found. Skipping Cloud Run IAM updates.");
|
|
15834
|
-
log.warn(
|
|
16764
|
+
log.warn(
|
|
16765
|
+
"Functions may require manual IAM configuration for CORS preflight."
|
|
16766
|
+
);
|
|
15835
16767
|
log.warn("Install gcloud: https://cloud.google.com/sdk/docs/install");
|
|
15836
16768
|
return;
|
|
15837
16769
|
}
|
|
15838
16770
|
const s = Y2();
|
|
15839
|
-
s.start(
|
|
16771
|
+
s.start(
|
|
16772
|
+
`Updating Cloud Run IAM for ${functionNames.length} functions (CORS preflight)...`
|
|
16773
|
+
);
|
|
15840
16774
|
const deployEnv = createDeployEnv(serviceAccountPath);
|
|
15841
16775
|
let successCount = 0;
|
|
15842
16776
|
let failCount = 0;
|
|
15843
16777
|
for (const funcName of functionNames) {
|
|
15844
16778
|
try {
|
|
15845
|
-
const result =
|
|
16779
|
+
const result = spawnSync4(
|
|
15846
16780
|
"gcloud",
|
|
15847
16781
|
[
|
|
15848
16782
|
"run",
|
|
@@ -15875,7 +16809,9 @@ function updateCloudRunIAM(functionNames, projectId, region, serviceAccountPath,
|
|
|
15875
16809
|
}
|
|
15876
16810
|
} catch (error2) {
|
|
15877
16811
|
failCount++;
|
|
15878
|
-
log.warn(
|
|
16812
|
+
log.warn(
|
|
16813
|
+
`Error updating IAM for ${funcName}: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
16814
|
+
);
|
|
15879
16815
|
}
|
|
15880
16816
|
}
|
|
15881
16817
|
if (successCount > 0) {
|
|
@@ -15884,7 +16820,9 @@ function updateCloudRunIAM(functionNames, projectId, region, serviceAccountPath,
|
|
|
15884
16820
|
s.stop("Cloud Run IAM update completed with errors");
|
|
15885
16821
|
}
|
|
15886
16822
|
if (failCount > 0) {
|
|
15887
|
-
log.warn(
|
|
16823
|
+
log.warn(
|
|
16824
|
+
`${failCount} function(s) failed IAM update. They may require manual configuration.`
|
|
16825
|
+
);
|
|
15888
16826
|
}
|
|
15889
16827
|
}
|
|
15890
16828
|
function getFirebaseRegion(functionsDir) {
|
|
@@ -15925,7 +16863,11 @@ async function autoSyncSecrets(functionsDir, projectId, config) {
|
|
|
15925
16863
|
const envContent = readSync(envPath, { format: "text" });
|
|
15926
16864
|
if (!envContent) return;
|
|
15927
16865
|
const reservedPrefixes = ["X_GOOGLE_", "FIREBASE_", "EXT_", "GCLOUD_"];
|
|
15928
|
-
const configOnlyKeys = [
|
|
16866
|
+
const configOnlyKeys = [
|
|
16867
|
+
"FIREBASE_REGION",
|
|
16868
|
+
"FIREBASE_PROJECT_ID",
|
|
16869
|
+
"FIREBASE_AUTH_DOMAIN"
|
|
16870
|
+
];
|
|
15929
16871
|
const secrets = [];
|
|
15930
16872
|
for (const line of envContent.split("\n")) {
|
|
15931
16873
|
const trimmed = line.trim();
|
|
@@ -15944,13 +16886,15 @@ async function autoSyncSecrets(functionsDir, projectId, config) {
|
|
|
15944
16886
|
return;
|
|
15945
16887
|
}
|
|
15946
16888
|
const s = Y2();
|
|
15947
|
-
s.start(
|
|
16889
|
+
s.start(
|
|
16890
|
+
`Syncing ${secrets.length} secret(s) to Firebase Secret Manager...`
|
|
16891
|
+
);
|
|
15948
16892
|
const firebaseCmd = process.platform === "win32" ? "firebase.cmd" : "firebase";
|
|
15949
16893
|
let successCount = 0;
|
|
15950
16894
|
let failCount = 0;
|
|
15951
16895
|
for (const { key, value } of secrets) {
|
|
15952
16896
|
try {
|
|
15953
|
-
const result =
|
|
16897
|
+
const result = spawnSync4(
|
|
15954
16898
|
firebaseCmd,
|
|
15955
16899
|
["functions:secrets:set", key, "--project", projectId],
|
|
15956
16900
|
{
|
|
@@ -15984,8 +16928,12 @@ async function autoSyncSecrets(functionsDir, projectId, config) {
|
|
|
15984
16928
|
log.warn('Could not sync secrets. Run "dn sync-secrets" manually.');
|
|
15985
16929
|
}
|
|
15986
16930
|
} catch (error2) {
|
|
15987
|
-
log.debug(
|
|
15988
|
-
|
|
16931
|
+
log.debug(
|
|
16932
|
+
`Could not sync secrets: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
16933
|
+
);
|
|
16934
|
+
log.info(
|
|
16935
|
+
'Run "dn sync-secrets" manually to sync secrets to Firebase Secret Manager.'
|
|
16936
|
+
);
|
|
15989
16937
|
}
|
|
15990
16938
|
}
|
|
15991
16939
|
async function deployFunctions(appDir, serviceAccountPath, projectId, config) {
|
|
@@ -16046,7 +16994,8 @@ To fix this, run:
|
|
|
16046
16994
|
"functions",
|
|
16047
16995
|
projectId,
|
|
16048
16996
|
config.debug,
|
|
16049
|
-
config.force
|
|
16997
|
+
config.force ?? true
|
|
16998
|
+
// Default --force for functions (auto-sets artifact cleanup policy)
|
|
16050
16999
|
);
|
|
16051
17000
|
const result = executeFirebaseCommand(args, {
|
|
16052
17001
|
cwd: firebaseProjectDir,
|
|
@@ -16070,7 +17019,13 @@ To fix this, run:
|
|
|
16070
17019
|
const functionNames = getDeployedFunctionNames(functionsDir);
|
|
16071
17020
|
if (functionNames.length > 0) {
|
|
16072
17021
|
const region = getFirebaseRegion(functionsDir);
|
|
16073
|
-
updateCloudRunIAM(
|
|
17022
|
+
updateCloudRunIAM(
|
|
17023
|
+
functionNames,
|
|
17024
|
+
projectId,
|
|
17025
|
+
region,
|
|
17026
|
+
serviceAccountPath,
|
|
17027
|
+
config.verbose ?? false
|
|
17028
|
+
);
|
|
16074
17029
|
}
|
|
16075
17030
|
await autoSyncSecrets(functionsDir, projectId, config);
|
|
16076
17031
|
} finally {
|
|
@@ -16097,7 +17052,7 @@ async function deployRules(appDir, serviceAccountPath, projectId, config, option
|
|
|
16097
17052
|
const targetNames = targets.join(", ");
|
|
16098
17053
|
const s = Y2();
|
|
16099
17054
|
s.start(`Deploying ${targetNames}...`);
|
|
16100
|
-
const args = buildFirebaseDeployArgs(targets, projectId, config.debug);
|
|
17055
|
+
const args = buildFirebaseDeployArgs(targets, projectId, config.debug, config.force ?? true);
|
|
16101
17056
|
const result = executeFirebaseCommand(args, {
|
|
16102
17057
|
cwd: appDir,
|
|
16103
17058
|
serviceAccountPath,
|
|
@@ -16121,8 +17076,10 @@ async function deployRules(appDir, serviceAccountPath, projectId, config, option
|
|
|
16121
17076
|
|
|
16122
17077
|
// packages/tooling/src/apps/deploy-utils.ts
|
|
16123
17078
|
init_utils();
|
|
16124
|
-
import { spawnSync as
|
|
17079
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
16125
17080
|
init_pathResolver();
|
|
17081
|
+
init_typed_file_operations();
|
|
17082
|
+
init_error_handling();
|
|
16126
17083
|
function detectAvailableApps() {
|
|
16127
17084
|
const currentDir = process.cwd();
|
|
16128
17085
|
const appsDir = joinPath(currentDir, "apps");
|
|
@@ -16134,8 +17091,11 @@ function detectAvailableApps() {
|
|
|
16134
17091
|
const stat = statSync2(itemPath);
|
|
16135
17092
|
return stat?.isDirectory() === true;
|
|
16136
17093
|
}).filter((app) => {
|
|
16137
|
-
const
|
|
16138
|
-
|
|
17094
|
+
const appDir = joinPath(appsDir, app);
|
|
17095
|
+
const firebaseJsonPath = joinPath(appDir, "firebase.json");
|
|
17096
|
+
const supabaseDir = joinPath(appDir, "supabase");
|
|
17097
|
+
const vercelJsonPath = joinPath(appDir, "vercel.json");
|
|
17098
|
+
return pathExists(firebaseJsonPath) || pathExists(supabaseDir) || pathExists(vercelJsonPath);
|
|
16139
17099
|
}).sort();
|
|
16140
17100
|
}
|
|
16141
17101
|
function detectAppDir(appName) {
|
|
@@ -16174,40 +17134,7 @@ function validateServiceAccount(appDir, options) {
|
|
|
16174
17134
|
};
|
|
16175
17135
|
}
|
|
16176
17136
|
try {
|
|
16177
|
-
const keyData =
|
|
16178
|
-
if (!keyData) {
|
|
16179
|
-
return {
|
|
16180
|
-
valid: false,
|
|
16181
|
-
info: {
|
|
16182
|
-
path: keyPath,
|
|
16183
|
-
projectId: "",
|
|
16184
|
-
clientEmail: "",
|
|
16185
|
-
valid: false,
|
|
16186
|
-
errors: ["Service account key file is empty or invalid JSON"]
|
|
16187
|
-
}
|
|
16188
|
-
};
|
|
16189
|
-
}
|
|
16190
|
-
if (!keyData.project_id) {
|
|
16191
|
-
errors.push("Missing required field: project_id");
|
|
16192
|
-
}
|
|
16193
|
-
if (!keyData.private_key) {
|
|
16194
|
-
errors.push("Missing required field: private_key");
|
|
16195
|
-
}
|
|
16196
|
-
if (!keyData.client_email) {
|
|
16197
|
-
errors.push("Missing required field: client_email");
|
|
16198
|
-
}
|
|
16199
|
-
if (errors.length > 0) {
|
|
16200
|
-
return {
|
|
16201
|
-
valid: false,
|
|
16202
|
-
info: {
|
|
16203
|
-
path: keyPath,
|
|
16204
|
-
projectId: keyData.project_id || "",
|
|
16205
|
-
clientEmail: keyData.client_email || "",
|
|
16206
|
-
valid: false,
|
|
16207
|
-
errors
|
|
16208
|
-
}
|
|
16209
|
-
};
|
|
16210
|
-
}
|
|
17137
|
+
const keyData = readServiceAccountKey(keyPath);
|
|
16211
17138
|
return {
|
|
16212
17139
|
valid: true,
|
|
16213
17140
|
info: {
|
|
@@ -16226,9 +17153,7 @@ function validateServiceAccount(appDir, options) {
|
|
|
16226
17153
|
projectId: "",
|
|
16227
17154
|
clientEmail: "",
|
|
16228
17155
|
valid: false,
|
|
16229
|
-
errors: [
|
|
16230
|
-
`Invalid JSON format: ${error2 instanceof DoNotDevError ? error2.message : error2 instanceof Error ? error2.message : String(error2)}`
|
|
16231
|
-
]
|
|
17156
|
+
errors: [`Invalid service account key: ${getErrorMessage(error2)}`]
|
|
16232
17157
|
}
|
|
16233
17158
|
};
|
|
16234
17159
|
}
|
|
@@ -16247,12 +17172,7 @@ function validateFirebaseJson(appDir) {
|
|
|
16247
17172
|
};
|
|
16248
17173
|
}
|
|
16249
17174
|
try {
|
|
16250
|
-
const config =
|
|
16251
|
-
if (!config) {
|
|
16252
|
-
throw new Error(
|
|
16253
|
-
`firebase.json is empty or invalid JSON at ${firebaseJsonPath}`
|
|
16254
|
-
);
|
|
16255
|
-
}
|
|
17175
|
+
const config = readFirebaseJson(firebaseJsonPath);
|
|
16256
17176
|
const hasHosting = !!config.hosting;
|
|
16257
17177
|
const hasFunctions = !!config.functions && Array.isArray(config.functions);
|
|
16258
17178
|
const hasFirestoreRules = !!config.firestore?.rules && pathExists(joinPath(appDir, config.firestore.rules));
|
|
@@ -16276,9 +17196,7 @@ function validateFirebaseJson(appDir) {
|
|
|
16276
17196
|
hasFirestoreRules: false,
|
|
16277
17197
|
hasFirestoreIndexes: false,
|
|
16278
17198
|
hasStorageRules: false,
|
|
16279
|
-
errors: [
|
|
16280
|
-
`Invalid JSON format: ${error2 instanceof DoNotDevError ? error2.message : error2 instanceof Error ? error2.message : String(error2)}`
|
|
16281
|
-
]
|
|
17199
|
+
errors: [`Invalid firebase.json: ${getErrorMessage(error2)}`]
|
|
16282
17200
|
};
|
|
16283
17201
|
}
|
|
16284
17202
|
}
|
|
@@ -16304,11 +17222,11 @@ function readStagingProject(startDir) {
|
|
|
16304
17222
|
const firebasercPath = joinPath(current, ".firebaserc");
|
|
16305
17223
|
if (pathExists(firebasercPath)) {
|
|
16306
17224
|
try {
|
|
16307
|
-
const firebaserc =
|
|
17225
|
+
const firebaserc = readFirebaserc(firebasercPath);
|
|
16308
17226
|
if (firebaserc?.projects?.staging) {
|
|
16309
17227
|
return firebaserc.projects.staging;
|
|
16310
17228
|
}
|
|
16311
|
-
} catch {
|
|
17229
|
+
} catch (error2) {
|
|
16312
17230
|
}
|
|
16313
17231
|
}
|
|
16314
17232
|
const parent = joinPath(current, "..");
|
|
@@ -16346,7 +17264,7 @@ How to fix:
|
|
|
16346
17264
|
if (shouldOpen) {
|
|
16347
17265
|
try {
|
|
16348
17266
|
const openCommand = process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
16349
|
-
|
|
17267
|
+
spawnSync5(openCommand, [consoleUrl], { shell: true });
|
|
16350
17268
|
log.success("Opening Firebase Console...");
|
|
16351
17269
|
} catch {
|
|
16352
17270
|
log.warn("Could not open browser. Please open the URL manually.");
|
|
@@ -16394,12 +17312,85 @@ function clearFirebaseCache(appDir) {
|
|
|
16394
17312
|
}
|
|
16395
17313
|
}
|
|
16396
17314
|
|
|
17315
|
+
// packages/tooling/src/utils/provider-detection.ts
|
|
17316
|
+
init_utils();
|
|
17317
|
+
init_pathResolver();
|
|
17318
|
+
function detectProvider(appDir) {
|
|
17319
|
+
const firebaseJsonPath = joinPath(appDir, "firebase.json");
|
|
17320
|
+
const supabaseDir = joinPath(appDir, "supabase");
|
|
17321
|
+
const supabaseConfigPath = joinPath(supabaseDir, "config.toml");
|
|
17322
|
+
const supabaseFunctionsDir = joinPath(supabaseDir, "functions");
|
|
17323
|
+
const supabaseMigrationsDir = joinPath(supabaseDir, "migrations");
|
|
17324
|
+
const hasFirebase = pathExists(firebaseJsonPath);
|
|
17325
|
+
const hasSupabase = pathExists(supabaseDir) || pathExists(supabaseConfigPath);
|
|
17326
|
+
let provider = "unknown";
|
|
17327
|
+
if (hasFirebase && hasSupabase) {
|
|
17328
|
+
provider = "both";
|
|
17329
|
+
} else if (hasFirebase) {
|
|
17330
|
+
provider = "firebase";
|
|
17331
|
+
} else if (hasSupabase) {
|
|
17332
|
+
provider = "supabase";
|
|
17333
|
+
}
|
|
17334
|
+
let firebaseConfig;
|
|
17335
|
+
if (hasFirebase) {
|
|
17336
|
+
try {
|
|
17337
|
+
const config = readSync(firebaseJsonPath, { format: "json" });
|
|
17338
|
+
if (config && typeof config === "object") {
|
|
17339
|
+
const firebaseConfigObj = config;
|
|
17340
|
+
firebaseConfig = {
|
|
17341
|
+
projectId: firebaseConfigObj.projectId,
|
|
17342
|
+
hasHosting: !!firebaseConfigObj.hosting,
|
|
17343
|
+
hasFunctions: !!firebaseConfigObj.functions,
|
|
17344
|
+
hasFirestoreRules: !!firebaseConfigObj.firestore?.rules,
|
|
17345
|
+
hasStorageRules: !!firebaseConfigObj.storage?.rules
|
|
17346
|
+
};
|
|
17347
|
+
}
|
|
17348
|
+
} catch {
|
|
17349
|
+
firebaseConfig = {
|
|
17350
|
+
hasHosting: false,
|
|
17351
|
+
hasFunctions: false,
|
|
17352
|
+
hasFirestoreRules: false,
|
|
17353
|
+
hasStorageRules: false
|
|
17354
|
+
};
|
|
17355
|
+
}
|
|
17356
|
+
}
|
|
17357
|
+
let supabaseConfig;
|
|
17358
|
+
if (hasSupabase) {
|
|
17359
|
+
let url;
|
|
17360
|
+
try {
|
|
17361
|
+
const envPath = joinPath(appDir, ".env");
|
|
17362
|
+
if (pathExists(envPath)) {
|
|
17363
|
+
const envContent = readSync(envPath);
|
|
17364
|
+
const urlMatch = envContent.match(/VITE_SUPABASE_URL=(.+)/);
|
|
17365
|
+
if (urlMatch && urlMatch[1]) {
|
|
17366
|
+
url = urlMatch[1].trim();
|
|
17367
|
+
}
|
|
17368
|
+
}
|
|
17369
|
+
} catch {
|
|
17370
|
+
}
|
|
17371
|
+
supabaseConfig = {
|
|
17372
|
+
url,
|
|
17373
|
+
hasFunctions: pathExists(supabaseFunctionsDir),
|
|
17374
|
+
hasMigrations: pathExists(supabaseMigrationsDir)
|
|
17375
|
+
};
|
|
17376
|
+
}
|
|
17377
|
+
return {
|
|
17378
|
+
provider,
|
|
17379
|
+
hasFirebase,
|
|
17380
|
+
hasSupabase,
|
|
17381
|
+
firebaseConfig,
|
|
17382
|
+
supabaseConfig
|
|
17383
|
+
};
|
|
17384
|
+
}
|
|
17385
|
+
|
|
16397
17386
|
// packages/tooling/src/apps/deploy.ts
|
|
17387
|
+
init_cli_input();
|
|
16398
17388
|
init_cli_output();
|
|
16399
17389
|
init_cli_output();
|
|
17390
|
+
init_cli_tools();
|
|
16400
17391
|
init_errors();
|
|
16401
17392
|
init_pathResolver();
|
|
16402
|
-
async function
|
|
17393
|
+
async function main2(options = {}) {
|
|
16403
17394
|
const isStaging = options.staging ?? false;
|
|
16404
17395
|
const config = {
|
|
16405
17396
|
app: options.app,
|
|
@@ -16407,27 +17398,43 @@ async function main(options = {}) {
|
|
|
16407
17398
|
verbose: options.verbose ?? false,
|
|
16408
17399
|
skipBuild: options.skipBuild ?? false,
|
|
16409
17400
|
debug: options.debug ?? false,
|
|
16410
|
-
force: options.force
|
|
17401
|
+
force: options.force,
|
|
16411
17402
|
staging: isStaging
|
|
16412
17403
|
};
|
|
16413
17404
|
try {
|
|
16414
|
-
|
|
16415
|
-
|
|
16416
|
-
|
|
16417
|
-
"
|
|
17405
|
+
const projectRoot = process.cwd();
|
|
17406
|
+
const providerInfo = detectProvider(projectRoot);
|
|
17407
|
+
Ie(
|
|
17408
|
+
isStaging ? "DoNotDev Staging Deployment" : providerInfo.provider === "supabase" ? "DoNotDev Supabase Deployment" : providerInfo.provider === "both" ? "DoNotDev Deployment (Firebase + Supabase)" : "DoNotDev Firebase Deployment"
|
|
16418
17409
|
);
|
|
17410
|
+
if (providerInfo.hasFirebase) {
|
|
17411
|
+
requireCLI(
|
|
17412
|
+
CLI_TOOLS.FIREBASE,
|
|
17413
|
+
"This script uses service account authentication.\nThe Firebase CLI is only needed as a deployment tool, not for authentication."
|
|
17414
|
+
);
|
|
17415
|
+
}
|
|
17416
|
+
if (providerInfo.hasSupabase && providerInfo.supabaseConfig?.hasFunctions) {
|
|
17417
|
+
requireCLI(
|
|
17418
|
+
CLI_TOOLS.SUPABASE,
|
|
17419
|
+
"Supabase CLI is required to deploy Edge Functions.\nInstall: https://supabase.com/docs/guides/cli"
|
|
17420
|
+
);
|
|
17421
|
+
}
|
|
16419
17422
|
const COMMAND_NAMES = ["deploy", "staging", "uat"];
|
|
16420
17423
|
const cliArgs = process.argv.slice(2);
|
|
16421
17424
|
let appName = config.app || (cliArgs[0] && !cliArgs[0].startsWith("--") && !COMMAND_NAMES.includes(cliArgs[0]) ? cliArgs[0] : void 0);
|
|
16422
17425
|
if (!appName) {
|
|
16423
17426
|
const availableApps = detectAvailableApps();
|
|
16424
17427
|
if (availableApps.length === 0) {
|
|
16425
|
-
|
|
16426
|
-
|
|
16427
|
-
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
17428
|
+
if (providerInfo.hasSupabase && !providerInfo.hasFirebase) {
|
|
17429
|
+
log.info("Supabase project detected. Deploying Supabase resources...");
|
|
17430
|
+
} else {
|
|
17431
|
+
log.info("No apps with firebase.json or supabase/ directory found.");
|
|
17432
|
+
log.info(
|
|
17433
|
+
"To deploy, ensure your app has a firebase.json or supabase/ configuration."
|
|
17434
|
+
);
|
|
17435
|
+
log.info("Run from a DoNotDev project, or create one with: dndev init");
|
|
17436
|
+
return;
|
|
17437
|
+
}
|
|
16431
17438
|
}
|
|
16432
17439
|
if (availableApps.length === 1) {
|
|
16433
17440
|
appName = availableApps[0];
|
|
@@ -16443,55 +17450,92 @@ async function main(options = {}) {
|
|
|
16443
17450
|
log.error("App name is required but was not provided.");
|
|
16444
17451
|
process.exit(1);
|
|
16445
17452
|
}
|
|
16446
|
-
const appDir = detectAppDir(appName);
|
|
17453
|
+
const appDir = appName ? detectAppDir(appName) : projectRoot;
|
|
17454
|
+
const appProviderInfo = detectProvider(appDir);
|
|
17455
|
+
const hasVercelConfig = pathExists(joinPath(appDir, "vercel.json"));
|
|
16447
17456
|
let deploymentType = config.deploymentType;
|
|
16448
17457
|
if (!deploymentType) {
|
|
16449
|
-
const firebaseConfig2 = validateFirebaseJson(appDir);
|
|
16450
17458
|
const choices = [];
|
|
16451
|
-
if (
|
|
16452
|
-
|
|
16453
|
-
|
|
16454
|
-
|
|
16455
|
-
|
|
16456
|
-
|
|
16457
|
-
|
|
16458
|
-
|
|
16459
|
-
|
|
16460
|
-
|
|
16461
|
-
|
|
16462
|
-
|
|
16463
|
-
|
|
16464
|
-
|
|
16465
|
-
|
|
16466
|
-
|
|
16467
|
-
|
|
16468
|
-
|
|
16469
|
-
|
|
16470
|
-
|
|
16471
|
-
|
|
17459
|
+
if (appProviderInfo.hasFirebase && appProviderInfo.firebaseConfig) {
|
|
17460
|
+
const fbConfig = appProviderInfo.firebaseConfig;
|
|
17461
|
+
if (fbConfig.hasHosting) {
|
|
17462
|
+
choices.push({
|
|
17463
|
+
title: "Frontend only (Firebase Hosting)",
|
|
17464
|
+
value: "frontend",
|
|
17465
|
+
hint: "Deploy Firebase hosting"
|
|
17466
|
+
});
|
|
17467
|
+
}
|
|
17468
|
+
if (fbConfig.hasFunctions) {
|
|
17469
|
+
choices.push({
|
|
17470
|
+
title: "Functions only (Firebase Cloud Functions)",
|
|
17471
|
+
value: "functions",
|
|
17472
|
+
hint: "Deploy Firebase cloud functions"
|
|
17473
|
+
});
|
|
17474
|
+
}
|
|
17475
|
+
const hasRules = fbConfig.hasFirestoreRules || fbConfig.hasStorageRules;
|
|
17476
|
+
if (hasRules) {
|
|
17477
|
+
choices.push({
|
|
17478
|
+
title: "Rules only (Firestore/Storage)",
|
|
17479
|
+
value: "rules",
|
|
17480
|
+
hint: "Deploy Firestore/Storage rules"
|
|
17481
|
+
});
|
|
17482
|
+
}
|
|
17483
|
+
if (fbConfig.hasHosting && fbConfig.hasFunctions) {
|
|
17484
|
+
choices.push({
|
|
17485
|
+
title: "Frontend + Functions (Firebase)",
|
|
17486
|
+
value: "both",
|
|
17487
|
+
hint: "Deploy Firebase hosting and functions"
|
|
17488
|
+
});
|
|
17489
|
+
}
|
|
17490
|
+
const deployableKindsCount = [
|
|
17491
|
+
fbConfig.hasHosting,
|
|
17492
|
+
fbConfig.hasFunctions,
|
|
17493
|
+
hasRules
|
|
17494
|
+
].filter(Boolean).length;
|
|
17495
|
+
if (deployableKindsCount > 1) {
|
|
17496
|
+
choices.push({
|
|
17497
|
+
title: "All (Firebase)",
|
|
17498
|
+
value: "all",
|
|
17499
|
+
hint: "Deploy everything Firebase (hosting, functions, rules)"
|
|
17500
|
+
});
|
|
17501
|
+
}
|
|
16472
17502
|
}
|
|
16473
|
-
if (
|
|
17503
|
+
if (hasVercelConfig) {
|
|
17504
|
+
const fbFrontendIdx = choices.findIndex(
|
|
17505
|
+
(c) => c.value === "frontend" && c.title.includes("Firebase")
|
|
17506
|
+
);
|
|
17507
|
+
if (fbFrontendIdx !== -1) choices.splice(fbFrontendIdx, 1);
|
|
17508
|
+
const fbBothIdx = choices.findIndex(
|
|
17509
|
+
(c) => c.value === "both" && c.title.includes("Firebase")
|
|
17510
|
+
);
|
|
17511
|
+
if (fbBothIdx !== -1) choices.splice(fbBothIdx, 1);
|
|
16474
17512
|
choices.push({
|
|
16475
|
-
title: "Frontend
|
|
16476
|
-
value: "
|
|
16477
|
-
hint: "Deploy
|
|
17513
|
+
title: "Frontend only (Vercel)",
|
|
17514
|
+
value: "frontend",
|
|
17515
|
+
hint: "Deploy frontend to Vercel"
|
|
16478
17516
|
});
|
|
17517
|
+
const hasFunctions = appProviderInfo.hasFirebase && appProviderInfo.firebaseConfig?.hasFunctions || appProviderInfo.hasSupabase && appProviderInfo.supabaseConfig?.hasFunctions;
|
|
17518
|
+
if (hasFunctions) {
|
|
17519
|
+
choices.push({
|
|
17520
|
+
title: "Frontend + Functions (Vercel + backend)",
|
|
17521
|
+
value: "both",
|
|
17522
|
+
hint: "Deploy frontend to Vercel and functions to backend provider"
|
|
17523
|
+
});
|
|
17524
|
+
}
|
|
16479
17525
|
}
|
|
16480
|
-
|
|
16481
|
-
|
|
16482
|
-
|
|
16483
|
-
|
|
16484
|
-
|
|
16485
|
-
|
|
16486
|
-
|
|
16487
|
-
|
|
16488
|
-
|
|
16489
|
-
hint: "Deploy everything (hosting, functions, rules)"
|
|
16490
|
-
});
|
|
17526
|
+
if (appProviderInfo.hasSupabase && appProviderInfo.supabaseConfig) {
|
|
17527
|
+
const sbConfig = appProviderInfo.supabaseConfig;
|
|
17528
|
+
if (sbConfig.hasFunctions) {
|
|
17529
|
+
choices.push({
|
|
17530
|
+
title: "Edge Functions only (Supabase)",
|
|
17531
|
+
value: "functions",
|
|
17532
|
+
hint: "Deploy Supabase Edge Functions"
|
|
17533
|
+
});
|
|
17534
|
+
}
|
|
16491
17535
|
}
|
|
16492
17536
|
if (choices.length === 0) {
|
|
16493
17537
|
log.error(
|
|
16494
|
-
"No deployment targets found. firebase.json
|
|
17538
|
+
"No deployment targets found. Ensure your project has:\n - Firebase: firebase.json with hosting/functions/rules\n - Supabase: supabase/functions/ directory"
|
|
16495
17539
|
);
|
|
16496
17540
|
process.exit(1);
|
|
16497
17541
|
}
|
|
@@ -16505,71 +17549,78 @@ async function main(options = {}) {
|
|
|
16505
17549
|
);
|
|
16506
17550
|
}
|
|
16507
17551
|
}
|
|
16508
|
-
|
|
16509
|
-
|
|
16510
|
-
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
);
|
|
16515
|
-
|
|
16516
|
-
|
|
16517
|
-
|
|
16518
|
-
|
|
16519
|
-
|
|
16520
|
-
projectId = readStagingProject(appDir) ?? void 0;
|
|
16521
|
-
if (!projectId) {
|
|
16522
|
-
log.error(
|
|
16523
|
-
'Staging project not found. Add "staging" alias to .firebaserc:\n { "projects": { "default": "my-app", "staging": "my-app-uat" } }'
|
|
17552
|
+
let serviceAccountPath;
|
|
17553
|
+
let projectId;
|
|
17554
|
+
const needsFirebase = appProviderInfo.hasFirebase && (deploymentType === "frontend" && !hasVercelConfig || deploymentType === "functions" || deploymentType === "rules" || deploymentType === "both" || deploymentType === "all");
|
|
17555
|
+
if (needsFirebase) {
|
|
17556
|
+
const serviceAccountResult = validateServiceAccount(appDir, {
|
|
17557
|
+
staging: isStaging
|
|
17558
|
+
});
|
|
17559
|
+
if (!serviceAccountResult.valid) {
|
|
17560
|
+
const firebaseConfig2 = validateFirebaseJson(appDir);
|
|
17561
|
+
await showServiceAccountError(
|
|
17562
|
+
serviceAccountResult.info,
|
|
17563
|
+
firebaseConfig2.projectId
|
|
16524
17564
|
);
|
|
16525
17565
|
process.exit(1);
|
|
16526
17566
|
}
|
|
16527
|
-
|
|
16528
|
-
|
|
16529
|
-
|
|
16530
|
-
|
|
16531
|
-
|
|
16532
|
-
|
|
16533
|
-
|
|
16534
|
-
|
|
16535
|
-
|
|
16536
|
-
|
|
16537
|
-
|
|
16538
|
-
if (!
|
|
16539
|
-
|
|
17567
|
+
serviceAccountPath = normalizePath(serviceAccountResult.info.path);
|
|
17568
|
+
projectId = config.project;
|
|
17569
|
+
if (!projectId && isStaging) {
|
|
17570
|
+
projectId = readStagingProject(appDir) ?? void 0;
|
|
17571
|
+
if (!projectId) {
|
|
17572
|
+
log.error(
|
|
17573
|
+
'Staging project not found. Add "staging" alias to .firebaserc:\n { "projects": { "default": "my-app", "staging": "my-app-uat" } }'
|
|
17574
|
+
);
|
|
17575
|
+
process.exit(1);
|
|
17576
|
+
}
|
|
17577
|
+
}
|
|
17578
|
+
if (!projectId) {
|
|
17579
|
+
const fallbackConfig = validateFirebaseJson(appDir);
|
|
17580
|
+
projectId = serviceAccountResult.info.projectId || fallbackConfig.projectId;
|
|
17581
|
+
}
|
|
17582
|
+
if (!projectId) {
|
|
17583
|
+
const inputProjectId = await askForInput(
|
|
17584
|
+
"Firebase project ID not detected. Please enter it:"
|
|
17585
|
+
);
|
|
17586
|
+
if (!inputProjectId) {
|
|
17587
|
+
log.error("Project ID is required for deployment.");
|
|
17588
|
+
process.exit(1);
|
|
17589
|
+
}
|
|
17590
|
+
config.project = inputProjectId;
|
|
17591
|
+
} else {
|
|
17592
|
+
config.project = projectId;
|
|
17593
|
+
}
|
|
17594
|
+
const firebaseConfig = await validateFirebaseJson(appDir);
|
|
17595
|
+
if (!firebaseConfig.valid) {
|
|
17596
|
+
showFirebaseJsonError(firebaseConfig.errors, appDir, deploymentType);
|
|
16540
17597
|
process.exit(1);
|
|
16541
17598
|
}
|
|
16542
|
-
config.project = inputProjectId;
|
|
16543
|
-
} else {
|
|
16544
|
-
config.project = projectId;
|
|
16545
|
-
}
|
|
16546
|
-
const firebaseConfig = validateFirebaseJson(appDir);
|
|
16547
|
-
if (!firebaseConfig.valid) {
|
|
16548
|
-
showFirebaseJsonError(firebaseConfig.errors, appDir, deploymentType);
|
|
16549
|
-
process.exit(1);
|
|
16550
17599
|
}
|
|
16551
|
-
const
|
|
16552
|
-
const
|
|
16553
|
-
const
|
|
16554
|
-
|
|
17600
|
+
const shouldDeployFirebaseFrontend = appProviderInfo.hasFirebase && appProviderInfo.firebaseConfig?.hasHosting && !hasVercelConfig && (deploymentType === "frontend" || deploymentType === "both" || deploymentType === "all");
|
|
17601
|
+
const shouldDeployFirebaseFunctions = appProviderInfo.hasFirebase && appProviderInfo.firebaseConfig?.hasFunctions && (deploymentType === "functions" || deploymentType === "both" || deploymentType === "all");
|
|
17602
|
+
const shouldDeployFirebaseRules = appProviderInfo.hasFirebase && (deploymentType === "rules" || deploymentType === "all") && (appProviderInfo.firebaseConfig?.hasFirestoreRules || appProviderInfo.firebaseConfig?.hasStorageRules);
|
|
17603
|
+
const shouldDeploySupabaseFunctions = appProviderInfo.hasSupabase && appProviderInfo.supabaseConfig?.hasFunctions && deploymentType === "functions";
|
|
17604
|
+
const shouldDeployVercelFrontend = hasVercelConfig && (deploymentType === "frontend" || deploymentType === "both" || deploymentType === "all");
|
|
17605
|
+
if (deploymentType === "frontend" && !shouldDeployFirebaseFrontend && !shouldDeployVercelFrontend) {
|
|
16555
17606
|
log.error(
|
|
16556
|
-
"firebase.json
|
|
17607
|
+
"No hosting configuration found. Ensure firebase.json has hosting config, or (for Supabase apps) vercel.json exists."
|
|
16557
17608
|
);
|
|
16558
17609
|
process.exit(1);
|
|
16559
17610
|
}
|
|
16560
|
-
if (deploymentType === "functions" && !
|
|
17611
|
+
if (deploymentType === "functions" && !shouldDeployFirebaseFunctions && !shouldDeploySupabaseFunctions) {
|
|
16561
17612
|
log.error(
|
|
16562
|
-
"firebase.json
|
|
17613
|
+
"No functions configuration found. Ensure firebase.json has functions config or supabase/functions/ directory exists."
|
|
16563
17614
|
);
|
|
16564
17615
|
process.exit(1);
|
|
16565
17616
|
}
|
|
16566
|
-
if (deploymentType === "rules" && !
|
|
17617
|
+
if (deploymentType === "rules" && !shouldDeployFirebaseRules) {
|
|
16567
17618
|
log.error(
|
|
16568
17619
|
"firebase.json does not contain rules configuration, but rules deployment was requested."
|
|
16569
17620
|
);
|
|
16570
17621
|
process.exit(1);
|
|
16571
17622
|
}
|
|
16572
|
-
if (
|
|
17623
|
+
if (shouldDeployFirebaseFrontend || shouldDeployVercelFrontend) {
|
|
16573
17624
|
const buildStatus = validateBuild(appDir);
|
|
16574
17625
|
let shouldBuild = false;
|
|
16575
17626
|
if (!buildStatus.exists || buildStatus.isEmpty) {
|
|
@@ -16596,9 +17647,11 @@ async function main(options = {}) {
|
|
|
16596
17647
|
if (shouldBuild) {
|
|
16597
17648
|
const s = Y2();
|
|
16598
17649
|
const buildCmd = isStaging ? "bun run build -- --mode staging" : "bun run build";
|
|
16599
|
-
s.start(
|
|
17650
|
+
s.start(
|
|
17651
|
+
isStaging ? "Building (staging mode)..." : "Building application..."
|
|
17652
|
+
);
|
|
16600
17653
|
try {
|
|
16601
|
-
|
|
17654
|
+
execSync4(buildCmd, {
|
|
16602
17655
|
cwd: appDir,
|
|
16603
17656
|
stdio: "inherit",
|
|
16604
17657
|
env: {
|
|
@@ -16613,19 +17666,52 @@ async function main(options = {}) {
|
|
|
16613
17666
|
}
|
|
16614
17667
|
}
|
|
16615
17668
|
}
|
|
16616
|
-
|
|
17669
|
+
if (appProviderInfo.hasFirebase) {
|
|
17670
|
+
clearFirebaseCache(appDir);
|
|
17671
|
+
}
|
|
16617
17672
|
const envLabel = isStaging ? " (STAGING)" : "";
|
|
16618
|
-
|
|
16619
|
-
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16623
|
-
|
|
16624
|
-
|
|
16625
|
-
|
|
17673
|
+
const providerLabel = appProviderInfo.provider === "both" ? " (Firebase + Supabase)" : appProviderInfo.provider === "supabase" ? " (Supabase)" : " (Firebase)";
|
|
17674
|
+
const configNote = [
|
|
17675
|
+
`App: ${appName || "root"}`,
|
|
17676
|
+
...config.project ? [`Project: ${config.project}${envLabel}`] : [],
|
|
17677
|
+
`Provider: ${appProviderInfo.provider}${providerLabel}`,
|
|
17678
|
+
`Deployment: ${deploymentType}`,
|
|
17679
|
+
...serviceAccountPath ? [`Service Account: ${serviceAccountPath}`] : []
|
|
17680
|
+
].join("\n");
|
|
17681
|
+
Me(configNote, "Deployment Configuration");
|
|
17682
|
+
if (shouldDeployVercelFrontend) {
|
|
17683
|
+
requireCLI(
|
|
17684
|
+
CLI_TOOLS.VERCEL,
|
|
17685
|
+
"Vercel CLI is required to deploy the frontend.\nInstall: npm i -g vercel or use npx vercel"
|
|
17686
|
+
);
|
|
17687
|
+
}
|
|
17688
|
+
if (shouldDeployFirebaseFrontend && serviceAccountPath && config.project) {
|
|
16626
17689
|
await deployFrontend(appDir, serviceAccountPath, config.project, config);
|
|
16627
17690
|
}
|
|
16628
|
-
if (
|
|
17691
|
+
if (shouldDeployVercelFrontend) {
|
|
17692
|
+
await deployVercelFrontend(appDir, config);
|
|
17693
|
+
}
|
|
17694
|
+
if (shouldDeployFirebaseFunctions && serviceAccountPath && config.project) {
|
|
17695
|
+
const functionsEnvPath = joinPath(appDir, "functions", ".env");
|
|
17696
|
+
if (pathExists(functionsEnvPath)) {
|
|
17697
|
+
const shouldSync = await askForConfirmation(
|
|
17698
|
+
"Sync runtime secrets (functions/.env) before deploying?"
|
|
17699
|
+
);
|
|
17700
|
+
if (shouldSync) {
|
|
17701
|
+
const { main: syncSecrets } = await Promise.resolve().then(() => (init_sync_secrets(), sync_secrets_exports));
|
|
17702
|
+
const syncResult = await syncSecrets({
|
|
17703
|
+
envFile: normalizePath(functionsEnvPath),
|
|
17704
|
+
projectId: config.project,
|
|
17705
|
+
dryRun: false,
|
|
17706
|
+
verbose: config.verbose
|
|
17707
|
+
});
|
|
17708
|
+
if (syncResult !== 0) {
|
|
17709
|
+
log.warn(
|
|
17710
|
+
"Secrets sync returned non-zero. Functions may not have latest secrets at runtime."
|
|
17711
|
+
);
|
|
17712
|
+
}
|
|
17713
|
+
}
|
|
17714
|
+
}
|
|
16629
17715
|
await deployFunctions(
|
|
16630
17716
|
appDir,
|
|
16631
17717
|
serviceAccountPath,
|
|
@@ -16633,13 +17719,18 @@ Service Account: ${serviceAccountResult.info.clientEmail}`,
|
|
|
16633
17719
|
config
|
|
16634
17720
|
);
|
|
16635
17721
|
}
|
|
16636
|
-
if (
|
|
17722
|
+
if (shouldDeployFirebaseRules && serviceAccountPath && config.project && appProviderInfo.firebaseConfig) {
|
|
16637
17723
|
await deployRules(appDir, serviceAccountPath, config.project, config, {
|
|
16638
|
-
firestore: firebaseConfig.hasFirestoreRules,
|
|
16639
|
-
firestoreIndexes:
|
|
16640
|
-
|
|
17724
|
+
firestore: appProviderInfo.firebaseConfig.hasFirestoreRules,
|
|
17725
|
+
firestoreIndexes: false,
|
|
17726
|
+
// TODO: detect this
|
|
17727
|
+
storage: appProviderInfo.firebaseConfig.hasStorageRules
|
|
16641
17728
|
});
|
|
16642
17729
|
}
|
|
17730
|
+
if (shouldDeploySupabaseFunctions) {
|
|
17731
|
+
const { deploySupabaseFunctions: deploySupabaseFunctions2 } = await Promise.resolve().then(() => (init_deploy_supabase_functions(), deploy_supabase_functions_exports));
|
|
17732
|
+
await deploySupabaseFunctions2(appDir, config);
|
|
17733
|
+
}
|
|
16643
17734
|
Se("Deployment completed successfully!");
|
|
16644
17735
|
} catch (error2) {
|
|
16645
17736
|
if (error2 instanceof DoNotDevError) {
|
|
@@ -16656,7 +17747,7 @@ Service Account: ${serviceAccountResult.info.clientEmail}`,
|
|
|
16656
17747
|
}
|
|
16657
17748
|
}
|
|
16658
17749
|
export {
|
|
16659
|
-
main
|
|
17750
|
+
main2 as main
|
|
16660
17751
|
};
|
|
16661
17752
|
/*! Bundled license information:
|
|
16662
17753
|
|