boringbuilder 0.1.0.alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/AGENTS.md +38 -0
- data/CHANGELOG.md +13 -0
- data/LICENSE +21 -0
- data/README.md +131 -0
- data/SECURITY.md +25 -0
- data/SOUL.md +40 -0
- data/STYLE.md +33 -0
- data/docs/artifacts.md +106 -0
- data/docs/building.md +175 -0
- data/docs/caching.md +53 -0
- data/exe/boringbuilder +6 -0
- data/lib/boringbuilder/artifact.rb +87 -0
- data/lib/boringbuilder/boring_cache.rb +126 -0
- data/lib/boringbuilder/build_progress.rb +90 -0
- data/lib/boringbuilder/builder.rb +33 -0
- data/lib/boringbuilder/cli.rb +234 -0
- data/lib/boringbuilder/config_file.rb +40 -0
- data/lib/boringbuilder/configuration.rb +165 -0
- data/lib/boringbuilder/errors.rb +9 -0
- data/lib/boringbuilder/exporter.rb +144 -0
- data/lib/boringbuilder/exporters/asset.rb +7 -0
- data/lib/boringbuilder/exporters/boring_cache.rb +83 -0
- data/lib/boringbuilder/exporters/local.rb +27 -0
- data/lib/boringbuilder/exporters/receipt.rb +11 -0
- data/lib/boringbuilder/exporters/resolver.rb +57 -0
- data/lib/boringbuilder/exporters.rb +7 -0
- data/lib/boringbuilder/initializer.rb +180 -0
- data/lib/boringbuilder/mise.rb +88 -0
- data/lib/boringbuilder/pipeline.rb +156 -0
- data/lib/boringbuilder/project.rb +264 -0
- data/lib/boringbuilder/project_plan.rb +78 -0
- data/lib/boringbuilder/rails_build.rb +6 -0
- data/lib/boringbuilder/railtie.rb +9 -0
- data/lib/boringbuilder/result.rb +33 -0
- data/lib/boringbuilder/ruby_application.rb +142 -0
- data/lib/boringbuilder/ruby_build.rb +212 -0
- data/lib/boringbuilder/runtime.rb +89 -0
- data/lib/boringbuilder/tasks/boringbuilder.rake +40 -0
- data/lib/boringbuilder/version.rb +5 -0
- data/lib/boringbuilder.rb +45 -0
- metadata +108 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e20e908005e1a14c27c241b1540be57b73b7fa20f4a79513a22ce9535149fce4
|
|
4
|
+
data.tar.gz: e7b7c17cf76c2fcc852dd03765c1002bb8c32a993d3aac87b0b0561ac8e7a382
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 3950b539d458467b05b258bceb0d0e5a04f1c08fbb3dc1943db5f2750200c337694459090e5318334910be42f03495f260d134044e9a61fb4c6d4806bf4e5d9e
|
|
7
|
+
data.tar.gz: a7c8ee8d4b21afa7362c5f15846f943eff9ef1009c40b3860eae2802cddac826a25c6bd12e8c182468237378630cfbb0dbb53c2d9d5d6d6a020976ca37e20537
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Working on BoringBuilder
|
|
2
|
+
|
|
3
|
+
Read [SOUL.md](SOUL.md) and [STYLE.md](STYLE.md) before changing the product, code, or public language.
|
|
4
|
+
|
|
5
|
+
## Product shape
|
|
6
|
+
|
|
7
|
+
- BoringBuilder builds any application. Rails is the first-class convention over a shared Mise-powered foundation.
|
|
8
|
+
- Hanami, Rack, and other Bundler applications share the built-in Ruby foundation.
|
|
9
|
+
- Other workloads use the same Mise foundation from an ordinary Ruby recipe backed by Dagger. Ruby is the extension
|
|
10
|
+
language, not an application requirement.
|
|
11
|
+
- Keep `boringbuilder build` conventional and `boringbuilder init` the small escape hatch.
|
|
12
|
+
- Do not introduce a Dockerfile frontend, a build YAML language, or provider-specific behavior into the build graph.
|
|
13
|
+
- Local and BoringCache caches use the same declared step paths. Local and BoringCache artifact exporters receive the
|
|
14
|
+
same normalized artifact.
|
|
15
|
+
|
|
16
|
+
## Engineering habits
|
|
17
|
+
|
|
18
|
+
- Write clear Ruby against the current development toolchain while preserving the support contract in the gemspec
|
|
19
|
+
and CI.
|
|
20
|
+
- Keep the public surface small. Prefer a plain object or method over a new abstraction.
|
|
21
|
+
- Treat commands, paths, build contexts, archives, registries, and credentials as security boundaries.
|
|
22
|
+
- Never commit credentials, private source, local absolute paths, generated gems, dependency directories, or
|
|
23
|
+
application-specific integration details.
|
|
24
|
+
- Keep documentation human, direct, and useful to someone building their first production artifact.
|
|
25
|
+
- Add focused tests for behavior changes. Run `bin/ci` before committing.
|
|
26
|
+
- Run the relevant live Dagger build when changing the build, cache, runtime, or export graph.
|
|
27
|
+
- Inspect the built gem and the complete diff before release work.
|
|
28
|
+
- Treat these as defaults with reasons, not substitutes for repository invariants, security boundaries, or evidence.
|
|
29
|
+
|
|
30
|
+
## Product checks
|
|
31
|
+
|
|
32
|
+
Before handing off a change, ask:
|
|
33
|
+
|
|
34
|
+
1. Does the conventional Rails path remain simple?
|
|
35
|
+
2. Can a non-Ruby application still express the same idea with a small recipe?
|
|
36
|
+
3. Are local and shared caching behavior understandable?
|
|
37
|
+
4. Can the result be exported or published without rebuilding it differently?
|
|
38
|
+
5. Is the code pleasant for the next human—or agent—to read?
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0.alpha.1 - 2026-08-27
|
|
4
|
+
|
|
5
|
+
- Reintroduce BoringBuilder as a Ruby gem backed by Dagger Ruby.
|
|
6
|
+
- Add a convention-driven production Rails build.
|
|
7
|
+
- Support Apple Container and Docker Dagger runners.
|
|
8
|
+
- Support selective directory, tar, tar.zst, OCI, Docker, runtime-load, and registry-push outputs.
|
|
9
|
+
- Add local and BoringCache Artifact exporter adapters with automatic shared publication.
|
|
10
|
+
- Add cache-aware custom Dagger pipeline steps backed by local mounts and optional BoringCache persistence.
|
|
11
|
+
- Add a shared Mise toolchain foundation and `boringbuilder init` scaffolds for Ruby, Node, Rust, Go, and generic
|
|
12
|
+
Dagger pipelines.
|
|
13
|
+
- Add a Ruby API, CLI, Rails rake tasks, tests, CI, dependency auditing, and trusted RubyGems publishing.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 BoringCache
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# BoringBuilder
|
|
2
|
+
|
|
3
|
+
[](https://github.com/boringcache/builder/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
BoringBuilder turns application source into deployable artifacts and container images with
|
|
6
|
+
[Dagger](https://dagger.io). Rails is the first-class path, and every application can use the same
|
|
7
|
+
[Mise-powered](https://mise.jdx.dev/) toolchain from a small
|
|
8
|
+
[Dagger Ruby](https://github.com/boringcache/dagger_ruby) recipe. Local builds use Dagger's cache; shared builds use
|
|
9
|
+
[BoringCache](https://boringcache.com) without changing the recipe.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## Getting started
|
|
14
|
+
|
|
15
|
+
BoringBuilder itself runs on Ruby, but the application being built does not need to. Dagger runs through
|
|
16
|
+
[Apple Container](https://docs.dagger.io/reference/container-runtimes/apple-container/) on supported Macs,
|
|
17
|
+
Docker on Linux or macOS, or an existing Dagger engine on a Linux VM.
|
|
18
|
+
|
|
19
|
+
Install the builder and run it from an application root:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
gem install boringbuilder --version "~> 0.1.0.alpha"
|
|
23
|
+
boringbuilder doctor
|
|
24
|
+
boringbuilder build
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Rails and other Ruby applications can keep the builder in their bundle:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
bundle add boringbuilder --version "~> 0.1.0.alpha"
|
|
31
|
+
bundle exec boringbuilder build
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The default build produces a zstd-compressed artifact with normalized archive metadata. Without shared credentials
|
|
35
|
+
it is written under `dist/`. BoringBuilder automatically:
|
|
36
|
+
|
|
37
|
+
1. loads `config/boringbuilder.rb` when present;
|
|
38
|
+
2. runs its custom Dagger Ruby recipe for any application; or
|
|
39
|
+
3. uses the built-in Mise-powered Ruby pipeline when a `Gemfile` is present.
|
|
40
|
+
|
|
41
|
+
The built-in pipeline reads the Ruby toolchain from `mise.toml`, `.mise.toml`, `.tool-versions`, `.ruby-version`, or
|
|
42
|
+
`Gemfile`, then `Gemfile.lock`. It installs the selected toolchain with Mise inside the Dagger build. Generated
|
|
43
|
+
recipes use that same foundation for Node, Rust, Go, or any other tool available through Mise.
|
|
44
|
+
|
|
45
|
+
A root `Gemfile` identifies a Ruby application. Rails receives asset, Bootsnap, entrypoint, and `/up` health-check
|
|
46
|
+
conventions. Hanami, Rack, and `Procfile` applications receive production commands without pretending to be Rails.
|
|
47
|
+
|
|
48
|
+
Non-Ruby applications provide the few commands that make their build distinctive; BoringBuilder does not guess a
|
|
49
|
+
package manager or production output. Generate a detected Node, Rust, Go, Ruby, or generic starting point when
|
|
50
|
+
needed:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
boringbuilder init
|
|
54
|
+
boringbuilder build
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The generated Ruby stays small: `pipeline.mise` prepares the tools and source, while `pipeline.run` persists declared
|
|
58
|
+
dependency caches locally or through BoringCache. Builds print clean, numbered application steps while Dagger's
|
|
59
|
+
internal graph stays out of the way.
|
|
60
|
+
|
|
61
|
+
See [Building applications](docs/building.md) for the generated recipes and the complete pipeline API.
|
|
62
|
+
|
|
63
|
+
Command-line options remain explicit overrides:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
boringbuilder build --runtime apple
|
|
67
|
+
boringbuilder build --runtime docker --platform linux/arm64
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Rails applications can also use the included task:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
bin/rails boringbuilder:build
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Caching
|
|
77
|
+
|
|
78
|
+
Every build uses the selected Dagger engine's local cache. When BoringCache credentials and a workspace are present,
|
|
79
|
+
shared BoringCache entries replace the corresponding local mounts for Mise, Bundler, and declared custom pipeline
|
|
80
|
+
caches across fresh engines and CI runners.
|
|
81
|
+
|
|
82
|
+
See [Caching](docs/caching.md) for custom cached steps and restore-only builds.
|
|
83
|
+
|
|
84
|
+
## Artifacts and images
|
|
85
|
+
|
|
86
|
+
The default `tar.zst` output is intended for atomic filesystem deployers such as BoringDeploy. Without shared
|
|
87
|
+
credentials it is written under `dist/`. With BoringCache save credentials it is published as a shared BoringCache
|
|
88
|
+
Artifact instead; pass `--output` when a local copy is also wanted.
|
|
89
|
+
|
|
90
|
+
The same build can produce or publish a container image:
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
boringbuilder build --output dist/app.tar.zst
|
|
94
|
+
boringbuilder build --format oci --output dist/app.oci.tar
|
|
95
|
+
boringbuilder build --format docker --output dist/app.docker.tar
|
|
96
|
+
boringbuilder build --push ghcr.io/acme/app:latest
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Application-specific filesystem layouts live in ordinary Ruby:
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
# config/boringbuilder.rb
|
|
103
|
+
BoringBuilder.configure do |config|
|
|
104
|
+
config.artifact.directory("/app", at: "/opt/my-app/current")
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
See [Artifacts and images](docs/artifacts.md) for directory selection, OCI and Docker archives, registry publishing,
|
|
109
|
+
and the Ruby API.
|
|
110
|
+
|
|
111
|
+
## Documentation
|
|
112
|
+
|
|
113
|
+
- [Building applications](docs/building.md)
|
|
114
|
+
- [Artifacts and images](docs/artifacts.md)
|
|
115
|
+
- [Caching](docs/caching.md)
|
|
116
|
+
- [Project soul](SOUL.md)
|
|
117
|
+
- [Code and writing style](STYLE.md)
|
|
118
|
+
- [Agent guide](AGENTS.md)
|
|
119
|
+
- [Changelog](CHANGELOG.md)
|
|
120
|
+
- [Contributing](CONTRIBUTING.md)
|
|
121
|
+
- [Security policy](SECURITY.md)
|
|
122
|
+
|
|
123
|
+
## Development
|
|
124
|
+
|
|
125
|
+
```sh
|
|
126
|
+
mise install
|
|
127
|
+
bin/setup
|
|
128
|
+
bin/ci
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The checked-in toolchain is the development default. The gemspec and CI define the supported Ruby range.
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes are applied to the latest published release. During the alpha, upgrade to the newest prerelease before
|
|
6
|
+
reporting a problem.
|
|
7
|
+
|
|
8
|
+
## Report a vulnerability
|
|
9
|
+
|
|
10
|
+
Do not open a public issue for a suspected vulnerability. Email
|
|
11
|
+
[security@boringcache.com](mailto:security@boringcache.com) or use
|
|
12
|
+
[GitHub private vulnerability reporting](https://github.com/boringcache/builder/security/advisories/new).
|
|
13
|
+
|
|
14
|
+
Include the affected version, impact, reproduction steps, and any suggested mitigation. Do not include credentials,
|
|
15
|
+
private source code, or registry tokens unless the maintainers provide a secure channel.
|
|
16
|
+
|
|
17
|
+
## Build boundaries
|
|
18
|
+
|
|
19
|
+
BoringBuilder sends the selected project context to a local Dagger Engine. `.gitignore` and the built-in source
|
|
20
|
+
exclusions reduce accidental context, but users remain responsible for excluding secrets from the project and image.
|
|
21
|
+
Registry authentication is handled by the selected runtime and Dagger; BoringBuilder does not accept credentials on
|
|
22
|
+
the command line.
|
|
23
|
+
|
|
24
|
+
`config/boringbuilder.rb` and files passed with `--config` are executable Ruby loaded on the host. Review recipes
|
|
25
|
+
before building source you do not trust.
|
data/SOUL.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# The soul of BoringBuilder
|
|
2
|
+
|
|
3
|
+
Building an application should feel calm.
|
|
4
|
+
|
|
5
|
+
BoringBuilder exists for people who like the way Ruby and Rails make difficult work feel ordinary. They want a
|
|
6
|
+
production build without learning another configuration language, maintaining a pile of shell scripts, or giving up
|
|
7
|
+
control to a black box.
|
|
8
|
+
|
|
9
|
+
The goal is the ease of a buildpack with the clarity of a program. Dagger provides the build graph. Ruby makes that
|
|
10
|
+
graph readable. BoringBuilder adds strong conventions for the work most applications have in common.
|
|
11
|
+
|
|
12
|
+
## What we believe
|
|
13
|
+
|
|
14
|
+
- `boringbuilder build` should be enough for a conventional Rails application.
|
|
15
|
+
- Rails is the first-class path, not the limit of the product.
|
|
16
|
+
- Any application can own a small Ruby recipe when its build needs are different.
|
|
17
|
+
- Mise should give every application the same predictable toolchain foundation without making developers think
|
|
18
|
+
about toolchain plumbing.
|
|
19
|
+
- A build should produce the same useful result as a filesystem artifact, OCI image, or Docker image.
|
|
20
|
+
- Caching should be automatic, close to the step that uses it, and easy to understand.
|
|
21
|
+
- Local builds should stay local. Shared BoringCache credentials should make the same steps portable across fresh
|
|
22
|
+
machines and CI runners.
|
|
23
|
+
- Secrets belong in secret channels, never command lines, images, archives, plans, or logs.
|
|
24
|
+
|
|
25
|
+
## Our taste
|
|
26
|
+
|
|
27
|
+
We prefer a small amount of ordinary Ruby over a large amount of YAML. We choose conventions that solve a real
|
|
28
|
+
production build, then leave an honest extension point for applications that are different. We would rather add one
|
|
29
|
+
clear method than invent a framework around a possibility.
|
|
30
|
+
|
|
31
|
+
Errors should tell a tired person what happened and what to do next. Defaults should be safe. Generated files should
|
|
32
|
+
be short enough to understand in one sitting. Public documentation should sound like one human helping another.
|
|
33
|
+
|
|
34
|
+
## The human reason
|
|
35
|
+
|
|
36
|
+
The human behind BoringBuilder was tired of build tooling asking people to choose between convenience and control.
|
|
37
|
+
They should not have to. A build can be simple, programmatic, fast, repeatable, and owned by the application at the
|
|
38
|
+
same time.
|
|
39
|
+
|
|
40
|
+
That is the standard: Rails-shaped happiness, buildpack-shaped ease, and Dagger-shaped control.
|
data/STYLE.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# BoringBuilder style
|
|
2
|
+
|
|
3
|
+
Code should feel like a clear explanation of the build it performs. These are defaults with reasons, not laws. When
|
|
4
|
+
the code, security boundary, or user experience calls for something different, make that choice visible.
|
|
5
|
+
|
|
6
|
+
## Ruby
|
|
7
|
+
|
|
8
|
+
- Write ordinary, readable Ruby. Prefer a small object or method over a framework made for a possibility.
|
|
9
|
+
- Arrange a class so its public story reads from top to bottom: class methods, public initialization and entry points,
|
|
10
|
+
supporting public methods, then private implementation.
|
|
11
|
+
- Keep methods close to invocation order when that makes the flow easier to follow.
|
|
12
|
+
- Prefer expanded conditionals when both branches matter. Use an early return near the start of a method when it
|
|
13
|
+
removes an exceptional or invalid case.
|
|
14
|
+
- Reserve a bang method for a meaningful counterpart without a bang.
|
|
15
|
+
- Follow RuboCop for mechanical style. The configuration is the executable baseline shared by editors and CI.
|
|
16
|
+
|
|
17
|
+
## Product language
|
|
18
|
+
|
|
19
|
+
- Describe BoringBuilder as an application builder. Rails is the first-class convention, not the boundary.
|
|
20
|
+
- Lead with the command and the outcome. Explain Dagger, Mise, caching, or exporters only when they help someone make
|
|
21
|
+
the next decision.
|
|
22
|
+
- Write errors and documentation for a tired human. Be direct, specific, and calm.
|
|
23
|
+
|
|
24
|
+
## Changes
|
|
25
|
+
|
|
26
|
+
- Find the closest existing shape before introducing a new one.
|
|
27
|
+
- Keep provider details behind cache and exporter adapters. Keep build recipes ordinary Dagger Ruby over the shared
|
|
28
|
+
Mise foundation.
|
|
29
|
+
- Surface invariants from CI, security policy, the gemspec, and public APIs; do not restate version promises in prose
|
|
30
|
+
where they can quietly go stale.
|
|
31
|
+
- Test the behavior at its narrowest boundary, then run `bin/ci`. Changes to the Dagger graph also need a live build.
|
|
32
|
+
- Before calling a change done, attack the diff: look for accidental complexity, secret exposure, private paths,
|
|
33
|
+
generated files, misleading examples, and behavior that was only mocked.
|
data/docs/artifacts.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Artifacts and images
|
|
2
|
+
|
|
3
|
+
BoringBuilder exports a directory, tar archive, reproducible zstd tar archive, OCI image archive, or Docker image
|
|
4
|
+
archive. It can also publish artifacts to BoringCache, load an image into the selected runtime, or publish an image
|
|
5
|
+
to a registry.
|
|
6
|
+
|
|
7
|
+
## Export destinations
|
|
8
|
+
|
|
9
|
+
The default exporter is automatic:
|
|
10
|
+
|
|
11
|
+
- with a BoringCache save token and workspace, publish a shared BoringCache Artifact;
|
|
12
|
+
- otherwise, write the artifact under `dist/`;
|
|
13
|
+
- when `--output` is explicit, always keep that local copy as well.
|
|
14
|
+
|
|
15
|
+
Override the destination when needed:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
bundle exec boringbuilder build --exporter local
|
|
19
|
+
bundle exec boringbuilder build --exporter boringcache --artifact-name web-production
|
|
20
|
+
bundle exec boringbuilder build --json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The BoringCache exporter uploads directly from the Dagger graph and returns a ready Artifact ID. It does not stage
|
|
24
|
+
the build through a host directory. Exporters are small Ruby adapters around the same normalized build asset, so a
|
|
25
|
+
future repository backend does not change the build pipeline or artifact model.
|
|
26
|
+
|
|
27
|
+
## Filesystem artifacts
|
|
28
|
+
|
|
29
|
+
The default format is `tar.zst`. The built-in Ruby pipeline selects the application (`/rails` for Rails or `/app`
|
|
30
|
+
for other Ruby applications), `/usr/local`, and `/mise`. A custom pipeline selects the complete container root
|
|
31
|
+
unless the application narrows it.
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
bundle exec boringbuilder build --format directory --output dist/rootfs
|
|
35
|
+
bundle exec boringbuilder build --format tar --output dist/app.tar
|
|
36
|
+
bundle exec boringbuilder build --format tar.zst --output dist/app.tar.zst
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
A directory export replaces its destination. Archive exports replace the destination file.
|
|
40
|
+
|
|
41
|
+
Tar exports normalize ordering, timestamps, and ownership so an unchanged build produces the same archive.
|
|
42
|
+
|
|
43
|
+
Select or remap container paths with repeatable options:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
bundle exec boringbuilder build \
|
|
47
|
+
--path /rails=/opt/my-app/current \
|
|
48
|
+
--path /usr/local/bundle=/opt/my-app/current/vendor/bundle \
|
|
49
|
+
--file /usr/local/bin/mise=/usr/local/bin/mise \
|
|
50
|
+
--host-path ../release-metadata=/opt/my-app/release-metadata \
|
|
51
|
+
--format tar.zst \
|
|
52
|
+
--output dist/my-app-snapshot.tar.zst
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`--host-path` is explicit and requires a destination. It adds release metadata without widening the Docker build
|
|
56
|
+
context.
|
|
57
|
+
|
|
58
|
+
For a reusable layout, put the same choices in `config/boringbuilder.rb`:
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
BoringBuilder.configure do |config|
|
|
62
|
+
config.artifact.directory("/rails", at: "/opt/my-app/current")
|
|
63
|
+
config.artifact.directory("/usr/local/bundle", at: "/opt/my-app/current/vendor/bundle")
|
|
64
|
+
config.artifact.file("/usr/local/bin/mise")
|
|
65
|
+
config.artifact.host_path("../release-metadata", at: "/opt/my-app/release-metadata")
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Container images
|
|
70
|
+
|
|
71
|
+
Create portable image archives:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
bundle exec boringbuilder build --format oci --output dist/app.oci.tar
|
|
75
|
+
bundle exec boringbuilder build --format docker --output dist/app.docker.tar
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Load or publish the same built image:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
bundle exec boringbuilder build --load my-app:latest
|
|
82
|
+
bundle exec boringbuilder build --push ghcr.io/acme/my-app:latest
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Pass `--output` as well when both a local archive and a loaded or published image are required. Registry credentials
|
|
86
|
+
are owned by Dagger and the selected runtime; BoringBuilder does not accept them as command-line values.
|
|
87
|
+
|
|
88
|
+
## Ruby API
|
|
89
|
+
|
|
90
|
+
Applications and deployment tools can use the same configuration directly:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
result = BoringBuilder.build(
|
|
94
|
+
root: Rails.root,
|
|
95
|
+
runtime: :docker,
|
|
96
|
+
platform: "linux/arm64",
|
|
97
|
+
format: :tar_zst,
|
|
98
|
+
output: Rails.root.join("dist/app.tar.zst")
|
|
99
|
+
) do |config|
|
|
100
|
+
config.artifact.directory("/rails", at: "/opt/my-app/current")
|
|
101
|
+
config.artifact.directory("/usr/local/bundle", at: "/opt/my-app/current/vendor/bundle")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
puts result.path
|
|
105
|
+
puts result.artifact_id
|
|
106
|
+
```
|
data/docs/building.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Building applications
|
|
2
|
+
|
|
3
|
+
BoringBuilder resolves one production build for an application, then executes it through Dagger. Run commands from
|
|
4
|
+
the application root unless a project directory is passed explicitly.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
bundle exec boringbuilder build
|
|
8
|
+
bundle exec boringbuilder build /path/to/application
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Build resolution
|
|
12
|
+
|
|
13
|
+
BoringBuilder chooses one pipeline:
|
|
14
|
+
|
|
15
|
+
1. the custom Dagger Ruby pipeline in `config/boringbuilder.rb`, when configured;
|
|
16
|
+
2. otherwise, the built-in Ruby pipeline.
|
|
17
|
+
|
|
18
|
+
An application recipe can also tune the built-in pipeline and define its artifact layout. Command-line options
|
|
19
|
+
override those settings for a one-off build.
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
bundle exec boringbuilder build --config config/release.rb
|
|
23
|
+
bundle exec boringbuilder build --no-config
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Applications with `package.json` must provide a custom pipeline. BoringBuilder does not guess which JavaScript
|
|
27
|
+
runtime or package manager an application uses.
|
|
28
|
+
|
|
29
|
+
## Generate a recipe
|
|
30
|
+
|
|
31
|
+
Run `init` when an application needs a custom pipeline or checked-in overrides:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
boringbuilder init
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
BoringBuilder detects a conventional Ruby, Node, Rust, or Go project and writes `config/boringbuilder.rb`. An
|
|
38
|
+
unrecognized project receives a small generic Dagger recipe. Select a starting point explicitly when detection is
|
|
39
|
+
not enough:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
boringbuilder init --template node
|
|
43
|
+
boringbuilder init --template rust
|
|
44
|
+
boringbuilder init --template go
|
|
45
|
+
boringbuilder init --template generic
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The generated file is ordinary Ruby using BoringBuilder's Mise toolchain, Dagger containers, and cache/export
|
|
49
|
+
conventions. It is a starting point owned by the application, not generated state or a separate configuration
|
|
50
|
+
language. Recipes execute as Ruby on the host, so review them before building source you do not trust. `init` refuses
|
|
51
|
+
to replace an existing recipe; pass `--force` only when replacement is intentional.
|
|
52
|
+
|
|
53
|
+
## Built-in Rails and Ruby pipeline
|
|
54
|
+
|
|
55
|
+
The built-in pipeline:
|
|
56
|
+
|
|
57
|
+
- reads the Ruby version from `mise.toml`, `.mise.toml`, `.tool-versions`, `.ruby-version`, `Gemfile`, or
|
|
58
|
+
`Gemfile.lock`;
|
|
59
|
+
- installs the toolchain with Mise and locked production gems with separate persistent caches;
|
|
60
|
+
- separates build packages from the runtime image;
|
|
61
|
+
- places the application under a non-root user; and
|
|
62
|
+
- leaves runtime secrets out of the image and exported artifact.
|
|
63
|
+
|
|
64
|
+
`mise.toml` (or `.mise.toml`) is read from its `[tools]` section using the conventional exact string form:
|
|
65
|
+
|
|
66
|
+
```toml
|
|
67
|
+
[tools]
|
|
68
|
+
ruby = "4.0.6"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The project file is copied into the build so its Ruby version and Mise settings are respected. The built-in path
|
|
72
|
+
installs Ruby only; custom pipelines can install any additional tools. When no Mise file is present, BoringBuilder
|
|
73
|
+
generates the minimal Ruby declaration from the other version sources.
|
|
74
|
+
|
|
75
|
+
## Framework conventions
|
|
76
|
+
|
|
77
|
+
A root `Gemfile` selects the Ruby builder. Rails applications are detected by `config/application.rb` and receive
|
|
78
|
+
asset, Bootsnap, entrypoint, Thruster, port, and `/up` health-check conventions.
|
|
79
|
+
|
|
80
|
+
Hanami applications are detected by `config/app.rb` and use the production Puma configuration when present. Rack
|
|
81
|
+
applications are detected by `config.ru`. For any Ruby application, a `web:` entry in `Procfile` takes precedence.
|
|
82
|
+
Applications without a detected process still build a complete artifact; set a command in the recipe when producing
|
|
83
|
+
a directly runnable image:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
BoringBuilder.configure do |config|
|
|
87
|
+
config.command = %w[bundle exec sidekiq]
|
|
88
|
+
config.port = 3000
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Add application-specific Debian packages without replacing the built-in pipeline:
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
BoringBuilder.configure do |config|
|
|
96
|
+
config.build_packages += %w[libsodium-dev]
|
|
97
|
+
config.runtime_packages += %w[libsodium23]
|
|
98
|
+
config.build_environment["REDIS_URL"] = "redis://127.0.0.1:6379/0"
|
|
99
|
+
end
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`build_environment` is for non-secret values needed while compiling. Use Dagger secrets in a custom pipeline for
|
|
103
|
+
sensitive build inputs.
|
|
104
|
+
|
|
105
|
+
Use a custom pipeline when the application needs JavaScript package installation, unusual system packages, a
|
|
106
|
+
different operating-system foundation, or is not a Ruby application.
|
|
107
|
+
|
|
108
|
+
## Mise-powered application pipeline
|
|
109
|
+
|
|
110
|
+
For any application outside the built-in conventions, keep the build definition in Ruby. A recipe block receives a
|
|
111
|
+
small pipeline object and returns the final Dagger container:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
# config/boringbuilder.rb
|
|
115
|
+
BoringBuilder.configure do |config|
|
|
116
|
+
config.artifact.directory("/app/dist", at: "/app")
|
|
117
|
+
|
|
118
|
+
config.pipeline do |pipeline|
|
|
119
|
+
app = pipeline.mise(tools: { node: "24" }, workdir: "/app")
|
|
120
|
+
|
|
121
|
+
app = pipeline.run(
|
|
122
|
+
app,
|
|
123
|
+
%w[npm ci],
|
|
124
|
+
cache: "npm-downloads",
|
|
125
|
+
at: "/root/.npm/_cacache",
|
|
126
|
+
workdir: "/app",
|
|
127
|
+
name: "Install dependencies"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
pipeline.exec(app, %w[npm run build], name: "Build application")
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`pipeline.mise` prepares the requested tools and application source in a Dagger container. Project versions in
|
|
136
|
+
`mise.toml`, `.mise.toml`, or `.tool-versions` override recipe fallbacks, and `mise.lock` is respected when present.
|
|
137
|
+
Any tool in the [Mise registry](https://mise.jdx.dev/registry.html) can use the same shape.
|
|
138
|
+
|
|
139
|
+
`pipeline.run` persists the declared path in the local Dagger engine or through BoringCache when shared credentials
|
|
140
|
+
are present. `pipeline.exec` runs an ordinary named command. Both execute as real Dagger boundaries and show the
|
|
141
|
+
command's output beneath their numbered build step. Use a hash to cache several paths in one step. See
|
|
142
|
+
[Caching](caching.md) for both modes.
|
|
143
|
+
|
|
144
|
+
Both helpers return ordinary Dagger Ruby containers and use the same artifact and image exporters as the built-in
|
|
145
|
+
build. `pipeline.container` remains available when an application needs a different foundation.
|
|
146
|
+
|
|
147
|
+
## Container runtime
|
|
148
|
+
|
|
149
|
+
`auto` prefers Apple Container on supported Apple silicon Macs and Docker elsewhere. A configured Dagger session can
|
|
150
|
+
point at an engine on a Linux VM without changing the build. Select a local runtime explicitly in CI and other
|
|
151
|
+
reproducible environments:
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
bundle exec boringbuilder doctor --runtime apple
|
|
155
|
+
bundle exec boringbuilder build --runtime apple
|
|
156
|
+
bundle exec boringbuilder build --runtime docker
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Start Apple Container with `container system start`, or start the Docker daemon before selecting Docker. An existing
|
|
160
|
+
Dagger session or custom runner is used without probing the host runtime.
|
|
161
|
+
|
|
162
|
+
Dagger CLI and Engine versions must match the version required by Dagger Ruby. `boringbuilder doctor` checks that
|
|
163
|
+
contract before a build; pointing a different CLI at the expected Engine image is not an equivalent setup.
|
|
164
|
+
|
|
165
|
+
## Build options
|
|
166
|
+
|
|
167
|
+
Target a deployment platform or inspect the resolved plan without building:
|
|
168
|
+
|
|
169
|
+
```sh
|
|
170
|
+
bundle exec boringbuilder build --platform linux/arm64
|
|
171
|
+
bundle exec boringbuilder build --command "bundle exec puma -C config/puma.rb" --port 3000
|
|
172
|
+
bundle exec boringbuilder build --dry-run
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Run `bundle exec boringbuilder build --help` for the complete command reference.
|
data/docs/caching.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Caching
|
|
2
|
+
|
|
3
|
+
BoringBuilder has two cache paths. The build command and pipeline stay the same for both.
|
|
4
|
+
|
|
5
|
+
## Local cache
|
|
6
|
+
|
|
7
|
+
Without BoringCache credentials, Dagger stores its content-addressed build graph plus BoringBuilder's named cache
|
|
8
|
+
mounts in the selected engine. Repeated builds on a persistent developer or CI engine reuse them automatically.
|
|
9
|
+
|
|
10
|
+
This cache belongs to that Dagger engine. A fresh ephemeral runner starts cold unless it connects to a persistent
|
|
11
|
+
Dagger engine or uses the shared mode below.
|
|
12
|
+
|
|
13
|
+
## Shared cache with BoringCache
|
|
14
|
+
|
|
15
|
+
Shared cache turns on when BoringBuilder finds both:
|
|
16
|
+
|
|
17
|
+
- `BORINGCACHE_RESTORE_TOKEN` or `BORINGCACHE_SAVE_TOKEN`; and
|
|
18
|
+
- `BORINGCACHE_DEFAULT_WORKSPACE`, `BORINGCACHE_WORKSPACE`, or a project `.boringcache.toml` with its workspace.
|
|
19
|
+
|
|
20
|
+
The built-in Ruby pipeline then runs both `mise install` and `bundle install` through `boringcache run`. Tokens are
|
|
21
|
+
Dagger secrets and are not written to the resulting image or artifact. A restore token without a save token selects
|
|
22
|
+
read-only mode.
|
|
23
|
+
|
|
24
|
+
BoringCache persists those directories across fresh engines and runners. When shared caching is enabled it owns the
|
|
25
|
+
declared cache directories for that step; Dagger still reuses the rest of its local build graph. Selecting one owner
|
|
26
|
+
avoids overlapping mount and restore semantics. Restore, command execution, and save all happen inside the Dagger
|
|
27
|
+
container, with no host staging copy.
|
|
28
|
+
|
|
29
|
+
## Custom application steps
|
|
30
|
+
|
|
31
|
+
Custom application recipes use the same cache primitive as the built-in Mise and Bundler steps:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
app = pipeline.run(
|
|
35
|
+
app,
|
|
36
|
+
%w[bundle exec rake compile],
|
|
37
|
+
cache: {
|
|
38
|
+
"compile" => "/app/tmp/cache",
|
|
39
|
+
"downloads" => "/app/tmp/downloads"
|
|
40
|
+
},
|
|
41
|
+
name: "Compile application"
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Each hash key names a stable cache and each value is the directory visible to the command. With BoringCache
|
|
46
|
+
credentials those names and paths are restored and saved remotely. Without credentials, Dagger mounts named local
|
|
47
|
+
volumes at the same paths. Manual cache paths are removed after the command so dependency stores do not leak into
|
|
48
|
+
an image or artifact; copy any required build output elsewhere inside the cached command. Pass the single-cache
|
|
49
|
+
shorthand with `entry: "bundler"` (or another built-in/project entry) when BoringCache should use that existing
|
|
50
|
+
entry definition.
|
|
51
|
+
|
|
52
|
+
Cache persistence stays inside the Dagger step. Wrapping `boringbuilder build` with a host-side Docker or cache
|
|
53
|
+
command cannot see Dagger's named cache volumes, so BoringBuilder intentionally has no Dockerfile frontend.
|