Skip to content

Commit

Permalink
fix: add better error handling to XCode 16 ipsw idev img cmds 🏎️
Browse files Browse the repository at this point in the history
  • Loading branch information
blacktop committed Sep 12, 2024
1 parent 78dccbc commit bcbcb14
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 13 deletions.
20 changes: 20 additions & 0 deletions cmd/ipsw/cmd/idev/idev_img_mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,26 @@ var idevImgMountCmd = &cobra.Command{
imageType = "Personalized"

if len(dmgPath) == 0 {
xcodeVersion, err := utils.GetXCodeVersion(xcode)
if err != nil {
return fmt.Errorf("failed to get Xcode version: %w", err)
}
xcver, err := semver.NewVersion(xcodeVersion) // check
if err != nil {
return fmt.Errorf("failed to convert version into semver object")
}
var ddiPath string
if xcver.LessThan(semver.Must(semver.NewVersion("16.0"))) {
ddiPath = filepath.Join(xcode, "/Contents/Resources/CoreDeviceDDIs/iOS_DDI.dmg")
if _, err := os.Stat(ddiPath); errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("failed to find iOS_DDI.dmg in '%s' (install NEW XCode.app or Xcode-beta.app)", ddiPath)
}
} else {
ddiPath = "/Library/Developer/DeveloperDiskImages/iOS_DDI.dmg"
if _, err := os.Stat(ddiPath); errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("failed to find iOS_DDI.dmg in '%s' (run `%s -runFirstLaunch` and try again)", ddiPath, filepath.Join(xcode, "Contents/Developer/usr/bin/xcodebuild"))
}
}
ddiDMG := filepath.Join(xcode, "/Contents/Resources/CoreDeviceDDIs/iOS_DDI.dmg")
if _, err := os.Stat(ddiDMG); errors.Is(err, os.ErrNotExist) {
ddiDMG = "/Library/Developer/DeveloperDiskImages/iOS_DDI.dmg"
Expand Down
32 changes: 23 additions & 9 deletions cmd/ipsw/cmd/idev/idev_img_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/blacktop/ipsw/pkg/plist"
"github.com/blacktop/ipsw/pkg/tss"
"github.com/fatih/color"
semver "github.com/hashicorp/go-version"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand Down Expand Up @@ -134,27 +135,40 @@ var idevImgSignCmd = &cobra.Command{
}

if xcode != "" {
dmgPath := filepath.Join(xcode, "/Contents/Resources/CoreDeviceDDIs/iOS_DDI.dmg")
if _, err := os.Stat(dmgPath); errors.Is(err, os.ErrNotExist) {
dmgPath = "/Library/Developer/DeveloperDiskImages/iOS_DDI.dmg"
if _, err := os.Stat(dmgPath); errors.Is(err, os.ErrNotExist) {
xcodeVersion, err := utils.GetXCodeVersion(xcode)
if err != nil {
return fmt.Errorf("failed to get Xcode version: %w", err)
}
xcver, err := semver.NewVersion(xcodeVersion) // check
if err != nil {
return fmt.Errorf("failed to convert version into semver object")
}
var ddiPath string
if xcver.LessThan(semver.Must(semver.NewVersion("16.0"))) {
ddiPath = filepath.Join(xcode, "/Contents/Resources/CoreDeviceDDIs/iOS_DDI.dmg")
if _, err := os.Stat(ddiPath); errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("failed to find iOS_DDI.dmg in '%s' (install NEW XCode.app or Xcode-beta.app)", xcode)
}
} else {
ddiPath = "/Library/Developer/DeveloperDiskImages/iOS_DDI.dmg"
if _, err := os.Stat(ddiPath); errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("failed to find iOS_DDI.dmg in '%s' (run `%s -runFirstLaunch` and try again)", ddiPath, filepath.Join(xcode, "Contents/Developer/usr/bin/xcodebuild"))
}
}
utils.Indent(log.Info, 2)(fmt.Sprintf("Mounting %s", dmgPath))
mountPoint, alreadyMounted, err := utils.MountDMG(dmgPath)
utils.Indent(log.Info, 2)(fmt.Sprintf("Mounting %s", ddiPath))
mountPoint, alreadyMounted, err := utils.MountDMG(ddiPath)
if err != nil {
return fmt.Errorf("failed to mount iOS_DDI.dmg: %w", err)
}
if alreadyMounted {
utils.Indent(log.Info, 3)(fmt.Sprintf("%s already mounted", dmgPath))
utils.Indent(log.Info, 3)(fmt.Sprintf("%s already mounted", ddiPath))
} else {
defer func() {
utils.Indent(log.Debug, 2)(fmt.Sprintf("Unmounting %s", dmgPath))
utils.Indent(log.Debug, 2)(fmt.Sprintf("Unmounting %s", ddiPath))
if err := utils.Retry(3, 2*time.Second, func() error {
return utils.Unmount(mountPoint, false)
}); err != nil {
log.Errorf("failed to unmount %s at %s: %v", dmgPath, mountPoint, err)
log.Errorf("failed to unmount %s at %s: %v", ddiPath, mountPoint, err)
}
}()
}
Expand Down
46 changes: 42 additions & 4 deletions internal/utils/macos.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func CodeSignAdHoc(filePath string) error {

func CodesignVerify(path string) (string, error) {
if runtime.GOOS == "darwin" {
cmd := exec.Command("codesign", "--verify", "--deep", "--strict", "--verbose=4", path)
cmd := exec.Command("/usr/bin/codesign", "--verify", "--deep", "--strict", "--verbose=4", path)
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("%v: %s", err, out)
Expand All @@ -159,7 +159,7 @@ func CodesignVerify(path string) (string, error) {

func CodesignShow(path string) (string, error) {
if runtime.GOOS == "darwin" {
cmd := exec.Command("codesign", "-d", "--verbose=4", path)
cmd := exec.Command("/usr/bin/codesign", "-d", "--verbose=4", path)
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("%v: %s", err, out)
Expand All @@ -173,7 +173,7 @@ func CodesignShow(path string) (string, error) {
func CreateSparseDiskImage(volumeName, diskPath string) (string, error) {
if runtime.GOOS == "darwin" {

cmd := exec.Command("hdiutil", "create", "-size", "16g", "-fs", "HFS+", "-volname", volumeName, "-type", "SPARSE", "-plist", diskPath)
cmd := exec.Command("usr/bin/hdiutil", "create", "-size", "16g", "-fs", "HFS+", "-volname", volumeName, "-type", "SPARSE", "-plist", diskPath)

out, err := cmd.CombinedOutput()
if err != nil {
Expand Down Expand Up @@ -372,7 +372,7 @@ func GetBuildInfo() (*BuildInfo, error) {

func GetXCodePath() (string, error) {
if runtime.GOOS == "darwin" {
cmd := exec.Command("xcode-select", "-p")
cmd := exec.Command("/usr/bin/xcode-select", "-p")
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("%v: %s", err, out)
Expand All @@ -382,6 +382,44 @@ func GetXCodePath() (string, error) {
return "", fmt.Errorf("only supported on macOS")
}

type xcodeVersionPlist struct {
BuildVersion string `json:"BuildVersion"`
CFBundleShortVersionString string `json:"CFBundleShortVersionString"`
CFBundleVersion string `json:"CFBundleVersion"`
ProductBuildVersion string `json:"ProductBuildVersion"`
ProjectName string `json:"ProjectName"`
SourceVersion string `json:"SourceVersion"`
}

func GetXCodeVersion(path ...string) (string, error) {
if runtime.GOOS == "darwin" {
if len(path) == 0 {
cmd := exec.Command("/usr/bin/xcodebuild", "-version")
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("%v: %s", err, out)
}
version, _, ok := strings.Cut(strings.TrimSpace(string(out)), "\n")
if !ok {
return "", fmt.Errorf("failed to parse xcodebuild version: %s", out)
}
return strings.TrimPrefix(version, "Xcode "), nil
} else {
f, err := os.Open(filepath.Join(path[0], "Contents", "version.plist"))
if err != nil {
return "", fmt.Errorf("failed to open Xcode version.plist: %v", err)
}
defer f.Close()
var version xcodeVersionPlist
if err := plist.NewDecoder(f).Decode(&version); err != nil {
return "", fmt.Errorf("failed to decode Xcode version.plist: %v", err)
}
return version.CFBundleShortVersionString, nil
}
}
return "", fmt.Errorf("only supported on macOS")
}

func GetKernelPath() (string, error) {
if runtime.GOOS == "darwin" {
cmd := exec.Command("uname", "-a")
Expand Down

0 comments on commit bcbcb14

Please sign in to comment.