mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-08-13 22:41:54 +02:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ea5134ac6 | ||
|
|
9dd9204937 |
+1
-1
@@ -65,7 +65,7 @@ builds:
|
|||||||
flags:
|
flags:
|
||||||
- -trimpath
|
- -trimpath
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w -X gitea.com/gitea/runner/internal/pkg/ver.version={{ .Summary }}
|
- -s -w -X main.version={{ .Summary }}
|
||||||
binary: >-
|
binary: >-
|
||||||
{{ .ProjectName }}-
|
{{ .ProjectName }}-
|
||||||
{{- .Version }}-
|
{{- .Version }}-
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
TAGS ?=
|
TAGS ?=
|
||||||
LDFLAGS ?= -X "gitea.com/gitea/runner/internal/pkg/ver.version=v$(RELASE_VERSION)"
|
LDFLAGS ?= -X "main.version=v$(RELASE_VERSION)"
|
||||||
|
VERSION_CHECK_BIN := $(DIST)/version-check$(suffix $(EXECUTABLE))
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: build
|
all: build
|
||||||
@@ -113,7 +114,19 @@ deps-tools: ## install tool dependencies
|
|||||||
wait
|
wait
|
||||||
|
|
||||||
.PHONY: checks
|
.PHONY: checks
|
||||||
checks: tidy-check fmt-check security-check ## run the non-lint source checks
|
checks: tidy-check fmt-check security-check version-check ## run the non-lint source checks
|
||||||
|
|
||||||
|
.PHONY: version-check
|
||||||
|
version-check: ## verify the version is injected into the binary
|
||||||
|
@mkdir -p $(DIST)
|
||||||
|
@$(GO) build -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) -X "main.version=v0.0.0-injected"' -o $(VERSION_CHECK_BIN) .
|
||||||
|
@case "$$($(VERSION_CHECK_BIN) --version)" in \
|
||||||
|
*v0.0.0-injected*) ;; \
|
||||||
|
*) echo "version injection is broken, the Makefile -X target no longer matches a variable" >&2; exit 1;; \
|
||||||
|
esac
|
||||||
|
@rm -f $(VERSION_CHECK_BIN)
|
||||||
|
@# goreleaser builds releases from its own ldflags, so a stale -X target there ships an unversioned binary
|
||||||
|
@grep -q -- '-X main.version=' .goreleaser.yaml || { echo ".goreleaser.yaml no longer injects main.version" >&2; exit 1; }
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint: lint-go lint-go-windows ## lint everything
|
lint: lint-go lint-go-windows ## lint everything
|
||||||
|
|||||||
@@ -829,13 +829,19 @@ func (s *Step) Type() StepType {
|
|||||||
} else if strings.HasPrefix(s.Uses, "./") {
|
} else if strings.HasPrefix(s.Uses, "./") {
|
||||||
return StepTypeUsesActionLocal
|
return StepTypeUsesActionLocal
|
||||||
}
|
}
|
||||||
return StepTypeUsesActionRemote
|
return StepTypeUsesActionRemote // `$/` self-repository refs land here and resolve in prepareActionExecutor
|
||||||
}
|
}
|
||||||
|
|
||||||
// UsesHash returns a hash of the uses string.
|
// UsesHash returns a hash of the uses string.
|
||||||
// For Gitea.
|
// For Gitea.
|
||||||
func (s *Step) UsesHash() string {
|
func (s *Step) UsesHash() string {
|
||||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(s.Uses)))
|
return UsesHash(s.Uses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsesHash returns a hash of a `uses:` value.
|
||||||
|
// For Gitea.
|
||||||
|
func UsesHash(uses string) string {
|
||||||
|
return fmt.Sprintf("%x", sha256.Sum256([]byte(uses)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadWorkflow returns a list of jobs for a given workflow file reader
|
// ReadWorkflow returns a list of jobs for a given workflow file reader
|
||||||
|
|||||||
@@ -597,7 +597,7 @@ func actionStagePaths(step actionStep) (actionDir, actionPath, actionName, conta
|
|||||||
|
|
||||||
if sar, ok := step.(*stepActionRemote); ok {
|
if sar, ok := step.(*stepActionRemote); ok {
|
||||||
actionDir = sar.actionDir()
|
actionDir = sar.actionDir()
|
||||||
actionPath = newRemoteAction(stepModel.Uses).Path
|
actionPath = sar.remoteAction.Path
|
||||||
} else {
|
} else {
|
||||||
actionDir = filepath.Join(rc.Config.Workdir, stepModel.Uses)
|
actionDir = filepath.Join(rc.Config.Workdir, stepModel.Uses)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ type stepActionRemote struct {
|
|||||||
|
|
||||||
var stepActionRemoteNewCloneExecutor = git.NewGitCloneExecutor
|
var stepActionRemoteNewCloneExecutor = git.NewGitCloneExecutor
|
||||||
|
|
||||||
|
// selfRepoPrefix introduces a self-repository reference: the action lives in the repo holding the file that wrote the `uses:`.
|
||||||
|
const selfRepoPrefix = "$/"
|
||||||
|
|
||||||
func (sar *stepActionRemote) prepareActionExecutor() common.Executor {
|
func (sar *stepActionRemote) prepareActionExecutor() common.Executor {
|
||||||
return func(ctx context.Context) error {
|
return func(ctx context.Context) error {
|
||||||
if sar.remoteAction != nil && sar.action != nil {
|
if sar.remoteAction != nil && sar.action != nil {
|
||||||
@@ -52,12 +55,16 @@ func (sar *stepActionRemote) prepareActionExecutor() common.Executor {
|
|||||||
// so we need to interpolate the expression value for uses first.
|
// so we need to interpolate the expression value for uses first.
|
||||||
sar.Step.Uses = sar.RunContext.NewExpressionEvaluator(ctx).Interpolate(ctx, sar.Step.Uses)
|
sar.Step.Uses = sar.RunContext.NewExpressionEvaluator(ctx).Interpolate(ctx, sar.Step.Uses)
|
||||||
|
|
||||||
sar.remoteAction = newRemoteAction(sar.Step.Uses)
|
github := sar.getGithubContext(ctx) // read before remoteAction is set, so `$/` resolves against the enclosing action
|
||||||
|
if strings.HasPrefix(sar.Step.Uses, selfRepoPrefix) {
|
||||||
|
sar.remoteAction = newSelfRepoAction(sar.Step.Uses, github)
|
||||||
|
} else {
|
||||||
|
sar.remoteAction = newRemoteAction(sar.Step.Uses)
|
||||||
|
}
|
||||||
if sar.remoteAction == nil {
|
if sar.remoteAction == nil {
|
||||||
return fmt.Errorf("Expected format {org}/{repo}[/path]@ref. Actual '%s' Input string was not in a correct format", sar.Step.Uses)
|
return fmt.Errorf("Expected format {org}/{repo}[/path]@ref or %s{path}. Actual '%s' Input string was not in a correct format", selfRepoPrefix, sar.Step.Uses)
|
||||||
}
|
}
|
||||||
|
|
||||||
github := sar.getGithubContext(ctx)
|
|
||||||
if sar.remoteAction.IsCheckout() && isLocalCheckout(github, sar.Step) && !sar.RunContext.Config.NoSkipCheckout {
|
if sar.remoteAction.IsCheckout() && isLocalCheckout(github, sar.Step) && !sar.RunContext.Config.NoSkipCheckout {
|
||||||
common.Logger(ctx).Debugf("Skipping local actions/checkout because workdir was already copied")
|
common.Logger(ctx).Debugf("Skipping local actions/checkout because workdir was already copied")
|
||||||
return nil
|
return nil
|
||||||
@@ -260,7 +267,12 @@ func (sar *stepActionRemote) revertToolkitOnFailure(exec common.Executor) common
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sar *stepActionRemote) actionDir() string {
|
func (sar *stepActionRemote) actionDir() string {
|
||||||
return fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), sar.Step.UsesHash())
|
uses := sar.Step.Uses
|
||||||
|
if strings.HasPrefix(uses, selfRepoPrefix) {
|
||||||
|
// The same `$/x` names a different action per enclosing repo, so key the cache on what it resolved to.
|
||||||
|
uses = sar.remoteAction.URL + "/" + sar.remoteAction.Reference()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), model.UsesHash(uses))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sar *stepActionRemote) getRunContext() *RunContext {
|
func (sar *stepActionRemote) getRunContext() *RunContext {
|
||||||
@@ -390,6 +402,23 @@ func (ra *remoteAction) IsCheckout() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newSelfRepoAction resolves `$/{path}` against the enclosing composite action, falling back to the workflow's own repo and commit.
|
||||||
|
func newSelfRepoAction(action string, github *model.GithubContext) *remoteAction {
|
||||||
|
subPath := strings.TrimLeft(strings.TrimPrefix(action, selfRepoPrefix), "/")
|
||||||
|
if subPath == "" || strings.Contains(subPath, "@") || path.Clean("/"+subPath) != "/"+subPath { // rooted, so a leading ".." is rejected too
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
repo, ref := github.ActionRepository, github.ActionRef
|
||||||
|
if repo == "" || ref == "" {
|
||||||
|
repo, ref = github.Repository, github.Sha
|
||||||
|
}
|
||||||
|
org, name, _ := strings.Cut(repo, "/")
|
||||||
|
if org == "" || name == "" || ref == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &remoteAction{URL: github.ServerURL, Org: org, Repo: name, Path: subPath, Ref: ref}
|
||||||
|
}
|
||||||
|
|
||||||
func newRemoteAction(action string) *remoteAction {
|
func newRemoteAction(action string) *remoteAction {
|
||||||
// support http(s)://host/owner/repo@v3
|
// support http(s)://host/owner/repo@v3
|
||||||
for _, schema := range []string{"https://", "http://", "ssh://"} {
|
for _, schema := range []string{"https://", "http://", "ssh://"} {
|
||||||
|
|||||||
@@ -653,6 +653,8 @@ func TestStepActionRemotePost(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Step: tt.stepModel,
|
Step: tt.stepModel,
|
||||||
action: tt.actionModel,
|
action: tt.actionModel,
|
||||||
|
// post only ever runs after prepareActionExecutor resolved the action
|
||||||
|
remoteAction: newRemoteAction(tt.stepModel.Uses),
|
||||||
}
|
}
|
||||||
sar.RunContext.ExprEval = sar.RunContext.NewExpressionEvaluator(ctx)
|
sar.RunContext.ExprEval = sar.RunContext.NewExpressionEvaluator(ctx)
|
||||||
|
|
||||||
@@ -821,6 +823,73 @@ func Test_newRemoteAction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_newSelfRepoAction(t *testing.T) {
|
||||||
|
workflow := &model.GithubContext{
|
||||||
|
ServerURL: "https://gitea.example.com",
|
||||||
|
Repository: "owner/workflow-repo",
|
||||||
|
Sha: "abc123",
|
||||||
|
}
|
||||||
|
composite := &model.GithubContext{
|
||||||
|
ServerURL: "https://gitea.example.com",
|
||||||
|
Repository: "owner/workflow-repo",
|
||||||
|
Sha: "abc123",
|
||||||
|
ActionRepository: "other/action-repo",
|
||||||
|
ActionRef: "v1",
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
action string
|
||||||
|
github *model.GithubContext
|
||||||
|
want *remoteAction
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "top level resolves to the workflow repo at its commit",
|
||||||
|
action: "$/.gitea/actions/build",
|
||||||
|
github: workflow,
|
||||||
|
want: &remoteAction{
|
||||||
|
URL: "https://gitea.example.com",
|
||||||
|
Org: "owner",
|
||||||
|
Repo: "workflow-repo",
|
||||||
|
Path: ".gitea/actions/build",
|
||||||
|
Ref: "abc123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "inside a composite resolves to the enclosing action",
|
||||||
|
action: "$/.gitea/actions/build",
|
||||||
|
github: composite,
|
||||||
|
want: &remoteAction{
|
||||||
|
URL: "https://gitea.example.com",
|
||||||
|
Org: "other",
|
||||||
|
Repo: "action-repo",
|
||||||
|
Path: ".gitea/actions/build",
|
||||||
|
Ref: "v1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{name: "empty path", action: "$/", github: workflow},
|
||||||
|
{name: "ref suffix is not allowed", action: "$/.gitea/actions/build@v1", github: workflow},
|
||||||
|
{name: "path traversal", action: "$/../escape", github: workflow},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.want, newSelfRepoAction(tt.action, tt.github))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_stepActionRemoteSelfRepoActionDir(t *testing.T) {
|
||||||
|
dirFor := func(repo string) string {
|
||||||
|
sar := &stepActionRemote{
|
||||||
|
Step: &model.Step{Uses: "$/.gitea/actions/build"},
|
||||||
|
RunContext: &RunContext{Config: &Config{ActionCacheDir: "/cache"}},
|
||||||
|
remoteAction: &remoteAction{Org: "owner", Repo: repo, Ref: "v1"},
|
||||||
|
}
|
||||||
|
return sar.actionDir()
|
||||||
|
}
|
||||||
|
// The same `$/x` in two repos must not share a cache directory.
|
||||||
|
assert.NotEqual(t, dirFor("one"), dirFor("two"))
|
||||||
|
}
|
||||||
|
|
||||||
func Test_remoteActionReference(t *testing.T) {
|
func Test_remoteActionReference(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
uses string
|
uses string
|
||||||
|
|||||||
@@ -3,9 +3,15 @@
|
|||||||
|
|
||||||
package ver
|
package ver
|
||||||
|
|
||||||
// go build -ldflags "-X gitea.com/gitea/runner/internal/pkg/ver.version=1.2.3"
|
|
||||||
var version = "dev"
|
var version = "dev"
|
||||||
|
|
||||||
|
// SetVersion records the version injected into package main at build time.
|
||||||
|
func SetVersion(v string) {
|
||||||
|
if v != "" {
|
||||||
|
version = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Version() string {
|
func Version() string {
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,3 +11,17 @@ func TestVersion(t *testing.T) {
|
|||||||
t.Errorf("Version() = %q, want %q", got, version)
|
t.Errorf("Version() = %q, want %q", got, version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetVersion(t *testing.T) {
|
||||||
|
t.Cleanup(func() { version = "dev" })
|
||||||
|
|
||||||
|
SetVersion("v1.2.3")
|
||||||
|
if got := Version(); got != "v1.2.3" {
|
||||||
|
t.Errorf("Version() = %q, want %q", got, "v1.2.3")
|
||||||
|
}
|
||||||
|
|
||||||
|
SetVersion("")
|
||||||
|
if got := Version(); got != "v1.2.3" {
|
||||||
|
t.Errorf("Version() = %q, want %q", got, "v1.2.3")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,9 +9,14 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"gitea.com/gitea/runner/internal/app/cmd"
|
"gitea.com/gitea/runner/internal/app/cmd"
|
||||||
|
"gitea.com/gitea/runner/internal/pkg/ver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// version is injected at build time with `-ldflags "-X main.version=v1.2.3"`.
|
||||||
|
var version = "dev"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
ver.SetVersion(version)
|
||||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
defer stop()
|
defer stop()
|
||||||
// run the command
|
// run the command
|
||||||
|
|||||||
Reference in New Issue
Block a user