Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Codebuilder Laravel Package

keboola/codebuilder

Keboola CodeBuilder is a PHP library for programmatically generating and composing code, helping developers build reusable snippets, templates, and transformation scripts for Keboola workflows with a lightweight, consistent API.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is installed via Composer:

composer require keboola/codebuilder

Because the package has almost no documentation, read the source and the test suite before the README. The tests/ directory is the de-facto specification — it shows the canonical object graph (how a Program is constructed, how Runners are wired, how output is captured). Then read the README to confirm the supported runner backends and required runtime (usually a Docker daemon and/or the Keboola sandbox).

Pin to a known-good version (1.0.1 is the current line) and check composer.lock into your repository — with a small, low-activity upstream, your lockfile is part of your supply-chain boundary.

Your first use case should be the smallest possible version of what production will do:

  1. Build a Program from a hand-written JSON definition (one phase, one trivial command such as echo "hello" or a one-line Python script).
  2. Attach a runner (Docker or the Keboola sandbox runner, depending on what's configured).
  3. Execute, inspect the returned result object (stdout/stderr/exit code or the runner's native result structure).

Don't start with a multi-phase, multi-language program. The failure modes when nothing is wired correctly (missing Docker socket, missing image, bad working directory) all look the same from the outside, and a one-line program proves the plumbing.

As a second step, feed the parser a definition with a deliberately wrong attribute type (e.g. a numeric where a string is expected). Since 1.0.1 this is validated eagerly, so you should get a clear exception at build time rather than a confusing runtime failure — good way to confirm you're catching the right exception class before you wire it into your API.

Implementation Patterns

Drive everything from a data-model object, not from raw arrays. Treat the JSON document as a transport format and hydrate it into the package's Program (or equivalent) class early. Validate/normalize once at the boundary, then pass the object around internally. This keeps the runner code free of isset($json['phases'][0]['script'])-style checks. Since 1.0.1 the package itself checks attribute types during hydration, so this is the natural place to convert its exceptions into your own validation errors.

Separate "definition" from "execution". The natural pattern with this package is:

  • A builder/parser layer that turns a user-supplied JSON config (phases, scripts, env, working dir, image name) into the package objects.
  • A runner layer that actually executes them (Docker, sandbox, etc.).
  • A result layer that normalizes output (stdout, stderr, exit code, artifacts).

Keep these three boundaries explicit in your own code so that swapping runner backends doesn't ripple into your controllers or queue jobs.

Compose programs as pipelines of phases, not one monolith. Each phase should be independently testable. If phase 2 fails, you want phase 1's output preserved for debugging. Log the boundary between phases with a correlation/run ID.

Wrap execution in a queue job, not a request. User scripts are long-running and consume external resources (Docker containers). The idiomatic integration is a queued job that publishes status (running, succeeded, failed, timeout) to your persistence layer, with the package used purely as the execution engine inside handle().

Use it as the low-level primitive inside a higher-level service. In the Keboola ecosystem, codebuilder is usually consumed by something like the transformations service — you're unlikely to expose it directly. If you're integrating it, your integration point is typically "accept a config + workspace, produce files + status messages," which is the same contract Keboola's own services use.

Pin the runner image and the package version together. When the package executes user code inside a container, the container image and the PHP client that talks to it are coupled. Bump them in the same PR.

Gotchas and Tips

Low activity — treat upstream as "barely maintained". The 1.0.1 release is a single small PR (an attribute-type validation check plus its test). That means the project is not abandoned outright, but releases are infrequent and narrow in scope: expect no proactive PHP 8.x work, no security patches, and only occasional point fixes. Run composer outdated and composer why keboola/codebuilder before adopting, watch the changelog between patch bumps, and keep fork-and-vendor on the table as the realistic fallback.

Attribute types are validated eagerly (since 1.0.1). Definitions that used to slip through with a loosely-typed attribute (string "1" where an int is expected, or an array where a scalar is expected) now raise during parsing/hydration. This is a good thing, but it is a behaviour change: any config in your database that was accepted under 1.0.0 may start failing at build time. Before bumping, replay your stored definitions against the new version in a test environment, and map the new exception to a user-facing validation error (4xx) rather than letting it bubble as a 500.

Docker daemon is a hard runtime dependency for most runners. Unit tests that don't hit real Docker will pass while handle() fails in staging because /var/run/docker.sock is not mounted or the container user lacks permission. Assert at boot time that the daemon is reachable rather than failing inside a user job.

Error handling is not uniform across failure modes. Expect to distinguish at least: (a) program definition is invalid / fails to parse / has a wrongly-typed attribute, (b) runner cannot start (infra), (c) user script exits non-zero (user error), (d) timeout. The package surfaces (a) and (c) cleanly; (b) and (d) usually require you to detect them from runner state or your own wall-clock guard. Wrap every call and map exceptions to your own error types — do not let a RuntimeException from the runner leak to your API as a 500 when it's really a user-script failure.

Working directories and file permissions are the most common "silent" failure. User scripts writing to /data or /tmp inside the container will succeed in local dev (where permissions are loose) and fail in a hardened production container. Test the execution path as the same non-root user that production uses.

Capture stdout and stderr separately and always log both. Many debugging sessions end with "the script returned exit code 0 but produced nothing useful" — that's almost always stderr-only output from the user script being swallowed.

Version-pin and lock everything downstream. Because the upstream is small and releases infrequently, your composer.lock is now part of your security boundary. Do not use ^ constraints that will float to an untested version; use exact pins (1.0.1, not ^1.0) and review the (short) changelog manually when bumping.

Extension points to know about before you need them. Inspect the interfaces the package exposes for runners/programs. If there's a runner interface, implement a FakeRunner in your own test suite that records the Program it received instead of executing it. This lets you unit-test your parser, validation (including the new attribute-type checks), and status-reporting logic without a Docker daemon, which is the difference between a 2-second test suite and a 2-minute one.

Timeouts must be enforced by you, not by the package. Do not assume a default timeout is applied. Set one at the process level (e.g., timeout command, or Docker's --stop-timeout/--kill-after via run options) plus a wall-clock guard in your job — otherwise a runaway user script hangs a queue worker indefinitely.

Fork-and-vendor is a realistic fallback. Budget a day to vendor it into your monorepo (or a private Composer repo) as part of adoption, so you can apply one-line patches for PHP version compatibility, a validation edge case, or a timeout bug without waiting for an upstream release that may be months away.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console