Skip to content
Open
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
25 changes: 12 additions & 13 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Roundup - Issue Tracking System
===============================

`Roundup`_ is a simple-to-use and and powerful issue-tracking system
`Roundup`_ is a simple-to-use and powerful issue-tracking system
with command-line, web and e-mail interfaces. Roundup is being used for
bug tracking and TODO list management, issue management, customer help
desk support, and sales lead tracking.
Expand All @@ -11,25 +11,24 @@ and on top of that:

- Roundup configurations:

- Installed via pip into a python virtual env, within it's own user account.
See /var/www for links to file paths.
- Roundup 2.6 is installed from its verified official PyPI source
distribution in ``/home/roundup/venv``. Python, the MariaDB driver and
timezone data are maintained through Debian Trixie packages.
- Roundup served via Apache mod_wsgi.
- Domain to serve, set on first boot.
- Disabled registration confirmation via email (requires mail
server).
- Includes Xapian full text indexer (recommended for large issue DB).
- Includes full timezone support and documentation.

**Security note**: Updates to Roundup may require supervision so
they **ARE NOT** configured to install automatically. See `Roundup
documentation`_ for upgrading. Please note specific user account and
location of virtual env when updating.
**Security note**: Roundup updates may require a tracker migration, so
they are not installed automatically. Back up the appliance, read the
`Roundup documentation`_, then install a selected release with::

As a convenience a script ``roundup-install.sh`` is included. This script
will perform the installation and setup convenience symlinks. All steps
other than installation will still have to be performed manually.
roundup-update VERSION

Usage is: ``roundup-install.sh [opts] <version>``
The command obtains the release URL and SHA-256 digest from the official
PyPI metadata, updates the deployed virtual environment, runs the Roundup
tracker migration, and restarts Apache.


- SSL support out of the box.
Expand All @@ -55,5 +54,5 @@ Credentials *(passwords set at first boot)*


.. _Roundup: https://roundup-tracker.org/
.. _Roundup documentation: https://roundup.sourceforge.net/docs/upgrading.html
.. _Roundup documentation: https://docs.roundup-tracker.org/en/latest/installation.html#upgrading
.. _TurnKey Core: https://www.turnkeylinux.org/core
17 changes: 17 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
turnkey-roundup-19.0 (1) turnkey; urgency=low

* Upgrade the base distribution to Debian 13/Trixie.

* Update Roundup to 2.6.0 from its verified official PyPI source
distribution and use Debian packages for its Python runtime, MariaDB
driver and timezone data.

* Replace the obsolete installer with a supervised updater that verifies
PyPI release metadata and runs the tracker migration.

* Use Roundup's password API for the firstboot administrator credential.

* See the TurnKey Core 19.0 changelog for changes common to all appliances.

-- TurnKey Linux release engineering <release@turnkeylinux.org> Mon, 24 Aug 2026 00:00:00 +0000

turnkey-roundup-18.0 (1) turnkey; urgency=low

* Update roundup to 2.3.0.
Expand Down
23 changes: 14 additions & 9 deletions conf.d/main
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,33 @@ DB_PASS=$(mcookie)
RT_TRACKER=/var/lib/roundup/tracker
RT_TEMPLATE=classic
RT_BACKEND=mysql
RT_VER=$(gh_releases roundup-tracker/roundup | grep -v 'git\|alpha\|beta\|b' | sort -V | tail -1)
RT_VER=2.6.0
RT_URL=https://files.pythonhosted.org/packages/64/93/17eca3bc4e9bda03c45a9b3bf4fd972d96865efa7b0c09100a55cd58d4e7/roundup-2.6.0.tar.gz
RT_SHA256=12fd8fb806047415f22131f965c6ab4026a28dc63f7054e055c8f891950591d4
RT_CONF=/etc/roundup/tracker-config.ini
RT_HOME="/home/$USER"
RT_VENV="$RT_HOME/venv"

