An isolated build repository for generating .deb packages for Notolog Editor - a Python-powered Markdown editor featuring an integrated AI assistant, on-device LLM support, and optional file encryption.
▒█▄░▒█ ▒█▀▀▀▄ ▀▀█▀▀ ▒█▀▀▀▄ ▒█░░░ ▒█▀▀▀▄ ▒█▀▀█░
▒█▒█▒█ ▒█░░▒█ ░▒█░░ ▒█░░▒█ ▒█░░░ ▒█░░▒█ ▒█░░▄▄
▒█░░▀█ ▒█▄▄▄█ ░▒█░░ ▒█▄▄▄█ ▒█▄▄▄ ▒█▄▄▄█ ▒█▄▄▄█
This repo contains build scripts, CI/CD workflows, and Debian packaging files used to create and lint compliant .deb packages. It does not include application source code.
- 🌐 Official site: https://notolog.com
- 📚 Documentation: https://notolog.app
- 📦 Main app repo: https://github.com/notolog/notolog-editor
- 🛠️ This builder repo: https://github.com/notolog/notolog-debian
- 🗃️ PPA packages: https://launchpad.net/~notolog/+archive/ubuntu/ppa
- ⚖️ License: MIT License
The easiest way to install Notolog on Ubuntu:
# Add the PPA repository
sudo add-apt-repository ppa:notolog/ppa
sudo apt update
# Install Notolog
sudo apt install notolog
# Run
notologThe amd64 PPA workflow targets Jammy (22.04 LTS), Noble (24.04 LTS), and Resolute
(26.04 LTS). Its default all selection builds one application executable and one
shared upstream source archive, then creates the three series-specific source
packages using sortable ~ppa1~ubuntu22.04.1-style versions. If the PPA already
accepted that upstream version, the workflow downloads its original archive,
verifies its SHA-256 checksum and application source against the pinned release,
and reuses its executable. Pending uploads count as accepted, even before an APT
index is published. The workflow skips source-package versions already registered
in the PPA and prepares only the missing ones. Selecting an individual series uses
the same archive as all.
Uploads reference an already accepted upstream archive without transferring it
again; the source artifact still retains that archive for verification and recovery.
A dry run checks source packaging without signing or uploading. A new upstream version also runs the complete application build and bundle checks; an existing version reuses the accepted build. The configured Launchpad owner and PPA must be available for both modes so the workflow can check for an existing archive.
GitHub release builds are native for each architecture: amd64 is built on Ubuntu
22.04 and targets Ubuntu 22.04 or newer; arm64 is built on Ubuntu 24.04 and targets
Ubuntu 24.04 or newer. The PPA remains an amd64-only binary-repack workflow because
its source upload carries the architecture-specific PyInstaller executable produced
by the Ubuntu 22.04 workflow runner. Launchpad rebuilds the Debian package around
that executable; it does not recompile the application. GitHub's native release
builds are the authoritative standalone package artifacts.
Note: The Git repository does not commit generated application binaries or third-party runtime packages. Release builds bundle their dependencies and record the corresponding licence information in the generated package.
Essential packages required to perform the build:
sudo apt-get install build-essential zlib1g-dev debhelper devscripts lintian libegl1 libportaudio2Release builds use Python 3.14.10 or a newer Python 3.14 maintenance release with
venv support. The build preflight also accepts patched stable Python 3.11–3.13;
select another interpreter with PYTHON_BIN. The selected application's dependency
requirements and all native compatibility and package checks still apply.
The patch-level minimums follow the upstream security releases.
build.sh creates a builder-owned virtual environment and installs the pinned
PyInstaller and Python build tools;
do not install a separate global PyInstaller for this workflow. Release builds pin
the llama-cpp-python version used by the runtime-backend adapter so rebuilding a
builder tag cannot silently select a different native ABI.
Clone the matching application release into src/, then run the builder from the
repository root:
git clone --depth 1 --branch v1.2.4 https://github.com/notolog/notolog-editor.git src
./pyinstaller/build.shThe default auto profile uses runtime-dispatched llama.cpp CPU backends on
amd64 and a portable backend on arm64, where compiling every backend variant
would make release builds substantially slower. On amd64, llama.cpp selects the
best backend supported by the current processor at startup, allowing one package
to retain the ordinary architecture baseline while using F16C, AVX2, and newer
instruction sets where available.
For a smaller generic build without runtime-dispatched backends:
LLAMA_CPU_PROFILE=portable ./pyinstaller/build.shThe f16c profile enables hardware-accelerated half-precision conversions and
can substantially improve llama.cpp CPU inference on compatible amd64
processors:
grep -m1 -w f16c /proc/cpuinfo
LLAMA_CPU_PROFILE=f16c ./pyinstaller/build.shThe build host and every system running an f16c build must support F16C. The
script verifies the build host automatically. Prefer auto for distributed
packages; use runtime, portable, or f16c only for controlled builds with
specific performance, size, or hardware requirements.
Application and builder tags have different purposes:
notolog-editoruses the upstream application tag, for examplev1.2.4.notolog-debianuses the full Debian version, for examplev1.2.4-1ubuntu1.
The builder workflow requires its release tag to equal the version in
debian/changelog. The application source must declare the matching upstream
version in pyproject.toml; builder tag v1.2.4-1ubuntu1 therefore packages
application version 1.2.4. Create the builder tag only after the matching
application release is public and the builder's Launchpad dry run succeeds.
For a release:
- Run the Upload to Launchpad PPA workflow from the release commit with
version_overrideset to the application tag,ubuntu_seriesset toall,ppa_revisionset to1, anddry_runenabled. - Review the generated source-package artifact and confirm that all source, lint, bundle, frozen-module, GUI smoke, architecture, glibc, and hardening checks pass.
- Create and push the signed builder tag using the full Debian version.
- Run the same Launchpad workflow with
dry_rundisabled. Check acceptance for each selected series after transfer. Launchpad's binary builds may remain queued independently of the upload workflow.
Each source-package version and upstream archive is immutable. To update Debian
packaging, increase the Debian revision or ppa_revision; the workflow still
reuses the accepted upstream archive. Replacing the application executable or
upstream contents requires a new upstream version. Do not move a published tag.
If an upload stops after one series succeeds, rerun with the same application
version and ppa_revision, choosing all or a missing series. Accepted versions
are skipped, and missing versions reference the original archive already held by
Launchpad. Increasing ppa_revision alone cannot resolve a conflicting
.orig.tar.xz: the archive belongs to the upstream version, not the series or
Debian revision. Transfer retries resend the same signed files. Source artifacts
are retained before upload for recovery. Check Launchpad's acceptance and build
status separately from the workflow's transfer result.
To remove builder-owned generated artifacts:
./pyinstaller/clean-build.shThe built package can be installed with the command below.
sudo apt install ../notolog_X.Y.Z-REVISION_ARCH.debIf the package was built on a local machine, the resulting .deb file is typically located in the parent directory (..) relative to where the debian/ directory resides.
pyinstaller/notolog-onefile.spec is the authoritative standalone-binary definition.
Use ./pyinstaller/build.sh so the native bootloader, llama.cpp library, dependency
audit, license report, and Debian package are produced consistently.
The init script pyinstaller/runtime_hooks/init.py configures the app on first run based on user settings and package type. It verifies the current package type and adjusts settings accordingly.
If you see:
qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Available platform plugins are: linuxfb, minimalegl, xcb, minimal, vkkhrdisplay, wayland, vnc, offscreen, wayland-egl, eglfs.
Aborted (core dumped)Install the required package:
sudo apt-get install libxcb-cursor0- REUSE https://reuse.software/faq/
- Bulk-license whole directories https://reuse.software/faq/#bulk-license
- Add copyright and licensing information to an uncommentable file https://reuse.software/faq/#uncommentable-file
- SPDX Licenses https://spdx.org/licenses/
- See
debian/copyrightfor a full SPDX-compatible license inventory.
Install REUSE
pip install reuseFetch missing licenses
# Download all SPDX-referenced licenses
reuse download --all
# Or download a specific license and place it in the LICENSES/ directory
reuse download PSF-2.0Run REUSE linting
reuse lintAnnotate specific files
reuse annotate --copyright="2024-2026 Author Name" \
--license="MIT" --skip-unrecognised \
debian/README.md debian/changelog debian/controlLint the built .deb package:
sudo apt-get install -y lintian
lintian notolog.debFor detailed output and to help debug any issues, use the --info flag:
lintian -i notolog.deb
# Or, for even more verbose output:
lintian -iIv notolog.debPIE (Position Independent Executable) hardening ensures that the binary can be loaded at a random memory address each time it runs.
This enhances security by enabling ASLR (Address Space Layout Randomization), which protects against memory corruption attacks like buffer overflows.
For .deb packages, this is a Debian security best practice and is checked by tools like lintian.
After building, run:
hardening-check pyinstaller/dist/notolog
# Or, if the app is already installed:
hardening-check /usr/bin/notologYou want to see:
Position Independent Executable: yes
Or, to check manually with readelf:
readelf -h /usr/bin/notolog | grep 'Type'Expected output:
Type: DYN (Position-Independent Executable)
Run this to validate:
desktop-file-validate /usr/share/applications/notolog.desktopdpkg-deb -c notolog_*.deb | grep 'man/man1'The output should be:
./usr/share/man/man1/notolog.1.gz
Then check the man entry:
man notologUse these steps to verify what's bundled into the PyInstaller binary - important for compliance, reproducibility, and Debian policy alignment.
Inspect .deb contents:
dpkg-deb -c notolog_*.debPyInstaller one-file packages store shared objects inside the executable archive,
so they do not appear as separate files in dpkg-deb -c output.
If using a --onefile PyInstaller build, run the app once to trigger runtime extraction. Then inspect with:
ps aux | grep notolog
lsof -p $(pgrep notolog) | grep _MEI
# The default extraction directory is a private, per-process _MEI directory
# under the user's secure temporary directory.Optional: Use strace if you need syscall-level info:
strace -f -e trace=openat /usr/bin/notologNote: strace is a syscall tracer. It works by launching (or attaching to) the process and intercepting all syscalls (like openat, read, etc.).
This means it will run the app while tracing - that's expected behavior.
See linked shared libraries:
ldd /usr/bin/notologCheck which Debian package provides a library:
dpkg -S libexpat.so.1
# or
apt show libexpat1
# and
cat /usr/share/doc/libexpat1/copyrightTo inspect the contents of a PyInstaller executable (ensure the virtual environment is activated):
pyi-archive_viewer pyinstaller/dist/notologInside the viewer shell:
> ls
> extract libexpat.so.1To confirm libllama.so and its dependencies are dynamically linked and runtime-bundled:
RUNTIME_DIR=$(lsof -p "$(pgrep -n notolog)" | awk '/_MEI/ {print $9; exit}')
find "$RUNTIME_DIR" -path '*llama_cpp*' -name 'libllama.so' -exec ldd {} \;To verify bundled binaries for license metadata, use licensecheck:
licensecheck --shortname-scheme new --recursive "$RUNTIME_DIR"RUNTIME_DIR is discovered from the running process because PyInstaller creates a
private randomized extraction directory for every launch.
The --shortname-scheme new flag ensures SPDX-style identifiers (e.g. mit, lgpl-2.1+, etc.).
This is especially useful for auditing libraries bundled in --onefile PyInstaller builds.
The build process is automated using GitHub Actions, and the resulting artifact is uploaded after the workflow completes.
Refer to .github/workflows/build.yaml for details and step-by-step instructions.
See CONTRIBUTING.md for guidelines.
This repository contains only build scripts and CI workflows. It does not include or redistribute any third-party software.
All third-party dependencies are pulled by the main application during build and bundled into the final .deb as part of the PyInstaller runtime. Licensing for each is tracked and included in the package metadata.
🦙 The CPU-only llama-cpp backend is built from source and dynamically links to:
libgompandlibstdc++: covered under the GPL with the GCC Runtime Library Exception, which permits linking from non-GPL software.- MIT-licensed internal
libggmlbackends (libggml.so,libggml-base.so, and the selectedlibggml-cpu-*.sovariant).