Skip to content

Commit

Permalink
feat: backend implementation of previous and next buttons on commit i…
Browse files Browse the repository at this point in the history
…nfo page (#1435)

Backend Implementation for next and previous commit buttons on Commit
Info Page. See [Frontend
Implementation](#1434)
  • Loading branch information
miguel-crespo-fdc authored Mar 20, 2024
1 parent 746fd0e commit f81f135
Show file tree
Hide file tree
Showing 8 changed files with 318 additions and 26 deletions.
3 changes: 3 additions & 0 deletions infrastructure/scripts/create-testdata/create-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ set -o pipefail

name=${1}
applicationOwnerTeam=${2:-sreteam}
prev=${3:-""}

# 40 is the length of a full git commit hash.
commit_id=$(LC_CTYPE=C tr -dc a-f0-9 </dev/urandom | head -c 40 ; echo '')
authors[0]="urbansky"
Expand Down Expand Up @@ -102,6 +104,7 @@ curl http://localhost:${FRONTEND_PORT}/release \
--form-string "application=$name" \
--form-string "source_commit_id=${commit_id}" \
--form-string "source_author=${author}" \
--form-string "previous_commit_id=${prev}" \
${release_version} \
--form-string "display_version=${displayVersion}" \
--form "source_message=<${commit_message_file}" \
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/v1/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ message CreateReleaseRequest {
string source_message = 8;
string source_repo_url = 9;
string display_version = 10;
string previous_commit_id = 11;
string next_commit_id = 12;
}

message CreateReleaseResponseSuccess {
Expand Down
87 changes: 74 additions & 13 deletions services/cd-service/pkg/repository/transformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,17 @@ import (
)

const (
queueFileName = "queued_version"
yamlParsingError = "# yaml parsing error"
fieldSourceAuthor = "source_author"
fieldSourceMessage = "source_message"
fieldSourceCommitId = "source_commit_id"
fieldDisplayVersion = "display_version"
fieldSourceRepoUrl = "sourceRepoUrl" // urgh, inconsistent
fieldCreatedAt = "created_at"
fieldTeam = "team"
queueFileName = "queued_version"
yamlParsingError = "# yaml parsing error"
fieldSourceAuthor = "source_author"
fieldSourceMessage = "source_message"
fieldSourceCommitId = "source_commit_id"
fieldDisplayVersion = "display_version"
fieldSourceRepoUrl = "sourceRepoUrl" // urgh, inconsistent
fieldCreatedAt = "created_at"
fieldTeam = "team"
fieldNextCommidId = "nextCommit"
fieldPreviousCommitId = "previousCommit"
// number of old releases that will ALWAYS be kept in addition to the ones that are deployed:
keptVersionsOnCleanup = 20
)
Expand Down Expand Up @@ -345,6 +347,8 @@ type CreateApplicationVersion struct {
Team string
DisplayVersion string
WriteCommitData bool
PreviousCommit string
NextCommit string
}

type ctxMarkerGenerateUuid struct{}
Expand Down Expand Up @@ -396,10 +400,18 @@ func (c *CreateApplicationVersion) Transform(
return "", GetCreateReleaseGeneralFailure(err)
}

if !valid.SHA1CommitID(c.SourceCommitId) {
logger.FromContext(ctx).Sugar().Warnf("commit ID is not a valid SHA1 hash, should be exactly 40 characters [0-9a-fA-F] %s\n", c.SourceCommitId)
var checkForInvalidCommitId = func(commitId, helperText string) {
if !valid.SHA1CommitID(commitId) {
logger.FromContext(ctx).
Sugar().
Warnf("%s commit ID is not a valid SHA1 hash, should be exactly 40 characters [0-9a-fA-F] %s\n", commitId, helperText)
}
}

checkForInvalidCommitId(c.SourceCommitId, "Source")
checkForInvalidCommitId(c.PreviousCommit, "Previous")
checkForInvalidCommitId(c.NextCommit, "Next")

configs, err := state.GetEnvironmentConfigs()
if err != nil {
if errors.Is(err, InvalidJson) {
Expand All @@ -414,6 +426,7 @@ func (c *CreateApplicationVersion) Transform(
return "", GetCreateReleaseGeneralFailure(err)
}
}

if c.SourceAuthor != "" {
if err := util.WriteFile(fs, fs.Join(releaseDir, fieldSourceAuthor), []byte(c.SourceAuthor), 0666); err != nil {
return "", GetCreateReleaseGeneralFailure(err)
Expand Down Expand Up @@ -467,7 +480,7 @@ func (c *CreateApplicationVersion) Transform(
gen := getGenerator(ctx)
eventUuid := gen.Generate()
if c.WriteCommitData {
err = writeCommitData(ctx, c.SourceCommitId, c.SourceMessage, c.Application, eventUuid, allEnvsOfThisApp, fs)
err = writeCommitData(ctx, c.SourceCommitId, c.SourceMessage, c.Application, eventUuid, allEnvsOfThisApp, c.PreviousCommit, c.NextCommit, fs)
if err != nil {
return "", GetCreateReleaseGeneralFailure(err)
}
Expand Down Expand Up @@ -533,7 +546,7 @@ func AddGeneratorToContext(ctx context.Context, gen uuid.GenerateUUIDs) context.
return context.WithValue(ctx, ctxMarkerGenerateUuidKey, gen)
}

func writeCommitData(ctx context.Context, sourceCommitId string, sourceMessage string, app string, eventId string, environments []string, fs billy.Filesystem) error {
func writeCommitData(ctx context.Context, sourceCommitId string, sourceMessage string, app string, eventId string, environments []string, previousCommitId string, nextCommitId string, fs billy.Filesystem) error {
if !valid.SHA1CommitID(sourceCommitId) {
return nil
}
Expand All @@ -544,6 +557,18 @@ func writeCommitData(ctx context.Context, sourceCommitId string, sourceMessage s
if err := util.WriteFile(fs, fs.Join(commitDir, ".empty"), make([]byte, 0), 0666); err != nil {
return GetCreateReleaseGeneralFailure(err)
}

if previousCommitId != "" {
if err := writeNextPrevInfo(sourceCommitId, strings.ToLower(previousCommitId), fieldPreviousCommitId, fs); err != nil {
return GetCreateReleaseGeneralFailure(err)
}
}
if nextCommitId != "" {
if err := writeNextPrevInfo(sourceCommitId, strings.ToLower(nextCommitId), fieldNextCommidId, fs); err != nil {
return GetCreateReleaseGeneralFailure(err)
}
}

commitAppDir := commitApplicationDirectory(fs, sourceCommitId, app)
if err := fs.MkdirAll(commitAppDir, 0777); err != nil {
return GetCreateReleaseGeneralFailure(err)
Expand All @@ -554,6 +579,7 @@ func writeCommitData(ctx context.Context, sourceCommitId string, sourceMessage s
if err := util.WriteFile(fs, fs.Join(commitDir, "source_message"), []byte(sourceMessage), 0666); err != nil {
return GetCreateReleaseGeneralFailure(err)
}

if err := util.WriteFile(fs, fs.Join(commitAppDir, ".gitkeep"), make([]byte, 0), 0666); err != nil {
return GetCreateReleaseGeneralFailure(err)
}
Expand All @@ -570,6 +596,41 @@ func writeCommitData(ctx context.Context, sourceCommitId string, sourceMessage s
return nil
}

func writeNextPrevInfo(sourceCommitId string, otherCommitId string, fieldSource string, fs billy.Filesystem) error {

otherCommitId = strings.ToLower(otherCommitId)
sourceCommitDir := commitDirectory(fs, sourceCommitId)

otherCommitDir := commitDirectory(fs, otherCommitId)

if _, err := fs.Stat(otherCommitDir); err != nil {
return err
}

if err := util.WriteFile(fs, fs.Join(sourceCommitDir, fieldSource), []byte(otherCommitId), 0666); err != nil {
return err
}
fieldOther := ""
if otherCommitId != "" {

if fieldSource == fieldPreviousCommitId {
fieldOther = fieldNextCommidId
} else {
fieldOther = fieldPreviousCommitId
}

//This is a workaround. util.WriteFile does NOT truncate file contents, so we simply delete the file before writing.
if err := fs.Remove(fs.Join(otherCommitDir, fieldOther)); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}

if err := util.WriteFile(fs, fs.Join(otherCommitDir, fieldOther), []byte(sourceCommitId), 0666); err != nil {
return err
}
}
return nil
}

func writeEvent(
eventId string,
sourceCommitId string,
Expand Down
192 changes: 192 additions & 0 deletions services/cd-service/pkg/repository/transformer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,198 @@ func TestApplicationDeploymentEvent(t *testing.T) {
})
}
}
func TestNextAndPreviousCommitCreation(t *testing.T) {
type TestCase struct {
Name string
Transformers []Transformer
expectedContent []FileWithContent
}

tcs := []TestCase{
{
Name: "Create a single application Version",
// no need to bother with environments here
Transformers: []Transformer{
&CreateApplicationVersion{
Application: "app",
SourceCommitId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
Manifests: map[string]string{
"staging": "doesn't matter",
},
WriteCommitData: true,
NextCommit: "",
PreviousCommit: "",
},
&CreateApplicationVersion{
Application: "app",
SourceCommitId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
Manifests: map[string]string{
"staging": "doesn't matter",
},
WriteCommitData: true,
NextCommit: "",
PreviousCommit: "",
},
&CreateApplicationVersion{
Application: "app",
SourceCommitId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac",
Manifests: map[string]string{
"staging": "doesn't matter",
},
WriteCommitData: true,
NextCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
PreviousCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
},
expectedContent: []FileWithContent{
{
Path: "commits/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac/nextCommit",
Content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
},
{
Path: "commits/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac/previousCommit",
Content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
},
},
{
Name: "Create a circle of next and prev",
// no need to bother with environments here
Transformers: []Transformer{
&CreateApplicationVersion{
Application: "app",
SourceCommitId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
Manifests: map[string]string{
"staging": "doesn't matter",
},
WriteCommitData: true,
NextCommit: "",
PreviousCommit: "",
},
&CreateApplicationVersion{
Application: "app",
SourceCommitId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
Manifests: map[string]string{
"staging": "doesn't matter",
},
WriteCommitData: true,
NextCommit: "",
PreviousCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
&CreateApplicationVersion{
Application: "app",
SourceCommitId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac",
Manifests: map[string]string{
"staging": "doesn't matter",
},
WriteCommitData: true,
NextCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
PreviousCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
},
},
expectedContent: []FileWithContent{
{
Path: "commits/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/nextCommit",
Content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
},
{
Path: "commits/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/previousCommit",
Content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac",
},
{
Path: "commits/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab/nextCommit",
Content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac",
},
{
Path: "commits/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab/previousCommit",
Content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
{
Path: "commits/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac/nextCommit",
Content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
{
Path: "commits/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac/previousCommit",
Content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
},
},
},
{
Name: "New Release overwrites",
// no need to bother with environments here
Transformers: []Transformer{
&CreateApplicationVersion{
Application: "app",
SourceCommitId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
Manifests: map[string]string{
"staging": "doesn't matter",
},
WriteCommitData: true,
NextCommit: "",
PreviousCommit: "",
},
&CreateApplicationVersion{
Application: "app",
SourceCommitId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
Manifests: map[string]string{
"staging": "doesn't matter",
},
WriteCommitData: true,
NextCommit: "",
PreviousCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
&CreateApplicationVersion{
Application: "app",
SourceCommitId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac",
Manifests: map[string]string{
"staging": "doesn't matter",
},
WriteCommitData: true,
NextCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
PreviousCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
},
expectedContent: []FileWithContent{
{
Path: "commits/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac/nextCommit",
Content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
},
{
Path: "commits/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac/previousCommit",
Content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
{
Path: "commits/aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/nextCommit",
Content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac",
},
},
},
}

for _, tc := range tcs {
t.Run(tc.Name, func(t *testing.T) {
tc := tc
t.Parallel()

fakeGen := testutil.NewIncrementalUUIDGenerator()
ctx := testutil.MakeTestContext()
ctx = AddGeneratorToContext(ctx, fakeGen)

repo := setupRepositoryTest(t)
_, updatedState, _, err := repo.ApplyTransformersInternal(ctx, tc.Transformers...)
if err != nil {
t.Fatalf("encountered error but no error is expected here: %v", err)
}
fs := updatedState.Filesystem

verErr := verifyContent(fs, tc.expectedContent)

if verErr != nil {
t.Fatalf("Error while verifying content of : %v. Filesystem content:\n%s", verErr, strings.Join(listFiles(fs), "\n"))
}
})
}
}

func TestReplacedByEvent(t *testing.T) {
type TestCase struct {
Expand Down
2 changes: 2 additions & 0 deletions services/cd-service/pkg/service/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ func (d *BatchServer) processAction(
SourceAuthor: in.SourceAuthor,
SourceMessage: in.SourceMessage,
SourceRepoUrl: in.SourceRepoUrl,
PreviousCommit: in.PreviousCommitId,
NextCommit: in.NextCommitId,
Team: in.Team,
DisplayVersion: in.DisplayVersion,
Authentication: repository.Authentication{RBACConfig: d.RBACConfig},
Expand Down
Loading

0 comments on commit f81f135

Please sign in to comment.