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

Implement git refs #79

Merged
merged 11 commits into from
Jan 13, 2021
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ authentication. Fetch makes it possible to handle all of these cases with a one-

#### Features

- Download from a specific git tag, branch, or commit SHA.
- Download from any git reference, such as a specific git tag, branch, or commit SHA.
- Download a single file, a subset of files, or all files from the repo.
- Download one or more binary assets from a specific release that match a regular expression.
- Verify the SHA256 or SHA512 checksum of a binary asset.
Expand Down Expand Up @@ -74,6 +74,7 @@ fetch [OPTIONS] <local-download-path>
The supported options are:

- `--repo` (**Required**): The fully qualified URL of the GitHub repo to download from (e.g. https://github.com/foo/bar).
- `--ref` (**Optional**): The git reference to download. If specified, will override `--commit`, `--branch`, and `--tag`.
- `--tag` (**Optional**): The git tag to download. Can be a specific tag or a [Tag Constraint
Expression](#tag-constraint-expressions).
- `--branch` (**Optional**): The git branch from which to download; the latest commit in the branch will be used. If
Expand Down Expand Up @@ -137,7 +138,7 @@ fetch --repo="https://github.com/foo/bar" --tag="~>0.1.5" --source-path="/module
Download all files in `/modules/foo` from a GitHub release where the tag is exactly `0.1.5`, and save them to `/tmp`:

```
fetch --repo="https://github.com/foo/bar" --tag="0.1.5" --source-path="/modules/foo" /tmp
fetch --repo="https://github.com/foo/bar" --ref="0.1.5" --source-path="/modules/foo" /tmp
```

#### Usage Example 3
Expand All @@ -147,39 +148,39 @@ Download all files from a private GitHub repo using the GitHUb oAuth Token `123`
```
GITHUB_OAUTH_TOKEN=123

fetch --repo="https://github.com/foo/bar" --tag="0.1.5" /tmp
fetch --repo="https://github.com/foo/bar" --ref="0.1.5" /tmp
```

#### Usage Example 4

Download all files from the latest commit on the `sample-branch` branch, and save them to `/tmp`:

```
fetch --repo="https://github.com/foo/bar" --branch="sample-branch" /tmp/josh1
fetch --repo="https://github.com/foo/bar" --ref="sample-branch" /tmp/josh1
```

#### Usage Example 5

Download all files from the git commit `f32a08313e30f116a1f5617b8b68c11f1c1dbb61`, and save them to `/tmp`:

```
fetch --repo="https://github.com/foo/bar" --commit="f32a08313e30f116a1f5617b8b68c11f1c1dbb61" /tmp/josh1
fetch --repo="https://github.com/foo/bar" --ref="f32a08313e30f116a1f5617b8b68c11f1c1dbb61" /tmp
```

#### Usage Example 6

Download the release asset `foo.exe` from a GitHub release where the tag is exactly `0.1.5`, and save it to `/tmp`:

```
fetch --repo="https://github.com/foo/bar" --tag="0.1.5" --release-asset="foo.exe" /tmp
fetch --repo="https://github.com/foo/bar" --ref="0.1.5" --release-asset="foo.exe" /tmp
```

#### Usage Example 7

Download the release asset `foo.exe` from a GitHub release hosted on a GitHub Enterprise instance running at `ghe.mycompany.com` where the tag is exactly `0.1.5`, and save it to `/tmp`:

```
fetch --repo="https://ghe.mycompany.com/foo/bar" --tag="0.1.5" --release-asset="foo.exe" /tmp
fetch --repo="https://ghe.mycompany.com/foo/bar" --ref="0.1.5" --release-asset="foo.exe" /tmp
```

## License
Expand All @@ -190,4 +191,4 @@ This code is released under the MIT License. See [LICENSE.txt](/LICENSE.txt).

- Introduce code verification using something like GPG signatures or published checksums
- Explicitly test for exotic repo and org names
- Apply stricter parsing for repo-filter command-line arg
- Apply stricter parsing for repo-filter command-line arg
4 changes: 3 additions & 1 deletion file.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ func MakeGitHubZipFileRequest(gitHubCommit GitHubCommit, gitHubToken string, ins

// This represents either a commit, branch, or git tag
var gitRef string
if gitHubCommit.CommitSha != "" {
if gitHubCommit.GitRef != "" {
gitRef = gitHubCommit.GitRef
} else if gitHubCommit.CommitSha != "" {
gitRef = gitHubCommit.CommitSha
} else if gitHubCommit.BranchName != "" {
gitRef = gitHubCommit.BranchName
Expand Down
228 changes: 146 additions & 82 deletions file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,44 +44,56 @@ func TestDownloadGitTagZipFile(t *testing.T) {
}

for _, tc := range cases {
gitHubCommit := GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
gitHubCommits := []GitHubCommit{
// Test as a GitTag
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
GitTag: tc.gitTag,
},
// Test as a GitRef
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
GitRef: tc.gitTag,
},
GitTag: tc.gitTag,
}
for _, gitHubCommit := range gitHubCommits {
zipFilePath, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)

zipFilePath, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)

defer os.RemoveAll(zipFilePath)
defer os.RemoveAll(zipFilePath)

// We don't have a running instance of GitHub Enterprise against which to validate tests as we do for GitHub public,
// so this test will only validate that fetch attempted to download from the expected URL. The download itself
// will fail.
// We don't have a running instance of GitHub Enterprise against which to validate tests as we do for GitHub public,
// so this test will only validate that fetch attempted to download from the expected URL. The download itself
// will fail.

githubEnterpriseDownloadUrl := fmt.Sprintf("https://%s/repos/%s/%s/zipball/%s", tc.instance.ApiUrl, tc.repoOwner, tc.repoName, tc.gitTag)
githubEnterpriseDownloadUrl := fmt.Sprintf("https://%s/repos/%s/%s/zipball/%s", tc.instance.ApiUrl, tc.repoOwner, tc.repoName, tc.gitTag)

// TODO: The awkwardness of this test makes it clear that a better structure for this program would be to refactor
// the downloadGithubZipFile() function to a function called downloadGithubFile() that would accept a URL as a
// param. We could then test explicitly that the URL is as expected, which would make GitHub Enterprise test cases
// simpler to handle.
// TODO: The awkwardness of this test makes it clear that a better structure for this program would be to refactor
// the downloadGithubZipFile() function to a function called downloadGithubFile() that would accept a URL as a
// param. We could then test explicitly that the URL is as expected, which would make GitHub Enterprise test cases
// simpler to handle.

if err != nil && strings.Contains(err.Error(), "no such host") {
if strings.Contains(err.Error(), githubEnterpriseDownloadUrl) {
t.Logf("Found expected download URL %s. Download itself failed as expected because no GitHub Enterprise instance exists at the given URL.", githubEnterpriseDownloadUrl)
return
} else {
t.Fatalf("Attempted to download from URL other than the expected download URL of %s. Full error: %s", githubEnterpriseDownloadUrl, err.Error())
if err != nil && strings.Contains(err.Error(), "no such host") {
if strings.Contains(err.Error(), githubEnterpriseDownloadUrl) {
t.Logf("Found expected download URL %s. Download itself failed as expected because no GitHub Enterprise instance exists at the given URL.", githubEnterpriseDownloadUrl)
return
} else {
t.Fatalf("Attempted to download from URL other than the expected download URL of %s. Full error: %s", githubEnterpriseDownloadUrl, err.Error())
}
}
}

if err != nil {
t.Fatalf("Failed to download file: %s", err)
}
if err != nil {
t.Fatalf("Failed to download file: %s", err)
}

if _, err := os.Stat(zipFilePath); os.IsNotExist(err) {
t.Fatalf("Downloaded file doesn't exist at the expected path of %s", zipFilePath)
if _, err := os.Stat(zipFilePath); os.IsNotExist(err) {
t.Fatalf("Downloaded file doesn't exist at the expected path of %s", zipFilePath)
}
}
}
}
Expand All @@ -106,22 +118,32 @@ func TestDownloadGitBranchZipFile(t *testing.T) {
}

for _, tc := range cases {
gitHubCommit := GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
gitHubCommits := []GitHubCommit{
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
BranchName: tc.branchName,
},
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
GitRef: tc.branchName,
},
BranchName: tc.branchName,
}

zipFilePath, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)
defer os.RemoveAll(zipFilePath)
if err != nil {
t.Fatalf("Failed to download file: %s", err)
}
for _, gitHubCommit := range gitHubCommits {
zipFilePath, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)
defer os.RemoveAll(zipFilePath)
if err != nil {
t.Fatalf("Failed to download file: %s", err)
}

if _, err := os.Stat(zipFilePath); os.IsNotExist(err) {
t.Fatalf("Downloaded file doesn't exist at the expected path of %s", zipFilePath)
if _, err := os.Stat(zipFilePath); os.IsNotExist(err) {
t.Fatalf("Downloaded file doesn't exist at the expected path of %s", zipFilePath)
}
}
}
}
Expand All @@ -145,18 +167,28 @@ func TestDownloadBadGitBranchZipFile(t *testing.T) {
}

for _, tc := range cases {
gitHubCommit := GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
gitHubCommits := []GitHubCommit{
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
BranchName: tc.branchName,
},
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
GitRef: tc.branchName,
},
BranchName: tc.branchName,
}

zipFilePath, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)
defer os.RemoveAll(zipFilePath)
if err == nil {
t.Fatalf("Expected that attempt to download repo %s/%s for branch \"%s\" would fail, but received no error.", tc.repoOwner, tc.repoName, tc.branchName)
for _, gitHubCommit := range gitHubCommits {
zipFilePath, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)
defer os.RemoveAll(zipFilePath)
if err == nil {
t.Fatalf("Expected that attempt to download repo %s/%s for branch \"%s\" would fail, but received no error.", tc.repoOwner, tc.repoName, tc.branchName)
}
}
}
}
Expand All @@ -183,22 +215,32 @@ func TestDownloadGitCommitFile(t *testing.T) {
}

