mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2026-05-16 04:52:39 +02:00
Added proper automation for playstore builds.
This commit is contained in:
+25
-16
@@ -1,20 +1,17 @@
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
|
||||
stages:
|
||||
- buildAndDeployApkUnstable
|
||||
- buildAndDeployApkStable
|
||||
- buildAndDeployPlaystore
|
||||
|
||||
buildAndDeployApkUnstable:
|
||||
stage: buildAndDeployApkUnstable
|
||||
script:
|
||||
- sh deploy-unstable.sh
|
||||
only:
|
||||
- tags
|
||||
except:
|
||||
- ^(dev)
|
||||
when: manual
|
||||
needs: []
|
||||
allow_failure: true
|
||||
artifacts:
|
||||
when: always
|
||||
expire_in: 30 days
|
||||
paths:
|
||||
- app/build/outputs/apk/unstable/release/*.apk
|
||||
|
||||
buildAndDeployApkStable:
|
||||
stage: buildAndDeployApkStable
|
||||
@@ -22,16 +19,28 @@ buildAndDeployApkStable:
|
||||
- sh deploy-stable.sh
|
||||
only:
|
||||
- tags
|
||||
except:
|
||||
- branches
|
||||
when: manual
|
||||
needs: []
|
||||
artifacts:
|
||||
when: always
|
||||
expire_in: 30 days
|
||||
paths:
|
||||
- app/build/outputs/apk/stable/release/*.apk
|
||||
|
||||
buildAndDeployPlaystore:
|
||||
stage: buildAndDeployPlaystore
|
||||
script:
|
||||
- sh deploy-playstore.sh
|
||||
- sh build-playstore.sh
|
||||
- bash tools/venv_playstore.sh
|
||||
- . .venv-playstore/bin/activate
|
||||
- python publish_playstore.py --sa /root/grayjay.json --package com.futo.platformplayer.playstore --aab ./app/build/outputs/bundle/playstoreRelease/app-playstore-release.aab --track production --status completed
|
||||
only:
|
||||
- tags
|
||||
except:
|
||||
- branches
|
||||
when: manual
|
||||
when: on_success
|
||||
needs:
|
||||
- buildAndDeployApkStable
|
||||
artifacts:
|
||||
when: always
|
||||
expire_in: 30 days
|
||||
paths:
|
||||
- app/build/outputs/bundle/playstoreRelease/*.aab
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
DOCUMENT_ROOT=/var/www/html
|
||||
MAINT_FILE="$DOCUMENT_ROOT/maintenance.file"
|
||||
|
||||
cleanup() {
|
||||
rm -f "$MAINT_FILE"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
# Sign sources
|
||||
echo "Signing all sources..."
|
||||
@@ -11,12 +19,12 @@ echo "Building content..."
|
||||
|
||||
# Take site offline
|
||||
echo "Taking site offline..."
|
||||
touch $DOCUMENT_ROOT/maintenance.file
|
||||
touch "$MAINT_FILE"
|
||||
|
||||
# Swap over the content
|
||||
echo "Deploying content..."
|
||||
cp ./app/build/outputs/bundle/playstoreRelease/app-playstore-release.aab $DOCUMENT_ROOT/app-playstore-release.aab
|
||||
aws s3 cp ./app/build/outputs/bundle/playstoreRelease/app-playstore-release.aab s3://artifacts-grayjay-app/app-playstore-release.aab
|
||||
cp ./app/build/outputs/bundle/playstoreRelease/app-playstore-release.aab \
|
||||
"$DOCUMENT_ROOT/app-playstore-release.aab"
|
||||
|
||||
# Notify Cloudflare to wipe the CDN cache
|
||||
echo "Purging Cloudflare cache for zone $CLOUDFLARE_ZONE_ID..."
|
||||
@@ -29,4 +37,5 @@ sleep 30
|
||||
|
||||
# Take site back online
|
||||
echo "Bringing site back online..."
|
||||
rm $DOCUMENT_ROOT/maintenance.file
|
||||
rm -f "$MAINT_FILE"
|
||||
trap - EXIT INT TERM
|
||||
+21
-26
@@ -1,5 +1,13 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
DOCUMENT_ROOT=/var/www/html
|
||||
MAINT_FILE="$DOCUMENT_ROOT/maintenance.file"
|
||||
|
||||
cleanup() {
|
||||
rm -f "$MAINT_FILE"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
# Sign sources
|
||||
echo "Signing all sources..."
|
||||
@@ -11,35 +19,21 @@ echo "Building content..."
|
||||
|
||||
# Take site offline
|
||||
echo "Taking site offline..."
|
||||
touch $DOCUMENT_ROOT/maintenance.file
|
||||
touch "$MAINT_FILE"
|
||||
|
||||
# Swap over the content
|
||||
echo "Deploying content..."
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-x86_64-release.apk $DOCUMENT_ROOT/app-x86_64-release.apk
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-arm64-v8a-release.apk $DOCUMENT_ROOT/app-arm64-v8a-release.apk
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-armeabi-v7a-release.apk $DOCUMENT_ROOT/app-armeabi-v7a-release.apk
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-universal-release.apk $DOCUMENT_ROOT/app-universal-release.apk
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-x86-release.apk $DOCUMENT_ROOT/app-x86-release.apk
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-arm64-v8a-release.apk $DOCUMENT_ROOT/app-release.apk
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-x86_64-release.apk "$DOCUMENT_ROOT/app-x86_64-release.apk"
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-arm64-v8a-release.apk "$DOCUMENT_ROOT/app-arm64-v8a-release.apk"
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-armeabi-v7a-release.apk "$DOCUMENT_ROOT/app-armeabi-v7a-release.apk"
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-universal-release.apk "$DOCUMENT_ROOT/app-universal-release.apk"
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-x86-release.apk "$DOCUMENT_ROOT/app-x86-release.apk"
|
||||
cp ./app/build/outputs/apk/stable/release/app-stable-arm64-v8a-release.apk "$DOCUMENT_ROOT/app-release.apk"
|
||||
|
||||
VERSION=$(git describe --tags)
|
||||
echo $VERSION > $DOCUMENT_ROOT/version.txt
|
||||
mkdir -p $DOCUMENT_ROOT/changelogs
|
||||
git tag -l --format='%(contents)' $VERSION > $DOCUMENT_ROOT/changelogs/$VERSION
|
||||
|
||||
aws s3 cp ./app/build/outputs/apk/stable/release/app-stable-x86_64-release.apk s3://artifacts-grayjay-app/app-x86_64-release.apk
|
||||
aws s3 cp ./app/build/outputs/apk/stable/release/app-stable-arm64-v8a-release.apk s3://artifacts-grayjay-app/app-arm64-v8a-release.apk
|
||||
aws s3 cp ./app/build/outputs/apk/stable/release/app-stable-armeabi-v7a-release.apk s3://artifacts-grayjay-app/app-armeabi-v7a-release.apk
|
||||
aws s3 cp ./app/build/outputs/apk/stable/release/app-stable-universal-release.apk s3://artifacts-grayjay-app/app-universal-release.apk
|
||||
aws s3 cp ./app/build/outputs/apk/stable/release/app-stable-x86-release.apk s3://artifacts-grayjay-app/app-x86-release.apk
|
||||
aws s3 cp ./app/build/outputs/apk/stable/release/app-stable-arm64-v8a-release.apk s3://artifacts-grayjay-app/app-release.apk
|
||||
|
||||
VERSION=$(git describe --tags)
|
||||
echo $VERSION > ./version.txt
|
||||
git tag -l --format='%(contents)' $VERSION > ./changelog.txt
|
||||
|
||||
aws s3 cp ./version.txt s3://artifacts-grayjay-app/version.txt
|
||||
aws s3 cp ./changelog.txt s3://artifacts-grayjay-app/changelogs/$VERSION
|
||||
VERSION="$(git describe --tags)"
|
||||
echo "$VERSION" > "$DOCUMENT_ROOT/version.txt"
|
||||
mkdir -p "$DOCUMENT_ROOT/changelogs"
|
||||
git tag -l --format='%(contents)' "$VERSION" > "$DOCUMENT_ROOT/changelogs/$VERSION"
|
||||
|
||||
# Notify Cloudflare to wipe the CDN cache
|
||||
echo "Purging Cloudflare cache for zone $CLOUDFLARE_ZONE_ID..."
|
||||
@@ -52,4 +46,5 @@ sleep 30
|
||||
|
||||
# Take site back online
|
||||
echo "Bringing site back online..."
|
||||
rm $DOCUMENT_ROOT/maintenance.file
|
||||
rm -f "$MAINT_FILE"
|
||||
trap - EXIT INT TERM
|
||||
|
||||
+19
-18
@@ -1,5 +1,13 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
DOCUMENT_ROOT=/var/www/html
|
||||
MAINT_FILE="$DOCUMENT_ROOT/maintenance.file"
|
||||
|
||||
cleanup() {
|
||||
rm -f "$MAINT_FILE"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
# Sign sources
|
||||
echo "Signing all sources..."
|
||||
@@ -11,27 +19,19 @@ echo "Building content..."
|
||||
|
||||
# Take site offline
|
||||
echo "Taking site offline..."
|
||||
touch $DOCUMENT_ROOT/maintenance.file
|
||||
touch "$MAINT_FILE"
|
||||
|
||||
# Swap over the content
|
||||
echo "Deploying content..."
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-x86_64-release.apk $DOCUMENT_ROOT/app-x86_64-release-unstable.apk
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-arm64-v8a-release.apk $DOCUMENT_ROOT/app-arm64-v8a-release-unstable.apk
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-armeabi-v7a-release.apk $DOCUMENT_ROOT/app-armeabi-v7a-release-unstable.apk
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-universal-release.apk $DOCUMENT_ROOT/app-universal-release-unstable.apk
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-x86-release.apk $DOCUMENT_ROOT/app-x86-release-unstable.apk
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-arm64-v8a-release.apk $DOCUMENT_ROOT/app-release-unstable.apk
|
||||
git describe --tags > $DOCUMENT_ROOT/version-unstable.txt
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-x86_64-release.apk "$DOCUMENT_ROOT/app-x86_64-release-unstable.apk"
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-arm64-v8a-release.apk "$DOCUMENT_ROOT/app-arm64-v8a-release-unstable.apk"
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-armeabi-v7a-release.apk "$DOCUMENT_ROOT/app-armeabi-v7a-release-unstable.apk"
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-universal-release.apk "$DOCUMENT_ROOT/app-universal-release-unstable.apk"
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-x86-release.apk "$DOCUMENT_ROOT/app-x86-release-unstable.apk"
|
||||
cp ./app/build/outputs/apk/unstable/release/app-unstable-arm64-v8a-release.apk "$DOCUMENT_ROOT/app-release-unstable.apk"
|
||||
|
||||
aws s3 cp ./app/build/outputs/apk/unstable/release/app-unstable-x86_64-release.apk s3://artifacts-grayjay-app/app-x86_64-release-unstable.apk
|
||||
aws s3 cp ./app/build/outputs/apk/unstable/release/app-unstable-arm64-v8a-release.apk s3://artifacts-grayjay-app/app-arm64-v8a-release-unstable.apk
|
||||
aws s3 cp ./app/build/outputs/apk/unstable/release/app-unstable-armeabi-v7a-release.apk s3://artifacts-grayjay-app/app-armeabi-v7a-release-unstable.apk
|
||||
aws s3 cp ./app/build/outputs/apk/unstable/release/app-unstable-universal-release.apk s3://artifacts-grayjay-app/app-universal-release-unstable.apk
|
||||
aws s3 cp ./app/build/outputs/apk/unstable/release/app-unstable-x86-release.apk s3://artifacts-grayjay-app/app-x86-release-unstable.apk
|
||||
aws s3 cp ./app/build/outputs/apk/unstable/release/app-unstable-arm64-v8a-release.apk s3://artifacts-grayjay-app/app-release-unstable.apk
|
||||
|
||||
git describe --tags > ./version-unstable.txt
|
||||
aws s3 cp ./version-unstable.txt s3://artifacts-grayjay-app/version-unstable.txt
|
||||
VERSION="$(git describe --tags)"
|
||||
echo "$VERSION" > "$DOCUMENT_ROOT/version-unstable.txt"
|
||||
|
||||
# Notify Cloudflare to wipe the CDN cache
|
||||
echo "Purging Cloudflare cache for zone $CLOUDFLARE_ZONE_ID..."
|
||||
@@ -44,4 +44,5 @@ sleep 30
|
||||
|
||||
# Take site back online
|
||||
echo "Bringing site back online..."
|
||||
rm $DOCUMENT_ROOT/maintenance.file
|
||||
rm -f "$MAINT_FILE"
|
||||
trap - EXIT INT TERM
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import random
|
||||
import time
|
||||
import httplib2
|
||||
import socket
|
||||
|
||||
from google_auth_httplib2 import AuthorizedHttp
|
||||
from google.oauth2 import service_account
|
||||
from googleapiclient.discovery import build
|
||||
from googleapiclient.http import MediaFileUpload
|
||||
from googleapiclient.errors import HttpError
|
||||
from googleapiclient.http import build_http
|
||||
|
||||
SCOPE = "https://www.googleapis.com/auth/androidpublisher"
|
||||
socket.setdefaulttimeout(30 * 60)
|
||||
|
||||
def die(msg: str, code: int = 1):
|
||||
print(msg, file=sys.stderr)
|
||||
raise SystemExit(code)
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--sa", required=True, help="Service account JSON file path")
|
||||
ap.add_argument("--package", required=True, help="ApplicationId / package name")
|
||||
ap.add_argument("--aab", required=True, help="Path to .aab file")
|
||||
ap.add_argument("--track", default="internal", help="internal|alpha|beta|production")
|
||||
ap.add_argument("--status", default="completed", help="draft|inProgress|halted|completed")
|
||||
ap.add_argument("--name", default=None, help="Release name (defaults to CI_COMMIT_TAG)")
|
||||
ap.add_argument("--rollout", type=float, default=None, help="For staged rollout: 0 < rollout < 1")
|
||||
args = ap.parse_args()
|
||||
|
||||
if not os.path.isfile(args.sa):
|
||||
die(f"Missing service account JSON: {args.sa}")
|
||||
if not os.path.isfile(args.aab):
|
||||
die(f"Missing AAB: {args.aab}")
|
||||
|
||||
release_name = args.name or os.environ.get("CI_COMMIT_TAG")
|
||||
if not release_name:
|
||||
die("Missing release name: pass --name or set CI_COMMIT_TAG")
|
||||
|
||||
staged = args.status in ("inProgress", "halted")
|
||||
if staged:
|
||||
if args.rollout is None:
|
||||
die("--rollout is required when --status is inProgress or halted")
|
||||
if not (0.0 < args.rollout < 1.0):
|
||||
die("--rollout must satisfy 0 < rollout < 1")
|
||||
else:
|
||||
args.rollout = None
|
||||
|
||||
print(f"Loading service account")
|
||||
|
||||
creds = service_account.Credentials.from_service_account_file(
|
||||
args.sa, scopes=[SCOPE]
|
||||
)
|
||||
|
||||
print(f"Loaded service account")
|
||||
|
||||
|
||||
print(f"Building service")
|
||||
http = build_http()
|
||||
authed_http = AuthorizedHttp(creds, http=http)
|
||||
service = build("androidpublisher", "v3", http=authed_http, cache_discovery=False)
|
||||
print(f"Built service")
|
||||
|
||||
try:
|
||||
print(f"Creating edit")
|
||||
|
||||
edit = service.edits().insert(body={}, packageName=args.package).execute()
|
||||
edit_id = edit["id"]
|
||||
|
||||
UPLOAD_CHUNK_SIZE = 10 * 1024 * 1024
|
||||
MAX_RETRIES = 8
|
||||
|
||||
print(f"Media upload started")
|
||||
|
||||
media = MediaFileUpload(
|
||||
args.aab,
|
||||
mimetype="application/octet-stream",
|
||||
resumable=True,
|
||||
chunksize=UPLOAD_CHUNK_SIZE,
|
||||
)
|
||||
|
||||
request = service.edits().bundles().upload(
|
||||
packageName=args.package,
|
||||
editId=edit_id,
|
||||
media_body=media,
|
||||
)
|
||||
|
||||
response = None
|
||||
last_pct = -1
|
||||
attempt = 0
|
||||
|
||||
while response is None:
|
||||
try:
|
||||
status, response = request.next_chunk(num_retries=3)
|
||||
attempt = 0 # reset after any successful chunk
|
||||
|
||||
if status:
|
||||
pct = int(status.progress() * 100)
|
||||
if pct != last_pct:
|
||||
last_pct = pct
|
||||
print(f"Upload progress: {pct}%", flush=True)
|
||||
|
||||
except HttpError as e:
|
||||
# Retry transient server-side errors with exponential backoff
|
||||
code = getattr(getattr(e, "resp", None), "status", None)
|
||||
if code in (500, 502, 503, 504) and attempt < MAX_RETRIES:
|
||||
sleep_s = min(60, (2 ** attempt)) + random.random()
|
||||
print(f"Transient HTTP {code}; retrying in {sleep_s:.1f}s...", flush=True)
|
||||
time.sleep(sleep_s)
|
||||
attempt += 1
|
||||
continue
|
||||
raise
|
||||
|
||||
print("Media upload finished")
|
||||
bundle = response
|
||||
version_code = bundle["versionCode"]
|
||||
|
||||
release = {
|
||||
"name": release_name,
|
||||
"status": args.status,
|
||||
"versionCodes": [str(version_code)],
|
||||
}
|
||||
if args.rollout is not None:
|
||||
release["userFraction"] = args.rollout
|
||||
|
||||
track_body = {"releases": [release]}
|
||||
|
||||
print(f"Updating track")
|
||||
|
||||
service.edits().tracks().update(
|
||||
packageName=args.package,
|
||||
editId=edit_id,
|
||||
track=args.track,
|
||||
body=track_body,
|
||||
).execute()
|
||||
|
||||
print(f"Updated track")
|
||||
print(f"Committing")
|
||||
|
||||
service.edits().commit(packageName=args.package, editId=edit_id).execute()
|
||||
print(f"Committed")
|
||||
|
||||
print(f"OK: package={args.package} track={args.track} status={args.status} versionCode={version_code} name={release_name}")
|
||||
except HttpError as e:
|
||||
content = e.content.decode("utf-8", errors="replace") if getattr(e, "content", None) else str(e)
|
||||
die(f"Google API error (HTTP {e.resp.status if e.resp else '??'}):\n{content}")
|
||||
except Exception as e:
|
||||
die(f"Unexpected error: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
VENV_DIR="${VENV_DIR:-.venv-playstore}"
|
||||
|
||||
if [[ ! -d "$VENV_DIR" ]]; then
|
||||
python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
|
||||
source "$VENV_DIR/bin/activate"
|
||||
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
python -m pip install --upgrade google-api-python-client google-auth google-auth-httplib2
|
||||
Reference in New Issue
Block a user