Skip to content

Commit

Permalink
publish multi-arch docker image (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
hallettj authored Mar 27, 2024
1 parent f29fa07 commit aa82502
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 114 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,4 @@ jobs:
draft: true
tag: v${{ steps.get-version.outputs.tagged_version }}
body: ${{ steps.changelog-reader.outputs.changes }}
artifacts: release/artifacts/*
artifacts: release/artifacts/*
91 changes: 0 additions & 91 deletions deploy.sh

This file was deleted.

39 changes: 22 additions & 17 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -152,23 +152,34 @@
};
});

packages = eachSystem (pkgs: {
packages = eachSystem (pkgs: rec {
default = pkgs.mongodb-connector;

# Note: these outputs are overridden to build statically-linked
mongodb-connector-x86_64-linux = pkgs.pkgsCross.x86_64-linux.mongodb-connector.override { staticallyLinked = true; };
mongodb-connector-aarch64-linux = pkgs.pkgsCross.aarch64-linux.mongodb-connector.override { staticallyLinked = true; };

docker = pkgs.callPackage ./nix/docker.nix { inherit (pkgs) mongodb-connector; };

docker-x86_64-linux = pkgs.callPackage ./nix/docker.nix {
mongodb-connector = pkgs.pkgsCross.x86_64-linux.mongodb-connector; # Note: dynamically-linked
architecture = "amd64";
};

docker-aarch64-linux = pkgs.callPackage ./nix/docker.nix {
mongodb-connector = pkgs.pkgsCross.aarch64-linux.mongodb-connector; # Note: dynamically-linked
architecture = "arm64";
# Builds a docker image for the MongoDB connector for amd64 Linux. To
# get a multi-arch image run `publish-docker-image`.
docker-image-x86_64-linux = pkgs.pkgsCross.x86_64-linux.callPackage ./nix/docker-connector.nix { };

# Builds a docker image for the MongoDB connector for arm64 Linux. To
# get a multi-arch image run `publish-docker-image`.
docker-image-aarch64-linux = pkgs.pkgsCross.aarch64-linux.callPackage ./nix/docker-connector.nix { };

# Publish multi-arch docker image for the MongoDB connector to Github
# registry. This must be run with a get-ref argument to calculate image
# tags:
#
# $ nix run .#publish-docker-image <git-ref>
#
# You must be logged in to the docker registry. See the CI configuration
# in `.github/workflows/deploy.yml` where this command is run.
publish-docker-image = pkgs.callPackage ./scripts/publish-docker-image.nix {
docker-images = [
docker-image-aarch64-linux
docker-image-x86_64-linux
];
};

# CLI plugin packages with cross-compilation options
Expand All @@ -180,12 +191,6 @@
mongodb-cli-plugin-docker = pkgs.callPackage ./nix/docker-cli-plugin.nix { };
mongodb-cli-plugin-docker-x86_64-linux = pkgs.pkgsCross.x86_64-linux.callPackage ./nix/docker-cli-plugin.nix { };
mongodb-cli-plugin-docker-aarch64-linux = pkgs.pkgsCross.aarch64-linux.callPackage ./nix/docker-cli-plugin.nix { };

publish-docker-image = pkgs.writeShellApplication {
name = "publish-docker-image";
runtimeInputs = with pkgs; [ coreutils skopeo ];
text = builtins.readFile ./deploy.sh;
};
});

# Export our nixpkgs package set, which has been extended with the
Expand Down
5 changes: 0 additions & 5 deletions nix/docker.nix → nix/docker-connector.nix
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# This is a function that returns a derivation for a docker image.
{ mongodb-connector
, dockerTools
, lib
, architecture ? null
, name ? "ghcr.io/hasura/ndc-mongodb"

# See config options at https://github.com/moby/docker-image-spec/blob/main/spec.md
Expand Down Expand Up @@ -35,9 +33,6 @@ let
"${config-directory}" = { };
};
} // extraConfig;
}
// lib.optionalAttrs (architecture != null) {
inherit architecture;
};
in
dockerTools.buildLayeredImage args
142 changes: 142 additions & 0 deletions scripts/publish-docker-image.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# This is run via a nix flake package:
#
# $ nix run .#publish-docker-image <git-ref>
#
# The script is automatically checked with shellcheck, and run with bash using
# a sensible set of options.
{
# These arguments are passed explicitly
docker-images
, format ? "oci"
, registry ? { host = "ghcr.io"; repo = "hasura/ndc-mongodb"; }
, target-protocol ? "docker://"

# These arguments are automatically populated from nixpkgs via `callPackage`
, buildah
, coreutils
, git
, writeShellApplication
}:
writeShellApplication {
name = "publish-docker-image";
runtimeInputs = [ coreutils git buildah ];
text = ''
# Nix uses the same dollar-braces interpolation syntax as bash so we escape $ as ''$
if [ -z "''${1+x}" ]; then
echo "Expected argument of the form refs/heads/<branch name> or refs/tags/<tag name>."
echo "(In a Github workflow the variable github.ref has this format)"
exit 1
fi
github_ref="$1"
# Assumes that the given ref is a branch name. Sets a tag for a docker image of
# the form:
#
# dev-main-20230601T1933-bffd555
# --- ---- ------------- -------
# ↑ ↑ ↑ ↑
# prefix "dev" branch | commit hash
# |
# commit date & time (UTC)
#
# Additionally sets a branch tag assuming this is the latest tag for the given
# branch. The branch tag has the form: dev-main
function set_dev_tags {
local branch="$1"
local branch_prefix="dev-$branch"
local version
version=$(
TZ=UTC0 git show \
--quiet \
--date='format-local:%Y%m%dT%H%M' \
--format="$branch_prefix-%cd-%h"
)
export docker_tags=("$version" "$branch_prefix")
}
# The Github workflow passes a ref of the form refs/heads/<branch name> or
# refs/tags/<tag name>. This function sets an array of docker image tags based
# on either the given branch or tag name.
#
# If a tag name does not start with a "v" it is assumed to not be a release tag
# so the function sets an empty array.
#
# If the input does look like a release tag, set the tag name as the sole docker
# tag.
#
# If the input is a branch, set docker tags via `set_dev_tags`.
function set_docker_tags {
local input="$1"
if [[ $input =~ ^refs/tags/(v.*)$ ]]; then
local tag="''${BASH_REMATCH[1]}"
export docker_tags=("$tag")
elif [[ $input =~ ^refs/heads/(.*)$ ]]; then
local branch="''${BASH_REMATCH[1]}"
set_dev_tags "$branch"
else
export docker_tags=()
fi
}
# We are given separate docker images for each target architecture. Create
# a list manifest that combines the manifests of each individual image to
# produce a multi-arch image.
#
# The buildah steps are adapted from https://github.com/mirkolenz/flocken
function publish {
local manifestName="ndc-mongodb/list"
local datetimeNow
datetimeNow="$(TZ=UTC0 date --iso-8601=seconds)"
if buildah manifest exists "$manifestName"; then
buildah manifest rm "$manifestName";
fi
local manifest
manifest=$(buildah manifest create "$manifestName")
for image in ${builtins.toString docker-images}; do
local manifestOutput
manifestOutput=$(buildah manifest add "$manifest" "docker-archive:$image")
local digest
digest=$(echo "$manifestOutput" | cut "-d " -f2)
buildah manifest annotate \
--annotation org.opencontainers.image.created="$datetimeNow" \
--annotation org.opencontainers.image.revision="$(git rev-parse HEAD)" \
"$manifest" "$digest"
done
echo
echo "Multi-arch manifests:"
buildah manifest inspect "$manifest"
for tag in "''${docker_tags[@]}";
do
local image_dest="${target-protocol}${registry.host}/${registry.repo}:$tag"
echo
echo "Pushing $image_dest"
buildah manifest push --all \
--format ${format} \
"$manifest" \
"$image_dest"
done
}
function maybe_publish {
local input="$1"
set_docker_tags "$input"
if [[ ''${#docker_tags[@]} == 0 ]]; then
echo "The given ref, $input, was not a release tag or a branch - will not publish a docker image"
exit
fi
echo "Will publish docker image with tags: ''${docker_tags[*]}"
publish
}
maybe_publish "$github_ref"
'';
}

0 comments on commit aa82502

Please sign in to comment.