diff --git a/docs/docs/20-usage/50-environment.md b/docs/docs/20-usage/50-environment.md index 794348de22a..625f93dad38 100644 --- a/docs/docs/20-usage/50-environment.md +++ b/docs/docs/20-usage/50-environment.md @@ -76,6 +76,7 @@ This is the reference list of all environment variables available to your pipeli | `CI_COMMIT_PULL_REQUEST_LABELS` | labels assigned to pull request (set only for pull request events) | `server` | | `CI_COMMIT_PULL_REQUEST_MILESTONE` | milestone assigned to pull request (set only for `pull_request` and `pull_request_closed` events) | `summer-sprint` | | `CI_COMMIT_MESSAGE` | commit message | `Initial commit` | +| `CI_COMMIT_TIMESTAMP` | commit timestamp in RFC 3339 format | `2024-08-02T16:51:59Z` | | `CI_COMMIT_AUTHOR` | commit author username | `john-doe` | | `CI_COMMIT_AUTHOR_EMAIL` | commit author email address | `john-doe@example.com` | | `CI_COMMIT_PRERELEASE` | release is a pre-release (empty if event is not `release`) | `false` | @@ -112,6 +113,7 @@ This is the reference list of all environment variables available to your pipeli | `CI_PREV_COMMIT_TARGET_BRANCH` | previous commit target branch (set only for pull request events) | `main` | | `CI_PREV_COMMIT_URL` | previous commit link in forge | `https://git.example.com/john-doe/my-repo/commit/15784117e4e103f36cba75a9e29da48046eb82c4` | | `CI_PREV_COMMIT_MESSAGE` | previous commit message | `test` | +| `CI_PREV_COMMIT_TIMESTAMP` | previous commit timestamp in RFC 3339 format | `2024-08-02T14:49:33Z` | | `CI_PREV_COMMIT_AUTHOR` | previous commit author username | `john-doe` | | `CI_PREV_COMMIT_AUTHOR_EMAIL` | previous commit author email address | `john-doe@example.com` | | | **Previous pipeline** | | diff --git a/pipeline/frontend/metadata/environment.go b/pipeline/frontend/metadata/environment.go index 205d7a87bbe..9d6d523748d 100644 --- a/pipeline/frontend/metadata/environment.go +++ b/pipeline/frontend/metadata/environment.go @@ -21,6 +21,7 @@ import ( "regexp" "strconv" "strings" + "time" "github.com/rs/zerolog/log" ) @@ -97,6 +98,7 @@ func (m *Metadata) Environ() map[string]string { setNonEmptyEnvVar(params, "CI_COMMIT_REFSPEC", commit.Refspec) setNonEmptyEnvVar(params, "CI_COMMIT_MESSAGE", commit.Message) setNonEmptyEnvVar(params, "CI_COMMIT_BRANCH", commit.Branch) + setNonEmptyEnvVar(params, "CI_COMMIT_TIMESTAMP", formatUnixTimestamp(commit.Timestamp)) setNonEmptyEnvVar(params, "CI_COMMIT_AUTHOR", commit.Author.Name) setNonEmptyEnvVar(params, "CI_COMMIT_AUTHOR_EMAIL", commit.Author.Email) if p, f := strings.CutPrefix(pipeline.Commit.Ref, "refs/tags/"); f { @@ -150,6 +152,7 @@ func (m *Metadata) Environ() map[string]string { setNonEmptyEnvVar(params, "CI_PREV_COMMIT_REFSPEC", prevCommit.Refspec) setNonEmptyEnvVar(params, "CI_PREV_COMMIT_MESSAGE", prevCommit.Message) setNonEmptyEnvVar(params, "CI_PREV_COMMIT_BRANCH", prevCommit.Branch) + setNonEmptyEnvVar(params, "CI_PREV_COMMIT_TIMESTAMP", formatUnixTimestamp(prevCommit.Timestamp)) setNonEmptyEnvVar(params, "CI_PREV_COMMIT_AUTHOR", prevCommit.Author.Name) setNonEmptyEnvVar(params, "CI_PREV_COMMIT_AUTHOR_EMAIL", prevCommit.Author.Email) if prevPipeline.Event.IsPull() { @@ -188,6 +191,14 @@ func getSourceTargetBranches(refspec string) (string, string) { return sourceBranch, targetBranch } +func formatUnixTimestamp(timestamp int64) string { + if timestamp <= 0 { + return "" + } + + return time.Unix(timestamp, 0).UTC().Format(time.RFC3339) +} + func setNonEmptyEnvVar(env map[string]string, key, value string) { if len(value) > 0 { env[key] = value diff --git a/pipeline/frontend/metadata/environment_test.go b/pipeline/frontend/metadata/environment_test.go index 154aa76a5ed..ecc6f9c2e3f 100644 --- a/pipeline/frontend/metadata/environment_test.go +++ b/pipeline/frontend/metadata/environment_test.go @@ -27,13 +27,15 @@ func TestEnviron(t *testing.T) { Event: EventRelease, Commit: Commit{ Ref: "refs/tags/v1.2.3", + Timestamp: 1722617519, IsPrerelease: true, }, }, Prev: Pipeline{ Event: EventPullMetadata, Commit: Commit{ - Refspec: "branch-a:branch-b", + Refspec: "branch-a:branch-b", + Timestamp: 1722610173, }, }, } @@ -47,6 +49,8 @@ func TestEnviron(t *testing.T) { assert.Equal(t, "branch-b", envs["CI_PREV_COMMIT_TARGET_BRANCH"]) assert.Equal(t, "[]", envs["CI_PIPELINE_FILES"]) assert.Equal(t, "v1.2.3", envs["CI_COMMIT_TAG"]) + assert.Equal(t, "2024-08-02T16:51:59Z", envs["CI_COMMIT_TIMESTAMP"]) + assert.Equal(t, "2024-08-02T14:49:33Z", envs["CI_PREV_COMMIT_TIMESTAMP"]) m = Metadata{ Sys: System{Name: "wp"}, diff --git a/pipeline/frontend/metadata/types.go b/pipeline/frontend/metadata/types.go index 43fba4716d9..5ef7598e4df 100644 --- a/pipeline/frontend/metadata/types.go +++ b/pipeline/frontend/metadata/types.go @@ -69,6 +69,7 @@ type ( Refspec string `json:"refspec,omitempty"` Branch string `json:"branch,omitempty"` Message string `json:"message,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` Author Author `json:"author"` ChangedFiles []string `json:"changed_files,omitempty"` PullRequestLabels []string `json:"labels,omitempty"` diff --git a/server/pipeline/metadata/metadata.go b/server/pipeline/metadata/metadata.go index 6d9764b2857..172a984160d 100644 --- a/server/pipeline/metadata/metadata.go +++ b/server/pipeline/metadata/metadata.go @@ -140,11 +140,12 @@ func metadataPipelineFromModelPipeline(pipeline *model.Pipeline, includeParent b DeployTo: pipeline.DeployTo, DeployTask: pipeline.DeployTask, Commit: metadata.Commit{ - Sha: pipeline.Commit, - Ref: pipeline.Ref, - Refspec: pipeline.Refspec, - Branch: pipeline.Branch, - Message: pipeline.Message, + Sha: pipeline.Commit, + Ref: pipeline.Ref, + Refspec: pipeline.Refspec, + Branch: pipeline.Branch, + Message: pipeline.Message, + Timestamp: pipeline.Timestamp, Author: metadata.Author{ Name: pipeline.Author, Email: pipeline.Email, diff --git a/server/pipeline/metadata/metadata_test.go b/server/pipeline/metadata/metadata_test.go index 6675b38c2ff..b58501abe9c 100644 --- a/server/pipeline/metadata/metadata_test.go +++ b/server/pipeline/metadata/metadata_test.go @@ -59,8 +59,8 @@ func TestGetWorkflowMetadata(t *testing.T) { name: "Test with forge", forge: forge, repo: &model.Repo{FullName: "testUser/testRepo", ForgeURL: "https://gitea.com/testUser/testRepo", Clone: "https://gitea.com/testUser/testRepo.git", CloneSSH: "git@gitea.com:testUser/testRepo.git", Branch: "main", IsSCMPrivate: true}, - pipeline: &model.Pipeline{Number: 3, ChangedFiles: []string{"test.go", "markdown file.md"}}, - prev: &model.Pipeline{Number: 2}, + pipeline: &model.Pipeline{Number: 3, Timestamp: 1722617519, ChangedFiles: []string{"test.go", "markdown file.md"}}, + prev: &model.Pipeline{Number: 2, Timestamp: 1722610173}, workflow: &builder.Workflow{Name: "hello"}, sysURL: "https://example.com", expectedMetadata: metadata.Metadata{ @@ -69,16 +69,18 @@ func TestGetWorkflowMetadata(t *testing.T) { Repo: metadata.Repo{Owner: "testUser", Name: "testRepo", ForgeURL: "https://gitea.com/testUser/testRepo", CloneURL: "https://gitea.com/testUser/testRepo.git", CloneSSHURL: "git@gitea.com:testUser/testRepo.git", Branch: "main", Private: true}, Curr: metadata.Pipeline{ Number: 3, - Commit: metadata.Commit{ChangedFiles: []string{"test.go", "markdown file.md"}}, + Commit: metadata.Commit{Timestamp: 1722617519, ChangedFiles: []string{"test.go", "markdown file.md"}}, }, - Prev: metadata.Pipeline{Number: 2}, + Prev: metadata.Pipeline{Number: 2, Commit: metadata.Commit{Timestamp: 1722610173}}, Workflow: metadata.Workflow{Name: "hello"}, }, expectedEnviron: map[string]string{ "CI": "woodpecker", "CI_FORGE_TYPE": "gitea", "CI_FORGE_URL": "https://gitea.com", + "CI_COMMIT_TIMESTAMP": "2024-08-02T16:51:59Z", "CI_PIPELINE_CREATED": "0", "CI_PIPELINE_FILES": `["test.go","markdown file.md"]`, "CI_PIPELINE_NUMBER": "3", "CI_PIPELINE_PARENT": "0", "CI_PIPELINE_STARTED": "0", "CI_PIPELINE_URL": "https://example.com/repos/0/pipeline/3", + "CI_PREV_COMMIT_TIMESTAMP": "2024-08-02T14:49:33Z", "CI_PREV_PIPELINE_CREATED": "0", "CI_PREV_PIPELINE_FINISHED": "0", "CI_PREV_PIPELINE_NUMBER": "2", "CI_PREV_PIPELINE_PARENT": "0", "CI_PREV_PIPELINE_STARTED": "0", "CI_PREV_PIPELINE_URL": "https://example.com/repos/0/pipeline/2", "CI_REPO": "testUser/testRepo", "CI_REPO_CLONE_URL": "https://gitea.com/testUser/testRepo.git", "CI_REPO_CLONE_SSH_URL": "git@gitea.com:testUser/testRepo.git",