diff --git a/.github/workflows/main-build.yml b/.github/workflows/main-build.yml index 51e43ac8..b39addd2 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 @@ -109,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 new file mode 100644 index 00000000..a8532695 --- /dev/null +++ b/.github/workflows/nightly-build.yml @@ -0,0 +1,133 @@ +name: Nightly Upstream Snapshot Build + +on: + schedule: + - cron: "21 3 * * *" + workflow_dispatch: + push: + branches: + - zhaez/nightly-build + +env: + AWS_DEFAULT_REGION: us-east-1 + BRANCH_NAME: nightly-dependency-updates + +permissions: + contents: write + pull-requests: write + id-token: write + +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: 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" + + - 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 + + - name: Update dependencies + run: node scripts/update_dependencies.js + + - name: Check for changes and commit + 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" + 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 "$PR_BODY" \ + --base main \ + --head "$BRANCH_NAME" + 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 + + 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 diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/package.json b/aws-distro-opentelemetry-node-autoinstrumentation/package.json index 72892008..a1ee135b 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/package.json +++ b/aws-distro-opentelemetry-node-autoinstrumentation/package.json @@ -41,7 +41,7 @@ ], "exclude": [ "src/third-party/**/*.ts", - "src/exporter/otlp/aws/common/aws-authenticator.ts" + "src/exporter/otlp/aws/common/aws-authenticator.ts" ] }, "bugs": { @@ -100,31 +100,31 @@ "dependencies": { "@aws-sdk/client-cloudwatch-logs": "3.621.0", "@opentelemetry/api": "1.9.0", - "@opentelemetry/auto-configuration-propagators": "0.3.2", - "@opentelemetry/auto-instrumentations-node": "0.56.0", - "@opentelemetry/api-events": "0.57.1", - "@opentelemetry/baggage-span-processor": "0.3.1", - "@opentelemetry/core": "1.30.1", - "@opentelemetry/exporter-metrics-otlp-grpc": "0.57.1", - "@opentelemetry/exporter-metrics-otlp-http": "0.57.1", - "@opentelemetry/exporter-trace-otlp-proto": "0.57.1", - "@opentelemetry/exporter-logs-otlp-grpc": "0.57.1", - "@opentelemetry/exporter-logs-otlp-http": "0.57.1", - "@opentelemetry/exporter-logs-otlp-proto": "0.57.1", - "@opentelemetry/exporter-zipkin": "1.30.1", - "@opentelemetry/id-generator-aws-xray": "1.2.3", - "@opentelemetry/instrumentation": "0.57.1", - "@opentelemetry/instrumentation-aws-sdk": "0.49.0", - "@opentelemetry/otlp-transformer": "0.57.1", - "@opentelemetry/propagator-aws-xray": "1.26.2", - "@opentelemetry/resource-detector-aws": "1.12.0", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/sdk-events": "0.57.1", - "@opentelemetry/sdk-logs": "0.57.1", - "@opentelemetry/sdk-metrics": "1.30.1", - "@opentelemetry/sdk-node": "0.57.1", - "@opentelemetry/sdk-trace-base": "1.30.1", - "@opentelemetry/semantic-conventions": "1.28.0" + "@opentelemetry/auto-configuration-propagators": "0.4.3", + "@opentelemetry/auto-instrumentations-node": "0.64.4", + "@opentelemetry/api-events": "0.205.0", + "@opentelemetry/baggage-span-processor": "0.4.1", + "@opentelemetry/core": "2.1.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.205.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.205.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.205.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.205.0", + "@opentelemetry/exporter-logs-otlp-http": "0.205.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.205.0", + "@opentelemetry/exporter-zipkin": "2.1.0", + "@opentelemetry/id-generator-aws-xray": "2.0.2", + "@opentelemetry/instrumentation": "0.205.0", + "@opentelemetry/instrumentation-aws-sdk": "0.61.1", + "@opentelemetry/otlp-transformer": "0.205.0", + "@opentelemetry/propagator-aws-xray": "2.1.2", + "@opentelemetry/resource-detector-aws": "2.5.2", + "@opentelemetry/resources": "2.1.0", + "@opentelemetry/sdk-events": "0.205.0", + "@opentelemetry/sdk-logs": "0.205.0", + "@opentelemetry/sdk-metrics": "2.1.0", + "@opentelemetry/sdk-node": "0.205.0", + "@opentelemetry/sdk-trace-base": "2.1.0", + "@opentelemetry/semantic-conventions": "1.37.0" }, "files": [ "build/src/**/*.js", diff --git a/scripts/find_breaking_changes.js b/scripts/find_breaking_changes.js new file mode 100644 index 00000000..3f07f1c6 --- /dev/null +++ b/scripts/find_breaking_changes.js @@ -0,0 +1,328 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); +const { getLatestVersionsFromGitHub } = require('./get_upstream_versions.js'); + +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 have breaking changes as markdown headers + const body = release.body || ''; + const breakingHeaderRegex = /^#+.*breaking changes/im; + if (breakingHeaderRegex.test(body)) { + 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 { + // 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 allReleases) { + 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 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 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 + }); + } + } + } + } + } + + return breakingReleases; + + } catch (error) { + console.warn(`Warning: Could not get contrib releases: ${error.message}`); + return []; + } +} + +async function main() { + console.log('Getting latest versions from GitHub...'); + const latestVersions = await getLatestVersionsFromGitHub(); + + const currentVersions = getCurrentVersionsFromPackageJson(); + + console.log('Checking for breaking changes in JS releases...'); + + let breakingInfo = ''; + + // Check core releases + if (latestVersions.core && currentVersions.core) { + const coreBreaking = await findBreakingChangesInReleases( + 'opentelemetry-js', + currentVersions.core, + latestVersions.core, + 'core' + ); + + if (coreBreaking.length > 0) { + breakingInfo += '\n**opentelemetry-js (core):**\n'; + for (const release of coreBreaking) { + breakingInfo += `- [${release.name}](${release.url})\n`; + } + } + } + + // Check experimental releases + if (latestVersions.experimental && currentVersions.experimental) { + const experimentalBreaking = await findBreakingChangesInReleases( + 'opentelemetry-js', + currentVersions.experimental, + latestVersions.experimental, + 'experimental' + ); + + if (experimentalBreaking.length > 0) { + breakingInfo += '\n**opentelemetry-js (experimental):**\n'; + for (const release of experimentalBreaking) { + breakingInfo += `- [${release.name}](${release.url})\n`; + } + } + } + + // Check API releases + if (latestVersions.api && currentVersions.api) { + const apiBreaking = await findBreakingChangesInReleases( + 'opentelemetry-js', + currentVersions.api, + latestVersions.api, + 'api' + ); + + if (apiBreaking.length > 0) { + breakingInfo += '\n**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 += '\n**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) { + const contribBreaking = await findContribBreakingChanges(currentVersions.contrib, latestVersions); + + if (contribBreaking.length > 0) { + breakingInfo += '\n**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< { + 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&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:', allContribReleases.length); + + 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 (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; + } + } + } + } + + 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 new file mode 100644 index 00000000..2d2c4124 --- /dev/null +++ b/scripts/update_dependencies.js @@ -0,0 +1,163 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); +const { getLatestVersionsFromGitHub } = require('./get_upstream_versions.js'); + +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 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'); + + try { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + let updated = false; + + // Get versions from GitHub releases + const githubVersions = await getLatestVersionsFromGitHub(); + + // Get all @opentelemetry packages from dependencies + const dependencies = packageJson.dependencies || {}; + const otelPackages = Object.keys(dependencies).filter(pkg => pkg.startsWith('@opentelemetry/')); + + // 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 { + // 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}`); + } + } + + 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); +}