Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 193 additions & 0 deletions src/content/docs/blog/recipe-v2.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
---
title: Introducing Recipe V2
description: Why we revamped the Recipe format, and what you need to do.
date: 2026-06-13
authors:
- name: xyny
img: https://avatars.githubusercontent.com/u/60004820
github: xynydev
---

:::tip[TLDR]
No, you don't need to do anything. **Your builds will not break.** You may choose to refactor your recipe files to the new V2 format based on the [guide below](#refactoring-a-recipe-from-v1-to-v2). _You may also choose to read the technical waffling below, if you're interested in the reasoning for this update._
:::

For [over two years now](https://github.com/blue-build/cli/issues/180), I have been of the opinion that the Recipe format that BlueBuild has been using since the start is very much _not perfect_. It works, yes, and that's why we haven't been in a rush to change it, but over the years as the project has grown the format has become ever more cluttered. Though the format started with having just the four essential top-level keys (`name:`, `description:`, `base-image:` and `image-version:`), we now also have `alt-tags:`, `blue-build-tag:`, `cosign-version:` and `labels:` and many more. As such, it became clear to us that this mess needs some organization, with related options grouped together.

Additionally, with `base-image:` and `image-version:` being used together to construct the [ref to the specific OCI image](https://oras.land/docs/concepts/reference/) with no way for the user to specify a full ref or a digest, we effectively made it impossible to build images on top of a specific digest.

To fix all this, we recently [finalized the schema for Recipe V2](https://github.com/blue-build/schema/pull/19) and [implemented support for it in the CLI](https://github.com/blue-build/cli/pull/789) (thanks Jerry). The CLI version v0.9.36 should now fully support using Recipe V2 on an opt-in basis (add `version: 2` at the top of your recipe).

As part of this transition we will also refactor the template to use Recipe V2 and the documentation to prefer Recipe V2 whenever applicable. You may still choose to keep using V1 and documentation for it will keep being available, even though we recommend using V2 first and foremost.

## What does it look like, then?

The easiest way to illustrate this point is to just show a recipe file declaring the exact same image in both the Recipe V1 and V2 formats.

### V1

```yaml
# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v1.json

# metadata
name: weird-os
description: This is my personal OS image.

# image ref
base-image: ghcr.io/blue-build/base-images/fedora-silverblue
image-version: gts

# we're on the latest fedora version -1 so let's call it gts
alt-tags: [gts]

# we don't need these i guess
cosign-version: none
nushell-version: none

# building arm images
platforms:
- linux/amd64
- linux/arm64

# add labels for the sake of it
labels:
- my.custom.label: test

stages:
# ... omitted, because nothing changed here

modules:
# ... omitted, because nothing changed here
```

### V2

```yaml
# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v2.json
version: 2

# metadata tells the world what the image is
metadata:
name: weird-os
description: This is my personal OS image.
tags: [gts]
labels:
- my.custom.label: test

# base is the image you are building on top of
base:
# the whole ref can be defined at once
image: ghcr.io/blue-build/base-images/fedora-silverblue:gts

# or it can be defined in parts
# image:
# registry: ghcr.io
# repository: bluebuild/base-images/fedora-silverblue
# tag: gts

# we can now verify the base image using a public key

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code for this hasn't been written quite yet

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it should be done before V2 is out?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes definitely. I'll put it behind a feature flag until it's all ready in case I gotta push a patch out

public-key: https://raw.githubusercontent.com/blue-build/base-images/refs/heads/main/cosign.pub

# spec changes how the image is built
spec:
# we don't need these i guess
tool-versions:
cosign: none
nushell: none

# building arm images
platforms:
- linux/amd64
- linux/arm64

stages:
# ... omitted, because nothing changed here

modules:
# ... omitted, because nothing changed here
```

## I don't like it!

That's alright. You do not need to use it.

## Refactoring a Recipe from V1 to V2

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm planning on adding a recipe subcommand which will allow upgrading the recipe file automatically, though this process would end up deleting any comments the user added. That way it's easier for the user to upgrade.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I shall document this in the announcement as well. I think having both the manual instructions and the automatic method is good, especially since it deletes comments.


1. Replace the top of your Recipe to opt in to V2.

Before:
```yaml
# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v1.json
```
After:
```yaml
# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v2.json
version: 2
```

2. Add the mandatory `metadata:` top-level key and move your existing `name:`, `description:` and `labels:` declarations under it.

Before:
```yaml
name: weird-os
description: This is my personal OS image.
alt-tags: [gts]
labels:
- my.custom.label: test
```
After:
```yaml
metadata:
name: weird-os
description: This is my personal OS image.
tags: [gts]
labels:
- my.custom.label: test
```

3. Add the mandatory `base:` top-level key and combine your `base-image:` and `image-version:` declarations into the `image:` declaration.

Before:
```yaml
base-image: ghcr.io/blue-build/base-images/fedora-silverblue
image-version: latest
```
After:
```yaml
base:
image: ghcr.io/blue-build/base-images/fedora-silverblue:latest
```

After (alternative, if preferred):
```yaml
base:
image:
registry: ghcr.io
repository: blue-build/base-images/fedora-silverblue
tag: latest
```

4. If applicable, add the optional `spec:` top-level key and move related declarations under it.

Before:
```yaml
blue-build-tag: main
cosign-version: none
nushell-version: none
platforms:
- linux/amd64
- linux/arm64
```
After:
```yaml
spec:
tool-versions:
bluebuild: main
cosign: none
nushell: none
Comment on lines +186 to +187

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default in v2, cosign and nushell won't be automatically installed unless a version is explicitly set, so removing these 2 lines would be equivalent to setting them as none in v1. The bluebuild CLI still follows the v1 pattern though where it's automatically installed since it's our tool.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I guess I should refactor this part then. We don't need these tools on image anymore?

Oh and BTW, I don't/didn't think Nushell was ever installed in PATH. At least on my machine I added /usr/libexec/bluebuild/nu to PATH manually for testing purposes. The only purpose of Nushell on the image is to make dependent modules work, and using the BlueBuild Nushell version is very much opt-in.

If it isn't automatically installed, what happens to modules like default-flatpaks? Does Nushell get installed automatically when a dependent module is installed?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it isn't automatically installed, what happens to modules like default-flatpaks? Does Nushell get installed automatically when a dependent module is installed?

Wait are the scripts used by systemd written in nushell? Not just the module?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and small CLI bluebuild-flatpak-manager too. They all use a #!/usr/libexec/bluebuild/nu/nu shebang so that Nu need not be in path.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll change the logic for nushell then. Cosign isn't needed by default since the CLI has the sigstore crate. That will become the only method the CLI uses for cosign activities in 1.0

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably deprecate cosign installation when it become unused then? There's no need to have a feature in BlueBuild that just installs one program that is not needed IMO.

platforms:
- linux/amd64
- linux/arm64
```

5. You're done! `modules:` (and `stages:` if you're using them) do not need to be touched at all.
Loading