diff --git a/README.md b/README.md index a360cff..bef1930 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ updates, also add `packages//upstream.json` (see below). | `pullrun` | [pullrun/pullrun](https://github.com/pullrun/pullrun) | `curl -fsSL https://github.com/pullrun/pullrun/raw/main/install.sh \| bash` | | `gleam-preview` | [gleam-lang/gleam](https://github.com/gleam-lang/gleam) (prereleases) | [install docs](https://gleam.run/getting-started/installing/) / release tarballs | | `zed-preview` | [zed-industries/zed](https://github.com/zed-industries/zed) (preview channel) | [official Linux installer](https://zed.dev/docs/linux) / `zed-linux-x86_64.tar.gz` | +| `pulp` | [cheywood/Pulp](https://gitlab.gnome.org/cheywood/Pulp) | [Flathub](https://flathub.org/apps/details/org.gnome.gitlab.cheywood.Pulp) | ## Usage @@ -47,6 +48,7 @@ nix build .#whetuu # linux + darwin (all four systems) nix build .#pullrun # linux + darwin (all four systems) nix build .#gleam-preview # linux + darwin (all four systems) nix build .#zed-preview # x86_64-linux only +nix build .#pulp # linux only (GNOME/GTK4) # run without installing nix run .#vit -- --help @@ -58,6 +60,7 @@ nix run .#whetuu -- --version nix run .#pullrun -- --version nix run .#gleam-preview -- --version nix run .#zed-preview -- --version +nix run .#pulp # install into your profile nix profile install .#vit @@ -68,6 +71,7 @@ nix profile install .#whetuu nix profile install .#pullrun nix profile install .#gleam-preview nix profile install .#zed-preview +nix profile install .#pulp ``` ## Updating packages @@ -108,6 +112,21 @@ Supported types: `npm-github` packages are expected to use `buildNpmPackage` + `fetchFromGitHub` with a `version` field and optional vendored `package-lock.json`. +**gitlab-tag** — tagged source packages on GitLab (including GNOME GitLab): + +```json +{ + "type": "gitlab-tag", + "domain": "gitlab.gnome.org", + "gitlab": "owner/repo", + "tag_prefix": "" +} +``` + +Tracks the newest tag matching `{tag_prefix}` + numeric version (CalVer like +`2026.4` or semver). Refreshes `version` and the `fetchFromGitLab` `hash`. +Used by `pulp`. + **github-unstable** — projects without release tags (track branch tip): ```json @@ -339,3 +358,22 @@ By hand: nix hash convert --hash-algo sha256 --to sri ``` 3. `nix build .#gleam-preview` and smoke-test `./result/bin/gleam-preview --version`. + +### Manual steps (pulp) + +`pulp` is a GNOME RSS reader built from source (Meson + Python + GTK4). Prefer +the updater: + +```bash +./scripts/update-packages.sh pulp +``` + +By hand: + +1. Bump `version` in `packages/pulp/default.nix` (e.g. `2026.4`). +2. Prefetch the source hash: + ```bash + nix-prefetch-url --unpack "https://gitlab.gnome.org/cheywood/Pulp/-/archive/X.Y/Pulp-X.Y.tar.gz" + nix hash convert --hash-algo sha256 --to sri + ``` +3. Paste the SRI hash into `fetchFromGitLab.hash`, then `nix build .#pulp`. diff --git a/flake.nix b/flake.nix index 5ea1bab..8292f69 100644 --- a/flake.nix +++ b/flake.nix @@ -53,6 +53,10 @@ # Official preview tarball is only fetched for x86_64-linux. zed-preview = pkgs.callPackage ./packages/zed-preview { }; } + // pkgs.lib.optionalAttrs (pkgs.lib.hasSuffix "-linux" system) { + # GNOME/GTK4 RSS reader; Linux only (WebKitGTK + libadwaita). + pulp = pkgs.callPackage ./packages/pulp { }; + } ); # Convenience: `nix run .#vit -- --help`, `nix run .#spinel -- --help` @@ -107,6 +111,12 @@ program = "${self.packages.${system}.zed-preview}/bin/zed-preview"; }; } + // nixpkgs.lib.optionalAttrs (nixpkgs.lib.hasSuffix "-linux" system) { + pulp = { + type = "app"; + program = "${self.packages.${system}.pulp}/bin/pulp"; + }; + } ); }; } diff --git a/packages/pulp/default.nix b/packages/pulp/default.nix new file mode 100644 index 0000000..d7cf552 --- /dev/null +++ b/packages/pulp/default.nix @@ -0,0 +1,79 @@ +{ + lib, + python3, + fetchFromGitLab, + meson, + ninja, + pkg-config, + gobject-introspection, + wrapGAppsHook4, + appstream-glib, + desktop-file-utils, + blueprint-compiler, + glib, + gtk4, + libadwaita, + libsecret, + webkitgtk_6_0, +}: + +python3.pkgs.buildPythonApplication (finalAttrs: { + pname = "pulp"; + version = "2026.4"; + # Built with meson, not a Python packaging format. + pyproject = false; + + src = fetchFromGitLab { + domain = "gitlab.gnome.org"; + owner = "cheywood"; + repo = "Pulp"; + tag = finalAttrs.version; + hash = "sha256-jw7tGA9baSSJEspOGQq2VIzPDHWc1PNv5gBRtSz4d/w="; + }; + + nativeBuildInputs = [ + meson + ninja + pkg-config + gobject-introspection + wrapGAppsHook4 + appstream-glib + desktop-file-utils + blueprint-compiler + # glib-compile-schemas / gtk-update-icon-cache (meson post_install). + glib + gtk4 + ]; + + buildInputs = [ + glib + gtk4 + libadwaita + libsecret + webkitgtk_6_0 + ]; + + dependencies = with python3.pkgs; [ + pygobject3 + requests + beautifulsoup4 + cairosvg + lxml + pillow + ]; + + # Prevent double wrapping. + dontWrapGApps = true; + preFixup = '' + makeWrapperArgs+=("''${gappsWrapperArgs[@]}") + ''; + + meta = { + description = "RSS reader focused on clearing a large volume of articles efficiently"; + homepage = "https://gitlab.gnome.org/cheywood/Pulp"; + changelog = "https://gitlab.gnome.org/cheywood/Pulp/-/blob/${finalAttrs.version}/CHANGELOG.md"; + license = lib.licenses.gpl3Plus; + mainProgram = "pulp"; + platforms = lib.platforms.linux; + }; +}) diff --git a/packages/pulp/upstream.json b/packages/pulp/upstream.json new file mode 100644 index 0000000..b4aafef --- /dev/null +++ b/packages/pulp/upstream.json @@ -0,0 +1,7 @@ +{ + "type": "gitlab-tag", + "domain": "gitlab.gnome.org", + "gitlab": "cheywood/Pulp", + "tag_prefix": "", + "description": "GNOME RSS reader (Python/GTK4/libadwaita). Tracks GitLab release tags (CalVer, e.g. 2026.4)." +} diff --git a/scripts/update-packages.sh b/scripts/update-packages.sh index e0304bb..22a8e69 100755 --- a/scripts/update-packages.sh +++ b/scripts/update-packages.sh @@ -759,6 +759,109 @@ open(path, "w").write(new) log "${name}: updated successfully to ${latest}" } +set_fetch_from_gitlab_field() { + # set_fetch_from_gitlab_field + # Replaces attr = "..." inside the first fetchFromGitLab { ... } block. + local file="$1" attr="$2" value="$3" + python3 -c ' +import re, sys +path, attr, value = sys.argv[1], sys.argv[2], sys.argv[3] +text = open(path).read() +pat = re.compile( + rf"(fetchFromGitLab\s*\{{[^}}]*?{re.escape(attr)}\s*=\s*\")([^\"]+)(\")", + re.S, +) +new, n = pat.subn(rf"\g<1>{value}\g<3>", text, count=1) +if n != 1: + sys.exit(f"failed to set fetchFromGitLab.{attr} in {path} (matches={n})") +open(path, "w").write(new) +' "$file" "$attr" "$value" +} + +latest_gitlab_tag() { + # latest_gitlab_tag + # Prints bare version (without prefix). Matches CalVer (2026.4) and semver. + local domain="$1" project="$2" prefix="$3" + local encoded + encoded="$(python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=""))' "$project")" + local url="https://${domain}/api/v4/projects/${encoded}/repository/tags?per_page=50" + curl -fsSL "$url" | python3 -c ' +import json, re, sys +prefix = sys.argv[1] +tags = json.load(sys.stdin) +# Allow 2+ numeric components so CalVer (2026.4) and semver both match. +pat = re.compile( + r"^" + re.escape(prefix) + r"(\d+(?:\.\d+)+(?:[-+][0-9A-Za-z.-]+)?)$" +) +versions = [] +for t in tags: + name = t.get("name") or "" + m = pat.match(name) + if m: + versions.append(m.group(1)) +if not versions: + sys.exit("no matching GitLab tags for prefix " + repr(prefix)) +def key(v): + core = re.split(r"[+-]", v, maxsplit=1)[0] + return [int(x) for x in core.split(".")] +versions.sort(key=key) +print(versions[-1]) +' "$prefix" +} + +update_gitlab_tag() { + # Source package from a GitLab release tag (fetchFromGitLab). + # upstream.json: + # type: gitlab-tag + # domain: "gitlab.gnome.org" + # gitlab: "owner/repo" + # tag_prefix: "" | "v" + local name="$1" + local pkg_dir="$ROOT/packages/${name}" + local default_nix="$pkg_dir/default.nix" + local upstream="$pkg_dir/upstream.json" + local domain project tag_prefix + domain="$(read_field "$upstream" domain)" + project="$(read_field "$upstream" gitlab)" + tag_prefix="$(read_field "$upstream" tag_prefix)" + [[ -n "$domain" ]] || die "${name}: upstream.json missing domain" + [[ -n "$project" ]] || die "${name}: upstream.json missing gitlab" + tag_prefix="${tag_prefix:-}" + + local cur latest + cur="$(current_version "$default_nix")" + log "${name}: current=${cur}" + latest="$(latest_gitlab_tag "$domain" "$project" "$tag_prefix")" + log "${name}: latest upstream=${latest}" + + if ! version_gt "$latest" "$cur"; then + log "${name}: already up to date" + return 0 + fi + + if [[ "$CHECK_ONLY" -eq 1 ]]; then + log "${name}: OUTDATED (${cur} -> ${latest})" + return 10 + fi + + log "${name}: updating ${cur} -> ${latest}" + set_field_string "$default_nix" version "$latest" + + # Prefer owner/repo basename for archive name (GitLab convention). + local repo_name + repo_name="${project##*/}" + local tag="${tag_prefix}${latest}" + local src_url src_hash + src_url="https://${domain}/${project}/-/archive/${tag}/${repo_name}-${tag}.tar.gz" + log "${name}: prefetching src ${src_url}" + src_hash="$(sri_from_url_unpack "$src_url")" + set_fetch_from_gitlab_field "$default_nix" hash "$src_hash" + log "${name}: src hash ${src_hash}" + + verify_build "$name" + log "${name}: updated successfully to ${latest}" +} + update_github_unstable() { # Track tip of a branch for projects without release tags. # version format: 0-unstable-YYYY-MM-DD @@ -832,6 +935,9 @@ for name in "${PACKAGES[@]}"; do url-manifest-binary) update_url_manifest_binary "$name" || status=$? ;; + gitlab-tag) + update_gitlab_tag "$name" || status=$? + ;; *) die "${name}: unsupported upstream type '${type}'" ;;