Skip to content

Commit 2eed8ea

Browse files
committed
[ci] Add riscv{32/64}imac-unknown-none-elf cross-compilation images
1 parent 87fff07 commit 2eed8ea

6 files changed

Lines changed: 376 additions & 0 deletions

File tree

.github/workflows/docker.yml

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
name: Build and Publish Docker Image
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
target:
7+
type: choice
8+
description: Which image to release
9+
required: true
10+
options:
11+
- riscv-unknown-elf-toolchain
12+
- rust-riscv32imac-cross
13+
- rust-riscv64imac-cross
14+
push:
15+
tags:
16+
# matches tags like `service/v1.0.0`
17+
- "*/v*"
18+
19+
env:
20+
REGISTRY: ghcr.io
21+
REGISTRY_IMAGE: ghcr.io/commonwarexyz/monorepo
22+
GIT_REF_NAME: ${{ github.ref_name }}
23+
24+
jobs:
25+
prepare:
26+
name: Prepare Bake
27+
runs-on: ubuntu-latest
28+
permissions:
29+
packages: write
30+
outputs:
31+
matrix: ${{ steps.platforms.outputs.matrix }}
32+
target: ${{ steps.target-spec.outputs.target }}
33+
steps:
34+
- name: Checkout
35+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
36+
- name: Specify Target
37+
id: target-spec
38+
run: |
39+
export TARGET="${{ inputs.target }}"
40+
if [[ -z $TARGET ]]; then
41+
export TARGET="${GIT_REF_NAME%/*}"
42+
fi
43+
echo "Target: $TARGET"
44+
echo "target=$TARGET" >> $GITHUB_OUTPUT
45+
- name: Create matrix
46+
id: platforms
47+
run: |
48+
echo "matrix=$(docker buildx bake -f docker/docker-bake.hcl ${{ steps.target-spec.outputs.target }} --print | jq -cr '.target."${{ steps.target-spec.outputs.target }}".platforms')" >> ${GITHUB_OUTPUT}
49+
- name: Show matrix
50+
run: |
51+
echo ${{ steps.platforms.outputs.matrix }}
52+
- name: Docker meta
53+
id: meta
54+
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
55+
with:
56+
images: ${{ env.REGISTRY_IMAGE }}/${{ steps.target-spec.outputs.target }}
57+
tags: |
58+
type=ref,event=branch
59+
type=match,pattern=v(.*),group=1,event=tag
60+
type=ref,event=pr
61+
- name: Rename meta bake definition file
62+
run: |
63+
mv "${{ steps.meta.outputs.bake-file }}" "${{ runner.temp }}/bake-meta.json"
64+
- name: Upload meta bake definition
65+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
66+
with:
67+
name: bake-meta
68+
path: ${{ runner.temp }}/bake-meta.json
69+
if-no-files-found: error
70+
retention-days: 1
71+
72+
build:
73+
name: Build Image (${{ needs.prepare.outputs.target }} - ${{ matrix.platform }})
74+
runs-on: ${{ matrix.platform == 'linux/amd64' && 'ubuntu-latest' || 'ubuntu-22.04-arm' }}
75+
permissions:
76+
packages: write
77+
needs:
78+
- prepare
79+
strategy:
80+
fail-fast: false
81+
matrix:
82+
platform: ${{ fromJson(needs.prepare.outputs.matrix) }}
83+
steps:
84+
- name: Free Disk Space (Ubuntu)
85+
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
86+
with:
87+
large-packages: false
88+
- name: Prepare
89+
run: |
90+
platform=${{ matrix.platform }}
91+
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
92+
- name: Download meta bake definition
93+
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
94+
with:
95+
name: bake-meta
96+
path: ${{ runner.temp }}
97+
- name: Authenticate with container registry
98+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
99+
with:
100+
registry: ${{ env.REGISTRY }}
101+
username: ${{ github.actor }}
102+
password: ${{ secrets.GITHUB_TOKEN }}
103+
- name: Set up Docker Buildx
104+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
105+
- name: Build
106+
id: bake
107+
uses: docker/bake-action@3acf805d94d93a86cce4ca44798a76464a75b88c # v6.9.0
108+
with:
109+
files: |
110+
./docker/docker-bake.hcl
111+
cwd://${{ runner.temp }}/bake-meta.json
112+
targets: ${{ needs.prepare.outputs.target }}
113+
set: |
114+
*.tags=
115+
*.platform=${{ matrix.platform }}
116+
*.output=type=image,"name=${{ env.REGISTRY_IMAGE }}/${{ needs.prepare.outputs.target }}",push-by-digest=true,name-canonical=true,push=true
117+
- name: Export digest
118+
run: |
119+
mkdir -p ${{ runner.temp }}/digests
120+
digest="${{ fromJSON(steps.bake.outputs.metadata)[needs.prepare.outputs.target]['containerimage.digest'] }}"
121+
touch "${{ runner.temp }}/digests/${digest#sha256:}"
122+
- name: Upload digest
123+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
124+
with:
125+
name: digests-${{ env.PLATFORM_PAIR }}
126+
path: ${{ runner.temp }}/digests/*
127+
if-no-files-found: error
128+
retention-days: 1
129+
130+
merge:
131+
name: Publish Manifest (${{ needs.prepare.outputs.target }})
132+
runs-on: ubuntu-latest
133+
permissions:
134+
packages: write
135+
needs:
136+
- build
137+
- prepare
138+
steps:
139+
- name: Download meta bake definition
140+
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
141+
with:
142+
name: bake-meta
143+
path: ${{ runner.temp }}
144+
- name: Download digests
145+
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
146+
with:
147+
path: ${{ runner.temp }}/digests
148+
pattern: digests-*
149+
merge-multiple: true
150+
- name: Authenticate with container registry
151+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
152+
with:
153+
registry: ${{ env.REGISTRY }}
154+
username: ${{ github.actor }}
155+
password: ${{ secrets.GITHUB_TOKEN }}
156+
- name: Set up Docker Buildx
157+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
158+
- name: Create manifest list and push
159+
working-directory: ${{ runner.temp }}/digests
160+
run: |
161+
docker buildx imagetools create $(jq -cr '.target."docker-metadata-action".tags | map(select(startswith("${{ env.REGISTRY_IMAGE }}/${{ needs.prepare.outputs.target }}")) | "-t " + .) | join(" ")' ${{ runner.temp }}/bake-meta.json) \
162+
$(printf '${{ env.REGISTRY_IMAGE }}/${{ needs.prepare.outputs.target }}@sha256:%s ' *)
163+
- name: Inspect image
164+
run: |
165+
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}/${{ needs.prepare.outputs.target }}:$(jq -r '.target."docker-metadata-action".args.DOCKER_META_VERSION' ${{ runner.temp }}/bake-meta.json)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ _Examples may include insecure code (i.e. deriving keypairs from an integer argu
4343
_Sometimes, we opt to maintain software that is neither a primitive nor an example to make it easier to interact with the Commonware Library. Unless otherwise indicated, code in this section is intended to be used in production. Please refer to our [security policy](./SECURITY.md) before disclosing an exploit publicly._
4444

4545
* [docs](./docs): Access information about Commonware at https://commonware.xyz.
46+
* [docker](./docker): Dockerfiles used for cross-compilation and CI.
4647
* [macros](./macros/README.md): Augment the development of primitives with procedural macros.
4748
* [pipeline](./pipeline): Mechanisms under development.
4849
* [utils](./utils/README.md): Leverage common functionality across multiple primitives.

docker/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# `commonware-docker`
2+
3+
This directory contains all of the repositories' dockerfiles as well as the [bake file](https://docs.docker.com/build/bake/)
4+
used to define this repository's docker build configuration.
5+
6+
## Install Dependencies
7+
8+
* `docker`: https://www.docker.com/get-started/
9+
* `docker-buildx`: https://github.com/docker/buildx?tab=readme-ov-file#installing
10+
11+
## Building Locally
12+
13+
To build any image in the bake file locally, use `docker buildx bake`:
14+
15+
```sh
16+
export TARGET="<target_name>"
17+
18+
# Optional: adjust the tag for the image
19+
# Defaults to `commonware:local`
20+
export DEFAULT_TAG="my-image:local"
21+
22+
# Optional: Override the platforms to build the image for.
23+
# Defaults to `linux/amd64,linux/arm64`
24+
export PLATFORMS="<platforms>"
25+
26+
docker buildx bake \
27+
--progress plain \
28+
-f docker/docker-bake.hcl \
29+
$TARGET
30+
```
31+
32+
#### Troubleshooting
33+
34+
If you receive an error like the following:
35+
36+
```
37+
ERROR: Multi-platform build is not supported for the docker driver.
38+
Switch to a different driver, or turn on the containerd image store, and try again.
39+
Learn more at https://docs.docker.com/go/build-multi-platform/
40+
```
41+
42+
Create and activate a new builder and retry the bake command.
43+
44+
```sh
45+
docker buildx create --name commonware-builder --use
46+
```
47+
48+
## Cutting a Release (for maintainers / forks)
49+
50+
To cut a release of the docker image for any of the targets, cut a new annotated tag for the target like so:
51+
52+
```sh
53+
# Example formats:
54+
# - `rust-riscv-cross/v0.1.0-beta.8`
55+
# - `riscv-unknown-elf-toolchain/v1.2.0`
56+
TAG="<target_name>/<version>"
57+
git tag -a $TAG -m "<tag description>" && git push origin tag $TAG
58+
```
59+
60+
To run the workflow manually, navigate over to the ["Build and Publish Docker Image"](https://github.com/commonwarexyz/monorepo/actions/workflows/docker.yaml)
61+
action. From there, run a `workflow_dispatch` trigger, select the tag you just pushed, and then finally select the image to release.
62+
63+
Or, if you prefer to use the `gh` CLI, you can run:
64+
```sh
65+
gh workflow run "Build and Publish Docker Image" --ref <tag> -f target=<target>
66+
```
67+

docker/docker-bake.hcl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
variable "REGISTRY" {
2+
default = "ghcr.io"
3+
}
4+
5+
variable "REPOSITORY" {
6+
default = "commonwarexyz/monorepo"
7+
}
8+
9+
variable "DEFAULT_TAG" {
10+
default = "commonware:local"
11+
}
12+
13+
variable "PLATFORMS" {
14+
// Only specify a single platform when `--load` ing into docker.
15+
// Multi-platform is supported when outputting to disk or pushing to a registry.
16+
// Multi-platform builds can be tested locally with: --set="*.output=type=image,push=false"
17+
default = "linux/amd64,linux/arm64"
18+
}
19+
20+
// Special target: https://github.com/docker/metadata-action#bake-definition
21+
target "docker-metadata-action" {
22+
tags = ["${DEFAULT_TAG}"]
23+
}
24+
25+
target "riscv-unknown-elf-toolchain" {
26+
inherits = ["docker-metadata-action"]
27+
context = "."
28+
dockerfile = "docker/riscv-unknown-elf-toolchain.dockerfile"
29+
platforms = split(",", PLATFORMS)
30+
}
31+
32+
target "rust-riscv32imac-cross" {
33+
inherits = ["docker-metadata-action"]
34+
context = "."
35+
dockerfile = "docker/rust-riscv-cross.dockerfile"
36+
args = {
37+
ARCH = "riscv32imac"
38+
}
39+
platforms = split(",", PLATFORMS)
40+
}
41+
42+
target "rust-riscv64imac-cross" {
43+
inherits = ["docker-metadata-action"]
44+
context = "."
45+
dockerfile = "docker/rust-riscv-cross.dockerfile"
46+
args = {
47+
ARCH = "riscv64imac"
48+
}
49+
platforms = split(",", PLATFORMS)
50+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
FROM ubuntu:22.04
2+
3+
# Install core build dependencies
4+
RUN apt-get update && \
5+
apt-get install --assume-yes --no-install-recommends \
6+
ca-certificates \
7+
autoconf \
8+
automake \
9+
autotools-dev \
10+
curl \
11+
python3 \
12+
python3-pip \
13+
python3-tomli \
14+
libmpc-dev \
15+
libmpfr-dev \
16+
libgmp-dev \
17+
gawk \
18+
build-essential \
19+
bison \
20+
flex \
21+
texinfo \
22+
gperf \
23+
libtool \
24+
patchutils \
25+
bc \
26+
zlib1g-dev \
27+
libexpat-dev \
28+
ninja-build \
29+
git \
30+
cmake \
31+
libglib2.0-dev \
32+
libslirp-dev
33+
34+
ENV RISCV=/opt/riscv
35+
ENV RISCV_TAG=2025.09.28
36+
ENV PATH=$PATH:$RISCV/bin
37+
38+
# https://github.com/riscv-collab/riscv-gnu-toolchain/issues/1669#issuecomment-2682013720
39+
RUN git clone https://github.com/riscv/riscv-gnu-toolchain --branch $RISCV_TAG && \
40+
cd riscv-gnu-toolchain && \
41+
sed -i '/shallow = true/d' .gitmodules && \
42+
sed -i 's/--depth 1//g' Makefile.in && \
43+
./configure --prefix=$RISCV --enable-multilib && \
44+
make && \
45+
cd .. && \
46+
rm -rf riscv-gnu-toolchain

docker/rust-riscv-cross.dockerfile

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
FROM ghcr.io/commonwarexyz/monorepo/riscv-unknown-elf-toolchain@sha256:09897c042df2487352e499a5ec121e4ad8ea4828d691aa2e6b9435e5ca59f25b AS rv-bare
2+
FROM ubuntu:22.04
3+
4+
ARG ARCH
5+
6+
ENV SHELL=/bin/bash
7+
ENV DEBIAN_FRONTEND=noninteractive
8+
9+
# Install core dependencies
10+
RUN apt-get update && apt-get install --assume-yes --no-install-recommends \
11+
ca-certificates \
12+
build-essential \
13+
autoconf \
14+
automake \
15+
autotools-dev \
16+
git \
17+
curl \
18+
make \
19+
cmake \
20+
xxd \
21+
g++-riscv64-linux-gnu \
22+
libc6-dev-riscv64-cross \
23+
binutils-riscv64-linux-gnu \
24+
llvm \
25+
clang
26+
27+
# Copy the RISC-V GNU toolchain from the previous stage
28+
COPY --from=rv-bare /opt/riscv /opt/riscv
29+
30+
# Install Rustup and Rust
31+
ENV RUST_VERSION=nightly
32+
ENV PATH="/root/.cargo/bin:${PATH}"
33+
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y --default-toolchain ${RUST_VERSION} --component rust-src
34+
35+
# Add the RV toolchain to the path
36+
ENV PATH="/opt/riscv/bin:${PATH}"
37+
38+
# Set up the env vars to instruct rustc to use the correct compiler and linker
39+
ENV CC_riscv32_unknown_none_elf=riscv64-linux-gnu-gcc \
40+
CC_riscv64_unknown_none_elf=riscv64-linux-gnu-gcc \
41+
CXX_riscv32_unknown_none_elf=riscv64-linux-gnu-g++ \
42+
CXX_riscv64_unknown_none_elf=riscv64-linux-gnu-g++ \
43+
CARGO_TARGET_RISCV32_UNKNOWN_NONE_ELF_LINKER=riscv64-linux-gnu-gcc \
44+
CARGO_TARGET_RISCV64_UNKNOWN_NONE_ELF_LINKER=riscv64-linux-gnu-gcc \
45+
RUSTFLAGS="-Clink-arg=-e_start" \
46+
CARGO_BUILD_TARGET="$ARCH-unknown-none-elf" \
47+
RUSTUP_TOOLCHAIN=${RUST_VERSION}

0 commit comments

Comments
 (0)