Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(create-remix): Allow private GitHub repos to use the latest release url for tarballs #4329

Merged
merged 7 commits into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/tame-hotels-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"remix": patch
"@remix-run/dev": patch
---

Allow private GitHub repos to use the latest release url for tarballs when using `create-remix`
1 change: 1 addition & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@
- jodygeraldo
- joelazar
- johannesbraeunig
- johnmberger
- johnpolacek
- johnson444
- joms
Expand Down
1 change: 1 addition & 0 deletions docs/other-api/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ remix create ./my-app --template :username/:repo
remix create ./my-app --template https://github.com/:username/:repo
remix create ./my-app --template https://github.com/:username/:repo/tree/:branch
remix create ./my-app --template https://github.com/:username/:repo/archive/refs/tags/:tag.tar.gz
remix create ./my-app --template https://github.com/:username/:repo/releases/latest/download/:tag.tar.gz
remix create ./my-app --template https://example.com/remix-template.tar.gz
```

Expand Down
2 changes: 1 addition & 1 deletion packages/remix-dev/__tests__/create-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ describe("the create command", () => {
"create",
projectDir,
"--template",
"https://example.com/remix-stack.tar.gz",
"https://github.com/private-org/private-repo/releases/download/v0.0.1/stack.tar.gz",
johnmberger marked this conversation as resolved.
Show resolved Hide resolved
"--no-install",
"--typescript",
"--token",
Expand Down
68 changes: 55 additions & 13 deletions packages/remix-dev/cli/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,20 +307,31 @@ async function downloadAndExtractTarball(
// asset id
let info = getGithubReleaseAssetInfo(url);
headers.Accept = "application/vnd.github.v3+json";
let response = await fetch(
`https://api.github.com/repos/${info.owner}/${info.name}/releases/tags/${info.tag}`,
{ agent: agent("https://api.github.com"), headers }
);

let releaseUrl =
info.tag === "latest"
? `https://api.github.com/repos/${info.owner}/${info.name}/releases/latest`
: `https://api.github.com/repos/${info.owner}/${info.name}/releases/tags/${info.tag}`;

let response = await fetch(releaseUrl, {
agent: agent("https://api.github.com"),
headers,
});

if (response.status !== 200) {
throw Error(
"🚨 There was a problem fetching the file from GitHub. The request " +
`responded with a ${response.status} status. Please try again later.`
);
}
let body = await response.json();
let assetId: number | undefined = body?.assets?.find(
(a: any) => a?.browser_download_url === url
)?.id;
// If the release is "latest", the url won't match the download url, so we grab the id from the response
let assetId: number | undefined =
info.tag === "latest"
? body?.assets?.find((a: any) =>
a?.browser_download_url?.includes(info.asset)
)?.id
: body?.assets?.find((a: any) => a?.browser_download_url === url)?.id;
if (!assetId) {
throw Error(
"🚨 There was a problem fetching the file from GitHub. No asset was " +
Expand Down Expand Up @@ -434,8 +445,16 @@ function getGithubUrl(info: Omit<RepoInfo, "url">) {
}

function isGithubReleaseAssetUrl(url: string) {
/**
* Accounts for the following formats:
* https://github.com/owner/repository/releases/download/v0.0.1/stack.tar.gz
* ~or~
* https://github.com/owner/repository/releases/latest/download/stack.tar.gz
*/
return (
url.startsWith("https://github.com") && url.includes("/releases/download/")
url.startsWith("https://github.com") &&
(url.includes("/releases/download/") ||
url.includes("/releases/latest/download/"))
);
}
interface ReleaseAssetInfo {
Expand All @@ -446,18 +465,30 @@ interface ReleaseAssetInfo {
tag: string;
}
function getGithubReleaseAssetInfo(browserUrl: string): ReleaseAssetInfo {
// for example, https://github.com/owner/repository/releases/download/v0.0.1/stack.tar.gz
/**
* https://github.com/owner/repository/releases/download/v0.0.1/stack.tar.gz
* ~or~
* https://github.com/owner/repository/releases/latest/download/stack.tar.gz
*/

let url = new URL(browserUrl);
let [, owner, name, , , tag, asset] = url.pathname.split("/") as [
let [, owner, name, , downloadOrLatest, tag, asset] = url.pathname.split(
"/"
) as [
_: string,
Owner: string,
Name: string,
Releases: string,
Download: string,
DownloadOrLatest: string,
Tag: string,
AssetFilename: string
];

if (downloadOrLatest === "latest" && tag === "download") {
// handle the Github URL quirk for latest releases
tag = "latest";
}

return {
browserUrl,
owner,
Expand Down Expand Up @@ -583,7 +614,10 @@ export async function validateTemplate(
let headers: Record<string, string> = {};
if (isGithubReleaseAssetUrl(input)) {
let info = getGithubReleaseAssetInfo(input);
apiUrl = `https://api.github.com/repos/${info.owner}/${info.name}/releases/tags/${info.tag}`;
apiUrl =
info.tag === "latest"
? `https://api.github.com/repos/${info.owner}/${info.name}/releases/latest`
: `https://api.github.com/repos/${info.owner}/${info.name}/releases/tags/${info.tag}`;
headers = {
Authorization: `token ${options?.githubToken}`,
Accept: "application/vnd.github.v3+json",
Expand All @@ -609,9 +643,17 @@ export async function validateTemplate(
switch (response.status) {
case 200:
if (isGithubReleaseAssetUrl(input)) {
let info = getGithubReleaseAssetInfo(input);
let body = await response.json();
if (
!body?.assets?.some((a: any) => a?.browser_download_url === input)
// if a tag is specified, make sure it exists.
!body?.assets?.some(
(a: any) => a?.browser_download_url === input
) &&
// if the latest is specified, make sure there is an asset
!body?.assets?.some((a: any) =>
a?.browser_download_url.includes(info.asset)
)
) {
throw Error(
"🚨 The template file could not be verified. Please double check " +
Expand Down