196 lines
4.4 KiB
Markdown
196 lines
4.4 KiB
Markdown
# Reproducible Builds
|
|
|
|
Stella Ops releases are **reproducible**: given the same source code and build environment, anyone can produce byte-identical artifacts.
|
|
|
|
## Overview
|
|
|
|
Reproducible builds provide:
|
|
|
|
1. **Verifiability** - Anyone can verify that released binaries match source code
|
|
2. **Trust** - No need to trust the build infrastructure
|
|
3. **Auditability** - Build process can be independently audited
|
|
4. **Security** - Compromised builds can be detected
|
|
|
|
## How It Works
|
|
|
|
### SOURCE_DATE_EPOCH
|
|
|
|
All timestamps in build outputs use the `SOURCE_DATE_EPOCH` environment variable instead of the current time. This is set to the git commit timestamp:
|
|
|
|
```bash
|
|
export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)
|
|
```
|
|
|
|
### Deterministic Build Settings
|
|
|
|
The following MSBuild properties ensure deterministic .NET builds:
|
|
|
|
```xml
|
|
<!-- src/Directory.Build.props -->
|
|
<PropertyGroup>
|
|
<Deterministic>true</Deterministic>
|
|
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
|
<PathMap>$(MSBuildProjectDirectory)=/src/</PathMap>
|
|
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
|
</PropertyGroup>
|
|
```
|
|
|
|
### Pinned Dependencies
|
|
|
|
All dependencies are pinned to exact versions in `Directory.Packages.props`:
|
|
|
|
```xml
|
|
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
|
```
|
|
|
|
### Containerized Builds
|
|
|
|
Release builds run in containerized environments with:
|
|
|
|
- Fixed base images
|
|
- Pinned tool versions
|
|
- Isolated network (no external fetches during build)
|
|
|
|
## Reproducing a Build
|
|
|
|
### Prerequisites
|
|
|
|
- .NET SDK (version in `global.json`)
|
|
- Git
|
|
- Docker (optional, for containerized builds)
|
|
|
|
### Steps
|
|
|
|
1. **Clone the repository**
|
|
|
|
```bash
|
|
git clone https://git.stella-ops.org/stella-ops.org/git.stella-ops.org.git
|
|
cd git.stella-ops.org
|
|
```
|
|
|
|
2. **Checkout the release tag**
|
|
|
|
```bash
|
|
git checkout v1.2.3
|
|
```
|
|
|
|
3. **Set SOURCE_DATE_EPOCH**
|
|
|
|
Get the value from the release evidence pack `manifest.json`:
|
|
|
|
```bash
|
|
export SOURCE_DATE_EPOCH=1705315800
|
|
```
|
|
|
|
Or compute from git:
|
|
|
|
```bash
|
|
export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)
|
|
```
|
|
|
|
4. **Build**
|
|
|
|
```bash
|
|
# Using make
|
|
make release
|
|
|
|
# Or using dotnet directly
|
|
dotnet publish src/Cli/StellaOps.Cli/StellaOps.Cli.csproj \
|
|
--configuration Release \
|
|
--runtime linux-x64 \
|
|
--self-contained true \
|
|
/p:Deterministic=true \
|
|
/p:ContinuousIntegrationBuild=true \
|
|
/p:SourceRevisionId=$(git rev-parse HEAD)
|
|
```
|
|
|
|
5. **Compare checksums**
|
|
|
|
```bash
|
|
sha256sum dist/stella-* | diff - path/to/evidence-pack/checksums/SHA256SUMS
|
|
```
|
|
|
|
## CI Verification
|
|
|
|
The CI pipeline automatically verifies reproducibility:
|
|
|
|
1. Builds artifacts twice with the same `SOURCE_DATE_EPOCH`
|
|
2. Compares checksums between builds
|
|
3. Fails if checksums don't match
|
|
|
|
See `.gitea/workflows/verify-reproducibility.yml`.
|
|
|
|
## What Can Cause Non-Reproducibility
|
|
|
|
### Timestamps
|
|
|
|
- **Problem**: Build tools embed current time
|
|
- **Solution**: Use `SOURCE_DATE_EPOCH`
|
|
|
|
### Path Information
|
|
|
|
- **Problem**: Absolute paths embedded in binaries/PDBs
|
|
- **Solution**: Use `PathMap` to normalize paths
|
|
|
|
### Random Values
|
|
|
|
- **Problem**: GUIDs, random seeds
|
|
- **Solution**: Use deterministic generation or inject via DI
|
|
|
|
### Unordered Collections
|
|
|
|
- **Problem**: Dictionary/HashSet iteration order varies
|
|
- **Solution**: Use `ImmutableSortedDictionary` or explicit sorting
|
|
|
|
### External Resources
|
|
|
|
- **Problem**: Network fetches return different content
|
|
- **Solution**: Pin dependencies, use hermetic builds
|
|
|
|
### Compiler/Tool Versions
|
|
|
|
- **Problem**: Different tool versions produce different output
|
|
- **Solution**: Pin all tool versions in `global.json` and CI
|
|
|
|
## Debugging Non-Reproducible Builds
|
|
|
|
### Compare binaries
|
|
|
|
```bash
|
|
# Install diffoscope
|
|
pip install diffoscope
|
|
|
|
# Compare two builds
|
|
diffoscope build1/stella.dll build2/stella.dll
|
|
```
|
|
|
|
### Check for timestamps
|
|
|
|
```bash
|
|
# Look for embedded timestamps
|
|
strings stella.dll | grep -E '20[0-9]{2}-[0-9]{2}'
|
|
```
|
|
|
|
### Check PDB content
|
|
|
|
```bash
|
|
# Examine PDB for path information
|
|
dotnet tool install -g dotnet-symbol
|
|
dotnet symbol --symbols stella.dll
|
|
```
|
|
|
|
## Verification in Evidence Pack
|
|
|
|
The Release Evidence Pack includes:
|
|
|
|
1. **SOURCE_DATE_EPOCH** in `manifest.json`
|
|
2. **Source commit** for exact source checkout
|
|
3. **Checksums** for comparison
|
|
4. **Build instructions** in `VERIFY.md`
|
|
|
|
## Related Documentation
|
|
|
|
- [Release Evidence Pack](./RELEASE_EVIDENCE_PACK.md)
|
|
- [SLSA Compliance](./SLSA_COMPLIANCE.md)
|
|
- [Release Engineering Playbook](./RELEASE_ENGINEERING_PLAYBOOK.md)
|