From bf915fc86c8b22c73adce75292192628b2cbf122 Mon Sep 17 00:00:00 2001 From: Kaushik Wavhal Date: Wed, 10 Dec 2025 17:32:03 -0500 Subject: [PATCH 1/9] add skip override flag --- README.md | 2 + src/cli/main.spec.ts | 107 +++++++++++++++++++++++++++++++++++++++++++ src/cli/main.ts | 38 +++++++++++++-- 3 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 src/cli/main.spec.ts diff --git a/README.md b/README.md index 2e66bcb..c78598d 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ The arguments after the override command are the flags you want to `+` enable or You can run `yarn build-flags ota-override` instead of "override" to do the same but also consider the branch name in two supported CI environments: Github and Gitlab. Use the `ota.branches` array in the flags.yml to setup that matching and branch-based enablement. +Use `--skip-if-env ` to skip the override command when a certain environment variable is set. This can be useful in CI job runs. + ### Set Flags in CI & for Static Builds To set flags for EAS builds, set the `EXPO_BUILD_FLAGS` environment variable in `eas.json` for your profile. This value will be available to the config plugin at build time in EAS when you add it to your `app.json` plugins array: diff --git a/src/cli/main.spec.ts b/src/cli/main.spec.ts new file mode 100644 index 0000000..2af1549 --- /dev/null +++ b/src/cli/main.spec.ts @@ -0,0 +1,107 @@ +import { describe, it, expect } from "@jest/globals"; +import { getOptionValue, parseArgs } from "./main"; + +describe("getOptionValue", () => { + it("should return the value after the option", () => { + const args = ["yarn", "build-flags", "--skip-if-env", "EAS_BUILD"]; + expect(getOptionValue(args, "--skip-if-env")).toBe("EAS_BUILD"); + }); + + it("should return undefined if option is not present", () => { + const args = ["yarn", "build-flags", "override", "+flag"]; + expect(getOptionValue(args, "--skip-if-env")).toBeUndefined(); + }); + + it("should return undefined if option is last argument (no value)", () => { + const args = ["yarn", "build-flags","+flag", "--skip-if-env"]; + expect(getOptionValue(args, "--skip-if-env")).toBeUndefined(); + }); +}); + +describe("parseArgs", () => { + describe("command parsing", () => { + it("should parse override command", () => { + const args = ["yarn", "build-flags", "override"]; + const result = parseArgs(args); + expect(result.command).toBe("override"); + }); + + it("should parse init command", () => { + const args = ["yarn", "build-flags", "init"]; + const result = parseArgs(args); + expect(result.command).toBe("init"); + }); + + it("should parse ota-override command", () => { + const args = ["yarn", "build-flags", "ota-override"]; + const result = parseArgs(args); + expect(result.command).toBe("ota-override"); + }); + }); + + describe("flag parsing", () => { + it("should parse flags to enable with + prefix", () => { + const args = ["yarn", "build-flags", "override", "+feature1", "+feature2"]; + const result = parseArgs(args); + expect(result.flagsToEnable).toEqual(new Set(["feature1", "feature2"])); + }); + + it("should parse flags to disable with - prefix", () => { + const args = ["yarn", "build-flags", "override", "-feature1", "-feature2"]; + const result = parseArgs(args); + expect(result.flagsToDisable).toEqual(new Set(["feature1", "feature2"])); + }); + + it("should parse mixed enable and disable flags", () => { + const args = [ + "yarn", + "build-flags", + "override", + "+enableThis", + "-disableThis", + ]; + const result = parseArgs(args); + expect(result.flagsToEnable).toEqual(new Set(["enableThis"])); + expect(result.flagsToDisable).toEqual(new Set(["disableThis"])); + }); + }); + + describe("--skip-if-env option", () => { + it("should extract skipIfEnv when option comes after command", () => { + const args = [ + "yarn", + "build-flags", + "override", + "--skip-if-env", + "EAS_BUILD", + ]; + const result = parseArgs(args); + expect(result.skipIfEnv).toBe("EAS_BUILD"); + expect(result.command).toBe("override"); + }); + + it("should handle flags alongside --skip-if-env", () => { + const args = [ + "yarn", + "build-flags", + "override", + "+feature", + "--skip-if-env", + "CI", + "-other", + ]; + const result = parseArgs(args); + expect(result.command).toBe("override"); + expect(result.skipIfEnv).toBe("CI"); + expect(result.flagsToEnable).toEqual(new Set(["feature"])); + expect(result.flagsToDisable).toEqual(new Set(["other"])); + }); + + it("should return undefined skipIfEnv when option is not provided", () => { + const args = ["yarn", "build-flags", "override", "+flag"]; + const result = parseArgs(args); + expect(result.skipIfEnv).toBeUndefined(); + }); + }); +}); + diff --git a/src/cli/main.ts b/src/cli/main.ts index 12495df..8f1f7c2 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -4,11 +4,27 @@ import { readFile, writeFile } from "fs/promises"; import { BuildFlags } from "../api/BuildFlags"; import { generateOverrides } from "../api/generateOverrides"; -const parseArgs = (args: string[]) => { +export const getOptionValue = (args: string[], option: string): string | undefined => { + const index = args.indexOf(option); + return index !== -1 ? args[index + 1] : undefined; +}; + +export const parseArgs = (args: string[]) => { let command; const flagsToDisable = new Set(); const flagsToEnable = new Set(); - args.slice(2).forEach((arg) => { + + const skipIfEnv = getOptionValue(args, "--skip-if-env"); + + const skipIfEnvIndex = args.indexOf("--skip-if-env"); + const filteredArgs = args + .slice(2) + .filter((_, i) => { + const originalIndex = i + 2; + return originalIndex !== skipIfEnvIndex && originalIndex !== skipIfEnvIndex + 1; + }); + + filteredArgs.forEach((arg) => { if (arg.startsWith("-")) { flagsToDisable.add(arg.replace("-", "")); return; @@ -21,18 +37,22 @@ const parseArgs = (args: string[]) => { command = arg; }); - return { command, flagsToDisable, flagsToEnable }; + + return { command, flagsToDisable, flagsToEnable, skipIfEnv }; }; const printHelp = (command?: string) => { if (command) { console.error(`Unknown command: ${command}`); } - console.log(`Usage: build-flags [command] [flags] + console.log(`Usage: build-flags [command] [options] [flags] Commands: init Initialize flags.yml and buildFlags.ts for the project in the current directory override Override default flags with provided flag arguments: +flag to enable, -flag to disable ota-override Override default flags like "override" but also consider branch matching rules + + Options: + --skip-if-env Skip execution if the specified environment variable is set `); }; @@ -71,7 +91,9 @@ const initFlagsFile = async () => { }; const run = async () => { - const { command, flagsToDisable, flagsToEnable } = parseArgs(process.argv); + const { command, flagsToDisable, flagsToEnable, skipIfEnv } = parseArgs( + process.argv + ); if (command === "init") { await initFlagsFile(); @@ -79,6 +101,12 @@ const run = async () => { } if (command === "override") { + if (skipIfEnv && process.env[skipIfEnv] !== undefined) { + console.log( + `Skipping build-flags override because ${skipIfEnv} is set in environment` + ); + return; + } await generateOverrides({ flagsToEnable, flagsToDisable }); return; } From 702394a72b1c8e934deb3f161169ad668a2afe31 Mon Sep 17 00:00:00 2001 From: Kaushik Wavhal Date: Wed, 10 Dec 2025 17:40:50 -0500 Subject: [PATCH 2/9] move skip logic to its own function --- src/cli/main.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/cli/main.ts b/src/cli/main.ts index 8f1f7c2..565a41f 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -9,6 +9,14 @@ export const getOptionValue = (args: string[], option: string): string | undefin return index !== -1 ? args[index + 1] : undefined; }; +export const shouldSkip = (envKey: string | undefined): boolean => { + if (envKey && process.env[envKey] !== undefined) { + console.log(`Skipping build-flags override because ${envKey} is set in environment`); + return true; + } + return false; +}; + export const parseArgs = (args: string[]) => { let command; const flagsToDisable = new Set(); @@ -101,10 +109,7 @@ const run = async () => { } if (command === "override") { - if (skipIfEnv && process.env[skipIfEnv] !== undefined) { - console.log( - `Skipping build-flags override because ${skipIfEnv} is set in environment` - ); + if (shouldSkip(skipIfEnv)) { return; } await generateOverrides({ flagsToEnable, flagsToDisable }); From bb40df38b30ca086a295dd2b10739a24fe6790cf Mon Sep 17 00:00:00 2001 From: Kaushik Wavhal Date: Wed, 10 Dec 2025 17:41:39 -0500 Subject: [PATCH 3/9] bump package version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b65256a..61290de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "expo-build-flags", - "version": "0.8.2", + "version": "0.8.3", "repository": "https://github.com/fullscript/expo-build-flags", "author": "Wes Johnson ", "license": "MIT", From ec1ec97715db6f765a8737967028c001a9aec17c Mon Sep 17 00:00:00 2001 From: Kaushik Wavhal Date: Wed, 10 Dec 2025 17:44:04 -0500 Subject: [PATCH 4/9] undo manual bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 61290de..b65256a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "expo-build-flags", - "version": "0.8.3", + "version": "0.8.2", "repository": "https://github.com/fullscript/expo-build-flags", "author": "Wes Johnson ", "license": "MIT", From 664d4120ee3f598208cd293343087b2cee0e2f5e Mon Sep 17 00:00:00 2001 From: Kaushik Wavhal Date: Thu, 11 Dec 2025 09:45:02 -0500 Subject: [PATCH 5/9] Update README.md Co-authored-by: Wes Johnson --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c78598d..37e5c93 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ The arguments after the override command are the flags you want to `+` enable or You can run `yarn build-flags ota-override` instead of "override" to do the same but also consider the branch name in two supported CI environments: Github and Gitlab. Use the `ota.branches` array in the flags.yml to setup that matching and branch-based enablement. -Use `--skip-if-env ` to skip the override command when a certain environment variable is set. This can be useful in CI job runs. +Use `--skip-if-env ` passing the name of the environment variable to check. If the variable has a truthy value the CLI command will be a no-op/skipped. This can be useful in CI contexts like EAS where other processes may handle generating the runtime build flags. In the case of EAS you could use `--skip-if-env EAS_BUILD` ### Set Flags in CI & for Static Builds From 2f529097da287158bf53d0703ab43e0bca778828 Mon Sep 17 00:00:00 2001 From: Kaushik Wavhal Date: Thu, 11 Dec 2025 09:56:30 -0500 Subject: [PATCH 6/9] move shouldSkip check to top of run as a global option to skip --- src/cli/main.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cli/main.ts b/src/cli/main.ts index 565a41f..365dd8f 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -103,15 +103,16 @@ const run = async () => { process.argv ); + if (shouldSkip(skipIfEnv)) { + return; + } + if (command === "init") { await initFlagsFile(); return; } if (command === "override") { - if (shouldSkip(skipIfEnv)) { - return; - } await generateOverrides({ flagsToEnable, flagsToDisable }); return; } From d64395135150102316229f1cfe1f4ea0fad4c379 Mon Sep 17 00:00:00 2001 From: Kaushik Wavhal Date: Thu, 11 Dec 2025 10:08:20 -0500 Subject: [PATCH 7/9] simplify args processing --- src/cli/main.spec.ts | 19 +------------------ src/cli/main.ts | 23 ++++++++--------------- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/src/cli/main.spec.ts b/src/cli/main.spec.ts index 2af1549..ac73a10 100644 --- a/src/cli/main.spec.ts +++ b/src/cli/main.spec.ts @@ -1,22 +1,5 @@ import { describe, it, expect } from "@jest/globals"; -import { getOptionValue, parseArgs } from "./main"; - -describe("getOptionValue", () => { - it("should return the value after the option", () => { - const args = ["yarn", "build-flags", "--skip-if-env", "EAS_BUILD"]; - expect(getOptionValue(args, "--skip-if-env")).toBe("EAS_BUILD"); - }); - - it("should return undefined if option is not present", () => { - const args = ["yarn", "build-flags", "override", "+flag"]; - expect(getOptionValue(args, "--skip-if-env")).toBeUndefined(); - }); - - it("should return undefined if option is last argument (no value)", () => { - const args = ["yarn", "build-flags","+flag", "--skip-if-env"]; - expect(getOptionValue(args, "--skip-if-env")).toBeUndefined(); - }); -}); +import { parseArgs } from "./main"; describe("parseArgs", () => { describe("command parsing", () => { diff --git a/src/cli/main.ts b/src/cli/main.ts index 365dd8f..6393182 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -4,11 +4,6 @@ import { readFile, writeFile } from "fs/promises"; import { BuildFlags } from "../api/BuildFlags"; import { generateOverrides } from "../api/generateOverrides"; -export const getOptionValue = (args: string[], option: string): string | undefined => { - const index = args.indexOf(option); - return index !== -1 ? args[index + 1] : undefined; -}; - export const shouldSkip = (envKey: string | undefined): boolean => { if (envKey && process.env[envKey] !== undefined) { console.log(`Skipping build-flags override because ${envKey} is set in environment`); @@ -19,20 +14,18 @@ export const shouldSkip = (envKey: string | undefined): boolean => { export const parseArgs = (args: string[]) => { let command; + let skipIfEnv: string | undefined; const flagsToDisable = new Set(); const flagsToEnable = new Set(); - const skipIfEnv = getOptionValue(args, "--skip-if-env"); - - const skipIfEnvIndex = args.indexOf("--skip-if-env"); - const filteredArgs = args - .slice(2) - .filter((_, i) => { - const originalIndex = i + 2; - return originalIndex !== skipIfEnvIndex && originalIndex !== skipIfEnvIndex + 1; - }); + const argsCopy = [...args].slice(2); + const skipIfEnvIndex = argsCopy.indexOf("--skip-if-env"); + if (skipIfEnvIndex !== -1) { + skipIfEnv = argsCopy[skipIfEnvIndex + 1]; + argsCopy.splice(skipIfEnvIndex, 2); + } - filteredArgs.forEach((arg) => { + argsCopy.forEach((arg) => { if (arg.startsWith("-")) { flagsToDisable.add(arg.replace("-", "")); return; From 1f2438004c7979bc5e130dd8c0751b46308c3f04 Mon Sep 17 00:00:00 2001 From: Kaushik Wavhal Date: Thu, 11 Dec 2025 10:59:14 -0500 Subject: [PATCH 8/9] Fix array copy --- src/cli/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/main.ts b/src/cli/main.ts index 6393182..a98dddc 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -18,7 +18,7 @@ export const parseArgs = (args: string[]) => { const flagsToDisable = new Set(); const flagsToEnable = new Set(); - const argsCopy = [...args].slice(2); + const argsCopy = args.slice(2); const skipIfEnvIndex = argsCopy.indexOf("--skip-if-env"); if (skipIfEnvIndex !== -1) { skipIfEnv = argsCopy[skipIfEnvIndex + 1]; From 380973c2457b5681e3c218199d7727b6a2735566 Mon Sep 17 00:00:00 2001 From: Kaushik Wavhal Date: Thu, 11 Dec 2025 10:59:48 -0500 Subject: [PATCH 9/9] Update src/cli/main.ts Co-authored-by: Wes Johnson --- src/cli/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/main.ts b/src/cli/main.ts index a98dddc..69c117f 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -6,7 +6,7 @@ import { generateOverrides } from "../api/generateOverrides"; export const shouldSkip = (envKey: string | undefined): boolean => { if (envKey && process.env[envKey] !== undefined) { - console.log(`Skipping build-flags override because ${envKey} is set in environment`); + console.log(`Skipping build-flags command because ${envKey} is set in environment (--skip-if-env)`); return true; } return false;