fix(companion): prevent localhost URL from being baked into production extension builds (#26032)

* fix(companion): separate dev and prod build scripts to prevent localhost in production

- Add ext:build-dev and ext:build-prod scripts with clear separation
- Add BUILD_FOR_STORE flag to force production URL in store builds
- Add visual build indicators (store / dev) for clarity
- Prevent EXPO_PUBLIC_COMPANION_DEV_URL from leaking into production builds

* update commands to work for ci

* update version

---------

Co-authored-by: Volnei Munhoz <volnei@cal.com>
This commit is contained in:
Dhairyashil Shinde
2025-12-18 19:19:04 +00:00
committed by GitHub
co-authored by Volnei Munhoz
parent f291c1c4e7
commit c8a8c37b45
3 changed files with 78 additions and 57 deletions
@@ -56,6 +56,9 @@ export default defineContentScript({
iframeContainer.style.pointerEvents = "none";
const iframe = document.createElement("iframe");
// URL is determined at build time:
// - ext:build-dev → uses EXPO_PUBLIC_COMPANION_DEV_URL (localhost)
// - ext:build-prod → uses https://companion.cal.com
const COMPANION_URL =
(import.meta.env.EXPO_PUBLIC_COMPANION_DEV_URL as string) || "https://companion.cal.com";
iframe.src = COMPANION_URL;
+5 -1
View File
@@ -1,7 +1,7 @@
{
"name": "cal-companion",
"displayName": "Cal.com Companion",
"version": "1.7.1",
"version": "1.7.3",
"main": "index.js",
"scripts": {
"start": "expo start",
@@ -9,10 +9,14 @@
"ios": "expo run:ios",
"web": "expo start --web",
"ext:build": "wxt build",
"ext:build-prod": "BUILD_FOR_STORE=true wxt build",
"build:firefox": "wxt build -b firefox",
"build:firefox-prod": "BUILD_FOR_STORE=true wxt build -b firefox",
"build:safari": "wxt build -b safari",
"build:safari-prod": "BUILD_FOR_STORE=true wxt build -b safari",
"ext": "wxt",
"ext:zip": "wxt zip",
"ext:zip-prod": "BUILD_FOR_STORE=true wxt zip",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\"",
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,md}\"",
"prepare": "cd .. && husky companion/.husky"
+70 -56
View File
@@ -1,7 +1,8 @@
import { defineConfig } from "wxt";
const devUrl = process.env.EXPO_PUBLIC_COMPANION_DEV_URL;
const isLocalDev = Boolean(devUrl && devUrl.includes("localhost"));
// BUILD_FOR_STORE=true is set by ext:build-prod, ext:zip-prod, etc.
// Forces production URL (https://companion.cal.com) and excludes localhost permissions
const isBuildForStore = process.env.BUILD_FOR_STORE === "true";
export default defineConfig({
hooks: {
@@ -19,7 +20,7 @@ export default defineConfig({
outDir: ".output",
manifest: {
name: "Cal.com Companion",
version: "1.7.1",
version: "1.7.3",
description: "Your calendar companion for quick booking and scheduling",
permissions: ["activeTab", "storage", "identity"],
host_permissions: [
@@ -27,10 +28,11 @@ export default defineConfig({
"https://api.cal.com/*",
"https://app.cal.com/*",
"https://mail.google.com/*",
...(isLocalDev ? ["http://localhost:*/*"] : []),
// Include localhost permission for dev builds (needed for iframe to load)
...(!isBuildForStore ? ["http://localhost:*/*"] : []),
],
content_security_policy: {
extension_pages: isLocalDev
extension_pages: !isBuildForStore
? "script-src 'self'; object-src 'self'; frame-src 'self' https://companion.cal.com http://localhost:*"
: "script-src 'self'; object-src 'self'; frame-src 'self' https://companion.cal.com",
},
@@ -48,57 +50,69 @@ export default defineConfig({
"128": "icon-128.png",
},
},
vite: () => ({
resolve: {
alias: {
"react-native": "react-native-web",
},
},
define: {
global: "globalThis",
__DEV__: JSON.stringify(false),
"import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID": JSON.stringify(
process.env.EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID
),
"import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI": JSON.stringify(
process.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI
),
"import.meta.env.EXPO_PUBLIC_COMPANION_DEV_URL": JSON.stringify(
process.env.EXPO_PUBLIC_COMPANION_DEV_URL || ""
),
"import.meta.env.EXPO_PUBLIC_CACHE_STALE_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_CACHE_STALE_TIME_MINUTES
),
"import.meta.env.EXPO_PUBLIC_CACHE_GC_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_CACHE_GC_TIME_MINUTES
),
"import.meta.env.EXPO_PUBLIC_BOOKINGS_CACHE_STALE_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_BOOKINGS_CACHE_STALE_TIME_MINUTES
),
"import.meta.env.EXPO_PUBLIC_EVENT_TYPES_CACHE_STALE_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_EVENT_TYPES_CACHE_STALE_TIME_MINUTES
),
"import.meta.env.EXPO_PUBLIC_SCHEDULES_CACHE_STALE_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_SCHEDULES_CACHE_STALE_TIME_MINUTES
),
"import.meta.env.EXPO_PUBLIC_USER_PROFILE_CACHE_STALE_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_USER_PROFILE_CACHE_STALE_TIME_MINUTES
),
...(process.env.NODE_ENV !== "production" && process.env.EXPO_PUBLIC_CAL_API_KEY
? {
"import.meta.env.EXPO_PUBLIC_CAL_API_KEY": JSON.stringify(
process.env.EXPO_PUBLIC_CAL_API_KEY
),
}
: {}),
},
optimizeDeps: {
include: ["react-native-web"],
esbuildOptions: {
loader: {
".js": "jsx",
vite: () => {
// Determine companion URL based on build type
const devUrl = isBuildForStore ? "" : process.env.EXPO_PUBLIC_COMPANION_DEV_URL || "";
const isLocalDev = Boolean(devUrl && devUrl.includes("localhost"));
// Log build mode for clarity
if (isBuildForStore) {
console.log("\n🏪 STORE BUILD: Using https://companion.cal.com\n");
} else if (isLocalDev) {
console.log(`\n🔧 DEV BUILD: Using ${devUrl}\n`);
}
return {
resolve: {
alias: {
"react-native": "react-native-web",
},
},
},
}),
define: {
global: "globalThis",
__DEV__: JSON.stringify(false),
"import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID": JSON.stringify(
process.env.EXPO_PUBLIC_CALCOM_OAUTH_CLIENT_ID
),
"import.meta.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI": JSON.stringify(
process.env.EXPO_PUBLIC_CALCOM_OAUTH_REDIRECT_URI
),
// Use devUrl which respects BUILD_FOR_STORE flag
"import.meta.env.EXPO_PUBLIC_COMPANION_DEV_URL": JSON.stringify(devUrl),
"import.meta.env.EXPO_PUBLIC_CACHE_STALE_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_CACHE_STALE_TIME_MINUTES
),
"import.meta.env.EXPO_PUBLIC_CACHE_GC_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_CACHE_GC_TIME_MINUTES
),
"import.meta.env.EXPO_PUBLIC_BOOKINGS_CACHE_STALE_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_BOOKINGS_CACHE_STALE_TIME_MINUTES
),
"import.meta.env.EXPO_PUBLIC_EVENT_TYPES_CACHE_STALE_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_EVENT_TYPES_CACHE_STALE_TIME_MINUTES
),
"import.meta.env.EXPO_PUBLIC_SCHEDULES_CACHE_STALE_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_SCHEDULES_CACHE_STALE_TIME_MINUTES
),
"import.meta.env.EXPO_PUBLIC_USER_PROFILE_CACHE_STALE_TIME_MINUTES": JSON.stringify(
process.env.EXPO_PUBLIC_USER_PROFILE_CACHE_STALE_TIME_MINUTES
),
...(process.env.NODE_ENV !== "production" && process.env.EXPO_PUBLIC_CAL_API_KEY
? {
"import.meta.env.EXPO_PUBLIC_CAL_API_KEY": JSON.stringify(
process.env.EXPO_PUBLIC_CAL_API_KEY
),
}
: {}),
},
optimizeDeps: {
include: ["react-native-web"],
esbuildOptions: {
loader: {
".js": "jsx",
},
},
},
};
},
});