-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathuses.go
More file actions
146 lines (118 loc) · 3.07 KB
/
uses.go
File metadata and controls
146 lines (118 loc) · 3.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package githubactions
import (
"archive/tar"
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
xtar "github.com/frantjc/x/archive/tar"
)
type Uses struct {
Path string
Version string
}
// IsLocal returns if the Uses refers to an Action on the local filesystem.
func (u *Uses) IsLocal() bool {
// A GitHub org cannot start with ".", so this correctly identifies if the path is "." or "./path/to/action".
return strings.HasPrefix(u.Path, ".") || filepath.IsAbs(u.Path) || len(strings.Split(u.Path, "/")) < 2
}
// IsRemote returns if the Uses refers to an Action in a GitHub repository.
func (u *Uses) IsRemote() bool {
return !u.IsLocal()
}
func (u *Uses) String() string {
uses := u.Path
if v := u.Version; v != "" {
uses = fmt.Sprintf("%s@%s", uses, v)
}
return uses
}
func (u *Uses) GetOwner() string {
if u.IsRemote() {
return strings.Split(u.Path, "/")[0]
}
return ""
}
func (u *Uses) GetRepository() string {
if u.IsRemote() {
return strings.Split(u.Path, "/")[1]
}
return ""
}
func (u *Uses) GetActionPath() string {
if u.IsRemote() {
elements := strings.Split(u.Path, "/")
if len(elements) > 2 {
return filepath.Join(elements[2:]...)
}
}
return ""
}
func (u *Uses) GoString() string {
return "&Uses{" + u.String() + "}"
}
// Parse parses a reference to a GitHub Action that would appear as the value
// of `uses` in a GitHub Actions Workflow Step, such as:
//
// steps:
// - uses: frantjc/actions/setup-tool@v1
// - uses: ./
// - uses: ./my/local/action
//
// Also supports the special case ".".
func Parse(uses string) (*Uses, error) {
r := &Uses{}
switch {
case strings.HasPrefix(uses, "/"):
r.Path = filepath.Clean(uses)
case strings.HasPrefix(uses, "."):
r.Path = filepath.Clean(uses)
if r.Path != "." {
r.Path = "./" + r.Path
}
default:
before, after, ok := strings.Cut(uses, "@")
if !ok {
return nil, fmt.Errorf("parse uses: not a path or a versioned reference: %s", uses)
}
r.Path = filepath.Clean(before)
r.Version = after
}
return r, nil
}
func (u *Uses) MarshalJSON() ([]byte, error) {
return []byte("\"" + u.String() + "\""), nil
}
func GetUsesMetadata(ctx context.Context, uses *Uses, dir string) (*Metadata, error) {
if r, err := OpenDirectoryMetadata(filepath.Join(dir, uses.GetActionPath())); err == nil {
return NewMetadataFromReader(r)
}
if uses.IsRemote() {
metadata, rc, err := DownloadAction(ctx, uses)
if err != nil {
return nil, err
}
defer rc.Close()
if err = xtar.Extract(tar.NewReader(rc), dir); err != nil {
return nil, err
}
return metadata, nil
}
return nil, fmt.Errorf("open local action: %s", dir)
}
func OpenUsesMetadata(uses *Uses) (io.Reader, error) {
if uses.IsRemote() {
return nil, fmt.Errorf("open remote action: %s", uses.Path)
}
return OpenDirectoryMetadata(filepath.Clean(uses.Path))
}
func OpenDirectoryMetadata(dir string) (_ io.Reader, err error) {
for _, filename := range ActionYAMLFilenames {
if f, err := os.Open(filepath.Join(dir, filename)); err == nil {
return f, nil
}
}
return nil, fmt.Errorf("open action in directory: %s", dir)
}