diff --git a/.github/workflows/docker-build-ontop.yml b/.github/workflows/docker-build-ontop.yml index 2546180b98f2..8676e334b324 100644 --- a/.github/workflows/docker-build-ontop.yml +++ b/.github/workflows/docker-build-ontop.yml @@ -68,6 +68,7 @@ jobs: core.setOutput("active", membership.state == "active"); - name: Checkout + id: checkout uses: actions/checkout@v4 with: submodules: 'recursive' @@ -77,18 +78,9 @@ jobs: - name: Calculate Docker tags id: calculate-docker-tags uses: actions/github-script@v7 - env: - TAGS: ${{ inputs.tags }} - IMAGE_NAME: ${{ env.IMAGE_NAME }} with: script: | - const raw_tags_input = process.env.TAGS; - const image_name = process.env.IMAGE_NAME; - - const tags = raw_tags_input.split(',').map(x => x.trim()); - const docker_tags = tags.map(x => `${image_name}:${x}`).join(','); - console.log(docker_tags); - core.setOutput("docker-tags", docker_tags); + core.setOutput('docker-tags', `${{ inputs.tags }}`.split(",").join("\n")) - name: Edit Dockerfile env: @@ -107,6 +99,16 @@ jobs: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ steps.checkout.outputs.commit }} + tags: | + ${{ steps.calculate-docker-tags.outputs.docker-tags }} + - name: Push Docker image uses: docker/build-push-action@v6.9.0 id: docker_build_and_push @@ -115,6 +117,9 @@ jobs: file: ${{ inputs.dockerfile_path }} platforms: linux/amd64,linux/arm64 push: ${{ steps.actor-membership.outputs.active }} - tags: ${{ steps.calculate-docker-tags.outputs.docker-tags }} + sbom: true + provenance: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 790b298d871e..a54fd9e994f7 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -117,6 +117,17 @@ jobs: exit 1 fi + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ inputs.sha }} + tags: | + type=raw,value=${{ steps.version.outputs.image_version }} + type=raw,value=${{ steps.version.outputs.build_version }} + - name: Push Docker image uses: docker/build-push-action@v6.9.0 id: docker_build_and_push @@ -125,7 +136,10 @@ jobs: file: Dockerfile platforms: linux/amd64,linux/arm64 push: ${{ steps.actor-membership.outputs.active }} - tags: ${{ env.IMAGE_NAME }}:${{ steps.version.outputs.image_version }},${{ env.IMAGE_NAME }}:${{ steps.version.outputs.build_version }} + sbom: true + provenance: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max build-args: | diff --git a/.github/workflows/docker-release-promote.yml b/.github/workflows/docker-release-promote.yml index 5007a7a362d8..ff1a49fea2bf 100644 --- a/.github/workflows/docker-release-promote.yml +++ b/.github/workflows/docker-release-promote.yml @@ -62,7 +62,7 @@ jobs: with: ref: ${{ inputs.release_tag }} - - name: Check is latest tag needs to be updated + - name: Check if the latest tag needs to be updated uses: actions/github-script@v7 id: generate-tags with: @@ -70,8 +70,6 @@ jobs: script: | const {repo, owner} = context.repo; const newTag = '${{ inputs.release_tag }}'; - const dockerHubImageName = '${{ env.IMAGE_NAME }}'; - const redHatImageName = 'quay.io/redhat-isv-containers/${{ secrets.REDHAT_MARKETPLACE_LS_PROJECT_ID }}'; const regexp = '^[v]?([0-9]+)\.([0-9]+)\.([0-9]+)(\.post([0-9]+))?$'; function compareVersions(a, b) { @@ -109,36 +107,22 @@ jobs: console.log(`Newest tag: ${newestVersion[0]}`) let dockerHubUbuntuRawTags = [newTag]; - let dockerHubUbiRawTags = [`ubi_${newTag}`]; - let redHatUbiRawTags = [newTag]; - if (compareVersions(newTag.match(regexp), newestVersion) >= 0) { console.log(`new tag ${newTag} is higher that all existing tags`) console.log(dockerHubUbuntuRawTags) dockerHubUbuntuRawTags.push('latest') - dockerHubUbiRawTags.push('ubi_latest') - redHatUbiRawTags.push('latest') core.setOutput("latest", true); } else { console.log('not latest') core.setOutput("latest", false); } - const ubuntuTags = dockerHubUbuntuRawTags.map(e => `${dockerHubImageName}:${e}`) - const redHatTags = redHatUbiRawTags.map(e => `${redHatImageName}:${e}`) - const ubiTags = redHatTags.concat( - dockerHubUbiRawTags.map(e => `${dockerHubImageName}:${e}`) - ) - + const ubuntuTags = dockerHubUbuntuRawTags.join("\n"); console.log('Ubuntu tags:') console.log(ubuntuTags) - console.log('Ubi tags:') - console.log(ubiTags) - core.setOutput("ubuntu-tags", ubuntuTags.join(',')); - core.setOutput("redhat-tags", redHatTags.join(',')); - core.setOutput("ubi-tags", ubiTags.join(',')); + core.setOutput("ubuntu-tags", ubuntuTags); - name: Set up Python uses: actions/setup-python@v5 @@ -178,13 +162,6 @@ jobs: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Login to RedHat Registry - uses: docker/login-action@v3.3.0 - with: - registry: quay.io - username: ${{ secrets.REDHAT_REGISTRY_LOGIN }} - password: ${{ secrets.REDHAT_REGISTRY_PASSWORD }} - - name: Prepare Release Dockerfile id: release_dockerfile env: @@ -207,6 +184,16 @@ jobs: COPY --chown=54546:0 ${{ env.LAUNCHDARKLY_DOWNLOAD_PATH }} /label-studio/label_studio/feature_flags.json EOF + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.revision=${{ steps.get_info.outputs.sha }} + tags: | + ${{ steps.generate-tags.outputs.ubuntu-tags }} + - name: Build and Push Release Ubuntu Docker image uses: docker/build-push-action@v6.9.0 id: docker_build @@ -214,7 +201,10 @@ jobs: context: ${{ steps.release_dockerfile.outputs.release_dir }} file: ${{ steps.release_dockerfile.outputs.release_dir }}/${{ env.RELEASE_DOCKERFILE }} push: true - tags: ${{ steps.generate-tags.outputs.ubuntu-tags }} + sbom: true + provenance: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max platforms: linux/amd64,linux/arm64 @@ -231,7 +221,7 @@ jobs: continue-on-error: true env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_ORG: ${{ vars.SENTRY_ORG }} with: version: label-studio@${{ inputs.release_tag }} projects: opensource-v1-backend @@ -241,7 +231,7 @@ jobs: continue-on-error: true env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_ORG: ${{ vars.SENTRY_ORG }} with: version: label-studio@${{ inputs.release_tag }} projects: opensource-v1-frontend diff --git a/Dockerfile b/Dockerfile index ac30e78b7aa4..41ea43f924b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -121,14 +121,26 @@ ENV LS_DIR=/label-studio \ WORKDIR $LS_DIR -# incapsulate nginx install & configure to a single layer +# install prerequisites for app RUN --mount=type=cache,target="/var/cache/apt",sharing=locked \ --mount=type=cache,target="/var/lib/apt/lists",sharing=locked \ set -eux; \ apt-get update; \ apt-get upgrade -y; \ apt-get install --no-install-recommends -y libexpat1 \ - nginx curl; \ + gnupg2 curl; \ + apt-get autoremove -y + +# install nginx +RUN --mount=type=cache,target="/var/cache/apt",sharing=locked \ + --mount=type=cache,target="/var/lib/apt/lists",sharing=locked \ + set -eux; \ + curl -sSL https://nginx.org/keys/nginx_signing.key | gpg --dearmor -o /etc/apt/keyrings/nginx-archive-keyring.gpg >/dev/null; \ + DEBIAN_VERSION=$(awk -F '=' '/^VERSION_CODENAME=/ {print $2}' /etc/os-release); \ + printf "deb [signed-by=/etc/apt/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/debian ${DEBIAN_VERSION} nginx\n" > /etc/apt/sources.list.d/nginx.list; \ + printf "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" > /etc/apt/preferences.d/99nginx; \ + apt-get update; \ + apt-get install --no-install-recommends -y nginx; \ apt-get autoremove -y RUN set -eux; \ @@ -144,8 +156,6 @@ COPY --chown=1001:0 README.md . COPY --chown=1001:0 LICENSE LICENSE COPY --chown=1001:0 licenses licenses COPY --chown=1001:0 deploy deploy -# We need these files for security scanners -COPY --chown=1001:0 web/yarn.lock $LS_DIR/web/yarn.lock # Copy files from build stages COPY --chown=1001:0 --from=venv-builder $LS_DIR $LS_DIR diff --git a/Dockerfile.redhat b/Dockerfile.redhat deleted file mode 100644 index 178d866cb8d0..000000000000 --- a/Dockerfile.redhat +++ /dev/null @@ -1,76 +0,0 @@ -# syntax=docker/dockerfile:1.3 -FROM registry.access.redhat.com/ubi8/nodejs-18-minimal AS frontend-builder - -ENV NPM_CACHE_LOCATION=$HOME/.cache/yarn/v6 \ - PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true - -WORKDIR /label-studio/label_studio/frontend - -COPY --chown=1001:0 label_studio/frontend . -COPY --chown=1001:0 pyproject.toml /label-studio - -RUN --mount=type=cache,target=$NPM_CACHE_LOCATION,uid=1001,gid=0 \ - npm install -g yarn - -RUN --mount=type=cache,target=$NPM_CACHE_LOCATION,uid=1001,gid=0 \ - yarn install --frozen-lockfile \ - && yarn run build:production - -FROM registry.access.redhat.com/ubi8/python-39 - -ENV LS_DIR=/label-studio \ - PIP_CACHE_DIR=$HOME/.cache \ - POETRY_CACHE_DIR=$HOME/.poetry-cache \ - DJANGO_SETTINGS_MODULE=core.settings.label_studio \ - LABEL_STUDIO_BASE_DATA_DIR=/label-studio/data \ - OPT_DIR=/opt/heartex/instance-data/etc \ - SETUPTOOLS_USE_DISTUTILS=stdlib - -USER 0 -RUN dnf module enable -y nginx:1.20 && \ - dnf -y install nginx && \ - mkdir -p $OPT_DIR && \ - chown -R 1001:0 $OPT_DIR /etc/nginx/nginx.conf && \ - chmod -R g=u $OPT_DIR /etc/nginx/nginx.conf -USER 1001 - -WORKDIR $LS_DIR - -RUN --mount=type=cache,target=$PIP_CACHE_DIR,uid=1001,gid=0 \ - pip3 install poetry uwsgi uwsgitop - -# Copy essential files for installing Label Studio and its dependencies -COPY --chown=1001:0 pyproject.toml . -COPY --chown=1001:0 poetry.lock . -COPY --chown=1001:0 README.md . -COPY --chown=1001:0 label_studio/__init__.py ./label_studio/__init__.py - -# Ensure the poetry lockfile is up to date, then install all deps from it to -# the system python. This includes label-studio itself. For caching purposes, -# do this before copying the rest of the source code. -RUN --mount=type=cache,target=$POETRY_CACHE_DIR,uid=1001,gid=0 \ - poetry check --lock && POETRY_VIRTUALENVS_CREATE=false poetry install - -COPY --chown=1001:0 . . - -RUN rm -rf ./label_studio/frontend -COPY --chown=1001:0 --from=frontend-builder /label-studio/label_studio/frontend/dist ./label_studio/frontend/dist - -RUN python3 label_studio/manage.py collectstatic --no-input - -EXPOSE 8080 - -LABEL name="LabelStudio" \ - maintainer="infra@heartex.com" \ - vendor="Heartex" \ - version="1.5.0dev" \ - release="1" \ - summary="LabelStudio" \ - description="Label Studio is an open source data labeling tool." - -COPY --chown=1001:0 licenses/ /licenses -RUN cp $LS_DIR/LICENSE /licenses -ENV HOME=/label-studio - -ENTRYPOINT ["./deploy/docker-entrypoint.sh"] -CMD ["label-studio"]