name: Build Docker Images

# Run this job on all non-pull-request events,
# or if Docker-related files are changed in a pull request.
on:
  push:
    branches:
      - "main"
      - "v*"
    tags:
      - "v*"
  pull_request:
    paths:
      - "docker/Dockerfile"
      - ".github/workflows/docker.yaml"
    branches:
      - "main"
      - "v*"

permissions:
  contents: read
  packages: write

jobs:
  # --------------------------------
  # 1. BUILD & TEST
  # --------------------------------
  build-and-test-rp:
    strategy:
      matrix:
        arch: [amd64, arm64]
    runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
    steps:
      - name: Checkout
        uses: actions/checkout@v5
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Build (no push) and Load
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          file: docker/Dockerfile
          # no pushing here, so we can test locally
          push: false
          # load the built image into the local Docker daemon on the runner
          load: true
          target: rosenpass
          tags: rosenpass:test
          platforms: linux/${{ matrix.arch }}
      - name: Integration Test - Standalone Key Exchange
        run: |
          # Create separate workdirs
          mkdir -p workdir-server workdir-client

          # Create a Docker network
          docker network create -d bridge rp

          echo "=== GENERATE SERVER KEYS ==="
          docker run --rm \
            -v $PWD/workdir-server:/workdir \
            rosenpass:test gen-keys \
              --public-key=workdir/server-public \
              --secret-key=workdir/server-secret

          echo "=== GENERATE CLIENT KEYS ==="
          docker run --rm \
            -v $PWD/workdir-client:/workdir \
            rosenpass:test gen-keys \
              --public-key=workdir/client-public \
              --secret-key=workdir/client-secret

          echo "=== SHARE PUBLIC KEYS ==="
          cp workdir-client/client-public workdir-server/client-public
          cp workdir-server/server-public workdir-client/server-public

          echo "=== START SERVER CONTAINER ==="
          docker run -d --rm \
            --name rpserver \
            --network rp \
            -v $PWD/workdir-server:/workdir \
            rosenpass:test exchange \
              private-key workdir/server-secret \
              public-key  workdir/server-public \
              listen 0.0.0.0:9999 \
              peer public-key workdir/client-public \
              outfile workdir/server-sharedkey

          # Get the container IP of the server
          SERVER_IP=$(docker inspect --format='{{.NetworkSettings.Networks.rp.IPAddress}}' rpserver)
          echo "SERVER_IP=$SERVER_IP"

          echo "=== START CLIENT CONTAINER ==="
          docker run -d --rm \
            --name rpclient \
            --network rp \
            -v $PWD/workdir-client:/workdir \
            rosenpass:test exchange \
              private-key workdir/client-secret \
              public-key  workdir/client-public \
              peer public-key workdir/server-public \
              endpoint ${SERVER_IP}:9999 \
              outfile workdir/client-sharedkey

          echo "=== COMPARE SHARED KEYS ==="
          echo "Waiting up to 30 seconds for the server to generate 'server-sharedkey'..."
          for i in $(seq 1 30); do
            if [ -f "workdir-server/server-sharedkey" ]; then
              echo "server-sharedkey found!"
              break
            fi
            sleep 1
          done
          sudo cmp workdir-server/server-sharedkey workdir-client/client-sharedkey

          echo "Standalone Key Exchange test OK."
  # --------------------------------
  # 2. PUSH (only if tests pass)
  # --------------------------------
  docker-image-rp:
    needs:
      - build-and-test-rp
    # Skip if this is not a PR. Then we want to push this image.
    if: ${{ github.event_name != 'pull_request' }}
    # Use a matrix to build for both AMD64 and ARM64
    strategy:
      matrix:
        arch: [amd64, arm64]
    # Switch the runner based on the architecture
    runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
    steps:
      - name: Checkout
        uses: actions/checkout@v5

      - name: Docker meta
        id: meta

        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository_owner }}/rp
          labels: |
            maintainer=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
            org.opencontainers.image.authors=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
            org.opencontainers.image.title=Rosenpass
            org.opencontainers.image.description=The rp command-line integrates Rosenpass and WireGuard to help you create a VPN
            org.opencontainers.image.vendor=Rosenpass e.V.
            org.opencontainers.image.licenses=MIT OR Apache-2.0
            org.opencontainers.image.url=https://rosenpass.eu
            org.opencontainers.image.documentation=https://rosenpass.eu/docs/
            org.opencontainers.image.source=https://github.com/rosenpass/rosenpass

      - name: Log in to registry
        run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build and push by digest
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          file: docker/Dockerfile
          push: ${{ github.event_name != 'pull_request' }}
          labels: ${{ steps.meta.outputs.labels }}
          tags: ghcr.io/${{ github.repository_owner }}/rp
          target: rp
          platforms: linux/${{ matrix.arch }}
          outputs: type=image,push-by-digest=true,name-canonical=true,push=true

      - name: Export digest
        run: |
          mkdir -p ${{ runner.temp }}/digests
          digest="${{ steps.build.outputs.digest }}"
          touch "${{ runner.temp }}/digests/${digest#sha256:}"

      - name: Upload digest
        uses: actions/upload-artifact@v5
        with:
          name: digests-rp-${{ matrix.arch }}
          path: ${{ runner.temp }}/digests/*
          if-no-files-found: error
          retention-days: 1

  docker-image-rosenpass:
    needs:
      - build-and-test-rp
    # Skip if this is not a PR. Then we want to push this image.
    if: ${{ github.event_name != 'pull_request' }}
    # Use a matrix to build for both AMD64 and ARM64
    strategy:
      matrix:
        arch: [amd64, arm64]
    # Switch the runner based on the architecture
    runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
    steps:
      - name: Checkout
        uses: actions/checkout@v5

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository_owner }}/rosenpass
          labels: |
            maintainer=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
            org.opencontainers.image.authors=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
            org.opencontainers.image.title=Rosenpass
            org.opencontainers.image.description=Reference implementation of the protocol rosenpass protocol
            org.opencontainers.image.vendor=Rosenpass e.V.
            org.opencontainers.image.licenses=MIT OR Apache-2.0
            org.opencontainers.image.url=https://rosenpass.eu
            org.opencontainers.image.documentation=https://rosenpass.eu/docs/
            org.opencontainers.image.source=https://github.com/rosenpass/rosenpass

      - name: Log in to registry
        run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build and push by digest
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          file: docker/Dockerfile
          push: ${{ github.event_name != 'pull_request' }}
          labels: ${{ steps.meta.outputs.labels }}
          tags: ghcr.io/${{ github.repository_owner }}/rosenpass
          target: rosenpass
          platforms: linux/${{ matrix.arch }}
          outputs: type=image,push-by-digest=true,name-canonical=true,push=true

      - name: Export digest
        run: |
          mkdir -p ${{ runner.temp }}/digests
          digest="${{ steps.build.outputs.digest }}"
          touch "${{ runner.temp }}/digests/${digest#sha256:}"

      - name: Upload digest
        uses: actions/upload-artifact@v5
        with:
          name: digests-rosenpass-${{ matrix.arch }}
          path: ${{ runner.temp }}/digests/*
          if-no-files-found: error
          retention-days: 1

  merge-digests:
    runs-on: ubuntu-latest
    needs:
      - docker-image-rosenpass
      - docker-image-rp
    if: ${{ github.event_name != 'pull_request' }}
    strategy:
      matrix:
        target: [rp, rosenpass]
    steps:
      - name: Download digests
        uses: actions/download-artifact@v5
        with:
          path: ${{ runner.temp }}/digests
          pattern: digests-${{ matrix.target }}-*
          merge-multiple: true

      - name: Log in to registry
        run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository_owner }}/${{ matrix.target }}
          tags: |
            type=edge,branch=main
            type=sha,branch=main
            type=semver,pattern={{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 'ghcr.io/${{ github.repository_owner }}/${{ matrix.target }}@sha256:%s ' *)

      - name: Inspect image
        run: |
          docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/${{ matrix.target }}:${{ steps.meta.outputs.version }}
