mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2025-03-05 20:49:04 -08:00
feat(upgrade): select source
This commit is contained in:
parent
6068b56e40
commit
06a372424f
|
@ -2,9 +2,10 @@ package cli
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -23,7 +24,12 @@ var noticeCmd = &cobra.Command{
|
|||
env.Init(flags)
|
||||
defer env.Close()
|
||||
|
||||
if notice, hasNotice := upgrade.Notice(env, false); hasNotice {
|
||||
sh := os.Getenv("POSH_SHELL")
|
||||
configFile := config.Path(configFlag)
|
||||
cfg := config.Load(configFile, sh, false)
|
||||
cfg.Upgrade.Cache = env.Cache()
|
||||
|
||||
if notice, hasNotice := cfg.Upgrade.Notice(); hasNotice {
|
||||
fmt.Println(notice)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"slices"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/build"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
|
||||
|
@ -32,36 +33,42 @@ var upgradeCmd = &cobra.Command{
|
|||
return
|
||||
}
|
||||
|
||||
sh := os.Getenv("POSH_SHELL")
|
||||
|
||||
env := &runtime.Terminal{}
|
||||
env.Init(nil)
|
||||
defer env.Close()
|
||||
|
||||
terminal.Init(env.Shell())
|
||||
terminal.Init(sh)
|
||||
fmt.Print(terminal.StartProgress())
|
||||
|
||||
latest, err := upgrade.Latest(env)
|
||||
configFile := config.Path(configFlag)
|
||||
cfg := config.Load(configFile, sh, false)
|
||||
cfg.Upgrade.Cache = env.Cache()
|
||||
|
||||
latest, err := cfg.Upgrade.Latest()
|
||||
if err != nil {
|
||||
fmt.Printf("\n❌ %s\n\n%s", err, terminal.StopProgress())
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
cfg.Upgrade.Version = fmt.Sprintf("v%s", latest)
|
||||
|
||||
if force {
|
||||
executeUpgrade(latest)
|
||||
executeUpgrade(cfg.Upgrade)
|
||||
return
|
||||
}
|
||||
|
||||
version := fmt.Sprintf("v%s", build.Version)
|
||||
|
||||
if upgrade.IsMajorUpgrade(version, latest) {
|
||||
if upgrade.IsMajorUpgrade(build.Version, latest) {
|
||||
message := terminal.StopProgress()
|
||||
message += fmt.Sprintf("\n🚨 major upgrade available: %s -> %s, use oh-my-posh upgrade --force to upgrade\n\n", version, latest)
|
||||
message += fmt.Sprintf("\n🚨 major upgrade available: v%s -> v%s, use oh-my-posh upgrade --force to upgrade\n\n", build.Version, latest)
|
||||
fmt.Print(message)
|
||||
return
|
||||
}
|
||||
|
||||
if version != latest {
|
||||
executeUpgrade(latest)
|
||||
if build.Version != latest {
|
||||
executeUpgrade(cfg.Upgrade)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -69,8 +76,8 @@ var upgradeCmd = &cobra.Command{
|
|||
},
|
||||
}
|
||||
|
||||
func executeUpgrade(latest string) {
|
||||
err := upgrade.Run(latest)
|
||||
func executeUpgrade(cfg *upgrade.Config) {
|
||||
err := upgrade.Run(cfg)
|
||||
fmt.Print(terminal.StopProgress())
|
||||
if err == nil {
|
||||
return
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -30,26 +31,27 @@ type Config struct {
|
|||
SecondaryPrompt *Segment `json:"secondary_prompt,omitempty" toml:"secondary_prompt,omitempty"`
|
||||
TransientPrompt *Segment `json:"transient_prompt,omitempty" toml:"transient_prompt,omitempty"`
|
||||
ErrorLine *Segment `json:"error_line,omitempty" toml:"error_line,omitempty"`
|
||||
ConsoleTitleTemplate string `json:"console_title_template,omitempty" toml:"console_title_template,omitempty"`
|
||||
Format string `json:"-" toml:"-"`
|
||||
TerminalBackground color.Ansi `json:"terminal_background,omitempty" toml:"terminal_background,omitempty"`
|
||||
origin string
|
||||
PWD string `json:"pwd,omitempty" toml:"pwd,omitempty"`
|
||||
AccentColor color.Ansi `json:"accent_color,omitempty" toml:"accent_color,omitempty"`
|
||||
Output string `json:"-" toml:"-"`
|
||||
TerminalBackground color.Ansi `json:"terminal_background,omitempty" toml:"terminal_background,omitempty"`
|
||||
ConsoleTitleTemplate string `json:"console_title_template,omitempty" toml:"console_title_template,omitempty"`
|
||||
Format string `json:"-" toml:"-"`
|
||||
Upgrade *upgrade.Config `json:"upgrade,omitempty" toml:"upgrade,omitempty"`
|
||||
Cycle color.Cycle `json:"cycle,omitempty" toml:"cycle,omitempty"`
|
||||
ITermFeatures terminal.ITermFeatures `json:"iterm_features,omitempty" toml:"iterm_features,omitempty"`
|
||||
Blocks []*Block `json:"blocks,omitempty" toml:"blocks,omitempty"`
|
||||
Tooltips []*Segment `json:"tooltips,omitempty" toml:"tooltips,omitempty"`
|
||||
Version int `json:"version" toml:"version"`
|
||||
UpgradeNotice bool `json:"upgrade_notice,omitempty" toml:"upgrade_notice,omitempty"`
|
||||
AutoUpgrade bool `json:"auto_upgrade,omitempty" toml:"auto_upgrade,omitempty"`
|
||||
AutoUpgrade bool `json:"-" toml:"-"`
|
||||
ShellIntegration bool `json:"shell_integration,omitempty" toml:"shell_integration,omitempty"`
|
||||
MigrateGlyphs bool `json:"-" toml:"-"`
|
||||
PatchPwshBleed bool `json:"patch_pwsh_bleed,omitempty" toml:"patch_pwsh_bleed,omitempty"`
|
||||
EnableCursorPositioning bool `json:"enable_cursor_positioning,omitempty" toml:"enable_cursor_positioning,omitempty"`
|
||||
updated bool
|
||||
FinalSpace bool `json:"final_space,omitempty" toml:"final_space,omitempty"`
|
||||
UpgradeNotice bool `json:"-" toml:"-"`
|
||||
}
|
||||
|
||||
func (cfg *Config) MakeColors(env runtime.Environment) color.String {
|
||||
|
@ -98,12 +100,12 @@ func (cfg *Config) Features(env runtime.Environment) shell.Features {
|
|||
feats = append(feats, shell.FTCSMarks)
|
||||
}
|
||||
|
||||
autoUpgrade := cfg.AutoUpgrade
|
||||
autoUpgrade := cfg.Upgrade.Auto
|
||||
if _, OK := env.Cache().Get(AUTOUPGRADE); OK {
|
||||
autoUpgrade = true
|
||||
}
|
||||
|
||||
upgradeNotice := cfg.UpgradeNotice
|
||||
upgradeNotice := cfg.Upgrade.DisplayNotice
|
||||
if _, OK := env.Cache().Get(UPGRADENOTICE); OK {
|
||||
upgradeNotice = true
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/jandedobbeleer/oh-my-posh/src/log"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/path"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
|
||||
|
||||
json "github.com/goccy/go-json"
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
|
@ -32,6 +33,19 @@ func Load(configFile, sh string, migrate bool) *Config {
|
|||
cfg.BackupAndMigrate()
|
||||
}
|
||||
|
||||
if cfg.Upgrade == nil {
|
||||
cfg.Upgrade = &upgrade.Config{
|
||||
Source: upgrade.CDN,
|
||||
DisplayNotice: cfg.UpgradeNotice,
|
||||
Auto: cfg.AutoUpgrade,
|
||||
Interval: cache.ONEWEEK,
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Upgrade.Interval.IsEmpty() {
|
||||
cfg.Upgrade.Interval = cache.ONEWEEK
|
||||
}
|
||||
|
||||
if !cfg.ShellIntegration {
|
||||
return cfg
|
||||
}
|
||||
|
|
|
@ -68,23 +68,29 @@ func (u *Upgrade) cachedLatest(current string) (*UpgradeCache, error) {
|
|||
}
|
||||
|
||||
func (u *Upgrade) checkUpdate(current string) (*UpgradeCache, error) {
|
||||
tag, err := upgrade.Latest(u.env)
|
||||
duration := u.props.GetString(properties.CacheDuration, string(cache.ONEWEEK))
|
||||
source := u.props.GetString(Source, string(upgrade.CDN))
|
||||
|
||||
cfg := &upgrade.Config{
|
||||
Source: upgrade.Source(source),
|
||||
Interval: cache.Duration(duration),
|
||||
}
|
||||
|
||||
latest, err := cfg.Latest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
latest := tag[1:]
|
||||
cacheData := &UpgradeCache{
|
||||
Latest: latest,
|
||||
Current: current,
|
||||
}
|
||||
|
||||
cacheJSON, err := json.Marshal(cacheData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// update cache
|
||||
duration := u.props.GetString(properties.CacheDuration, string(cache.ONEWEEK))
|
||||
u.env.Cache().Set(UPGRADECACHEKEY, string(cacheJSON), cache.Duration(duration))
|
||||
|
||||
return cacheData, nil
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package segments
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
|
@ -16,8 +15,10 @@ import (
|
|||
)
|
||||
|
||||
func TestUpgrade(t *testing.T) {
|
||||
ugc := &upgrade.Config{}
|
||||
latest, _ := ugc.Latest()
|
||||
|
||||
cases := []struct {
|
||||
Error error
|
||||
Case string
|
||||
CurrentVersion string
|
||||
LatestVersion string
|
||||
|
@ -33,33 +34,28 @@ func TestUpgrade(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Case: "On latest",
|
||||
CurrentVersion: "1.0.1",
|
||||
LatestVersion: "1.0.1",
|
||||
},
|
||||
{
|
||||
Case: "Error on update check",
|
||||
Error: errors.New("error"),
|
||||
CurrentVersion: latest,
|
||||
},
|
||||
{
|
||||
Case: "On previous, from cache",
|
||||
HasCache: true,
|
||||
CurrentVersion: "1.0.2",
|
||||
LatestVersion: "1.0.3",
|
||||
LatestVersion: latest,
|
||||
CachedVersion: "1.0.2",
|
||||
ExpectedEnabled: true,
|
||||
},
|
||||
{
|
||||
Case: "On latest, version changed",
|
||||
HasCache: true,
|
||||
CurrentVersion: "1.0.2",
|
||||
LatestVersion: "1.0.2",
|
||||
CurrentVersion: latest,
|
||||
LatestVersion: latest,
|
||||
CachedVersion: "1.0.1",
|
||||
},
|
||||
{
|
||||
Case: "On previous, version changed",
|
||||
HasCache: true,
|
||||
CurrentVersion: "1.0.2",
|
||||
LatestVersion: "1.0.3",
|
||||
LatestVersion: latest,
|
||||
CachedVersion: "1.0.1",
|
||||
ExpectedEnabled: true,
|
||||
},
|
||||
|
@ -73,15 +69,13 @@ func TestUpgrade(t *testing.T) {
|
|||
if len(tc.CachedVersion) == 0 {
|
||||
tc.CachedVersion = tc.CurrentVersion
|
||||
}
|
||||
|
||||
cacheData := fmt.Sprintf(`{"latest":"%s", "current": "%s"}`, tc.LatestVersion, tc.CachedVersion)
|
||||
cache.On("Get", UPGRADECACHEKEY).Return(cacheData, tc.HasCache)
|
||||
cache.On("Set", testify_.Anything, testify_.Anything, testify_.Anything)
|
||||
|
||||
build.Version = tc.CurrentVersion
|
||||
|
||||
json := fmt.Sprintf(`{"tag_name":"v%s"}`, tc.LatestVersion)
|
||||
env.On("HTTPRequest", upgrade.RELEASEURL).Return([]byte(json), tc.Error)
|
||||
|
||||
ug := &Upgrade{}
|
||||
ug.Init(properties.Map{}, env)
|
||||
|
||||
|
|
|
@ -39,42 +39,42 @@ type stateMsg state
|
|||
|
||||
type model struct {
|
||||
error error
|
||||
config *Config
|
||||
message string
|
||||
tag string
|
||||
spinner spinner.Model
|
||||
state state
|
||||
}
|
||||
|
||||
func initialModel(tag string) *model {
|
||||
func initialModel(cfg *Config) *model {
|
||||
s := spinner.New()
|
||||
s.Spinner = spinner.Dot
|
||||
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("170"))
|
||||
return &model{spinner: s, tag: tag}
|
||||
return &model{spinner: s, config: cfg}
|
||||
}
|
||||
|
||||
func (m *model) Init() tea.Cmd {
|
||||
defer func() {
|
||||
go func() {
|
||||
if err := install(m.tag); err != nil {
|
||||
m.error = err
|
||||
program.Send(resultMsg(fmt.Sprintf("❌ upgrade failed: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
message := "🚀 Upgrade successful"
|
||||
|
||||
current := fmt.Sprintf("v%s", build.Version)
|
||||
if current != m.tag {
|
||||
message += ", restart your shell to take full advantage of the new functionality"
|
||||
}
|
||||
|
||||
program.Send(resultMsg(message))
|
||||
}()
|
||||
}()
|
||||
go m.start()
|
||||
|
||||
return m.spinner.Tick
|
||||
}
|
||||
|
||||
func (m *model) start() {
|
||||
if err := install(m.config); err != nil {
|
||||
m.error = err
|
||||
program.Send(resultMsg(fmt.Sprintf("❌ upgrade failed: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
message := "🚀 Upgrade successful"
|
||||
|
||||
current := fmt.Sprintf("v%s", build.Version)
|
||||
if current != m.config.Version {
|
||||
message += ", restart your shell to take full advantage of the new functionality"
|
||||
}
|
||||
|
||||
program.Send(resultMsg(message))
|
||||
}
|
||||
|
||||
func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
|
@ -113,7 +113,7 @@ func (m *model) View() string {
|
|||
message = "Validating current installation"
|
||||
case downloading:
|
||||
m.spinner.Spinner = spinner.Globe
|
||||
message = "Downloading latest version"
|
||||
message = fmt.Sprintf("Downloading latest version from %s", m.config.Source.String())
|
||||
case verifying:
|
||||
m.spinner.Spinner = spinner.Moon
|
||||
message = "Verifying download"
|
||||
|
@ -125,19 +125,19 @@ func (m *model) View() string {
|
|||
return title + textStyle.Render(fmt.Sprintf("%s %s", m.spinner.View(), message))
|
||||
}
|
||||
|
||||
func Run(latest string) error {
|
||||
func Run(cfg *Config) error {
|
||||
titleStyle := lipgloss.NewStyle().Margin(1, 0, 1, 0)
|
||||
title = "📦 Upgrading Oh My Posh"
|
||||
|
||||
current := build.Version
|
||||
current := fmt.Sprintf("v%s", build.Version)
|
||||
if len(current) == 0 {
|
||||
current = "dev"
|
||||
}
|
||||
|
||||
title = fmt.Sprintf("%s from %s to %s", title, current, latest)
|
||||
title = fmt.Sprintf("%s from %s to %s", title, current, cfg.Version)
|
||||
title = titleStyle.Render(title)
|
||||
|
||||
program = tea.NewProgram(initialModel(latest))
|
||||
program = tea.NewProgram(initialModel(cfg))
|
||||
resultModel, _ := program.Run()
|
||||
|
||||
programModel, OK := resultModel.(*model)
|
||||
|
|
100
src/upgrade/config.go
Normal file
100
src/upgrade/config.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package upgrade
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
httplib "net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/http"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Cache cache.Cache `json:"-" toml:"-"`
|
||||
Source Source `json:"source" toml:"source"`
|
||||
Interval cache.Duration `json:"interval" toml:"interval"`
|
||||
Version string `json:"-" toml:"-"`
|
||||
Auto bool `json:"auto" toml:"auto"`
|
||||
DisplayNotice bool `json:"notice" toml:"notice"`
|
||||
Force bool `json:"-" toml:"-"`
|
||||
}
|
||||
|
||||
type Source string
|
||||
|
||||
const (
|
||||
GitHub Source = "github"
|
||||
CDN Source = "cdn"
|
||||
)
|
||||
|
||||
func (s Source) String() string {
|
||||
switch s {
|
||||
case GitHub:
|
||||
return "github.com"
|
||||
case CDN:
|
||||
return "cdn.ohmyposh.dev"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) Latest() (string, error) {
|
||||
cfg.Version = "latest"
|
||||
v, err := cfg.DownloadAsset("version.txt")
|
||||
version := strings.TrimSpace(string(v))
|
||||
return strings.TrimPrefix(version, "v"), err
|
||||
}
|
||||
|
||||
func (cfg *Config) DownloadAsset(asset string) ([]byte, error) {
|
||||
if len(cfg.Source) == 0 {
|
||||
cfg.Source = GitHub
|
||||
}
|
||||
|
||||
switch cfg.Source {
|
||||
case GitHub:
|
||||
var url string
|
||||
|
||||
switch cfg.Version {
|
||||
case "latest":
|
||||
url = fmt.Sprintf("https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/%s", asset)
|
||||
default:
|
||||
url = fmt.Sprintf("https://github.com/JanDeDobbeleer/oh-my-posh/releases/download/%s/%s", cfg.Version, asset)
|
||||
}
|
||||
|
||||
return cfg.Download(url)
|
||||
case CDN:
|
||||
fallthrough
|
||||
default:
|
||||
url := fmt.Sprintf("https://cdn.ohmyposh.dev/releases/%s/%s", cfg.Version, asset)
|
||||
return cfg.Download(url)
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) Download(url string) ([]byte, error) {
|
||||
req, err := httplib.NewRequestWithContext(context.Background(), "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("User-Agent", "oh-my-posh")
|
||||
req.Header.Add("Cache-Control", "max-age=0")
|
||||
|
||||
resp, err := http.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != httplib.StatusOK {
|
||||
return nil, fmt.Errorf("failed to download asset: %s", url)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package upgrade
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
httplib "net/http"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/http"
|
||||
)
|
||||
|
||||
func downloadReleaseAsset(tag, asset string) ([]byte, error) {
|
||||
url := fmt.Sprintf("https://github.com/JanDeDobbeleer/oh-my-posh/releases/download/%s/%s", tag, asset)
|
||||
|
||||
req, err := httplib.NewRequestWithContext(context.Background(), "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("User-Agent", "oh-my-posh")
|
||||
|
||||
resp, err := http.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != httplib.StatusOK {
|
||||
return nil, fmt.Errorf("failed to download asset: %s", url)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
|
@ -9,7 +9,7 @@ import (
|
|||
"path/filepath"
|
||||
)
|
||||
|
||||
func install(tag string) error {
|
||||
func install(cfg *Config) error {
|
||||
setState(validating)
|
||||
|
||||
executable, err := os.Executable()
|
||||
|
@ -28,7 +28,7 @@ func install(tag string) error {
|
|||
|
||||
setState(downloading)
|
||||
|
||||
data, err := downloadAndVerify(tag)
|
||||
data, err := downloadAndVerify(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ func install(tag string) error {
|
|||
_ = hideFile(oldPath)
|
||||
}
|
||||
|
||||
updateRegistry(tag, executable)
|
||||
updateRegistry(cfg.Version, executable)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,5 +5,3 @@ package upgrade
|
|||
func hideFile(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateRegistry(_, _ string) {}
|
||||
|
|
|
@ -1,72 +1,38 @@
|
|||
package upgrade
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
"os"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/build"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/log"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/http"
|
||||
)
|
||||
|
||||
type release struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
PublishedAt time.Time `json:"published_at"`
|
||||
NodeID string `json:"node_id"`
|
||||
TagName string `json:"tag_name"`
|
||||
TarballURL string `json:"tarball_url"`
|
||||
ZipballURL string `json:"zipball_url"`
|
||||
DiscussionURL string `json:"discussion_url"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
URL string `json:"url"`
|
||||
UploadURL string `json:"upload_url"`
|
||||
TargetCommitish string `json:"target_commitish"`
|
||||
Name string `json:"name"`
|
||||
Body string `json:"body"`
|
||||
AssetsURL string `json:"assets_url"`
|
||||
ID int `json:"id"`
|
||||
Prerelease bool `json:"prerelease"`
|
||||
Draft bool `json:"draft"`
|
||||
}
|
||||
|
||||
const (
|
||||
RELEASEURL = "https://api.github.com/repos/jandedobbeleer/oh-my-posh/releases/latest"
|
||||
CACHEKEY = "upgrade_check"
|
||||
CACHEKEY = "upgrade_check"
|
||||
|
||||
upgradeNotice = `
|
||||
A new release of Oh My Posh is available: %s → %s
|
||||
A new release of Oh My Posh is available: v%s → v%s
|
||||
To upgrade, run: 'oh-my-posh upgrade%s'
|
||||
|
||||
To enable automated upgrades, run: 'oh-my-posh enable upgrade'.
|
||||
`
|
||||
)
|
||||
|
||||
func Latest(env runtime.Environment) (string, error) {
|
||||
body, err := env.HTTPRequest(RELEASEURL, nil, 5000)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var release release
|
||||
// this can't fail
|
||||
_ = json.Unmarshal(body, &release)
|
||||
|
||||
if len(release.TagName) == 0 {
|
||||
return "", fmt.Errorf("failed to get latest release")
|
||||
}
|
||||
|
||||
return release.TagName, nil
|
||||
}
|
||||
|
||||
// Returns the upgrade notice if a new version is available
|
||||
// that should be displayed to the user.
|
||||
//
|
||||
// The upgrade check is only performed every other week.
|
||||
func Notice(env runtime.Environment, force bool) (string, bool) {
|
||||
func (cfg *Config) Notice() (string, bool) {
|
||||
// never validate when we install using the Windows Store
|
||||
if os.Getenv("POSH_INSTALLER") == "ws" {
|
||||
log.Debug("skipping upgrade check because we are using the Windows Store")
|
||||
return "", false
|
||||
}
|
||||
|
||||
// do not check when last validation was < 1 week ago
|
||||
if _, OK := env.Cache().Get(CACHEKEY); OK && !force {
|
||||
if _, OK := cfg.Cache.Get(CACHEKEY); OK && !cfg.Force {
|
||||
return "", false
|
||||
}
|
||||
|
||||
|
@ -74,27 +40,21 @@ func Notice(env runtime.Environment, force bool) (string, bool) {
|
|||
return "", false
|
||||
}
|
||||
|
||||
// never validate when we install using the Windows Store
|
||||
if env.Getenv("POSH_INSTALLER") == "ws" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
latest, err := Latest(env)
|
||||
latest, err := cfg.Latest()
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
env.Cache().Set(CACHEKEY, latest, cache.ONEWEEK)
|
||||
cfg.Cache.Set(CACHEKEY, latest, cfg.Interval)
|
||||
|
||||
version := fmt.Sprintf("v%s", build.Version)
|
||||
if latest == version {
|
||||
if latest == build.Version {
|
||||
return "", false
|
||||
}
|
||||
|
||||
var forceUpdate string
|
||||
if IsMajorUpgrade(version, latest) {
|
||||
if IsMajorUpgrade(build.Version, latest) {
|
||||
forceUpdate = " --force"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(upgradeNotice, version, latest, forceUpdate), true
|
||||
return fmt.Sprintf(upgradeNotice, build.Version, latest, forceUpdate), true
|
||||
}
|
||||
|
|
|
@ -1,52 +1,47 @@
|
|||
package upgrade
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/build"
|
||||
cache "github.com/jandedobbeleer/oh-my-posh/src/cache/mock"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
|
||||
cache_ "github.com/jandedobbeleer/oh-my-posh/src/cache/mock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
testify "github.com/stretchr/testify/mock"
|
||||
testify_ "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func TestCanUpgrade(t *testing.T) {
|
||||
ugc := &Config{}
|
||||
latest, _ := ugc.Latest()
|
||||
|
||||
cases := []struct {
|
||||
Error error
|
||||
Case string
|
||||
CurrentVersion string
|
||||
LatestVersion string
|
||||
GOOS string
|
||||
Installer string
|
||||
Expected bool
|
||||
Cache bool
|
||||
}{
|
||||
{Case: "Up to date", CurrentVersion: "3.0.0", LatestVersion: "v3.0.0"},
|
||||
{Case: "Outdated Windows", Expected: true, CurrentVersion: "3.0.0", LatestVersion: "v3.0.1", GOOS: runtime.WINDOWS},
|
||||
{Case: "Outdated Linux", Expected: true, CurrentVersion: "3.0.0", LatestVersion: "v3.0.1", GOOS: runtime.LINUX},
|
||||
{Case: "Outdated Darwin", Expected: true, CurrentVersion: "3.0.0", LatestVersion: "v3.0.1", GOOS: runtime.DARWIN},
|
||||
{Case: "Up to date", CurrentVersion: latest},
|
||||
{Case: "Outdated Linux", Expected: true, CurrentVersion: "3.0.0"},
|
||||
{Case: "Outdated Darwin", Expected: true, CurrentVersion: "3.0.0"},
|
||||
{Case: "Cached", Cache: true},
|
||||
{Case: "Error", Error: fmt.Errorf("error")},
|
||||
{Case: "Windows Store", Installer: "ws"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
env := new(mock.Environment)
|
||||
build.Version = tc.CurrentVersion
|
||||
c := &cache.Cache{}
|
||||
c := &cache_.Cache{}
|
||||
c.On("Get", CACHEKEY).Return("", tc.Cache)
|
||||
c.On("Set", testify.Anything, testify.Anything, testify.Anything)
|
||||
env.On("Cache").Return(c)
|
||||
env.On("GOOS").Return(tc.GOOS)
|
||||
env.On("Getenv", "POSH_INSTALLER").Return(tc.Installer)
|
||||
c.On("Set", testify_.Anything, testify_.Anything, testify_.Anything)
|
||||
ugc.Cache = c
|
||||
|
||||
json := fmt.Sprintf(`{"tag_name":"%s"}`, tc.LatestVersion)
|
||||
env.On("HTTPRequest", RELEASEURL).Return([]byte(json), tc.Error)
|
||||
// ignore the notice
|
||||
_, canUpgrade := Notice(env, false)
|
||||
if len(tc.Installer) > 0 {
|
||||
os.Setenv("POSH_INSTALLER", tc.Installer)
|
||||
}
|
||||
|
||||
_, canUpgrade := ugc.Notice()
|
||||
assert.Equal(t, tc.Expected, canUpgrade, tc.Case)
|
||||
|
||||
os.Setenv("POSH_INSTALLER", "")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
//go:embed public_key.pem
|
||||
var publicKey []byte
|
||||
|
||||
func downloadAndVerify(tag string) ([]byte, error) {
|
||||
func downloadAndVerify(cfg *Config) ([]byte, error) {
|
||||
extension := ""
|
||||
if stdruntime.GOOS == runtime.WINDOWS {
|
||||
extension = ".exe"
|
||||
|
@ -40,14 +40,14 @@ func downloadAndVerify(tag string) ([]byte, error) {
|
|||
|
||||
asset := fmt.Sprintf("posh-%s-%s%s", stdruntime.GOOS, stdruntime.GOARCH, extension)
|
||||
|
||||
data, err := downloadReleaseAsset(tag, asset)
|
||||
data, err := cfg.DownloadAsset(asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setState(verifying)
|
||||
|
||||
err = verify(tag, asset, data)
|
||||
err = verify(cfg, asset, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -55,13 +55,13 @@ func downloadAndVerify(tag string) ([]byte, error) {
|
|||
return data, nil
|
||||
}
|
||||
|
||||
func verify(tag, asset string, binary []byte) error {
|
||||
checksums, err := downloadReleaseAsset(tag, "checksums.txt")
|
||||
func verify(cfg *Config, asset string, binary []byte) error {
|
||||
checksums, err := cfg.DownloadAsset("checksums.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signature, err := downloadReleaseAsset(tag, "checksums.txt.sig")
|
||||
signature, err := cfg.DownloadAsset("checksums.txt.sig")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1281,8 +1281,22 @@
|
|||
"description": "The extensions to look for when determining if a folder is a Fortran workspace",
|
||||
"default": [
|
||||
"fpm.toml",
|
||||
"*.f", "*.for", "*.fpp", "*.f77", "*.f90", "*.f95", "*.f03", "*.f08",
|
||||
"*.F", "*.FOR", "*.FPP", "*.F77", "*.F90", "*.F95", "*.F03", "*.F08"
|
||||
"*.f",
|
||||
"*.for",
|
||||
"*.fpp",
|
||||
"*.f77",
|
||||
"*.f90",
|
||||
"*.f95",
|
||||
"*.f03",
|
||||
"*.f08",
|
||||
"*.F",
|
||||
"*.FOR",
|
||||
"*.FPP",
|
||||
"*.F77",
|
||||
"*.F90",
|
||||
"*.F95",
|
||||
"*.F03",
|
||||
"*.F08"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
|
@ -5036,17 +5050,36 @@
|
|||
"description": "https://ohmyposh.dev/docs/configuration/general#general-settings",
|
||||
"default": ""
|
||||
},
|
||||
"upgrade_notice": {
|
||||
"type": "boolean",
|
||||
"upgrade": {
|
||||
"type": "object",
|
||||
"title": "Enable Upgrade Notice",
|
||||
"description": "https://ohmyposh.dev/docs/configuration/general#general-settings",
|
||||
"default": false
|
||||
},
|
||||
"auto_upgrade": {
|
||||
"type": "boolean",
|
||||
"title": "Enable automatic upgrades for Oh My Posh (supports Windows/macOS only)",
|
||||
"description": "https://ohmyposh.dev/docs/configuration/general#general-settings",
|
||||
"default": false
|
||||
"default": {
|
||||
"source": "cdn",
|
||||
"auto": false,
|
||||
"notice": false
|
||||
},
|
||||
"properties": {
|
||||
"interval": {
|
||||
"$ref": "#/definitions/cache_duration"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"cdn",
|
||||
"github"
|
||||
],
|
||||
"default": "cdn"
|
||||
},
|
||||
"auto": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"notice": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch_pwsh_bleed": {
|
||||
"type": "boolean",
|
||||
|
|
|
@ -136,8 +136,7 @@ For example, the following is a valid `--config` flag:
|
|||
| `shell_integration` | `boolean` | `false` | enable shell integration using FinalTerm's OSC sequences. Works in bash, cmd (Clink v1.14.25+), fish, powershell and zsh |
|
||||
| `enable_cursor_positioning` | `boolean` | `false` | enable fetching the cursor position in bash and zsh to allow automatic hiding of leading newlines when at the top of the shell |
|
||||
| `patch_pwsh_bleed` | `boolean` | `false` | patch a PowerShell bug where the background colors bleed into the next line at the end of the buffer (can be removed when [this][pwsh-bleed] is merged) |
|
||||
| `upgrade_notice` | `boolean` | `false` | enable the notice that a new upgrade is available when `auto_upgrade` is disabled |
|
||||
| `auto_upgrade` | `boolean` | `false` | enable [automatic upgrades][upgrade] for Oh My Posh (supports Windows, macOS and Linux) |
|
||||
| `upgrade` | `Upgrade` | | enable auto upgrade or the upgrade notice. See [Upgrade] |
|
||||
| `iterm_features` | `[]string` | `false` | enable iTerm2 specific features:<ul><li>`prompt_mark`: add the `iterm2_prompt_mark` [function][iterm2-si] for supported shells</li><li>`current_dir`: expose the current directory for iTerm2</li><li>`remote_host`: expose the current remote and user for iTerm2</li></ul> |
|
||||
|
||||
### JSON Schema Validation
|
||||
|
@ -194,4 +193,4 @@ Converters won't catch this change, so you will need to adjust manually.
|
|||
[templates]: /docs/configuration/templates#config-variables
|
||||
[pwsh-bleed]: https://github.com/PowerShell/PowerShell/pull/19019
|
||||
[iterm2-si]: https://iterm2.com/documentation-shell-integration.html
|
||||
[upgrade]: /docs/installation/upgrade#automatic
|
||||
[Upgrade]: /docs/installation/upgrade
|
||||
|
|
|
@ -6,8 +6,34 @@ sidebar_label: ♻️ Upgrade
|
|||
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
import Config from "@site/src/components/Config.js";
|
||||
|
||||
## Manual
|
||||
## Configuration
|
||||
|
||||
Oh My Posh can display the availability of an update, or auto update itself when
|
||||
enabled by adding the following to your configuration.
|
||||
|
||||
<Config
|
||||
data={{
|
||||
upgrade: {
|
||||
notice: true,
|
||||
interval: "168h",
|
||||
auto: false,
|
||||
source: "cdn",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| ---------- | :-------: | :-----: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `notice` | `boolean` | `false` | enbale displaying the upgrade notice on shell start, only checks based on `interval` |
|
||||
| `auto` | `boolean` | `false` | automatically update Oh My Posh when an update is found, only checks based on `interval` |
|
||||
| `interval` | `string` | `24h` | the duration for which not to check for an update. The duration is a string in the format `1h2m3s` and is parsed using the [time.ParseDuration] function from the Go standard library |
|
||||
| `source` | `string` | `cdn` | where to fetch the information from. Accepted values are `cdn` (`https://cdn.ohmyposh.dev/releases/latest/version.txt`) and `github` (`https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/version.txt`) |
|
||||
|
||||
## Upgrade
|
||||
|
||||
### Manual
|
||||
|
||||
While you can always follow the upgrade steps listed under the installation section,
|
||||
you can also use the `upgrade` command to update Oh My Posh to the latest version.
|
||||
|
@ -16,7 +42,7 @@ you can also use the `upgrade` command to update Oh My Posh to the latest versio
|
|||
oh-my-posh upgrade
|
||||
```
|
||||
|
||||
## Automated
|
||||
### Automated
|
||||
|
||||
<Tabs
|
||||
defaultValue="cli"
|
||||
|
@ -35,11 +61,13 @@ oh-my-posh enable upgrade
|
|||
</TabItem>
|
||||
<TabItem value="config">
|
||||
|
||||
import Config from "@site/src/components/Config.js";
|
||||
|
||||
<Config
|
||||
data={{
|
||||
"auto_upgrade": true
|
||||
upgrade: {
|
||||
interval: "168h",
|
||||
auto: true,
|
||||
source: "cdn",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
|
|
Loading…
Reference in a new issue