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

This commit is contained in:
Mathias Fredriksson 2023-07-11 13:11:08 +03:00 committed by GitHub
parent b4a7fe3221
commit 75f62dc39d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 245 additions and 14 deletions

View File

@ -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 {
@ -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),
@ -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
@ -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),

View File

@ -5,6 +5,7 @@ import (
"fmt"
"io"
"path/filepath"
"strings"
"time"
"github.com/briandowns/spinner"
@ -21,6 +22,7 @@ import (
type templateUploadFlags struct {
directory string
ignoreLockfile bool
message string
}
func (pf *templateUploadFlags) options() []clibase.Option {
@ -35,6 +37,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),
}}
}
@ -110,6 +117,20 @@ func (pf *templateUploadFlags) checkForLockfile(inv *clibase.Invocation) error {
return nil
}
func (pf *templateUploadFlags) templateMessage(inv *clibase.Invocation) string {
title := strings.SplitN(pf.message, "\n", 2)[0]
if len(title) > 72 {
cliui.Warn(inv.Stdout, "Template message is longer than 72 characters, it will be displayed as truncated.")
}
if title != pf.message {
cliui.Warn(inv.Stdout, "Template message contains newlines, only the first line will be displayed.")
}
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.
@ -174,6 +195,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
@ -186,6 +209,7 @@ func (r *RootCmd) templatePush() *clibase.Cmd {
job, err := createValidTemplateVersion(inv, createValidTemplateVersionArgs{
Name: versionName,
Message: message,
Client: client,
Organization: organization,
Provisioner: database.ProvisionerType(provisioner),

View File

@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/stretchr/testify/assert"
@ -70,6 +71,90 @@ 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,
})
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
for i, tt := range []struct {
wantMessage string
wantMatch string
}{
{wantMessage: strings.Repeat("a", 73), wantMatch: "Template message is longer than 72 characters"},
{wantMessage: "This is my title\n\nAnd this is my body.", wantMatch: "Template message contains newlines"},
} {
inv, root := clitest.New(t, "templates", "push", template.Name, "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--message", tt.wantMessage, "--yes")
clitest.SetupConfig(t, client, root)
pty := ptytest.New(t).Attach(inv)
inv = inv.WithContext(ctx)
w := clitest.StartWithWaiter(t, inv)
pty.ExpectMatchContext(ctx, tt.wantMatch)
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+i)
assert.NotEqual(t, template.ActiveVersionID, templateVersions[1+i].ID)
require.Equal(t, tt.wantMessage, templateVersions[1+i].Message)
}
})
t.Run("NoLockfile", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})

View File

@ -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

View File

@ -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.

6
coderd/apidoc/docs.go generated
View File

@ -6989,6 +6989,9 @@ const docTemplate = `{
"type": "string",
"format": "uuid"
},
"message": {
"type": "string"
},
"name": {
"type": "string"
},
@ -9081,6 +9084,9 @@ const docTemplate = `{
"job": {
"$ref": "#/definitions/codersdk.ProvisionerJob"
},
"message": {
"type": "string"
},
"name": {
"type": "string"
},

View File

@ -6229,6 +6229,9 @@
"type": "string",
"format": "uuid"
},
"message": {
"type": "string"
},
"name": {
"type": "string"
},
@ -8197,6 +8200,9 @@
"job": {
"$ref": "#/definitions/codersdk.ProvisionerJob"
},
"message": {
"type": "string"
},
"name": {
"type": "string"
},

View File

@ -3921,6 +3921,10 @@ func (q *fakeQuerier) InsertTemplateVersion(_ context.Context, arg database.Inse
return database.TemplateVersion{}, err
}
if len(arg.Message) > 1048576 {
return database.TemplateVersion{}, xerrors.New("message too long")
}
q.mutex.Lock()
defer q.mutex.Unlock()
@ -3932,6 +3936,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,

View File

@ -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,
Readme: takeFirst(orig.Readme, namesgenerator.GetRandomName(1)),
JobID: takeFirst(orig.JobID, uuid.New()),
CreatedBy: takeFirst(orig.CreatedBy, uuid.New()),

View File

@ -536,11 +536,14 @@ CREATE TABLE template_versions (
readme character varying(1048576) NOT NULL,
job_id uuid NOT NULL,
created_by uuid NOT NULL,
git_auth_providers text[]
git_auth_providers text[],
message character varying(1048576) DEFAULT ''::character varying NOT NULL
);
COMMENT ON COLUMN template_versions.git_auth_providers IS 'IDs of Git auth providers for a specific template version';
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.';
CREATE TABLE templates (
id uuid NOT NULL,
created_at timestamp with time zone NOT NULL,

View File

@ -0,0 +1 @@
ALTER TABLE template_versions DROP COLUMN message;

View File

@ -0,0 +1,3 @@
ALTER TABLE template_versions ADD COLUMN message varchar(1048576) 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.';

View File

@ -1609,6 +1609,8 @@ type TemplateVersion struct {
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
// IDs of Git auth providers for a specific template version
GitAuthProviders []string `db:"git_auth_providers" json:"git_auth_providers"`
// 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.
Message string `db:"message" json:"message"`
}
type TemplateVersionParameter struct {

View File

@ -4326,7 +4326,7 @@ func (q *sqlQuerier) InsertTemplateVersionParameter(ctx context.Context, arg Ins
const getPreviousTemplateVersion = `-- name: GetPreviousTemplateVersion :one
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers, message
FROM
template_versions
WHERE
@ -4361,13 +4361,14 @@ func (q *sqlQuerier) GetPreviousTemplateVersion(ctx context.Context, arg GetPrev
&i.JobID,
&i.CreatedBy,
pq.Array(&i.GitAuthProviders),
&i.Message,
)
return i, err
}
const getTemplateVersionByID = `-- name: GetTemplateVersionByID :one
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers, message
FROM
template_versions
WHERE
@ -4388,13 +4389,14 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) (
&i.JobID,
&i.CreatedBy,
pq.Array(&i.GitAuthProviders),
&i.Message,
)
return i, err
}
const getTemplateVersionByJobID = `-- name: GetTemplateVersionByJobID :one
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers, message
FROM
template_versions
WHERE
@ -4415,13 +4417,14 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U
&i.JobID,
&i.CreatedBy,
pq.Array(&i.GitAuthProviders),
&i.Message,
)
return i, err
}
const getTemplateVersionByTemplateIDAndName = `-- name: GetTemplateVersionByTemplateIDAndName :one
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers, message
FROM
template_versions
WHERE
@ -4448,13 +4451,14 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context,
&i.JobID,
&i.CreatedBy,
pq.Array(&i.GitAuthProviders),
&i.Message,
)
return i, err
}
const getTemplateVersionsByIDs = `-- name: GetTemplateVersionsByIDs :many
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers, message
FROM
template_versions
WHERE
@ -4481,6 +4485,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU
&i.JobID,
&i.CreatedBy,
pq.Array(&i.GitAuthProviders),
&i.Message,
); err != nil {
return nil, err
}
@ -4497,7 +4502,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU
const getTemplateVersionsByTemplateID = `-- name: GetTemplateVersionsByTemplateID :many
SELECT
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers
id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers, message
FROM
template_versions
WHERE
@ -4562,6 +4567,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge
&i.JobID,
&i.CreatedBy,
pq.Array(&i.GitAuthProviders),
&i.Message,
); err != nil {
return nil, err
}
@ -4577,7 +4583,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge
}
const getTemplateVersionsCreatedAfter = `-- name: GetTemplateVersionsCreatedAfter :many
SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers FROM template_versions WHERE created_at > $1
SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers, message FROM template_versions WHERE created_at > $1
`
func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt time.Time) ([]TemplateVersion, error) {
@ -4600,6 +4606,7 @@ func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, create
&i.JobID,
&i.CreatedBy,
pq.Array(&i.GitAuthProviders),
&i.Message,
); err != nil {
return nil, err
}
@ -4623,12 +4630,13 @@ INSERT INTO
created_at,
updated_at,
"name",
message,
readme,
job_id,
created_by
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers, message
`
type InsertTemplateVersionParams struct {
@ -4638,6 +4646,7 @@ type InsertTemplateVersionParams struct {
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
Message string `db:"message" json:"message"`
Readme string `db:"readme" json:"readme"`
JobID uuid.UUID `db:"job_id" json:"job_id"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
@ -4651,6 +4660,7 @@ func (q *sqlQuerier) InsertTemplateVersion(ctx context.Context, arg InsertTempla
arg.CreatedAt,
arg.UpdatedAt,
arg.Name,
arg.Message,
arg.Readme,
arg.JobID,
arg.CreatedBy,
@ -4667,6 +4677,7 @@ func (q *sqlQuerier) InsertTemplateVersion(ctx context.Context, arg InsertTempla
&i.JobID,
&i.CreatedBy,
pq.Array(&i.GitAuthProviders),
&i.Message,
)
return i, err
}
@ -4679,7 +4690,7 @@ SET
updated_at = $3,
name = $4
WHERE
id = $1 RETURNING id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers
id = $1 RETURNING id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers, message
`
type UpdateTemplateVersionByIDParams struct {
@ -4708,6 +4719,7 @@ func (q *sqlQuerier) UpdateTemplateVersionByID(ctx context.Context, arg UpdateTe
&i.JobID,
&i.CreatedBy,
pq.Array(&i.GitAuthProviders),
&i.Message,
)
return i, err
}

View File

@ -77,12 +77,13 @@ INSERT INTO
created_at,
updated_at,
"name",
message,
readme,
job_id,
created_by
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *;
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *;
-- name: UpdateTemplateVersionByID :one
UPDATE

View File

@ -1306,6 +1306,7 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
Name: req.Name,
Message: req.Message,
Readme: "",
JobID: provisionerJob.ID,
CreatedBy: apiKey.UserID,
@ -1420,6 +1421,7 @@ func convertTemplateVersion(version database.TemplateVersion, job codersdk.Provi
CreatedAt: version.CreatedAt,
UpdatedAt: version.UpdatedAt,
Name: version.Name,
Message: version.Message,
Job: job,
Readme: version.Readme,
CreatedBy: createdBy,

View File

@ -5,6 +5,7 @@ import (
"context"
"net/http"
"regexp"
"strings"
"testing"
"github.com/google/uuid"
@ -33,7 +34,10 @@ func TestTemplateVersion(t *testing.T) {
user := coderdtest.CreateFirstUser(t, client)
authz := coderdtest.AssertRBAC(t, api, client).Reset()
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil, func(req *codersdk.CreateTemplateVersionRequest) {
req.Name = "bananas"
req.Message = "first try"
})
authz.AssertChecked(t, rbac.ActionCreate, rbac.ResourceTemplate.InOrg(user.OrganizationID))
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
@ -43,6 +47,29 @@ func TestTemplateVersion(t *testing.T) {
tv, err := client.TemplateVersion(ctx, version.ID)
authz.AssertChecked(t, rbac.ActionRead, tv)
require.NoError(t, err)
assert.Equal(t, "bananas", tv.Name)
assert.Equal(t, "first try", tv.Message)
})
t.Run("Message limit exceeded", func(t *testing.T) {
t.Parallel()
client, _, _ := coderdtest.NewWithAPI(t, nil)
user := coderdtest.CreateFirstUser(t, client)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
file, err := client.Upload(ctx, codersdk.ContentTypeTar, bytes.NewReader([]byte{}))
require.NoError(t, err)
_, err = client.CreateTemplateVersion(ctx, user.OrganizationID, codersdk.CreateTemplateVersionRequest{
Name: "bananas",
Message: strings.Repeat("a", 1048577),
StorageMethod: codersdk.ProvisionerStorageMethodFile,
FileID: file.ID,
Provisioner: codersdk.ProvisionerTypeEcho,
})
require.Error(t, err, "message too long, create should fail")
})
t.Run("MemberCanRead", func(t *testing.T) {

View File

@ -42,7 +42,8 @@ type OrganizationMember struct {
// CreateTemplateVersionRequest enables callers to create a new Template Version.
type CreateTemplateVersionRequest struct {
Name string `json:"name,omitempty" validate:"omitempty,template_version_name"`
Name string `json:"name,omitempty" validate:"omitempty,template_version_name"`
Message string `json:"message,omitempty" validate:"lt=1048577"`
// TemplateID optionally associates a version with a template.
TemplateID uuid.UUID `json:"template_id,omitempty" format:"uuid"`
StorageMethod ProvisionerStorageMethod `json:"storage_method" validate:"oneof=file,required" enums:"file"`

View File

@ -25,6 +25,7 @@ type TemplateVersion struct {
CreatedAt time.Time `json:"created_at" format:"date-time"`
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
Name string `json:"name"`
Message string `json:"message"`
Job ProvisionerJob `json:"job"`
Readme string `json:"readme"`
CreatedBy User `json:"created_by"`

View File

@ -17,7 +17,7 @@ We track the following resources:
| GitSSHKey<br><i>create</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>private_key</td><td>true</td></tr><tr><td>public_key</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| License<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>exp</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>jwt</td><td>false</td></tr><tr><td>uploaded_at</td><td>true</td></tr><tr><td>uuid</td><td>true</td></tr></tbody></table> |
| Template<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>active_version_id</td><td>true</td></tr><tr><td>allow_user_autostart</td><td>true</td></tr><tr><td>allow_user_autostop</td><td>true</td></tr><tr><td>allow_user_cancel_workspace_jobs</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>default_ttl</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>failure_ttl</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>inactivity_ttl</td><td>true</td></tr><tr><td>locked_ttl</td><td>true</td></tr><tr><td>max_ttl</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>provisioner</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_acl</td><td>true</td></tr></tbody></table> |
| TemplateVersion<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>git_auth_providers</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>readme</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| TemplateVersion<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>git_auth_providers</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>message</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>readme</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| User<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>email</td><td>true</td></tr><tr><td>hashed_password</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_seen_at</td><td>false</td></tr><tr><td>login_type</td><td>true</td></tr><tr><td>rbac_roles</td><td>true</td></tr><tr><td>status</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
| Workspace<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>autostart_schedule</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>locked_at</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>ttl</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| WorkspaceBuild<br><i>start, stop</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>build_number</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>daily_cost</td><td>false</td></tr><tr><td>deadline</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>initiator_id</td><td>false</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>max_deadline</td><td>false</td></tr><tr><td>provisioner_state</td><td>false</td></tr><tr><td>reason</td><td>false</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>transition</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>workspace_id</td><td>false</td></tr></tbody></table> |

View File

@ -1435,6 +1435,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
{
"example_id": "string",
"file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767",
"message": "string",
"name": "string",
"provisioner": "terraform",
"storage_method": "file",
@ -1458,6 +1459,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| ---------------------- | ---------------------------------------------------------------------- | -------- | ------------ | ------------------------------------------------------------ |
| `example_id` | string | false | | |
| `file_id` | string | false | | |
| `message` | string | false | | |
| `name` | string | false | | |
| `provisioner` | string | true | | |
| `storage_method` | [codersdk.ProvisionerStorageMethod](#codersdkprovisionerstoragemethod) | true | | |
@ -4149,6 +4151,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
},
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
},
"message": "string",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"readme": "string",
@ -4166,6 +4169,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `created_by` | [codersdk.User](#codersdkuser) | false | | |
| `id` | string | false | | |
| `job` | [codersdk.ProvisionerJob](#codersdkprovisionerjob) | false | | |
| `message` | string | false | | |
| `name` | string | false | | |
| `organization_id` | string | false | | |
| `readme` | string | false | | |

View File

@ -389,6 +389,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
},
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
},
"message": "string",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"readme": "string",
@ -469,6 +470,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
},
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
},
"message": "string",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"readme": "string",
@ -506,6 +508,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
{
"example_id": "string",
"file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767",
"message": "string",
"name": "string",
"provisioner": "terraform",
"storage_method": "file",
@ -572,6 +575,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
},
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
},
"message": "string",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"readme": "string",
@ -875,6 +879,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions \
},
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
},
"message": "string",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"readme": "string",
@ -927,6 +932,7 @@ Status Code **200**
| `»» tags` | object | false | | |
| `»»» [any property]` | string | false | | |
| `»» worker_id` | string(uuid) | false | | |
| `» message` | string | false | | |
| `» name` | string | false | | |
| `» organization_id` | string(uuid) | false | | |
| `» readme` | string | false | | |
@ -1068,6 +1074,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions/{templ
},
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
},
"message": "string",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"readme": "string",
@ -1120,6 +1127,7 @@ Status Code **200**
| `»» tags` | object | false | | |
| `»»» [any property]` | string | false | | |
| `»» worker_id` | string(uuid) | false | | |
| `» message` | string | false | | |
| `» name` | string | false | | |
| `» organization_id` | string(uuid) | false | | |
| `» readme` | string | false | | |
@ -1205,6 +1213,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion} \
},
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
},
"message": "string",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"readme": "string",
@ -1293,6 +1302,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversion}
},
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
},
"message": "string",
"name": "string",
"organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
"readme": "string",

View File

@ -57,6 +57,14 @@ Ignore warnings about not having a .terraform.lock.hcl file present in the templ
Specify an inactivity TTL for workspaces created from this template. This licensed feature's default is 0h (off).
### -m, --message
| | |
| ---- | ------------------- |
| Type | <code>string</code> |
Specify a message describing the changes in this version of the template. Messages longer than 72 characters will be displayed as truncated.
### --private
| | |

View File

@ -47,6 +47,14 @@ Specify the directory to create from, use '-' to read tar from stdin.
Ignore warnings about not having a .terraform.lock.hcl file present in the template.
### -m, --message
| | |
| ---- | ------------------- |
| Type | <code>string</code> |
Specify a message describing the changes in this version of the template. Messages longer than 72 characters will be displayed as truncated.
### --name
| | |

View File

@ -88,6 +88,7 @@ var auditableResourcesTypes = map[any]map[string]Action{
"created_at": ActionIgnore, // Never changes, but is implicit and not helpful in a diff.
"updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff.
"name": ActionTrack,
"message": ActionIgnore, // Never changes after creation.
"readme": ActionTrack,
"job_id": ActionIgnore, // Not helpful in a diff because jobs aren't tracked in audit logs.
"created_by": ActionTrack,

View File

@ -200,6 +200,7 @@ export interface CreateTemplateVersionDryRunRequest {
// From codersdk/organizations.go
export interface CreateTemplateVersionRequest {
readonly name?: string
readonly message?: string
readonly template_id?: string
readonly storage_method: ProvisionerStorageMethod
readonly file_id?: string
@ -915,6 +916,7 @@ export interface TemplateVersion {
readonly created_at: string
readonly updated_at: string
readonly name: string
readonly message: string
readonly job: ProvisionerJob
readonly readme: string
readonly created_by: User

View File

@ -352,6 +352,7 @@ export const MockTemplateVersion: TypesGen.TemplateVersion = {
template_id: "test-template",
job: MockProvisionerJob,
name: "test-version",
message: "first version",
readme: `---
name:Template test
---
@ -369,6 +370,7 @@ export const MockTemplateVersion2: TypesGen.TemplateVersion = {
template_id: "test-template",
job: MockProvisionerJob,
name: "test-version-2",
message: "first version",
readme: `---
name:Template test 2
---
@ -386,6 +388,7 @@ export const MockTemplateVersion3: TypesGen.TemplateVersion = {
template_id: "test-template",
job: MockProvisionerJob,
name: "test-version-3",
message: "first version",
readme: "README",
created_by: MockUser,
warnings: ["UNSUPPORTED_WORKSPACES"],