Skip to content

Commit

Permalink
Publish container images (#443)
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 697cddf
Show file tree
Hide file tree
Showing 6 changed files with 188 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
41 changes: 40 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,45 @@ 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
# Get the tag for a specific commit
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 697cddf

Please sign in to comment.