mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 18:39:55 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56d19014d4 | |||
| 51bf0abba2 | |||
| e0fb84ccb9 | |||
| 30025a4ebf |
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -295,10 +295,6 @@ jobs:
|
|||||||
SELLER_ID: ${{ secrets.SELLER_ID }}
|
SELLER_ID: ${{ secrets.SELLER_ID }}
|
||||||
MICROSOFT_STORE_PRODUCT_ID: ${{ vars.MICROSOFT_STORE_PRODUCT_ID }}
|
MICROSOFT_STORE_PRODUCT_ID: ${{ vars.MICROSOFT_STORE_PRODUCT_ID }}
|
||||||
run: |
|
run: |
|
||||||
msstore settings --enableTelemetry false
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw "Failed to disable Microsoft Store CLI telemetry"
|
|
||||||
}
|
|
||||||
msstore reconfigure `
|
msstore reconfigure `
|
||||||
--tenantId "$env:AZURE_AD_TENANT_ID" `
|
--tenantId "$env:AZURE_AD_TENANT_ID" `
|
||||||
--sellerId "$env:SELLER_ID" `
|
--sellerId "$env:SELLER_ID" `
|
||||||
@@ -307,6 +303,10 @@ jobs:
|
|||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
throw "Microsoft Store authentication failed"
|
throw "Microsoft Store authentication failed"
|
||||||
}
|
}
|
||||||
|
msstore settings --enableTelemetry false
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Failed to disable Microsoft Store CLI telemetry"
|
||||||
|
}
|
||||||
msstore apps get "$env:MICROSOFT_STORE_PRODUCT_ID"
|
msstore apps get "$env:MICROSOFT_STORE_PRODUCT_ID"
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
throw "The Microsoft Store application is not accessible"
|
throw "The Microsoft Store application is not accessible"
|
||||||
|
|||||||
@@ -184,6 +184,19 @@ def generated_apks_url(package_name: str, version_code: int) -> str:
|
|||||||
return f"{API_ROOT}/applications/{package}/generatedApks/{version_code}"
|
return f"{API_ROOT}/applications/{package}/generatedApks/{version_code}"
|
||||||
|
|
||||||
|
|
||||||
|
def generated_apk_download_url(
|
||||||
|
package_name: str,
|
||||||
|
version_code: int,
|
||||||
|
download_id: str,
|
||||||
|
) -> str:
|
||||||
|
package = urllib.parse.quote(package_name, safe="")
|
||||||
|
download = urllib.parse.quote(download_id, safe="")
|
||||||
|
return (
|
||||||
|
f"{API_ROOT}/applications/{package}/generatedApks/"
|
||||||
|
f"{version_code}/downloads/{download}:download?alt=media"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_generated_apks(
|
def get_generated_apks(
|
||||||
client: PlayClient,
|
client: PlayClient,
|
||||||
package_name: str,
|
package_name: str,
|
||||||
@@ -216,22 +229,23 @@ def download_universal_apk(
|
|||||||
selected = find_universal_apk(response, expected_fingerprint)
|
selected = find_universal_apk(response, expected_fingerprint)
|
||||||
if selected is not None:
|
if selected is not None:
|
||||||
fingerprint, download_id = selected
|
fingerprint, download_id = selected
|
||||||
package = urllib.parse.quote(package_name, safe="")
|
apk = client.request(
|
||||||
download = urllib.parse.quote(download_id, safe="")
|
"GET",
|
||||||
url = (
|
generated_apk_download_url(
|
||||||
f"{API_ROOT}/applications/{package}/generatedApks/"
|
package_name,
|
||||||
f"{version_code}/downloads/{download}:download"
|
version_code,
|
||||||
|
download_id,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
if apk:
|
||||||
output.parent.mkdir(parents=True, exist_ok=True)
|
output.parent.mkdir(parents=True, exist_ok=True)
|
||||||
output.write_bytes(client.request("GET", url))
|
output.write_bytes(apk)
|
||||||
if output.stat().st_size == 0:
|
|
||||||
raise RuntimeError("Google Play returned an empty universal APK")
|
|
||||||
return fingerprint
|
return fingerprint
|
||||||
if attempt < attempts:
|
if attempt < attempts:
|
||||||
time.sleep(interval_seconds)
|
time.sleep(interval_seconds)
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Google Play did not provide a universal APK signed with the expected "
|
"Google Play did not provide a non-empty universal APK signed with the "
|
||||||
f"certificate after {attempts} attempts"
|
f"expected certificate after {attempts} attempts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import importlib.util
|
import importlib.util
|
||||||
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -44,6 +45,69 @@ class PublishPlayTests(unittest.TestCase):
|
|||||||
("aabb", "correct"),
|
("aabb", "correct"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_downloads_generated_apk_as_media(self):
|
||||||
|
class FakePlayClient:
|
||||||
|
def __init__(self):
|
||||||
|
self.download_urls = []
|
||||||
|
self.media_attempts = 0
|
||||||
|
|
||||||
|
def request_json(self, method, url):
|
||||||
|
self.assert_request(method, url)
|
||||||
|
return {
|
||||||
|
"generatedApks": [
|
||||||
|
{
|
||||||
|
"certificateSha256Hash": "AA:BB",
|
||||||
|
"generatedUniversalApk": {
|
||||||
|
"downloadId": "download/id+=",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def request(self, method, url):
|
||||||
|
self.assert_request(method, url)
|
||||||
|
self.download_urls.append(url)
|
||||||
|
if not url.endswith("?alt=media"):
|
||||||
|
return b""
|
||||||
|
self.media_attempts += 1
|
||||||
|
return b"apk" if self.media_attempts == 2 else b""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def assert_request(method, url):
|
||||||
|
if method != "GET" or not url.startswith(publish_play.API_ROOT):
|
||||||
|
raise AssertionError(f"unexpected request: {method} {url}")
|
||||||
|
|
||||||
|
client = FakePlayClient()
|
||||||
|
with tempfile.TemporaryDirectory() as scratch:
|
||||||
|
output = Path(scratch) / "universal.apk"
|
||||||
|
fingerprint = publish_play.download_universal_apk(
|
||||||
|
client,
|
||||||
|
"com.example app",
|
||||||
|
2002,
|
||||||
|
"aa:bb",
|
||||||
|
output,
|
||||||
|
attempts=2,
|
||||||
|
interval_seconds=0,
|
||||||
|
)
|
||||||
|
self.assertEqual(fingerprint, "aabb")
|
||||||
|
self.assertEqual(output.read_bytes(), b"apk")
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
client.download_urls,
|
||||||
|
[
|
||||||
|
(
|
||||||
|
f"{publish_play.API_ROOT}/applications/com.example%20app/"
|
||||||
|
"generatedApks/2002/downloads/"
|
||||||
|
"download%2Fid%2B%3D:download?alt=media"
|
||||||
|
),
|
||||||
|
(
|
||||||
|
f"{publish_play.API_ROOT}/applications/com.example%20app/"
|
||||||
|
"generatedApks/2002/downloads/"
|
||||||
|
"download%2Fid%2B%3D:download?alt=media"
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
def test_track_update_preserves_existing_releases_and_adds_draft(self):
|
def test_track_update_preserves_existing_releases_and_adds_draft(self):
|
||||||
track = {
|
track = {
|
||||||
"track": "closed-beta",
|
"track": "closed-beta",
|
||||||
|
|||||||
@@ -25,6 +25,21 @@ grep -F 'run: make build-apple-dmg' \
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
store_reconfigure_line="$(
|
||||||
|
awk '/msstore reconfigure/ {print NR; exit}' \
|
||||||
|
"$repo_root/.github/workflows/release.yml"
|
||||||
|
)"
|
||||||
|
store_settings_line="$(
|
||||||
|
awk '/msstore settings --enableTelemetry false/ {print NR; exit}' \
|
||||||
|
"$repo_root/.github/workflows/release.yml"
|
||||||
|
)"
|
||||||
|
[[ -n $store_reconfigure_line &&
|
||||||
|
-n $store_settings_line &&
|
||||||
|
$store_reconfigure_line -lt $store_settings_line ]] || {
|
||||||
|
printf 'Microsoft Store CLI credentials must be configured before changing settings\n' >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
signing_line="$(
|
signing_line="$(
|
||||||
awk '/sign-exported-app\.sh/ {print NR; exit}' \
|
awk '/sign-exported-app\.sh/ {print NR; exit}' \
|
||||||
"$repo_root/apple/scripts/build-dmg.sh"
|
"$repo_root/apple/scripts/build-dmg.sh"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
PRODUCT_VERSION=0.2.2
|
PRODUCT_VERSION=0.2.4
|
||||||
RELEASE_CHANNEL=beta
|
RELEASE_CHANNEL=beta
|
||||||
WINDOWS_VERSION_EPOCH=1
|
WINDOWS_VERSION_EPOCH=1
|
||||||
|
|||||||
Reference in New Issue
Block a user