hot-updater 0.35.7 → 0.35.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +532 -91
  2. package/package.json +16 -15
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env -S node --
2
2
  import { C as __toESM, S as __require, _ as IosConfigParser, a as createAndInjectFingerprintFiles, b as AndroidConfigParser, c as generateFingerprints, d as getFingerprintDiff, f as showFingerprintDiff, g as warnIfExpoCNG, h as isMissingFingerprintDependencyError, i as saveKeyPair, l as nativeFingerprint, m as require_out, n as getPublicKeyFromPrivate, p as isFingerprintEquals, r as loadPrivateKey, t as generateKeyPair, u as readLocalFingerprint, v as parsePlist, x as __commonJSMin, y as require_base64_js } from "./keyGeneration-DVUvCTrW.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import fs, { createReadStream, existsSync, realpathSync, statSync } from "fs";
5
5
  import path from "path";
6
- import { HotUpdateDirUtil, banner, colors, createTarBrTargetFiles, createTarGzTargetFiles, createZipTargetFiles, ensureInstallPackages, getCwd, getPackageManager, loadConfig, p, printBanner, promoteBundle, readPackageUp } from "@hot-updater/cli-tools";
6
+ import { HotUpdateDirUtil, InitError, MissingInitInputsError, banner, colors, createTarBrTargetFiles, createTarGzTargetFiles, createZipTargetFiles, ensureInstallPackages, getCwd, getHotUpdaterEnvValue, getMissingInitInputs, getMissingInitProviderInputs, getPackageManager, loadConfig, makeEnv, p, printBanner, promoteBundle, readHotUpdaterInitEnv, readPackageUp, resolveInitProviderInputs } from "@hot-updater/cli-tools";
7
7
  import { promisify } from "util";
8
8
  import crypto from "node:crypto";
9
9
  import fs$1, { constants } from "node:fs/promises";
@@ -21,7 +21,7 @@ import { fileURLToPath } from "node:url";
21
21
  import { StringDecoder } from "node:string_decoder";
22
22
  import { aborted, callbackify, debuglog, inspect, promisify as promisify$1, stripVTControlCharacters } from "node:util";
23
23
  import tty from "node:tty";
24
- import { exec } from "child_process";
24
+ import { exec, spawnSync as spawnSync$1 } from "child_process";
25
25
  import { scheduler, setImmediate, setTimeout } from "node:timers/promises";
26
26
  import { serialize } from "node:v8";
27
27
  import { finished } from "node:stream/promises";
@@ -30046,6 +30046,15 @@ const appendToProjectRootGitignore = ({ cwd, globLines }) => {
30046
30046
  } else fs.writeFileSync(gitIgnorePath, `${[comment, ...globLines].join("\n")}\n`, { encoding: "utf8" });
30047
30047
  return true;
30048
30048
  };
30049
+ const isProjectFileTracked = ({ cwd, filePath }) => spawnSync$1("git", [
30050
+ "ls-files",
30051
+ "--error-unmatch",
30052
+ "--",
30053
+ filePath
30054
+ ], {
30055
+ cwd: cwd ?? getCwd(),
30056
+ stdio: "ignore"
30057
+ }).status === 0;
30049
30058
  //#endregion
30050
30059
  //#region src/utils/signing/bundleSigning.ts
