Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
96 changes: 84 additions & 12 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,8 +261,17 @@ 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 }})"
name: "install-deb (${{ matrix.distro }} ${{ matrix.version }} ${{ matrix.package }})"
runs-on: ubuntu-latest
needs:
- build-deb
Expand All @@ -260,18 +280,31 @@ jobs:
include:
- distro: ubuntu
version: "22.04"
package: "dangerzone.deb"
- distro: ubuntu
version: "24.04"
package: "dangerzone.deb"
- distro: ubuntu
version: "26.04"
package: "dangerzone.deb"
- distro: ubuntu
version: "25.10"
package: "dangerzone.deb"
- distro: debian
version: bullseye
package: "dangerzone.deb"
- distro: debian
version: bookworm
package: "dangerzone.deb"
- distro: debian
version: bookworm
package: "dangerzone.deb"
- distro: debian
version: bookworm
package: "dangerzone-full.deb"
- distro: debian
version: trixie
package: "dangerzone.deb"

steps:
- name: Checkout
Expand All @@ -284,7 +317,7 @@ jobs:
- name: Download Dangerzone .deb
uses: actions/download-artifact@v8
with:
name: dangerzone.deb
name: ${{ matrix.package }}
path: "deb_dist/"

- name: Build end-user environment
Expand All @@ -293,6 +326,13 @@ jobs:
--version ${{ matrix.version }} \
build

- if: matrix.package == 'dangerzone-full.deb'
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 @@ -312,6 +352,7 @@ jobs:
matrix:
distro: ["fedora"]
version: ["42", "43", "44"]
full: [true, false]
steps:
- name: Checkout
uses: actions/checkout@v6
Expand Down Expand Up @@ -354,46 +395,77 @@ jobs:
id: image-digest
run: echo "digest=$(crane digest $(cat share/image-name.txt):latest)" >> $GITHUB_OUTPUT

- name: Cache container image archive
- if: matrix.full
name: Cache container image archive
id: cache-container
uses: actions/cache@v5
with:
path: share/container.tar
key: v1-container-tar-${{ runner.arch }}-${{ steps.image-digest.outputs.digest }}

- name: Download the container image as a tar archive
if: steps.cache-container.outputs.cache-hit != 'true'
- if: matrix.full and steps.cache-container.outputs.cache-hit != 'true'
name: Download the container image as a tar archive
env:
DANGERZONE_DEV: "1"
run: >-
poetry run dangerzone-image prepare-archive
--output share/container.tar
poetry run dangerzone-image prepare-archive --output share/container.tar

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

- name: Upload Dangerzone .rpm
- if: matrix.full
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

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

- if: matrix.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

# Reclaim some space in this step, now that the dev environment is no
# longer necessary. Previously, we encountered out-of-space issues while
# running this CI job.
- name: Reclaim some storage space
run: podman system reset -f

- name: Build end-user environment
- if: not matrix.full
name: Build (slim) end-user environment
run: |
./dev_scripts/env.py --distro ${{ matrix.distro }} \
--version ${{ matrix.version }} \
build

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

- if: not matrix.full
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 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."
)
24 changes: 24 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,31 @@ 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")
# Shutting down using exit(2) because nothing has been started yet.
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