mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add --experiments flag to replace --experimental (#5767)
- Deprecates the --experimental flag - Adds a new flag --experiments which supports passing multiple comma-separated values or a wildcard value. - Exposes a new endpoint /api/v2/experiments that returns the list of enabled experiments. - Deprecates the field Features.Experimental in favour of this new API. - Updates apidocgen to support type aliases (shoutout to @mtojek). - Modifies apitypings to support generating slice types. - Updates develop.sh to pass additional args after -- to $CODERD_SHIM.
This commit is contained in:
@@ -636,6 +636,18 @@ export const getEntitlements = async (): Promise<TypesGen.Entitlements> => {
|
||||
}
|
||||
}
|
||||
|
||||
export const getExperiments = async (): Promise<TypesGen.Experiment[]> => {
|
||||
try {
|
||||
const response = await axios.get("/api/v2/experiments")
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
||||
return []
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export const getAuditLogs = async (
|
||||
options: TypesGen.AuditLogsRequest,
|
||||
): Promise<TypesGen.AuditLogResponse> => {
|
||||
|
||||
@@ -311,11 +311,12 @@ export interface DeploymentConfig {
|
||||
readonly scim_api_key: DeploymentConfigField<string>
|
||||
readonly provisioner: ProvisionerConfig
|
||||
readonly rate_limit: RateLimitConfig
|
||||
readonly experimental: DeploymentConfigField<boolean>
|
||||
readonly experiments: DeploymentConfigField<string[]>
|
||||
readonly update_check: DeploymentConfigField<boolean>
|
||||
readonly max_token_lifetime: DeploymentConfigField<number>
|
||||
readonly swagger: SwaggerConfig
|
||||
readonly logging: LoggingConfig
|
||||
readonly experimental: DeploymentConfigField<boolean>
|
||||
}
|
||||
|
||||
// From codersdk/deploymentconfig.go
|
||||
@@ -337,10 +338,13 @@ export interface Entitlements {
|
||||
readonly warnings: string[]
|
||||
readonly errors: string[]
|
||||
readonly has_license: boolean
|
||||
readonly experimental: boolean
|
||||
readonly trial: boolean
|
||||
readonly experimental: boolean
|
||||
}
|
||||
|
||||
// From codersdk/experiments.go
|
||||
export type Experiments = Experiment[]
|
||||
|
||||
// From codersdk/features.go
|
||||
export interface Feature {
|
||||
readonly entitlement: Entitlement
|
||||
@@ -1079,6 +1083,10 @@ export const Entitlements: Entitlement[] = [
|
||||
"not_entitled",
|
||||
]
|
||||
|
||||
// From codersdk/experiments.go
|
||||
export type Experiment = "vscode_local"
|
||||
export const Experiments: Experiment[] = ["vscode_local"]
|
||||
|
||||
// From codersdk/features.go
|
||||
export type FeatureName =
|
||||
| "appearance"
|
||||
|
||||
@@ -41,9 +41,9 @@ export const WorkspaceReadyPage = ({
|
||||
workspaceState.children["scheduleBannerMachine"],
|
||||
)
|
||||
const xServices = useContext(XServiceContext)
|
||||
const experimental = useSelector(
|
||||
xServices.entitlementsXService,
|
||||
(state) => state.context.entitlements.experimental,
|
||||
const experiments = useSelector(
|
||||
xServices.experimentsXService,
|
||||
(state) => state.context.experiments || [],
|
||||
)
|
||||
const featureVisibility = useSelector(
|
||||
xServices.entitlementsXService,
|
||||
@@ -124,7 +124,8 @@ export const WorkspaceReadyPage = ({
|
||||
canUpdateWorkspace={canUpdateWorkspace}
|
||||
hideSSHButton={featureVisibility["browser_only"]}
|
||||
hideVSCodeDesktopButton={
|
||||
!experimental || featureVisibility["browser_only"]
|
||||
!experiments.includes("vscode_local") ||
|
||||
featureVisibility["browser_only"]
|
||||
}
|
||||
workspaceErrors={{
|
||||
[WorkspaceErrors.GET_RESOURCES_ERROR]: refreshWorkspaceWarning,
|
||||
|
||||
@@ -982,6 +982,8 @@ export const MockEntitlementsWithAuditLog: TypesGen.Entitlements = {
|
||||
}),
|
||||
}
|
||||
|
||||
export const MockExperiments: TypesGen.Experiment[] = ["vscode_local"]
|
||||
|
||||
export const MockAuditLog: TypesGen.AuditLog = {
|
||||
id: "fbd2116a-8961-4954-87ae-e4575bd29ce0",
|
||||
request_id: "53bded77-7b9d-4e82-8771-991a34d759f9",
|
||||
|
||||
@@ -15,6 +15,11 @@ export const handlers = [
|
||||
return res(ctx.status(200), ctx.json(M.MockBuildInfo))
|
||||
}),
|
||||
|
||||
// experiments
|
||||
rest.get("/api/v2/experiments", async (req, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json(M.MockExperiments))
|
||||
}),
|
||||
|
||||
// update check
|
||||
rest.get("/api/v2/updatecheck", async (req, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json(M.MockUpdateCheck))
|
||||
|
||||
@@ -4,12 +4,14 @@ import { ActorRefFrom } from "xstate"
|
||||
import { authMachine } from "./auth/authXService"
|
||||
import { buildInfoMachine } from "./buildInfo/buildInfoXService"
|
||||
import { entitlementsMachine } from "./entitlements/entitlementsXService"
|
||||
import { experimentsMachine } from "./experiments/experimentsMachine"
|
||||
import { appearanceMachine } from "./appearance/appearanceXService"
|
||||
|
||||
interface XServiceContextType {
|
||||
authXService: ActorRefFrom<typeof authMachine>
|
||||
buildInfoXService: ActorRefFrom<typeof buildInfoMachine>
|
||||
entitlementsXService: ActorRefFrom<typeof entitlementsMachine>
|
||||
experimentsXService: ActorRefFrom<typeof experimentsMachine>
|
||||
appearanceXService: ActorRefFrom<typeof appearanceMachine>
|
||||
}
|
||||
|
||||
@@ -30,6 +32,7 @@ export const XServiceProvider: FC<{ children: ReactNode }> = ({ children }) => {
|
||||
authXService: useInterpret(authMachine),
|
||||
buildInfoXService: useInterpret(buildInfoMachine),
|
||||
entitlementsXService: useInterpret(entitlementsMachine),
|
||||
experimentsXService: useInterpret(experimentsMachine),
|
||||
appearanceXService: useInterpret(appearanceMachine),
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -22,6 +22,7 @@ const emptyEntitlements = {
|
||||
features: withDefaultFeatures({}),
|
||||
has_license: false,
|
||||
experimental: false,
|
||||
experimental_features: [],
|
||||
trial: false,
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { getExperiments } from "api/api"
|
||||
import { Experiment } from "api/typesGenerated"
|
||||
import { createMachine, assign } from "xstate"
|
||||
|
||||
export interface ExperimentsContext {
|
||||
experiments?: Experiment[]
|
||||
getExperimentsError?: Error | unknown
|
||||
}
|
||||
|
||||
export const experimentsMachine = createMachine(
|
||||
{
|
||||
id: "experimentsState",
|
||||
predictableActionArguments: true,
|
||||
tsTypes: {} as import("./experimentsMachine.typegen").Typegen0,
|
||||
schema: {
|
||||
context: {} as ExperimentsContext,
|
||||
services: {} as {
|
||||
getExperiments: {
|
||||
data: Experiment[]
|
||||
}
|
||||
},
|
||||
},
|
||||
context: {
|
||||
experiments: undefined,
|
||||
},
|
||||
initial: "gettingExperiments",
|
||||
states: {
|
||||
gettingExperiments: {
|
||||
invoke: {
|
||||
src: "getExperiments",
|
||||
id: "getExperiments",
|
||||
onDone: [
|
||||
{
|
||||
actions: ["assignExperiments", "clearGetExperimentsError"],
|
||||
target: "#experimentsState.success",
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: ["assignGetExperimentsError", "clearExperiments"],
|
||||
target: "#experimentsState.failure",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
success: {
|
||||
type: "final",
|
||||
},
|
||||
failure: {
|
||||
type: "final",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
services: {
|
||||
getExperiments: getExperiments,
|
||||
},
|
||||
actions: {
|
||||
assignExperiments: assign({
|
||||
experiments: (_, event) => event.data,
|
||||
}),
|
||||
clearExperiments: assign((context: ExperimentsContext) => ({
|
||||
...context,
|
||||
experiments: undefined,
|
||||
})),
|
||||
assignGetExperimentsError: assign({
|
||||
getExperimentsError: (_, event) => event.data,
|
||||
}),
|
||||
clearGetExperimentsError: assign((context: ExperimentsContext) => ({
|
||||
...context,
|
||||
getExperimentsError: undefined,
|
||||
})),
|
||||
},
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user