GitHub Enterprise support (#658)
* Add option to specify custom GitHub instance * Use correct GHE API endpoint URLs Co-authored-by: Markus Wolf <knister.peter@shadowrun-clan.de> * Extract slug from GitHub Enterprise URLs Co-authored-by: Markus Wolf <knister.peter@shadowrun-clan.de> * Use GITHUB_TOKEN for clone authenticate if provided This change will allow use authentication for cloning actions from private repositories or github enterprise instances. Co-Authored-By: Markus Wolf <knister.peter@shadowrun-clan.de> * Add section about using act on GitHub Enterprise to README Co-authored-by: Markus Wolf <knister.peter@shadowrun-clan.de> * Set GitHubInstance in runnerConfig in runner_test Co-authored-by: Markus Wolf <knister.peter@shadowrun-clan.de> Co-authored-by: hackercat <me@hackerc.at> Co-authored-by: Markus Wolf <knister.peter@shadowrun-clan.de>
This commit is contained in:
parent
710a3ac94c
commit
0c4374ec41
10
README.md
10
README.md
|
@ -129,6 +129,7 @@ It will save that information to `~/.actrc`, please refer to [Configuration](#co
|
||||||
--env stringArray env to make available to actions with optional value (e.g. --e myenv=foo or -s myenv)
|
--env stringArray env to make available to actions with optional value (e.g. --e myenv=foo or -s myenv)
|
||||||
--env-file string environment file to read and use as env in the containers (default ".env")
|
--env-file string environment file to read and use as env in the containers (default ".env")
|
||||||
-e, --eventpath string path to event JSON file
|
-e, --eventpath string path to event JSON file
|
||||||
|
--github-instance string GitHub instance to use. Don't use this if you are not using GitHub Enterprise Server. (default "github.com")
|
||||||
-g, --graph draw workflows
|
-g, --graph draw workflows
|
||||||
-h, --help help for act
|
-h, --help help for act
|
||||||
--insecure-secrets NOT RECOMMENDED! Doesn't hide secrets while printing logs.
|
--insecure-secrets NOT RECOMMENDED! Doesn't hide secrets while printing logs.
|
||||||
|
@ -306,6 +307,15 @@ act -e pull-request.json
|
||||||
|
|
||||||
Act will properly provide `github.head_ref` and `github.base_ref` to the action as expected.
|
Act will properly provide `github.head_ref` and `github.base_ref` to the action as expected.
|
||||||
|
|
||||||
|
# GitHub Enterprise
|
||||||
|
|
||||||
|
Act supports using and authenticating against private GitHub Enterprise servers.
|
||||||
|
To use your custom GHE server, set the CLI flag `--github-instance` to your hostname (e.g. `github.company.com`).
|
||||||
|
|
||||||
|
Please note that if your GHE server requires authentication, we will use the secret provided via `GITHUB_TOKEN`.
|
||||||
|
|
||||||
|
Please also see the [official documentation for GitHub actions on GHE](https://docs.github.com/en/enterprise-server@3.0/admin/github-actions/about-using-actions-in-your-enterprise) for more information on how to use actions.
|
||||||
|
|
||||||
# Support
|
# Support
|
||||||
|
|
||||||
Need help? Ask on [Gitter](https://gitter.im/nektos/act)!
|
Need help? Ask on [Gitter](https://gitter.im/nektos/act)!
|
||||||
|
|
|
@ -29,6 +29,7 @@ type Input struct {
|
||||||
containerArchitecture string
|
containerArchitecture string
|
||||||
noWorkflowRecurse bool
|
noWorkflowRecurse bool
|
||||||
useGitIgnore bool
|
useGitIgnore bool
|
||||||
|
githubInstance string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) resolve(path string) string {
|
func (i *Input) resolve(path string) string {
|
||||||
|
|
|
@ -61,6 +61,7 @@ func Execute(ctx context.Context, version string) {
|
||||||
rootCmd.PersistentFlags().BoolVarP(&input.insecureSecrets, "insecure-secrets", "", false, "NOT RECOMMENDED! Doesn't hide secrets while printing logs.")
|
rootCmd.PersistentFlags().BoolVarP(&input.insecureSecrets, "insecure-secrets", "", false, "NOT RECOMMENDED! Doesn't hide secrets while printing logs.")
|
||||||
rootCmd.PersistentFlags().StringVarP(&input.envfile, "env-file", "", ".env", "environment file to read and use as env in the containers")
|
rootCmd.PersistentFlags().StringVarP(&input.envfile, "env-file", "", ".env", "environment file to read and use as env in the containers")
|
||||||
rootCmd.PersistentFlags().StringVarP(&input.containerArchitecture, "container-architecture", "", "", "Architecture which should be used to run containers, e.g.: linux/amd64. If not specified, will use host default architecture. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms.")
|
rootCmd.PersistentFlags().StringVarP(&input.containerArchitecture, "container-architecture", "", "", "Architecture which should be used to run containers, e.g.: linux/amd64. If not specified, will use host default architecture. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms.")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&input.githubInstance, "github-instance", "", "github.com", "GitHub instance to use. Don't use this if you are not using GitHub Enterprise Server.")
|
||||||
rootCmd.SetArgs(args())
|
rootCmd.SetArgs(args())
|
||||||
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
@ -254,6 +255,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
|
||||||
UsernsMode: input.usernsMode,
|
UsernsMode: input.usernsMode,
|
||||||
ContainerArchitecture: input.containerArchitecture,
|
ContainerArchitecture: input.containerArchitecture,
|
||||||
UseGitIgnore: input.useGitIgnore,
|
UseGitIgnore: input.useGitIgnore,
|
||||||
|
GitHubInstance: input.githubInstance,
|
||||||
}
|
}
|
||||||
r, err := runner.New(config)
|
r, err := runner.New(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
git "github.com/go-git/go-git/v5"
|
git "github.com/go-git/go-git/v5"
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||||
"github.com/go-ini/ini"
|
"github.com/go-ini/ini"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
@ -142,12 +143,12 @@ func findGitPrettyRef(head, root, sub string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindGithubRepo get the repo
|
// FindGithubRepo get the repo
|
||||||
func FindGithubRepo(file string) (string, error) {
|
func FindGithubRepo(file string, githubInstance string) (string, error) {
|
||||||
url, err := findGitRemoteURL(file)
|
url, err := findGitRemoteURL(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
_, slug, err := findGitSlug(url)
|
_, slug, err := findGitSlug(url, githubInstance)
|
||||||
return slug, err
|
return slug, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +175,7 @@ func findGitRemoteURL(file string) (string, error) {
|
||||||
return url, nil
|
return url, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findGitSlug(url string) (string, string, error) {
|
func findGitSlug(url string, githubInstance string) (string, string, error) {
|
||||||
if matches := codeCommitHTTPRegex.FindStringSubmatch(url); matches != nil {
|
if matches := codeCommitHTTPRegex.FindStringSubmatch(url); matches != nil {
|
||||||
return "CodeCommit", matches[2], nil
|
return "CodeCommit", matches[2], nil
|
||||||
} else if matches := codeCommitSSHRegex.FindStringSubmatch(url); matches != nil {
|
} else if matches := codeCommitSSHRegex.FindStringSubmatch(url); matches != nil {
|
||||||
|
@ -183,6 +184,14 @@ func findGitSlug(url string) (string, string, error) {
|
||||||
return "GitHub", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
return "GitHub", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
||||||
} else if matches := githubSSHRegex.FindStringSubmatch(url); matches != nil {
|
} else if matches := githubSSHRegex.FindStringSubmatch(url); matches != nil {
|
||||||
return "GitHub", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
return "GitHub", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
||||||
|
} else if githubInstance != "github.com" {
|
||||||
|
gheHTTPRegex := regexp.MustCompile(fmt.Sprintf(`^https?://%s/(.+)/(.+?)(?:.git)?$`, githubInstance))
|
||||||
|
gheSSHRegex := regexp.MustCompile(fmt.Sprintf(`%s[:/](.+)/(.+).git$`, githubInstance))
|
||||||
|
if matches := gheHTTPRegex.FindStringSubmatch(url); matches != nil {
|
||||||
|
return "GitHubEnterprise", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
||||||
|
} else if matches := gheSSHRegex.FindStringSubmatch(url); matches != nil {
|
||||||
|
return "GitHubEnterprise", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return "", url, nil
|
return "", url, nil
|
||||||
}
|
}
|
||||||
|
@ -218,9 +227,10 @@ func findGitDirectory(fromFile string) (string, error) {
|
||||||
|
|
||||||
// NewGitCloneExecutorInput the input for the NewGitCloneExecutor
|
// NewGitCloneExecutorInput the input for the NewGitCloneExecutor
|
||||||
type NewGitCloneExecutorInput struct {
|
type NewGitCloneExecutorInput struct {
|
||||||
URL string
|
URL string
|
||||||
Ref string
|
Ref string
|
||||||
Dir string
|
Dir string
|
||||||
|
Token string
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloneIfRequired ...
|
// CloneIfRequired ...
|
||||||
|
@ -237,10 +247,24 @@ func CloneIfRequired(ctx context.Context, refName plumbing.ReferenceName, input
|
||||||
progressWriter = os.Stdout
|
progressWriter = os.Stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err = git.PlainCloneContext(ctx, input.Dir, false, &git.CloneOptions{
|
var cloneOptions git.CloneOptions
|
||||||
URL: input.URL,
|
if input.Token != "" {
|
||||||
Progress: progressWriter,
|
cloneOptions = git.CloneOptions{
|
||||||
})
|
URL: input.URL,
|
||||||
|
Progress: progressWriter,
|
||||||
|
Auth: &http.BasicAuth{
|
||||||
|
Username: "token",
|
||||||
|
Password: input.Token,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cloneOptions = git.CloneOptions{
|
||||||
|
URL: input.URL,
|
||||||
|
Progress: progressWriter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err = git.PlainCloneContext(ctx, input.Dir, false, &cloneOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Unable to clone %v %s: %v", input.URL, refName, err)
|
logger.Errorf("Unable to clone %v %s: %v", input.URL, refName, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -34,7 +34,7 @@ func TestFindGitSlug(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range slugTests {
|
for _, tt := range slugTests {
|
||||||
provider, slug, err := findGitSlug(tt.url)
|
provider, slug, err := findGitSlug(tt.url, "github.com")
|
||||||
|
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.Equal(tt.provider, provider)
|
assert.Equal(tt.provider, provider)
|
||||||
|
|
|
@ -503,7 +503,7 @@ func (rc *RunContext) getGithubContext() *githubContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
repoPath := rc.Config.Workdir
|
repoPath := rc.Config.Workdir
|
||||||
repo, err := common.FindGithubRepo(repoPath)
|
repo, err := common.FindGithubRepo(repoPath, rc.Config.GitHubInstance)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("unable to get git repo: %v", err)
|
log.Warningf("unable to get git repo: %v", err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -644,6 +644,11 @@ func (rc *RunContext) withGithubEnv(env map[string]string) map[string]string {
|
||||||
env["GITHUB_SERVER_URL"] = "https://github.com"
|
env["GITHUB_SERVER_URL"] = "https://github.com"
|
||||||
env["GITHUB_API_URL"] = "https://api.github.com"
|
env["GITHUB_API_URL"] = "https://api.github.com"
|
||||||
env["GITHUB_GRAPHQL_URL"] = "https://api.github.com/graphql"
|
env["GITHUB_GRAPHQL_URL"] = "https://api.github.com/graphql"
|
||||||
|
if rc.Config.GitHubInstance != "github.com" {
|
||||||
|
env["GITHUB_SERVER_URL"] = fmt.Sprintf("https://%s", rc.Config.GitHubInstance)
|
||||||
|
env["GITHUB_API_URL"] = fmt.Sprintf("https://%s/api/v3", rc.Config.GitHubInstance)
|
||||||
|
env["GITHUB_GRAPHQL_URL"] = fmt.Sprintf("https://%s/api/graphql", rc.Config.GitHubInstance)
|
||||||
|
}
|
||||||
|
|
||||||
job := rc.Run.Job()
|
job := rc.Run.Job()
|
||||||
if job.RunsOn() != nil {
|
if job.RunsOn() != nil {
|
||||||
|
|
|
@ -38,6 +38,7 @@ type Config struct {
|
||||||
UsernsMode string // user namespace to use
|
UsernsMode string // user namespace to use
|
||||||
ContainerArchitecture string // Desired OS/architecture platform for running containers
|
ContainerArchitecture string // Desired OS/architecture platform for running containers
|
||||||
UseGitIgnore bool // controls if paths in .gitignore should not be copied into container, default true
|
UseGitIgnore bool // controls if paths in .gitignore should not be copied into container, default true
|
||||||
|
GitHubInstance string // GitHub instance to use, default "github.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolves the equivalent host path inside the container
|
// Resolves the equivalent host path inside the container
|
||||||
|
|
|
@ -56,6 +56,7 @@ func runTestJobFile(ctx context.Context, t *testing.T, tjfi TestJobFileInfo, sec
|
||||||
ReuseContainers: false,
|
ReuseContainers: false,
|
||||||
ContainerArchitecture: tjfi.containerArchitecture,
|
ContainerArchitecture: tjfi.containerArchitecture,
|
||||||
Secrets: secrets,
|
Secrets: secrets,
|
||||||
|
GitHubInstance: "github.com",
|
||||||
}
|
}
|
||||||
|
|
||||||
runner, err := New(runnerConfig)
|
runner, err := New(runnerConfig)
|
||||||
|
|
|
@ -70,7 +70,11 @@ func (sc *StepContext) Executor() common.Executor {
|
||||||
if remoteAction == nil {
|
if remoteAction == nil {
|
||||||
return common.NewErrorExecutor(formatError(step.Uses))
|
return common.NewErrorExecutor(formatError(step.Uses))
|
||||||
}
|
}
|
||||||
if remoteAction.IsCheckout() && rc.getGithubContext().isLocalCheckout(step) {
|
|
||||||
|
remoteAction.URL = rc.Config.GitHubInstance
|
||||||
|
|
||||||
|
github := rc.getGithubContext()
|
||||||
|
if remoteAction.IsCheckout() && github.isLocalCheckout(step) {
|
||||||
return func(ctx context.Context) error {
|
return func(ctx context.Context) error {
|
||||||
common.Logger(ctx).Debugf("Skipping actions/checkout")
|
common.Logger(ctx).Debugf("Skipping actions/checkout")
|
||||||
return nil
|
return nil
|
||||||
|
@ -80,9 +84,10 @@ func (sc *StepContext) Executor() common.Executor {
|
||||||
actionDir := fmt.Sprintf("%s/%s", rc.ActionCacheDir(), strings.ReplaceAll(step.Uses, "/", "-"))
|
actionDir := fmt.Sprintf("%s/%s", rc.ActionCacheDir(), strings.ReplaceAll(step.Uses, "/", "-"))
|
||||||
return common.NewPipelineExecutor(
|
return common.NewPipelineExecutor(
|
||||||
common.NewGitCloneExecutor(common.NewGitCloneExecutorInput{
|
common.NewGitCloneExecutor(common.NewGitCloneExecutorInput{
|
||||||
URL: remoteAction.CloneURL(),
|
URL: remoteAction.CloneURL(),
|
||||||
Ref: remoteAction.Ref,
|
Ref: remoteAction.Ref,
|
||||||
Dir: actionDir,
|
Dir: actionDir,
|
||||||
|
Token: github.Token,
|
||||||
}),
|
}),
|
||||||
sc.setupAction(actionDir, remoteAction.Path),
|
sc.setupAction(actionDir, remoteAction.Path),
|
||||||
sc.runAction(actionDir, remoteAction.Path),
|
sc.runAction(actionDir, remoteAction.Path),
|
||||||
|
@ -568,6 +573,7 @@ func (sc *StepContext) runAction(actionDir string, actionPath string) common.Exe
|
||||||
}
|
}
|
||||||
|
|
||||||
type remoteAction struct {
|
type remoteAction struct {
|
||||||
|
URL string
|
||||||
Org string
|
Org string
|
||||||
Repo string
|
Repo string
|
||||||
Path string
|
Path string
|
||||||
|
@ -575,7 +581,7 @@ type remoteAction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ra *remoteAction) CloneURL() string {
|
func (ra *remoteAction) CloneURL() string {
|
||||||
return fmt.Sprintf("https://github.com/%s/%s", ra.Org, ra.Repo)
|
return fmt.Sprintf("https://%s/%s/%s", ra.URL, ra.Org, ra.Repo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ra *remoteAction) IsCheckout() bool {
|
func (ra *remoteAction) IsCheckout() bool {
|
||||||
|
@ -601,6 +607,7 @@ func newRemoteAction(action string) *remoteAction {
|
||||||
Repo: matches[2],
|
Repo: matches[2],
|
||||||
Path: matches[4],
|
Path: matches[4],
|
||||||
Ref: matches[6],
|
Ref: matches[6],
|
||||||
|
URL: "github.com",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue