From a9f2d17d8d8e84e797dbeb5757868d5cdf99d8e3 Mon Sep 17 00:00:00 2001 From: sirily11 <32106111+sirily11@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:13:26 +0800 Subject: [PATCH] feat: move logic to cli folder and separate the template --- .github/workflows/create-release.yaml | 18 +++++ .github/workflows/release-cli.yaml | 71 +++++++++++++++++++ .gitignore | 7 +- .releaserc | 7 ++ README.md | 52 ++++++++++---- Makefile => cli/Makefile | 27 ++++--- .../assets}/com.mirego.ekiden.plist.tmpl | 0 {assets => cli/assets}/config.yaml.example | 0 {assets => cli/assets}/embed.go | 0 go.mod => cli/go.mod | 0 go.sum => cli/go.sum | 0 {internal => cli/internal}/config/config.go | 0 .../internal}/config/validation.go | 0 {internal => cli/internal}/daemon/launchd.go | 0 {internal => cli/internal}/monitor/logtail.go | 0 {internal => cli/internal}/posthog/client.go | 0 {internal => cli/internal}/runner/github.go | 0 {internal => cli/internal}/runner/loop.go | 0 {internal => cli/internal}/runner/ssh.go | 0 {internal => cli/internal}/runner/vm.go | 0 {internal => cli/internal}/setup/setup.go | 28 -------- {internal => cli/internal}/tui/app.go | 24 +++++-- {internal => cli/internal}/tui/commands.go | 6 +- {internal => cli/internal}/tui/config_form.go | 0 {internal => cli/internal}/tui/config_io.go | 0 {internal => cli/internal}/tui/logging.go | 0 {internal => cli/internal}/tui/menu_build.go | 2 +- {internal => cli/internal}/tui/menu_config.go | 0 {internal => cli/internal}/tui/menu_daemon.go | 0 .../internal}/tui/menu_daemon_install.go | 0 .../internal}/tui/menu_daemon_status.go | 0 .../internal}/tui/menu_daemon_uninstall.go | 0 {internal => cli/internal}/tui/menu_entry.go | 0 {internal => cli/internal}/tui/menu_images.go | 0 .../internal}/tui/menu_list_images.go | 0 .../internal}/tui/menu_monitor_daemon.go | 0 .../tui/menu_monitor_daemon_install.go | 0 .../tui/menu_monitor_daemon_status.go | 0 .../tui/menu_monitor_daemon_uninstall.go | 0 .../internal}/tui/menu_pull_image.go | 0 .../internal}/tui/menu_push_image.go | 0 {internal => cli/internal}/tui/menu_quit.go | 0 {internal => cli/internal}/tui/menu_root.go | 0 {internal => cli/internal}/tui/menu_run.go | 0 {internal => cli/internal}/tui/menu_setup.go | 0 .../internal}/tui/menu_view_logs.go | 0 {internal => cli/internal}/tui/views.go | 2 +- main.go => cli/main.go | 0 cli/scripts/binaries.sh | 6 ++ cli/scripts/package-notarize.sh | 53 ++++++++++++++ cli/scripts/sign.sh | 20 ++++++ guest/runner.pkr.hcl | 4 +- 52 files changed, 264 insertions(+), 63 deletions(-) create mode 100644 .github/workflows/create-release.yaml create mode 100644 .github/workflows/release-cli.yaml create mode 100644 .releaserc rename Makefile => cli/Makefile (68%) rename {assets => cli/assets}/com.mirego.ekiden.plist.tmpl (100%) rename {assets => cli/assets}/config.yaml.example (100%) rename {assets => cli/assets}/embed.go (100%) rename go.mod => cli/go.mod (100%) rename go.sum => cli/go.sum (100%) rename {internal => cli/internal}/config/config.go (100%) rename {internal => cli/internal}/config/validation.go (100%) rename {internal => cli/internal}/daemon/launchd.go (100%) rename {internal => cli/internal}/monitor/logtail.go (100%) rename {internal => cli/internal}/posthog/client.go (100%) rename {internal => cli/internal}/runner/github.go (100%) rename {internal => cli/internal}/runner/loop.go (100%) rename {internal => cli/internal}/runner/ssh.go (100%) rename {internal => cli/internal}/runner/vm.go (100%) rename {internal => cli/internal}/setup/setup.go (87%) rename {internal => cli/internal}/tui/app.go (93%) rename {internal => cli/internal}/tui/commands.go (89%) rename {internal => cli/internal}/tui/config_form.go (100%) rename {internal => cli/internal}/tui/config_io.go (100%) rename {internal => cli/internal}/tui/logging.go (100%) rename {internal => cli/internal}/tui/menu_build.go (86%) rename {internal => cli/internal}/tui/menu_config.go (100%) rename {internal => cli/internal}/tui/menu_daemon.go (100%) rename {internal => cli/internal}/tui/menu_daemon_install.go (100%) rename {internal => cli/internal}/tui/menu_daemon_status.go (100%) rename {internal => cli/internal}/tui/menu_daemon_uninstall.go (100%) rename {internal => cli/internal}/tui/menu_entry.go (100%) rename {internal => cli/internal}/tui/menu_images.go (100%) rename {internal => cli/internal}/tui/menu_list_images.go (100%) rename {internal => cli/internal}/tui/menu_monitor_daemon.go (100%) rename {internal => cli/internal}/tui/menu_monitor_daemon_install.go (100%) rename {internal => cli/internal}/tui/menu_monitor_daemon_status.go (100%) rename {internal => cli/internal}/tui/menu_monitor_daemon_uninstall.go (100%) rename {internal => cli/internal}/tui/menu_pull_image.go (100%) rename {internal => cli/internal}/tui/menu_push_image.go (100%) rename {internal => cli/internal}/tui/menu_quit.go (100%) rename {internal => cli/internal}/tui/menu_root.go (100%) rename {internal => cli/internal}/tui/menu_run.go (100%) rename {internal => cli/internal}/tui/menu_setup.go (100%) rename {internal => cli/internal}/tui/menu_view_logs.go (100%) rename {internal => cli/internal}/tui/views.go (95%) rename main.go => cli/main.go (100%) create mode 100755 cli/scripts/binaries.sh create mode 100755 cli/scripts/package-notarize.sh create mode 100755 cli/scripts/sign.sh diff --git a/.github/workflows/create-release.yaml b/.github/workflows/create-release.yaml new file mode 100644 index 0000000..fcbfa7d --- /dev/null +++ b/.github/workflows/create-release.yaml @@ -0,0 +1,18 @@ +on: workflow_dispatch +name: Create a new release + +jobs: + create-release: + runs-on: ubuntu-latest + permissions: + contents: write + if: ${{ (github.event.pusher.name != 'github action') && (github.ref == 'refs/heads/main') }} + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Semantic Release + uses: cycjimmy/semantic-release-action@v6 + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + with: + branch: main diff --git a/.github/workflows/release-cli.yaml b/.github/workflows/release-cli.yaml new file mode 100644 index 0000000..b63f168 --- /dev/null +++ b/.github/workflows/release-cli.yaml @@ -0,0 +1,71 @@ +name: Release rvmm CLI + +on: + push: + release: + types: + - released + workflow_dispatch: + +jobs: + build-macos: + name: Build, Sign and Notarize macOS Binary + runs-on: [self-hosted, macOS, ARM64] + permissions: + contents: write + defaults: + run: + working-directory: cli + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: cli/go.mod + check-latest: true + + - name: Install dependencies + run: go mod download + + - name: Run tests + run: go test ./... + + - name: Import code signing certificates + uses: apple-actions/import-codesign-certs@v3 + with: + p12-file-base64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} + p12-password: ${{ secrets.P12_PASSWORD }} + + - name: Build and sign the binary + run: make build && ./scripts/sign.sh + env: + SIGNING_CERTIFICATE_NAME: ${{ secrets.SIGNING_CERTIFICATE_NAME }} + + - name: Import installer signing certificate + uses: apple-actions/import-codesign-certs@v3 + with: + p12-file-base64: ${{ secrets.INSTALLER_CERTIFICATE_BASE64 }} + p12-password: ${{ secrets.P12_PASSWORD }} + keychain: installer + + - name: Package and notarize + if: github.event_name == 'release' + run: | + # Strip leading 'v' from semver tags (v1.2.3 -> 1.2.3) for pkg version + PKG_VERSION="${GITHUB_REF_NAME#v}" + export PKG_VERSION + ./scripts/package-notarize.sh + env: + INSTALLER_SIGNING_CERTIFICATE_NAME: ${{ secrets.INSTALLER_SIGNING_CERTIFICATE_NAME }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_ID_PWD: ${{ secrets.APPLE_ID_PWD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + + - name: Upload pkg to release + if: github.event_name == 'release' + uses: softprops/action-gh-release@v2 + with: + files: cli/rvmm_macOS_arm64.pkg + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 714248f..0772845 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ rvmm +cli/bin/ +*.pkg *.yaml -*.log \ No newline at end of file +*.log + +# Keep CI workflows tracked despite the broad *.yaml ignore above +!.github/workflows/*.yaml diff --git a/.releaserc b/.releaserc new file mode 100644 index 0000000..a966fe6 --- /dev/null +++ b/.releaserc @@ -0,0 +1,7 @@ +{ + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/github" + ] +} diff --git a/README.md b/README.md index 232b4ec..5f76e5b 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,23 @@ A tool for managing macOS virtual machines as GitHub Actions self-hosted runners - `wget` - File downloads - `packer` - VM image building +### Install from Release + +Download the notarized `rvmm_macOS_arm64.pkg` installer from the [GitHub Releases](../../releases) page and install it. The binary is installed to `/usr/local/bin/rvmm`. + ### Build from Source +The CLI lives in the `cli/` directory: + ```bash git clone -cd macos-github-action-vm -go build -o rvmm main.go +cd macos-github-action-vm/cli +make build # outputs cli/bin/rvmm ``` ## Configuration -Create a `rvmm.yaml` configuration file (see `assets/config.yaml.example` for a template): +Create a `rvmm.yaml` configuration file (see `cli/assets/config.yaml.example` for a template): ```yaml github: @@ -88,7 +94,7 @@ Run without arguments to start the interactive terminal UI: The TUI provides menus for: - **Setup**: Install required dependencies -- **Build**: Build VM image from IPSW +- **Build**: Build VM image from an external Packer template - **Config**: Edit configuration - **Run**: Start runners interactively - **Images**: List local VM images @@ -96,6 +102,19 @@ The TUI provides menus for: - **Monitor Daemon**: Install/manage log monitoring daemon - **View Logs**: Tail log files +### VM Image Templates + +VM image templates are not bundled with the CLI — they are provided externally. When you select "Build VM image" in the TUI, you are prompted for the path to a Packer template (`.pkr.hcl`). The CLI runs `packer init` and `packer build` in the template's directory, so any supporting files (setup scripts, etc.) should live next to the template. + +An example template is provided in [`guest/runner.pkr.hcl`](guest/runner.pkr.hcl): + +```bash +rvmm # Open TUI +# Select "Build VM image" and enter e.g. /path/to/guest/runner.pkr.hcl +``` + +You can maintain your own templates in a separate repository and point the CLI at them. + ### Headless Mode #### Run Runner @@ -213,14 +232,23 @@ In PostHog, you can: ## Architecture -- **internal/config**: Configuration management with Viper -- **internal/runner**: GitHub Actions runner logic and VM management -- **internal/daemon**: LaunchAgent/LaunchDaemon installation and management -- **internal/monitor**: Log file monitoring with tail-follow logic -- **internal/posthog**: PostHog API client for log event capture -- **internal/tui**: Bubble Tea terminal UI -- **assets**: Embedded templates and example configs -- **guest**: VM image building scripts (Packer) +- **cli**: The rvmm CLI (Go module) + - **cli/internal/config**: Configuration management with Viper + - **cli/internal/runner**: GitHub Actions runner logic and VM management + - **cli/internal/daemon**: LaunchAgent/LaunchDaemon installation and management + - **cli/internal/monitor**: Log file monitoring with tail-follow logic + - **cli/internal/posthog**: PostHog API client for log event capture + - **cli/internal/tui**: Bubble Tea terminal UI + - **cli/assets**: Embedded plist template and example config + - **cli/scripts**: Build, signing, and notarization scripts for releases +- **guest**: Example external Packer template for building the runner VM image + +## Releases + +Releases are automated via GitHub Actions: + +1. The "Create a new release" workflow (`create-release.yaml`) runs semantic-release to tag and create a GitHub release. +2. The "Release rvmm CLI" workflow (`release-cli.yaml`) triggers on the release event: it builds the CLI, signs the binary, packages it as a `.pkg` installer, notarizes it with Apple, and uploads `rvmm_macOS_arm64.pkg` to the GitHub release. ## Daemon Files diff --git a/Makefile b/cli/Makefile similarity index 68% rename from Makefile rename to cli/Makefile index ecc8ce6..f3707dd 100644 --- a/Makefile +++ b/cli/Makefile @@ -1,6 +1,7 @@ SHELL := /bin/bash BINARY_NAME := rvmm +BUILD_DIR := ./bin VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev) COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo none) @@ -11,7 +12,7 @@ LDFLAGS := -s -w \ -X github.com/rxtech-lab/rvmm/cmd.Commit=$(COMMIT) \ -X github.com/rxtech-lab/rvmm/cmd.BuildDate=$(BUILD_DATE) -.PHONY: deps build build-all install clean test +.PHONY: deps build build-all install package clean test deps: @echo "==> Fetching dependencies..." @@ -24,27 +25,33 @@ build: deps @echo " Version: $(VERSION)" @echo " Commit: $(COMMIT)" @echo " Date: $(BUILD_DATE)" - go build -ldflags "$(LDFLAGS)" -o "$(BINARY_NAME)" . - @echo "==> Built: $(BINARY_NAME)" + mkdir -p $(BUILD_DIR) + go build -ldflags "$(LDFLAGS)" -o "$(BUILD_DIR)/$(BINARY_NAME)" . + @echo "==> Built: $(BUILD_DIR)/$(BINARY_NAME)" build-all: deps @echo "==> Building for all platforms..." + mkdir -p $(BUILD_DIR) @echo " Building darwin/arm64..." - GOOS=darwin GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o "$(BINARY_NAME)-darwin-arm64" . + GOOS=darwin GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o "$(BUILD_DIR)/$(BINARY_NAME)-darwin-arm64" . @echo " Building darwin/amd64..." - GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o "$(BINARY_NAME)-darwin-amd64" . + GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o "$(BUILD_DIR)/$(BINARY_NAME)-darwin-amd64" . @echo "==> Built binaries:" - ls -la "$(BINARY_NAME)"-darwin-* + ls -la "$(BUILD_DIR)/$(BINARY_NAME)"-darwin-* -install: deps - $(MAKE) build +install: build @echo "==> Installing to /usr/local/bin..." - sudo cp "$(BINARY_NAME)" /usr/local/bin/ + sudo cp "$(BUILD_DIR)/$(BINARY_NAME)" /usr/local/bin/ @echo "==> Installed: /usr/local/bin/$(BINARY_NAME)" +package: build + ./scripts/sign.sh + ./scripts/package-notarize.sh + clean: @echo "==> Cleaning..." - rm -f "$(BINARY_NAME)" "$(BINARY_NAME)"-darwin-* + rm -rf $(BUILD_DIR) + rm -f $(BINARY_NAME)_macOS_*.pkg @echo "==> Cleaned" test: diff --git a/assets/com.mirego.ekiden.plist.tmpl b/cli/assets/com.mirego.ekiden.plist.tmpl similarity index 100% rename from assets/com.mirego.ekiden.plist.tmpl rename to cli/assets/com.mirego.ekiden.plist.tmpl diff --git a/assets/config.yaml.example b/cli/assets/config.yaml.example similarity index 100% rename from assets/config.yaml.example rename to cli/assets/config.yaml.example diff --git a/assets/embed.go b/cli/assets/embed.go similarity index 100% rename from assets/embed.go rename to cli/assets/embed.go diff --git a/go.mod b/cli/go.mod similarity index 100% rename from go.mod rename to cli/go.mod diff --git a/go.sum b/cli/go.sum similarity index 100% rename from go.sum rename to cli/go.sum diff --git a/internal/config/config.go b/cli/internal/config/config.go similarity index 100% rename from internal/config/config.go rename to cli/internal/config/config.go diff --git a/internal/config/validation.go b/cli/internal/config/validation.go similarity index 100% rename from internal/config/validation.go rename to cli/internal/config/validation.go diff --git a/internal/daemon/launchd.go b/cli/internal/daemon/launchd.go similarity index 100% rename from internal/daemon/launchd.go rename to cli/internal/daemon/launchd.go diff --git a/internal/monitor/logtail.go b/cli/internal/monitor/logtail.go similarity index 100% rename from internal/monitor/logtail.go rename to cli/internal/monitor/logtail.go diff --git a/internal/posthog/client.go b/cli/internal/posthog/client.go similarity index 100% rename from internal/posthog/client.go rename to cli/internal/posthog/client.go diff --git a/internal/runner/github.go b/cli/internal/runner/github.go similarity index 100% rename from internal/runner/github.go rename to cli/internal/runner/github.go diff --git a/internal/runner/loop.go b/cli/internal/runner/loop.go similarity index 100% rename from internal/runner/loop.go rename to cli/internal/runner/loop.go diff --git a/internal/runner/ssh.go b/cli/internal/runner/ssh.go similarity index 100% rename from internal/runner/ssh.go rename to cli/internal/runner/ssh.go diff --git a/internal/runner/vm.go b/cli/internal/runner/vm.go similarity index 100% rename from internal/runner/vm.go rename to cli/internal/runner/vm.go diff --git a/internal/setup/setup.go b/cli/internal/setup/setup.go similarity index 87% rename from internal/setup/setup.go rename to cli/internal/setup/setup.go index 3b9b310..47f29fe 100644 --- a/internal/setup/setup.go +++ b/cli/internal/setup/setup.go @@ -42,11 +42,6 @@ func RunWithIO(log *zap.Logger, stdout, stderr io.Writer, stdin io.Reader) error } } - // Initialize Packer plugins (installs the tart plugin) - if err := initPacker(log, stdout, stderr); err != nil { - return fmt.Errorf("packer init failed: %w", err) - } - // Validate system if err := validateSystem(log); err != nil { log.Warn("System validation warnings", zap.Error(err)) @@ -194,29 +189,6 @@ func ensureTap(log *zap.Logger, tap string, stdout, stderr io.Writer) error { return nil } -func initPacker(log *zap.Logger, stdout, stderr io.Writer) error { - workingDir, err := os.Getwd() - if err != nil { - return fmt.Errorf("get working directory: %w", err) - } - - pkrFile := filepath.Join(workingDir, "guest", "runner.pkr.hcl") - if _, err := os.Stat(pkrFile); os.IsNotExist(err) { - log.Info("Packer file not found, skipping packer init", zap.String("path", pkrFile)) - return nil - } - - log.Info("Initializing Packer plugins") - cmd := exec.Command("packer", "init", pkrFile) - cmd.Stdout = stdout - cmd.Stderr = stderr - if err := cmd.Run(); err != nil { - return fmt.Errorf("packer init %s failed: %w", pkrFile, err) - } - log.Info("Packer plugins initialized") - return nil -} - // CheckDependencies verifies all required tools are available func CheckDependencies() error { var missing []string diff --git a/internal/tui/app.go b/cli/internal/tui/app.go similarity index 93% rename from internal/tui/app.go rename to cli/internal/tui/app.go index 4333e0c..8e32570 100644 --- a/internal/tui/app.go +++ b/cli/internal/tui/app.go @@ -7,6 +7,7 @@ import ( "io" "os" "os/exec" + "path/filepath" "strings" "github.com/charmbracelet/bubbles/list" @@ -95,7 +96,7 @@ func newModel() model { menu := newMenuModel(rootMenuEntries()) buildInput := textinput.New() - buildInput.Placeholder = "Press Enter to build runner" + buildInput.Placeholder = "/path/to/runner.pkr.hcl" buildInput.CharLimit = 512 buildInput.Width = 50 @@ -296,10 +297,15 @@ func (m model) updateBuildPrompt(msg tea.KeyMsg) (tea.Model, tea.Cmd) { m.state = stateMenu return m, nil case "enter": + templatePath := strings.TrimSpace(m.buildInput.Value()) + if templatePath == "" { + m.lastError = "packer template path is required" + return m, nil + } m.state = stateMenu m.busy = true m.busyLabel = "Build VM image" - return m, tea.Batch(m.runBuildCmd(m.buildInput.Value()), m.spinner.Tick) + return m, tea.Batch(m.runBuildCmd(templatePath), m.spinner.Tick) } var cmd tea.Cmd @@ -385,10 +391,18 @@ func (m model) runSetupCmd() tea.Cmd { } } -func (m model) runBuildCmd(ipsw string) tea.Cmd { +func (m model) runBuildCmd(templatePath string) tea.Cmd { return func() tea.Msg { - guestDir := "guest" - if err := runCommandSeries(m.logWriter, guestDir, buildCommands(ipsw)...); err != nil { + info, err := os.Stat(templatePath) + if err != nil { + return taskDoneMsg{action: actionBuild, err: fmt.Errorf("packer template not found: %w", err)} + } + if info.IsDir() { + return taskDoneMsg{action: actionBuild, err: fmt.Errorf("packer template %q is a directory, expected a .pkr.hcl file", templatePath)} + } + templateDir := filepath.Dir(templatePath) + templateFile := filepath.Base(templatePath) + if err := runCommandSeries(m.logWriter, templateDir, buildCommands(templateFile)...); err != nil { return taskDoneMsg{action: actionBuild, err: err} } return taskDoneMsg{action: actionBuild, err: nil} diff --git a/internal/tui/commands.go b/cli/internal/tui/commands.go similarity index 89% rename from internal/tui/commands.go rename to cli/internal/tui/commands.go index 753562e..fcb9910 100644 --- a/internal/tui/commands.go +++ b/cli/internal/tui/commands.go @@ -54,10 +54,10 @@ func streamReader(writer io.Writer, reader io.Reader, wg *sync.WaitGroup) { } } -func buildCommands(ipsw string) []*exec.Cmd { +func buildCommands(templateFile string) []*exec.Cmd { return []*exec.Cmd{ - exec.Command("packer", "init", "runner.pkr.hcl"), - exec.Command("packer", "build", "runner.pkr.hcl"), + exec.Command("packer", "init", templateFile), + exec.Command("packer", "build", templateFile), } } diff --git a/internal/tui/config_form.go b/cli/internal/tui/config_form.go similarity index 100% rename from internal/tui/config_form.go rename to cli/internal/tui/config_form.go diff --git a/internal/tui/config_io.go b/cli/internal/tui/config_io.go similarity index 100% rename from internal/tui/config_io.go rename to cli/internal/tui/config_io.go diff --git a/internal/tui/logging.go b/cli/internal/tui/logging.go similarity index 100% rename from internal/tui/logging.go rename to cli/internal/tui/logging.go diff --git a/internal/tui/menu_build.go b/cli/internal/tui/menu_build.go similarity index 86% rename from internal/tui/menu_build.go rename to cli/internal/tui/menu_build.go index 1f4bf07..29a2fee 100644 --- a/internal/tui/menu_build.go +++ b/cli/internal/tui/menu_build.go @@ -9,7 +9,7 @@ func (buildMenuItem) Title() string { } func (buildMenuItem) Description() string { - return "Run Packer/Tart build for runner" + return "Run Packer/Tart build from an external template" } func (buildMenuItem) OnSelect(m *model) (tea.Model, tea.Cmd) { diff --git a/internal/tui/menu_config.go b/cli/internal/tui/menu_config.go similarity index 100% rename from internal/tui/menu_config.go rename to cli/internal/tui/menu_config.go diff --git a/internal/tui/menu_daemon.go b/cli/internal/tui/menu_daemon.go similarity index 100% rename from internal/tui/menu_daemon.go rename to cli/internal/tui/menu_daemon.go diff --git a/internal/tui/menu_daemon_install.go b/cli/internal/tui/menu_daemon_install.go similarity index 100% rename from internal/tui/menu_daemon_install.go rename to cli/internal/tui/menu_daemon_install.go diff --git a/internal/tui/menu_daemon_status.go b/cli/internal/tui/menu_daemon_status.go similarity index 100% rename from internal/tui/menu_daemon_status.go rename to cli/internal/tui/menu_daemon_status.go diff --git a/internal/tui/menu_daemon_uninstall.go b/cli/internal/tui/menu_daemon_uninstall.go similarity index 100% rename from internal/tui/menu_daemon_uninstall.go rename to cli/internal/tui/menu_daemon_uninstall.go diff --git a/internal/tui/menu_entry.go b/cli/internal/tui/menu_entry.go similarity index 100% rename from internal/tui/menu_entry.go rename to cli/internal/tui/menu_entry.go diff --git a/internal/tui/menu_images.go b/cli/internal/tui/menu_images.go similarity index 100% rename from internal/tui/menu_images.go rename to cli/internal/tui/menu_images.go diff --git a/internal/tui/menu_list_images.go b/cli/internal/tui/menu_list_images.go similarity index 100% rename from internal/tui/menu_list_images.go rename to cli/internal/tui/menu_list_images.go diff --git a/internal/tui/menu_monitor_daemon.go b/cli/internal/tui/menu_monitor_daemon.go similarity index 100% rename from internal/tui/menu_monitor_daemon.go rename to cli/internal/tui/menu_monitor_daemon.go diff --git a/internal/tui/menu_monitor_daemon_install.go b/cli/internal/tui/menu_monitor_daemon_install.go similarity index 100% rename from internal/tui/menu_monitor_daemon_install.go rename to cli/internal/tui/menu_monitor_daemon_install.go diff --git a/internal/tui/menu_monitor_daemon_status.go b/cli/internal/tui/menu_monitor_daemon_status.go similarity index 100% rename from internal/tui/menu_monitor_daemon_status.go rename to cli/internal/tui/menu_monitor_daemon_status.go diff --git a/internal/tui/menu_monitor_daemon_uninstall.go b/cli/internal/tui/menu_monitor_daemon_uninstall.go similarity index 100% rename from internal/tui/menu_monitor_daemon_uninstall.go rename to cli/internal/tui/menu_monitor_daemon_uninstall.go diff --git a/internal/tui/menu_pull_image.go b/cli/internal/tui/menu_pull_image.go similarity index 100% rename from internal/tui/menu_pull_image.go rename to cli/internal/tui/menu_pull_image.go diff --git a/internal/tui/menu_push_image.go b/cli/internal/tui/menu_push_image.go similarity index 100% rename from internal/tui/menu_push_image.go rename to cli/internal/tui/menu_push_image.go diff --git a/internal/tui/menu_quit.go b/cli/internal/tui/menu_quit.go similarity index 100% rename from internal/tui/menu_quit.go rename to cli/internal/tui/menu_quit.go diff --git a/internal/tui/menu_root.go b/cli/internal/tui/menu_root.go similarity index 100% rename from internal/tui/menu_root.go rename to cli/internal/tui/menu_root.go diff --git a/internal/tui/menu_run.go b/cli/internal/tui/menu_run.go similarity index 100% rename from internal/tui/menu_run.go rename to cli/internal/tui/menu_run.go diff --git a/internal/tui/menu_setup.go b/cli/internal/tui/menu_setup.go similarity index 100% rename from internal/tui/menu_setup.go rename to cli/internal/tui/menu_setup.go diff --git a/internal/tui/menu_view_logs.go b/cli/internal/tui/menu_view_logs.go similarity index 100% rename from internal/tui/menu_view_logs.go rename to cli/internal/tui/menu_view_logs.go diff --git a/internal/tui/views.go b/cli/internal/tui/views.go similarity index 95% rename from internal/tui/views.go rename to cli/internal/tui/views.go index 1be8297..6c64480 100644 --- a/internal/tui/views.go +++ b/cli/internal/tui/views.go @@ -66,7 +66,7 @@ func (m model) viewConfig() string { } func (m model) viewBuildPrompt() string { - return "Build VM image\n\n" + m.buildInput.View() + "\n\nEnter to start, Esc to cancel" + return "Build VM image\n\nPath to Packer template (.pkr.hcl):\n" + m.buildInput.View() + "\n\nEnter to start, Esc to cancel" } func (m model) viewPushPrompt() string { diff --git a/main.go b/cli/main.go similarity index 100% rename from main.go rename to cli/main.go diff --git a/cli/scripts/binaries.sh b/cli/scripts/binaries.sh new file mode 100755 index 0000000..adaac6e --- /dev/null +++ b/cli/scripts/binaries.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Define the list of binaries to process +BINARIES=( + "rvmm" +) diff --git a/cli/scripts/package-notarize.sh b/cli/scripts/package-notarize.sh new file mode 100755 index 0000000..b237b09 --- /dev/null +++ b/cli/scripts/package-notarize.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +set -e + +if [ -z "${INSTALLER_SIGNING_CERTIFICATE_NAME}" ]; then + echo "Error: INSTALLER_SIGNING_CERTIFICATE_NAME is not set" + exit 1 +fi + +if [ -z "${APPLE_ID}" ] || [ -z "${APPLE_ID_PWD}" ] || [ -z "${APPLE_TEAM_ID}" ]; then + echo "Error: Apple ID credentials not set (APPLE_ID, APPLE_ID_PWD, APPLE_TEAM_ID)" + exit 1 +fi + +source "$(dirname "$0")/binaries.sh" + +PKG_FILE="${PKG_FILE:-rvmm_macOS_arm64.pkg}" +PKG_VERSION="${PKG_VERSION:-1.0}" +PKG_IDENTIFIER="${PKG_IDENTIFIER:-lab.rxtech.rvmm}" +TMP_DIR="tmp_pkg_build" + +echo "Creating package structure" +mkdir -p "${TMP_DIR}/usr/local/bin" + +for binary in "${BINARIES[@]}"; do + BINARY_PATH="bin/${binary}" + + echo "Verifying binary signature: ${BINARY_PATH}" + codesign --verify --verbose "${BINARY_PATH}" || { + echo "Error: Binary ${binary} is not properly signed. Run sign.sh first." + exit 1 + } + + cp "${BINARY_PATH}" "${TMP_DIR}/usr/local/bin/" +done + +echo "Building pkg installer (version ${PKG_VERSION})" +pkgbuild --root "${TMP_DIR}" \ + --identifier "${PKG_IDENTIFIER}" \ + --version "${PKG_VERSION}" \ + --sign "${INSTALLER_SIGNING_CERTIFICATE_NAME}" \ + --install-location "/" \ + "${PKG_FILE}" + +rm -rf "${TMP_DIR}" + +echo "Submitting for notarization" +xcrun notarytool submit "${PKG_FILE}" --verbose --apple-id "${APPLE_ID}" --team-id "${APPLE_TEAM_ID}" --password "${APPLE_ID_PWD}" --wait + +echo "Stapling notarization ticket" +xcrun stapler staple -v "${PKG_FILE}" + +echo "Package created, signed, notarized and stapled successfully: ${PKG_FILE}" diff --git a/cli/scripts/sign.sh b/cli/scripts/sign.sh new file mode 100755 index 0000000..7655ba1 --- /dev/null +++ b/cli/scripts/sign.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -e + +if [ -z "${SIGNING_CERTIFICATE_NAME}" ]; then + echo "Error: SIGNING_CERTIFICATE_NAME is not set" + exit 1 +fi + +source "$(dirname "$0")/binaries.sh" + +for binary in "${BINARIES[@]}"; do + BINARY_PATH="bin/${binary}" + + echo "Signing binary: ${BINARY_PATH}" + codesign --force --options runtime --timestamp --sign "${SIGNING_CERTIFICATE_NAME}" "${BINARY_PATH}" + codesign --verify --verbose "${BINARY_PATH}" +done + +echo "Binaries signed successfully" diff --git a/guest/runner.pkr.hcl b/guest/runner.pkr.hcl index 06738f1..ad80143 100644 --- a/guest/runner.pkr.hcl +++ b/guest/runner.pkr.hcl @@ -8,11 +8,11 @@ packer { } source "tart-cli" "tart" { - vm_base_name = "ghcr.io/cirruslabs/macos-tahoe-xcode:26.2" + vm_base_name = "ghcr.io/cirruslabs/macos-tahoe-xcode:26.5" vm_name = "runner" cpu_count = 10 memory_gb = 32 - disk_size_gb = 120 + disk_size_gb = 140 display = "3840x2160" ssh_password = "admin" ssh_username = "admin"