diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7fa682b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +.devcontainer/ +.github/ +.venv/ +.vscode/ +node_modules/ +website/ +.dockerignore +.editorconfig +.gitignore +.pre-commit-config.yaml +.prettier* +.yarnrc +Dockerfile +LICENSE +package.json +yarn.lock diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2195324..9288b5b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,3 +34,142 @@ jobs: gh release upload --clobber ${{ steps.version.outputs.TAG_NAME }} dist/*.tar.gz dist/*.whl env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + build: + runs-on: ubuntu-latest + needs: release + permissions: + contents: read + packages: write + strategy: + fail-fast: true + matrix: + python_version: + - 3.9 + - 3.10 + - 3.11 + python_variant: + - "" + - "-slim" + platform: + - linux/amd64 + - linux/arm64 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Setup Docker + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Generate Image Name and Scope + id: image + run: | + echo "IMAGE=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT + echo "SCOPE=${{ env.GITHUB_REF_NAME }}-${{ matrix.python_version }}${{ matrix.python_variant }}-${{ matrix.platform }}" >> $GITHUB_OUTPUT + + - name: Generate Labels + uses: docker/metadata-action@v5 + id: metadata + with: + images: ${{ steps.image.outputs.IMAGE }} + + - name: Build and Publish + uses: docker/build-push-action@v5 + id: build + with: + context: . + platforms: ${{ matrix.platform }} + build-args: | + PYTHON_IMAGE=${{ matrix.python_version }} + VARIANT=${{ matrix.python_variant }} + labels: ${{ steps.metadata.outputs.labels }} + cache-from: type=gha,scope=${{ steps.image.outputs.SCOPE }} + cache-to: type=gha,scope=${{ steps.image.outputs.SCOPE }},mode=max + outputs: type=image,name=${{ steps.image.outputs.IMAGE }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v3 + with: + name: digests + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + push: + runs-on: ubuntu-latest + needs: build + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download digests + uses: actions/download-artifact@v3 + with: + name: digests + path: /tmp/digests + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Login to Github Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate Image Name + id: image + run: | + echo "IMAGE=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT + + - name: Generate Tags + uses: docker/metadata-action@v5 + id: metadata + with: + images: | + ${{ github.repository }} + ghcr.io/${{ github.repository }} + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create --dry-run $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ steps.image.outputs.IMAGE }}@sha256:%s ' *) + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ steps.image.outputs.IMAGE }}@sha256:%s ' *) + + - name: Docker Hub Description + uses: peter-evans/dockerhub-description@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + short-description: ${{ github.event.repository.description }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3b8da90 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# syntax=docker/dockerfile:1 + +ARG PYTHON_IMAGE=3.11 +ARG VARIANT= + +# build stage +FROM python:${PYTHON_IMAGE}${VARIANT} AS build-stage + +RUN pip install pipx + +COPY . /project/ + +WORKDIR /project + +RUN mkdir __pypackages__ && \ + python -m pipx run --no-cache pdm sync --prod --no-editable + +# run stage +FROM python:${PYTHON_IMAGE}${VARIANT} + +ARG PYTHON_IMAGE + +ENV PYTHONPATH=/opt/nb-cli/pkgs + +COPY --from=build-stage /project/__pypackages__/${PYTHON_IMAGE}/lib /opt/nb-cli/pkgs +COPY --from=build-stage /project/__pypackages__/${PYTHON_IMAGE}/bin/* /bin/ + +WORKDIR /workspaces + +ENTRYPOINT ["nb"]