Skip to content

Commit 79edfa5

Browse files
committed
[ci] Add riscv{32/64}imac-unknown-none-elf cross-compilation images
1 parent 9c9378f commit 79edfa5

6 files changed

Lines changed: 369 additions & 0 deletions

File tree

.github/workflows/docker.yml

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
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: Prepare
85+
run: |
86+
platform=${{ matrix.platform }}
87+
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
88+
- name: Download meta bake definition
89+
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
90+
with:
91+
name: bake-meta
92+
path: ${{ runner.temp }}
93+
- name: Authenticate with container registry
94+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
95+
with:
96+
registry: ${{ env.REGISTRY }}
97+
username: ${{ github.actor }}
98+
password: ${{ secrets.GITHUB_TOKEN }}
99+
- name: Set up Docker Buildx
100+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
101+
- name: Build
102+
id: bake
103+
uses: docker/bake-action@3acf805d94d93a86cce4ca44798a76464a75b88c # v6.9.0
104+
with:
105+
files: |
106+
./docker/docker-bake.hcl
107+
cwd://${{ runner.temp }}/bake-meta.json
108+
targets: ${{ needs.prepare.outputs.target }}
109+
set: |
110+
*.tags=
111+
*.platform=${{ matrix.platform }}
112+
*.output=type=image,"name=${{ env.REGISTRY_IMAGE }}/${{ needs.prepare.outputs.target }}",push-by-digest=true,name-canonical=true,push=true
113+
- name: Export digest
114+
run: |
115+
mkdir -p ${{ runner.temp }}/digests
116+
digest="${{ fromJSON(steps.bake.outputs.metadata)[needs.prepare.outputs.target]['containerimage.digest'] }}"
117+
touch "${{ runner.temp }}/digests/${digest#sha256:}"
118+
- name: Upload digest
119+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
120+
with:
121+
name: digests-${{ env.PLATFORM_PAIR }}
122+
path: ${{ runner.temp }}/digests/*
123+
if-no-files-found: error
124+
retention-days: 1
125+
126+
merge:
127+
name: Publish Manifest (${{ needs.prepare.outputs.target }})
128+
runs-on: ubuntu-latest
129+
permissions:
130+
packages: write
131+
needs:
132+
- build
133+
- prepare
134+
steps:
135+
- name: Download meta bake definition
136+
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
137+
with:
138+
name: bake-meta
139+
path: ${{ runner.temp }}
140+
- name: Download digests
141+
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
142+
with:
143+
path: ${{ runner.temp }}/digests
144+
pattern: digests-*
145+
merge-multiple: true
146+
- name: Authenticate with container registry
147+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
148+
with:
149+
registry: ${{ env.REGISTRY }}
150+
username: ${{ github.actor }}
151+
password: ${{ secrets.GITHUB_TOKEN }}
152+
- name: Set up Docker Buildx
153+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
154+
- name: Create manifest list and push
155+
working-directory: ${{ runner.temp }}/digests
156+
run: |
157+
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) \
158+
$(printf '${{ env.REGISTRY_IMAGE }}/${{ needs.prepare.outputs.target }}@sha256:%s ' *)
159+
- name: Inspect image
160+
run: |
161+
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: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.08.29
36+
ENV PATH=$PATH:$RISCV/bin
37+
38+
RUN git clone --recursive https://github.com/riscv/riscv-gnu-toolchain --branch $RISCV_TAG && \
39+
cd riscv-gnu-toolchain && \
40+
./configure --prefix=$RISCV --enable-multilib && \
41+
make && \
42+
cd .. && \
43+
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:main 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)