Skip to content

Commit

Permalink
Merge pull request #105 from PierreBeucher/event-on-install
Browse files Browse the repository at this point in the history
generate event on install
  • Loading branch information
PierreBeucher authored Jan 6, 2025
2 parents bcca270 + 42ecea8 commit 0fc5e28
Show file tree
Hide file tree
Showing 14 changed files with 164 additions and 93 deletions.
29 changes: 18 additions & 11 deletions .github/workflows/test-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -84,19 +87,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
Expand All @@ -119,5 +125,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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand Down
2 changes: 1 addition & 1 deletion cloudypad.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/src/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
28 changes: 18 additions & 10 deletions docs/src/usage/installation.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -18,33 +20,35 @@
- 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).

Once WSL is installed, run a Linux shell and follow [Linux installation steps](#linux).

Note: If you are using SSH keys mounted from Windows host, make sure they have proper permissions: `chmod 0600 ~/.ssh/<key>`

## Nix / NixOS
### Nix / NixOS

Cloudy Pad is packaged as a [Nix Flake](https://nixos.wiki/wiki/flakes), see [`flake.nix`](./flake.nix)

Expand All @@ -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.
35 changes: 26 additions & 9 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,28 @@ 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)-$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 -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\",
\"properties\": {
\"\$process_person_profile\": false,
\"os_name\": \"$(uname -s)\",
\"os_arch\": \"$(uname -m)\"
}
}" https://eu.i.posthog.com/capture/
fi
}

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"
Expand Down Expand Up @@ -68,14 +90,7 @@ 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
$SCRIPT_PATH download-container-images

# Identify shell to update *.rc file with PATH update
SHELL_NAME=$(basename "${SHELL}")
Expand Down Expand Up @@ -139,4 +154,6 @@ 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

send_analytics_event "cli_install_finish"
42 changes: 29 additions & 13 deletions src/core/config/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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),
{
Expand All @@ -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")
Expand All @@ -30,11 +32,12 @@ export type PostHogConfig = z.infer<typeof PostHogConfigSchema>
export type GlobalCliConfigV1 = z.infer<typeof GlobalCliConfigSchemaV1>
export type AnalyticsConfig = z.infer<typeof AnalyticsConfigSchema>

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" }
}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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)
}

Expand Down
9 changes: 2 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
32 changes: 10 additions & 22 deletions src/tools/analytics/initializer.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,32 @@

import { confirm } from '@inquirer/prompts'
import { v4 as uuidv4 } from 'uuid'
import { ConfigManager } from '../../core/config/manager'

export class AnalyticsInitializer {

async promptApproval(): Promise<boolean>{
private configManager = ConfigManager.getInstance()

private async promptApprovalAndSetAnalytics(): Promise<void>{

// Skip prompt if no TTY to avoid failures
if(!process.stdin.isTTY) {
return true
return
}

const approveAnalytics = await confirm({
message: `Do you allow Cloudy Pad to collect anonymous usage data ? Data won't be used for targeted ads or sold to third parties. It will only be used internally to improve Cloudy Pad. By approving you'll help Cloudy Pad !`,
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)
}
}
}
Expand Down
Loading

0 comments on commit 0fc5e28

Please sign in to comment.