Compare commits

..
5 changed files with 104 additions and 15 deletions

No files matched your search

@@ -98,6 +98,17 @@ describe('renderTrayIcon', () => {
assert.deepEqual(pixel(bitmap, 32, 6, 16), [0xff, 0xff, 0xff, 0xff]);
});
test('keeps the Linux monochrome pixmap within 256 pixels', () => {
const result = renderTrayIcon(FakeNativeImage.fromBitmap(256), 'white', false, 'linux');
const [representation] = result.representations.values();
assert.equal(representation.width, 256);
assert.equal(representation.height, 256);
const bitmap = result.toBitmap();
assert.deepEqual(pixel(bitmap, 256, 128, 50), [0xff, 0xff, 0xff, 0xff]);
assert.deepEqual(pixel(bitmap, 256, 128, 128), [0, 0, 0, 0]);
assert.deepEqual(pixel(bitmap, 256, 20, 128), [0, 0, 0, 0]);
});
test('skips representations whose bitmap does not match their size', () => {
const base = FakeNativeImage.fromBitmap(16);
base.toBitmap = () => Buffer.alloc(3);
+38 -1
View File
@@ -18,6 +18,7 @@ const MIN_GLYPH_CONTRAST = 32;
const GLYPH_CUTOUT_START = 0.1;
const GLYPH_CUTOUT_END = 0.5;
const LINUX_MONOCHROME_CANVAS_RATIO = 22 / 16;
const LINUX_MAX_PIXMAP_SIZE = 256;
const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
const DOT_BGR: Bgr = [0x00, 0x00, 0xdd];
const TEMPLATE_DOT_BGR: Bgr = [0x00, 0x00, 0x00];
@@ -149,6 +150,36 @@ function cropTrayGlyph(buffer: Buffer, width: number, height: number): TrayBitma
return {buffer: target, width: size, height: size};
}
function downscaleTrayBitmap(bitmap: TrayBitmap, size: number): TrayBitmap {
if (bitmap.width <= size) return bitmap;
const scale = bitmap.width / size;
const buffer = Buffer.alloc(size * size * 4);
const sums = [0, 0, 0, 0];
for (let y = 0; y < size; y++) {
const top = y * scale;
const bottom = top + scale;
for (let x = 0; x < size; x++) {
const left = x * scale;
const right = left + scale;
sums.fill(0);
for (let sy = Math.floor(top); sy < Math.min(bitmap.height, Math.ceil(bottom)); sy++) {
const weightY = Math.min(bottom, sy + 1) - Math.max(top, sy);
for (let sx = Math.floor(left); sx < Math.min(bitmap.width, Math.ceil(right)); sx++) {
const weight = weightY * (Math.min(right, sx + 1) - Math.max(left, sx));
const offset = (sy * bitmap.width + sx) * 4;
for (let channel = 0; channel < 4; channel++) {
sums[channel] += bitmap.buffer[offset + channel] * weight;
}
}
}
for (let channel = 0; channel < 4; channel++) {
buffer[(y * size + x) * 4 + channel] = Math.round(sums[channel] / (scale * scale));
}
}
}
return {buffer, width: size, height: size};
}
function addMonochromeTrayMargins(bitmap: TrayBitmap, size: number): TrayBitmap {
const inset = Math.floor((size - bitmap.width) / 2);
const buffer = Buffer.alloc(size * size * 4);
@@ -210,7 +241,13 @@ export function renderTrayIcon(
appearance === 'template' ? TEMPLATE_DOT_BGR : DOT_BGR,
);
}
if (linuxMonochrome) bitmap = addMonochromeTrayMargins(bitmap, Math.ceil(logicalCanvasSize * scaleFactor));
if (linuxMonochrome) {
const canvasSize = Math.min(Math.ceil(logicalCanvasSize * scaleFactor), LINUX_MAX_PIXMAP_SIZE);
bitmap = addMonochromeTrayMargins(
downscaleTrayBitmap(bitmap, Math.floor(canvasSize / LINUX_MONOCHROME_CANVAS_RATIO)),
canvasSize,
);
}
result.addRepresentation({scaleFactor, ...bitmap});
}
if (result.isEmpty()) {
@@ -58,6 +58,8 @@ If both answer, skip ahead. On a Mac or a Windows PC, install [Docker Desktop](h
Debian and Ubuntu need Docker's apt repository from [Install Docker Engine](https://docs.docker.com/engine/install/), because the distribution's own `docker.io` package ships no Compose plugin. Follow the [post-installation steps](https://docs.docker.com/engine/install/linux-postinstall/) to add your user to the `docker` group, then log out and back in so the commands below run without `sudo`.
Podman 5 works too, with `docker-compose` installed so `podman compose` has a provider. The installer picks Podman when `docker` is missing or does not answer. Podman logs every healthcheck run to the journal by default, so with Podman the installer writes `fluxer-healthcheck-events.conf` to `~/.config/containers/containers.conf.d/` (or `/etc/containers/containers.conf.d/` as root) to turn that off. Health checks still run. If you set `healthcheck_events` yourself, the installer leaves it alone.
Linux also needs `curl` and `openssl`, which the steps below use:
```bash
@@ -184,7 +186,7 @@ Leave `FLUXER_PUBLIC_ORIGIN` commented out, or keep it consistent with the publi
| `--update` | Upgrade: record, back up, refresh, pull, recreate, verify |
| `--rollback` | Restore the images and stack files that the newest `--update` run recorded |
| `--allow-root` | Permit running as root |
| `--engine <command>` | Container engine to drive. Default `docker`, or `podman` when `docker` is absent |
| `--engine <command>` | Container engine to drive. Default `docker`, or `podman` when `docker` is absent or does not answer |
Run `--dry-run` first to see what a set of flags does. The PowerShell script takes the same flags as `-Domain`, `-Email` and so on, and has no `-AllowRoot`. [Upgrading](/operator/upgrading/#run-the-upgrade) has the flags that only `--update` and `--rollback` read, and the exit codes.
+9 -1
View File
@@ -240,7 +240,7 @@ function Show-FluxerUsage {
Write-FluxerLine ' -Domain <host> Hostname the instance answers on. Prompted when absent.'
Write-FluxerLine ' -Email <address> Contact email for web push. Default: admin@<domain>.'
Write-FluxerLine ' -Engine <command> Container engine to drive. Default: docker, or podman when'
Write-FluxerLine ' docker is absent.'
Write-FluxerLine ' docker is absent or does not answer.'
Write-FluxerLine ' -Dir <path> Working directory. Default: the fluxer folder in the home'
Write-FluxerLine ' directory, or the working directory itself when -Update or'
Write-FluxerLine ' -Rollback is given and it holds an instance.'
@@ -385,6 +385,14 @@ function Invoke-FluxerPreflight {
$script:FluxerEngine = $Engine
} elseif ($null -ne (Get-Command docker -ErrorAction SilentlyContinue)) {
$script:FluxerEngine = 'docker'
if ($null -ne (Get-Command podman -ErrorAction SilentlyContinue) -and (Invoke-FluxerCapture @('info')).Code -ne 0) {
$script:FluxerEngine = 'podman'
if ((Invoke-FluxerCapture @('info')).Code -eq 0) {
Write-FluxerLine 'docker does not answer and podman does, so this run drives podman. Pass -Engine docker to use docker instead.'
} else {
$script:FluxerEngine = 'docker'
}
}
} elseif ($null -ne (Get-Command podman -ErrorAction SilentlyContinue)) {
$script:FluxerEngine = 'podman'
} else {
+43 -12
View File
@@ -214,7 +214,7 @@ Options:
--domain <host> Hostname the instance answers on. Prompted when absent.
--email <address> Contact email for web push. Default admin@<domain>.
--engine <command> Container engine to drive. Default docker, or podman
when docker is absent.
when docker is absent or does not answer.
--dir <path> Working directory. Default ~/fluxer, or the working
directory itself when --update or --rollback is given
and it holds an instance.
@@ -561,6 +561,10 @@ fluxer_resolve_engine() {
fluxer_engine=$opt_engine
elif command -v docker >/dev/null 2>&1; then
fluxer_engine='docker'
if command -v podman >/dev/null 2>&1 && ! docker info >/dev/null 2>&1 && podman info >/dev/null 2>&1; then
fluxer_engine='podman'
fluxer_say 'docker does not answer and podman does, so this run drives podman. Pass --engine docker to use docker instead.'
fi
elif command -v podman >/dev/null 2>&1; then
fluxer_engine='podman'
else
@@ -576,18 +580,11 @@ fluxer_preflight() {
fluxer_fail 2 'Running as root. Use an account in the docker group, or pass --allow-root.'
fi
fluxer_resolve_engine
if ! $fluxer_engine compose version >/dev/null 2>&1; then
fluxer_fail 2 "$fluxer_engine has no compose subcommand. $(fluxer_docker_hint)"
fi
if $fluxer_engine compose version 2>/dev/null | grep -q '^podman-compose version'; then
fluxer_fail 2 "$fluxer_engine compose runs podman-compose, which lacks compose ps -a and compose config --images that this script runs. $(fluxer_docker_hint)"
fi
fluxer_engine_report=$($fluxer_engine --version 2>/dev/null)
fluxer_engine_kind=$(printf '%s\n' "$fluxer_engine_report" | sed -n 's/^\([A-Za-z][A-Za-z]*\) version .*/\1/p' | tr 'A-Z' 'a-z')
fluxer_engine_version=$(printf '%s\n' "$fluxer_engine_report" | sed -n 's/^[A-Za-z][A-Za-z]* version v\{0,1\}\([0-9][0-9.]*\).*/\1/p')
fluxer_compose_version=$($fluxer_engine compose version --short 2>/dev/null | sed -n 's/^v\{0,1\}\([0-9][0-9.]*\).*/\1/p')
if [ -z "$fluxer_engine_version" ] || [ -z "$fluxer_compose_version" ]; then
fluxer_fail 2 "Cannot read the engine and Compose versions from $fluxer_engine. It answered \"$fluxer_engine_report\" to --version."
if [ -z "$fluxer_engine_version" ]; then
fluxer_fail 2 "Cannot read the engine version from $fluxer_engine. It answered \"$fluxer_engine_report\" to --version."
fi
case $fluxer_engine_kind in
podman)
@@ -600,7 +597,17 @@ fluxer_preflight() {
;;
esac
if ! fluxer_version_ge "$fluxer_engine_version" "$fluxer_min_engine"; then
fluxer_fail 2 "$fluxer_engine_label $fluxer_engine_version with Compose $fluxer_compose_version. Fluxer needs $fluxer_engine_label $fluxer_min_engine or newer."
fluxer_fail 2 "$fluxer_engine_label $fluxer_engine_version. Fluxer needs $fluxer_engine_label $fluxer_min_engine or newer. $(fluxer_docker_hint)"
fi
if ! $fluxer_engine compose version >/dev/null 2>&1; then
fluxer_fail 2 "$fluxer_engine has no compose subcommand. $(fluxer_docker_hint)"
fi
if $fluxer_engine compose version 2>/dev/null | grep -q '^podman-compose version'; then
fluxer_fail 2 "$fluxer_engine compose runs podman-compose, which lacks compose ps -a and compose config --images that this script runs. $(fluxer_docker_hint)"
fi
fluxer_compose_version=$($fluxer_engine compose version --short 2>/dev/null | sed -n 's/^v\{0,1\}\([0-9][0-9.]*\).*/\1/p')
if [ -z "$fluxer_compose_version" ]; then
fluxer_fail 2 "Cannot read the Compose version from $fluxer_engine compose version --short."
fi
if ! fluxer_version_ge "$fluxer_compose_version" "$FLUXER_MIN_COMPOSE"; then
fluxer_fail 2 "$fluxer_engine_label $fluxer_engine_version with Compose $fluxer_compose_version. Fluxer needs Compose $FLUXER_MIN_COMPOSE or newer."
@@ -837,7 +844,11 @@ fluxer_print_plan() {
fluxer_say " email admin@$opt_domain, derived by compose"
fluxer_plan_non_secret=$((fluxer_plan_non_secret - 1))
fi
fluxer_say ' action download the stack files, write .env, start the stack'
if [ "$opt_no_start" -eq 1 ]; then
fluxer_say ' action download the stack files, write .env, leave the stack stopped'
else
fluxer_say ' action download the stack files, write .env, start the stack'
fi
fluxer_say " .env keys $fluxer_plan_non_secret non-secret values and $(fluxer_secret_keys | wc -l | tr -d ' ') secrets"
fluxer_say ' files docker-compose.yml docker-compose.proxy.yml tunnel.compose.yml Caddyfile .env.example'
if [ -e "$opt_dir/.env" ]; then
@@ -2195,9 +2206,29 @@ fluxer_run_rollback() {
exit 0
}
# Podman writes a health_status event to the journal for every healthcheck run,
# with every label of the container in it, and this stack runs two dozen checks
# every ten seconds. Compose and this script read health from inspect, so the
# events have no reader here. An operator who set the key either way keeps it.
fluxer_quiet_podman_health_events() {
[ "$fluxer_engine_kind" = 'podman' ] || return 0
[ "$opt_dry_run" -eq 0 ] || return 0
if [ "$(id -u)" -eq 0 ]; then
fluxer_conf_dir=/etc/containers/containers.conf.d
else
fluxer_conf_dir=${XDG_CONFIG_HOME:-$HOME/.config}/containers/containers.conf.d
fi
if grep -qs '^[[:space:]]*healthcheck_events[[:space:]]*=' /usr/share/containers/containers.conf /etc/containers/containers.conf /etc/containers/containers.conf.d/*.conf "${XDG_CONFIG_HOME:-$HOME/.config}/containers/containers.conf" "${XDG_CONFIG_HOME:-$HOME/.config}/containers/containers.conf.d/"*.conf; then
return 0
fi
mkdir -p "$fluxer_conf_dir" && printf '[engine]\nhealthcheck_events = false\n' > "$fluxer_conf_dir/fluxer-healthcheck-events.conf" || return 0
fluxer_say "Wrote $fluxer_conf_dir/fluxer-healthcheck-events.conf, which stops Podman from logging every healthcheck run to the journal. Health checks still run."
}
fluxer_preflight
fluxer_validate_options
fluxer_resolve_values
fluxer_quiet_podman_health_events
if [ "$opt_update" -eq 1 ]; then
fluxer_refresh_installer