Skip to content

feat: add support for template version messages in api and cli #8336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 11, 2023
5 changes: 5 additions & 0 deletions cli/templatecreate.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ func (r *RootCmd) templateCreate() *clibase.Cmd {
return xerrors.Errorf("check for lockfile: %w", err)
}

message := uploadFlags.templateMessage(inv)

// Confirm upload of the directory.
resp, err := uploadFlags.upload(inv, client)
if err != nil {
Expand All @@ -104,6 +106,7 @@ func (r *RootCmd) templateCreate() *clibase.Cmd {
}

job, err := createValidTemplateVersion(inv, createValidTemplateVersionArgs{
Message: message,
Client: client,
Organization: organization,
Provisioner: database.ProvisionerType(provisioner),
Expand Down Expand Up @@ -205,6 +208,7 @@ func (r *RootCmd) templateCreate() *clibase.Cmd {

type createValidTemplateVersionArgs struct {
Name string
Message string
Client *codersdk.Client
Organization codersdk.Organization
Provisioner database.ProvisionerType
Expand Down Expand Up @@ -238,6 +242,7 @@ func createValidTemplateVersion(inv *clibase.Invocation, args createValidTemplat

req := codersdk.CreateTemplateVersionRequest{
Name: args.Name,
Message: args.Message,
StorageMethod: codersdk.ProvisionerStorageMethodFile,
FileID: args.FileID,
Provisioner: codersdk.ProvisionerType(args.Provisioner),
Expand Down
19 changes: 19 additions & 0 deletions cli/templatepush.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
type templateUploadFlags struct {
directory string
ignoreLockfile bool
message string
}

func (pf *templateUploadFlags) options() []clibase.Option {
Expand All @@ -35,6 +36,11 @@ func (pf *templateUploadFlags) options() []clibase.Option {
Description: "Ignore warnings about not having a .terraform.lock.hcl file present in the template.",
Default: "false",
Value: clibase.BoolOf(&pf.ignoreLockfile),
}, {
Flag: "message",
FlagShorthand: "m",
Description: "Specify a message describing the changes in this version of the template. Messages longer than 72 characters will be displayed as truncated.",
Value: clibase.StringOf(&pf.message),
}}
}

Expand Down Expand Up @@ -110,6 +116,16 @@ func (pf *templateUploadFlags) checkForLockfile(inv *clibase.Invocation) error {
return nil
}

func (pf *templateUploadFlags) templateMessage(inv *clibase.Invocation) string {
if len(pf.message) > 72 {
cliui.Warn(inv.Stdout, "Template message is longer than 72 characters, it will be displayed as truncated.")
}
if pf.message != "" {
return pf.message
}
return "Uploaded from the CLI"
}

func (pf *templateUploadFlags) templateName(args []string) (string, error) {
if pf.stdin() {
// Can't infer name from directory if none provided.
Expand Down Expand Up @@ -174,6 +190,8 @@ func (r *RootCmd) templatePush() *clibase.Cmd {
return xerrors.Errorf("check for lockfile: %w", err)
}

message := uploadFlags.templateMessage(inv)

resp, err := uploadFlags.upload(inv, client)
if err != nil {
return err
Expand All @@ -186,6 +204,7 @@ func (r *RootCmd) templatePush() *clibase.Cmd {

job, err := createValidTemplateVersion(inv, createValidTemplateVersionArgs{
Name: versionName,
Message: message,
Client: client,
Organization: organization,
Provisioner: database.ProvisionerType(provisioner),
Expand Down
79 changes: 79 additions & 0 deletions cli/templatepush_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -70,6 +71,84 @@ func TestTemplatePush(t *testing.T) {
require.Equal(t, "example", templateVersions[1].Name)
})

t.Run("Message less than or equal to 72 chars", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)

template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
Parse: echo.ParseComplete,
ProvisionApply: echo.ProvisionComplete,
})

wantMessage := strings.Repeat("a", 72)

inv, root := clitest.New(t, "templates", "push", template.Name, "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--name", "example", "--message", wantMessage, "--yes")
clitest.SetupConfig(t, client, root)
pty := ptytest.New(t).Attach(inv)

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()

inv = inv.WithContext(ctx)
w := clitest.StartWithWaiter(t, inv)

pty.ExpectNoMatchBefore(ctx, "Template message is longer than 72 characters", "Updated version at")

w.RequireSuccess()

// Assert that the template version changed.
templateVersions, err := client.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
TemplateID: template.ID,
})
require.NoError(t, err)
assert.Len(t, templateVersions, 2)
assert.NotEqual(t, template.ActiveVersionID, templateVersions[1].ID)
require.Equal(t, wantMessage, templateVersions[1].Message)
})

t.Run("Message too long, warn but continue", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)

template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
Parse: echo.ParseComplete,
ProvisionApply: echo.ProvisionComplete,
})

wantMessage := strings.Repeat("a", 73)

inv, root := clitest.New(t, "templates", "push", template.Name, "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--name", "example", "--message", wantMessage, "--yes")
clitest.SetupConfig(t, client, root)
pty := ptytest.New(t).Attach(inv)

ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()

inv = inv.WithContext(ctx)
w := clitest.StartWithWaiter(t, inv)

pty.ExpectMatchContext(ctx, "Template message is longer than 72 characters")

w.RequireSuccess()

// Assert that the template version changed.
templateVersions, err := client.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
TemplateID: template.ID,
})
require.NoError(t, err)
assert.Len(t, templateVersions, 2)
assert.NotEqual(t, template.ActiveVersionID, templateVersions[1].ID)
require.Equal(t, wantMessage, templateVersions[1].Message)
})

