Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
name: Haskell CI

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

on:
pull_request:
merge_group:
push:
branches:
- main
- "release/*"

jobs:
build:
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
matrix:
ghc: ["9.6", "9.12"]
os: [ubuntu-latest]

env:
# Modify this value to "invalidate" all cabal caches.
CABAL_CACHE_VERSION: "2024-05-22"
# Modify this value to "invalidate" the cabal store cache only.
CACHE_VERSION: "20220919"
# Modify this value to "invalidate" the dist-newstyle cache only.
DIST_CACHE_VERSION: "20221122"

steps:

- name: "Install System Dependencies via pacman (msys2)"
if: runner.os == 'Windows'
shell: 'C:/msys64/usr/bin/bash.exe -e {0}'
run: |
/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

- name: Install Haskell
uses: input-output-hk/actions/haskell@latest
id: setup-haskell
with:
ghc-version: ${{ matrix.ghc }}
cabal-version: "3.12.1.0"

- name: Install system dependencies
uses: input-output-hk/actions/base@latest
with:
use-sodium-vrf: true # default is true

- uses: actions/checkout@v6

- name: "Configure cabal.project.local"
run: |
cp scripts/ci/cabal.project.local.${{ runner.os }} cabal.project.local

- name: Update PATH
if: runner.os == 'Windows'
run: |
$env:PATH=("C:\msys64\mingw64\bin;{0}" -f $env:PATH)
echo "PATH=$env:PATH" >> $env:GITHUB_ENV

- name: Update Hackage and CHaP
run: cabal update

- name: Record dependencies
id: record-deps
run: |
cabal build all --dry-run
cat dist-newstyle/cache/plan.json | jq -L .github/workflows/jq-install-plan | sort | uniq > dependencies.txt

- uses: actions/cache/restore@v4
name: "Restore cache: `cabal store`"
id: cache-dependencies
with:
path: ${{ steps.setup-haskell.outputs.cabal-store }}
key: cache-dependencies-${{ env.CABAL_CACHE_VERSION }}-${{ env.CACHE_VERSION }}-${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('dependencies.txt') }}
restore-keys: cache-dependencies-${{ env.CABAL_CACHE_VERSION }}-${{ env.CACHE_VERSION }}-${{ runner.os }}-${{ matrix.ghc }}

- uses: actions/cache@v4
name: "Cache `dist-newstyle`"
with:
path: |
dist-newstyle
!dist-newstyle/**/.git
key: cache-dist-${{ env.CABAL_CACHE_VERSION }}-${{ env.DIST_CACHE_VERSION }}-${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project') }}
restore-keys: cache-dist-${{ env.CABAL_CACHE_VERSION }}-${{ env.DIST_CACHE_VERSION }}-${{ runner.os }}-${{ matrix.ghc }}

- name: Build dependencies
run: cabal build --only-dependencies all -j

- uses: actions/cache/save@v4
name: "Save cache: `cabal store`"
if: always() && steps.cache-dependencies.outputs.cache-hit != 'true'
with:
path: ${{ steps.setup-haskell.outputs.cabal-store }}
key: ${{ steps.cache-dependencies.outputs.cache-primary-key }}

- name: Build all packages
run: cabal build all -j

# Uncomment the following back in for debugging. Remember to launch a `pwsh` from
# the tmux session to debug `pwsh` issues. And be reminded that the `/msys2` and
# `/msys2/mingw64` paths are not in PATH by default for the workflow, but tmate
# will put them in.
# You may also want to run
#
# $env:PATH=("C:\Program Files\PowerShell\7;{0}" -f $env:ORIGINAL_PATH)
#
# to restore the original path. Do note that some test might need msys2
# and will silently fail if msys2 is not in path. See the "Run tests" step.
#
# - name: Setup tmate session
# if: ${{ failure() }}
# uses: mxschmitt/action-tmate@v3
# with:
# limit-access-to-actor: true
28 changes: 28 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Project checks

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

on:
pull_request:
merge_group:

jobs:
check-changelogs:
name: Check changelogs
runs-on: ubuntu-latest
defaults:
run:
shell: bash

steps:
- name: Install dependencies
run: sudo apt install -y fd-find

- uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Check changelogs
run: ./scripts/ci/check-changelogs.sh
1 change: 1 addition & 0 deletions .github/workflows/jq-install-plan
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.["install-plan"][].id
207 changes: 207 additions & 0 deletions .github/workflows/release-ghcr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
name: Upload to ghcr.io

on:
push:
tags:
- '**'
# GITHUB_SHA: Last commit in the tagged release
# GITHUB_REF: Tag ref of release refs/tags/<tag_name>
release:
types:
- published
# GITHUB_SHA: Last commit on the GITHUB_REF branch or tag
# GITHUB_REF: Branch or tag that received dispatch
workflow_dispatch: {}

permissions:
contents: read
packages: write

env:
# Only to avoid some repetition
FLAKE_REF: github:${{ github.repository }}/${{ github.ref_name }}
GH_TOKEN: ${{ github.token }}

jobs:
wait-for-hydra:
name: "Wait for hydra check-runs"
runs-on: ubuntu-latest

steps:
- name: Waiting for ci/hydra-build:x86_64-linux.required to complete
run: |
while [[ true ]]; do
check_name='ci/hydra-build:x86_64-linux.required'
conclusion=$(gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/check-runs?check_name=$check_name" --paginate --jq '.check_runs[].conclusion')
case "$conclusion" in
success)
echo "$check_name succeeded"
exit 0;;
'')
echo "$check_name pending. Waiting 30s..."
sleep 30;;
*)
echo "$check_name terminated unsuccessfully"
exit 1;;
esac
done


