1// Copyright 2020 The go-github AUTHORS. All rights reserved. 2// 3// Use of this source code is governed by a BSD-style 4// license that can be found in the LICENSE file. 5 6package github 7 8import ( 9 "context" 10 "fmt" 11 "net/http" 12 "net/url" 13) 14 15// Artifact reprents a GitHub artifact. Artifacts allow sharing 16// data between jobs in a workflow and provide storage for data 17// once a workflow is complete. 18// 19// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/artifacts/ 20type Artifact struct { 21 ID *int64 `json:"id,omitempty"` 22 NodeID *string `json:"node_id,omitempty"` 23 Name *string `json:"name,omitempty"` 24 SizeInBytes *int64 `json:"size_in_bytes,omitempty"` 25 ArchiveDownloadURL *string `json:"archive_download_url,omitempty"` 26 Expired *bool `json:"expired,omitempty"` 27 CreatedAt *Timestamp `json:"created_at,omitempty"` 28 ExpiresAt *Timestamp `json:"expires_at,omitempty"` 29} 30 31// ArtifactList represents a list of GitHub artifacts. 32// 33// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/artifacts/ 34type ArtifactList struct { 35 TotalCount *int64 `json:"total_count,omitempty"` 36 Artifacts []*Artifact `json:"artifacts,omitempty"` 37} 38 39// ListArtifacts lists all artifacts that belong to a repository. 40// 41// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#list-artifacts-for-a-repository 42func (s *ActionsService) ListArtifacts(ctx context.Context, owner, repo string, opts *ListOptions) (*ArtifactList, *Response, error) { 43 u := fmt.Sprintf("repos/%v/%v/actions/artifacts", owner, repo) 44 u, err := addOptions(u, opts) 45 if err != nil { 46 return nil, nil, err 47 } 48 49 req, err := s.client.NewRequest("GET", u, nil) 50 if err != nil { 51 return nil, nil, err 52 } 53 54 artifactList := new(ArtifactList) 55 resp, err := s.client.Do(ctx, req, artifactList) 56 if err != nil { 57 return nil, resp, err 58 } 59 60 return artifactList, resp, nil 61} 62 63// ListWorkflowRunArtifacts lists all artifacts that belong to a workflow run. 64// 65// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#list-workflow-run-artifacts 66func (s *ActionsService) ListWorkflowRunArtifacts(ctx context.Context, owner, repo string, runID int64, opts *ListOptions) (*ArtifactList, *Response, error) { 67 u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/artifacts", owner, repo, runID) 68 u, err := addOptions(u, opts) 69 if err != nil { 70 return nil, nil, err 71 } 72 73 req, err := s.client.NewRequest("GET", u, nil) 74 if err != nil { 75 return nil, nil, err 76 } 77 78 artifactList := new(ArtifactList) 79 resp, err := s.client.Do(ctx, req, artifactList) 80 if err != nil { 81 return nil, resp, err 82 } 83 84 return artifactList, resp, nil 85} 86 87// GetArtifact gets a specific artifact for a workflow run. 88// 89// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#get-an-artifact 90func (s *ActionsService) GetArtifact(ctx context.Context, owner, repo string, artifactID int64) (*Artifact, *Response, error) { 91 u := fmt.Sprintf("repos/%v/%v/actions/artifacts/%v", owner, repo, artifactID) 92 93 req, err := s.client.NewRequest("GET", u, nil) 94 if err != nil { 95 return nil, nil, err 96 } 97 98 artifact := new(Artifact) 99 resp, err := s.client.Do(ctx, req, artifact) 100 if err != nil { 101 return nil, resp, err 102 } 103 104 return artifact, resp, nil 105} 106 107// DownloadArtifact gets a redirect URL to download an archive for a repository. 108// 109// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/artifacts/#download-an-artifact 110func (s *ActionsService) DownloadArtifact(ctx context.Context, owner, repo string, artifactID int64, followRedirects bool) (*url.URL, *Response, error) { 111 u := fmt.Sprintf("repos/%v/%v/actions/artifacts/%v/zip", owner, repo, artifactID) 112 113 resp, err := s.getDownloadArtifactFromURL(ctx, u, followRedirects) 114 if err != nil { 115 return nil, nil, err 116 } 117 118 if resp.StatusCode != http.StatusFound { 119 return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status) 120 } 121 parsedURL, err := url.Parse(resp.Header.Get("Location")) 122 return parsedURL, newResponse(resp), nil 123} 124 125func (s *ActionsService) getDownloadArtifactFromURL(ctx context.Context, u string, followRedirects bool) (*http.Response, error) { 126 req, err := s.client.NewRequest("GET", u, nil) 127 if err != nil { 128 return nil, err 129 } 130 131 var resp *http.Response 132 // Use http.DefaultTransport if no custom Transport is configured 133 req = withContext(ctx, req) 134 if s.client.client.Transport == nil { 135 resp, err = http.DefaultTransport.RoundTrip(req) 136 } else { 137 resp, err = s.client.client.Transport.RoundTrip(req) 138 } 139 if err != nil { 140 return nil, err 141 } 142 resp.Body.Close() 143 144 // If redirect response is returned, follow it 145 if followRedirects && resp.StatusCode == http.StatusMovedPermanently { 146 u = resp.Header.Get("Location") 147 resp, err = s.getDownloadArtifactFromURL(ctx, u, false) 148 } 149 return resp, err 150} 151 152// DeleteArtifact deletes a workflow run artifact. 153// 154// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#delete-an-artifact 155func (s *ActionsService) DeleteArtifact(ctx context.Context, owner, repo string, artifactID int64) (*Response, error) { 156 u := fmt.Sprintf("repos/%v/%v/actions/artifacts/%v", owner, repo, artifactID) 157 158 req, err := s.client.NewRequest("DELETE", u, nil) 159 if err != nil { 160 return nil, err 161 } 162 163 return s.client.Do(ctx, req, nil) 164} 165