as_user() { su - "$USER" -c "$*"; }

adduser --disabled-login --gecos 'roundup user' --shell /bin/bash $USER

as_user mkdir versions
as_user python3 -m venv "versions/$RT_VER"
as_user python3 -m venv --system-site-packages "$RT_VENV"

as_user echo ". versions/$RT_VER/bin/activate" '>' .bashrc.d/roundup-venv
as_user echo ". $RT_VENV/bin/activate" '>' .bashrc.d/roundup-venv
as_user chmod +x .bashrc.d/roundup-venv

as_user python3 -m pip install mysqlclient roundup pytz
as_user "$RT_VENV/bin/python -m pip install --no-deps \
'roundup @ $RT_URL#sha256=$RT_SHA256'"
version_output=$(as_user "$RT_VENV/bin/roundup-admin -v")
[[ $version_output == "$RT_VER (python 3.13."*")" ]]

# allow Apache access to docs - requires search perms (x on directory)
DOCS="$RT_HOME/versions/$RT_VER/share/doc/roundup/html"
DOCS="$RT_VENV/share/doc/roundup/html"
chmod o+x $DOCS{,/_{images,sources,static}}

# allow Apache access to venv dir listing
chmod o+x $RT_HOME{,/versions{,/$RT_VER}}
chmod o+x "$RT_HOME" "$RT_VENV"

a2enmod wsgi
a2enmod rewrite
Expand All @@ -46,8 +51,8 @@ mkdir -p $RT_TRACKER
mkdir /etc/roundup
chown -R roundup:roundup $RT_TRACKER
OPTIONS="admin_email=admin,dispatcher_email=admin,tracker_web=https://www.example.com/,mail_domain=example.com,mail_host=localhost,rdbms_password=$DB_PASS"
as_user "roundup-admin -i $RT_TRACKER install $RT_TEMPLATE $RT_BACKEND $OPTIONS"
as_user "roundup-admin -i $RT_TRACKER initialise $ADMIN_PASS"
as_user "$RT_VENV/bin/roundup-admin -i $RT_TRACKER install $RT_TEMPLATE $RT_BACKEND $OPTIONS"
as_user "$RT_VENV/bin/roundup-admin -i $RT_TRACKER initialise $ADMIN_PASS"
echo "tracker = $RT_TRACKER" >> /etc/roundup/roundup-server.ini
chown -R www-data:www-data $RT_TRACKER/db

Expand Down
95 changes: 95 additions & 0 deletions docs/v19.0-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Roundup 19.0 testing

## Scope and source decision

Debian 13 Trixie does not provide a Roundup package. Roundup 19.0 therefore
uses the official Roundup 2.6.0 source distribution from PyPI. The build pins
its files.pythonhosted.org URL and SHA-256 digest
`12fd8fb806047415f22131f965c6ab4026a28dc63f7054e055c8f891950591d4`.
Roundup 2.6.0 supports Python 3.13 and identifies itself as a stable release.

Python 3.13, Apache mod_wsgi, MariaDB, the Python MariaDB driver and timezone
data come from signed Debian Trixie repositories. The virtual environment uses
Debian's system Python modules, while pip installs only the pinned Roundup
source distribution with dependency resolution disabled.

The maintained upstream update path is `roundup-update VERSION`. It reads the
selected release's source URL and SHA-256 digest from official PyPI JSON,
updates the deployed virtual environment with hash verification, runs the
Roundup tracker migration, and restarts Apache. An administrator must review
the selected release and back up the appliance before running it.

## Acceptance command

```sh
/sandboxed-git/turnkey/tools/test-v19-appliance roundup \
--source /home/agent/.local/worktrees/turnkey-apps/roundup/wish-roundup-v19-trixie
```

The command builds the Trixie root filesystem, imports it into Docker, boots
the normal systemd and firstboot path, then runs `tests/v19.sh`.

## README crosswalk

