github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/extension/http.go (about)

     1  package extension
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"os"
    10  
    11  	"github.com/ungtb10d/cli/v2/api"
    12  	"github.com/ungtb10d/cli/v2/internal/ghinstance"
    13  	"github.com/ungtb10d/cli/v2/internal/ghrepo"
    14  )
    15  
    16  func repoExists(httpClient *http.Client, repo ghrepo.Interface) (bool, error) {
    17  	url := fmt.Sprintf("%srepos/%s/%s", ghinstance.RESTPrefix(repo.RepoHost()), repo.RepoOwner(), repo.RepoName())
    18  	req, err := http.NewRequest("GET", url, nil)
    19  	if err != nil {
    20  		return false, err
    21  	}
    22  
    23  	resp, err := httpClient.Do(req)
    24  	if err != nil {
    25  		return false, err
    26  	}
    27  	defer resp.Body.Close()
    28  
    29  	switch resp.StatusCode {
    30  	case 200:
    31  		return true, nil
    32  	case 404:
    33  		return false, nil
    34  	default:
    35  		return false, api.HandleHTTPError(resp)
    36  	}
    37  }
    38  
    39  func hasScript(httpClient *http.Client, repo ghrepo.Interface) (bool, error) {
    40  	path := fmt.Sprintf("repos/%s/%s/contents/%s",
    41  		repo.RepoOwner(), repo.RepoName(), repo.RepoName())
    42  	url := ghinstance.RESTPrefix(repo.RepoHost()) + path
    43  	req, err := http.NewRequest("GET", url, nil)
    44  	if err != nil {
    45  		return false, err
    46  	}
    47  
    48  	resp, err := httpClient.Do(req)
    49  	if err != nil {
    50  		return false, err
    51  	}
    52  	defer resp.Body.Close()
    53  
    54  	if resp.StatusCode == 404 {
    55  		return false, nil
    56  	}
    57  
    58  	if resp.StatusCode > 299 {
    59  		err = api.HandleHTTPError(resp)
    60  		return false, err
    61  	}
    62  
    63  	return true, nil
    64  }
    65  
    66  type releaseAsset struct {
    67  	Name   string
    68  	APIURL string `json:"url"`
    69  }
    70  
    71  type release struct {
    72  	Tag    string `json:"tag_name"`
    73  	Assets []releaseAsset
    74  }
    75  
    76  // downloadAsset downloads a single asset to the given file path.
    77  func downloadAsset(httpClient *http.Client, asset releaseAsset, destPath string) error {
    78  	req, err := http.NewRequest("GET", asset.APIURL, nil)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	req.Header.Set("Accept", "application/octet-stream")
    84  
    85  	resp, err := httpClient.Do(req)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	defer resp.Body.Close()
    90  
    91  	if resp.StatusCode > 299 {
    92  		return api.HandleHTTPError(resp)
    93  	}
    94  
    95  	f, err := os.OpenFile(destPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	defer f.Close()
   100  
   101  	_, err = io.Copy(f, resp.Body)
   102  	return err
   103  }
   104  
   105  var commitNotFoundErr = errors.New("commit not found")
   106  var releaseNotFoundErr = errors.New("release not found")
   107  var repositoryNotFoundErr = errors.New("repository not found")
   108  
   109  // fetchLatestRelease finds the latest published release for a repository.
   110  func fetchLatestRelease(httpClient *http.Client, baseRepo ghrepo.Interface) (*release, error) {
   111  	path := fmt.Sprintf("repos/%s/%s/releases/latest", baseRepo.RepoOwner(), baseRepo.RepoName())
   112  	url := ghinstance.RESTPrefix(baseRepo.RepoHost()) + path
   113  	req, err := http.NewRequest("GET", url, nil)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	resp, err := httpClient.Do(req)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	defer resp.Body.Close()
   123  
   124  	if resp.StatusCode == 404 {
   125  		return nil, releaseNotFoundErr
   126  	}
   127  	if resp.StatusCode > 299 {
   128  		return nil, api.HandleHTTPError(resp)
   129  	}
   130  
   131  	b, err := io.ReadAll(resp.Body)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	var r release
   137  	err = json.Unmarshal(b, &r)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	return &r, nil
   143  }
   144  
   145  // fetchReleaseFromTag finds release by tag name for a repository
   146  func fetchReleaseFromTag(httpClient *http.Client, baseRepo ghrepo.Interface, tagName string) (*release, error) {
   147  	fullRepoName := fmt.Sprintf("%s/%s", baseRepo.RepoOwner(), baseRepo.RepoName())
   148  	path := fmt.Sprintf("repos/%s/releases/tags/%s", fullRepoName, tagName)
   149  	url := ghinstance.RESTPrefix(baseRepo.RepoHost()) + path
   150  	req, err := http.NewRequest("GET", url, nil)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	resp, err := httpClient.Do(req)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	defer resp.Body.Close()
   161  	if resp.StatusCode == 404 {
   162  		return nil, releaseNotFoundErr
   163  	}
   164  	if resp.StatusCode > 299 {
   165  		return nil, api.HandleHTTPError(resp)
   166  	}
   167  
   168  	b, err := io.ReadAll(resp.Body)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	var r release
   174  	err = json.Unmarshal(b, &r)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	return &r, nil
   180  }
   181  
   182  // fetchCommitSHA finds full commit SHA from a target ref in a repo
   183  func fetchCommitSHA(httpClient *http.Client, baseRepo ghrepo.Interface, targetRef string) (string, error) {
   184  	path := fmt.Sprintf("repos/%s/%s/commits/%s", baseRepo.RepoOwner(), baseRepo.RepoName(), targetRef)
   185  	url := ghinstance.RESTPrefix(baseRepo.RepoHost()) + path
   186  	req, err := http.NewRequest("GET", url, nil)
   187  	if err != nil {
   188  		return "", err
   189  	}
   190  
   191  	req.Header.Set("Accept", "application/vnd.github.v3.sha")
   192  	resp, err := httpClient.Do(req)
   193  	if err != nil {
   194  		return "", err
   195  	}
   196  
   197  	defer resp.Body.Close()
   198  	if resp.StatusCode == 422 {
   199  		return "", commitNotFoundErr
   200  	}
   201  	if resp.StatusCode > 299 {
   202  		return "", api.HandleHTTPError(resp)
   203  	}
   204  
   205  	body, err := io.ReadAll(resp.Body)
   206  	if err != nil {
   207  		return "", err
   208  	}
   209  
   210  	return string(body), nil
   211  }