diff --git a/.github/workflows/services-base.yml b/.github/workflows/services-base.yml new file mode 100644 index 000000000..e4227b7f7 --- /dev/null +++ b/.github/workflows/services-base.yml @@ -0,0 +1,123 @@ +on: + push: + branches: + - 'main' + paths: + - .github/workflows/base-image.yml + - docker/base.Dockerfile + pull_request: + paths: + - .github/workflows/base-image.yml + - docker/base.Dockerfile + +env: + REGISTRY: ghcr.io + IMAGE_NAME: blockscout/services-base + +jobs: + check_tag: + name: Check tag existence + runs-on: ubuntu-latest + outputs: + tag: ${{ steps.get_image_tag.outputs.tag }} + is_new: ${{ steps.check_existence.outputs.is_new }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get image tag + id: get_image_tag + env: + DOCKERFILE_PATH: "docker/base.Dockerfile" + run: | + CONTENT=$(cat $DOCKERFILE_PATH) + + nl=$'\n' + + if [[ $CONTENT =~ ARG\ CARGO_CHEF_VERSION=([^$nl]+) ]]; then + CARGO_CHEF_TAG=${BASH_REMATCH[1]} + else + echo "Failed to extract CARGO_CHEF_TAG from Dockerfile" + exit 1 + fi + + if [[ $CONTENT =~ ARG\ PROTOC_VERSION=([^$nl]+) ]]; then + PROTOC_VERSION=${BASH_REMATCH[1]} + else + echo "Failed to extract PROTOC_VERSION from Dockerfile" + exit 1 + fi + + if [[ $CONTENT =~ ARG\ PROTOC_GEN_OPENAPIV2_VERSION=([^$nl]+) ]]; then + PROTOC_GEN_OPENAPIV2_VERSION=${BASH_REMATCH[1]} + else + echo "Failed to extract PROTOC_GEN_OPENAPIV2_VERSION from Dockerfile" + exit 1 + fi + + # E.g, chef-0.1.62-rust-1.75-buster-protoc-25.2-openapi-2.19.0 + TAG=chef-${CARGO_CHEF_TAG}-protoc-${PROTOC_VERSION}-openapi-${PROTOC_GEN_OPENAPIV2_VERSION} + echo "TAG=$TAG" + + # Save values to be available from the next steps + echo "tag=$TAG" >> $GITHUB_OUTPUT + + - name: Check image tag exists + id: check_existence + env: + TAG: ${{ steps.get_image_tag.outputs.tag }} + run: | + TOKEN=$(echo ${{ secrets.GITHUB_TOKEN }} | base64) + TAGS_RESPONSE=$(curl -H "Authorization: Bearer ${TOKEN}" https://ghcr.io/v2/${IMAGE_NAME}/tags/list) + + # E.g, {"name":"blockscout/services-base","tags":["latest","chef-0.1.62-rust-1.75-buster-protoc-25.2-openapi-2.19.0"]} + # or {"errors":[{"code":"NAME_UNKNOWN","message":"repository name not known to registry"}]} + echo "List tags response: ${TAGS_RESPONSE}" + + if echo "${TAGS_RESPONSE}" | jq -e "if has(\"tags\") then .tags else [] end | map(select(. == \"$TAG\")) | length > 0" > /dev/null; then + echo "There is already a pushed image with ${TAG} as tag. Skipping." + echo "is_new=false" >> $GITHUB_OUTPUT + else + echo "is_new=true" >> $GITHUB_OUTPUT + fi + + build_and_push: + name: Build and push + needs: + - check_tag + if: needs.check_tag.outputs.is_new == 'true' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: "docker" + file: "docker/base.Dockerfile" + push: ${{ github.ref == 'refs/heads/main' }} + tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.check_tag.outputs.tag }} , ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + platforms: | + linux/amd64 + linux/arm64/v8 + labels: ${{ steps.meta.outputs.labels }} diff --git a/docker/base.Dockerfile b/docker/base.Dockerfile new file mode 100644 index 000000000..e73521bf0 --- /dev/null +++ b/docker/base.Dockerfile @@ -0,0 +1,26 @@ +ARG CARGO_CHEF_VERSION=0.1.62-rust-1.75-buster +FROM lukemathwalker/cargo-chef:${CARGO_CHEF_VERSION} + +ARG PROTOC_VERSION=25.2 +ARG PROTOC_GEN_OPENAPIV2_VERSION=2.19.0 + +RUN apt-get update && apt-get install -y curl wget unzip + +ARG TARGETARCH +RUN case ${TARGETARCH} in \ + "arm64") TARGETARCH=aarch_64 ;; \ + "amd64") TARGETARCH=x86_64 ;; \ + esac \ + && wget https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-${TARGETARCH}.zip -O ./protoc.zip \ + && unzip protoc.zip \ + && mv ./include/* /usr/include/ \ + && mv ./bin/protoc /usr/bin/protoc + +RUN case ${TARGETARCH} in \ + "amd64") TARGETARCH=x86_64 ;; \ + esac \ + && wget https://github.com/grpc-ecosystem/grpc-gateway/releases/download/v${PROTOC_GEN_OPENAPIV2_VERSION}/protoc-gen-openapiv2-v${PROTOC_GEN_OPENAPIV2_VERSION}-linux-${TARGETARCH} -O ./protoc-gen-openapiv2 \ + && chmod +x protoc-gen-openapiv2 \ + && mv ./protoc-gen-openapiv2 /usr/bin/protoc-gen-openapiv2 + +WORKDIR /app