| README contract | Focused check | Required result | Evidence |
| --- | --- | --- | --- |
| Roundup 2.6 runs in its dedicated Python virtual environment | Query Roundup and Python, import its WSGI handler, and inspect Debian module ownership | Roundup 2.6.0 runs on Python 3.13; WSGI imports; MySQLdb and pytz resolve from Debian | `tests/v19.sh` |
| Apache mod_wsgi serves Roundup over TLS | Validate Apache configuration and module state, then request the tracker | Apache is active; WSGI is loaded; the tracker page is returned over HTTPS | `tests/v19.sh` |
| Firstboot configures the administrator account and domain | Log in through the real CSRF-protected web form with the preseeded administrator password | Roundup creates an authenticated administrator session | `tests/v19.sh` |
| The web issue workflow persists in MariaDB | Create, read and update an issue through the web forms, then read its title from MariaDB | The updated issue appears in both the web response and database | `tests/v19.sh` |
| Local Roundup documentation is available | Request `/docs/` through Apache | The installed Roundup documentation is returned | `tests/v19.sh` |
| Postfix supports local application mail | Check service supervision and the local SMTP listener | Postfix is enabled and active with port 25 bound to loopback | `tests/v19.sh` |
| Webmin manages Apache and MariaDB | Check the modules and request the Webmin HTTPS endpoint | Both modules are installed and Webmin answers on port 12321 | `tests/v19.sh` |
| Debian and upstream components have maintained update paths | Refresh APT metadata, inspect eligible candidates, query official PyPI metadata, and inspect installed direct URL metadata | Trixie candidates remain eligible; installed Debian packages do not change; PyPI exposes a source digest; installed Roundup has the pinned URL and digest | `tests/v19.sh`; `tests/v19-upgrade.sh` |
| Root SSH, backup and other base services are inherited from Core | Cite the unchanged Core layer | Core 19 baseline passes | Core run `20260824t010251z-1634-32241`, source `24c82ee3540ce545422742b0e28ba6b687c53ec2`, verdict `PASS` |

## Disposable upstream upgrade

`tests/v19-upgrade.sh` exercises the real compatible-version update path in a
disposable Debian Trixie container. It obtains the official source URLs and
SHA-256 digests from PyPI, installs Roundup 2.5.0, initializes an SQLite-backed
classic tracker, creates and reads an issue, upgrades to 2.6.0, runs
`roundup-admin migrate`, reads the pre-upgrade issue, and updates it.

```sh
docker run --rm --name tkl-roundup-v19-upgrade \
--mount type=bind,src=/home/agent/.local/worktrees/turnkey-apps/roundup/wish-roundup-v19-trixie,dst=/src,readonly \
--mount type=bind,src=/usr/local/share/ca-certificates/microsandbox-ca.crt,dst=/usr/local/share/ca-certificates/microsandbox-ca.crt,readonly \
debian:trixie-slim /src/tests/v19-upgrade.sh
```

The fixture returned:

```text
No migration action required. At schema version 8.
roundup_upgrade=2.5.0_to_2.6.0 issue=1 create_read_update=pass
```

## Accepted evidence

The exact acceptance command passed in run
`20260824t115118z-474-23163` using harness commit
`b6f8b8c2f3e8f00fd5cf36869e645fc08f01f87e`. The harness built clean source
commit `17be462687b772cec9b72e2acb7dc8dd4f516f55`, whose transported archive had
SHA-256 digest
`8dbc31a2f59f3191a620c9023bbbca9f9cdb1be912f5866e1e2c6e084a7a2487`.
Build, import, normal runtime readiness, inithooks and focused runtime tests all
passed. The retained report records Roundup 2.6.0 on Python 3.13.5, Apache
2.4.68 and MariaDB 11.8.6, plus the successful administrator login and issue
create, read and update flow.

The authoritative report is
`/home/agent/.local/state/turnkey-v19-harness/runs/roundup/20260824t115118z-474-23163/report.txt`.

