Skip to content

Commit 6b5009f

Browse files
committed
Update GitHub.java
1 parent a31b9dc commit 6b5009f

1 file changed

Lines changed: 114 additions & 6 deletions

File tree

  • src/main/java/io/github/intisy/gradle/github/impl

src/main/java/io/github/intisy/gradle/github/impl/GitHub.java

Lines changed: 114 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,102 @@ private Response makeGitHubApiRequest(String url) throws IOException {
560560
return httpClient.newCall(requestBuilder.build()).execute();
561561
}
562562

563+
/**
564+
* Builds a user-friendly error message for a failed GitHub API response.
565+
* Consumes the response body if present. Call only when the response is not successful.
566+
*
567+
* @param response the failed HTTP response (body will be consumed)
568+
* @param context description of what was being requested (e.g. "release owner/repo tag v1.0")
569+
* @return a detailed error message including remediation hints
570+
*/
571+
private String buildGitHubApiErrorMessage(Response response, String context) {
572+
int code = response.code();
573+
String statusMessage = response.message();
574+
String bodyMessage = null;
575+
if (response.body() != null) {
576+
try {
577+
String body = response.body().string();
578+
if (body != null && !body.isEmpty()) {
579+
try {
580+
JsonObject json = gson.fromJson(body, JsonObject.class);
581+
if (json.has("message")) {
582+
bodyMessage = json.get("message").getAsString();
583+
}
584+
} catch (Exception e) {
585+
bodyMessage = body.length() > 200 ? body.substring(0, 200) + "..." : body;
586+
}
587+
}
588+
} catch (IOException e) {
589+
// ignore when reading error body
590+
}
591+
}
592+
593+
StringBuilder msg = new StringBuilder();
594+
msg.append("GitHub API request failed for ").append(context).append(". ");
595+
msg.append("HTTP ").append(code).append(" ").append(statusMessage);
596+
if (bodyMessage != null) {
597+
msg.append(" — ").append(bodyMessage);
598+
}
599+
msg.append(".\n\n");
600+
601+
switch (code) {
602+
case 401:
603+
msg.append("FIX: Add a GitHub Personal Access Token so the plugin can access the API.\n");
604+
if (getApiKey() == null) {
605+
msg.append(" • In build.gradle add: github { accessToken = \"ghp_YOUR_TOKEN\" }\n");
606+
msg.append(" • Or set the GITHUB_TOKEN environment variable.\n");
607+
} else {
608+
msg.append(" • Your token is set but was rejected. Check it is valid and not expired.\n");
609+
msg.append(" • Create or regenerate a PAT at: https://github.com/settings/tokens\n");
610+
}
611+
msg.append(" • For public repos no scope is needed; for private repos enable the 'repo' scope.");
612+
break;
613+
case 403:
614+
if (bodyMessage != null && bodyMessage.toLowerCase().contains("rate limit")) {
615+
msg.append("FIX: GitHub API rate limit exceeded (60/hr unauthenticated).\n");
616+
msg.append(" • Add a token to get 5,000 requests/hour: github { accessToken = \"ghp_YOUR_TOKEN\" } in build.gradle\n");
617+
msg.append(" • Or wait and retry later.");
618+
} else {
619+
msg.append("FIX: Request forbidden — token may lack permission.\n");
620+
msg.append(" • For private repositories, ensure your PAT has the 'repo' scope.\n");
621+
msg.append(" • Update token at: https://github.com/settings/tokens");
622+
}
623+
break;
624+
case 404:
625+
msg.append("FIX: Repository or release not found.\n");
626+
msg.append(" • Check that the owner, repo name, and release tag are correct in your githubImplementation dependency.\n");
627+
msg.append(" • If the repo is private, ensure your token has access to it.");
628+
break;
629+
default:
630+
msg.append("FIX: Check your network and GitHub status. Retry with --info for more details.");
631+
break;
632+
}
633+
return msg.toString();
634+
}
635+
636+
/**
637+
* Builds a user-friendly error message for a failed HTTP response when the body is not JSON
638+
* (e.g. asset download). Does not consume the response body.
639+
*/
640+
private String buildHttpErrorMessage(int code, String statusMessage, String context) {
641+
StringBuilder msg = new StringBuilder();
642+
msg.append("Download failed for ").append(context).append(". HTTP ").append(code).append(" ").append(statusMessage).append(".\n\n");
643+
switch (code) {
644+
case 401:
645+
msg.append("FIX: Add a token so the plugin can download the asset: github { accessToken = \"ghp_YOUR_TOKEN\" } in build.gradle, or set GITHUB_TOKEN.");
646+
break;
647+
case 403:
648+
msg.append("FIX: If rate limited, add a token (github { accessToken = \"...\" }). If forbidden, ensure your PAT has the 'repo' scope for this repository.");
649+
break;
650+
case 404:
651+
msg.append("FIX: Check that the release and asset exist at the given tag. For private repos, ensure your token has access.");
652+
break;
653+
default:
654+
break;
655+
}
656+
return msg.toString();
657+
}
658+
563659
/**
564660
* Downloads and caches a release asset JAR file from a GitHub repository.
565661
*
@@ -591,8 +687,12 @@ public File getAsset(String repoOwner, String repoName, String version) {
591687
logger.debug("Fetching release from: " + apiUrl);
592688

593689
try (Response response = makeGitHubApiRequest(apiUrl)) {
594-
if (!response.isSuccessful() || response.body() == null) {
595-
throw new RuntimeException("Failed to fetch release: " + response.code() + " " + response.message());
690+
if (!response.isSuccessful()) {
691+
String context = String.format("release %s/%s tag %s", repoOwner, repoName, version);
692+
throw new RuntimeException(buildGitHubApiErrorMessage(response, context));
693+
}
694+
if (response.body() == null) {
695+
throw new RuntimeException("GitHub API returned empty body for release " + repoOwner + "/" + repoName + " tag " + version + ".");
596696
}
597697

598698
JsonObject release = gson.fromJson(response.body().string(), JsonObject.class);
@@ -663,8 +763,12 @@ private void downloadAssetFromUrl(File destination, String downloadUrl, String r
663763

664764
try (Response response = httpClient.newCall(request).execute()) {
665765
logger.debug("HTTP response: " + response.code() + " " + response.message());
666-
if (!response.isSuccessful() || response.body() == null) {
667-
throw new IOException("Failed to download asset: " + response);
766+
if (!response.isSuccessful()) {
767+
String context = repoOwner + "/" + repoName;
768+
throw new IOException(buildHttpErrorMessage(response.code(), response.message(), context));
769+
}
770+
if (response.body() == null) {
771+
throw new IOException("Empty response body when downloading " + repoOwner + "/" + repoName + ".");
668772
}
669773
byte[] bytes = response.body().bytes();
670774
logger.debug("Download size: " + bytes.length + " bytes.");
@@ -712,8 +816,12 @@ public JsonObject getLatestRelease(String repoOwner, String repoName) {
712816
return null;
713817
}
714818

715-
if (!response.isSuccessful() || response.body() == null) {
716-
throw new RuntimeException("Failed to fetch latest release: " + response.code() + " " + response.message());
819+
if (!response.isSuccessful()) {
820+
String context = String.format("latest release for %s/%s", repoOwner, repoName);
821+
throw new RuntimeException(buildGitHubApiErrorMessage(response, context));
822+
}
823+
if (response.body() == null) {
824+
throw new RuntimeException("GitHub API returned empty body for latest release " + repoOwner + "/" + repoName + ".");
717825
}
718826

719827
JsonObject release = gson.fromJson(response.body().string(), JsonObject.class);

0 commit comments

Comments
 (0)