From 0ebe48fca0b1a787e2f4ac74f97c410636ad6b16 Mon Sep 17 00:00:00 2001 From: Joel Winarske Date: Fri, 14 Aug 2026 07:33:33 -0700 Subject: [PATCH 1/3] chore: bump hooks to 2.x, move repository, fix C++ test gate Raise the pub score from 140/160 to 150/160 and clear the remaining publishing gaps. - Bump `hooks` ^1.0.2 -> ^2.1.0 and `code_assets` ^1.0.0 -> ^1.2.1. The 1.x bound was pinning code_assets to 1.0.0 and holding native_toolchain_c and record_use at 1.x-era versions. The build hook API is unchanged across the major bump, so hook/build.dart needed no edits; both packages still require only sdk >=3.10.0, so the declared environment constraint is unaffected. - Point `repository` and `issue_tracker` at github.com/flatpak-minimal/appstream_dart. pana verifies the URL by cloning it and comparing the pubspec on the default branch, so this clears its check only once pushed there. - Fix scripts/test.sh passing -DBUILD_TESTING=ON, a flag the build ignores; the gate has been -DAPPSTREAM_BUILD_TESTS=ON since 0.2.2. The C++ suite was never configured or rebuilt, so ctest ran a stale binary that still linked against a removed gtest 1.15.2 and reported 149 spurious failures. With the correct flag the suite builds against system gtest 1.17.0 and passes 149/149. - Apply clang-tidy fixes in AppStreamParser and XmlScanner: explicit parentheses in mixed */+ accumulator arithmetic, contains() in place of a find() != npos membership test, and consistent braces across the provides if/else chain. All are semantics-preserving; clang-tidy now reports no warnings across src/. Verified: dart analyze clean, dart format clean, clang-tidy clean, clang-format applied last, 149/149 C++ tests and 45/45 Dart tests pass. --- CHANGELOG.md | 16 +++++++++++++++- pubspec.yaml | 8 ++++---- scripts/test.sh | 2 +- src/AppStreamParser.cpp | 21 +++++++++++---------- src/XmlScanner.cpp | 10 +++++----- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbbabe3..e738f1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,21 @@ `THIRD_PARTY_LICENSES` cataloging every direct dependency. - LICENSE file replaced with the compact SPDX-standard Apache-2.0 text. - Dependency bumps: `sqlite3` ^2.4.0 → ^3.3.1, `lints` ^4.0.0 → ^6.1.0 - (applies to both the main package and the Flutter example). + (applies to both the main package and the Flutter example), + `hooks` ^1.0.2 → ^2.1.0 and `code_assets` ^1.0.0 → ^1.2.1. The build + hook API is unchanged between `hooks` 1.x and 2.x, so `hook/build.dart` + needed no edits; the bump also unpins `native_toolchain_c` and + `record_use` from their 1.x-era versions. +- Repository moved to `github.com/flatpak-minimal/appstream_dart`; + `repository` and `issue_tracker` updated to match. +- Fix `scripts/test.sh` passing `-DBUILD_TESTING=ON`, which the CMake build + ignores — the gate has been `-DAPPSTREAM_BUILD_TESTS=ON` since 0.2.2. The + C++ suite was therefore never configured or rebuilt, and `ctest` silently + ran whatever stale binary was left in the build directory. +- clang-tidy cleanups in `AppStreamParser` and `XmlScanner`: explicit + parentheses in mixed `*`/`+` accumulator arithmetic, `contains()` in place + of a `find() != npos` membership test, and consistent braces across the + `provides` if/else chain. - Public API documentation: add dartdoc comments to all exported classes, fields, and constructors in `lib/appstream.dart`, `lib/src/database/database.dart`, and `lib/src/database/tables.dart`. diff --git a/pubspec.yaml b/pubspec.yaml index b2f2097..1c0656b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,8 +3,8 @@ description: >- High-performance AppStream XML parser with C++23 FFI bridge. Streams catalog metadata into SQLite with Drift ORM and FTS5 search. version: 0.3.0 -repository: https://github.com/meta-flutter/appstream -issue_tracker: https://github.com/meta-flutter/appstream/issues +repository: https://github.com/flatpak-minimal/appstream_dart +issue_tracker: https://github.com/flatpak-minimal/appstream_dart/issues topics: - appstream - flathub @@ -27,9 +27,9 @@ environment: dependencies: drift: ^2.22.0 - code_assets: ^1.0.0 + code_assets: ^1.2.1 ffi: ^2.1.0 - hooks: ^1.0.2 + hooks: ^2.1.0 http: ^1.2.0 path: ^1.9.0 sqlite3: ^3.3.1 diff --git a/scripts/test.sh b/scripts/test.sh index 446b0fd..dc4b81d 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -36,7 +36,7 @@ if [[ -z "${SKIP_CXX:-}" ]]; then cmake -S . -B "$BUILD_DIR" \ "${GEN_ARGS[@]}" \ -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ - -DBUILD_TESTING=ON \ + -DAPPSTREAM_BUILD_TESTS=ON \ -DENABLE_SANITIZER="$SANITIZER" \ -DENABLE_COVERAGE="$COVERAGE" \ -DENABLE_BENCHMARKS="$BENCHMARKS" diff --git a/src/AppStreamParser.cpp b/src/AppStreamParser.cpp index ae4c139..d426e06 100644 --- a/src/AppStreamParser.cpp +++ b/src/AppStreamParser.cpp @@ -34,7 +34,7 @@ static int convertToInt(const std::string_view sv) { for (; i < sv.size(); ++i) { if (sv[i] < '0' || sv[i] > '9') break; - result = result * 10 + (sv[i] - '0'); + result = (result * 10) + (sv[i] - '0'); } return neg ? -result : result; } @@ -44,7 +44,7 @@ static size_t convertToSizeT(const std::string_view sv) { for (const char c : sv) { if (c < '0' || c > '9') break; - result = result * 10 + static_cast(c - '0'); + result = (result * 10) + static_cast(c - '0'); } return result; } @@ -54,7 +54,7 @@ static std::string unixEpochToISO8601(const std::string_view epochStr) { for (char c : epochStr) { if (c < '0' || c > '9') break; - epoch = epoch * 10 + (c - '0'); + epoch = (epoch * 10) + (c - '0'); } const auto t = static_cast(epoch); std::tm tm{}; @@ -76,7 +76,7 @@ void AppStreamParser::mmapFile(const std::string &filename, void *&data, size_t spdlog::error("Failed to open: {}", filename); return; } - struct stat sb {}; + struct stat sb{}; if (fstat(fd, &sb) == -1) { close(fd); return; @@ -751,20 +751,21 @@ AppStreamParser::doParse(XmlScanner &scanner, const std::string &language, Compo break; } if (insideProvides) { - if (tag == "binary"sv) + if (tag == "binary"sv) { currentComponent.provides.binaries.push_back(std::move(textAccum)); - else if (tag == "library"sv) + } else if (tag == "library"sv) { currentComponent.provides.libraries.push_back(std::move(textAccum)); - else if (tag == "mediatype"sv) + } else if (tag == "mediatype"sv) { currentComponent.provides.mediatypes.push_back(std::move(textAccum)); - else if (tag == "id"sv) + } else if (tag == "id"sv) { currentComponent.provides.ids.push_back(std::move(textAccum)); - else if (tag == "dbus"sv) { + } else if (tag == "dbus"sv) { currentComponent.provides.dbus.emplace_back(std::move(currentDbusType), std::move(textAccum)); currentDbusType.clear(); - } else if (tag == "firmware"sv) + } else if (tag == "firmware"sv) { currentComponent.provides.firmware.push_back(std::move(textAccum)); + } currentElement.clear(); textAccum.clear(); break; diff --git a/src/XmlScanner.cpp b/src/XmlScanner.cpp index db214ae..6186885 100644 --- a/src/XmlScanner.cpp +++ b/src/XmlScanner.cpp @@ -113,7 +113,7 @@ void XmlScanner::skipComment() { } bool XmlScanner::containsEntity(std::string_view sv) noexcept { - return sv.find('&') != std::string_view::npos; + return sv.contains('&'); } void XmlScanner::decodeEntities(std::string_view src) { @@ -157,11 +157,11 @@ void XmlScanner::decodeEntities(std::string_view src) { for (size_t i = 2; i < entity.size() && valid; ++i) { char c = entity[i]; if (c >= '0' && c <= '9') - cp = cp * 16 + static_cast(c - '0'); + cp = (cp * 16) + static_cast(c - '0'); else if (c >= 'a' && c <= 'f') - cp = cp * 16 + static_cast(c - 'a' + 10); + cp = (cp * 16) + static_cast(c - 'a' + 10); else if (c >= 'A' && c <= 'F') - cp = cp * 16 + static_cast(c - 'A' + 10); + cp = (cp * 16) + static_cast(c - 'A' + 10); else valid = false; if (cp > 0x10FFFF) @@ -170,7 +170,7 @@ void XmlScanner::decodeEntities(std::string_view src) { } else { for (size_t i = 1; i < entity.size() && valid; ++i) { if (entity[i] >= '0' && entity[i] <= '9') - cp = cp * 10 + static_cast(entity[i] - '0'); + cp = (cp * 10) + static_cast(entity[i] - '0'); else valid = false; if (cp > 0x10FFFF) From 411f2d0cb189ddae6b09e47a72bbfa2ee5958ab0 Mon Sep 17 00:00:00 2001 From: Joel Winarske Date: Fri, 14 Aug 2026 07:47:49 -0700 Subject: [PATCH 2/3] chore: release 0.4.0 0.3.0 is already published on pub.dev and its versions are immutable, so this work ships as a new release rather than a re-publish. 0.4.0 rather than 0.3.1: raising `hooks` to ^2.1.0 is resolution-breaking for any consumer pinned to 1.x, and in 0.x semver a breaking change bumps the minor. The public Dart API is unchanged. Move the entries added for this work into a 0.4.0 section and restore the 0.3.0 section to what was actually published. --- CHANGELOG.md | 30 ++++++++++++++++++------------ pubspec.yaml | 2 +- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e738f1a..439a6c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,25 +1,31 @@ -## 0.3.0 +## 0.4.0 -- Licensing: adopt SPDX license headers (`SPDX-License-Identifier` / - `SPDX-FileCopyrightText`) across all source files; add - `THIRD_PARTY_LICENSES` cataloging every direct dependency. -- LICENSE file replaced with the compact SPDX-standard Apache-2.0 text. -- Dependency bumps: `sqlite3` ^2.4.0 → ^3.3.1, `lints` ^4.0.0 → ^6.1.0 - (applies to both the main package and the Flutter example), - `hooks` ^1.0.2 → ^2.1.0 and `code_assets` ^1.0.0 → ^1.2.1. The build - hook API is unchanged between `hooks` 1.x and 2.x, so `hook/build.dart` - needed no edits; the bump also unpins `native_toolchain_c` and - `record_use` from their 1.x-era versions. +- **Breaking (dependency resolution):** `hooks` ^1.0.2 → ^2.1.0 and + `code_assets` ^1.0.0 → ^1.2.1. Consumers pinned to `hooks` 1.x will no + longer resolve. The build hook API is unchanged between `hooks` 1.x and + 2.x, so `hook/build.dart` needed no edits and the public Dart API is + untouched; the bump also unpins `native_toolchain_c` and `record_use` + from their 1.x-era versions. The SDK constraint stays `^3.10.0`. - Repository moved to `github.com/flatpak-minimal/appstream_dart`; `repository` and `issue_tracker` updated to match. - Fix `scripts/test.sh` passing `-DBUILD_TESTING=ON`, which the CMake build ignores — the gate has been `-DAPPSTREAM_BUILD_TESTS=ON` since 0.2.2. The C++ suite was therefore never configured or rebuilt, and `ctest` silently - ran whatever stale binary was left in the build directory. + ran whatever stale binary was left in the build directory. CI already + passed the correct flag, so only local runs were affected. - clang-tidy cleanups in `AppStreamParser` and `XmlScanner`: explicit parentheses in mixed `*`/`+` accumulator arithmetic, `contains()` in place of a `find() != npos` membership test, and consistent braces across the `provides` if/else chain. + +## 0.3.0 + +- Licensing: adopt SPDX license headers (`SPDX-License-Identifier` / + `SPDX-FileCopyrightText`) across all source files; add + `THIRD_PARTY_LICENSES` cataloging every direct dependency. +- LICENSE file replaced with the compact SPDX-standard Apache-2.0 text. +- Dependency bumps: `sqlite3` ^2.4.0 → ^3.3.1, `lints` ^4.0.0 → ^6.1.0 + (applies to both the main package and the Flutter example). - Public API documentation: add dartdoc comments to all exported classes, fields, and constructors in `lib/appstream.dart`, `lib/src/database/database.dart`, and `lib/src/database/tables.dart`. diff --git a/pubspec.yaml b/pubspec.yaml index 1c0656b..9542132 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: appstream_dart description: >- High-performance AppStream XML parser with C++23 FFI bridge. Streams catalog metadata into SQLite with Drift ORM and FTS5 search. -version: 0.3.0 +version: 0.4.0 repository: https://github.com/flatpak-minimal/appstream_dart issue_tracker: https://github.com/flatpak-minimal/appstream_dart/issues topics: From 1b0a683376c6a27de0742414efad4803d69f16be Mon Sep 17 00:00:00 2001 From: Joel Winarske Date: Fri, 14 Aug 2026 07:53:08 -0700 Subject: [PATCH 3/3] fix: restore clang-format 18 formatting, apply Dart 3.13 formatting Both CI format jobs failed on toolchain version drift, not on logic. clang-format: CI installs Ubuntu's clang-format 18, but the local run used clang-format 22, which rewrites `struct stat sb {}` to `sb{}`. Version 22 then reports that same line as a violation under --dry-run --Werror, so it cannot be satisfied by its own -i output. Version 18 accepts only the original spacing, which is what CI enforces and what was green on main, so line 79 is restored. The clang-tidy brace changes elsewhere in the file are accepted by both versions. dart format: CI's setup-dart resolves to Dart 3.13.0 while the local SDK is 3.12.2, and 3.13 collapses the `test(...)` call in appstream_parse_fallback_test.dart differently. Formatted with 3.13 to match the gate. Note that 3.12.2 disagrees and will want to revert this, so local formatting needs a 3.13 SDK until CI pins a version. Verified against CI's exact toolchain (clang-format 18.1.8, Dart 3.13.0): both format gates clean, dart analyze --fatal-infos clean, 149/149 C++ and 45/45 Dart tests pass. --- src/AppStreamParser.cpp | 2 +- test/appstream_parse_fallback_test.dart | 72 ++++++++++++------------- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/AppStreamParser.cpp b/src/AppStreamParser.cpp index d426e06..25e4562 100644 --- a/src/AppStreamParser.cpp +++ b/src/AppStreamParser.cpp @@ -76,7 +76,7 @@ void AppStreamParser::mmapFile(const std::string &filename, void *&data, size_t spdlog::error("Failed to open: {}", filename); return; } - struct stat sb{}; + struct stat sb {}; if (fstat(fd, &sb) == -1) { close(fd); return; diff --git a/test/appstream_parse_fallback_test.dart b/test/appstream_parse_fallback_test.dart index d9ddb9f..db0a0c0 100644 --- a/test/appstream_parse_fallback_test.dart +++ b/test/appstream_parse_fallback_test.dart @@ -21,14 +21,12 @@ bool _initNative() { final _nativeReady = _initNative(); void main() { - test( - 'parseToSqlite falls back when isolate spawn fails', - () async { - final tempDir = await Directory.systemTemp.createTemp('appstream_test_'); - final xmlPath = '${tempDir.path}/appstream.xml'; - final dbPath = '${tempDir.path}/catalog.db'; + test('parseToSqlite falls back when isolate spawn fails', () async { + final tempDir = await Directory.systemTemp.createTemp('appstream_test_'); + final xmlPath = '${tempDir.path}/appstream.xml'; + final dbPath = '${tempDir.path}/catalog.db'; - const xml = ''' + const xml = ''' com.example.Test @@ -38,42 +36,40 @@ void main() { '''; - try { - await File(xmlPath).writeAsString(xml); + try { + await File(xmlPath).writeAsString(xml); - final events = await Appstream.parseToSqlite( - xmlPath: xmlPath, - dbPath: dbPath, - workerSpawner: - ( - void Function(Map) _, - Map _, - ) async { - throw StateError('forced spawn failure'); - }, - ).toList().timeout(const Duration(seconds: 30)); + final events = await Appstream.parseToSqlite( + xmlPath: xmlPath, + dbPath: dbPath, + workerSpawner: + ( + void Function(Map) _, + Map _, + ) async { + throw StateError('forced spawn failure'); + }, + ).toList().timeout(const Duration(seconds: 30)); - final failures = events.whereType().toList(); - expect( - failures, - isEmpty, - reason: 'Fallback worker should complete parse without failures', - ); + final failures = events.whereType().toList(); + expect( + failures, + isEmpty, + reason: 'Fallback worker should complete parse without failures', + ); - final done = events.whereType().toList(); - expect(done, hasLength(1)); - expect(done.single.count, greaterThanOrEqualTo(1)); + final done = events.whereType().toList(); + expect(done, hasLength(1)); + expect(done.single.count, greaterThanOrEqualTo(1)); - expect(File(dbPath).existsSync(), isTrue); - expect(File(dbPath).lengthSync(), greaterThan(0)); - } finally { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } + expect(File(dbPath).existsSync(), isTrue); + expect(File(dbPath).lengthSync(), greaterThan(0)); + } finally { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); } - }, - skip: _nativeReady ? null : 'native library not available', - ); + } + }, skip: _nativeReady ? null : 'native library not available'); test( 'parseToSqlite emits ParseFailed or empty ParseDone on loosely-malformed XML',