Skip to content

Commit ffbaa93

Browse files
authored
feat: add experimental flag (#4364)
1 parent 18b282c commit ffbaa93

File tree

12 files changed

+55
-9
lines changed

12 files changed

+55
-9
lines changed

cli/root.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ const (
4848
varNoFeatureWarning = "no-feature-warning"
4949
varForceTty = "force-tty"
5050
varVerbose = "verbose"
51+
varExperimental = "experimental"
5152
notLoggedInMessage = "You are not logged in. Try logging in using 'coder login <url>'."
5253

5354
envNoVersionCheck = "CODER_NO_VERSION_WARNING"
5455
envNoFeatureWarning = "CODER_NO_FEATURE_WARNING"
56+
envExperimental = "CODER_EXPERIMENTAL"
5557
)
5658

5759
var (
@@ -184,6 +186,7 @@ func Root(subcommands []*cobra.Command) *cobra.Command {
184186
cmd.PersistentFlags().Bool(varNoOpen, false, "Block automatically opening URLs in the browser.")
185187
_ = cmd.PersistentFlags().MarkHidden(varNoOpen)
186188
cliflag.Bool(cmd.PersistentFlags(), varVerbose, "v", "CODER_VERBOSE", false, "Enable verbose output.")
189+
cliflag.Bool(cmd.PersistentFlags(), varExperimental, "", envExperimental, false, "Enable experimental features. Experimental features are not ready for production.")
187190

188191
return cmd
189192
}
@@ -598,3 +601,18 @@ func (h *headerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
598601
}
599602
return h.transport.RoundTrip(req)
600603
}
604+
605+
// ExperimentalEnabled returns if the experimental feature flag is enabled.
606+
func ExperimentalEnabled(cmd *cobra.Command) bool {
607+
return cliflag.IsSetBool(cmd, varExperimental)
608+
}
609+
610+
// EnsureExperimental will ensure that the experimental feature flag is set if the given flag is set.
611+
func EnsureExperimental(cmd *cobra.Command, name string) error {
612+
_, set := cliflag.IsSet(cmd, name)
613+
if set && !ExperimentalEnabled(cmd) {
614+
return xerrors.Errorf("flag %s is set but requires flag --experimental or environment variable CODER_EXPERIMENTAL=true.", name)
615+
}
616+
617+
return nil
618+
}

cli/root_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/coder/coder/buildinfo"
1515
"github.com/coder/coder/cli"
16+
"github.com/coder/coder/cli/cliflag"
1617
"github.com/coder/coder/cli/clitest"
1718
"github.com/coder/coder/codersdk"
1819
)
@@ -153,4 +154,19 @@ func TestRoot(t *testing.T) {
153154
// This won't succeed, because we're using the login cmd to assert requests.
154155
_ = cmd.Execute()
155156
})
157+
158+
t.Run("Experimental", func(t *testing.T) {
159+
t.Parallel()
160+
161+
cmd, _ := clitest.New(t, "--experimental")
162+
err := cmd.Execute()
163+
require.NoError(t, err)
164+
require.True(t, cli.ExperimentalEnabled(cmd))
165+
166+
cmd, _ = clitest.New(t, "help", "--verbose")
167+
_ = cmd.Execute()
168+
_, set := cliflag.IsSet(cmd, "verbose")
169+
require.True(t, set)
170+
require.ErrorContains(t, cli.EnsureExperimental(cmd, "verbose"), "--experimental")
171+
})
156172
}

cli/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
368368
AutoImportTemplates: validatedAutoImportTemplates,
369369
MetricsCacheRefreshInterval: metricsCacheRefreshInterval,
370370
AgentStatsRefreshInterval: agentStatRefreshInterval,
371+
Experimental: ExperimentalEnabled(cmd),
371372
}
372373

373374
if oauth2GithubClientSecret != "" {

coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ type Options struct {
8181

8282
MetricsCacheRefreshInterval time.Duration
8383
AgentStatsRefreshInterval time.Duration
84+
Experimental bool
8485
}
8586

8687
// New constructs a Coder API handler.

codersdk/features.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ type Feature struct {
3838
}
3939

4040
type Entitlements struct {
41-
Features map[string]Feature `json:"features"`
42-
Warnings []string `json:"warnings"`
43-
HasLicense bool `json:"has_license"`
41+
Features map[string]Feature `json:"features"`
42+
Warnings []string `json:"warnings"`
43+
HasLicense bool `json:"has_license"`
44+
Experimental bool `json:"experimental"`
4445
}
4546

4647
func (c *Client) Entitlements(ctx context.Context) (Entitlements, error) {

enterprise/cli/features_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,6 @@ func TestFeaturesList(t *testing.T) {
6868
assert.Equal(t, codersdk.EntitlementNotEntitled,
6969
entitlements.Features[codersdk.FeatureWorkspaceQuota].Entitlement)
7070
assert.False(t, entitlements.HasLicense)
71+
assert.False(t, entitlements.Experimental)
7172
})
7273
}

enterprise/cli/licenses_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,9 @@ func (*fakeLicenseAPI) entitlements(rw http.ResponseWriter, r *http.Request) {
346346
}
347347
}
348348
httpapi.Write(r.Context(), rw, http.StatusOK, codersdk.Entitlements{
349-
Features: features,
350-
Warnings: []string{testWarning},
351-
HasLicense: true,
349+
Features: features,
350+
Warnings: []string{testWarning},
351+
HasLicense: true,
352+
Experimental: true,
352353
})
353354
}

enterprise/coderd/coderd.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,10 @@ func (api *API) serveEntitlements(rw http.ResponseWriter, r *http.Request) {
237237
api.entitlementsMu.RUnlock()
238238

239239
resp := codersdk.Entitlements{
240-
Features: make(map[string]codersdk.Feature),
241-
Warnings: make([]string, 0),
242-
HasLicense: entitlements.hasLicense,
240+
Features: make(map[string]codersdk.Feature),
241+
Warnings: make([]string, 0),
242+
HasLicense: entitlements.hasLicense,
243+
Experimental: api.Experimental,
243244
}
244245

245246
if entitlements.activeUsers.Limit != nil {

site/src/api/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export const defaultEntitlements = (): TypesGen.Entitlements => {
2929
features: features,
3030
has_license: false,
3131
warnings: [],
32+
experimental: false,
3233
}
3334
}
3435

site/src/api/typesGenerated.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ export interface Entitlements {
244244
readonly features: Record<string, Feature>
245245
readonly warnings: string[]
246246
readonly has_license: boolean
247+
readonly experimental: boolean
247248
}
248249

249250
// From codersdk/features.go

site/src/testHelpers/entities.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,11 +771,13 @@ export const MockEntitlements: TypesGen.Entitlements = {
771771
warnings: [],
772772
has_license: false,
773773
features: {},
774+
experimental: false,
774775
}
775776

776777
export const MockEntitlementsWithWarnings: TypesGen.Entitlements = {
777778
warnings: ["You are over your active user limit.", "And another thing."],
778779
has_license: true,
780+
experimental: false,
779781
features: {
780782
user_limit: {
781783
enabled: true,
@@ -797,6 +799,7 @@ export const MockEntitlementsWithWarnings: TypesGen.Entitlements = {
797799
export const MockEntitlementsWithAuditLog: TypesGen.Entitlements = {
798800
warnings: [],
799801
has_license: true,
802+
experimental: false,
800803
features: {
801804
audit_log: {
802805
enabled: true,

site/src/xServices/entitlements/entitlementsXService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const emptyEntitlements = {
2323
warnings: [],
2424
features: {},
2525
has_license: false,
26+
experimental: false,
2627
}
2728

2829
export const entitlementsMachine = createMachine(

0 commit comments

Comments
 (0)