From e340d34dcddc612503b8b57ea82b64bac07ec8f6 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 10:12:41 -0300 Subject: [PATCH 01/16] fix(release): escape GitHub Actions env on stable34 Signed-off-by: Vitor Mattos --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a5e16b10a3..1ccab638d9 100644 --- a/Makefile +++ b/Makefile @@ -142,7 +142,7 @@ appstore: verify-release-metadata cp tests/php/fixtures/pdfs/small_valid.pdf $(appstore_sign_dir)/$(app_name)/tests/php/fixtures mkdir -p $(cert_dir) - if [ -f $(cert_dir)/$(app_name).key ] && [ "${GITHUB_ACTIONS:-}" = "true" ]; then \ + if [ -f $(cert_dir)/$(app_name).key ] && [ "$${GITHUB_ACTIONS:-}" = "true" ]; then \ set -e; \ echo "⌛️ Starting Nextcloud setup..."; \ mkdir $(CURDIR)/../nextcloud/data; \ From 97d5c709f3f2eb3b69111632fdb657f2b2fcb985 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 10:12:47 -0300 Subject: [PATCH 02/16] fix(release): recover with current packaging logic on stable34 Signed-off-by: Vitor Mattos --- .github/workflows/appstore-build-publish.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/appstore-build-publish.yml index 6f14d1fa2a..e21b407097 100644 --- a/.github/workflows/appstore-build-publish.yml +++ b/.github/workflows/appstore-build-publish.yml @@ -164,6 +164,16 @@ jobs: repository: nextcloud/server path: nextcloud + - name: Restore current packaging Makefile for manual recovery + if: github.event_name == 'workflow_dispatch' && steps.krankerl.outputs.files_exists != 'true' + env: + RECOVERY_WORKFLOW_SHA: ${{ github.sha }} + run: | + set -euo pipefail + cd ${{ env.APP_NAME }} + git fetch --quiet --depth=1 origin "${RECOVERY_WORKFLOW_SHA}" + git show "${RECOVERY_WORKFLOW_SHA}:Makefile" > Makefile + - name: Package and sign with Makefile if: steps.krankerl.outputs.files_exists != 'true' env: From 657356d46ab2f5bb608686698243749ee5e10497 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 10:20:31 -0300 Subject: [PATCH 03/16] ci: refresh appstore workflow patch Signed-off-by: Vitor Mattos --- .../appstore-build-publish.yml.patch | 191 ++++++++++++------ 1 file changed, 125 insertions(+), 66 deletions(-) diff --git a/.github/workflows/appstore-build-publish.yml.patch b/.github/workflows/appstore-build-publish.yml.patch index e84388af3f..0559b49197 100644 --- a/.github/workflows/appstore-build-publish.yml.patch +++ b/.github/workflows/appstore-build-publish.yml.patch @@ -1,89 +1,118 @@ diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/appstore-build-publish.yml --- a/.github/workflows/appstore-build-publish.yml +++ b/.github/workflows/appstore-build-publish.yml -@@ -28,6 +28,7 @@ +@@ -11,6 +11,12 @@ + on: + release: + types: [published] ++ workflow_dispatch: ++ inputs: ++ release_tag: ++ description: Existing release tag to build and publish, e.g. v13.4.3 ++ required: true ++ type: string + + permissions: + contents: write +@@ -29,25 +35,25 @@ + run: | + # Split and keep last + echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV ++ echo "APP_VERSION=${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref_name }}" >> $GITHUB_ENV +- echo "APP_VERSION=${GITHUB_REF##*/}" >> $GITHUB_ENV + + - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false + submodules: true ++ fetch-tags: true ++ ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.event.release.tag_name }} path: ${{ env.APP_NAME }} -@@ -132,12 +133,41 @@ + ++ - name: Validate release identity ++ id: release-identity ++ uses: LibreCodeCoop/release-tool/actions/release-identity@385ca7732db12e5c79590bb21be8da3608194595 +- - name: Get app version number +- id: app-version +- uses: skjnldsv/xpath-action@f5b036e9d973f42c86324833fd00be90665fbf77 # v1.0.0 + with: ++ tag: ${{ env.APP_VERSION }} ++ working-directory: ${{ env.APP_NAME }} ++ require-tag-exists: 'true' +- filename: ${{ env.APP_NAME }}/appinfo/info.xml +- expression: "//info//version/text()" + +- - name: Validate app version against tag +- run: | +- [ "${{ env.APP_VERSION }}" = "v${{ fromJSON(steps.app-version.outputs.result).version }}" ] +- + - name: Get appinfo data + id: appinfo + uses: skjnldsv/xpath-action@f5b036e9d973f42c86324833fd00be90665fbf77 # v1.0.0 +@@ -134,20 +140,6 @@ cd ${{ env.APP_NAME }} krankerl package - - name: Package ${{ env.APP_NAME }} ${{ env.APP_VERSION }} with makefile -+ - name: Check server download link for ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} -+ run: | -+ NCVERSION='${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }}' -+ DOWNLOAD_URL=$(curl -s "https://updates.nextcloud.com/updater_server/latest?channel=beta&version=$NCVERSION" | jq -r '.downloads.zip[0]') -+ echo "DOWNLOAD_URL=$DOWNLOAD_URL" >> $GITHUB_ENV -+ -+ - name: Download server ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} -+ continue-on-error: true -+ id: server-download -+ if: ${{ env.DOWNLOAD_URL != 'null' }} +- if: steps.krankerl.outputs.files_exists != 'true' +- run: | +- cd ${{ env.APP_NAME }} +- make appstore +- +- - name: Verify app store package +- if: steps.krankerl.outputs.files_exists != 'true' +- working-directory: ${{ env.APP_NAME }} +- run: | +- if make -qp 2>/dev/null | grep -q '^verify-appstore-package:'; then +- make verify-appstore-package +- fi +- + - name: Check server download link for ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} + run: | + NCVERSION='${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }}' +@@ -172,28 +164,50 @@ + repository: nextcloud/server + path: nextcloud + ++ - name: Restore current packaging Makefile for manual recovery ++ if: github.event_name == 'workflow_dispatch' && steps.krankerl.outputs.files_exists != 'true' ++ env: ++ RECOVERY_WORKFLOW_SHA: ${{ github.sha }} + run: | -+ echo "Downloading release tarball from $DOWNLOAD_URL" -+ wget $DOWNLOAD_URL -O nextcloud.zip -+ unzip nextcloud.zip -+ -+ - name: Checkout server master fallback -+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 -+ if: ${{ steps.server-download.outcome != 'success' }} -+ with: -+ persist-credentials: false -+ submodules: true -+ repository: nextcloud/server -+ path: nextcloud -+ ++ set -euo pipefail ++ cd ${{ env.APP_NAME }} ++ git fetch --quiet --depth=1 origin "${RECOVERY_WORKFLOW_SHA}" ++ git show "${RECOVERY_WORKFLOW_SHA}:Makefile" > Makefile + + - name: Package and sign with Makefile - if: steps.krankerl.outputs.files_exists != 'true' ++ if: steps.krankerl.outputs.files_exists != 'true' + env: + APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }} - run: | - cd ${{ env.APP_NAME }} ++ run: | ++ cd ${{ env.APP_NAME }} + mkdir -p build/tools/certificates + printf '%s' "$APP_PRIVATE_KEY" > "build/tools/certificates/${APP_NAME}.key" - make appstore - - - name: Verify app store package - if: steps.krankerl.outputs.files_exists != 'true' - working-directory: ${{ env.APP_NAME }} ++ make appstore ++ ++ - name: Verify app store package ++ if: steps.krankerl.outputs.files_exists != 'true' ++ working-directory: ${{ env.APP_NAME }} + env: + REQUIRE_SETUP_SIGNATURES: 'true' - run: | - if make -qp 2>/dev/null | grep -q '^verify-appstore-package:'; then - make verify-appstore-package -@@ -145,40 +175,6 @@ - fi - -- - name: Check server download link for ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} -- run: | -- NCVERSION='${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }}' -- DOWNLOAD_URL=$(curl -s "https://updates.nextcloud.com/updater_server/latest?channel=beta&version=$NCVERSION" | jq -r '.downloads.zip[0]') -- echo "DOWNLOAD_URL=$DOWNLOAD_URL" >> $GITHUB_ENV -- -- - name: Download server ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} -- continue-on-error: true -- id: server-download -- if: ${{ env.DOWNLOAD_URL != 'null' }} -- run: | -- echo "Downloading release tarball from $DOWNLOAD_URL" -- wget $DOWNLOAD_URL -O nextcloud.zip -- unzip nextcloud.zip -- -- - name: Checkout server master fallback -- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 -- if: ${{ steps.server-download.outcome != 'success' }} -- with: -- persist-credentials: false -- submodules: true -- repository: nextcloud/server -- path: nextcloud -- -- - - name: Sign app -- run: | + run: | ++ if make -qp 2>/dev/null | grep -q '^verify-appstore-package:'; then ++ make verify-appstore-package ++ fi ++ ++ - name: Set up PHP 8.3 for release-tool ++ uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 ++ with: ++ php-version: '8.3' ++ coverage: none ++ env: ++ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Extracting release - cd ${{ env.APP_NAME }}/build/artifacts - tar -xvf ${{ env.APP_NAME }}.tar.gz @@ -96,5 +125,35 @@ diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/ap - # Rebuilding archive - cd ${{ env.APP_NAME }}/build/artifacts - tar -zcvf ${{ env.APP_NAME }}.tar.gz ${{ env.APP_NAME }} -- + - name: Validate release artifact ++ uses: LibreCodeCoop/release-tool/actions/artifact-validate@385ca7732db12e5c79590bb21be8da3608194595 +- uses: LibreCodeCoop/release-tool/actions/artifact-validate@710c4c83fba47bf01713f46e9c4cb4cf63debfba + with: + artifact: ${{ env.APP_NAME }}/build/artifacts/${{ env.APP_NAME }}.tar.gz + app-name: ${{ env.APP_NAME }} ++ version: ${{ steps.release-identity.outputs.version }} +- version: ${{ env.APP_VERSION }} + + - name: Attach tarball to github release + uses: svenstaro/upload-release-action@29e53e917877a24fad85510ded594ab3c9ca12de # 2.11.5 +@@ -202,7 +216,7 @@ + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ${{ env.APP_NAME }}/build/artifacts/${{ env.APP_NAME }}.tar.gz + asset_name: ${{ env.APP_NAME }}-${{ env.APP_VERSION }}.tar.gz ++ tag: ${{ env.APP_VERSION }} +- tag: ${{ github.ref }} + overwrite: true + + - name: Upload app to Nextcloud appstore +@@ -214,8 +228,8 @@ + app_private_key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Verify App Store publication ++ uses: LibreCodeCoop/release-tool/actions/appstore-publication-wait@385ca7732db12e5c79590bb21be8da3608194595 +- uses: LibreCodeCoop/release-tool/actions/appstore-publication-wait@710c4c83fba47bf01713f46e9c4cb4cf63debfba + with: + app-name: ${{ env.APP_NAME }} ++ version: ${{ steps.release-identity.outputs.version }} +- version: ${{ env.APP_VERSION }} + platform: ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} From 8770c39ae7de1450e6824fb4f756febeba7f144d Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 10:27:20 -0300 Subject: [PATCH 04/16] fix(release): constrain manual recovery to release line Signed-off-by: Vitor Mattos --- .github/workflows/appstore-build-publish.yml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/appstore-build-publish.yml index e21b407097..1cf7238cfb 100644 --- a/.github/workflows/appstore-build-publish.yml +++ b/.github/workflows/appstore-build-publish.yml @@ -43,6 +43,7 @@ jobs: persist-credentials: false submodules: true fetch-tags: true + fetch-depth: 0 ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.event.release.tag_name }} path: ${{ env.APP_NAME }} @@ -168,10 +169,27 @@ jobs: if: github.event_name == 'workflow_dispatch' && steps.krankerl.outputs.files_exists != 'true' env: RECOVERY_WORKFLOW_SHA: ${{ github.sha }} + RELEASE_TAG: ${{ env.APP_VERSION }} run: | set -euo pipefail + + case "${GITHUB_REF_NAME}" in + stable*) ;; + *) + echo "::error::Manual release recovery must be dispatched from a stable branch" + exit 1 + ;; + esac + cd ${{ env.APP_NAME }} - git fetch --quiet --depth=1 origin "${RECOVERY_WORKFLOW_SHA}" + git fetch --quiet origin "${RECOVERY_WORKFLOW_SHA}" + + tag_sha="$(git rev-parse "refs/tags/${RELEASE_TAG}^{commit}")" + if ! git merge-base --is-ancestor "${tag_sha}" "${RECOVERY_WORKFLOW_SHA}"; then + echo "::error::${GITHUB_REF_NAME} does not contain ${RELEASE_TAG}; refusing to mix packaging logic across release lines" + exit 1 + fi + git show "${RECOVERY_WORKFLOW_SHA}:Makefile" > Makefile - name: Package and sign with Makefile From b32c312da1e2b590d0c3b3430e879af2024a6d25 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 10:28:08 -0300 Subject: [PATCH 05/16] ci: refresh appstore workflow patch after recovery hardening Signed-off-by: Vitor Mattos --- .../appstore-build-publish.yml.patch | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/.github/workflows/appstore-build-publish.yml.patch b/.github/workflows/appstore-build-publish.yml.patch index 0559b49197..e6537d7650 100644 --- a/.github/workflows/appstore-build-publish.yml.patch +++ b/.github/workflows/appstore-build-publish.yml.patch @@ -14,7 +14,7 @@ diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/ap permissions: contents: write -@@ -29,25 +35,25 @@ +@@ -29,25 +35,26 @@ run: | # Split and keep last echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV @@ -27,6 +27,7 @@ diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/ap persist-credentials: false + submodules: true + fetch-tags: true ++ fetch-depth: 0 + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.event.release.tag_name }} path: ${{ env.APP_NAME }} @@ -50,7 +51,7 @@ diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/ap - name: Get appinfo data id: appinfo uses: skjnldsv/xpath-action@f5b036e9d973f42c86324833fd00be90665fbf77 # v1.0.0 -@@ -134,20 +140,6 @@ +@@ -134,20 +141,6 @@ cd ${{ env.APP_NAME }} krankerl package @@ -71,7 +72,7 @@ diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/ap - name: Check server download link for ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} run: | NCVERSION='${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }}' -@@ -172,28 +164,50 @@ +@@ -172,28 +165,67 @@ repository: nextcloud/server path: nextcloud @@ -79,12 +80,29 @@ diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/ap + if: github.event_name == 'workflow_dispatch' && steps.krankerl.outputs.files_exists != 'true' + env: + RECOVERY_WORKFLOW_SHA: ${{ github.sha }} ++ RELEASE_TAG: ${{ env.APP_VERSION }} + run: | + set -euo pipefail + ++ case "${GITHUB_REF_NAME}" in ++ stable*) ;; ++ *) ++ echo "::error::Manual release recovery must be dispatched from a stable branch" ++ exit 1 ++ ;; ++ esac ++ + cd ${{ env.APP_NAME }} -+ git fetch --quiet --depth=1 origin "${RECOVERY_WORKFLOW_SHA}" ++ git fetch --quiet origin "${RECOVERY_WORKFLOW_SHA}" ++ ++ tag_sha="$(git rev-parse "refs/tags/${RELEASE_TAG}^{commit}")" ++ if ! git merge-base --is-ancestor "${tag_sha}" "${RECOVERY_WORKFLOW_SHA}"; then ++ echo "::error::${GITHUB_REF_NAME} does not contain ${RELEASE_TAG}; refusing to mix packaging logic across release lines" ++ exit 1 ++ fi ++ + git show "${RECOVERY_WORKFLOW_SHA}:Makefile" > Makefile - ++ + - name: Package and sign with Makefile + if: steps.krankerl.outputs.files_exists != 'true' + env: @@ -137,7 +155,7 @@ diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/ap - name: Attach tarball to github release uses: svenstaro/upload-release-action@29e53e917877a24fad85510ded594ab3c9ca12de # 2.11.5 -@@ -202,7 +216,7 @@ +@@ -202,7 +234,7 @@ repo_token: ${{ secrets.GITHUB_TOKEN }} file: ${{ env.APP_NAME }}/build/artifacts/${{ env.APP_NAME }}.tar.gz asset_name: ${{ env.APP_NAME }}-${{ env.APP_VERSION }}.tar.gz @@ -146,7 +164,7 @@ diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/ap overwrite: true - name: Upload app to Nextcloud appstore -@@ -214,8 +228,8 @@ +@@ -214,8 +246,8 @@ app_private_key: ${{ secrets.APP_PRIVATE_KEY }} - name: Verify App Store publication From 599fc086cae0b37979ffb18997edd8013872377e Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 10:54:32 -0300 Subject: [PATCH 06/16] chore: keep managed publication workflow unchanged on stable34 Signed-off-by: Vitor Mattos --- .github/workflows/appstore-build-publish.yml | 28 -------------------- 1 file changed, 28 deletions(-) diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/appstore-build-publish.yml index 1cf7238cfb..6f14d1fa2a 100644 --- a/.github/workflows/appstore-build-publish.yml +++ b/.github/workflows/appstore-build-publish.yml @@ -43,7 +43,6 @@ jobs: persist-credentials: false submodules: true fetch-tags: true - fetch-depth: 0 ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.event.release.tag_name }} path: ${{ env.APP_NAME }} @@ -165,33 +164,6 @@ jobs: repository: nextcloud/server path: nextcloud - - name: Restore current packaging Makefile for manual recovery - if: github.event_name == 'workflow_dispatch' && steps.krankerl.outputs.files_exists != 'true' - env: - RECOVERY_WORKFLOW_SHA: ${{ github.sha }} - RELEASE_TAG: ${{ env.APP_VERSION }} - run: | - set -euo pipefail - - case "${GITHUB_REF_NAME}" in - stable*) ;; - *) - echo "::error::Manual release recovery must be dispatched from a stable branch" - exit 1 - ;; - esac - - cd ${{ env.APP_NAME }} - git fetch --quiet origin "${RECOVERY_WORKFLOW_SHA}" - - tag_sha="$(git rev-parse "refs/tags/${RELEASE_TAG}^{commit}")" - if ! git merge-base --is-ancestor "${tag_sha}" "${RECOVERY_WORKFLOW_SHA}"; then - echo "::error::${GITHUB_REF_NAME} does not contain ${RELEASE_TAG}; refusing to mix packaging logic across release lines" - exit 1 - fi - - git show "${RECOVERY_WORKFLOW_SHA}:Makefile" > Makefile - - name: Package and sign with Makefile if: steps.krankerl.outputs.files_exists != 'true' env: From 7d8dcd5dfb568b8d57d2f6571a6b06dca82988cb Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 10:54:39 -0300 Subject: [PATCH 07/16] chore: keep managed publication workflow unchanged on stable34 Signed-off-by: Vitor Mattos --- .../appstore-build-publish.yml.patch | 203 ++++++------------ 1 file changed, 63 insertions(+), 140 deletions(-) diff --git a/.github/workflows/appstore-build-publish.yml.patch b/.github/workflows/appstore-build-publish.yml.patch index e6537d7650..e84388af3f 100644 --- a/.github/workflows/appstore-build-publish.yml.patch +++ b/.github/workflows/appstore-build-publish.yml.patch @@ -1,136 +1,89 @@ diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/appstore-build-publish.yml --- a/.github/workflows/appstore-build-publish.yml +++ b/.github/workflows/appstore-build-publish.yml -@@ -11,6 +11,12 @@ - on: - release: - types: [published] -+ workflow_dispatch: -+ inputs: -+ release_tag: -+ description: Existing release tag to build and publish, e.g. v13.4.3 -+ required: true -+ type: string - - permissions: - contents: write -@@ -29,25 +35,26 @@ - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV -+ echo "APP_VERSION=${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref_name }}" >> $GITHUB_ENV -- echo "APP_VERSION=${GITHUB_REF##*/}" >> $GITHUB_ENV - - - name: Checkout +@@ -28,6 +28,7 @@ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false + submodules: true -+ fetch-tags: true -+ fetch-depth: 0 -+ ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.event.release.tag_name }} path: ${{ env.APP_NAME }} - -+ - name: Validate release identity -+ id: release-identity -+ uses: LibreCodeCoop/release-tool/actions/release-identity@385ca7732db12e5c79590bb21be8da3608194595 -- - name: Get app version number -- id: app-version -- uses: skjnldsv/xpath-action@f5b036e9d973f42c86324833fd00be90665fbf77 # v1.0.0 - with: -+ tag: ${{ env.APP_VERSION }} -+ working-directory: ${{ env.APP_NAME }} -+ require-tag-exists: 'true' -- filename: ${{ env.APP_NAME }}/appinfo/info.xml -- expression: "//info//version/text()" - -- - name: Validate app version against tag -- run: | -- [ "${{ env.APP_VERSION }}" = "v${{ fromJSON(steps.app-version.outputs.result).version }}" ] -- - - name: Get appinfo data - id: appinfo - uses: skjnldsv/xpath-action@f5b036e9d973f42c86324833fd00be90665fbf77 # v1.0.0 -@@ -134,20 +141,6 @@ +@@ -132,12 +133,41 @@ cd ${{ env.APP_NAME }} krankerl package - - name: Package ${{ env.APP_NAME }} ${{ env.APP_VERSION }} with makefile -- if: steps.krankerl.outputs.files_exists != 'true' -- run: | -- cd ${{ env.APP_NAME }} -- make appstore -- -- - name: Verify app store package -- if: steps.krankerl.outputs.files_exists != 'true' -- working-directory: ${{ env.APP_NAME }} -- run: | -- if make -qp 2>/dev/null | grep -q '^verify-appstore-package:'; then -- make verify-appstore-package -- fi -- - - name: Check server download link for ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} - run: | - NCVERSION='${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }}' -@@ -172,28 +165,67 @@ - repository: nextcloud/server - path: nextcloud - -+ - name: Restore current packaging Makefile for manual recovery -+ if: github.event_name == 'workflow_dispatch' && steps.krankerl.outputs.files_exists != 'true' -+ env: -+ RECOVERY_WORKFLOW_SHA: ${{ github.sha }} -+ RELEASE_TAG: ${{ env.APP_VERSION }} ++ - name: Check server download link for ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} + run: | -+ set -euo pipefail - -+ case "${GITHUB_REF_NAME}" in -+ stable*) ;; -+ *) -+ echo "::error::Manual release recovery must be dispatched from a stable branch" -+ exit 1 -+ ;; -+ esac -+ -+ cd ${{ env.APP_NAME }} -+ git fetch --quiet origin "${RECOVERY_WORKFLOW_SHA}" ++ NCVERSION='${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }}' ++ DOWNLOAD_URL=$(curl -s "https://updates.nextcloud.com/updater_server/latest?channel=beta&version=$NCVERSION" | jq -r '.downloads.zip[0]') ++ echo "DOWNLOAD_URL=$DOWNLOAD_URL" >> $GITHUB_ENV + -+ tag_sha="$(git rev-parse "refs/tags/${RELEASE_TAG}^{commit}")" -+ if ! git merge-base --is-ancestor "${tag_sha}" "${RECOVERY_WORKFLOW_SHA}"; then -+ echo "::error::${GITHUB_REF_NAME} does not contain ${RELEASE_TAG}; refusing to mix packaging logic across release lines" -+ exit 1 -+ fi ++ - name: Download server ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} ++ continue-on-error: true ++ id: server-download ++ if: ${{ env.DOWNLOAD_URL != 'null' }} ++ run: | ++ echo "Downloading release tarball from $DOWNLOAD_URL" ++ wget $DOWNLOAD_URL -O nextcloud.zip ++ unzip nextcloud.zip + -+ git show "${RECOVERY_WORKFLOW_SHA}:Makefile" > Makefile ++ - name: Checkout server master fallback ++ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 ++ if: ${{ steps.server-download.outcome != 'success' }} ++ with: ++ persist-credentials: false ++ submodules: true ++ repository: nextcloud/server ++ path: nextcloud + + - name: Package and sign with Makefile -+ if: steps.krankerl.outputs.files_exists != 'true' + if: steps.krankerl.outputs.files_exists != 'true' + env: + APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }} -+ run: | -+ cd ${{ env.APP_NAME }} + run: | + cd ${{ env.APP_NAME }} + mkdir -p build/tools/certificates + printf '%s' "$APP_PRIVATE_KEY" > "build/tools/certificates/${APP_NAME}.key" -+ make appstore -+ -+ - name: Verify app store package -+ if: steps.krankerl.outputs.files_exists != 'true' -+ working-directory: ${{ env.APP_NAME }} + make appstore + + - name: Verify app store package + if: steps.krankerl.outputs.files_exists != 'true' + working-directory: ${{ env.APP_NAME }} + env: + REQUIRE_SETUP_SIGNATURES: 'true' -- - name: Sign app run: | -+ if make -qp 2>/dev/null | grep -q '^verify-appstore-package:'; then -+ make verify-appstore-package -+ fi -+ -+ - name: Set up PHP 8.3 for release-tool -+ uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 -+ with: -+ php-version: '8.3' -+ coverage: none -+ env: -+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if make -qp 2>/dev/null | grep -q '^verify-appstore-package:'; then + make verify-appstore-package +@@ -145,40 +175,6 @@ + fi + +- - name: Check server download link for ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} +- run: | +- NCVERSION='${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }}' +- DOWNLOAD_URL=$(curl -s "https://updates.nextcloud.com/updater_server/latest?channel=beta&version=$NCVERSION" | jq -r '.downloads.zip[0]') +- echo "DOWNLOAD_URL=$DOWNLOAD_URL" >> $GITHUB_ENV +- +- - name: Download server ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} +- continue-on-error: true +- id: server-download +- if: ${{ env.DOWNLOAD_URL != 'null' }} +- run: | +- echo "Downloading release tarball from $DOWNLOAD_URL" +- wget $DOWNLOAD_URL -O nextcloud.zip +- unzip nextcloud.zip +- +- - name: Checkout server master fallback +- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 +- if: ${{ steps.server-download.outcome != 'success' }} +- with: +- persist-credentials: false +- submodules: true +- repository: nextcloud/server +- path: nextcloud +- +- +- - name: Sign app +- run: | - # Extracting release - cd ${{ env.APP_NAME }}/build/artifacts - tar -xvf ${{ env.APP_NAME }}.tar.gz @@ -143,35 +96,5 @@ diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/ap - # Rebuilding archive - cd ${{ env.APP_NAME }}/build/artifacts - tar -zcvf ${{ env.APP_NAME }}.tar.gz ${{ env.APP_NAME }} - +- - name: Validate release artifact -+ uses: LibreCodeCoop/release-tool/actions/artifact-validate@385ca7732db12e5c79590bb21be8da3608194595 -- uses: LibreCodeCoop/release-tool/actions/artifact-validate@710c4c83fba47bf01713f46e9c4cb4cf63debfba - with: - artifact: ${{ env.APP_NAME }}/build/artifacts/${{ env.APP_NAME }}.tar.gz - app-name: ${{ env.APP_NAME }} -+ version: ${{ steps.release-identity.outputs.version }} -- version: ${{ env.APP_VERSION }} - - - name: Attach tarball to github release - uses: svenstaro/upload-release-action@29e53e917877a24fad85510ded594ab3c9ca12de # 2.11.5 -@@ -202,7 +234,7 @@ - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ env.APP_NAME }}/build/artifacts/${{ env.APP_NAME }}.tar.gz - asset_name: ${{ env.APP_NAME }}-${{ env.APP_VERSION }}.tar.gz -+ tag: ${{ env.APP_VERSION }} -- tag: ${{ github.ref }} - overwrite: true - - - name: Upload app to Nextcloud appstore -@@ -214,8 +246,8 @@ - app_private_key: ${{ secrets.APP_PRIVATE_KEY }} - - - name: Verify App Store publication -+ uses: LibreCodeCoop/release-tool/actions/appstore-publication-wait@385ca7732db12e5c79590bb21be8da3608194595 -- uses: LibreCodeCoop/release-tool/actions/appstore-publication-wait@710c4c83fba47bf01713f46e9c4cb4cf63debfba - with: - app-name: ${{ env.APP_NAME }} -+ version: ${{ steps.release-identity.outputs.version }} -- version: ${{ env.APP_VERSION }} - platform: ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} From cd2dc6deb0a77e1d9dc37cb784167457f2884f6b Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 10:54:45 -0300 Subject: [PATCH 08/16] fix(release): use Make-native GitHub Actions condition on stable34 Signed-off-by: Vitor Mattos --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1ccab638d9..cef7f88649 100644 --- a/Makefile +++ b/Makefile @@ -142,7 +142,7 @@ appstore: verify-release-metadata cp tests/php/fixtures/pdfs/small_valid.pdf $(appstore_sign_dir)/$(app_name)/tests/php/fixtures mkdir -p $(cert_dir) - if [ -f $(cert_dir)/$(app_name).key ] && [ "$${GITHUB_ACTIONS:-}" = "true" ]; then \ + if [ -f $(cert_dir)/$(app_name).key ] && [ "$(GITHUB_ACTIONS)" = "true" ]; then \ set -e; \ echo "⌛️ Starting Nextcloud setup..."; \ mkdir $(CURDIR)/../nextcloud/data; \ From e28bed2ec813e1e9e7180b7d8a3fedeed50bb045 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 10:54:49 -0300 Subject: [PATCH 09/16] test(release): guard Makefile CI condition on stable34 Signed-off-by: Vitor Mattos --- .../php/Unit/ReleasePackagingMakefileTest.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/php/Unit/ReleasePackagingMakefileTest.php diff --git a/tests/php/Unit/ReleasePackagingMakefileTest.php b/tests/php/Unit/ReleasePackagingMakefileTest.php new file mode 100644 index 0000000000..e7f624d5d9 --- /dev/null +++ b/tests/php/Unit/ReleasePackagingMakefileTest.php @@ -0,0 +1,22 @@ + Date: Tue, 29 Sep 2026 11:01:32 -0300 Subject: [PATCH 10/16] test(release): replace PHPUnit Makefile assertion on stable34 Signed-off-by: Vitor Mattos --- .../php/Unit/ReleasePackagingMakefileTest.php | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 tests/php/Unit/ReleasePackagingMakefileTest.php diff --git a/tests/php/Unit/ReleasePackagingMakefileTest.php b/tests/php/Unit/ReleasePackagingMakefileTest.php deleted file mode 100644 index e7f624d5d9..0000000000 --- a/tests/php/Unit/ReleasePackagingMakefileTest.php +++ /dev/null @@ -1,22 +0,0 @@ - Date: Tue, 29 Sep 2026 11:01:37 -0300 Subject: [PATCH 11/16] test(release): exercise Makefile CI expansion on stable34 Signed-off-by: Vitor Mattos --- tests/ci/test-release-makefile.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/ci/test-release-makefile.sh diff --git a/tests/ci/test-release-makefile.sh b/tests/ci/test-release-makefile.sh new file mode 100644 index 0000000000..b79629efd8 --- /dev/null +++ b/tests/ci/test-release-makefile.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +output="$(GITHUB_ACTIONS=true make --dry-run appstore)" + +if ! grep -Fq '[ "true" = "true" ]' <<<"${output}"; then + echo "Expected GNU Make to expand GITHUB_ACTIONS=true inside the appstore recipe." >&2 + exit 1 +fi + +if ! grep -Fq 'maintenance:install' <<<"${output}"; then + echo "Expected appstore recipe to include Nextcloud setup when packaging in CI." >&2 + exit 1 +fi + +if ! grep -Fq 'app:enable --force libresign' <<<"${output}"; then + echo "Expected appstore recipe to enable LibreSign before running its occ commands." >&2 + exit 1 +fi From 8e5b85df364bab82f5f6e7b932c9c4ee42c08314 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 11:01:43 -0300 Subject: [PATCH 12/16] ci: run Makefile release packaging regression test on stable34 Signed-off-by: Vitor Mattos --- .github/workflows/test-release-makefile.yml | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/test-release-makefile.yml diff --git a/.github/workflows/test-release-makefile.yml b/.github/workflows/test-release-makefile.yml new file mode 100644 index 0000000000..c45cd49442 --- /dev/null +++ b/.github/workflows/test-release-makefile.yml @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2026 LibreCode coop and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +name: Makefile release packaging test + +on: + pull_request: + paths: + - 'Makefile' + - 'tests/ci/test-release-makefile.sh' + - '.github/workflows/test-release-makefile.yml' + push: + branches: + - main + - stable* + paths: + - 'Makefile' + - 'tests/ci/test-release-makefile.sh' + - '.github/workflows/test-release-makefile.yml' + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Validate release packaging recipe + run: bash tests/ci/test-release-makefile.sh From f0790177068603cc74aa5a099f1c34b00c1258b9 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 11:05:16 -0300 Subject: [PATCH 13/16] ci: fold Makefile regression test into release metadata on stable34 Signed-off-by: Vitor Mattos --- .github/workflows/test-release-makefile.yml | 35 --------------------- 1 file changed, 35 deletions(-) delete mode 100644 .github/workflows/test-release-makefile.yml diff --git a/.github/workflows/test-release-makefile.yml b/.github/workflows/test-release-makefile.yml deleted file mode 100644 index c45cd49442..0000000000 --- a/.github/workflows/test-release-makefile.yml +++ /dev/null @@ -1,35 +0,0 @@ -# SPDX-FileCopyrightText: 2026 LibreCode coop and contributors -# SPDX-License-Identifier: AGPL-3.0-or-later - -name: Makefile release packaging test - -on: - pull_request: - paths: - - 'Makefile' - - 'tests/ci/test-release-makefile.sh' - - '.github/workflows/test-release-makefile.yml' - push: - branches: - - main - - stable* - paths: - - 'Makefile' - - 'tests/ci/test-release-makefile.sh' - - '.github/workflows/test-release-makefile.yml' - -permissions: - contents: read - -jobs: - test: - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Validate release packaging recipe - run: bash tests/ci/test-release-makefile.sh From 1080cd022a239df49e40df00075a99e819e85540 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 11:05:22 -0300 Subject: [PATCH 14/16] ci: validate Makefile release packaging on stable34 Signed-off-by: Vitor Mattos --- .github/workflows/release-metadata.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/release-metadata.yml b/.github/workflows/release-metadata.yml index 3cd2aa9ed6..81a9c8601e 100644 --- a/.github/workflows/release-metadata.yml +++ b/.github/workflows/release-metadata.yml @@ -11,6 +11,7 @@ on: - 'package-lock.json' - 'docs/changelogs/**' - 'Makefile' + - 'tests/ci/test-release-makefile.sh' push: branches: - main @@ -22,6 +23,7 @@ on: - 'package-lock.json' - 'docs/changelogs/**' - 'Makefile' + - 'tests/ci/test-release-makefile.sh' permissions: contents: read @@ -47,3 +49,6 @@ jobs: env: EXPECTED_CHANGELOG: ${{ steps.release.outputs.changelog-path }} run: test "$(make -s print-release-changelog)" = "${EXPECTED_CHANGELOG}" + + - name: Verify release packaging recipe + run: bash tests/ci/test-release-makefile.sh From 2a55ba78005811e07fc8349fde5cdc15e724b38f Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 11:13:03 -0300 Subject: [PATCH 15/16] chore: keep release metadata workflow unchanged on stable34 Signed-off-by: Vitor Mattos --- .github/workflows/release-metadata.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/release-metadata.yml b/.github/workflows/release-metadata.yml index 81a9c8601e..3cd2aa9ed6 100644 --- a/.github/workflows/release-metadata.yml +++ b/.github/workflows/release-metadata.yml @@ -11,7 +11,6 @@ on: - 'package-lock.json' - 'docs/changelogs/**' - 'Makefile' - - 'tests/ci/test-release-makefile.sh' push: branches: - main @@ -23,7 +22,6 @@ on: - 'package-lock.json' - 'docs/changelogs/**' - 'Makefile' - - 'tests/ci/test-release-makefile.sh' permissions: contents: read @@ -49,6 +47,3 @@ jobs: env: EXPECTED_CHANGELOG: ${{ steps.release.outputs.changelog-path }} run: test "$(make -s print-release-changelog)" = "${EXPECTED_CHANGELOG}" - - - name: Verify release packaging recipe - run: bash tests/ci/test-release-makefile.sh From 9467bdd6bf1d0a89877bb3f4c01ea359cc00ceab Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 29 Sep 2026 11:13:08 -0300 Subject: [PATCH 16/16] test(release): defer Makefile regression harness on stable34 Signed-off-by: Vitor Mattos --- tests/ci/test-release-makefile.sh | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 tests/ci/test-release-makefile.sh diff --git a/tests/ci/test-release-makefile.sh b/tests/ci/test-release-makefile.sh deleted file mode 100644 index b79629efd8..0000000000 --- a/tests/ci/test-release-makefile.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -output="$(GITHUB_ACTIONS=true make --dry-run appstore)" - -if ! grep -Fq '[ "true" = "true" ]' <<<"${output}"; then - echo "Expected GNU Make to expand GITHUB_ACTIONS=true inside the appstore recipe." >&2 - exit 1 -fi - -if ! grep -Fq 'maintenance:install' <<<"${output}"; then - echo "Expected appstore recipe to include Nextcloud setup when packaging in CI." >&2 - exit 1 -fi - -if ! grep -Fq 'app:enable --force libresign' <<<"${output}"; then - echo "Expected appstore recipe to enable LibreSign before running its occ commands." >&2 - exit 1 -fi