diff --git a/.github/workflows/build-base-image.yml b/.github/workflows/build-base-image.yml new file mode 100644 index 000000000..fa62a7d0e --- /dev/null +++ b/.github/workflows/build-base-image.yml @@ -0,0 +1,47 @@ +name: Build base image + +on: + workflow_dispatch: + inputs: + version: + description: "Version number" + required: true + type: string +env: + PYTHON_VERSION: "3.11" + +jobs: + build-base-image: + name: Builds and pushes the Music Assistant base container to ghcr.io + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - uses: actions/checkout@v4.1.4 + - name: Download Widevine CDM client files from private repository + shell: bash + env: + TOKEN: ${{ secrets.PRIVILEGED_GITHUB_TOKEN }} + run: | + mkdir -p widevine_cdm && cd widevine_cdm + curl -OJ -H "Authorization: token ${TOKEN}" https://raw.githubusercontent.com/music-assistant/appvars/main/widevine_cdm_client/private_key.pem + curl -OJ -H "Authorization: token ${TOKEN}" https://raw.githubusercontent.com/music-assistant/appvars/main/widevine_cdm_client/client_id.bin + - name: Log in to the GitHub container registry + uses: docker/login-action@v3.3.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3.6.1 + + - name: Build and Push image + uses: docker/build-push-action@v6.7.0 + with: + context: . + platforms: linux/amd64,linux/arm64 + file: Dockerfile.base + tags: |- + ghcr.io/${{ github.repository_owner }}/base:${{ inputs.version }}, + ghcr.io/${{ github.repository_owner }}/base:latest + push: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d88f519e6..9bec3e83f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,14 +3,11 @@ name: Publish releases on: release: types: [published] - workflow_dispatch: - inputs: - dev_version: - description: 'DEV Build version (postfix)' - required: true - type: string + env: PYTHON_VERSION: "3.11" + BASE_IMAGE_VERSION_STABLE: "1.0.0" + BASE_IMAGE_VERSION_BETA: "1.0.0" jobs: build-artifact: @@ -23,28 +20,20 @@ jobs: - name: Get tag id: vars run: >- - if [[ -n "${{ inputs.dev_version }}" ]]; then - echo "tag=${inputs.dev_version}.dev$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_OUTPUT - else - echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT - fi + echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT - name: Validate version number run: >- - if [[ -n "${{ inputs.dev_version }}" ]]; then - echo "Skipping version validation for dev version via manual trigger" - else - if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then - if ! [[ "${{ steps.vars.outputs.tag }}" =~ "b" || "${{ steps.vars.outputs.tag }}" =~ "rc" ]]; then - echo "Pre-release: Tag is missing beta suffix (${{ steps.vars.outputs.tag }})" - exit 1 - fi - else - if [[ "${{ steps.vars.outputs.tag }}" =~ "b" || "${{ steps.vars.outputs.tag }}" =~ "rc" ]]; then - echo "Release: Tag must not have a beta (or rc) suffix (${{ steps.vars.outputs.tag }})" - exit 1 - fi + if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then + if ! [[ "${{ steps.vars.outputs.tag }}" =~ "b" || "${{ steps.vars.outputs.tag }}" =~ "rc" ]]; then + echo "Pre-release: Tag is missing beta suffix (${{ steps.vars.outputs.tag }})" + exit 1 + fi + else + if [[ "${{ steps.vars.outputs.tag }}" =~ "b" || "${{ steps.vars.outputs.tag }}" =~ "rc" ]]; then + echo "Release: Tag must not have a beta (or rc) suffix (${{ steps.vars.outputs.tag }})" + exit 1 fi - fi + fi - name: Set up Python ${{ env.PYTHON_VERSION }} uses: actions/setup-python@v5.2.0 with: @@ -73,10 +62,11 @@ jobs: with: name: release-dists path: dist/ - + pypi-publish: + name: Publish release to PyPI (stable releases only) runs-on: ubuntu-latest - if: ${{ inputs.dev_version }} + if: ${{ !github.event.release.prerelease }} needs: build-artifact steps: - name: Retrieve release distributions @@ -97,24 +87,11 @@ jobs: runs-on: ubuntu-latest permissions: packages: write - needs: + needs: - build-artifact - pypi-publish - if: | - always() && - ((github.event.release.prerelease == false && needs.pypi-publish.result == 'success') || - (github.event.release.prerelease == true && needs.pypi-publish.result == 'skipped') || - (inputs.dev_version && needs.pypi-publish.result == 'skipped')) steps: - uses: actions/checkout@v4.1.4 - - name: Download Widevine CDM client files from private repository - shell: bash - env: - TOKEN: ${{ secrets.PRIVILEGED_GITHUB_TOKEN }} - run: | - mkdir -p widevine_cdm && cd widevine_cdm - curl -OJ -H "Authorization: token ${TOKEN}" https://raw.githubusercontent.com/music-assistant/appvars/main/widevine_cdm_client/private_key.pem - curl -OJ -H "Authorization: token ${TOKEN}" https://raw.githubusercontent.com/music-assistant/appvars/main/widevine_cdm_client/client_id.bin - name: Retrieve release distributions uses: actions/download-artifact@v4 with: @@ -138,7 +115,7 @@ jobs: echo "major=${patch%.*.*}" >> $GITHUB_OUTPUT - name: Build and Push release uses: docker/build-push-action@v6.7.0 - if: ${{ ! inputs.dev_version && github.event.release.prerelease == false }} + if: ${{ github.event.release.prerelease == false }} with: context: . platforms: linux/amd64,linux/arm64 @@ -150,10 +127,10 @@ jobs: ghcr.io/${{ github.repository_owner }}/server:stable, ghcr.io/${{ github.repository_owner }}/server:latest push: true - build-args: "MASS_VERSION=${{ needs.build-artifact.outputs.version }}" + build-args: "MASS_VERSION=${{ needs.build-artifact.outputs.version }},BASE_IMAGE_VERSION=${{env.BASE_IMAGE_VERSION_STABLE}}" - name: Build and Push pre-release uses: docker/build-push-action@v6.7.0 - if: ${{ ! inputs.dev_version && github.event.release.prerelease == true }} + if: ${{ github.event.release.prerelease == true }} with: context: . platforms: linux/amd64,linux/arm64 @@ -162,20 +139,7 @@ jobs: ghcr.io/${{ github.repository_owner }}/server:${{ steps.tags.outputs.patch }}, ghcr.io/${{ github.repository_owner }}/server:beta push: true - build-args: "MASS_VERSION=${{ needs.build-artifact.outputs.version }}" - - name: Build and Push dev-release - uses: docker/build-push-action@v6.7.0 - if: inputs.dev_version - with: - context: . - platforms: linux/amd64,linux/arm64 - file: Dockerfile - tags: |- - ghcr.io/${{ github.repository_owner }}/server:${{ needs.build-artifact.outputs.version }}, - ghcr.io/${{ github.repository_owner }}/server:dev - push: true - build-args: | - MASS_VERSION=${{ needs.build-artifact.outputs.version }} + build-args: "MASS_VERSION=${{ needs.build-artifact.outputs.version }},BASE_IMAGE_VERSION=${{env.BASE_IMAGE_VERSION_BETA}}" release-notes-update: name: Updates the release notes and changelog @@ -193,7 +157,7 @@ jobs: name: Updates the Addon repository with the new version needs: [ - build-artifact, + build-artifact, pypi-publish, build-and-push-container-image, release-notes-update, diff --git a/Dockerfile b/Dockerfile index 0fde41096..a180546e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,48 +1,23 @@ # syntax=docker/dockerfile:1 -FROM python:3.12-alpine3.20 +# FINAL docker image for music assistant server +# This image is based on the base image and installs +# the music assistant server from our built wheel on top. -ARG MASS_VERSION -ARG TARGETPLATFORM - -RUN set -x \ - && apk add --no-cache \ - ca-certificates \ - jemalloc \ - curl \ - git \ - wget \ - tzdata \ - sox \ - cifs-utils \ - # install ffmpeg from community repo - && apk add --no-cache ffmpeg --repository=https://dl-cdn.alpinelinux.org/alpine/v3.20/community \ - # install snapcast from community repo - && apk add --no-cache snapcast --repository=https://dl-cdn.alpinelinux.org/alpine/v3.20/community \ - # install libnfs from community repo - && apk add --no-cache libnfs --repository=https://dl-cdn.alpinelinux.org/alpine/v3.20/community \ - # install openssl-dev (needed for airplay) - && apk add --no-cache openssl-dev +ARG BASE_IMAGE_VERSION = "latest" -# Copy widevine client files to container -RUN mkdir -p /usr/local/bin/widevine_cdm -COPY widevine_cdm/* /usr/local/bin/widevine_cdm/ +FROM ghcr.io/music-assistant/base:${BASE_IMAGE_VERSION} -# Upgrade pip + Install uv -RUN pip install --upgrade pip \ - && pip install uv==0.2.27 +ARG MASS_VERSION +ARG TARGETPLATFORM -# Install Music Assistant from published wheel on PyPi +# Install Music Assistant from prebuilt wheel RUN uv pip install \ --system \ --no-cache \ --find-links "https://wheels.home-assistant.io/musllinux/" \ dist/music_assistant-${MASS_VERSION}-py3-none-any.whl -# Configure runtime environmental variables -ENV LD_PRELOAD="/usr/lib/libjemalloc.so.2" -ENV UV_SYSTEM_PYTHON="1" - # Set some labels LABEL \ org.opencontainers.image.title="Music Assistant Server" \ diff --git a/Dockerfile.base b/Dockerfile.base new file mode 100644 index 000000000..4f8e698b1 --- /dev/null +++ b/Dockerfile.base @@ -0,0 +1,45 @@ +# syntax=docker/dockerfile:1 + +# BASE docker image for music assistant container + +FROM python:3.12-alpine3.20 + +ARG TARGETPLATFORM + +RUN set -x \ + && apk add --no-cache \ + ca-certificates \ + jemalloc \ + curl \ + git \ + wget \ + tzdata \ + sox \ + cifs-utils \ + # install ffmpeg from community repo + && apk add --no-cache ffmpeg --repository=https://dl-cdn.alpinelinux.org/alpine/v3.20/community \ + # install snapcast from community repo + && apk add --no-cache snapcast --repository=https://dl-cdn.alpinelinux.org/alpine/v3.20/community \ + # install libnfs from community repo + && apk add --no-cache libnfs --repository=https://dl-cdn.alpinelinux.org/alpine/v3.20/community \ + # install openssl-dev (needed for airplay) + && apk add --no-cache openssl-dev + +# Copy widevine client files to container +RUN mkdir -p /usr/local/bin/widevine_cdm +COPY widevine_cdm/* /usr/local/bin/widevine_cdm/ + +# Upgrade pip + Install uv +RUN pip install --upgrade pip \ + && pip install uv==0.2.27 + +# Configure runtime environmental variables +ENV LD_PRELOAD="/usr/lib/libjemalloc.so.2" +ENV UV_SYSTEM_PYTHON="1" + +LABEL \ + org.opencontainers.image.title="Music Assistant Base Image" \ + org.opencontainers.image.description="Base Image for Music Assistant server - not to be used directly" \ + org.opencontainers.image.source="https://github.com/music-assistant/server" \ + org.opencontainers.image.authors="The Music Assistant Team" \ + org.opencontainers.image.licenses="Apache License 2.0"