Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: one database supports multiple channel #169

Merged
merged 23 commits into from
Mar 9, 2025
Merged
4 changes: 2 additions & 2 deletions docs/docs/guide/hot-updater/checkForUpdate.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export interface UpdateInfo {
id: string;
shouldForceUpdate: boolean;
fileUrl: string | null;
fileHash: string | null;
message: string | null;
status: UpdateStatus;
}
```
Expand All @@ -67,7 +67,7 @@ export interface UpdateInfo {
"shouldForceUpdate": true,
"status": "UPDATE",
"fileUrl": "https://example.com/bundles/update.bundle",
"fileHash": "abc123",
"message": "This is a test message",
}
```

13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@nx/js": "^20.3.0",
"@rslib/core": "^0.4.0",
"nx": "20.3.0",
"@cloudflare/vitest-pool-workers": "^0.7.7",
"@nx/js": "^20.5.0",
"@rslib/core": "^0.5.3",
"nx": "20.5.0",
"rimraf": "^5.0.7",
"tsup": "^8.3.6",
"typescript": "^5.7.2",
"vitest": "^2.1.8"
"tsup": "^8.4.0",
"typescript": "^5.8.2",
"vitest": "^3.0.8"
},
"pnpm": {
"overrides": {
Expand Down
5 changes: 2 additions & 3 deletions packages/console/src-server/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,15 @@ export const bundleSchema = v.object({
fileHash: v.string(),
gitCommitHash: v.nullable(v.string()),
message: v.nullable(v.string()),
channel: v.string(),
});

let config: Config | null = null;
let databasePlugin: DatabasePlugin | null = null;

const prepareConfig = async () => {
if (!config) {
config = await loadConfig({
platform: "console",
});
config = await loadConfig(null);
databasePlugin =
(await config?.database({
cwd: getCwd(),
Expand Down
86 changes: 68 additions & 18 deletions packages/core/src/test-utils/setupGetUpdateInfoTestSuite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import { NIL_UUID } from "../uuid";

const DEFAULT_BUNDLE = {
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
platform: "ios",
gitCommitHash: null,
message: null,
fileHash: "hash",
channel: "production",
} as const;

const INIT_BUNDLE_ROLLBACK_UPDATE_INFO = {
fileHash: null,
fileUrl: null,
id: NIL_UUID,
message: null,
shouldForceUpdate: true,
status: "ROLLBACK",
} as const;
Expand Down Expand Up @@ -46,7 +47,7 @@ export const setupGetUpdateInfoTestSuite = ({
expect(update).toStrictEqual({
id: "00000000-0000-0000-0000-000000000001",
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
shouldForceUpdate: false,
status: "UPDATE",
});
Expand Down Expand Up @@ -110,7 +111,7 @@ export const setupGetUpdateInfoTestSuite = ({
shouldForceUpdate: false,
status: "UPDATE",
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
});
});

Expand All @@ -135,7 +136,7 @@ export const setupGetUpdateInfoTestSuite = ({
shouldForceUpdate: true,
status: "UPDATE",
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
});
});

Expand All @@ -160,7 +161,7 @@ export const setupGetUpdateInfoTestSuite = ({
shouldForceUpdate: false,
status: "UPDATE",
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
});
});

Expand All @@ -184,7 +185,7 @@ export const setupGetUpdateInfoTestSuite = ({
id: "00000000-0000-0000-0000-000000000005",
shouldForceUpdate: false,
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
status: "UPDATE",
});
});
Expand Down Expand Up @@ -214,7 +215,7 @@ export const setupGetUpdateInfoTestSuite = ({
});
expect(update).toStrictEqual({
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
id: "00000000-0000-0000-0000-000000000001",
shouldForceUpdate: false,
status: "UPDATE",
Expand Down Expand Up @@ -281,7 +282,7 @@ export const setupGetUpdateInfoTestSuite = ({
fileUrl: "20240722210327/build.zip",
fileHash:
"a5cbf59a627759a88d472c502423ff55a4f6cd1aafeed3536f6a5f6e870c2290",
message: "",
message: "hi",
targetAppVersion: "1.0",
id: "00000000-0000-0000-0000-000000000001",
enabled: true,
Expand All @@ -298,8 +299,7 @@ export const setupGetUpdateInfoTestSuite = ({
shouldForceUpdate: false,
status: "UPDATE",
fileUrl: "20240722210327/build.zip",
fileHash:
"a5cbf59a627759a88d472c502423ff55a4f6cd1aafeed3536f6a5f6e870c2290",
message: "hi",
});
});

Expand Down Expand Up @@ -357,7 +357,7 @@ export const setupGetUpdateInfoTestSuite = ({
platform: "ios",
});
expect(update).toStrictEqual({
fileHash: "hash",
message: "hello",
fileUrl: "http://example.com/bundle.zip",
id: "00000000-0000-0000-0000-000000000001",
shouldForceUpdate: true,
Expand Down Expand Up @@ -397,7 +397,7 @@ export const setupGetUpdateInfoTestSuite = ({
});
expect(update).toStrictEqual({
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
id: "00000000-0000-0000-0000-000000000003",
shouldForceUpdate: false,
status: "UPDATE",
Expand Down Expand Up @@ -451,7 +451,7 @@ export const setupGetUpdateInfoTestSuite = ({
});
expect(update).toStrictEqual({
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
id: "00000000-0000-0000-0000-000000000005",
shouldForceUpdate: false,
status: "UPDATE",
Expand Down Expand Up @@ -517,7 +517,7 @@ export const setupGetUpdateInfoTestSuite = ({

expect(update).toStrictEqual({
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
id: "00000000-0000-0000-0000-000000000001",
shouldForceUpdate: true, // Cause the app to reload
status: "ROLLBACK",
Expand Down Expand Up @@ -597,7 +597,7 @@ export const setupGetUpdateInfoTestSuite = ({
expect(update).toStrictEqual({
id: "0195715d-42db-7475-9204-31819efc2f1d", // 2025-03-07T16:08:12.251Z
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
shouldForceUpdate: false,
status: "UPDATE",
});
Expand Down Expand Up @@ -759,7 +759,7 @@ export const setupGetUpdateInfoTestSuite = ({
expect(update).toStrictEqual({
id: "0195716c-82f5-7e5e-ac8c-d4fbf5bc7555", // 2025-03-07T16:24:51.701Z
fileUrl: "http://example.com/bundle.zip",
fileHash: "hash",
message: "hello",
shouldForceUpdate: true,
status: "ROLLBACK",
});
Expand All @@ -784,4 +784,54 @@ export const setupGetUpdateInfoTestSuite = ({
});
expect(update).toBeNull();
});

it("does not update bundles from different channels", async () => {
const bundles: Bundle[] = [
{
...DEFAULT_BUNDLE,
targetAppVersion: "1.0",
shouldForceUpdate: false,
enabled: true,
channel: "beta",
id: "00000000-0000-0000-0000-000000000001",
},
];

const update = await getUpdateInfo(bundles, {
appVersion: "1.0",
bundleId: NIL_UUID,
platform: "ios",
channel: "production",
});

expect(update).toBeNull();
});

it("updates bundles from the same channel", async () => {
const bundles: Bundle[] = [
{
...DEFAULT_BUNDLE,
targetAppVersion: "1.0",
shouldForceUpdate: false,
enabled: true,
channel: "beta",
id: "00000000-0000-0000-0000-000000000001",
},
];

const update = await getUpdateInfo(bundles, {
appVersion: "1.0",
bundleId: NIL_UUID,
platform: "ios",
channel: "beta",
});

expect(update).toStrictEqual({
id: "00000000-0000-0000-0000-000000000001",
fileUrl: "http://example.com/bundle.zip",
message: "hello",
shouldForceUpdate: false,
status: "UPDATE",
});
});
};
40 changes: 38 additions & 2 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ export interface Bundle {
* The message of the bundle.
*/
message: string | null;
/**
* The name of the channel where the bundle is deployed.
*
* Examples:
* - production: Production channel for end users
* - development: Development channel for testing
* - staging: Staging channel for quality assurance before production
* - app-name: Channel for specific app instances (e.g., my-app, app-test)
*
* Different channel values can be used based on each app's requirements.
*/
channel: string;
}

type SnakeCase<S extends string> = S extends `${infer T}${infer U}`
Expand Down Expand Up @@ -66,13 +78,37 @@ export interface UpdateInfo {
id: string;
shouldForceUpdate: boolean;
fileUrl: string | null;
fileHash: string | null;
message: string | null;
status: UpdateStatus;
}

export interface GetBundlesArgs {
platform: Platform;
/**
* The current bundle id of the app.
*/
bundleId: string;
minBundleId?: string;
/**
* The current app version.
*/
appVersion: string;
/**
* Minimum bundle id that should be used.
* This value is generated at build time via getMinBundleId().
*
* @default "00000000-0000-0000-0000-000000000000"
*/
minBundleId?: string;
/**
* The name of the channel where the bundle is deployed.
*
* @default "production"
*
* Examples:
* - production: Production channel for end users
* - development: Development channel for testing
* - staging: Staging channel for quality assurance before production
* - app-name: Channel for specific app instances (e.g., my-app, app-test)
*/
channel?: string;
}
4 changes: 1 addition & 3 deletions packages/hot-updater/src/commands/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ export const CONSOLE_DEFAULT_PORT = 1422;
export const getConsolePort = async (config?: Config) => {
let $config: Config | undefined | null = config;
if (!$config) {
$config = await loadConfig({
platform: "console",
});
$config = await loadConfig(null);
}
return $config?.console?.port ?? CONSOLE_DEFAULT_PORT;
};
Expand Down
12 changes: 10 additions & 2 deletions packages/hot-updater/src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export interface DeployOptions {
platform?: Platform;
forceUpdate: boolean;
interactive: boolean;
channel: string;
message?: string;
}

export const deploy = async (options: DeployOptions) => {
Expand Down Expand Up @@ -55,7 +57,9 @@ export const deploy = async (options: DeployOptions) => {
return;
}

const config = await loadConfig({ platform });
const channel = options.channel;

const config = await loadConfig({ platform, channel });
if (!config) {
console.error("No config found. Please run `hot-updater init` first.");
process.exit(1);
Expand Down Expand Up @@ -120,12 +124,15 @@ export const deploy = async (options: DeployOptions) => {
buildResult: null,
};

p.log.info(`Channel: ${channel}`);

await p.tasks([
{
title: `📦 Building Bundle (${buildPlugin.name})`,
task: async () => {
taskRef.buildResult = await buildPlugin.build({
platform: platform,
channel,
});
bundlePath = path.join(getCwd(), "bundle.zip");

Expand Down Expand Up @@ -183,10 +190,11 @@ export const deploy = async (options: DeployOptions) => {
fileUrl,
fileHash,
gitCommitHash,
message: gitMessage,
message: options?.message ?? gitMessage,
targetAppVersion,
id: bundleId,
enabled: true,
channel,
});
await databasePlugin.commitBundle();
} catch (e) {
Expand Down
Loading