Skip to content

chore: one-off App Store ownership transfer for launchpad #1

chore: one-off App Store ownership transfer for launchpad

chore: one-off App Store ownership transfer for launchpad #1

name: App Store ownership transfer
# One-off, delete this file and its branch once the transfer has landed.
#
# The Nextcloud app store has no "transfer to user X" button and no
# co-maintainer concept. Ownership moves the other way around: the CURRENT
# owner unlocks the app (Account > Transfer app ownership), and the NEW owner
# registers it again with the app certificate plus a signature over the app id.
# That registration is the transfer, and it is exactly the POST below.
#
# See https://nextcloudappstore.readthedocs.io/en/latest/developer.html
# #transferring-your-app-to-a-new-owner
#
# The app ends up owned by whichever account owns NEXTCLOUD_APPSTORE_TOKEN.
# The signing key never leaves the runner and the signature is never printed:
# together with the public certificate it is enough for anyone to claim an
# app that is currently unlocked for transfer.
on:
push:
branches: [chore/appstore-ownership-transfer]
jobs:
transfer:
runs-on: ubuntu-latest
steps:
- name: Register launchpad under the App Store token's account
env:
APP_ID: launchpad
SIGNING_KEY: ${{ secrets.NEXTCLOUD_SIGNING_KEY }}
SIGNING_CERT: ${{ secrets.NEXTCLOUD_SIGNING_CERT }}
APPSTORE_TOKEN: ${{ secrets.NEXTCLOUD_APPSTORE_TOKEN }}
run: |
set -euo pipefail
# An org secret that is not shared with this repo arrives as an empty
# string, not as an error, so the request would 401 for a reason the
# response body does not explain. Fail here instead.
[ -n "$APPSTORE_TOKEN" ] || { echo "::error::NEXTCLOUD_APPSTORE_TOKEN is empty in this repository's scope"; exit 1; }
[ -n "$SIGNING_KEY" ] || { echo "::error::NEXTCLOUD_SIGNING_KEY is empty in this repository's scope"; exit 1; }
printf '%s\n' "$SIGNING_KEY" > signing-key.key
printf '%s\n' "$SIGNING_CERT" > signing-cert.crt
SIG=$(echo -n "$APP_ID" | openssl dgst -sha512 -sign signing-key.key | openssl base64 -A)
CODE=$(curl -s -o response.json -w '%{http_code}' \
-X POST "https://apps.nextcloud.com/api/v1/apps" \
-H "Authorization: Token $APPSTORE_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"certificate\": $(jq -Rs . < signing-cert.crt), \"signature\": \"$SIG\"}")
rm -f signing-key.key
echo "HTTP $CODE"
cat response.json || true
echo
case "$CODE" in
200|201) echo "OK: $APP_ID is now registered to the account that owns NEXTCLOUD_APPSTORE_TOKEN" ;;
403) echo "::error::403: the app is locked for transfer, or the token's account may not register it"; exit 1 ;;
401) echo "::error::401: the App Store token was rejected"; exit 1 ;;
*) echo "::error::unexpected HTTP $CODE"; exit 1 ;;
esac