diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a1cdee2c..8a4d3ec8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -491,6 +491,10 @@ jobs: -authenticationKeyPath $ASC_KEY_PATH \ -authenticationKeyID $ASC_KEY_ID \ -authenticationKeyIssuerID $ASC_KEY_ISSUER_ID + - name: Publish to TestFlight + if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch' + run: |- + go run -v ./cmd/internal/app_store_connect publish_testflight ${{ matrix.platform }} - name: Build image if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch' run: |- diff --git a/cmd/internal/app_store_connect/main.go b/cmd/internal/app_store_connect/main.go index 75e00475..f2193a45 100644 --- a/cmd/internal/app_store_connect/main.go +++ b/cmd/internal/app_store_connect/main.go @@ -54,12 +54,12 @@ const ( groupID = "5c5f3b78-b7a0-40c0-bcad-e6ef87bbefda" ) -func createClient() *Client { +func createClient(expireDuration time.Duration) *Client { privateKey, err := os.ReadFile(os.Getenv("ASC_KEY_PATH")) if err != nil { log.Fatal(err) } - tokenConfig, err := asc.NewTokenConfig(os.Getenv("ASC_KEY_ID"), os.Getenv("ASC_KEY_ISSUER_ID"), time.Minute, privateKey) + tokenConfig, err := asc.NewTokenConfig(os.Getenv("ASC_KEY_ID"), os.Getenv("ASC_KEY_ISSUER_ID"), expireDuration, privateKey) if err != nil { log.Fatal(err) } @@ -67,7 +67,7 @@ func createClient() *Client { } func fetchMacOSVersion(ctx context.Context) error { - client := createClient() + client := createClient(time.Minute) versions, _, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{ FilterPlatform: []string{"MAC_OS"}, }) @@ -105,71 +105,101 @@ func publishTestflight(ctx context.Context) error { return err } tag := tagVersion.VersionString() - client := createClient() + client := createClient(10 * time.Minute) + log.Info(tag, " list build IDs") buildIDsResponse, _, err := client.TestFlight.ListBuildIDsForBetaGroup(ctx, groupID, nil) if err != nil { return err } - buildIDS := common.Map(buildIDsResponse.Data, func(it asc.RelationshipData) string { + buildIDs := common.Map(buildIDsResponse.Data, func(it asc.RelationshipData) string { return it.ID }) - for _, platform := range []asc.Platform{ - asc.PlatformIOS, - asc.PlatformMACOS, - asc.PlatformTVOS, - } { + var platforms []asc.Platform + if len(os.Args) == 3 { + switch os.Args[2] { + case "ios": + platforms = []asc.Platform{asc.PlatformIOS} + case "macos": + platforms = []asc.Platform{asc.PlatformMACOS} + case "tvos": + platforms = []asc.Platform{asc.PlatformTVOS} + default: + return E.New("unknown platform: ", os.Args[2]) + } + } else { + platforms = []asc.Platform{ + asc.PlatformIOS, + asc.PlatformMACOS, + asc.PlatformTVOS, + } + } + for _, platform := range platforms { log.Info(string(platform), " list builds") - builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{ - FilterApp: []string{appID}, - FilterPreReleaseVersionPlatform: []string{string(platform)}, - }) - if err != nil { - return err - } - log.Info(string(platform), " ", tag, " list localizations") - localizations, _, err := client.TestFlight.ListBetaBuildLocalizationsForBuild(ctx, builds.Data[0].ID, nil) - if err != nil { - return err - } - localization := common.Find(localizations.Data, func(it asc.BetaBuildLocalization) bool { - return *it.Attributes.Locale == "en-US" - }) - if localization.ID == "" { - log.Fatal(string(platform), " ", tag, " no en-US localization found") - } - if localization.Attributes == nil || localization.Attributes.WhatsNew == nil || *localization.Attributes.WhatsNew == "" { - log.Info(string(platform), " ", tag, " update localization") - _, _, err = client.TestFlight.UpdateBetaBuildLocalization(ctx, localization.ID, common.Ptr( - F.ToString("sing-box ", tag), - )) + for { + builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{ + FilterApp: []string{appID}, + FilterPreReleaseVersionPlatform: []string{string(platform)}, + }) if err != nil { return err } - } - if !common.Contains(buildIDS, builds.Data[0].ID) { + build := builds.Data[0] + if common.Contains(buildIDs, build.ID) || time.Since(build.Attributes.UploadedDate.Time) > 5*time.Minute { + log.Info(string(platform), " ", tag, " waiting for process") + time.Sleep(15 * time.Second) + continue + } + if *build.Attributes.ProcessingState != "VALID" { + log.Info(string(platform), " ", tag, " waiting for process: ", *build.Attributes.ProcessingState) + time.Sleep(15 * time.Second) + continue + } + log.Info(string(platform), " ", tag, " list localizations") + localizations, _, err := client.TestFlight.ListBetaBuildLocalizationsForBuild(ctx, build.ID, nil) + if err != nil { + return err + } + localization := common.Find(localizations.Data, func(it asc.BetaBuildLocalization) bool { + return *it.Attributes.Locale == "en-US" + }) + if localization.ID == "" { + log.Fatal(string(platform), " ", tag, " no en-US localization found") + } + if localization.Attributes == nil || localization.Attributes.WhatsNew == nil || *localization.Attributes.WhatsNew == "" { + log.Info(string(platform), " ", tag, " update localization") + _, _, err = client.TestFlight.UpdateBetaBuildLocalization(ctx, localization.ID, common.Ptr( + F.ToString("sing-box ", tag), + )) + if err != nil { + return err + } + } log.Info(string(platform), " ", tag, " publish") - _, err = client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{builds.Data[0].ID}) + response, err := client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{build.ID}) + if response != nil && response.StatusCode == http.StatusUnprocessableEntity { + log.Info("waiting for process") + time.Sleep(15 * time.Second) + continue + } else if err != nil { + return err + } + log.Info(string(platform), " ", tag, " list submissions") + betaSubmissions, _, err := client.TestFlight.ListBetaAppReviewSubmissions(ctx, &asc.ListBetaAppReviewSubmissionsQuery{ + FilterBuild: []string{build.ID}, + }) if err != nil { return err } - } - log.Info(string(platform), " ", tag, " list submissions") - betaSubmissions, _, err := client.TestFlight.ListBetaAppReviewSubmissions(ctx, &asc.ListBetaAppReviewSubmissionsQuery{ - FilterBuild: []string{builds.Data[0].ID}, - }) - if err != nil { - return err - } - if len(betaSubmissions.Data) == 0 { - log.Info(string(platform), " ", tag, " create submission") - _, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, builds.Data[0].ID) - if err != nil { - return err + if len(betaSubmissions.Data) == 0 { + log.Info(string(platform), " ", tag, " create submission") + _, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, build.ID) + if err != nil { + return err + } } - continue + break } - } return nil } @@ -187,7 +217,7 @@ func cancelAppStore(ctx context.Context, platform string) error { if err != nil { return err } - client := createClient() + client := createClient(time.Minute) log.Info(platform, " list versions") versions, _, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{ FilterPlatform: []string{string(platform)}, @@ -222,7 +252,7 @@ func prepareAppStore(ctx context.Context) error { if err != nil { return err } - client := createClient() + client := createClient(time.Minute) for _, platform := range []asc.Platform{ asc.PlatformIOS, asc.PlatformMACOS, @@ -364,7 +394,7 @@ func publishAppStore(ctx context.Context) error { if err != nil { return err } - client := createClient() + client := createClient(time.Minute) for _, platform := range []asc.Platform{ asc.PlatformIOS, asc.PlatformMACOS,