diff --git a/.github/workflows/main-build.yml b/.github/workflows/main-build.yml index 1b8715f4..3db6794d 100644 --- a/.github/workflows/main-build.yml +++ b/.github/workflows/main-build.yml @@ -9,6 +9,11 @@ on: required: false type: string default: Dev + ref: + description: 'The branch, tag or SHA to checkout' + required: false + type: string + default: '' workflow_dispatch: # be able to run the workflow on demand push: branches: @@ -31,6 +36,8 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 + with: + ref: ${{ inputs.ref || github.sha }} - name: Setup .NET uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d #v5.0.0 @@ -89,6 +96,7 @@ jobs: uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 with: fetch-depth: 0 + ref: ${{ inputs.ref || github.sha }} - name: Build in Docker container run: | @@ -108,6 +116,8 @@ jobs: environment: ${{ inputs.environment }} steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 + with: + ref: ${{ inputs.ref || github.sha }} - name: Setup .NET uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d #v5.0.0 @@ -134,6 +144,7 @@ jobs: uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 with: fetch-depth: 0 + ref: ${{ inputs.ref || github.sha }} - name: Build in Docker container run: | @@ -158,6 +169,8 @@ jobs: os: [windows-2022, ubuntu-latest] steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 + with: + ref: ${{ inputs.ref || github.sha }} - name: Build and scan Linux images if: runner.os == 'Linux' diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml new file mode 100644 index 00000000..0ec1cb9f --- /dev/null +++ b/.github/workflows/nightly-build.yml @@ -0,0 +1,132 @@ +name: Nightly Upstream Snapshot Build + +on: + schedule: + - cron: "21 3 * * *" + workflow_dispatch: + push: + branches: + - zhaez/nightly-build + +permissions: + contents: write + pull-requests: write + id-token: write + +env: + BRANCH_NAME: nightly-dependency-updates + AWS_DEFAULT_REGION: us-east-1 + +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: Configure git + 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: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c #v6.0.0 + with: + python-version: '3.11' + + - name: Install Python dependencies + run: pip install requests + + - name: Update dependencies + run: python3 scripts/update_dependencies.py + + - 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 src/AWS.Distro.OpenTelemetry.AutoInstrumentation/AWS.Distro.OpenTelemetry.AutoInstrumentation.csproj build/Build.cs + 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. + + **Check for breaking changes:** + - [opentelemetry-dotnet releases](https://github.com/open-telemetry/opentelemetry-dotnet/releases) + - [opentelemetry-dotnet-contrib releases](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/releases)" + + 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: write + with: + caller-workflow-name: nightly-build + environment: Dev + 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/build/Build.cs b/build/Build.cs index 98b046e8..571bdea7 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -12,7 +12,7 @@ internal partial class Build : NukeBuild { - private const string OpenTelemetryAutoInstrumentationDefaultVersion = "v1.11.0"; + private const string OpenTelemetryAutoInstrumentationDefaultVersion = "v1.12.0"; private static readonly AbsolutePath TestNuGetPackageApps = NukeBuild.RootDirectory / "test" / "test-applications" / "nuget-package"; [Solution("AWS.Distro.OpenTelemetry.AutoInstrumentation.sln")] diff --git a/scripts/update_dependencies.py b/scripts/update_dependencies.py new file mode 100644 index 00000000..8b04f05a --- /dev/null +++ b/scripts/update_dependencies.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python3 + +import requests +import re +import sys +import xml.etree.ElementTree as ET + +def get_latest_versions_from_github(): + """Get the latest versions from GitHub releases.""" + try: + print("Fetching releases from opentelemetry-dotnet...") + dotnet_response = requests.get( + 'https://api.github.com/repos/open-telemetry/opentelemetry-dotnet/releases?per_page=50', + timeout=30 + ) + dotnet_response.raise_for_status() + + print("Fetching releases from opentelemetry-dotnet-contrib...") + contrib_response = requests.get( + 'https://api.github.com/repos/open-telemetry/opentelemetry-dotnet-contrib/releases?per_page=100', + timeout=30 + ) + contrib_response.raise_for_status() + + dotnet_releases = dotnet_response.json() + contrib_releases = contrib_response.json() + + print(f"Found {len(dotnet_releases)} dotnet releases") + print(f"Found {len(contrib_releases)} contrib releases") + + versions = {} + + # Process opentelemetry-dotnet releases (core packages) + for release in dotnet_releases: + if release.get('prerelease', False): + continue # Skip pre-releases + + tag_name = release['tag_name'] + # Core releases are typically tagged as "v1.9.0" or "core-1.9.0" + version_match = re.match(r'^(?:v|core-)?(\d+\.\d+\.\d+)$', tag_name) + if version_match and 'core' not in versions: + versions['core'] = version_match.group(1) + print(f"Found core version: {versions['core']}") + break # Take the first (latest) stable release + + # Process opentelemetry-dotnet-contrib releases + # Map package names to their release tag prefixes + package_mappings = { + 'OpenTelemetry.Extensions.AWS': 'Extensions.AWS', + 'OpenTelemetry.Resources.AWS': 'Resources.AWS', + 'OpenTelemetry.Instrumentation.AspNetCore': 'Instrumentation.AspNetCore', + 'OpenTelemetry.Instrumentation.AspNet': 'Instrumentation.AspNet', + 'OpenTelemetry.Instrumentation.AWSLambda': 'Instrumentation.AWS', # Maps to AWS releases + 'OpenTelemetry.Instrumentation.Http': 'Instrumentation.Http', + 'OpenTelemetry.Sampler.AWS': 'Sampler.AWS', + 'OpenTelemetry.SemanticConventions': 'SemanticConventions' + } + + for release in contrib_releases: + if release.get('prerelease', False): + continue # Skip pre-releases + + tag_name = release['tag_name'] + # Parse contrib releases like "Instrumentation.AspNetCore-1.12.0" + match = re.match(r'^(.+)-(\d+\.\d+\.\d+)$', tag_name) + if match: + component_name = match.group(1) + version = match.group(2) + + # Find packages that map to this component + for package_name, expected_component in package_mappings.items(): + if component_name == expected_component and package_name not in versions: + versions[package_name] = version + print(f"Found {package_name}: {version}") + + return versions + + except requests.RequestException as request_error: + print(f"Warning: Could not get GitHub releases: {request_error}") + return {} + +def get_latest_dotnet_instrumentation_version(): + """Get the latest version of opentelemetry-dotnet-instrumentation from GitHub releases.""" + try: + response = requests.get( + 'https://api.github.com/repos/open-telemetry/opentelemetry-dotnet-instrumentation/releases/latest', + timeout=30 + ) + response.raise_for_status() + + release_data = response.json() + tag_name = release_data['tag_name'] + + return tag_name + + except requests.RequestException as request_error: + print(f"Warning: Could not get latest dotnet-instrumentation version: {request_error}") + return None + +def update_csproj_file(file_path, github_versions): + """Update OpenTelemetry package versions in a .csproj file.""" + try: + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + updated = False + + # Package categorization + core_packages = [ + 'OpenTelemetry', + 'OpenTelemetry.Api', + 'OpenTelemetry.Exporter.OpenTelemetryProtocol', + 'OpenTelemetry.Extensions.Propagators' + ] + + # Find and update PackageReference elements using regex + def update_package_version(match): + nonlocal updated + package_name = match.group(1) + current_version = match.group(2) + + # Only update OpenTelemetry packages + if not package_name.startswith('OpenTelemetry'): + return match.group(0) + + latest_version = None + + # Try to get version from GitHub releases only + if package_name in core_packages and 'core' in github_versions: + latest_version = github_versions['core'] + elif package_name in github_versions: + latest_version = github_versions[package_name] + else: + # Skip packages not found in GitHub (likely only have pre-releases) + print(f"Skipping {package_name} - no stable release found in GitHub") + return match.group(0) + + if latest_version and current_version != latest_version: + updated = True + print(f"Updated {package_name}: {current_version} → {latest_version}") + return f'' + elif latest_version: + print(f"{package_name} already at latest version: {latest_version}") + + return match.group(0) + + # Use regex to find and replace PackageReference elements + pattern = r'' + new_content = re.sub(pattern, update_package_version, content) + + if updated: + with open(file_path, 'w', encoding='utf-8') as f: + f.write(new_content) + print("Dependencies updated successfully") + return True + else: + print("No OpenTelemetry dependencies needed updating") + return False + + except (OSError, IOError) as file_error: + print(f"Error updating dependencies: {file_error}") + sys.exit(1) + +def update_build_cs_file(file_path): + """Update the openTelemetryAutoInstrumentationDefaultVersion in Build.cs.""" + try: + latest_version = get_latest_dotnet_instrumentation_version() + if not latest_version: + print("Could not get latest dotnet-instrumentation version") + return False + + with open(file_path, 'r', encoding='utf-8') as input_file: + content = input_file.read() + + pattern = r'private const string OpenTelemetryAutoInstrumentationDefaultVersion = "v[^"]*";' + replacement = f'private const string OpenTelemetryAutoInstrumentationDefaultVersion = "{latest_version}";' + + if re.search(pattern, content): + new_content = re.sub(pattern, replacement, content) + + if new_content != content: + with open(file_path, 'w', encoding='utf-8') as output_file: + output_file.write(new_content) + print(f"Updated OpenTelemetryAutoInstrumentationDefaultVersion to {latest_version}") + return True + else: + print(f"OpenTelemetryAutoInstrumentationDefaultVersion already at latest version: {latest_version}") + return False + else: + print("Could not find OpenTelemetryAutoInstrumentationDefaultVersion in Build.cs") + return False + + except (OSError, IOError) as file_error: + print(f"Error updating Build.cs: {file_error}") + sys.exit(1) + +def main(): + print("Getting latest versions from GitHub releases...") + github_versions = get_latest_versions_from_github() + + if not github_versions: + print("No versions found from GitHub releases, exiting") + return + + print("Found versions:", github_versions) + + csproj_path = 'src/AWS.Distro.OpenTelemetry.AutoInstrumentation/AWS.Distro.OpenTelemetry.AutoInstrumentation.csproj' + build_cs_path = 'build/Build.cs' + + csproj_updated = update_csproj_file(csproj_path, github_versions) + build_cs_updated = update_build_cs_file(build_cs_path) + + if not csproj_updated and not build_cs_updated: + print("No updates were made") + +if __name__ == '__main__': + main() diff --git a/src/AWS.Distro.OpenTelemetry.AutoInstrumentation/AWS.Distro.OpenTelemetry.AutoInstrumentation.csproj b/src/AWS.Distro.OpenTelemetry.AutoInstrumentation/AWS.Distro.OpenTelemetry.AutoInstrumentation.csproj index c11c31f4..56b059b7 100644 --- a/src/AWS.Distro.OpenTelemetry.AutoInstrumentation/AWS.Distro.OpenTelemetry.AutoInstrumentation.csproj +++ b/src/AWS.Distro.OpenTelemetry.AutoInstrumentation/AWS.Distro.OpenTelemetry.AutoInstrumentation.csproj @@ -21,17 +21,17 @@ - - - + + + - - + + - +