mirror of
https://github.com/zealdocs/zeal.git
synced 2026-08-29 08:34:50 +08:00
ci(github): add Fedora COPR publishing (#1904)
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
name: Publish COPR Packages
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
schedule:
|
||||
# Skipped when main has not advanced since the last build (see "Resolve version").
|
||||
- cron: "2 4 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Release version to upload (without 'v'). Leave blank to build a nightly snapshot from main."
|
||||
required: false
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
COPR_PROJECT_STABLE: "@zealdocs/zeal"
|
||||
COPR_PROJECT_NIGHTLY: "@zealdocs/zeal-nightly"
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish
|
||||
if: github.repository == 'zealdocs/zeal'
|
||||
runs-on: ubuntu-latest
|
||||
# Build the source RPM on Fedora so the Fedora spec macros (%cmake, %autosetup,
|
||||
# %{?dist}) and rpmbuild/copr-cli are all native. The first step installs git
|
||||
# so the checkout below can use it.
|
||||
container:
|
||||
image: fedora:latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Install packaging tools
|
||||
run: dnf -y install git rpm-build copr-cli jq
|
||||
|
||||
- name: Determine mode and target
|
||||
id: mode
|
||||
# Untrusted event/input values go through env and are validated before use
|
||||
# to block shell injection.
|
||||
env:
|
||||
DISPATCH_VERSION: ${{ inputs.version }}
|
||||
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ "${{ github.event_name }}" = "release" ]; then
|
||||
raw="$RELEASE_TAG"; stable=true
|
||||
elif [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "$DISPATCH_VERSION" ]; then
|
||||
raw="$DISPATCH_VERSION"; stable=true
|
||||
else
|
||||
stable=false
|
||||
fi
|
||||
|
||||
if [ "$stable" = true ]; then
|
||||
version="${raw#v}" # tolerate an optional leading v
|
||||
# RPM forbids '-' in Version: (it delimits version from release), so
|
||||
# reject it here along with anything else outside an RPM version.
|
||||
case "$version" in
|
||||
''|*[!0-9A-Za-z.+~]*)
|
||||
echo "::error::Unexpected version string: '${version}'"; exit 1 ;;
|
||||
esac
|
||||
{
|
||||
echo "mode=stable"
|
||||
echo "ref=v${version}"
|
||||
echo "version=${version}"
|
||||
echo "project=${COPR_PROJECT_STABLE}"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
echo "Stable release: ${version}"
|
||||
else
|
||||
# Version is derived from the checkout below, so it is left empty here.
|
||||
{
|
||||
echo "mode=nightly"
|
||||
echo "ref=main"
|
||||
echo "version="
|
||||
echo "project=${COPR_PROJECT_NIGHTLY}"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
echo "Nightly snapshot from main"
|
||||
fi
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ steps.mode.outputs.ref }}
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
persist-credentials: false
|
||||
|
||||
- name: Configure COPR credentials
|
||||
env:
|
||||
COPR_CONFIG: ${{ secrets.COPR_API_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "$COPR_CONFIG" ]; then
|
||||
echo "::error::COPR_API_TOKEN secret is empty; copy the token block from https://copr.fedorainfracloud.org/api/."
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "${HOME}/.config"
|
||||
printf '%s' "$COPR_CONFIG" > "${HOME}/.config/copr"
|
||||
chmod 600 "${HOME}/.config/copr"
|
||||
|
||||
- name: Resolve version
|
||||
id: resolve
|
||||
env:
|
||||
MODE: ${{ steps.mode.outputs.mode }}
|
||||
VERSION: ${{ steps.mode.outputs.version }}
|
||||
PROJECT: ${{ steps.mode.outputs.project }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# The checkout populates the workspace as a different owner than the
|
||||
# container's root, which trips git's ownership guard.
|
||||
git config --global --add safe.directory "$PWD"
|
||||
|
||||
if [ "$MODE" = "stable" ]; then
|
||||
version="$VERSION"
|
||||
else
|
||||
# Snapshot version from main: 0.8.2-15-gabcdef -> 0.8.2^15.gabcdef, which
|
||||
# sorts above the last release and below the next.
|
||||
version=$(git describe --tags --long | sed 's/^v//; s/-\([0-9]*\)-g/^\1.g/')
|
||||
|
||||
# Skip when COPR's latest build already has this version (the version
|
||||
# embeds the commit). An empty result never skips, so a failed or missing
|
||||
# build is retried. Confirm the JSON path against copr-cli; a wrong path
|
||||
# just falls back to always building.
|
||||
last=$(copr-cli get-package "$PROJECT" --name zeal --with-latest-build \
|
||||
--output-format json 2>/dev/null | jq -r '.latest_build.source_package.version // empty' || true)
|
||||
if [ -n "$last" ] && [ "$version" = "$last" ]; then
|
||||
echo "main has not advanced since the last build (${last}); nothing to do."
|
||||
echo "proceed=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Building version: ${version}"
|
||||
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
||||
echo "proceed=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build source RPM
|
||||
if: steps.resolve.outputs.proceed == 'true'
|
||||
env:
|
||||
VERSION: ${{ steps.resolve.outputs.version }}
|
||||
PACKAGER: Zeal Release <release@zealdocs.org>
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [ ! -f pkg/copr/zeal.spec ]; then
|
||||
echo "::error::pkg/copr/zeal.spec missing in this checkout."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${HOME}/rpmbuild/SOURCES" "${HOME}/rpmbuild/SPECS"
|
||||
|
||||
# Tarball name and prefix must match the spec's Source0.
|
||||
git archive --prefix="zeal-${VERSION}/" HEAD \
|
||||
| gzip -9 > "${HOME}/rpmbuild/SOURCES/zeal-${VERSION}.tar.gz"
|
||||
|
||||
# Replace the spec's %changelog with a single dated entry.
|
||||
spec="${HOME}/rpmbuild/SPECS/zeal.spec"
|
||||
sed '/^%changelog/q' pkg/copr/zeal.spec > "$spec"
|
||||
{
|
||||
printf '* %s %s - %s-1\n' "$(LC_ALL=C date '+%a %b %d %Y')" "$PACKAGER" "$VERSION"
|
||||
printf -- '- Build of %s.\n' "$VERSION"
|
||||
} >> "$spec"
|
||||
|
||||
# Pass the bundled cpp-httplib version (from its header) to the spec's
|
||||
# bundled() Provides. Omitted if not found, leaving it unversioned.
|
||||
httplib_version=""
|
||||
if [ -f src/contrib/cpp-httplib/httplib.h ]; then
|
||||
httplib_version=$(sed -n 's/^#define CPPHTTPLIB_VERSION "\([^"]*\)".*/\1/p' \
|
||||
src/contrib/cpp-httplib/httplib.h | head -n1)
|
||||
fi
|
||||
|
||||
defines=(--define "_topdir ${HOME}/rpmbuild" --define "zeal_version ${VERSION}")
|
||||
if [ -n "$httplib_version" ]; then
|
||||
defines+=(--define "httplib_version ${httplib_version}")
|
||||
fi
|
||||
|
||||
rpmbuild -bs "$spec" "${defines[@]}"
|
||||
|
||||
srpm=$(find "${HOME}/rpmbuild/SRPMS" -name "zeal-${VERSION}-1*.src.rpm" | head -n1)
|
||||
if [ -z "$srpm" ]; then
|
||||
echo "::error::Source RPM was not produced."
|
||||
exit 1
|
||||
fi
|
||||
echo "SRPM=${srpm}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Submit build to COPR
|
||||
if: steps.resolve.outputs.proceed == 'true'
|
||||
# No --nowait: wait so a failing chroot fails the workflow. COPR rebuilds
|
||||
# for every enabled chroot.
|
||||
env:
|
||||
PROJECT: ${{ steps.mode.outputs.project }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
copr-cli build "$PROJECT" "$SRPM"
|
||||
@@ -0,0 +1,58 @@
|
||||
# Fedora COPR packaging
|
||||
|
||||
RPM packaging for Zeal and the workflow that publishes it to
|
||||
[Fedora COPR](https://copr.fedorainfracloud.org/). COPR builds binaries from a
|
||||
source RPM, once per enabled chroot.
|
||||
|
||||
`publish-copr.yaml` runs two flows:
|
||||
|
||||
| Flow | COPR project | Trigger |
|
||||
| ------- | ------------------------ | -------------------------------------------- |
|
||||
| Release | `@zealdocs/zeal` | GitHub release, or manual run with a version |
|
||||
| Nightly | `@zealdocs/zeal-nightly` | daily cron, or manual run with no version |
|
||||
|
||||
Both build the source RPM in the workflow and submit it with `copr-cli`. The
|
||||
nightly run skips when `main` has not advanced since its last build.
|
||||
|
||||
## Files
|
||||
|
||||
`zeal.spec` is the RPM spec. `%build` and `%install` are `%cmake` macros, since
|
||||
CMake installs the binary, desktop file, metainfo, and icons under the prefix.
|
||||
toml++ comes from `tomlplusplus-devel`; cpp-httplib stays bundled in `src/contrib`.
|
||||
|
||||
`Version:` is a placeholder. The workflow sets it with `--define "zeal_version <v>"`:
|
||||
the tag for releases, a `git describe` snapshot for nightlies (which sorts above
|
||||
the last release and below the next).
|
||||
|
||||
## Chroots
|
||||
|
||||
Chroots live on the COPR projects, not in the repo. Targets are the supported
|
||||
Fedora releases plus rawhide, and the EPEL releases that ship `qt6-qtwebengine`
|
||||
(RHEL, AlmaLinux, Rocky, CentOS Stream; EPEL 8 lacks it). "Follow Fedora branching"
|
||||
adds new Fedora chroots automatically; add new EPEL releases by hand.
|
||||
|
||||
Support windows: [Fedora](https://endoflife.date/fedora),
|
||||
[CentOS Stream](https://endoflife.date/centos-stream),
|
||||
[RHEL](https://endoflife.date/rhel).
|
||||
|
||||
## Setup
|
||||
|
||||
1. Create `@zealdocs/zeal` and `@zealdocs/zeal-nightly`, enable the chroots, and turn
|
||||
on "Follow Fedora branching". Enable "Generate AppStream metadata" on the
|
||||
release project.
|
||||
2. Add the repository secret `COPR_API_TOKEN`: the `[copr-cli]` block from
|
||||
<https://copr.fedorainfracloud.org/api/>. Both flows use it; refresh it when it
|
||||
expires.
|
||||
|
||||
## Local testing
|
||||
|
||||
```sh
|
||||
VERSION=$(git describe --tags --abbrev=0 | sed 's/^v//')
|
||||
mkdir -p ~/rpmbuild/SOURCES ~/rpmbuild/SPECS
|
||||
git archive --prefix="zeal-$VERSION/" "v$VERSION" | gzip -9 \
|
||||
> ~/rpmbuild/SOURCES/zeal-$VERSION.tar.gz
|
||||
cp pkg/copr/zeal.spec ~/rpmbuild/SPECS/
|
||||
rpmbuild -bs ~/rpmbuild/SPECS/zeal.spec \
|
||||
--define "_topdir $HOME/rpmbuild" --define "zeal_version $VERSION"
|
||||
mock ~/rpmbuild/SRPMS/zeal-$VERSION-1*.src.rpm
|
||||
```
|
||||
@@ -0,0 +1,84 @@
|
||||
# RPM spec for Zeal, built on Fedora COPR.
|
||||
#
|
||||
# The Version tag is a placeholder overridden by the release automation, which
|
||||
# passes --define "zeal_version <version>" when building the source RPM (see
|
||||
# .github/workflows/publish-copr.yaml). A plain build with no override produces
|
||||
# 0.0.0, matching the AUR PKGBUILD convention. CMake already installs the binary,
|
||||
# .desktop file, AppStream metainfo, and icons under the prefix on Linux
|
||||
# (see assets/freedesktop/CMakeLists.txt), so %%cmake_install needs no extra glue.
|
||||
# toml++ is taken from the distro (tomlplusplus-devel). cpp-httplib stays bundled
|
||||
# in src/contrib because the system versions span too wide a range to rely on; its
|
||||
# bundled() Provides version is injected by the workflow via
|
||||
# --define "httplib_version <v>", read from the bundled header.
|
||||
|
||||
Name: zeal
|
||||
Version: %{?zeal_version}%{!?zeal_version:0.0.0}
|
||||
Release: 1%{?dist}
|
||||
Summary: Simple offline documentation browser
|
||||
|
||||
License: GPL-3.0-or-later
|
||||
URL: https://zealdocs.org
|
||||
# URL form is metadata for release builds only; it does not resolve for nightly
|
||||
# snapshot versions (e.g. 0.8.2^15.gabcdef). Every build supplies the tarball
|
||||
# locally, so Source0 is never fetched.
|
||||
Source0: https://github.com/zealdocs/zeal/archive/refs/tags/v%{version}/%{name}-%{version}.tar.gz
|
||||
|
||||
BuildRequires: cmake >= 3.25.1
|
||||
BuildRequires: extra-cmake-modules
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: ninja-build
|
||||
|
||||
BuildRequires: qt6-qtbase-devel
|
||||
BuildRequires: qt6-qtsvg-devel
|
||||
BuildRequires: qt6-qtwebchannel-devel
|
||||
BuildRequires: qt6-qtwebengine-devel
|
||||
|
||||
BuildRequires: libarchive-devel
|
||||
BuildRequires: tomlplusplus-devel
|
||||
BuildRequires: sqlite-devel
|
||||
BuildRequires: mesa-libGL-devel
|
||||
BuildRequires: vulkan-headers
|
||||
BuildRequires: libX11-devel
|
||||
BuildRequires: libxcb-devel
|
||||
BuildRequires: xcb-util-keysyms-devel
|
||||
BuildRequires: libxkbcommon-devel
|
||||
|
||||
Requires: hicolor-icon-theme
|
||||
|
||||
%if "%{?httplib_version}" != ""
|
||||
Provides: bundled(cpp-httplib) = %{httplib_version}
|
||||
%else
|
||||
Provides: bundled(cpp-httplib)
|
||||
%endif
|
||||
|
||||
%description
|
||||
Zeal is a simple offline documentation browser inspired by Dash. It offers
|
||||
access to over 200 docsets covering various libraries and APIs.
|
||||
|
||||
* Quickly search documentation using a customisable global hotkey from any
|
||||
place in your workspace.
|
||||
* Search in multiple sets of documentation at once.
|
||||
* Don't be dependent on your internet connection.
|
||||
* Integrate Zeal with Emacs, Sublime Text, or Vim.
|
||||
|
||||
%prep
|
||||
%autosetup -n %{name}-%{version}
|
||||
|
||||
%build
|
||||
%cmake -GNinja
|
||||
%cmake_build
|
||||
|
||||
%install
|
||||
%cmake_install
|
||||
|
||||
%files
|
||||
%license COPYING
|
||||
%doc README.md
|
||||
%{_bindir}/zeal
|
||||
%{_datadir}/applications/org.zealdocs.zeal.desktop
|
||||
%{_datadir}/metainfo/org.zealdocs.zeal.appdata.xml
|
||||
%{_datadir}/icons/hicolor/*/apps/zeal*
|
||||
|
||||
%changelog
|
||||
* Mon Jun 01 2026 Zeal Release <release@zealdocs.org> - 0.0.0-1
|
||||
- Placeholder entry; the release automation regenerates this for each upload.
|
||||
Reference in New Issue
Block a user