The 1.79.0 listing includes ja, so Fastlane rejected submit_review when release-summary.json had no Japanese notes. Fail in store-delivery and Fastfile instead of creating a stuck draft.
233 lines
8.6 KiB
JavaScript
233 lines
8.6 KiB
JavaScript
import { describe, expect, test } from "bun:test";
|
|
import { readFileSync } from "node:fs";
|
|
import { parseStoreDeliveryArgs } from "./store-delivery.mjs";
|
|
|
|
describe("store delivery command", () => {
|
|
test("uses full production delivery defaults", () => {
|
|
expect(parseStoreDeliveryArgs(["--release", "v1.7.0"])).toMatchObject({
|
|
releaseTag: "v1.7.0",
|
|
platform: "both",
|
|
androidTrack: "production",
|
|
});
|
|
});
|
|
|
|
test("accepts an explicit single platform", () => {
|
|
expect(
|
|
parseStoreDeliveryArgs(["--release", "v1.7.0", "--platform", "ios"])
|
|
.platform,
|
|
).toBe("ios");
|
|
});
|
|
|
|
test("supports recovering a Play-signed APK without re-uploading", () => {
|
|
expect(
|
|
parseStoreDeliveryArgs([
|
|
"--release",
|
|
"v1.7.0",
|
|
"--platform",
|
|
"android",
|
|
"--recover-play-apk",
|
|
]).recoverPlayApk,
|
|
).toBe(true);
|
|
});
|
|
|
|
test("supports submitting an existing iOS build without rebuilding", () => {
|
|
expect(
|
|
parseStoreDeliveryArgs([
|
|
"--release",
|
|
"v1.7.0",
|
|
"--platform",
|
|
"ios",
|
|
"--ios-build-number",
|
|
"30",
|
|
]).iosBuildNumber,
|
|
).toBe("30");
|
|
});
|
|
|
|
test("rejects malformed release tags", () => {
|
|
expect(() => parseStoreDeliveryArgs(["--release", "latest"])).toThrow(
|
|
"stable vX.Y.Z",
|
|
);
|
|
});
|
|
|
|
test("uses the pinned official EAS CLI setup in store jobs", () => {
|
|
const workflow = readFileSync(
|
|
new URL("../.github/workflows/store-delivery.yml", import.meta.url),
|
|
"utf8",
|
|
);
|
|
|
|
expect(workflow).not.toContain("bunx eas-cli");
|
|
expect(workflow.match(/uses: expo\/expo-github-action@v8/g)).toHaveLength(
|
|
1,
|
|
);
|
|
expect(workflow.match(/eas-version: 21\.4\.0/g)).toHaveLength(1);
|
|
expect(workflow.match(/packager: npm/g)).toHaveLength(1);
|
|
expect(workflow).toContain("for attempt in 1 2 3");
|
|
expect(workflow).toContain("Dependency install failed on attempt ${attempt}/3");
|
|
expect(
|
|
workflow.match(
|
|
/edgeever-bun-cache-\$\{GITHUB_RUN_ID\}-\$\{GITHUB_RUN_ATTEMPT\}/g,
|
|
),
|
|
).toHaveLength(2);
|
|
expect(workflow).not.toContain("eas build");
|
|
expect(workflow).not.toContain("--platform ios");
|
|
expect(workflow).toContain("working-directory: apps/ios");
|
|
expect(workflow).toContain(
|
|
"group: edgeever-store-delivery-${{ inputs.release_tag }}-${{ inputs.platform }}",
|
|
);
|
|
expect(workflow).toContain(".edgeever-ci/scripts/xcode-cloud-control.py");
|
|
expect(workflow).toContain("--wait-valid");
|
|
expect(workflow).not.toContain("--require-sha");
|
|
expect(workflow).not.toContain("--git-ref");
|
|
expect(workflow).toContain("git show \"$source_sha:apps/ios/Config/Version.xcconfig\"");
|
|
expect(workflow).toContain("cloud_version");
|
|
expect(workflow).toContain("grep -E '^(build_number|app_store_build_id|build_run_id|source_sha|processing_state)='");
|
|
expect(workflow).toContain('refs/tags/${RELEASE_TAG}');
|
|
expect(workflow).toContain(
|
|
"APP_STORE_BUILD_NUMBER: ${{ steps.ios_build.outputs.build_number }}",
|
|
);
|
|
expect(workflow).toContain("APP_STORE_RELEASE_NOTES_EN:");
|
|
expect(workflow).toContain("missing Japanese What's New");
|
|
expect(workflow).not.toContain(
|
|
"Pass ios_build_number; do not build from apps/mobile or EAS.",
|
|
);
|
|
expect(workflow).toContain(
|
|
"APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.EDGEEVER_APPLE_API_ISSUER }}",
|
|
);
|
|
expect(workflow).toContain(
|
|
"APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.EDGEEVER_APPLE_API_KEY_ID }}",
|
|
);
|
|
expect(workflow).toContain(
|
|
"APP_STORE_CONNECT_API_KEY_P8_BASE64: ${{ secrets.EDGEEVER_APPLE_API_KEY_BASE64 }}",
|
|
);
|
|
expect(workflow).toContain(
|
|
'PRECHECK_INCLUDE_IN_APP_PURCHASES: "false"',
|
|
);
|
|
expect(workflow).toContain("for attempt in {1..20}");
|
|
expect(workflow).toContain(
|
|
'Build number: ${APP_STORE_BUILD_NUMBER} does not exist',
|
|
);
|
|
expect(workflow).toContain("retrying in 60 seconds (${attempt}/20)");
|
|
const fastfile = readFileSync(
|
|
new URL("../apps/ios/fastlane/Fastfile", import.meta.url),
|
|
"utf8",
|
|
);
|
|
expect(fastfile).toContain("precheck_include_in_app_purchases: false");
|
|
expect(fastfile).toContain("APP_STORE_RELEASE_NOTES_JA is required");
|
|
});
|
|
|
|
test("replaces the GitHub APK with the Play-signed universal APK", () => {
|
|
const workflow = readFileSync(
|
|
new URL("../.github/workflows/store-delivery.yml", import.meta.url),
|
|
"utf8",
|
|
);
|
|
|
|
expect(workflow).toContain("GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_BASE64");
|
|
expect(workflow).toContain("ANDROID_PLAY_APP_SIGNER_SHA256");
|
|
expect(workflow).toContain("scripts/download-play-universal-apk.mjs");
|
|
expect(workflow).toContain('gh release upload "$RELEASE_TAG" "$apk_path"');
|
|
expect(
|
|
workflow.match(/if: \$\{\{ !inputs\.recover_play_apk \}\}/g),
|
|
).toHaveLength(6);
|
|
expect(workflow).toContain("if: ${{ inputs.recover_play_apk }}");
|
|
expect(workflow).toContain(
|
|
"node --input-type=module -e \"await import('google-auth-library')\"",
|
|
);
|
|
});
|
|
|
|
test("can prepare a matching Draft before formal publication", () => {
|
|
const workflow = readFileSync(
|
|
new URL("../.github/workflows/store-delivery.yml", import.meta.url),
|
|
"utf8",
|
|
);
|
|
|
|
expect(workflow).toContain('if [[ "$release_is_draft" = "true" ]]');
|
|
expect(workflow).toContain('previous_tag="${release_tags[0]:-}"');
|
|
expect(workflow).toContain('git tag "$RELEASE_TAG" "$RELEASE_TARGET"');
|
|
expect(workflow).toContain("set -o pipefail");
|
|
expect(workflow).toContain(
|
|
"release_target: ${{ steps.release.outputs.release_target }}",
|
|
);
|
|
expect(workflow).toContain('gh release view "$RELEASE_TAG"');
|
|
expect(workflow).not.toContain("releases/tags/${RELEASE_TAG}");
|
|
expect(workflow).toContain(
|
|
"ref: ${{ steps.release.outputs.release_target }}",
|
|
);
|
|
expect(
|
|
workflow.match(/ref: \$\{\{ needs\.plan\.outputs\.release_target \}\}/g),
|
|
).toHaveLength(2);
|
|
expect(workflow).not.toContain("ref: ${{ inputs.release_tag }}");
|
|
expect(workflow).not.toContain(
|
|
'test "$(jq -r \'.draft\' <<<"$release")" = "false"',
|
|
);
|
|
});
|
|
|
|
test("uses a dedicated Draft audit that accepts only the Play app signer", () => {
|
|
const workflow = readFileSync(
|
|
new URL(
|
|
"../.github/workflows/android-play-signature-audit.yml",
|
|
import.meta.url,
|
|
),
|
|
"utf8",
|
|
);
|
|
|
|
expect(workflow).toContain("github.repository == 'tianma-if/edgeever'");
|
|
expect(workflow).toContain(
|
|
"permissions:\n # Draft Releases are only visible to a token with repository write access.\n contents: write",
|
|
);
|
|
expect(workflow).toContain('gh release view "$RELEASE_TAG"');
|
|
expect(workflow).toContain(
|
|
'test "$(jq -r \'.isDraft\' <<<"$release")" = "true"',
|
|
);
|
|
expect(workflow).toContain(
|
|
"ref: ${{ steps.release.outputs.target_commitish }}",
|
|
);
|
|
expect(workflow).not.toContain("ref: ${{ inputs.release_tag }}");
|
|
expect(workflow).toContain(
|
|
"EDGE_EVER_ANDROID_ALLOWED_SIGNER_SHA256: ${{ secrets.ANDROID_PLAY_APP_SIGNER_SHA256 }}",
|
|
);
|
|
expect(workflow).not.toContain(
|
|
"22bf52a9501c89020f5acc966960152c826bfa64f31e578e858d088f8cd75d87",
|
|
);
|
|
});
|
|
|
|
test("keeps the Play bundle and generated Release APK arm64-only", () => {
|
|
const workflow = readFileSync(
|
|
new URL("../.github/workflows/store-delivery.yml", import.meta.url),
|
|
"utf8",
|
|
);
|
|
const buildScript = readFileSync(
|
|
new URL("./build-android-local.sh", import.meta.url),
|
|
"utf8",
|
|
);
|
|
|
|
expect(workflow).toContain("EDGE_EVER_ANDROID_PLAY_ARCHS: arm64-v8a");
|
|
expect(workflow).toContain("Verify Play APK architecture");
|
|
expect(workflow).toContain('if [[ "$actual_archs" != "arm64-v8a" ]]');
|
|
expect(buildScript).toContain(
|
|
'PLAY_ARCHS="${EDGE_EVER_ANDROID_PLAY_ARCHS:-arm64-v8a}"',
|
|
);
|
|
expect(buildScript).toContain(
|
|
'if [[ "$ACTUAL_PLAY_ARCHS" != "$PLAY_ARCHS" ]]',
|
|
);
|
|
expect(buildScript).not.toContain("EDGE_EVER_ANDROID_ARCHS:-");
|
|
expect(buildScript).not.toContain("armeabi-v7a,arm64-v8a,x86,x86_64");
|
|
});
|
|
|
|
test("keeps the self-update install permission out of Android builds", () => {
|
|
const buildScript = readFileSync(
|
|
new URL("./build-android-local.sh", import.meta.url),
|
|
"utf8",
|
|
);
|
|
const appConfig = readFileSync(
|
|
new URL("../apps/mobile/app.json", import.meta.url),
|
|
"utf8",
|
|
);
|
|
|
|
expect(appConfig).not.toContain("android.permission.REQUEST_INSTALL_PACKAGES");
|
|
expect(buildScript).not.toContain("configure-android-package-permissions");
|
|
expect(buildScript).toContain(
|
|
'grep -q "android.permission.REQUEST_INSTALL_PACKAGES" "$PACKAGED_MANIFEST"',
|
|
);
|
|
});
|
|
});
|