Skip to content
Merged
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
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## [0.8.2] - 2026-07-05

### πŸ› Bug Fixes

- Docker `tool` builds now show the same clean, named steps as the Apptainer/Singularity backends (Presetup β†’ Installing prerequisites β†’ Installing tool(s) β†’ Slimming β†’ Cleaning) instead of echoing the whole single-layer `RUN` through the spinner. Full build output is still available with `-v` / `--verbose`.
- The Docker `tool` header now prints the base image (`ℹ️ Base image: …`), matching the SIF backends.

## [0.8.1] - 2026-07-03

### πŸš€ Features

- The `tool` subcommand now always installs the latest pixi and never downgrades. The `-V` / `--pixi-version` and `-L` / `--latest` options have been removed from `tool` mode (project mode keeps them).

## [0.8.0] - 2026-07-01

### πŸš€ Features
Expand Down Expand Up @@ -80,4 +93,4 @@
### πŸ’Ό Other

- Well, it works
- Removed unused code and add explaination on how to use the image correctly in the README
- Removed unused code and add explaination on how to use the image correctly in the README
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Pixitainer

![Version](https://img.shields.io/badge/Version-0.8.0-blue)
![Version](https://img.shields.io/badge/Version-0.8.2-blue)
[![Pixi Badge](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/prefix-dev/pixi/main/assets/badge/v0.json)](https://pixi.sh)
[![License](https://img.shields.io/badge/Licence-BSD--3--Clause-green)](LICENSE)
[![Forge](https://img.shields.io/badge/Forge-Prefix.dev--Forge-yellow)](https://prefix.dev/channels/raphaelribes/packages/pixitainer)
Expand Down Expand Up @@ -74,13 +74,13 @@ pixi build

```bash
# Apptainer
pixi global install pixitainer --path pixitainer-0.8.0*.conda --channel conda-forge
pixi global install pixitainer --path pixitainer-0.8.2*.conda --channel conda-forge

# Singularity
pixi global install pixitainer-singularity --path pixitainer-singularity-0.8.0*.conda --channel conda-forge
pixi global install pixitainer-singularity --path pixitainer-singularity-0.8.2*.conda --channel conda-forge

# Docker
pixi global install pixitainer-docker --path pixitainer-docker-0.8.0*.conda --channel conda-forge
pixi global install pixitainer-docker --path pixitainer-docker-0.8.2*.conda --channel conda-forge
```

## How to use
Expand Down Expand Up @@ -217,7 +217,6 @@ docker run --rm fastp:latest fastp --version
| `-o, --output` | Output `.sif` path / Docker tag. Defaults to the package name. |
| `-m, --manual` | Use a shell entrypoint instead of the tool binary. By default the package's binary is the image entrypoint, so you run the tool directly. |
| `-b, --base-image` | Base image (default: `debian:stable-slim`, a small glibc base). |
| `-V, --pixi-version` / `-L, --latest` | Pixi version selection (same as project mode). |
| `-a, --add-file SRC:DEST` | Add an extra file/folder to the image (repeatable). |
| `--post-command CMD` | Run an extra command after install (repeatable). |
| `-l, --label KEY:VALUE` | Add a custom label (repeatable). |
Expand Down Expand Up @@ -430,4 +429,4 @@ TODO:

## License

Pixitainer is licensed under the BSD 3-Clause License. See the [LICENSE](LICENSE) file for more details.
Pixitainer is licensed under the BSD 3-Clause License. See the [LICENSE](LICENSE) file for more details.
7 changes: 6 additions & 1 deletion lib/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ bootstrap_install() {
export PIXI_DIR=/opt/pixi
curl -fsSL https://pixi.sh/install.sh | bash
export PATH="/opt/pixi/bin:$PATH"

if [ ! -x /opt/pixi/bin/pixi ]; then
echo "Error: pixi installation failed (/opt/pixi/bin/pixi not found or not executable)" >&2
exit 1
fi
}

bootstrap_cleanup() {
Expand All @@ -77,4 +82,4 @@ bootstrap_cleanup() {
dnf|yum) $PKGMGR clean all ;;
zypper) zypper clean ;;
esac
}
}
2 changes: 1 addition & 1 deletion lib/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Sourced by pixi-containerize, pixi-containerize-singularity, pixi-containerize-docker.
# shellcheck disable=SC2034 # Variables are used by the sourcing scripts.

PIXITAINER_VERSION="0.8.0"
PIXITAINER_VERSION="0.8.2"

# ---------------------------------------------------------------------------
# Logging
Expand Down
50 changes: 41 additions & 9 deletions lib/tool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,6 @@ print_tool_usage() {
echo " -b, --base-image IMAGE Specify base image (default: $TOOL_DEFAULT_BASE)"
echo ""

echo "Pixi Versioning:"
echo " -V, --pixi-version VER Specify pixi version (default: same as host)"
echo " -L, --latest Force the use of the latest pixi version (cannot be used with -V)"
echo ""

echo "Advanced Modifications:"
echo " -a, --add-file SRC:DEST Add a file/folder to the image (format: source:destination)"
echo " --post-command CMD Add a command to run after install (repeatable)"
Expand Down Expand Up @@ -95,13 +90,17 @@ TOOL_DEFAULT_BASE="debian:stable-slim"

# ---------------------------------------------------------------------------
# Initialise tool-mode default variables. Call after init_common_defaults.
# Sets: TOOL_PKGS, CHANNELS, CHANNEL_FLAGS, BASE_IMAGE
# Sets: TOOL_PKGS, CHANNELS, CHANNEL_FLAGS, BASE_IMAGE, LATEST_PIXI
# ---------------------------------------------------------------------------
init_tool_defaults() {
TOOL_PKGS=()
CHANNELS=()
CHANNEL_FLAGS=""
BASE_IMAGE="$TOOL_DEFAULT_BASE"
# Tool mode always uses the latest pixi and never downgrades: install.sh
# pulls the newest release and no self-update pin is emitted. There is no
# -V/-L override here (unlike project mode).
LATEST_PIXI=true
}

# ---------------------------------------------------------------------------
Expand All @@ -116,8 +115,6 @@ parse_tool_args() {
-c|--channel) CHANNELS+=("$2"); shift 2 ;;
-o|--output) OUTPUT="$2"; shift 2 ;;
-b|--base-image) BASE_IMAGE="$2"; shift 2 ;;
-V|--pixi-version) TARGET_PIXI_VERSION="$2"; shift 2 ;;
-L|--latest) LATEST_PIXI=true; shift ;;
-a|--add-file) EXTRA_FILES+=("$2"); shift 2 ;;
--post-command) POST_COMMANDS+=("$2"); shift 2 ;;
-l|--label) LABELS+=("$2"); shift 2 ;;
Expand Down Expand Up @@ -359,6 +356,27 @@ $(emit_bootstrap_body bootstrap_cleanup)
EOF
}

# ---------------------------------------------------------------------------
# Step extractor for the tool-mode Docker build. Unlike the project-mode
# _docker_step_extractor (which surfaces docker's own "Step N/M" instructions),
# this looks only for the "STEP: …" markers echoed inside the single RUN layer,
# so the spinner shows the same named phases as the SIF backends instead of the
# giant chained RUN command.
#
# Only runtime output is matched, never the echoed RUN *instruction* line (which
# also contains the literal STEP: strings): the legacy builder streams markers
# at the start of the line, and BuildKit (--progress=plain) prefixes them with
# "#N <seconds> ".
# ---------------------------------------------------------------------------
_docker_tool_step_extractor() {
local line="$1"
if [[ "$line" == "STEP: "* ]]; then
echo "${line#STEP: }"
elif [[ "$line" =~ ^#[0-9]+\ [0-9.]+\ STEP:\ (.+)$ ]]; then
echo "${BASH_REMATCH[1]}"
fi
}

# ---------------------------------------------------------------------------
# Generate the tool-mode Dockerfile (no manifest, uses `pixi global install`).
# Sets: DOCKERFILE
Expand Down Expand Up @@ -399,13 +417,20 @@ generate_tool_dockerfile() {
# the final image, whereas deleting them in a later layer would not.
local -a chain=(
'. /opt/bootstrap.sh'
'echo "STEP: Installing system prerequisites and Pixi"'
'bootstrap_install'
'echo "STEP: Installing tool(s) globally"'
'pixi config set --global run-post-link-scripts insecure'
"$INSTALL_CMD"
)
local cmd slim
for cmd in "${POST_COMMANDS[@]}"; do chain+=("$cmd"); done
if [ ${#POST_COMMANDS[@]} -gt 0 ]; then
chain+=('echo "STEP: Running extra post commands"')
for cmd in "${POST_COMMANDS[@]}"; do chain+=("$cmd"); done
fi
chain+=('echo "STEP: Slimming image"')
while IFS= read -r slim; do chain+=("$slim"); done < <(tool_slim_steps)
chain+=('echo "STEP: Cleaning"')
chain+=('bootstrap_cleanup')

local run_block="RUN set -e; \\"
Expand Down Expand Up @@ -511,6 +536,7 @@ tool_main_docker() {
WD="$(pwd -P)" # for relative --add-file sources
TMP_DIR="$(pwd -P)/.tmp_pixitainer_docker"

log "ℹ️ Base image: $BASE_IMAGE"
log "🐳 Docker image tag: $OUTPUT"

mkdir -p "$TMP_DIR/ctx"
Expand All @@ -527,6 +553,12 @@ tool_main_docker() {
exit 0
fi

# Drive the shared Docker spinner with the STEP markers baked into the RUN
# layer, so tool builds read like the Apptainer/Singularity ones. Full
# docker output is still available via -v/--verbose.
DOCKER_STEP_EXTRACTOR=_docker_tool_step_extractor
DOCKER_SPINNER_INITIAL="Presetup (pulling base image, build context)"

run_docker_build

if [ "$KEEP_DEF" = true ]; then
Expand Down
4 changes: 2 additions & 2 deletions pixi-containerize-docker
Original file line number Diff line number Diff line change
Expand Up @@ -476,8 +476,8 @@ run_docker_build() {
"${cmd[@]}"
else
if ! run_with_spinner "Docker" \
"Setting up build context" \
_docker_step_extractor \
"${DOCKER_SPINNER_INITIAL:-Setting up build context}" \
"${DOCKER_STEP_EXTRACTOR:-_docker_step_extractor}" \
"${cmd[@]}"; then
echo "--- LOGS ---"
cat "$BUILD_LOG_FILE"
Expand Down
Loading
Loading