## Known limitation

Docker runtime validation does not exercise the installer, kernel, bootloader
or physical hardware. Roundup adds no appliance-specific behavior at those
boundaries, so the validated Core 19 baseline supplies inherited evidence.

## Deferred issues

The focused acceptance creates and updates an issue but does not exercise
inbound email issue creation. The normal local Postfix dependency and Roundup
mail configuration are validated, while end-to-end delivery would require an
external mail system and is disproportionate for this migration.
8 changes: 4 additions & 4 deletions overlay/etc/apache2/sites-available/roundup.conf
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
ServerName localhost

Alias /docs /home/roundup/versions/2.3.0/share/doc/roundup/html
Alias /docs /home/roundup/venv/share/doc/roundup/html
Alias /robots.txt /var/www/static/robots.txt
Alias /favicon.ico /var/www/static/favicon.ico

AliasMatch ^/@@file/(.*) /var/lib/roundup/tracker/html/$1

WSGIScriptAlias / /var/lib/roundup/tracker/wsgi.py
WSGIPythonHome /home/roundup/versions/2.3.0
WSGIPythonPath /home/roundup/versions/2.3.0/lib/python3.11/site-packages
WSGIPythonHome /home/roundup/venv
WSGIPythonPath /home/roundup/venv/lib/python3.13/site-packages

<VirtualHost *:80>
UseCanonicalName Off
Expand All @@ -34,7 +34,7 @@ WSGIPythonPath /home/roundup/versions/2.3.0/lib/python3.11/site-packages
Options None
</Directory>

<Directory /home/roundup/versions/2.3.0/share/doc/roundup/html>
<Directory /home/roundup/venv/share/doc/roundup/html>
Require all granted
AllowOverride None
Options None
Expand Down
25 changes: 12 additions & 13 deletions overlay/usr/lib/inithooks/bin/roundup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@
DEFAULT=www.example.com
"""

import os
import sys
import getopt
from libinithooks import inithooks_cache
import hashlib

from libinithooks.dialog_wrapper import Dialog
from mysqlconf import MySQL
import subprocess

def usage(s=None):
Expand Down Expand Up @@ -78,22 +75,24 @@ def main():

inithooks_cache.write('APP_DOMAIN', domain)

hashpass = "{SHA}" + hashlib.sha1(password.encode('utf8')).hexdigest()

m = MySQL()
m.execute('UPDATE roundup._user SET _address=%s WHERE _username=\"admin\";', (email,))
m.execute('UPDATE roundup._user SET _password=%s WHERE _username=\"admin\";', (hashpass,))
subprocess.run([
"/home/roundup/venv/bin/roundup-admin",
"-i", "/var/lib/roundup/tracker",
"-u", "admin:turnkey",
"set", "user1",
"password=%s" % password,
"address=%s" % email,
], check=True)

conf = "/etc/roundup/tracker-config.ini"
subprocess.run(["sed", "-i", "s|^web =.*|web = https://%s/|" % domain, conf])
subprocess.run(["sed", "-i", "s|^web =.*|web = https://%s/|" % domain, conf], check=True)

apache_conf = "/etc/apache2/sites-available/roundup.conf"
subprocess.run(["sed", "-i", "\|RewriteRule|s|https://.*|https://%s/\$1 [L,R=301]|" % domain, apache_conf])
subprocess.run(["sed", "-i", "\|RewriteCond|s|!^.*|!^%s$|" % domain, apache_conf])
subprocess.run(["sed", "-i", r"\|RewriteRule|s|https://.*|https://%s/\$1 [L,R=301]|" % domain, apache_conf], check=True)
subprocess.run(["sed", "-i", r"\|RewriteCond|s|!^.*|!^%s$|" % domain, apache_conf], check=True)

subprocess.run(['service', 'apache2', 'restart'])
subprocess.run(['service', 'apache2', 'restart'], check=True)


if __name__ == "__main__":
main()

Loading