Skip to content

Commit d892427

Browse files
authored
fix: do not warn on valid known experiments (#18514)
Fixes #18024 * drive-by: renames `handleExperimentsSafe` to `handleExperimentsAvailable` to better match semantics * defines list of `codersdk.ExperimentsKnown` and updates `ReadExperiments` to log on invalid experiments * typescript-ignores `codersdk.Experiments` so apitypings generates a valid enum list of possible values of experiment * updates OverviewPageView to distinguish between known 'hidden' experiments and unknown 'invalid' experiments
1 parent 4f98fd4 commit d892427

File tree

10 files changed

+54
-21
lines changed

10 files changed

+54
-21
lines changed

coderd/coderd.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -972,7 +972,7 @@ func New(options *Options) *API {
972972
})
973973
r.Route("/experiments", func(r chi.Router) {
974974
r.Use(apiKeyMiddleware)
975-
r.Get("/available", handleExperimentsSafe)
975+
r.Get("/available", handleExperimentsAvailable)
976976
r.Get("/", api.handleExperimentsGet)
977977
})
978978
r.Get("/updatecheck", api.updateCheck)
@@ -1895,7 +1895,9 @@ func ReadExperiments(log slog.Logger, raw []string) codersdk.Experiments {
18951895
exps = append(exps, codersdk.ExperimentsSafe...)
18961896
default:
18971897
ex := codersdk.Experiment(strings.ToLower(v))
1898-
if !slice.Contains(codersdk.ExperimentsSafe, ex) {
1898+
if !slice.Contains(codersdk.ExperimentsKnown, ex) {
1899+
log.Warn(context.Background(), "ignoring unknown experiment", slog.F("experiment", ex))
1900+
} else if !slice.Contains(codersdk.ExperimentsSafe, ex) {
18991901
log.Warn(context.Background(), "🐉 HERE BE DRAGONS: opting into hidden experiment", slog.F("experiment", ex))
19001902
}
19011903
exps = append(exps, ex)

coderd/experiments.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func (api *API) handleExperimentsGet(rw http.ResponseWriter, r *http.Request) {
2626
// @Tags General
2727
// @Success 200 {array} codersdk.Experiment
2828
// @Router /experiments/available [get]
29-
func handleExperimentsSafe(rw http.ResponseWriter, r *http.Request) {
29+
func handleExperimentsAvailable(rw http.ResponseWriter, r *http.Request) {
3030
ctx := r.Context()
3131
httpapi.Write(ctx, rw, http.StatusOK, codersdk.AvailableExperiments{
3232
Safe: codersdk.ExperimentsSafe,

codersdk/deployment.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3372,6 +3372,18 @@ const (
33723372
ExperimentAITasks Experiment = "ai-tasks" // Enables the new AI tasks feature.
33733373
)
33743374

3375+
// ExperimentsKnown should include all experiments defined above.
3376+
var ExperimentsKnown = Experiments{
3377+
ExperimentExample,
3378+
ExperimentAutoFillParameters,
3379+
ExperimentNotifications,
3380+
ExperimentWorkspaceUsage,
3381+
ExperimentWebPush,
3382+
ExperimentWorkspacePrebuilds,
3383+
ExperimentAgenticChat,
3384+
ExperimentAITasks,
3385+
}
3386+
33753387
// ExperimentsSafe should include all experiments that are safe for
33763388
// users to opt-in to via --experimental='*'.
33773389
// Experiments that are not ready for consumption by all users should
@@ -3384,6 +3396,9 @@ var ExperimentsSafe = Experiments{
33843396
// Multiple experiments may be enabled at the same time.
33853397
// Experiments are not safe for production use, and are not guaranteed to
33863398
// be backwards compatible. They may be removed or renamed at any time.
3399+
// The below typescript-ignore annotation allows our typescript generator
3400+
// to generate an enum list, which is used in the frontend.
3401+
// @typescript-ignore Experiments
33873402
type Experiments []Experiment
33883403

33893404
// Returns a list of experiments that are enabled for the deployment.

site/src/api/queries/experiments.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { API } from "api/api";
2-
import type { Experiments } from "api/typesGenerated";
2+
import { type Experiment, Experiments } from "api/typesGenerated";
33
import type { MetadataState } from "hooks/useEmbeddedMetadata";
44
import { cachedQuery } from "./util";
55

66
const experimentsKey = ["experiments"] as const;
77

8-
export const experiments = (metadata: MetadataState<Experiments>) => {
8+
export const experiments = (metadata: MetadataState<Experiment[]>) => {
99
return cachedQuery({
1010
metadata,
1111
queryKey: experimentsKey,
@@ -19,3 +19,7 @@ export const availableExperiments = () => {
1919
queryFn: async () => API.getAvailableExperiments(),
2020
};
2121
};
22+
23+
export const isKnownExperiment = (experiment: string): boolean => {
24+
return Experiments.includes(experiment as Experiment);
25+
};

site/src/api/typesGenerated.ts

Lines changed: 10 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/src/hooks/useEmbeddedMetadata.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type {
22
AppearanceConfig,
33
BuildInfoResponse,
44
Entitlements,
5-
Experiments,
5+
Experiment,
66
Region,
77
User,
88
UserAppearanceSettings,
@@ -24,7 +24,7 @@ export const DEFAULT_METADATA_KEY = "property";
2424
*/
2525
type AvailableMetadata = Readonly<{
2626
user: User;
27-
experiments: Experiments;
27+
experiments: Experiment[];
2828
appearance: AppearanceConfig;
2929
userAppearance: UserAppearanceSettings;
3030
entitlements: Entitlements;
@@ -89,7 +89,7 @@ export class MetadataManager implements MetadataManagerApi {
8989
userAppearance:
9090
this.registerValue<UserAppearanceSettings>("userAppearance"),
9191
entitlements: this.registerValue<Entitlements>("entitlements"),
92-
experiments: this.registerValue<Experiments>("experiments"),
92+
experiments: this.registerValue<Experiment[]>("experiments"),
9393
"build-info": this.registerValue<BuildInfoResponse>("build-info"),
9494
regions: this.registerRegionValue(),
9595
tasksTabVisible: this.registerValue<boolean>("tasksTabVisible"),

site/src/modules/dashboard/DashboardProvider.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { organizations } from "api/queries/organizations";
55
import type {
66
AppearanceConfig,
77
Entitlements,
8-
Experiments,
8+
Experiment,
99
Organization,
1010
} from "api/typesGenerated";
1111
import { ErrorAlert } from "components/Alert/ErrorAlert";
@@ -19,7 +19,7 @@ import { selectFeatureVisibility } from "./entitlements";
1919

2020
export interface DashboardValue {
2121
entitlements: Entitlements;
22-
experiments: Experiments;
22+
experiments: Experiment[];
2323
appearance: AppearanceConfig;
2424
organizations: readonly Organization[];
2525
showOrganizations: boolean;

site/src/pages/DeploymentSettingsPage/OverviewPage/OverviewPage.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { deploymentDAUs } from "api/queries/deployment";
2-
import { availableExperiments, experiments } from "api/queries/experiments";
2+
import {
3+
availableExperiments,
4+
experiments,
5+
isKnownExperiment,
6+
} from "api/queries/experiments";
37
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
48
import { useDeploymentConfig } from "modules/management/DeploymentConfigProvider";
59
import type { FC } from "react";
@@ -18,7 +22,7 @@ const OverviewPage: FC = () => {
1822
const safeExperiments = safeExperimentsQuery.data?.safe ?? [];
1923
const invalidExperiments =
2024
enabledExperimentsQuery.data?.filter((exp) => {
21-
return !safeExperiments.includes(exp);
25+
return !isKnownExperiment(exp);
2226
}) ?? [];
2327

2428
const { data: dailyActiveUsers } = useQuery(deploymentDAUs());

site/src/pages/DeploymentSettingsPage/OverviewPage/OverviewPageView.stories.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const meta: Meta<typeof OverviewPageView> = {
3030
description:
3131
"Enable one or more experiments. These are not ready for production. Separate multiple experiments with commas, or enter '*' to opt-in to all available experiments.",
3232
flag: "experiments",
33-
value: ["workspace_actions"],
33+
value: ["example"],
3434
flag_shorthand: "",
3535
hidden: false,
3636
},
@@ -82,8 +82,8 @@ export const allExperimentsEnabled: Story = {
8282
hidden: false,
8383
},
8484
],
85-
safeExperiments: ["shared-ports"],
86-
invalidExperiments: ["invalid"],
85+
safeExperiments: ["example"],
86+
invalidExperiments: [],
8787
},
8888
};
8989

@@ -118,7 +118,7 @@ export const invalidExperimentsEnabled: Story = {
118118
hidden: false,
119119
},
120120
],
121-
safeExperiments: ["shared-ports"],
121+
safeExperiments: ["example"],
122122
invalidExperiments: ["invalid"],
123123
},
124124
};

site/src/pages/DeploymentSettingsPage/OverviewPage/OverviewPageView.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import AlertTitle from "@mui/material/AlertTitle";
22
import type {
33
DAUsResponse,
4-
Experiments,
4+
Experiment,
55
SerpentOption,
66
} from "api/typesGenerated";
77
import { Link } from "components/Link/Link";
@@ -22,8 +22,8 @@ import { UserEngagementChart } from "./UserEngagementChart";
2222
type OverviewPageViewProps = {
2323
deploymentOptions: SerpentOption[];
2424
dailyActiveUsers: DAUsResponse | undefined;
25-
readonly invalidExperiments: Experiments | string[];
26-
readonly safeExperiments: Experiments | string[];
25+
readonly invalidExperiments: readonly string[];
26+
readonly safeExperiments: readonly Experiment[];
2727
};
2828

2929
export const OverviewPageView: FC<OverviewPageViewProps> = ({

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy