Commit Graph
39 Commits
Author SHA1 Message Date
Environment TestandClaude Sonnet 4.6 eebb3b9788 refactor(utils): move utils/docker/image_*.py into utils/docker/image/ subpackage
- Move image_discovery.py → utils/docker/image/discovery.py
- Move image_ref.py → utils/docker/image/ref.py
- Add utils/docker/image/__init__.py
- Update all imports and references across cli/, tests/, utils/, and docs/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 19:00:02 +02:00
Environment TestandClaude Sonnet 4.6 ce19aff65f feat(mirror): add cleanup module to delete private GHCR packages
Adds cli.mirror.cleanup for deleting GHCR container packages by prefix
and visibility via the GitHub Packages REST API. Supports dry-run mode.
Requires a classic PAT with delete:packages scope (GITHUB_TOKEN from
gh auth or GitHub Actions does not include this scope).

Also adds unit tests and a usage guide at
docs/contributing/flow/security/mirror-cleanup.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 14:54:39 +02:00
Environment TestandClaude Sonnet 4.6 7cb80eb830 fix(mirror): use GITHUB_TOKEN for GHCR push to auto-publish packages
Replaces GHCR_PAT-based authentication with GITHUB_TOKEN across all
mirror and build workflows. Packages pushed from a public repository
with GITHUB_TOKEN are automatically linked to the repository and
inherit its public visibility — no publish step required.

Removes cli.mirror.publish (used a non-existent GitHub REST API
endpoint for changing package visibility), the associated unit tests,
and the GHCR_PAT debug helper. Cleans up all GHCR_PAT/GHCR_USERNAME
references and rewrites the GHCR authentication documentation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 14:25:51 +02:00
Environment TestandClaude Sonnet 4.6 aa33256cde feat(mirror): split resolver output, move images to defaults, add lookup plugin
- resolver: output both applications: (config/main.yml compose images) and
  images: (defaults/main.yml images) keyed by source_file
- image_discovery: scan defaults/main.yml instead of vars/main.yml for images:
- mirror_overrides: write mirror URLs to applications.*.compose.services.*
  (mirror_policy-aware) and images_overrides.*.* (always merged, if_missing)
- roles/sys-ctl-hlth-csp, roles/test-e2e-playwright: move images: from
  vars/main.yml to defaults/main.yml for correct Ansible variable precedence
- plugins/lookup/image.py: new Ansible lookup plugin that prefers
  images_overrides over role-local images defaults
- docs: add artefact/image.md and artefact/mirror.md as SPOTs; add
  code/role/image.md contributor guide; link from ci.md
- tests: update test_util, test_mirror_overrides; add test_resolver,
  test_publish (404/401 coverage), test_image (lookup plugin)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 11:17:42 +02:00
Environment TestandClaude Sonnet 4.6 0c2c9d592c fix(publish): encode slashes in package name and fix skipped-count logic
- Replace safe="/" with safe="" so package names like
  mirror/docker.io/openresty/openresty are encoded as a single URL path
  segment (mirror%2Fdocker.io%2Fopenresty%2Fopenresty). With literal
  slashes GitHub's router matched only "mirror" as the package name → 404.
- _set_public now returns bool; main() only prints "✓ set to public" and
  increments updated when True. Previously a 404-skip returned None which
  still triggered the success path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:48:37 +02:00
Environment TestandClaude Sonnet 4.6 6faf3c9a2d fix(publish): treat HTTP 404 as missing package, not token error
Old packages (pre-rename) have no versions left in GHCR and return 404
on PATCH. This caused an early exit that skipped all remaining packages.
404 means the package is gone; actual token scope failures return 401/403.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:39:52 +02:00
Environment TestandClaude Sonnet 4.6 1530788dbe feat(mirror): add repository segment to GHCR image path
Image names now follow {owner}/{repository}/mirror/{registry}/{name}
instead of {owner}/mirror/{registry}/{name}.

