-
Notifications
You must be signed in to change notification settings - Fork 26
Add nightly dependency bump workflow #479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e74800c
7252cb3
e56456b
c2d895d
6cc475d
e9c78dd
7349738
ca80bbb
cdbfea2
feb2005
d9d2dfe
e2a6c4c
b074e69
3fb046f
351813e
9d57171
9f0ebbb
60b627a
1544468
a6d58c1
e84deb6
51a9ee0
957024b
076e529
30d7d0a
f3b7a09
5182877
9efd150
5ebbe34
1b56b75
2f26b9c
0329b09
4db8d3e
84706be
d9d3a9a
1357fc8
74c6fb4
f5ba036
8b05265
dda3ac0
63b72f3
1c7bdf3
bb66be4
692abde
12cc0e5
e7fb5f7
15e4550
b1b4a87
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the workflow should be:
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
name: Nightly Upstream Snapshot Build | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets add a description |
||
|
||
on: | ||
schedule: | ||
- cron: "21 3 * * *" | ||
workflow_dispatch: | ||
|
||
env: | ||
AWS_DEFAULT_REGION: us-east-1 | ||
BRANCH_NAME: nightly-dependency-updates | ||
|
||
permissions: | ||
id-token: write | ||
contents: write | ||
pull-requests: write | ||
|
||
jobs: | ||
update-and-create-pr: | ||
runs-on: ubuntu-latest | ||
outputs: | ||
has_changes: ${{ steps.check_changes.outputs.has_changes }} | ||
otel_python_version: ${{ steps.get_versions.outputs.otel_python_version }} | ||
otel_contrib_version: ${{ steps.get_versions.outputs.otel_contrib_version }} | ||
|
||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #5.0.0 | ||
with: | ||
fetch-depth: 0 | ||
token: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Set up Python | ||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c #v6.0.0 | ||
with: | ||
python-version: '3.x' | ||
|
||
- name: Install build tools | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install toml requests packaging | ||
|
||
- name: Get latest upstream versions | ||
id: get_versions | ||
run: python scripts/get_upstream_versions.py | ||
|
||
- name: Check for breaking changes | ||
id: breaking_changes | ||
env: | ||
OTEL_PYTHON_VERSION: ${{ steps.get_versions.outputs.otel_python_version }} | ||
OTEL_CONTRIB_VERSION: ${{ steps.get_versions.outputs.otel_contrib_version }} | ||
Comment on lines
+49
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting, does that work? I didn't realize a script could send outputs to the GH action like this. Just want to confirm we tested this. |
||
run: python scripts/find_breaking_changes.py | ||
|
||
- name: Setup Git | ||
run: | | ||
git config user.name "github-actions" | ||
git config user.email "[email protected]" | ||
|
||
- 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: Update dependencies | ||
env: | ||
OTEL_PYTHON_VERSION: ${{ steps.get_versions.outputs.otel_python_version }} | ||
OTEL_CONTRIB_VERSION: ${{ steps.get_versions.outputs.otel_contrib_version }} | ||
run: python 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 . | ||
git commit -m "chore: update OpenTelemetry dependencies to ${{ steps.get_versions.outputs.otel_python_version }}/${{ steps.get_versions.outputs.otel_contrib_version }}" | ||
git push origin "$BRANCH_NAME" | ||
fi | ||
|
||
- name: Create or update PR | ||
run: | | ||
PR_BODY="Automated update of OpenTelemetry dependencies. | ||
|
||
**Updated versions:** | ||
- OpenTelemetry Python: ${{ steps.get_versions.outputs.otel_python_version }} | ||
- OpenTelemetry Contrib: ${{ steps.get_versions.outputs.otel_contrib_version }} | ||
Comment on lines
+94
to
+95
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should get AWS_DEPS as well. Also we can link to the PyPI URL to make this easy to verify. E.g. https://pypi.org/project/opentelemetry-sdk-extension-aws/2.1.0/ |
||
|
||
**Upstream releases with breaking changes:** | ||
${{ steps.breaking_changes.outputs.breaking_changes_info }}" | ||
Comment on lines
+97
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Elaborate just a bit - callout this is not a perfect system and say what we are looking for. |
||
|
||
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 ${{ steps.get_versions.outputs.otel_python_version }}/${{ steps.get_versions.outputs.otel_contrib_version }}" \ | ||
--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 #5.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'}}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sanity check: Does this return a success IFF all jobs/steps in |
||
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 | ||
Comment on lines
+125
to
+147
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not blocking: We do this in a bunch of places now. Can we extract it into an action that we can commonly use? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
import os | ||
import re | ||
import sys | ||
|
||
import requests | ||
from packaging import version | ||
|
||
|
||
def get_current_version_from_pyproject(): | ||
"""Extract current OpenTelemetry versions from pyproject.toml.""" | ||
try: | ||
with open("aws-opentelemetry-distro/pyproject.toml", "r", encoding="utf-8") as file: | ||
content = file.read() | ||
|
||
# Find first opentelemetry-api version (core version) | ||
api_match = re.search(r'"opentelemetry-api == ([^"]*)"', content) | ||
current_core_version = api_match.group(1) if api_match else None | ||
|
||
# Find first opentelemetry-distro version (contrib version) | ||
distro_match = re.search(r'"opentelemetry-distro == ([^"]*)"', content) | ||
current_contrib_version = distro_match.group(1) if distro_match else None | ||
|
||
return current_core_version, current_contrib_version | ||
|
||
except (OSError, IOError) as error: | ||
print(f"Error reading current versions: {error}") | ||
return None, None | ||
|
||
|
||
def get_releases_with_breaking_changes(repo, current_version, new_version): | ||
"""Get releases between current and new version that mention breaking changes.""" | ||
try: | ||
response = requests.get(f"https://api.github.com/repos/open-telemetry/{repo}/releases", timeout=30) | ||
response.raise_for_status() | ||
|
||
releases = response.json() | ||
breaking_releases = [] | ||
|
||
for release in releases: | ||
release_version = release["tag_name"].lstrip("v") | ||
|
||
# Check if this release is between current and new version | ||
try: | ||
if version.parse(release_version) > version.parse(current_version) and version.parse( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sanity check: Does this handle patch versions well? Would like to see a bit of testing here, can just be a manual thing. |
||
release_version | ||
) <= version.parse(new_version): | ||
|
||
# Check if release notes mention breaking changes | ||
body = release.get("body", "").lower() | ||
if any( | ||
keyword in body for keyword in ["breaking change", "breaking changes", "breaking:", "breaking"] | ||
): | ||
Comment on lines
+54
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's this based on? Does this work for |
||
breaking_releases.append( | ||
{ | ||
"version": release_version, | ||
"name": release["name"], | ||
"url": release["html_url"], | ||
"body": release.get("body", ""), | ||
} | ||
) | ||
except (ValueError, KeyError): | ||
# Skip releases with invalid version formats or missing data | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets at least log it. |
||
continue | ||
|
||
return breaking_releases | ||
|
||
except requests.RequestException as request_error: | ||
print(f"Warning: Could not get releases for {repo}: {request_error}") | ||
return [] | ||
Comment on lines
+71
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we fail? |
||
|
||
|
||
def main(): | ||
new_core_version = os.environ.get("OTEL_PYTHON_VERSION") | ||
new_contrib_version = os.environ.get("OTEL_CONTRIB_VERSION") | ||
|
||
if not new_core_version or not new_contrib_version: | ||
print("Error: OTEL_PYTHON_VERSION and OTEL_CONTRIB_VERSION environment variables required") | ||
sys.exit(1) | ||
|
||
current_core_version, current_contrib_version = get_current_version_from_pyproject() | ||
|
||
if not current_core_version or not current_contrib_version: | ||
print("Could not determine current versions") | ||
sys.exit(1) | ||
|
||
print("Checking for breaking changes:") | ||
print(f"Core: {current_core_version} → {new_core_version}") | ||
print(f"Contrib: {current_contrib_version} → {new_contrib_version}") | ||
|
||
# Check both repos for breaking changes | ||
core_breaking = get_releases_with_breaking_changes("opentelemetry-python", current_core_version, new_core_version) | ||
contrib_breaking = get_releases_with_breaking_changes( | ||
"opentelemetry-python-contrib", current_contrib_version, new_contrib_version | ||
) | ||
thpierce marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Output for GitHub Actions | ||
breaking_info = "" | ||
|
||
if core_breaking: | ||
breaking_info += "**opentelemetry-python:**\n" | ||
for release in core_breaking: | ||
breaking_info += f"- [{release['name']}]({release['url']})\n" | ||
|
||
if contrib_breaking: | ||
breaking_info += "\n**opentelemetry-python-contrib:**\n" | ||
for release in contrib_breaking: | ||
breaking_info += f"- [{release['name']}]({release['url']})\n" | ||
|
||
# Set GitHub output | ||
if os.environ.get("GITHUB_OUTPUT"): | ||
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output_file: | ||
output_file.write(f"breaking_changes_info<<EOF\n{breaking_info}EOF\n") | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
import os | ||
import re | ||
import sys | ||
|
||
import requests | ||
|
||
|
||
def get_latest_otel_versions(): | ||
"""Get latest OpenTelemetry versions from GitHub releases.""" | ||
try: | ||
# Query GitHub API for latest release | ||
response = requests.get( | ||
"https://api.github.com/repos/open-telemetry/opentelemetry-python/releases/latest", timeout=30 | ||
) | ||
response.raise_for_status() | ||
|
||
release_data = response.json() | ||
release_title = release_data["name"] | ||
|
||
# Parse "Version 1.37.0/0.58b0" format | ||
match = re.search(r"Version\s+(\d+\.\d+\.\d+)/(\d+\.\d+b\d+)", release_title) | ||
Comment on lines
+24
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Callout that this format has changed in past (look at old releases). It's ok for this to be a bit fragile for now, but maybe a better way would be to use pip or something and just check current |
||
if not match: | ||
print(f"Could not parse release title: {release_title}") | ||
sys.exit(1) | ||
|
||
otel_python_version = match.group(1) | ||
otel_contrib_version = match.group(2) | ||
thpierce marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return otel_python_version, otel_contrib_version | ||
|
||
except requests.RequestException as request_error: | ||
print(f"Error getting OpenTelemetry versions: {request_error}") | ||
sys.exit(1) | ||
|
||
|
||
def main(): | ||
otel_python_version, otel_contrib_version = get_latest_otel_versions() | ||
|
||
print(f"OTEL_PYTHON_VERSION={otel_python_version}") | ||
print(f"OTEL_CONTRIB_VERSION={otel_contrib_version}") | ||
|
||
# Write to GitHub output if in CI | ||
if "GITHUB_OUTPUT" in os.environ: | ||
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output_file: | ||
output_file.write(f"otel_python_version={otel_python_version}\n") | ||
output_file.write(f"otel_contrib_version={otel_contrib_version}\n") | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why
github.sha
? Is there nogithub.ref
? Just a bit unclear why this is correct.