[GH-ISSUE #50] Add builds add/remove beta groups #12

Closed
opened 2026-02-26 21:32:45 +03:00 by kerem · 3 comments
Owner

Originally created by @rudrankriyam on GitHub (Jan 23, 2026).
Original GitHub issue: https://github.com/rudrankriyam/App-Store-Connect-CLI/issues/50

Summary

Add commands to attach/detach beta groups to a build.

API endpoints

CLI

  • asc builds add-groups --build "BUILD_ID" --group "GROUP_ID[,GROUP_ID...]"
  • asc builds remove-groups --build "BUILD_ID" --group "GROUP_ID[,GROUP_ID...]"

Output

  • JSON minified default; --output table|markdown; --pretty for JSON.

Tests

  • Relationship POST/DELETE client tests
  • CLI tests for payload shaping and validation
Originally created by @rudrankriyam on GitHub (Jan 23, 2026). Original GitHub issue: https://github.com/rudrankriyam/App-Store-Connect-CLI/issues/50 ## Summary Add commands to attach/detach beta groups to a build. ## API endpoints - `POST /v1/builds/{id}/relationships/betaGroups` (add) - `DELETE /v1/builds/{id}/relationships/betaGroups` (remove) - Relationship docs are under the Build resource: https://developer.apple.com/documentation/appstoreconnectapi/build ## CLI - `asc builds add-groups --build "BUILD_ID" --group "GROUP_ID[,GROUP_ID...]"` - `asc builds remove-groups --build "BUILD_ID" --group "GROUP_ID[,GROUP_ID...]"` ## Output - JSON minified default; `--output table|markdown`; `--pretty` for JSON. ## Tests - Relationship POST/DELETE client tests - CLI tests for payload shaping and validation
kerem closed this issue 2026-02-26 21:32:46 +03:00
Author
Owner

@rudrankriyam commented on GitHub (Jan 23, 2026):

@cursor Please implement this issue.

Implementation Guide

1. Add Commands to cmd/builds.go

Add two new subcommands to BuildsCommand():

BuildsAddGroupsCommand()
BuildsRemoveGroupsCommand()

2. Add Client Methods in internal/asc/client.go

// AddBetaGroupsToBuild adds beta groups to a build for TestFlight distribution
// POST /v1/builds/{id}/relationships/betaGroups
func (c *Client) AddBetaGroupsToBuild(ctx context.Context, buildID string, groupIDs []string) error {
    url := fmt.Sprintf("%s/builds/%s/relationships/betaGroups", baseURL, buildID)
    payload := RelationshipRequest{
        Data: make([]RelationshipData, len(groupIDs)),
    }
    for i, id := range groupIDs {
        payload.Data[i] = RelationshipData{Type: "betaGroups", ID: id}
    }
    // POST request
}

// RemoveBetaGroupsFromBuild removes beta groups from a build
// DELETE /v1/builds/{id}/relationships/betaGroups
func (c *Client) RemoveBetaGroupsFromBuild(ctx context.Context, buildID string, groupIDs []string) error {
    // DELETE with body
}

3. CLI Flags

For add-groups:

  • --build (required, build ID)
  • --group (required, comma-separated beta group IDs)

For remove-groups:

  • --build (required, build ID)
  • --group (required, comma-separated beta group IDs)

4. Command Implementation

func BuildsAddGroupsCommand() *ffcli.Command {
    fs := flag.NewFlagSet("add-groups", flag.ExitOnError)
    buildID := fs.String("build", "", "Build ID")
    groups := fs.String("group", "", "Comma-separated beta group IDs")
    
    return &ffcli.Command{
        Name:       "add-groups",
        ShortUsage: "asc builds add-groups --build BUILD_ID --group GROUP_ID[,GROUP_ID...]",
        ShortHelp:  "Add beta groups to a build for TestFlight distribution.",
        LongHelp: `Add beta groups to a build for TestFlight distribution.

Examples:
  asc builds add-groups --build "BUILD_ID" --group "GROUP_ID"
  asc builds add-groups --build "BUILD_ID" --group "GROUP1,GROUP2"`,
        // ...
    }
}

5. Testing

Unit tests in cmd/commands_test.go:

{"builds add-groups missing build", []string{"builds", "add-groups"}, ...}
{"builds add-groups missing group", []string{"builds", "add-groups", "--build", "X"}, ...}
{"builds remove-groups validation", ...}

Client tests in internal/asc/client_http_test.go:

  • Test relationship payload structure for builds
  • Test DELETE with body

Live API test:

# Get build and group IDs
./asc builds list --app 6747745091 --limit 1
./asc beta-groups list --app 6747745091

# Test (use Beta group: e601afa2-e59f-472f-9280-e35ab2f9bbe9)
./asc builds add-groups --build "BUILD_ID" --group "e601afa2-e59f-472f-9280-e35ab2f9bbe9"
./asc builds remove-groups --build "BUILD_ID" --group "e601afa2-e59f-472f-9280-e35ab2f9bbe9"

6. Use Case

This is how you distribute a build to TestFlight groups:

  1. Upload build (via Xcode)
  2. Wait for processing
  3. asc builds add-groups --build X --group Y to enable TestFlight for that group

7. Success Output

fmt.Fprintf(os.Stderr, "Successfully added %d group(s) to build %s\n", len(groupIDs), buildID)
fmt.Fprintf(os.Stderr, "Successfully removed %d group(s) from build %s\n", len(groupIDs), buildID)

8. Code Standards

  • Use strings.TrimSpace() on build ID and all group IDs
  • Reuse parseCommaSeparatedIDs() helper
  • Reuse RelationshipRequest type from other implementations
  • Run make format && make lint && make test before committing
<!-- gh-comment-id:3792267059 --> @rudrankriyam commented on GitHub (Jan 23, 2026): @cursor Please implement this issue. ## Implementation Guide ### 1. Add Commands to `cmd/builds.go` Add two new subcommands to `BuildsCommand()`: ```go BuildsAddGroupsCommand() BuildsRemoveGroupsCommand() ``` ### 2. Add Client Methods in `internal/asc/client.go` ```go // AddBetaGroupsToBuild adds beta groups to a build for TestFlight distribution // POST /v1/builds/{id}/relationships/betaGroups func (c *Client) AddBetaGroupsToBuild(ctx context.Context, buildID string, groupIDs []string) error { url := fmt.Sprintf("%s/builds/%s/relationships/betaGroups", baseURL, buildID) payload := RelationshipRequest{ Data: make([]RelationshipData, len(groupIDs)), } for i, id := range groupIDs { payload.Data[i] = RelationshipData{Type: "betaGroups", ID: id} } // POST request } // RemoveBetaGroupsFromBuild removes beta groups from a build // DELETE /v1/builds/{id}/relationships/betaGroups func (c *Client) RemoveBetaGroupsFromBuild(ctx context.Context, buildID string, groupIDs []string) error { // DELETE with body } ``` ### 3. CLI Flags For `add-groups`: - `--build` (required, build ID) - `--group` (required, comma-separated beta group IDs) For `remove-groups`: - `--build` (required, build ID) - `--group` (required, comma-separated beta group IDs) ### 4. Command Implementation ```go func BuildsAddGroupsCommand() *ffcli.Command { fs := flag.NewFlagSet("add-groups", flag.ExitOnError) buildID := fs.String("build", "", "Build ID") groups := fs.String("group", "", "Comma-separated beta group IDs") return &ffcli.Command{ Name: "add-groups", ShortUsage: "asc builds add-groups --build BUILD_ID --group GROUP_ID[,GROUP_ID...]", ShortHelp: "Add beta groups to a build for TestFlight distribution.", LongHelp: `Add beta groups to a build for TestFlight distribution. Examples: asc builds add-groups --build "BUILD_ID" --group "GROUP_ID" asc builds add-groups --build "BUILD_ID" --group "GROUP1,GROUP2"`, // ... } } ``` ### 5. Testing **Unit tests** in `cmd/commands_test.go`: ```go {"builds add-groups missing build", []string{"builds", "add-groups"}, ...} {"builds add-groups missing group", []string{"builds", "add-groups", "--build", "X"}, ...} {"builds remove-groups validation", ...} ``` **Client tests** in `internal/asc/client_http_test.go`: - Test relationship payload structure for builds - Test DELETE with body **Live API test**: ```bash # Get build and group IDs ./asc builds list --app 6747745091 --limit 1 ./asc beta-groups list --app 6747745091 # Test (use Beta group: e601afa2-e59f-472f-9280-e35ab2f9bbe9) ./asc builds add-groups --build "BUILD_ID" --group "e601afa2-e59f-472f-9280-e35ab2f9bbe9" ./asc builds remove-groups --build "BUILD_ID" --group "e601afa2-e59f-472f-9280-e35ab2f9bbe9" ``` ### 6. Use Case This is how you distribute a build to TestFlight groups: 1. Upload build (via Xcode) 2. Wait for processing 3. `asc builds add-groups --build X --group Y` to enable TestFlight for that group ### 7. Success Output ```go fmt.Fprintf(os.Stderr, "Successfully added %d group(s) to build %s\n", len(groupIDs), buildID) fmt.Fprintf(os.Stderr, "Successfully removed %d group(s) from build %s\n", len(groupIDs), buildID) ``` ### 8. Code Standards - Use `strings.TrimSpace()` on build ID and all group IDs - Reuse `parseCommaSeparatedIDs()` helper - Reuse `RelationshipRequest` type from other implementations - Run `make format && make lint && make test` before committing
Author
Owner

@cursor[bot] commented on GitHub (Jan 23, 2026):

Summary:

  • Added builds add-groups and builds remove-groups commands with JSON/table/markdown output and success messaging, plus a shared comma-separated ID parser.
  • Implemented build beta group relationship add/remove client methods and a new CLI result type with table/markdown formatting.
  • Added CLI validation tests, client HTTP payload tests (including DELETE body), and output formatting tests.

Tests:

  • make format
  • make lint
  • make test

Notes:

  • Live API tests were not run in this environment.

View PR
Open in Cursor Open in Web

<!-- gh-comment-id:3792267416 --> @cursor[bot] commented on GitHub (Jan 23, 2026): Summary: - Added `builds add-groups` and `builds remove-groups` commands with JSON/table/markdown output and success messaging, plus a shared comma-separated ID parser. - Implemented build beta group relationship add/remove client methods and a new CLI result type with table/markdown formatting. - Added CLI validation tests, client HTTP payload tests (including DELETE body), and output formatting tests. Tests: - `make format` - `make lint` - `make test` Notes: - Live API tests were not run in this environment. <a href="https://github.com/rudrankriyam/App-Store-Connect-CLI/pull/58"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/view-pr-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/view-pr-light.svg"><img alt="View PR" src="https://cursor.com/view-pr-light.svg"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-774b8e87-c568-4c28-831f-cebd69affce9"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/open-in-cursor-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/open-in-cursor-light.svg"><img alt="Open in Cursor" src="https://cursor.com/open-in-cursor.svg"></picture></a>&nbsp;<a href="https://cursor.com/agents?id=bc-774b8e87-c568-4c28-831f-cebd69affce9"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/open-in-web-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/open-in-web-light.svg"><img alt="Open in Web" src="https://cursor.com/open-in-web.svg"></picture></a>
Author
Owner

@rudrankriyam commented on GitHub (Jan 23, 2026):

Implemented in PR #58 - builds add-groups and remove-groups commands.

<!-- gh-comment-id:3792500027 --> @rudrankriyam commented on GitHub (Jan 23, 2026): Implemented in PR #58 - builds add-groups and remove-groups commands.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/App-Store-Connect-CLI#12
No description provided.