Skip to content

Check for new version of extension when invoked, print message notifying user of upgrade #8183

@andyfeller

Description

@andyfeller

Describe the feature or problem you’d like to solve

GitHub CLI extension authors want to ensure users are running the newest version of extensions.
Like the GitHub CLI checks for updates every 24 hours when invoked, the GitHub CLI could perform the same logic when an extension command is invoked.

This would ensure GitHub CLI extension users knew about potentially necessary or valuable updates while minimizing the amount of effort on extension authors from reimplementing the logic already within GitHub CLI.

Proposed solution

  1. Refactor the update logic within cmd/gh/main.go to be reusable

    cli/cmd/gh/main.go

    Lines 59 to 68 in 64f4660

    updateCtx, updateCancel := context.WithCancel(ctx)
    defer updateCancel()
    updateMessageChan := make(chan *update.ReleaseInfo)
    go func() {
    rel, err := checkForUpdate(updateCtx, cmdFactory, buildVersion)
    if err != nil && hasDebug {
    fmt.Fprintf(stderr, "warning: checking for update failed: %v", err)
    }
    updateMessageChan <- rel
    }()

    cli/cmd/gh/main.go

    Lines 165 to 182 in 64f4660

    updateCancel() // if the update checker hasn't completed by now, abort it
    newRelease := <-updateMessageChan
    if newRelease != nil {
    isHomebrew := isUnderHomebrew(cmdFactory.Executable())
    if isHomebrew && isRecentRelease(newRelease.PublishedAt) {
    // do not notify Homebrew users before the version bump had a chance to get merged into homebrew-core
    return exitOK
    }
    fmt.Fprintf(stderr, "\n\n%s %s → %s\n",
    ansi.Color("A new release of gh is available:", "yellow"),
    ansi.Color(strings.TrimPrefix(buildVersion, "v"), "cyan"),
    ansi.Color(strings.TrimPrefix(newRelease.Version, "v"), "cyan"))
    if isHomebrew {
    fmt.Fprintf(stderr, "To upgrade, run: %s\n", "brew upgrade gh")
    }
    fmt.Fprintf(stderr, "%s\n\n",
    ansi.Color(newRelease.URL, "yellow"))
    }

  2. Enhance dynamic extension command generation to ensure extension release is checked independently of one another or GitHub CLI

    cli/pkg/cmd/root/root.go

    Lines 170 to 175 in 64f4660

    // Extensions
    em := f.ExtensionManager
    for _, e := range em.List() {
    extensionCmd := NewCmdExtension(io, em, e)
    cmd.AddCommand(extensionCmd)
    }

  3. Ensure latest release information logic supports GitHub Enterprise users

    func getLatestReleaseInfo(ctx context.Context, client *http.Client, repo string) (*ReleaseInfo, error) {
    req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.github.com/repos/%s/releases/latest", repo), nil)
    if err != nil {
    return nil, err
    }
    res, err := client.Do(req)
    if err != nil {
    return nil, err
    }
    defer func() {
    _, _ = io.Copy(io.Discard, res.Body)
    res.Body.Close()
    }()
    if res.StatusCode != 200 {
    return nil, fmt.Errorf("unexpected HTTP %d", res.StatusCode)
    }
    dec := json.NewDecoder(res.Body)
    var latestRelease ReleaseInfo
    if err := dec.Decode(&latestRelease); err != nil {
    return nil, err
    }
    return &latestRelease, nil
    }

cc: @samcoe @nickfyson

Additional context

I was originally attempting to move the internal/update package to cli/go-gh, requiring the standardization on its api clients for the function. After talking with @samcoe about the process going from cli/cli api clients to cli/go-gh api clients, Sam's suggestion of having GitHub CLI do the checking would benefit all extensions without effort.

Metadata

Metadata

Assignees

Labels

coreThis issue is not accepting PRs from outside contributorsenhancementa request to improve CLIgh-extensionrelating to the gh extension command

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions