refactor: embed install.sh script in uc CLI to not curl | sh and version together with CLI

This commit is contained in:
Pasha Sviderski
2026-04-24 11:37:12 +10:00
parent cd07278858
commit fe829efbf5
3 changed files with 61 additions and 42 deletions
+16 -17
View File
@@ -2,6 +2,7 @@ package cli
import ( import (
"context" "context"
"encoding/base64"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@@ -13,14 +14,11 @@ import (
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/internal/machine/api/pb" "github.com/psviderski/uncloud/internal/machine/api/pb"
"github.com/psviderski/uncloud/internal/sshexec" "github.com/psviderski/uncloud/internal/sshexec"
"github.com/psviderski/uncloud/scripts"
"google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/emptypb"
) )
const ( const rootUser = "root"
// TODO: support pinning the script version to the CLI version.
installScriptURL = "https://raw.githubusercontent.com/psviderski/uncloud/refs/heads/main/scripts/install.sh"
rootUser = "root"
)
type RemoteMachine struct { type RemoteMachine struct {
User string User string
@@ -30,26 +28,30 @@ type RemoteMachine struct {
UseSSHGo bool // Use Go's built-in SSH library instead of the system ssh CLI command. UseSSHGo bool // Use Go's built-in SSH library instead of the system ssh CLI command.
} }
func installCmd(user string, version string) string { // installCmd returns a shell command that decodes the base64-encoded install script and pipes it
// into bash, optionally via sudo and with UNCLOUD_* environment variables set.
func installCmd(scriptBase64, user, version string) string {
sudoPrefix := "" sudoPrefix := ""
var env []string var env []string
// Add the SSH user (non-root) to the uncloud group to allow access to the Uncloud daemon unix socket. // Add the SSH user (non-root) to the uncloud group to allow access to the Uncloud daemon unix socket.
if user != rootUser { if user != rootUser {
sudoPrefix = "sudo" sudoPrefix = "sudo "
env = append(env, "UNCLOUD_GROUP_ADD_USER="+sshexec.Quote(user)) env = append(env, "UNCLOUD_GROUP_ADD_USER="+sshexec.Quote(user))
} }
if version != "" { if version != "" {
env = append(env, "UNCLOUD_VERSION="+sshexec.Quote(version)) env = append(env, "UNCLOUD_VERSION="+sshexec.Quote(version))
} }
envCmd := strings.Join(env, " ") envPrefix := ""
curlBashCmd := fmt.Sprintf("curl -fsSL %s | %s %s bash", sshexec.Quote(installScriptURL), sudoPrefix, envCmd) if len(env) > 0 {
envPrefix = strings.Join(env, " ") + " "
}
return curlBashCmd return fmt.Sprintf("printf '%%s' %s | base64 -d | %s%sbash", scriptBase64, sudoPrefix, envPrefix)
} }
// provisionMachine provisions the remote machine by downloading the Uncloud install script from GitHub and running it. // provisionMachine provisions the remote machine by running the Uncloud install script embedded in the uc CLI.
// If version is specified, it will be passed to the install script as UNCLOUD_VERSION environment variable. // If version is specified, it will be passed to the install script as UNCLOUD_VERSION environment variable.
func provisionMachine(ctx context.Context, exec sshexec.Executor, version string) error { func provisionMachine(ctx context.Context, exec sshexec.Executor, version string) error {
user, err := exec.Run(ctx, "whoami") user, err := exec.Run(ctx, "whoami")
@@ -88,13 +90,10 @@ func provisionMachine(ctx context.Context, exec sshexec.Executor, version string
} }
} }
cmd := installCmd(user, version) scriptBase64 := base64.StdEncoding.EncodeToString([]byte(scripts.InstallScript))
cmd := sshexec.QuoteCommand("bash", "-c", "set -o pipefail; "+installCmd(scriptBase64, user, version))
fmt.Println("Downloading Uncloud install script:", installScriptURL)
cmd = sshexec.QuoteCommand("bash", "-c", "set -o pipefail; "+cmd)
if err = exec.Stream(ctx, cmd, os.Stdout, os.Stderr); err != nil { if err = exec.Stream(ctx, cmd, os.Stdout, os.Stderr); err != nil {
return fmt.Errorf("download and run install script: %w", err) return fmt.Errorf("run install script: %w", err)
} }
return nil return nil
} }
+36 -24
View File
@@ -7,30 +7,42 @@ import (
) )
func TestInstallCmd(t *testing.T) { func TestInstallCmd(t *testing.T) {
t.Run("root", func(t *testing.T) { const scriptB64 = "SCRIPT_BASE64_PLACEHOLDER"
cmd := installCmd("root", "")
assert.NotContains(t, cmd, "sudo")
assert.NotContains(t, cmd, "UNCLOUD_GROUP_ADD_USER")
})
// Test with version tests := []struct {
t.Run("root with version", func(t *testing.T) { name string
cmd := installCmd("root", "v1.2.3") user string
assert.NotContains(t, cmd, "sudo") version string
assert.NotContains(t, cmd, "UNCLOUD_GROUP_ADD_USER") want string
assert.Contains(t, cmd, "UNCLOUD_VERSION=v1.2.3") }{
}) {
name: "root",
user: "root",
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | bash",
},
{
name: "root with version",
user: "root",
version: "v1.2.3",
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | UNCLOUD_VERSION=v1.2.3 bash",
},
{
name: "nonroot",
user: "nonroot",
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | sudo UNCLOUD_GROUP_ADD_USER=nonroot bash",
},
{
name: "nonroot with version",
user: "nonroot",
version: "v1.2.3",
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | " +
"sudo UNCLOUD_GROUP_ADD_USER=nonroot UNCLOUD_VERSION=v1.2.3 bash",
},
}
t.Run("nonroot", func(t *testing.T) { for _, tt := range tests {
cmd := installCmd("nonroot", "") t.Run(tt.name, func(t *testing.T) {
assert.Contains(t, cmd, "sudo") assert.Equal(t, tt.want, installCmd(scriptB64, tt.user, tt.version))
assert.Contains(t, cmd, "UNCLOUD_GROUP_ADD_USER=nonroot") })
}) }
t.Run("nonroot with version", func(t *testing.T) {
cmd := installCmd("nonroot", "v1.2.3")
assert.Contains(t, cmd, "sudo")
assert.Contains(t, cmd, "UNCLOUD_GROUP_ADD_USER=nonroot")
assert.Contains(t, cmd, "UNCLOUD_VERSION=v1.2.3")
})
} }
+8
View File
@@ -0,0 +1,8 @@
// Package scripts embeds shell scripts shipped with the uc CLI so they can be
// executed on remote machines without being fetched from the network.
package scripts
import _ "embed"
//go:embed install.sh
var InstallScript string