Skip to content

Commit

Permalink
Add support for publishing macOS builds to TestFlight and App Store (#…
Browse files Browse the repository at this point in the history
…340)

Co-authored-by: Nils Reichardt <[email protected]>
  • Loading branch information
nilsreichardt authored Sep 8, 2023
1 parent 45dfff5 commit e1aab86
Show file tree
Hide file tree
Showing 12 changed files with 343 additions and 28 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
Version 0.43.0
-------------

Additions and changes from [pull request #340](https://github.com/codemagic-ci-cd/cli-tools/pull/340). Resolves [issue #339](https://github.com/codemagic-ci-cd/cli-tools/issues/339).

**Features**
- Support submitting macOS packages to TestFlight using `app-store-connect publish --testflight`.
- Add new action `app-store-connect builds beta-details` to show beta detail information for specific build.
- Waiting for App Store Connect build processing also waits for beta builds details to be processed before returning.

**Development**
- Add new client method `read_beta_detail` to builds resource manager in `src/codemagic/apple/app_store_connect/builds/builds.py`.
- Add new definitions for App Store Connect models:
- `BuildBetaDetail` for https://developer.apple.com/documentation/appstoreconnectapi/buildbetadetail,
- `ExternalBetaState` enumeration for https://developer.apple.com/documentation/appstoreconnectapi/externalbetastate,
- `InternalBetaState` for https://developer.apple.com/documentation/appstoreconnectapi/internalbetastate.

**Documentation**
- Add documentation for action `app-store-connect builds betat-details`.

Special thanks for contribution to [@nilsreichardt](https://github.com/nilsreichardt).

Version 0.42.2
-------------

Expand Down
1 change: 1 addition & 0 deletions docs/app-store-connect/builds.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ Enable verbose logging for commands
|[`get`](builds/get.md)|Get information about a specific build|
|[`app`](builds/app.md)|Get the App details for a specific build.|
|[`app-store-version`](builds/app-store-version.md)|Get the App Store version of a specific build.|
|[`beta-details`](builds/beta-details.md)|Get Build Beta Details Information of a specific build.|
|[`pre-release-version`](builds/pre-release-version.md)|Get the prerelease version for a specific build|
|[`submit-to-app-store`](builds/submit-to-app-store.md)|Submit build to App Store review|
|[`submit-to-testflight`](builds/submit-to-testflight.md)|Submit build to TestFlight|
95 changes: 95 additions & 0 deletions docs/app-store-connect/builds/beta-details.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

beta-details
============


**Get Build Beta Details Information of a specific build.**
### Usage
```bash
app-store-connect builds beta-details [-h] [--log-stream STREAM] [--no-color] [--version] [-s] [-v]
[--log-api-calls]
[--api-unauthorized-retries UNAUTHORIZED_REQUEST_RETRIES]
[--api-server-error-retries SERVER_ERROR_RETRIES]
[--disable-jwt-cache]
[--json]
[--issuer-id ISSUER_ID]
[--key-id KEY_IDENTIFIER]
[--private-key PRIVATE_KEY]
[--certificates-dir CERTIFICATES_DIRECTORY]
[--profiles-dir PROFILES_DIRECTORY]
BUILD_ID_RESOURCE_ID
```
### Required arguments for action `beta-details`

##### `BUILD_ID_RESOURCE_ID`


Alphanumeric ID value of the Build
### Optional arguments for command `app-store-connect`

##### `--log-api-calls`


Turn on logging for App Store Connect API HTTP requests
##### `--api-unauthorized-retries, -r=UNAUTHORIZED_REQUEST_RETRIES`


Specify how many times the App Store Connect API request should be retried in case the called request fails due to an authentication error (401 Unauthorized response from the server). In case of the above authentication error, the request is retried usinga new JSON Web Token as many times until the number of retries is exhausted. If not given, the value will be checked from the environment variable `APP_STORE_CONNECT_API_UNAUTHORIZED_RETRIES`. [Default: 3]
##### `--api-server-error-retries=SERVER_ERROR_RETRIES`


Specify how many times the App Store Connect API request should be retried in case the called request fails due to a server error (response with status code 5xx). In case of server error, the request is retried until the number of retries is exhausted. If not given, the value will be checked from the environment variable `APP_STORE_CONNECT_API_SERVER_ERROR_RETRIES`. [Default: 3]
##### `--disable-jwt-cache`


Turn off caching App Store Connect JSON Web Tokens to disk. By default generated tokens are cached to disk to be reused between separate processes, which can can reduce number of false positive authentication errors from App Store Connect API. If not given, the value will be checked from the environment variable `APP_STORE_CONNECT_DISABLE_JWT_CACHE`.
##### `--json`


Whether to show the resource in JSON format
##### `--issuer-id=ISSUER_ID`


App Store Connect API Key Issuer ID. Identifies the issuer who created the authentication token. Learn more at https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api. If not given, the value will be checked from the environment variable `APP_STORE_CONNECT_ISSUER_ID`. Alternatively to entering `ISSUER_ID` in plaintext, it may also be specified using the `@env:` prefix followed by an environment variable name, or the `@file:` prefix followed by a path to the file containing the value. Example: `@env:<variable>` uses the value in the environment variable named `<variable>`, and `@file:<file_path>` uses the value from the file at `<file_path>`.
##### `--key-id=KEY_IDENTIFIER`


App Store Connect API Key ID. Learn more at https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api. If not given, the value will be checked from the environment variable `APP_STORE_CONNECT_KEY_IDENTIFIER`. Alternatively to entering `KEY_IDENTIFIER` in plaintext, it may also be specified using the `@env:` prefix followed by an environment variable name, or the `@file:` prefix followed by a path to the file containing the value. Example: `@env:<variable>` uses the value in the environment variable named `<variable>`, and `@file:<file_path>` uses the value from the file at `<file_path>`.
##### `--private-key=PRIVATE_KEY`


App Store Connect API private key used for JWT authentication to communicate with Apple services. Learn more at https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api. If not provided, the key will be searched from the following directories in sequence for a private key file with the name `AuthKey_<key_identifier>.p8`: private_keys, ~/private_keys, ~/.private_keys, ~/.appstoreconnect/private_keys, where <key_identifier> is the value of `--key-id`. If not given, the value will be checked from the environment variable `APP_STORE_CONNECT_PRIVATE_KEY`. Alternatively to entering `PRIVATE_KEY` in plaintext, it may also be specified using the `@env:` prefix followed by an environment variable name, or the `@file:` prefix followed by a path to the file containing the value. Example: `@env:<variable>` uses the value in the environment variable named `<variable>`, and `@file:<file_path>` uses the value from the file at `<file_path>`.
##### `--certificates-dir=CERTIFICATES_DIRECTORY`


Directory where the code signing certificates will be saved. Default:&nbsp;`$HOME/Library/MobileDevice/Certificates`
##### `--profiles-dir=PROFILES_DIRECTORY`


Directory where the provisioning profiles will be saved. Default:&nbsp;`$HOME/Library/MobileDevice/Provisioning Profiles`
### Common options

##### `-h, --help`


show this help message and exit
##### `--log-stream=stderr | stdout`


Log output stream. Default `stderr`
##### `--no-color`


Do not use ANSI colors to format terminal output
##### `--version`


Show tool version and exit
##### `-s, --silent`


Disable log output for commands
##### `-v, --verbose`


Enable verbose logging for commands
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "codemagic-cli-tools"
version = "0.42.2"
version = "0.43.0"
description = "CLI tools used in Codemagic builds"
readme = "README.md"
authors = [
Expand Down
2 changes: 1 addition & 1 deletion src/codemagic/__version__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__title__ = "codemagic-cli-tools"
__description__ = "CLI tools used in Codemagic builds"
__version__ = "0.42.2.dev"
__version__ = "0.43.0.dev"
__url__ = "https://github.com/codemagic-ci-cd/cli-tools"
__licence__ = "GNU General Public License v3.0"
13 changes: 13 additions & 0 deletions src/codemagic/apple/app_store_connect/builds/builds.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from codemagic.apple.resources import AppStoreVersion
from codemagic.apple.resources import BetaReviewState
from codemagic.apple.resources import Build
from codemagic.apple.resources import BuildBetaDetail
from codemagic.apple.resources import BuildProcessingState
from codemagic.apple.resources import LinkedResourceData
from codemagic.apple.resources import PreReleaseVersion
Expand Down Expand Up @@ -118,6 +119,18 @@ def read_pre_release_version(self, build: Union[Build, ResourceId]) -> Optional[
return None
return PreReleaseVersion(response["data"])

def read_beta_detail(self, build: Union[Build, ResourceId]) -> BuildBetaDetail:
"""
https://developer.apple.com/documentation/appstoreconnectapi/read_the_build_beta_details_information_of_a_build
"""
url = None
if isinstance(build, Build) and build.relationships is not None:
url = build.relationships.buildBetaDetail.links.related
if url is None:
url = f"{self.client.API_URL}/builds/{build}/buildBetaDetail"
response = self.client.session.get(url).json()
return BuildBetaDetail(response["data"])

def read_with_include(
self,
build: Union[LinkedResourceData, ResourceId],
Expand Down
3 changes: 3 additions & 0 deletions src/codemagic/apple/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .beta_build_localization import BetaBuildLocalization
from .beta_group import BetaGroup
from .build import Build
from .build_beta_detail import BuildBetaDetail
from .bundle_id import BundleId
from .bundle_id_capability import BundleIdCapability
from .bundle_id_capability import CapabilitySetting
Expand All @@ -24,6 +25,8 @@
from .enums import ContentRightsDeclaration
from .enums import DeviceClass
from .enums import DeviceStatus
from .enums import ExternalBetaState
from .enums import InternalBetaState
from .enums import Locale
from .enums import Platform
from .enums import ProfileState
Expand Down
34 changes: 34 additions & 0 deletions src/codemagic/apple/resources/build_beta_detail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import Optional

from .enums import ExternalBetaState
from .enums import InternalBetaState
from .resource import Relationship
from .resource import Resource


class BuildBetaDetail(Resource):
"""
https://developer.apple.com/documentation/appstoreconnectapi/buildbetadetail
"""

attributes: Attributes
relationships: Optional[Relationships] = None

@dataclass
class Attributes(Resource.Attributes):
autoNotifyEnabled: bool
externalBuildState: ExternalBetaState
internalBuildState: InternalBetaState

def __post_init__(self):
if isinstance(self.externalBuildState, str):
self.externalBuildState = ExternalBetaState(self.externalBuildState)
if isinstance(self.internalBuildState, str):
self.internalBuildState = InternalBetaState(self.internalBuildState)

@dataclass
class Relationships(Resource.Relationships):
build: Relationship
26 changes: 26 additions & 0 deletions src/codemagic/apple/resources/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,31 @@ class DeviceStatus(ResourceEnum):
ENABLED = "ENABLED"


class ExternalBetaState(ResourceEnum):
BETA_APPROVED = "BETA_APPROVED"
BETA_REJECTED = "BETA_REJECTED"
EXPIRED = "EXPIRED"
IN_BETA_REVIEW = "IN_BETA_REVIEW"
IN_BETA_TESTING = "IN_BETA_TESTING"
IN_EXPORT_COMPLIANCE_REVIEW = "IN_EXPORT_COMPLIANCE_REVIEW"
MISSING_EXPORT_COMPLIANCE = "MISSING_EXPORT_COMPLIANCE"
PROCESSING = "PROCESSING"
PROCESSING_EXCEPTION = "PROCESSING_EXCEPTION"
READY_FOR_BETA_SUBMISSION = "READY_FOR_BETA_SUBMISSION"
READY_FOR_BETA_TESTING = "READY_FOR_BETA_TESTING"
WAITING_FOR_BETA_REVIEW = "WAITING_FOR_BETA_REVIEW"


class InternalBetaState(ResourceEnum):
EXPIRED = "EXPIRED"
IN_BETA_TESTING = "IN_BETA_TESTING"
IN_EXPORT_COMPLIANCE_REVIEW = "IN_EXPORT_COMPLIANCE_REVIEW"
MISSING_EXPORT_COMPLIANCE = "MISSING_EXPORT_COMPLIANCE"
PROCESSING = "PROCESSING"
PROCESSING_EXCEPTION = "PROCESSING_EXCEPTION"
READY_FOR_BETA_TESTING = "READY_FOR_BETA_TESTING"


class Platform(ResourceEnum):
IOS = "IOS"
MAC_OS = "MAC_OS"
Expand Down Expand Up @@ -246,6 +271,7 @@ class ResourceType(ResourceEnum):
BETA_APP_LOCALIZATIONS = "betaAppLocalizations"
BETA_APP_REVIEW_DETAILS = "betaAppReviewDetails"
BETA_APP_REVIEW_SUBMISSIONS = "betaAppReviewSubmissions"
BETA_BUILD_DETAILS = "buildBetaDetails"
BETA_BUILD_LOCALIZATIONS = "betaBuildLocalizations"
BETA_GROUPS = "betaGroups"
BUILDS = "builds"
Expand Down
Loading

0 comments on commit e1aab86

Please sign in to comment.