mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
feat(desktop): add protected-build Bestie experiment (#6902)
## Summary Introduces a protected-build boundary for the default-off Bestie experiment without adding any Bestie product surface. - Official OSS builds select an empty protected-feature module and emit no Bestie/Chief metadata or implementation content. - Protected internal builds select a separate module graph containing the Bestie experiment definition. - Within an internal build, Bestie remains disabled until the user opts in under Settings → Experiments. - The production build runs an artifact matrix and fails if OSS output contains protected content or internal output lacks the Bestie manifest. ## Build contract | Build variant | User opt-in | Result | | --- | --- | --- | | Official OSS | Any/forged | Bestie absent from the compiled artifact | | Protected internal | Off | Bestie available but disabled | | Protected internal | On | Bestie enabled | The companion protected-release change is squareup/buzz-releases#91. It sets `VITE_BUZZ_BESTIE=1`, requires that exact value, forwards it into the signed macOS build, and asserts the contract in release validation. ## Why this is separate This gives later Bestie PRs one build-selected import seam. Protected implementations must be reachable only from the internal module so they never enter the official OSS module graph. ## Non-goals - No Bestie persona or provisioning - No sidebar, app-chrome, or message-toolbar UI - No entitlement or secrecy claim: the source is public; this boundary controls official Block artifacts ## Verification - Exact commit `523cf49ced03cba9be43836a54d6aa5d6923cc82` - Full `just ci`: 5,673 Desktop tests, 2,773 Tauri tests, 1,860 mobile tests, Rust/Tauri/web/mobile static checks and builds - OSS production artifact: scanner confirms no `Bestie`, `Chief of Staff`, or `builtin:bestie` content - Internal production artifact: scanner confirms the protected Bestie manifest is emitted - Both build orders verified; `dist` retains the requested variant for Vite/Tauri packaging --------- Signed-off-by: Arjun Mahanti <arjun@squareup.com> Signed-off-by: Fizz <fizz@buzz.local> Signed-off-by: Fizz <dae5f6af70b8695a8b83c8deae555f63be41630ec2b8cd493e41a439c9527dd8@buzz.block.builderlab.xyz> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Fizz <fizz@buzz.local> Co-authored-by: Fizz <dae5f6af70b8695a8b83c8deae555f63be41630ec2b8cd493e41a439c9527dd8@buzz.block.builderlab.xyz>
This commit is contained in:
151
desktop/scripts/build-protected-feature-artifacts.mjs
Normal file
151
desktop/scripts/build-protected-feature-artifacts.mjs
Normal file
@@ -0,0 +1,151 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import {
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
readdirSync,
|
||||
rmSync,
|
||||
statSync,
|
||||
} from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { loadEnv } from "vite";
|
||||
|
||||
const desktopRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
);
|
||||
const vitePackageJsonPath = fileURLToPath(
|
||||
import.meta.resolve("vite/package.json"),
|
||||
);
|
||||
const vitePackage = JSON.parse(readFileSync(vitePackageJsonPath, "utf8"));
|
||||
const viteEntrypoint = path.resolve(
|
||||
path.dirname(vitePackageJsonPath),
|
||||
vitePackage.bin.vite,
|
||||
);
|
||||
|
||||
function buildVariant({ internal, output }) {
|
||||
const env = {
|
||||
...process.env,
|
||||
// Pin both children explicitly. Deleting the OSS value lets Vite reload
|
||||
// `=1` from .env.local or a mode-specific env file.
|
||||
VITE_BUZZ_BESTIE: internal ? "1" : "0",
|
||||
};
|
||||
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[viteEntrypoint, "build", "--outDir", output, "--emptyOutDir"],
|
||||
{
|
||||
cwd: desktopRoot,
|
||||
env,
|
||||
stdio: "inherit",
|
||||
},
|
||||
);
|
||||
if (result.error) throw result.error;
|
||||
if (result.status !== 0) {
|
||||
throw new Error(
|
||||
`${internal ? "internal" : "OSS"} desktop build failed with status ${result.status}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function emittedText(root) {
|
||||
const chunks = [];
|
||||
const visit = (candidate) => {
|
||||
const stat = statSync(candidate);
|
||||
if (stat.isDirectory()) {
|
||||
for (const child of readdirSync(candidate)) {
|
||||
visit(path.join(candidate, child));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (/\.(?:css|html|js|json)$/u.test(candidate)) {
|
||||
chunks.push(readFileSync(candidate, "utf8"));
|
||||
}
|
||||
};
|
||||
visit(root);
|
||||
return chunks.join("\n");
|
||||
}
|
||||
|
||||
export function assertArtifactContract({ ossOutput, internalOutput }) {
|
||||
const ossText = emittedText(ossOutput);
|
||||
const internalText = emittedText(internalOutput);
|
||||
const protectedContent = /\bbestie\b|chief of staff|builtin:bestie/iu;
|
||||
const internalManifestMarker =
|
||||
"Try a personal agent that is always close at hand";
|
||||
|
||||
if (protectedContent.test(ossText)) {
|
||||
throw new Error(
|
||||
"Official OSS desktop artifact contains protected Bestie/Chief content",
|
||||
);
|
||||
}
|
||||
if (!internalText.includes(internalManifestMarker)) {
|
||||
throw new Error(
|
||||
"Protected internal desktop artifact is missing the Bestie manifest",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Resolve the requested output with the same precedence used by Vite config. */
|
||||
export function selectInternalVariant({ processEnv, modeEnv }) {
|
||||
return (processEnv.VITE_BUZZ_BESTIE ?? modeEnv.VITE_BUZZ_BESTIE) === "1";
|
||||
}
|
||||
|
||||
/** Build and inspect both graphs, leaving the requested variant in dist. */
|
||||
export function buildArtifactMatrix({
|
||||
selectedInternalVariant,
|
||||
selectedOutput,
|
||||
alternateOutput,
|
||||
build = buildVariant,
|
||||
}) {
|
||||
// Build the unselected variant outside dist first, then leave the requested
|
||||
// variant in dist for Vite/Tauri's ordinary packaging contract.
|
||||
build({
|
||||
internal: !selectedInternalVariant,
|
||||
output: alternateOutput,
|
||||
});
|
||||
build({
|
||||
internal: selectedInternalVariant,
|
||||
output: selectedOutput,
|
||||
});
|
||||
|
||||
assertArtifactContract({
|
||||
ossOutput: selectedInternalVariant ? alternateOutput : selectedOutput,
|
||||
internalOutput: selectedInternalVariant ? selectedOutput : alternateOutput,
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
const selectedInternalVariant = selectInternalVariant({
|
||||
processEnv: process.env,
|
||||
modeEnv: loadEnv("production", desktopRoot, ""),
|
||||
});
|
||||
const scratchRoot = mkdtempSync(
|
||||
path.join(tmpdir(), "buzz-protected-feature-artifacts-"),
|
||||
);
|
||||
const selectedOutput = process.env.BUZZ_PROTECTED_BUILD_OUTPUT
|
||||
? path.resolve(process.env.BUZZ_PROTECTED_BUILD_OUTPUT)
|
||||
: path.join(desktopRoot, "dist");
|
||||
const alternateOutput = path.join(scratchRoot, "alternate");
|
||||
|
||||
try {
|
||||
buildArtifactMatrix({
|
||||
selectedInternalVariant,
|
||||
selectedOutput,
|
||||
alternateOutput,
|
||||
});
|
||||
} finally {
|
||||
rmSync(scratchRoot, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Protected feature artifact matrix passed; dist contains the ${selectedInternalVariant ? "internal" : "OSS"} variant.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
process.argv[1] &&
|
||||
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
||||
) {
|
||||
main();
|
||||
}
|
||||
63
desktop/scripts/tauri-command.mjs
Normal file
63
desktop/scripts/tauri-command.mjs
Normal file
@@ -0,0 +1,63 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const desktopRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
);
|
||||
const tauriPackageJsonPath = fileURLToPath(
|
||||
import.meta.resolve("@tauri-apps/cli/package.json"),
|
||||
);
|
||||
const tauriPackage = JSON.parse(readFileSync(tauriPackageJsonPath, "utf8"));
|
||||
const defaultTauriEntrypoint = path.resolve(
|
||||
path.dirname(tauriPackageJsonPath),
|
||||
tauriPackage.bin.tauri,
|
||||
);
|
||||
|
||||
function runTauri(args, options = {}) {
|
||||
const entrypoint =
|
||||
process.env.BUZZ_TAURI_CLI_ENTRYPOINT ?? defaultTauriEntrypoint;
|
||||
const result = spawnSync(process.execPath, [entrypoint, ...args], {
|
||||
cwd: desktopRoot,
|
||||
env: { ...process.env, ...options.env },
|
||||
stdio: "inherit",
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
return result.status ?? 1;
|
||||
}
|
||||
|
||||
export function runTauriCommand(args) {
|
||||
if (args[0] !== "build") return runTauri(args);
|
||||
|
||||
// Tauri runs beforeBuildCommand and then consumes frontendDist. Give the
|
||||
// entire invocation a private directory so concurrent OSS/internal packages
|
||||
// cannot replace one another's assets between those two operations.
|
||||
const invocationRoot = mkdtempSync(
|
||||
path.join(tmpdir(), "buzz-tauri-package-assets-"),
|
||||
);
|
||||
const frontendDist = path.join(invocationRoot, "dist");
|
||||
const outputOverride = JSON.stringify({ build: { frontendDist } });
|
||||
|
||||
try {
|
||||
const delimiterIndex = args.indexOf("--");
|
||||
const configIndex = delimiterIndex === -1 ? args.length : delimiterIndex;
|
||||
const tauriArgs = [...args];
|
||||
tauriArgs.splice(configIndex, 0, "--config", outputOverride);
|
||||
return runTauri(tauriArgs, {
|
||||
env: { BUZZ_PROTECTED_BUILD_OUTPUT: frontendDist },
|
||||
});
|
||||
} finally {
|
||||
rmSync(invocationRoot, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
process.argv[1] &&
|
||||
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
||||
) {
|
||||
process.exitCode = runTauriCommand(process.argv.slice(2));
|
||||
}
|
||||
Reference in New Issue
Block a user