Skip to content
Draft
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ coverage.txt
dist/
.scannerwork/
engine-ci-*
trivy.json
trivy.json
/main
10 changes: 10 additions & 0 deletions client/pkg/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,13 @@ func NewPythonLibraryBuild(appName string) *BuildArgs {
lib.Image = ""
return lib
}

func NewRustServiceBuild(appName string) *BuildArgs {
return NewServiceBuild(appName, protos2.BuildType_Rust)
}

func NewRustLibraryBuild(appName string) *BuildArgs {
lib := NewRustServiceBuild(appName)
lib.Image = ""
return lib
}
3 changes: 3 additions & 0 deletions cmd/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/containifyci/engine-ci/pkg/protobuf"
"github.com/containifyci/engine-ci/pkg/pulumi"
"github.com/containifyci/engine-ci/pkg/python"
"github.com/containifyci/engine-ci/pkg/rust"
"github.com/containifyci/engine-ci/pkg/sonarcloud"
"github.com/containifyci/engine-ci/pkg/trivy"
"github.com/containifyci/engine-ci/pkg/utils"
Expand Down Expand Up @@ -417,13 +418,15 @@ func InitBuildSteps() {
addStep(build.Build, golang.NewCGO()) // CGO variant
addStep(build.Build, maven.New()) // Maven
addStep(build.Build, python.New()) // Python
addStep(build.Build, rust.New()) // Rust
addStep(build.Build, zig.New()) // Zig

// PostBuild: Production artifacts, packaging
addStep(build.PostBuild, golang.NewProd()) // Alpine prod
addStep(build.PostBuild, golang.NewProdDebian()) // Debian prod
addStep(build.PostBuild, maven.NewProd()) // Maven prod
addStep(build.PostBuild, python.NewProd()) // Python prod
addStep(build.PostBuild, rust.NewProd()) // Rust prod
addStep(build.PostBuild, zig.NewProd()) // Zig prod

// Quality: Linting, testing, security scanning
Expand Down
123 changes: 123 additions & 0 deletions docs/RUST_BUILD_EXAMPLES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Rust Build Script Examples

This document shows example build scripts generated by the Rust build support.

## Example 1: Basic Release Build

**Configuration:**
```go
profile: "release"
folder: "."
verbose: false
```

**Generated Script:**
```bash
#!/bin/sh
set -e
export CARGO_HOME=/root/.cargo
cargo build --color never --release
cargo test --color never --release
```

## Example 2: Debug Build with Verbose Output

**Configuration:**
```go
profile: "debug"
folder: "."
verbose: true
```

**Generated Script:**
```bash
#!/bin/sh
set -xe
export CARGO_HOME=/root/.cargo
cargo build --color never --verbose
cargo test --color never --verbose
```

## Example 3: Release Build with Target Platform

**Configuration:**
```go
profile: "release"
target: "x86_64-unknown-linux-musl"
folder: "."
```

**Generated Script:**
```bash
#!/bin/sh
set -e
export CARGO_HOME=/root/.cargo
cargo build --color never --release --target x86_64-unknown-linux-musl
cargo test --color never --release --target x86_64-unknown-linux-musl
```

## Example 4: Release Build with Cargo Features

**Configuration:**
```go
profile: "release"
features: ["tokio", "serde"]
folder: "."
```

**Generated Script:**
```bash
#!/bin/sh
set -e
export CARGO_HOME=/root/.cargo
cargo build --color never --release --features tokio,serde
cargo test --color never --release --features tokio,serde
```

## Example 5: Build in Subdirectory

**Configuration:**
```go
profile: "release"
folder: "my-project"
```

**Generated Script:**
```bash
#!/bin/sh
set -e
export CARGO_HOME=/root/.cargo
cd my-project
cargo build --color never --release
cargo test --color never --release
```

## Example 6: Full-Featured Build

**Configuration:**
```go
profile: "release"
target: "x86_64-unknown-linux-musl"
features: ["server", "tls", "logging"]
folder: "services/api"
verbose: true
```

**Generated Script:**
```bash
#!/bin/sh
set -xe
export CARGO_HOME=/root/.cargo
cd services/api
cargo build --color never --release --target x86_64-unknown-linux-musl --features server,tls,logging --verbose
cargo test --color never --release --target x86_64-unknown-linux-musl --verbose
```

## Notes

- All scripts start with proper shebang (`#!/bin/sh`)
- Error handling is enabled with `set -e` (exit on error)
- Verbose mode adds `set -x` for command echoing
- Cargo output colors are disabled for better log parsing
- Tests are always run after the build to ensure correctness
- Cache location is explicitly set via `CARGO_HOME`
215 changes: 215 additions & 0 deletions docs/RUST_BUILD_SUPPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# Rust Build Support for Engine-CI

## Overview

Engine-CI now supports building Rust projects using Cargo in containerized environments. The implementation follows the same pattern as Go and Zig builds, providing a consistent experience across languages.

## Features

- **Containerized Builds**: Uses `rust:1.83-alpine` base image for consistent build environments
- **Build Profiles**: Support for release and debug builds
- **Cross-compilation**: Target specification for different platforms
- **Cargo Features**: Enable specific cargo features during build
- **Caching**: Intelligent caching of Cargo dependencies via `CARGO_HOME`
- **Testing**: Automatic test execution as part of the build process
- **Production Images**: Create optimized production images with compiled binaries

## Usage

### Basic Build Configuration

To build a Rust project with engine-ci, configure your build with `BuildType: Rust`:

```go
build := container.Build{
BuildType: container.Rust,
App: "my-app",
Image: "my-rust-app",
ImageTag: "latest",
Folder: ".",
}
```

### Build Profiles

Specify the build profile (defaults to "release"):

```go
build.Custom.Set("profile", "release") // Release build (optimized)
build.Custom.Set("profile", "debug") // Debug build (with debug symbols)
```

### Target Platform

Specify a target for cross-compilation:

```go
build.Custom.Set("target", "x86_64-unknown-linux-musl")
```

### Cargo Features

Enable specific cargo features:

```go
build.Custom.Set("features", "tokio,serde")
```

### Cache Configuration

The Rust build uses Cargo's cache to speed up subsequent builds. The cache location can be configured via environment variables:

- `CARGO_HOME`: Primary location for Cargo cache
- `CONTAINIFYCI_CACHE`: Fallback cache location

Default cache location (if not set): `/tmp/.cargo`

## Build Process

The Rust build process follows these steps:

1. **Pull Base Images**: Downloads the Alpine and Rust base images
2. **Build Rust Image**: Creates an intermediate image with Rust toolchain
3. **Build Binary**: Runs `cargo build` with specified options
4. **Run Tests**: Executes `cargo test` to validate the build
5. **Create Image**: Commits the container with the built binary
6. **Tag Image**: Tags the image with the specified name and tag

## Production Build

The production build (PostBuild step) creates an optimized final image:

1. Creates a minimal Alpine-based container
2. Copies only the compiled binary from `target/{profile}/`
3. Sets appropriate CMD and WORKDIR
4. Pushes to the specified registry

## Build Script Generation

The build script generator creates shell scripts for the containerized build:

### Release Build
```bash
#!/bin/sh
set -e
export CARGO_HOME=/root/.cargo
cargo build --color never --release
cargo test --color never --release
```

### Debug Build with Target
```bash
#!/bin/sh
set -e
export CARGO_HOME=/root/.cargo
cargo build --color never --target x86_64-unknown-linux-musl
cargo test --color never --target x86_64-unknown-linux-musl
```

## Project Structure Requirements

Your Rust project should have the standard Cargo structure:

```
my-rust-project/
├── Cargo.toml
├── Cargo.lock (optional)
└── src/
└── main.rs (or lib.rs)
```

## Examples

### Simple CLI Application

```go
build := container.Build{
BuildType: container.Rust,
App: "hello-cli",
Image: "myorg/hello-cli",
ImageTag: "v1.0.0",
Folder: ".",
}
build.Custom.Set("profile", "release")
```

### Web Service with Features

```go
build := container.Build{
BuildType: container.Rust,
App: "web-service",
Image: "myorg/web-service",
ImageTag: "latest",
Folder: "./services/web",
}
build.Custom.Set("profile", "release")
build.Custom.Set("features", "server,tls")
```

### Cross-compiled Binary

```go
build := container.Build{
BuildType: container.Rust,
App: "my-tool",
Image: "myorg/my-tool",
ImageTag: "latest",
Folder: ".",
}
build.Custom.Set("profile", "release")
build.Custom.Set("target", "x86_64-unknown-linux-musl")
```

## Integration with Build Pipeline

The Rust build steps are registered in the build pipeline:

- **Build Category**: `rust.New()` - Main build step
- **PostBuild Category**: `rust.NewProd()` - Production image creation

## Troubleshooting

### Cache Issues

If you encounter cache-related issues, clear the Cargo cache:

```bash
rm -rf /tmp/.cargo
# or
rm -rf $CARGO_HOME
```

### Build Failures

Check the build logs for cargo errors. Common issues:
- Missing dependencies in Cargo.toml
- Compilation errors in Rust code
- Test failures

### Binary Not Found

Ensure your `App` name matches the binary name in Cargo.toml:

```toml
[package]
name = "my-app" # This should match the App field
```

## Implementation Details

The Rust support is implemented in `/pkg/rust/` following the established pattern:

- `rust.go`: Main implementation with container orchestration
- `buildscript.go`: Build script generation for cargo commands
- `Dockerfile.rust`: Base image definition with Rust toolchain
- `docker_metadata_gen.go`: Generated metadata from Dockerfile

## Future Enhancements

Potential future improvements:
- Auto-discovery of Rust projects via Cargo.toml detection
- Support for workspaces
- Custom cargo commands and flags
- Build time optimization hints
- Multi-stage build improvements
Loading
Loading