16 Commits

Author SHA1 Message Date
Oleg Shparber 0dfb36c837 build(cmake): set CMP0218 to NEW (#1952) 2026-08-14 04:47:44 +03:00
Oleg Shparber ae4086f7a0 fix(util,registry): tolerate broken Info.plist (#1951)
Fixes #1949.
2026-08-13 22:24:25 +03:00
Oleg Shparber 25ad56c62e ci(github): revert github/codeql-action to v4 (#1948)
This reverts commit 25c0e915a7.
2026-08-05 01:07:18 +03:00
dependabot[bot] 25c0e915a7 ci(github): bump github/codeql-action from 4 to 4.37.4 (#1947) 2026-08-05 00:42:28 +03:00
Oleg Shparber 366d6f47c3 ci(github): fix nightly PPA package version 2026-07-31 19:40:03 +03:00
Oleg Shparber fc8da85fb1 fix(ui): replace IRC link with contact page 2026-07-29 14:04:02 +03:00
Oleg Shparber a6f03e9547 build(just): move release-push into a script 2026-07-29 13:57:44 +03:00
Oleg Shparber ac6eca8a9b ci(github): publish snap edge nightly 2026-07-29 02:48:23 +03:00
zealdocs-ci[bot] a716f6c170 chore: bump version to 0.9.2 2026-07-28 21:15:50 +00:00
Oleg Shparber 7a881b6921 chore: release v0.9.1 2026-07-28 23:54:46 +03:00
Oleg Shparber e6a3eb7afc build(tools): harden appdata bullet generation 2026-07-28 23:50:15 +03:00
Oleg Shparber 530fa44395 ci(github): install-test the MSI on Windows 2026-07-28 23:20:47 +03:00
Oleg Shparber c7bcf3d4b4 fix(wix): drop unusable Windows build launch condition 2026-07-28 23:12:23 +03:00
Oleg Shparber f854fcf8d7 ci(github): drop EOL questing from PPA series 2026-07-28 22:53:13 +03:00
Oleg Shparber cd12f8dcf5 ci(github): skip build-dep check in PPA source build 2026-07-28 22:42:20 +03:00
zealdocs-ci[bot] 5edfac529b chore: bump version to 0.9.1 2026-07-28 19:34:52 +00:00
16 changed files with 246 additions and 35 deletions
+5
View File
@@ -163,6 +163,11 @@ jobs:
if: matrix.config.publishArtifacts
run: cmake --build --preset ${{ matrix.config.buildPreset }} --target package
- name: Test MSI
if: matrix.config.publishArtifacts && matrix.config.configurePreset == 'release'
shell: pwsh
run: pwsh -NoProfile -File pkg/wix/tools/test-msi.ps1 -Install
- name: Upload ZIP Artifacts
if: matrix.config.publishArtifacts
uses: actions/upload-artifact@v7
+5 -1
View File
@@ -38,13 +38,17 @@ jobs:
next_version="${major}.${minor}.${next_patch}"
sed -i "/^project(/,/^)/ s/${version}/${next_version}/" CMakeLists.txt
# The nightly recipe reads this version via {debupstream}.
sed -i "1s/(1:[^-]*-/(1:${next_version}-/" pkg/ppa/debian/changelog
echo "NEXT_VERSION=${next_version}" >> $GITHUB_ENV
- name: Commit & Push
run: |
git config user.name "zealdocs-ci[bot]"
git config user.email "273095467+zealdocs-ci[bot]@users.noreply.github.com"
git add CMakeLists.txt
git add CMakeLists.txt pkg/ppa/debian/changelog
git diff --staged --quiet && echo "::error::Version bump failed, no changes detected." && exit 1
git commit -m "chore: bump version to ${{ env.NEXT_VERSION }}"
git push
+4 -2
View File
@@ -19,7 +19,7 @@ env:
# Ubuntu series as <codename>:<version> pairs. 24.04 (noble) is the floor; 22.04's
# Qt 6.2 is too old. <version> forms the ~ubuntu<version> suffix, <codename> is the
# changelog distribution. Launchpad rejects EOL or unknown series.
SERIES: "noble:24.04 questing:25.10 resolute:26.04"
SERIES: "noble:24.04 resolute:26.04"
PPA: "ppa:zealdocs/ppa"
jobs:
@@ -166,7 +166,9 @@ jobs:
# Guard so one rejected series doesn't abort the rest (the if-context
# suspends set -e). Flip `first` only on success, so the orig tarball
# still ships with the next series if this upload failed.
if ( cd "$work" && debuild --no-lintian -S "$tarball_opt" -k"$GPG_KEYID" -p"$HOME/.gpg-sign" ) \
# -d skips the build-dependency check: nothing is compiled here, and
# the dependencies are resolved on the Launchpad builders instead.
if ( cd "$work" && debuild --no-lintian -d -S "$tarball_opt" -k"$GPG_KEYID" -p"$HOME/.gpg-sign" ) \
&& dput "$PPA" "$src_root/zeal_${file_ver}_source.changes"; then
first=false
else
+1 -1
View File
@@ -16,7 +16,7 @@ on:
- beta
- edge
schedule:
- cron: '0 0 * * 2'
- cron: '2 4 * * *'
permissions: {}
+9 -3
View File
@@ -9,21 +9,27 @@ setup_homebrew()
# CMake options.
set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
set(CMAKE_DISABLE_SOURCE_CHANGES ON)
set(CMAKE_ERROR_DEPRECATED TRUE)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
if(POLICY CMP0218)
cmake_policy(SET CMP0218 NEW)
cmake_diagnostic(SET CMD_DEPRECATED FATAL_ERROR)
else()
set(CMAKE_ERROR_DEPRECATED TRUE)
endif()
# VERSION is the next development version, bumped automatically after each release.
# ZEAL_RELEASE_VERSION is the last released version, updated by `just release-prepare`.
# When both match, the build is treated as a release; otherwise it's a dev build.
# The `# x-version` marker on each version line acts as a sed anchor for the bump.
project(Zeal
VERSION 0.9.0 # x-version
VERSION 0.9.2 # x-version
DESCRIPTION "A simple documentation browser."
HOMEPAGE_URL "https://zealdocs.org"
LANGUAGES CXX
)
set(ZEAL_RELEASE_VERSION "0.9.0") # x-version
set(ZEAL_RELEASE_VERSION "0.9.1") # x-version
include(ZealHelpers)
@@ -49,6 +49,14 @@
<id>zeal.desktop</id>
</provides>
<releases>@ZEAL_APPSTREAM_DEV_RELEASE@
<release date="2026-07-28" version="0.9.1" type="stable">
<description>
<ul>
<li>Maintenance release with no changes for Linux users</li>
</ul>
</description>
<url>https://github.com/zealdocs/zeal/releases/tag/v0.9.1</url>
</release>
<release date="2026-07-28" version="0.9.0" type="stable">
<description>
<ul>
+1 -11
View File
@@ -98,17 +98,7 @@ release-prepare version:
# Maintainer use only.
[group('release')]
release-push version remote="origin":
git add assets/freedesktop/org.zealdocs.zeal.appdata.xml.in CMakeLists.txt
git diff --staged --quiet || git commit -m "chore: release v{{version}}"
# Annotated: `git describe` without `--tags` ignores lightweight tags, and
# the AUR zeal-git package derives its version that way.
git rev-parse -q --verify refs/tags/v{{version}} > /dev/null || git tag -a v{{version}} -m "v{{version}}"
git push {{remote}} HEAD
gh release create v{{version}} --draft --notes-file build/release-notes.md \
--repo "$(git remote get-url {{remote}} | sed -E 's|^.*github\.com[:/]||; s|\.git$||')"
git push {{remote}} v{{version}}
@echo "Pushed v{{version}} with draft release. CI will upload artifacts."
@echo "Approve the 'release' environment to let the Windows jobs run."
bash tools/release-push.sh {{version}} {{remote}}
# Delete the draft release, the remote tag, and the local tag for a version,
# undoing `just release-push` so it can be run again. Refuses to delete a
+7 -5
View File
@@ -1,8 +1,10 @@
zeal (1:0.8.2-0ubuntu1) UNRELEASED; urgency=medium
zeal (1:0.9.2-0ubuntu1) UNRELEASED; urgency=medium
* Placeholder entry. The real version and target series are set at upload
time by debian/rules consumers: the release workflow rewrites this with
`dch` per series, and the Launchpad nightly recipe derives the version
from its `deb-version` template. See pkg/ppa/README.md.
* Placeholder entry, except for the version above. Releases rewrite this file
with `dch` per series in publish-ppa.yaml, but the Launchpad nightly recipe
reads that version through `{debupstream}` and appends `-0~{revtime}`. It
must name the next, unreleased version, so a nightly sorts above the last
release and below the next one. Keep it in step with CMakeLists.txt;
chore-version-bump.yaml does that on release. See pkg/ppa/README.md.
-- Zeal Release <release@zealdocs.org> Thu, 01 May 2025 00:00:00 +0000
-4
View File
@@ -14,10 +14,6 @@
<MajorUpgrade Schedule="afterInstallInitialize" DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." />
<Launch Condition="VersionNT64" Message="This package can only be installed on 64-bit versions of Windows." />
<!-- Qt supports Windows 10 version 1809 (build 17763) and later. Repair and removal of an
existing installation remain possible on older builds, while an upgrade is refused
there, since the newer version would not start. -->
<Launch Condition="Installed OR WindowsBuild &gt;= 17763" Message="[ProductName] requires Windows 10 version 1809 or later." />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALL_ROOT" />
-1
View File
@@ -104,7 +104,6 @@ Docset::Docset(QString path)
if (plist.hasError()) {
qCWarning(log, "Failed to parse 'Info.plist' for docset at '%s'.", qPrintable(m_path));
return;
}
if (m_name.isEmpty()) {
+1 -1
View File
@@ -79,7 +79,7 @@ Copyright &amp;copy; Oleg Shparber and other contributors, 2013-2026.
&lt;br&gt;
&lt;a href=&quot;https://zealdocs.org&quot;&gt;zealdocs.org&lt;/a&gt;
&lt;br&gt;
&lt;a href=&quot;ircs://irc.libera.chat:6697/zealdocs&quot;&gt;#zealdocs&lt;/a&gt; on &lt;a href=&quot;https://libera.chat&quot;&gt;Libera Chat&lt;/a&gt;
&lt;a href=&quot;https://go.zealdocs.org/l/contact&quot;&gt;Get in touch&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
Zeal is an open source software available under the terms of the General Public License version 3 (&lt;a href=&quot;https://www.gnu.org/copyleft/gpl.html&quot;&gt;GPLv3&lt;/a&gt;) or later.
&lt;br&gt;&lt;br&gt;
+2 -1
View File
@@ -22,7 +22,8 @@ bool Plist::read(const QString &fileName)
return false;
}
QXmlStreamReader xml(&file);
// Some docsets ship an Info.plist with whitespace before the XML declaration.
QXmlStreamReader xml(file.readAll().trimmed());
while (!xml.atEnd()) {
const QXmlStreamReader::TokenType token = xml.readNext();
+6
View File
@@ -6,6 +6,12 @@ target_link_libraries(fuzzy_test PRIVATE Util Qt6::Test)
zeal_add_test(fuzzy_test)
# Property list tests
add_executable(plist_test plist_test.cpp)
target_link_libraries(plist_test PRIVATE Util Qt6::Test)
zeal_add_test(plist_test)
# SQLite Statement tests
add_executable(statement_test statement_test.cpp)
target_link_libraries(statement_test PRIVATE Util Qt6::Test)
+116
View File
@@ -0,0 +1,116 @@
// Copyright (C) Oleg Shparber, et al. <https://zealdocs.org>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "../plist.h"
#include <QtTest>
using namespace Zeal::Util;
class PlistTest : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void testReadsKeys();
void testLeadingNewline();
void testLeadingIndentation();
void testByteOrderMark();
void testMalformedContent();
void testMissingFile();
private:
QString writePlist(const QByteArray &data);
QTemporaryDir m_dir;
int m_counter = 0;
};
namespace {
const QByteArray Body = QByteArrayLiteral("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\""
" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
"<plist version=\"1.0\">\n"
" <dict>\n"
" <key>CFBundleName</key>\n"
" <string>Android KTX</string>\n"
" <key>isDashDocset</key>\n"
" <true/>\n"
" <key>isJavaScriptEnabled</key>\n"
" <false/>\n"
" </dict>\n"
"</plist>\n");
} // namespace
QString PlistTest::writePlist(const QByteArray &data)
{
const QString path = m_dir.filePath(QStringLiteral("Info%1.plist").arg(++m_counter));
QFile file(path);
if (!file.open(QIODevice::WriteOnly) || file.write(data) != data.size()) {
return {};
}
return path;
}
void PlistTest::initTestCase()
{
QVERIFY(m_dir.isValid());
}
void PlistTest::testReadsKeys()
{
Plist plist;
QVERIFY(plist.read(writePlist(Body)));
QVERIFY(!plist.hasError());
QCOMPARE(plist.value(QStringLiteral("CFBundleName")), QVariant(QStringLiteral("Android KTX")));
QCOMPARE(plist.value(QStringLiteral("isDashDocset")), QVariant(true));
QCOMPARE(plist.value(QStringLiteral("isJavaScriptEnabled")), QVariant(false));
}
// See: https://github.com/zealdocs/zeal/issues/1949
void PlistTest::testLeadingNewline()
{
Plist plist;
QVERIFY(plist.read(writePlist(QByteArrayLiteral("\n") + Body)));
QVERIFY(!plist.hasError());
QCOMPARE(plist[QStringLiteral("CFBundleName")].toString(), QStringLiteral("Android KTX"));
}
void PlistTest::testLeadingIndentation()
{
Plist plist;
QVERIFY(plist.read(writePlist(QByteArrayLiteral("\n ") + Body)));
QVERIFY(!plist.hasError());
QCOMPARE(plist[QStringLiteral("CFBundleName")].toString(), QStringLiteral("Android KTX"));
}
void PlistTest::testByteOrderMark()
{
Plist plist;
QVERIFY(plist.read(writePlist(QByteArrayLiteral("\xEF\xBB\xBF") + Body)));
QVERIFY(!plist.hasError());
QCOMPARE(plist[QStringLiteral("CFBundleName")].toString(), QStringLiteral("Android KTX"));
}
void PlistTest::testMalformedContent()
{
const QString path = writePlist(QByteArrayLiteral("<plist><dict><key>CFBundleName</key>"));
QVERIFY(!path.isEmpty());
Plist plist;
QVERIFY(!plist.read(path));
QVERIFY(plist.hasError());
}
void PlistTest::testMissingFile()
{
Plist plist;
QVERIFY(!plist.read(m_dir.filePath(QStringLiteral("Missing.plist"))));
QVERIFY(plist.hasError());
}
QTEST_GUILESS_MAIN(PlistTest)
#include "plist_test.moc"
+23 -5
View File
@@ -75,20 +75,38 @@ would notice and care about. There is no target count: include a change \
only if it earns its place, and leave out cosmetic trivia, internal \
refactors, build/CI/dependency commits, and Windows-only or macOS-only \
changes. If you cannot tell a change's user-visible effect from its diff, \
omit it. Write each as one <li>...</li> bullet that starts with Added, \
omit it. Write each as one plain-text line that starts with Added, \
Changed, Removed, Deprecated, or Fixed, grouped in that order (Added first, \
Fixed last) and most significant first within each group. Keep each bullet \
Fixed last) and most significant first within each group. Keep each line \
concrete and specific about what changed, in plain language a non-developer \
understands; a short clause on why it helps is welcome. No trailing period. \
Avoid AI cliches (em-dashes, 'delve into', 'leverage', 'robust', 'ensure', \
'seamless', 'comprehensive'). Output the <li> lines only: no preamble, \
commentary, or wrapping <ul>." \
'seamless', 'comprehensive'). Output the lines only: no markup, numbering, \
bullet characters, preamble, or commentary. If nothing qualifies, output \
exactly one line reading: Maintenance release with no changes for Linux \
users" \
--allowedTools "Bash(git log:*) Bash(git show:*) Bash(git diff:*)" \
< /dev/null \
> build/appdata-bullets.txt
today=$(date -u +%Y-%m-%d)
indented=$(sed 's/^[[:space:]]*//; s/^/ /' build/appdata-bullets.txt)
# Bullets arrive as plain text, one per line. Wrapping them here, rather than
# asking for markup, keeps stray commentary from producing invalid XML.
indented=$(while read -r line; do
[ -n "$line" ] || continue
# Ampersands are escaped as \& because bash 5.2+ reads a bare & in the
# replacement as the matched text.
line=${line//&/\&amp;}
line=${line//</\&lt;}
line=${line//>/\&gt;}
printf ' <li>%s</li>\n' "$line"
done < build/appdata-bullets.txt)
if [ -z "$indented" ]; then
echo "build/appdata-bullets.txt produced no bullets; aborting." >&2
exit 1
fi
cat > build/release-block.xml <<EOF
<release date="${today}" version="${VERSION}" type="stable">
<description>
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
#
# release-push.sh - Commit, tag, and push a prepared release.
#
# SPDX-FileCopyrightText: Oleg Shparber, et al. <https://zealdocs.org>
# SPDX-License-Identifier: MIT
#
# Commits the appdata and version bump left by release-prepare.sh, pushes the
# branch, creates the draft GitHub release from the generated notes, then tags
# and pushes the tag, which starts the build CI. The commit and tag steps are
# skipped if already done, so the same version can go to a fork first and then
# be promoted to upstream.
#
# Usage: tools/release-push.sh <version> [remote]
#
set -euo pipefail
trap 'echo "ERROR: failed at line ${LINENO}: ${BASH_COMMAND}" >&2' ERR
if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then
echo "Usage: $0 <version> [remote]" >&2
exit 2
fi
VERSION="$1"
REMOTE="${2:-origin}"
TAG="v${VERSION}"
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Invalid version: '${VERSION}'. Expected major.minor.patch (e.g. 0.9.0)." >&2
exit 2
fi
repo=$(git remote get-url "$REMOTE" | sed -E 's|^.*github\.com[:/]||; s|\.git$||')
git add assets/freedesktop/org.zealdocs.zeal.appdata.xml.in CMakeLists.txt
if git diff --staged --quiet; then
echo "Nothing to commit; ${TAG} content is already committed."
else
git commit -m "chore: release ${TAG}"
fi
# Annotated: `git describe` without `--tags` ignores lightweight tags, and the
# AUR zeal-git package derives its version that way.
if git rev-parse -q --verify "refs/tags/${TAG}" > /dev/null; then
echo "Tag ${TAG} already exists; reusing it."
else
git tag -a "$TAG" -m "$TAG"
fi
git push "$REMOTE" HEAD
# Created before the tag is pushed, so the draft is in place when CI starts.
gh release create "$TAG" --draft --notes-file build/release-notes.md --repo "$repo"
git push "$REMOTE" "$TAG"
echo "Pushed ${TAG} to ${repo}. CI will build, upload, and publish."