wails/v3/internal/keychain/keychain.go
Lea Anthony 3594b77666 feat(v3): add cross-platform build system and signing support
Add Docker-based cross-compilation for building Wails apps on any platform:
- Linux builds from macOS/Windows using Docker with Zig
- Windows builds with CGO from Linux/macOS using Docker
- macOS builds from Linux/Windows using Docker with osxcross

Add wails3 tool lipo command using konoui/lipo library for creating
macOS universal binaries on any platform.

Add code signing infrastructure:
- wails3 sign wrapper command (like build/package)
- wails3 tool sign low-level command for Taskfiles
- wails3 setup signing interactive wizard
- wails3 setup entitlements for macOS entitlements
- Keychain integration for secure credential storage

Update all platform Taskfiles with signing tasks:
- darwin:sign, darwin:sign:notarize
- windows:sign, windows:sign:installer
- linux:sign:deb, linux:sign:rpm, linux:sign:packages

Reorganize documentation:
- Move building/signing guides to guides/build/
- Add platform-specific packaging guides (macos, linux, windows)
- Add cross-platform build documentation
- Add comprehensive signing guide with CI/CD examples
- Add auto-updates guide and updater reference
- Add distribution tutorial

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-06 13:53:37 +11:00

89 lines
2.5 KiB
Go

// Package keychain provides secure credential storage using the system keychain.
// On macOS it uses Keychain, on Windows it uses Credential Manager,
// and on Linux it uses Secret Service (via D-Bus).
package keychain
import (
"fmt"
"os"
"github.com/zalando/go-keyring"
)
const (
// ServiceName is the service identifier used for all Wails credentials
ServiceName = "wails"
// Credential keys
KeyWindowsCertPassword = "windows-cert-password"
KeyPGPPassword = "pgp-password"
)
// Set stores a credential in the system keychain.
// The credential is identified by a key and can be retrieved later with Get.
func Set(key, value string) error {
err := keyring.Set(ServiceName, key, value)
if err != nil {
return fmt.Errorf("failed to store credential in keychain: %w", err)
}
return nil
}
// Get retrieves a credential from the system keychain.
// Returns the value and nil error if found, or empty string and error if not found.
// Also checks environment variables as a fallback (useful for CI).
func Get(key string) (string, error) {
// First check environment variable (for CI/automation)
envKey := "WAILS_" + toEnvName(key)
if val := os.Getenv(envKey); val != "" {
return val, nil
}
// Try keychain
value, err := keyring.Get(ServiceName, key)
if err != nil {
if err == keyring.ErrNotFound {
return "", fmt.Errorf("credential %q not found in keychain (set with: wails3 setup signing, or set env var %s)", key, envKey)
}
return "", fmt.Errorf("failed to retrieve credential from keychain: %w", err)
}
return value, nil
}
// Delete removes a credential from the system keychain.
func Delete(key string) error {
err := keyring.Delete(ServiceName, key)
if err != nil && err != keyring.ErrNotFound {
return fmt.Errorf("failed to delete credential from keychain: %w", err)
}
return nil
}
// Exists checks if a credential exists in the keychain or environment.
func Exists(key string) bool {
// Check environment variable first
envKey := "WAILS_" + toEnvName(key)
if os.Getenv(envKey) != "" {
return true
}
// Check keychain
_, err := keyring.Get(ServiceName, key)
return err == nil
}
// toEnvName converts a key to an environment variable name.
// e.g., "windows-cert-password" -> "WINDOWS_CERT_PASSWORD"
func toEnvName(key string) string {
result := make([]byte, len(key))
for i, c := range key {
if c == '-' {
result[i] = '_'
} else if c >= 'a' && c <= 'z' {
result[i] = byte(c - 'a' + 'A')
} else {
result[i] = byte(c)
}
}
return string(result)
}