|
| 1 | +name: App Store ownership transfer |
| 2 | + |
| 3 | +# One-off, delete this file and its branch once the transfer has landed. |
| 4 | +# |
| 5 | +# The Nextcloud app store has no "transfer to user X" button and no |
| 6 | +# co-maintainer concept. Ownership moves the other way around: the CURRENT |
| 7 | +# owner unlocks the app (Account > Transfer app ownership), and the NEW owner |
| 8 | +# registers it again with the app certificate plus a signature over the app id. |
| 9 | +# That registration is the transfer, and it is exactly the POST below. |
| 10 | +# |
| 11 | +# See https://nextcloudappstore.readthedocs.io/en/latest/developer.html |
| 12 | +# #transferring-your-app-to-a-new-owner |
| 13 | +# |
| 14 | +# The app ends up owned by whichever account owns NEXTCLOUD_APPSTORE_TOKEN. |
| 15 | +# The signing key never leaves the runner and the signature is never printed: |
| 16 | +# together with the public certificate it is enough for anyone to claim an |
| 17 | +# app that is currently unlocked for transfer. |
| 18 | +on: |
| 19 | + push: |
| 20 | + branches: [chore/appstore-ownership-transfer] |
| 21 | + |
| 22 | +jobs: |
| 23 | + transfer: |
| 24 | + runs-on: ubuntu-latest |
| 25 | + steps: |
| 26 | + - name: Register launchpad under the App Store token's account |
| 27 | + env: |
| 28 | + APP_ID: launchpad |
| 29 | + SIGNING_KEY: ${{ secrets.NEXTCLOUD_SIGNING_KEY }} |
| 30 | + SIGNING_CERT: ${{ secrets.NEXTCLOUD_SIGNING_CERT }} |
| 31 | + APPSTORE_TOKEN: ${{ secrets.NEXTCLOUD_APPSTORE_TOKEN }} |
| 32 | + run: | |
| 33 | + set -euo pipefail |
| 34 | +
|
| 35 | + # An org secret that is not shared with this repo arrives as an empty |
| 36 | + # string, not as an error, so the request would 401 for a reason the |
| 37 | + # response body does not explain. Fail here instead. |
| 38 | + [ -n "$APPSTORE_TOKEN" ] || { echo "::error::NEXTCLOUD_APPSTORE_TOKEN is empty in this repository's scope"; exit 1; } |
| 39 | + [ -n "$SIGNING_KEY" ] || { echo "::error::NEXTCLOUD_SIGNING_KEY is empty in this repository's scope"; exit 1; } |
| 40 | +
|
| 41 | + printf '%s\n' "$SIGNING_KEY" > signing-key.key |
| 42 | + printf '%s\n' "$SIGNING_CERT" > signing-cert.crt |
| 43 | +
|
| 44 | + SIG=$(echo -n "$APP_ID" | openssl dgst -sha512 -sign signing-key.key | openssl base64 -A) |
| 45 | + CODE=$(curl -s -o response.json -w '%{http_code}' \ |
| 46 | + -X POST "https://apps.nextcloud.com/api/v1/apps" \ |
| 47 | + -H "Authorization: Token $APPSTORE_TOKEN" \ |
| 48 | + -H "Content-Type: application/json" \ |
| 49 | + -d "{\"certificate\": $(jq -Rs . < signing-cert.crt), \"signature\": \"$SIG\"}") |
| 50 | + rm -f signing-key.key |
| 51 | +
|
| 52 | + echo "HTTP $CODE" |
| 53 | + cat response.json || true |
| 54 | + echo |
| 55 | +
|
| 56 | + case "$CODE" in |
| 57 | + 200|201) echo "OK: $APP_ID is now registered to the account that owns NEXTCLOUD_APPSTORE_TOKEN" ;; |
| 58 | + 403) echo "::error::403: the app is locked for transfer, or the token's account may not register it"; exit 1 ;; |
| 59 | + 401) echo "::error::401: the App Store token was rejected"; exit 1 ;; |
| 60 | + *) echo "::error::unexpected HTTP $CODE"; exit 1 ;; |
| 61 | + esac |
0 commit comments