t.Run("NoLockfile", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
Expand Down
5 changes: 5 additions & 0 deletions cli/testdata/coder_templates_create_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ Create a template from the current directory or as specified by flag
Specify an inactivity TTL for workspaces created from this template.
This licensed feature's default is 0h (off).

-m, --message string
Specify a message describing the changes in this version of the
template. Messages longer than 72 characters will be displayed as
truncated.

--private bool
Disable the default behavior of granting template access to the
'everyone' group. The template permissions must be updated to allow
Expand Down
5 changes: 5 additions & 0 deletions cli/testdata/coder_templates_push_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ Push a new template version from the current directory or as specified by flag
Ignore warnings about not having a .terraform.lock.hcl file present in
the template.

-m, --message string
Specify a message describing the changes in this version of the
template. Messages longer than 72 characters will be displayed as
truncated.

--name string
Specify a name for the new template version. It will be automatically
generated if not provided.
Expand Down
6 changes: 6 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions coderd/database/dbfake/dbfake.go
Original file line number Diff line number Diff line change
Expand Up @@ -3932,6 +3932,7 @@ func (q *fakeQuerier) InsertTemplateVersion(_ context.Context, arg database.Inse
CreatedAt: arg.CreatedAt,
UpdatedAt: arg.UpdatedAt,
Name: arg.Name,
Message: arg.Message,
Readme: arg.Readme,
JobID: arg.JobID,
CreatedBy: arg.CreatedBy,
Expand Down
1 change: 1 addition & 0 deletions coderd/database/dbgen/dbgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ func TemplateVersion(t testing.TB, db database.Store, orig database.TemplateVers
CreatedAt: takeFirst(orig.CreatedAt, database.Now()),
UpdatedAt: takeFirst(orig.UpdatedAt, database.Now()),
Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)),
Message: orig.Message,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: what about testing? do we need a random message generated for unit tests or maybe benchmarks in the future?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a valid concern, although I didn't do it because I'm sure how we'd set an empty message if we use takeFirst here on a randomly generated one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mafredri the empty takeFirst is such an annoying problem 😢

Readme: takeFirst(orig.Readme, namesgenerator.GetRandomName(1)),
JobID: takeFirst(orig.JobID, uuid.New()),
CreatedBy: takeFirst(orig.CreatedBy, uuid.New()),
Expand Down
5 changes: 4 additions & 1 deletion coderd/database/dump.sql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE template_versions DROP COLUMN message;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE template_versions ADD COLUMN message text NOT NULL DEFAULT '';

COMMENT ON COLUMN template_versions.message IS 'Message describing the changes in this version of the template, similar to a Git commit message. Like a commit message, this should be a short, high-level description of the changes in this version of the template. This message is immutable and should not be updated after the fact.';
2 changes: 2 additions & 0 deletions coderd/database/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading