From ed8d0aa9a68bee8b3b0c01ec8bcf96974e835615 Mon Sep 17 00:00:00 2001 From: inkeliz Date: Fri, 25 Aug 2023 17:42:49 +0100 Subject: [PATCH] gogio: [macOS] support custom profile This patch enables `-signkey` to load provisioning profiles. Signed-off-by: inkeliz Signed-off-by: Elias Naur --- gogio/help.go | 3 ++- gogio/iosbuild.go | 60 +++++++++++++++++++++++++++++++++------------ gogio/macosbuild.go | 8 +++++- gogio/main.go | 2 +- 4 files changed, 54 insertions(+), 19 deletions(-) diff --git a/gogio/help.go b/gogio/help.go index 0338344..778a559 100644 --- a/gogio/help.go +++ b/gogio/help.go @@ -69,7 +69,8 @@ its deletion. The -x flag will print all the external commands executed by the gogio tool. The -signkey flag specifies the path of the keystore, used for signing Android apk/aab files -or specifies the name of key on Keychain to sign MacOS app. +or specifies the name of key on Keychain to sign MacOS apps. On iOS and macOS it can be used +to specify the path of a provisioning profile (.mobileprovision/.provisionprofile). The -signpass flag specifies the password of the keystore, ignored if -signkey is not provided. If -signpass is not sepecified it will be read from the environment variable GOGIO_SIGNPASS. diff --git a/gogio/iosbuild.go b/gogio/iosbuild.go index 5d1db69..08bca1b 100644 --- a/gogio/iosbuild.go +++ b/gogio/iosbuild.go @@ -75,7 +75,29 @@ func buildIOS(tmpDir, target string, bi *buildInfo) error { if err := exeIOS(tmpDir, target, appDir, bi); err != nil { return err } - if err := signIOS(bi, tmpDir, appDir); err != nil { + + embedded := filepath.Join(appDir, "embedded.mobileprovision") + + var provisions []string + if bi.key == "" { + if ext := filepath.Ext(bi.key); ext != ".mobileprovision" && ext != ".provisionprofile" { + return fmt.Errorf("sign: -signkey specifies an Apple provisioning profile, but %q does not end in .mobileprovision or .provisionprofile", bi.key) + } + provisions = []string{bi.key} + } else { + home, err := os.UserHomeDir() + if err != nil { + return err + } + + p, err := filepath.Glob(filepath.Join(home, "Library", "MobileDevice", "Provisioning Profiles", "*.mobileprovision")) + if err != nil { + return err + } + provisions = p + } + + if err := signApple(bi.appID, tmpDir, embedded, appDir, provisions); err != nil { return err } return zipDir(out, tmpDir, "Payload") @@ -84,16 +106,8 @@ func buildIOS(tmpDir, target string, bi *buildInfo) error { } } -func signIOS(bi *buildInfo, tmpDir, app string) error { - home, err := os.UserHomeDir() - if err != nil { - return err - } - provPattern := filepath.Join(home, "Library", "MobileDevice", "Provisioning Profiles", "*.mobileprovision") - provisions, err := filepath.Glob(provPattern) - if err != nil { - return err - } +// signApple is shared between iOS and macOS. +func signApple(appID, tmpDir, embedded, app string, provisions []string) error { provInfo := filepath.Join(tmpDir, "provision.plist") var avail []string for _, prov := range provisions { @@ -117,17 +131,23 @@ func signIOS(bi *buildInfo, tmpDir, app string) error { if err != nil { return err } - provAppID, err := runCmd(exec.Command("/usr/libexec/PlistBuddy", "-c", "Print:Entitlements:application-identifier", provInfo)) + + // iOS/macOS Catalyst + provAppIDSearchKey := "Print:Entitlements:application-identifier" + if filepath.Ext(prov) == ".provisionprofile" { + // macOS + provAppIDSearchKey = "Print:Entitlements:com.apple.application-identifier" + } + provAppID, err := runCmd(exec.Command("/usr/libexec/PlistBuddy", "-c", provAppIDSearchKey, provInfo)) if err != nil { return err } - expAppID := fmt.Sprintf("%s.%s", appIDPrefix, bi.appID) + expAppID := fmt.Sprintf("%s.%s", appIDPrefix, appID) avail = append(avail, provAppID) if expAppID != provAppID { continue } // Copy provisioning file. - embedded := filepath.Join(app, "embedded.mobileprovision") if err := copyFile(embedded, prov); err != nil { return err } @@ -147,10 +167,18 @@ func signIOS(bi *buildInfo, tmpDir, app string) error { } identity := sha1.Sum(certDER) idHex := hex.EncodeToString(identity[:]) - _, err = runCmd(exec.Command("codesign", "-s", idHex, "-v", "--entitlements", entFile, app)) + _, err = runCmd(exec.Command( + "codesign", + "--sign", idHex, + "--deep", + "--force", + "--options", "runtime", + "--entitlements", + entFile, + app)) return err } - return fmt.Errorf("sign: no valid provisioning profile found for bundle id %q among %v", bi.appID, avail) + return fmt.Errorf("sign: no valid provisioning profile found for bundle id %q among %v", appID, avail) } func exeIOS(tmpDir, target, app string, bi *buildInfo) error { diff --git a/gogio/macosbuild.go b/gogio/macosbuild.go index b9b79dd..2413b9e 100644 --- a/gogio/macosbuild.go +++ b/gogio/macosbuild.go @@ -148,7 +148,7 @@ func (b *macBuilder) setInfo(buildInfo *buildInfo, name string) { NSHighResolutionCapable CFBundlePackageType - APPL + BNDL {{if .Schemes}} CFBundleURLTypes @@ -232,6 +232,12 @@ func (b *macBuilder) signProgram(buildInfo *buildInfo, binDest string, name stri return err } + // If the key is a provisioning profile use the same signing process as iOS + if filepath.Ext(buildInfo.key) == ".provisionprofile" { + embedded := filepath.Join(binDest, "Contents", "embedded.provisionprofile") + return signApple(buildInfo.appID, b.TempDir, embedded, binDest, []string{buildInfo.key}) + } + cmd := exec.Command( "codesign", "--deep", diff --git a/gogio/main.go b/gogio/main.go index fb730f2..8ef6f89 100644 --- a/gogio/main.go +++ b/gogio/main.go @@ -36,7 +36,7 @@ var ( extraLdflags = flag.String("ldflags", "", "extra flags to the Go linker") extraTags = flag.String("tags", "", "extra tags to the Go tool") iconPath = flag.String("icon", "", "specify an icon for iOS and Android") - signKey = flag.String("signkey", "", "specify the path of the keystore to be used to sign Android apk files.") + signKey = flag.String("signkey", "", "specify the path of the keystore (Android) or provisioning profile (macOS or iOS) for signing") signPass = flag.String("signpass", "", "specify the password to decrypt the signkey.") notaryID = flag.String("notaryid", "", "specify the apple id to use for notarization.") notaryPass = flag.String("notarypass", "", "specify app-specific password of the Apple ID to be used for notarization.")