name: "Docker build & push"
|
|
on:
|
workflow_dispatch:
|
inputs:
|
sha:
|
description: 'The commit SHA to build'
|
type: string
|
required: true
|
branch_name:
|
description: 'The branch name to build'
|
type: string
|
required: true
|
workflow_call:
|
inputs:
|
sha:
|
required: true
|
type: string
|
branch_name:
|
required: true
|
type: string
|
outputs:
|
image_version:
|
description: "Docker branch tag"
|
value: ${{ jobs.calculate_version.outputs.image_version }}
|
build_version:
|
description: "Docker version tag"
|
value: ${{ jobs.calculate_version.outputs.build_version }}
|
|
env:
|
DOCKER_CLI_EXPERIMENTAL: enabled
|
IMAGE_NAME: "${{ vars.DOCKERHUB_ORG }}/label-studio"
|
DOCKER_IMAGE_TAG_CHECK_NAME: "Docker image tag"
|
POETRY_VERSION: 2.1.4
|
|
jobs:
|
calculate_version:
|
name: "Calculate version"
|
runs-on: ubuntu-latest
|
outputs:
|
image_version: ${{ steps.version.outputs.image_version }}
|
build_version: ${{ steps.version.outputs.build_version }}
|
pretty_branch_name: ${{ steps.version.outputs.pretty_branch_name }}
|
sha: ${{ steps.version.outputs.sha }}
|
steps:
|
- name: Checkout
|
uses: actions/checkout@v6
|
with:
|
submodules: 'recursive'
|
ref: ${{ inputs.sha }}
|
fetch-depth: 1
|
|
- name: Calculate version
|
id: version
|
env:
|
BRANCH_NAME: ${{ inputs.branch_name }}
|
run: |
|
set -x
|
sha="$(git rev-parse HEAD)"
|
echo "sha=$sha" >> $GITHUB_OUTPUT
|
|
pretty_branch_name="$(echo -n "${BRANCH_NAME#refs/heads/}" | sed -E 's#[/_\.-]+#-#g' | tr '[:upper:]' '[:lower:]' | cut -c1-25 | sed -E 's#-$##g')"
|
echo "pretty_branch_name=${pretty_branch_name}" >> "${GITHUB_OUTPUT}"
|
|
regexp='^ls-release\/(.*)$';
|
if [[ "$BRANCH_NAME" =~ $regexp ]]; then
|
image_version="${BASH_REMATCH[1]}rc${sha}"
|
else
|
image_version="${pretty_branch_name}"
|
fi
|
echo "image_version=${image_version}" >> $GITHUB_OUTPUT
|
|
current_time="$(date +'%Y%m%d.%H%M%S')"
|
branch="-${pretty_branch_name}"
|
short_sha="$(git rev-parse --short HEAD)"
|
long_sha="$(git rev-parse HEAD)"
|
echo "sha=$long_sha" >> $GITHUB_OUTPUT
|
short_sha_length="$(echo $short_sha | awk '{print length}')"
|
current_time_length="$(echo $current_time | awk '{print length}')"
|
version="${current_time}$(echo $branch | cut -c1-$((50 - short_sha_length - current_time_length)))-${short_sha}"
|
echo "build_version=$version" >> $GITHUB_OUTPUT
|
|
docker_build:
|
name: "Docker image (${{ matrix.platform }})"
|
timeout-minutes: 90
|
runs-on: ${{ matrix.runner }}
|
needs: calculate_version
|
strategy:
|
fail-fast: false
|
matrix:
|
include:
|
- platform: linux/amd64
|
runner: ubuntu-latest
|
- platform: linux/arm64
|
runner: ubuntu-24.04-arm
|
steps:
|
- uses: hmarr/debug-action@v3.0.0
|
|
- name: Prepare
|
run: |
|
platform=${{ matrix.platform }}
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
- name: Checkout
|
uses: actions/checkout@v6
|
with:
|
submodules: 'recursive'
|
ref: ${{ inputs.sha }}
|
fetch-depth: 2147483647
|
|
- name: Set up Docker Buildx
|
uses: docker/setup-buildx-action@v3.11.1
|
|
- name: Login to DockerHub
|
uses: docker/login-action@v3.5.0
|
with:
|
username: ${{ vars.DOCKERHUB_USERNAME }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
- name: Download LaunchDarkly Community config
|
env:
|
LAUNCHDARKLY_COMMUNITY_SDK_KEY: ${{ secrets.LAUNCHDARKLY_COMMUNITY_SDK_KEY }}
|
LAUNCHDARKLY_DOWNLOAD_PATH: "label_studio/feature_flags.json"
|
run: |
|
set -xeuo pipefail
|
curl \
|
--connect-timeout 30 \
|
--retry 5 \
|
--retry-delay 10 \
|
-H "Authorization: $LAUNCHDARKLY_COMMUNITY_SDK_KEY" \
|
"https://sdk.launchdarkly.com/sdk/latest-all" >"$LAUNCHDARKLY_DOWNLOAD_PATH"
|
if [ "$(jq 'has("flags")' <<< cat $LAUNCHDARKLY_DOWNLOAD_PATH)" = "true" ]; then
|
echo "feature_flags.json is valid"
|
else
|
echo "feature_flags.json is invalid"
|
cat $LAUNCHDARKLY_DOWNLOAD_PATH
|
exit 1
|
fi
|
|
- name: Docker meta
|
id: meta
|
uses: docker/metadata-action@v5
|
with:
|
images: ${{ env.IMAGE_NAME }}
|
|
- name: Push Docker image (${{ matrix.platform }})
|
uses: docker/build-push-action@v6.18.0
|
id: docker_build_and_push
|
with:
|
context: .
|
file: Dockerfile
|
platforms: ${{ matrix.platform }}
|
sbom: true
|
provenance: true
|
tags: ${{ env.IMAGE_NAME }}
|
labels: ${{ steps.meta.outputs.labels }}
|
cache-from: type=gha
|
cache-to: type=gha,mode=min
|
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
|
build-args: |
|
BRANCH_OVERRIDE=${{ inputs.branch_name }}
|
VERSION_OVERRIDE=${{ needs.calculate_version.outputs.build_version }}
|
POETRY_VERSION=${{ env.POETRY_VERSION }}
|
|
- name: Export digest
|
run: |
|
mkdir -p ${{ runner.temp }}/digests
|
digest="${{ steps.docker_build_and_push.outputs.digest }}"
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
|
- name: Upload digest
|
uses: actions/upload-artifact@v6
|
with:
|
name: digests-${{ env.PLATFORM_PAIR }}
|
path: ${{ runner.temp }}/digests/*
|
if-no-files-found: error
|
retention-days: 1
|
|
merge_docker_manifest:
|
runs-on: ubuntu-latest
|
needs:
|
- docker_build
|
- calculate_version
|
steps:
|
- name: Download digests
|
uses: actions/download-artifact@v7
|
with:
|
path: ${{ runner.temp }}/digests
|
pattern: digests-*
|
merge-multiple: true
|
|
- name: Login to Docker Hub
|
uses: docker/login-action@v3
|
with:
|
username: ${{ vars.DOCKERHUB_USERNAME }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
- name: Set up Docker Buildx
|
uses: docker/setup-buildx-action@v3
|
|
- name: Docker meta
|
id: meta
|
uses: docker/metadata-action@v5
|
with:
|
images: ${{ env.IMAGE_NAME }}
|
tags: |
|
type=raw,value=${{ needs.calculate_version.outputs.image_version }}
|
type=raw,value=${{ needs.calculate_version.outputs.build_version }}
|
|
- name: Create manifest list and push
|
working-directory: ${{ runner.temp }}/digests
|
run: |
|
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
$(printf '${{ env.IMAGE_NAME }}@sha256:%s ' *)
|
|
- name: Inspect image
|
run: |
|
docker buildx imagetools inspect ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
|
create_check:
|
name: "Create Docker image tag Check"
|
runs-on: ubuntu-latest
|
needs: [calculate_version, docker_build, merge_docker_manifest]
|
if: success()
|
steps:
|
- name: Create Docker image tag Check
|
uses: actions/github-script@v8
|
with:
|
script: |
|
const { repo, owner } = context.repo;
|
const details = {
|
"branch": "${{ inputs.branch_name }}",
|
"pretty_branch_name": "${{ needs.calculate_version.outputs.pretty_branch_name }}",
|
"image_version": "${{ needs.calculate_version.outputs.image_version }}",
|
"sha": "${{ needs.calculate_version.outputs.sha }}"
|
}
|
const { data: check } = await github.rest.checks.create({
|
owner,
|
repo,
|
name: '${{ env.DOCKER_IMAGE_TAG_CHECK_NAME }}',
|
head_sha: '${{ needs.calculate_version.outputs.sha }}',
|
status: 'in_progress',
|
output: {
|
title: '${{ env.DOCKER_IMAGE_TAG_CHECK_NAME }}',
|
summary: JSON.stringify(details)
|
}
|
});
|
await github.rest.checks.update({
|
owner,
|
repo,
|
check_run_id: check.id,
|
status: 'completed',
|
conclusion: 'success'
|
});
|
|
notify_slack:
|
name: "Notify Slack"
|
runs-on: ubuntu-latest
|
needs: [calculate_version, docker_build, create_check, merge_docker_manifest]
|
if: always() && github.event_name == 'push' && inputs.branch_name == 'develop'
|
steps:
|
- name: Notify to Slack on failure
|
if: failure()
|
env:
|
GITHUB_REPOSITORY: "${{ github.repository }}"
|
GITHUB_RUN_ID: "${{ github.run_id }}"
|
GITHUB_SHA: "${{ github.sha }}"
|
SLACK_BOT_TOKEN: ${{ secrets.SLACK_LSE_BOT_TOKEN }}
|
uses: slackapi/slack-github-action@v1.27
|
with:
|
channel-id: 'C01RJV08UJK'
|
slack-message: |
|
❌ Docker build failed for *develop* branch! <!subteam^${{ vars.SLACK_GR_DEVOPS }}>
|
|
><https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}|[Workflow run]>
|