30051
30060
  /**
@@ -30742,7 +30751,415 @@ const ensureInstallPackages$1 = async (buildPluginPackages) => {
30742
30751
  await ensureInstallPackages(buildPluginPackages, { versionResolver: ensurePackageVersion });
30743
30752
  };
30744
30753
  //#endregion
30754
+ //#region ../../plugins/aws/dist/init/index.mjs
30755
+ const AWS_AUTH_MODES = [
30756
+ "local-session",
30757
+ "shared-profile",
30758
+ "sso",
30759
+ "account"
30760
+ ];
30761
+ const isAwsAuthMode = (value) => value !== void 0 && AWS_AUTH_MODES.some((mode) => mode === value);
30762
+ const AWS_REGION_VALUES = [
30763
+ "af-south-1",
30764
+ "ap-east-1",
30765
+ "ap-east-2",
30766
+ "ap-northeast-1",
30767
+ "ap-northeast-2",
30768
+ "ap-northeast-3",
30769
+ "ap-south-1",
30770
+ "ap-south-2",
30771
+ "ap-southeast-1",
30772
+ "ap-southeast-2",
30773
+ "ap-southeast-3",
30774
+ "ap-southeast-4",
30775
+ "ap-southeast-5",
30776
+ "ap-southeast-6",
30777
+ "ap-southeast-7",
30778
+ "ca-central-1",
30779
+ "ca-west-1",
30780
+ "cn-north-1",
30781
+ "cn-northwest-1",
30782
+ "eu-central-1",
30783
+ "eu-central-2",
30784
+ "eu-north-1",
30785
+ "eu-south-1",
30786
+ "eu-south-2",
30787
+ "eu-west-1",
30788
+ "eu-west-2",
30789
+ "eu-west-3",
30790
+ "il-central-1",
30791
+ "me-central-1",
30792
+ "me-south-1",
30793
+ "mx-central-1",
30794
+ "sa-east-1",
30795
+ "us-east-1",
30796
+ "us-east-2",
30797
+ "us-gov-east-1",
30798
+ "us-gov-west-1",
30799
+ "us-west-1",
30800
+ "us-west-2"
30801
+ ];
30802
+ const isAwsRegionValue = (value) => value !== void 0 && AWS_REGION_VALUES.some((region) => region === value);
30803
+ const initProvider$3 = {
30804
+ label: "AWS S3 + Lambda@Edge",
30805
+ inputs: {
30806
+ authMode: {
30807
+ envKey: "HOT_UPDATER_AWS_AUTH_MODE",
30808
+ help: "AWS authentication mode: local-session, shared-profile, sso, or account",
30809
+ prompt: {
30810
+ message: "Select the mode to login to AWS",
30811
+ type: "select"
30812
+ },
30813
+ validate: isAwsAuthMode
30814
+ },
30815
+ profile: {
30816
+ envKey: "HOT_UPDATER_AWS_PROFILE",
30817
+ help: "AWS profile name",
30818
+ requiredWhen: (inputs) => inputs.authMode === "shared-profile" || inputs.authMode === "sso",
30819
+ requirementHint: "required for shared-profile and sso auth",
30820
+ prompt: {
30821
+ defaultValue: "default",
30822
+ message: "Enter the AWS profile name",
30823
+ placeholder: "default",
30824
+ type: "text"
30825
+ }
30826
+ },
30827
+ accessKeyId: {
30828
+ envKey: "HOT_UPDATER_S3_ACCESS_KEY_ID",
30829
+ help: "AWS access key ID",
30830
+ persistence: "with-consent",
30831
+ requiredWhen: (inputs) => inputs.authMode === "account",
30832
+ requirementHint: "required for account auth",
30833
+ prompt: {
30834
+ message: "Enter your AWS Access Key ID",
30835
+ placeholder: "AKIA...",
30836
+ type: "text"
30837
+ }
30838
+ },
30839
+ secretAccessKey: {
30840
+ envKey: "HOT_UPDATER_S3_SECRET_ACCESS_KEY",
30841
+ help: "AWS secret access key",
30842
+ persistence: "with-consent",
30843
+ requiredWhen: (inputs) => inputs.authMode === "account",
30844
+ requirementHint: "required for account auth",
30845
+ prompt: {
30846
+ message: "Enter your AWS Secret Access Key",
30847
+ type: "password"
30848
+ }
30849
+ },
30850
+ bucketName: {
30851
+ envKey: "HOT_UPDATER_S3_BUCKET_NAME",
30852
+ help: "S3 bucket name",
30853
+ prompt: {
30854
+ defaultValue: "hot-updater-storage",
30855
+ message: "Enter the name of the new S3 Bucket",
30856
+ placeholder: "hot-updater-storage",
30857
+ type: "text"
30858
+ }
30859
+ },
30860
+ bucketRegion: {
30861
+ envKey: "HOT_UPDATER_S3_REGION",
30862
+ help: "S3 bucket region",
30863
+ prompt: {
30864
+ message: "Enter AWS region for the S3 bucket",
30865
+ type: "select"
30866
+ },
30867
+ validate: isAwsRegionValue
30868
+ },
30869
+ lambdaName: {
30870
+ envKey: "HOT_UPDATER_AWS_LAMBDA_NAME",
30871
+ help: "Lambda@Edge function name",
30872
+ prompt: {
30873
+ defaultValue: "hot-updater-edge",
30874
+ message: "Enter the name of the Lambda@Edge function",
30875
+ placeholder: "hot-updater-edge",
30876
+ type: "text"
30877
+ }
30878
+ },
30879
+ distributionId: {
30880
+ envKey: "HOT_UPDATER_CLOUDFRONT_DISTRIBUTION_ID",
30881
+ help: "Existing CloudFront distribution ID",
30882
+ optional: true
30883
+ },
30884
+ migrationApproved: {
30885
+ envKey: "HOT_UPDATER_AWS_MIGRATION_APPROVED",
30886
+ help: "Allow pending Hot Updater S3 migrations (true)",
30887
+ optional: true,
30888
+ prompt: {
30889
+ message: "Apply pending Hot Updater S3 migrations during init and future infrastructure updates?",
30890
+ type: "confirm"
30891
+ },
30892
+ validate: (value) => value === "true"
30893
+ }
30894
+ }
30895
+ };
30896
+ //#endregion
30897
+ //#region ../../plugins/cloudflare/dist/init/index.mjs
30898
+ const initProvider$2 = {
30899
+ label: "Cloudflare D1 + R2 + Worker",
30900
+ inputs: {
30901
+ accountId: {
30902
+ envKey: "HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID",
30903
+ help: "Cloudflare account ID",
30904
+ prompt: {
30905
+ message: "Account List",
30906
+ type: "select"
30907
+ }
30908
+ },
30909
+ apiToken: {
30910
+ envKey: "HOT_UPDATER_CLOUDFLARE_API_TOKEN",
30911
+ help: "API token with D1 edit permission",
30912
+ persistence: "with-consent",
30913
+ prompt: {
30914
+ message: "Enter the Cloudflare D1 API Token",
30915
+ type: "password"
30916
+ }
30917
+ },
30918
+ bucketName: {
30919
+ envKey: "HOT_UPDATER_CLOUDFLARE_R2_BUCKET_NAME",
30920
+ help: "R2 bucket name",
30921
+ prompt: {
30922
+ defaultValue: "hot-updater-storage",
30923
+ message: "Enter the name of the new R2 Bucket",
30924
+ placeholder: "hot-updater-storage",
30925
+ type: "text"
30926
+ }
30927
+ },
30928
+ accessKeyId: {
30929
+ envKey: "HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID",
30930
+ help: "R2 access key ID",
30931
+ persistence: "with-consent",
30932
+ prompt: {
30933
+ message: "Enter the R2 Access Key ID",
30934
+ type: "password"
30935
+ }
30936
+ },
30937
+ secretAccessKey: {
30938
+ envKey: "HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY",
30939
+ help: "R2 secret access key",
30940
+ persistence: "with-consent",
30941
+ prompt: {
30942
+ message: "Enter the R2 Secret Access Key",
30943
+ type: "password"
30944
+ }
30945
+ },
30946
+ workerName: {
30947
+ envKey: "HOT_UPDATER_CLOUDFLARE_WORKER_NAME",
30948
+ help: "Worker name",
30949
+ prompt: {
30950
+ defaultValue: "hot-updater",
30951
+ message: "Enter the name of the worker",
30952
+ placeholder: "hot-updater",
30953
+ type: "text"
30954
+ }
30955
+ },
30956
+ d1DatabaseId: {
30957
+ envKey: "HOT_UPDATER_CLOUDFLARE_D1_DATABASE_ID",
30958
+ help: "D1 database ID"
30959
+ },
30960
+ d1DatabaseName: {
30961
+ envKey: "HOT_UPDATER_CLOUDFLARE_D1_DATABASE_NAME",
30962
+ help: "D1 database name",
30963
+ prompt: {
30964
+ defaultValue: "hot-updater",
30965
+ message: "Enter the name of the new D1 Database",
30966
+ placeholder: "hot-updater",
30967
+ type: "text"
30968
+ }
30969
+ },
30970
+ r2Private: {
30971
+ envKey: "HOT_UPDATER_CLOUDFLARE_R2_PRIVATE",
30972
+ help: "Whether the R2 bucket is private (true or false)",
30973
+ prompt: {
30974
+ message: "Make R2 bucket private?",
30975
+ type: "confirm"
30976
+ },
30977
+ validate: (value) => value === "true" || value === "false"
30978
+ }
30979
+ }
30980
+ };
30981
+ //#endregion
30982
+ //#region ../../plugins/firebase/dist/init/index.mjs
30983
+ const isFirebaseRegion = (value) => value !== void 0 && /^[a-z]+(?:-[a-z]+)+[0-9]+$/.test(value);
30984
+ const isFirebaseProjectId = (value) => value !== void 0 && /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/.test(value);
30985
+ const initProvider$1 = {
30986
+ label: "Firebase",
30987
+ inputs: {
30988
+ projectId: {
30989
+ envKey: "HOT_UPDATER_FIREBASE_PROJECT_ID",
30990
+ help: "Firebase project ID",
30991
+ prompt: {
30992
+ message: "Enter the Firebase project ID:",
30993
+ placeholder: "hot-updater-app",
30994
+ type: "text"
30995
+ },
30996
+ validate: isFirebaseProjectId
30997
+ },
30998
+ region: {
30999
+ envKey: "HOT_UPDATER_FIREBASE_REGION",
31000
+ help: "Firebase Functions region",
31001
+ prompt: {
31002
+ message: "Select Region",
31003
+ type: "select"
31004
+ },
31005
+ validate: isFirebaseRegion
31006
+ },
31007
+ applicationCredentials: {
31008
+ envKey: "GOOGLE_APPLICATION_CREDENTIALS",
31009
+ help: "Service account JSON path",
31010
+ optional: true,
31011
+ persistence: "with-consent",
31012
+ prompt: {
31013
+ message: "Enter the service account JSON path (press Enter to configure later)",
31014
+ placeholder: "~/Downloads/firebase-service-account.json",
31015
+ type: "text"
31016
+ }
31017
+ }
31018
+ }
31019
+ };
31020
+ //#endregion
31021
+ //#region ../../plugins/supabase/dist/init/index.mjs
31022
+ const SUPABASE_REGION_VALUES = [
31023
+ "ap-east-1",
31024
+ "ap-northeast-1",
31025
+ "ap-northeast-2",
31026
+ "ap-south-1",
31027
+ "ap-southeast-1",
31028
+ "ap-southeast-2",
31029
+ "ca-central-1",
31030
+ "eu-central-1",
31031
+ "eu-central-2",
31032
+ "eu-north-1",
31033
+ "eu-west-1",
31034
+ "eu-west-2",
31035
+ "eu-west-3",
31036
+ "sa-east-1",
31037
+ "us-east-1",
31038
+ "us-east-2",
31039
+ "us-west-1",
31040
+ "us-west-2"
31041
+ ];
31042
+ const isSupabaseRegion = (value) => value !== void 0 && SUPABASE_REGION_VALUES.some((region) => region === value);
31043
+ const isSupabaseFunctionName = (value) => value !== void 0 && /^[A-Za-z][A-Za-z0-9_-]*$/.test(value);
31044
+ //#endregion
31045
+ //#region src/commands/initProviders.ts
31046
+ const INIT_PROVIDER_PACKAGES = {
31047
+ cloudflare: {
31048
+ definition: initProvider$2,
31049
+ devDependencies: ["wrangler"],
31050
+ load: () => import("@hot-updater/cloudflare/iac"),
31051
+ packageName: "@hot-updater/cloudflare"
31052
+ },
31053
+ aws: {
31054
+ definition: initProvider$3,
31055
+ devDependencies: [],
31056
+ load: () => import("@hot-updater/aws/iac"),
31057
+ packageName: "@hot-updater/aws"
31058
+ },
31059
+ supabase: {
31060
+ definition: {
31061
+ label: "Supabase",
31062
+ inputs: {
31063
+ projectId: {
31064
+ envKey: "HOT_UPDATER_SUPABASE_PROJECT_ID",
31065
+ help: "Supabase project reference"
31066
+ },
31067
+ projectName: {
31068
+ envKey: "HOT_UPDATER_SUPABASE_PROJECT_NAME",
31069
+ help: "Project name used when creating a Supabase project",
31070
+ optional: true,
31071
+ prompt: {
31072
+ defaultValue: "hot-updater",
31073
+ message: "Enter a name for the new Supabase project",
31074
+ placeholder: "hot-updater",
31075
+ type: "text"
31076
+ }
31077
+ },
31078
+ organizationSlug: {
31079
+ envKey: "HOT_UPDATER_SUPABASE_ORGANIZATION_SLUG",
31080
+ help: "Organization slug used when creating a Supabase project",
31081
+ optional: true,
31082
+ prompt: {
31083
+ message: "Select a Supabase organization",
31084
+ type: "select"
31085
+ }
31086
+ },
31087
+ region: {
31088
+ envKey: "HOT_UPDATER_SUPABASE_REGION",
31089
+ help: "Region used when creating a Supabase project",
31090
+ optional: true,
31091
+ prompt: {
31092
+ defaultValue: "us-east-1",
31093
+ message: "Select a region for the new Supabase project",
31094
+ type: "select"
31095
+ },
31096
+ validate: isSupabaseRegion
31097
+ },
31098
+ accessToken: {
31099
+ envKey: "SUPABASE_ACCESS_TOKEN",
31100
+ help: "Supabase personal access token",
31101
+ persistence: "with-consent",
31102
+ preflight: false,
31103
+ prompt: {
31104
+ message: "Enter your Supabase personal access token",
31105
+ type: "password"
31106
+ }
31107
+ },
31108
+ bucketName: {
31109
+ envKey: "HOT_UPDATER_SUPABASE_BUCKET_NAME",
31110
+ help: "Storage bucket name",
31111
+ prompt: {
31112
+ defaultValue: "hot-updater-storage",
31113
+ message: "Enter a name for the new storage bucket",
31114
+ placeholder: "hot-updater-storage",
31115
+ type: "text"
31116
+ }
31117
+ },
31118
+ functionName: {
31119
+ envKey: "HOT_UPDATER_SUPABASE_FUNCTION_NAME",
31120
+ help: "Edge Function name",
31121
+ prompt: {
31122
+ defaultValue: "update-server",
31123
+ message: "Enter a name for the edge function",
31124
+ placeholder: "update-server",
31125
+ type: "text"
31126
+ },
31127
+ validate: isSupabaseFunctionName
31128
+ },
31129
+ databasePassword: {
31130
+ envKey: "HOT_UPDATER_SUPABASE_DB_PASSWORD",
31131
+ help: "Database password, when required by the linked project",
31132
+ optional: true,
31133
+ persistence: "with-consent",
31134
+ prompt: {
31135
+ message: "Enter your Supabase database password (press Enter to skip if none)",
31136
+ type: "password"
31137
+ }
31138
+ }
31139
+ }
31140
+ },
31141
+ devDependencies: [],
31142
+ load: () => import("@hot-updater/supabase/iac"),
31143
+ packageName: "@hot-updater/supabase"
31144
+ },
31145
+ firebase: {
31146
+ definition: initProvider$1,
31147
+ devDependencies: ["firebase-tools", "firebase-admin"],
31148
+ load: () => import("@hot-updater/firebase/iac"),
31149
+ packageName: "@hot-updater/firebase"
31150
+ }
31151
+ };
31152
+ const isInitProvider = (value) => value !== void 0 && Object.hasOwn(INIT_PROVIDER_PACKAGES, value);
31153
+ const INIT_PROVIDER_NAMES = Object.keys(INIT_PROVIDER_PACKAGES).filter(isInitProvider);
31154
+ //#endregion
30745
31155
  //#region src/commands/init.ts
31156
+ const INIT_BUILD_ENV_KEY = "HOT_UPDATER_INIT_BUILD";
31157
+ const INIT_PROVIDER_ENV_KEY = "HOT_UPDATER_INIT_PROVIDER";
31158
+ const BUILD_PLUGIN_KEYS = [
31159
+ "bare",
31160
+ "rock",
31161
+ "expo"
31162
+ ];
30746
31163
  const REQUIRED_PACKAGES = {
30747
31164
  dependencies: ["@hot-updater/react-native"],
30748
31165
  devDependencies: ["dotenv"]
@@ -30769,74 +31186,91 @@ const BUILD_PLUGINS = {
30769
31186
  devDependencies: ["@hot-updater/expo"]
30770
31187
  }
30771
31188
  };
30772
- const PROVIDER_LABELS = {
30773
- cloudflare: "Cloudflare D1 + R2 + Worker",
30774
- aws: "AWS S3 + Lambda@Edge",
30775
- supabase: "Supabase",
30776
- firebase: "Firebase"
30777
- };
30778
- const PACKAGE_MAP = {
30779
- supabase: {
30780
- dependencies: [],
30781
- devDependencies: ["@hot-updater/supabase"]
30782
- },
30783
- aws: {
30784
- dependencies: [],
30785
- devDependencies: ["@hot-updater/aws"]
30786
- },
30787
- cloudflare: {
30788
- dependencies: [],
30789
- devDependencies: ["wrangler", "@hot-updater/cloudflare"]
30790
- },
30791
- firebase: {
30792
- dependencies: [],
30793
- devDependencies: [
30794
- "firebase-tools",
30795
- "firebase-admin",
30796
- "@hot-updater/firebase"
30797
- ]
30798
- }
30799
- };
30800
- const resolveBuildPlugin = async (flag) => {
30801
- if (flag) return BUILD_PLUGINS[flag];
30802
- const selected = await p.select({
30803
- message: "Select a build plugin",
30804
- options: Object.values(BUILD_PLUGINS).map((plugin) => ({
30805
- value: plugin,
30806
- label: plugin.label,
30807
- hint: plugin.hint
30808
- }))
30809
- });
30810
- if (p.isCancel(selected)) process.exit(0);
30811
- return selected;
31189
+ const isBuildPluginKey = (value) => {
31190
+ return value !== void 0 && Object.keys(BUILD_PLUGINS).includes(value);
31191
+ };
31192
+ const collectInitChoices = async (options) => {
31193
+ const { env: existingEnv } = await readHotUpdaterInitEnv(process.cwd(), options.envFile);
31194
+ const savedBuild = getHotUpdaterEnvValue(existingEnv, INIT_BUILD_ENV_KEY);
31195
+ const savedProvider = getHotUpdaterEnvValue(existingEnv, INIT_PROVIDER_ENV_KEY);
31196
+ const build = options.build ?? (isBuildPluginKey(savedBuild) ? savedBuild : null);
31197
+ const provider = options.provider ?? (isInitProvider(savedProvider) ? savedProvider : null);
31198
+ if (options.envFile !== void 0) {
31199
+ const missingInputs = [...getMissingInitInputs({
31200
+ [INIT_BUILD_ENV_KEY]: build ?? void 0,
31201
+ [INIT_PROVIDER_ENV_KEY]: provider ?? void 0
31202
+ }), ...provider ? getMissingInitProviderInputs({
31203
+ inputs: resolveInitProviderInputs(existingEnv, INIT_PROVIDER_PACKAGES[provider].definition),
31204
+ preflightOnly: true,
31205
+ provider: INIT_PROVIDER_PACKAGES[provider].definition
31206
+ }) : []];
31207
+ if (missingInputs.length > 0) throw new MissingInitInputsError([...new Set(missingInputs)]);
31208
+ }
31209
+ if (build && provider) return {
31210
+ build,
31211
+ provider
31212
+ };
31213
+ return await p.group({
31214
+ build: () => build ? Promise.resolve(build) : p.select({
31215
+ message: "Select a build plugin",
31216
+ options: BUILD_PLUGIN_KEYS.map((value) => ({
31217
+ value,
31218
+ label: BUILD_PLUGINS[value].label,
31219
+ hint: BUILD_PLUGINS[value].hint
31220
+ }))
31221
+ }),
31222
+ provider: () => provider ? Promise.resolve(provider) : p.select({
31223
+ message: "Select a provider",
31224
+ options: INIT_PROVIDER_NAMES.map((value) => ({
31225
+ value,
31226
+ label: INIT_PROVIDER_PACKAGES[value].definition.label
31227
+ }))
31228
+ })
31229
+ }, { onCancel: () => process.exit(0) });
30812
31230
  };
30813
- const resolveProvider = async (flag) => {
30814
- if (flag) return flag;
30815
- const selected = await p.select({
30816
- message: "Select a provider",
30817
- options: Object.keys(PROVIDER_LABELS).map((value) => ({
30818
- value,
30819
- label: PROVIDER_LABELS[value]
30820
- }))
30821
- });
30822
- if (p.isCancel(selected)) process.exit(0);
30823
- return selected;
31231
+ const handleInitError = (error) => {
31232
+ if (!(error instanceof InitError)) return false;
31233
+ p.log.error(error.message);
31234
+ process.exitCode = 1;
31235
+ return true;
30824
31236
  };
30825
31237
  const init = async (options = {}) => {
30826
31238
  printBanner$1();
30827
- const buildPluginPackage = await resolveBuildPlugin(options.build);
30828
- const provider = await resolveProvider(options.provider);
31239
+ let choices;
31240
+ try {
31241
+ choices = await collectInitChoices(options);
31242
+ } catch (error) {
31243
+ if (handleInitError(error)) return;
31244
+ throw error;
31245
+ }
31246
+ if (isProjectFileTracked({
31247
+ cwd: process.cwd(),
31248
+ filePath: ".env.hotupdater"
31249
+ })) {
31250
+ p.log.error("Refusing to save init credentials because .env.hotupdater is tracked by Git. Untrack it before running init.");
31251
+ process.exitCode = 1;
31252
+ return;
31253
+ }
31254
+ if (appendToProjectRootGitignore({ globLines: [
31255
+ ".env.hotupdater",
31256
+ HotUpdateDirUtil.outputGitignorePath,
31257
+ HotUpdateDirUtil.logGitignorePath
31258
+ ] })) p.log.info(".gitignore has been modified to include hot-updater entries");
31259
+ const buildPluginPackage = BUILD_PLUGINS[choices.build];
31260
+ const provider = choices.provider;
31261
+ const providerPackage = INIT_PROVIDER_PACKAGES[provider];
31262
+ await makeEnv({
31263
+ [INIT_BUILD_ENV_KEY]: choices.build,
31264
+ [INIT_PROVIDER_ENV_KEY]: provider
31265
+ });
30829
31266
  try {
30830
31267
  await ensureInstallPackages$1({
30831
- dependencies: [
30832
- ...buildPluginPackage.dependencies,
30833
- ...REQUIRED_PACKAGES.dependencies,
30834
- ...PACKAGE_MAP[provider].dependencies
30835
- ],
31268
+ dependencies: [...buildPluginPackage.dependencies, ...REQUIRED_PACKAGES.dependencies],
30836
31269
  devDependencies: [
30837
31270
  ...buildPluginPackage.devDependencies,
30838
31271
  ...REQUIRED_PACKAGES.devDependencies,
30839
- ...PACKAGE_MAP[provider].devDependencies
31272
+ ...providerPackage.devDependencies,
31273
+ providerPackage.packageName
30840
31274
  ]
30841
31275
  });
30842
31276
  } catch (e) {
@@ -30844,29 +31278,38 @@ const init = async (options = {}) => {
30844
31278
  else if (e instanceof Error) p.log.error(e.message);
30845
31279
  process.exit(1);
30846
31280
  }
30847
- const build = buildPluginPackage.name;
30848
- switch (provider) {
30849
- case "supabase":
30850
- await (await import("@hot-updater/supabase/iac")).runInit({ build });
30851
- break;
30852
- case "cloudflare":
30853
- await (await import("@hot-updater/cloudflare/iac")).runInit({ build });
30854
- break;
30855
- case "aws":
30856
- await (await import("@hot-updater/aws/iac")).runInit({ build });
30857
- break;
30858
- case "firebase":
30859
- await (await import("@hot-updater/firebase/iac")).runInit({ build });
30860
- break;
30861
- default: throw new Error("Invalid provider");
31281
+ const runInitOptions = {
31282
+ build: buildPluginPackage.name,
31283
+ envFile: options.envFile
31284
+ };
31285
+ try {
31286
+ await (await providerPackage.load()).runInit(runInitOptions);
31287
+ } catch (error) {
31288
+ if (handleInitError(error)) return;
31289
+ throw error;
30862
31290
  }
30863
- if (appendToProjectRootGitignore({ globLines: [
30864
- ".env.hotupdater",
30865
- HotUpdateDirUtil.outputGitignorePath,
30866
- HotUpdateDirUtil.logGitignorePath
30867
- ] })) p.log.info(".gitignore has been modified to include hot-updater entries");
30868
31291
  };
30869
31292
  //#endregion
31293
+ //#region src/commands/initHelp.ts
31294
+ const formatInput = ({ envKey, help, optional, requirementHint }) => {
31295
+ return ` ${envKey} ${help} (${optional ? "optional" : requirementHint ? requirementHint : "required"})`;
31296
+ };
31297
+ const initHelp = [
31298
+ "",
31299
+ "Environment file replay:",
31300
+ " Re-run init with saved values to reconcile provider infrastructure:",
31301
+ " $ hot-updater init --provider aws --env-file .env.hotupdater",
31302
+ "",
31303
+ " --env-file disables init prompts and reports every missing value.",
31304
+ " Interactive init asks once before saving credential inputs for reuse.",
31305
+ "",
31306
+ "Provider inputs:",
31307
+ ...INIT_PROVIDER_NAMES.flatMap((providerName) => {
31308
+ const provider = INIT_PROVIDER_PACKAGES[providerName].definition;
31309
+ return [`\n${providerName} (${provider.label})`, ...Object.values(provider.inputs).map(formatInput)];
31310
+ })
31311
+ ].join("\n");
31312
+ //#endregion
30870
31313
  //#region src/commands/patch.ts
30871
31314
  const createPatch = async (options) => {
30872
31315
  printBanner$1();
@@ -54621,16 +55064,11 @@ const parseRolloutCohortCount = (value) => {
54621
55064
  };
54622
55065
  const program = new Command$1();
54623
55066
  program.name("hot-updater").description(banner(version)).version(version);
54624
- program.command("init").description("Initialize Hot Updater").addOption(new Option("--provider <provider>", "provider to use; skips the prompt").choices([
54625
- "cloudflare",
54626
- "aws",
54627
- "supabase",
54628
- "firebase"
54629
- ])).addOption(new Option("--build <plugin>", "build plugin to use; skips the prompt").choices([
55067
+ program.command("init").description("Initialize Hot Updater").addOption(new Option("--provider <provider>", "provider to use; skips the prompt").choices(INIT_PROVIDER_NAMES)).addOption(new Option("--build <plugin>", "build plugin to use; skips the prompt").choices([
54630
55068
  "bare",
54631
55069
  "rock",
54632
55070
  "expo"
54633
- ])).action((options) => init(options));
55071
+ ])).option("--env-file <path>", "load saved init inputs and fail if any are missing").addHelpText("after", initHelp).action((options) => init(options));
54634
55072
  program.command("doctor").description("Check the health of Hot Updater").option("--server-base-url <url>", "server base URL used by update checks (doctor appends /version)").option("--json", "output machine-readable doctor result").action(handleDoctor);
54635
55073
  const fingerprintCommand = program.command("fingerprint").description("Generate fingerprint");
54636
55074
  fingerprintCommand.action(handleFingerprint);
@@ -54671,8 +55109,11 @@ program.command("deploy").description("deploy a new version").addOption(platform
54671
55109
  try {
54672
55110
  return normalizeRolloutPercentage(value);
54673
55111
  } catch (error) {
54674
- p.log.error(error.message);
54675
- process.exit(1);
55112
+ if (error instanceof Error) {
55113
+ p.log.error(error.message);
55114
+ process.exit(1);
55115
+ }
55116
+ throw error;
54676
55117
  }
54677
55118
  }).default(100)).addOption(interactiveCommandOption).addOption(new Option("-c, --channel <channel>", "specify the channel to deploy").default(DEFAULT_CHANNEL)).addOption(new Option("-m, --message <message>", "Specify a custom message for this deployment. If not provided, the latest git commit message will be used as the deployment message")).action(async (options) => deploy(options));
54678
55119
  program.command("patch").description("create patch artifacts for a deployed bundle").requiredOption("-b, --bundle-id <bundleId>", "target bundle id that should receive the patch artifact").requiredOption("--base-bundle-id <baseBundleId>", "older bundle id to use as the patch base").addOption(platformCommandOption).addOption(interactiveCommandOption).addOption(new Option("-c, --channel <channel>", "specify the channel used to load config").default(DEFAULT_CHANNEL)).action(async (options) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hot-updater",
3
3
  "type": "module",
4
- "version": "0.35.7",
4
+ "version": "0.35.9",
5
5
  "engines": {
6
6
  "node": ">=20.19.0"
7
7
  },
@@ -49,13 +49,13 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "jiti": "2.6.1",
52
- "@hot-updater/android-helper": "0.35.7",
53
- "@hot-updater/apple-helper": "0.35.7",
54
- "@hot-updater/cli-tools": "0.35.7",
55
- "@hot-updater/console": "0.35.7",
56
- "@hot-updater/core": "0.35.7",
57
- "@hot-updater/plugin-core": "0.35.7",
58
- "@hot-updater/server": "0.35.7"
52
+ "@hot-updater/apple-helper": "0.35.9",
53
+ "@hot-updater/core": "0.35.9",
54
+ "@hot-updater/plugin-core": "0.35.9",
55
+ "@hot-updater/server": "0.35.9",
56
+ "@hot-updater/cli-tools": "0.35.9",
57
+ "@hot-updater/console": "0.35.9",
58
+ "@hot-updater/android-helper": "0.35.9"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@bacons/xcode": "1.0.0-alpha.24",
@@ -77,13 +77,14 @@
77
77
  "semver": "^7.6.3",
78
78
  "tsdown": "0.21.6",
79
79
  "sql-formatter": "15.6.10",
80
- "typescript": "6.0.2",
81
- "@hot-updater/cloudflare": "0.35.7",
82
- "@hot-updater/server": "0.35.7",
83
- "@hot-updater/aws": "0.35.7",
84
- "@hot-updater/supabase": "0.35.7",
85
- "@hot-updater/firebase": "0.35.7",
86
- "@hot-updater/test-utils": "0.35.7"
80
+ "@typescript/native": "npm:typescript@7.0.2",
81
+ "typescript": "npm:@typescript/typescript6@6.0.2",
82
+ "@hot-updater/cloudflare": "0.35.9",
83
+ "@hot-updater/server": "0.35.9",
84
+ "@hot-updater/firebase": "0.35.9",
85
+ "@hot-updater/supabase": "0.35.9",
86
+ "@hot-updater/test-utils": "0.35.9",
87
+ "@hot-updater/aws": "0.35.9"
87
88
  },
88
89
  "peerDependencies": {
89
90
  "@expo/fingerprint": "*",