Files
kernalix7--winpodx/.github/workflows/obs-publish.yml
T
wehub-resource-sync 3e779be6f3
CI / test (3.14, ubuntu-24.04-arm) (push) Waiting to run
CI / lint (push) Failing after 13m4s
CI / test (3.11, ubuntu-latest) (push) Failing after 2m4s
CI / test (3.13, ubuntu-latest) (push) Successful in 13m30s
CI / test (3.14, ubuntu-latest) (push) Successful in 17m21s
CI / test (3.12, ubuntu-latest) (push) Successful in 17m55s
CI / discover-apps-ps (push) Successful in 1m56s
CI / test (3.9, ubuntu-latest) (push) Successful in 13m17s
CI / test (3.10, ubuntu-latest) (push) Successful in 26m21s
CI / audit (push) Successful in 13m38s
Deploy site / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:37 +08:00

265 lines
12 KiB
YAML

name: OBS Publish
# OBS builds RPMs asynchronously on shared openSUSE infrastructure
# (~30-60 min per rebuild). Trying to trigger the rebuild AND download the
# freshly-built RPMs in the same tag-push run was structurally broken: the
# download ran minutes after the trigger, long before OBS had published the
# new version, so every release push failed with "No RPMs were downloaded".
#
# This workflow is split to match OBS's async reality:
# * `trigger` (tag push / dispatch) — fire the OBS rebuild, then exit
# green. It never tries to download, so a release push can't go red here.
# * `attach` (hourly schedule / dispatch) — once OBS has published the
# RPMs for the latest release's version, download them and attach them to
# the existing GitHub Release. It is a no-op (green) when the RPMs aren't
# built yet (retries next hour) or are already attached (no churn).
#
# Single-writer rule: this workflow NEVER creates the GitHub Release or edits
# its title/notes — release.yml (REL- marker tag) is the sole creator and the
# only writer of the release body. `attach` only uploads assets to a release
# that already exists, so it can't cause the "edited N times" / placeholder-
# notes churn the old per-workflow `gh release create --notes …` stubs did.
on:
push:
tags:
- "v*.*.*"
schedule:
# Hourly sweep: attach OBS RPMs to the latest release once OBS finishes
# building them. A no-op the rest of the time (already attached / not
# built yet), so it never edits a release it has nothing new for.
- cron: "17 * * * *"
workflow_dispatch:
inputs:
tag:
description: "Release tag to trigger / attach RPMs to (e.g. v0.6.0)"
required: true
force:
description: "Force OBS runservice even if Tumbleweed RPM exists (use when source bumped without version change)"
required: false
default: "false"
env:
OBS_PROJECT: home:Kernalix7
OBS_PACKAGE: winpodx
OBS_API: https://api.opensuse.org
# /public/ is OBS's anonymous read-only API mirror — no auth needed for
# public home: projects. The runservice token can't read /build/.
OBS_PUBLIC_API: https://api.opensuse.org/public
OBS_DOWNLOAD: https://download.opensuse.org/repositories/home:/Kernalix7
permissions:
contents: write
jobs:
# Fire the OBS rebuild and exit. Runs on tag push and manual dispatch only.
# Never downloads — so a release push is always green here; the RPMs land
# asynchronously via the `attach` job once OBS finishes the server-side build.
trigger:
if: github.event_name != 'schedule'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Resolve tag
id: tag
run: |
if [ "${{ github.event_name }}" = "push" ]; then
tag="${{ github.ref_name }}"
else
tag="${{ inputs.tag }}"
fi
# OBS feeds the version into both deb and rpm specs; rpm forbids
# '-' in Version: (Version/Release delimiter). Strip the RTM
# gate suffix so `0.3.0-RTM1` lands as `0.3.0`.
version="${tag#v}"
version="${version%%-RTM*}"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Install curl + jq
run: sudo apt-get update && sudo apt-get install -y curl jq
- name: Probe whether OBS already has target version built
# Avoid triggering an unnecessary OBS rebuild when the target version
# is already published. A single rebuild costs ~30-60min on OBS shared
# infrastructure and is pure waste when the RPMs we'd download are
# byte-identical to what's already on the server. Probe a representative
# shipping repo (Tumbleweed) for a winpodx-<version>-*.rpm filename in
# the public download index; if found, OBS has the build cached.
id: probe
run: |
version="${{ steps.tag.outputs.version }}"
if [ "${{ inputs.force }}" = "true" ]; then
echo "force=true requested — bypassing Tumbleweed RPM probe and triggering runservice."
echo "skip_runservice=false" >> "$GITHUB_OUTPUT"
exit 0
fi
listing_url="${OBS_DOWNLOAD}/openSUSE_Tumbleweed/noarch/"
html=$(curl -sS -f -L "$listing_url" 2>/dev/null || echo "")
if echo "$html" | grep -qE "winpodx-${version}-[0-9]+\.[0-9]+\.noarch\.rpm"; then
echo "OBS already has openSUSE_Tumbleweed RPM for v${version} — skipping runservice."
echo "skip_runservice=true" >> "$GITHUB_OUTPUT"
else
echo "OBS doesn't yet have openSUSE_Tumbleweed RPM for v${version} — will trigger runservice."
echo "skip_runservice=false" >> "$GITHUB_OUTPUT"
fi
- name: Trigger OBS service run (token)
if: steps.probe.outputs.skip_runservice == 'false'
env:
OBS_TOKEN: ${{ secrets.OBS_TOKEN }}
run: |
if [ -z "$OBS_TOKEN" ]; then
echo "OBS_TOKEN secret is not set" >&2
exit 1
fi
curl -sS -f -X POST \
-H "Authorization: Token $OBS_TOKEN" \
"$OBS_API/trigger/runservice?project=$OBS_PROJECT&package=$OBS_PACKAGE"
echo "OBS rebuild triggered for v${{ steps.tag.outputs.version }}. RPMs will be attached by the hourly 'attach' job once OBS finishes (~30-60 min)."
# Download OBS-built RPMs once they're published and attach them to the
# existing GitHub Release. Runs hourly (sweeps the latest release) and on
# manual dispatch (targets inputs.tag). Never creates or edits the release.
attach:
if: github.event_name != 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Install curl + jq
run: sudo apt-get update && sudo apt-get install -y curl jq
- name: Resolve target tag
id: tag
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
tag="${{ inputs.tag }}"
else
# schedule: sweep the most recent published release.
tag=$(gh release list --repo "${{ github.repository }}" --limit 1 \
--json tagName --jq '.[0].tagName' 2>/dev/null || echo "")
fi
if [ -z "$tag" ]; then
echo "No target tag (no releases yet?) — nothing to attach."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
version="${tag#v}"
version="${version%%-RTM*}"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "skip=false" >> "$GITHUB_OUTPUT"
- name: Skip if release already has OBS RPMs attached
id: gate
if: steps.tag.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
tag="${{ steps.tag.outputs.tag }}"
# release.yml owns creation — only attach to a release that exists.
if ! gh release view "$tag" --repo "${{ github.repository }}" >/dev/null 2>&1; then
echo "Release $tag does not exist yet (release.yml owns creation) — nothing to attach."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
# Tumbleweed is always built and is the canary for "OBS RPMs already
# attached". If its asset is present, the sweep already ran for this
# release — exit green without re-uploading (no edit churn).
assets=$(gh release view "$tag" --repo "${{ github.repository }}" \
--json assets --jq '.assets[].name' 2>/dev/null || echo "")
if echo "$assets" | grep -q "openSUSE_Tumbleweed\.rpm$"; then
echo "OBS RPMs already attached to $tag — no-op."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Discover repos and download RPMs
id: download
if: steps.gate.outputs.skip == 'false'
run: |
mkdir -p dist-rpms
version="${{ steps.tag.outputs.version }}"
# Parse enabled repos from project _meta (public, no auth)
meta=$(curl -sS -f "$OBS_PUBLIC_API/source/$OBS_PROJECT/_meta")
repos=$(echo "$meta" | grep -oP '(?<=<repository name=")[^"]+')
if [ -z "$repos" ]; then
echo "No repositories configured on $OBS_PROJECT" >&2
exit 1
fi
for repo in $repos; do
# openSUSE_Factory_{ARM,PowerPC,RISCV,zSystems} builds the same
# noarch content as openSUSE_Tumbleweed — publishing all five would
# just list four identical RPMs with misleading architecture-looking
# names. Skip the non-x86 Factory rebuilds; Tumbleweed users on
# aarch64/ppc64/riscv64/s390x install the Tumbleweed noarch RPM.
case "$repo" in
openSUSE_Factory_*) echo "Skipping $repo (noarch duplicate of Tumbleweed)"; continue ;;
esac
# Tumbleweed / Fedora / Slowroll all produce the same filename
# (winpodx-<ver>-<rel>.noarch.rpm) because their release tags lack
# a distro suffix. Without renaming, only the last-iterated repo's
# file survives in dist-rpms/. Append the repo name to every RPM so
# each distro's build gets a stable, unique asset name on the
# Release — matches the RHEL convention (.el9.rpm / .el10.rpm).
# Normalize short Leap repo names ("15.6", "16.0") to the familiar
# openSUSE_Leap_* form so the final filename is self-describing
# instead of ending in a bare version number like ".15.6.rpm".
case "$repo" in
1[0-9].[0-9]|1[0-9].[0-9][0-9]) repo_slug="openSUSE_Leap_$repo" ;;
*) repo_slug=$(echo "$repo" | tr '/:' '__') ;;
esac
for arch in x86_64 noarch; do
base="$OBS_DOWNLOAD/$repo/$arch"
# MirrorCache listing hrefs have a "./" prefix
# (href="./winpodx-<ver>-<rel>.noarch.rpm"); match the filename
# directly instead of anchoring on `href="winpodx-`.
listing=$(curl -sSL "$base/" || true)
[ -z "$listing" ] && continue
# Anchor the filename match to the current tag's version so a
# stale older RPM still in the OBS publish index (Tumbleweed /
# Slowroll rolling-release repos are slow to garbage-collect)
# doesn't get downloaded and re-uploaded as a v${version}
# release asset.
rpms=$(echo "$listing" | grep -oE "winpodx-${version}-[^\"]+\.rpm" | sort -u)
for f in $rpms; do
case "$f" in
*debuginfo*|*debugsource*|*.src.rpm) continue ;;
esac
out="dist-rpms/${f%.rpm}.${repo_slug}.rpm"
echo "Fetching $repo/$arch/$f -> $(basename "$out")"
curl -sS -fL -o "$out" "$base/$f" || true
done
done
done
ls -la dist-rpms/
if ! ls dist-rpms/*.rpm >/dev/null 2>&1; then
echo "::notice::OBS hasn't published RPMs for v${version} yet — they build asynchronously (~30-60 min). The hourly sweep will retry; or re-run this workflow once the build finishes."
echo "have_rpms=false" >> "$GITHUB_OUTPUT"
else
echo "have_rpms=true" >> "$GITHUB_OUTPUT"
fi
- name: Attach RPMs to GitHub release
if: steps.download.outputs.have_rpms == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "${{ steps.tag.outputs.tag }}" dist-rpms/*.rpm --clobber \
--repo "${{ github.repository }}"
echo "Attached OBS RPMs to ${{ steps.tag.outputs.tag }}."
- uses: actions/upload-artifact@v4
if: steps.download.outputs.have_rpms == 'true'
with:
name: rpms-${{ steps.tag.outputs.version }}
path: dist-rpms/