mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-10-20 16:24:25 +00:00
659 lines
22 KiB
Go
659 lines
22 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"os"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/sagernet/asc-go/asc"
|
|
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
|
"github.com/sagernet/sing-box/common/badversion"
|
|
"github.com/sagernet/sing-box/log"
|
|
"github.com/sagernet/sing/common"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
F "github.com/sagernet/sing/common/format"
|
|
)
|
|
|
|
func main() {
|
|
ctx := context.Background()
|
|
switch os.Args[1] {
|
|
case "next_project_version":
|
|
if len(os.Args) < 3 {
|
|
log.Fatal("platform required: ios, macos, or tvos")
|
|
}
|
|
err := fetchNextProjectVersion(ctx, os.Args[2])
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
case "publish_testflight":
|
|
err := publishTestflight(ctx)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
case "list_builds":
|
|
if len(os.Args) < 3 {
|
|
log.Fatal("platform required: ios, macos, or tvos")
|
|
}
|
|
err := listBuilds(ctx, os.Args[2])
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
case "remove_testflight":
|
|
if len(os.Args) < 5 {
|
|
log.Fatal("usage: remove_testflight <platform> <version> <build>")
|
|
}
|
|
err := removeTestflight(ctx, os.Args[2], os.Args[3], os.Args[4])
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
case "cancel_app_store":
|
|
if len(os.Args) < 3 {
|
|
log.Fatal("platform required: ios, macos, or tvos")
|
|
}
|
|
err := cancelAppStore(ctx, os.Args[2])
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
case "submit_app_store":
|
|
err := submitAppStore(ctx, os.Args[2:])
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
case "publish_app_store":
|
|
err := publishAppStore(ctx, os.Args[2:])
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
default:
|
|
log.Fatal("unknown action: ", os.Args[1])
|
|
}
|
|
}
|
|
|
|
const (
|
|
appID = "6785326793"
|
|
groupID = "39f9ebdc-05d4-421f-9595-dae71df227c4"
|
|
|
|
applePath = "../sing-box-for-apple"
|
|
appStorePromotionalText = "The universal proxy platform."
|
|
appStoreWhatsNew = "Fixes and improvements"
|
|
buildPollInterval = 15 * time.Second
|
|
)
|
|
|
|
var (
|
|
appStorePlatforms = []string{"ios", "tvos"}
|
|
platformDirectories = map[asc.Platform]string{
|
|
asc.PlatformIOS: "SFI",
|
|
asc.PlatformMACOS: "SFM",
|
|
asc.PlatformTVOS: "SFT",
|
|
}
|
|
)
|
|
|
|
func parsePlatform(name string) (asc.Platform, error) {
|
|
switch name {
|
|
case "ios":
|
|
return asc.PlatformIOS, nil
|
|
case "macos":
|
|
return asc.PlatformMACOS, nil
|
|
case "tvos":
|
|
return asc.PlatformTVOS, nil
|
|
default:
|
|
return "", E.New("unknown platform: ", name)
|
|
}
|
|
}
|
|
|
|
func createClient(expireDuration time.Duration) *asc.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"), expireDuration, privateKey)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return asc.NewClient(tokenConfig.Client())
|
|
}
|
|
|
|
func fetchNextProjectVersion(ctx context.Context, platformName string) error {
|
|
platform, err := parsePlatform(platformName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
query := &asc.ListBuildsQuery{
|
|
FilterApp: []string{appID},
|
|
FilterPreReleaseVersionPlatform: []string{string(platform)},
|
|
Limit: 200,
|
|
}
|
|
if platform != asc.PlatformMACOS {
|
|
tagVersion, err := build_shared.ReadTagVersion()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
query.FilterPreReleaseVersionVersion = []string{build_shared.TestFlightVersion(tagVersion)}
|
|
}
|
|
|
|
client := createClient(time.Minute)
|
|
builds, _, err := client.Builds.ListBuilds(ctx, query)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
nextProjectVersion := 1
|
|
var projectVersion int
|
|
for _, build := range builds.Data {
|
|
projectVersion, err = strconv.Atoi(*build.Attributes.Version)
|
|
if err != nil {
|
|
return E.Cause(err, "parse version code")
|
|
}
|
|
if projectVersion >= nextProjectVersion {
|
|
nextProjectVersion = projectVersion + 1
|
|
}
|
|
}
|
|
os.Stdout.WriteString(F.ToString(nextProjectVersion, "\n"))
|
|
return nil
|
|
}
|
|
|
|
func listBuilds(ctx context.Context, platformName string) error {
|
|
platform, err := parsePlatform(platformName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
client := createClient(time.Minute)
|
|
buildIDsResponse, _, err := client.TestFlight.ListBuildIDsForBetaGroup(ctx, groupID, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
betaBuildIDs := common.Map(buildIDsResponse.Data, func(it asc.RelationshipData) string {
|
|
return it.ID
|
|
})
|
|
builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{
|
|
FilterApp: []string{appID},
|
|
FilterPreReleaseVersionPlatform: []string{string(platform)},
|
|
Include: []string{"preReleaseVersion"},
|
|
Sort: []string{"-uploadedDate"},
|
|
Limit: 10,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, build := range builds.Data {
|
|
var betaGroup string
|
|
if slices.Contains(betaBuildIDs, build.ID) {
|
|
betaGroup = " testflight"
|
|
}
|
|
var versionString string
|
|
if build.Relationships != nil && build.Relationships.PreReleaseVersion != nil && build.Relationships.PreReleaseVersion.Data != nil {
|
|
for _, included := range builds.Included {
|
|
preReleaseVersion := included.PrereleaseVersion()
|
|
if preReleaseVersion != nil && preReleaseVersion.ID == build.Relationships.PreReleaseVersion.Data.ID {
|
|
versionString = *preReleaseVersion.Attributes.Version
|
|
}
|
|
}
|
|
}
|
|
os.Stdout.WriteString(F.ToString(versionString, " (", *build.Attributes.Version, ") ", *build.Attributes.ProcessingState, " ", build.Attributes.UploadedDate.Time.Format(time.RFC3339), betaGroup, "\n"))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func publishTestflight(ctx context.Context) error {
|
|
if len(os.Args) < 3 {
|
|
return E.New("platform required: ios, macos, or tvos")
|
|
}
|
|
platform, err := parsePlatform(os.Args[2])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tagVersion, err := build_shared.ReadTagVersion()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tag := tagVersion.VersionString()
|
|
testFlightVersion := build_shared.TestFlightVersion(tagVersion)
|
|
projectVersion := os.Getenv(strings.ToUpper(os.Args[2]) + "_PROJECT_VERSION")
|
|
|
|
releaseNotes := F.ToString("sing-box ", tagVersion.String())
|
|
if len(os.Args) >= 4 {
|
|
releaseNotes = strings.Join(os.Args[3:], " ")
|
|
}
|
|
|
|
client := createClient(20 * 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 {
|
|
return it.ID
|
|
})
|
|
|
|
query := &asc.ListBuildsQuery{
|
|
FilterApp: []string{appID},
|
|
FilterPreReleaseVersionPlatform: []string{string(platform)},
|
|
FilterPreReleaseVersionVersion: []string{testFlightVersion},
|
|
Sort: []string{"-uploadedDate"},
|
|
Limit: 1,
|
|
}
|
|
if projectVersion != "" {
|
|
query.FilterVersion = []string{projectVersion}
|
|
}
|
|
log.Info(string(platform), " ", testFlightVersion, " (", projectVersion, ") list builds")
|
|
for {
|
|
builds, _, err := client.Builds.ListBuilds(ctx, query)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(builds.Data) == 0 {
|
|
log.Info(string(platform), " ", testFlightVersion, " waiting for build upload")
|
|
time.Sleep(buildPollInterval)
|
|
continue
|
|
}
|
|
build := builds.Data[0]
|
|
log.Info(string(platform), " ", testFlightVersion, " found build: ", build.ID, " (", *build.Attributes.Version, ")")
|
|
if projectVersion == "" && common.Contains(buildIDs, build.ID) {
|
|
log.Info(string(platform), " ", testFlightVersion, " build ", *build.Attributes.Version, " already published, waiting for new upload")
|
|
time.Sleep(buildPollInterval)
|
|
continue
|
|
}
|
|
if *build.Attributes.ProcessingState != "VALID" {
|
|
log.Info(string(platform), " ", testFlightVersion, " waiting for process: ", *build.Attributes.ProcessingState)
|
|
time.Sleep(buildPollInterval)
|
|
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(releaseNotes))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
log.Info(string(platform), " ", tag, " publish")
|
|
response, err := client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{build.ID})
|
|
if response != nil && (response.StatusCode == http.StatusUnprocessableEntity || response.StatusCode == http.StatusNotFound) {
|
|
log.Info("waiting for process")
|
|
time.Sleep(buildPollInterval)
|
|
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
|
|
}
|
|
if len(betaSubmissions.Data) == 0 {
|
|
log.Info(string(platform), " ", tag, " create submission")
|
|
_, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, build.ID)
|
|
if err != nil {
|
|
if strings.Contains(err.Error(), "ANOTHER_BUILD_IN_REVIEW") {
|
|
log.Error(err)
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
break
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func removeTestflight(ctx context.Context, platformName string, versionString string, buildNumber string) error {
|
|
platform, err := parsePlatform(platformName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
client := createClient(time.Minute)
|
|
builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{
|
|
FilterApp: []string{appID},
|
|
FilterPreReleaseVersionPlatform: []string{string(platform)},
|
|
FilterPreReleaseVersionVersion: []string{versionString},
|
|
FilterVersion: []string{buildNumber},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(builds.Data) != 1 {
|
|
return E.New(string(platform), " ", versionString, " (", buildNumber, ") matched ", len(builds.Data), " builds")
|
|
}
|
|
build := builds.Data[0]
|
|
log.Info(string(platform), " ", versionString, " (", buildNumber, ") remove from beta group")
|
|
_, err = client.Builds.RemoveAccessForBetaGroupsFromBuild(ctx, build.ID, []string{groupID})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cancelAppStore(ctx context.Context, platformName string) error {
|
|
platform, err := parsePlatform(platformName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tag, err := build_shared.ReadTag()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
client := createClient(time.Minute)
|
|
log.Info(string(platform), " list versions")
|
|
versions, _, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{
|
|
FilterPlatform: []string{string(platform)},
|
|
FilterVersionString: []string{tag},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(versions.Data) == 0 {
|
|
return nil
|
|
}
|
|
versionID := versions.Data[0].ID
|
|
log.Info(string(platform), " ", tag, " list review submissions")
|
|
submissions, _, err := client.Submission.ListReviewSubmissions(ctx, &asc.ListReviewSubmissionsQuery{
|
|
FilterApp: []string{appID},
|
|
FilterPlatform: []string{string(platform)},
|
|
FilterState: []string{
|
|
string(asc.ReviewSubmissionStateReadyForReview),
|
|
string(asc.ReviewSubmissionStateWaitingForReview),
|
|
string(asc.ReviewSubmissionStateInReview),
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, submission := range submissions.Data {
|
|
containsVersion, err := reviewSubmissionContainsVersion(ctx, client, submission.ID, versionID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !containsVersion {
|
|
continue
|
|
}
|
|
log.Info(string(platform), " ", tag, " cancel review submission ", submission.ID)
|
|
_, _, err = client.Submission.UpdateReviewSubmission(ctx, submission.ID, nil, common.Ptr(true))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func reviewSubmissionContainsVersion(ctx context.Context, client *asc.Client, submissionID string, versionID string) (bool, error) {
|
|
items, _, err := client.Submission.ListItemsForReviewSubmission(ctx, submissionID, nil)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return slices.ContainsFunc(items.Data, func(it asc.ReviewSubmissionItem) bool {
|
|
return it.Relationships != nil && it.Relationships.AppStoreVersion != nil &&
|
|
it.Relationships.AppStoreVersion.Data != nil && it.Relationships.AppStoreVersion.Data.ID == versionID
|
|
}), nil
|
|
}
|
|
|
|
func submitAppStore(ctx context.Context, platformNames []string) error {
|
|
if len(platformNames) == 0 {
|
|
platformNames = appStorePlatforms
|
|
}
|
|
client := createClient(20 * time.Minute)
|
|
for _, platformName := range platformNames {
|
|
platform, err := parsePlatform(platformName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
versionString, err := build_shared.ReadAppleMarketingVersion(applePath, platformDirectories[platform])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = submitAppStoreVersion(ctx, client, platform, versionString)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func submitAppStoreVersion(ctx context.Context, client *asc.Client, platform asc.Platform, versionString string) error {
|
|
log.Info(string(platform), " ", versionString, " list versions")
|
|
versions, _, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{
|
|
FilterPlatform: []string{string(platform)},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
localVersion := badversion.Parse(versionString)
|
|
var version asc.AppStoreVersion
|
|
for _, remoteVersion := range versions.Data {
|
|
remoteVersionString := *remoteVersion.Attributes.VersionString
|
|
if remoteVersionString == versionString {
|
|
version = remoteVersion
|
|
continue
|
|
}
|
|
if !badversion.Parse(remoteVersionString).LessThan(localVersion) {
|
|
return E.New(string(platform), " ", versionString, " is not newer than App Store version ", remoteVersionString)
|
|
}
|
|
}
|
|
if version.ID != "" {
|
|
state := *version.Attributes.AppStoreState
|
|
switch state {
|
|
case asc.AppStoreVersionStatePrepareForSubmission,
|
|
asc.AppStoreVersionStateRejected,
|
|
asc.AppStoreVersionStateDeveloperRejected,
|
|
asc.AppStoreVersionStateMetadataRejected,
|
|
asc.AppStoreVersionStateInvalidBinary:
|
|
log.Info(string(platform), " ", versionString, " found version in state ", string(state))
|
|
case asc.AppStoreVersionStateWaitingForReview, asc.AppStoreVersionStateInReview:
|
|
log.Info(string(platform), " ", versionString, " already submitted: ", string(state))
|
|
return nil
|
|
case asc.AppStoreVersionStatePendingDeveloperRelease:
|
|
log.Info(string(platform), " ", versionString, " approved, run publish_app_store to release")
|
|
return nil
|
|
default:
|
|
return E.New(string(platform), " ", versionString, " already released: ", string(state))
|
|
}
|
|
} else {
|
|
log.Info(string(platform), " ", versionString, " create version")
|
|
created, _, err := client.Apps.CreateAppStoreVersion(ctx, asc.AppStoreVersionCreateRequestAttributes{
|
|
Platform: platform,
|
|
ReleaseType: common.Ptr("MANUAL"),
|
|
VersionString: versionString,
|
|
}, appID, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
version = created.Data
|
|
}
|
|
|
|
log.Info(string(platform), " ", versionString, " list localizations")
|
|
localizations, _, err := client.Apps.ListLocalizationsForAppStoreVersion(ctx, version.ID, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
localization := common.Find(localizations.Data, func(it asc.AppStoreVersionLocalization) bool {
|
|
return *it.Attributes.Locale == "en-US"
|
|
})
|
|
if localization.ID == "" {
|
|
log.Info(string(platform), " ", versionString, " create en-US localization")
|
|
_, _, err = client.Apps.CreateAppStoreVersionLocalization(ctx, asc.AppStoreVersionLocalizationCreateRequestAttributes{
|
|
Locale: "en-US",
|
|
PromotionalText: common.Ptr(appStorePromotionalText),
|
|
WhatsNew: common.Ptr(appStoreWhatsNew),
|
|
}, version.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else if localization.Attributes.PromotionalText == nil || *localization.Attributes.PromotionalText != appStorePromotionalText ||
|
|
localization.Attributes.WhatsNew == nil || *localization.Attributes.WhatsNew != appStoreWhatsNew {
|
|
log.Info(string(platform), " ", versionString, " update en-US localization")
|
|
_, _, err = client.Apps.UpdateAppStoreVersionLocalization(ctx, localization.ID, &asc.AppStoreVersionLocalizationUpdateRequestAttributes{
|
|
PromotionalText: common.Ptr(appStorePromotionalText),
|
|
WhatsNew: common.Ptr(appStoreWhatsNew),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var build asc.Build
|
|
for {
|
|
builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{
|
|
FilterApp: []string{appID},
|
|
FilterPreReleaseVersionPlatform: []string{string(platform)},
|
|
FilterPreReleaseVersionVersion: []string{versionString},
|
|
Sort: []string{"-uploadedDate"},
|
|
Limit: 1,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(builds.Data) == 0 {
|
|
log.Info(string(platform), " ", versionString, " waiting for build upload")
|
|
time.Sleep(buildPollInterval)
|
|
continue
|
|
}
|
|
build = builds.Data[0]
|
|
processingState := *build.Attributes.ProcessingState
|
|
if processingState == "VALID" {
|
|
break
|
|
}
|
|
if processingState == "PROCESSING" {
|
|
log.Info(string(platform), " ", versionString, " waiting for build ", *build.Attributes.Version, ": ", processingState)
|
|
time.Sleep(buildPollInterval)
|
|
continue
|
|
}
|
|
return E.New(string(platform), " ", versionString, " build ", *build.Attributes.Version, " ", processingState)
|
|
}
|
|
log.Info(string(platform), " ", versionString, " found build ", *build.Attributes.Version)
|
|
|
|
currentBuild, response, err := client.Apps.GetBuildIDForAppStoreVersion(ctx, version.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if response.StatusCode != http.StatusOK || currentBuild.Data.ID != build.ID {
|
|
log.Info(string(platform), " ", versionString, " attach build ", *build.Attributes.Version)
|
|
_, err = client.Apps.UpdateBuildForAppStoreVersion(ctx, version.ID, common.Ptr(build.ID))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
log.Info(string(platform), " ", versionString, " list review submissions")
|
|
submissions, _, err := client.Submission.ListReviewSubmissions(ctx, &asc.ListReviewSubmissionsQuery{
|
|
FilterApp: []string{appID},
|
|
FilterPlatform: []string{string(platform)},
|
|
FilterState: []string{
|
|
string(asc.ReviewSubmissionStateReadyForReview),
|
|
string(asc.ReviewSubmissionStateWaitingForReview),
|
|
string(asc.ReviewSubmissionStateInReview),
|
|
string(asc.ReviewSubmissionStateUnresolvedIssues),
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var (
|
|
submission asc.ReviewSubmission
|
|
submissionHasItem bool
|
|
)
|
|
for _, remoteSubmission := range submissions.Data {
|
|
containsVersion, err := reviewSubmissionContainsVersion(ctx, client, remoteSubmission.ID, version.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
state := *remoteSubmission.Attributes.State
|
|
switch state {
|
|
case asc.ReviewSubmissionStateReadyForReview:
|
|
submission = remoteSubmission
|
|
submissionHasItem = containsVersion
|
|
case asc.ReviewSubmissionStateWaitingForReview, asc.ReviewSubmissionStateInReview:
|
|
if containsVersion {
|
|
log.Info(string(platform), " ", versionString, " already submitted: ", string(state))
|
|
return nil
|
|
}
|
|
return E.New(string(platform), " review submission ", remoteSubmission.ID, " with another version is ", string(state))
|
|
default:
|
|
return E.New(string(platform), " review submission ", remoteSubmission.ID, " is ", string(state))
|
|
}
|
|
}
|
|
if submission.ID == "" {
|
|
log.Info(string(platform), " ", versionString, " create review submission")
|
|
created, _, err := client.Submission.CreateReviewSubmission(ctx, appID, common.Ptr(platform))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
submission = created.Data
|
|
}
|
|
if !submissionHasItem {
|
|
log.Info(string(platform), " ", versionString, " add version to review submission ", submission.ID)
|
|
_, _, err = client.Submission.CreateReviewSubmissionItemForAppStoreVersion(ctx, submission.ID, version.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
log.Info(string(platform), " ", versionString, " submit review submission ", submission.ID)
|
|
_, _, err = client.Submission.UpdateReviewSubmission(ctx, submission.ID, common.Ptr(true), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func publishAppStore(ctx context.Context, platformNames []string) error {
|
|
if len(platformNames) == 0 {
|
|
platformNames = appStorePlatforms
|
|
}
|
|
client := createClient(time.Minute)
|
|
for _, platformName := range platformNames {
|
|
platform, err := parsePlatform(platformName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
versionString, err := build_shared.ReadAppleMarketingVersion(applePath, platformDirectories[platform])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Info(string(platform), " ", versionString, " list versions")
|
|
versions, _, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{
|
|
FilterPlatform: []string{string(platform)},
|
|
FilterVersionString: []string{versionString},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(versions.Data) == 0 {
|
|
return E.New(string(platform), " ", versionString, " not found")
|
|
}
|
|
version := versions.Data[0]
|
|
state := *version.Attributes.AppStoreState
|
|
switch state {
|
|
case asc.AppStoreVersionStatePrepareForSubmission, asc.AppStoreVersionStateDeveloperRejected:
|
|
return E.New(string(platform), " ", versionString, " not submitted")
|
|
case asc.AppStoreVersionStateWaitingForReview, asc.AppStoreVersionStateInReview:
|
|
log.Warn(string(platform), " ", versionString, " waiting for review")
|
|
continue
|
|
case asc.AppStoreVersionStatePendingDeveloperRelease:
|
|
default:
|
|
return E.New(string(platform), " ", versionString, " unknown state ", string(state))
|
|
}
|
|
log.Info(string(platform), " ", versionString, " release")
|
|
_, _, err = client.Publishing.CreatePhasedRelease(ctx, common.Ptr(asc.PhasedReleaseStateComplete), version.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|