diff --git a/.github/workflows/container-images.yml b/.github/workflows/container-images.yml new file mode 100644 index 00000000..ad582e91 --- /dev/null +++ b/.github/workflows/container-images.yml @@ -0,0 +1,80 @@ +name: container-images + +on: + push: + branches: + - main + tags: + - '**' + pull_request: + branches: + - main + +jobs: + build-and-push: + runs-on: ubuntu-latest + + env: + DOCKER_BUILDX_PLATFORM: linux/amd64 + + steps: + - uses: actions/checkout@v4 + + - name: set up Docker buildx + uses: docker/setup-buildx-action@v3 + with: + platforms: ${{ env.DOCKER_BUILDX_PLATFORM }} + + - uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: info + run: | + docker version + docker info + + echo '${{ github.ref_name }}' | sed -e 's/[^a-zA-Z0-9._-]/_/g' > VERSION_TAG + echo "version_tag=$(cat VERSION_TAG)" + + - name: build + run: | + docker buildx build \ + --load \ + --platform "${DOCKER_BUILDX_PLATFORM}" \ + \ + --build-arg "VERSION=${{ github.ref_name }}" \ + --build-arg "FFMPEG_VERSION=release" \ + --build-arg "BUILD_DATE=$(date -u +"%Y-%m-%dT%TZ")" \ + --build-arg "GIT_COMMIT=${{ github.sha }}" \ + \ + -t "ghcr.io/opencast/pyca:latest" \ + -t "ghcr.io/opencast/pyca:main" \ + -t "ghcr.io/opencast/pyca:${{ github.sha }}" \ + -t "ghcr.io/opencast/pyca:$(cat VERSION_TAG)" \ + . + + - name: push release + if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }} + run: | + docker push "ghcr.io/opencast/pyca:$(cat VERSION_TAG)" + # assumption: last tag is always latest version + docker push "ghcr.io/opencast/pyca:latest" + + - name: push dev version + if: ${{ github.event_name == 'push' && github.ref_name == 'main' }} + run: | + docker push "ghcr.io/opencast/pyca:main" + + - name: delete untagged container images + uses: snok/container-retention-policy@v3.0.0 + if: ${{ github.event_name == 'push' }} + with: + account: opencast + token: ${{ secrets.GITHUB_TOKEN }} + tag-selection: untagged + image-names: pyca + cut-off: 1y diff --git a/Dockerfile b/Dockerfile index a4751343..d7cf4441 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,10 @@ -FROM alpine:3.13 AS build - -RUN apk --no-cache add \ +FROM docker.io/library/alpine:3.20 AS base +RUN apk --no-cache --virtual .run-deps add \ + libcurl \ + postgresql-libs \ + py3-pip \ + python3 +RUN apk --no-cache --virtual .build-deps add \ curl-dev \ g++ \ gcc \ @@ -9,63 +13,82 @@ RUN apk --no-cache add \ musl-dev \ nodejs \ npm \ - py3-pip \ - python3 \ - python3-dev \ - util-linux \ - && ln -s /usr/bin/python3 /usr/bin/python + postgresql-dev \ + python3-dev +RUN pip install --break-system-packages \ + gunicorn \ + psycopg2 -WORKDIR /usr/local/src +FROM base as build-pyca +WORKDIR /usr/local/src COPY requirements.txt package.json package-lock.json ./ -RUN pip install -r requirements.txt \ - && npm i - +RUN pip install --break-system-packages -r requirements.txt +RUN npm ci COPY . . RUN make pypi -FROM alpine:3.13 -LABEL maintainer="pyCA team" -COPY --from=build /usr/local/src/dist/pyca-*.tar.gz /tmp/pyca.tar.gz +FROM base as build -RUN apk --no-cache --virtual .run-deps add \ - libcurl \ - postgresql-libs \ - py3-pip \ - python3 \ - && apk --no-cache --virtual .build-deps add \ +COPY --from=build-pyca /usr/local/src/dist/pyca-*.tar.gz /tmp/pyca.tar.gz +RUN pip install --break-system-packages \ + /tmp/pyca.tar.gz +RUN apk del .build-deps \ + && rm /tmp/pyca.tar.gz + + +FROM docker.io/library/alpine:3.20 AS build-ffmpeg +ARG TARGETARCH +ARG FFMPEG_VERSION=release +RUN apk add --no-cache \ curl \ - curl-dev \ - g++ \ - gcc \ - linux-headers \ - make \ - musl-dev \ - postgresql-dev \ - python3-dev \ tar \ xz \ - && ln -s /usr/bin/python3 /usr/bin/python \ - && pip install \ - /tmp/pyca.tar.gz \ - gunicorn \ - psycopg2 \ - && cd /usr/local/bin \ - && curl -sSL "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz" \ + && mkdir -p /tmp/ffmpeg \ + && cd /tmp/ffmpeg \ + && curl -sSL "https://s3.opencast.org/opencast-ffmpeg-static/ffmpeg-${FFMPEG_VERSION}-${TARGETARCH}-static.tar.xz" \ | tar xJf - --strip-components 1 --wildcards '*/ffmpeg' '*/ffprobe' \ - && apk del .build-deps \ - && rm -rf /tmp/pyca.tar.gz + && chown root:root ff* \ + && mv ff* /usr/local/bin + + +FROM scratch AS assembly +COPY --from=build / / +COPY --from=build-ffmpeg /usr/local/bin/ff* /usr/local/bin/ + +COPY etc/pyca.conf etc/gunicorn.conf.py /etc/pyca/ +RUN echo 'bind = "0.0.0.0:8000"' >> /etc/pyca/gunicorn.conf.py RUN addgroup -S -g 800 pyca \ && adduser -S -D -h /var/lib/pyca -G pyca -u 800 pyca \ && addgroup pyca audio \ && addgroup pyca video -COPY etc/pyca.conf etc/gunicorn.conf.py /etc/pyca/ -RUN echo 'bind = "0.0.0.0:8000"' >> /etc/pyca/gunicorn.conf.py +FROM scratch AS squash +LABEL org.opencontainers.image.base.name="docker.io/library/alpine:3.20" + +COPY --from=assembly / / WORKDIR /var/lib/pyca + +ARG VERSION=main +ARG BUILD_DATE=unknown +ARG GIT_COMMIT=unknown + +LABEL maintainer="pyCA team" \ + org.opencontainers.image.title="pyCA" \ + org.opencontainers.image.description="Python Capture Agent for Opencast" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.vendor="Opencast" \ + org.opencontainers.image.authors="pyCA team" \ + org.opencontainers.image.licenses="LGPL-3.0-only" \ + org.opencontainers.image.url="https://github.com/opencast/pyCA/blob/${VERSION}/README.rst" \ + org.opencontainers.image.documentation="https://github.com/opencast/pyCA/blob/${VERSION}/README.rst" \ + org.opencontainers.image.source="https://github.com/opencast/pyCA" \ + org.opencontainers.image.created="${BUILD_DATE}" \ + org.opencontainers.image.revision="${GIT_COMMIT}" + USER pyca VOLUME [ "/var/lib/pyca" ] EXPOSE 8000 diff --git a/Makefile b/Makefile index 5891a82b..d1da3fc2 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ pypi: clean build @printf "\nUpload to PyPI with \"twine upload dist/$$(python setup.py --fullname).tar.gz\"\n" docker: - @docker build -t quay.io/opencast/pyca . + @docker build -t ghcr.io/opencast/pyca . clean: @python setup.py clean --all diff --git a/docs/install/container.rst b/docs/install/container.rst index acc82ebf..df0d04ec 100644 --- a/docs/install/container.rst +++ b/docs/install/container.rst @@ -3,7 +3,7 @@ Install PyCA via Container PyCA containers will automatically be built for each release and commit. This can be used to easily deploy pyCA e.g. for capturing network streams. -The containers can be found at `quay.io/repository/opencast/pyca `_. +The containers can be found at `ghcr.io/opencast/pyca `_. Compose Files diff --git a/init/container/README.rst b/init/container/README.rst index aefc4fa7..3c75303b 100644 --- a/init/container/README.rst +++ b/init/container/README.rst @@ -10,7 +10,7 @@ PyCA + SQLite cp etc/pyca.conf init/container/pyca.conf sed -i "s|#name .*|name = pyca-container|g" init/container/pyca.conf - docker-compose -f init/container/docker-compose.sqlite.yml up + docker compose -f init/container/docker-compose.sqlite.yml up PyCA + PostgreSQL ------------- @@ -20,4 +20,4 @@ PyCA + PostgreSQL cp etc/pyca.conf init/container/pyca.conf sed -i "s|#name .*|name = pyca-container|g" init/container/pyca.conf sed -i "s|#database .*|database = postgresql://pyca:pyca@database/pyca|g" init/container/pyca.conf - docker-compose -f init/container/docker-compose.postgres.yml up + docker compose -f init/container/docker-compose.postgres.yml up diff --git a/init/container/docker-compose.postgres.yml b/init/container/docker-compose.postgres.yml index 63c4ffa2..5c478c0f 100644 --- a/init/container/docker-compose.postgres.yml +++ b/init/container/docker-compose.postgres.yml @@ -7,7 +7,7 @@ volumes: services: pyca-schedule: command: schedule - image: quay.io/opencast/pyca + image: ghcr.io/opencast/pyca restart: always volumes: - ./pyca.conf:/etc/pyca/pyca.conf:ro @@ -15,7 +15,7 @@ services: pyca-ingest: command: ingest - image: quay.io/opencast/pyca + image: ghcr.io/opencast/pyca restart: always volumes: - ./pyca.conf:/etc/pyca/pyca.conf:ro @@ -23,7 +23,7 @@ services: pyca-capture: command: capture - image: quay.io/opencast/pyca + image: ghcr.io/opencast/pyca restart: always volumes: - ./pyca.conf:/etc/pyca/pyca.conf:ro @@ -31,7 +31,7 @@ services: pyca-agentstate: command: agentstate - image: quay.io/opencast/pyca + image: ghcr.io/opencast/pyca restart: always volumes: - ./pyca.conf:/etc/pyca/pyca.conf:ro @@ -39,7 +39,7 @@ services: pyca-ui: entrypoint: ["gunicorn", "--config=/etc/pyca/gunicorn.conf.py", "pyca.ui:app"] - image: quay.io/opencast/pyca + image: ghcr.io/opencast/pyca restart: always volumes: - ./pyca.conf:/etc/pyca/pyca.conf:ro @@ -48,7 +48,7 @@ services: - "8000:8000" database: - image: postgres:12.3 + image: docker.io/library/postgres:latest restart: always environment: - POSTGRES_PASSWORD=pyca diff --git a/init/container/docker-compose.sqlite.yml b/init/container/docker-compose.sqlite.yml index 1052ab7c..af0819cc 100644 --- a/init/container/docker-compose.sqlite.yml +++ b/init/container/docker-compose.sqlite.yml @@ -6,7 +6,7 @@ volumes: services: pyca-schedule: command: schedule - image: quay.io/opencast/pyca + image: ghcr.io/opencast/pyca restart: always volumes: - ./pyca.conf:/etc/pyca/pyca.conf:ro @@ -14,7 +14,7 @@ services: pyca-ingest: command: ingest - image: quay.io/opencast/pyca + image: ghcr.io/opencast/pyca restart: always volumes: - ./pyca.conf:/etc/pyca/pyca.conf:ro @@ -22,7 +22,7 @@ services: pyca-capture: command: capture - image: quay.io/opencast/pyca + image: ghcr.io/opencast/pyca restart: always volumes: - ./pyca.conf:/etc/pyca/pyca.conf:ro @@ -30,7 +30,7 @@ services: pyca-agentstate: command: agentstate - image: quay.io/opencast/pyca + image: ghcr.io/opencast/pyca restart: always volumes: - ./pyca.conf:/etc/pyca/pyca.conf:ro @@ -38,7 +38,7 @@ services: pyca-ui: entrypoint: ["gunicorn", "--config=/etc/pyca/gunicorn.conf.py", "pyca.ui:app"] - image: quay.io/opencast/pyca + image: ghcr.io/opencast/pyca restart: always volumes: - ./pyca.conf:/etc/pyca/pyca.conf:ro