Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/auto-label.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ name: Auto Label
on:
pull_request:
types: [opened, reopened, synchronized]
permissions:
issues: write
pull-requests: write
jobs:
label:
runs-on: ubuntu-latest
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/auto-label.yml.orig
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Auto Label
on:
pull_request:
types: [opened, reopened, synchronized]
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
script: |
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number
});

const labels = new Set();

for (const file of files) {
const path = file.filename;
if (path.includes('docs/') || path.endsWith('.md')) labels.add('documentation');
if (path.includes('test/') || path.includes('.test.') || path.includes('.spec.')) labels.add('tests');
if (path.includes('.github/workflows/')) labels.add('ci/cd');
if (path.endsWith('.js') || path.endsWith('.ts') || path.endsWith('.jsx') || path.endsWith('.tsx')) labels.add('javascript');
if (path.endsWith('.py')) labels.add('python');
if (path.endsWith('.css') || path.endsWith('.scss') || path.endsWith('.sass')) labels.add('styling');
}

if (labels.size > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels: Array.from(labels)
});
}
1 change: 1 addition & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
types: [opened, reopened, synchronize, edited]
permissions:
issues: write
pull-requests: write
jobs:
validate:
runs-on: ubuntu-latest
Expand Down
48 changes: 48 additions & 0 deletions .github/workflows/pr-checks.yml.orig
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: PR Checks
on:
pull_request:
types: [opened, reopened, synchronize, edited]
permissions:
issues: write
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const issues = [];

if (pr.title.length < 10) {
issues.push('❌ PR title too short (minimum 10 characters)');
}
if (!/^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?:/.test(pr.title)) {
issues.push('⚠️ PR title should follow conventional commits format');
}

if (!pr.body || pr.body.length < 20) {
issues.push('❌ PR description is required (minimum 20 characters)');
}

const totalChanges = (pr.additions || 0) + (pr.deletions || 0);
if (totalChanges > 500) {
issues.push(`⚠️ Large PR detected (${totalChanges} lines changed)`);
}

