-
Notifications
You must be signed in to change notification settings - Fork 1
Add release automation and self-update functionality #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
89ccae7
Fix Homebrew formula style issues
jfox85 4751e6c
Add self-update functionality with GitHub release integration
jfox85 1a8227c
Fix lint errors: add error checking for os.Chdir calls in tests
jfox85 ae39818
Fix critical update system issues and security vulnerabilities
jfox85 d14ed9c
Add MarkUpdateNotified call in version command
jfox85 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| devx | ||
| .envrc | ||
| .tmuxp.yaml | ||
| .tmuxp.yaml | ||
| dist/ | ||
| .devx/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| package cmd | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
|
|
||
| "github.com/jfox85/devx/update" | ||
| "github.com/spf13/cobra" | ||
| ) | ||
|
|
||
| var ( | ||
| checkOnly bool | ||
| forceUpdate bool | ||
| ) | ||
|
|
||
| // updateCmd represents the update command | ||
| var updateCmd = &cobra.Command{ | ||
| Use: "update", | ||
| Short: "Update devx to the latest version", | ||
| Long: `Update devx to the latest version from GitHub releases. | ||
|
|
||
| This command will: | ||
| - Check for the latest release on GitHub | ||
| - Download and replace the current binary if a newer version is available | ||
| - Preserve the installation method when possible | ||
| - Show progress during download | ||
|
|
||
| Note: If devx was installed via Homebrew, you'll be directed to use 'brew upgrade' instead. | ||
|
|
||
| Examples: | ||
| devx update # Update to latest version | ||
| devx update --check # Only check for updates | ||
| devx update --force # Force update even if same version`, | ||
| Run: func(cmd *cobra.Command, args []string) { | ||
| if checkOnly { | ||
| checkForUpdates() | ||
| return | ||
| } | ||
|
|
||
| performUpdate() | ||
| }, | ||
| } | ||
|
|
||
| func init() { | ||
| rootCmd.AddCommand(updateCmd) | ||
|
|
||
| updateCmd.Flags().BoolVar(&checkOnly, "check", false, "Only check for updates without downloading") | ||
| updateCmd.Flags().BoolVar(&forceUpdate, "force", false, "Force update even if current version is latest") | ||
| } | ||
|
|
||
| // checkForUpdates checks if a newer version is available | ||
| func checkForUpdates() { | ||
| fmt.Println("Checking for updates...") | ||
|
|
||
| info, err := update.CheckForUpdates() | ||
| if err != nil { | ||
| fmt.Printf("Error checking for updates: %v\n", err) | ||
| os.Exit(1) | ||
| } | ||
|
|
||
| fmt.Printf("Current version: %s\n", info.CurrentVersion) | ||
| fmt.Printf("Latest version: %s\n", info.LatestVersion) | ||
|
|
||
| if !info.Available { | ||
| fmt.Println("✅ You are running the latest version!") | ||
| return | ||
| } | ||
|
|
||
| fmt.Printf("🆙 A newer version is available: %s → %s\n", info.CurrentVersion, info.LatestVersion) | ||
| fmt.Printf("Release URL: %s\n", info.ReleaseURL) | ||
| fmt.Println("\nRun 'devx update' to upgrade.") | ||
| } | ||
|
|
||
| // performUpdate downloads and installs the latest version | ||
| func performUpdate() { | ||
| // Check if we can self-update | ||
| if !update.CanSelfUpdate() { | ||
| fmt.Println(update.GetUpdateInstructions()) | ||
| return | ||
| } | ||
|
|
||
| fmt.Println("Checking for updates...") | ||
|
|
||
| // Check for updates first | ||
| info, err := update.CheckForUpdates() | ||
| if err != nil { | ||
| fmt.Printf("Error checking for updates: %v\n", err) | ||
| os.Exit(1) | ||
| } | ||
|
|
||
| fmt.Printf("Current version: %s\n", info.CurrentVersion) | ||
| fmt.Printf("Latest version: %s\n", info.LatestVersion) | ||
|
|
||
| // Check if update is needed | ||
| if !info.Available && !forceUpdate { | ||
| fmt.Println("✅ You are already running the latest version!") | ||
| return | ||
| } | ||
|
|
||
| if forceUpdate && !info.Available { | ||
| fmt.Println("Forcing update due to --force flag...") | ||
| } else { | ||
| fmt.Printf("🔄 Updating from %s to %s...\n", info.CurrentVersion, info.LatestVersion) | ||
| } | ||
|
|
||
| fmt.Println("📥 Downloading update...") | ||
|
|
||
| // Perform the update | ||
| if err := update.PerformUpdate(forceUpdate); err != nil { | ||
| fmt.Printf("❌ Update failed: %v\n", err) | ||
| os.Exit(1) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| package config | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "os" | ||
| "path/filepath" | ||
| "time" | ||
| ) | ||
|
|
||
| // UpdateCheckState stores the last update check information | ||
| type UpdateCheckState struct { | ||
| LastCheck time.Time `json:"last_check"` | ||
| LastNotifiedVersion string `json:"last_notified_version,omitempty"` | ||
| } | ||
|
|
||
| // GetUpdateCheckPath returns the path to the update check state file | ||
| func GetUpdateCheckPath() (string, error) { | ||
| configDir := GetConfigDir() | ||
| return filepath.Join(configDir, "updatecheck.json"), nil | ||
| } | ||
|
|
||
| // LoadUpdateCheckState loads the update check state from disk | ||
| func LoadUpdateCheckState() (*UpdateCheckState, error) { | ||
| path, err := GetUpdateCheckPath() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| data, err := os.ReadFile(path) | ||
| if err != nil { | ||
| if os.IsNotExist(err) { | ||
| // Return empty state if file doesn't exist | ||
| return &UpdateCheckState{}, nil | ||
| } | ||
| return nil, err | ||
| } | ||
|
|
||
| var state UpdateCheckState | ||
| if err := json.Unmarshal(data, &state); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return &state, nil | ||
| } | ||
|
|
||
| // SaveUpdateCheckState saves the update check state to disk | ||
| func SaveUpdateCheckState(state *UpdateCheckState) error { | ||
| path, err := GetUpdateCheckPath() | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Ensure config directory exists | ||
| if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| data, err := json.MarshalIndent(state, "", " ") | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return os.WriteFile(path, data, 0644) | ||
| } | ||
|
|
||
| // ShouldCheckForUpdates determines if we should check for updates based on interval | ||
| func ShouldCheckForUpdates(lastCheck time.Time, interval time.Duration) bool { | ||
| if lastCheck.IsZero() { | ||
| return true | ||
| } | ||
| return time.Since(lastCheck) >= interval | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.