Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/snap.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Build Callimachus from its checksum-verified, prebuilt Linux release archive.
name: Snap package

on:
pull_request:
paths:
- 'snap/**'
- 'packaging/prepare_snap_release.py'
- '.github/workflows/snap.yml'
workflow_dispatch:
inputs:
release_tag:
description: Published Callimachus release (e.g. v2.0.2)
type: string
required: true
default: v2.0.2
release:
types: [published]

permissions:
contents: read

jobs:
snap:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Pin the exact GitHub release and Linux asset checksum
env:
CALLIMACHUS_RELEASE_TAG: ${{ github.event.release.tag_name || inputs.release_tag || 'v2.0.2' }}
run: python packaging/prepare_snap_release.py "$CALLIMACHUS_RELEASE_TAG"
- name: Build Snap package
id: build
uses: canonical/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1.3.0
- name: Retain built package for testing
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: callimachus-snap
path: ${{ steps.build.outputs.snap }}
if-no-files-found: error
- name: Check whether store publishing is configured
id: creds
env:
STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
run: |
if [ -n "$STORE_CREDENTIALS" ]; then
echo 'available=true' >> "$GITHUB_OUTPUT"
else
echo 'available=false' >> "$GITHUB_OUTPUT"
fi
- name: Upload to Snap Store edge channel
if: github.event_name != 'pull_request' && steps.creds.outputs.available == 'true'
uses: canonical/action-publish@214b86e5ca036ead1668c79afb81e550e6c54d40 # v1.2.0
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
with:
snap: ${{ steps.build.outputs.snap }}
release: edge
56 changes: 56 additions & 0 deletions packaging/prepare_snap_release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Pin a Snapcraft build to an immutable, checksummed GitHub release asset."""
from __future__ import annotations

import json
import re
import sys
from pathlib import Path
from urllib.parse import quote
from urllib.request import Request, urlopen


REPOSITORY = "SNatangelo/Callimachus"
ASSET_NAME = "Callimachus-linux-x64.tar.gz"
SNAPCRAFT_YAML = Path(__file__).resolve().parents[1] / "snap" / "snapcraft.yaml"


def replace_once(text: str, pattern: str, replacement: str) -> str:
updated, count = re.subn(pattern, lambda _: replacement, text, flags=re.MULTILINE)
if count != 1:
raise RuntimeError(f"expected exactly one Snapcraft field matching {pattern!r}; got {count}")
return updated


def main(tag: str) -> None:
if not re.fullmatch(r"v\d+\.\d+\.\d+", tag):
raise ValueError(f"expected a stable release tag (vMAJOR.MINOR.PATCH), got {tag!r}")

url = f"https://api.github.com/repos/{REPOSITORY}/releases/tags/{quote(tag)}"
request = Request(url, headers={"Accept": "application/vnd.github+json", "User-Agent": "Callimachus-Snapcraft"})
with urlopen(request, timeout=30) as response:
release = json.load(response)
if release.get("draft") or release.get("prerelease") or release.get("tag_name") != tag:
raise RuntimeError("the requested tag is not a published stable release")
assets = [asset for asset in release.get("assets", []) if asset.get("name") == ASSET_NAME]
if len(assets) != 1:
raise RuntimeError(f"expected one asset named {ASSET_NAME}; found {len(assets)}")
asset = assets[0]
digest = asset.get("digest", "")
download_url = asset.get("browser_download_url", "")
expected_url = f"https://github.com/{REPOSITORY}/releases/download/{tag}/{ASSET_NAME}"
if not re.fullmatch(r"sha256:[0-9a-f]{64}", digest) or download_url != expected_url:
raise RuntimeError("release asset digest or download URL is missing or inconsistent")

recipe = SNAPCRAFT_YAML.read_text(encoding="utf-8")
recipe = replace_once(recipe, r"^version: .*$", f"version: '{tag[1:]}'")
recipe = replace_once(recipe, r"^ source: https://github\.com/SNatangelo/Callimachus/releases/download/.*$", f" source: {download_url}")
recipe = replace_once(recipe, r"^ source-checksum: sha256/[0-9a-f]{64}$", f" source-checksum: sha256/{digest[7:]}")
SNAPCRAFT_YAML.write_text(recipe, encoding="utf-8")
print(f"Pinned Snapcraft to {tag}: {digest}")


if __name__ == "__main__":
if len(sys.argv) != 2:
raise SystemExit("usage: python packaging/prepare_snap_release.py vX.Y.Z")
main(sys.argv[1])
10 changes: 10 additions & 0 deletions snap/gui/callimachus.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Desktop Entry]
Type=Application
Name=Callimachus
GenericName=Citation Verifier
Comment=Verify citations and trace source evidence
Exec=callimachus
Icon=${SNAP}/meta/gui/callimachus.png
Terminal=false
Categories=Science;Education;Office;
StartupNotify=true
Binary file added snap/gui/callimachus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions snap/snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: callimachus
title: Callimachus
base: core22
version: '2.0.2'
summary: Verify citations and source evidence in scientific manuscripts
description: |
Callimachus is an open-source citation verification workspace for scientific
writing. It checks references, identifies potentially unsupported claims,
retrieves source evidence, and produces traceable verification reports.

The desktop interface includes guided source retrieval, PDF handling, and
configurable language-model checks. Results remain reviewable by the user.

To store provider credentials in the Linux Secret Service, connect the
password-manager-service interface after installation:
sudo snap connect callimachus:password-manager-service

Files on removable drives require the removable-media connection.
website: https://callimachus.science
source-code: https://github.com/SNatangelo/Callimachus
issues: https://github.com/SNatangelo/Callimachus/issues
license: AGPL-3.0-only
grade: stable
confinement: strict
architectures:
- build-on: amd64
build-for: amd64
icon: snap/gui/callimachus.png

apps:
callimachus:
command: Callimachus/Callimachus
plugs:
- desktop
- desktop-legacy
- home
- network
- opengl
- password-manager-service
- removable-media
- wayland
- x11

parts:
callimachus:
plugin: dump
# Use the audited, prebuilt Linux bundle from the GitHub release rather
# than rebuilding the Qt/PySide/PyInstaller toolchain a second time.
# packaging/prepare_snap_release.py updates all three pinned values for CI.
source: https://github.com/SNatangelo/Callimachus/releases/download/v2.0.2/Callimachus-linux-x64.tar.gz
source-type: tar
source-checksum: sha256/19567db4ecc13835753f25b9977224a78d5d2ef379197ae4c32be7b302eb154d
stage-packages:
- libegl1
- libfontconfig1
- libfreetype6
- libgl1
- libgtk-3-0
- libxcb1
- libxkbcommon-x11-0
Loading