Skip to content

Commit b634fba

Browse files
feat: contextdata overlay api and more gh/gt instance flags (#105)
* add TestGetGitHubContextOverlay
1 parent 6440a41 commit b634fba

8 files changed

Lines changed: 180 additions & 15 deletions

File tree

cmd/input.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ type Input struct {
3838
workflowRecurse bool
3939
useGitIgnore bool
4040
githubInstance string
41+
gitHubServerURL string
42+
gitHubAPIServerURL string
43+
gitHubGraphQlAPIServerURL string
4144
containerCapAdd []string
4245
containerCapDrop []string
4346
autoRemove bool

cmd/root.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ func createRootCommand(ctx context.Context, input *Input, version string) *cobra
113113
rootCmd.PersistentFlags().StringVarP(&input.containerDaemonSocket, "container-daemon-socket", "", "", "URI to Docker Engine socket (e.g.: unix://~/.docker/run/docker.sock or - to disable bind mounting the socket)")
114114
rootCmd.PersistentFlags().StringVarP(&input.containerOptions, "container-options", "", "", "Custom docker container options for the job container without an options property in the job definition")
115115
rootCmd.PersistentFlags().StringVarP(&input.githubInstance, "github-instance", "", "github.com", "GitHub instance to use. Only use this when using GitHub Enterprise Server.")
116+
rootCmd.PersistentFlags().StringVarP(&input.gitHubServerURL, "github-server-url", "", "", "Fully qualified URL to the GitHub instance to use with http/https protocol. Only use this when using GitHub Enterprise Server or Gitea.")
117+
rootCmd.PersistentFlags().StringVarP(&input.gitHubAPIServerURL, "github-api-server-url", "", "", "Fully qualified URL to the GitHub instance api url to use with http/https protocol. Only use this when using GitHub Enterprise Server or Gitea.")
118+
rootCmd.PersistentFlags().StringVarP(&input.gitHubGraphQlAPIServerURL, "github-graph-ql-api-server-url", "", "", "Fully qualified URL to the GitHub instance graphql api to use with http/https protocol. Only use this when using GitHub Enterprise Server or Gitea.")
116119
rootCmd.PersistentFlags().StringVarP(&input.artifactServerPath, "artifact-server-path", "", "", "Defines the path where the artifact server stores uploads and retrieves downloads from. If not specified the artifact server will not start.")
117120
rootCmd.PersistentFlags().StringVarP(&input.artifactServerAddr, "artifact-server-addr", "", common.GetOutboundIP().String(), "Defines the address to which the artifact server binds.")
118121
rootCmd.PersistentFlags().StringVarP(&input.artifactServerPort, "artifact-server-port", "", "34567", "Defines the port where the artifact server listens.")
@@ -630,6 +633,9 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
630633
ContainerOptions: input.containerOptions,
631634
UseGitIgnore: input.useGitIgnore,
632635
GitHubInstance: input.githubInstance,
636+
GitHubServerURL: input.gitHubServerURL,
637+
GitHubAPIServerURL: input.gitHubAPIServerURL,
638+
GitHubGraphQlAPIServerURL: input.gitHubGraphQlAPIServerURL,
633639
ContainerCapAdd: input.containerCapAdd,
634640
ContainerCapDrop: input.containerCapDrop,
635641
AutoRemove: input.autoRemove,

pkg/runner/action_composite.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ func newCompositeRunContext(ctx context.Context, parent *RunContext, step action
7676
EventJSON: parent.EventJSON,
7777
nodeToolFullPath: parent.nodeToolFullPath,
7878
}
79+
if parent.ContextData != nil {
80+
compositerc.ContextData = map[string]interface{}{}
81+
for k, v := range parent.ContextData {
82+
if !strings.EqualFold("inputs", k) {
83+
compositerc.ContextData[k] = v
84+
}
85+
}
86+
}
7987
compositerc.ExprEval = compositerc.NewExpressionEvaluator(ctx)
8088

8189
return compositerc

pkg/runner/expression.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ func (rc *RunContext) NewExpressionEvaluatorWithEnv(ctx context.Context, env map
9090
Needs: using,
9191
Inputs: inputs,
9292
HashFiles: getHashFilesFunction(ctx, rc),
93+
CtxData: rc.ContextData,
9394
}
9495
if rc.JobContainer != nil {
9596
ee.Runner = rc.JobContainer.GetRunnerContext(ctx)
@@ -155,9 +156,11 @@ func (rc *RunContext) newStepExpressionEvaluator(ctx context.Context, step step,
155156
// but required to interpolate/evaluate the inputs in actions/composite
156157
Inputs: inputs,
157158
HashFiles: getHashFilesFunction(ctx, rc),
159+
CtxData: rc.ContextData,
158160
}
159161
if rc.JobContainer != nil {
160162
ee.Runner = rc.JobContainer.GetRunnerContext(ctx)
163+
ee.EnvCS = !rc.JobContainer.IsEnvironmentCaseInsensitive()
161164
}
162165
return expressionEvaluator{
163166
interpreter: exprparser.NewInterpeter(ee, exprparser.Config{

pkg/runner/run_context.go

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type RunContext struct {
5353
cleanUpJobContainer common.Executor
5454
caller *caller // job calling this RunContext (reusable workflows)
5555
Cancelled bool
56+
ContextData map[string]interface{}
5657
nodeToolFullPath string
5758
}
5859

@@ -959,6 +960,8 @@ func (rc *RunContext) getGithubContext(ctx context.Context) *model.GithubContext
959960
ghc.Workspace = rc.JobContainer.ToContainerPath(rc.Config.Workdir)
960961
}
961962

963+
rc.mergeGitHubContextWithContextData(ghc)
964+
962965
if ghc.RunAttempt == "" {
963966
ghc.RunAttempt = "1"
964967
}
@@ -994,7 +997,7 @@ func (rc *RunContext) getGithubContext(ctx context.Context) *model.GithubContext
994997

995998
ghc.SetBaseAndHeadRef()
996999
repoPath := rc.Config.Workdir
997-
ghc.SetRepositoryAndOwner(ctx, rc.Config.GitHubInstance, rc.Config.RemoteName, repoPath)
1000+
ghc.SetRepositoryAndOwner(ctx, rc.Config.GetGitHubInstance(), rc.Config.RemoteName, repoPath)
9981001
if ghc.Ref == "" {
9991002
ghc.SetRef(ctx, rc.Config.DefaultBranch, repoPath)
10001003
}
@@ -1004,16 +1007,16 @@ func (rc *RunContext) getGithubContext(ctx context.Context) *model.GithubContext
10041007

10051008
ghc.SetRefTypeAndName()
10061009

1007-
// defaults
1008-
ghc.ServerURL = "https://github.com"
1009-
ghc.APIURL = "https://api.github.com"
1010-
ghc.GraphQLURL = "https://api.github.com/graphql"
1011-
// per GHES
1012-
if rc.Config.GitHubInstance != "github.com" {
1013-
ghc.ServerURL = fmt.Sprintf("https://%s", rc.Config.GitHubInstance)
1014-
ghc.APIURL = fmt.Sprintf("https://%s/api/v3", rc.Config.GitHubInstance)
1015-
ghc.GraphQLURL = fmt.Sprintf("https://%s/api/graphql", rc.Config.GitHubInstance)
1010+
if ghc.ServerURL == "" {
1011+
ghc.ServerURL = rc.Config.GetGitHubServerURL()
1012+
}
1013+
if ghc.APIURL == "" {
1014+
ghc.APIURL = rc.Config.GetGitHubAPIServerURL()
1015+
}
1016+
if ghc.GraphQLURL == "" {
1017+
ghc.GraphQLURL = rc.Config.GetGitHubGraphQlAPIServerURL()
10161018
}
1019+
10171020
// allow to be overridden by user
10181021
if rc.Config.Env["GITHUB_SERVER_URL"] != "" {
10191022
ghc.ServerURL = rc.Config.Env["GITHUB_SERVER_URL"]
@@ -1028,6 +1031,25 @@ func (rc *RunContext) getGithubContext(ctx context.Context) *model.GithubContext
10281031
return ghc
10291032
}
10301033

1034+
func (rc *RunContext) mergeGitHubContextWithContextData(ghc *model.GithubContext) {
1035+
if rnout, ok := rc.ContextData["github"]; ok {
1036+
nout, ok := rnout.(map[string]interface{})
1037+
if ok {
1038+
var out map[string]interface{}
1039+
content, _ := json.Marshal(ghc)
1040+
_ = json.Unmarshal(content, &out)
1041+
for k, v := range nout {
1042+
// gitea sends empty string github contextdata, which replaced github.workspace
1043+
if v != nil && v != "" {
1044+
out[k] = v
1045+
}
1046+
}
1047+
content, _ = json.Marshal(out)
1048+
_ = json.Unmarshal(content, &ghc)
1049+
}
1050+
}
1051+
}
1052+
10311053
func isLocalCheckout(ghc *model.GithubContext, step *model.Step) bool {
10321054
if step.Type() == model.StepTypeInvalid {
10331055
// This will be errored out by the executor later, we need this here to avoid a null panic though

pkg/runner/run_context_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,54 @@ func TestGetGitHubContext(t *testing.T) {
393393
assert.Equal(t, "job1", ghc.Job)
394394
}
395395

396+
func TestGetGitHubContextOverlay(t *testing.T) {
397+
log.SetLevel(log.DebugLevel)
398+
399+
cwd, err := os.Getwd()
400+
assert.Nil(t, err)
401+
402+
rc := &RunContext{
403+
Config: &Config{
404+
EventName: "push",
405+
Workdir: cwd,
406+
},
407+
Run: &model.Run{
408+
Workflow: &model.Workflow{
409+
Name: "GitHubContextTest",
410+
},
411+
},
412+
Name: "GitHubContextTest",
413+
CurrentStep: "step",
414+
Matrix: map[string]interface{}{},
415+
Env: map[string]string{},
416+
ExtraPath: []string{},
417+
StepResults: map[string]*model.StepResult{},
418+
OutputMappings: map[MappableOutput]MappableOutput{},
419+
ContextData: map[string]interface{}{
420+
"github": map[string]interface{}{
421+
"actor": "me",
422+
"repository": "myowner/myrepo",
423+
"repository_owner": "myowner",
424+
},
425+
},
426+
}
427+
rc.Run.JobID = "job1"
428+
429+
ghc := rc.getGithubContext(context.Background())
430+
431+
log.Debugf("%v", ghc)
432+
433+
assert.Equal(t, "1", ghc.RunID)
434+
assert.Equal(t, "1", ghc.RunNumber)
435+
assert.Equal(t, "0", ghc.RetentionDays)
436+
assert.Equal(t, "me", ghc.Actor)
437+
assert.Equal(t, "myowner/myrepo", ghc.Repository)
438+
assert.Equal(t, "myowner", ghc.RepositoryOwner)
439+
assert.Equal(t, "/dev/null", ghc.RunnerPerflog)
440+
assert.Equal(t, rc.Config.Secrets["GITHUB_TOKEN"], ghc.Token)
441+
assert.Equal(t, "job1", ghc.Job)
442+
}
443+
396444
func TestGetGithubContextRef(t *testing.T) {
397445
table := []struct {
398446
event string

pkg/runner/runner.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"fmt"
77
"os"
8+
"regexp"
89
"runtime"
910

1011
"github.com/actions-oss/act-cli/pkg/common"
@@ -48,6 +49,9 @@ type Config struct {
4849
ContainerOptions string // Options for the job container
4950
UseGitIgnore bool // controls if paths in .gitignore should not be copied into container, default true
5051
GitHubInstance string // GitHub instance to use, default "github.com"
52+
GitHubServerURL string // GitHub server url to use
53+
GitHubAPIServerURL string // GitHub api server url to use
54+
GitHubGraphQlAPIServerURL string // GitHub graphql server url to use
5155
ContainerCapAdd []string // list of kernel capabilities to add to the containers
5256
ContainerCapDrop []string // list of kernel capabilities to remove from the containers
5357
AutoRemove bool // controls if the container is automatically removed upon workflow completion
@@ -64,6 +68,38 @@ type Config struct {
6468
HostEnvironmentDir string // Custom folder for host environment, parallel jobs must be 1
6569
}
6670

71+
func (runnerConfig *Config) GetGitHubServerURL() string {
72+
if len(runnerConfig.GitHubServerURL) > 0 {
73+
return runnerConfig.GitHubServerURL
74+
}
75+
return fmt.Sprintf("https://%s", runnerConfig.GitHubInstance)
76+
}
77+
func (runnerConfig *Config) GetGitHubAPIServerURL() string {
78+
if len(runnerConfig.GitHubAPIServerURL) > 0 {
79+
return runnerConfig.GitHubAPIServerURL
80+
}
81+
if runnerConfig.GitHubInstance == "github.com" {
82+
return "https://api.github.com"
83+
}
84+
return fmt.Sprintf("https://%s/api/v3", runnerConfig.GitHubInstance)
85+
}
86+
func (runnerConfig *Config) GetGitHubGraphQlAPIServerURL() string {
87+
if len(runnerConfig.GitHubGraphQlAPIServerURL) > 0 {
88+
return runnerConfig.GitHubGraphQlAPIServerURL
89+
}
90+
if runnerConfig.GitHubInstance == "github.com" {
91+
return "https://api.github.com/graphql"
92+
}
93+
return fmt.Sprintf("https://%s/api/graphql", runnerConfig.GitHubInstance)
94+
}
95+
func (runnerConfig *Config) GetGitHubInstance() string {
96+
if len(runnerConfig.GitHubServerURL) > 0 {
97+
regex := regexp.MustCompile("^https?://(.*)$")
98+
return regex.ReplaceAllString(runnerConfig.GitHubServerURL, "$1")
99+
}
100+
return runnerConfig.GitHubInstance
101+
}
102+
67103
type caller struct {
68104
runContext *RunContext
69105
}

pkg/runner/step_action_remote_test.go

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ func TestStepActionRemote(t *testing.T) {
5555
read bool
5656
run bool
5757
}
58-
runError error
58+
runError error
59+
gitHubServerURL string
60+
gitHubAPIServerURL string
61+
gitHubGraphQlAPIServerURL string
5962
}{
6063
{
6164
name: "run-successful",
@@ -80,6 +83,32 @@ func TestStepActionRemote(t *testing.T) {
8083
run: true,
8184
},
8285
},
86+
{
87+
name: "run-successful",
88+
stepModel: &model.Step{
89+
ID: "step",
90+
Uses: "remote/action@v1",
91+
},
92+
result: &model.StepResult{
93+
Conclusion: model.StepStatusSuccess,
94+
Outcome: model.StepStatusSuccess,
95+
Outputs: map[string]string{},
96+
},
97+
mocks: struct {
98+
env bool
99+
cloned bool
100+
read bool
101+
run bool
102+
}{
103+
env: true,
104+
cloned: true,
105+
read: true,
106+
run: true,
107+
},
108+
gitHubServerURL: "http://localhost:3000",
109+
gitHubAPIServerURL: "http://localhost:3000/api/v1",
110+
gitHubGraphQlAPIServerURL: "http://localhost:3000/api/graphql",
111+
},
83112
{
84113
name: "run-skipped",
85114
stepModel: &model.Step{
@@ -142,8 +171,11 @@ func TestStepActionRemote(t *testing.T) {
142171
sar := &stepActionRemote{
143172
RunContext: &RunContext{
144173
Config: &Config{
145-
GitHubInstance: "github.com",
146-
ActionCache: cacheMock,
174+
GitHubInstance: "github.com",
175+
ActionCache: cacheMock,
176+
GitHubServerURL: tt.gitHubServerURL,
177+
GitHubAPIServerURL: tt.gitHubAPIServerURL,
178+
GitHubGraphQlAPIServerURL: tt.gitHubGraphQlAPIServerURL,
147179
},
148180
Run: &model.Run{
149181
JobID: "1",
@@ -162,7 +194,12 @@ func TestStepActionRemote(t *testing.T) {
162194
}
163195
sar.RunContext.ExprEval = sar.RunContext.NewExpressionEvaluator(ctx)
164196

165-
cacheMock.Mock.On("Fetch", ctx, mock.AnythingOfType("string"), "https://github.com/remote/action", "v1", "").Return("someval")
197+
serverURL := "https://github.com"
198+
if tt.gitHubServerURL != "" {
199+
serverURL = tt.gitHubServerURL
200+
}
201+
202+
cacheMock.Mock.On("Fetch", ctx, mock.AnythingOfType("string"), serverURL+"/remote/action", "v1", "").Return("someval")
166203
suffixMatcher := func(suffix string) interface{} {
167204
return mock.MatchedBy(func(actionDir string) bool {
168205
return strings.HasSuffix(actionDir, suffix)
@@ -173,7 +210,9 @@ func TestStepActionRemote(t *testing.T) {
173210
sarm.Mock.On("readAction", sar.Step, "someval", "", mock.Anything, mock.Anything).Return(&model.Action{}, nil)
174211
}
175212
if tt.mocks.run {
176-
sarm.On("runAction", sar, suffixMatcher("act/remote-action@v1"), newRemoteAction(sar.Step.Uses)).Return(func(_ context.Context) error { return tt.runError })
213+
remoteAction := newRemoteAction(sar.Step.Uses)
214+
remoteAction.URL = serverURL
215+
sarm.On("runAction", sar, suffixMatcher("act/remote-action@v1"), remoteAction).Return(func(_ context.Context) error { return tt.runError })
177216

178217
cm.On("Copy", "/var/run/act", mock.AnythingOfType("[]*container.FileEntry")).Return(func(_ context.Context) error {
179218
return nil

0 commit comments

Comments
 (0)