if (issues.length > 0) {
// Fork PRs get a read-only GITHUB_TOKEN; skip commenting to avoid errors
if (pr.head.repo.full_name === pr.base.repo.full_name) {
const comment = `## 🔍 PR Validation\n\n${issues.join('\n')}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: comment
});
} else {
core.warning('Skipping PR comment for fork PR (read-only token)');
issues.forEach(issue => core.warning(issue));
}
core.setFailed('PR validation failed');
}
73 changes: 19 additions & 54 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 10 additions & 7 deletions packages/observability/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,23 @@
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"lint": "eslint . --max-warnings 0"
"lint": "eslint . --max-warnings 0",
"test": "vitest run"
},
"dependencies": {
"@opentelemetry/api": "^1.7.0",
"@opentelemetry/sdk-node": "^0.45.0",
"@opentelemetry/exporter-trace-otlp-grpc": "^0.45.0",
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.45.0",
"@opentelemetry/exporter-trace-otlp-grpc": "^0.45.0",
"@opentelemetry/instrumentation": "^0.45.0",
"@opentelemetry/resources": "^1.18.0",
"@opentelemetry/sdk-node": "^0.45.0",
"@opentelemetry/semantic-conventions": "^1.18.0"
},
"devDependencies": {
"@types/node": "^20",
"typescript": "^5",
"eslint": "^8"
"@repo/typescript-config": "*",
"@types/node": "^20.19.37",
"eslint": "^8",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
}
}
}
91 changes: 91 additions & 0 deletions packages/observability/src/observability.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import afterEach.

Copilot Autofix

AI 4 days ago

In general, unused imports should be removed from the import list to avoid confusion and keep the codebase clean. Here, we only need to adjust the vitest import on line 1 to no longer include afterEach.

The best fix without changing functionality is to edit packages/observability/src/observability.test.ts and remove afterEach from the destructuring import from 'vitest'. No other lines or imports need to change, and no new methods or definitions are required. This keeps all currently used testing helpers (describe, it, expect, beforeEach, vi) while eliminating the unused one.

Suggested changeset 1
packages/observability/src/observability.test.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/observability/src/observability.test.ts b/packages/observability/src/observability.test.ts
--- a/packages/observability/src/observability.test.ts
+++ b/packages/observability/src/observability.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
+import { describe, it, expect, beforeEach, vi } from 'vitest';
 import * as observabilityModule from './observability';
 import { Observability } from './observability';
 
EOF
@@ -1,4 +1,4 @@
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import * as observabilityModule from './observability';
import { Observability } from './observability';

Copilot is powered by AI and may make mistakes. Always verify output.
import * as observabilityModule from './observability';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import observabilityModule.

Copilot Autofix

AI 4 days ago

In general, unused imports should be removed to improve readability and avoid confusion. If the module is only needed for side effects, it should instead be imported without binding it to a name (e.g., import './module';). Here, the module is already imported dynamically inside each test, so the top-level namespace import is redundant.

The best fix without changing existing functionality is to delete the unused import line import * as observabilityModule from './observability'; in packages/observability/src/observability.test.ts. No additional imports or code changes are required because all references to the observability module use await import('./observability') within tests. Removing this line will not alter runtime behavior or test logic.

Concretely: in packages/observability/src/observability.test.ts, remove line 2 containing the unused import. No new methods, imports, or definitions are needed.

Suggested changeset 1
packages/observability/src/observability.test.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/observability/src/observability.test.ts b/packages/observability/src/observability.test.ts
--- a/packages/observability/src/observability.test.ts
+++ b/packages/observability/src/observability.test.ts
@@ -1,5 +1,4 @@
 import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
-import * as observabilityModule from './observability';
 import { Observability } from './observability';
 
 // Mock the open telemetry modules
EOF
@@ -1,5 +1,4 @@
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import * as observabilityModule from './observability';
import { Observability } from './observability';

// Mock the open telemetry modules
Copilot is powered by AI and may make mistakes. Always verify output.
import { Observability } from './observability';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import Observability.

Copilot Autofix

AI 4 days ago

In general, unused imports should be removed to improve readability and avoid confusion. Since all uses of the Observability class go through the dynamically imported mod object (e.g., mod.Observability), the direct named import is unnecessary.

The best fix here is to delete line 3 (import { Observability } from './observability';) from packages/observability/src/observability.test.ts. No other code changes are required, because tests already work with mod.Observability and observabilityModule and do not rely on the directly imported Observability symbol. No additional methods, imports, or definitions are needed.

Suggested changeset 1
packages/observability/src/observability.test.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/observability/src/observability.test.ts b/packages/observability/src/observability.test.ts
--- a/packages/observability/src/observability.test.ts
+++ b/packages/observability/src/observability.test.ts
@@ -1,6 +1,5 @@
 import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
 import * as observabilityModule from './observability';
-import { Observability } from './observability';
 
 // Mock the open telemetry modules
 vi.mock('@opentelemetry/sdk-node', () => {
EOF
@@ -1,6 +1,5 @@
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import * as observabilityModule from './observability';
import { Observability } from './observability';

// Mock the open telemetry modules
vi.mock('@opentelemetry/sdk-node', () => {
Copilot is powered by AI and may make mistakes. Always verify output.

// Mock the open telemetry modules
vi.mock('@opentelemetry/sdk-node', () => {
return {
NodeSDK: vi.fn().mockImplementation(() => ({
start: vi.fn(),
shutdown: vi.fn(),
})),
};
});

vi.mock('@opentelemetry/api', async () => {
const actual = await vi.importActual('@opentelemetry/api');
return {
...actual,
trace: {
getTracer: vi.fn().mockReturnValue({
startActiveSpan: vi.fn((name, callback) => {
const mockSpan = {
setAttribute: vi.fn(),
setStatus: vi.fn(),
recordException: vi.fn(),
end: vi.fn(),
};
return callback(mockSpan);
}),
}),
},
metrics: {
getMeter: vi.fn().mockReturnValue({
createCounter: vi.fn().mockReturnValue({ add: vi.fn() }),
createHistogram: vi.fn().mockReturnValue({ record: vi.fn() }),
}),
},
};
});

describe('observability module exports', () => {
const testConfig = {
serviceName: 'test-service',
enabled: false // disabled to prevent actual connections
};

beforeEach(() => {
// Reset vi modules so that each test gets a fresh internal observabilityInstance variable
vi.resetModules();
vi.clearAllMocks();
});

describe('initObservability', () => {
it('should initialize and return an Observability instance', async () => {
// Import freshly to get a new instance
const mod = await import('./observability');
const instance = mod.initObservability(testConfig);

expect(instance).toBeInstanceOf(mod.Observability);
// @ts-ignore - accessing private member for verification
expect(instance.serviceName).toBe('test-service');
// @ts-ignore - accessing private member for verification
expect(instance.enabled).toBe(false);
Comment on lines +60 to +63
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using @ts-ignore to access private members for verification makes tests brittle and bypasses type safety. This goes against the 'strict type hinting' principle from the repository style guide.

A better approach is to test the observable behavior. For example, to test the enabled: false case, you could assert that NodeSDK is not instantiated. To test the serviceName, you could assert that trace.getTracer is called with the correct name in a separate test where observability is enabled.

References
  1. The use of @ts-ignore bypasses TypeScript's type checking, which is contrary to the principle of strict type hinting mentioned in the repository style guide (line 7). (link)

Comment on lines +60 to +63
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These assertions rely on @ts-ignore to access private members (serviceName, enabled). That makes the tests brittle against refactors (e.g., switching to #private fields) and tests implementation details rather than behavior. Prefer asserting observable behavior instead (e.g., verify OpenTelemetry SDK setup methods are/aren’t invoked based on config), or add a minimal read-only getter if you really need to validate config.

Copilot uses AI. Check for mistakes.
});

it('should return the same instance when called multiple times', async () => {
const mod = await import('./observability');
const instance1 = mod.initObservability(testConfig);
const instance2 = mod.initObservability({ serviceName: 'other-service' }); // Should ignore this config

expect(instance1).toBe(instance2);
// @ts-ignore
expect(instance2.serviceName).toBe('test-service');
Comment on lines +72 to +73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

As with the previous comment, it's best to avoid @ts-ignore to maintain strict type safety.

To verify that the singleton instance is not reconfigured on subsequent calls, you could check that the initialization logic (e.g., calls to trace.getTracer()) is only executed once, even when initObservability is called multiple times. This tests the behavior without accessing private implementation details.

References
  1. The use of @ts-ignore bypasses TypeScript's type checking, which is contrary to the principle of strict type hinting mentioned in the repository style guide (line 7). (link)

});
});

describe('getObservability', () => {
it('should throw an error if called before initialization', async () => {
const mod = await import('./observability');
expect(() => mod.getObservability()).toThrow('Observability not initialized. Call initObservability() first.');
});

it('should return the instance if already initialized', async () => {
const mod = await import('./observability');
const initialized = mod.initObservability(testConfig);
const retrieved = mod.getObservability();

expect(retrieved).toBe(initialized);
});
});
});
11 changes: 11 additions & 0 deletions patch.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--- .github/workflows/auto-label.yml
+++ .github/workflows/auto-label.yml
@@ -3,6 +3,9 @@
pull_request:
types: [opened, reopened, synchronized]
+permissions:
+ issues: write
+ pull-requests: write
jobs:
label:
runs-on: ubuntu-latest
10 changes: 10 additions & 0 deletions patch2.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--- .github/workflows/pr-checks.yml
+++ .github/workflows/pr-checks.yml
@@ -5,6 +5,7 @@
types: [opened, reopened, synchronize, edited]
permissions:
issues: write
+ pull-requests: write
jobs:
validate:
runs-on: ubuntu-latest
Loading