Introduction
Image Toolkit is a desktop app for culling a photo shoot quickly. Open a folder, flick through the thumbnails, look at any photo fitted to the window, and move on. RAW files are first-class: they are shown from the JPEG preview the camera embedded in them, so a folder of CR3 or NEF files browses about as fast as a folder of JPEGs.
It works directly on your folders. There is no import step, no catalog and no database, and version 0.1.0 never modifies your files.
What 0.1.0 does
- Opens a folder, optionally with all its subfolders, and lists every supported image in a thumbnail grid that stays smooth with thousands of photos.
- Decodes the thumbnails in the background, in parallel on all CPU cores but one, visible ones first.
- Shows any photo fitted to the window, with the previous and next ones loaded ahead of time.
- Is driven from the keyboard as much as with the mouse (see Keyboard shortcuts).
- Speaks French (the default) and English, and remembers your preferences between sessions.
What comes next
Version 0.1.0 is the first step of a roadmap towards a complete culling tool: tags stored in XMP sidecars (0.2.0), built-in blur detection (0.3.0), deletion of the rejects (0.4.0), then zoom, comparison, lens corrections and export.
The blur detection comes from the author’s command-line tool blurdetector. Its scoring is being moved into Image Toolkit’s own code as a native feature, and further work on it happens here. The project also builds on rawcleaner, a command-line tool that deletes the RAW files left without an exported counterpart after culling.
Where to start
- Installation: download a release or build from source.
- Getting started: a tour of the window.
- Building from source and Architecture if you want to work on the code.
The source code is on GitLab, licensed under MIT OR Apache-2.0.
Installation
Prebuilt binaries
Every version is built by the project’s CI and published on the GitLab Releases page, one archive per platform:
| Platform | Archive | Contains | Requirements |
|---|---|---|---|
| Linux | image-toolkit-X.Y.Z-linux-x86_64.tar.gz | image-toolkit, x86-64 | glibc 2.31 or newer (Ubuntu 20.04, Debian 11 and later) |
| Windows | image-toolkit-X.Y.Z-windows-x86_64.zip | image-toolkit.exe, x86-64 | Windows 10 or later |
| macOS | image-toolkit-X.Y.Z-macos-universal.zip | Image Toolkit.app, universal (Apple Silicon and Intel) | macOS 11 Big Sur or later |
Each archive also holds README.md, the two license files and THIRD-PARTY-LICENSES.html, the list of the third-party licenses that apply to the binary (see Licenses below). A SHA256SUMS.txt file published with each release lets you check the downloads.
There is no installer: extract the archive and run the program. On macOS, move the .app to /Applications if you like.
The binaries are not signed with a paid certificate, so each operating system warns about them on first launch. The sections below explain how to get past that.
macOS
The app is ad-hoc signed but not notarized by Apple, so Gatekeeper blocks it the first time you open it.
- Open the app once. macOS refuses to open it and shows a warning; close the warning without moving the app to the Trash.
- Go to System Settings → Privacy & Security (System Preferences → Security & Privacy → General on macOS 12 and earlier), scroll down to the message about the app, and click Open Anyway.
- Confirm. From then on the app opens normally.
Alternatively, remove the quarantine flag that macOS put on the download, from the Terminal app. Replace the placeholder with the path of the app; dragging the app from the Finder onto the Terminal window types its path for you:
xattr -dr com.apple.quarantine /path/to/the/app
Windows
SmartScreen may show “Windows protected your PC” on first launch. Click More info, then Run anyway.
Linux
The program needs:
- A graphics driver with Vulkan or OpenGL support. The interface is drawn on the GPU.
- An X11 or Wayland session. Both are supported by the same binary; the client libraries are loaded at run time and are part of any desktop installation.
xdg-desktop-portalfor the folder picker, together with a backend that provides the file chooser, for examplexdg-desktop-portal-gtk,xdg-desktop-portal-gnomeorxdg-desktop-portal-kde. GNOME and KDE Plasma install them by default. On minimal window-manager setups you may have to install them yourself;xdg-desktop-portal-wlralone does not provide the file chooser. If no portal works, the folder picker falls back tozenitywhen it is installed; with neither, the Open folder… dialog cannot appear.
Building from source
With rustup installed:
git clone https://gitlab.com/julienbltt/image-toolkit.git
cd image-toolkit
cargo run --release
The repository pins its Rust version in rust-toolchain.toml, and rustup installs it on first use. The runtime requirements above apply to your own builds too. See Building from source for details.
Licenses
Image Toolkit is licensed under MIT OR Apache-2.0, at your option.
The release binaries include third-party code under other licenses. THIRD-PARTY-LICENSES.html, in every release archive, names every dependency compiled into the binary, with its license. In particular, rawler, which reads the RAW files, is licensed under the LGPL-2.1 and is statically linked; since the complete source code of Image Toolkit is public, you can rebuild it against a modified version of rawler, as the LGPL allows.
The lens corrections planned for 0.8.0 will add lensfun: the library is licensed under the LGPL-3.0 and its lens database under CC BY-SA 3.0.
Getting started
Language. The interface starts in French. To switch to English, open the language selector at the right end of the toolbar (it shows Français) and pick English. This guide uses the English labels; in French, Open folder… reads Ouvrir un dossier… and Include subfolders reads Inclure les sous-dossiers. See Preferences & language.
The window has three parts: the toolbar at the top, the thumbnail grid (or the viewer) in the middle, and the status bar at the bottom.
Opening a folder
Click Open folder… in the toolbar, or press Ctrl+O (Cmd+O on macOS), and pick a folder. You can also drop a folder from your file manager onto the window. Its path is then shown in the toolbar.
The folder is opened in place: nothing is imported or copied, and version 0.1.0 never modifies, moves or deletes your files.
Every file with a supported extension is listed, in file-name order (case-insensitive); other files are ignored. At the next launch, the last folder you opened is opened again. If it is not available (a memory card or a network share that is not connected), the app starts empty and tries again at the following launch.
Include subfolders
With the Include subfolders checkbox ticked, the scan descends into every subfolder as well, and the photos are sorted by their path relative to the opened folder. Ticking or unticking it rescans the open folder.
Two rules apply during the scan:
- Hidden entries are skipped: files and folders whose name starts with a dot. Besides the usual hidden files, this drops the
._IMG_0001.CR3companion files macOS writes on memory cards formatted in exFAT, which carry an image extension but contain no image. - Symbolic links to folders are not followed, so a link cannot make the scan loop forever. Symbolic links to files are listed.
An entry that cannot be read, such as a subfolder without read permission, is skipped and the scan goes on. The status bar then shows a warning count; hover it to see the list.
The thumbnail grid
The thumbnails fill in as they are decoded. Decoding runs in the background, in parallel on all CPU cores but one, which stays free for the interface. The visible thumbnails are decoded first, so the interface stays responsive while a large folder loads, and the grid stays smooth with thousands of photos.
- The file name is shown under each thumbnail; hover a thumbnail to see its path inside the opened folder, which tells photos with the same name in different subfolders apart.
- A spinner marks a thumbnail that is still loading.
- A warning sign (⚠) marks a file that cannot be decoded. Hover it to read the error message. The most common cause is a RAW file without a usable embedded JPEG preview (see Supported formats).
- The thumbnail size slider in the toolbar, left of the language selector, makes the thumbnails larger or smaller. It sets the smallest size: the columns then widen so that the grid fills the width of the window. Thumbnails are decoded again at a higher resolution when they grow, so they stay sharp on high-resolution screens.
Click a thumbnail to select it. Move the selection with the arrow keys, by a screenful with Page Up and Page Down, or jump to the first or last image with Home and End. The grid scrolls to keep the selection in view.
The viewer
Double-click a thumbnail, or press Enter or Space on the selected one, to open it in the viewer. The image is fitted to the window; images smaller than the window are not enlarged beyond their actual size.
- ← and → show the previous and next images. The neighbours of the current image are loaded ahead of time, so stepping through a shoot rarely waits on decoding. When it does, the thumbnail is shown in the meantime.
- Home and End jump to the first and last images.
- Esc, Enter, Space or a double-click returns to the grid, with the selection on the last image you viewed.
For RAW files, the viewer shows the camera’s full-size embedded JPEG (see Supported formats).
The status bar
While a folder is being scanned, the status bar shows Scanning the folder…. Then it shows how many images the folder contains and, while images are being decoded, how many are still waiting. Only the thumbnails on screen and a few rows around them are queued, so that number stays small and disappears once the visible part of the grid is complete. In the viewer, the status bar also shows the position of the current image, for example 12 / 340.
What is not there yet
Tagging, filtering, user-chosen sorting, blur detection, zoom and deletion are planned for later versions; see the roadmap.
Keyboard shortcuts
On macOS, Cmd replaces Ctrl.
General
| Shortcut | Action |
|---|---|
| Ctrl+O | Open a folder |
Thumbnail grid
| Shortcut | Action |
|---|---|
| ← → ↑ ↓ | Move the selection |
| Page Up Page Down | Move the selection by a screenful |
| Home | Select the first image |
| End | Select the last image |
| Enter or Space | Open the selected image in the viewer |
| Double-click | Open that image in the viewer |
Viewer
| Shortcut | Action |
|---|---|
| ← | Previous image |
| → | Next image |
| Home | First image |
| End | Last image |
| Esc, Enter or Space | Back to the grid |
| Double-click | Back to the grid |
Supported formats
Files are recognized by their extension, case-insensitively (IMG_0001.CR3 and img_0001.cr3 are both listed). Files with any other extension are ignored.
RAW
| Brand | Extensions |
|---|---|
| Canon | cr2 cr3 |
| Fujifilm | raf |
| Leica | rwl |
| Nikon | nef nrw |
| Sony | arw srf sr2 |
| Cross-brand | dng, Adobe’s open RAW format, used by many brands |
Canon’s older CRW format is not supported: the RAW library used here cannot extract its embedded preview.
Standard images
jpg jpeg png tif tiff bmp webp
How images are displayed
RAW files are displayed from the JPEG previews the camera embedded in them; nothing is demosaiced. Reading an embedded JPEG takes a fraction of the time a RAW development would, which is what makes browsing a RAW shoot fast.
- CR3 (Canon). A CR3 file holds several JPEG renditions of the shot. Thumbnails use the 1620×1080 preview, which decodes in a few milliseconds; the viewer uses the full-size embedded JPEG.
- Other RAW formats. The embedded preview is extracted with rawler and used for both the thumbnail and the viewer. Its resolution depends on the camera: often full size, sometimes smaller.
- Standard images are decoded directly.
The orientation recorded by the camera (EXIF orientation) is applied, so portrait shots are shown upright. Images are also reduced to the size the screen needs: thumbnails to the size of their cell on screen (between 256 and 1024 pixels on the long edge), the viewer image to the resolution of your monitor (at most 4096 pixels).
Keep in mind what an embedded preview is: the camera’s own JPEG rendering of the shot, with its picture style and white balance as set at capture time. That is what you see in Image Toolkit, not a neutral development of the RAW data, and the level of detail is that of the preview the camera chose to embed.
When a file cannot be displayed
A file that cannot be read or decoded gets a warning sign (⚠) in the grid instead of a thumbnail; hover it to read the error message. The viewer shows the message in place of the image. The rest of the folder is not affected.
Since RAW files are only shown through their embedded preview, a RAW file without a usable embedded JPEG cannot be displayed. For example, CR3 files shot with Canon’s HDR PQ mode turned on carry HEIF previews instead of JPEG ones, which Image Toolkit does not decode.
Preferences & language
Interface language
The interface is available in French and English. It starts in French. To change it, open the language selector at the right end of the toolbar and pick a language. Each language is listed under its own name (Français, English), so you can find yours even in an interface you cannot read.
This documentation is in English only.
What is remembered
There is no separate preferences window: each setting is changed where it is used, and Image Toolkit remembers it for the next session.
| Setting | Changed with | Default |
|---|---|---|
| Interface language | The language selector in the toolbar | French |
| Last opened folder | Open folder… or dropping a folder on the window; it is opened again at launch when available | None |
| Include subfolders | The checkbox in the toolbar | Off |
| Thumbnail size | The slider in the toolbar | Towards the small end of the slider |
| Window size and position | Resizing or moving the window | 1280 × 800 |
Where the settings are stored
The settings are saved automatically by eframe, the GUI framework the app is built on, in a file named app.ron:
| Platform | Location |
|---|---|
| Linux | ~/.local/share/image-toolkit/app.ron (or under $XDG_DATA_HOME when it is set) |
| macOS | ~/Library/Application Support/image-toolkit/app.ron |
| Windows | %APPDATA%\image-toolkit\data\app.ron |
Deleting that file restores the defaults. Your photos and folders are never written to.
Roadmap
Image Toolkit grows one feature set per version until 1.0. Everything on this page except 0.1.0 is planned, not available; the order and the contents may still change. The changelog records what each released version actually contains.
0.1.0: browse (current version)
Open a folder in place, thumbnail grid, single-image viewer, keyboard navigation, French and English interface. Files are never modified. See Getting started.
0.2.0: display modes
Three ways to look at a shoot, switched with the keyboard (as in Lightroom) or from the toolbar:
- Grid (G): the thumbnail grid.
- Loupe (E): one image fitted to the window, with a filmstrip of every image of the folder along the bottom; the current image is highlighted in it, and clicking a thumbnail shows that image.
- Full screen (F): the system full screen, with the image alone on a black background; Esc or F returns to the previous mode.
0.3.0: tags, filters, sorting
- Three tags: Favorite, Neutral and Rejected.
- Tags stored in Adobe-style XMP sidecar files next to the photos (
IMG_0001.xmpforIMG_0001.CR3). This is the first version that writes files to your folders. - Filters, sorting and multi-selection.
0.4.0: blur detection
The sharpness scoring of blurdetector, integrated into the app as a native feature. From this version on, blur detection evolves as part of Image Toolkit.
- A sharpness score for every photo.
- Adjustable parameters: threshold, grid size, top fraction, pre-blur and long edge.
- Hide the blurry photos.
- Reject all the blurry photos in one batch.
0.5.0: deletion
- Delete the selected images.
- Deleted files go to the operating system’s trash by default; permanent deletion is available as an option.
- A confirmation lists every file that will be removed, sidecars included.
0.6.0: closer inspection
100% zoom to check focus, and an EXIF panel.
0.7.0: comparison
A sharpness map overlay showing where the image is sharp, and side-by-side comparison of 2 to 4 images.
0.8.0: lens corrections
Lens corrections applied on display, from the lensfun database: distortion and vignetting.
0.9.0: export
Corrected export from a 16-bit RAW development to JPEG, PNG or TIFF.
0.10.0: hardening
Hardening and performance work; chromatic aberration correction if time allows.
1.0.0: stable
The first stable release.
Changelog
All notable changes to this project are documented in this file. The format
is based on Keep a Changelog and the
project follows Semantic Versioning. A new version
section pushed to main triggers the release pipeline.
[Unreleased]
[0.1.0] - 2026-10-22
First release: a fast photo viewer for culling, straight from RAW files. Files are only read; tagging, blur detection and deletion arrive in the next versions.
Added
- Open a folder of photos in place (toolbar button, Ctrl/Cmd+O, or drag and
drop a folder onto the window), optionally including its subfolders; hidden
files, macOS
._*companion files and symbolic links to directories are skipped - Supported formats: Canon CR3 and CR2, Fujifilm RAF, Leica RWL, Nikon NEF and NRW, Sony ARW, SRF and SR2, DNG, plus JPEG, PNG, TIFF, BMP and WebP (extensions matched case-insensitively)
- RAW files are shown from their embedded JPEG previews, never demosaiced: for CR3, the 1620×1080 preview feeds the thumbnails (≈ 10 ms each) and the full-size JPEG the viewer; orientation metadata is applied
- Virtualized thumbnail grid that stays fluid with thousands of photos: thumbnails decoded in the background on every core except one, the visible rows first, sharp at any size on high-DPI screens, with a GPU memory budget; the thumbnail size slider sets the minimum size and the columns fill the window width
- Single-image viewer, fit to the window without enlarging past 100 %, with the neighbouring images decoded ahead
- Keyboard navigation: arrow keys, Page Up/Down, Home/End, Enter or Space to open the viewer, Left/Right in the viewer, Esc to go back to the grid; double-click opens the viewer and returns from it
- Unreadable or corrupt files show a warning placeholder with the error as a tooltip instead of interrupting anything; unreadable subfolders are listed in the status bar
- French and English interface (French by default); language, last folder, subfolder option, thumbnail size and window geometry are remembered
- Release archives for Linux x86_64, Windows x86_64 and macOS universal
(Apple Silicon and Intel,
.app), with third-party license notices - mdBook documentation published on GitLab Pages with each release
Building from source
Toolchain
Install Rust with rustup. The repository pins its toolchain in rust-toolchain.toml: Rust 1.99.1 with the rustfmt and clippy components. Inside the checkout, rustup selects that version and downloads it on first use, so there is nothing to configure. If your rustup is set up not to install toolchains automatically, run rustup toolchain install once in the repository.
CI uses the same pinned version.
Running the app
git clone https://gitlab.com/julienbltt/image-toolkit.git
cd image-toolkit
cargo run --release
Always use release builds to actually use the app: image decoding is much slower in debug builds. The binary is written to target/release/image-toolkit (image-toolkit.exe on Windows).
The runtime requirements of the prebuilt binaries apply to your own builds too: on Linux, a Vulkan or OpenGL driver, an X11 or Wayland session, and xdg-desktop-portal for the folder picker.
Everyday commands
cargo check # fast validation
cargo test # unit tests
cargo clippy --all-targets -- -D warnings # lints; CI fails on any warning
cargo fmt # formatting (CI runs cargo fmt --check)
Cargo.lock is committed and CI builds with --locked: commit it whenever a dependency or the version changes.
The unit tests need no test data. A larger check decodes every photo of a local folder, kept out of the repository; it is ignored by default:
IMAGE_TOOLKIT_SAMPLES=/path/to/photos cargo test --release sample_corpus -- --ignored --nocapture
Coding rules
The coding guidelines are in rules.md at the repository root. In short: correctness and clarity first, no panicking shortcuts such as unwrap(), errors are handled rather than discarded, full-word names, and comments only to explain a non-obvious why.
Working on this documentation
This book lives in docs/ and is built with mdBook 0.5 (a later major version may reject the configuration):
cargo install mdbook --version 0.5.4 --locked
mdbook serve docs --open # live preview, rebuilt on every change
mdbook build docs # output in docs/book/ (ignored by git)
The Changelog page includes CHANGELOG.md from the repository root, so it is edited there. The published site is only rebuilt for releases (see Releasing), so build the book locally after changing it: a broken page would otherwise go unnoticed until the next release.
Architecture
This page is a high-level overview. The source code in src/ is the reference for the details; each module starts with a comment describing its role. AGENTS.md, at the root of the repository, keeps the detailed notes for contributors and coding agents: mechanisms, decisions and conventions.
Layout
Image Toolkit is a single Cargo crate (no workspace) that builds one binary, image-toolkit. The interface is built with egui through its eframe framework, drawn on the GPU with wgpu.
| Module | Role |
|---|---|
main.rs | Creates the window and starts the app. |
app.rs | The interface: toolbar, thumbnail grid, viewer, status bar, keyboard handling, saved preferences. |
library.rs | Scans the opened folder: which files are listed, in which order. |
decode.rs | Turns a file into pixels for display: embedded JPEG previews of RAW files (through rawler), standard images (through image), orientation, reduction to the displayed size. |
loader.rs | The background decoding queue and its worker threads. |
i18n.rs | Every interface text, in French and English. |
How the pieces fit
- The interface thread draws every frame and never decodes images itself.
- The folder scan runs on a thread of its own, so opening a large tree does not freeze the window.
- Decoding runs on a pool of worker threads, one per CPU core minus one. At every frame the interface tells the loader which images it needs, most urgent first: the visible thumbnails, then a few rows around them; in the viewer, the current image, then its neighbours. Workers always take the most urgent request. Requests that scrolled out of view before a worker reached them are dropped, which keeps a fast scroll through thousands of photos from queueing thousands of decodes.
- The grid is virtualized: only the visible rows are laid out and drawn, and decoded thumbnails beyond a fixed budget are evicted, farthest from view first, and decoded again when needed.
- Errors stay per file. A file that fails to decode, even one that makes rawler panic, becomes an error placeholder; it never stops the scan or the other decodes.
Files are only ever read in 0.1.0. Writing (XMP sidecars, deletion) arrives with the versions listed in the roadmap.
Blur detection
The blur detection planned for 0.4.0 comes from the author’s command-line tool blurdetector. Its code is being moved into this crate rather than pulled in as a dependency, so that from then on blur detection is developed here, as a native feature of Image Toolkit.
Translations
Every fixed interface text is a variant of one enumeration in i18n.rs, translated in both languages in the same place. A missing translation is therefore a compile error rather than a blank label. Texts that contain a number, such as the image count with its plural rules, are small functions in the same file.
Continuous integration
The GitLab CI pipeline is defined in .gitlab-ci.yml. This page describes what it does; the file itself is the reference for the details.
The project runs on GitLab.com’s Free tier, which allows 400 compute minutes a month on GitLab-hosted runners. That budget shapes the pipeline: everyday pushes only run the quick checks, and the three release builds run only when a version is actually released.
On every push
| Check | What it does |
|---|---|
| Format | cargo fmt --check |
| Lint | cargo clippy with -D warnings: any warning fails the pipeline |
| Tests | cargo test (headless: the tests that need a GPU or a local photo corpus are skipped) |
| Documentation | Builds this book with mdBook 0.5.4; a page listed in SUMMARY.md but missing fails the build |
| Third-party licenses | Generates the license list with cargo-about and fails if a dependency uses a license not accepted in about.toml |
Cargo commands run with --locked, so an out-of-date Cargo.lock fails the pipeline instead of being silently updated.
On a release
When a push to main brings a new version section in CHANGELOG.md, the pipeline also builds, publishes and tags the release. Whether a push is a release is only known once the release:detect job has read the changelog, so the builds live in a child pipeline (ci/release-pipeline.yml) that this job generates and that the release job starts once every check has passed: an ordinary push spends its minutes on the checks only. The trigger and the steps are described in Releasing.
On other branches and in merge requests, a manual builds job runs the same three builds without releasing anything, to try a change to the build before merging it.
The builds all run on Linux runners:
| Platform | How it is built |
|---|---|
| Linux x86-64 | Built with cargo-zigbuild against glibc 2.31, so the binary runs on distributions as old as Ubuntu 20.04 and Debian 11 rather than only on the runner’s own glibc. |
| Windows x86-64 | Cross-compiled with the mingw-w64 toolchain (x86_64-pc-windows-gnu target). |
| macOS universal2 | Cross-compiled for Apple Silicon and Intel in one binary with cargo-zigbuild, using the official cargo-zigbuild container image, which bundles the macOS 11.3 SDK. A GUI app links against Apple’s system frameworks (AppKit, Metal…), whose SDK stubs Zig alone does not provide. The binary is packaged as an ad-hoc signed .app bundle that runs on macOS 11 and later. |
The ad-hoc signature is what lets the app run on Apple Silicon at all, which refuses unsigned native code. It is not a Developer ID signature, hence the Gatekeeper warning described in Installation.
Documentation deployment
This book is deployed to GitLab Pages by the pipeline of a release tag, so the published documentation always matches the latest released version rather than unreleased work on main. A manual job on main pipelines can republish it in between, for example to fix a typo. See Releasing.
Releasing
CHANGELOG.md drives the releases. It follows the Keep a Changelog format: a ## [Unreleased] section at the top, then one ## [X.Y.Z] - YYYY-MM-DD section per released version, newest first. There is no release command to run and no tag to push: writing a new version section and pushing it to main is the release.
Day to day
Record every user-visible change under ## [Unreleased], in the same commit as the change, grouped under the usual Keep a Changelog headings (### Added, ### Changed, ### Fixed, ### Removed…).
Pushes that do not add a version section only run the checks described in Continuous integration.
Cutting a release
-
Rename the
[Unreleased]section to the new version and today’s date, and add a fresh, empty[Unreleased]section above it:# Changelog ## [Unreleased] ## [0.2.0] - 2026-12-01 ### Added - Three display modes: grid, loupe with a filmstrip, full screen -
Bump the version in
Cargo.tomlto the same number (version = "0.2.0"), then runcargo checkso thatCargo.lockrecords it too. CI builds with--lockedand fails on an outdatedCargo.lock. -
Commit both files and push to
main, directly or by merging a merge request.
What CI does
The pipeline of a push to main reads the topmost version heading of CHANGELOG.md, ignoring [Unreleased]. That heading must be exactly ## [X.Y.Z] - YYYY-MM-DD: any other level-2 heading in its place (a typo in the date, missing brackets) fails the pipeline rather than being skipped. If the tag vX.Y.Z for that version does not exist yet, the push is a release, and the pipeline:
- Runs the usual checks.
- Checks that the
versioninCargo.tomlequalsX.Y.Zand thatX.Y.Zis greater than every existingvX.Y.Ztag, and fails otherwise. - Builds Linux, Windows and macOS release binaries (see Continuous integration).
- Uploads the release archives to the project’s generic package registry.
- Creates the tag
vX.Y.Zon the pushed commit and the GitLab Release, whose notes are the## [X.Y.Z]section of the changelog and whose assets link to the uploaded archives.
The new tag starts a pipeline of its own, which deploys the documentation (see below).
When the tag already exists, the version is considered released and the push only runs the checks. That makes later pushes safe: they never release the same version twice.
When a release fails
The tag is created only after the builds and the uploads have succeeded. If any earlier step fails, for example on a version mismatch, nothing is tagged: fix the problem, push to main again, and the release runs again.
If the last job, release:publish, fails after the tag was created but before the GitLab Release exists, retry that job: it notices the tag on its own commit and creates the missing release. A new push would not help, because it treats a tagged version as released.
Do not create vX.Y.Z tags by hand. CI treats an existing tag as an already released version, so a hand-made tag would skip the builds and the GitLab Release.
Documentation
The documentation site at https://julienbltt.gitlab.io/image-toolkit/ is deployed by the pipeline of each release tag. It therefore describes the latest released version, not the work in progress on main.
To republish it between releases, for example after fixing a typo, run the manual documentation job from the latest pipeline on main. Keep in mind that it publishes the state of main, including any documentation of unreleased features.
Do not re-run the documentation job of an older tag’s pipeline: it would put that older version of the site back online.