I'm writing an install script that's supposed to download a private repository from GitHub:
if Pat <> '' then
begin
Pat := Pat + '@'
end;
Url := Format('https://%sgithub.com/<MY_REPO>/%s/archive/main.zip', [Pat, RepoName]);
try
DownloadTemporaryFile(Url, 'main.zip', '', @OnDownloadProgress);
except
MsgBox('Failed to download the files from the GitHub repository.', mbError, MB_OK);
Abort();
end;
For a public repository, I can set Pat
to an empty string and this works without issue. However, for private repositories, I get a 404 response code. I've tried using a personal access token, and I've tried doing username:token
as well. Furthermore, I have verified that the organization name and repository name match and, when trying the URL from a browser, I can download the file. I also have MFA enabled on my account. How can I make this work?
This script is part of an installer where the user enters their PAT, which allows them to download and install the code in the repository. Only people with a valid PAT can access the code.
For PAT, see Clone A Private Repository (Github).
After searching through other Stack Overflow questions, I found Get www/web content using HTTP request in Inno Setup, which showed me the hint I needed. This code relies on the WinHttpRequest
object, to make the GET request with the required authentication, which is my GitHub Personal Access Token (PAT). This code works.
const
WinHttpRequestLib = 'WinHttp.WinHttpRequest.5.1';
function ByteArrayToString(const ByteArray: array of Byte): AnsiString;
var
I: Integer;
begin
SetLength(Result, GetArrayLength(ByteArray));
for I := 1 to GetArrayLength(ByteArray) do
Result[I] := Chr(ByteArray[I - 1]);
end;
procedure GetGitHubArchive(Url, AccessToken: string);
var
Request: Variant;
ResponseBody: string;
begin
// First, load the OLE object associated with the WinHTTP library
Request := CreateOleObject(WinHttpRequestLib);
// Next, open a new GET request to the URL;
// if we have a non-empty access token then add the
// associated authorization header here
Request.Open('GET', Url, false);
if AccessToken <> '' then
begin
Request.SetRequestHeader('Authorization', 'Bearer ' + AccessToken);
end;
// Finally, send the request; if we received a non-200 response code
// then show the error.
// Otherwise, extract the response body and write it to a file
Request.Send;
if Request.Status <> 200 then
begin
MsgBox('Failed to download file: ' + Request.StatusText, mbError, MB_OK);
Abort();
end
else
begin
ResponseBody := ByteArrayToString(Request.ResponseBody);
SaveStringToFile(ExpandConstant('{tmp}\main.zip'), ResponseBody, False);
end;
end;