-
Notifications
You must be signed in to change notification settings - Fork 15
On-Demand Nightly PyPI Publishing #216
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| #!/bin/bash | ||
| # Copyright (c) Meta Platforms, Inc. and affiliates. | ||
| # Check if there are new commits since the last nightly PyPI release. | ||
| # This script is used by nightly-pypi.yml to implement on-demand nightly publishing. | ||
| # | ||
| # Exit codes: | ||
| # 0 - Should publish (new commits found or check skipped due to errors) | ||
| # 1 - Should skip publish (no new commits) | ||
| # | ||
| # Output: | ||
| # Sets GITHUB_OUTPUT variable: should_publish=true/false | ||
|
|
||
| set -o pipefail | ||
| # Note: set -e is intentionally not used to allow explicit error handling. | ||
| # The script implements fail-open behavior where errors should not block publishing. | ||
|
Comment on lines
+13
to
+15
|
||
|
|
||
| # Configuration | ||
| MAX_RETRIES=3 | ||
| RETRY_DELAY=5 | ||
| CURL_TIMEOUT=10 | ||
| PACKAGE_NAME="${PACKAGE_NAME:-tritonparse}" | ||
|
|
||
| # Dependencies: This script requires 'jq' and 'curl' to be installed. | ||
| # These are pre-installed on ubuntu-latest GitHub Actions runners. | ||
|
|
||
|
Comment on lines
+23
to
+25
|
||
| # Function: Fetch latest nightly version from PyPI with retry | ||
| fetch_latest_nightly() { | ||
| local retries=0 | ||
|
|
||
| while [ $retries -lt $MAX_RETRIES ]; do | ||
| local response | ||
| response=$(curl -s --max-time $CURL_TIMEOUT \ | ||
| "https://pypi.org/pypi/${PACKAGE_NAME}/json" 2>/dev/null) | ||
| local curl_exit=$? | ||
|
|
||
| if [ $curl_exit -eq 0 ] && [ -n "$response" ]; then | ||
| # Try to parse and extract latest dev version | ||
| local latest | ||
| latest=$(echo "$response" | \ | ||
| jq -r '.releases | keys[] | select(contains(".dev"))' 2>/dev/null | \ | ||
| sort -V | tail -1) | ||
|
Comment on lines
+39
to
+41
|
||
| local jq_exit=$? | ||
|
|
||
| if [ $jq_exit -eq 0 ] && [ -n "$latest" ]; then | ||
| echo "$latest" | ||
| return 0 | ||
| fi | ||
| fi | ||
|
|
||
| retries=$((retries + 1)) | ||
| echo "::warning::PyPI API request failed (attempt $retries/$MAX_RETRIES)" | ||
|
|
||
| if [ $retries -lt $MAX_RETRIES ]; then | ||
| echo "Retrying in ${RETRY_DELAY}s..." | ||
| sleep $RETRY_DELAY | ||
| fi | ||
| done | ||
|
|
||
| echo "::warning::All $MAX_RETRIES attempts failed" | ||
| return 1 | ||
| } | ||
|
|
||
| main() { | ||
| echo "Checking for new commits since last nightly release..." | ||
|
|
||
| # Step 1: Fetch latest nightly version (with retry) | ||
| LATEST_NIGHTLY=$(fetch_latest_nightly) | ||
| FETCH_STATUS=$? | ||
|
|
||
| # Step 2: Network request failed -> skip check, proceed with publish | ||
| if [ $FETCH_STATUS -ne 0 ]; then | ||
| echo "::warning::Failed to fetch PyPI data after $MAX_RETRIES attempts" | ||
| echo "::warning::Skipping commit check, proceeding with publish" | ||
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | ||
|
||
| exit 0 | ||
| fi | ||
|
|
||
| # Step 3: No nightly version exists -> first nightly release | ||
| if [ -z "$LATEST_NIGHTLY" ]; then | ||
| echo "No existing nightly version found, proceeding with publish" | ||
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "Latest nightly on PyPI: $LATEST_NIGHTLY" | ||
|
|
||
| # Step 4: Extract timestamp from version (format: X.Y.Z.devYYYYMMDDHHMMSS) | ||
| TIMESTAMP=$(echo "$LATEST_NIGHTLY" | sed -n 's/.*\.dev\([0-9]\{14\}\).*/\1/p') | ||
|
|
||
| if [ -z "$TIMESTAMP" ]; then | ||
| echo "::warning::Cannot parse timestamp from version, proceeding with publish" | ||
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
|
|
||
| # Step 5: Convert to date format for git log | ||
| SINCE_DATE="${TIMESTAMP:0:4}-${TIMESTAMP:4:2}-${TIMESTAMP:6:2} ${TIMESTAMP:8:2}:${TIMESTAMP:10:2}:${TIMESTAMP:12:2} UTC" | ||
| echo "Last nightly published at: $SINCE_DATE" | ||
|
|
||
| # Step 6: Check for new commits since last nightly | ||
| COMMITS_SINCE=$(git log --since="$SINCE_DATE" --oneline | wc -l) | ||
|
||
|
|
||
| if [ "$COMMITS_SINCE" -eq 0 ]; then | ||
| echo "No new commits since last nightly, skipping publish" | ||
| echo "should_publish=false" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| else | ||
| echo "Found $COMMITS_SINCE new commit(s) since last nightly" | ||
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
| } | ||
|
|
||
| main | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,9 +23,15 @@ jobs: | |
| - uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: "3.11" | ||
|
|
||
| - name: Check for new commits since last nightly | ||
| id: check | ||
| if: github.ref_type != 'tag' | ||
| run: bash .github/scripts/check_new_commits.sh | ||
|
|
||
| - name: Compute nightly version from latest tag (next patch + timestamp) | ||
| id: ver | ||
| if: github.ref_type != 'tag' | ||
| if: github.ref_type != 'tag' && steps.check.outputs.should_publish != 'false' | ||
| run: | | ||
| # Get latest tag; allow 'v' prefix; fail if none | ||
| if ! TAG=$(git describe --tags --abbrev=0 2>/dev/null); then | ||
|
|
@@ -46,6 +52,7 @@ jobs: | |
| echo "Computed nightly version: ${NEXT}.dev${DATE}" | ||
|
|
||
| - name: Build sdist/wheel | ||
| if: github.ref_type == 'tag' || steps.check.outputs.should_publish != 'false' | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install build setuptools-scm | ||
|
|
@@ -55,12 +62,13 @@ jobs: | |
| python -m build | ||
|
|
||
| - name: Check metadata | ||
| if: github.ref_type == 'tag' || steps.check.outputs.should_publish != 'false' | ||
| run: | | ||
| pip install twine | ||
| twine check dist/* | ||
|
|
||
| - name: Publish to PyPI (API token) | ||
| if: github.event_name == 'schedule' || github.ref_type == 'tag' | ||
| if: (github.event_name == 'schedule' || github.ref_type == 'tag') && steps.check.outputs.should_publish != 'false' | ||
|
||
| uses: pypa/gh-action-pypi-publish@release/v1 | ||
| with: | ||
| user: __token__ | ||
|
|
||
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.
This script only uses
set -o pipefailwithoutset -e, which is inconsistent with other bash scripts in the repository (e.g., .ci/setup.sh, .ci/install-project.sh, .ci/run-tests.sh) that all useset -e.However, in this case, not using
set -eappears to be intentional since the script has explicit error handling and wants to control when to fail vs. succeed. Consider adding a comment explaining whyset -eis not used: