Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6766d78
Adapt to dangerzone-image split
almet May 4, 2026
7a13788
Packaging: Fedora dangerzone-slim version
almet May 4, 2026
230e1c4
Packaging: Debian dangerzone-slim version
almet May 4, 2026
6269ead
Update dodo.py to handle slim/full variants
almet May 4, 2026
4b94f66
Packaging: container.tar moves to -full package
almet May 4, 2026
f9b1607
GUI: handle missing container.tar
almet May 4, 2026
764934a
CLI: error out when no container image is found
almet May 4, 2026
b9945bf
Linux: Make machine-related tasks output to DEBUG
almet May 4, 2026
d729de8
Docs: Update github.com/freedomofpress/packages links
almet May 4, 2026
8db71a3
CI: test dangerzone-slim.deb and dangerzone-full.rpm
almet May 4, 2026
2015e4e
Add changelog entry
almet May 4, 2026
cdec2fe
Dev scripts: persist settings and auto-build dev image
almet May 4, 2026
8703bde
Add debian reproducibility
almet May 4, 2026
15e752c
Ensure the image is available with Podman
almet May 4, 2026
f578304
Retry wsl --update on Windows runners
almet May 4, 2026
26f92cc
Fixup: Add dangerzone-full.deb in the matrix
almet May 13, 2026
6d881b2
Fixup: point to Linux installation instructions
almet May 13, 2026
8c6f33c
Fixup: update language in the user prompt
almet May 13, 2026
4de0948
Fixup: prompt the user even if they explicitely disabled updates
almet May 13, 2026
c174332
Fixup: Remove dangerzone-qubes.deb
almet May 13, 2026
430f317
Fixup: use matrix for Fedora builds (full and slim)
almet May 13, 2026
8785bee
Fixup: update logic, and tests
almet May 13, 2026
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
139 changes: 136 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,19 @@ jobs:
# `dangerzone-cli` command and its WSL detection will fail.
#
# For this reason, we prefer to run `wsl --update` before the tests.
# Microsoft's WSL update servers occasionally return HTTP 403, so retry
# before failing the job.
- name: Update WSL
run: wsl --update
shell: pwsh
run: |
$ok = $false
for ($i = 1; $i -le 5; $i++) {
wsl --update
if ($LASTEXITCODE -eq 0) { $ok = $true; break }
Write-Host "wsl --update attempt $i failed (exit $LASTEXITCODE), retrying..."
Start-Sleep -Seconds 15
}
if (-not $ok) { exit 1 }
- name: Smoke test
run: poetry run dangerzone-cli .\tests\test_docs\sample-pdf.pdf --ocr-lang eng --debug
- name: Run CLI tests
Expand Down Expand Up @@ -235,7 +246,7 @@ jobs:
poetry run dangerzone-image prepare-archive
--output share/container.tar

- name: Build Dangerzone .deb
- name: Build Dangerzone .deb packages
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} \
--version ${{ matrix.version }} \
Expand All @@ -250,6 +261,15 @@ jobs:
if-no-files-found: error
compression-level: 0

- name: Upload Dangerzone-full .deb
if: matrix.distro == 'debian' && matrix.version == 'bookworm'
uses: actions/upload-artifact@v6
with:
name: dangerzone-full.deb
path: "deb_dist/dangerzone-full_*_*.deb"
if-no-files-found: error
compression-level: 0

install-deb:
name: "install-deb (${{ matrix.distro }} ${{ matrix.version }})"
runs-on: ubuntu-latest
Expand Down Expand Up @@ -293,6 +313,12 @@ jobs:
--version ${{ matrix.version }} \
build

- name: Download container image
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} \
--version ${{ matrix.version }} \
run dangerzone-image upgrade

- name: Run a test command
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} \
Expand All @@ -305,6 +331,50 @@ jobs:
--version ${{ matrix.version }} \
run dangerzone --help


install-deb-full:
Comment thread
apyrgio marked this conversation as resolved.
Outdated
name: "install-deb-full (${{ matrix.distro }} ${{ matrix.version }})"
runs-on: ubuntu-latest
needs:
- build-deb
strategy:
matrix:
include:
- distro: debian
version: bookworm

steps:
- name: Checkout
uses: actions/checkout@v6

- uses: actions/setup-python@v6
with:
python-version: "3.10"

- name: Download Dangerzone full .deb
uses: actions/download-artifact@v6
with:
name: dangerzone-full.deb
path: "deb_dist/"

- name: Build end-user environment with full package
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} \
--version ${{ matrix.version }} \
build --full

- name: Run a test command (container bundled)
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} \
--version ${{ matrix.version }} \
run dangerzone-cli dangerzone/tests/test_docs/sample-pdf.pdf --ocr-lang eng --debug

- name: Check that the Dangerzone GUI imports work
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} \
--version ${{ matrix.version }} \
run dangerzone --help

build-install-rpm:
name: "build-install-rpm (${{ matrix.distro }} ${{matrix.version}})"
runs-on: ubuntu-latest
Expand Down Expand Up @@ -378,7 +448,22 @@ jobs:
uses: actions/upload-artifact@v7
with:
name: dangerzone-${{ matrix.distro }}-${{ matrix.version }}.rpm
path: "dist/dangerzone-*.x86_64.rpm"
path: |
dist/dangerzone-*.x86_64.rpm
!dist/dangerzone-full-*.x86_64.rpm
if-no-files-found: error
compression-level: 0

- name: Build Dangerzone-full .rpm
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} --version ${{ matrix.version }} \
run --dev --no-gui ./dangerzone/install/linux/build-rpm.py --full

- name: Upload Dangerzone-full .rpm
uses: actions/upload-artifact@v7
with:
name: dangerzone-full-${{ matrix.distro }}-${{ matrix.version }}.rpm
path: "dist/dangerzone-full-*.x86_64.rpm"
if-no-files-found: error
compression-level: 0

Expand All @@ -394,6 +479,11 @@ jobs:
--version ${{ matrix.version }} \
build

- name: Download container image
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} --version ${{ matrix.version }} \
run dangerzone-image upgrade

- name: Run a test command
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} --version ${{ matrix.version }} \
Expand All @@ -404,6 +494,49 @@ jobs:
./dev_scripts/env.py --distro ${{ matrix.distro }} --version ${{ matrix.version }} \
run dangerzone --help

install-rpm-full:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mentioned in https://github.com/freedomofpress/dangerzone/pull/1423/changes#r3219532167 also applies here, with the added benefit that:

  1. This job will no longer need to wait for build-install-rpm to finish.
  2. Having a variant: "full" option in the matrix will help build just the packages we need per job, which means we will build the full variant for just one of our the Fedora versions (Fedora 43 works fine).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually not sure we want this here. For Debian, because the packages is built once and installed everywhere the story is a bit different, but since here we need to build on each system, I believe we could just build the -full variant and try it on each system.

We could try to depend on some more specific jobs to finish, but I'm not sure the benefit would be worth the cost. Having things run in a separate job is useful to ensure we're not using the local caches (when testing that conversions work)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually implemented this as 430f317

name: "install-rpm-full (${{ matrix.distro }} ${{ matrix.version }})"
runs-on: ubuntu-latest
needs:
- build-install-rpm
strategy:
matrix:
include:
- distro: fedora
version: "43"

steps:
- name: Checkout
uses: actions/checkout@v6

- uses: actions/setup-python@v6
with:
python-version: "3.11"

- name: Download Dangerzone-full .rpm
uses: actions/download-artifact@v8
with:
name: dangerzone-full-${{ matrix.distro }}-${{ matrix.version }}.rpm
path: "dist/"

- name: Build end-user environment with full package
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} \
--version ${{ matrix.version }} \
build --full

- name: Run a test command (container bundled)
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} \
--version ${{ matrix.version }} \
run dangerzone-cli dangerzone/tests/test_docs/sample-pdf.pdf --ocr-lang eng --debug

- name: Check that the Dangerzone GUI imports work
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} \
--version ${{ matrix.version }} \
run dangerzone --help

run-tests:
name: "run tests (${{ matrix.distro }} ${{ matrix.version }})"
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,11 @@ dmypy.json

debian/.debhelper
debian/dangerzone
debian/dangerzone-full
debian/files
debian/debhelper-build-stamp
debian/dangerzone.*
debian/dangerzone-full.*
.pybuild/

# Other
Expand Down
18 changes: 18 additions & 0 deletions BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ Create a .deb:
./install/linux/build-deb.py
```

This builds both the `dangerzone` (slim) and `dangerzone-full` packages in a
single run. The slim package does not contain the `container.tar` image (it
will be downloaded on the first run), while `dangerzone-full` bundles the
container image for offline or air-gapped installations.

> **Note**: `container.tar` must be present in `share/` before running this
> command, as it is required for the `dangerzone-full` package. See the
> instructions above for how to download or build it.

## Fedora

Install dependencies:
Expand Down Expand Up @@ -164,6 +173,15 @@ Create a .rpm:
./install/linux/build-rpm.py
```

This builds the `dangerzone` package, which doesn't contain the `container.tar`
image (it will be downloaded on the first run).

To build the `dangerzone-full` package with the container bundled:

```sh
./install/linux/build-rpm.py --full
```

## Qubes OS

> :warning: Native Qubes support is in beta stage, so the instructions below
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ since 0.4.1, and this project adheres to [Semantic Versioning](https://semver.or

## [Unreleased](https://github.com/freedomofpress/dangerzone/compare/v0.10.0...HEAD)

### Changes

- Linux packages dropped the `container.tar` file, it is expected to be downloaded at the first usage, and then kept up to date. If you prefer to use packages with the `container.tar`, use the `dangerzone-full` variant ([#1069](https://github.com/freedomofpress/dangerzone/issues/1069))
- **Breaking change (Linux CLI)**: with the slim `dangerzone` package, the CLI no longer auto-downloads the conversion sandbox. New installations must initialize the sandbox once with `dangerzone-image upgrade` (or run the GUI, which prompts for the download) before `dangerzone-cli` can convert documents. Users of the `dangerzone-full` package or who already have an image installed are unaffected.


### Fixed

- Remember user-selected output directory across sessions
Expand Down
11 changes: 11 additions & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ Install Dangerzone:
```sh
sudo apt update
sudo apt install -y dangerzone

# Alternatively, use dangerzone-full if you prefer to install dangerzone
# with its container bundled (larger package, but no download at first use)
sudo apt install -y dangerzone-full
```

<table>
Expand Down Expand Up @@ -165,6 +169,10 @@ Type the following commands in a terminal:
```sh
sudo dnf config-manager addrepo --from-repofile=https://packages.freedom.press/yum-tools-prod/dangerzone/dangerzone.repo
sudo dnf install dangerzone

# Alternatively, use dangerzone-full if you prefer to install dangerzone
# with its container bundled (larger package, but no download at first use)
sudo dnf install dangerzone-full
```

##### Verifying Dangerzone GPG key
Expand Down Expand Up @@ -213,6 +221,9 @@ Type the following commands in a terminal:
curl -o - https://packages.freedom.press/yum-tools-prod/dangerzone/dangerzone.repo \
| sudo tee /etc/yum.repos.d/dangerzone.repo > /dev/null
rpm-ostree install dangerzone
# Alternatively, use dangerzone-full if you prefer to install dangerzone
# with its container bundled (larger package, but no download at first use)
rpm-ostree install dangerzone-full
```

After the above command completes, restart your computer to complete the installation.
Expand Down
14 changes: 13 additions & 1 deletion dangerzone/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,19 @@ def run(
]

try:
startup.StartupLogic(tasks=tasks).run()
try:
startup.StartupLogic(tasks=tasks).run()
except errors.UpdaterDisabledNoContainer:
click.echo(
"\n"
+ Fore.RED
+ Style.BRIGHT
+ "No container image found."
+ Style.RESET_ALL
+ " Please initialize Dangerzone by running:\n\n"
" dangerzone-image upgrade\n"
)
sys.exit(1)
Comment thread
apyrgio marked this conversation as resolved.
print_header("Converting document(s) to safe PDF")
dangerzone.convert_documents(ocr_lang)
finally:
Expand Down
10 changes: 10 additions & 0 deletions dangerzone/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,13 @@ def __init__(self) -> None:

class WinShellExecProcessError(WinShellExecError):
pass


class UpdaterDisabledNoContainer(Exception):
"""User declined to enable updates, but no container image is available."""

def __init__(self) -> None:
super().__init__(
"No container image is available. "
"Updates must be enabled to download the container and use Dangerzone."
)
23 changes: 23 additions & 0 deletions dangerzone/gui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,9 @@ def __init__(self, dangerzone: DangerzoneGui) -> None:
task_update_check.needs_user_input.connect(
self.handle_needs_user_input_enable_updates
)
task_update_check.needs_user_input_download.connect(
self.handle_needs_user_input_download_container
)

task_container_install.load_container.connect(
self.status_bar.handle_task_container_install_local
Expand Down Expand Up @@ -685,10 +688,30 @@ def handle_update_check_completed(self) -> None:
self.dangerzone.settings.save()

def handle_needs_user_input_enable_updates(self) -> None:
"""Handle the prompt to enable updates (container already available)."""
check = prompt_for_checks(self.dangerzone)
if check is not None:
self.dangerzone.settings.set("updater_check_all", check, autosave=True)

def handle_needs_user_input_download_container(
self, req: startup.PromptRequest
) -> None:
"""Handle the prompt to download container (no container available).

This is a blocking prompt - the user must accept or the app will shut down.
"""
check = prompt_for_checks(self.dangerzone, download_required=True)
if check is True:
req.reply(True)
else:
# User declined or pressed X - shutdown
req.reply(False)
QtCore.QTimer.singleShot(0, self._handle_download_declined)
Comment on lines +708 to +709
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we use this method of exiting the app versus self.begin_shutdown(ret=2), which is used by the other tasks? I recall that there were some weird Python tracebacks if we didn't exit gracefully.


def _handle_download_declined(self) -> None:
log.debug("User declined container download, shutting down")
self.exit(2)

def handle_needs_user_input_stop_others(self, req: startup.PromptRequest) -> None:
machine_name = req.req_data["name"]
log.debug(f"Prompting user to stop Podman machine '{machine_name}'")
Expand Down
Loading
Loading