for _, tc := range cases {
gitHubCommit := GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
GitHubCommits := []GitHubCommit{
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
CommitSha: tc.commitSha,
},
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
GitRef: tc.commitSha,
},
CommitSha: tc.commitSha,
}

zipFilePath, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)
defer os.RemoveAll(zipFilePath)
if err != nil {
t.Fatalf("Failed to download file: %s", err)
}
for _, gitHubCommit := range GitHubCommits {
zipFilePath, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)
defer os.RemoveAll(zipFilePath)
if err != nil {
t.Fatalf("Failed to download file: %s", err)
}

if _, err := os.Stat(zipFilePath); os.IsNotExist(err) {
t.Fatalf("Downloaded file doesn't exist at the expected path of %s", zipFilePath)
if _, err := os.Stat(zipFilePath); os.IsNotExist(err) {
t.Fatalf("Downloaded file doesn't exist at the expected path of %s", zipFilePath)
}
}
}
}
Expand Down Expand Up @@ -227,18 +269,29 @@ func TestDownloadBadGitCommitFile(t *testing.T) {
}

for _, tc := range cases {
gitHubCommit := GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,

gitHubCommits := []GitHubCommit{
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
CommitSha: tc.commitSha,
},
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
GitRef: tc.commitSha,
},
CommitSha: tc.commitSha,
}

zipFilePath, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)
defer os.RemoveAll(zipFilePath)
if err == nil {
t.Fatalf("Expected that attempt to download repo %s/%s at commmit sha \"%s\" would fail, but received no error.", tc.repoOwner, tc.repoName, tc.commitSha)
for _, gitHubCommit := range gitHubCommits {
zipFilePath, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)
defer os.RemoveAll(zipFilePath)
if err == nil {
t.Fatalf("Expected that attempt to download repo %s/%s at commmit sha \"%s\" would fail, but received no error.", tc.repoOwner, tc.repoName, tc.commitSha)
}
}
}
}
Expand All @@ -262,17 +315,28 @@ func TestDownloadZipFileWithBadRepoValues(t *testing.T) {
}

for _, tc := range cases {
gitHubCommit := GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
gitHubCommits := []GitHubCommit{
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
GitTag: tc.gitTag,
},
GitHubCommit{
Repo: GitHubRepo{
Owner: tc.repoOwner,
Name: tc.repoName,
},
GitRef: tc.gitTag,
},
GitTag: tc.gitTag,
}
for _, gitHubCommit := range gitHubCommits {

_, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)
if err == nil && err.errorCode != 500 {
t.Fatalf("Expected error for bad repo values: %s/%s:%s", tc.repoOwner, tc.repoName, tc.gitTag)
_, err := downloadGithubZipFile(gitHubCommit, tc.githubToken, tc.instance)
if err == nil && err.errorCode != 500 {
t.Fatalf("Expected error for bad repo values: %s/%s:%s", tc.repoOwner, tc.repoName, tc.gitTag)
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions github.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type GitHubInstance struct {
// - Example: BranchName alone is specified; use BranchName
type GitHubCommit struct {
Repo GitHubRepo // The GitHub repo where this release lives
GitRef string // The git reference
GitTag string // The specific git tag for this release
BranchName string // If specified, indicates that this commit should be the latest commit on the given branch
CommitSha string // If specified, indicates that this commit should be exactly this Git Commit SHA.
Expand Down
Loading