prepare:
needs: [wait-for-hydra]
name: "Prepare metadata"
runs-on: ubuntu-latest
outputs:
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
LOCKED_URL: ${{ steps.flake-metadata.outputs.LOCKED_URL }}

steps:
- name: Install Nix
uses: cachix/install-nix-action@v31

- name: Display flake metadata
id: flake-metadata
run: |
nix flake metadata ${{ env.FLAKE_REF }}
nix flake metadata ${{ env.FLAKE_REF }} --json | jq -r '"LOCKED_URL=\(.url)"' >> "$GITHUB_OUTPUT"

- name: Obtaining latest release tag
id: latest-tag
run: |
LATEST_TAG=0.2.0.0
# LATEST_TAG=$(gh api repos/$GITHUB_REPOSITORY/releases/latest --paginate --jq '.tag_name')
# echo "LATEST_TAG=$LATEST_TAG" >> "$GITHUB_OUTPUT"
# echo "Latest release tag is: $LATEST_TAG"


build:
needs: [prepare]
name: "Upload to ghcr.io"
runs-on: ubuntu-latest
strategy:
matrix:
arch:
- name: amd64
system: x86_64-linux
image:
- name: dmq-node
nix_key: docker-dmq

steps:
- name: Install Nix
uses: cachix/install-nix-action@v31

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# NOTE We assume that hydra has already built the image, this is
# reasonable since, before applying the tag, we must have already
# pushed the tagged commit somewhere, and Hydra will have had the
# change to build the image.

- name: Uploading ${{ matrix.image.name }} (${{ matrix.arch.name }})
run: |
echo "::group::Downloading from cache"
nix build \
--accept-flake-config \
--print-out-paths \
--builders "" \
--max-jobs 0 \
--out-link ./result-${{ matrix.image.name }}-${{ matrix.arch.name }} \
${{ needs.prepare.outputs.LOCKED_URL }}#packages.${{ matrix.arch.system }}.${{ matrix.image.nix_key }}
echo "::endgroup::"

echo "::group::Uploading to registry"
skopeo copy \
docker-archive:./result-${{ matrix.image.name }}-${{ matrix.arch.name }} \
docker://ghcr.io/intersectmbo/${{ matrix.image.name }}:$GITHUB_REF_NAME-${{ matrix.arch.name }}
echo "::endgroup::"


create-manifest:
needs: [prepare, build]
name: "Create Multi-Arch Manifest"
runs-on: ubuntu-latest

steps:
- name: Install Nix
uses: cachix/install-nix-action@v31

