From f3a31b37aba3b51d186335f38fca27379237f249 Mon Sep 17 00:00:00 2001 From: rizlas Date: Fri, 10 Apr 2026 14:45:45 +0200 Subject: [PATCH 01/15] feat: build docker image via github actions Signed-off-by: rizlas --- .github/workflows/docker-image.yml | 58 ++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 00000000..3c3e3ea3 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,58 @@ +name: Docker Image CI + +on: + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + inputs: + tag: + description: "Version tag to use for the Docker image" + required: true + +jobs: + build-and-push: + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Log in to GitHub Packages + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Lower case docker image name + id: docker_image + uses: ASzc/change-string-case-action@v8 + with: + string: ${{ github.repository }} + + - name: Build and push latest + uses: docker/build-push-action@v7 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ghcr.io/${{ steps.docker_image.outputs.lowercase }}:latest + + - name: Build and push version tag + uses: docker/build-push-action@v7 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ghcr.io/${{ steps.docker_image.outputs.lowercase }}:${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} From 063e3cd7e9bc5c945d72167bc4cd67bab84f9623 Mon Sep 17 00:00:00 2001 From: rizlas Date: Fri, 10 Apr 2026 15:29:48 +0200 Subject: [PATCH 02/15] ci: add upstream tag support, GHA layer cache and conditional version tag push Signed-off-by: rizlas --- .github/workflows/docker-image.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 3c3e3ea3..a566084e 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -6,9 +6,9 @@ on: - "[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: inputs: - tag: - description: "Version tag to use for the Docker image" - required: true + upstream_tag: + description: "Upstream tag of nextcloud/notify_push to build" + required: false jobs: build-and-push: @@ -21,6 +21,9 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v6 + with: + repository: ${{ inputs.upstream_tag && 'nextcloud/notify_push' || github.repository }} + ref: ${{ inputs.upstream_tag || github.ref_name }} - name: Log in to GitHub Packages uses: docker/login-action@v4 @@ -48,11 +51,16 @@ jobs: push: true platforms: linux/amd64,linux/arm64 tags: ghcr.io/${{ steps.docker_image.outputs.lowercase }}:latest + cache-from: type=gha + cache-to: type=gha,mode=max - name: Build and push version tag + if: github.ref_type == 'tag' || inputs.upstream_tag != '' uses: docker/build-push-action@v7 with: context: . push: true platforms: linux/amd64,linux/arm64 - tags: ghcr.io/${{ steps.docker_image.outputs.lowercase }}:${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} + tags: ghcr.io/${{ steps.docker_image.outputs.lowercase }}:${{ inputs.upstream_tag || github.ref_name }} + cache-from: type=gha + cache-to: type=gha,mode=max From 8d9a98dd15c6487cd465ced27714687ec35dcb20 Mon Sep 17 00:00:00 2001 From: rizlas Date: Fri, 10 Apr 2026 15:55:11 +0200 Subject: [PATCH 03/15] ci: use metadata-action for tag management, strip v prefix from upstream_tag Signed-off-by: rizlas --- .github/workflows/docker-image.yml | 107 +++++++++++++++-------------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index a566084e..cbf0b1ec 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,66 +1,67 @@ name: Docker Image CI on: - push: - tags: - - "[0-9]+.[0-9]+.[0-9]+" - workflow_dispatch: - inputs: - upstream_tag: - description: "Upstream tag of nextcloud/notify_push to build" - required: false + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + inputs: + upstream_tag: + description: "Upstream tag of nextcloud/notify_push to build" + required: false jobs: - build-and-push: - runs-on: ubuntu-latest + build-and-push: + runs-on: ubuntu-latest - permissions: - contents: read - packages: write + permissions: + contents: read + packages: write - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - repository: ${{ inputs.upstream_tag && 'nextcloud/notify_push' || github.repository }} - ref: ${{ inputs.upstream_tag || github.ref_name }} + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + repository: ${{ inputs.upstream_tag && 'nextcloud/notify_push' || github.repository }} + ref: ${{ inputs.upstream_tag || github.ref_name }} - - name: Log in to GitHub Packages - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + - name: Log in to GitHub Packages + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Set up QEMU - uses: docker/setup-qemu-action@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 - - name: Lower case docker image name - id: docker_image - uses: ASzc/change-string-case-action@v8 - with: - string: ${{ github.repository }} + - name: Sanitize upstream tag + if: inputs.upstream_tag != '' + id: tag + run: echo "value=${INPUT_TAG#v}" >> $GITHUB_OUTPUT + env: + INPUT_TAG: ${{ inputs.upstream_tag }} - - name: Build and push latest - uses: docker/build-push-action@v7 - with: - context: . - push: true - platforms: linux/amd64,linux/arm64 - tags: ghcr.io/${{ steps.docker_image.outputs.lowercase }}:latest - cache-from: type=gha - cache-to: type=gha,mode=max + - name: Extract metadata + id: meta + uses: docker/metadata-action@v6 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=tag + type=raw,value=latest + type=raw,value=${{ steps.tag.outputs.value }},enable=${{ inputs.upstream_tag != '' }} - - name: Build and push version tag - if: github.ref_type == 'tag' || inputs.upstream_tag != '' - uses: docker/build-push-action@v7 - with: - context: . - push: true - platforms: linux/amd64,linux/arm64 - tags: ghcr.io/${{ steps.docker_image.outputs.lowercase }}:${{ inputs.upstream_tag || github.ref_name }} - cache-from: type=gha - cache-to: type=gha,mode=max + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max From 4c88a6c9610b44b51ca4108c5666649aebdfcc92 Mon Sep 17 00:00:00 2001 From: rizlas Date: Fri, 10 Apr 2026 16:29:26 +0200 Subject: [PATCH 04/15] ci: use native runners matrix to eliminate QEMU overhead Thanks to https://github.com/sredevopsorg/multi-arch-docker-github-workflow Signed-off-by: rizlas --- .github/workflows/docker-image.yml | 116 ++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 11 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index cbf0b1ec..f604c29f 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,3 +1,4 @@ +# Inspired by https://github.com/sredevopsorg/multi-arch-docker-github-workflow name: Docker Image CI on: @@ -11,13 +12,22 @@ on: required: false jobs: - build-and-push: - runs-on: ubuntu-latest + build: + runs-on: ${{ matrix.runner }} permissions: contents: read packages: write + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-24.04-arm + steps: - name: Checkout code uses: actions/checkout@v6 @@ -32,9 +42,6 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Set up QEMU - uses: docker/setup-qemu-action@v4 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 @@ -55,13 +62,100 @@ jobs: type=raw,value=latest type=raw,value=${{ steps.tag.outputs.value }},enable=${{ inputs.upstream_tag != '' }} - - name: Build and push + - name: Build and push by digest + id: build uses: docker/build-push-action@v7 with: context: . - push: true - platforms: linux/amd64,linux/arm64 - tags: ${{ steps.meta.outputs.tags }} + platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max + outputs: type=image,name=ghcr.io/${{ github.repository }},push-by-digest=true,name-canonical=true,push=true + cache-from: type=gha,scope=${{ matrix.platform }} + cache-to: type=gha,mode=max,scope=${{ matrix.platform }} + + - name: Export digest + run: | + mkdir -p /tmp/digests + touch "/tmp/digests/${DIGEST#sha256:}" + env: + DIGEST: ${{ steps.build.outputs.digest }} + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: build + + permissions: + contents: read + packages: write + + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: /tmp/digests + pattern: digests-* + merge-multiple: true + + - name: Log in to GitHub Packages + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Sanitize upstream tag + if: inputs.upstream_tag != '' + id: tag + run: echo "value=${INPUT_TAG#v}" >> $GITHUB_OUTPUT + env: + INPUT_TAG: ${{ inputs.upstream_tag }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v6 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=tag + type=raw,value=latest + type=raw,value=${{ steps.tag.outputs.value }},enable=${{ inputs.upstream_tag != '' }} + + - name: Get timestamp + id: timestamp + run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT + + - name: Create and push manifest + id: manifest + working-directory: /tmp/digests + continue-on-error: true + run: | + docker buildx imagetools create \ + $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + --annotation='index:org.opencontainers.image.description=${{ github.event.repository.description }}' \ + --annotation='index:org.opencontainers.image.created=${{ steps.timestamp.outputs.timestamp }}' \ + --annotation='index:org.opencontainers.image.url=${{ github.event.repository.url }}' \ + --annotation='index:org.opencontainers.image.source=${{ github.event.repository.url }}' \ + $(printf 'ghcr.io/${{ github.repository }}@sha256:%s ' *) + + - name: Create and push manifest (without annotations) + if: steps.manifest.outcome == 'failure' + working-directory: /tmp/digests + run: | + docker buildx imagetools create \ + $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf 'ghcr.io/${{ github.repository }}@sha256:%s ' *) + + - name: Inspect manifest + run: | + docker buildx imagetools inspect 'ghcr.io/${{ github.repository }}:${{ steps.meta.outputs.version }}' From 26343ff637cbc3d3766d2b3d4d06df7134689359 Mon Sep 17 00:00:00 2001 From: rizlas Date: Fri, 10 Apr 2026 16:37:56 +0200 Subject: [PATCH 05/15] ci: fix lower casing Signed-off-by: rizlas --- .github/workflows/docker-image.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index f604c29f..35967b83 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -45,6 +45,12 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 + - name: Lower case docker image name + id: image + uses: ASzc/change-string-case-action@v8 + with: + string: ${{ github.repository }} + - name: Sanitize upstream tag if: inputs.upstream_tag != '' id: tag @@ -69,7 +75,7 @@ jobs: context: . platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} - outputs: type=image,name=ghcr.io/${{ github.repository }},push-by-digest=true,name-canonical=true,push=true + outputs: type=image,name=ghcr.io/${{ steps.image.outputs.lowercase }},push-by-digest=true,name-canonical=true,push=true cache-from: type=gha,scope=${{ matrix.platform }} cache-to: type=gha,mode=max,scope=${{ matrix.platform }} @@ -114,6 +120,12 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 + - name: Lower case docker image name + id: image + uses: ASzc/change-string-case-action@v8 + with: + string: ${{ github.repository }} + - name: Sanitize upstream tag if: inputs.upstream_tag != '' id: tag @@ -146,7 +158,7 @@ jobs: --annotation='index:org.opencontainers.image.created=${{ steps.timestamp.outputs.timestamp }}' \ --annotation='index:org.opencontainers.image.url=${{ github.event.repository.url }}' \ --annotation='index:org.opencontainers.image.source=${{ github.event.repository.url }}' \ - $(printf 'ghcr.io/${{ github.repository }}@sha256:%s ' *) + $(printf 'ghcr.io/${{ steps.image.outputs.lowercase }}@sha256:%s ' *) - name: Create and push manifest (without annotations) if: steps.manifest.outcome == 'failure' @@ -154,8 +166,8 @@ jobs: run: | docker buildx imagetools create \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf 'ghcr.io/${{ github.repository }}@sha256:%s ' *) + $(printf 'ghcr.io/${{ steps.image.outputs.lowercase }}@sha256:%s ' *) - name: Inspect manifest run: | - docker buildx imagetools inspect 'ghcr.io/${{ github.repository }}:${{ steps.meta.outputs.version }}' + docker buildx imagetools inspect 'ghcr.io/${{ steps.image.outputs.lowercase }}:${{ steps.meta.outputs.version }}' From c834c2ce87edbb6318eefbd211a96a766948c263 Mon Sep 17 00:00:00 2001 From: rizlas Date: Fri, 10 Apr 2026 16:40:35 +0200 Subject: [PATCH 06/15] chore: indentation Signed-off-by: rizlas --- .github/workflows/docker-image.yml | 334 ++++++++++++++--------------- 1 file changed, 167 insertions(+), 167 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 35967b83..c234098b 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -2,172 +2,172 @@ name: Docker Image CI on: - push: - tags: - - "[0-9]+.[0-9]+.[0-9]+" - workflow_dispatch: - inputs: - upstream_tag: - description: "Upstream tag of nextcloud/notify_push to build" - required: false + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + inputs: + upstream_tag: + description: "Upstream tag of nextcloud/notify_push to build" + required: false jobs: - build: - runs-on: ${{ matrix.runner }} - - permissions: - contents: read - packages: write - - strategy: - fail-fast: false - matrix: - include: - - platform: linux/amd64 - runner: ubuntu-latest - - platform: linux/arm64 - runner: ubuntu-24.04-arm - - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - repository: ${{ inputs.upstream_tag && 'nextcloud/notify_push' || github.repository }} - ref: ${{ inputs.upstream_tag || github.ref_name }} - - - name: Log in to GitHub Packages - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - - name: Lower case docker image name - id: image - uses: ASzc/change-string-case-action@v8 - with: - string: ${{ github.repository }} - - - name: Sanitize upstream tag - if: inputs.upstream_tag != '' - id: tag - run: echo "value=${INPUT_TAG#v}" >> $GITHUB_OUTPUT - env: - INPUT_TAG: ${{ inputs.upstream_tag }} - - - name: Extract metadata - id: meta - uses: docker/metadata-action@v6 - with: - images: ghcr.io/${{ github.repository }} - tags: | - type=ref,event=tag - type=raw,value=latest - type=raw,value=${{ steps.tag.outputs.value }},enable=${{ inputs.upstream_tag != '' }} - - - name: Build and push by digest - id: build - uses: docker/build-push-action@v7 - with: - context: . - platforms: ${{ matrix.platform }} - labels: ${{ steps.meta.outputs.labels }} - outputs: type=image,name=ghcr.io/${{ steps.image.outputs.lowercase }},push-by-digest=true,name-canonical=true,push=true - cache-from: type=gha,scope=${{ matrix.platform }} - cache-to: type=gha,mode=max,scope=${{ matrix.platform }} - - - name: Export digest - run: | - mkdir -p /tmp/digests - touch "/tmp/digests/${DIGEST#sha256:}" - env: - DIGEST: ${{ steps.build.outputs.digest }} - - - name: Upload digest - uses: actions/upload-artifact@v4 - with: - name: digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }} - path: /tmp/digests/* - if-no-files-found: error - retention-days: 1 - - merge: - runs-on: ubuntu-latest - needs: build - - permissions: - contents: read - packages: write - - steps: - - name: Download digests - uses: actions/download-artifact@v4 - with: - path: /tmp/digests - pattern: digests-* - merge-multiple: true - - - name: Log in to GitHub Packages - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - - name: Lower case docker image name - id: image - uses: ASzc/change-string-case-action@v8 - with: - string: ${{ github.repository }} - - - name: Sanitize upstream tag - if: inputs.upstream_tag != '' - id: tag - run: echo "value=${INPUT_TAG#v}" >> $GITHUB_OUTPUT - env: - INPUT_TAG: ${{ inputs.upstream_tag }} - - - name: Extract metadata - id: meta - uses: docker/metadata-action@v6 - with: - images: ghcr.io/${{ github.repository }} - tags: | - type=ref,event=tag - type=raw,value=latest - type=raw,value=${{ steps.tag.outputs.value }},enable=${{ inputs.upstream_tag != '' }} - - - name: Get timestamp - id: timestamp - run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT - - - name: Create and push manifest - id: manifest - working-directory: /tmp/digests - continue-on-error: true - run: | - docker buildx imagetools create \ - $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - --annotation='index:org.opencontainers.image.description=${{ github.event.repository.description }}' \ - --annotation='index:org.opencontainers.image.created=${{ steps.timestamp.outputs.timestamp }}' \ - --annotation='index:org.opencontainers.image.url=${{ github.event.repository.url }}' \ - --annotation='index:org.opencontainers.image.source=${{ github.event.repository.url }}' \ - $(printf 'ghcr.io/${{ steps.image.outputs.lowercase }}@sha256:%s ' *) - - - name: Create and push manifest (without annotations) - if: steps.manifest.outcome == 'failure' - working-directory: /tmp/digests - run: | - docker buildx imagetools create \ - $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf 'ghcr.io/${{ steps.image.outputs.lowercase }}@sha256:%s ' *) - - - name: Inspect manifest - run: | - docker buildx imagetools inspect 'ghcr.io/${{ steps.image.outputs.lowercase }}:${{ steps.meta.outputs.version }}' + build: + runs-on: ${{ matrix.runner }} + + permissions: + contents: read + packages: write + + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-24.04-arm + + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + repository: ${{ inputs.upstream_tag && 'nextcloud/notify_push' || github.repository }} + ref: ${{ inputs.upstream_tag || github.ref_name }} + + - name: Log in to GitHub Packages + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Lower case docker image name + id: image + uses: ASzc/change-string-case-action@v8 + with: + string: ${{ github.repository }} + + - name: Sanitize upstream tag + if: inputs.upstream_tag != '' + id: tag + run: echo "value=${INPUT_TAG#v}" >> $GITHUB_OUTPUT + env: + INPUT_TAG: ${{ inputs.upstream_tag }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v6 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=tag + type=raw,value=latest + type=raw,value=${{ steps.tag.outputs.value }},enable=${{ inputs.upstream_tag != '' }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v7 + with: + context: . + platforms: ${{ matrix.platform }} + labels: ${{ steps.meta.outputs.labels }} + outputs: type=image,name=ghcr.io/${{ steps.image.outputs.lowercase }},push-by-digest=true,name-canonical=true,push=true + cache-from: type=gha,scope=${{ matrix.platform }} + cache-to: type=gha,mode=max,scope=${{ matrix.platform }} + + - name: Export digest + run: | + mkdir -p /tmp/digests + touch "/tmp/digests/${DIGEST#sha256:}" + env: + DIGEST: ${{ steps.build.outputs.digest }} + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: build + + permissions: + contents: read + packages: write + + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: /tmp/digests + pattern: digests-* + merge-multiple: true + + - name: Log in to GitHub Packages + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Lower case docker image name + id: image + uses: ASzc/change-string-case-action@v8 + with: + string: ${{ github.repository }} + + - name: Sanitize upstream tag + if: inputs.upstream_tag != '' + id: tag + run: echo "value=${INPUT_TAG#v}" >> $GITHUB_OUTPUT + env: + INPUT_TAG: ${{ inputs.upstream_tag }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v6 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=tag + type=raw,value=latest + type=raw,value=${{ steps.tag.outputs.value }},enable=${{ inputs.upstream_tag != '' }} + + - name: Get timestamp + id: timestamp + run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT + + - name: Create and push manifest + id: manifest + working-directory: /tmp/digests + continue-on-error: true + run: | + docker buildx imagetools create \ + $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + --annotation='index:org.opencontainers.image.description=${{ github.event.repository.description }}' \ + --annotation='index:org.opencontainers.image.created=${{ steps.timestamp.outputs.timestamp }}' \ + --annotation='index:org.opencontainers.image.url=${{ github.event.repository.url }}' \ + --annotation='index:org.opencontainers.image.source=${{ github.event.repository.url }}' \ + $(printf 'ghcr.io/${{ steps.image.outputs.lowercase }}@sha256:%s ' *) + + - name: Create and push manifest (without annotations) + if: steps.manifest.outcome == 'failure' + working-directory: /tmp/digests + run: | + docker buildx imagetools create \ + $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf 'ghcr.io/${{ steps.image.outputs.lowercase }}@sha256:%s ' *) + + - name: Inspect manifest + run: | + docker buildx imagetools inspect 'ghcr.io/${{ steps.image.outputs.lowercase }}:${{ steps.meta.outputs.version }}' From 79db3321a4df2d631b8ffec673fa77fe6134d618 Mon Sep 17 00:00:00 2001 From: rizlas Date: Fri, 10 Apr 2026 16:47:29 +0200 Subject: [PATCH 07/15] ci: actions update Signed-off-by: rizlas --- .github/workflows/docker-image.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index c234098b..637f09a4 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -87,7 +87,7 @@ jobs: DIGEST: ${{ steps.build.outputs.digest }} - name: Upload digest - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }} path: /tmp/digests/* @@ -104,7 +104,7 @@ jobs: steps: - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: /tmp/digests pattern: digests-* From 2550278a7f1e29e250852fc482928dd3a2fe2cfb Mon Sep 17 00:00:00 2001 From: rizlas Date: Fri, 10 Apr 2026 20:45:57 +0200 Subject: [PATCH 08/15] ci: fix tag naming pattern Signed-off-by: rizlas --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 637f09a4..bbd94e7d 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -4,7 +4,7 @@ name: Docker Image CI on: push: tags: - - "[0-9]+.[0-9]+.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: inputs: upstream_tag: From e863e8aa6b6991836aa417b66389fd95ab14a400 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 18 May 2026 15:48:23 +0200 Subject: [PATCH 09/15] fix: fix noticition deprecation warning Signed-off-by: Robin Appelman --- appinfo/info.xml | 4 ++-- lib/Listener.php | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 8a570c9d..3daa82b6 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -11,7 +11,7 @@ - 1.3.2 + 1.3.3 agpl Robin Appelman NotifyPush @@ -27,7 +27,7 @@ Once the app is installed, the push binary needs to be setup. You can either use https://github.com/nextcloud/notify_push/issues - + diff --git a/lib/Listener.php b/lib/Listener.php index a5526ae7..97b13eec 100644 --- a/lib/Listener.php +++ b/lib/Listener.php @@ -22,6 +22,7 @@ use OCP\Notification\IDismissableNotifier; use OCP\Notification\INotification; use OCP\Notification\INotifier; +use OCP\Notification\UnknownNotificationException; use OCP\Share\Events\ShareCreatedEvent; use OCP\Share\IShare; @@ -102,7 +103,7 @@ public function getName(): string { } public function prepare(INotification $notification, string $languageCode): INotification { - throw new \InvalidArgumentException(); + throw new UnknownNotificationException(); } public function dismissNotification(INotification $notification): void { From fac22515073af0720a1a5bbef054014918d4c55d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 18 May 2026 15:52:12 +0200 Subject: [PATCH 10/15] chore: psalm php version Signed-off-by: Robin Appelman --- psalm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psalm.xml b/psalm.xml index 55758e1b..7a444190 100644 --- a/psalm.xml +++ b/psalm.xml @@ -8,7 +8,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config https://getpsalm.org/schema/config" - phpVersion="8.0" + phpVersion="8.1" > From 9b36fc04415a03b83382bd72cb142c2add86619a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 4 Mar 2026 15:26:33 +0100 Subject: [PATCH 11/15] silence more innocent websocket errors fixes #676 Signed-off-by: Robin Appelman --- src/connection.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/connection.rs b/src/connection.rs index e4cf55ae..91bdf564 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -226,6 +226,7 @@ pub async fn handle_user_socket( // hack while warp only has opaque error types match formatted.as_str() { "WebSocket protocol error: Connection reset without closing handshake" + | "Broken pipe (os error 32)" | "IO error: Connection reset by peer (os error 104)" => { log::debug!("websocket error: {e:#}") } From 51b8e03d6a91de2bcead61cdedf69e55ab1c10cd Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 4 Mar 2026 15:36:34 +0100 Subject: [PATCH 12/15] fix: bind to ipv6 dualstack by default Signed-off-by: Robin Appelman --- src/config.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/config.rs b/src/config.rs index 1c0f2ec4..965b7dd1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -20,7 +20,7 @@ use sqlx::any::AnyConnectOptions; use std::convert::{TryFrom, TryInto}; use std::env::var; use std::fmt::{Debug, Display, Formatter}; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::net::{IpAddr, Ipv6Addr, SocketAddr}; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -183,9 +183,7 @@ impl TryFrom for Config { let bind = match config.socket { Some(socket) => Bind::Unix(socket, socket_permissions), None => { - let ip = config - .bind - .unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))); + let ip = config.bind.unwrap_or(IpAddr::V6(Ipv6Addr::UNSPECIFIED)); let port = config.port.unwrap_or(7867); Bind::Tcp((ip, port).into()) } @@ -194,9 +192,7 @@ impl TryFrom for Config { let metrics_bind = match (config.metrics_socket, config.metrics_port) { (Some(socket), _) => Some(Bind::Unix(socket, socket_permissions)), (None, Some(port)) => { - let ip = config - .bind - .unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))); + let ip = config.bind.unwrap_or(IpAddr::V6(Ipv6Addr::UNSPECIFIED)); Some(Bind::Tcp((ip, port).into())) } _ => None, From c44b8777c4c996b548dea93147c009eea140d6b8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 4 Mar 2026 15:37:22 +0100 Subject: [PATCH 13/15] fix: trim incomming credentials Signed-off-by: Robin Appelman --- src/connection.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/connection.rs b/src/connection.rs index 91bdf564..2d458e94 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -263,11 +263,13 @@ async fn socket_auth( let username_msg = read_socket_auth_message(rx).await?; let username = username_msg .to_str() - .map_err(|_| AuthenticationError::InvalidMessage)?; + .map_err(|_| AuthenticationError::InvalidMessage)? + .trim(); let password_msg = read_socket_auth_message(rx).await?; let password = password_msg .to_str() - .map_err(|_| AuthenticationError::InvalidMessage)?; + .map_err(|_| AuthenticationError::InvalidMessage)? + .trim(); // cleanup all pre_auth tokens older than 15s let cutoff = Instant::now() - Duration::from_secs(15); From 036b5157931a435c6353067b6da9d8e40b27c083 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 4 Mar 2026 15:39:36 +0100 Subject: [PATCH 14/15] chore: fix flake deprecation Signed-off-by: Robin Appelman --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index fb776f23..b4775e5a 100644 --- a/flake.nix +++ b/flake.nix @@ -27,7 +27,7 @@ }; lib = pkgs.lib; - hostTarget = pkgs.hostPlatform.config; + hostTarget = pkgs.stdenv.hostPlatform.config; targets = [ "x86_64-unknown-linux-musl" "i686-unknown-linux-musl" From 9484005d726ef052d977f5f7ada771dbf5feaff9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 6 Mar 2026 13:26:17 +0100 Subject: [PATCH 15/15] config parser 0.15.2 Signed-off-by: Robin Appelman --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33c740d6..7c3c02a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -621,7 +621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2068,7 +2068,7 @@ dependencies = [ "once_cell", "socket2 0.6.3", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -2341,7 +2341,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2423,7 +2423,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 26a56a78..da387087 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ rand = { version = "0.8.5", features = ["small_rng"] } ahash = "0.8.12" flexi_logger = { version = "0.31.8", features = ["colors"] } tokio-stream = { version = "0.1.17", features = ["net"] } -nextcloud-config-parser = "0.15.1" +nextcloud-config-parser = "0.15.2" url = "2.5.4" clap = { version = "4.5.43", features = ["derive"] } sd-notify = { version = "0.5.0", optional = true }