Skip to content

Commit 6abda9e

Browse files
Add gh_cli_jll as fallback when gh is not installed (#687)
- Added gh_cli_jll as a dependency to LinearSolveAutotune - Implemented get_gh_command() function that prefers system gh if available, falls back to JLL - Updated all gh CLI calls to use the get_gh_command() wrapper - Added tests to verify both system and JLL gh work correctly This ensures LinearSolveAutotune works even when gh CLI is not installed on the system. Co-authored-by: ChrisRackauckas <[email protected]>
1 parent c3dab6b commit 6abda9e

File tree

4 files changed

+80
-18
lines changed

4 files changed

+80
-18
lines changed

lib/LinearSolveAutotune/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
1010
CPUSummary = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9"
1111
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
1212
GitHub = "bc5e4493-9b4d-5f90-b8aa-2b2bcaad7a26"
13+
gh_cli_jll = "5d31d589-30fb-542f-b82d-10325e863e38"
1314
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
1415
PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
1516
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
@@ -33,6 +34,7 @@ Base64 = "1"
3334
CPUSummary = "0.2"
3435
DataFrames = "1"
3536
GitHub = "5"
37+
gh_cli_jll = "2"
3638
Plots = "1"
3739
PrettyTables = "2"
3840
Preferences = "1"

lib/LinearSolveAutotune/src/LinearSolveAutotune.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ using Metal
2424

2525
# Optional dependencies for telemetry and plotting
2626
using GitHub
27+
using gh_cli_jll
2728
using Plots
2829

2930
export autotune_setup, share_results, AutotuneResults, plot

lib/LinearSolveAutotune/src/telemetry.jl

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,42 @@
11
# Telemetry functionality for sharing benchmark results
22

3+
"""
4+
get_gh_command()
5+
6+
Get the gh command, preferring the system-installed version if available,
7+
falling back to the JLL-provided version.
8+
"""
9+
function get_gh_command()
10+
# First check if gh is installed on the system
11+
if !isnothing(Sys.which("gh"))
12+
return `gh`
13+
else
14+
# Use the JLL-provided gh
15+
return `$(gh_cli_jll.gh())`
16+
end
17+
end
18+
319
"""
420
setup_github_authentication()
521
622
Set up GitHub authentication for telemetry uploads.
723
Returns an authentication method indicator if successful, nothing if setup fails.
824
"""
925
function setup_github_authentication()
10-
# 1. Check for `gh` CLI
11-
if !isnothing(Sys.which("gh"))
12-
try
13-
# Suppress output of gh auth status check
14-
if success(pipeline(`gh auth status`; stdout=devnull, stderr=devnull))
15-
# Check if logged in to github.com
16-
auth_status_output = read(`gh auth status`, String)
17-
if contains(auth_status_output, "Logged in to github.com")
18-
println("✅ Found active `gh` CLI session. Will use it for upload.")
19-
return (:gh_cli, "GitHub CLI")
20-
end
26+
# 1. Check for `gh` CLI (system or JLL)
27+
try
28+
gh_cmd = get_gh_command()
29+
# Suppress output of gh auth status check
30+
if success(pipeline(`$gh_cmd auth status`; stdout=devnull, stderr=devnull))
31+
# Check if logged in to github.com
32+
auth_status_output = read(`$gh_cmd auth status`, String)
33+
if contains(auth_status_output, "Logged in to github.com")
34+
println("✅ Found active `gh` CLI session. Will use it for upload.")
35+
return (:gh_cli, "GitHub CLI")
2136
end
22-
catch e
23-
@debug "gh CLI check failed: $e"
2437
end
38+
catch e
39+
@debug "gh CLI check failed: $e"
2540
end
2641

2742
# 2. Check for GITHUB_TOKEN environment variable
@@ -532,6 +547,7 @@ function upload_plots_to_gist_gh(plot_files::Union{Nothing, Tuple, Dict}, eltype
532547
end
533548

534549
try
550+
gh_cmd = get_gh_command()
535551
# Handle different plot_files formats
536552
files_to_upload = if isa(plot_files, Tuple)
537553
# Legacy format: (png_file, pdf_file)
@@ -585,7 +601,7 @@ The PNG images can be viewed directly in the browser. Click on any `.png` file a
585601
# Create initial gist with README
586602
out = Pipe()
587603
err = Pipe()
588-
run(pipeline(`gh gist create -d $gist_desc -p $readme_file`, stdout=out, stderr=err))
604+
run(pipeline(`$gh_cmd gist create -d $gist_desc -p $readme_file`, stdout=out, stderr=err))
589605
close(out.in)
590606
close(err.in)
591607

@@ -603,7 +619,7 @@ The PNG images can be viewed directly in the browser. Click on any `.png` file a
603619
temp_dir = mktempdir()
604620
try
605621
# Clone the gist
606-
run(`gh gist clone $gist_id $temp_dir`)
622+
run(`$gh_cmd gist clone $gist_id $temp_dir`)
607623

608624
# Copy all plot files to the gist directory
609625
for (name, filepath) in existing_files
@@ -622,7 +638,7 @@ The PNG images can be viewed directly in the browser. Click on any `.png` file a
622638

623639
# Get username for constructing raw URLs
624640
username_out = Pipe()
625-
run(pipeline(`gh api user --jq .login`, stdout=username_out))
641+
run(pipeline(`$gh_cmd api user --jq .login`, stdout=username_out))
626642
close(username_out.in)
627643
username = strip(read(username_out, String))
628644

@@ -673,13 +689,14 @@ function comment_on_issue_gh(target_repo, issue_number, body)
673689
err_str = ""
674690
out_str = ""
675691
try
692+
gh_cmd = get_gh_command()
676693
# Use a temporary file for the body to avoid command line length limits
677694
mktemp() do path, io
678695
write(io, body)
679696
flush(io)
680697

681698
# Construct and run the gh command
682-
cmd = `gh issue comment $issue_number --repo $target_repo --body-file $path`
699+
cmd = `$gh_cmd issue comment $issue_number --repo $target_repo --body-file $path`
683700

684701
out = Pipe()
685702
err = Pipe()
@@ -725,13 +742,14 @@ function create_benchmark_issue_gh(target_repo, title, body)
725742
err_str = ""
726743
out_str = ""
727744
try
745+
gh_cmd = get_gh_command()
728746
# Use a temporary file for the body to avoid command line length limits
729747
mktemp() do path, io
730748
write(io, body)
731749
flush(io)
732750

733751
# Construct and run the gh command
734-
cmd = `gh issue create --repo $target_repo --title $title --body-file $path --label benchmark-data`
752+
cmd = `$gh_cmd issue create --repo $target_repo --title $title --body-file $path --label benchmark-data`
735753

736754
out = Pipe()
737755
err = Pipe()
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using Test
2+
using LinearSolveAutotune
3+
using gh_cli_jll
4+
5+
@testset "gh CLI fallback tests" begin
6+
# Test get_gh_command function
7+
@testset "get_gh_command" begin
8+
gh_cmd = LinearSolveAutotune.get_gh_command()
9+
@test gh_cmd isa Cmd
10+
11+
# Test that the command can be executed
12+
@test_nowarn begin
13+
version = read(`$gh_cmd version`, String)
14+
@test !isempty(version)
15+
@test occursin("gh version", version)
16+
end
17+
end
18+
19+
# Test JLL-provided gh directly
20+
@testset "JLL gh" begin
21+
jll_gh_cmd = `$(gh_cli_jll.gh())`
22+
@test jll_gh_cmd isa Cmd
23+
24+
# Test that JLL gh works
25+
@test_nowarn begin
26+
version = read(`$jll_gh_cmd version`, String)
27+
@test !isempty(version)
28+
@test occursin("gh version", version)
29+
end
30+
end
31+
32+
# Test authentication setup (may fail if not authenticated)
33+
@testset "Authentication setup" begin
34+
auth_result = LinearSolveAutotune.setup_github_authentication()
35+
@test auth_result isa Tuple
36+
@test length(auth_result) == 2
37+
# We don't require authentication to succeed, just that the function works
38+
end
39+
end
40+
41+
println("✅ All gh fallback tests passed!")

0 commit comments

Comments
 (0)