[GH-ISSUE #82] Add end-to-end publish workflow #25

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

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

Summary

Add an end‑to‑end publish workflow that stitches together upload + distribution + submission into a single command. Today ASC has all the primitives (build upload prep, build attach, submit) but not a unified flow.

Proposed CLI (brainstorm)

asc publish testflight --app APP_ID --ipa app.ipa --group GROUP_ID[,GROUP_ID...] [--notify] [--wait]
asc publish appstore --app APP_ID --ipa app.ipa --version 1.2.3 --build-number 456 --submit --confirm

Flags (brainstorm)

  • --app, --ipa, --version, --build-number
  • --group to add to beta groups
  • --submit to App Store review (requires --confirm)
  • --wait to wait for processing / review
  • --timeout to override upload + processing wait
  • --output-format and --pretty for metadata

Workflow (brainstorm)

  1. Create build upload + upload file reservation
  2. Upload IPA to presigned URL(s) + commit upload file
  3. Wait for processing (optional)
  4. TestFlight: add build to groups, optionally notify testers
  5. App Store: create or fetch app store version, attach build, submit for review

API Endpoints

  • Build upload: /v1/buildUploads, /v1/buildUploadFiles, PATCH /v1/buildUploadFiles/{id}
  • Builds list/get: /v1/builds and /v1/builds/{id}
  • Versions + attach build: /v1/appStoreVersions, /v1/appStoreVersions/{id}
  • Submissions: /v1/appStoreVersionSubmissions
  • Beta groups relations: /v1/builds/{id}/relationships/betaGroups

Implementation Plan

  1. Extend build upload flow to support real file upload + commit (if not already done).
  2. Add cmd/publish.go (or cmd/release.go) to orchestrate steps.
  3. Add internal helpers for polling build processing state.
  4. Output a structured JSON summary (build ID, version ID, submission ID, group IDs, timings).

Tests

  • CLI validation tests (missing app/ipa/version conflicts).
  • Orchestration tests with stubbed client.
  • Upload + commit tests (can be shared with build upload flow issue).

Acceptance Criteria

  • Single command can upload and distribute to TestFlight or App Store.
  • Clear JSON metadata output with IDs and status.
  • No interactive prompts; explicit --confirm for submission.
Originally created by @rudrankriyam on GitHub (Jan 24, 2026). Original GitHub issue: https://github.com/rudrankriyam/App-Store-Connect-CLI/issues/82 ## Summary Add an end‑to‑end publish workflow that stitches together upload + distribution + submission into a single command. Today ASC has all the primitives (build upload prep, build attach, submit) but not a unified flow. ## Proposed CLI (brainstorm) ``` asc publish testflight --app APP_ID --ipa app.ipa --group GROUP_ID[,GROUP_ID...] [--notify] [--wait] asc publish appstore --app APP_ID --ipa app.ipa --version 1.2.3 --build-number 456 --submit --confirm ``` Flags (brainstorm) - `--app`, `--ipa`, `--version`, `--build-number` - `--group` to add to beta groups - `--submit` to App Store review (requires `--confirm`) - `--wait` to wait for processing / review - `--timeout` to override upload + processing wait - `--output-format` and `--pretty` for metadata ## Workflow (brainstorm) 1) Create build upload + upload file reservation 2) Upload IPA to presigned URL(s) + commit upload file 3) Wait for processing (optional) 4) TestFlight: add build to groups, optionally notify testers 5) App Store: create or fetch app store version, attach build, submit for review ## API Endpoints - Build upload: `/v1/buildUploads`, `/v1/buildUploadFiles`, `PATCH /v1/buildUploadFiles/{id}` - Builds list/get: `/v1/builds` and `/v1/builds/{id}` - Versions + attach build: `/v1/appStoreVersions`, `/v1/appStoreVersions/{id}` - Submissions: `/v1/appStoreVersionSubmissions` - Beta groups relations: `/v1/builds/{id}/relationships/betaGroups` ## Implementation Plan 1) Extend build upload flow to support real file upload + commit (if not already done). 2) Add `cmd/publish.go` (or `cmd/release.go`) to orchestrate steps. 3) Add internal helpers for polling build processing state. 4) Output a structured JSON summary (build ID, version ID, submission ID, group IDs, timings). ## Tests - CLI validation tests (missing app/ipa/version conflicts). - Orchestration tests with stubbed client. - Upload + commit tests (can be shared with build upload flow issue). ## Acceptance Criteria - Single command can upload and distribute to TestFlight or App Store. - Clear JSON metadata output with IDs and status. - No interactive prompts; explicit `--confirm` for submission.
kerem closed this issue 2026-02-26 21:32:52 +03:00
Author
Owner

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

@cursor

Implementation Guide

Codebase Context

This is a high-level orchestration command that stitches together existing primitives (build upload, version management, beta groups, submission). Depends on #78 (build upload flow) being complete.

Prerequisites

  • Issue #78 (Complete build upload flow) should be implemented first
  • Existing commands to reuse:
    • asc builds upload --upload for IPA upload
    • asc versions attach-build for attaching build to version
    • asc submit create for App Store submission
    • asc builds add-groups for TestFlight distribution

File Structure

1. internal/asc/publish.go (~100-150 lines)

// Result types for the publish workflow
type TestFlightPublishResult struct {
    BuildID       string   `json:"buildId"`
    BuildVersion  string   `json:"buildVersion,omitempty"`
    BuildNumber   string   `json:"buildNumber,omitempty"`
    GroupIDs      []string `json:"groupIds,omitempty"`
    Uploaded      bool     `json:"uploaded"`
    ProcessingState string `json:"processingState,omitempty"`
    Notified      bool     `json:"notified,omitempty"`
}

type AppStorePublishResult struct {
    BuildID       string `json:"buildId"`
    VersionID     string `json:"versionId"`
    SubmissionID  string `json:"submissionId,omitempty"`
    Uploaded      bool   `json:"uploaded"`
    Attached      bool   `json:"attached"`
    Submitted     bool   `json:"submitted"`
}

// Build processing states to poll for
const (
    BuildProcessingStateProcessing = "PROCESSING"
    BuildProcessingStateValid      = "VALID"
    BuildProcessingStateInvalid    = "INVALID"
)

2. internal/asc/client_publish.go (~100-150 lines)

// Polling helper for build processing
func (c *Client) WaitForBuildProcessing(ctx context.Context, buildID string, pollInterval time.Duration) (*BuildResponse, error) {
    ticker := time.NewTicker(pollInterval)
    defer ticker.Stop()

    for {
        select {
        case <-ctx.Done():
            return nil, ctx.Err()
        case <-ticker.C:
            build, err := c.GetBuild(ctx, buildID)
            if err != nil {
                return nil, err
            }

            state := build.Data.Attributes.ProcessingState
            switch state {
            case BuildProcessingStateValid:
                return build, nil
            case BuildProcessingStateInvalid:
                return nil, fmt.Errorf("build processing failed: %s", state)
            }
            // Continue polling if still PROCESSING
        }
    }
}

// Helper to find or create app store version
func (c *Client) FindOrCreateAppStoreVersion(ctx context.Context, appID, version, platform string) (*AppStoreVersionResponse, error) {
    // Try to find existing version
    versions, err := c.GetAppStoreVersions(ctx, appID)
    if err != nil {
        return nil, err
    }

    for _, v := range versions.Data {
        if v.Attributes.VersionString == version && v.Attributes.Platform == platform {
            return &AppStoreVersionResponse{Data: v}, nil
        }
    }

    // Create new version if not found
    return c.CreateAppStoreVersion(ctx, appID, version, platform)
}

3. cmd/publish.go (~350-400 lines)

func PublishCommand() *ffcli.Command {
    return &ffcli.Command{
        Name:       "publish",
        ShortUsage: "asc publish <subcommand> [flags]",
        ShortHelp:  "End-to-end publish workflows for TestFlight and App Store.",
        LongHelp: `End-to-end publish workflows.

Combines upload, distribution, and submission into single commands.

Examples:
  asc publish testflight --app APP_ID --ipa app.ipa --group GROUP_ID
  asc publish appstore --app APP_ID --ipa app.ipa --version 1.2.3 --submit --confirm`,
        UsageFunc: DefaultUsageFunc,
        Subcommands: []*ffcli.Command{
            PublishTestFlightCommand(),
            PublishAppStoreCommand(),
        },
        Exec: func(ctx context.Context, args []string) error {
            return flag.ErrHelp
        },
    }
}

func PublishTestFlightCommand() *ffcli.Command {
    fs := flag.NewFlagSet("publish testflight", flag.ExitOnError)

    appID := fs.String("app", "", "App ID (required)")
    ipaPath := fs.String("ipa", "", "Path to IPA file (required)")
    groupIDs := fs.String("group", "", "Beta group ID(s), comma-separated")
    notify := fs.Bool("notify", false, "Notify testers after adding to groups")
    wait := fs.Bool("wait", false, "Wait for build processing to complete")
    pollInterval := fs.Duration("poll-interval", 30*time.Second, "Polling interval for --wait")
    output := fs.String("output", "json", "Output format")
    pretty := fs.Bool("pretty", false, "Pretty-print JSON")

    return &ffcli.Command{
        Name:       "testflight",
        ShortUsage: "asc publish testflight [flags]",
        ShortHelp:  "Upload and distribute to TestFlight.",
        LongHelp: `Upload IPA and distribute to TestFlight beta groups.

Steps:
1. Upload IPA to App Store Connect
2. Wait for processing (if --wait)
3. Add build to specified beta groups
4. Optionally notify testers

Examples:
  asc publish testflight --app "123" --ipa app.ipa --group "GROUP_ID"
  asc publish testflight --app "123" --ipa app.ipa --group "G1,G2" --wait --notify`,
        FlagSet:   fs,
        UsageFunc: DefaultUsageFunc,
        Exec: func(ctx context.Context, args []string) error {
            // Validation
            // ...

            // Step 1: Upload IPA
            // (reuse existing upload logic from builds upload)

            // Step 2: Wait for processing if requested
            if *wait {
                build, err = client.WaitForBuildProcessing(ctx, buildID, *pollInterval)
                // ...
            }

            // Step 3: Add to beta groups
            groups := splitCSV(*groupIDs)
            for _, groupID := range groups {
                err := client.AddBuildToBetaGroup(ctx, buildID, groupID)
                // ...
            }

            // Step 4: Notify testers if requested
            // (handled by beta group settings or explicit API)

            return printOutput(result, *output, *pretty)
        },
    }
}

func PublishAppStoreCommand() *ffcli.Command {
    fs := flag.NewFlagSet("publish appstore", flag.ExitOnError)

    appID := fs.String("app", "", "App ID (required)")
    ipaPath := fs.String("ipa", "", "Path to IPA file (required)")
    version := fs.String("version", "", "Version string (e.g., 1.2.3)")
    buildNumber := fs.String("build-number", "", "Build number")
    platform := fs.String("platform", "IOS", "Platform: IOS, MAC_OS, TV_OS, VISION_OS")
    submit := fs.Bool("submit", false, "Submit for review after attaching build")
    confirm := fs.Bool("confirm", false, "Confirm submission (required with --submit)")
    wait := fs.Bool("wait", false, "Wait for build processing")
    pollInterval := fs.Duration("poll-interval", 30*time.Second, "Polling interval")
    output := fs.String("output", "json", "Output format")
    pretty := fs.Bool("pretty", false, "Pretty-print JSON")

    return &ffcli.Command{
        Name:       "appstore",
        ShortUsage: "asc publish appstore [flags]",
        ShortHelp:  "Upload and submit to App Store.",
        LongHelp: `Upload IPA, attach to version, and optionally submit for review.

Steps:
1. Upload IPA to App Store Connect
2. Wait for processing (if --wait)
3. Find or create App Store version
4. Attach build to version
5. Submit for review (if --submit --confirm)

Examples:
  asc publish appstore --app "123" --ipa app.ipa --version 1.2.3
  asc publish appstore --app "123" --ipa app.ipa --version 1.2.3 --submit --confirm`,
        FlagSet:   fs,
        UsageFunc: DefaultUsageFunc,
        Exec: func(ctx context.Context, args []string) error {
            // Validation
            if *submit && !*confirm {
                return fmt.Errorf("--confirm is required with --submit")
            }

            // Step 1: Upload IPA

            // Step 2: Wait for processing

            // Step 3: Find or create version
            versionResp, err := client.FindOrCreateAppStoreVersion(ctx, *appID, *version, *platform)

            // Step 4: Attach build to version
            err = client.AttachBuildToVersion(ctx, versionResp.Data.ID, buildID)

            // Step 5: Submit if requested
            if *submit {
                submission, err := client.CreateAppStoreVersionSubmission(ctx, versionResp.Data.ID)
                result.SubmissionID = submission.Data.ID
                result.Submitted = true
            }

            return printOutput(result, *output, *pretty)
        },
    }
}

4. Register in cmd/commands.go
Add PublishCommand() to RootCommand().Subcommands

Workflow Diagrams

TestFlight Flow:

IPA → Upload → [Wait for Processing] → Add to Beta Groups → [Notify Testers]

App Store Flow:

IPA → Upload → [Wait for Processing] → Find/Create Version → Attach Build → [Submit for Review]

CLI Usage Examples

# TestFlight: Upload and distribute to one group
asc publish testflight --app "123456789" --ipa ./app.ipa --group "GROUP_ID"

# TestFlight: Multiple groups with wait and notify
asc publish testflight --app "123456789" --ipa ./app.ipa --group "G1,G2,G3" --wait --notify

# App Store: Upload and attach to version
asc publish appstore --app "123456789" --ipa ./app.ipa --version 1.2.3

# App Store: Full flow with submission
asc publish appstore --app "123456789" --ipa ./app.ipa --version 1.2.3 --submit --confirm --wait

Testing

  • Run make test && make lint
  • Test validation: --submit requires --confirm
  • Test orchestration with stubbed client methods
  • Test timeout handling for --wait
<!-- gh-comment-id:3795331264 --> @rudrankriyam commented on GitHub (Jan 24, 2026): @cursor ## Implementation Guide ### Codebase Context This is a high-level orchestration command that stitches together existing primitives (build upload, version management, beta groups, submission). Depends on #78 (build upload flow) being complete. ### Prerequisites - Issue #78 (Complete build upload flow) should be implemented first - Existing commands to reuse: - `asc builds upload --upload` for IPA upload - `asc versions attach-build` for attaching build to version - `asc submit create` for App Store submission - `asc builds add-groups` for TestFlight distribution ### File Structure **1. `internal/asc/publish.go`** (~100-150 lines) ```go // Result types for the publish workflow type TestFlightPublishResult struct { BuildID string `json:"buildId"` BuildVersion string `json:"buildVersion,omitempty"` BuildNumber string `json:"buildNumber,omitempty"` GroupIDs []string `json:"groupIds,omitempty"` Uploaded bool `json:"uploaded"` ProcessingState string `json:"processingState,omitempty"` Notified bool `json:"notified,omitempty"` } type AppStorePublishResult struct { BuildID string `json:"buildId"` VersionID string `json:"versionId"` SubmissionID string `json:"submissionId,omitempty"` Uploaded bool `json:"uploaded"` Attached bool `json:"attached"` Submitted bool `json:"submitted"` } // Build processing states to poll for const ( BuildProcessingStateProcessing = "PROCESSING" BuildProcessingStateValid = "VALID" BuildProcessingStateInvalid = "INVALID" ) ``` **2. `internal/asc/client_publish.go`** (~100-150 lines) ```go // Polling helper for build processing func (c *Client) WaitForBuildProcessing(ctx context.Context, buildID string, pollInterval time.Duration) (*BuildResponse, error) { ticker := time.NewTicker(pollInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return nil, ctx.Err() case <-ticker.C: build, err := c.GetBuild(ctx, buildID) if err != nil { return nil, err } state := build.Data.Attributes.ProcessingState switch state { case BuildProcessingStateValid: return build, nil case BuildProcessingStateInvalid: return nil, fmt.Errorf("build processing failed: %s", state) } // Continue polling if still PROCESSING } } } // Helper to find or create app store version func (c *Client) FindOrCreateAppStoreVersion(ctx context.Context, appID, version, platform string) (*AppStoreVersionResponse, error) { // Try to find existing version versions, err := c.GetAppStoreVersions(ctx, appID) if err != nil { return nil, err } for _, v := range versions.Data { if v.Attributes.VersionString == version && v.Attributes.Platform == platform { return &AppStoreVersionResponse{Data: v}, nil } } // Create new version if not found return c.CreateAppStoreVersion(ctx, appID, version, platform) } ``` **3. `cmd/publish.go`** (~350-400 lines) ```go func PublishCommand() *ffcli.Command { return &ffcli.Command{ Name: "publish", ShortUsage: "asc publish <subcommand> [flags]", ShortHelp: "End-to-end publish workflows for TestFlight and App Store.", LongHelp: `End-to-end publish workflows. Combines upload, distribution, and submission into single commands. Examples: asc publish testflight --app APP_ID --ipa app.ipa --group GROUP_ID asc publish appstore --app APP_ID --ipa app.ipa --version 1.2.3 --submit --confirm`, UsageFunc: DefaultUsageFunc, Subcommands: []*ffcli.Command{ PublishTestFlightCommand(), PublishAppStoreCommand(), }, Exec: func(ctx context.Context, args []string) error { return flag.ErrHelp }, } } func PublishTestFlightCommand() *ffcli.Command { fs := flag.NewFlagSet("publish testflight", flag.ExitOnError) appID := fs.String("app", "", "App ID (required)") ipaPath := fs.String("ipa", "", "Path to IPA file (required)") groupIDs := fs.String("group", "", "Beta group ID(s), comma-separated") notify := fs.Bool("notify", false, "Notify testers after adding to groups") wait := fs.Bool("wait", false, "Wait for build processing to complete") pollInterval := fs.Duration("poll-interval", 30*time.Second, "Polling interval for --wait") output := fs.String("output", "json", "Output format") pretty := fs.Bool("pretty", false, "Pretty-print JSON") return &ffcli.Command{ Name: "testflight", ShortUsage: "asc publish testflight [flags]", ShortHelp: "Upload and distribute to TestFlight.", LongHelp: `Upload IPA and distribute to TestFlight beta groups. Steps: 1. Upload IPA to App Store Connect 2. Wait for processing (if --wait) 3. Add build to specified beta groups 4. Optionally notify testers Examples: asc publish testflight --app "123" --ipa app.ipa --group "GROUP_ID" asc publish testflight --app "123" --ipa app.ipa --group "G1,G2" --wait --notify`, FlagSet: fs, UsageFunc: DefaultUsageFunc, Exec: func(ctx context.Context, args []string) error { // Validation // ... // Step 1: Upload IPA // (reuse existing upload logic from builds upload) // Step 2: Wait for processing if requested if *wait { build, err = client.WaitForBuildProcessing(ctx, buildID, *pollInterval) // ... } // Step 3: Add to beta groups groups := splitCSV(*groupIDs) for _, groupID := range groups { err := client.AddBuildToBetaGroup(ctx, buildID, groupID) // ... } // Step 4: Notify testers if requested // (handled by beta group settings or explicit API) return printOutput(result, *output, *pretty) }, } } func PublishAppStoreCommand() *ffcli.Command { fs := flag.NewFlagSet("publish appstore", flag.ExitOnError) appID := fs.String("app", "", "App ID (required)") ipaPath := fs.String("ipa", "", "Path to IPA file (required)") version := fs.String("version", "", "Version string (e.g., 1.2.3)") buildNumber := fs.String("build-number", "", "Build number") platform := fs.String("platform", "IOS", "Platform: IOS, MAC_OS, TV_OS, VISION_OS") submit := fs.Bool("submit", false, "Submit for review after attaching build") confirm := fs.Bool("confirm", false, "Confirm submission (required with --submit)") wait := fs.Bool("wait", false, "Wait for build processing") pollInterval := fs.Duration("poll-interval", 30*time.Second, "Polling interval") output := fs.String("output", "json", "Output format") pretty := fs.Bool("pretty", false, "Pretty-print JSON") return &ffcli.Command{ Name: "appstore", ShortUsage: "asc publish appstore [flags]", ShortHelp: "Upload and submit to App Store.", LongHelp: `Upload IPA, attach to version, and optionally submit for review. Steps: 1. Upload IPA to App Store Connect 2. Wait for processing (if --wait) 3. Find or create App Store version 4. Attach build to version 5. Submit for review (if --submit --confirm) Examples: asc publish appstore --app "123" --ipa app.ipa --version 1.2.3 asc publish appstore --app "123" --ipa app.ipa --version 1.2.3 --submit --confirm`, FlagSet: fs, UsageFunc: DefaultUsageFunc, Exec: func(ctx context.Context, args []string) error { // Validation if *submit && !*confirm { return fmt.Errorf("--confirm is required with --submit") } // Step 1: Upload IPA // Step 2: Wait for processing // Step 3: Find or create version versionResp, err := client.FindOrCreateAppStoreVersion(ctx, *appID, *version, *platform) // Step 4: Attach build to version err = client.AttachBuildToVersion(ctx, versionResp.Data.ID, buildID) // Step 5: Submit if requested if *submit { submission, err := client.CreateAppStoreVersionSubmission(ctx, versionResp.Data.ID) result.SubmissionID = submission.Data.ID result.Submitted = true } return printOutput(result, *output, *pretty) }, } } ``` **4. Register in `cmd/commands.go`** Add `PublishCommand()` to RootCommand().Subcommands ### Workflow Diagrams **TestFlight Flow:** ``` IPA → Upload → [Wait for Processing] → Add to Beta Groups → [Notify Testers] ``` **App Store Flow:** ``` IPA → Upload → [Wait for Processing] → Find/Create Version → Attach Build → [Submit for Review] ``` ### CLI Usage Examples ```bash # TestFlight: Upload and distribute to one group asc publish testflight --app "123456789" --ipa ./app.ipa --group "GROUP_ID" # TestFlight: Multiple groups with wait and notify asc publish testflight --app "123456789" --ipa ./app.ipa --group "G1,G2,G3" --wait --notify # App Store: Upload and attach to version asc publish appstore --app "123456789" --ipa ./app.ipa --version 1.2.3 # App Store: Full flow with submission asc publish appstore --app "123456789" --ipa ./app.ipa --version 1.2.3 --submit --confirm --wait ``` ### Testing - Run `make test && make lint` - Test validation: --submit requires --confirm - Test orchestration with stubbed client methods - Test timeout handling for --wait
Author
Owner

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

Summary:

  • Added asc publish with testflight and appstore workflows (upload, build discovery, optional processing wait, group distribution, attach/submission).
  • Implemented publish helpers (WaitForBuildProcessing, FindOrCreate/Create App Store version) and new publish output types, plus notify-aware beta group additions.
  • Added IPA upload + commit logic with MD5 checksum and tests for publish validations/client helpers.

Tests:

  • make test
  • make lint

View PR
Open in Cursor Open in Web

<!-- gh-comment-id:3795331327 --> @cursor[bot] commented on GitHub (Jan 24, 2026): Summary: - Added `asc publish` with `testflight` and `appstore` workflows (upload, build discovery, optional processing wait, group distribution, attach/submission). - Implemented publish helpers (WaitForBuildProcessing, FindOrCreate/Create App Store version) and new publish output types, plus notify-aware beta group additions. - Added IPA upload + commit logic with MD5 checksum and tests for publish validations/client helpers. Tests: - `make test` - `make lint` <a href="https://github.com/rudrankriyam/App-Store-Connect-CLI/pull/110"><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-1702d37a-e6fc-4f2d-a1b0-4bfc535c4969"><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-1702d37a-e6fc-4f2d-a1b0-4bfc535c4969"><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 25, 2026):

Closed: implemented in #110.

<!-- gh-comment-id:3796237611 --> @rudrankriyam commented on GitHub (Jan 25, 2026): Closed: implemented in #110.
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#25
No description provided.