Skip to content

Commit 050878f

Browse files
authored
Add nightly dependency bump workflow (#479)
*Issue #, if available:* *Description of changes:* Add a workflow `nightly-build.yml` with following actions: - creates or checks out existing `nightly-dependency-updates` branch - checks for newest versions of dependencies in [opentelemetry-python](https://github.com/open-telemetry/opentelemetry-python/releases) and [opentelemetry-python-contrib](https://github.com/open-telemetry/opentelemetry-python-contrib/releases), bumps versions if applicable, and creates/updates PR - search for releases with breaking changes in release notes, and links in the PR - runs main build checks on the branch, publishes a failure metric based on the outcome. Example run: https://github.com/aws-observability/aws-otel-python-instrumentation/actions/runs/19088083503 PR created: #533 By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
1 parent 3db8be3 commit 050878f

File tree

5 files changed

+557
-1
lines changed

5 files changed

+557
-1
lines changed

.github/workflows/main-build.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ on:
77
- "release/v*"
88
- ci-workflow
99
workflow_dispatch: # be able to run the workflow on demand
10+
workflow_call:
11+
inputs:
12+
ref:
13+
description: 'Git ref to checkout'
14+
required: false
15+
type: string
1016
env:
1117
AWS_DEFAULT_REGION: us-east-1
1218
STAGING_ECR_REGISTRY: 637423224110.dkr.ecr.us-east-1.amazonaws.com
@@ -34,6 +40,8 @@ jobs:
3440
steps:
3541
- name: Checkout Repo @ SHA - ${{ github.sha }}
3642
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #5.0.0
43+
with:
44+
ref: ${{ inputs.ref || github.sha }}
3745

3846
- name: Get Python Distro Output
3947
id: python_output
@@ -114,7 +122,7 @@ jobs:
114122
name: "Publish Main Build Status"
115123
needs: [ build, application-signals-e2e-test ]
116124
runs-on: ubuntu-latest
117-
if: always()
125+
if: always() && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/v'))
118126
steps:
119127
- name: Configure AWS Credentials for emitting metrics
120128
uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 #5.0.0
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
name: Nightly Upstream Snapshot Build
2+
description: This workflow checks for new upstream versions of OpenTelemetry dependencies, creates a PR to update them, and builds and tests the new changes.
3+
4+
on:
5+
schedule:
6+
- cron: "21 3 * * *"
7+
workflow_dispatch:
8+
9+
env:
10+
AWS_DEFAULT_REGION: us-east-1
11+
BRANCH_NAME: nightly-dependency-updates
12+
13+
permissions:
14+
id-token: write
15+
contents: write
16+
pull-requests: write
17+
18+
jobs:
19+
update-dependencies:
20+
runs-on: ubuntu-latest
21+
outputs:
22+
has_changes: ${{ steps.check_changes.outputs.has_changes }}
23+
otel_python_version: ${{ steps.get_versions.outputs.otel_python_version }}
24+
otel_contrib_version: ${{ steps.get_versions.outputs.otel_contrib_version }}
25+
aws_sdk_ext_version: ${{ steps.get_versions.outputs.opentelemetry_sdk_extension_aws_version }}
26+
aws_xray_prop_version: ${{ steps.get_versions.outputs.opentelemetry_propagator_aws_xray_version }}
27+
breaking_changes_info: ${{ steps.breaking_changes.outputs.breaking_changes_info }}
28+
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #5.0.0
32+
with:
33+
fetch-depth: 0
34+
token: ${{ secrets.GITHUB_TOKEN }}
35+
36+
- name: Set up Python
37+
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c #v6.0.0
38+
with:
39+
python-version: '3.x'
40+
41+
- name: Install build tools
42+
run: |
43+
python -m pip install --upgrade pip
44+
pip install toml requests packaging
45+
46+
- name: Get latest upstream versions
47+
id: get_versions
48+
run: python scripts/get_upstream_versions.py
49+
50+
- name: Check for breaking changes
51+
id: breaking_changes
52+
env:
53+
OTEL_PYTHON_VERSION: ${{ steps.get_versions.outputs.otel_python_version }}
54+
OTEL_CONTRIB_VERSION: ${{ steps.get_versions.outputs.otel_contrib_version }}
55+
run: python scripts/find_breaking_changes.py
56+
57+
- name: Setup Git
58+
run: |
59+
git config user.name "github-actions"
60+
git config user.email "github-actions@github.com"
61+
62+
- name: Check out dependency update branch
63+
run: |
64+
if git ls-remote --exit-code --heads origin "$BRANCH_NAME"; then
65+
echo "Branch $BRANCH_NAME already exists, checking out..."
66+
git checkout "$BRANCH_NAME"
67+
else
68+
echo "Branch $BRANCH_NAME does not exist, creating new branch..."
69+
git checkout -b "$BRANCH_NAME"
70+
fi
71+
72+
- name: Update dependencies
73+
env:
74+
OTEL_PYTHON_VERSION: ${{ steps.get_versions.outputs.otel_python_version }}
75+
OTEL_CONTRIB_VERSION: ${{ steps.get_versions.outputs.otel_contrib_version }}
76+
OPENTELEMETRY_SDK_EXTENSION_AWS_VERSION: ${{ steps.get_versions.outputs.opentelemetry_sdk_extension_aws_version }}
77+
OPENTELEMETRY_PROPAGATOR_AWS_XRAY_VERSION: ${{ steps.get_versions.outputs.opentelemetry_propagator_aws_xray_version }}
78+
run: python scripts/update_dependencies.py
79+
80+
- name: Check for changes and commit
81+
id: check_changes
82+
run: |
83+
if git diff --quiet; then
84+
echo "No dependency updates needed"
85+
echo "has_changes=false" >> $GITHUB_OUTPUT
86+
else
87+
echo "Dependencies were updated"
88+
echo "has_changes=true" >> $GITHUB_OUTPUT
89+
90+
git add .
91+
git commit -m "chore: update OpenTelemetry dependencies to ${{ steps.get_versions.outputs.otel_python_version }}/${{ steps.get_versions.outputs.otel_contrib_version }}"
92+
git push origin "$BRANCH_NAME"
93+
fi
94+
95+
build-and-test:
96+
needs: update-dependencies
97+
if: needs.update-dependencies.outputs.has_changes == 'true'
98+
uses: ./.github/workflows/main-build.yml
99+
secrets: inherit
100+
permissions:
101+
id-token: write
102+
contents: read
103+
with:
104+
ref: nightly-dependency-updates
105+
106+
create-pr:
107+
needs: [update-dependencies, build-and-test]
108+
if: always() && needs.update-dependencies.outputs.has_changes == 'true'
109+
runs-on: ubuntu-latest
110+
steps:
111+
- name: Checkout repository
112+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #5.0.0
113+
with:
114+
token: ${{ secrets.GITHUB_TOKEN }}
115+
116+
- name: Create or update PR
117+
run: |
118+
BUILD_STATUS="${{ needs.build-and-test.result }}"
119+
BUILD_EMOJI="${{ needs.build-and-test.result == 'success' && '✅' || '❌' }}"
120+
BUILD_LINK="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
121+
122+
PR_BODY="Automated update of OpenTelemetry dependencies.
123+
124+
**Build Status:** ${BUILD_EMOJI} [${BUILD_STATUS}](${BUILD_LINK})
125+
126+
**Updated versions:**
127+
- [OpenTelemetry Python](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v${{ needs.update-dependencies.outputs.otel_python_version }}): ${{ needs.update-dependencies.outputs.otel_python_version }}
128+
- [OpenTelemetry Contrib](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v${{ needs.update-dependencies.outputs.otel_contrib_version }}): ${{ needs.update-dependencies.outputs.otel_contrib_version }}
129+
- [opentelemetry-sdk-extension-aws](https://pypi.org/project/opentelemetry-sdk-extension-aws/${{ needs.update-dependencies.outputs.aws_sdk_ext_version }}/): ${{ needs.update-dependencies.outputs.aws_sdk_ext_version }}
130+
- [opentelemetry-propagator-aws-xray](https://pypi.org/project/opentelemetry-propagator-aws-xray/${{ needs.update-dependencies.outputs.aws_xray_prop_version }}/): ${{ needs.update-dependencies.outputs.aws_xray_prop_version }}
131+
132+
**Upstream releases with breaking changes:**
133+
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.
134+
135+
${{ needs.update-dependencies.outputs.breaking_changes_info }}"
136+
137+
if gh pr view "$BRANCH_NAME" --json state --jq '.state' 2>/dev/null | grep -q "OPEN"; then
138+
echo "Open PR already exists, updating description..."
139+
gh pr edit "$BRANCH_NAME" --body "$PR_BODY"
140+
else
141+
echo "Creating new PR..."
142+
gh pr create \
143+
--title "Nightly dependency update: OpenTelemetry ${{ needs.update-dependencies.outputs.otel_python_version }}/${{ needs.update-dependencies.outputs.otel_contrib_version }}" \
144+
--body "$PR_BODY" \
145+
--base main \
146+
--head "$BRANCH_NAME"
147+
fi
148+
env:
149+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
150+
151+
publish-nightly-build-status:
152+
name: "Publish Nightly Build Status"
153+
needs: [ update-dependencies, build-and-test, create-pr ]
154+
runs-on: ubuntu-latest
155+
if: always()
156+
steps:
157+
- name: Configure AWS Credentials for emitting metrics
158+
uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 #5.0.0
159+
with:
160+
role-to-assume: ${{ secrets.MONITORING_ROLE_ARN }}
161+
aws-region: ${{ env.AWS_DEFAULT_REGION }}
162+
163+
- name: Publish nightly build status
164+
run: |
165+
if [[ "${{ needs.build-and-test.result }}" == "skipped" ]]; then
166+
echo "Build was skipped (no changes)"
167+
value="0.0"
168+
else
169+
value="${{ (needs.build-and-test.result == 'success' && needs.create-pr.result == 'success') && '0.0' || '1.0'}}"
170+
fi
171+
172+
aws cloudwatch put-metric-data --namespace 'ADOT/GitHubActions' \
173+
--metric-name Failure \
174+
--dimensions repository=${{ github.repository }},branch=${{ github.ref_name }},workflow=nightly_build \
175+
--value $value

scripts/find_breaking_changes.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/usr/bin/env python3
2+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import os
6+
import re
7+
import sys
8+
9+
import requests
10+
from packaging import version
11+
12+
13+
def get_current_version_from_pyproject():
14+
"""Extract current OpenTelemetry versions from pyproject.toml."""
15+
try:
16+
with open("aws-opentelemetry-distro/pyproject.toml", "r", encoding="utf-8") as file:
17+
content = file.read()
18+
19+
# Find first opentelemetry-api version (core version)
20+
api_match = re.search(r'"opentelemetry-api == ([^"]*)"', content)
21+
current_core_version = api_match.group(1) if api_match else None
22+
23+
# Find first opentelemetry-distro version (contrib version)
24+
distro_match = re.search(r'"opentelemetry-distro == ([^"]*)"', content)
25+
current_contrib_version = distro_match.group(1) if distro_match else None
26+
27+
return current_core_version, current_contrib_version
28+
29+
except (OSError, IOError) as error:
30+
print(f"Error reading current versions: {error}")
31+
return None, None
32+
33+
34+
def get_releases_with_breaking_changes(repo, current_version, new_version):
35+
"""Get releases between current and new version that mention breaking changes."""
36+
try:
37+
response = requests.get(f"https://api.github.com/repos/open-telemetry/{repo}/releases", timeout=30)
38+
response.raise_for_status()
39+
40+
releases = response.json()
41+
breaking_releases = []
42+
43+
for release in releases:
44+
release_version = release["tag_name"].lstrip("v")
45+
46+
# Check if this release is between current and new version
47+
try:
48+
if version.parse(release_version) > version.parse(current_version) and version.parse(
49+
release_version
50+
) <= version.parse(new_version):
51+
52+
# Check if release notes have breaking changes header or bold text
53+
body = release.get("body", "")
54+
if re.search(r"^(#+|\*\*)\s*breaking changes", body, re.MULTILINE | re.IGNORECASE):
55+
breaking_releases.append(
56+
{
57+
"version": release_version,
58+
"name": release["name"],
59+
"url": release["html_url"],
60+
"body": release.get("body", ""),
61+
}
62+
)
63+
except (ValueError, KeyError) as parse_error:
64+
print(f"Warning: Skipping release {release.get('name', 'unknown')} due to error: {parse_error}")
65+
continue
66+
67+
return breaking_releases
68+
69+
except requests.RequestException as request_error:
70+
print(f"Error: Could not get releases for {repo}: {request_error}")
71+
sys.exit(1)
72+
73+
74+
def main():
75+
new_core_version = os.environ.get("OTEL_PYTHON_VERSION")
76+
new_contrib_version = os.environ.get("OTEL_CONTRIB_VERSION")
77+
78+
if not new_core_version or not new_contrib_version:
79+
print("Error: OTEL_PYTHON_VERSION and OTEL_CONTRIB_VERSION environment variables required")
80+
sys.exit(1)
81+
82+
current_core_version, current_contrib_version = get_current_version_from_pyproject()
83+
84+
if not current_core_version or not current_contrib_version:
85+
print("Could not determine current versions")
86+
sys.exit(1)
87+
88+
print("Checking for breaking changes:")
89+
print(f"Core: {current_core_version}{new_core_version}")
90+
print(f"Contrib: {current_contrib_version}{new_contrib_version}")
91+
92+
# Check both repos for breaking changes
93+
core_breaking = get_releases_with_breaking_changes("opentelemetry-python", current_core_version, new_core_version)
94+
contrib_breaking = get_releases_with_breaking_changes(
95+
"opentelemetry-python-contrib", current_contrib_version, new_contrib_version
96+
)
97+
98+
# Output for GitHub Actions
99+
breaking_info = ""
100+
101+
if core_breaking:
102+
breaking_info += "**opentelemetry-python:**\n"
103+
for release in core_breaking:
104+
breaking_info += f"- [{release['name']}]({release['url']})\n"
105+
106+
if contrib_breaking:
107+
breaking_info += "\n**opentelemetry-python-contrib:**\n"
108+
for release in contrib_breaking:
109+
breaking_info += f"- [{release['name']}]({release['url']})\n"
110+
111+
# Set GitHub output
112+
if os.environ.get("GITHUB_OUTPUT"):
113+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output_file:
114+
output_file.write(f"breaking_changes_info<<EOF\n{breaking_info}EOF\n")
115+
116+
117+
if __name__ == "__main__":
118+
main()

0 commit comments

Comments
 (0)