Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions .github/scripts/app_store_connect_release.rb
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,19 @@ def distribute_testflight(build)
raise AppStoreConnectError, "TestFlight build cannot be distributed: #{state}"
end
end
@client.post("/v1/builds/#{build_id}/relationships/betaGroups", data: selected.map do |group|
{ type: "betaGroups", id: group.fetch("id") }
end)
puts "Assigned #{@options[:platform]} build #{@options[:build_number]} to #{@options[:testflight_groups].join(', ')}"
selected.each do |group|
# Automatic internal groups already receive every build; Apple rejects
# manually assigning them. The group endpoint supports manual groups.
if group.dig("attributes", "isInternalGroup") && group.dig("attributes", "hasAccessToAllBuilds")
puts "#{group.dig('attributes', 'name')} already has automatic access to all builds"
next
end

@client.post("/v1/betaGroups/#{group.fetch('id')}/relationships/builds", data: [
{ type: "builds", id: build_id }
])
end
puts "Distributed #{@options[:platform]} build #{@options[:build_number]} to #{@options[:testflight_groups].join(', ')}"
end

def find_or_create_version
Expand Down
29 changes: 26 additions & 3 deletions .github/scripts/test/app_store_connect_release_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def patch(path, body)

def beta_groups
[
{ 'id' => 'internal', 'attributes' => { 'name' => 'Internal Testing', 'isInternalGroup' => true } },
{ 'id' => 'internal', 'attributes' => { 'name' => 'Internal Testing', 'isInternalGroup' => true, 'hasAccessToAllBuilds' => true } },
{ 'id' => 'external', 'attributes' => { 'name' => 'External Testing', 'isInternalGroup' => false } }
]
end
Expand All @@ -131,11 +131,34 @@ def test_testflight_uses_only_existing_groups_and_beta_review
release(client, process_only: false, testflight_groups: ['Internal Testing', 'External Testing']).run
end
assert client.mutations.any? { |_, path, _| path == '/v1/betaAppReviewSubmissions' }
assert_equal '/v1/builds/uploaded-build/relationships/betaGroups', client.mutations.last[1]
assert_equal %w[internal external], client.mutations.last[2][:data].map { |group| group[:id] }
assignments = client.mutations.select { |_, path, _| path.start_with?('/v1/betaGroups/') }
assert_equal [[:post, '/v1/betaGroups/external/relationships/builds',
{ data: [{ type: 'builds', id: 'uploaded-build' }] }]], assignments
refute client.mutations.any? { |_, path, _| path.end_with?('/relationships/betaGroups') }
refute client.mutations.any? { |_, path, _| path.include?('appStoreVersions') || path.include?('reviewSubmissions') }
end

def test_automatic_internal_group_needs_no_assignment_or_beta_review
client = BetaClient.new([build], groups: beta_groups)
output, = capture_io do
release(client, process_only: false, testflight_groups: ['Internal Testing']).run
end
assert_match(/already has automatic access/, output)
refute client.mutations.any? { |_, path, _| path.include?('/relationships/') || path == '/v1/betaAppReviewSubmissions' }
end

def test_manual_internal_group_uses_group_build_relationship_without_beta_review
groups = beta_groups
groups.first['attributes']['hasAccessToAllBuilds'] = false
client = BetaClient.new([build], groups: groups)
capture_io do
release(client, process_only: false, testflight_groups: ['Internal Testing']).run
end
assert_equal [:post, '/v1/betaGroups/internal/relationships/builds',
{ data: [{ type: 'builds', id: 'uploaded-build' }] }], client.mutations.last
refute client.mutations.any? { |_, path, _| path == '/v1/betaAppReviewSubmissions' }
end

def test_missing_testflight_group_fails_without_changing_apple
client = BetaClient.new([build], groups: beta_groups)
assert_raises(AppStoreConnectError) do
Expand Down
4 changes: 4 additions & 0 deletions open_wearable/docs/apple-builds.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ Xcode Cloud main-branch distribution settings, including automatic notification
when Apple makes an external build available. It does not submit a public App
Store release.

Internal groups with automatic access to all builds require no manual assignment.
Other internal and external groups receive the build through their group build
relationship; the build-to-groups endpoint rejects internal groups.

The workflow can also be run manually against a chosen ref. Manual runs default
to upload-only validation; enable `distribute` to send the build to those groups.

Expand Down
Loading