### Setup Dashboard Locally Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/testing.mdx Navigate to the dashboard directory, install dependencies, and start the dashboard in development mode. ```bash cd apps/dashboard npm install npm run dev ``` -------------------------------- ### Build Example App for Android Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/testing.mdx Prebuild the example app for production, clean previous builds, and then assemble and install the release APK on a connected device or emulator. ```bash yarn prebuild_production # Prebuild the app with the 'production' config cd android ./gradlew clean # Clean previous builds ./gradlew assembleRelease # Build the release APK ./gradlew installRelease # Install the release APK on a connected device/emulator ``` -------------------------------- ### Build Example App Variants Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/testing.mdx Build the example app using either the production or staging configuration. ```bash yarn prebuild_production OR yarn prebuild_staging ``` -------------------------------- ### Setup iOS Dependencies Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/testing.mdx Clean the CocoaPods cache, install dependencies, and configure Xcode for a release build. ```bash cd ios pod cache clean --all pod install ``` -------------------------------- ### Install Project Dependencies Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/README.md Run this command to install all necessary project dependencies. ```bash $ yarn ``` -------------------------------- ### Start Local Development Server Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/README.md Starts a local development server and opens the website in a browser. Changes are reflected live without restarting. ```bash $ yarn start ``` -------------------------------- ### Install Project Dependencies Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/example-app-runtime-switch/README.md Run this command in your project directory to install all necessary npm packages. ```bash npm install ``` -------------------------------- ### Start the Expo Development Server Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/example-app-runtime-switch/README.md Executes the Expo CLI to start the development server, allowing you to run your app on simulators, emulators, or Expo Go. ```bash npx expo start ``` -------------------------------- ### Start Expo Open OTA Server (Docker) Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/getting-started/quick-start.mdx This command starts the Expo Open OTA server using Docker with a minimal local configuration. Ensure you replace placeholders like your-expo-token and your-project-id with your actual credentials. The server runs on http://localhost:3000. ```bash docker run --rm -it \ -p 3000:3000 \ -e BASE_URL=http://localhost:3000 \ -e EXPO_ACCESS_TOKEN=your-expo-token \ -e EXPO_APP_ID=your-project-id \ -e JWT_SECRET=$(openssl rand -base64 32) \ -e STORAGE_MODE=local \ -e KEYS_STORAGE_TYPE=local \ -e PUBLIC_LOCAL_EXPO_KEY_PATH=/keys/public-key.pem \ -e PRIVATE_LOCAL_EXPO_KEY_PATH=/keys/private-key.pem \ -e CACHE_MODE=local \ -e USE_DASHBOARD=true \ -e ADMIN_PASSWORD=admin \ -v $(pwd)/certs:/keys:ro \ -v ./updates:/updates \ ghcr.io/axelmarciano/expo-open-ota:latest ``` -------------------------------- ### Install Helm Chart with Default Values Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/helm.mdx Install the Expo Open OTA Helm chart using default configuration values. Ensure you replace NAMESPACE with your target Kubernetes namespace. ```bash helm install expo-open-ota -n NAMESPACE ./chart ``` -------------------------------- ### Run Server with Docker Compose Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/testing.mdx Build the Docker images and start the server using Docker Compose. The server will be accessible on port 3000. ```bash docker-compose build docker-compose up ``` -------------------------------- ### Install Helm Chart with Custom Values Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/helm.mdx Install the Expo Open OTA Helm chart by overriding default values with a custom 'my-values.yaml' file. Replace NAMESPACE with your target Kubernetes namespace. ```bash helm install expo-open-ota ./chart -n NAMESPACE -f my-values.yaml ``` -------------------------------- ### Publish Update with Environment Variables Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/eoas/publish.mdx When publishing, ensure necessary environment variables like `EXPO_TOKEN` and `RELEASE_CHANNEL` are set. This example shows how to set them directly or using a `.env.local` file with `dotenv`. ```bash EXPO_TOKEN=your_token RELEASE_CHANNEL=staging npx eoas publish --branch ``` ```bash dotenv -e .env.local -- npx eoas publish --branch ``` -------------------------------- ### Update app.json with Project ID Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/testing.mdx Configure the example app's `app.json` file by replacing `YOUR_PROJECT_ID` with your actual Expo Project ID. ```json "extra": { "router": { "origin": false }, "eas": { "projectId": "YOUR_PROJECT_ID" } } ``` -------------------------------- ### Get Application URL with LoadBalancer Service Source: https://github.com/axelmarciano/expo-open-ota/blob/main/helm/templates/NOTES.txt Run these commands for a LoadBalancer service type. Note that it may take a few minutes for the LoadBalancer IP to become available. You can monitor its status with 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "expo-open-ota.fullname" . }}'. ```bash export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "expo-open-ota.fullname" . }} --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}") echo http://$SERVICE_IP:{{ .Values.service.port }} ``` -------------------------------- ### Get Application URL with Ingress Enabled Source: https://github.com/axelmarciano/expo-open-ota/blob/main/helm/templates/NOTES.txt Use this command when ingress is enabled to construct the application URL. It iterates through hosts and paths defined in the ingress configuration. ```go-template {{- if .Values.ingress.enabled }} {{- range $host := .Values.ingress.hosts }} {{- range .paths }} http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} {{- end }} {{- end }} {{- end }} ``` -------------------------------- ### Get Expo Project Info Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/getting-started/prerequisites.mdx Retrieve essential project information, including the project ID, using the EAS CLI. This is required for server configuration. ```bash eas project:info ``` -------------------------------- ### Get Application URL with NodePort Service Source: https://github.com/axelmarciano/expo-open-ota/blob/main/helm/templates/NOTES.txt Execute these commands when the service type is NodePort to retrieve the application URL. It exports the NodePort and Node IP to construct the URL. ```bash export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "expo-open-ota.fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT ``` -------------------------------- ### Get Application URL with ClusterIP Service Source: https://github.com/axelmarciano/expo-open-ota/blob/main/helm/templates/NOTES.txt Use these commands for a ClusterIP service type. This method forwards port 8080 on localhost to the container's port and provides a local access URL. ```bash export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "expo-open-ota.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT ``` -------------------------------- ### Reset Project to a Fresh State Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/example-app-runtime-switch/README.md This command moves the existing starter code to 'app-example' and creates a new, blank 'app' directory for development. ```bash npm run reset-project ``` -------------------------------- ### Initialize Expo Project with Expo Open OTA Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/eoas/configure.mdx Run this command in your Expo project's root directory to initialize it for OTA updates. Follow the on-screen instructions. This command modifies your `app.config.js` file. ```bash npx eoas init ``` -------------------------------- ### Total Update Downloads by Update and Runtime (PromQL) Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/advanced/prometheus.mdx This PromQL query calculates the total downloads, grouped by both update and runtime. It allows for filtering by platform, runtime, branch, update, and update type. Useful for detailed analysis of download sources. ```PromQL sum by (update, runtime) (update_downloads_total{platform=~"$platform", runtime=~"$runtime", branch=~"$branch", update=~"$update", updateType=~"$updateType|.*"}) ``` -------------------------------- ### Clone Repository Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/testing.mdx Clone the Expo Open OTA repository and navigate into the project directory. ```bash git clone https://github.com/axelmarciano/expo-open-ota cd expo-open-ota ``` -------------------------------- ### Publish First OTA Update Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/getting-started/quick-start.mdx After configuring your app and setting up a release channel, use this command to publish your first OTA update to the specified branch (e.g., 'production'). ```bash npx eoas publish --branch production ``` -------------------------------- ### Clone Helm Chart Repository Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/helm.mdx Clone the Expo Open OTA repository and navigate to the Helm chart directory to begin deployment. ```bash git clone https://github.com/axelmarciano/expo-open-ota cd expo-open-ota/helm ``` -------------------------------- ### Enable Dashboard Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/dashboard.mdx Set the USE_DASHBOARD environment variable to true to enable the dashboard. ```sh USE_DASHBOARD=true ``` -------------------------------- ### Google Cloud Storage Configuration Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/storage.mdx Set up Google Cloud Storage for storing update assets. Requires bucket name and base64-encoded service account credentials. ```bash STORAGE_MODE=gcs GCS_BUCKET_NAME=your-bucket-name GOOGLE_APPLICATION_CREDENTIALS_B64= ``` -------------------------------- ### Local File System Storage Configuration Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/storage.mdx Configure the local file system for development and testing. Ensure no multiple server instances are running in production as assets are not shared. ```bash STORAGE_MODE=local LOCAL_BUCKET_BASE_PATH=/path/to/your/assets ``` -------------------------------- ### Deploy Website using SSH Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/README.md Deploys the website using SSH. This command builds the static content and pushes it to the 'gh-pages' branch. ```bash $ USE_SSH=true yarn deploy ``` -------------------------------- ### Build Static Website Content Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/README.md Generates the static content for the website, typically placed in the 'build' directory for hosting. ```bash $ yarn build ``` -------------------------------- ### Local Key Store Configuration Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/key-store.mdx Configure the local key store by specifying the paths to your PEM-encoded key files. This method is suitable for development and testing. ```bash KEYS_STORAGE_TYPE=local PUBLIC_LOCAL_EXPO_KEY_PATH=/path/to/public-key.pem PRIVATE_LOCAL_EXPO_KEY_PATH=/path/to/private-key.pem PRIVATE_LOCAL_CLOUDFRONT_KEY_PATH=/path/to/cloudfront-private-key.pem ``` -------------------------------- ### Configure iOS Build for Release Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/testing.mdx Open the iOS project in Xcode and set the Build Configuration to 'Release' under Run settings in the scheme editor. ```bash cd ios pod cache clean --all pod install # Then: # 1. Open the iOS project in Xcode: open ios/*.xcworkspace # 2. Go to: Product → Scheme → Edit Scheme # 3. Under Run, set Build Configuration to Release ``` -------------------------------- ### Total Update Downloads by Runtime (PromQL) Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/advanced/prometheus.mdx This PromQL query aggregates the total number of downloads for each runtime. It filters by platform, runtime, branch, and update. Use this to understand runtime-specific download trends. ```PromQL sum(update_downloads_total{platform=~"$platform", runtime=~"$runtime", branch=~"$branch", update=~"$update"}) by (runtime) ``` -------------------------------- ### Local Key Store Configuration for Cloudfront Key Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/cdn/cloudfront.mdx Specify the file path to your Cloudfront private key when using a local key store. ```bash PRIVATE_LOCAL_CLOUDFRONT_KEY_PATH=/path/to/cloudfront-private-key.pem ``` -------------------------------- ### Encode Private Key for Environment Variable Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/cdn/cloudfront.mdx Use this command to base64 encode your private key before storing it in environment variables. ```bash echo -n "your private key" | base64 ``` -------------------------------- ### Environment Variable Configuration for Keys Storage Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/cdn/cloudfront.mdx Set the `KEYS_STORAGE_TYPE` to 'environment' and provide the base64 encoded private key. ```bash KEYS_STORAGE_TYPE=environment PRIVATE_CLOUDFRONT_KEY_B64=base64-encoded-cloudfront-private-key ``` -------------------------------- ### Environment Variables Key Store Configuration Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/key-store.mdx Configure the environment variable key store by setting base64-encoded keys. Ensure keys are properly encoded before setting these variables. ```bash KEYS_STORAGE_TYPE=environment PUBLIC_EXPO_KEY_B64=base64-encoded-public-key PRIVATE_EXPO_KEY_B64=base64-encoded-private-key PRIVATE_CLOUDFRONT_KEY_B64=base64-encoded-cloudfront-private-key ``` -------------------------------- ### Generate Signing Certificates Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/getting-started/quick-start.mdx Run this command in your Expo project directory to generate the necessary signing key pair for OTA updates. It creates private-key.pem, public-key.pem, and certificate.pem in the certs/ directory. ```bash npx eoas generate-certs ``` -------------------------------- ### Amazon S3 Storage Configuration Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/storage.mdx Set up Amazon S3 for storing update assets. Includes basic configuration and options for S3-compatible storage. ```bash STORAGE_MODE=s3 AWS_REGION=your-region S3_BUCKET_NAME=your-bucket-name ``` -------------------------------- ### Redis Sentinel Environment Variables for Caching Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/cache.mdx Set up Redis Sentinel for high availability and automatic failover by configuring these environment variables. Includes connection addresses, master name, and optional security settings. ```bash CACHE_MODE=redis-sentinel REDIS_SENTINEL_ADDRS=sentinel-0:26379,sentinel-1:26379,sentinel-2:26379 REDIS_SENTINEL_MASTER_NAME=mymaster // optional, defaults to "mymaster" REDIS_PASSWORD=your-redis-password // optional if auth is enabled REDIS_USE_TLS=true // optional if you are using a TLS connection REDIS_USERNAME=your-redis-username // optional for ACL authentication REDIS_CA_CERT_B64=base64-encoded-ca-certificate // optional for TLS with custom CA ``` -------------------------------- ### Run Docker Container with Direct Environment Variables Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/custom.mdx Run the Expo Open OTA Docker container and pass environment variables directly to the docker run command. The server will be accessible on the specified PORT. ```bash docker run --rm -it -e PORT=3000 -e ENV_KEY=value ... --platform linux/amd64 ghcr.io/axelmarciano/expo-open-ota:latest ``` -------------------------------- ### Run Docker Container with .env file Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/custom.mdx Run the Expo Open OTA Docker container using environment variables defined in a .env file. Ensure the .env file is in the current directory. ```bash docker run --rm -it --env-file .env --platform linux/amd64 ghcr.io/axelmarciano/expo-open-ota:latest ``` -------------------------------- ### Redis Environment Variables for Caching Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/cache.mdx Configure Redis as a cache by setting these environment variables. Ensure correct host, port, and optional authentication/TLS settings are provided. ```bash REDIS_HOST=your-redis-host REDIS_PORT=your-redis-port REDIS_PASSWORD=your-redis-password REDIS_USE_TLS=true // optional if you are using a TLS connection REDIS_USERNAME=your-redis-username // optional for ACL authentication REDIS_CA_CERT_B64=base64-encoded-ca-certificate // optional for TLS with custom CA ``` -------------------------------- ### Republish an OTA Update Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/eoas/republish.mdx Use the `republish` command to resend an existing update. Specify the target branch and optionally the platform. If no platform is specified, all platforms will be used. ```bash npx eoas republish --branch [--platform ] ``` -------------------------------- ### Set Generic CDN Domain with Bucket Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/cdn/generic.mdx If your CDN domain requires the bucket name to be specified, include it directly in the S3_CDN_PREFIX environment variable. ```bash S3_CDN_PREFIX=https://cdn.example.com/my-bucket ``` -------------------------------- ### Encode Keys for Environment Variables Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/key-store.mdx Encode your private keys using base64 before setting them as environment variables. This is required for the environment variable key store. ```bash echo -n "your-private-key" | base64 ``` -------------------------------- ### Publish an OTA Update Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/eoas/publish.mdx Use this command to publish an OTA update for your Expo project. Ensure your Expo credentials are set up in `.expo/state.json` or via the `EXPO_TOKEN` environment variable for authentication. ```bash npx eoas publish --branch [--nonInteractive] [--outputDir ] [--platform ] [--packageRunner ] [--message ] [--dumpSourcemap] ``` -------------------------------- ### S3 Multi-App Bucket Sharing Configuration Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/storage.mdx Use S3_KEY_PREFIX to namespace keys within a shared S3 bucket, preventing conflicts between multiple Expo Open OTA instances. ```bash S3_KEY_PREFIX=myapp ``` -------------------------------- ### Set Generic CDN Domain Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/cdn/generic.mdx Configure the S3_CDN_PREFIX environment variable with your CDN accelerated domain. This is required for the generic CDN feature. ```bash S3_CDN_PREFIX=https://cdn.example.com ``` -------------------------------- ### Configure Fatal Error Panel Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/advanced/prometheus.mdx Sets up a panel to display fatal errors per update, using a Prometheus data source. The PromQL query aggregates errors by update. This panel is useful for tracking update-related issues. ```json { "datasource": { "uid": "$DS_PROM" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "unit": "none" }, "overrides": [] }, "gridPos": { "h": 8, "w": 8, "x": 0, "y": 10 }, "id": 6, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.5.1", "targets": [ { "datasource": { "uid": "$DS_PROM" }, "editorMode": "code", "expr": "sum(update_error_users_total{platform=~"$platform", runtime=~"$runtime", branch=~"$branch", update=~"$update"})\n by (update)", "legendFormat": "{{platform}}", "range": true, "refId": "A" } ], "title": "🚨 Fatal error per update" } ``` -------------------------------- ### Integrate ESLint React Plugin Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/dashboard/README.md Integrate the eslint-plugin-react to enable its recommended rules and JSX runtime rules. Set the React version in the settings. ```javascript // eslint.config.js import react from 'eslint-plugin-react' export default tseslint.config({ // Set the react version settings: { react: { version: '18.3' } }, plugins: { // Add the react plugin react, }, rules: { // other rules... // Enable its recommended rules ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, }) ``` -------------------------------- ### S3 Path-Style Addressing Configuration Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/storage.mdx Enable path-style addressing for S3 if your provider requires it instead of virtual-hosted-style URLs. ```bash AWS_S3_FORCE_PATH_STYLE=true ``` -------------------------------- ### Deploy Website without SSH Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/README.md Deploys the website without using SSH. Replace '' with your actual GitHub username. This command builds the static content and pushes it to the 'gh-pages' branch. ```bash $ GIT_USER= yarn deploy ``` -------------------------------- ### Configure Unique Active Users Panel Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/advanced/prometheus.mdx Defines a timeseries panel to display unique active users by platform. It uses a Prometheus data source and a PromQL query to aggregate active users. Configure this panel to monitor user engagement across different platforms. ```json { "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "unit": "none" }, "overrides": [] }, "gridPos": { "h": 8, "w": 8, "x": 16, "y": 1 }, "id": 2, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.5.1", "targets": [ { "datasource": { "uid": "$DS_PROM" }, "editorMode": "code", "expr": "max by (platform) (\n sum by (platform, instance) (\n global_active_users_total{platform=~"$platform"} ) )", "legendFormat": "{{platform}}", "range": true, "refId": "A" } ], "title": "📊 Unique Active Users by Platform", "type": "timeseries" } ``` -------------------------------- ### S3-Compatible Storage Configuration Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/storage.mdx Configure Expo Open OTA to use S3-compatible object storage like Cloudflare R2 or MinIO. Set AWS_REGION to 'auto' and provide the base endpoint. ```bash STORAGE_MODE=s3 AWS_REGION=auto AWS_BASE_ENDPOINT=https://account-id.r2.cloudflarestorage.com S3_BUCKET_NAME=your-bucket-name ``` -------------------------------- ### Generate Key Pair Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/cdn/cloudfront.mdx Use these bash commands to generate a private and public RSA key pair for Cloudfront authentication. The public key will be uploaded to AWS. ```bash openssl genrsa -out private_key.pem 2048 openssl rsa -in private_key.pem -pubout -out public_key.pem ``` -------------------------------- ### Publish OTA Updates Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/testing.mdx Publish an update to the 'production' or 'staging' release channel using the provided yarn commands. ```bash yarn release_production # Publish an update to the 'production' channel yarn release_staging # Publish an update to the 'staging' channel ``` -------------------------------- ### Publish Update in CI/CD Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/eoas/publish.mdx Automate OTA update publishing in CI/CD pipelines using the `--nonInteractive` flag. Ensure the `EXPO_TOKEN` is securely configured in your CI/CD environment. ```bash npx eoas publish --nonInteractive ``` -------------------------------- ### Pull Docker Image Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/custom.mdx Use this command to pull the latest Expo Open OTA Docker image from GitHub Container Registry. ```bash docker pull ghcr.io/axelmarciano/expo-open-ota:latest ``` -------------------------------- ### Dashboard Access URL Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/dashboard.mdx The dashboard will be available at this URL after enabling and configuring it. ```sh http:///dashboard ``` -------------------------------- ### Upgrade Existing Helm Release Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/deployment/helm.mdx Upgrade an existing Expo Open OTA Helm release with new configurations specified in 'my-values.yaml'. Replace NAMESPACE with your target Kubernetes namespace. ```bash helm upgrade expo-open-ota -n NAMESPACE ./chart -f my-values.yaml ``` -------------------------------- ### Set Admin Password Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/dashboard.mdx Set the ADMIN_PASSWORD environment variable to a secure password for dashboard access. ```sh ADMIN_PASSWORD=your-password ``` -------------------------------- ### Perform a Rollback Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/eoas/rollback.mdx Specify the branch and optionally the platform to revert the application to the embedded update. Not compatible with `disableAntiBrickingMeasure`. ```bash npx eoas rollback --branch [--platform ] ``` -------------------------------- ### Publish Update and Dump Source Maps Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/eoas/publish.mdx Use the `--dumpSourcemap` flag to include Hermes source maps in the output directory alongside the bundle. These maps are not uploaded to the OTA server but can be used for symbolication services. ```bash npx eoas publish --branch production --dumpSourcemap ``` -------------------------------- ### Configure ESLint for Type-Aware Linting Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/dashboard/README.md Configure the top-level parserOptions property to enable type-aware lint rules. Ensure tsconfig.node.json and tsconfig.app.json are correctly referenced. ```javascript export default tseslint.config({ languageOptions: { // other options... parserOptions: { project: ['./tsconfig.node.json', './tsconfig.app.json'], tsconfigRootDir: import.meta.dirname, }, }, }) ``` -------------------------------- ### Expo OTA Update Downloads Total Panel Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/advanced/prometheus.mdx Configures a timeseries panel to display the total number of update downloads. It queries Prometheus for update download metrics, allowing filtering by platform, runtime, branch, and update type, and displays results grouped by update. ```json { "datasource": { "uid": "$DS_PROM" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 8, "x": 0, "y": 19 }, "id": 3, "options": { "legend": { "calcs": [], "displayMode": "table", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.5.1", "targets": [ { "datasource": { "uid": "$DS_PROM" }, "expr": "sum(update_downloads_total{platform=~"$platform", runtime=~"$runtime", branch=~"$branch", update=~"$update"}) by (update)", "legendFormat": "{{update}}", "refId": "A" } ] } ``` -------------------------------- ### S3 Access Key Configuration Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/storage.mdx Provide AWS access key ID and secret access key if not using IAM roles for authentication. ```bash AWS_ACCESS_KEY_ID=your-access-key-id AWS_SECRET_ACCESS_KEY=your-secret-access-key ``` -------------------------------- ### Cloudfront Environment Variables Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/cdn/cloudfront.mdx Set these environment variables in your .env file. CLOUDFRONT_KEY_PAIR_ID is the ID of the key group created in AWS, and CLOUDFRONT_DOMAIN is your Cloudfront distribution's domain name. ```env CLOUDFRONT_KEY_PAIR_ID=your-public-key-id ``` ```env CLOUDFRONT_DOMAIN=your-cloudfront-domain ``` -------------------------------- ### Grafana Dashboard Configuration Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/advanced/prometheus.mdx This JSON defines a Grafana dashboard for visualizing server metrics. It includes panels for total unique active users and a breakdown by runtime and update. ```json { "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "datasource", "uid": "grafana" }, "enable": true, "hide": true, "iconColor": "rgba(0,211,255,1)", "name": "Annotations & Alerts", "target": { "limit": 100, "matchAny": false, "tags": [], "type": "dashboard" }, "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": 145, "links": [], "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 10, "panels": [], "title": "Users", "type": "row" }, { "datasource": { "uid": "$DS_PROM" }, "fieldConfig": { "defaults": { "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "unit": "short" }, "overrides": [] }, "gridPos": { "h": 8, "w": 8, "x": 0, "y": 1 }, "id": 1, "options": { "colorMode": "value", "graphMode": "area", "justifyMode": "auto", "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { "calcs": [ "last" ], "fields": "", "values": false }, "showPercentChange": false, "textMode": "auto", "wideLayout": true }, "pluginVersion": "11.5.1", "targets": [ { "datasource": { "uid": "$DS_PROM" }, "editorMode": "code", "expr": "max( sum by (instance) (global_active_users_total) )", "legendFormat": "Total Unique Active Users", "range": true, "refId": "A" } ], "title": "🟢 Total Unique Active Users (4h)", "type": "stat" }, { "datasource": { "uid": "$DS_PROM" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "unit": "none" }, "overrides": [] }, "gridPos": { "h": 8, "w": 8, "x": 8, "y": 1 }, "id": 5, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.5.1", "targets": [ { "datasource": { "uid": "$DS_PROM" }, "editorMode": "code", "expr": "max by (runtime, update) ( sum by (platform, runtime, branch, update, instance) ( active_users_total{platform=~"$platform", runtime=~"$runtime", branch=~"$branch", update=~"$update"} ) )", "legendFormat": "{{platform}}", "range": true, "refId": "A" } ], "title": "📊 Unique Active Users by Runtime And Update", "type": "timeseries" }, { "datasource": { "uid": "$DS_PROM" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "unit": "none" }, "overrides": [] }, "gridPos": { "h": 8, "w": 8, "x": 16, "y": 1 }, "id": 7, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "11.5.1", "targets": [ { "datasource": { "uid": "$DS_PROM" }, "editorMode": "code", "expr": "max by (branch, update) ( sum by (platform, branch, runtime, update, instance) ( active_users_total{platform=~"$platform", runtime=~"$runtime", branch=~"$branch", update=~"$update"} ) )", "legendFormat": "{{branch}}", "range": true, "refId": "A" } ], "title": "📊 Unique Active Users by Branch And Update", "type": "timeseries" } ], "schemaVersion": 39, "style": "dark", "tags": [], "templating": { "list": [ { "current": { "selected": false, "text": "All", "value": "All" }, "hide": 0, "includeAll": true, "label": "Platform", "multi": true, "name": "platform", "options": [], "query": "label_values(active_users_total, platform)", "refresh": 1, "type": "query" }, { "current": { "selected": false, "text": "All", "value": "All" }, "hide": 0, "includeAll": true, "label": "Runtime", "multi": true, "name": "runtime", "options": [], "query": "label_values(active_users_total, runtime)", "refresh": 1, "type": "query" }, { "current": { "selected": false, "text": "All", "value": "All" }, "hide": 0, "includeAll": true, "label": "Branch", "multi": true, "name": "branch", "options": [], "query": "label_values(active_users_total, branch)", "refresh": 1, "type": "query" }, { "current": { "selected": false, "text": "All", "value": "All" }, "hide": 0, "includeAll": true, "label": "Update", "multi": true, "name": "update", "options": [], "query": "label_values(active_users_total, update)", "refresh": 1, "type": "query" } ] }, "time": { "from": "now-4h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Expo Open OTA Metrics", "uid": "expo-open-ota-metrics", "version": 1 } ``` -------------------------------- ### Publish Update with a Custom Message Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/eoas/publish.mdx Attach a custom message to your OTA update using the `--message` or `-m` flag. This message is visible in the dashboard and helps describe the update's purpose. ```bash npx eoas publish --branch production -m "Fix login crash on Android" ``` -------------------------------- ### Verify Server Health Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/getting-started/quick-start.mdx Use this curl command to check if the Expo Open OTA server is running and accessible on http://localhost:3000. ```bash curl http://localhost:3000/hc ``` -------------------------------- ### Base64 Encoding GCP Credentials Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/storage.mdx Encode your GCP service account JSON key to base64 for the GOOGLE_APPLICATION_CREDENTIALS_B64 environment variable. ```bash base64 -i /path/to/service-account.json | tr -d '\n' ``` -------------------------------- ### View EAS Account Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/getting-started/prerequisites.mdx Ensure you are logged into your Expo account using the EAS CLI. This command helps verify your account status. ```bash eas account:view ``` -------------------------------- ### S3 Bucket Policy for Cloudfront Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/cdn/cloudfront.mdx This JSON policy grants Cloudfront the necessary permissions to read objects from your S3 bucket. Ensure you replace {{YOUR_BUCKET_NAME}} and {{YOUR_CLOUDFRONT_DISTRIBUTION_ID}} with your actual values. ```json { "Version": "2008-10-17", "Id": "PolicyForCloudFrontPrivateContent", "Statement": [ { "Sid": "AllowCloudFrontServicePrincipal", "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::{{YOUR_BUCKET_NAME}}/*", "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::{{AWS_ACCOUNT_ID}}:distribution/{{YOUR_CLOUDFRONT_DISTRIBUTION_ID}}" } } } ] } ``` -------------------------------- ### AWS Secrets Manager Key Store Configuration Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/server-configuration/key-store.mdx Configure the AWS Secrets Manager key store by providing the secret IDs for your keys and your AWS region. Ensure correct permissions are set for the server to access these secrets. ```bash KEYS_STORAGE_TYPE=aws-secrets-manager AWS_KEYS_PRIVATE_EXPO_KEY_SECRET_ID=The secret name of the expo private key AWS_KEYS_PUBLIC_EXPO_KEY_SECRET_ID=The secret name of the expo public key AWSSM_CLOUDFRONT_PRIVATE_KEY_SECRET_ID=The secret name of the cloudfront private key AWS_REGION=your-region ``` -------------------------------- ### Publish Update with Different Package Runners Source: https://github.com/axelmarciano/expo-open-ota/blob/main/apps/docs/docs/eoas/publish.mdx EOAS defaults to `npx` for Expo CLI commands. You can explicitly set a package runner using the `--packageRunner` flag, the `EOAS_PACKAGE_RUNNER` environment variable, or by defining `packageManager` in your `package.json`. ```bash # Auto-detected from package.json (zero config) npx eoas publish --branch production ``` ```bash # Explicit flag npx eoas publish --branch production --packageRunner bunx ``` ```bash # Via environment variable EOAS_PACKAGE_RUNNER=bunx eoas publish --branch production ```