← Back 03

Incident & Compatibility Log: Forgejo Actions Transition

May 2026 · Part 3 of a 5-part series on migrating CI/CD from GitHub to Codeberg

Infrastructure Constraints & Known Discrepancies

While Forgejo Actions maintains syntactic parity with GitHub Actions YAML specifications, underlying execution context divergence introduces several compatibility issues. The following boundaries were identified during initial implementation.

Production Regression Analysis

The following runtime errors surfaced under production workloads after bypassing primary staging checks.

1. Hyphenated Job-ID Syntax Parsing Error

The act compiler fails to resolve the runs-on property when a job identifier includes hyphen characters (e.g., ci/test-backend). This triggers an ambiguous 'runs-on' key not defined parsing error.

Remediation: Rename all jobs, dependency lists (needs), and concurrency bounds across the codebase to adhere to a snake_case naming convention.

2. Path Isolation Collision (Docker 29.5.1 Validation Engine)

A security patch introduced in Docker 29.5.1 leverages Go’s os.Root implementation to tighten directory boundaries for safety. The act runner mounts internal job workspaces under /var/run/act. Because /var/run exists as a system symlink pointing directly to /run, the stricter engine flags this interaction as a path traversal exception (path escapes from parent), terminating all jobs.

Remediation: Pin the local Docker-in-Docker sidecar instance strictly to version docker:29.5.0-dind to prevent validation failures pending an official patch release.

3. Non-Standard API Responses & tea CLI Panics

Executing pipeline dispatches via the official Codeberg command-line interface (tea) yields an unhandled exception: unexpected end of JSON input. Forgejo processes asynchronous dispatches by returning an empty HTTP status code (204 No Content). The CLI expects a structured JSON object response and throws a fatal parsing error despite successful task scheduling.

Remediation: Suppress standard error monitoring on dispatches; poll task status using structured logging arrays via tea actions runs list.

4. Silent Variable Nullification (Implicit Dependency Errors)

If a step relies on data mapped from an independent job (e.g., needs.build_backend.outputs.image_sha) but omits that prerequisite from the job’s structural execution graph (needs), Forgejo suppresses the error. The reference evaluates to an empty string. This results in invalid string construction during deployment commands (e.g., backend:-staging), crashing the orchestration process.

Remediation: Enforce explicit configuration tracking on every data-dependent node within the deployment manifest.

5. Concurrent Resource Contention (Helm Lock Overlaps)

Simultaneous execution triggers targeting the same deployment namespace induce race conditions inside the Helm state machine, returning a fatal UPGRADE FAILED: another operation is in progress message. This locks the configuration context into a persistent pending-upgrade error status.

Remediation: Insert an automated recovery routine prior to chart deployment to explicitly check for, and roll back, any locked application releases.

6. Git Context Disconnection (Commit SHA Race Condition)

A build pipeline compiles artifacts against a distinct point-in-time reference (abc123). If a parallel merge occurs before the deployment stage executes, a standard repository checkout pulls the new head commit (def456). The deployment container then searches for matching artifacts under the wrong reference, triggering an ImagePullBackOff loop.

Remediation: Bind the lifecycle of all down-stream deployment tasks to the initial build environment’s immutable commit identifier, forcing the checkout stage to target that specific hash.

7. Non-Validating Sub-Shell Replacements (envsubst)

The envsubst utility ignores missing environment parameters, injecting empty strings into configuration templates without outputting warning signals. This creates structural drift, such as compiling application manifests with unassigned connection endpoints.

Remediation: Transition variable extraction from custom shell scripts to native Kubernetes specifications via container-level secretRef arrays.

8. Multi-Stage Environment Stripping (uv Execution Missing)

Using uv run alembic upgrade head within a Kubernetes database migration job crashes if the application’s production layer relies on an optimized, multi-stage Docker build pattern. Multi-stage compilation drops developer toolchains like uv from the final artifact layer to save disk space.

Remediation: Reference the production binary directly via its absolute virtual environment file path: /app/.venv/bin/alembic upgrade head.

9. File Filtering Failures via Root .gitignore Configurations

A global filter pattern targeting asset files (e.g., *.png) prevents application graphics from inclusion in the Git history tracker. Local builds succeed due to untracked local cache availability, but remote Docker builds (which use the clean Git isolation pool) compile projects with missing assets.

Remediation: Introduce a staging integration check that polls asset endpoints via HTTP headers, checking explicitly for content-type: image/png.

10. Local State Drift (Alembic Migration Duplications)

Executing an Alembic auto-generation command (alembic revision --autogenerate) against an outdated local data storage instance creates duplicate table definition blocks (op.create_table). Running this migration file against a synchronized remote database crashes the pipeline due to a DuplicateTable database constraint error.

Remediation: Standardize developer workflows to enforce execution of alembic upgrade head before any schema changes are generated.

11. Static Analysis Failures on Migration Code

Altering auto-generated database migration scripts via comment blocks often leaves stale, unused package imports behind. Subsequent linting phases (Ruff) catch these violations and block the pipeline from advancing.

Remediation: Append database migration directories to the exclusion configuration array within the project’s main linting properties file.

12. External Registry Availability Dependency

The runner setup downloads environment dependencies (catthehacker/ubuntu:act-22.04) from public Docker Hub infrastructure at the start of every pipeline cycle. External service degradation, network latency, or IP rate-limiting blocks pipeline progress across all tasks.

Remediation: Implement a private mirror of the runner image within a registry hosted on internal network infrastructure.

Cryptographic & Authentication Tradeoffs

← Architecture & Topology Next: Results & Costs →