Miniflux 2.2.19 + container.py migration + ty typechecker #331
10 changed files with 160 additions and 53 deletions
Miniflux 2.2.19 upgrade, container.py migration, ty typechecker, mise version tracking
- Upgrade miniflux from 2.2.17 to 2.2.19 (SSRF hardening, performance) - Migrate miniflux from Dockerfile to native Dagger container.py build - Refactor alpine_runtime() with create_user param for existing users - Add ty Python typechecker to prek hooks with Dagger SDK config - Pin all mise.toml tool versions (no more "latest") - Add type: mise to service-versions.yaml for development tool tracking - Add mise-specific review guidance to service-review script and docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
commit
99480ce4f3
|
|
@ -1,35 +0,0 @@
|
||||||
# Miniflux RSS feed reader
|
|
||||||
# Based on upstream packaging/docker/alpine/Dockerfile
|
|
||||||
|
|
||||||
ARG CONTAINER_APP_VERSION=2.2.17
|
|
||||||
ARG MINIFLUX_VERSION=${CONTAINER_APP_VERSION}
|
|
||||||
|
|
||||||
FROM golang:alpine3.22 AS build
|
|
||||||
|
|
||||||
ARG MINIFLUX_VERSION
|
|
||||||
RUN apk add --no-cache build-base git make
|
|
||||||
|
|
||||||
# Clone specific version
|
|
||||||
RUN git clone --depth 1 --branch ${MINIFLUX_VERSION} \
|
|
||||||
https://forge.ops.eblu.me/mirrors/miniflux.git /go/src/app
|
|
||||||
|
|
||||||
WORKDIR /go/src/app
|
|
||||||
RUN make miniflux
|
|
||||||
|
|
||||||
FROM alpine:3.22
|
|
||||||
|
|
||||||
ARG CONTAINER_APP_VERSION
|
|
||||||
LABEL org.opencontainers.image.title="Miniflux"
|
|
||||||
LABEL org.opencontainers.image.description="Miniflux is a minimalist and opinionated feed reader"
|
|
||||||
LABEL org.opencontainers.image.version="${CONTAINER_APP_VERSION}"
|
|
||||||
LABEL org.opencontainers.image.source="https://forge.eblu.me/eblume/blumeops"
|
|
||||||
LABEL org.opencontainers.image.vendor="blumeops"
|
|
||||||
|
|
||||||
EXPOSE 8080
|
|
||||||
ENV LISTEN_ADDR=0.0.0.0:8080
|
|
||||||
|
|
||||||
RUN apk --no-cache add ca-certificates tzdata
|
|
||||||
COPY --from=build /go/src/app/miniflux /usr/bin/miniflux
|
|
||||||
|
|
||||||
USER 65534
|
|
||||||
CMD ["/usr/bin/miniflux"]
|
|
||||||
61
containers/miniflux/container.py
Normal file
61
containers/miniflux/container.py
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
"""Miniflux RSS feed reader — native Dagger build.
|
||||||
|
|
||||||
|
Two-stage build: Go (backend with PIE), Alpine (runtime).
|
||||||
|
Source cloned from forge mirror.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import dagger
|
||||||
|
from dagger import dag
|
||||||
|
|
||||||
|
from blumeops.containers import (
|
||||||
|
alpine_runtime,
|
||||||
|
clone_from_forge,
|
||||||
|
oci_labels,
|
||||||
|
)
|
||||||
|
|
||||||
|
VERSION = "2.2.19"
|
||||||
|
|
||||||
|
|
||||||
|
async def build(src: dagger.Directory) -> dagger.Container:
|
||||||
|
source = clone_from_forge("miniflux", VERSION)
|
||||||
|
|
||||||
|
# Stage 1: Build Go backend (PIE mode, matching upstream Makefile)
|
||||||
|
ldflags = f"-s -w -X 'miniflux.app/v2/internal/version.Version={VERSION}'"
|
||||||
|
backend = (
|
||||||
|
dag.container()
|
||||||
|
.from_("golang:alpine3.22")
|
||||||
|
.with_exec(["apk", "add", "--no-cache", "build-base", "git"])
|
||||||
|
.with_directory("/app", source)
|
||||||
|
.with_workdir("/app")
|
||||||
|
.with_env_variable("CGO_ENABLED", "1")
|
||||||
|
.with_exec(
|
||||||
|
[
|
||||||
|
"go",
|
||||||
|
"build",
|
||||||
|
"-buildmode=pie",
|
||||||
|
f"-ldflags={ldflags}",
|
||||||
|
"-o",
|
||||||
|
"/miniflux",
|
||||||
|
".",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Stage 2: Runtime (uses Alpine's built-in nobody:65534)
|
||||||
|
runtime = alpine_runtime(
|
||||||
|
extra_apk=["ca-certificates", "tzdata"],
|
||||||
|
create_user=False,
|
||||||
|
)
|
||||||
|
runtime = oci_labels(
|
||||||
|
runtime,
|
||||||
|
title="Miniflux",
|
||||||
|
description="Miniflux is a minimalist and opinionated feed reader",
|
||||||
|
version=VERSION,
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
runtime.with_file("/usr/bin/miniflux", backend.file("/miniflux"))
|
||||||
|
.with_exposed_port(8080)
|
||||||
|
.with_env_variable("LISTEN_ADDR", "0.0.0.0:8080")
|
||||||
|
.with_user("65534")
|
||||||
|
.with_default_args(args=["/usr/bin/miniflux"])
|
||||||
|
)
|
||||||
1
docs/changelog.d/miniflux-upgrade-and-ty.infra.md
Normal file
1
docs/changelog.d/miniflux-upgrade-and-ty.infra.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Upgrade miniflux to 2.2.19 with native Dagger container.py build (second container migrated from Dockerfile). Add `ty` Python typechecker to prek hooks. Pin all mise.toml tool versions and track them as `type: mise` in service-versions.yaml. Refactor `alpine_runtime()` to support existing users via `create_user=False`.
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: Review Services
|
title: Review Services
|
||||||
modified: 2026-03-24
|
modified: 2026-04-12
|
||||||
last-reviewed: 2026-03-07
|
last-reviewed: 2026-04-12
|
||||||
tags:
|
tags:
|
||||||
- how-to
|
- how-to
|
||||||
- maintenance
|
- maintenance
|
||||||
|
|
@ -66,6 +66,16 @@ Versioned NixOS services (forgejo-runner, snowflake, k3s) are pinned via a `nixp
|
||||||
4. Deploy via `mise run provision-ringtail`
|
4. Deploy via `mise run provision-ringtail`
|
||||||
5. Update `service-versions.yaml` with the new version
|
5. Update `service-versions.yaml` with the new version
|
||||||
|
|
||||||
|
### Mise Tools (`type: mise`)
|
||||||
|
|
||||||
|
Development tools managed via `mise.toml` with pinned versions. These are local CLI tools (dagger, pulumi, prek, ty, ansible-core) rather than deployed services.
|
||||||
|
|
||||||
|
1. Check the upstream releases page for new versions
|
||||||
|
2. Review the changelog for breaking changes
|
||||||
|
3. Update the pinned version in `mise.toml`
|
||||||
|
4. Run `mise install` to verify the new version installs correctly
|
||||||
|
5. Update `service-versions.yaml` with the new version
|
||||||
|
|
||||||
### Private Forge Repos (`upstream-source` under `forge.eblu.me/eblume/`)
|
### Private Forge Repos (`upstream-source` under `forge.eblu.me/eblume/`)
|
||||||
|
|
||||||
Some services are built from private repos on the forge rather than tracking an external upstream project. When `upstream-source` points to a `forge.eblu.me/eblume/` repo:
|
Some services are built from private repos on the forge rather than tracking an external upstream project. When `upstream-source` points to a `forge.eblu.me/eblume/` repo:
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# ///
|
# ///
|
||||||
#MISE description="Review the most stale service for version freshness"
|
#MISE description="Review the most stale service for version freshness"
|
||||||
#USAGE flag "--limit <limit>" default="15" help="Number of services to show in the table"
|
#USAGE flag "--limit <limit>" default="15" help="Number of services to show in the table"
|
||||||
#USAGE flag "--type <type>" help="Filter by service type (argocd, ansible, nixos)"
|
#USAGE flag "--type <type>" help="Filter by service type (argocd, ansible, nixos, fly, mise)"
|
||||||
"""Review the most stale service for version freshness.
|
"""Review the most stale service for version freshness.
|
||||||
|
|
||||||
Reads ``docs/reference/services/service-versions.yaml`` and sorts services
|
Reads ``docs/reference/services/service-versions.yaml`` and sorts services
|
||||||
|
|
@ -197,6 +197,13 @@ def main(
|
||||||
"• Update: dagger call flake-update --src=. export --path=nixos/ringtail/flake.lock\n",
|
"• Update: dagger call flake-update --src=. export --path=nixos/ringtail/flake.lock\n",
|
||||||
"• Deploy: mise run provision-ringtail\n",
|
"• Deploy: mise run provision-ringtail\n",
|
||||||
]
|
]
|
||||||
|
elif svc_type == "mise":
|
||||||
|
checklist_parts += [
|
||||||
|
"\n[bold]Mise Tool Update:[/bold]\n",
|
||||||
|
"• Update pinned version in mise.toml\n",
|
||||||
|
"• Run: mise install to verify\n",
|
||||||
|
"• Check for breaking changes in release notes\n",
|
||||||
|
]
|
||||||
|
|
||||||
checklist_parts += [
|
checklist_parts += [
|
||||||
"\n[bold]Health Check:[/bold]\n",
|
"\n[bold]Health Check:[/bold]\n",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
[tools]
|
[tools]
|
||||||
"pipx:ansible-core" = { version = "latest", uvx = "true", uvx_args = "--with botocore --with boto3" }
|
"pipx:ansible-core" = { version = "2.20.1", uvx = "true", uvx_args = "--with botocore --with boto3" }
|
||||||
prek = "latest"
|
prek = "0.3.4"
|
||||||
pulumi = "latest"
|
pulumi = "3.215.0"
|
||||||
dagger = "0.20.1"
|
dagger = "0.20.1"
|
||||||
|
ty = "0.0.29"
|
||||||
|
|
|
||||||
12
prek.toml
12
prek.toml
|
|
@ -77,6 +77,18 @@ repo = "https://github.com/astral-sh/ruff-pre-commit"
|
||||||
rev = "v0.15.7"
|
rev = "v0.15.7"
|
||||||
hooks = [{ id = "ruff", args = ["--fix"] }, { id = "ruff-format" }]
|
hooks = [{ id = "ruff", args = ["--fix"] }, { id = "ruff-format" }]
|
||||||
|
|
||||||
|
# Python - ty type checker
|
||||||
|
[[repos]]
|
||||||
|
repo = "local"
|
||||||
|
|
||||||
|
[[repos.hooks]]
|
||||||
|
id = "ty-check"
|
||||||
|
name = "ty type check"
|
||||||
|
entry = "mise x ty -- ty check"
|
||||||
|
language = "system"
|
||||||
|
types = ["python"]
|
||||||
|
pass_filenames = false
|
||||||
|
|
||||||
# Shell scripts - shellcheck and shfmt
|
# Shell scripts - shellcheck and shfmt
|
||||||
[[repos]]
|
[[repos]]
|
||||||
repo = "https://github.com/shellcheck-py/shellcheck-py"
|
repo = "https://github.com/shellcheck-py/shellcheck-py"
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,10 @@ build-backend = "uv_build"
|
||||||
|
|
||||||
[tool.uv.sources]
|
[tool.uv.sources]
|
||||||
dagger-io = { path = "sdk", editable = true }
|
dagger-io = { path = "sdk", editable = true }
|
||||||
|
|
||||||
|
[tool.ty.environment]
|
||||||
|
python-version = "3.13"
|
||||||
|
extra-paths = ["sdk/src"]
|
||||||
|
|
||||||
|
[tool.ty.src]
|
||||||
|
exclude = ["pulumi/", "containers/transmission-exporter/"]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
#
|
#
|
||||||
# Fields:
|
# Fields:
|
||||||
# name - kebab-case service identifier
|
# name - kebab-case service identifier
|
||||||
# type - argocd | ansible | nixos | fly
|
# type - argocd | ansible | nixos | fly | mise
|
||||||
# last-reviewed - date (YYYY-MM-DD) or null
|
# last-reviewed - date (YYYY-MM-DD) or null
|
||||||
# current-version - deployed version string or null
|
# current-version - deployed version string or null
|
||||||
# upstream-source - URL to upstream releases/changelog
|
# upstream-source - URL to upstream releases/changelog
|
||||||
|
|
@ -184,8 +184,8 @@ services:
|
||||||
|
|
||||||
- name: miniflux
|
- name: miniflux
|
||||||
type: argocd
|
type: argocd
|
||||||
last-reviewed: 2026-03-02
|
last-reviewed: 2026-04-12
|
||||||
current-version: "2.2.17"
|
current-version: "2.2.19"
|
||||||
upstream-source: https://github.com/miniflux/v2/releases
|
upstream-source: https://github.com/miniflux/v2/releases
|
||||||
|
|
||||||
- name: teslamate
|
- name: teslamate
|
||||||
|
|
@ -393,3 +393,40 @@ services:
|
||||||
current-version: "v1.14.1"
|
current-version: "v1.14.1"
|
||||||
upstream-source: https://github.com/grafana/alloy/releases
|
upstream-source: https://github.com/grafana/alloy/releases
|
||||||
notes: COPY --from in fly/Dockerfile for log shipping and metrics
|
notes: COPY --from in fly/Dockerfile for log shipping and metrics
|
||||||
|
|
||||||
|
# --- Mise-managed development tools ---
|
||||||
|
|
||||||
|
- name: dagger
|
||||||
|
type: mise
|
||||||
|
last-reviewed: 2026-04-12
|
||||||
|
current-version: "0.20.1"
|
||||||
|
upstream-source: https://github.com/dagger/dagger/releases
|
||||||
|
notes: Dagger CI/CD engine; pinned in mise.toml
|
||||||
|
|
||||||
|
- name: ansible-core
|
||||||
|
type: mise
|
||||||
|
last-reviewed: 2026-04-12
|
||||||
|
current-version: "2.20.1"
|
||||||
|
upstream-source: https://github.com/ansible/ansible/releases
|
||||||
|
notes: Installed via pipx/uvx with botocore and boto3
|
||||||
|
|
||||||
|
- name: prek
|
||||||
|
type: mise
|
||||||
|
last-reviewed: 2026-04-12
|
||||||
|
current-version: "0.3.4"
|
||||||
|
upstream-source: https://github.com/j178/prek/releases
|
||||||
|
notes: Pre-commit hook runner (Rust reimplementation)
|
||||||
|
|
||||||
|
- name: pulumi-cli
|
||||||
|
type: mise
|
||||||
|
last-reviewed: 2026-04-12
|
||||||
|
current-version: "3.215.0"
|
||||||
|
upstream-source: https://github.com/pulumi/pulumi/releases
|
||||||
|
notes: IaC CLI for tailscale and gandi stacks
|
||||||
|
|
||||||
|
- name: ty
|
||||||
|
type: mise
|
||||||
|
last-reviewed: 2026-04-12
|
||||||
|
current-version: "0.0.29"
|
||||||
|
upstream-source: https://github.com/astral-sh/ty/releases
|
||||||
|
notes: Astral Python typechecker (beta); prek hook
|
||||||
|
|
|
||||||
|
|
@ -131,20 +131,26 @@ def alpine_runtime(
|
||||||
uid: int = 65534,
|
uid: int = 65534,
|
||||||
gid: int = 65534,
|
gid: int = 65534,
|
||||||
username: str = "app",
|
username: str = "app",
|
||||||
|
create_user: bool = True,
|
||||||
) -> dagger.Container:
|
) -> dagger.Container:
|
||||||
"""Standard Alpine 3.22 runtime base with non-root user."""
|
"""Standard Alpine 3.22 runtime base.
|
||||||
|
|
||||||
|
When create_user is True (default), creates a non-root user with the given
|
||||||
|
uid/gid/username. Set create_user=False to use an existing user (e.g.
|
||||||
|
Alpine's built-in nobody:65534).
|
||||||
|
"""
|
||||||
packages = extra_apk or []
|
packages = extra_apk or []
|
||||||
setup_cmds = []
|
setup_cmds = []
|
||||||
if packages:
|
if packages:
|
||||||
setup_cmds.append(f"apk add --no-cache {' '.join(packages)}")
|
setup_cmds.append(f"apk add --no-cache {' '.join(packages)}")
|
||||||
setup_cmds.append(f"addgroup -g {gid} {username}")
|
if create_user:
|
||||||
setup_cmds.append(f"adduser -u {uid} -G {username} -D {username}")
|
setup_cmds.append(f"addgroup -g {gid} {username}")
|
||||||
|
setup_cmds.append(f"adduser -u {uid} -G {username} -D {username}")
|
||||||
|
|
||||||
return (
|
ctr = dag.container().from_("alpine:3.22")
|
||||||
dag.container()
|
if setup_cmds:
|
||||||
.from_("alpine:3.22")
|
ctr = ctr.with_exec(["sh", "-c", " && ".join(setup_cmds)])
|
||||||
.with_exec(["sh", "-c", " && ".join(setup_cmds)])
|
return ctr
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def oci_labels(
|
def oci_labels(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue