Skip to content

Commit

Permalink
Publish container images
Browse files Browse the repository at this point in the history
Introduce signed, fully reproducible container images that are created
on pushes to main and published via GitHub packages.
  • Loading branch information
aaronmondal committed Dec 5, 2023
1 parent b8f3ef1 commit 131775e
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 3 deletions.
44 changes: 44 additions & 0 deletions .github/workflows/image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
name: Create OCI image
on:
pull_request:
branches:
- main
push:
branches:
- main

permissions: read-all

jobs:
publish-image:
runs-on: ubuntu-22.04
permissions:
packages: write
id-token: write
steps:
- name: Checkout
uses: >- # v3.5.3
actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
- name: Install Nix
uses: >- # v7
DeterminateSystems/nix-installer-action@5620eb4af6b562c53e4d4628c0b6e4f9d9ae8612
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Cache Nix derivations
uses: >- # Custom commit, last pinned at 2023-11-17.
DeterminateSystems/magic-nix-cache-action@a04e6275a6bea232cd04fc6f3cbf20d4cb02a3e1
- name: Test image
run: |
nix run .#local-image-test
- name: Upload image
run: |
nix run .#publish-ghcr
env:
GHCR_REGISTRY: ghcr.io
GHCR_USERNAME: ${{ github.actor }}
GHCR_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
GHCR_IMAGE_NAME: ${{ github.repository }}
if: github.ref == 'refs/heads/main'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ __pycache__
.direnv/
.DS_Store
.pre-commit-config.yaml
result
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ remote executor for systems that communicate using the [Remote execution
protocol](https://github.com/bazelbuild/remote-apis/blob/main/build/bazel/remote/execution/v2/remote_execution.proto) such as [Bazel](https://bazel.build), [Buck2](https://buck2.build), [Goma](https://chromium.googlesource.com/infra/goma/client/) and
[Reclient](https://github.com/bazelbuild/reclient).

Supports Unix based operating systems and Windows.
Supports Unix-based operating systems and Windows.

## ❄️ Installing with Nix

Expand All @@ -30,6 +30,44 @@ For use in production pin the executable to a specific revision:
nix run github:TraceMachina/native-link/<revision> ./basic_cas.json
```

## 📦 Using the OCI image

See the published [OCI images](https://github.com/TraceMachina/native-link/pkgs/container/native-link)
for pull commands.

Images are tagged by nix derivation hash. The most recently pushed image
corresponds to the `main` branch. Images are signed by the GitHub action that
produced the image. Note that the [OCI workflow](https://github.com/TraceMachina/native-link/actions/workflows/image.yaml)
might take a few minutes to publish the latest image.

```sh
# Get the tag for the latest commit
export LATEST=$(nix eval github:TraceMachina/native-link#image.imageTag --raw)

# Verify the signature
cosign verify ghcr.io/TraceMachina/native-link:${LATEST} \
--certificate-identity=https://github.com/TraceMachina/native-link/.github/workflows/image.yaml@refs/heads/main \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
```

For use in production pin the image to a specific revision:

```sh
export PINNED_TAG=$(nix eval github:TraceMachina/native-link/<revision>#image.imageTag --raw)
# Verify the signature
cosign verify ghcr.io/TraceMachina/native-link:${PINNED_TAG} \
--certificate-identity=https://github.com/TraceMachina/native-link/.github/workflows/image.yaml@refs/heads/main \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
```
> [TIP!]
> The images are reproducible on `X86_64-unknown-linux-gnu`. If you're on such a
> system you can produce a binary-identical image by building the `.#image`
> flake output locally. Make sure that your `git status` is completely clean and
> aligned with the commit you want to reproduce. Otherwise the image will be
> tainted with a `"dirty"` revision label.
## 🌱 Building with Bazel
**Build requirements:**
Expand Down
37 changes: 35 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
};
};

outputs = inputs @ { flake-parts, crane, ... }:
outputs = inputs @ { self, flake-parts, crane, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [ "x86_64-linux" ];
imports = [ inputs.pre-commit-hooks.flakeModule ];
perSystem = { config, self', inputs', pkgs, system, ... }:
perSystem = { config, pkgs, system, ... }:
let
customStdenv = import ./tools/llvmStdenv.nix { inherit pkgs; };

Expand Down Expand Up @@ -47,6 +47,10 @@
});

hooks = import ./tools/pre-commit-hooks.nix { inherit pkgs; };

publish-ghcr = import ./tools/publish-ghcr.nix { inherit pkgs; };

local-image-test = import ./tools/local-image-test.nix { inherit pkgs; };
in
{
apps = {
Expand All @@ -55,6 +59,29 @@
program = "${native-link}/bin/cas";
};
};
packages = {
inherit publish-ghcr local-image-test;
default = native-link;
image = pkgs.dockerTools.streamLayeredImage {
name = "native-link";
contents = [
native-link
pkgs.dockerTools.caCertificates
];
config = {
Entrypoint = [ "/bin/cas" ];
Labels = {
"org.opencontainers.image.description" = "An RBE compatible, high-performance cache and remote executor.";
"org.opencontainers.image.documentation" = "https://github.com/TraceMachina/native-link";
"org.opencontainers.image.licenses" = "Apache-2.0";
"org.opencontainers.image.revision" = "${self.rev or self.dirtyRev or "dirty"}";
"org.opencontainers.image.source" = "https://github.com/TraceMachina/native-link";
"org.opencontainers.image.title" = "Native Link";
"org.opencontainers.image.vendor" = "Trace Machina, Inc.";
};
};
};
};
checks = {
# TODO(aaronmondal): Fix the tests.
# tests = craneLib.cargoNextest (commonArgs
Expand All @@ -74,6 +101,12 @@
pkgs.pre-commit
pkgs.bazel
pkgs.awscli2
pkgs.skopeo
pkgs.dive
pkgs.cosign

# Additional tools from within our development environment.
local-image-test
];
shellHook = ''
# Generate the .pre-commit-config.yaml symlink when entering the
Expand Down
24 changes: 24 additions & 0 deletions tools/local-image-test.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{ pkgs, ... }:

pkgs.writeShellScriptBin "local-image-test" ''
set -xeuo pipefail
# Commit hashes would not be a good choice here as they are not
# fully dependent on the inputs to the image. For instance, amending
# nothing would still lead to a new hash. Instead we use the
# derivation hash as the tag so that the tag is reused if the image
# didn't change.
IMAGE_TAG=$(nix eval .#image.imageTag --raw)
$(nix build .#image --print-build-logs --verbose) \
&& ./result \
| ${pkgs.skopeo}/bin/skopeo \
copy \
docker-archive:/dev/stdin \
docker-daemon:native-link:''${IMAGE_TAG}
# Ensure that the image has minimal closure size.
CI=1 ${pkgs.dive}/bin/dive \
native-link:''${IMAGE_TAG} \
--highestWastedBytes=0
''
44 changes: 44 additions & 0 deletions tools/publish-ghcr.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{ pkgs, ... }:

pkgs.writeShellScriptBin "publish-ghcr" ''
set -xeuo pipefail
echo $GHCR_PASSWORD | ${pkgs.skopeo}/bin/skopeo \
login \
--username=$GHCR_USERNAME \
--password-stdin \
ghcr.io
# Commit hashes would not be a good choice here as they are not
# fully dependent on the inputs to the image. For instance, amending
# nothing would still lead to a new hash. Instead we use the
# derivation hash as the tag so that the tag is reused if the image
# didn't change.
IMAGE_TAG=$(nix eval .#image.imageTag --raw)
TAGGED_IMAGE=''${GHCR_REGISTRY}/''${GHCR_IMAGE_NAME}:''${IMAGE_TAG}
$(nix build .#image --print-build-logs --verbose) \
&& ./result \
| ${pkgs.zstd}/bin/zstd \
| ${pkgs.skopeo}/bin/skopeo \
copy \
docker-archive:/dev/stdin \
docker://''${TAGGED_IMAGE}
echo $GHCR_PASSWORD | ${pkgs.cosign}/bin/cosign \
login \
--username=$GHCR_USERNAME \
--password-stdin \
ghcr.io
${pkgs.cosign}/bin/cosign \
sign \
--yes \
''${GHCR_REGISTRY}/''${GHCR_IMAGE_NAME}@$( \
${pkgs.skopeo}/bin/skopeo \
inspect \
--format "{{ .Digest }}" \
docker://''${TAGGED_IMAGE} \
)
''

0 comments on commit 131775e

Please sign in to comment.