Check OptiScaler Upstream Release #155
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ───────────────────────────────────────────────────────────────────────────── | |
| # check-optiscaler-release.yml | |
| # | |
| # Purpose: | |
| # Monitor the upstream OptiScaler repository for new releases. | |
| # When a new release is detected this workflow: | |
| # 1. Updates the tracking file (.github/optiscaler-version.txt). | |
| # 2. Opens a GitHub Issue summarising the new release so a maintainer | |
| # can decide whether OptiScaler-GUI needs any changes. | |
| # | |
| # Triggers: | |
| # • Scheduled: daily at 06:00 UTC. | |
| # • Manual: workflow_dispatch (useful for testing or one-off checks). | |
| # | |
| # Secrets required: | |
| # None beyond the built-in GITHUB_TOKEN. | |
| # | |
| # No auto-merge, no auto-commit to application code — human review always | |
| # happens before any change ships to users. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| name: Check OptiScaler Upstream Release | |
| on: | |
| # Run once a day at 06:00 UTC. | |
| schedule: | |
| - cron: '0 6 * * *' | |
| # Allow maintainers to trigger this manually from the Actions tab. | |
| workflow_dispatch: | |
| # Minimal permissions: read the repo contents + create issues/PRs. | |
| permissions: | |
| contents: write # needed to push the updated tracking file | |
| issues: write # needed to open the notification issue | |
| jobs: | |
| check-release: | |
| name: Detect new upstream release | |
| runs-on: ubuntu-latest | |
| steps: | |
| # ── 1. Checkout ────────────────────────────────────────────────────── | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| with: | |
| # Fetch full history so we can commit cleanly. | |
| fetch-depth: 0 | |
| # ── 2. Fetch latest OptiScaler release from GitHub API ─────────────── | |
| - name: Fetch latest OptiScaler release metadata | |
| id: upstream | |
| env: | |
| # Using the built-in token avoids rate-limiting on the API. | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| # /releases/latest only returns stable (non-draft, non-prerelease) releases | |
| # and returns 404 if none exist. Fall back to /releases?per_page=1 which | |
| # includes pre-releases, so we always track the most recent published tag. | |
| STABLE_URL="https://api.github.com/repos/cdozdil/OmniScaler/releases/latest" | |
| ALL_URL="https://api.github.com/repos/cdozdil/OmniScaler/releases?per_page=1" | |
| COMMON_HEADERS=( | |
| -H "Accept: application/vnd.github+json" | |
| -H "Authorization: Bearer ${GH_TOKEN}" | |
| -H "X-GitHub-Api-Version: 2022-11-28" | |
| ) | |
| echo "Querying stable release: $STABLE_URL" | |
| HTTP_STATUS=$(curl -o /tmp/gh_release.json -w "%{http_code}" -sSL \ | |
| "${COMMON_HEADERS[@]}" "$STABLE_URL") | |
| if [[ "$HTTP_STATUS" == "200" ]]; then | |
| echo "Stable release found." | |
| RESPONSE=$(cat /tmp/gh_release.json) | |
| else | |
| echo "Stable release returned HTTP ${HTTP_STATUS} — falling back to latest pre-release." | |
| echo "Querying: $ALL_URL" | |
| ALL_RESPONSE=$(curl -fsSL "${COMMON_HEADERS[@]}" "$ALL_URL") | |
| # /releases returns an array; unwrap the first element. | |
| RESPONSE=$(echo "$ALL_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(json.dumps(d[0])) if d else (print('{}') or exit(1))") | |
| fi | |
| # Parse the fields we care about. | |
| LATEST_TAG=$(echo "$RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['tag_name'])") | |
| RELEASE_NAME=$(echo "$RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['name'])") | |
| RELEASE_URL=$(echo "$RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['html_url'])") | |
| PUBLISHED_AT=$(echo "$RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['published_at'])") | |
| BODY=$(echo "$RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('body','') or '')") | |
| # Build a markdown list of release assets (filenames only). | |
| ASSETS=$(echo "$RESPONSE" | python3 -c " | |
| import sys, json | |
| d = json.load(sys.stdin) | |
| assets = d.get('assets', []) | |
| if assets: | |
| lines = ['- ' + a['name'] + ' (' + '{:.1f}'.format(a['size']/1024/1024) + ' MB)' for a in assets] | |
| print('\n'.join(lines)) | |
| else: | |
| print('_(no binary assets attached)_') | |
| ") | |
| # Surface values to subsequent steps via GITHUB_OUTPUT. | |
| { | |
| echo "latest_tag=${LATEST_TAG}" | |
| echo "release_name=${RELEASE_NAME}" | |
| echo "release_url=${RELEASE_URL}" | |
| echo "published_at=${PUBLISHED_AT}" | |
| # Multi-line values must use the heredoc delimiter syntax. | |
| echo "assets<<ASSETS_EOF" | |
| echo "$ASSETS" | |
| echo "ASSETS_EOF" | |
| echo "body<<BODY_EOF" | |
| echo "$BODY" | |
| echo "BODY_EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "Latest upstream tag: ${LATEST_TAG}" | |
| # ── 3. Read the last known version from our tracking file ──────────── | |
| - name: Read tracked version | |
| id: tracked | |
| run: | | |
| set -euo pipefail | |
| TRACKING_FILE=".github/optiscaler-version.txt" | |
| if [[ -f "$TRACKING_FILE" ]]; then | |
| # Strip comment lines and blank lines; take the first real line. | |
| KNOWN=$(grep -v '^\s*#' "$TRACKING_FILE" | grep -v '^\s*$' | head -1 | tr -d '[:space:]') | |
| else | |
| KNOWN="unknown" | |
| fi | |
| echo "known_tag=${KNOWN}" >> "$GITHUB_OUTPUT" | |
| echo "Tracked version: ${KNOWN}" | |
| # ── 4. Decide whether anything changed ────────────────────────────── | |
| - name: Compare versions | |
| id: compare | |
| run: | | |
| set -euo pipefail | |
| LATEST="${{ steps.upstream.outputs.latest_tag }}" | |
| KNOWN="${{ steps.tracked.outputs.known_tag }}" | |
| if [[ "$LATEST" == "$KNOWN" ]]; then | |
| echo "new_release=false" >> "$GITHUB_OUTPUT" | |
| echo "No new release detected (still at ${KNOWN})." | |
| else | |
| echo "new_release=true" >> "$GITHUB_OUTPUT" | |
| echo "New release detected: ${KNOWN} → ${LATEST}" | |
| fi | |
| # ── 5. Update tracking file (only when a new release was found) ────── | |
| - name: Update tracking file | |
| if: steps.compare.outputs.new_release == 'true' | |
| run: | | |
| set -euo pipefail | |
| LATEST="${{ steps.upstream.outputs.latest_tag }}" | |
| TRACKING_FILE=".github/optiscaler-version.txt" | |
| # Rewrite the file, keeping the header comment intact. | |
| cat > "$TRACKING_FILE" <<EOF | |
| # This file tracks the last upstream OptiScaler release seen by the | |
| # check-optiscaler-release workflow. Do not edit manually. | |
| # Format: a single release tag string, e.g. v0.7.9 | |
| # The workflow compares the GitHub API's latest tag against this value | |
| # and opens an issue when they differ. | |
| ${LATEST} | |
| EOF | |
| echo "Tracking file updated to ${LATEST}." | |
| # ── 6. Commit the tracking file update ────────────────────────────── | |
| - name: Commit updated tracking file | |
| if: steps.compare.outputs.new_release == 'true' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add .github/optiscaler-version.txt | |
| git commit -m "chore: track OptiScaler upstream release ${{ steps.upstream.outputs.latest_tag }}" | |
| # Push via authenticated remote URL. | |
| git remote set-url origin \ | |
| "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}" | |
| git push origin HEAD:main | |
| # ── 7. Check for an already-open issue for this upstream version ───── | |
| # Prevents duplicate issues if the workflow runs multiple times | |
| # before a maintainer closes the previous one. | |
| - name: Check for duplicate open issue | |
| if: steps.compare.outputs.new_release == 'true' | |
| id: dedup | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| LATEST="${{ steps.upstream.outputs.latest_tag }}" | |
| # Search open issues in this repo with the exact tag in the title. | |
| COUNT=$(gh issue list \ | |
| --repo "${{ github.repository }}" \ | |
| --label "optiscaler-update" \ | |
| --state open \ | |
| --search "OptiScaler ${LATEST} in:title" \ | |
| --json number \ | |
| --jq 'length') | |
| if [[ "$COUNT" -gt 0 ]]; then | |
| echo "skip_issue=true" >> "$GITHUB_OUTPUT" | |
| echo "Issue for ${LATEST} already exists — skipping creation." | |
| else | |
| echo "skip_issue=false" >> "$GITHUB_OUTPUT" | |
| echo "No existing issue found for ${LATEST} — will create one." | |
| fi | |
| # ── 8. Ensure the label exists (idempotent) ────────────────────────── | |
| - name: Ensure optiscaler-update label exists | |
| if: > | |
| steps.compare.outputs.new_release == 'true' && | |
| steps.dedup.outputs.skip_issue == 'false' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| # Create the label; if it already exists the API returns 422 — ignore that. | |
| gh label create "optiscaler-update" \ | |
| --repo "${{ github.repository }}" \ | |
| --description "Upstream OptiScaler release notification" \ | |
| --color "0075ca" \ | |
| 2>/dev/null || true | |
| # ── 9. Open a notification issue ──────────────────────────────────── | |
| - name: Open upstream-release notification issue | |
| if: > | |
| steps.compare.outputs.new_release == 'true' && | |
| steps.dedup.outputs.skip_issue == 'false' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| LATEST="${{ steps.upstream.outputs.latest_tag }}" | |
| RELEASE_NAME="${{ steps.upstream.outputs.release_name }}" | |
| RELEASE_URL="${{ steps.upstream.outputs.release_url }}" | |
| PUBLISHED_AT="${{ steps.upstream.outputs.published_at }}" | |
| ASSETS="${{ steps.upstream.outputs.assets }}" | |
| BODY="${{ steps.upstream.outputs.body }}" | |
| PREV="${{ steps.tracked.outputs.known_tag }}" | |
| # Build a well-structured issue body using a heredoc. | |
| ISSUE_BODY=$(cat <<ISSUE_BODY_EOF | |
| ## Upstream OptiScaler release detected | |
| A new [OptiScaler](https://github.com/cdozdil/OptiScaler) release has been | |
| published. Please review whether OptiScaler-GUI needs any changes to remain | |
| compatible. | |
| | Field | Value | | |
| |---|---| | |
| | **Previous tracked version** | \`${PREV}\` | | |
| | **New version** | \`${LATEST}\` | | |
| | **Release name** | ${RELEASE_NAME} | | |
| | **Published** | ${PUBLISHED_AT} | | |
| | **Release page** | [${RELEASE_URL}](${RELEASE_URL}) | | |
| ### Release assets | |
| ${ASSETS} | |
| ### Upstream release notes | |
| <details> | |
| <summary>Click to expand</summary> | |
| ${BODY} | |
| </details> | |
| --- | |
| ### Suggested review checklist | |
| - [ ] Verify \`OptiScalerConfig.GITHUB_API_URL\` in \`src/optiscaler/manager.py\` still points to the correct endpoint. | |
| - [ ] Check that the expected archive asset name/format hasn't changed (the manager downloads a \`.7z\` or \`.zip\` asset). | |
| - [ ] Review any new or renamed DLL files listed in the assets above against \`ADDITIONAL_FILES\` and \`PROXY_FILENAMES\` in \`manager.py\`. | |
| - [ ] Check the upstream release notes for breaking API or config format changes. | |
| - [ ] Update \`CHANGELOG.md\` and bump \`src/__version__.py\` if OptiScaler-GUI changes are required. | |
| _This issue was opened automatically by the | |
| [check-optiscaler-release](${{ github.server_url }}/${{ github.repository }}/actions/workflows/check-optiscaler-release.yml) | |
| workflow. Close it once reviewed, whether or not changes are needed._ | |
| ISSUE_BODY_EOF | |
| ) | |
| gh issue create \ | |
| --repo "${{ github.repository }}" \ | |
| --title "OptiScaler ${LATEST} released — review for GUI compatibility" \ | |
| --body "$ISSUE_BODY" \ | |
| --label "optiscaler-update" | |
| echo "Issue created for OptiScaler ${LATEST}." |