Skip to content

Commit e12cb0c

Browse files
Merge pull request #2 from IntersectMBO/mw/docker-images
Fix CI
2 parents a89c583 + 91abf6b commit e12cb0c

File tree

9 files changed

+417
-22
lines changed

9 files changed

+417
-22
lines changed

.github/workflows/build.yml

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
name: Haskell CI
2+
3+
concurrency:
4+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
5+
cancel-in-progress: true
6+
7+
on:
8+
pull_request:
9+
merge_group:
10+
push:
11+
branches:
12+
- main
13+
- "release/*"
14+
15+
jobs:
16+
build:
17+
runs-on: ${{ matrix.os }}
18+
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
ghc: ["9.6", "9.12"]
23+
os: [ubuntu-latest]
24+
25+
env:
26+
# Modify this value to "invalidate" all cabal caches.
27+
CABAL_CACHE_VERSION: "2024-05-22"
28+
# Modify this value to "invalidate" the cabal store cache only.
29+
CACHE_VERSION: "20220919"
30+
# Modify this value to "invalidate" the dist-newstyle cache only.
31+
DIST_CACHE_VERSION: "20221122"
32+
33+
steps:
34+
35+
- name: "Install System Dependencies via pacman (msys2)"
36+
if: runner.os == 'Windows'
37+
shell: 'C:/msys64/usr/bin/bash.exe -e {0}'
38+
run: |
39+
/usr/bin/pacman -S --noconfirm mingw-w64-x86_64-pkg-config mingw-w64-x86_64-openssl mingw-w64-x86_64-sed base-devel autoconf-wrapper autoconf automake libtool make
40+
41+
- name: Install Haskell
42+
uses: input-output-hk/actions/haskell@latest
43+
id: setup-haskell
44+
with:
45+
ghc-version: ${{ matrix.ghc }}
46+
cabal-version: "3.12.1.0"
47+
48+
- name: Install system dependencies
49+
uses: input-output-hk/actions/base@latest
50+
with:
51+
use-sodium-vrf: true # default is true
52+
53+
- uses: actions/checkout@v6
54+
55+
- name: "Configure cabal.project.local"
56+
run: |
57+
cp scripts/ci/cabal.project.local.${{ runner.os }} cabal.project.local
58+
59+
- name: Update PATH
60+
if: runner.os == 'Windows'
61+
run: |
62+
$env:PATH=("C:\msys64\mingw64\bin;{0}" -f $env:PATH)
63+
echo "PATH=$env:PATH" >> $env:GITHUB_ENV
64+
65+
- name: Update Hackage and CHaP
66+
run: cabal update
67+
68+
- name: Record dependencies
69+
id: record-deps
70+
run: |
71+
cabal build all --dry-run
72+
cat dist-newstyle/cache/plan.json | jq -L .github/workflows/jq-install-plan | sort | uniq > dependencies.txt
73+
74+
- uses: actions/cache/restore@v4
75+
name: "Restore cache: `cabal store`"
76+
id: cache-dependencies
77+
with:
78+
path: ${{ steps.setup-haskell.outputs.cabal-store }}
79+
key: cache-dependencies-${{ env.CABAL_CACHE_VERSION }}-${{ env.CACHE_VERSION }}-${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('dependencies.txt') }}
80+
restore-keys: cache-dependencies-${{ env.CABAL_CACHE_VERSION }}-${{ env.CACHE_VERSION }}-${{ runner.os }}-${{ matrix.ghc }}
81+
82+
- uses: actions/cache@v4
83+
name: "Cache `dist-newstyle`"
84+
with:
85+
path: |
86+
dist-newstyle
87+
!dist-newstyle/**/.git
88+
key: cache-dist-${{ env.CABAL_CACHE_VERSION }}-${{ env.DIST_CACHE_VERSION }}-${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project') }}
89+
restore-keys: cache-dist-${{ env.CABAL_CACHE_VERSION }}-${{ env.DIST_CACHE_VERSION }}-${{ runner.os }}-${{ matrix.ghc }}
90+
91+
- name: Build dependencies
92+
run: cabal build --only-dependencies all -j
93+
94+
- uses: actions/cache/save@v4
95+
name: "Save cache: `cabal store`"
96+
if: always() && steps.cache-dependencies.outputs.cache-hit != 'true'
97+
with:
98+
path: ${{ steps.setup-haskell.outputs.cabal-store }}
99+
key: ${{ steps.cache-dependencies.outputs.cache-primary-key }}
100+
101+
- name: Build all packages
102+
run: cabal build all -j
103+
104+
# Uncomment the following back in for debugging. Remember to launch a `pwsh` from
105+
# the tmux session to debug `pwsh` issues. And be reminded that the `/msys2` and
106+
# `/msys2/mingw64` paths are not in PATH by default for the workflow, but tmate
107+
# will put them in.
108+
# You may also want to run
109+
#
110+
# $env:PATH=("C:\Program Files\PowerShell\7;{0}" -f $env:ORIGINAL_PATH)
111+
#
112+
# to restore the original path. Do note that some test might need msys2
113+
# and will silently fail if msys2 is not in path. See the "Run tests" step.
114+
#
115+
# - name: Setup tmate session
116+
# if: ${{ failure() }}
117+
# uses: mxschmitt/action-tmate@v3
118+
# with:
119+
# limit-access-to-actor: true

.github/workflows/checks.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Project checks
2+
3+
concurrency:
4+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
5+
cancel-in-progress: true
6+
7+
on:
8+
pull_request:
9+
merge_group:
10+
11+
jobs:
12+
check-changelogs:
13+
name: Check changelogs
14+
runs-on: ubuntu-latest
15+
defaults:
16+
run:
17+
shell: bash
18+
19+
steps:
20+
- name: Install dependencies
21+
run: sudo apt install -y fd-find
22+
23+
- uses: actions/checkout@v6
24+
with:
25+
fetch-depth: 0
26+
27+
- name: Check changelogs
28+
run: ./scripts/ci/check-changelogs.sh

.github/workflows/jq-install-plan

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.["install-plan"][].id
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
name: Upload to ghcr.io
2+
3+
on:
4+
push:
5+
tags:
6+
- '**'
7+
# GITHUB_SHA: Last commit in the tagged release
8+
# GITHUB_REF: Tag ref of release refs/tags/<tag_name>
9+
release:
10+
types:
11+
- published
12+
# GITHUB_SHA: Last commit on the GITHUB_REF branch or tag
13+
# GITHUB_REF: Branch or tag that received dispatch
14+
workflow_dispatch: {}
15+
16+
permissions:
17+
contents: read
18+
packages: write
19+
20+
env:
21+
# Only to avoid some repetition
22+
FLAKE_REF: github:${{ github.repository }}/${{ github.ref_name }}
23+
GH_TOKEN: ${{ github.token }}
24+
25+
jobs:
26+
wait-for-hydra:
27+
name: "Wait for hydra check-runs"
28+
runs-on: ubuntu-latest
29+
30+
steps:
31+
- name: Waiting for ci/hydra-build:x86_64-linux.required to complete
32+
run: |
33+
while [[ true ]]; do
34+
check_name='ci/hydra-build:x86_64-linux.required'
35+
conclusion=$(gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/check-runs?check_name=$check_name" --paginate --jq '.check_runs[].conclusion')
36+
case "$conclusion" in
37+
success)
38+
echo "$check_name succeeded"
39+
exit 0;;
40+
'')
41+
echo "$check_name pending. Waiting 30s..."
42+
sleep 30;;
43+
*)
44+
echo "$check_name terminated unsuccessfully"
45+
exit 1;;
46+
esac
47+
done
48+
49+
50+
prepare:
51+
needs: [wait-for-hydra]
52+
name: "Prepare metadata"
53+
runs-on: ubuntu-latest
54+
outputs:
55+
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
56+
LOCKED_URL: ${{ steps.flake-metadata.outputs.LOCKED_URL }}
57+
58+
steps:
59+
- name: Install Nix
60+
uses: cachix/install-nix-action@v31
61+
62+
- name: Display flake metadata
63+
id: flake-metadata
64+
run: |
65+
nix flake metadata ${{ env.FLAKE_REF }}
66+
nix flake metadata ${{ env.FLAKE_REF }} --json | jq -r '"LOCKED_URL=\(.url)"' >> "$GITHUB_OUTPUT"
67+
68+
- name: Obtaining latest release tag
69+
id: latest-tag
70+
run: |
71+
LATEST_TAG=0.2.0.0
72+
# LATEST_TAG=$(gh api repos/$GITHUB_REPOSITORY/releases/latest --paginate --jq '.tag_name')
73+
# echo "LATEST_TAG=$LATEST_TAG" >> "$GITHUB_OUTPUT"
74+
# echo "Latest release tag is: $LATEST_TAG"
75+
76+
77+
build:
78+
needs: [prepare]
79+
name: "Upload to ghcr.io"
80+
runs-on: ubuntu-latest
81+
strategy:
82+
matrix:
83+
arch:
84+
- name: amd64
85+
system: x86_64-linux
86+
image:
87+
- name: dmq-node
88+
nix_key: docker-dmq
89+
90+
steps:
91+
- name: Install Nix
92+
uses: cachix/install-nix-action@v31
93+
94+
- name: Log in to GitHub Container Registry
95+
uses: docker/login-action@v3
96+
with:
97+
registry: ghcr.io
98+
username: ${{ github.actor }}
99+
password: ${{ secrets.GITHUB_TOKEN }}
100+
101+
# NOTE We assume that hydra has already built the image, this is
102+
# reasonable since, before applying the tag, we must have already
103+
# pushed the tagged commit somewhere, and Hydra will have had the
104+
# change to build the image.
105+
106+
- name: Uploading ${{ matrix.image.name }} (${{ matrix.arch.name }})
107+
run: |
108+
echo "::group::Downloading from cache"
109+
nix build \
110+
--accept-flake-config \
111+
--print-out-paths \
112+
--builders "" \
113+
--max-jobs 0 \
114+
--out-link ./result-${{ matrix.image.name }}-${{ matrix.arch.name }} \
115+
${{ needs.prepare.outputs.LOCKED_URL }}#packages.${{ matrix.arch.system }}.${{ matrix.image.nix_key }}
116+
echo "::endgroup::"
117+
118+
echo "::group::Uploading to registry"
119+
skopeo copy \
120+
docker-archive:./result-${{ matrix.image.name }}-${{ matrix.arch.name }} \
121+
docker://ghcr.io/intersectmbo/${{ matrix.image.name }}:$GITHUB_REF_NAME-${{ matrix.arch.name }}
122+
echo "::endgroup::"
123+
124+
125+
create-manifest:
126+
needs: [prepare, build]
127+
name: "Create Multi-Arch Manifest"
128+
runs-on: ubuntu-latest
129+
130+
steps:
131+
- name: Install Nix
132+
uses: cachix/install-nix-action@v31
133+
134+
# Regctl simplifies obtaining multi-arch digests
135+
- name: Install Nix Profile Commands
136+
run: nix profile install nixpkgs#regctl
137+
138+
# The docker buildx action has a tight coupling with GH runners
139+
- name: Setup Docker Buildx
140+
uses: docker/setup-buildx-action@v3
141+
142+
- name: Show buildx configuration
143+
run: docker buildx ls
144+
145+
- name: Log in to GitHub Container Registry
146+
uses: docker/login-action@v3
147+
with:
148+
registry: ghcr.io
149+
username: ${{ github.actor }}
150+
password: ${{ secrets.GITHUB_TOKEN }}
151+
152+
- name: Create Manifests
153+
run: |
154+
REPOS=(dmq-node)
155+
ARCHES=(amd64)
156+
157+
for REPO in "${REPOS[@]}"; do
158+
IMAGE_REPO="ghcr.io/intersectmbo/$REPO"
159+
DIGESTS=()
160+
161+
echo "::group::Fetching digests for $REPO"
162+
for ARCH in "${ARCHES[@]}"; do
163+
DIGEST=$(skopeo inspect --no-tags "docker://$IMAGE_REPO:$GITHUB_REF_NAME-$ARCH" | jq -r .Digest)
164+
echo "$REPO $ARCH digest: $DIGEST"
165+
DIGESTS+=("$IMAGE_REPO@$DIGEST")
166+
done
167+
echo "::endgroup::"
168+
169+
echo "::group::Creating manifest for $REPO:$GITHUB_REF_NAME"
170+
docker buildx imagetools create --tag "$IMAGE_REPO:$GITHUB_REF_NAME" "${DIGESTS[@]}"
171+
echo "::endgroup::"
172+
done
173+
174+
- name: Verify multi-arch manifests
175+
run: |
176+
for REPO in dmq-node; do
177+
IMAGE_REPO="ghcr.io/intersectmbo/$REPO"
178+
echo "::group::Inspecting $REPO:$GITHUB_REF_NAME"
179+
180+
DIGEST=$(regctl manifest head "$IMAGE_REPO:$GITHUB_REF_NAME")
181+
echo "$REPO multi-arch manifest digest: $DIGEST"
182+
skopeo inspect --raw "docker://$IMAGE_REPO:$GITHUB_REF_NAME" | jq
183+
184+
echo "::endgroup::"
185+
done
186+
187+
- name: Tag Containers as :latest
188+
# Github releases are checked for latest tag in the first `or` operand of
189+
# the if statement. However, promoted pre-releases or changed full
190+
# releases do not count as a `published` event and so won't trigger
191+
# this workflow. For those use cases a manual workflow must be run
192+
# from the matching release tag which the second `or` operand checks
193+
# for.
194+
if: |
195+
(github.event_name == 'release' && github.event.release.tag_name == needs.prepare.outputs.LATEST_TAG) ||
196+
(github.event_name == 'workflow_dispatch' && github.ref == format('refs/tags/{0}', needs.prepare.outputs.LATEST_TAG))
197+
run: |
198+
REPOS=(dmq-node)
199+
200+
for REPO in "${REPOS[@]}"; do
201+
IMAGE_REPO="ghcr.io/intersectmbo/$REPO"
202+
DIGEST=$(regctl manifest head "$IMAGE_REPO:$GITHUB_REF_NAME")
203+
204+
echo "::group::Creating manifest for $IMAGE_REPO:latest"
205+
docker buildx imagetools create --tag "$IMAGE_REPO:latest" "$IMAGE_REPO@$DIGEST"
206+
echo "::endgroup::"
207+
done

nix/outputs.nix

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ let
1111

1212
mkShell = ghc: import ./shell.nix { inherit inputs pkgs lib project utils ghc; };
1313

14+
buildSystem = pkgs.buildPlatform.system;
15+
1416
packages = rec {
1517
# TODO: `nix build .\#dmq-node` will have the git revision set in the binary,
1618
# `nib build .\#hydraJobs.x86_64-linux.packages.dmq-node:exe:dmq-node` won't
@@ -19,11 +21,27 @@ let
1921
# pkgs.setGitRev
2022
# (inputs.self.rev or inputs.self.dirtyShortRev)
2123
project.hsPkgs.dmq-node.components.exes.dmq-node;
24+
default = dmq-node;
25+
} // lib.optionalAttrs (buildSystem == "x86_64-linux") {
2226
dmq-node-static =
2327
# pkgs.setGitRev
2428
# (inputs.self.rev or inputs.self.dirtyShortRev)
2529
project.projectCross.musl64.hsPkgs.dmq-node.components.exes.dmq-node;
26-
default = dmq-node;
30+
docker-dmq = pkgs.dockerTools.buildImage {
31+
name = "docker-dmq-node";
32+
tag = "latest";
33+
created = "now";
34+
copyToRoot = pkgs.buildEnv {
35+
name = "dmq-env";
36+
paths = [
37+
pkgs.busybox
38+
pkgs.dockerTools.caCertificates
39+
];
40+
};
41+
config = {
42+
Entrypoint = [ "${packages.dmq-node-static}/bin/dmq-node-static" ];
43+
};
44+
};
2745
};
2846

2947
app = {

0 commit comments

Comments
 (0)