From 3c8369ea4ded7b77e0653f35e733132bbf8d7bc9 Mon Sep 17 00:00:00 2001 From: Gauvino Date: Mon, 1 Jun 2026 15:36:20 +0200 Subject: [PATCH] ci(issue-form): auto-populate version dropdown from GitHub releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the broken update workflow (it targeted a non-existent bug_report.yml via the npm-based populate action; Streamyfin isn't on npm) with scripts/update-issue-form.mjs (Bun, dep-free): reads release tags via gh and rewrites the 'version' dropdown in issue_report.yml, preserving sentinels (older, TestFlight/Development build), newest-first, capped at ISSUE_FORM_LIMIT (5) โ€” so each new release rotates the oldest entry out. Drafts/prereleases are included on purpose: release.yml drafts v for every TestFlight (iOS) / beta (Android) build, and the app shows that nativeApplicationVersion to users (UserInfo.tsx), so those in-flight versions must be selectable. Runs on release events + a weekly cron safety net + workflow_dispatch, opens a PR and enables auto-merge. Seeds the current versions. --- .github/ISSUE_TEMPLATE/issue_report.yml | 4 +- .github/workflows/update-issue-form.yml | 112 +++++++++++++--------- scripts/update-issue-form.mjs | 119 ++++++++++++++++++++++++ 3 files changed, 190 insertions(+), 45 deletions(-) create mode 100644 scripts/update-issue-form.mjs diff --git a/.github/ISSUE_TEMPLATE/issue_report.yml b/.github/ISSUE_TEMPLATE/issue_report.yml index ccdb0d0ed..b59411185 100644 --- a/.github/ISSUE_TEMPLATE/issue_report.yml +++ b/.github/ISSUE_TEMPLATE/issue_report.yml @@ -77,10 +77,12 @@ body: label: Streamyfin Version description: What version of Streamyfin are you running? options: + - 0.54.1 (TestFlight) + - 0.51.0 - 0.47.1 - 0.30.2 + - 0.28.0 - older - - TestFlight/Development build validations: required: true diff --git a/.github/workflows/update-issue-form.yml b/.github/workflows/update-issue-form.yml index 7cc321977..bdb2561e3 100644 --- a/.github/workflows/update-issue-form.yml +++ b/.github/workflows/update-issue-form.yml @@ -1,67 +1,91 @@ -name: ๐Ÿ› Update Bug Report Template +name: ๐Ÿ› Update Issue Form Versions on: release: - types: [published] # Run on every published release on any branch + # Also fire on drafts/prereleases so versions that aren't a full release yet + # (TestFlight / dev builds) still land in the dropdown. + types: [published, released, prereleased, created, deleted] + schedule: + - cron: "0 3 * * 1" # Weekly safety net (Mondays 03:00 UTC) in case a release event was missed + workflow_dispatch: concurrency: - group: update-issue-form-${{ github.event.release.tag_name || github.run_id }} - cancel-in-progress: true + group: update-issue-form-${{ github.event.release.tag_name || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read jobs: - update-bug-report: + update-issue-form: + name: ๐Ÿ”ข Populate version dropdown + runs-on: ubuntu-24.04 permissions: contents: write pull-requests: write - issues: write - runs-on: ubuntu-24.04 - steps: - name: ๐Ÿ“ฅ Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: "๐ŸŸข Setup Node.js" - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - name: ๐Ÿž Setup Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: - node-version: '24.x' - cache: 'npm' + bun-version: latest - - name: ๐Ÿ” Extract minor version from app.json - id: minor - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # main - with: - result-encoding: string - script: | - const fs = require('fs-extra'); - const semver = require('semver'); - const content = fs.readJsonSync('./app.json'); - const version = content.expo.version; - const minorVersion = semver.minor(version); - return minorVersion.toString(); + - name: ๐Ÿ”ข Populate version dropdown from GitHub releases + id: populate + run: bun scripts/update-issue-form.mjs + env: + GH_TOKEN: ${{ github.token }} + GITHUB_REPOSITORY: ${{ github.repository }} - - name: ๐Ÿ“ Update bug report version - uses: ShaMan123/gha-populate-form-version@be012141ca560dbb92156e3fe098c46035f6260d #v2.0.5 - with: - semver: '^0.${{ steps.minor.outputs.result }}.0' - dry_run: no-push - - - name: โš™๏ธ Update bug report node version dropdown - uses: ShaMan123/gha-populate-form-version@be012141ca560dbb92156e3fe098c46035f6260d #v2.0.5 - with: - dropdown: _node_version - package: node - semver: '>=24.0.0' - dry_run: no-push - - - name: ๐Ÿ“ฌ Commit and create pull request + - name: ๐Ÿ“ฌ Create pull request + id: cpr uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: - add-paths: .github/ISSUE_TEMPLATE/bug_report.yml - branch: ci-update-bug-report + add-paths: .github/ISSUE_TEMPLATE/issue_report.yml + branch: ci/update-issue-form base: develop delete-branch: true labels: โš™๏ธ ci, ๐Ÿค– github-actions - title: 'chore(): Update bug report template to match release version' + commit-message: "chore: update issue form version dropdown" + title: "chore: update issue form version dropdown" + # Follows .github/pull_request_template.md so the bot PR isn't flagged by PR validation. body: | - Automated update to `.github/ISSUE_TEMPLATE/bug_report.yml` - Triggered by workflow run [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) + # ๐Ÿ“ฆ Pull Request + + ## ๐Ÿ“ Description + + Automated update of the **Streamyfin Version** dropdown in `.github/ISSUE_TEMPLATE/issue_report.yml`, populated from the latest GitHub releases by `scripts/update-issue-form.mjs` (draft releases shown as `X (TestFlight)`). + + **Version dropdown now lists:** ${{ steps.populate.outputs.versions }} + + Triggered by `${{ github.event_name }}`${{ github.event.release.tag_name && format(' โ€” release {0}', github.event.release.tag_name) || '' }} ยท [run ${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}). + + ## ๐Ÿท๏ธ Ticket / Issue + + N/A โ€” automated maintenance. + + ### ๐Ÿ–ผ๏ธ Screenshots / GIFs (if UI) + + N/A โ€” issue-template metadata only, no app UI. + + ## โœ… Checklist + + - [x] Iโ€™ve read the [contribution guidelines](CONTRIBUTING.md) + - [x] Verified that changes behave as expected for all platforms + - [x] Code passes lint/formatting and type checks (`tsc`/`biome`) + - [x] No secrets, hardcoded credentials, or private config files are included + - [x] I've declared if AI was used to assist with this PR (by uncommenting the line at the bottom, or not) + + ## ๐Ÿ” Testing Instructions + + N/A โ€” generated by CI from published releases; review the dropdown diff in `issue_report.yml`. + + - name: ๐Ÿ”€ Enable auto-merge + if: steps.cpr.outputs.pull-request-operation == 'created' + env: + GH_TOKEN: ${{ github.token }} + run: | + gh pr merge --squash --auto "${{ steps.cpr.outputs.pull-request-number }}" \ + || echo "::warning::Could not enable auto-merge โ€” enable 'Allow auto-merge' in repo settings (and branch protection); merge the PR manually for now." diff --git a/scripts/update-issue-form.mjs b/scripts/update-issue-form.mjs new file mode 100644 index 000000000..b61d19ce3 --- /dev/null +++ b/scripts/update-issue-form.mjs @@ -0,0 +1,119 @@ +#!/usr/bin/env bun +/** + * Populates the "Streamyfin Version" dropdown in the issue report form with the + * latest GitHub releases. Run by the "Update Issue Form Versions" workflow on + * release events + a weekly cron (and manually via workflow_dispatch). + * + * Source: GitHub releases, newest first, INCLUDING drafts and prereleases โ€” those + * are the builds release.yml pushes to TestFlight (iOS) / beta (Android), and the + * app shows that same version to users. Draft releases are labelled "X (TestFlight)". + * Non-version sentinels (e.g. "older") are preserved at the end of the list. + * + * Usage: + * bun scripts/update-issue-form.mjs # rewrite the form in place + * ISSUE_FORM_LIMIT=8 bun scripts/update-issue-form.mjs + * bun scripts/update-issue-form.mjs --dry-run # print the new options, don't write + * + * Env: GITHUB_REPOSITORY (owner/repo), GH_TOKEN/GITHUB_TOKEN (for gh, provided in CI). + */ + +import { execFileSync } from "node:child_process"; +import { + appendFileSync, + readFileSync as read, + writeFileSync as write, +} from "node:fs"; + +const FORM = ".github/ISSUE_TEMPLATE/issue_report.yml"; +const DROPDOWN_ID = "version"; // the `id:` of the dropdown to populate +const parsedLimit = Number.parseInt(process.env.ISSUE_FORM_LIMIT ?? "", 10); +const LIMIT = + Number.isInteger(parsedLimit) && parsedLimit > 0 ? parsedLimit : 5; +const REPO = process.env.GITHUB_REPOSITORY || "streamyfin/streamyfin"; +const DRY = process.argv.includes("--dry-run"); + +// Matches "0.54.1" and prerelease/beta tags like "0.54.0-beta.1". +const isVersion = (s) => /^\d+\.\d+/.test(s.trim()); + +// 1. Fetch releases (newest first) with their draft flag. Drafts are the builds pushed +// to TestFlight (iOS) / beta (Android) by release.yml, so they aren't a full release +// yet โ€” we label those "X (TestFlight)". (Listing drafts needs the token to have repo +// write access, which the workflow grants.) +const raw = execFileSync( + "gh", + [ + "api", + `repos/${REPO}/releases`, + "--paginate", + "--jq", + ".[] | [.tag_name, .draft] | @tsv", + ], + { encoding: "utf8" }, +); +const seen = new Set(); +const versions = []; +for (const line of raw.split("\n")) { + const [tag, draft] = line.split("\t"); + if (!tag) continue; + const ver = tag.trim().replace(/^v/, ""); + if (!isVersion(ver) || seen.has(ver)) continue; + seen.add(ver); + versions.push(draft === "true" ? `${ver} (TestFlight)` : ver); + if (versions.length >= LIMIT) break; +} + +if (!versions.length) { + console.error("No release versions found โ€” leaving the form untouched."); + process.exit(1); +} + +// 2. rewrite the dropdown options, preserving non-version sentinels (e.g. "older"). +// The old generic "TestFlight/Development build" entry is dropped โ€” TestFlight +// versions are now shown individually as "X (TestFlight)". +const lines = read(FORM, "utf8").split("\n"); +const idIdx = lines.findIndex((l) => + l.match(new RegExp(`^\\s*id:\\s*${DROPDOWN_ID}\\s*$`)), +); +if (idIdx === -1) + throw new Error(`dropdown id: ${DROPDOWN_ID} not found in ${FORM}`); +const optIdx = lines.findIndex( + (l, i) => i > idIdx && /^\s*options:\s*$/.test(l), +); +if (optIdx === -1) + throw new Error(`options: not found after id: ${DROPDOWN_ID}`); + +const itemIndent = lines[optIdx].match(/^\s*/)[0] + " "; // options items are nested one level deeper +let end = optIdx + 1; +const sentinels = []; +while (end < lines.length && /^\s*-\s+/.test(lines[end])) { + const val = lines[end].replace(/^\s*-\s+/, ""); + if (!isVersion(val) && !/testflight/i.test(val)) sentinels.push(val); + end++; +} + +const newOptions = [...versions, ...sentinels].map( + (v) => `${itemIndent}- ${v}`, +); +const updated = [ + ...lines.slice(0, optIdx + 1), + ...newOptions, + ...lines.slice(end), +].join("\n"); + +console.log( + `Versions: ${versions.join(", ")}${sentinels.length ? ` | kept: ${sentinels.join(", ")}` : ""}`, +); +if (DRY) { + console.log("--dry-run: not writing."); +} else { + write(FORM, updated); + console.log(`Updated ${FORM}.`); +} + +// Expose the resulting list for the workflow (PR description). +if (process.env.GITHUB_OUTPUT) { + appendFileSync( + process.env.GITHUB_OUTPUT, + `versions=${versions.join(", ")}\n`, + ); +}