Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
/result
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024"

[dependencies]
aho-corasick = "1.1.3"
async-tempfile = "0.7.0"
clap = { version = "4.5.45", features = ["derive"] }
docker_credential = "1.3.2"
Expand All @@ -17,12 +18,14 @@ oci-client = { version = "0.15.0", default-features = false, features = [
"rustls-tls",
] }
olpc-cjson = "0.1.4"
once_cell = "1.21.3"
prodash = { version = "30.0.1", features = [
"render-line",
"render-line-crossterm",
] }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.142"
serde_repr = "0.1.20"
serde_yml = "0.0.12"
sha2 = "0.10.9"
subst = "0.3.8"
Expand Down
74 changes: 72 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,89 @@ Key difference from Skaffold: Steiger works directly with OCI image layouts, ski

Supports [Ko](https://ko.build/) for building Go applications into container images without Dockerfiles.

### Nix
Integrates with [Nix](https://nixos.org/) flake outputs that produce OCI images.

Requirements

- Flakes enabled (`--extra-experimental-features 'nix-command flakes'`)
- `pkgs.ociTools.buildImage` (available via Steiger overlay or [nixpkgs#390624](https://github.com/NixOS/nixpkgs/pull/390624))

<details>
<summary>Example flake</summary>

```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
steiger.url = "github:brainhivenl/steiger";
};

outputs = {
nixpkgs,
steiger,
...
}: let
system = "x86_64-linux";
overlays = [steiger.overlays.ociTools];
pkgs = import nixpkgs { inherit system overlays; };
in {
packages.${system} = {
default = pkgs.ociTools.buildImage {
name = "hello";

copyToRoot = pkgs.buildEnv {
name = "hello-env";
paths = [pkgs.hello];
pathsToLink = ["/bin"];
};

config.Cmd = ["/bin/hello"];
compressor = "none";
};
};

devShells.${system} = {
default = pkgs.mkShell {
packages = [steiger.packages.${system}.default];
};
};
};
}
```
</details>

## Build Caching

Steiger delegates caching to the underlying build systems rather than implementing its own cache layer:

- **Docker BuildKit**: Leverages BuildKit's native layer caching and build cache
- **Bazel**: Uses Bazel's extensive caching system (action cache, remote cache, etc.)
- **Ko**: Benefits from Go's build cache and Ko's layer caching
- **Nix**: Utilizes Nix's content-addressed store and binary cache system for reproducible, cached builds

This approach avoids cache invalidation issues and performs comparably to Skaffold in cached scenarios, with better performance in some cases.

## Installation

### Using cargo

```bash
cargo install steiger --git https://github.com/brainhivenl/steiger.git
```

Or build from source:
### Using nix

Run directly without installation:

```bash
nix run github:brainhivenl/steiger -- build
```

### Build from source

```bash
git clone https://github.com/yourusername/steiger
git clone https://github.com/brainhivenl/steiger
cd steiger
cargo build --release
```
Expand Down Expand Up @@ -83,6 +146,13 @@ services:
type: ko
importPath: ./cmd/service

flake:
build:
type: nix
systems: ["x86_64-linux"]
packages:
api: default # attribute path to package e.g. `outputs.packages.<system>.default`

profiles:
prod:
env: prod
Expand Down
81 changes: 81 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
nixpkgs-ocitools.url = "github:msanft/nixpkgs/msanft/oci/refactor";

crane.url = "github:ipetkov/crane";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};

outputs = {
self,
nixpkgs,
crane,
...
} @ inputs: let
inherit (nixpkgs) lib;

forEachSystem = fun:
lib.genAttrs (lib.systems.flakeExposed) (
system: fun (import nixpkgs {inherit system;})
);
in {
packages = forEachSystem (
pkgs: let
craneLib = crane.mkLib pkgs;
commonArgs = {
src = craneLib.cleanCargoSource ./.;
strictDeps = true;
buildInputs =
[pkgs.nix pkgs.nix-eval-jobs]
++ lib.optionals pkgs.stdenv.isDarwin [pkgs.libiconv];
};
in {
default = craneLib.buildPackage (
commonArgs
// {
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
meta.mainProgram = "steiger";

NIX_BINARY = lib.getExe pkgs.nix;
NIX_EVAL_JOBS_BINARY = lib.getExe pkgs.nix-eval-jobs;
}
);
}
);

checks = forEachSystem (pkgs: {
inherit (self.packages.${pkgs.system}) default;
});

devShells = forEachSystem (
pkgs: let
craneLib = crane.mkLib pkgs;
in {
default = craneLib.devShell {
packages = [
pkgs.nix-eval-jobs
];
};
}
);

overlays.ociTools = final: prev: let
pkgs = import inputs.nixpkgs-ocitools {inherit (final) system;};
in {
inherit (pkgs) ociTools;
};
};
}
2 changes: 1 addition & 1 deletion src/builder/bazel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub enum BazelError {
Exit(#[from] ExitError),
#[error("failed to deserialize cquery output")]
Serde(#[from] serde_json::Error),
#[error("unable to find artifact for target")]
#[error("unable to find artifact for target: {0}")]
MissingArtifact(String),
}

Expand Down
18 changes: 15 additions & 3 deletions src/builder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
builder::{bazel::BazelBuilder, docker::DockerBuilder, ko::KoBuilder},
builder::{bazel::BazelBuilder, docker::DockerBuilder, ko::KoBuilder, nix::NixBuilder},
config::{Build, Config},
image::Image,
};
Expand All @@ -12,18 +12,22 @@ use tokio::{task::JoinSet, time::Instant};
mod bazel;
mod docker;
mod ko;
mod nix;

#[derive(Debug, Diagnostic, thiserror::Error)]
pub enum BuildError {
#[error("ko error")]
#[diagnostic(transparent)]
Ko(#[from] ErrorOf<KoBuilder>),
#[error("bazel error")]
#[diagnostic(transparent)]
Bazel(#[from] ErrorOf<BazelBuilder>),
#[error("docker error")]
#[diagnostic(transparent)]
Docker(#[from] ErrorOf<DockerBuilder>),
#[error("bazel error")]
#[error("nix error")]
#[diagnostic(transparent)]
Bazel(#[from] ErrorOf<BazelBuilder>),
Nix(#[from] ErrorOf<NixBuilder>),
}

#[derive(Debug, Default)]
Expand Down Expand Up @@ -107,6 +111,7 @@ pub struct MetaBuild {
ko: Option<KoBuilder>,
bazel: Option<BazelBuilder>,
docker: Option<DockerBuilder>,
nix: Option<NixBuilder>,
}

impl MetaBuild {
Expand All @@ -116,6 +121,7 @@ impl MetaBuild {
ko: None,
bazel: None,
docker: None,
nix: None,
}
}

Expand Down Expand Up @@ -150,6 +156,12 @@ impl MetaBuild {
.map_err(BuildError::Docker),
);
}
Build::Nix(nix) => {
set.spawn(
run_builder(&mut self.nix, progress, ctx.with(nix.clone()))?
.map_err(BuildError::Nix),
);
}
};
}

Expand Down
Loading