diff --git a/.gitignore b/.gitignore index 30bf3e7..0d89584 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ Local.xcconfig # App Review contact details for Scripts/asc.py — a person's phone and email Store/review-contact.local.json + +# Python bytecode from running Scripts/asc.py +__pycache__/ diff --git a/README.md b/README.md index 78e5110..d7c00c4 100644 --- a/README.md +++ b/README.md @@ -66,8 +66,8 @@ Requires macOS 14+, and Xcode 16+ with [XcodeGen](https://github.com/yonaskolb/X GitCleared is built from source: ```sh -git clone https://github.com/hollandcloud/cleared.git -cd cleared +git clone https://github.com/hollandcloud/gitcleared.git +cd gitcleared make install # builds Release, copies it to /Applications, launches it ``` diff --git a/Scripts/asc.py b/Scripts/asc.py index 7170a7f..b449a12 100755 --- a/Scripts/asc.py +++ b/Scripts/asc.py @@ -17,6 +17,8 @@ Scripts/asc.py status # what ASC currently has. Run this first. Scripts/asc.py version 1.1.1 # open a new version once the last one is on sale Scripts/asc.py text # name, subtitle, description, keywords, URLs + Scripts/asc.py info # category and the content-rights answer + Scripts/asc.py pricing # free, everywhere but China mainland (one-time) # release notes come from # release_notes-.txt where present Scripts/asc.py screenshots # upload Store/screenshots// @@ -370,6 +372,133 @@ def push_text() -> None: print(f" {platform}: {', '.join(attributes)}{note}") +def read_meta(name: str) -> str | None: + """A field that is not per-language, from Store/metadata/.txt.""" + path = STORE / "metadata" / f"{name}.txt" + return path.read_text().strip() if path.exists() else None + + +def push_info() -> None: + """App Information that is not per-language: the category, and the content-rights answer. + + Both hang off the app rather than a version. The category is set on whichever + `appInfo` is still editable — once a version is on sale its `appInfo` is frozen and a + new one is opened alongside it for the next. + """ + app = app_id() + + rights = read_meta("content_rights") + if rights: + call("PATCH", f"/v1/apps/{app}", { + "data": {"type": "apps", "id": app, "attributes": {"contentRightsDeclaration": rights}} + }) + print(f" content rights: {rights}") + + category = read_meta("primary_category") + if category: + for info in call("GET", f"/v1/apps/{app}/appInfos")["data"]: + state = info["attributes"].get("appStoreState") or info["attributes"].get("state") + if state not in ("PREPARE_FOR_SUBMISSION", "DEVELOPER_REJECTED", "REJECTED", "METADATA_REJECTED"): + continue + call("PATCH", f"/v1/appInfos/{info['id']}", { + "data": { + "type": "appInfos", + "id": info["id"], + "relationships": { + "primaryCategory": {"data": {"type": "appCategories", "id": category}} + }, + } + }) + print(f" primary category: {category}") + + +# Territories the app is deliberately not offered in. China mainland requires an ICP +# filing number before an app can be listed there, which this app does not have. +EXCLUDED_TERRITORIES = {"CHN"} + + +def push_pricing() -> None: + """Free, in every territory except the excluded ones, and in new territories as Apple adds them. + + Both are one-time set-ups on a new app — neither exists until something creates it, + and App Store Connect will not accept a submission without them — so this creates + whichever is missing and leaves an existing one alone rather than guessing at an edit. + """ + app = app_id() + + def exists(path: str) -> bool: + try: + return bool(call("GET", path, tolerate=True).get("data")) + except ASCError as error: + if error.status == 404: + return False + raise + + if exists(f"/v1/apps/{app}/appPriceSchedule"): + print(" price: already set, left alone") + else: + free = [ + point + for point in call( + "GET", + f"/v1/apps/{app}/appPricePoints?filter[territory]=USA&limit=5" + "&fields[appPricePoints]=customerPrice", + )["data"] + if float(point["attributes"]["customerPrice"]) == 0 + ] + if not free: + sys.exit("no free price point found for the USA") + call("POST", "/v1/appPriceSchedules", { + "data": { + "type": "appPriceSchedules", + "relationships": { + "app": {"data": {"type": "apps", "id": app}}, + "baseTerritory": {"data": {"type": "territories", "id": "USA"}}, + "manualPrices": {"data": [{"type": "appPrices", "id": "${free}"}]}, + }, + }, + "included": [{ + "type": "appPrices", + "id": "${free}", + "attributes": {"startDate": None}, + "relationships": { + "appPricePoint": {"data": {"type": "appPricePoints", "id": free[0]["id"]}} + }, + }], + }) + print(" price: free") + + if exists(f"/v1/apps/{app}/appAvailabilityV2"): + print(" availability: already set, left alone") + else: + territories = [ + t["id"] for t in call("GET", "/v1/territories?limit=200")["data"] + if t["id"] not in EXCLUDED_TERRITORIES + ] + call("POST", "/v2/appAvailabilities", { + "data": { + "type": "appAvailabilities", + "attributes": {"availableInNewTerritories": True}, + "relationships": { + "app": {"data": {"type": "apps", "id": app}}, + "territoryAvailabilities": { + "data": [{"type": "territoryAvailabilities", "id": f"${{{t}}}"} for t in territories] + }, + }, + }, + "included": [ + { + "type": "territoryAvailabilities", + "id": f"${{{t}}}", + "attributes": {"available": True}, + "relationships": {"territory": {"data": {"type": "territories", "id": t}}}, + } + for t in territories + ], + }) + print(f" availability: {len(territories)} territories, not {', '.join(sorted(EXCLUDED_TERRITORIES))}") + + def reviewer_notes() -> str | None: """The reviewer-facing block out of `review-notes.md`. @@ -841,6 +970,10 @@ def main() -> None: push_review() elif command == "agerating": push_age_rating() + elif command == "info": + push_info() + elif command == "pricing": + push_pricing() elif command == "version": if len(sys.argv) < 3: sys.exit("usage: asc.py version ") @@ -851,6 +984,7 @@ def main() -> None: submit_for_review() elif command == "all": push_text() + push_info() push_screenshots() push_review() push_age_rating() diff --git a/Sources/Cleared/GitHub/GitHubClient.swift b/Sources/Cleared/GitHub/GitHubClient.swift index b911ee5..f3df730 100644 --- a/Sources/Cleared/GitHub/GitHubClient.swift +++ b/Sources/Cleared/GitHub/GitHubClient.swift @@ -78,7 +78,7 @@ actor GitHubClient { config.timeoutIntervalForRequest = 20 config.waitsForConnectivity = false config.httpAdditionalHeaders = [ - "User-Agent": "\(AppEnvironment.name)/\(AppEnvironment.version) (+https://github.com/hollandcloud/cleared)", + "User-Agent": "\(AppEnvironment.name)/\(AppEnvironment.version) (+https://github.com/hollandcloud/gitcleared)", ] self.session = URLSession(configuration: config) } diff --git a/Store/metadata/content_rights.txt b/Store/metadata/content_rights.txt new file mode 100644 index 0000000..06f4edf --- /dev/null +++ b/Store/metadata/content_rights.txt @@ -0,0 +1 @@ +USES_THIRD_PARTY_CONTENT diff --git a/Store/metadata/en-US/keywords.txt b/Store/metadata/en-US/keywords.txt index 9228a69..ea7f257 100644 --- a/Store/metadata/en-US/keywords.txt +++ b/Store/metadata/en-US/keywords.txt @@ -1 +1 @@ -github,deploy,deployment,approve,actions,pull request,merge,ci,cd,devops,workflow,menu bar,release +git,github,cicd,devops,deploy,deployment,approve,actions,pull request,merge,workflow,menu bar diff --git a/Store/metadata/en-US/marketing_url.txt b/Store/metadata/en-US/marketing_url.txt index e6ff7a5..76062f6 100644 --- a/Store/metadata/en-US/marketing_url.txt +++ b/Store/metadata/en-US/marketing_url.txt @@ -1 +1 @@ -https://github.com/hollandcloud/cleared +https://github.com/hollandcloud/gitcleared diff --git a/Store/metadata/en-US/name.txt b/Store/metadata/en-US/name.txt index fa6a69d..c5ae8c1 100644 --- a/Store/metadata/en-US/name.txt +++ b/Store/metadata/en-US/name.txt @@ -1 +1 @@ -GitCleared +Git Cleared diff --git a/Store/metadata/en-US/privacy_policy_url.txt b/Store/metadata/en-US/privacy_policy_url.txt index 203782d..798c033 100644 --- a/Store/metadata/en-US/privacy_policy_url.txt +++ b/Store/metadata/en-US/privacy_policy_url.txt @@ -1 +1 @@ -https://github.com/hollandcloud/cleared/blob/main/docs/privacy.md +https://github.com/hollandcloud/gitcleared/blob/main/docs/privacy.md diff --git a/Store/metadata/en-US/support_url.txt b/Store/metadata/en-US/support_url.txt index 7893e41..cd25d75 100644 --- a/Store/metadata/en-US/support_url.txt +++ b/Store/metadata/en-US/support_url.txt @@ -1 +1 @@ -https://github.com/hollandcloud/cleared/blob/main/docs/support.md +https://github.com/hollandcloud/gitcleared/blob/main/docs/support.md diff --git a/Store/metadata/primary_category.txt b/Store/metadata/primary_category.txt new file mode 100644 index 0000000..99a5bd9 --- /dev/null +++ b/Store/metadata/primary_category.txt @@ -0,0 +1 @@ +DEVELOPER_TOOLS diff --git a/Store/screenshots/mac/01-queue.png b/Store/screenshots/mac/01-queue.png index 3208703..ccbab83 100644 Binary files a/Store/screenshots/mac/01-queue.png and b/Store/screenshots/mac/01-queue.png differ diff --git a/Store/screenshots/mac/02-all-clear.png b/Store/screenshots/mac/02-all-clear.png index 6e4e902..ab40c03 100644 Binary files a/Store/screenshots/mac/02-all-clear.png and b/Store/screenshots/mac/02-all-clear.png differ diff --git a/Store/screenshots/mac/03-start-a-workflow.png b/Store/screenshots/mac/03-start-a-workflow.png index b0c9de5..f419ec2 100644 Binary files a/Store/screenshots/mac/03-start-a-workflow.png and b/Store/screenshots/mac/03-start-a-workflow.png differ diff --git a/Store/screenshots/mac/04-settings.png b/Store/screenshots/mac/04-settings.png index 80344a7..a6bc0dc 100644 Binary files a/Store/screenshots/mac/04-settings.png and b/Store/screenshots/mac/04-settings.png differ diff --git a/Store/screenshots/mac/05-connect.png b/Store/screenshots/mac/05-connect.png index ec1b8a0..f15d65b 100644 Binary files a/Store/screenshots/mac/05-connect.png and b/Store/screenshots/mac/05-connect.png differ diff --git a/Store/screenshots/raw/all-clear.png b/Store/screenshots/raw/all-clear.png index 71fe018..4b54660 100644 Binary files a/Store/screenshots/raw/all-clear.png and b/Store/screenshots/raw/all-clear.png differ diff --git a/Store/screenshots/raw/connect.png b/Store/screenshots/raw/connect.png index 4a534b9..4264bc8 100644 Binary files a/Store/screenshots/raw/connect.png and b/Store/screenshots/raw/connect.png differ diff --git a/Store/screenshots/raw/queue.png b/Store/screenshots/raw/queue.png index 1f6e4fa..d098c22 100644 Binary files a/Store/screenshots/raw/queue.png and b/Store/screenshots/raw/queue.png differ diff --git a/Store/screenshots/raw/settings.png b/Store/screenshots/raw/settings.png index 409b427..0dbe5be 100644 Binary files a/Store/screenshots/raw/settings.png and b/Store/screenshots/raw/settings.png differ diff --git a/Store/screenshots/raw/start-a-workflow.png b/Store/screenshots/raw/start-a-workflow.png index c0f92dc..6895562 100644 Binary files a/Store/screenshots/raw/start-a-workflow.png and b/Store/screenshots/raw/start-a-workflow.png differ diff --git a/docs/privacy.md b/docs/privacy.md index c636a6d..8433374 100644 --- a/docs/privacy.md +++ b/docs/privacy.md @@ -55,6 +55,6 @@ a new date, and the change will be visible in the repository's history. ## Contact Questions about this policy: open an issue at -https://github.com/hollandcloud/cleared/issues +https://github.com/hollandcloud/gitcleared/issues GitCleared is an independent project and is not affiliated with GitHub. diff --git a/docs/support.md b/docs/support.md index f19d083..89eee40 100644 --- a/docs/support.md +++ b/docs/support.md @@ -5,7 +5,7 @@ listing. ## Getting help or reporting a bug -Open an issue: **https://github.com/hollandcloud/cleared/issues** +Open an issue: **https://github.com/hollandcloud/gitcleared/issues** It helps to include: