Skip to content

Commit 75f62dc

Browse files
authored
feat: add support for template version messages in api and cli (#8336)
1 parent b4a7fe3 commit 75f62dc

27 files changed

+245
-14
lines changed

cli/templatecreate.go

+5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ func (r *RootCmd) templateCreate() *clibase.Cmd {
9292
return xerrors.Errorf("check for lockfile: %w", err)
9393
}
9494

95+
message := uploadFlags.templateMessage(inv)
96+
9597
// Confirm upload of the directory.
9698
resp, err := uploadFlags.upload(inv, client)
9799
if err != nil {
@@ -104,6 +106,7 @@ func (r *RootCmd) templateCreate() *clibase.Cmd {
104106
}
105107

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

206209
type createValidTemplateVersionArgs struct {
207210
Name string
211+
Message string
208212
Client *codersdk.Client
209213
Organization codersdk.Organization
210214
Provisioner database.ProvisionerType
@@ -238,6 +242,7 @@ func createValidTemplateVersion(inv *clibase.Invocation, args createValidTemplat
238242

239243
req := codersdk.CreateTemplateVersionRequest{
240244
Name: args.Name,
245+
Message: args.Message,
241246
StorageMethod: codersdk.ProvisionerStorageMethodFile,
242247
FileID: args.FileID,
243248
Provisioner: codersdk.ProvisionerType(args.Provisioner),

cli/templatepush.go

+24
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77
"path/filepath"
8+
"strings"
89
"time"
910

1011
"github.com/briandowns/spinner"
@@ -21,6 +22,7 @@ import (
2122
type templateUploadFlags struct {
2223
directory string
2324
ignoreLockfile bool
25+
message string
2426
}
2527

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

@@ -110,6 +117,20 @@ func (pf *templateUploadFlags) checkForLockfile(inv *clibase.Invocation) error {
110117
return nil
111118
}
112119

120+
func (pf *templateUploadFlags) templateMessage(inv *clibase.Invocation) string {
121+
title := strings.SplitN(pf.message, "\n", 2)[0]
122+
if len(title) > 72 {
123+
cliui.Warn(inv.Stdout, "Template message is longer than 72 characters, it will be displayed as truncated.")
124+
}
125+
if title != pf.message {
126+
cliui.Warn(inv.Stdout, "Template message contains newlines, only the first line will be displayed.")
127+
}
128+
if pf.message != "" {
129+
return pf.message
130+
}
131+
return "Uploaded from the CLI"
132+
}
133+
113134
func (pf *templateUploadFlags) templateName(args []string) (string, error) {
114135
if pf.stdin() {
115136
// Can't infer name from directory if none provided.
@@ -174,6 +195,8 @@ func (r *RootCmd) templatePush() *clibase.Cmd {
174195
return xerrors.Errorf("check for lockfile: %w", err)
175196
}
176197

198+
message := uploadFlags.templateMessage(inv)
199+
177200
resp, err := uploadFlags.upload(inv, client)
178201
if err != nil {
179202
return err
@@ -186,6 +209,7 @@ func (r *RootCmd) templatePush() *clibase.Cmd {
186209

187210
job, err := createValidTemplateVersion(inv, createValidTemplateVersionArgs{
188211
Name: versionName,
212+
Message: message,
189213
Client: client,
190214
Organization: organization,
191215
Provisioner: database.ProvisionerType(provisioner),

cli/templatepush_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77
"path/filepath"
88
"runtime"
9+
"strings"
910
"testing"
1011

1112
"github.com/stretchr/testify/assert"
@@ -70,6 +71,90 @@ func TestTemplatePush(t *testing.T) {
7071
require.Equal(t, "example", templateVersions[1].Name)
7172
})
7273

74+
t.Run("Message less than or equal to 72 chars", func(t *testing.T) {
75+
t.Parallel()
76+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
77+
user := coderdtest.CreateFirstUser(t, client)
78+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
79+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
80+
81+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
82+
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
83+
Parse: echo.ParseComplete,
84+
ProvisionApply: echo.ProvisionComplete,
85+
})
86+
87+
wantMessage := strings.Repeat("a", 72)
88+
89+
inv, root := clitest.New(t, "templates", "push", template.Name, "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--name", "example", "--message", wantMessage, "--yes")
90+
clitest.SetupConfig(t, client, root)
91+
pty := ptytest.New(t).Attach(inv)
92+
93+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
94+
defer cancel()
95+
96+
inv = inv.WithContext(ctx)
97+
w := clitest.StartWithWaiter(t, inv)
98+
99+
pty.ExpectNoMatchBefore(ctx, "Template message is longer than 72 characters", "Updated version at")
100+
101+
w.RequireSuccess()
102+
103+
// Assert that the template version changed.
104+
templateVersions, err := client.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
105+
TemplateID: template.ID,
106+
})
107+
require.NoError(t, err)
108+
assert.Len(t, templateVersions, 2)
109+
assert.NotEqual(t, template.ActiveVersionID, templateVersions[1].ID)
110+
require.Equal(t, wantMessage, templateVersions[1].Message)
111+
})
112+
113+
t.Run("Message too long, warn but continue", func(t *testing.T) {
114+
t.Parallel()
115+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
116+
user := coderdtest.CreateFirstUser(t, client)
117+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
118+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
119+
120+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
121+
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
122+
Parse: echo.ParseComplete,
123+
ProvisionApply: echo.ProvisionComplete,
124+
})
125+
126+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
127+
defer cancel()
128+
129+
for i, tt := range []struct {
130+
wantMessage string
131+
wantMatch string
132+
}{
133+
{wantMessage: strings.Repeat("a", 73), wantMatch: "Template message is longer than 72 characters"},
134+
{wantMessage: "This is my title\n\nAnd this is my body.", wantMatch: "Template message contains newlines"},
135+
} {
136+
inv, root := clitest.New(t, "templates", "push", template.Name, "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--message", tt.wantMessage, "--yes")
137+
clitest.SetupConfig(t, client, root)
138+
pty := ptytest.New(t).Attach(inv)
139+
140+
inv = inv.WithContext(ctx)
141+
w := clitest.StartWithWaiter(t, inv)
142+
143+
pty.ExpectMatchContext(ctx, tt.wantMatch)
144+
145+
w.RequireSuccess()
146+
147+
// Assert that the template version changed.
148+
templateVersions, err := client.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
149+
TemplateID: template.ID,
150+
})
151+
require.NoError(t, err)
152+
assert.Len(t, templateVersions, 2+i)
153+
assert.NotEqual(t, template.ActiveVersionID, templateVersions[1+i].ID)
154+
require.Equal(t, tt.wantMessage, templateVersions[1+i].Message)
155+
}
156+
})
157+
73158
t.Run("NoLockfile", func(t *testing.T) {
74159
t.Parallel()
75160
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})

cli/testdata/coder_templates_create_--help.golden

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ Create a template from the current directory or as specified by flag
2121
Specify an inactivity TTL for workspaces created from this template.
2222
This licensed feature's default is 0h (off).
2323

24+
-m, --message string
25+
Specify a message describing the changes in this version of the
26+
template. Messages longer than 72 characters will be displayed as
27+
truncated.
28+
2429
--private bool
2530
Disable the default behavior of granting template access to the
2631
'everyone' group. The template permissions must be updated to allow

cli/testdata/coder_templates_push_--help.golden

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ Push a new template version from the current directory or as specified by flag
1717
Ignore warnings about not having a .terraform.lock.hcl file present in
1818
the template.
1919

20+
-m, --message string
21+
Specify a message describing the changes in this version of the
22+
template. Messages longer than 72 characters will be displayed as
23+
truncated.
24+
2025
--name string
2126
Specify a name for the new template version. It will be automatically
2227
generated if not provided.

coderd/apidoc/docs.go

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbfake/dbfake.go

+5
Original file line numberDiff line numberDiff line change
@@ -3921,6 +3921,10 @@ func (q *fakeQuerier) InsertTemplateVersion(_ context.Context, arg database.Inse
39213921
return database.TemplateVersion{}, err
39223922
}
39233923

3924+
if len(arg.Message) > 1048576 {
3925+
return database.TemplateVersion{}, xerrors.New("message too long")
3926+
}
3927+
39243928
q.mutex.Lock()
39253929
defer q.mutex.Unlock()
39263930

@@ -3932,6 +3936,7 @@ func (q *fakeQuerier) InsertTemplateVersion(_ context.Context, arg database.Inse
39323936
CreatedAt: arg.CreatedAt,
39333937
UpdatedAt: arg.UpdatedAt,
39343938
Name: arg.Name,
3939+
Message: arg.Message,
39353940
Readme: arg.Readme,
39363941
JobID: arg.JobID,
39373942
CreatedBy: arg.CreatedBy,

coderd/database/dbgen/dbgen.go

+1
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ func TemplateVersion(t testing.TB, db database.Store, orig database.TemplateVers
477477
CreatedAt: takeFirst(orig.CreatedAt, database.Now()),
478478
UpdatedAt: takeFirst(orig.UpdatedAt, database.Now()),
479479
Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)),
480+
Message: orig.Message,
480481
Readme: takeFirst(orig.Readme, namesgenerator.GetRandomName(1)),
481482
JobID: takeFirst(orig.JobID, uuid.New()),
482483
CreatedBy: takeFirst(orig.CreatedBy, uuid.New()),

coderd/database/dump.sql

+4-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE template_versions DROP COLUMN message;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ALTER TABLE template_versions ADD COLUMN message varchar(1048576) NOT NULL DEFAULT '';
2+
3+
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.';

coderd/database/models.go

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)