giterm 2.0.3 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/giterm +100 -40
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 53b5e1ec2107115bc1d1b4ab6f65a466b65aa5a5702855d96439c16e14428fd0
|
|
4
|
+
data.tar.gz: a3fdd847c3d070482cf3454223c48db5106d7ea06e26e5cbe0b36b5dec2953d7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: da1850c2c21cc3665464754db30ff51f7ea23f50578ba405425cab78ed4c03e5ccc246325d3a51b69a5474dc7ae5b45ba056efa2e8ceb53eb03bd4a009806978
|
|
7
|
+
data.tar.gz: 9d765a7501cb42d820a85dfb45a14e1c1f320f47ce36bb349f5a2b2a7803c4a8ced05da83c82649625110833e83bf8773a93802f5100ea9fbf778b45b30d3bd4
|
data/giterm
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# Author: Geir Isene <g@isene.com> (adapted from RTFM)
|
|
8
8
|
# Github: https://github.com/isene/GiTerm
|
|
9
9
|
# License: Public domain
|
|
10
|
-
@version = '2.0.
|
|
10
|
+
@version = '2.0.4'
|
|
11
11
|
|
|
12
12
|
# SAVE & STORE TERMINAL {{{1
|
|
13
13
|
ORIG_STTY = `stty -g 2>/dev/null`.chomp rescue ''
|
|
@@ -555,7 +555,7 @@ def show_repo_overview
|
|
|
555
555
|
return
|
|
556
556
|
end
|
|
557
557
|
|
|
558
|
-
content = "Repository Overview\n".
|
|
558
|
+
content = "Repository Overview\n".bd.fg(156)
|
|
559
559
|
content += ('=' * 30) + "\n\n"
|
|
560
560
|
|
|
561
561
|
# Get repository information
|
|
@@ -911,7 +911,26 @@ def github_request_http(endpoint)
|
|
|
911
911
|
|
|
912
912
|
JSON.parse(body)
|
|
913
913
|
else
|
|
914
|
-
|
|
914
|
+
error_msg = case response.code
|
|
915
|
+
when '401'
|
|
916
|
+
'Authentication failed (token expired or invalid). Press T to update token.'
|
|
917
|
+
when '403'
|
|
918
|
+
remaining = response['X-RateLimit-Remaining']
|
|
919
|
+
if remaining == '0'
|
|
920
|
+
reset_time = response['X-RateLimit-Reset']
|
|
921
|
+
reset_in = reset_time ? ((reset_time.to_i - Time.now.to_i) / 60.0).ceil : '?'
|
|
922
|
+
"GitHub API rate limit exceeded. Resets in ~#{reset_in} minutes."
|
|
923
|
+
else
|
|
924
|
+
"Access forbidden (403). Check token permissions."
|
|
925
|
+
end
|
|
926
|
+
when '404'
|
|
927
|
+
'Resource not found (404). Repository may be private or deleted.'
|
|
928
|
+
when '429'
|
|
929
|
+
'Too many requests (429). Please wait a moment and retry.'
|
|
930
|
+
else
|
|
931
|
+
"GitHub API error: #{response.code} - #{response.message}"
|
|
932
|
+
end
|
|
933
|
+
{ error: error_msg }
|
|
915
934
|
end
|
|
916
935
|
end
|
|
917
936
|
rescue JSON::ParserError => e
|
|
@@ -931,33 +950,62 @@ end
|
|
|
931
950
|
|
|
932
951
|
def github_request_curl(endpoint)
|
|
933
952
|
begin
|
|
934
|
-
# Build curl command
|
|
953
|
+
# Build curl command with separate connect and total timeouts
|
|
935
954
|
url = "https://api.github.com#{endpoint}"
|
|
936
|
-
curl_cmd = "curl -s -
|
|
955
|
+
curl_cmd = "curl -s --connect-timeout 10 --max-time 20 " \
|
|
956
|
+
"-w '\\n%{http_code}' " \
|
|
937
957
|
"-H 'Authorization: token #{@github_token}' " \
|
|
938
958
|
"-H 'Accept: application/vnd.github.v3+json' " \
|
|
939
959
|
"-H 'User-Agent: GiTerm/1.0' " \
|
|
940
960
|
"'#{url}'"
|
|
941
|
-
|
|
961
|
+
|
|
942
962
|
# Debug: log the curl command (without showing full token)
|
|
943
963
|
debug_cmd = curl_cmd.sub(@github_token, "#{@github_token[0..10]}...")
|
|
944
964
|
log_debug("Executing curl: #{debug_cmd}")
|
|
945
|
-
|
|
965
|
+
|
|
946
966
|
# Capture both stdout and stderr for better debugging
|
|
947
|
-
|
|
967
|
+
raw_result = `#{curl_cmd} 2>&1`
|
|
948
968
|
exit_code = $?.exitstatus
|
|
949
|
-
|
|
950
|
-
if exit_code == 0
|
|
951
|
-
|
|
969
|
+
|
|
970
|
+
if exit_code == 0
|
|
971
|
+
# Extract HTTP status code from the last line (-w appends it)
|
|
972
|
+
lines = raw_result.rstrip.split("\n")
|
|
973
|
+
http_code = lines.last.strip
|
|
974
|
+
body = lines[0...-1].join("\n")
|
|
975
|
+
|
|
976
|
+
if http_code == '200' && !body.empty?
|
|
977
|
+
JSON.parse(body)
|
|
978
|
+
elsif body.empty?
|
|
979
|
+
{ error: 'Empty response body from GitHub API.' }
|
|
980
|
+
else
|
|
981
|
+
error_msg = case http_code
|
|
982
|
+
when '401'
|
|
983
|
+
'Authentication failed (token expired or invalid). Press T to update token.'
|
|
984
|
+
when '403'
|
|
985
|
+
parsed = JSON.parse(body) rescue {}
|
|
986
|
+
if parsed['message']&.include?('rate limit')
|
|
987
|
+
'GitHub API rate limit exceeded. Wait a few minutes and retry.'
|
|
988
|
+
else
|
|
989
|
+
"Access forbidden (403). Check token permissions."
|
|
990
|
+
end
|
|
991
|
+
when '404'
|
|
992
|
+
'Resource not found (404). Repository may be private or deleted.'
|
|
993
|
+
when '429'
|
|
994
|
+
'Too many requests (429). Please wait a moment and retry.'
|
|
995
|
+
else
|
|
996
|
+
"GitHub API error (HTTP #{http_code})."
|
|
997
|
+
end
|
|
998
|
+
{ error: error_msg }
|
|
999
|
+
end
|
|
952
1000
|
else
|
|
953
1001
|
error_msg = case exit_code
|
|
954
|
-
when
|
|
955
|
-
when
|
|
956
|
-
when
|
|
957
|
-
when
|
|
958
|
-
else "
|
|
1002
|
+
when 6 then 'Could not resolve host. Check network connection.'
|
|
1003
|
+
when 7 then 'Failed to connect to GitHub. Check network connection.'
|
|
1004
|
+
when 28 then 'Request timed out. GitHub may be slow or unreachable.'
|
|
1005
|
+
when 35 then 'SSL/TLS connection error.'
|
|
1006
|
+
else "Network error (curl exit code: #{exit_code})."
|
|
959
1007
|
end
|
|
960
|
-
{ error:
|
|
1008
|
+
{ error: error_msg }
|
|
961
1009
|
end
|
|
962
1010
|
rescue JSON::ParserError => e
|
|
963
1011
|
{ error: "Invalid JSON from curl: #{e.message}" }
|
|
@@ -970,7 +1018,7 @@ def show_github_setup_in_nongit
|
|
|
970
1018
|
@mode = :status # Use status mode but show special message
|
|
971
1019
|
@status_items = [] # Empty items list
|
|
972
1020
|
|
|
973
|
-
content = "GiTerm - GitHub Terminal UI\n".
|
|
1021
|
+
content = "GiTerm - GitHub Terminal UI\n".bd.fg(156)
|
|
974
1022
|
content += ('=' * 35) + "\n\n"
|
|
975
1023
|
|
|
976
1024
|
content += "Not in a Git repository\n".fg(196)
|
|
@@ -997,7 +1045,7 @@ def show_github_setup_in_nongit
|
|
|
997
1045
|
|
|
998
1046
|
@p_right.clear
|
|
999
1047
|
if @github_token.empty?
|
|
1000
|
-
help_text = "GitHub Setup Guide\n".
|
|
1048
|
+
help_text = "GitHub Setup Guide\n".bd.fg(156)
|
|
1001
1049
|
help_text += ('=' * 25) + "\n\n"
|
|
1002
1050
|
help_text += "1. Press 'T' to start token setup\n".fg(249)
|
|
1003
1051
|
help_text += "2. Follow the instructions\n".fg(249)
|
|
@@ -1037,7 +1085,7 @@ def github_repos
|
|
|
1037
1085
|
@p_bottom.say('Loading GitHub repositories...')
|
|
1038
1086
|
|
|
1039
1087
|
if @github_token.empty?
|
|
1040
|
-
content = "GitHub Integration Setup\n".
|
|
1088
|
+
content = "GitHub Integration Setup\n".bd.fg(156)
|
|
1041
1089
|
content += ('=' * 35) + "\n\n"
|
|
1042
1090
|
content += "[!] No GitHub token found\n\n".fg(196)
|
|
1043
1091
|
|
|
@@ -1160,9 +1208,9 @@ def show_repo_details(repo, immediate_extended_fetch = false)
|
|
|
1160
1208
|
end
|
|
1161
1209
|
|
|
1162
1210
|
def display_basic_repo_info(repo)
|
|
1163
|
-
content = (repo[:full_name] || 'Unknown repository').
|
|
1211
|
+
content = (repo[:full_name] || 'Unknown repository').bd.fg(156) + "\n"
|
|
1164
1212
|
content += ('=' * 60) + "\n\n"
|
|
1165
|
-
|
|
1213
|
+
|
|
1166
1214
|
# Basic repo info with nil protection (always shown immediately)
|
|
1167
1215
|
content += 'Description: '.fg(249) + (repo[:description] || 'No description') + "\n"
|
|
1168
1216
|
content += 'Private: '.fg(249) + (repo[:private] ? 'Yes' : 'No') + "\n"
|
|
@@ -1190,9 +1238,9 @@ def fetch_and_display_extended_content(repo)
|
|
|
1190
1238
|
log_debug("EXECUTING fetch_and_display_extended_content for: #{repo[:full_name]}")
|
|
1191
1239
|
|
|
1192
1240
|
# Build the basic content again
|
|
1193
|
-
content = (repo[:full_name] || 'Unknown repository').
|
|
1241
|
+
content = (repo[:full_name] || 'Unknown repository').bd.fg(156) + "\n"
|
|
1194
1242
|
content += ('=' * 60) + "\n\n"
|
|
1195
|
-
|
|
1243
|
+
|
|
1196
1244
|
content += 'Description: '.fg(249) + (repo[:description] || 'No description') + "\n"
|
|
1197
1245
|
content += 'Private: '.fg(249) + (repo[:private] ? 'Yes' : 'No') + "\n"
|
|
1198
1246
|
content += 'Language: '.fg(249) + (repo[:language] || 'Not specified') + "\n"
|
|
@@ -1205,7 +1253,7 @@ def fetch_and_display_extended_content(repo)
|
|
|
1205
1253
|
# Fetch README
|
|
1206
1254
|
readme_content = fetch_readme(repo[:full_name])
|
|
1207
1255
|
if readme_content
|
|
1208
|
-
content += "README.md".
|
|
1256
|
+
content += "README.md".bd.fg(154) + "\n"
|
|
1209
1257
|
content += ('-' * 20) + "\n"
|
|
1210
1258
|
content += readme_content + "\n\n"
|
|
1211
1259
|
end
|
|
@@ -1213,7 +1261,7 @@ def fetch_and_display_extended_content(repo)
|
|
|
1213
1261
|
# Fetch directory structure
|
|
1214
1262
|
files_content = fetch_repo_files(repo[:full_name])
|
|
1215
1263
|
if files_content
|
|
1216
|
-
content += "Repository Files".
|
|
1264
|
+
content += "Repository Files".bd.fg(154) + "\n"
|
|
1217
1265
|
content += ('-' * 20) + "\n"
|
|
1218
1266
|
content += files_content
|
|
1219
1267
|
end
|
|
@@ -1348,15 +1396,21 @@ def github_issues
|
|
|
1348
1396
|
return if @selected_repo.empty?
|
|
1349
1397
|
|
|
1350
1398
|
result = github_request("/repos/#{@selected_repo}/issues?state=open&per_page=50")
|
|
1351
|
-
|
|
1352
|
-
if result[:error]
|
|
1399
|
+
|
|
1400
|
+
if result.is_a?(Hash) && result[:error]
|
|
1353
1401
|
@p_left.say(result[:error].fg(196))
|
|
1402
|
+
@p_bottom.say('Failed to load issues.')
|
|
1354
1403
|
return
|
|
1355
1404
|
end
|
|
1356
|
-
|
|
1405
|
+
|
|
1406
|
+
unless result.is_a?(Array)
|
|
1407
|
+
@p_left.say('Unexpected response format from GitHub API.'.fg(196))
|
|
1408
|
+
return
|
|
1409
|
+
end
|
|
1410
|
+
|
|
1357
1411
|
result.each do |issue|
|
|
1358
1412
|
next if issue['pull_request'] # Skip PRs
|
|
1359
|
-
|
|
1413
|
+
|
|
1360
1414
|
@github_issues << {
|
|
1361
1415
|
number: issue['number'],
|
|
1362
1416
|
title: issue['title'],
|
|
@@ -1405,7 +1459,7 @@ def display_github_issues
|
|
|
1405
1459
|
end
|
|
1406
1460
|
|
|
1407
1461
|
def show_issue_details(issue)
|
|
1408
|
-
content = "##{issue[:number]} #{issue[:title]}".
|
|
1462
|
+
content = "##{issue[:number]} #{issue[:title]}".bd.fg(156) + "\n"
|
|
1409
1463
|
content += ('=' * 60) + "\n\n"
|
|
1410
1464
|
content += 'Author: '.fg(249) + issue[:user] + "\n"
|
|
1411
1465
|
content += 'Created: '.fg(249) + issue[:created_at] + "\n"
|
|
@@ -1435,12 +1489,18 @@ def github_pull_requests
|
|
|
1435
1489
|
return if @selected_repo.empty?
|
|
1436
1490
|
|
|
1437
1491
|
result = github_request("/repos/#{@selected_repo}/pulls?state=open&per_page=50")
|
|
1438
|
-
|
|
1439
|
-
if result[:error]
|
|
1492
|
+
|
|
1493
|
+
if result.is_a?(Hash) && result[:error]
|
|
1440
1494
|
@p_left.say(result[:error].fg(196))
|
|
1495
|
+
@p_bottom.say('Failed to load pull requests.')
|
|
1441
1496
|
return
|
|
1442
1497
|
end
|
|
1443
|
-
|
|
1498
|
+
|
|
1499
|
+
unless result.is_a?(Array)
|
|
1500
|
+
@p_left.say('Unexpected response format from GitHub API.'.fg(196))
|
|
1501
|
+
return
|
|
1502
|
+
end
|
|
1503
|
+
|
|
1444
1504
|
result.each do |pr|
|
|
1445
1505
|
@github_prs << {
|
|
1446
1506
|
number: pr['number'],
|
|
@@ -1491,7 +1551,7 @@ def display_github_prs
|
|
|
1491
1551
|
end
|
|
1492
1552
|
|
|
1493
1553
|
def show_pr_details(pull_request)
|
|
1494
|
-
content = "##{pull_request[:number]} #{pull_request[:title]}".
|
|
1554
|
+
content = "##{pull_request[:number]} #{pull_request[:title]}".bd.fg(156) + "\n"
|
|
1495
1555
|
content += ('=' * 60) + "\n\n"
|
|
1496
1556
|
content += 'Author: '.fg(249) + pull_request[:user] + "\n"
|
|
1497
1557
|
content += 'Created: '.fg(249) + pull_request[:created_at] + "\n"
|
|
@@ -1557,14 +1617,14 @@ def github_search_repositories
|
|
|
1557
1617
|
# Perform the search
|
|
1558
1618
|
result = github_request("/search/repositories?q=#{CGI.escape(query)}&sort=stars&order=desc&per_page=50")
|
|
1559
1619
|
|
|
1560
|
-
if result[:error]
|
|
1620
|
+
if result.is_a?(Hash) && result[:error]
|
|
1561
1621
|
@p_left.clear
|
|
1562
1622
|
@p_left.say("Search Error: #{result[:error]}".fg(196))
|
|
1563
1623
|
return
|
|
1564
1624
|
end
|
|
1565
|
-
|
|
1625
|
+
|
|
1566
1626
|
# Process search results
|
|
1567
|
-
if result['items'] && result['items'].any?
|
|
1627
|
+
if result.is_a?(Hash) && result['items'] && result['items'].any?
|
|
1568
1628
|
result['items'].each do |repo|
|
|
1569
1629
|
@github_search_results << {
|
|
1570
1630
|
id: repo['id'],
|
|
@@ -1633,9 +1693,9 @@ def show_search_repo_details(repo, immediate_extended_fetch = false)
|
|
|
1633
1693
|
end
|
|
1634
1694
|
|
|
1635
1695
|
def display_basic_search_repo_info(repo)
|
|
1636
|
-
content = (repo[:full_name] || 'Unknown repository').
|
|
1696
|
+
content = (repo[:full_name] || 'Unknown repository').bd.fg(156) + "\n"
|
|
1637
1697
|
content += ('=' * 60) + "\n\n"
|
|
1638
|
-
|
|
1698
|
+
|
|
1639
1699
|
# Basic repo info with more details for search results (always shown immediately)
|
|
1640
1700
|
content += 'Owner: '.fg(249) + (repo[:owner] || 'Unknown') + "\n"
|
|
1641
1701
|
content += 'Description: '.fg(249) + (repo[:description] || 'No description') + "\n"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: giterm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Geir Isene
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: "."
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-04-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rcurses
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '7.0'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '7.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rubocop
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -54,8 +54,8 @@ dependencies:
|
|
|
54
54
|
version: '13.0'
|
|
55
55
|
description: 'GiTerm is a powerful terminal interface for Git and GitHub, providing
|
|
56
56
|
an intuitive TUI for repository management, issue tracking, and pull request handling.
|
|
57
|
-
Version 2.0.
|
|
58
|
-
|
|
57
|
+
Version 2.0.4: Improved GitHub API error recovery with specific messages for auth,
|
|
58
|
+
rate-limit, and network failures.'
|
|
59
59
|
email:
|
|
60
60
|
- g@isene.com
|
|
61
61
|
executables:
|