mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-23 19:03:07 +00:00
feat: add Docker/cloud environment detection to telemetry (v2.18.1)
Added isDocker and cloudPlatform fields to session_start telemetry events to enable measurement of the v2.17.1 user ID stability fix. Changes: - Added detectCloudPlatform() method to event-tracker.ts - Updated trackSessionStart() to include isDocker and cloudPlatform - Added 16 comprehensive unit tests for environment detection - Tests for all 8 cloud platforms (Railway, Render, Fly, Heroku, AWS, K8s, GCP, Azure) - Tests for Docker detection, local env, and combined scenarios - Version bumped to 2.18.1 - Comprehensive CHANGELOG entry Impact: - Enables validation of v2.17.1 boot_id-based user ID stability - Allows segmentation of metrics by environment - 100% backward compatible - only adds new fields - All tests passing, TypeScript compilation successful 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
111
CHANGELOG.md
111
CHANGELOG.md
@@ -5,6 +5,117 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2.18.1] - 2025-10-08
|
||||||
|
|
||||||
|
### 🔍 Telemetry Enhancement
|
||||||
|
|
||||||
|
**Added Docker/cloud environment detection to session_start events.**
|
||||||
|
|
||||||
|
This release enables measurement of the v2.17.1 user ID stability fix by tracking which users are in Docker/cloud environments.
|
||||||
|
|
||||||
|
#### Problem
|
||||||
|
|
||||||
|
The v2.17.1 fix for Docker/cloud user ID stability (boot_id-based IDs) could not be validated because telemetry didn't capture Docker/cloud environment flags. Analysis showed:
|
||||||
|
- Zero Docker/cloud users detected across all versions
|
||||||
|
- No way to measure if the fix is working
|
||||||
|
- Cannot determine what % of users are affected
|
||||||
|
- Cannot validate stable user IDs are being generated
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
|
||||||
|
- **Docker Detection**: `isDocker` boolean flag in session_start events
|
||||||
|
- Detects `IS_DOCKER=true` environment variable
|
||||||
|
- Identifies container deployments using boot_id-based stable IDs
|
||||||
|
|
||||||
|
- **Cloud Platform Detection**: `cloudPlatform` string in session_start events
|
||||||
|
- Detects 8 cloud platforms: Railway, Render, Fly.io, Heroku, AWS, Kubernetes, GCP, Azure
|
||||||
|
- Identifies which platform users are deploying to
|
||||||
|
- Returns `null` for local/non-cloud environments
|
||||||
|
|
||||||
|
- **New Detection Method**: `detectCloudPlatform()` in event tracker
|
||||||
|
- Checks platform-specific environment variables
|
||||||
|
- Returns platform name or null
|
||||||
|
- Uses same logic as config-manager's cloud detection
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
|
||||||
|
- `trackSessionStart()` in `src/telemetry/event-tracker.ts`
|
||||||
|
- Now includes `isDocker` field (boolean)
|
||||||
|
- Now includes `cloudPlatform` field (string | null)
|
||||||
|
- Backward compatible - only adds new fields
|
||||||
|
|
||||||
|
#### Testing
|
||||||
|
|
||||||
|
- 16 new unit tests for environment detection
|
||||||
|
- Tests for Docker detection with IS_DOCKER flag
|
||||||
|
- Tests for all 8 cloud platform detections
|
||||||
|
- Tests for local environment (no flags)
|
||||||
|
- Tests for combined Docker + cloud scenarios
|
||||||
|
- 100% coverage for new detection logic
|
||||||
|
|
||||||
|
#### Impact
|
||||||
|
|
||||||
|
**Enables Future Analysis**:
|
||||||
|
- Measure % of users in Docker/cloud vs local
|
||||||
|
- Validate v2.17.1 boot_id-based user ID stability
|
||||||
|
- Segment retention metrics by environment
|
||||||
|
- Identify environment-specific issues
|
||||||
|
- Calculate actual Docker user duplicate rate reduction
|
||||||
|
|
||||||
|
**Expected Insights** (once data collected):
|
||||||
|
- Actual % of Docker/cloud users in user base
|
||||||
|
- Validation that boot_id method is being used
|
||||||
|
- User ID stability improvements measurable
|
||||||
|
- Environment-specific error patterns
|
||||||
|
- Platform distribution of user base
|
||||||
|
|
||||||
|
**No Breaking Changes**:
|
||||||
|
- Only adds new fields to existing events
|
||||||
|
- All existing code continues working
|
||||||
|
- Event validator handles new fields automatically
|
||||||
|
- 100% backward compatible
|
||||||
|
|
||||||
|
#### Technical Details
|
||||||
|
|
||||||
|
**Detection Logic**:
|
||||||
|
```typescript
|
||||||
|
isDocker: process.env.IS_DOCKER === 'true'
|
||||||
|
cloudPlatform: detectCloudPlatform() // Checks 8 env vars
|
||||||
|
```
|
||||||
|
|
||||||
|
**Platform Detection Priority**:
|
||||||
|
1. Railway: `RAILWAY_ENVIRONMENT`
|
||||||
|
2. Render: `RENDER`
|
||||||
|
3. Fly.io: `FLY_APP_NAME`
|
||||||
|
4. Heroku: `HEROKU_APP_NAME`
|
||||||
|
5. AWS: `AWS_EXECUTION_ENV`
|
||||||
|
6. Kubernetes: `KUBERNETES_SERVICE_HOST`
|
||||||
|
7. GCP: `GOOGLE_CLOUD_PROJECT`
|
||||||
|
8. Azure: `AZURE_FUNCTIONS_ENVIRONMENT`
|
||||||
|
|
||||||
|
**Event Structure**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"event": "session_start",
|
||||||
|
"properties": {
|
||||||
|
"version": "2.18.1",
|
||||||
|
"platform": "linux",
|
||||||
|
"arch": "x64",
|
||||||
|
"nodeVersion": "v20.0.0",
|
||||||
|
"isDocker": true,
|
||||||
|
"cloudPlatform": "railway"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Next Steps
|
||||||
|
|
||||||
|
1. Deploy v2.18.1 to production
|
||||||
|
2. Wait 24-48 hours for data collection
|
||||||
|
3. Re-run telemetry analysis with environment segmentation
|
||||||
|
4. Validate v2.17.1 boot_id fix effectiveness
|
||||||
|
5. Calculate actual Docker user duplicate rate reduction
|
||||||
|
|
||||||
## [2.18.0] - 2025-10-08
|
## [2.18.0] - 2025-10-08
|
||||||
|
|
||||||
### 🎯 Validation Warning System Redesign
|
### 🎯 Validation Warning System Redesign
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n-mcp",
|
"name": "n8n-mcp",
|
||||||
"version": "2.18.0",
|
"version": "2.18.1",
|
||||||
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n-mcp-runtime",
|
"name": "n8n-mcp-runtime",
|
||||||
"version": "2.17.6",
|
"version": "2.18.1",
|
||||||
"description": "n8n MCP Server Runtime Dependencies Only",
|
"description": "n8n MCP Server Runtime Dependencies Only",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -175,9 +175,27 @@ export class TelemetryEventTracker {
|
|||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
arch: process.arch,
|
arch: process.arch,
|
||||||
nodeVersion: process.version,
|
nodeVersion: process.version,
|
||||||
|
isDocker: process.env.IS_DOCKER === 'true',
|
||||||
|
cloudPlatform: this.detectCloudPlatform(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect cloud platform from environment variables
|
||||||
|
* Returns platform name or null if not in cloud
|
||||||
|
*/
|
||||||
|
private detectCloudPlatform(): string | null {
|
||||||
|
if (process.env.RAILWAY_ENVIRONMENT) return 'railway';
|
||||||
|
if (process.env.RENDER) return 'render';
|
||||||
|
if (process.env.FLY_APP_NAME) return 'fly';
|
||||||
|
if (process.env.HEROKU_APP_NAME) return 'heroku';
|
||||||
|
if (process.env.AWS_EXECUTION_ENV) return 'aws';
|
||||||
|
if (process.env.KUBERNETES_SERVICE_HOST) return 'kubernetes';
|
||||||
|
if (process.env.GOOGLE_CLOUD_PROJECT) return 'gcp';
|
||||||
|
if (process.env.AZURE_FUNCTIONS_ENVIRONMENT) return 'azure';
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track search queries
|
* Track search queries
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -774,4 +774,197 @@ describe('TelemetryEventTracker', () => {
|
|||||||
expect(events[0].properties.context).toHaveLength(100);
|
expect(events[0].properties.context).toHaveLength(100);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('trackSessionStart()', () => {
|
||||||
|
// Store original env vars
|
||||||
|
const originalEnv = { ...process.env };
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Restore original env vars after each test
|
||||||
|
process.env = { ...originalEnv };
|
||||||
|
eventTracker.clearEventQueue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should track session start with basic environment info', () => {
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events).toHaveLength(1);
|
||||||
|
expect(events[0]).toMatchObject({
|
||||||
|
user_id: 'test-user-123',
|
||||||
|
event: 'session_start',
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = events[0].properties;
|
||||||
|
expect(props.version).toBeDefined();
|
||||||
|
expect(typeof props.version).toBe('string');
|
||||||
|
expect(props.platform).toBeDefined();
|
||||||
|
expect(props.arch).toBeDefined();
|
||||||
|
expect(props.nodeVersion).toBeDefined();
|
||||||
|
expect(props.isDocker).toBe(false);
|
||||||
|
expect(props.cloudPlatform).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect Docker environment', () => {
|
||||||
|
process.env.IS_DOCKER = 'true';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(true);
|
||||||
|
expect(events[0].properties.cloudPlatform).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect Railway cloud platform', () => {
|
||||||
|
process.env.RAILWAY_ENVIRONMENT = 'production';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(false);
|
||||||
|
expect(events[0].properties.cloudPlatform).toBe('railway');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect Render cloud platform', () => {
|
||||||
|
process.env.RENDER = 'true';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(false);
|
||||||
|
expect(events[0].properties.cloudPlatform).toBe('render');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect Fly.io cloud platform', () => {
|
||||||
|
process.env.FLY_APP_NAME = 'my-app';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(false);
|
||||||
|
expect(events[0].properties.cloudPlatform).toBe('fly');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect Heroku cloud platform', () => {
|
||||||
|
process.env.HEROKU_APP_NAME = 'my-app';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(false);
|
||||||
|
expect(events[0].properties.cloudPlatform).toBe('heroku');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect AWS cloud platform', () => {
|
||||||
|
process.env.AWS_EXECUTION_ENV = 'AWS_ECS_FARGATE';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(false);
|
||||||
|
expect(events[0].properties.cloudPlatform).toBe('aws');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect Kubernetes cloud platform', () => {
|
||||||
|
process.env.KUBERNETES_SERVICE_HOST = '10.0.0.1';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(false);
|
||||||
|
expect(events[0].properties.cloudPlatform).toBe('kubernetes');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect GCP cloud platform', () => {
|
||||||
|
process.env.GOOGLE_CLOUD_PROJECT = 'my-project';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(false);
|
||||||
|
expect(events[0].properties.cloudPlatform).toBe('gcp');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect Azure cloud platform', () => {
|
||||||
|
process.env.AZURE_FUNCTIONS_ENVIRONMENT = 'Production';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(false);
|
||||||
|
expect(events[0].properties.cloudPlatform).toBe('azure');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect Docker + cloud platform combination', () => {
|
||||||
|
process.env.IS_DOCKER = 'true';
|
||||||
|
process.env.RAILWAY_ENVIRONMENT = 'production';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(true);
|
||||||
|
expect(events[0].properties.cloudPlatform).toBe('railway');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle local environment (no Docker, no cloud)', () => {
|
||||||
|
// Ensure no Docker or cloud env vars are set
|
||||||
|
delete process.env.IS_DOCKER;
|
||||||
|
delete process.env.RAILWAY_ENVIRONMENT;
|
||||||
|
delete process.env.RENDER;
|
||||||
|
delete process.env.FLY_APP_NAME;
|
||||||
|
delete process.env.HEROKU_APP_NAME;
|
||||||
|
delete process.env.AWS_EXECUTION_ENV;
|
||||||
|
delete process.env.KUBERNETES_SERVICE_HOST;
|
||||||
|
delete process.env.GOOGLE_CLOUD_PROJECT;
|
||||||
|
delete process.env.AZURE_FUNCTIONS_ENVIRONMENT;
|
||||||
|
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(false);
|
||||||
|
expect(events[0].properties.cloudPlatform).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prioritize Railway over other cloud platforms', () => {
|
||||||
|
// Set multiple cloud env vars - Railway should win (first in detection chain)
|
||||||
|
process.env.RAILWAY_ENVIRONMENT = 'production';
|
||||||
|
process.env.RENDER = 'true';
|
||||||
|
process.env.FLY_APP_NAME = 'my-app';
|
||||||
|
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.cloudPlatform).toBe('railway');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not track when disabled', () => {
|
||||||
|
mockIsEnabled.mockReturnValue(false);
|
||||||
|
process.env.IS_DOCKER = 'true';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should treat IS_DOCKER=false as not Docker', () => {
|
||||||
|
process.env.IS_DOCKER = 'false';
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
expect(events[0].properties.isDocker).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should include version, platform, arch, and nodeVersion', () => {
|
||||||
|
eventTracker.trackSessionStart();
|
||||||
|
|
||||||
|
const events = eventTracker.getEventQueue();
|
||||||
|
const props = events[0].properties;
|
||||||
|
|
||||||
|
// Check all expected fields are present
|
||||||
|
expect(props).toHaveProperty('version');
|
||||||
|
expect(props).toHaveProperty('platform');
|
||||||
|
expect(props).toHaveProperty('arch');
|
||||||
|
expect(props).toHaveProperty('nodeVersion');
|
||||||
|
expect(props).toHaveProperty('isDocker');
|
||||||
|
expect(props).toHaveProperty('cloudPlatform');
|
||||||
|
|
||||||
|
// Verify types
|
||||||
|
expect(typeof props.version).toBe('string');
|
||||||
|
expect(typeof props.platform).toBe('string');
|
||||||
|
expect(typeof props.arch).toBe('string');
|
||||||
|
expect(typeof props.nodeVersion).toBe('string');
|
||||||
|
expect(typeof props.isDocker).toBe('boolean');
|
||||||
|
expect(props.cloudPlatform === null || typeof props.cloudPlatform === 'string').toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user