From 4c4c8bda4941bc604002c06efd3c2f54e09820bf Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Tue, 23 Sep 2025 18:25:24 -0700 Subject: [PATCH 01/36] add script to bump dependencies --- scripts/update_dependencies.js | 83 ++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 scripts/update_dependencies.js diff --git a/scripts/update_dependencies.js b/scripts/update_dependencies.js new file mode 100644 index 00000000..91fb36b0 --- /dev/null +++ b/scripts/update_dependencies.js @@ -0,0 +1,83 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); + +async function getLatestVersion(packageName) { + const https = require('https'); + + return new Promise((resolve, reject) => { + const url = `https://registry.npmjs.org/${packageName}/latest`; + const request = https.get(url, { timeout: 30000 }, (response) => { + let data = ''; + response.on('data', (chunk) => data += chunk); + response.on('end', () => { + try { + if (response.statusCode === 200) { + const packageData = JSON.parse(data); + resolve(packageData.version); + } else { + console.warn(`Warning: Could not get latest version for ${packageName}: HTTP ${response.statusCode}`); + resolve(null); + } + } catch (parseError) { + console.warn(`Warning: Could not parse response for ${packageName}: ${parseError.message}`); + resolve(null); + } + }); + }); + + request.on('error', (requestError) => { + console.warn(`Warning: Could not get latest version for ${packageName}: ${requestError.message}`); + resolve(null); + }); + + request.on('timeout', () => { + request.destroy(); + console.warn(`Warning: Timeout getting latest version for ${packageName}`); + resolve(null); + }); + }); +} + +async function main() { + const packageJsonPath = path.join('aws-distro-opentelemetry-node-autoinstrumentation', 'package.json'); + + try { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + let updated = false; + + const dependencies = packageJson.dependencies || {}; + const otelPackages = Object.keys(dependencies).filter(pkg => pkg.startsWith('@opentelemetry/')); + + for (const packageName of otelPackages) { + const latestVersion = await getLatestVersion(packageName); + if (latestVersion) { + const currentVersion = dependencies[packageName]; + + if (currentVersion !== latestVersion) { + packageJson.dependencies[packageName] = latestVersion; + updated = true; + console.log(`Updated ${packageName}: ${currentVersion} → ${latestVersion}`); + } else { + console.log(`${packageName} already at latest version: ${latestVersion}`); + } + } + } + + if (updated) { + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n'); + console.log('Dependencies updated successfully'); + } else { + console.log('No OpenTelemetry dependencies needed updating'); + } + + } catch (fileError) { + console.error(`Error updating dependencies: ${fileError.message}`); + process.exit(1); + } +} + +if (require.main === module) { + main().catch(console.error); +} From 5d6b7c9e970a12b6288505dcadf881f46ae8bc0f Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Tue, 23 Sep 2025 18:26:54 -0700 Subject: [PATCH 02/36] add nightly build workflow --- .github/workflows/nightly-build.yml | 70 +++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .github/workflows/nightly-build.yml diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml new file mode 100644 index 00000000..67f7791b --- /dev/null +++ b/.github/workflows/nightly-build.yml @@ -0,0 +1,70 @@ +name: Nightly Upstream Snapshot Build + +on: + schedule: + - cron: "21 3 * * *" + workflow_dispatch: + +env: + BRANCH_NAME: nightly-dependency-updates + +jobs: + update-and-create-pr: + runs-on: ubuntu-latest + outputs: + has_changes: ${{ steps.check_changes.outputs.has_changes }} + + steps: + - name: Checkout repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Check if nightly branch already exists + run: | + if git ls-remote --exit-code --heads origin "$BRANCH_NAME"; then + echo "Branch $BRANCH_NAME already exists. Skipping run to avoid conflicts." + echo "Please merge or close the existing PR before the next nightly run." + exit 1 + fi + + - name: Configure git and create branch + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git checkout -b "$BRANCH_NAME" + + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 #v5.0.0 + with: + node-version: '18' + + - name: Install dependencies + run: npm install + + - name: Update dependencies + run: node scripts/update_dependencies.js + + - name: Check for changes and create PR + id: check_changes + run: | + if git diff --quiet; then + echo "No dependency updates needed" + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "Dependencies were updated" + echo "has_changes=true" >> $GITHUB_OUTPUT + + git add aws-distro-opentelemetry-node-autoinstrumentation/package.json + git commit -m "chore: update OpenTelemetry dependencies to latest versions" + git push origin "$BRANCH_NAME" + + gh pr create \ + --title "Nightly dependency update: OpenTelemetry packages to latest versions" \ + --body "Automated update of OpenTelemetry dependencies to their latest available versions." \ + --base main \ + --head "$BRANCH_NAME" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 807dc858d51413fa2ee6f0b0e67405ada3ec708d Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Tue, 23 Sep 2025 18:30:14 -0700 Subject: [PATCH 03/36] let main build run on nightly build branch --- .github/workflows/main-build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main-build.yml b/.github/workflows/main-build.yml index 51e43ac8..7ec2826d 100644 --- a/.github/workflows/main-build.yml +++ b/.github/workflows/main-build.yml @@ -5,6 +5,7 @@ on: branches: - main - "release/v*" + - nightly-dependency-updates workflow_dispatch: env: @@ -109,7 +110,7 @@ jobs: name: "Publish Main Build Status" needs: [ build, application-signals-e2e-test ] runs-on: ubuntu-latest - if: always() + if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') steps: - name: Configure AWS Credentials for emitting metrics uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 #v5.0.0 From 6ffee3abac107e63cb81ae0564de86d6ec84cce9 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Thu, 25 Sep 2025 14:37:48 -0700 Subject: [PATCH 04/36] Revert "let main build run on nightly build branch" This reverts commit 807dc858d51413fa2ee6f0b0e67405ada3ec708d. --- .github/workflows/main-build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main-build.yml b/.github/workflows/main-build.yml index 7ec2826d..51e43ac8 100644 --- a/.github/workflows/main-build.yml +++ b/.github/workflows/main-build.yml @@ -5,7 +5,6 @@ on: branches: - main - "release/v*" - - nightly-dependency-updates workflow_dispatch: env: @@ -110,7 +109,7 @@ jobs: name: "Publish Main Build Status" needs: [ build, application-signals-e2e-test ] runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') + if: always() steps: - name: Configure AWS Credentials for emitting metrics uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 #v5.0.0 From 8c11228a81d25d43e999a32e849695231fb2620c Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Thu, 25 Sep 2025 16:58:43 -0700 Subject: [PATCH 05/36] update dependency upgrade script to use github releases --- scripts/generate-licenses.js | 85 ++++++++++++++++ scripts/update_dependencies.js | 173 +++++++++++++++++++++++++++++---- 2 files changed, 239 insertions(+), 19 deletions(-) create mode 100755 scripts/generate-licenses.js diff --git a/scripts/generate-licenses.js b/scripts/generate-licenses.js new file mode 100755 index 00000000..26063dae --- /dev/null +++ b/scripts/generate-licenses.js @@ -0,0 +1,85 @@ +#!/usr/bin/env node + +const checker = require('license-checker-rseidelsohn'); +const fs = require('fs'); +const path = require('path'); + +const workspaceDir = path.join(__dirname, '..', 'aws-distro-opentelemetry-node-autoinstrumentation'); + +checker.init({ + start: workspaceDir, + production: true, + excludePrivatePackages: true, + // Include packages from both local and hoisted node_modules + includePackages: '', + customFormat: { + name: '', + version: '', + repository: '', + licenseText: '' + } +}, (err, packages) => { + if (err) { + console.error('Error:', err); + process.exit(1); + } + + let output = ''; + const processedPackages = []; + + Object.keys(packages).forEach(packageKey => { + const pkg = packages[packageKey]; + + // Parse package name and version correctly for scoped packages + let name, version; + if (packageKey.startsWith('@')) { + const lastAtIndex = packageKey.lastIndexOf('@'); + name = packageKey.substring(0, lastAtIndex); + version = packageKey.substring(lastAtIndex + 1); + } else { + const atIndex = packageKey.indexOf('@'); + name = packageKey.substring(0, atIndex); + version = packageKey.substring(atIndex + 1); + } + + // Skip our own package + if (name === '@aws/aws-distro-opentelemetry-node-autoinstrumentation') { + return; + } + + processedPackages.push({ name, version, pkg }); + }); + + // Sort by package name for consistent output + processedPackages.sort((a, b) => a.name.localeCompare(b.name)); + + processedPackages.forEach(({ name, version, pkg }) => { + output += `** ${name}; version ${version}`; + if (pkg.repository) { + output += ` -- ${pkg.repository}`; + } + output += '\n'; + + if (pkg.licenseText) { + output += pkg.licenseText + '\n\n'; + } else { + output += 'License text not available\n\n'; + } + }); + + const outputPath = path.join(__dirname, '..', 'THIRD-PARTY-LICENSES'); + fs.writeFileSync(outputPath, output); + console.log(`Generated THIRD-PARTY-LICENSES with ${processedPackages.length} packages`); + + // Also show what direct dependencies might be missing + const workspacePackageJson = JSON.parse( + fs.readFileSync(path.join(workspaceDir, 'package.json')) + ); + const directDeps = Object.keys(workspacePackageJson.dependencies || {}); + const foundPackages = new Set(processedPackages.map(p => p.name)); + const missingDeps = directDeps.filter(dep => !foundPackages.has(dep)); + + if (missingDeps.length > 0) { + console.log(`\nDirect dependencies without license files found: ${missingDeps.join(', ')}`); + } +}); diff --git a/scripts/update_dependencies.js b/scripts/update_dependencies.js index 91fb36b0..735fe1aa 100644 --- a/scripts/update_dependencies.js +++ b/scripts/update_dependencies.js @@ -3,43 +3,151 @@ const fs = require('fs'); const path = require('path'); -async function getLatestVersion(packageName) { +async function httpsGet(url) { const https = require('https'); return new Promise((resolve, reject) => { - const url = `https://registry.npmjs.org/${packageName}/latest`; - const request = https.get(url, { timeout: 30000 }, (response) => { + const options = { + timeout: 30000, + headers: { + 'User-Agent': 'Mozilla/5.0 (compatible; Node.js script)' + } + }; + + const request = https.get(url, options, (response) => { let data = ''; response.on('data', (chunk) => data += chunk); response.on('end', () => { try { if (response.statusCode === 200) { - const packageData = JSON.parse(data); - resolve(packageData.version); + resolve(JSON.parse(data)); } else { - console.warn(`Warning: Could not get latest version for ${packageName}: HTTP ${response.statusCode}`); + console.warn(`Warning: HTTP ${response.statusCode} for ${url}`); resolve(null); } } catch (parseError) { - console.warn(`Warning: Could not parse response for ${packageName}: ${parseError.message}`); + console.warn(`Warning: Could not parse response for ${url}: ${parseError.message}`); resolve(null); } }); }); request.on('error', (requestError) => { - console.warn(`Warning: Could not get latest version for ${packageName}: ${requestError.message}`); + console.warn(`Warning: Request failed for ${url}: ${requestError.message}`); resolve(null); }); request.on('timeout', () => { request.destroy(); - console.warn(`Warning: Timeout getting latest version for ${packageName}`); + console.warn(`Warning: Timeout for ${url}`); resolve(null); }); }); } +async function getVersionsFromGitHubReleases() { + try { + // Get versions from opentelemetry-js releases + const jsReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js/releases'); + const contribReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases'); + + const versions = {}; + + // Process opentelemetry-js releases + if (jsReleases) { + for (const release of jsReleases) { + const tagName = release.tag_name; + + // Core packages: v2.0.0 -> 2.0.0 + if (/^v\d+\.\d+\.\d+$/.test(tagName) && !versions.core) { + versions.core = tagName.substring(1); + } + // Experimental packages: experimental/v0.57.1 -> 0.57.1 + else if (tagName.startsWith('experimental/v') && !versions.experimental) { + versions.experimental = tagName.substring('experimental/v'.length); + } + // API package: api/v1.9.0 -> 1.9.0 + else if (tagName.startsWith('api/v') && !versions.api) { + versions.api = tagName.substring('api/v'.length); + } + // Semantic conventions: semconv/v1.28.0 -> 1.28.0 + else if (tagName.startsWith('semconv/v') && !versions.semconv) { + versions.semconv = tagName.substring('semconv/v'.length); + } + } + } + + // Process opentelemetry-js-contrib releases + if (contribReleases) { + for (const release of contribReleases) { + const tagName = release.tag_name; + + // Extract component name and version from releases like "auto-instrumentations-node: v0.64.4" + const match = tagName.match(/^([^:]+):\s*v(.+)$/); + if (match) { + const componentName = match[1]; + const version = match[2]; + versions[componentName] = version; + } + } + } + + console.log('Found GitHub release versions:', versions); + return versions; + + } catch (error) { + console.warn(`Warning: Could not get GitHub releases: ${error.message}`); + return {}; + } +} + +async function getLatestVersionFromNpm(packageName) { + try { + const data = await httpsGet(`https://registry.npmjs.org/${packageName}/latest`); + return data ? data.version : null; + } catch (error) { + console.warn(`Warning: Could not get npm version for ${packageName}: ${error.message}`); + return null; + } +} + +// Package categorization based on their typical versioning patterns +const PACKAGE_CATEGORIES = { + api: ['@opentelemetry/api'], + core: [ + '@opentelemetry/core', + '@opentelemetry/exporter-zipkin', + '@opentelemetry/resources', + '@opentelemetry/sdk-metrics', + '@opentelemetry/sdk-trace-base' + ], + experimental: [ + '@opentelemetry/api-events', + '@opentelemetry/exporter-metrics-otlp-grpc', + '@opentelemetry/exporter-metrics-otlp-http', + '@opentelemetry/exporter-trace-otlp-proto', + '@opentelemetry/exporter-logs-otlp-grpc', + '@opentelemetry/exporter-logs-otlp-http', + '@opentelemetry/exporter-logs-otlp-proto', + '@opentelemetry/instrumentation', + '@opentelemetry/otlp-transformer', + '@opentelemetry/sdk-events', + '@opentelemetry/sdk-logs', + '@opentelemetry/sdk-node' + ], + semconv: ['@opentelemetry/semantic-conventions'], + // These have individual releases in opentelemetry-js-contrib + contrib: [ + '@opentelemetry/auto-configuration-propagators', + '@opentelemetry/auto-instrumentations-node', + '@opentelemetry/baggage-span-processor', + '@opentelemetry/instrumentation-aws-sdk', + '@opentelemetry/id-generator-aws-xray', + '@opentelemetry/propagator-aws-xray', + '@opentelemetry/resource-detector-aws' + ] +}; + async function main() { const packageJsonPath = path.join('aws-distro-opentelemetry-node-autoinstrumentation', 'package.json'); @@ -47,21 +155,48 @@ async function main() { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); let updated = false; + // Get versions from GitHub releases + const githubVersions = await getVersionsFromGitHubReleases(); + + // Get all @opentelemetry packages from dependencies const dependencies = packageJson.dependencies || {}; const otelPackages = Object.keys(dependencies).filter(pkg => pkg.startsWith('@opentelemetry/')); - - for (const packageName of otelPackages) { - const latestVersion = await getLatestVersion(packageName); - if (latestVersion) { - const currentVersion = dependencies[packageName]; - if (currentVersion !== latestVersion) { - packageJson.dependencies[packageName] = latestVersion; - updated = true; - console.log(`Updated ${packageName}: ${currentVersion} → ${latestVersion}`); + // Update each package + for (const packageName of otelPackages) { + const currentVersion = dependencies[packageName]; + let newVersion = null; + + // Try to get version from GitHub releases first + if (PACKAGE_CATEGORIES.api.includes(packageName) && githubVersions.api) { + newVersion = githubVersions.api; + } else if (PACKAGE_CATEGORIES.core.includes(packageName) && githubVersions.core) { + newVersion = githubVersions.core; + } else if (PACKAGE_CATEGORIES.experimental.includes(packageName) && githubVersions.experimental) { + newVersion = githubVersions.experimental; + } else if (PACKAGE_CATEGORIES.semconv.includes(packageName) && githubVersions.semconv) { + newVersion = githubVersions.semconv; + } else if (PACKAGE_CATEGORIES.contrib.includes(packageName)) { + // Try to get version from contrib releases by stripping @opentelemetry/ prefix + const componentName = packageName.replace('@opentelemetry/', ''); + if (githubVersions[componentName]) { + newVersion = githubVersions[componentName]; } else { - console.log(`${packageName} already at latest version: ${latestVersion}`); + // Fall back to npm registry + newVersion = await getLatestVersionFromNpm(packageName); } + } else { + // Fall back to npm registry for any uncategorized packages + console.log(`Package ${packageName} not categorized, fetching version from npm`); + newVersion = await getLatestVersionFromNpm(packageName); + } + + if (newVersion && currentVersion !== newVersion) { + packageJson.dependencies[packageName] = newVersion; + updated = true; + console.log(`Updated ${packageName}: ${currentVersion} → ${newVersion}`); + } else if (newVersion) { + console.log(`${packageName} already at latest version: ${newVersion}`); } } From 7cad67fe6a0866d7a025345000afd7801e90cb08 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 10:14:11 -0700 Subject: [PATCH 06/36] add script to find breaking changes --- .github/workflows/nightly-build.yml | 19 +- scripts/find_breaking_changes.js | 289 ++++++++++++++++++++++++++++ 2 files changed, 302 insertions(+), 6 deletions(-) create mode 100644 scripts/find_breaking_changes.js diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 67f7791b..df5b29b9 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -29,17 +29,21 @@ jobs: exit 1 fi + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 #v5.0.0 + with: + node-version: '18' + + - name: Check for breaking changes + id: breaking_changes + run: node scripts/find_breaking_changes.js + - name: Configure git and create branch run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" git checkout -b "$BRANCH_NAME" - - name: Set up Node.js - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 #v5.0.0 - with: - node-version: '18' - - name: Install dependencies run: npm install @@ -62,7 +66,10 @@ jobs: gh pr create \ --title "Nightly dependency update: OpenTelemetry packages to latest versions" \ - --body "Automated update of OpenTelemetry dependencies to their latest available versions." \ + --body "Automated update of OpenTelemetry dependencies to their latest available versions. + + **Upstream releases with breaking changes:** + ${{ steps.breaking_changes.outputs.breaking_changes_info }}" \ --base main \ --head "$BRANCH_NAME" fi diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js new file mode 100644 index 00000000..03e0fc12 --- /dev/null +++ b/scripts/find_breaking_changes.js @@ -0,0 +1,289 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); + +async function httpsGet(url) { + const https = require('https'); + + return new Promise((resolve, reject) => { + const options = { + timeout: 30000, + headers: { + 'User-Agent': 'Mozilla/5.0 (compatible; Node.js script)' + } + }; + + const request = https.get(url, options, (response) => { + let data = ''; + response.on('data', (chunk) => data += chunk); + response.on('end', () => { + try { + if (response.statusCode === 200) { + resolve(JSON.parse(data)); + } else { + console.warn(`Warning: HTTP ${response.statusCode} for ${url}`); + resolve(null); + } + } catch (parseError) { + console.warn(`Warning: Could not parse response for ${url}: ${parseError.message}`); + resolve(null); + } + }); + }); + + request.on('error', (requestError) => { + console.warn(`Warning: Request failed for ${url}: ${requestError.message}`); + resolve(null); + }); + + request.on('timeout', () => { + request.destroy(); + console.warn(`Warning: Timeout for ${url}`); + resolve(null); + }); + }); +} + +function getCurrentVersionsFromPackageJson() { + try { + const packageJsonPath = path.join('aws-distro-opentelemetry-node-autoinstrumentation', 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + + const dependencies = packageJson.dependencies || {}; + + // Find representative versions for each category + const currentVersions = {}; + + // API version + if (dependencies['@opentelemetry/api']) { + currentVersions.api = dependencies['@opentelemetry/api']; + } + + // Core version (use sdk-trace-base as representative) + if (dependencies['@opentelemetry/sdk-trace-base']) { + currentVersions.core = dependencies['@opentelemetry/sdk-trace-base']; + } + + // Experimental version (use sdk-node as representative) + if (dependencies['@opentelemetry/sdk-node']) { + currentVersions.experimental = dependencies['@opentelemetry/sdk-node']; + } + + // Semconv version + if (dependencies['@opentelemetry/semantic-conventions']) { + currentVersions.semconv = dependencies['@opentelemetry/semantic-conventions']; + } + + // Get all contrib packages we actually depend on + const contribPackages = {}; + for (const [packageName, version] of Object.entries(dependencies)) { + if (packageName.startsWith('@opentelemetry/') && + !['@opentelemetry/api', '@opentelemetry/sdk-trace-base', '@opentelemetry/sdk-node', '@opentelemetry/semantic-conventions'].includes(packageName)) { + // Check if it's likely a contrib package (not in core/experimental categories) + const componentName = packageName.replace('@opentelemetry/', ''); + contribPackages[componentName] = version; + } + } + + currentVersions.contrib = contribPackages; + + return currentVersions; + + } catch (error) { + console.warn(`Error reading current versions: ${error.message}`); + return {}; + } +} + +function compareVersions(current, target) { + // Simple version comparison - assumes semver format + const currentParts = current.split('.').map(Number); + const targetParts = target.split('.').map(Number); + + for (let i = 0; i < Math.max(currentParts.length, targetParts.length); i++) { + const currentPart = currentParts[i] || 0; + const targetPart = targetParts[i] || 0; + + if (currentPart < targetPart) return -1; + if (currentPart > targetPart) return 1; + } + + return 0; +} + +async function findBreakingChangesInReleases(repoName, currentVersion, newVersion, releasePattern) { + try { + const releases = await httpsGet(`https://api.github.com/repos/open-telemetry/${repoName}/releases?per_page=100`); + if (!releases) return []; + + const breakingReleases = []; + + for (const release of releases) { + const tagName = release.tag_name; + let releaseVersion = null; + + // Extract version based on pattern + if (releasePattern === 'core' && /^v\d+\.\d+\.\d+$/.test(tagName)) { + releaseVersion = tagName.substring(1); + } else if (releasePattern === 'experimental' && tagName.startsWith('experimental/v')) { + releaseVersion = tagName.substring('experimental/v'.length); + } else if (releasePattern === 'api' && tagName.startsWith('api/v')) { + releaseVersion = tagName.substring('api/v'.length); + } else if (releasePattern === 'semconv' && tagName.startsWith('semconv/v')) { + releaseVersion = tagName.substring('semconv/v'.length); + } + + if (releaseVersion) { + // Check if this release is between current and new version + if (compareVersions(releaseVersion, currentVersion) > 0 && + compareVersions(releaseVersion, newVersion) <= 0) { + + // Check if release notes mention breaking changes + const body = release.body || ''; + if (body.includes('💥 Breaking Changes')) { + breakingReleases.push({ + version: releaseVersion, + name: release.name || tagName, + url: release.html_url + }); + } + } + } + } + + return breakingReleases; + + } catch (error) { + console.warn(`Warning: Could not get releases for ${repoName}: ${error.message}`); + return []; + } +} + +async function findContribBreakingChanges(currentContribPackages, newContribVersions) { + try { + const releases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100'); + if (!releases) return []; + + const breakingReleases = []; + + for (const release of releases) { + const tagName = release.tag_name; + + // Extract component name and version from releases like "auto-instrumentations-node: v0.64.4" + const match = tagName.match(/^([^:]+):\s*v(.+)$/); + if (match) { + const componentName = match[1]; + const releaseVersion = match[2]; + + // Check if this is a package we depend on + if (currentContribPackages[componentName]) { + const currentVersion = currentContribPackages[componentName]; + const newVersion = newContribVersions[componentName]; + + if (newVersion && + compareVersions(releaseVersion, currentVersion) > 0 && + compareVersions(releaseVersion, newVersion) <= 0) { + + // Check if release notes mention breaking changes + const body = release.body || ''; + if (body.includes('⚠ BREAKING CHANGES')) { + breakingReleases.push({ + component: componentName, + version: releaseVersion, + name: release.name || tagName, + url: release.html_url + }); + } + } + } + } + } + + return breakingReleases; + + } catch (error) { + console.warn(`Warning: Could not get contrib releases: ${error.message}`); + return []; + } +} +async function main() { + const newCoreVersion = process.env.OTEL_CORE_VERSION; + const newExperimentalVersion = process.env.OTEL_EXPERIMENTAL_VERSION; + const newApiVersion = process.env.OTEL_API_VERSION; + const newSemconvVersion = process.env.OTEL_SEMCONV_VERSION; + + if (!newCoreVersion && !newExperimentalVersion && !newApiVersion && !newSemconvVersion) { + console.error('Error: At least one version environment variable required'); + process.exit(1); + } + + const currentVersions = getCurrentVersionsFromPackageJson(); + + console.log('Checking for breaking changes in JS releases...'); + + let breakingInfo = ''; + + // Check core releases + if (newCoreVersion && currentVersions.core) { + const coreBreaking = await findBreakingChangesInReleases( + 'opentelemetry-js', + currentVersions.core, + newCoreVersion, + 'core' + ); + + if (coreBreaking.length > 0) { + breakingInfo += '**opentelemetry-js (core):**\n'; + for (const release of coreBreaking) { + breakingInfo += `- [${release.name}](${release.url})\n`; + } + } + } + + // Check experimental releases + if (newExperimentalVersion && currentVersions.experimental) { + const experimentalBreaking = await findBreakingChangesInReleases( + 'opentelemetry-js', + currentVersions.experimental, + newExperimentalVersion, + 'experimental' + ); + + if (experimentalBreaking.length > 0) { + breakingInfo += '**opentelemetry-js (experimental):**\n'; + for (const release of experimentalBreaking) { + breakingInfo += `- [${release.name}](${release.url})\n`; + } + } + } + + // Check contrib releases for packages we actually depend on + if (currentVersions.contrib) { + // We need to get the new contrib versions from the update script + // For now, we'll check all contrib packages we depend on + const contribBreaking = await findContribBreakingChanges(currentVersions.contrib, {}); + + if (contribBreaking.length > 0) { + breakingInfo += '**opentelemetry-js-contrib:**\n'; + for (const release of contribBreaking) { + breakingInfo += `- [${release.name}](${release.url})\n`; + } + } + } + + // Set GitHub output + if (process.env.GITHUB_OUTPUT) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `breaking_changes_info< Date: Fri, 26 Sep 2025 10:19:09 -0700 Subject: [PATCH 07/36] have nightly build call main build --- .github/workflows/main-build.yml | 9 +++++++++ .github/workflows/nightly-build.yml | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/.github/workflows/main-build.yml b/.github/workflows/main-build.yml index 51e43ac8..c1185ff0 100644 --- a/.github/workflows/main-build.yml +++ b/.github/workflows/main-build.yml @@ -6,6 +6,13 @@ on: - main - "release/v*" workflow_dispatch: + workflow_call: + inputs: + ref: + description: 'The branch, tag or SHA to checkout' + required: false + type: string + default: '' env: AWS_DEFAULT_REGION: us-east-1 @@ -34,6 +41,8 @@ jobs: steps: - name: Checkout Contrib Repo @ SHA - ${{ github.sha }} uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 + with: + ref: ${{ inputs.ref || github.sha }} - name: Get Node Distro Output id: node_output diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index df5b29b9..c2ad72c0 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -75,3 +75,14 @@ jobs: fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + build-and-test: + needs: update-and-create-pr + if: needs.update-and-create-pr.outputs.has_changes == 'true' + uses: ./.github/workflows/main-build.yml + secrets: inherit + permissions: + id-token: write + contents: read + with: + ref: nightly-dependency-updates From 05402c09a66948288c0f2077749fa0cb8711ea06 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 10:27:06 -0700 Subject: [PATCH 08/36] fix branching and PR logic --- .github/workflows/nightly-build.yml | 47 ++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index c2ad72c0..0d940db1 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -8,6 +8,11 @@ on: env: BRANCH_NAME: nightly-dependency-updates +permissions: + contents: write + pull-requests: write + id-token: write + jobs: update-and-create-pr: runs-on: ubuntu-latest @@ -21,14 +26,6 @@ jobs: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - - name: Check if nightly branch already exists - run: | - if git ls-remote --exit-code --heads origin "$BRANCH_NAME"; then - echo "Branch $BRANCH_NAME already exists. Skipping run to avoid conflicts." - echo "Please merge or close the existing PR before the next nightly run." - exit 1 - fi - - name: Set up Node.js uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 #v5.0.0 with: @@ -42,7 +39,16 @@ jobs: run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - git checkout -b "$BRANCH_NAME" + + - name: Check out dependency update branch + run: | + if git ls-remote --exit-code --heads origin "$BRANCH_NAME"; then + echo "Branch $BRANCH_NAME already exists, checking out..." + git checkout "$BRANCH_NAME" + else + echo "Branch $BRANCH_NAME does not exist, creating new branch..." + git checkout -b "$BRANCH_NAME" + fi - name: Install dependencies run: npm install @@ -50,7 +56,7 @@ jobs: - name: Update dependencies run: node scripts/update_dependencies.js - - name: Check for changes and create PR + - name: Check for changes and commit id: check_changes run: | if git diff --quiet; then @@ -63,13 +69,24 @@ jobs: git add aws-distro-opentelemetry-node-autoinstrumentation/package.json git commit -m "chore: update OpenTelemetry dependencies to latest versions" git push origin "$BRANCH_NAME" - + fi + + - name: Create or update PR + if: steps.check_changes.outputs.has_changes == 'true' + run: | + PR_BODY="Automated update of OpenTelemetry dependencies to their latest available versions. + + **Upstream releases with breaking changes:** + ${{ steps.breaking_changes.outputs.breaking_changes_info }}" + + if gh pr view "$BRANCH_NAME" --json state --jq '.state' 2>/dev/null | grep -q "OPEN"; then + echo "Open PR already exists, updating description..." + gh pr edit "$BRANCH_NAME" --body "$PR_BODY" + else + echo "Creating new PR..." gh pr create \ --title "Nightly dependency update: OpenTelemetry packages to latest versions" \ - --body "Automated update of OpenTelemetry dependencies to their latest available versions. - - **Upstream releases with breaking changes:** - ${{ steps.breaking_changes.outputs.breaking_changes_info }}" \ + --body "$PR_BODY" \ --base main \ --head "$BRANCH_NAME" fi From 1f711a21bbf0deb96e35ca9a32ebc1523d1c7c4c Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 10:27:36 -0700 Subject: [PATCH 09/36] add push trigger for testing --- .github/workflows/nightly-build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 0d940db1..947a58ff 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -4,6 +4,9 @@ on: schedule: - cron: "21 3 * * *" workflow_dispatch: + push: + branches: + - zhaez/nightly-build env: BRANCH_NAME: nightly-dependency-updates From 60a7b99069749a79b7072769a0e839897a397933 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 10:43:21 -0700 Subject: [PATCH 10/36] extract scripts for getting upstream versions --- scripts/find_breaking_changes.js | 59 +++++++++++----- scripts/get_upstream_versions.js | 117 +++++++++++++++++++++++++++++++ scripts/update_dependencies.js | 59 +--------------- 3 files changed, 162 insertions(+), 73 deletions(-) create mode 100644 scripts/get_upstream_versions.js diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 03e0fc12..bc40623a 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); +const { getLatestVersionsFromGitHub } = require('./get_upstream_versions.js'); async function httpsGet(url) { const https = require('https'); @@ -207,16 +208,10 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers return []; } } + async function main() { - const newCoreVersion = process.env.OTEL_CORE_VERSION; - const newExperimentalVersion = process.env.OTEL_EXPERIMENTAL_VERSION; - const newApiVersion = process.env.OTEL_API_VERSION; - const newSemconvVersion = process.env.OTEL_SEMCONV_VERSION; - - if (!newCoreVersion && !newExperimentalVersion && !newApiVersion && !newSemconvVersion) { - console.error('Error: At least one version environment variable required'); - process.exit(1); - } + console.log('Getting latest versions from GitHub...'); + const latestVersions = await getLatestVersionsFromGitHub(); const currentVersions = getCurrentVersionsFromPackageJson(); @@ -225,11 +220,11 @@ async function main() { let breakingInfo = ''; // Check core releases - if (newCoreVersion && currentVersions.core) { + if (latestVersions.core && currentVersions.core) { const coreBreaking = await findBreakingChangesInReleases( 'opentelemetry-js', currentVersions.core, - newCoreVersion, + latestVersions.core, 'core' ); @@ -242,11 +237,11 @@ async function main() { } // Check experimental releases - if (newExperimentalVersion && currentVersions.experimental) { + if (latestVersions.experimental && currentVersions.experimental) { const experimentalBreaking = await findBreakingChangesInReleases( 'opentelemetry-js', currentVersions.experimental, - newExperimentalVersion, + latestVersions.experimental, 'experimental' ); @@ -258,11 +253,43 @@ async function main() { } } + // Check API releases + if (latestVersions.api && currentVersions.api) { + const apiBreaking = await findBreakingChangesInReleases( + 'opentelemetry-js', + currentVersions.api, + latestVersions.api, + 'api' + ); + + if (apiBreaking.length > 0) { + breakingInfo += '**opentelemetry-js (api):**\n'; + for (const release of apiBreaking) { + breakingInfo += `- [${release.name}](${release.url})\n`; + } + } + } + + // Check semconv releases + if (latestVersions.semconv && currentVersions.semconv) { + const semconvBreaking = await findBreakingChangesInReleases( + 'opentelemetry-js', + currentVersions.semconv, + latestVersions.semconv, + 'semconv' + ); + + if (semconvBreaking.length > 0) { + breakingInfo += '**opentelemetry-js (semconv):**\n'; + for (const release of semconvBreaking) { + breakingInfo += `- [${release.name}](${release.url})\n`; + } + } + } + // Check contrib releases for packages we actually depend on if (currentVersions.contrib) { - // We need to get the new contrib versions from the update script - // For now, we'll check all contrib packages we depend on - const contribBreaking = await findContribBreakingChanges(currentVersions.contrib, {}); + const contribBreaking = await findContribBreakingChanges(currentVersions.contrib, latestVersions); if (contribBreaking.length > 0) { breakingInfo += '**opentelemetry-js-contrib:**\n'; diff --git a/scripts/get_upstream_versions.js b/scripts/get_upstream_versions.js new file mode 100644 index 00000000..009b91b6 --- /dev/null +++ b/scripts/get_upstream_versions.js @@ -0,0 +1,117 @@ +#!/usr/bin/env node + +const fs = require('fs'); + +async function httpsGet(url) { + const https = require('https'); + + return new Promise((resolve, reject) => { + const options = { + timeout: 30000, + headers: { + 'User-Agent': 'Mozilla/5.0 (compatible; Node.js script)' + } + }; + + const request = https.get(url, options, (response) => { + let data = ''; + response.on('data', (chunk) => data += chunk); + response.on('end', () => { + try { + if (response.statusCode === 200) { + resolve(JSON.parse(data)); + } else { + console.warn(`Warning: HTTP ${response.statusCode} for ${url}`); + resolve(null); + } + } catch (parseError) { + console.warn(`Warning: Could not parse response for ${url}: ${parseError.message}`); + resolve(null); + } + }); + }); + + request.on('error', (requestError) => { + console.warn(`Warning: Request failed for ${url}: ${requestError.message}`); + resolve(null); + }); + + request.on('timeout', () => { + request.destroy(); + console.warn(`Warning: Timeout for ${url}`); + resolve(null); + }); + }); +} + +async function getLatestVersionsFromGitHub() { + try { + // Get versions from opentelemetry-js releases + const jsReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js/releases?per_page=100'); + const contribReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100'); + + console.log('JS releases found:', jsReleases ? jsReleases.length : 'none'); + console.log('Contrib releases found:', contribReleases ? contribReleases.length : 'none'); + + const versions = {}; + + // Process opentelemetry-js releases + if (jsReleases) { + for (const release of jsReleases) { + const tagName = release.tag_name; + + // Core packages: v2.0.0 -> 2.0.0 (only keep first/newest) + if (/^v\d+\.\d+\.\d+$/.test(tagName) && !versions.core) { + versions.core = tagName.substring(1); + } + // Experimental packages: experimental/v0.57.1 -> 0.57.1 (only keep first/newest) + else if (tagName.startsWith('experimental/v') && !versions.experimental) { + versions.experimental = tagName.substring('experimental/v'.length); + } + // API package: api/v1.9.0 -> 1.9.0 (only keep first/newest) + else if (tagName.startsWith('api/v') && !versions.api) { + versions.api = tagName.substring('api/v'.length); + } + // Semantic conventions: semconv/v1.28.0 -> 1.28.0 (only keep first/newest) + else if (tagName.startsWith('semconv/v') && !versions.semconv) { + versions.semconv = tagName.substring('semconv/v'.length); + } + } + } + + // Process opentelemetry-js-contrib releases + if (contribReleases) { + for (const release of contribReleases) { + const tagName = release.tag_name; + + // Extract component name and version from releases like "auto-instrumentations-node: v0.64.4" + const match = tagName.match(/^([^:]+):\s*v(.+)$/); + if (match) { + const componentName = match[1]; + const version = match[2]; + if (!versions[componentName]) { + versions[componentName] = version; + } + } + } + } + + console.log('Found GitHub release versions:', versions); + + return versions; + + } catch (error) { + console.warn(`Warning: Could not get GitHub releases: ${error.message}`); + return {}; + } +} + +async function main() { + await getLatestVersionsFromGitHub(); +} + +if (require.main === module) { + main().catch(console.error); +} + +module.exports = { getLatestVersionsFromGitHub }; diff --git a/scripts/update_dependencies.js b/scripts/update_dependencies.js index 735fe1aa..2d2c4124 100644 --- a/scripts/update_dependencies.js +++ b/scripts/update_dependencies.js @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); +const { getLatestVersionsFromGitHub } = require('./get_upstream_versions.js'); async function httpsGet(url) { const https = require('https'); @@ -45,62 +46,6 @@ async function httpsGet(url) { }); } -async function getVersionsFromGitHubReleases() { - try { - // Get versions from opentelemetry-js releases - const jsReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js/releases'); - const contribReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases'); - - const versions = {}; - - // Process opentelemetry-js releases - if (jsReleases) { - for (const release of jsReleases) { - const tagName = release.tag_name; - - // Core packages: v2.0.0 -> 2.0.0 - if (/^v\d+\.\d+\.\d+$/.test(tagName) && !versions.core) { - versions.core = tagName.substring(1); - } - // Experimental packages: experimental/v0.57.1 -> 0.57.1 - else if (tagName.startsWith('experimental/v') && !versions.experimental) { - versions.experimental = tagName.substring('experimental/v'.length); - } - // API package: api/v1.9.0 -> 1.9.0 - else if (tagName.startsWith('api/v') && !versions.api) { - versions.api = tagName.substring('api/v'.length); - } - // Semantic conventions: semconv/v1.28.0 -> 1.28.0 - else if (tagName.startsWith('semconv/v') && !versions.semconv) { - versions.semconv = tagName.substring('semconv/v'.length); - } - } - } - - // Process opentelemetry-js-contrib releases - if (contribReleases) { - for (const release of contribReleases) { - const tagName = release.tag_name; - - // Extract component name and version from releases like "auto-instrumentations-node: v0.64.4" - const match = tagName.match(/^([^:]+):\s*v(.+)$/); - if (match) { - const componentName = match[1]; - const version = match[2]; - versions[componentName] = version; - } - } - } - - console.log('Found GitHub release versions:', versions); - return versions; - - } catch (error) { - console.warn(`Warning: Could not get GitHub releases: ${error.message}`); - return {}; - } -} - async function getLatestVersionFromNpm(packageName) { try { const data = await httpsGet(`https://registry.npmjs.org/${packageName}/latest`); @@ -156,7 +101,7 @@ async function main() { let updated = false; // Get versions from GitHub releases - const githubVersions = await getVersionsFromGitHubReleases(); + const githubVersions = await getLatestVersionsFromGitHub(); // Get all @opentelemetry packages from dependencies const dependencies = packageJson.dependencies || {}; From 9cb1714cdec673b85f6835c5ef424b7db127431b Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 10:50:58 -0700 Subject: [PATCH 11/36] match more patterns for breaking changes --- scripts/find_breaking_changes.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index bc40623a..811645ed 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -140,9 +140,11 @@ async function findBreakingChangesInReleases(repoName, currentVersion, newVersio if (compareVersions(releaseVersion, currentVersion) > 0 && compareVersions(releaseVersion, newVersion) <= 0) { - // Check if release notes mention breaking changes + // Check if release notes mention breaking changes (multiple patterns) const body = release.body || ''; - if (body.includes('💥 Breaking Changes')) { + if (body.includes('💥 Breaking Changes') || + body.includes('Breaking changes') || + body.includes('BREAKING CHANGES')) { breakingReleases.push({ version: releaseVersion, name: release.name || tagName, @@ -186,9 +188,11 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers compareVersions(releaseVersion, currentVersion) > 0 && compareVersions(releaseVersion, newVersion) <= 0) { - // Check if release notes mention breaking changes + // Check if release notes mention breaking changes (multiple patterns) const body = release.body || ''; - if (body.includes('⚠ BREAKING CHANGES')) { + if (body.includes('⚠ BREAKING CHANGES') || + body.includes('Breaking changes') || + body.includes('BREAKING CHANGES')) { breakingReleases.push({ component: componentName, version: releaseVersion, From ca0a2ee4b1a37e1eb45bb6e4d768be1e55c645b2 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 11:00:10 -0700 Subject: [PATCH 12/36] use regex matching for breaking changes header --- scripts/find_breaking_changes.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 811645ed..dcf8644e 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -140,11 +140,10 @@ async function findBreakingChangesInReleases(repoName, currentVersion, newVersio if (compareVersions(releaseVersion, currentVersion) > 0 && compareVersions(releaseVersion, newVersion) <= 0) { - // Check if release notes mention breaking changes (multiple patterns) + // Check if release notes have breaking changes as markdown headers const body = release.body || ''; - if (body.includes('💥 Breaking Changes') || - body.includes('Breaking changes') || - body.includes('BREAKING CHANGES')) { + const breakingHeaderRegex = /^#+.*breaking changes/im; + if (breakingHeaderRegex.test(body)) { breakingReleases.push({ version: releaseVersion, name: release.name || tagName, @@ -188,11 +187,10 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers compareVersions(releaseVersion, currentVersion) > 0 && compareVersions(releaseVersion, newVersion) <= 0) { - // Check if release notes mention breaking changes (multiple patterns) + // Check if release notes have breaking changes as markdown headers const body = release.body || ''; - if (body.includes('⚠ BREAKING CHANGES') || - body.includes('Breaking changes') || - body.includes('BREAKING CHANGES')) { + const breakingHeaderRegex = /^#+.*breaking changes/im; + if (breakingHeaderRegex.test(body)) { breakingReleases.push({ component: componentName, version: releaseVersion, From 9aa1b704935861070c8735d061c7bbde7fd2c863 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 12:04:20 -0700 Subject: [PATCH 13/36] fetch more pages from contrib repo --- scripts/find_breaking_changes.js | 16 +++++++++++++--- scripts/get_upstream_versions.js | 19 +++++++++++++++---- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index dcf8644e..36a35240 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -164,12 +164,22 @@ async function findBreakingChangesInReleases(repoName, currentVersion, newVersio async function findContribBreakingChanges(currentContribPackages, newContribVersions) { try { - const releases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100'); - if (!releases) return []; + // Fetch multiple pages of contrib releases since they release frequently + const releases1 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=1'); + const releases2 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=2'); + const releases3 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=3'); + + const allReleases = [ + ...(releases1 || []), + ...(releases2 || []), + ...(releases3 || []) + ]; + + if (allReleases.length === 0) return []; const breakingReleases = []; - for (const release of releases) { + for (const release of allReleases) { const tagName = release.tag_name; // Extract component name and version from releases like "auto-instrumentations-node: v0.64.4" diff --git a/scripts/get_upstream_versions.js b/scripts/get_upstream_versions.js index 009b91b6..7e585a9f 100644 --- a/scripts/get_upstream_versions.js +++ b/scripts/get_upstream_versions.js @@ -48,10 +48,21 @@ async function getLatestVersionsFromGitHub() { try { // Get versions from opentelemetry-js releases const jsReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js/releases?per_page=100'); - const contribReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100'); + const contribReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=1'); + + // Get additional contrib releases (they release more frequently) + const contribReleases2 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=2'); + const contribReleases3 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=3'); + + // Combine contrib releases + const allContribReleases = [ + ...(contribReleases || []), + ...(contribReleases2 || []), + ...(contribReleases3 || []) + ]; console.log('JS releases found:', jsReleases ? jsReleases.length : 'none'); - console.log('Contrib releases found:', contribReleases ? contribReleases.length : 'none'); + console.log('Contrib releases found:', allContribReleases.length); const versions = {}; @@ -80,8 +91,8 @@ async function getLatestVersionsFromGitHub() { } // Process opentelemetry-js-contrib releases - if (contribReleases) { - for (const release of contribReleases) { + if (allContribReleases.length > 0) { + for (const release of allContribReleases) { const tagName = release.tag_name; // Extract component name and version from releases like "auto-instrumentations-node: v0.64.4" From a09a92dc06566fc5ed07e835e8973c403724cc43 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 12:08:52 -0700 Subject: [PATCH 14/36] log contrib releases found --- scripts/find_breaking_changes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 36a35240..453b68b8 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -187,6 +187,7 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers if (match) { const componentName = match[1]; const releaseVersion = match[2]; + console.log(`Found contrib release: ${componentName} version ${releaseVersion}`); // Check if this is a package we depend on if (currentContribPackages[componentName]) { From de00bef3e1572e5f9eb144c435ac4c652af46097 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 12:11:01 -0700 Subject: [PATCH 15/36] add log --- scripts/find_breaking_changes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 453b68b8..351c4780 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -180,6 +180,7 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers const breakingReleases = []; for (const release of allReleases) { + console.log(`Processing release: ${release.tag_name}`); const tagName = release.tag_name; // Extract component name and version from releases like "auto-instrumentations-node: v0.64.4" From 2c39cd7df4b48d18c250fdbb90c768511a7a9b17 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 12:13:57 -0700 Subject: [PATCH 16/36] fix tag parsing for contrib packages --- scripts/find_breaking_changes.js | 4 ++-- scripts/get_upstream_versions.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 351c4780..6f7b90c7 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -183,8 +183,8 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers console.log(`Processing release: ${release.tag_name}`); const tagName = release.tag_name; - // Extract component name and version from releases like "auto-instrumentations-node: v0.64.4" - const match = tagName.match(/^([^:]+):\s*v(.+)$/); + // Extract component name and version from releases like "resource-detector-aws-v2.3.0" + const match = tagName.match(/^(.+)-v(.+)$/); if (match) { const componentName = match[1]; const releaseVersion = match[2]; diff --git a/scripts/get_upstream_versions.js b/scripts/get_upstream_versions.js index 7e585a9f..ec7f8180 100644 --- a/scripts/get_upstream_versions.js +++ b/scripts/get_upstream_versions.js @@ -95,8 +95,8 @@ async function getLatestVersionsFromGitHub() { for (const release of allContribReleases) { const tagName = release.tag_name; - // Extract component name and version from releases like "auto-instrumentations-node: v0.64.4" - const match = tagName.match(/^([^:]+):\s*v(.+)$/); + // Extract component name and version from releases like "resource-detector-aws-v2.3.0" + const match = tagName.match(/^(.+)-v(.+)$/); if (match) { const componentName = match[1]; const version = match[2]; From 53d199cd3efb616cbdc38814e896e8fb7c07d3b5 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 12:27:24 -0700 Subject: [PATCH 17/36] fix PR breaking change formatting --- scripts/find_breaking_changes.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 6f7b90c7..3f07f1c6 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -180,7 +180,6 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers const breakingReleases = []; for (const release of allReleases) { - console.log(`Processing release: ${release.tag_name}`); const tagName = release.tag_name; // Extract component name and version from releases like "resource-detector-aws-v2.3.0" @@ -188,7 +187,6 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers if (match) { const componentName = match[1]; const releaseVersion = match[2]; - console.log(`Found contrib release: ${componentName} version ${releaseVersion}`); // Check if this is a package we depend on if (currentContribPackages[componentName]) { @@ -243,7 +241,7 @@ async function main() { ); if (coreBreaking.length > 0) { - breakingInfo += '**opentelemetry-js (core):**\n'; + breakingInfo += '\n**opentelemetry-js (core):**\n'; for (const release of coreBreaking) { breakingInfo += `- [${release.name}](${release.url})\n`; } @@ -260,7 +258,7 @@ async function main() { ); if (experimentalBreaking.length > 0) { - breakingInfo += '**opentelemetry-js (experimental):**\n'; + breakingInfo += '\n**opentelemetry-js (experimental):**\n'; for (const release of experimentalBreaking) { breakingInfo += `- [${release.name}](${release.url})\n`; } @@ -277,7 +275,7 @@ async function main() { ); if (apiBreaking.length > 0) { - breakingInfo += '**opentelemetry-js (api):**\n'; + breakingInfo += '\n**opentelemetry-js (api):**\n'; for (const release of apiBreaking) { breakingInfo += `- [${release.name}](${release.url})\n`; } @@ -294,7 +292,7 @@ async function main() { ); if (semconvBreaking.length > 0) { - breakingInfo += '**opentelemetry-js (semconv):**\n'; + breakingInfo += '\n**opentelemetry-js (semconv):**\n'; for (const release of semconvBreaking) { breakingInfo += `- [${release.name}](${release.url})\n`; } @@ -306,7 +304,7 @@ async function main() { const contribBreaking = await findContribBreakingChanges(currentVersions.contrib, latestVersions); if (contribBreaking.length > 0) { - breakingInfo += '**opentelemetry-js-contrib:**\n'; + breakingInfo += '\n**opentelemetry-js-contrib:**\n'; for (const release of contribBreaking) { breakingInfo += `- [${release.name}](${release.url})\n`; } From 895b495787dbb8a4ed82f8299fe34a83d79faff8 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 12:35:01 -0700 Subject: [PATCH 18/36] publish metric for nightly build outcome --- .github/workflows/main-build.yml | 2 +- .github/workflows/nightly-build.yml | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main-build.yml b/.github/workflows/main-build.yml index c1185ff0..b39addd2 100644 --- a/.github/workflows/main-build.yml +++ b/.github/workflows/main-build.yml @@ -118,7 +118,7 @@ jobs: name: "Publish Main Build Status" needs: [ build, application-signals-e2e-test ] runs-on: ubuntu-latest - if: always() + if: always() && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) steps: - name: Configure AWS Credentials for emitting metrics uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 #v5.0.0 diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 947a58ff..a8532695 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -9,6 +9,7 @@ on: - zhaez/nightly-build env: + AWS_DEFAULT_REGION: us-east-1 BRANCH_NAME: nightly-dependency-updates permissions: @@ -106,3 +107,27 @@ jobs: contents: read with: ref: nightly-dependency-updates + + publish-nightly-build-status: + name: "Publish Nightly Build Status" + needs: [update-and-create-pr, build-and-test] + runs-on: ubuntu-latest + if: always() + steps: + - name: Configure AWS Credentials for emitting metrics + uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 #v5.0.0 + with: + role-to-assume: ${{ secrets.MONITORING_ROLE_ARN }} + aws-region: ${{ env.AWS_DEFAULT_REGION}} + + - name: Publish nightly build status + run: | + if [[ "${{ needs.build-and-test.result }}" == "skipped" ]]; then + echo "Build was skipped (no changes), not publishing metric" + else + value="${{ needs.build-and-test.result == 'success' && '0.0' || '1.0'}}" + aws cloudwatch put-metric-data --namespace 'ADOT/GitHubActions' \ + --metric-name Failure \ + --dimensions repository=${{ github.repository }},branch=${{ github.ref_name }},workflow=nightly_build \ + --value $value + fi From b6df6cb7edac4b14c74ab22ae87d1b1f9d6dd6cc Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 12:42:35 -0700 Subject: [PATCH 19/36] remove untracked script --- scripts/generate-licenses.js | 85 ------------------------------------ 1 file changed, 85 deletions(-) delete mode 100755 scripts/generate-licenses.js diff --git a/scripts/generate-licenses.js b/scripts/generate-licenses.js deleted file mode 100755 index 26063dae..00000000 --- a/scripts/generate-licenses.js +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env node - -const checker = require('license-checker-rseidelsohn'); -const fs = require('fs'); -const path = require('path'); - -const workspaceDir = path.join(__dirname, '..', 'aws-distro-opentelemetry-node-autoinstrumentation'); - -checker.init({ - start: workspaceDir, - production: true, - excludePrivatePackages: true, - // Include packages from both local and hoisted node_modules - includePackages: '', - customFormat: { - name: '', - version: '', - repository: '', - licenseText: '' - } -}, (err, packages) => { - if (err) { - console.error('Error:', err); - process.exit(1); - } - - let output = ''; - const processedPackages = []; - - Object.keys(packages).forEach(packageKey => { - const pkg = packages[packageKey]; - - // Parse package name and version correctly for scoped packages - let name, version; - if (packageKey.startsWith('@')) { - const lastAtIndex = packageKey.lastIndexOf('@'); - name = packageKey.substring(0, lastAtIndex); - version = packageKey.substring(lastAtIndex + 1); - } else { - const atIndex = packageKey.indexOf('@'); - name = packageKey.substring(0, atIndex); - version = packageKey.substring(atIndex + 1); - } - - // Skip our own package - if (name === '@aws/aws-distro-opentelemetry-node-autoinstrumentation') { - return; - } - - processedPackages.push({ name, version, pkg }); - }); - - // Sort by package name for consistent output - processedPackages.sort((a, b) => a.name.localeCompare(b.name)); - - processedPackages.forEach(({ name, version, pkg }) => { - output += `** ${name}; version ${version}`; - if (pkg.repository) { - output += ` -- ${pkg.repository}`; - } - output += '\n'; - - if (pkg.licenseText) { - output += pkg.licenseText + '\n\n'; - } else { - output += 'License text not available\n\n'; - } - }); - - const outputPath = path.join(__dirname, '..', 'THIRD-PARTY-LICENSES'); - fs.writeFileSync(outputPath, output); - console.log(`Generated THIRD-PARTY-LICENSES with ${processedPackages.length} packages`); - - // Also show what direct dependencies might be missing - const workspacePackageJson = JSON.parse( - fs.readFileSync(path.join(workspaceDir, 'package.json')) - ); - const directDeps = Object.keys(workspacePackageJson.dependencies || {}); - const foundPackages = new Set(processedPackages.map(p => p.name)); - const missingDeps = directDeps.filter(dep => !foundPackages.has(dep)); - - if (missingDeps.length > 0) { - console.log(`\nDirect dependencies without license files found: ${missingDeps.join(', ')}`); - } -}); From b3ee1b120e23f9a150021a973d1654e7993e3bf0 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 26 Sep 2025 12:53:06 -0700 Subject: [PATCH 20/36] update script comments --- scripts/find_breaking_changes.js | 1 - scripts/get_upstream_versions.js | 10 ++++------ scripts/update_dependencies.js | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 3f07f1c6..6cd39f56 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -81,7 +81,6 @@ function getCurrentVersionsFromPackageJson() { for (const [packageName, version] of Object.entries(dependencies)) { if (packageName.startsWith('@opentelemetry/') && !['@opentelemetry/api', '@opentelemetry/sdk-trace-base', '@opentelemetry/sdk-node', '@opentelemetry/semantic-conventions'].includes(packageName)) { - // Check if it's likely a contrib package (not in core/experimental categories) const componentName = packageName.replace('@opentelemetry/', ''); contribPackages[componentName] = version; } diff --git a/scripts/get_upstream_versions.js b/scripts/get_upstream_versions.js index ec7f8180..8435255b 100644 --- a/scripts/get_upstream_versions.js +++ b/scripts/get_upstream_versions.js @@ -49,8 +49,6 @@ async function getLatestVersionsFromGitHub() { // Get versions from opentelemetry-js releases const jsReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js/releases?per_page=100'); const contribReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=1'); - - // Get additional contrib releases (they release more frequently) const contribReleases2 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=2'); const contribReleases3 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=3'); @@ -71,19 +69,19 @@ async function getLatestVersionsFromGitHub() { for (const release of jsReleases) { const tagName = release.tag_name; - // Core packages: v2.0.0 -> 2.0.0 (only keep first/newest) + // Core packages: v2.0.0 -> 2.0.0 if (/^v\d+\.\d+\.\d+$/.test(tagName) && !versions.core) { versions.core = tagName.substring(1); } - // Experimental packages: experimental/v0.57.1 -> 0.57.1 (only keep first/newest) + // Experimental packages: experimental/v0.57.1 -> 0.57.1 else if (tagName.startsWith('experimental/v') && !versions.experimental) { versions.experimental = tagName.substring('experimental/v'.length); } - // API package: api/v1.9.0 -> 1.9.0 (only keep first/newest) + // API package: api/v1.9.0 -> 1.9.0 else if (tagName.startsWith('api/v') && !versions.api) { versions.api = tagName.substring('api/v'.length); } - // Semantic conventions: semconv/v1.28.0 -> 1.28.0 (only keep first/newest) + // Semantic conventions: semconv/v1.28.0 -> 1.28.0 else if (tagName.startsWith('semconv/v') && !versions.semconv) { versions.semconv = tagName.substring('semconv/v'.length); } diff --git a/scripts/update_dependencies.js b/scripts/update_dependencies.js index 2d2c4124..88b8ef7c 100644 --- a/scripts/update_dependencies.js +++ b/scripts/update_dependencies.js @@ -122,7 +122,7 @@ async function main() { } else if (PACKAGE_CATEGORIES.semconv.includes(packageName) && githubVersions.semconv) { newVersion = githubVersions.semconv; } else if (PACKAGE_CATEGORIES.contrib.includes(packageName)) { - // Try to get version from contrib releases by stripping @opentelemetry/ prefix + // Independently versioned; get package name by stripping @opentelemetry/ prefix const componentName = packageName.replace('@opentelemetry/', ''); if (githubVersions[componentName]) { newVersion = githubVersions[componentName]; From 25e392e67146ed4c589c61bb9dd0fcbb51d2c332 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Tue, 4 Nov 2025 17:45:36 -0800 Subject: [PATCH 21/36] refactor workflow order --- .github/workflows/nightly-build.yml | 60 +++++++++++++++++++---------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index a8532695..83c538ce 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -18,10 +18,11 @@ permissions: id-token: write jobs: - update-and-create-pr: + update-dependencies: runs-on: ubuntu-latest outputs: has_changes: ${{ steps.check_changes.outputs.has_changes }} + breaking_changes_info: ${{ steps.breaking_changes.outputs.breaking_changes_info }} steps: - name: Checkout repository @@ -74,14 +75,40 @@ jobs: git commit -m "chore: update OpenTelemetry dependencies to latest versions" git push origin "$BRANCH_NAME" fi + + build-and-test: + needs: update-dependencies + if: needs.update-dependencies.outputs.has_changes == 'true' + uses: ./.github/workflows/main-build.yml + secrets: inherit + permissions: + id-token: write + contents: read + with: + ref: nightly-dependency-updates + + create-pr: + needs: [update-dependencies, build-and-test] + if: always() && needs.update-dependencies.outputs.has_changes == 'true' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} - name: Create or update PR - if: steps.check_changes.outputs.has_changes == 'true' run: | + BUILD_STATUS="${{ needs.build-and-test.result }}" + BUILD_EMOJI="${{ needs.build-and-test.result == 'success' && '✅' || '❌' }}" + BUILD_LINK="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + PR_BODY="Automated update of OpenTelemetry dependencies to their latest available versions. + **Build Status:** ${BUILD_EMOJI} [${BUILD_STATUS}](${BUILD_LINK}) + **Upstream releases with breaking changes:** - ${{ steps.breaking_changes.outputs.breaking_changes_info }}" + ${{ needs.update-dependencies.outputs.breaking_changes_info }}" if gh pr view "$BRANCH_NAME" --json state --jq '.state' 2>/dev/null | grep -q "OPEN"; then echo "Open PR already exists, updating description..." @@ -97,20 +124,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - build-and-test: - needs: update-and-create-pr - if: needs.update-and-create-pr.outputs.has_changes == 'true' - uses: ./.github/workflows/main-build.yml - secrets: inherit - permissions: - id-token: write - contents: read - with: - ref: nightly-dependency-updates - publish-nightly-build-status: name: "Publish Nightly Build Status" - needs: [update-and-create-pr, build-and-test] + needs: [update-dependencies, build-and-test, create-pr] runs-on: ubuntu-latest if: always() steps: @@ -123,11 +139,13 @@ jobs: - name: Publish nightly build status run: | if [[ "${{ needs.build-and-test.result }}" == "skipped" ]]; then - echo "Build was skipped (no changes), not publishing metric" + echo "Build was skipped (no changes)" + value="0.0" else - value="${{ needs.build-and-test.result == 'success' && '0.0' || '1.0'}}" - aws cloudwatch put-metric-data --namespace 'ADOT/GitHubActions' \ - --metric-name Failure \ - --dimensions repository=${{ github.repository }},branch=${{ github.ref_name }},workflow=nightly_build \ - --value $value + value="${{ (needs.build-and-test.result == 'success' && needs.create-pr.result == 'success') && '0.0' || '1.0'}}" fi + + aws cloudwatch put-metric-data --namespace 'ADOT/GitHubActions' \ + --metric-name Failure \ + --dimensions repository=${{ github.repository }},branch=${{ github.ref_name }},workflow=nightly_build \ + --value $value From ed6a13ddac2d0799ef319cdbf7fce0a0b9ce34ee Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 7 Nov 2025 16:06:27 -0800 Subject: [PATCH 22/36] link to latest releases --- .github/workflows/nightly-build.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 83c538ce..788c6a7a 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -22,6 +22,10 @@ jobs: runs-on: ubuntu-latest outputs: has_changes: ${{ steps.check_changes.outputs.has_changes }} + otel_js_core_version: ${{ steps.get_versions.outputs.otel_js_core_version }} + otel_js_experimental_version: ${{ steps.get_versions.outputs.otel_js_experimental_version }} + otel_js_api_version: ${{ steps.get_versions.outputs.otel_js_api_version }} + otel_js_semconv_version: ${{ steps.get_versions.outputs.otel_js_semconv_version }} breaking_changes_info: ${{ steps.breaking_changes.outputs.breaking_changes_info }} steps: @@ -36,6 +40,10 @@ jobs: with: node-version: '18' + - name: Get latest upstream versions + id: get_versions + run: node scripts/get_upstream_versions.js + - name: Check for breaking changes id: breaking_changes run: node scripts/find_breaking_changes.js @@ -103,11 +111,19 @@ jobs: BUILD_EMOJI="${{ needs.build-and-test.result == 'success' && '✅' || '❌' }}" BUILD_LINK="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - PR_BODY="Automated update of OpenTelemetry dependencies to their latest available versions. + PR_BODY="Automated update of OpenTelemetry dependencies. **Build Status:** ${BUILD_EMOJI} [${BUILD_STATUS}](${BUILD_LINK}) + **Updated versions:** + - [OpenTelemetry JS Core](https://github.com/open-telemetry/opentelemetry-js/releases/tag/v${{ needs.update-dependencies.outputs.otel_js_core_version }}): ${{ needs.update-dependencies.outputs.otel_js_core_version }} + - [OpenTelemetry JS Experimental](https://github.com/open-telemetry/opentelemetry-js/releases/tag/experimental/v${{ needs.update-dependencies.outputs.otel_js_experimental_version }}): ${{ needs.update-dependencies.outputs.otel_js_experimental_version }} + - [OpenTelemetry JS API](https://github.com/open-telemetry/opentelemetry-js/releases/tag/api/v${{ needs.update-dependencies.outputs.otel_js_api_version }}): ${{ needs.update-dependencies.outputs.otel_js_api_version }} + - [OpenTelemetry JS Semantic Conventions](https://github.com/open-telemetry/opentelemetry-js/releases/tag/semconv/v${{ needs.update-dependencies.outputs.otel_js_semconv_version }}): ${{ needs.update-dependencies.outputs.otel_js_semconv_version }} + **Upstream releases with breaking changes:** + Note: the mechanism to detect upstream breaking changes is not perfect. Be sure to check all new releases and understand if any additional changes need to be addressed. + ${{ needs.update-dependencies.outputs.breaking_changes_info }}" if gh pr view "$BRANCH_NAME" --json state --jq '.state' 2>/dev/null | grep -q "OPEN"; then From 64ccea159548bd1e9421abe97324666b1bb5b11f Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 7 Nov 2025 16:14:31 -0800 Subject: [PATCH 23/36] use npm for contrib packages --- scripts/find_breaking_changes.js | 6 +-- scripts/get_upstream_versions.js | 83 ++++++++++++++++++++------------ scripts/update_dependencies.js | 13 +++-- 3 files changed, 60 insertions(+), 42 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 6cd39f56..14ac4602 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -2,7 +2,7 @@ const fs = require('fs'); const path = require('path'); -const { getLatestVersionsFromGitHub } = require('./get_upstream_versions.js'); +const { getLatestOtelVersions } = require('./get_upstream_versions.js'); async function httpsGet(url) { const https = require('https'); @@ -221,8 +221,8 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers } async function main() { - console.log('Getting latest versions from GitHub...'); - const latestVersions = await getLatestVersionsFromGitHub(); + console.log('Getting latest versions...'); + const latestVersions = await getLatestOtelVersions(); const currentVersions = getCurrentVersionsFromPackageJson(); diff --git a/scripts/get_upstream_versions.js b/scripts/get_upstream_versions.js index 8435255b..b6a54de5 100644 --- a/scripts/get_upstream_versions.js +++ b/scripts/get_upstream_versions.js @@ -44,23 +44,26 @@ async function httpsGet(url) { }); } -async function getLatestVersionsFromGitHub() { +async function getNpmPackageVersion(packageName) { + const { exec } = require('child_process'); + const { promisify } = require('util'); + const execAsync = promisify(exec); + + try { + const { stdout } = await execAsync(`npm view ${packageName} version`); + return stdout.trim(); + } catch (error) { + console.warn(`Warning: Could not get npm version for ${packageName}: ${error.message}`); + return null; + } +} + +async function getLatestOtelVersions() { try { // Get versions from opentelemetry-js releases const jsReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js/releases?per_page=100'); - const contribReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=1'); - const contribReleases2 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=2'); - const contribReleases3 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=3'); - // Combine contrib releases - const allContribReleases = [ - ...(contribReleases || []), - ...(contribReleases2 || []), - ...(contribReleases3 || []) - ]; - - console.log('JS releases found:', jsReleases ? jsReleases.length : 'none'); - console.log('Contrib releases found:', allContribReleases.length); + console.log('opentelemetry-js releases found:', jsReleases ? jsReleases.length : 'none'); const versions = {}; @@ -88,39 +91,55 @@ async function getLatestVersionsFromGitHub() { } } - // Process opentelemetry-js-contrib releases - if (allContribReleases.length > 0) { - for (const release of allContribReleases) { - const tagName = release.tag_name; - - // Extract component name and version from releases like "resource-detector-aws-v2.3.0" - const match = tagName.match(/^(.+)-v(.+)$/); - if (match) { - const componentName = match[1]; - const version = match[2]; - if (!versions[componentName]) { - versions[componentName] = version; - } - } + // Get contrib package versions from npm, since each one is independently versioned. + const contribPackages = [ + '@opentelemetry/auto-instrumentations-node', + '@opentelemetry/instrumentation-aws-lambda', + '@opentelemetry/instrumentation-aws-sdk', + '@opentelemetry/resource-detector-aws' + ]; + + console.log('Getting opentelemetry-js-contrib versions from npm...'); + for (const packageName of contribPackages) { + const version = await getNpmPackageVersion(packageName); + if (version) { + // Use the full package name as the key for consistency + versions[packageName] = version; + console.log(`Found ${packageName}: ${version}`); } } - console.log('Found GitHub release versions:', versions); - return versions; } catch (error) { - console.warn(`Warning: Could not get GitHub releases: ${error.message}`); + console.warn(`Warning: Could not get versions: ${error.message}`); return {}; } } async function main() { - await getLatestVersionsFromGitHub(); + const versions = await getLatestOtelVersions(); + + // Set GitHub outputs + if (process.env.GITHUB_OUTPUT) { + const fs = require('fs'); + if (versions.core) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `otel_js_core_version=${versions.core}\n`); + } + if (versions.experimental) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `otel_js_experimental_version=${versions.experimental}\n`); + } + if (versions.api) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `otel_js_api_version=${versions.api}\n`); + } + if (versions.semconv) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `otel_js_semconv_version=${versions.semconv}\n`); + } + } } if (require.main === module) { main().catch(console.error); } -module.exports = { getLatestVersionsFromGitHub }; +module.exports = { getLatestOtelVersions }; diff --git a/scripts/update_dependencies.js b/scripts/update_dependencies.js index 88b8ef7c..71e391a5 100644 --- a/scripts/update_dependencies.js +++ b/scripts/update_dependencies.js @@ -2,7 +2,7 @@ const fs = require('fs'); const path = require('path'); -const { getLatestVersionsFromGitHub } = require('./get_upstream_versions.js'); +const { getLatestOtelVersions } = require('./get_upstream_versions.js'); async function httpsGet(url) { const https = require('https'); @@ -100,8 +100,8 @@ async function main() { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); let updated = false; - // Get versions from GitHub releases - const githubVersions = await getLatestVersionsFromGitHub(); + // Get versions from GitHub and npm + const githubVersions = await getLatestOtelVersions(); // Get all @opentelemetry packages from dependencies const dependencies = packageJson.dependencies || {}; @@ -122,10 +122,9 @@ async function main() { } else if (PACKAGE_CATEGORIES.semconv.includes(packageName) && githubVersions.semconv) { newVersion = githubVersions.semconv; } else if (PACKAGE_CATEGORIES.contrib.includes(packageName)) { - // Independently versioned; get package name by stripping @opentelemetry/ prefix - const componentName = packageName.replace('@opentelemetry/', ''); - if (githubVersions[componentName]) { - newVersion = githubVersions[componentName]; + // Independently versioned; check if we have the version from npm + if (githubVersions[packageName]) { + newVersion = githubVersions[packageName]; } else { // Fall back to npm registry newVersion = await getLatestVersionFromNpm(packageName); From e285ca8f5a3ca50f2c87f634c3c91a05fee46bff Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 7 Nov 2025 16:19:11 -0800 Subject: [PATCH 24/36] remove contrib version fetching --- scripts/get_upstream_versions.js | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/scripts/get_upstream_versions.js b/scripts/get_upstream_versions.js index b6a54de5..3664e48d 100644 --- a/scripts/get_upstream_versions.js +++ b/scripts/get_upstream_versions.js @@ -44,20 +44,6 @@ async function httpsGet(url) { }); } -async function getNpmPackageVersion(packageName) { - const { exec } = require('child_process'); - const { promisify } = require('util'); - const execAsync = promisify(exec); - - try { - const { stdout } = await execAsync(`npm view ${packageName} version`); - return stdout.trim(); - } catch (error) { - console.warn(`Warning: Could not get npm version for ${packageName}: ${error.message}`); - return null; - } -} - async function getLatestOtelVersions() { try { // Get versions from opentelemetry-js releases @@ -91,24 +77,6 @@ async function getLatestOtelVersions() { } } - // Get contrib package versions from npm, since each one is independently versioned. - const contribPackages = [ - '@opentelemetry/auto-instrumentations-node', - '@opentelemetry/instrumentation-aws-lambda', - '@opentelemetry/instrumentation-aws-sdk', - '@opentelemetry/resource-detector-aws' - ]; - - console.log('Getting opentelemetry-js-contrib versions from npm...'); - for (const packageName of contribPackages) { - const version = await getNpmPackageVersion(packageName); - if (version) { - // Use the full package name as the key for consistency - versions[packageName] = version; - console.log(`Found ${packageName}: ${version}`); - } - } - return versions; } catch (error) { From d66f77976baca3b00c7ee0e9d6793c589e1dd969 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 7 Nov 2025 16:25:20 -0800 Subject: [PATCH 25/36] use npm for contrib breaking changes --- scripts/find_breaking_changes.js | 92 ++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 14ac4602..2043e2b2 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -161,52 +161,66 @@ async function findBreakingChangesInReleases(repoName, currentVersion, newVersio } } -async function findContribBreakingChanges(currentContribPackages, newContribVersions) { +async function getNpmVersionsBetween(packageName, currentVersion, newVersion) { + const { exec } = require('child_process'); + const { promisify } = require('util'); + const execAsync = promisify(exec); + try { - // Fetch multiple pages of contrib releases since they release frequently - const releases1 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=1'); - const releases2 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=2'); - const releases3 = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases?per_page=100&page=3'); + const { stdout } = await execAsync(`npm view ${packageName} versions --json`); + const allVersions = JSON.parse(stdout); - const allReleases = [ - ...(releases1 || []), - ...(releases2 || []), - ...(releases3 || []) - ]; - - if (allReleases.length === 0) return []; + // Filter versions between current and new + const relevantVersions = allVersions.filter(version => + compareVersions(version, currentVersion) > 0 && + compareVersions(version, newVersion) <= 0 + ); + return relevantVersions; + } catch (error) { + console.warn(`Warning: Could not get npm versions for ${packageName}: ${error.message}`); + return []; + } +} + +async function getSpecificGitHubRelease(componentName, version) { + try { + const tagName = `${componentName}-v${version}`; + const release = await httpsGet(`https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases/tags/${tagName}`); + return release; + } catch (error) { + console.warn(`Warning: Could not get GitHub release for ${componentName}-v${version}: ${error.message}`); + return null; + } +} + +async function findContribBreakingChanges(currentContribPackages, newContribVersions) { + try { const breakingReleases = []; - for (const release of allReleases) { - const tagName = release.tag_name; + for (const [componentName, currentVersion] of Object.entries(currentContribPackages)) { + const packageName = `@opentelemetry/${componentName}`; + const newVersion = newContribVersions[packageName]; - // Extract component name and version from releases like "resource-detector-aws-v2.3.0" - const match = tagName.match(/^(.+)-v(.+)$/); - if (match) { - const componentName = match[1]; - const releaseVersion = match[2]; + if (!newVersion) continue; + + // Get all versions between current and new from npm + const versionsToCheck = await getNpmVersionsBetween(packageName, currentVersion, newVersion); + + // Check each version's GitHub release for breaking changes + for (const version of versionsToCheck) { + const release = await getSpecificGitHubRelease(componentName, version); - // Check if this is a package we depend on - if (currentContribPackages[componentName]) { - const currentVersion = currentContribPackages[componentName]; - const newVersion = newContribVersions[componentName]; - - if (newVersion && - compareVersions(releaseVersion, currentVersion) > 0 && - compareVersions(releaseVersion, newVersion) <= 0) { - - // Check if release notes have breaking changes as markdown headers - const body = release.body || ''; - const breakingHeaderRegex = /^#+.*breaking changes/im; - if (breakingHeaderRegex.test(body)) { - breakingReleases.push({ - component: componentName, - version: releaseVersion, - name: release.name || tagName, - url: release.html_url - }); - } + if (release) { + const body = release.body || ''; + const breakingHeaderRegex = /^#+.*breaking changes/im; + if (breakingHeaderRegex.test(body)) { + breakingReleases.push({ + component: componentName, + version: version, + name: release.name || `${componentName}-v${version}`, + url: release.html_url + }); } } } From 3efdb14344958e824d04bb40b3ba7881b5d1ea83 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 7 Nov 2025 16:27:38 -0800 Subject: [PATCH 26/36] exit early if functions fail to execute --- scripts/find_breaking_changes.js | 14 ++++++++++++-- scripts/get_upstream_versions.js | 9 +++++++-- scripts/update_dependencies.js | 5 +++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 2043e2b2..3a0a16b2 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -91,8 +91,8 @@ function getCurrentVersionsFromPackageJson() { return currentVersions; } catch (error) { - console.warn(`Error reading current versions: ${error.message}`); - return {}; + console.error(`Error reading current versions: ${error.message}`); + process.exit(1); } } @@ -238,8 +238,18 @@ async function main() { console.log('Getting latest versions...'); const latestVersions = await getLatestOtelVersions(); + if (!latestVersions || Object.keys(latestVersions).length === 0) { + console.error('Failed to get latest versions'); + process.exit(1); + } + const currentVersions = getCurrentVersionsFromPackageJson(); + if (!currentVersions || Object.keys(currentVersions).length === 0) { + console.error('Failed to get current versions from package.json'); + process.exit(1); + } + console.log('Checking for breaking changes in JS releases...'); let breakingInfo = ''; diff --git a/scripts/get_upstream_versions.js b/scripts/get_upstream_versions.js index 3664e48d..50c1fd28 100644 --- a/scripts/get_upstream_versions.js +++ b/scripts/get_upstream_versions.js @@ -80,14 +80,19 @@ async function getLatestOtelVersions() { return versions; } catch (error) { - console.warn(`Warning: Could not get versions: ${error.message}`); - return {}; + console.error(`Error: Could not get versions: ${error.message}`); + process.exit(1); } } async function main() { const versions = await getLatestOtelVersions(); + if (!versions || Object.keys(versions).length === 0) { + console.error('Failed to get any OpenTelemetry versions'); + process.exit(1); + } + // Set GitHub outputs if (process.env.GITHUB_OUTPUT) { const fs = require('fs'); diff --git a/scripts/update_dependencies.js b/scripts/update_dependencies.js index 71e391a5..24b1bace 100644 --- a/scripts/update_dependencies.js +++ b/scripts/update_dependencies.js @@ -103,6 +103,11 @@ async function main() { // Get versions from GitHub and npm const githubVersions = await getLatestOtelVersions(); + if (!githubVersions || Object.keys(githubVersions).length === 0) { + console.error('Failed to get latest OpenTelemetry versions'); + process.exit(1); + } + // Get all @opentelemetry packages from dependencies const dependencies = packageJson.dependencies || {}; const otelPackages = Object.keys(dependencies).filter(pkg => pkg.startsWith('@opentelemetry/')); From 2ecf182331c6d6fac9cb9ea0b3f3148ae03b61c5 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 7 Nov 2025 16:35:19 -0800 Subject: [PATCH 27/36] add logging for contrib breaking changes --- scripts/find_breaking_changes.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 3a0a16b2..3ed839b8 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -196,36 +196,55 @@ async function getSpecificGitHubRelease(componentName, version) { async function findContribBreakingChanges(currentContribPackages, newContribVersions) { try { + console.log('Checking contrib packages for breaking changes...'); + console.log('Current contrib packages:', currentContribPackages); + console.log('New contrib versions available:', newContribVersions); + const breakingReleases = []; for (const [componentName, currentVersion] of Object.entries(currentContribPackages)) { const packageName = `@opentelemetry/${componentName}`; const newVersion = newContribVersions[packageName]; - if (!newVersion) continue; + console.log(`Checking ${packageName}: ${currentVersion} -> ${newVersion || 'not found'}`); + + if (!newVersion) { + console.log(`Skipping ${packageName} - no new version found`); + continue; + } // Get all versions between current and new from npm + console.log(`Getting npm versions between ${currentVersion} and ${newVersion} for ${packageName}`); const versionsToCheck = await getNpmVersionsBetween(packageName, currentVersion, newVersion); + console.log(`Found ${versionsToCheck.length} versions to check:`, versionsToCheck); // Check each version's GitHub release for breaking changes for (const version of versionsToCheck) { + console.log(`Checking GitHub release for ${componentName}-v${version}`); const release = await getSpecificGitHubRelease(componentName, version); if (release) { + console.log(`Found release for ${componentName}-v${version}`); const body = release.body || ''; const breakingHeaderRegex = /^#+.*breaking changes/im; if (breakingHeaderRegex.test(body)) { + console.log(`Breaking changes found in ${componentName}-v${version}`); breakingReleases.push({ component: componentName, version: version, name: release.name || `${componentName}-v${version}`, url: release.html_url }); + } else { + console.log(`No breaking changes found in ${componentName}-v${version}`); } + } else { + console.log(`No GitHub release found for ${componentName}-v${version}`); } } } + console.log(`Found ${breakingReleases.length} contrib releases with breaking changes`); return breakingReleases; } catch (error) { @@ -323,7 +342,11 @@ async function main() { } // Check contrib releases for packages we actually depend on + console.log('Current versions object:', currentVersions); + console.log('Latest versions object:', latestVersions); + if (currentVersions.contrib) { + console.log('Found contrib packages to check:', Object.keys(currentVersions.contrib)); const contribBreaking = await findContribBreakingChanges(currentVersions.contrib, latestVersions); if (contribBreaking.length > 0) { From 03ee8ddae2da74839499783d026433ae90d80a69 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 7 Nov 2025 16:41:46 -0800 Subject: [PATCH 28/36] specify contrib packages --- scripts/find_breaking_changes.js | 43 ++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 3ed839b8..6d22f635 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -76,13 +76,22 @@ function getCurrentVersionsFromPackageJson() { currentVersions.semconv = dependencies['@opentelemetry/semantic-conventions']; } - // Get all contrib packages we actually depend on + // Get only contrib packages (independently versioned packages from opentelemetry-js-contrib) const contribPackages = {}; - for (const [packageName, version] of Object.entries(dependencies)) { - if (packageName.startsWith('@opentelemetry/') && - !['@opentelemetry/api', '@opentelemetry/sdk-trace-base', '@opentelemetry/sdk-node', '@opentelemetry/semantic-conventions'].includes(packageName)) { + const contribPackageNames = [ + '@opentelemetry/auto-configuration-propagators', + '@opentelemetry/auto-instrumentations-node', + '@opentelemetry/baggage-span-processor', + '@opentelemetry/instrumentation-aws-sdk', + '@opentelemetry/id-generator-aws-xray', + '@opentelemetry/propagator-aws-xray', + '@opentelemetry/resource-detector-aws' + ]; + + for (const packageName of contribPackageNames) { + if (dependencies[packageName]) { const componentName = packageName.replace('@opentelemetry/', ''); - contribPackages[componentName] = version; + contribPackages[componentName] = dependencies[packageName]; } } @@ -195,21 +204,33 @@ async function getSpecificGitHubRelease(componentName, version) { } async function findContribBreakingChanges(currentContribPackages, newContribVersions) { + const { exec } = require('child_process'); + const { promisify } = require('util'); + const execAsync = promisify(exec); + try { console.log('Checking contrib packages for breaking changes...'); console.log('Current contrib packages:', currentContribPackages); - console.log('New contrib versions available:', newContribVersions); const breakingReleases = []; for (const [componentName, currentVersion] of Object.entries(currentContribPackages)) { const packageName = `@opentelemetry/${componentName}`; - const newVersion = newContribVersions[packageName]; - console.log(`Checking ${packageName}: ${currentVersion} -> ${newVersion || 'not found'}`); + // Get latest version from npm since contrib packages aren't in latestVersions + let newVersion; + try { + const { stdout } = await execAsync(`npm view ${packageName} version`); + newVersion = stdout.trim(); + } catch (error) { + console.log(`Could not get latest version for ${packageName}: ${error.message}`); + continue; + } + + console.log(`Checking ${packageName}: ${currentVersion} -> ${newVersion}`); - if (!newVersion) { - console.log(`Skipping ${packageName} - no new version found`); + if (currentVersion === newVersion) { + console.log(`${packageName} already at latest version`); continue; } @@ -347,7 +368,7 @@ async function main() { if (currentVersions.contrib) { console.log('Found contrib packages to check:', Object.keys(currentVersions.contrib)); - const contribBreaking = await findContribBreakingChanges(currentVersions.contrib, latestVersions); + const contribBreaking = await findContribBreakingChanges(currentVersions.contrib); if (contribBreaking.length > 0) { breakingInfo += '\n**opentelemetry-js-contrib:**\n'; From b3c7c58e2ae609e9a56b49f5a396c5a15e1145cc Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Fri, 7 Nov 2025 16:53:53 -0800 Subject: [PATCH 29/36] reduce number of releases fetched --- scripts/find_breaking_changes.js | 2 +- scripts/get_upstream_versions.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 6d22f635..81dc083a 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -123,7 +123,7 @@ function compareVersions(current, target) { async function findBreakingChangesInReleases(repoName, currentVersion, newVersion, releasePattern) { try { - const releases = await httpsGet(`https://api.github.com/repos/open-telemetry/${repoName}/releases?per_page=100`); + const releases = await httpsGet(`https://api.github.com/repos/open-telemetry/${repoName}/releases`); if (!releases) return []; const breakingReleases = []; diff --git a/scripts/get_upstream_versions.js b/scripts/get_upstream_versions.js index 50c1fd28..409d5ffa 100644 --- a/scripts/get_upstream_versions.js +++ b/scripts/get_upstream_versions.js @@ -47,7 +47,7 @@ async function httpsGet(url) { async function getLatestOtelVersions() { try { // Get versions from opentelemetry-js releases - const jsReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js/releases?per_page=100'); + const jsReleases = await httpsGet('https://api.github.com/repos/open-telemetry/opentelemetry-js/releases'); console.log('opentelemetry-js releases found:', jsReleases ? jsReleases.length : 'none'); From 74622aba5104bd479b16df50fcb17bccc3429bc7 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Mon, 10 Nov 2025 13:54:28 -0800 Subject: [PATCH 30/36] minor changes --- .github/workflows/main-build.yml | 1 - .github/workflows/nightly-build.yml | 19 ++++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main-build.yml b/.github/workflows/main-build.yml index b39addd2..8b32bc83 100644 --- a/.github/workflows/main-build.yml +++ b/.github/workflows/main-build.yml @@ -12,7 +12,6 @@ on: description: 'The branch, tag or SHA to checkout' required: false type: string - default: '' env: AWS_DEFAULT_REGION: us-east-1 diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 788c6a7a..4b3e32e3 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -1,4 +1,5 @@ name: Nightly Upstream Snapshot Build +description: This workflow checks for new upstream versions of OpenTelemetry dependencies, creates a PR to update them, and builds and tests the new changes. on: schedule: @@ -13,9 +14,9 @@ env: BRANCH_NAME: nightly-dependency-updates permissions: - contents: write - pull-requests: write - id-token: write + contents: write + pull-requests: write + id-token: write jobs: update-dependencies: @@ -50,8 +51,8 @@ jobs: - name: Configure git and create branch run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" + git config --local user.name "github-actions" + git config --local user.email "github-actions@github.com" - name: Check out dependency update branch run: | @@ -63,12 +64,12 @@ jobs: git checkout -b "$BRANCH_NAME" fi - - name: Install dependencies - run: npm install - - name: Update dependencies run: node scripts/update_dependencies.js + - name: Install dependencies + run: npm install + - name: Check for changes and commit id: check_changes run: | @@ -79,7 +80,7 @@ jobs: echo "Dependencies were updated" echo "has_changes=true" >> $GITHUB_OUTPUT - git add aws-distro-opentelemetry-node-autoinstrumentation/package.json + git add . git commit -m "chore: update OpenTelemetry dependencies to latest versions" git push origin "$BRANCH_NAME" fi From 29092d92ecc1da11283de19d18410bf127535e31 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Mon, 10 Nov 2025 13:56:22 -0800 Subject: [PATCH 31/36] remove redundant version fetching --- .github/workflows/nightly-build.yml | 5 +++++ scripts/update_dependencies.js | 15 +++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 4b3e32e3..e37cdc2d 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -65,6 +65,11 @@ jobs: fi - name: Update dependencies + env: + OTEL_JS_CORE_VERSION: ${{ steps.get_versions.outputs.otel_js_core_version }} + OTEL_JS_EXPERIMENTAL_VERSION: ${{ steps.get_versions.outputs.otel_js_experimental_version }} + OTEL_JS_API_VERSION: ${{ steps.get_versions.outputs.otel_js_api_version }} + OTEL_JS_SEMCONV_VERSION: ${{ steps.get_versions.outputs.otel_js_semconv_version }} run: node scripts/update_dependencies.js - name: Install dependencies diff --git a/scripts/update_dependencies.js b/scripts/update_dependencies.js index 24b1bace..9a195494 100644 --- a/scripts/update_dependencies.js +++ b/scripts/update_dependencies.js @@ -2,7 +2,6 @@ const fs = require('fs'); const path = require('path'); -const { getLatestOtelVersions } = require('./get_upstream_versions.js'); async function httpsGet(url) { const https = require('https'); @@ -100,13 +99,13 @@ async function main() { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); let updated = false; - // Get versions from GitHub and npm - const githubVersions = await getLatestOtelVersions(); - - if (!githubVersions || Object.keys(githubVersions).length === 0) { - console.error('Failed to get latest OpenTelemetry versions'); - process.exit(1); - } + // Get opentelemetry-js versions from environment variables + const githubVersions = { + core: process.env.OTEL_JS_CORE_VERSION, + experimental: process.env.OTEL_JS_EXPERIMENTAL_VERSION, + api: process.env.OTEL_JS_API_VERSION, + semconv: process.env.OTEL_JS_SEMCONV_VERSION + }; // Get all @opentelemetry packages from dependencies const dependencies = packageJson.dependencies || {}; From c93e51a70e10ff1c0fc4a1869486601435b30b8c Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Mon, 10 Nov 2025 13:58:32 -0800 Subject: [PATCH 32/36] remove api-events and sdk-events --- scripts/update_dependencies.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/update_dependencies.js b/scripts/update_dependencies.js index 9a195494..96f717b2 100644 --- a/scripts/update_dependencies.js +++ b/scripts/update_dependencies.js @@ -66,7 +66,6 @@ const PACKAGE_CATEGORIES = { '@opentelemetry/sdk-trace-base' ], experimental: [ - '@opentelemetry/api-events', '@opentelemetry/exporter-metrics-otlp-grpc', '@opentelemetry/exporter-metrics-otlp-http', '@opentelemetry/exporter-trace-otlp-proto', @@ -75,7 +74,6 @@ const PACKAGE_CATEGORIES = { '@opentelemetry/exporter-logs-otlp-proto', '@opentelemetry/instrumentation', '@opentelemetry/otlp-transformer', - '@opentelemetry/sdk-events', '@opentelemetry/sdk-logs', '@opentelemetry/sdk-node' ], From 082f7bcf312e1af61f2ca0e0fd2cdbfb651ded1e Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Mon, 10 Nov 2025 15:20:41 -0800 Subject: [PATCH 33/36] only print updated dependencies --- .github/workflows/nightly-build.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index e37cdc2d..75ef2122 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -117,16 +117,26 @@ jobs: BUILD_EMOJI="${{ needs.build-and-test.result == 'success' && '✅' || '❌' }}" BUILD_LINK="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + VERSION_LIST="" + if [[ -n "${{ needs.update-dependencies.outputs.otel_js_core_version }}" ]]; then + VERSION_LIST+="- [OpenTelemetry JS Core](https://github.com/open-telemetry/opentelemetry-js/releases/tag/v${{ needs.update-dependencies.outputs.otel_js_core_version }}): ${{ needs.update-dependencies.outputs.otel_js_core_version }}\n" + fi + if [[ -n "${{ needs.update-dependencies.outputs.otel_js_experimental_version }}" ]]; then + VERSION_LIST+="- [OpenTelemetry JS Experimental](https://github.com/open-telemetry/opentelemetry-js/releases/tag/experimental/v${{ needs.update-dependencies.outputs.otel_js_experimental_version }}): ${{ needs.update-dependencies.outputs.otel_js_experimental_version }}\n" + fi + if [[ -n "${{ needs.update-dependencies.outputs.otel_js_api_version }}" ]]; then + VERSION_LIST+="- [OpenTelemetry JS API](https://github.com/open-telemetry/opentelemetry-js/releases/tag/api/v${{ needs.update-dependencies.outputs.otel_js_api_version }}): ${{ needs.update-dependencies.outputs.otel_js_api_version }}\n" + fi + if [[ -n "${{ needs.update-dependencies.outputs.otel_js_semconv_version }}" ]]; then + VERSION_LIST+="- [OpenTelemetry JS Semantic Conventions](https://github.com/open-telemetry/opentelemetry-js/releases/tag/semconv/v${{ needs.update-dependencies.outputs.otel_js_semconv_version }}): ${{ needs.update-dependencies.outputs.otel_js_semconv_version }}\n" + fi + PR_BODY="Automated update of OpenTelemetry dependencies. **Build Status:** ${BUILD_EMOJI} [${BUILD_STATUS}](${BUILD_LINK}) **Updated versions:** - - [OpenTelemetry JS Core](https://github.com/open-telemetry/opentelemetry-js/releases/tag/v${{ needs.update-dependencies.outputs.otel_js_core_version }}): ${{ needs.update-dependencies.outputs.otel_js_core_version }} - - [OpenTelemetry JS Experimental](https://github.com/open-telemetry/opentelemetry-js/releases/tag/experimental/v${{ needs.update-dependencies.outputs.otel_js_experimental_version }}): ${{ needs.update-dependencies.outputs.otel_js_experimental_version }} - - [OpenTelemetry JS API](https://github.com/open-telemetry/opentelemetry-js/releases/tag/api/v${{ needs.update-dependencies.outputs.otel_js_api_version }}): ${{ needs.update-dependencies.outputs.otel_js_api_version }} - - [OpenTelemetry JS Semantic Conventions](https://github.com/open-telemetry/opentelemetry-js/releases/tag/semconv/v${{ needs.update-dependencies.outputs.otel_js_semconv_version }}): ${{ needs.update-dependencies.outputs.otel_js_semconv_version }} - + ${VERSION_LIST} **Upstream releases with breaking changes:** Note: the mechanism to detect upstream breaking changes is not perfect. Be sure to check all new releases and understand if any additional changes need to be addressed. From 46782c373cd28133166895b4f3898de6363f5f79 Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Mon, 10 Nov 2025 15:26:08 -0800 Subject: [PATCH 34/36] remove unnecessary logs --- scripts/find_breaking_changes.js | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index 81dc083a..c7038e8b 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -209,9 +209,6 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers const execAsync = promisify(exec); try { - console.log('Checking contrib packages for breaking changes...'); - console.log('Current contrib packages:', currentContribPackages); - const breakingReleases = []; for (const [componentName, currentVersion] of Object.entries(currentContribPackages)) { @@ -223,49 +220,35 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers const { stdout } = await execAsync(`npm view ${packageName} version`); newVersion = stdout.trim(); } catch (error) { - console.log(`Could not get latest version for ${packageName}: ${error.message}`); continue; } - console.log(`Checking ${packageName}: ${currentVersion} -> ${newVersion}`); - if (currentVersion === newVersion) { - console.log(`${packageName} already at latest version`); continue; } // Get all versions between current and new from npm - console.log(`Getting npm versions between ${currentVersion} and ${newVersion} for ${packageName}`); const versionsToCheck = await getNpmVersionsBetween(packageName, currentVersion, newVersion); - console.log(`Found ${versionsToCheck.length} versions to check:`, versionsToCheck); // Check each version's GitHub release for breaking changes for (const version of versionsToCheck) { - console.log(`Checking GitHub release for ${componentName}-v${version}`); const release = await getSpecificGitHubRelease(componentName, version); if (release) { - console.log(`Found release for ${componentName}-v${version}`); const body = release.body || ''; const breakingHeaderRegex = /^#+.*breaking changes/im; if (breakingHeaderRegex.test(body)) { - console.log(`Breaking changes found in ${componentName}-v${version}`); breakingReleases.push({ component: componentName, version: version, name: release.name || `${componentName}-v${version}`, url: release.html_url }); - } else { - console.log(`No breaking changes found in ${componentName}-v${version}`); } - } else { - console.log(`No GitHub release found for ${componentName}-v${version}`); } } } - - console.log(`Found ${breakingReleases.length} contrib releases with breaking changes`); + return breakingReleases; } catch (error) { @@ -362,12 +345,7 @@ async function main() { } } - // Check contrib releases for packages we actually depend on - console.log('Current versions object:', currentVersions); - console.log('Latest versions object:', latestVersions); - if (currentVersions.contrib) { - console.log('Found contrib packages to check:', Object.keys(currentVersions.contrib)); const contribBreaking = await findContribBreakingChanges(currentVersions.contrib); if (contribBreaking.length > 0) { @@ -382,12 +360,6 @@ async function main() { if (process.env.GITHUB_OUTPUT) { fs.appendFileSync(process.env.GITHUB_OUTPUT, `breaking_changes_info< Date: Mon, 10 Nov 2025 15:31:20 -0800 Subject: [PATCH 35/36] fail workflow at certain points when needed --- scripts/find_breaking_changes.js | 19 +++++++++---------- scripts/get_upstream_versions.js | 3 +++ scripts/update_dependencies.js | 6 +++--- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js index c7038e8b..400abb1f 100644 --- a/scripts/find_breaking_changes.js +++ b/scripts/find_breaking_changes.js @@ -124,7 +124,6 @@ function compareVersions(current, target) { async function findBreakingChangesInReleases(repoName, currentVersion, newVersion, releasePattern) { try { const releases = await httpsGet(`https://api.github.com/repos/open-telemetry/${repoName}/releases`); - if (!releases) return []; const breakingReleases = []; @@ -165,8 +164,8 @@ async function findBreakingChangesInReleases(repoName, currentVersion, newVersio return breakingReleases; } catch (error) { - console.warn(`Warning: Could not get releases for ${repoName}: ${error.message}`); - return []; + console.error(`Warning: Could not get releases for ${repoName}: ${error.message}`); + process.exit(1); } } @@ -187,8 +186,8 @@ async function getNpmVersionsBetween(packageName, currentVersion, newVersion) { return relevantVersions; } catch (error) { - console.warn(`Warning: Could not get npm versions for ${packageName}: ${error.message}`); - return []; + console.error(`Failed to get npm versions for ${packageName}: ${error.message}`); + process.exit(1); } } @@ -198,12 +197,12 @@ async function getSpecificGitHubRelease(componentName, version) { const release = await httpsGet(`https://api.github.com/repos/open-telemetry/opentelemetry-js-contrib/releases/tags/${tagName}`); return release; } catch (error) { - console.warn(`Warning: Could not get GitHub release for ${componentName}-v${version}: ${error.message}`); - return null; + console.error(`Error: Could not get GitHub release for ${componentName}-v${version}: ${error.message}`); + process.exit(1); } } -async function findContribBreakingChanges(currentContribPackages, newContribVersions) { +async function findContribBreakingChanges(currentContribPackages) { const { exec } = require('child_process'); const { promisify } = require('util'); const execAsync = promisify(exec); @@ -252,8 +251,8 @@ async function findContribBreakingChanges(currentContribPackages, newContribVers return breakingReleases; } catch (error) { - console.warn(`Warning: Could not get contrib releases: ${error.message}`); - return []; + console.error(`Warning: Could not get contrib releases: ${error.message}`); + process.exit(1); } } diff --git a/scripts/get_upstream_versions.js b/scripts/get_upstream_versions.js index 409d5ffa..390c79f6 100644 --- a/scripts/get_upstream_versions.js +++ b/scripts/get_upstream_versions.js @@ -75,6 +75,9 @@ async function getLatestOtelVersions() { versions.semconv = tagName.substring('semconv/v'.length); } } + } else { + console.error('Failed to fetch GitHub releases - this is required for core package version detection'); + process.exit(1); } return versions; diff --git a/scripts/update_dependencies.js b/scripts/update_dependencies.js index 96f717b2..1e0b5efa 100644 --- a/scripts/update_dependencies.js +++ b/scripts/update_dependencies.js @@ -48,10 +48,10 @@ async function httpsGet(url) { async function getLatestVersionFromNpm(packageName) { try { const data = await httpsGet(`https://registry.npmjs.org/${packageName}/latest`); - return data ? data.version : null; + return data.version; } catch (error) { - console.warn(`Warning: Could not get npm version for ${packageName}: ${error.message}`); - return null; + console.error(`Failed to get npm version for ${packageName}: ${error.message}`); + process.exit(1); } } From 8e9e373ca46fbb4c6172f9b78467049cd563c56f Mon Sep 17 00:00:00 2001 From: Eric Zhang Date: Wed, 26 Nov 2025 17:23:25 -0800 Subject: [PATCH 36/36] minor fixes --- .github/workflows/main-build.yml | 2 +- .github/workflows/nightly-build.yml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main-build.yml b/.github/workflows/main-build.yml index 8b32bc83..0af72291 100644 --- a/.github/workflows/main-build.yml +++ b/.github/workflows/main-build.yml @@ -117,7 +117,7 @@ jobs: name: "Publish Main Build Status" needs: [ build, application-signals-e2e-test ] runs-on: ubuntu-latest - if: always() && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) + if: always() && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/v')) steps: - name: Configure AWS Credentials for emitting metrics uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 #v5.0.0 diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 75ef2122..e40737d4 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -49,10 +49,10 @@ jobs: id: breaking_changes run: node scripts/find_breaking_changes.js - - name: Configure git and create branch + - name: Setup Git run: | - git config --local user.name "github-actions" - git config --local user.email "github-actions@github.com" + git config user.name "github-actions" + git config user.email "github-actions@github.com" - name: Check out dependency update branch run: | @@ -158,15 +158,15 @@ jobs: publish-nightly-build-status: name: "Publish Nightly Build Status" - needs: [update-dependencies, build-and-test, create-pr] + needs: [ update-dependencies, build-and-test, create-pr ] runs-on: ubuntu-latest if: always() steps: - name: Configure AWS Credentials for emitting metrics - uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 #v5.0.0 + uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 #5.0.0 with: role-to-assume: ${{ secrets.MONITORING_ROLE_ARN }} - aws-region: ${{ env.AWS_DEFAULT_REGION}} + aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Publish nightly build status run: |