Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/integration-test-init.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Integration Tests - Init Command

on:
workflow_dispatch:
# Manual trigger for testing init command flags
push:
branches: [main]
paths:
- 'src/cmd/init.go'
- 'src/internal/config/settings.go'
- 'src/internal/path/path_windows.go'
- 'src/internal/path/path_unix.go'
- 'install.ps1'
- 'install.sh'
- '.github/workflows/integration-test-init.yml'
pull_request:
branches: [main]
paths:
- 'src/cmd/init.go'
- 'src/internal/config/settings.go'
- 'src/internal/path/path_windows.go'
- 'src/internal/path/path_unix.go'
- 'install.ps1'
- 'install.sh'
- '.github/workflows/integration-test-init.yml'

permissions:
contents: read

jobs:
init:
name: Init Command
uses: CodingWithCalvin/.github/.github/workflows/dtvem-integration-test-init.yml@main
19 changes: 16 additions & 3 deletions install.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# dtvem installer for Windows
# Usage: irm https://raw.githubusercontent.com/CodingWithCalvin/dtvem.cli/main/install.ps1 | iex
# Usage:
# Standard (admin required): irm https://raw.githubusercontent.com/CodingWithCalvin/dtvem.cli/main/install.ps1 | iex
# User install (no admin): iex "& { $(irm https://raw.githubusercontent.com/CodingWithCalvin/dtvem.cli/main/install.ps1) } -UserInstall"

param(
[switch]$UserInstall
)

$ErrorActionPreference = "Stop"

Expand Down Expand Up @@ -117,6 +123,7 @@ function Test-Checksum {
}

function Main {
param([switch]$UserInstall)
Write-Host ""
Write-Host "========================================" -ForegroundColor Blue
Write-Host " dtvem installer" -ForegroundColor Blue
Expand Down Expand Up @@ -269,7 +276,13 @@ function Main {
# Temporarily add to PATH for this session
$env:Path = "$INSTALL_DIR;$env:Path"

& $dtvemPath init
if ($UserInstall) {
Write-Info "Using user-level PATH (no admin required)"
& $dtvemPath init --user -y
}
else {
& $dtvemPath init
}
Write-Success "dtvem is ready to use!"
Write-Info "Both $INSTALL_DIR and $SHIMS_DIR have been added to PATH"
}
Expand Down Expand Up @@ -298,4 +311,4 @@ function Main {
}
}

Main
Main -UserInstall:$UserInstall
36 changes: 31 additions & 5 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,25 @@
set -e

# dtvem installer for macOS and Linux
# Usage: curl -fsSL https://raw.githubusercontent.com/CodingWithCalvin/dtvem.cli/main/install.sh | bash
# Usage:
# Standard: curl -fsSL https://raw.githubusercontent.com/CodingWithCalvin/dtvem.cli/main/install.sh | bash
# User install: curl -fsSL https://raw.githubusercontent.com/CodingWithCalvin/dtvem.cli/main/install.sh | bash -s -- --user-install

REPO="CodingWithCalvin/dtvem.cli"
USER_INSTALL=false

# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--user-install)
USER_INSTALL=true
shift
;;
*)
shift
;;
esac
done

# This will be replaced with the actual version during release
# Format: DTVEM_RELEASE_VERSION="1.0.0"
Expand Down Expand Up @@ -366,11 +382,21 @@ main() {
# Run init to add shims directory to PATH
echo ""
info "Running dtvem init to add shims directory to PATH..."
if "$INSTALL_DIR/dtvem" init; then
success "dtvem is ready to use!"
info "Both $INSTALL_DIR and $SHIMS_DIR have been added to PATH"
if [ "$USER_INSTALL" = true ]; then
info "Using user-level PATH"
if "$INSTALL_DIR/dtvem" init --user -y; then
success "dtvem is ready to use!"
info "Both $INSTALL_DIR and $SHIMS_DIR have been added to PATH"
else
warning "dtvem init failed - you may need to run it manually"
fi
else
warning "dtvem init failed - you may need to run it manually"
if "$INSTALL_DIR/dtvem" init; then
success "dtvem is ready to use!"
info "Both $INSTALL_DIR and $SHIMS_DIR have been added to PATH"
else
warning "dtvem init failed - you may need to run it manually"
fi
fi

echo ""
Expand Down
25 changes: 25 additions & 0 deletions schemas/settings.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/CodingWithCalvin/dtvem.cli/main/schemas/settings.schema.json",
"title": "dtvem Settings Configuration",
"description": "Settings file for dtvem (Development Tool Virtual Environment Manager) installation preferences",
"type": "object",
"properties": {
"installType": {
"type": "string",
"description": "The type of dtvem installation. 'system' uses System PATH (requires admin on Windows), 'user' uses User PATH (no admin required).",
"enum": ["system", "user"],
"default": "system"
}
},
"required": ["installType"],
"additionalProperties": false,
"examples": [
{
"installType": "system"
},
{
"installType": "user"
}
]
}
102 changes: 99 additions & 3 deletions src/cmd/init.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package cmd

import (
"runtime"

"github.com/CodingWithCalvin/dtvem.cli/src/internal/config"
"github.com/CodingWithCalvin/dtvem.cli/src/internal/constants"
"github.com/CodingWithCalvin/dtvem.cli/src/internal/path"
"github.com/CodingWithCalvin/dtvem.cli/src/internal/ui"
"github.com/spf13/cobra"
)

var initYes bool
var (
initYes bool
initUser bool
)

var initCmd = &cobra.Command{
Use: "init",
Expand All @@ -18,10 +24,15 @@ This command:
- Creates the ~/.dtvem directory structure
- Adds ~/.dtvem/shims to your PATH (with your permission)

Options:
--user Use User PATH instead of System PATH on Windows (no admin required)
Note: System-installed runtimes will take priority over dtvem shims

Run this command after installing dtvem for the first time.

Example:
dtvem init`,
dtvem init
dtvem init --user # Windows: use User PATH (no admin)`,
Run: func(cmd *cobra.Command, args []string) {
ui.Header("Initializing dtvem...")

Expand All @@ -37,24 +48,109 @@ Example:

spinner.Success("Directories created")

// Determine install type and check for switching
userInstall := determineInstallType(cmd)
previousSettings, _ := config.LoadSettings()
isSwitching := cmd.Flags().Changed("user") && previousSettings != nil &&
((userInstall && previousSettings.InstallType == config.InstallTypeSystem) ||
(!userInstall && previousSettings.InstallType == config.InstallTypeUser))

// Warn about switching install types on Windows
if isSwitching && runtime.GOOS == constants.OSWindows {
warnAboutInstallTypeSwitch(userInstall, previousSettings.InstallType)
}

// Setup PATH - AddToPath handles checking position and moving if needed
shimsDir := path.ShimsDir()

if err := path.AddToPath(shimsDir, initYes); err != nil {
if err := path.AddToPath(shimsDir, initYes, userInstall); err != nil {
ui.Error("Failed to configure PATH: %v", err)
ui.Info("You can manually add %s to your PATH", shimsDir)
return
}

// Save settings for future reference
installType := config.InstallTypeSystem
if userInstall {
installType = config.InstallTypeUser
}
settings := &config.Settings{InstallType: installType}
if err := config.SaveSettings(settings); err != nil {
ui.Warning("Failed to save settings: %v", err)
}

ui.Success("dtvem initialized successfully!")

// Show reminder for user-level installations on Windows
if userInstall && runtime.GOOS == constants.OSWindows {
ui.Info("")
ui.Warning("Note: Using User PATH. System-installed runtimes may take priority.")
ui.Info("Run 'dtvem init' as administrator for system-level PATH if needed.")
}

ui.Info("\nNext steps:")
ui.Info(" 1. Restart your terminal (required for PATH changes)")
ui.Info(" 2. Run: dtvem install <runtime> <version>")
ui.Info(" 3. Run: dtvem global <runtime> <version>")
},
}

// determineInstallType determines whether to use user-level or system-level installation.
// Priority: flag > saved settings > default (system)
func determineInstallType(cmd *cobra.Command) bool {
// If --user flag was explicitly set, use it
if cmd.Flags().Changed("user") {
return initUser
}

// Check saved settings
settings, err := config.LoadSettings()
if err == nil && settings.InstallType == config.InstallTypeUser {
return true
}

// Default to system install
return false
}

// warnAboutInstallTypeSwitch warns the user about switching install types
// and provides instructions for cleaning up the old PATH entry.
func warnAboutInstallTypeSwitch(toUser bool, previousType config.InstallType) {
shimsDir := path.ShimsDir()

ui.Warning("Switching install type from %s to %s", previousType, map[bool]string{true: "user", false: "system"}[toUser])
ui.Info("")

if toUser {
// Switching from system to user
ui.Info("Your previous system-level PATH entry may still exist.")
ui.Info("To avoid conflicts, you may want to remove the old System PATH entry:")
ui.Info("")
ui.Info(" Manual removal steps:")
ui.Info(" 1. Open System Properties > Environment Variables")
ui.Info(" 2. Under 'System variables', select 'Path' and click 'Edit'")
ui.Info(" 3. Remove the entry: %s", ui.Highlight(shimsDir))
ui.Info(" 4. Click OK to save")
ui.Info("")
ui.Info(" Or run as administrator:")
ui.Info(" dtvem init (without --user)")
ui.Info(" This will move the entry to System PATH properly.")
} else {
// Switching from user to system
ui.Info("Your previous user-level PATH entry may still exist.")
ui.Info("To avoid conflicts, you may want to remove the old User PATH entry:")
ui.Info("")
ui.Info(" Manual removal steps:")
ui.Info(" 1. Open System Properties > Environment Variables")
ui.Info(" 2. Under 'User variables', select 'Path' and click 'Edit'")
ui.Info(" 3. Remove the entry: %s", ui.Highlight(shimsDir))
ui.Info(" 4. Click OK to save")
}
ui.Info("")
}

func init() {
initCmd.Flags().BoolVarP(&initYes, "yes", "y", false, "Skip confirmation prompts")
initCmd.Flags().BoolVar(&initUser, "user", false, "Use User PATH instead of System PATH (Windows: no admin required)")
rootCmd.AddCommand(initCmd)
}
Loading