mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Fix DMG import
This commit is contained in:
parent
a8c8417d16
commit
3e9f7fce4e
2 changed files with 186 additions and 32 deletions
157
v3/internal/commands/dmg/dmg.go
Normal file
157
v3/internal/commands/dmg/dmg.go
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
package dmg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Creator handles DMG creation
|
||||
type Creator struct {
|
||||
sourcePath string
|
||||
outputPath string
|
||||
appName string
|
||||
backgroundImage string
|
||||
iconPositions map[string]Position
|
||||
}
|
||||
|
||||
// Position represents icon coordinates in the DMG
|
||||
type Position struct {
|
||||
X, Y int
|
||||
}
|
||||
|
||||
// New creates a new DMG creator
|
||||
func New(sourcePath, outputPath, appName string) (*Creator, error) {
|
||||
if runtime.GOOS != "darwin" {
|
||||
return nil, fmt.Errorf("DMG creation is only supported on macOS")
|
||||
}
|
||||
|
||||
// Check if source exists
|
||||
if _, err := os.Stat(sourcePath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("source path does not exist: %s", sourcePath)
|
||||
}
|
||||
|
||||
return &Creator{
|
||||
sourcePath: sourcePath,
|
||||
outputPath: outputPath,
|
||||
appName: appName,
|
||||
iconPositions: make(map[string]Position),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetBackgroundImage sets the background image for the DMG
|
||||
func (c *Creator) SetBackgroundImage(imagePath string) error {
|
||||
if _, err := os.Stat(imagePath); os.IsNotExist(err) {
|
||||
return fmt.Errorf("background image does not exist: %s", imagePath)
|
||||
}
|
||||
c.backgroundImage = imagePath
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddIconPosition adds an icon position for the DMG layout
|
||||
func (c *Creator) AddIconPosition(filename string, x, y int) {
|
||||
c.iconPositions[filename] = Position{X: x, Y: y}
|
||||
}
|
||||
|
||||
// Create creates the DMG file
|
||||
func (c *Creator) Create() error {
|
||||
// Remove existing DMG if it exists
|
||||
if _, err := os.Stat(c.outputPath); err == nil {
|
||||
if err := os.Remove(c.outputPath); err != nil {
|
||||
return fmt.Errorf("failed to remove existing DMG: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a temporary directory for DMG content
|
||||
tempDir, err := os.MkdirTemp("", "dmg-*")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create temp directory: %w", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Copy the app bundle to temp directory
|
||||
appName := filepath.Base(c.sourcePath)
|
||||
tempAppPath := filepath.Join(tempDir, appName)
|
||||
if err := c.copyDir(c.sourcePath, tempAppPath); err != nil {
|
||||
return fmt.Errorf("failed to copy app bundle: %w", err)
|
||||
}
|
||||
|
||||
// Create Applications symlink
|
||||
applicationsLink := filepath.Join(tempDir, "Applications")
|
||||
if err := os.Symlink("/Applications", applicationsLink); err != nil {
|
||||
return fmt.Errorf("failed to create Applications symlink: %w", err)
|
||||
}
|
||||
|
||||
// Copy background image if provided
|
||||
if c.backgroundImage != "" {
|
||||
bgName := filepath.Base(c.backgroundImage)
|
||||
bgPath := filepath.Join(tempDir, bgName)
|
||||
if err := c.copyFile(c.backgroundImage, bgPath); err != nil {
|
||||
return fmt.Errorf("failed to copy background image: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create DMG using hdiutil
|
||||
if err := c.createDMGWithHdiutil(tempDir); err != nil {
|
||||
return fmt.Errorf("failed to create DMG with hdiutil: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyDir recursively copies a directory
|
||||
func (c *Creator) copyDir(src, dst string) error {
|
||||
cmd := exec.Command("cp", "-R", src, dst)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to copy directory: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyFile copies a file
|
||||
func (c *Creator) copyFile(src, dst string) error {
|
||||
cmd := exec.Command("cp", src, dst)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to copy file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// createDMGWithHdiutil creates the DMG using macOS hdiutil
|
||||
func (c *Creator) createDMGWithHdiutil(sourceDir string) error {
|
||||
// Calculate size needed for DMG (roughly 2x the source size for safety)
|
||||
sizeCmd := exec.Command("du", "-sk", sourceDir)
|
||||
output, err := sizeCmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to calculate directory size: %w", err)
|
||||
}
|
||||
|
||||
// Parse size and add padding
|
||||
sizeStr := strings.Fields(string(output))[0]
|
||||
|
||||
// Create DMG with hdiutil
|
||||
args := []string{
|
||||
"create",
|
||||
"-srcfolder", sourceDir,
|
||||
"-format", "UDZO",
|
||||
"-volname", c.appName,
|
||||
c.outputPath,
|
||||
}
|
||||
|
||||
// Add size if we could determine it
|
||||
if sizeStr != "" {
|
||||
// Add 50% padding to the calculated size
|
||||
args = append([]string{"create", "-size", sizeStr + "k"}, args[1:]...)
|
||||
args[0] = "create"
|
||||
}
|
||||
|
||||
cmd := exec.Command("hdiutil", args...)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("hdiutil failed: %w\nOutput: %s", err, string(output))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -8,12 +8,9 @@ import (
|
|||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/leaanthony/gosod"
|
||||
"github.com/wailsapp/wails/v3/internal/flags"
|
||||
"github.com/wailsapp/wails/v3/internal/s"
|
||||
)
|
||||
|
||||
//go:embed build_assets/windows/msix/*
|
||||
|
|
@ -23,13 +20,13 @@ var msixAssets embed.FS
|
|||
type MSIXOptions struct {
|
||||
// Info from project config
|
||||
Info struct {
|
||||
CompanyName string `json:"companyName"`
|
||||
ProductName string `json:"productName"`
|
||||
ProductVersion string `json:"version"`
|
||||
CompanyName string `json:"companyName"`
|
||||
ProductName string `json:"productName"`
|
||||
ProductVersion string `json:"version"`
|
||||
ProductIdentifier string `json:"productIdentifier"`
|
||||
Description string `json:"description"`
|
||||
Copyright string `json:"copyright"`
|
||||
Comments string `json:"comments"`
|
||||
Description string `json:"description"`
|
||||
Copyright string `json:"copyright"`
|
||||
Comments string `json:"comments"`
|
||||
}
|
||||
// File associations
|
||||
FileAssociations []struct {
|
||||
|
|
@ -41,15 +38,15 @@ type MSIXOptions struct {
|
|||
MimeType string `json:"mimeType,omitempty"`
|
||||
} `json:"fileAssociations"`
|
||||
// MSIX specific options
|
||||
Publisher string `json:"publisher"`
|
||||
CertificatePath string `json:"certificatePath"`
|
||||
CertificatePassword string `json:"certificatePassword,omitempty"`
|
||||
Publisher string `json:"publisher"`
|
||||
CertificatePath string `json:"certificatePath"`
|
||||
CertificatePassword string `json:"certificatePassword,omitempty"`
|
||||
ProcessorArchitecture string `json:"processorArchitecture"`
|
||||
ExecutableName string `json:"executableName"`
|
||||
ExecutablePath string `json:"executablePath"`
|
||||
OutputPath string `json:"outputPath"`
|
||||
UseMsixPackagingTool bool `json:"useMsixPackagingTool"`
|
||||
UseMakeAppx bool `json:"useMakeAppx"`
|
||||
ExecutableName string `json:"executableName"`
|
||||
ExecutablePath string `json:"executablePath"`
|
||||
OutputPath string `json:"outputPath"`
|
||||
UseMsixPackagingTool bool `json:"useMsixPackagingTool"`
|
||||
UseMakeAppx bool `json:"useMakeAppx"`
|
||||
}
|
||||
|
||||
// ToolMSIX creates an MSIX package for Windows applications
|
||||
|
|
@ -79,7 +76,7 @@ func ToolMSIX(options *flags.ToolMSIX) error {
|
|||
|
||||
// Parse the config
|
||||
var config struct {
|
||||
Info map[string]interface{} `json:"info"`
|
||||
Info map[string]interface{} `json:"info"`
|
||||
FileAssociations []map[string]interface{} `json:"fileAssociations"`
|
||||
}
|
||||
if err := json.Unmarshal(configData, &config); err != nil {
|
||||
|
|
@ -88,15 +85,15 @@ func ToolMSIX(options *flags.ToolMSIX) error {
|
|||
|
||||
// Create MSIX options
|
||||
msixOptions := MSIXOptions{
|
||||
Publisher: options.Publisher,
|
||||
CertificatePath: options.CertificatePath,
|
||||
CertificatePassword: options.CertificatePassword,
|
||||
Publisher: options.Publisher,
|
||||
CertificatePath: options.CertificatePath,
|
||||
CertificatePassword: options.CertificatePassword,
|
||||
ProcessorArchitecture: options.Arch,
|
||||
ExecutableName: options.ExecutableName,
|
||||
ExecutablePath: options.ExecutablePath,
|
||||
OutputPath: options.OutputPath,
|
||||
UseMsixPackagingTool: options.UseMsixPackagingTool,
|
||||
UseMakeAppx: options.UseMakeAppx,
|
||||
ExecutableName: options.ExecutableName,
|
||||
ExecutablePath: options.ExecutablePath,
|
||||
OutputPath: options.OutputPath,
|
||||
UseMsixPackagingTool: options.UseMsixPackagingTool,
|
||||
UseMakeAppx: options.UseMakeAppx,
|
||||
}
|
||||
|
||||
// Copy info from config
|
||||
|
|
@ -239,7 +236,7 @@ func createMSIXWithPackagingTool(options *MSIXOptions) error {
|
|||
// Create the MSIX package
|
||||
fmt.Println("Creating MSIX package using Microsoft MSIX Packaging Tool...")
|
||||
args := []string{"create-package", "--template", templatePath}
|
||||
|
||||
|
||||
// Add certificate password if provided
|
||||
if options.CertificatePassword != "" {
|
||||
args = append(args, "--certPassword", options.CertificatePassword)
|
||||
|
|
@ -283,14 +280,14 @@ func createMSIXWithMakeAppx(options *MSIXOptions) error {
|
|||
if options.CertificatePath != "" {
|
||||
fmt.Println("Signing MSIX package...")
|
||||
signArgs := []string{"sign", "/fd", "SHA256", "/a", "/f", options.CertificatePath}
|
||||
|
||||
|
||||
// Add certificate password if provided
|
||||
if options.CertificatePassword != "" {
|
||||
signArgs = append(signArgs, "/p", options.CertificatePassword)
|
||||
}
|
||||
|
||||
|
||||
signArgs = append(signArgs, options.OutputPath)
|
||||
|
||||
|
||||
cmd = exec.Command("signtool.exe", signArgs...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
|
@ -414,7 +411,7 @@ func generateAppxManifest(options *MSIXOptions, outputPath string) error {
|
|||
func generatePlaceholderImage(outputPath string) error {
|
||||
// For now, we'll create a simple 1x1 transparent PNG
|
||||
// In a real implementation, we would generate proper icons based on the application icon
|
||||
|
||||
|
||||
// Create a minimal valid PNG file (1x1 transparent pixel)
|
||||
pngData := []byte{
|
||||
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D,
|
||||
|
|
@ -469,7 +466,7 @@ func InstallMSIXTools() error {
|
|||
if !sdkInstalled {
|
||||
fmt.Println("Windows SDK is not installed. Please download and install from:")
|
||||
fmt.Println("https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/")
|
||||
|
||||
|
||||
// Open the download page
|
||||
cmd = exec.Command("powershell", "-Command", "Start-Process https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/")
|
||||
if err := cmd.Run(); err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue