File-based configuration and composable build plans #508
coffee-cup
started this conversation in
RFCs
Replies: 2 comments 4 replies
-
I just came here to see if anything progressed from #120 (I'm adding nixpacks support to Dokku right now). This is great to see, and I'm excited for where this is headed, as I didn't want to add a bunch of knobs that might be overriden by a file-based config. |
Beta Was this translation helpful? Give feedback.
2 replies
-
Personally, I think |
Beta Was this translation helpful? Give feedback.
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
This RFC proposes an architectural change to Nixpacks that will allow
But before we get into the details, lets see some examples of
nixpacks.toml
files and how it could work.Simple configuration of build
More configuration with extending
Run multiple providers
Creating an new phase
screenshot-2022-09-06-19.22.00.mp4
Note: A lot of this PR is already implemented while I was experimenting with different configuration formats. It can easily change based on feedback on this proposal, but you can try it out with this branch
File Based Configuration
The only way to configure Nixpacks currently is through environment variables and CLI flags (which doesn't work on Railway). Like many other build tools, Nixpacks needs a way to be configured through a file so that it can be checked into version control and the changes tracked over time. I propose we support both a
nixpacks.toml
andnixpacks.json
file. The contents of these configuration files is simply a serializedBuildPlan
, the exact same plan that the providers generate based on the contents of the app. This allows every part of the build to be customized by the user and will ensure full customization moving forward since we don't have to maintain a separate configuration object.Everything is a plan
At the end of the day, everything that can configure a Nixpacks build becomes a plan. This includes things like CLI flags and environment variables, as well as the result of running a provider. These plans are then merged together resulting in a final plan that contains everything needed to create an image from the user's source code.
Merging priority
Build plans are merged together with the following priority
A non-null property in a plan will override the property in a lower priority plan. If a property is null in a high-priority plan but non-null in a lower-priority plan, the non-null value will be used. For example,
Property extending
Extending array properties is done with the
"..."
or"@auto"
values. This allows you to easily reference the array values of the plan being merged into. For exampleThis is powerful since the plan from the providers is always the lowest priority, which means you can always extend the automatic configuration, or override it completely.
Manually setting the provider
Providers can be manually specified by setting them in the
nixpacks.toml
file. A null value for the providers indicates that a single provider that passes the detect phase should be run (same as current behaviour). This is useful since you do not have to rely on detection to use a specific language if your app is setup in an unconventional way.Multiple Providers
This also opens the doors to specifying that multiple providers should run on the app.
This is useful if your app uses multiple languages and you want an easy way to install everything (e.g. Rust server that uses Node for the frontend). To avoid naming conflicts with provider build phases, the phases should be prefixed with the provider that generated them (e.g. "build" becomes "node:build").
Note: After some initial experimentation with multiple providers, a few providers will need to be updated to handle when certain language files are missing. There will also need to be some optimizations when installing Nix/Apt packages. But these problems are all easily fixable.
Reusable Providers
Since build plans can be composed together, all re-usable behaviour can be packaged into a provider. This includes things like Procfile support and Postgres/MySQL installation.
Provider Configuration
Many of the providers have some sort of configuration that the user can provide. This includes things like versions of tools to install or directories to serve. These will be able to be specified in the
nixpacks.toml
file under nested underconfig
.These will be deserialized into structs defined by each provider and passed into
get_build_phase(app, env, config)
.Nixpacks Freeze
There may be a case where you want to eject from the automatic Railway providers. You can accomplish this by setting
providers = []
which means that no Railway providers will run and only the plan from thenixpacks.toml
file (or environment variables) will be used. There will also exist anixpacks freeze
command that will generate a build plan off your app and freeze it, which can be saved to disk in anixpacks.toml
file. When building with this file, the same Nix archive, packages, and commands will be used every time. This will allow you full control over your build with no magic.Container building
Running Nixpacks with no providers allows you to do some interesting things since the
nixpacks.toml
file is basically a DSL on top of building Dockerfiles, but in a shortened and much more readable way. ConsiderThis creates an image that doesn't use Nix or the Nixpacks providers at all. This use case will need some optimizations, but it gives a look into the future of Nixpacks and file base configuration.
External static and dynamic providers
Being able to manually specify providers to run opens the door up for external providers.
Static providers
Static providers simply provide a build plan in either toml or json format. They do not have access to the user app or environment. This can be useful for things like postgres/mysql support or installing a combination certain Debian pacakges (e.g. font/emoji support, c libraries).
Supporting static providers is possible with the architecture proposed above and will open up Nixpacks to a re-usable build configuration marketplace.
Dynamic providers
Dynamic providers can analyze the user code and look at the environment to generate a build plan (like our providers). This has many security risk involved since you will be running untrusted code so needs to be handled with care. This is much more longer looking and will require interfacing with a binary or compiling code on the fly.
Beta Was this translation helpful? Give feedback.
All reactions