- GHCRProvider: add `repository` param + `add_args()`/`from_args()` classmethods
  as SPOT for CLI argument registration and provider construction
- sync, resolver: use GHCRProvider.add_args() / from_args() (no duplication)
- Workflows: resolve repository name via scripts/meta/resolve/repository/name.sh
  and pass --ghcr-repository to sync and publish
- mirrors.sh: require and forward GHCR_REPOSITORY env var
- deploy/development/mirrors.py: derive repository from GITHUB_REPOSITORY
- Tests: update expected paths, argv and env fixtures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 00:20:41 +02:00
Environment TestandClaude Sonnet 4.6 317287983a fix(mirror): resolve merge conflicts with fix/playwright-pull
Keep new multi-registry logic (feature/pre-commit):
- Merge is_mirrorable_image(), _detect_registry(), image_source() and
  canonical_image_name() into utils/docker/image_discovery.py
- iter_role_images() now scans both config/main.yml and vars/main.yml
  and uses image_source() for full registry-prefixed URLs
- Delete cli/mirror/util.py (superseded by utils/docker/image_discovery)
- model.py re-exports ImageRef from utils.docker.image_discovery
- sync/__main__.py: revert fix/playwright-pull's if/else src URL to
  simple `src = f"docker://{img.source}"` (source already contains
  the full registry prefix)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 23:44:58 +02:00
Environment TestandClaude Sonnet 4.6 e8a5896881 feat(mirror): include registry domain in package name and fix multi-registry source URLs
- Add `registry` field to `ImageRef` (default `"docker.io"`)
- Populate `registry` from `_detect_registry()` in `iter_role_images()`
- `GHCRProvider.image_base()` and `GiteaProvider.image_base()` now produce
  `{prefix}/{registry}/{name}` paths (e.g. `mirror/docker.io/nextcloud`,
  `mirror/quay.io/keycloak/keycloak`) instead of flattening slashes to dashes
- Fix `mirror()` in both providers: use `docker://{image.source}` directly
  instead of always prepending `docker://docker.io/`, which broke quay.io/ghcr.io images
- Update tests to reflect new naming convention and add quay.io test case
- Add missing __init__.py files in test subdirectories

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:57:33 +02:00
Environment TestandClaude Sonnet 4.6 3e0d9662a6 merge(mirror): resolve conflicts between feature/pre-commit and fix/playwright-pull
- Keep HEAD's registry-aware src construction in sync/__main__.py
  (docker://docker.io/{source} for Docker Hub, docker://{source} otherwise)
  to stay compatible with image_discovery.py's ImageRef.registry field
- Accept deletion of cli/mirror/util.py (content moved to utils/docker/image_discovery.py)
- Revert test_sync.py source format to library/nextcloud:31.0.0 (HEAD format)
- Retain publish/__main__.py improvements from feature/pre-commit:
  graceful 404 handling, no blind retries on client errors, pkg_url usage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:33:07 +02:00
Environment TestandClaude Sonnet 4.6 3f19ec1df4 fix(mirror): extend registry support and harden publish visibility handling
Extend image mirroring to cover quay.io and ghcr.io in addition to Docker Hub,
and make the publish step non-fatal when the token lacks write:packages scope.

cli/mirror/util.py:
- Add _detect_registry(), _strip_registry_prefix(), is_mirrorable_image()
  to support Docker Hub, quay.io, ghcr.io, and mcr.microsoft.com
- Update canonical_image_name() to strip any known registry prefix so that
  quay.io/keycloak/keycloak -> keycloak/keycloak (mirror/keycloak-keycloak)
- Add image_source() returning full registry-prefixed pull reference
  (docker.io/library/postgres:16, quay.io/keycloak/keycloak:latest, …)
- iter_role_images() now uses is_mirrorable_image + image_source, picking up
  the 17 previously skipped quay.io/ghcr.io images

cli/mirror/sync/__main__.py:
- Use docker://{img.source} directly (no hardcoded docker.io/ prefix)

cli/mirror/publish/__main__.py:
- Detect HTTP 404 on PATCH as _InsufficientTokenError (no retry, graceful
  GHA warning + exit 0) instead of retrying 5x and failing the workflow
- Use pkg.get("url") from listing response as canonical PATCH target URL
- Use safe="/" in urllib.parse.quote so mirror/name stays as literal path
- Do not retry on non-transient 4xx errors (400/401/403)

tests/unit/cli/mirror/test_sync.py:
- Update ImageRef.source to new docker.io/library/… format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:27:11 +02:00
Environment TestandClaude Sonnet 4.6 9ea2ad6ff1 refactor(docker): introduce utils/docker/image_discovery as single SPOT for image enumeration
- Move iter_role_images + all helpers + ImageRef from cli/mirror/util.py and
  cli/mirror/model.py into utils/docker/image_discovery.py; add source_file field
  to ImageRef to distinguish config/main.yml vs vars/main.yml entries
- Delete cli/mirror/util.py; update all callers to import from utils.docker.image_discovery
- Move utils into subpackages: utils/docker/ (image_ref, paths_utils, hadolint) and
  utils/roles/ (dependency_resolver, resource_validation); update all imports
- Reorganise tests into subpackages: tests/lint/docker/dockerfile/, tests/integration/docker/,
  tests/unit/utils/docker/, tests/unit/utils/roles/; fix parents[N] offsets after relocation
- tests/lint/docker/test_image_versions.py now uses iter_role_images (same SPOT as mirror)
- Add vars/main.yml images.<name>.{image,version} support for non-application roles
  (test-e2e-playwright, sys-ctl-hlth-csp); eliminate hardcoded duplicates in defaults/tasks
- Add web-app-matrix matrix-chatgpt-bot and web-app-mini-qr mini-qr entries in config/main.yml;
  replace hardcoded image strings in vars/templates with lookup('config', ...) references
- Fix sync/__main__.py log line to use correct registry prefix for non-Docker-Hub images
- Update docs/contributing/code/role/dockerfile.md with image declaration rules and SPOT reference

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 19:32:31 +02:00
Environment TestandClaude Sonnet 4.6 30f3b88f38 refactor(docker): replace Dockerfile.j2 with plain files/Dockerfile for 4 roles
Replace templated Dockerfiles with plain Dockerfiles for web-app-littlejs,
web-app-openproject, web-app-roulette-wheel, and web-app-wordpress.
Variables are passed as Docker ARG without defaults, keeping config/main.yml
and vars/main.yml as the single source of truth. Build args are declared
in compose.yml.j2 directly after the build.yml.j2 lookup. Image and version
for roulette-wheel and littlejs are added to config/main.yml and exposed
via vars/main.yml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:56:15 +02:00
Environment TestandClaude Sonnet 4.6 b1f2ccd471 fix(mirror): include link to ghcr.md in GHCR_PAT warning annotation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:27:18 +02:00
Environment TestandClaude Sonnet 4.6 eb1b20be30 fix(mirror): use GHA warning annotation and document GHCR_PAT requirement for personal accounts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:25:37 +02:00
Environment Test 4de93a285c lint 2026-04-09 10:57:01 +02:00
Environment TestandClaude Sonnet 4.6 ff277c88d9 fix(mirror): emit clear warning when GITHUB_TOKEN cannot list user packages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 10:50:09 +02:00
Environment TestandClaude Sonnet 4.6 b99e77beeb fix(mirror): pass explicit visibility param for user/packages (required by GITHUB_TOKEN)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 10:43:35 +02:00
Environment TestandClaude Sonnet 4.6 8ffe57da4f fix(mirror): use /user/packages endpoint for personal accounts (GITHUB_TOKEN incompatible with /users/{name}/packages)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 10:38:07 +02:00
Environment TestandClaude Sonnet 4.6 3ea40bbfc5 fix(mirror): fall back to user endpoint when org endpoint returns 404
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 10:25:58 +02:00
kevinveenbirkenbachandClaude Sonnet 4.6 70055f7861 refactor(mirror): extract visibility logic into standalone publish module
Move _set_public/ensure_public out of GHCRProvider into a new
cli.mirror.publish module that lists all non-public mirror packages
via the GitHub API and sets them public in bulk. The workflow runs
this as a separate step after mirroring. Mirror sync itself no longer
touches visibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 23:19:15 +02:00
kevinveenbirkenbachandClaude Sonnet 4.6 0ce6b1347c refactor(annotations): move to utils/annotations, add step summary support
- Move utils/gha/annotations.py → utils/annotations/message.py
- Move scripts/gha/summarize_annotations.py → utils/annotations/summarize.py
- Keep utils/gha/annotations.py as compatibility shim
- Update all imports across codebase to utils.annotations.message
- Wire tee + summary step into test-code-lint, unit, and integration workflows
- Add unit tests for utils/annotations/summarize (20 tests)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 14:05:24 +02:00
kevinveenbirkenbachandClaude Sonnet 4.6 5e970ff5bd feat(gha): introduce utils/gha/annotations for GitHub Actions workflow annotations
- Add utils/gha/annotations.py with warning/error/notice helpers and *_each
  variants that emit one annotation per list item
- Use warning() in GHCRProvider._set_public for missing GHCR_PAT case
- Expose GHCR_PAT_SET env var in mirror step to distinguish missing-PAT from
  real API failures
- Add unit tests for all annotation functions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 10:59:46 +02:00
kevinveenbirkenbachandClaude Sonnet 4.6 312233aa42 fix(mirror): use /user/packages endpoint for authenticated user visibility update
/users/{username}/packages does not support PATCH — it is a read-only endpoint
for public data. The correct endpoint for updating visibility of the authenticated
user's packages is /user/packages (without username).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 10:23:15 +02:00
kevinveenbirkenbachandClaude Sonnet 4.6 a751257b4b fix(mirror): retry visibility API call to handle GHCR propagation delay
A freshly pushed package may not be immediately visible in the GitHub
Packages API. Add up to 5 retries with 10s delay before failing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 10:08:09 +02:00
kevinveenbirkenbachandClaude Sonnet 4.6 7c8783c623 fix(mirror): use GHCR_PAT for package visibility API and remove ensure_public from skip path
Root cause: GITHUB_TOKEN in Actions is repo-scoped and cannot change package
visibility in user/org namespaces — all 58 mirror operations failed with 404.

- Use GHCR_PAT (which has write:packages scope) as GITHUB_TOKEN in the mirror
  step, falling back to GITHUB_TOKEN if GHCR_PAT is not set
- Remove ensure_public() call from the already-exists skip path (wrong place:
  existing images were already made public when first mirrored)
- Update test to assert ensure_public is not called for skipped images

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 09:45:38 +02:00
kevinveenbirkenbach 7bf06efc9e lint 2026-04-08 07:46:15 +02:00
kevinveenbirkenbachandClaude Sonnet 4.6 b3a4a81e01 fix(mirror): remove redundant mirror job and publish images as public
- Remove redundant `images-mirror-missing` job from entry-pull-request-change.yml
  (mirroring already runs via ci-orchestrator for same-repo PRs)
- Set GHCR package visibility to public after each successful mirror via GitHub API
- Add `ensure_public` hook to base class (no-op for other providers)
- Also call `ensure_public` for already-existing images to fix any that are still private
- Pass GITHUB_TOKEN explicitly to the mirror step
- Add unit tests for _set_public (user/org endpoint fallback) and sync skip-path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 07:45:33 +02:00
Alejandro Roman 0fbe38e2a7 fix(mirror-lowername): lowercase the name to aovid errors with uppercase name 2026-03-21 16:16:38 +01:00
kevinveenbirkenbachandCopilot Autofix powered by AI 8b793739f6 Potential fix for code scanning alert no. 192: Statement has no effect
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-02-08 18:42:32 +01:00
kevinveenbirkenbach b78ab29218 Renamed config entry from docker to compose
https://chatgpt.com/share/69870f54-d708-800f-9870-e9ae5a4bff44
2026-02-07 11:09:36 +01:00
kevinveenbirkenbach 8a81630391 Added newline at file and POSIX compatible 2026-02-04 19:49:56 +01:00
kevinveenbirkenbach c0cd4b8eda feat(ci,mirror): add branch-scoped mirror of missing images and gate deploys on it
- Introduce reusable workflow `mirror-images-missing.yml` to mirror only missing
  Docker Hub images to GHCR using `--only-missing`
- Scope mirror concurrency per branch using `github.ref_name`
- Rename full mirror workflow to `mirror-images-all.yml` for clarity
- Integrate missing-image mirror into CI orchestrator and gate server/universal
  deploys on successful mirror completion
- Add tag existence checks via `skopeo inspect` to avoid redundant uploads
- Keep mirror runs best-effort to avoid blocking CI on transient registry issues

https://chatgpt.com/share/698141e9-0844-800f-9c4f-fecd468ede73
2026-02-03 01:31:30 +01:00
kevinveenbirkenbach 734f013179 ci: wire GHCR mirror envs into deploy workflows and inventory init
- Add required GHCR mirror environment variables to CI workflows
- Generate mirrors.yml during development inventory init on CI
- Introduce mirrors helper module with strict env validation
- Minor formatting cleanup in mirror providers
`

https://chatgpt.com/share/6980ff2e-8f18-800f-85e1-79d56a2027f5
2026-02-02 20:46:41 +01:00
kevinveenbirkenbach 7faf87cc68 fix(mirror): remove invalid skopeo flag and add recompress fallback for GHCR
- drop unsupported --dest-no-reuse-blobs flag
- add retry with forced recompression to avoid GHCR blob reuse errors
- print skopeo stdout/stderr on failure for easier debugging

https://chatgpt.com/share/6980f7ca-2aa0-800f-ae64-60081aa88f1c
2026-02-02 20:14:49 +01:00
kevinveenbirkenbach 647e93ff18 mirror: disable blob reuse for skopeo copy to GHCR
https://chatgpt.com/share/6980c6ff-3a6c-800f-8c95-6acdbfebfbfd
2026-02-02 16:47:04 +01:00
kevinveenbirkenbach 4949b45f44 feat(mirror): add rate-limited image sync with concurrency protection
- add optional --images-per-hour throttle to mirror sync
- enforce safe default (50 images/hour) in CI
- prevent overlapping mirror runs via workflow concurrency
- make workflow robust for schedule, dispatch, and workflow_call
- fix GHCR namespace fallback to repository owner

https://chatgpt.com/share/6980be4f-d5d4-800f-a9ad-2aaba637897e
2026-02-02 16:09:57 +01:00
kevinveenbirkenbach e7fdaa1d10 fix(mirror): iterate all images and fail at end on mirror errors
- mirror sync now continues on individual skopeo failures
- collect and summarize failed images
- return non-zero exit code only after all images were processed
- make GitHub workflow respect mirror sync exit status

https://chatgpt.com/share/6980b558-5fdc-800f-911e-48bbc35a461f
2026-02-02 15:45:27 +01:00
kevinveenbirkenbach 221c1c67aa feat(cli): add flexible docker image mirror resolver and sync system
Introduce a modular mirror subsystem with:
- canonical ImageRef model
- shared discovery & normalization logic for Docker Hub images
- pluggable registry providers (GHCR, Gitea-ready)
- inventory resolver for mirrored image paths
- skopeo-based mirror sync implementation

Design follows SRP/KISS principles and allows future registry backends.

https://chatgpt.com/share/6971e6cf-be18-800f-9419-589fd3e22c3d
2026-01-22 09:58:48 +01:00