-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Description
CheckForUpdate behavior appears suboptimal as cli appears to do a ping to github on each run on macOS unless GH_NO_UPDATE_NOTIFIER is set because check for updates is a go routine that doesn't finish before the main task completes
$ for a in $(seq 2); do; echo "run $a:"; zGH_NO_UPDATE_NOTIFIER=1 GH_NO_EXTENSION_UPDATE_NOTIFIER=1 GH_HOST=localhost GH_DEBUG=api gh api /installation/repositories; echo; echo; done
run 1:
* Request at 2026-02-02 07:47:24.061823 -0500 EST m=+0.048067542
* Request to https://api.github.com/repos/cli/cli/releases/latest
> GET /repos/cli/cli/releases/latest HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ████████████████████
> Content-Type: application/json; charset=utf-8
> Time-Zone: America/Toronto
> User-Agent: GitHub CLI 2.86.0
* Request at 2026-02-02 07:47:24.06955 -0500 EST m=+0.055794042
* Request to https://localhost/api/v3/installation/repositories
> GET /api/v3/installation/repositories HTTP/1.1
> Host: localhost
> Accept: */*
> Content-Type: application/json; charset=utf-8
> Time-Zone: America/Toronto
> User-Agent: GitHub CLI 2.86.0
* dial tcp 127.0.0.1:443: connect: connection refused
* Request took 2.102209ms
Get "https://localhost/api/v3/installation/repositories": dial tcp 127.0.0.1:443: connect: connection refused
run 2:
* Request at 2026-02-02 07:47:24.164912 -0500 EST m=+0.080609501
* Request to https://api.github.com/repos/cli/cli/releases/latest
> GET /repos/cli/cli/releases/latest HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ████████████████████
> Content-Type: application/json; charset=utf-8
> Time-Zone: America/Toronto
> User-Agent: GitHub CLI 2.86.0
* Request at 2026-02-02 07:47:24.229458 -0500 EST m=+0.145155209
* Request to https://localhost/api/v3/installation/repositories
> GET /api/v3/installation/repositories HTTP/1.1
> Host: localhost
> Accept: */*
> Content-Type: application/json; charset=utf-8
> Time-Zone: America/Toronto
> User-Agent: GitHub CLI 2.86.0
* dial tcp 127.0.0.1:443: connect: connection refused
* Request took 2.027834ms
Get "https://localhost/api/v3/installation/repositories": dial tcp 127.0.0.1:443: connect: connection refused
Compare:
for a in $(seq 2); do; echo "run $a:"; GH_NO_UPDATE_NOTIFIER=1 GH_NO_EXTENSION_UPDATE_NOTIFIER=1 GH_HOST=localhost GH_DEBUG=api gh api /installation/repositories; echo; echo; done
run 1:
* Request at 2026-02-02 07:48:00.697519 -0500 EST m=+0.067201085
* Request to https://localhost/api/v3/installation/repositories
> GET /api/v3/installation/repositories HTTP/1.1
> Host: localhost
> Accept: */*
> Content-Type: application/json; charset=utf-8
> Time-Zone: America/Toronto
> User-Agent: GitHub CLI 2.86.0
* dial tcp 127.0.0.1:443: connect: connection refused
* Request took 1.969375ms
Get "https://localhost/api/v3/installation/repositories": dial tcp 127.0.0.1:443: connect: connection refused
run 2:
* Request at 2026-02-02 07:48:00.816502 -0500 EST m=+0.103877293
* Request to https://localhost/api/v3/installation/repositories
> GET /api/v3/installation/repositories HTTP/1.1
> Host: localhost
> Accept: */*
> Content-Type: application/json; charset=utf-8
> Time-Zone: America/Toronto
> User-Agent: GitHub CLI 2.86.0
* dial tcp 127.0.0.1:443: connect: connection refused
* Request took 1.702458ms
Get "https://localhost/api/v3/installation/repositories": dial tcp 127.0.0.1:443: connect: connection refused
cli/pkg/cmd/root/help_topic.go
Lines 94 to 96 in 150834b
| %[1]sGH_NO_UPDATE_NOTIFIER%[1]s: set to any value to disable GitHub CLI update notifications. | |
| When any command is executed, gh checks for new versions once every 24 hours. | |
| If a newer version was found, an upgrade notice is displayed on standard error. |
If it's supposedly checking once every 24 hours, then why is it pinging each time it's run unless it's told not to? These two runs are seconds apart.
Line 64 in 150834b
| rel, err := checkForUpdate(updateCtx, cmdFactory, buildVersion) |
I can see how to use GH_NO_UPDATE_NOTIFIER
Lines 79 to 81 in 150834b
| func ShouldCheckForUpdate() bool { | |
| if os.Getenv("GH_NO_UPDATE_NOTIFIER") != "" { | |
| return false |
to suppress:
Lines 216 to 225 in 150834b
| func checkForUpdate(ctx context.Context, f *cmdutil.Factory, currentVersion string) (*update.ReleaseInfo, error) { | |
| if updaterEnabled == "" || !update.ShouldCheckForUpdate() { | |
| return nil, nil | |
| } | |
| httpClient, err := f.HttpClient() | |
| if err != nil { | |
| return nil, err | |
| } | |
| stateFilePath := filepath.Join(config.StateDir(), "state.yml") | |
| return update.CheckForUpdate(ctx, httpClient, stateFilePath, updaterEnabled, currentVersion) |
But, I can't find any evidence that this works:
Lines 90 to 94 in 150834b
| func CheckForUpdate(ctx context.Context, client *http.Client, stateFilePath, repo, currentVersion string) (*ReleaseInfo, error) { | |
| stateEntry, _ := getStateEntry(stateFilePath) | |
| if stateEntry != nil && time.Since(stateEntry.CheckedForUpdateAt).Hours() < 24 { | |
| return nil, nil | |
| } |
I can't trace this code...
https://github.com/search?q=repo%3Acli/cli%20StateDir&type=code
Imagines a StateDir but there's apparently no public implementation??
no sign of `state` in strace
$ docker run -it ubuntu
root@ae5a1f4233d4:/# apt update; apt install -y strace gh
...
root@ae5a1f4233d4:/# strace -ff /usr/bin/gh api /installation/repositories 2>&1 |grep -E -v 'futex|poll|connect|sock|peer|mprotect|sigaltstack|rt_|sleep|mmap.NULL,|mmap.0x|dbus-|prlimit|random|munmap|sched_|fcntl|/etc/ld.so|/sys/kernel|tgkill|SIGURG|mmap resumed|rseq|gettid|getpid|brk|madvise|ioctl'
execve("/usr/bin/gh", ["/usr/bin/gh", "api", "/installation/repositories"], 0xffffeb181998 /* 10 vars */) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=5199, ...}) = 0
close(3) = 0
openat(AT_FDCWD, "/lib/aarch64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\360\206\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1722920, ...}) = 0
close(3) = 0
set_tid_address(0xffffb7e47fd0) = 402
set_robust_list(0xffffb7e47fe0, 24) = 0
openat(AT_FDCWD, "/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "00400000-02dd4000 r-xp 00000000 "..., 1024) = 1024
read(3, "00 00:00 0 \nffffb7e49000-ffffb7e"..., 1024) = 497
close(3) = 0
read(3, "2097152\n", 20) = 8
close(3) = 0
clone3({flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, child_tid=0xffff711cf250, parent_tid=0xffff711cf250, exit_signal=0, stack=0xffff709c0000, stack_size=0x80ea40, tls=0xffff711cf8c0}, 88) = -1 ENOSYS (Function not implemented)
clone(child_stack=0xffff711cea40, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTIDstrace: Process 403 attached
, parent_tid=[403], tls=0xffff711cf8c0, child_tidptr=0xffff711cf250) = 403
[pid 403] set_robust_list(0xffff711cf260, 24) = 0
[pid 402] clone(child_stack=0xffff6bffea40, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID <unfinished ...>
<unfinished ...>
[pid 402] <... clone resumed>, parent_tid=[404], tls=0xffff6bfff8c0, child_tidptr=0xffff6bfff250) = 404
[pid 404] set_robust_list(0xffff6bfff260, 24 <unfinished ...>
[pid 404] <... set_robust_list resumed>) = 0
[pid 402] clone(child_stack=0xffff709bea40, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID <unfinished ...>
strace: Process 405 attached
[pid 402] <... clone resumed>, parent_tid=[405], tls=0xffff709bf8c0, child_tidptr=0xffff709bf250) = 405
[pid 405] set_robust_list(0xffff709bf260, 24 <unfinished ...>
[pid 405] <... set_robust_list resumed>) = 0
[pid 402] clone(child_stack=0xffff6b7eea40, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID <unfinished ...>
strace: Process 406 attached
[pid 406] set_robust_list(0xffff6b7ef260, 24) = 0
[pid 402] <... clone resumed>, parent_tid=[406], tls=0xffff6b7ef8c0, child_tidptr=0xffff6b7ef250) = 406
[pid 402] readlinkat(AT_FDCWD, "/proc/self/exe", <unfinished ...>
[pid 402] <... readlinkat resumed>"/usr/bin/gh", 128) = 11
[pid 402] openat(AT_FDCWD, "/usr/bin/gh", O_RDONLY|O_CLOEXEC <unfinished ...>
[pid 402] <... openat resumed>) = 3
[pid 402] pipe2([5, 6], O_NONBLOCK|O_CLOEXEC) = 0
[pid 402] fstat(3, {st_mode=S_IFREG|0755, st_size=44919048, ...}) = 0
[pid 402] pread64(3, <unfinished ...>
[pid 402] <... pread64 resumed>"\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\2\0\267\0\1\0\0\0\300\26@\0\0\0\0\0"..., 64, 0) = 64
[pid 402] pread64(3, <unfinished ...>
[pid 402] <... pread64 resumed>"\3A\371\202Z\0\260B\200>\221\243\2\200\322(\361\352\227\341\3~\262\1\4\0\371\1\10\0\371\373"..., 64, 5614881) = 64
[pid 402] pread64(3, <unfinished ...>
[pid 402] <... pread64 resumed>"\1\371\340)\0\220\0\0 \221\371o\325\227\3413@\371\1\4\0\371;\5\1\220a\3I\271\201\0"..., 64, 11229762) = 64
[pid 402] pread64(3, "\1\0\0\0\0\340\26(\1\0\0\0\0\230\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\10\0\0"..., 64, 16844643) = 64
[pid 402] pread64(3, <unfinished ...>
[pid 402] <... pread64 resumed>"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 64, 22459524) = 64
[pid 402] pread64(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 64, 28074405) = 64
[pid 402] pread64(3, <unfinished ...>
[pid 402] <... pread64 resumed>"\4\0\0\0\0\0\0\0\0\0\240\4\0\0\0\0\0\0\0\0\0\240\244\4\0\0\0\0\0\0\0\0"..., 64, 33689286) = 64
[pid 402] pread64(3, "\4\20\2\3\1\2\2\2\0\0\6\2\10\2J\3\4\0\2\7\340\4J\337\4\1\340\4I\337\4\t"..., 64, 39304167) = 64
[pid 402] close(3) = 0
[pid 402] clone(child_stack=0xffff6afdea40, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID <unfinished ...>
strace: Process 407 attached
[pid 407] set_robust_list(0xffff6afdf260, 24) = 0
[pid 402] <... clone resumed>, parent_tid=[407], tls=0xffff6afdf8c0, child_tidptr=0xffff6afdf250) = 407
[pid 402] openat(AT_FDCWD, "/root/.config/gh/config.yml", O_RDONLY|O_CLOEXEC <unfinished ...>
[pid 402] <... openat resumed>) = -1 ENOENT (No such file or directory)
[pid 402] openat(AT_FDCWD, "/root/.config/gh/hosts.yml", O_RDONLY|O_CLOEXEC <unfinished ...>
[pid 402] <... openat resumed>) = -1 ENOENT (No such file or directory)
[pid 402] readlinkat(AT_FDCWD, "/proc/self/exe", <unfinished ...>
[pid 402] <... readlinkat resumed>"/usr/bin/gh", 128) = 11
[pid 402] newfstatat(AT_FDCWD, "/usr/local/sbin/gh", 0x4000142038, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
[pid 402] newfstatat(AT_FDCWD, "/usr/local/bin/gh", 0x4000142338, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
[pid 402] newfstatat(AT_FDCWD, "/usr/sbin/gh", <unfinished ...>
[pid 402] <... newfstatat resumed>0x40001423f8, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
[pid 402] newfstatat(AT_FDCWD, "/usr/bin/gh", {st_mode=S_IFREG|0755, st_size=44919048, ...}, AT_SYMLINK_NOFOLLOW) = 0
[pid 402] newfstatat(AT_FDCWD, "/etc/localtime", 0x4000142578, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
[pid 402] openat(AT_FDCWD, "/root/.local/share/gh/extensions", O_RDONLY|O_CLOEXEC <unfinished ...>
[pid 402] <... openat resumed>) = -1 ENOENT (No such file or directory)
[pid 402] newfstatat(AT_FDCWD, "/etc/localtime", 0x40000a27b8, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
[pid 402] openat(AT_FDCWD, "/dev/tty", O_RDONLY|O_CLOEXEC <unfinished ...>
[pid 402] <... openat resumed>) = 3
[pid 402] close(3) = 0
[pid 402] getuid( <unfinished ...>
[pid 402] <... getuid resumed>) = 0
[pid 402] close(3) = 0
[pid 402] close(3) = 0
[pid 402] newfstatat(AT_FDCWD, "/etc/nsswitch.conf", {st_mode=S_IFREG|0644, st_size=494, ...}, 0) = 0
[pid 402] newfstatat(AT_FDCWD, "/", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
[pid 402] openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3
[pid 402] fstat(3, {st_mode=S_IFREG|0644, st_size=494, ...}) = 0
[pid 402] read(3, "# /etc/nsswitch.conf\n#\n# Example"..., 4096) = 494
[pid 402] read(3, "", 4096) = 0
[pid 402] fstat(3, {st_mode=S_IFREG|0644, st_size=494, ...}) = 0
[pid 402] close(3 <unfinished ...>
[pid 402] <... close resumed>) = 0
[pid 402] openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC <unfinished ...>
[pid 402] <... openat resumed>) = 3
[pid 402] fstat(3, {st_mode=S_IFREG|0644, st_size=888, ...}) = 0
[pid 402] lseek(3, 0, SEEK_SET) = 0
[pid 402] read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 888
[pid 402] close(3) = 0
[pid 402] newfstatat(AT_FDCWD, "/run/user/0/bus", 0x40000a20f8, 0) = -1 ENOENT (No such file or directory)
[pid 402] openat(AT_FDCWD, "/etc/localtime", O_RDONLY <unfinished ...>
[pid 402] <... openat resumed>) = -1 ENOENT (No such file or directory)
[pid 402] write(2, "* Request at 2026-02-02 13:02:53"..., 68* Request at 2026-02-02 13:02:53.799175345 +0000 UTC m=+0.074146417
) = 68
[pid 402] write(2, "* Request to https://localhost/a"..., 64* Request to https://localhost/api/v3/installation/repositories
) = 64
[pid 402] write(2, "> GET /api/v3/installation/repos"..., 49> GET /api/v3/installation/repositories HTTP/1.1
) = 49
[pid 402] write(2, "> Host: localhost\n", 18> Host: localhost
) = 18
[pid 402] write(2, "> Accept: */*\n", 14> Accept: */*
) = 14
[pid 402] write(2, "> Content-Type: application/json"..., 48> Content-Type: application/json; charset=utf-8
) = 48
[pid 402] write(2, "> User-Agent: GitHub CLI 2.45.0\n", 32> User-Agent: GitHub CLI 2.45.0
) = 32
[pid 402] write(2, "\n", 1
) = 1
[pid 402] openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY|O_CLOEXEC <unfinished ...>
[pid 402] <... openat resumed>) = 3
[pid 402] fstat(3, {st_mode=S_IFREG|0644, st_size=233, ...}) = 0
[pid 402] read(3, "# Generated by Docker Engine.\n# "..., 65536) = 233
[pid 402] read(3, "", 65303) = 0
[pid 402] read(3, "", 65536) = 0
[pid 402] close(3) = 0
[pid 402] openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3
[pid 402] fstat(3, {st_mode=S_IFREG|0644, st_size=494, ...}) = 0
[pid 402] read(3, "# /etc/nsswitch.conf\n#\n# Example"..., 65536) = 494
[pid 402] read(3, "", 65042) = 0
[pid 402] read(3, "", 65536) = 0
[pid 402] close(3) = 0
[pid 402] newfstatat(AT_FDCWD, "/etc/hosts", {st_mode=S_IFREG|0644, st_size=172, ...}, 0) = 0
[pid 402] openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 3
[pid 402] read(3, "127.0.0.1\tlocalhost\n::1\tlocalhos"..., 65536) = 172
[pid 402] read(3, "", 65364) = 0
[pid 402] read(3, "", 65536) = 0
[pid 402] close(3) = 0
[pid 402] close(3) = 0
[pid 402] close(3) = 0
[pid 402] write(6, "\0", 1 <unfinished ...>
[pid 402] <... write resumed>) = 1
[pid 407] read(5, "\0", 16) = 1
[pid 407] close(3) = 0
[pid 407] write(6, "\0", 1 <unfinished ...>
[pid 407] <... write resumed>) = 1
[pid 402] read(5, <unfinished ...>
[pid 402] <... read resumed>"\0", 16) = 1
[pid 405] close(3) = 0
<unfinished ...>
[pid 405] <... write resumed>) = 50
[pid 405] write(2, "* Request took 9.522459ms\n", 26* Request took 9.522459ms
) = 26
) = 106
[pid 405] exit_group(1 <unfinished ...>
[pid 405] <... exit_group resumed>) = ?
[pid 405] +++ exited with 1 +++
[pid 404] +++ exited with 1 +++
[pid 403] +++ exited with 1 +++
[pid 407] +++ exited with 1 +++
[pid 406] +++ exited with 1 +++
+++ exited with 1 +++
root@ae5a1f4233d4:/#Note that I can't reproduce the update check behavior (see lack of requests in the ubuntu container). But on macOS it's consistent in performing an extra get for no apparent reason.