# Regctl simplifies obtaining multi-arch digests
- name: Install Nix Profile Commands
run: nix profile install nixpkgs#regctl

# The docker buildx action has a tight coupling with GH runners
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Show buildx configuration
run: docker buildx ls

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create Manifests
run: |
REPOS=(dmq-node)
ARCHES=(amd64)

for REPO in "${REPOS[@]}"; do
IMAGE_REPO="ghcr.io/intersectmbo/$REPO"
DIGESTS=()

echo "::group::Fetching digests for $REPO"
for ARCH in "${ARCHES[@]}"; do
DIGEST=$(skopeo inspect --no-tags "docker://$IMAGE_REPO:$GITHUB_REF_NAME-$ARCH" | jq -r .Digest)
echo "$REPO $ARCH digest: $DIGEST"
DIGESTS+=("$IMAGE_REPO@$DIGEST")
done
echo "::endgroup::"

echo "::group::Creating manifest for $REPO:$GITHUB_REF_NAME"
docker buildx imagetools create --tag "$IMAGE_REPO:$GITHUB_REF_NAME" "${DIGESTS[@]}"
echo "::endgroup::"
done

- name: Verify multi-arch manifests
run: |
for REPO in dmq-node; do
IMAGE_REPO="ghcr.io/intersectmbo/$REPO"
echo "::group::Inspecting $REPO:$GITHUB_REF_NAME"

DIGEST=$(regctl manifest head "$IMAGE_REPO:$GITHUB_REF_NAME")
echo "$REPO multi-arch manifest digest: $DIGEST"
skopeo inspect --raw "docker://$IMAGE_REPO:$GITHUB_REF_NAME" | jq

echo "::endgroup::"
done

- name: Tag Containers as :latest
# Github releases are checked for latest tag in the first `or` operand of
# the if statement. However, promoted pre-releases or changed full
# releases do not count as a `published` event and so won't trigger
# this workflow. For those use cases a manual workflow must be run
# from the matching release tag which the second `or` operand checks
# for.
if: |
(github.event_name == 'release' && github.event.release.tag_name == needs.prepare.outputs.LATEST_TAG) ||
(github.event_name == 'workflow_dispatch' && github.ref == format('refs/tags/{0}', needs.prepare.outputs.LATEST_TAG))
run: |
REPOS=(dmq-node)

for REPO in "${REPOS[@]}"; do
IMAGE_REPO="ghcr.io/intersectmbo/$REPO"
DIGEST=$(regctl manifest head "$IMAGE_REPO:$GITHUB_REF_NAME")

echo "::group::Creating manifest for $IMAGE_REPO:latest"
docker buildx imagetools create --tag "$IMAGE_REPO:latest" "$IMAGE_REPO@$DIGEST"
echo "::endgroup::"
done
20 changes: 19 additions & 1 deletion nix/outputs.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ let

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

buildSystem = pkgs.buildPlatform.system;

packages = rec {
# TODO: `nix build .\#dmq-node` will have the git revision set in the binary,
# `nib build .\#hydraJobs.x86_64-linux.packages.dmq-node:exe:dmq-node` won't
Expand All @@ -19,11 +21,27 @@ let
# pkgs.setGitRev
# (inputs.self.rev or inputs.self.dirtyShortRev)
project.hsPkgs.dmq-node.components.exes.dmq-node;
default = dmq-node;
} // lib.optionalAttrs (buildSystem == "x86_64-linux") {
dmq-node-static =
# pkgs.setGitRev
# (inputs.self.rev or inputs.self.dirtyShortRev)
project.projectCross.musl64.hsPkgs.dmq-node.components.exes.dmq-node;
default = dmq-node;
docker-dmq = pkgs.dockerTools.buildImage {
name = "docker-dmq-node";
tag = "latest";
created = "now";
copyToRoot = pkgs.buildEnv {
name = "dmq-env";
paths = [
pkgs.busybox
pkgs.dockerTools.caCertificates
];
};
config = {
Entrypoint = [ "${packages.dmq-node-static}/bin/dmq-node-static" ];
};
};
};

app = {
Expand Down
Loading
Loading