Skip to content

Commit aa7c914

Browse files
Merge pull request #496 from codemagic-ci-cd/feature/get-latest-build-number-version-flag
Add `--version-string` flag to get-latest-build-number action
2 parents f6e8c27 + 6490f11 commit aa7c914

11 files changed

Lines changed: 226 additions & 11 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
Version 0.66.0
2+
-------------
3+
4+
**Features**
5+
- Add `--version-string` argument to `app-store-connect get-latest-build-number` to pin the build number lookup to a specific version string across both App Store and TestFlight. Resolves part of [issue #320](https://github.com/codemagic-ci-cd/cli-tools/issues/320).
6+
- `--pre-release-version` argument can now also be specified using `--version-string` for consistency with `--app-store-version`.
7+
- Fix `app-store-connect get-latest-build-number` to compare build numbers when App Store and TestFlight expose the same version string, instead of always preferring TestFlight.
8+
9+
**Docs**
10+
- Update docs for `app-store-connect get-latest-build-number`.
11+
- Update docs for `app-store-connect get-latest-testflight-build-number`.
12+
- Update docs for `app-store-connect builds list`.
13+
- Update docs for `app-store-connect apps builds`.
14+
115
Version 0.65.0
216
-------------
317

‎docs/app-store-connect/apps/builds.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ List only expired builds. Mutually exclusive with option `--not-expired`.
5555

5656

5757
List only not expired builds. Mutually exclusive with option `--expired`.
58-
##### `--pre-release-version=PRE_RELEASE_VERSION`
58+
##### `--pre-release-version, --version-string=PRE_RELEASE_VERSION`
5959

6060

6161
Version of the build published to Testflight that identifies an iteration of the bundle. The string can only contain one to three groups of numeric characters (0-9) separated by period in the format [Major].[Minor].[Patch]. For example `3.2.46`

‎docs/app-store-connect/builds/list.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ List only expired builds. Mutually exclusive with option `--not-expired`.
5353

5454

5555
List only not expired builds. Mutually exclusive with option `--expired`.
56-
##### `--pre-release-version=PRE_RELEASE_VERSION`
56+
##### `--pre-release-version, --version-string=PRE_RELEASE_VERSION`
5757

5858

5959
Version of the build published to Testflight that identifies an iteration of the bundle. The string can only contain one to three groups of numeric characters (0-9) separated by period in the format [Major].[Minor].[Patch]. For example `3.2.46`

‎docs/app-store-connect/get-latest-build-number.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ app-store-connect get-latest-build-number [-h] [--log-stream STREAM] [--no-color
1717
[--private-key PRIVATE_KEY]
1818
[--certificates-dir CERTIFICATES_DIRECTORY]
1919
[--profiles-dir PROFILES_DIRECTORY]
20+
[--version-string VERSION]
2021
[--platform PLATFORM]
2122
[--include-version]
2223
APPLICATION_ID_RESOURCE_ID
@@ -29,6 +30,10 @@ app-store-connect get-latest-build-number [-h] [--log-stream STREAM] [--no-color
2930
Application Apple ID. An automatically generated ID assigned to your app
3031
### Optional arguments for action `get-latest-build-number`
3132

33+
##### `--version-string=VERSION`
34+
35+
36+
Version of the build to look up. Pins the search to a specific version string across both App Store and TestFlight. The string can only contain one to three groups of numeric characters (0-9) separated by period in the format [Major].[Minor].[Patch]. For example `3.2.46`
3237
##### `--platform=IOS | MAC_OS | TV_OS`
3338

3439

‎docs/app-store-connect/get-latest-testflight-build-number.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ app-store-connect get-latest-testflight-build-number [-h] [--log-stream STREAM]
3232
Application Apple ID. An automatically generated ID assigned to your app
3333
### Optional arguments for action `get-latest-testflight-build-number`
3434

35-
##### `--pre-release-version=PRE_RELEASE_VERSION`
35+
##### `--pre-release-version, --version-string=PRE_RELEASE_VERSION`
3636

3737

3838
Version of the build published to Testflight that identifies an iteration of the bundle. The string can only contain one to three groups of numeric characters (0-9) separated by period in the format [Major].[Minor].[Patch]. For example `3.2.46`

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "codemagic-cli-tools"
3-
version = "0.65.0"
3+
version = "0.66.0"
44
description = "CLI tools used in Codemagic builds"
55
authors = [{ name = "Priit Lätt", email = "priit@nevercode.io" }]
66
requires-python = ">=3.8,<4"

‎src/codemagic/__version__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
__title__ = "codemagic-cli-tools"
22
__description__ = "CLI tools used in Codemagic builds"
3-
__version__ = "0.65.0.dev"
3+
__version__ = "0.66.0.dev"
44
__url__ = "https://github.com/codemagic-ci-cd/cli-tools"
55
__licence__ = "GNU General Public License v3.0"

‎src/codemagic/tools/app_store_connect/actions/latest_build_number_actions.py‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,26 +204,44 @@ class GetLatestBuildNumberAction(AbstractGetLatestBuildNumberAction, ABC):
204204
@cli.action(
205205
"get-latest-build-number",
206206
AppArgument.APPLICATION_ID_RESOURCE_ID,
207+
BuildNumberArgument.VERSION,
207208
CommonArgument.PLATFORM,
208209
BuildNumberArgument.INCLUDE_VERSION,
209210
)
210211
def get_latest_build_number(
211212
self,
212213
application_id: ResourceId,
214+
version: Optional[str] = None,
213215
platform: Optional[Platform] = None,
214216
include_version: Optional[bool] = None,
215217
) -> Optional[str]:
216218
"""
217219
Get the highest build number of the highest version used for the given app.
218220
"""
219-
app_store_build_info = self._get_app_store_latest_build_info(application_id, platform=platform)
220-
testflight_build_info = self._get_testflight_latest_build_info(application_id, platform=platform)
221+
app_store_build_info = self._get_app_store_latest_build_info(
222+
application_id,
223+
version_string=version,
224+
platform=platform,
225+
)
226+
testflight_build_info = self._get_testflight_latest_build_info(
227+
application_id,
228+
pre_release_version=version,
229+
platform=platform,
230+
)
221231

222232
latest_build_info: _LatestBuildInfo
223233
if app_store_build_info is not None and testflight_build_info is not None:
224234
asv = versions.parse_version(app_store_build_info.version)
225235
tfv = versions.parse_version(testflight_build_info.version)
226-
latest_build_info = app_store_build_info if asv > tfv else testflight_build_info
236+
if asv > tfv:
237+
latest_build_info = app_store_build_info
238+
elif tfv > asv:
239+
latest_build_info = testflight_build_info
240+
else:
241+
latest_build_info = max(
242+
(app_store_build_info, testflight_build_info),
243+
key=lambda b: versions.sorting_key(b.build_number),
244+
)
227245
elif app_store_build_info is not None:
228246
latest_build_info = app_store_build_info
229247
elif testflight_build_info is not None:

‎src/codemagic/tools/app_store_connect/arguments.py‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,7 @@ class BuildArgument(cli.Argument):
11681168
)
11691169
PRE_RELEASE_VERSION = cli.ArgumentProperties(
11701170
key="pre_release_version",
1171-
flags=("--pre-release-version",),
1171+
flags=("--pre-release-version", "--version-string"),
11721172
description=(
11731173
"Version of the build published to Testflight "
11741174
"that identifies an iteration of the bundle. "
@@ -1285,6 +1285,18 @@ class BuildNumberArgument(cli.Argument):
12851285
description="Explicitly show version string in command output in addition to build number",
12861286
argparse_kwargs={"required": False, "action": "store_true"},
12871287
)
1288+
VERSION = cli.ArgumentProperties(
1289+
key="version",
1290+
flags=("--version-string",),
1291+
description=(
1292+
"Version of the build to look up. Pins the search to a specific version "
1293+
"string across both App Store and TestFlight. "
1294+
"The string can only contain one to three groups of numeric characters (0-9) "
1295+
"separated by period in the format [Major].[Minor].[Patch]. "
1296+
f"For example `{Colors.WHITE('3.2.46')}`"
1297+
),
1298+
argparse_kwargs={"required": False},
1299+
)
12881300

12891301

12901302
class BundleIdArgument(cli.Argument):
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
from unittest import mock
2+
3+
import pytest
4+
5+
from codemagic.apple.app_store_connect import IssuerId
6+
from codemagic.apple.app_store_connect import KeyIdentifier
7+
from codemagic.apple.resources import ResourceId
8+
from codemagic.tools import AppStoreConnect
9+
from codemagic.tools.app_store_connect.actions.latest_build_number_actions import _LatestBuildInfo
10+
11+
12+
@pytest.fixture
13+
def app_store_connect() -> AppStoreConnect:
14+
return AppStoreConnect(
15+
issuer_id=IssuerId("issuer-id"),
16+
key_identifier=KeyIdentifier("key-identifier"),
17+
private_key="private-key",
18+
)
19+
20+
21+
def test_get_latest_build_number_forwards_version_to_both_sides(app_store_connect: AppStoreConnect):
22+
application_id = ResourceId("application-id")
23+
24+
asc_info = _LatestBuildInfo(
25+
build_id="asc-build-id",
26+
build_number="42",
27+
app_store_version="1.0.0",
28+
)
29+
tf_info = _LatestBuildInfo(
30+
build_id="tf-build-id",
31+
build_number="43",
32+
pre_release_version="1.0.0",
33+
)
34+
35+
with mock.patch.object(
36+
app_store_connect,
37+
"_get_app_store_latest_build_info",
38+
return_value=asc_info,
39+
) as mock_app_store, mock.patch.object(
40+
app_store_connect,
41+
"_get_testflight_latest_build_info",
42+
return_value=tf_info,
43+
) as mock_testflight, mock.patch.object(
44+
app_store_connect,
45+
"_log_latest_build_info",
46+
):
47+
result = app_store_connect.get_latest_build_number(application_id, version="1.0.0")
48+
49+
mock_app_store.assert_called_once_with(
50+
application_id,
51+
version_string="1.0.0",
52+
platform=None,
53+
)
54+
mock_testflight.assert_called_once_with(
55+
application_id,
56+
pre_release_version="1.0.0",
57+
platform=None,
58+
)
59+
assert result == "43"
60+
61+
62+
def test_get_latest_build_number_without_version_passes_none(app_store_connect: AppStoreConnect):
63+
application_id = ResourceId("application-id")
64+
65+
with mock.patch.object(
66+
app_store_connect,
67+
"_get_app_store_latest_build_info",
68+
return_value=None,
69+
) as mock_app_store, mock.patch.object(
70+
app_store_connect,
71+
"_get_testflight_latest_build_info",
72+
return_value=None,
73+
) as mock_testflight:
74+
result = app_store_connect.get_latest_build_number(application_id)
75+
76+
mock_app_store.assert_called_once_with(
77+
application_id,
78+
version_string=None,
79+
platform=None,
80+
)
81+
mock_testflight.assert_called_once_with(
82+
application_id,
83+
pre_release_version=None,
84+
platform=None,
85+
)
86+
assert result is None
87+
88+
89+
def test_get_latest_build_number_returns_match_when_only_one_side_has_build(
90+
app_store_connect: AppStoreConnect,
91+
):
92+
application_id = ResourceId("application-id")
93+
94+
tf_info = _LatestBuildInfo(
95+
build_id="tf-build-id",
96+
build_number="7",
97+
pre_release_version="2.0.0",
98+
)
99+
100+
with mock.patch.object(
101+
app_store_connect,
102+
"_get_app_store_latest_build_info",
103+
return_value=None,
104+
), mock.patch.object(
105+
app_store_connect,
106+
"_get_testflight_latest_build_info",
107+
return_value=tf_info,
108+
), mock.patch.object(
109+
app_store_connect,
110+
"_log_latest_build_info",
111+
):
112+
result = app_store_connect.get_latest_build_number(application_id, version="2.0.0")
113+
114+
assert result == "7"
115+
116+
117+
def test_get_latest_build_number_picks_higher_build_when_versions_equal(
118+
app_store_connect: AppStoreConnect,
119+
):
120+
application_id = ResourceId("application-id")
121+
122+
asc_info = _LatestBuildInfo(
123+
build_id="asc-build-id",
124+
build_number="120",
125+
app_store_version="3.2.46",
126+
)
127+
tf_info = _LatestBuildInfo(
128+
build_id="tf-build-id",
129+
build_number="90",
130+
pre_release_version="3.2.46",
131+
)
132+
133+
with mock.patch.object(
134+
app_store_connect,
135+
"_get_app_store_latest_build_info",
136+
return_value=asc_info,
137+
), mock.patch.object(
138+
app_store_connect,
139+
"_get_testflight_latest_build_info",
140+
return_value=tf_info,
141+
), mock.patch.object(
142+
app_store_connect,
143+
"_log_latest_build_info",
144+
):
145+
result = app_store_connect.get_latest_build_number(application_id, version="3.2.46")
146+
147+
assert result == "120"

0 commit comments

Comments
 (0)