diff --git a/.github/workflows/main.yml b/.github/workflows/build-and-test.yml similarity index 50% rename from .github/workflows/main.yml rename to .github/workflows/build-and-test.yml index 5268f9fa..5204e8ad 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/build-and-test.yml @@ -1,25 +1,24 @@ -name: CI +name: Build, lint and test -on: - push: - branches: ['main'] - pull_request: - branches: ['*'] +on: workflow_call + +permissions: + contents: read jobs: clippy: - name: Check code formatting + name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - uses: giraffate/clippy-action@v1 - with: - reporter: 'github-pr-review' - github_token: ${{ secrets.GITHUB_TOKEN }} - clippy_flags: --all-features --all-targets --color always -- --deny warnings + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - uses: giraffate/clippy-action@v1 + with: + reporter: github-pr-review + github_token: ${{ secrets.GITHUB_TOKEN }} + clippy_flags: --all-features --all-targets --color always -- --deny warnings build: name: Build and test diff --git a/.github/workflows/on-push-main-or-pr.yml b/.github/workflows/on-push-main-or-pr.yml new file mode 100644 index 00000000..90fb7fcd --- /dev/null +++ b/.github/workflows/on-push-main-or-pr.yml @@ -0,0 +1,16 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: ["*"] + +permissions: + contents: read + +jobs: + test: + name: Test + uses: ./.github/workflows/build-and-test.yml + secrets: inherit diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml new file mode 100644 index 00000000..0a4d6c9c --- /dev/null +++ b/.github/workflows/on-tag.yml @@ -0,0 +1,23 @@ +name: Push docker image + +on: + push: + tags: + - v[0-9]+.[0-9]+.[0-9]+* + - "[0-9]+.[0-9]+.[0-9]+*" + +permissions: + contents: read + +jobs: + test: + uses: ./.github/workflows/build-and-test.yml + secrets: inherit + docker-hub-image: + needs: [test] + uses: ./.github/workflows/push-docker-image.yml + secrets: inherit + with: + image_name: carlakirkcohen/simln + artifact_name: simln-docker + dockerfile: ./docker/Dockerfile diff --git a/.github/workflows/push-docker-image.yml b/.github/workflows/push-docker-image.yml new file mode 100644 index 00000000..6ed2aae3 --- /dev/null +++ b/.github/workflows/push-docker-image.yml @@ -0,0 +1,110 @@ +name: Build and push docker image + +on: + workflow_call: + inputs: + image_name: + description: Name for the docker image + required: true + type: string + artifact_name: + description: Name for the temporary artifact + required: true + type: string + dockerfile: + description: Path to the dockerfile + required: true + type: string + latest: + description: Whether to tag the image as latest + required: false + type: boolean + default: false + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + # - linux/arm/v6 + # - linux/arm/v7 + - linux/arm64 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ inputs.image_name }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push by digest + id: build + uses: docker/build-push-action@v4 + with: + context: . + file: ${{ inputs.dockerfile}} + platforms: ${{ matrix.platform }} + labels: ${{ steps.meta.outputs.labels }} + outputs: type=image,name=${{ inputs.image_name }},push-by-digest=true,name-canonical=true,push=true + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + - name: Upload digest + uses: actions/upload-artifact@v3 + with: + name: digests${{ inputs.artifact_name }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: + - build + steps: + - name: Download digests + uses: actions/download-artifact@v3 + with: + name: digests${{ inputs.artifact_name }} + path: /tmp/digests + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ inputs.image_name }} + flavor: | + latest=${{ inputs.latest && 'true' || 'false' }} + tags: | + type=semver,pattern={{version}} + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ inputs.image_name }}@sha256:%s ' *) + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ inputs.image_name }}:${{ steps.meta.outputs.version }} diff --git a/.github/workflows/tag-image-as-latest.yml b/.github/workflows/tag-image-as-latest.yml new file mode 100644 index 00000000..b85255c5 --- /dev/null +++ b/.github/workflows/tag-image-as-latest.yml @@ -0,0 +1,33 @@ +name: Push latest tag to docker hub + +on: + workflow_dispatch: + inputs: + tag: + required: true + description: Docker tag to set as latest + +permissions: + contents: read + +env: + DOCKER_IMAGE: carlakirkcohen/simln + +jobs: + update-docker-images-to-latest: + name: Update docker images to latest + runs-on: ubuntu-latest + steps: + - uses: actions-ecosystem/action-regex-match@v2 + id: regex-match + with: + text: ${{ inputs.tag }} + regex: v?(.+) + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Docker tag and push latest + run: | + docker buildx imagetools create -t ${{ env.DOCKER_IMAGE }}:latest ${{ env.DOCKER_IMAGE }}:${{ steps.regex-match.outputs.group1 }} diff --git a/Makefile b/Makefile index cc55943b..68777f3f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ LOG_LEVEL ?= info build-docker: - chmod +x ./docker/build.sh && ./docker/build.sh + docker build -f docker/Dockerfile -t sim-ln . mount-volume: chmod +x ./docker/setup-volume.sh && ./docker/setup-volume.sh "$(SIMFILE_PATH)" @@ -28,4 +28,4 @@ run-interactive: docker run --rm --name sim-ln --init -v simln-data:/data -e SIMFILE_PATH=/data/sim.json -it sim-ln stop-docker: - docker stop sim-ln + docker stop sim-ln \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index ef93f0ed..7453bb5f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,34 +1,45 @@ -# Use the rust image as the base image for the build stage -FROM rust:latest AS builder - -# Receive architecture as an argument -ARG TARGET_ARCH +# Run from the root of the project +# Use the rust image as the base image for the build stage +FROM rust:latest AS base +# buildkit will provide this automatically, no need to pass it in +ARG TARGETARCH + +FROM base as builder-amd64 +ENV TARGET_RUST_ARCH="x86_64-unknown-linux-musl" + +FROM base as builder-arm64 +ENV TARGET_RUST_ARCH="aarch64-unknown-linux-musl" +RUN apt-get update && apt-get install clang llvm -y --no-install-recommends +ENV CC_aarch64_unknown_linux_musl=clang +ENV AR_aarch64_unknown_linux_musl=llvm-ar +ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUSTFLAGS="-Clink-self-contained=yes -Clinker=rust-lld" + +FROM builder-${TARGETARCH} as builder +RUN echo "Building for architecture: $TARGET_RUST_ARCH" # Copy the source code -COPY . /tmp/sim-ln +COPY . /sim-ln # Install the dependencies required for building sim-ln RUN apt-get update \ && apt-get -y --no-install-recommends install protobuf-compiler musl-tools -RUN cd /tmp/sim-ln \ - && rustup target add ${TARGET_ARCH} \ - && rustup component add rustfmt \ - && RUSTFLAGS='-C target-feature=+crt-static' cargo build --locked --release --target=${TARGET_ARCH} +RUN rustup target add ${TARGET_RUST_ARCH} +RUN rustup component add rustfmt +RUN cd /sim-ln && RUSTFLAGS='-C target-feature=+crt-static' cargo build --locked --release --target=${TARGET_RUST_ARCH} +RUN mv /sim-ln/target/${TARGET_RUST_ARCH}/release/sim-cli /sim-ln/sim-cli # Use a new stage with a smaller base image to reduce image size FROM alpine:latest -ARG TARGET_ARCH - RUN apk update && apk upgrade # Copy the sim-cli binaries from the build stage to the new stage -COPY --from=builder /tmp/sim-ln/target/${TARGET_ARCH}/release/sim-cli /usr/local/bin/ +COPY --from=builder /sim-ln/sim-cli /usr/local/bin/ # Copy the entrypoint script to the container COPY docker/entrypoint.sh /entrypoint.sh RUN chmod +x entrypoint.sh -ENTRYPOINT [ "/entrypoint.sh" ] +ENTRYPOINT [ "/entrypoint.sh" ] \ No newline at end of file diff --git a/docker/build.sh b/docker/build.sh deleted file mode 100755 index 44a0b150..00000000 --- a/docker/build.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -case $(uname -m) in - x86_64) - ARCH="amd64" - TARGET_ARCH="x86_64-unknown-linux-musl" - ;; - arm64) - ARCH="arm64" - TARGET_ARCH="aarch64-unknown-linux-musl" - ;; - *) - echo "Unsupported architecture" - exit 1 - ;; -esac - -echo "Building for architecture: $TARGET_ARCH" -docker build -f docker/Dockerfile --build-arg TARGET_ARCH=$TARGET_ARCH -t sim-ln .