From d6b11a3aaa299cc5c88b25701555559824c34192 Mon Sep 17 00:00:00 2001 From: Pierre beucher Date: Mon, 6 Jan 2025 07:50:23 +0100 Subject: [PATCH 1/6] chore: fix tests --- .github/workflows/test-unit.yml | 24 ++++++++++++++---------- cloudypad.sh | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index f8e8f887..7846ec61 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -84,19 +84,22 @@ jobs: # Test the install script and cloudypad.sh for current commit - name: Run install script run: | - curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/${{ github.sha }}/install.sh | CLOUDYPAD_VERSION=${{ github.sha }} sh + curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/${{ github.sha }}/install.sh | bash + echo " ==== .bachrc content ===" + cat ~/.bashrc + echo " ==== .bachrc content ===" + bash -i -c 'echo $PATH && which cloudypad' - - name: Check version - run: | - export PATH=$PATH:$HOME/.cloudypad/bin - export CLOUDYPAD_CONTAINER_NO_TTY=true - export CLOUDYPAD_CLI_LAUNCHER_DEBUG=true - cloudypad --version - test-install-container: name: Test install/run in containers if: "!startsWith(github.head_ref, 'release/')" # Install test will fail on release branch as version won't mach runs-on: ubuntu-22.04 + + # env: + # # Container do not have a tty, this will ensure script behave appropriately + # CLOUDYPAD_CONTAINER_NO_TTY: true + # # Enable debug logs + # CLOUDYPAD_CLI_LAUNCHER_DEBUG: true steps: - name: Checkout repository @@ -119,5 +122,6 @@ jobs: - uses: nixbuild/nix-quick-install-action@v27 - run: nix build .# - - - run: CLOUDYPAD_CONTAINER_NO_TTY=true nix run .# -- --version + + # Disable this for now as latets version does not respect no-tty properly + # - run: CLOUDYPAD_CONTAINER_NO_TTY=true nix run .# -- --version diff --git a/cloudypad.sh b/cloudypad.sh index 05d67be3..ccde357c 100755 --- a/cloudypad.sh +++ b/cloudypad.sh @@ -101,7 +101,7 @@ run_cloudypad_docker() { # Set interactive+tty by default # no tty if CLOUDYPAD_CONTAINER_NO_TTY is set (for CI) if [ -n "$CLOUDYPAD_CONTAINER_NO_TTY" ]; then - cmd="$cmd -t" + cmd="$cmd" else cmd="$cmd -it" fi From 80cbc3d1a31c5c479720d3a129a28ccee710acfb Mon Sep 17 00:00:00 2001 From: Pierre beucher Date: Mon, 6 Jan 2025 08:13:18 +0100 Subject: [PATCH 2/6] refactor: ConfigManager more decoupled function --- src/core/config/manager.ts | 42 +++++++++++++++------- src/index.ts | 9 ++--- src/program.ts | 2 +- src/tools/analytics/initializer.ts | 32 ++++++----------- test/unit/core/config/manager.spec.ts | 50 ++++++++++++++++++--------- 5 files changed, 75 insertions(+), 60 deletions(-) diff --git a/src/core/config/manager.ts b/src/core/config/manager.ts index 6e509d29..272fa99e 100644 --- a/src/core/config/manager.ts +++ b/src/core/config/manager.ts @@ -4,6 +4,8 @@ import yaml from 'js-yaml' import { z } from 'zod' import { DataRootDirManager } from '../data-dir' import { getLogger } from '../../log/utils' +import { v4 as uuidv4 } from 'uuid' +import * as lodash from 'lodash' const PostHogConfigSchema = z.object({ distinctId: z.string() @@ -12,7 +14,7 @@ const PostHogConfigSchema = z.object({ const AnalyticsConfigSchema = z.object({ enabled: z.boolean().describe("Whether analytics is enabled."), promptedApproval: z.boolean().describe("Whether user has been prompted for analytics approval yet."), - posthog: PostHogConfigSchema.optional() + posthog: PostHogConfigSchema }).refine( (data) => !data.enabled || (data.enabled && data.posthog !== undefined), { @@ -21,7 +23,7 @@ const AnalyticsConfigSchema = z.object({ } ) -const GlobalCliConfigSchemaV1 = z.object({ +export const GlobalCliConfigSchemaV1 = z.object({ version: z.literal("1"), analytics: AnalyticsConfigSchema }).describe("PostHog analytics config. https://posthog.com") @@ -30,11 +32,12 @@ export type PostHogConfig = z.infer export type GlobalCliConfigV1 = z.infer export type AnalyticsConfig = z.infer -export const DEFAULT_CONFIG: GlobalCliConfigV1 = { +export const BASE_DEFAULT_CONFIG: GlobalCliConfigV1 = { version: "1", analytics: { - enabled: false, - promptedApproval: false + enabled: true, + promptedApproval: false, + posthog: { distinctId: "dummy" } } } @@ -72,11 +75,25 @@ export class ConfigManager { init(): void { if (!fs.existsSync(this.configPath)) { this.logger.info(`CLI config not found at ${this.configPath}. Creating default config...`) - this.writeConfigSafe(DEFAULT_CONFIG) + + const initConfig = lodash.merge( + {}, + BASE_DEFAULT_CONFIG, + { + analytics: { + posthog: { + distinctId: uuidv4() + } + } + } + ) + this.writeConfigSafe(initConfig) + + this.logger.debug(`Generated default config: ${JSON.stringify(initConfig)}`) this.logger.info(`Default config created at ${this.configPath}`) } - this.logger.debug(`Found existing config at ${this.configPath}.`) + this.logger.debug(`Init: found existing config at ${this.configPath}. Not overwriting it.`) } load(): GlobalCliConfigV1 { @@ -93,20 +110,19 @@ export class ConfigManager { this.writeConfigSafe(updatedConfig) } - enableAnalyticsPosthHog(posthog: PostHogConfig): void { - this.logger.debug(`Enabling PostHog analytics: ${posthog}`) + setAnalyticsPosthHog(posthog: PostHogConfig): void { + this.logger.debug(`Setting PostHog analytics: ${JSON.stringify(posthog)}`) const updatedConfig = this.load() - updatedConfig.analytics.enabled = true updatedConfig.analytics.posthog = posthog this.writeConfigSafe(updatedConfig) } - disableAnalytics(): void { - this.logger.debug(`Disabling analytics`) + setAnalyticsEnabled(enable: boolean): void { + this.logger.debug(`Setting analytics enabled: ${enable}`) const updatedConfig = this.load() - updatedConfig.analytics.enabled = false + updatedConfig.analytics.enabled = enable this.writeConfigSafe(updatedConfig) } diff --git a/src/index.ts b/src/index.ts index e4480255..362d868d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,21 +11,16 @@ import { ConfigManager } from "./core/config/manager" import { getLogger } from "./log/utils" import { buildProgram } from "./program" -import { AnalyticsManager } from "./tools/analytics/manager" import { AnalyticsInitializer } from "./tools/analytics/initializer" +import { AnalyticsManager } from "./tools/analytics/manager" const logger = getLogger("main") async function main(){ try { - - // Init config ConfigManager.getInstance().init() - - // Prepare analytics - await new AnalyticsInitializer().prepareAnalyticsConfig() + await new AnalyticsInitializer().promptAnalyticsConsentUnlessAlreadyDone() - // Run program const program = buildProgram() await program.parseAsync(process.argv) diff --git a/src/program.ts b/src/program.ts index a8b9bd36..61df3b54 100644 --- a/src/program.ts +++ b/src/program.ts @@ -22,7 +22,7 @@ export function buildProgram(){ "Verbosity level (0: silly, 1: trace, 2: debug, 3: info, 4: warn, 5: error, 6: fatal). Alternatively, use CLOUDYPAD_LOG_LEVEL environment variable.", (v) => { setLogVerbosity(Number.parseInt(v)) }) .configureHelp({ showGlobalOptions: true}) - .version(CLOUDYPAD_VERSION); + .version(CLOUDYPAD_VERSION) const createCmd = program .command('create') diff --git a/src/tools/analytics/initializer.ts b/src/tools/analytics/initializer.ts index 9994a4bc..7af88e93 100644 --- a/src/tools/analytics/initializer.ts +++ b/src/tools/analytics/initializer.ts @@ -1,15 +1,16 @@ import { confirm } from '@inquirer/prompts' -import { v4 as uuidv4 } from 'uuid' import { ConfigManager } from '../../core/config/manager' export class AnalyticsInitializer { - async promptApproval(): Promise{ + private configManager = ConfigManager.getInstance() + + private async promptApprovalAndSetAnalytics(): Promise{ // Skip prompt if no TTY to avoid failures if(!process.stdin.isTTY) { - return true + return } const approveAnalytics = await confirm({ @@ -17,28 +18,15 @@ export class AnalyticsInitializer { default: true, }) - return approveAnalytics + this.configManager.setAnalyticsEnabled(approveAnalytics) } - /** - * Prepare global configuration to enable PostHog analytics. Prompt user for approval if not already done. - */ - async prepareAnalyticsConfig() { - const configManager = ConfigManager.getInstance() - const config = configManager.load() - + async promptAnalyticsConsentUnlessAlreadyDone() { + const config = this.configManager.load() + if(!config.analytics.promptedApproval) { - const enableAnalytics = await this.promptApproval() - - if(enableAnalytics){ - // Generate unique distinct if not already exists - const distinctId = config.analytics.posthog?.distinctId ? config.analytics.posthog?.distinctId : uuidv4() - configManager.enableAnalyticsPosthHog({ distinctId: distinctId }) - } else { - configManager.disableAnalytics() - } - - configManager.updateAnalyticsPromptedApproval(true) + await this.promptApprovalAndSetAnalytics() + this.configManager.updateAnalyticsPromptedApproval(true) } } } diff --git a/test/unit/core/config/manager.spec.ts b/test/unit/core/config/manager.spec.ts index 49952aa9..c9cb27f5 100644 --- a/test/unit/core/config/manager.spec.ts +++ b/test/unit/core/config/manager.spec.ts @@ -1,7 +1,7 @@ import * as fs from 'fs' import * as assert from 'assert' import * as yaml from 'js-yaml' -import { ConfigManager, DEFAULT_CONFIG, GlobalCliConfigV1 } from '../../../../src/core/config/manager' +import { ConfigManager, BASE_DEFAULT_CONFIG, GlobalCliConfigV1, GlobalCliConfigSchemaV1 } from '../../../../src/core/config/manager' import { createTempTestDir } from '../../utils' import path from 'path' import lodash from 'lodash' @@ -15,18 +15,35 @@ describe('ConfigManager', () => { const configManager = new ConfigManager(tmpDataRootDir) configManager.init() + // Ensure config has been written and is parseable wirh Zod const expectConfigPath = path.join(tmpDataRootDir, "config.yml") assert.ok(fs.existsSync(expectConfigPath), 'Default config file should be created') - const writtenConfig = yaml.load(fs.readFileSync(expectConfigPath, 'utf-8')) - assert.deepStrictEqual(writtenConfig, DEFAULT_CONFIG, 'Written config should match default config') - // Updating config should be taken into account - configManager.updateAnalyticsPromptedApproval(true) + const writtenConfigRaw = yaml.load(fs.readFileSync(expectConfigPath, 'utf-8')) + const writtenConfigParsed = GlobalCliConfigSchemaV1.safeParse(writtenConfigRaw) + assert.equal(writtenConfigParsed.success, true, 'Config should be parseable with Zod') + + const writtenConfig = writtenConfigParsed.data! + + assert.equal(writtenConfig.version, "1") + assert.equal(writtenConfig.analytics.enabled, BASE_DEFAULT_CONFIG.analytics.enabled) + assert.equal(writtenConfig.analytics.promptedApproval, BASE_DEFAULT_CONFIG.analytics.promptedApproval) + // assert distinctId looks like a UUID + assert.match(writtenConfig.analytics.posthog.distinctId, /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/) + + // Check loaded config match file content + const loadedConfig = configManager.load() + assert.deepEqual(loadedConfig, writtenConfig, 'Loaded config should match file content') + + // Calling init() again should not overwrite + // Update config (and check it's been updated) + // and call init() again to see if existing config is not overwritten + configManager.updateAnalyticsPromptedApproval(!loadedConfig.analytics.promptedApproval) const expectUpdatedConfig = lodash.merge({}, - DEFAULT_CONFIG, + loadedConfig, { analytics: { - promptedApproval: true + promptedApproval: !loadedConfig.analytics.promptedApproval } } ) @@ -57,19 +74,18 @@ describe('ConfigManager', () => { }) it('should update PostHog analytics', () => { + configUpdateTestManager.setAnalyticsPosthHog({ distinctId: "distinct-id-unit-test"}) + const configAfterUpdate = configUpdateTestManager.load() + assert.equal(configAfterUpdate.analytics.posthog?.distinctId, "distinct-id-unit-test") + }) + it('should update analytics enabled', () => { const configBeforeUpdate = configUpdateTestManager.load() - assert.equal(configBeforeUpdate.analytics.posthog, undefined) + assert.equal(configBeforeUpdate.analytics.enabled, true) - // Update and check OK - configUpdateTestManager.enableAnalyticsPosthHog({ distinctId: "dummy"}) + configUpdateTestManager.setAnalyticsEnabled(false) const configAfterUpdate = configUpdateTestManager.load() - assert.equal(configAfterUpdate.analytics.posthog?.distinctId, "dummy") - assert.equal(configAfterUpdate.analytics.enabled, true) - - configUpdateTestManager.disableAnalytics() - const configAfterUpdateBis = configUpdateTestManager.load() - assert.equal(configAfterUpdateBis.analytics.enabled, false) + assert.equal(configAfterUpdate.analytics.enabled, false) }) }) @@ -84,7 +100,7 @@ describe('ConfigManager', () => { } } } - const dummyConfig = lodash.merge({}, DEFAULT_CONFIG, dummyConfigDiff) + const dummyConfig = lodash.merge({}, BASE_DEFAULT_CONFIG, dummyConfigDiff) const tmpDataRootDir = createTempTestDir("config-manager-load") const expectConfigPath = path.join(tmpDataRootDir, "config.yml") From 15d26fa9e06d01012aa45a765cae4c61da7cd586 Mon Sep 17 00:00:00 2001 From: Pierre beucher Date: Mon, 6 Jan 2025 08:21:52 +0100 Subject: [PATCH 3/6] analytics: send anonymous event during install --- install.sh | 279 ++++++++++++++++++++----------------- test/shell/test-install.sh | 4 + 2 files changed, 152 insertions(+), 131 deletions(-) diff --git a/install.sh b/install.sh index 9410121a..de85e4a6 100755 --- a/install.sh +++ b/install.sh @@ -8,135 +8,152 @@ DEFAULT_CLOUDYPAD_SCRIPT_REF=v0.11.0 CLOUDYPAD_HOME=${CLOUDYPAD_HOME:-"$HOME/.cloudypad"} CLOUDYPAD_SCRIPT_REF=${CLOUDYPAD_SCRIPT_REF:-$DEFAULT_CLOUDYPAD_SCRIPT_REF} -echo "Installing Cloudy Pad version $CLOUDYPAD_SCRIPT_REF" - -CLOUDYPAD_SCRIPT_URL="https://raw.githubusercontent.com/PierreBeucher/cloudypad/$CLOUDYPAD_SCRIPT_REF/cloudypad.sh" -# === - -# Constants, do not override -INSTALL_DIR="$CLOUDYPAD_HOME/bin" -SCRIPT_NAME="cloudypad" -SCRIPT_PATH="$INSTALL_DIR/cloudypad" - -# Check if cloudypad is already in PATH -if [ -n "$(which cloudypad)" ]; then - CURRENT_CLOUDYPAD_PATH=$(which cloudypad) - - # Read from /dev/tty to ensure read will work even if script is piped to shell such as install.sh | sh - read -p "cloudypad is already installed at ${CURRENT_CLOUDYPAD_PATH}. Do you want to overwrite it? (y/N): " CONFIRM < /dev/tty - - if [[ "$CONFIRM" != "y" ]]; then - echo "Installation aborted." - exit 1 - fi -fi - -if [ "$CLOUDYPAD_INSTALL_SKIP_DOCKER_CHECK" != "true" ]; then - - # Check if Docker is installed and usable - if ! docker --version > /dev/null; then - echo "Docker is not installed or running 'docker --version' failed. Please make sure you have a working Docker installation. See https://docs.docker.com/engine/install/" - exit 1 - fi - - # Check if Docker daemon is accessible - if ! docker info > /dev/null; then - echo "Docker is installed but not usable. Have you added yourself to docker group? See https://docs.docker.com/engine/install/linux-postinstall/" - echo "You might need to run 'sudo usermod -aG docker \$USER' and restart your logout / log back before being able to use Docker" - exit 1 +INSTALL_POSTHOG_DISTINCT_ID="cli-install-$(date +%Y-%m-%d-%H-%M-%S)-$(tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 5)" +INSTALL_POSTHOG_API_KEY="phc_caJIOD8vW5727svQf90FNgdALIyYYouwEDEVh3BI1IH" + +# Sends anonymous analytics event during installation +send_analytics_event() { + event=$1 + if [ "$CLOUDYPAD_ANALYTICS_DISABLE" != "true" ]; then + curl -v -L --header "Content-Type: application/json" -d "{ + \"api_key\": \"$INSTALL_POSTHOG_API_KEY\", + \"event\": \"$event\", + \"distinct_id\": \"$INSTALL_POSTHOG_DISTINCT_ID\", + \"properties\": { + \"\$process_person_profile\": false, + \"os_name\": \"$(uname -s)\", + \"os_arch\": \"$(uname -m)\" + } + }" https://eu.i.posthog.com/capture/ fi -fi - -# Create secure directory for Cloudy Pad home as it may contain sensitive data -mkdir -p "$CLOUDYPAD_HOME" -chmod 0700 $CLOUDYPAD_HOME - -mkdir -p "$INSTALL_DIR" - -echo "Downloading $CLOUDYPAD_SCRIPT_URL..." - -if command -v curl >/dev/null 2>&1; then - curl --fail -sSL -o "$SCRIPT_PATH" "$CLOUDYPAD_SCRIPT_URL" -elif command -v wget >/dev/null 2>&1; then - wget -O "$SCRIPT_PATH" "$CLOUDYPAD_SCRIPT_URL" -else - echo "Error: Neither curl nor wget is available to download Cloudy Pad. Please install wget or curl and try again." - exit 1 -fi - -chmod +x "$SCRIPT_PATH" - -echo "Downloading Cloudy Pad container images..." - -if [ ! -n "$(which docker)" ]; then - echo - echo "WARNING: Docker CLI not found. Cloudy Pad will still get installed but you'll need Docker to run it." - echo " See https://docs.docker.com/engine/install/ for Docker installation instructions." - echo -else - $SCRIPT_PATH download-container-images -fi - -# Identify shell to update *.rc file with PATH update -SHELL_NAME=$(basename "${SHELL}") -STARTUP_FILE="" - -case "${SHELL_NAME}" in - "bash") - # Terminal.app on macOS prefers .bash_profile to .bashrc, so we prefer that - # file when trying to put our export into a profile. On *NIX, .bashrc is - # preferred as it is sourced for new interactive shells. - if [ "$(uname)" != "Darwin" ]; then - if [ -e "${HOME}/.bashrc" ]; then - STARTUP_FILE="${HOME}/.bashrc" - elif [ -e "${HOME}/.bash_profile" ]; then - STARTUP_FILE="${HOME}/.bash_profile" - fi - else - if [ -e "${HOME}/.bash_profile" ]; then - STARTUP_FILE="${HOME}/.bash_profile" - elif [ -e "${HOME}/.bashrc" ]; then - STARTUP_FILE="${HOME}/.bashrc" - fi - fi - ;; - "zsh") - STARTUP_FILE="${ZDOTDIR:-$HOME}/.zshrc" - ;; - *) - echo - echo "WARNING: Couldn't identify startup file to use (such as .bashrc or .zshrc) for your current shell." - echo " Detected shell from \$SHELL=$SHELL environment variable: '$SHELL_NAME'" - echo " To finalize installation please ensure $INSTALL_DIR is on your \$PATH" - echo " Otherwise you may not be able to run Cloudy Pad CLI." - echo " Alternatively, use directly $SCRIPT_PATH to run Cloudy Pad CLI." - echo " If you think this is a bug, please create an issue: https://github.com/PierreBeucher/cloudypad/issues" - ;; -esac - -if [ -n "${STARTUP_FILE}" ]; then - # Create startup file if it does not exists. Rare situation but may happen - touch "${STARTUP_FILE}" - - LINE_TO_ADD="export PATH=\$PATH:${INSTALL_DIR}" - if ! grep -q "# add CloudyPad CLI PATH" "${STARTUP_FILE}"; then - echo "Adding ${INSTALL_DIR} to \$PATH in ${STARTUP_FILE}" - printf "\\n# add CloudyPad CLI PATH\\n%s\\n\\n" "${LINE_TO_ADD}" >> "${STARTUP_FILE}" - fi - - echo "Successfully installed Cloudy Pad 🥳" - echo - echo "Restart your shell to add cloudypad on your PATH or run:" - echo - echo " source $STARTUP_FILE" - echo -fi - -echo -echo "Get started by creating a Cloudy Pad instance:" -echo -echo " cloudypad create" -echo -echo "If you enjoy Cloudy Pad, please star us ⭐ https://github.com/PierreBeucher/cloudypad" -echo "🐛 Found a bug? Create an issue: https://github.com/PierreBeucher/cloudypad/issues" -echo \ No newline at end of file +} + +send_analytics_event "cli_install_start" + +# echo "Installing Cloudy Pad version $CLOUDYPAD_SCRIPT_REF" + +# CLOUDYPAD_SCRIPT_URL="https://raw.githubusercontent.com/PierreBeucher/cloudypad/$CLOUDYPAD_SCRIPT_REF/cloudypad.sh" +# # === + +# # Constants, do not override +# INSTALL_DIR="$CLOUDYPAD_HOME/bin" +# SCRIPT_NAME="cloudypad" +# SCRIPT_PATH="$INSTALL_DIR/cloudypad" + +# # Check if cloudypad is already in PATH +# if [ -n "$(which cloudypad)" ]; then +# CURRENT_CLOUDYPAD_PATH=$(which cloudypad) + +# # Read from /dev/tty to ensure read will work even if script is piped to shell such as install.sh | sh +# read -p "cloudypad is already installed at ${CURRENT_CLOUDYPAD_PATH}. Do you want to overwrite it? (y/N): " CONFIRM < /dev/tty + +# if [[ "$CONFIRM" != "y" ]]; then +# echo "Installation aborted." +# exit 1 +# fi +# fi + +# if [ "$CLOUDYPAD_INSTALL_SKIP_DOCKER_CHECK" != "true" ]; then + +# # Check if Docker is installed and usable +# if ! docker --version > /dev/null; then +# echo "Docker is not installed or running 'docker --version' failed. Please make sure you have a working Docker installation. See https://docs.docker.com/engine/install/" +# exit 1 +# fi + +# # Check if Docker daemon is accessible +# if ! docker info > /dev/null; then +# echo "Docker is installed but not usable. Have you added yourself to docker group? See https://docs.docker.com/engine/install/linux-postinstall/" +# echo "You might need to run 'sudo usermod -aG docker \$USER' and restart your logout / log back before being able to use Docker" +# exit 1 +# fi +# fi + +# # Create secure directory for Cloudy Pad home as it may contain sensitive data +# mkdir -p "$CLOUDYPAD_HOME" +# chmod 0700 $CLOUDYPAD_HOME + +# mkdir -p "$INSTALL_DIR" + +# echo "Downloading $CLOUDYPAD_SCRIPT_URL..." + +# if command -v curl >/dev/null 2>&1; then +# curl --fail -sSL -o "$SCRIPT_PATH" "$CLOUDYPAD_SCRIPT_URL" +# elif command -v wget >/dev/null 2>&1; then +# wget -O "$SCRIPT_PATH" "$CLOUDYPAD_SCRIPT_URL" +# else +# echo "Error: Neither curl nor wget is available to download Cloudy Pad. Please install wget or curl and try again." +# exit 1 +# fi + +# chmod +x "$SCRIPT_PATH" + +# echo "Downloading Cloudy Pad container images..." + +# $SCRIPT_PATH download-container-images + +# # Identify shell to update *.rc file with PATH update +# SHELL_NAME=$(basename "${SHELL}") +# STARTUP_FILE="" + +# case "${SHELL_NAME}" in +# "bash") +# # Terminal.app on macOS prefers .bash_profile to .bashrc, so we prefer that +# # file when trying to put our export into a profile. On *NIX, .bashrc is +# # preferred as it is sourced for new interactive shells. +# if [ "$(uname)" != "Darwin" ]; then +# if [ -e "${HOME}/.bashrc" ]; then +# STARTUP_FILE="${HOME}/.bashrc" +# elif [ -e "${HOME}/.bash_profile" ]; then +# STARTUP_FILE="${HOME}/.bash_profile" +# fi +# else +# if [ -e "${HOME}/.bash_profile" ]; then +# STARTUP_FILE="${HOME}/.bash_profile" +# elif [ -e "${HOME}/.bashrc" ]; then +# STARTUP_FILE="${HOME}/.bashrc" +# fi +# fi +# ;; +# "zsh") +# STARTUP_FILE="${ZDOTDIR:-$HOME}/.zshrc" +# ;; +# *) +# echo +# echo "WARNING: Couldn't identify startup file to use (such as .bashrc or .zshrc) for your current shell." +# echo " Detected shell from \$SHELL=$SHELL environment variable: '$SHELL_NAME'" +# echo " To finalize installation please ensure $INSTALL_DIR is on your \$PATH" +# echo " Otherwise you may not be able to run Cloudy Pad CLI." +# echo " Alternatively, use directly $SCRIPT_PATH to run Cloudy Pad CLI." +# echo " If you think this is a bug, please create an issue: https://github.com/PierreBeucher/cloudypad/issues" +# ;; +# esac + +# if [ -n "${STARTUP_FILE}" ]; then +# # Create startup file if it does not exists. Rare situation but may happen +# touch "${STARTUP_FILE}" + +# LINE_TO_ADD="export PATH=\$PATH:${INSTALL_DIR}" +# if ! grep -q "# add CloudyPad CLI PATH" "${STARTUP_FILE}"; then +# echo "Adding ${INSTALL_DIR} to \$PATH in ${STARTUP_FILE}" +# printf "\\n# add CloudyPad CLI PATH\\n%s\\n\\n" "${LINE_TO_ADD}" >> "${STARTUP_FILE}" +# fi + +# echo "Successfully installed Cloudy Pad 🥳" +# echo +# echo "Restart your shell to add cloudypad on your PATH or run:" +# echo +# echo " source $STARTUP_FILE" +# echo +# fi + +# echo +# echo "Get started by creating a Cloudy Pad instance:" +# echo +# echo " cloudypad create" +# echo +# echo "If you enjoy Cloudy Pad, please star us ⭐ https://github.com/PierreBeucher/cloudypad" +# echo "🐛 Found a bug? Create an issue: https://github.com/PierreBeucher/cloudypad/issues" +# echo + +send_analytics_event "cli_install_finish" diff --git a/test/shell/test-install.sh b/test/shell/test-install.sh index ea254cd4..b36fdacc 100755 --- a/test/shell/test-install.sh +++ b/test/shell/test-install.sh @@ -9,6 +9,7 @@ docker run -i \ -v $PWD:/cloudypad -w /cloudypad \ -v /var/run/docker.sock:/var/run/docker.sock \ -e CLOUDYPAD_CONTAINER_NO_TTY="true" \ + -e CLOUDYPAD_ANALYTICS_DISABLE="true" \ cloudypad-test-install-ubuntu:local \ bash -e -i -c '/cloudypad/install.sh && source /root/.bashrc && echo $PATH && which cloudypad || (echo "Cloudypad not found on PATH after install" && false)' @@ -20,6 +21,7 @@ docker run -i \ -v $PWD:/cloudypad -w /cloudypad \ -v /var/run/docker.sock:/var/run/docker.sock \ -e CLOUDYPAD_CONTAINER_NO_TTY="true" \ + -e CLOUDYPAD_ANALYTICS_DISABLE="true" \ cloudypad-test-install-ubuntu:local \ sh -c '/cloudypad/install.sh && PATH=$PATH:/root/.cloudypad/bin && echo $PATH && which cloudypad || (echo "Cloudypad not found on PATH after install" && false)' @@ -31,6 +33,7 @@ docker run -i \ -v $PWD:/cloudypad -w /cloudypad \ -v /var/run/docker.sock:/var/run/docker.sock \ -e CLOUDYPAD_CONTAINER_NO_TTY="true" \ + -e CLOUDYPAD_ANALYTICS_DISABLE="true" \ cloudypad-test-install-debian:local \ bash -i -c '/cloudypad/install.sh && source /root/.bashrc && echo $PATH && which cloudypad || (echo "Cloudypad not found on PATH after install" && false)' @@ -43,5 +46,6 @@ docker run -i \ -v $PWD:/cloudypad -w /cloudypad \ -v /var/run/docker.sock:/var/run/docker.sock \ -e CLOUDYPAD_CONTAINER_NO_TTY="true" \ + -e CLOUDYPAD_ANALYTICS_DISABLE="true" \ cloudypad-test-install-alpine:local \ sh -c '/cloudypad/install.sh && PATH=$PATH:/root/.cloudypad/bin && echo $PATH && which cloudypad || (echo "Cloudypad not found on PATH after install" && false)' \ No newline at end of file From a4f9497c0f7536c8b6a877703b8ad5ca4dd64891 Mon Sep 17 00:00:00 2001 From: Pierre beucher Date: Mon, 6 Jan 2025 08:51:50 +0100 Subject: [PATCH 4/6] doc: use bash/zsh for install, add upgrade guide --- README.md | 2 +- docs/src/getting-started.md | 2 +- docs/src/usage/installation.md | 28 ++++++++++++++++++---------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 6cc03155..fa2f7f29 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Not familiar with terms like _"Cloud gaming"_, _"Moonlight"_, _"Cloud Provider"_ Install latest version of `cloudypad` CLI: ```sh -curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/master/install.sh | sh +curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/master/install.sh | bash ``` For other installation methods, see [Installation](https://cloudypad.gg/usage/installation) diff --git a/docs/src/getting-started.md b/docs/src/getting-started.md index 3fc1cc31..f7583641 100644 --- a/docs/src/getting-started.md +++ b/docs/src/getting-started.md @@ -21,7 +21,7 @@ Cloudy Pad deploys a Cloud gaming gear using a Cloud provider of your choice: Install latest version of `cloudypad` CLI: ```sh -curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/master/install.sh | sh +curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/master/install.sh | bash ``` For other installation methods, see [Installation](#installation) diff --git a/docs/src/usage/installation.md b/docs/src/usage/installation.md index a1020315..dba3b454 100644 --- a/docs/src/usage/installation.md +++ b/docs/src/usage/installation.md @@ -1,10 +1,12 @@ # Installation - [Requirements for all OS](#requirements-for-all-os) -- [Linux](#linux) -- [MacOS](#macos) -- [Windows](#windows) -- [Nix / NixOS](#nix--nixos) +- [Installation](#installation-1) + - [Linux](#linux) + - [MacOS](#macos) + - [Windows](#windows) + - [Nix / NixOS](#nix--nixos) +- [Upgrade](#upgrade) ## Requirements for all OS @@ -18,25 +20,27 @@ - Rootless Docker is not supported yet - For MacOS, [OrbStack](https://orbstack.dev/) is recommended over Docker Desktop -## Linux +## Installation + +### Linux Install latest version of `cloudypad` CLI: ```sh -curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/master/install.sh | sh +curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/master/install.sh | bash ``` -## MacOS +### MacOS Install latest version of `cloudypad` CLI: ```sh -curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/master/install.sh | sh +curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/master/install.sh | zsh ``` **[OrbStack](https://orbstack.dev/) is recommended** over Docker Desktop as it's more compatible for Cloudy Pad usage. -## Windows +### Windows Running Cloudy Pad on Windows [requires WSL to be installed](https://learn.microsoft.com/en-us/windows/wsl/install). @@ -44,7 +48,7 @@ Once WSL is installed, run a Linux shell and follow [Linux installation steps](# Note: If you are using SSH keys mounted from Windows host, make sure they have proper permissions: `chmod 0600 ~/.ssh/` -## Nix / NixOS +### Nix / NixOS Cloudy Pad is packaged as a [Nix Flake](https://nixos.wiki/wiki/flakes), see [`flake.nix`](./flake.nix) @@ -54,3 +58,7 @@ You can include it in your NixOS config or run directly with `nix run`: nix run github:PierreBeucher/cloudypad create nix run github:PierreBeucher/cloudypad -- --version ``` + +## Upgrade + +To upgrade to the latest version of `cloudypad`, run the installation process again. It will check for the latest version and install it. \ No newline at end of file From 690dc70db83b6e109899e087e15561d7fc5aef50 Mon Sep 17 00:00:00 2001 From: Pierre beucher Date: Mon, 6 Jan 2025 09:27:52 +0100 Subject: [PATCH 5/6] chore: slient install script analytics --- install.sh | 254 ++++++++++++++++++++++++++--------------------------- 1 file changed, 127 insertions(+), 127 deletions(-) diff --git a/install.sh b/install.sh index de85e4a6..38d41ae5 100755 --- a/install.sh +++ b/install.sh @@ -8,14 +8,14 @@ DEFAULT_CLOUDYPAD_SCRIPT_REF=v0.11.0 CLOUDYPAD_HOME=${CLOUDYPAD_HOME:-"$HOME/.cloudypad"} CLOUDYPAD_SCRIPT_REF=${CLOUDYPAD_SCRIPT_REF:-$DEFAULT_CLOUDYPAD_SCRIPT_REF} -INSTALL_POSTHOG_DISTINCT_ID="cli-install-$(date +%Y-%m-%d-%H-%M-%S)-$(tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 5)" +INSTALL_POSTHOG_DISTINCT_ID="cli-install-$(date +%Y-%m-%d-%H-%M-%S)-$RANDOM" INSTALL_POSTHOG_API_KEY="phc_caJIOD8vW5727svQf90FNgdALIyYYouwEDEVh3BI1IH" # Sends anonymous analytics event during installation send_analytics_event() { event=$1 if [ "$CLOUDYPAD_ANALYTICS_DISABLE" != "true" ]; then - curl -v -L --header "Content-Type: application/json" -d "{ + curl -s -o /dev/null -L --header "Content-Type: application/json" -d "{ \"api_key\": \"$INSTALL_POSTHOG_API_KEY\", \"event\": \"$event\", \"distinct_id\": \"$INSTALL_POSTHOG_DISTINCT_ID\", @@ -30,130 +30,130 @@ send_analytics_event() { send_analytics_event "cli_install_start" -# echo "Installing Cloudy Pad version $CLOUDYPAD_SCRIPT_REF" - -# CLOUDYPAD_SCRIPT_URL="https://raw.githubusercontent.com/PierreBeucher/cloudypad/$CLOUDYPAD_SCRIPT_REF/cloudypad.sh" -# # === - -# # Constants, do not override -# INSTALL_DIR="$CLOUDYPAD_HOME/bin" -# SCRIPT_NAME="cloudypad" -# SCRIPT_PATH="$INSTALL_DIR/cloudypad" - -# # Check if cloudypad is already in PATH -# if [ -n "$(which cloudypad)" ]; then -# CURRENT_CLOUDYPAD_PATH=$(which cloudypad) - -# # Read from /dev/tty to ensure read will work even if script is piped to shell such as install.sh | sh -# read -p "cloudypad is already installed at ${CURRENT_CLOUDYPAD_PATH}. Do you want to overwrite it? (y/N): " CONFIRM < /dev/tty - -# if [[ "$CONFIRM" != "y" ]]; then -# echo "Installation aborted." -# exit 1 -# fi -# fi - -# if [ "$CLOUDYPAD_INSTALL_SKIP_DOCKER_CHECK" != "true" ]; then - -# # Check if Docker is installed and usable -# if ! docker --version > /dev/null; then -# echo "Docker is not installed or running 'docker --version' failed. Please make sure you have a working Docker installation. See https://docs.docker.com/engine/install/" -# exit 1 -# fi - -# # Check if Docker daemon is accessible -# if ! docker info > /dev/null; then -# echo "Docker is installed but not usable. Have you added yourself to docker group? See https://docs.docker.com/engine/install/linux-postinstall/" -# echo "You might need to run 'sudo usermod -aG docker \$USER' and restart your logout / log back before being able to use Docker" -# exit 1 -# fi -# fi - -# # Create secure directory for Cloudy Pad home as it may contain sensitive data -# mkdir -p "$CLOUDYPAD_HOME" -# chmod 0700 $CLOUDYPAD_HOME - -# mkdir -p "$INSTALL_DIR" - -# echo "Downloading $CLOUDYPAD_SCRIPT_URL..." - -# if command -v curl >/dev/null 2>&1; then -# curl --fail -sSL -o "$SCRIPT_PATH" "$CLOUDYPAD_SCRIPT_URL" -# elif command -v wget >/dev/null 2>&1; then -# wget -O "$SCRIPT_PATH" "$CLOUDYPAD_SCRIPT_URL" -# else -# echo "Error: Neither curl nor wget is available to download Cloudy Pad. Please install wget or curl and try again." -# exit 1 -# fi - -# chmod +x "$SCRIPT_PATH" - -# echo "Downloading Cloudy Pad container images..." - -# $SCRIPT_PATH download-container-images - -# # Identify shell to update *.rc file with PATH update -# SHELL_NAME=$(basename "${SHELL}") -# STARTUP_FILE="" - -# case "${SHELL_NAME}" in -# "bash") -# # Terminal.app on macOS prefers .bash_profile to .bashrc, so we prefer that -# # file when trying to put our export into a profile. On *NIX, .bashrc is -# # preferred as it is sourced for new interactive shells. -# if [ "$(uname)" != "Darwin" ]; then -# if [ -e "${HOME}/.bashrc" ]; then -# STARTUP_FILE="${HOME}/.bashrc" -# elif [ -e "${HOME}/.bash_profile" ]; then -# STARTUP_FILE="${HOME}/.bash_profile" -# fi -# else -# if [ -e "${HOME}/.bash_profile" ]; then -# STARTUP_FILE="${HOME}/.bash_profile" -# elif [ -e "${HOME}/.bashrc" ]; then -# STARTUP_FILE="${HOME}/.bashrc" -# fi -# fi -# ;; -# "zsh") -# STARTUP_FILE="${ZDOTDIR:-$HOME}/.zshrc" -# ;; -# *) -# echo -# echo "WARNING: Couldn't identify startup file to use (such as .bashrc or .zshrc) for your current shell." -# echo " Detected shell from \$SHELL=$SHELL environment variable: '$SHELL_NAME'" -# echo " To finalize installation please ensure $INSTALL_DIR is on your \$PATH" -# echo " Otherwise you may not be able to run Cloudy Pad CLI." -# echo " Alternatively, use directly $SCRIPT_PATH to run Cloudy Pad CLI." -# echo " If you think this is a bug, please create an issue: https://github.com/PierreBeucher/cloudypad/issues" -# ;; -# esac - -# if [ -n "${STARTUP_FILE}" ]; then -# # Create startup file if it does not exists. Rare situation but may happen -# touch "${STARTUP_FILE}" - -# LINE_TO_ADD="export PATH=\$PATH:${INSTALL_DIR}" -# if ! grep -q "# add CloudyPad CLI PATH" "${STARTUP_FILE}"; then -# echo "Adding ${INSTALL_DIR} to \$PATH in ${STARTUP_FILE}" -# printf "\\n# add CloudyPad CLI PATH\\n%s\\n\\n" "${LINE_TO_ADD}" >> "${STARTUP_FILE}" -# fi - -# echo "Successfully installed Cloudy Pad 🥳" -# echo -# echo "Restart your shell to add cloudypad on your PATH or run:" -# echo -# echo " source $STARTUP_FILE" -# echo -# fi - -# echo -# echo "Get started by creating a Cloudy Pad instance:" -# echo -# echo " cloudypad create" -# echo -# echo "If you enjoy Cloudy Pad, please star us ⭐ https://github.com/PierreBeucher/cloudypad" -# echo "🐛 Found a bug? Create an issue: https://github.com/PierreBeucher/cloudypad/issues" -# echo +echo "Installing Cloudy Pad version $CLOUDYPAD_SCRIPT_REF" + +CLOUDYPAD_SCRIPT_URL="https://raw.githubusercontent.com/PierreBeucher/cloudypad/$CLOUDYPAD_SCRIPT_REF/cloudypad.sh" +# === + +# Constants, do not override +INSTALL_DIR="$CLOUDYPAD_HOME/bin" +SCRIPT_NAME="cloudypad" +SCRIPT_PATH="$INSTALL_DIR/cloudypad" + +# Check if cloudypad is already in PATH +if [ -n "$(which cloudypad)" ]; then + CURRENT_CLOUDYPAD_PATH=$(which cloudypad) + + # Read from /dev/tty to ensure read will work even if script is piped to shell such as install.sh | sh + read -p "cloudypad is already installed at ${CURRENT_CLOUDYPAD_PATH}. Do you want to overwrite it? (y/N): " CONFIRM < /dev/tty + + if [[ "$CONFIRM" != "y" ]]; then + echo "Installation aborted." + exit 1 + fi +fi + +if [ "$CLOUDYPAD_INSTALL_SKIP_DOCKER_CHECK" != "true" ]; then + + # Check if Docker is installed and usable + if ! docker --version > /dev/null; then + echo "Docker is not installed or running 'docker --version' failed. Please make sure you have a working Docker installation. See https://docs.docker.com/engine/install/" + exit 1 + fi + + # Check if Docker daemon is accessible + if ! docker info > /dev/null; then + echo "Docker is installed but not usable. Have you added yourself to docker group? See https://docs.docker.com/engine/install/linux-postinstall/" + echo "You might need to run 'sudo usermod -aG docker \$USER' and restart your logout / log back before being able to use Docker" + exit 1 + fi +fi + +# Create secure directory for Cloudy Pad home as it may contain sensitive data +mkdir -p "$CLOUDYPAD_HOME" +chmod 0700 $CLOUDYPAD_HOME + +mkdir -p "$INSTALL_DIR" + +echo "Downloading $CLOUDYPAD_SCRIPT_URL..." + +if command -v curl >/dev/null 2>&1; then + curl --fail -sSL -o "$SCRIPT_PATH" "$CLOUDYPAD_SCRIPT_URL" +elif command -v wget >/dev/null 2>&1; then + wget -O "$SCRIPT_PATH" "$CLOUDYPAD_SCRIPT_URL" +else + echo "Error: Neither curl nor wget is available to download Cloudy Pad. Please install wget or curl and try again." + exit 1 +fi + +chmod +x "$SCRIPT_PATH" + +echo "Downloading Cloudy Pad container images..." + +$SCRIPT_PATH download-container-images + +# Identify shell to update *.rc file with PATH update +SHELL_NAME=$(basename "${SHELL}") +STARTUP_FILE="" + +case "${SHELL_NAME}" in + "bash") + # Terminal.app on macOS prefers .bash_profile to .bashrc, so we prefer that + # file when trying to put our export into a profile. On *NIX, .bashrc is + # preferred as it is sourced for new interactive shells. + if [ "$(uname)" != "Darwin" ]; then + if [ -e "${HOME}/.bashrc" ]; then + STARTUP_FILE="${HOME}/.bashrc" + elif [ -e "${HOME}/.bash_profile" ]; then + STARTUP_FILE="${HOME}/.bash_profile" + fi + else + if [ -e "${HOME}/.bash_profile" ]; then + STARTUP_FILE="${HOME}/.bash_profile" + elif [ -e "${HOME}/.bashrc" ]; then + STARTUP_FILE="${HOME}/.bashrc" + fi + fi + ;; + "zsh") + STARTUP_FILE="${ZDOTDIR:-$HOME}/.zshrc" + ;; + *) + echo + echo "WARNING: Couldn't identify startup file to use (such as .bashrc or .zshrc) for your current shell." + echo " Detected shell from \$SHELL=$SHELL environment variable: '$SHELL_NAME'" + echo " To finalize installation please ensure $INSTALL_DIR is on your \$PATH" + echo " Otherwise you may not be able to run Cloudy Pad CLI." + echo " Alternatively, use directly $SCRIPT_PATH to run Cloudy Pad CLI." + echo " If you think this is a bug, please create an issue: https://github.com/PierreBeucher/cloudypad/issues" + ;; +esac + +if [ -n "${STARTUP_FILE}" ]; then + # Create startup file if it does not exists. Rare situation but may happen + touch "${STARTUP_FILE}" + + LINE_TO_ADD="export PATH=\$PATH:${INSTALL_DIR}" + if ! grep -q "# add CloudyPad CLI PATH" "${STARTUP_FILE}"; then + echo "Adding ${INSTALL_DIR} to \$PATH in ${STARTUP_FILE}" + printf "\\n# add CloudyPad CLI PATH\\n%s\\n\\n" "${LINE_TO_ADD}" >> "${STARTUP_FILE}" + fi + + echo "Successfully installed Cloudy Pad 🥳" + echo + echo "Restart your shell to add cloudypad on your PATH or run:" + echo + echo " source $STARTUP_FILE" + echo +fi + +echo +echo "Get started by creating a Cloudy Pad instance:" +echo +echo " cloudypad create" +echo +echo "If you enjoy Cloudy Pad, please star us ⭐ https://github.com/PierreBeucher/cloudypad" +echo "🐛 Found a bug? Create an issue: https://github.com/PierreBeucher/cloudypad/issues" +echo send_analytics_event "cli_install_finish" From 42ecea896337a3bf19ec38e7fb346e4d8685c00c Mon Sep 17 00:00:00 2001 From: Pierre beucher Date: Mon, 6 Jan 2025 10:02:12 +0100 Subject: [PATCH 6/6] chore: test analytics key matches --- .github/workflows/test-unit.yml | 7 +++++-- Taskfile.yml | 3 +++ test/shell/test-matching-analytics-key.sh | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100755 test/shell/test-matching-analytics-key.sh diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index 7846ec61..60087030 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -50,7 +50,10 @@ jobs: with: primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix') }} restore-prefixes-first-match: nix-${{ runner.os }}- - + + - name: test analytics key match + run: nix develop -c task test-analytics-key-match + - name: Build container image run: nix develop -c task build-local @@ -124,4 +127,4 @@ jobs: - run: nix build .# # Disable this for now as latets version does not respect no-tty properly - # - run: CLOUDYPAD_CONTAINER_NO_TTY=true nix run .# -- --version + # - run: CLOUDYPAD_CONTAINER_NO_TTY=true nix run .# -- --version \ No newline at end of file diff --git a/Taskfile.yml b/Taskfile.yml index 0168885d..292f2bfd 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -54,6 +54,9 @@ tasks: test-integ-full-lifecycle-paperspace: - test/integ/cli-full-lifecycle/run.sh paperspace + test-analytics-key-match: + - cmd: test/shell/test-matching-analytics-key.sh + # # Utils # diff --git a/test/shell/test-matching-analytics-key.sh b/test/shell/test-matching-analytics-key.sh new file mode 100755 index 00000000..9b70044d --- /dev/null +++ b/test/shell/test-matching-analytics-key.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Ensure analytics API key match between install.sh and JS app + +# Get install.sh analytics key +INSTALL_POSTHOG_API_KEY=$(grep -oP 'INSTALL_POSTHOG_API_KEY="\K[^"]+' install.sh) + +# Get JS app analytics key +JS_APP_POSTHOG_API_KEY=$(grep -oP "phc_\w+" src/tools/analytics/client.ts) + +if [ "$INSTALL_POSTHOG_API_KEY" != "$JS_APP_POSTHOG_API_KEY" ]; then + echo "Analytics API key mismatch between install.sh and JS app:" + echo "install.sh: '$INSTALL_POSTHOG_API_KEY'" + echo "JS app: '$JS_APP_POSTHOG_API_KEY'" + exit 1 +fi + +echo "Analytics API key match between install.sh and JS app" \ No newline at end of file