mirror of
https://github.com/coder/coder.git
synced 2026-10-16 14:48:56 +08:00
fix: support first-user-trial in non-interactive login (#28321)
## Summary `coder login --first-user-trial=true` was unusable in non-interactive environments: the trial info fields could only be collected via interactive prompts, so scripted setup failed with a bare `EOF`. This adds flags and env vars for every trial info field, extracts the functions into a helper, turns the EOF into an actionable error, and fixes a related quirk where enabling the trial via env didn't reliably suppress the trial prompt. Fixes coder/coder#23264 ## Changes * Add a flag + env var for each trial field (matching the existing `first-user-*` convention): `--first-user-trial-{first-name,last-name,phone-number,job-title,company-name,country,developers}` and their `CODER_FIRST_USER_TRIAL_*` env vars. * When a trial field is missing with no input available, return an error naming the exact flag/env instead of a raw EOF. * Read `CODER_FIRST_USER_TRIAL` from `inv.Environ` instead of `os.Getenv` so the flag binding and the "Start a trial?" prompt gate agree (previously the env toggle could hit an EOF prompt that silently reset `trial` to `false`). * Regenerate `coder_login_--help.golden` and `docs/reference/cli/login.md`. ## Testing * Non-interactive login via flags and via env vars (env test asserts the trial is actually provisioned via a recording `TrialGenerator`). * Missing trial info returns the clear flag-naming error instead of hanging. * Existing interactive `TestLogin` subtests pass; `golangci-lint` clean. ## Notes No strict CLI validation of `country`/`developers` against the UI option lists: the server doesn't enforce them, and the existing fixture uses `developers=10-50` which isn't in the CLI bucket list. Can add if preferred. --- *Created by Coder Agents on behalf of* @snagles\*.\*
This commit is contained in:
+100
-43
@@ -148,6 +148,7 @@ func (r *RootCmd) login() *serpent.Command {
|
||||
name string
|
||||
password string
|
||||
trial bool
|
||||
trialInfo codersdk.CreateFirstUserTrialInfo
|
||||
useTokenForSession bool
|
||||
)
|
||||
cmd := &serpent.Command{
|
||||
@@ -271,7 +272,7 @@ func (r *RootCmd) login() *serpent.Command {
|
||||
}
|
||||
}
|
||||
|
||||
if !inv.ParsedFlags().Changed("first-user-trial") && os.Getenv(firstUserTrialEnv) == "" {
|
||||
if !inv.ParsedFlags().Changed("first-user-trial") && inv.Environ.Get(firstUserTrialEnv) == "" {
|
||||
v, _ := cliui.Prompt(inv, cliui.PromptOptions{
|
||||
Text: "Start a trial of Enterprise?",
|
||||
IsConfirm: true,
|
||||
@@ -280,49 +281,9 @@ func (r *RootCmd) login() *serpent.Command {
|
||||
trial = v == "yes" || v == "y"
|
||||
}
|
||||
|
||||
var trialInfo codersdk.CreateFirstUserTrialInfo
|
||||
if trial {
|
||||
if trialInfo.FirstName == "" {
|
||||
trialInfo.FirstName, err = promptTrialInfo(inv, "firstName")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if trialInfo.LastName == "" {
|
||||
trialInfo.LastName, err = promptTrialInfo(inv, "lastName")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if trialInfo.PhoneNumber == "" {
|
||||
trialInfo.PhoneNumber, err = promptTrialInfo(inv, "phoneNumber")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if trialInfo.JobTitle == "" {
|
||||
trialInfo.JobTitle, err = promptTrialInfo(inv, "jobTitle")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if trialInfo.CompanyName == "" {
|
||||
trialInfo.CompanyName, err = promptTrialInfo(inv, "companyName")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if trialInfo.Country == "" {
|
||||
trialInfo.Country, err = promptCountry(inv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if trialInfo.Developers == "" {
|
||||
trialInfo.Developers, err = promptDevelopers(inv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := collectFirstUserTrialInfo(inv, &trialInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,6 +436,48 @@ func (r *RootCmd) login() *serpent.Command {
|
||||
Description: "Specifies whether a trial license should be provisioned for the Coder deployment or not.",
|
||||
Value: serpent.BoolOf(&trial),
|
||||
},
|
||||
{
|
||||
Flag: "first-user-trial-first-name",
|
||||
Env: "CODER_FIRST_USER_TRIAL_FIRST_NAME",
|
||||
Description: "Specifies the first name of the first user for the deployment. Only used if --first-user-trial is set.",
|
||||
Value: serpent.StringOf(&trialInfo.FirstName),
|
||||
},
|
||||
{
|
||||
Flag: "first-user-trial-last-name",
|
||||
Env: "CODER_FIRST_USER_TRIAL_LAST_NAME",
|
||||
Description: "Specifies the last name of the first user for the deployment. Only used if --first-user-trial is set.",
|
||||
Value: serpent.StringOf(&trialInfo.LastName),
|
||||
},
|
||||
{
|
||||
Flag: "first-user-trial-phone-number",
|
||||
Env: "CODER_FIRST_USER_TRIAL_PHONE_NUMBER",
|
||||
Description: "Specifies the phone number of the first user for the deployment. Only used if --first-user-trial is set.",
|
||||
Value: serpent.StringOf(&trialInfo.PhoneNumber),
|
||||
},
|
||||
{
|
||||
Flag: "first-user-trial-job-title",
|
||||
Env: "CODER_FIRST_USER_TRIAL_JOB_TITLE",
|
||||
Description: "Specifies the job title of the first user for the deployment. Only used if --first-user-trial is set.",
|
||||
Value: serpent.StringOf(&trialInfo.JobTitle),
|
||||
},
|
||||
{
|
||||
Flag: "first-user-trial-company-name",
|
||||
Env: "CODER_FIRST_USER_TRIAL_COMPANY_NAME",
|
||||
Description: "Specifies the company name of the first user for the deployment. Only used if --first-user-trial is set.",
|
||||
Value: serpent.StringOf(&trialInfo.CompanyName),
|
||||
},
|
||||
{
|
||||
Flag: "first-user-trial-country",
|
||||
Env: "CODER_FIRST_USER_TRIAL_COUNTRY",
|
||||
Description: "Specifies the country of the first user for the deployment. Only used if --first-user-trial is set.",
|
||||
Value: serpent.StringOf(&trialInfo.Country),
|
||||
},
|
||||
{
|
||||
Flag: "first-user-trial-developers",
|
||||
Env: "CODER_FIRST_USER_TRIAL_DEVELOPERS",
|
||||
Description: "Specifies the number of developers using the deployment. Only used if --first-user-trial is set.",
|
||||
Value: serpent.StringOf(&trialInfo.Developers),
|
||||
},
|
||||
{
|
||||
Flag: "use-token-as-session",
|
||||
Description: "By default, the CLI will generate a new session token when logging in. This flag will instead use the provided token as the session token.",
|
||||
@@ -580,6 +583,60 @@ func openURL(inv *serpent.Invocation, urlToOpen string) error {
|
||||
return browser.OpenURL(urlToOpen)
|
||||
}
|
||||
|
||||
// collectFirstUserTrialInfo fills in any trial info fields that were not
|
||||
// already supplied via CLI flags or environment variables.
|
||||
//
|
||||
// In a non-interactive environment (for example an automation script) there
|
||||
// is no input to prompt against. Some prompts surface that as an io.EOF, but
|
||||
// the country and developers selects run through bubbletea, which swallows
|
||||
// io.EOF and would block forever waiting for input that never arrives.
|
||||
// To avoid hanging, non-interactivity is decided up front via isTTYIn: when
|
||||
// there is no TTY, every missing field is reported in a single actionable
|
||||
// error naming the flag and environment variable to set, instead of
|
||||
// prompting.
|
||||
func collectFirstUserTrialInfo(inv *serpent.Invocation, info *codersdk.CreateFirstUserTrialInfo) error {
|
||||
fields := []struct {
|
||||
flag string
|
||||
env string
|
||||
value *string
|
||||
prompt func(*serpent.Invocation) (string, error)
|
||||
}{
|
||||
{"first-user-trial-first-name", "CODER_FIRST_USER_TRIAL_FIRST_NAME", &info.FirstName, func(inv *serpent.Invocation) (string, error) { return promptTrialInfo(inv, "firstName") }},
|
||||
{"first-user-trial-last-name", "CODER_FIRST_USER_TRIAL_LAST_NAME", &info.LastName, func(inv *serpent.Invocation) (string, error) { return promptTrialInfo(inv, "lastName") }},
|
||||
{"first-user-trial-phone-number", "CODER_FIRST_USER_TRIAL_PHONE_NUMBER", &info.PhoneNumber, func(inv *serpent.Invocation) (string, error) { return promptTrialInfo(inv, "phoneNumber") }},
|
||||
{"first-user-trial-job-title", "CODER_FIRST_USER_TRIAL_JOB_TITLE", &info.JobTitle, func(inv *serpent.Invocation) (string, error) { return promptTrialInfo(inv, "jobTitle") }},
|
||||
{"first-user-trial-company-name", "CODER_FIRST_USER_TRIAL_COMPANY_NAME", &info.CompanyName, func(inv *serpent.Invocation) (string, error) { return promptTrialInfo(inv, "companyName") }},
|
||||
{"first-user-trial-country", "CODER_FIRST_USER_TRIAL_COUNTRY", &info.Country, promptCountry},
|
||||
{"first-user-trial-developers", "CODER_FIRST_USER_TRIAL_DEVELOPERS", &info.Developers, promptDevelopers},
|
||||
}
|
||||
|
||||
if !isTTYIn(inv) {
|
||||
var missing []string
|
||||
for _, f := range fields {
|
||||
if *f.value == "" {
|
||||
missing = append(missing, fmt.Sprintf("--%s (or %s)", f.flag, f.env))
|
||||
}
|
||||
}
|
||||
if len(missing) > 0 {
|
||||
return xerrors.Errorf("--first-user-trial requires trial info that cannot be prompted for in a non-interactive environment; set %s", strings.Join(missing, ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, f := range fields {
|
||||
if *f.value != "" {
|
||||
continue
|
||||
}
|
||||
value, err := f.prompt(inv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*f.value = value
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func promptTrialInfo(inv *serpent.Invocation, fieldName string) (string, error) {
|
||||
value, err := cliui.Prompt(inv, cliui.PromptOptions{
|
||||
Text: fmt.Sprintf("Please enter %s:", pretty.Sprint(cliui.DefaultStyles.Field, fieldName)),
|
||||
|
||||
@@ -283,6 +283,7 @@ func TestLogin(t *testing.T) {
|
||||
client := coderdtest.New(t, nil)
|
||||
inv, _ := clitest.New(
|
||||
t, "login", client.URL.String(),
|
||||
"--force-tty",
|
||||
"--first-user-username", coderdtest.FirstUserParams.Username,
|
||||
"--first-user-full-name", coderdtest.FirstUserParams.Name,
|
||||
"--first-user-email", coderdtest.FirstUserParams.Email,
|
||||
@@ -325,6 +326,7 @@ func TestLogin(t *testing.T) {
|
||||
client := coderdtest.New(t, nil)
|
||||
inv, _ := clitest.New(
|
||||
t, "login", client.URL.String(),
|
||||
"--force-tty",
|
||||
"--first-user-username", coderdtest.FirstUserParams.Username,
|
||||
"--first-user-email", coderdtest.FirstUserParams.Email,
|
||||
"--first-user-password", coderdtest.FirstUserParams.Password,
|
||||
@@ -360,6 +362,151 @@ func TestLogin(t *testing.T) {
|
||||
assert.Empty(t, me.Name)
|
||||
})
|
||||
|
||||
t.Run("InitialUserTrialFlagsNonInteractive", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
inv, _ := clitest.New(
|
||||
t, "login", client.URL.String(),
|
||||
"--first-user-username", coderdtest.FirstUserParams.Username,
|
||||
"--first-user-full-name", coderdtest.FirstUserParams.Name,
|
||||
"--first-user-email", coderdtest.FirstUserParams.Email,
|
||||
"--first-user-password", coderdtest.FirstUserParams.Password,
|
||||
"--first-user-trial",
|
||||
"--first-user-trial-first-name", coderdtest.TrialUserParams.FirstName,
|
||||
"--first-user-trial-last-name", coderdtest.TrialUserParams.LastName,
|
||||
"--first-user-trial-phone-number", coderdtest.TrialUserParams.PhoneNumber,
|
||||
"--first-user-trial-job-title", coderdtest.TrialUserParams.JobTitle,
|
||||
"--first-user-trial-company-name", coderdtest.TrialUserParams.CompanyName,
|
||||
"--first-user-trial-country", coderdtest.TrialUserParams.Country,
|
||||
"--first-user-trial-developers", coderdtest.TrialUserParams.Developers,
|
||||
)
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
// No PTY is attached, so this exercises the non-interactive path.
|
||||
err := inv.WithContext(ctx).Run()
|
||||
require.NoError(t, err)
|
||||
resp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
|
||||
Email: coderdtest.FirstUserParams.Email,
|
||||
Password: coderdtest.FirstUserParams.Password,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
client.SetSessionToken(resp.SessionToken)
|
||||
me, err := client.User(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Username, me.Username)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Name, me.Name)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Email, me.Email)
|
||||
})
|
||||
|
||||
t.Run("InitialUserTrialFlagsNonInteractiveEnv", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var gotTrial codersdk.LicensorTrialRequest
|
||||
trialCalled := false
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
TrialGenerator: func(_ context.Context, req codersdk.LicensorTrialRequest) error {
|
||||
trialCalled = true
|
||||
gotTrial = req
|
||||
return nil
|
||||
},
|
||||
})
|
||||
inv, _ := clitest.New(
|
||||
t, "login", client.URL.String(),
|
||||
"--first-user-username", coderdtest.FirstUserParams.Username,
|
||||
"--first-user-full-name", coderdtest.FirstUserParams.Name,
|
||||
"--first-user-email", coderdtest.FirstUserParams.Email,
|
||||
"--first-user-password", coderdtest.FirstUserParams.Password,
|
||||
)
|
||||
// Enabling the trial purely through the environment must suppress
|
||||
// the interactive "Start a trial?" prompt and still collect the
|
||||
// trial info from the environment.
|
||||
inv.Environ.Set("CODER_FIRST_USER_TRIAL", "true")
|
||||
inv.Environ.Set("CODER_FIRST_USER_TRIAL_FIRST_NAME", coderdtest.TrialUserParams.FirstName)
|
||||
inv.Environ.Set("CODER_FIRST_USER_TRIAL_LAST_NAME", coderdtest.TrialUserParams.LastName)
|
||||
inv.Environ.Set("CODER_FIRST_USER_TRIAL_PHONE_NUMBER", coderdtest.TrialUserParams.PhoneNumber)
|
||||
inv.Environ.Set("CODER_FIRST_USER_TRIAL_JOB_TITLE", coderdtest.TrialUserParams.JobTitle)
|
||||
inv.Environ.Set("CODER_FIRST_USER_TRIAL_COMPANY_NAME", coderdtest.TrialUserParams.CompanyName)
|
||||
inv.Environ.Set("CODER_FIRST_USER_TRIAL_COUNTRY", coderdtest.TrialUserParams.Country)
|
||||
inv.Environ.Set("CODER_FIRST_USER_TRIAL_DEVELOPERS", coderdtest.TrialUserParams.Developers)
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
err := inv.WithContext(ctx).Run()
|
||||
require.NoError(t, err)
|
||||
// The trial must actually be provisioned, proving the env toggle was
|
||||
// honored and the prompt was skipped.
|
||||
require.True(t, trialCalled)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Email, gotTrial.Email)
|
||||
assert.Equal(t, coderdtest.TrialUserParams.FirstName, gotTrial.FirstName)
|
||||
assert.Equal(t, coderdtest.TrialUserParams.LastName, gotTrial.LastName)
|
||||
assert.Equal(t, coderdtest.TrialUserParams.PhoneNumber, gotTrial.PhoneNumber)
|
||||
assert.Equal(t, coderdtest.TrialUserParams.JobTitle, gotTrial.JobTitle)
|
||||
assert.Equal(t, coderdtest.TrialUserParams.CompanyName, gotTrial.CompanyName)
|
||||
assert.Equal(t, coderdtest.TrialUserParams.Country, gotTrial.Country)
|
||||
assert.Equal(t, coderdtest.TrialUserParams.Developers, gotTrial.Developers)
|
||||
resp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
|
||||
Email: coderdtest.FirstUserParams.Email,
|
||||
Password: coderdtest.FirstUserParams.Password,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
client.SetSessionToken(resp.SessionToken)
|
||||
me, err := client.User(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, coderdtest.FirstUserParams.Username, me.Username)
|
||||
})
|
||||
|
||||
t.Run("InitialUserTrialNonInteractiveMissingInfo", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
inv, _ := clitest.New(
|
||||
t, "login", client.URL.String(),
|
||||
"--first-user-username", coderdtest.FirstUserParams.Username,
|
||||
"--first-user-full-name", coderdtest.FirstUserParams.Name,
|
||||
"--first-user-email", coderdtest.FirstUserParams.Email,
|
||||
"--first-user-password", coderdtest.FirstUserParams.Password,
|
||||
"--first-user-trial",
|
||||
)
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
// No PTY is attached and the trial info flags are missing, so the
|
||||
// command must fail fast instead of blocking on a prompt that can
|
||||
// never receive input.
|
||||
err := inv.WithContext(ctx).Run()
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "non-interactive")
|
||||
// Every missing field must be reported in a single error.
|
||||
require.ErrorContains(t, err, "--first-user-trial-first-name")
|
||||
require.ErrorContains(t, err, "CODER_FIRST_USER_TRIAL_FIRST_NAME")
|
||||
require.ErrorContains(t, err, "--first-user-trial-developers")
|
||||
require.ErrorContains(t, err, "CODER_FIRST_USER_TRIAL_DEVELOPERS")
|
||||
})
|
||||
|
||||
t.Run("InitialUserTrialNonInteractiveMissingSelectInfo", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
// Supply every text field but omit the two select-backed fields
|
||||
// (country and developers). These go through bubbletea, which
|
||||
// swallows io.EOF and would otherwise hang forever; they must
|
||||
// instead produce the same actionable error as the text fields.
|
||||
inv, _ := clitest.New(
|
||||
t, "login", client.URL.String(),
|
||||
"--first-user-username", coderdtest.FirstUserParams.Username,
|
||||
"--first-user-full-name", coderdtest.FirstUserParams.Name,
|
||||
"--first-user-email", coderdtest.FirstUserParams.Email,
|
||||
"--first-user-password", coderdtest.FirstUserParams.Password,
|
||||
"--first-user-trial",
|
||||
"--first-user-trial-first-name", coderdtest.TrialUserParams.FirstName,
|
||||
"--first-user-trial-last-name", coderdtest.TrialUserParams.LastName,
|
||||
"--first-user-trial-phone-number", coderdtest.TrialUserParams.PhoneNumber,
|
||||
"--first-user-trial-job-title", coderdtest.TrialUserParams.JobTitle,
|
||||
"--first-user-trial-company-name", coderdtest.TrialUserParams.CompanyName,
|
||||
)
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
err := inv.WithContext(ctx).Run()
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "--first-user-trial-country")
|
||||
require.ErrorContains(t, err, "CODER_FIRST_USER_TRIAL_COUNTRY")
|
||||
require.ErrorContains(t, err, "--first-user-trial-developers")
|
||||
require.ErrorContains(t, err, "CODER_FIRST_USER_TRIAL_DEVELOPERS")
|
||||
// The satisfied text fields must not be named as missing.
|
||||
require.NotContains(t, err.Error(), "--first-user-trial-first-name")
|
||||
})
|
||||
|
||||
t.Run("InitialUserTTYConfirmPasswordFailAndReprompt", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
logger := testutil.Logger(t)
|
||||
|
||||
+28
@@ -28,6 +28,34 @@ OPTIONS:
|
||||
Specifies whether a trial license should be provisioned for the Coder
|
||||
deployment or not.
|
||||
|
||||
--first-user-trial-company-name string, $CODER_FIRST_USER_TRIAL_COMPANY_NAME
|
||||
Specifies the company name of the first user for the deployment. Only
|
||||
used if --first-user-trial is set.
|
||||
|
||||
--first-user-trial-country string, $CODER_FIRST_USER_TRIAL_COUNTRY
|
||||
Specifies the country of the first user for the deployment. Only used
|
||||
if --first-user-trial is set.
|
||||
|
||||
--first-user-trial-developers string, $CODER_FIRST_USER_TRIAL_DEVELOPERS
|
||||
Specifies the number of developers using the deployment. Only used if
|
||||
--first-user-trial is set.
|
||||
|
||||
--first-user-trial-first-name string, $CODER_FIRST_USER_TRIAL_FIRST_NAME
|
||||
Specifies the first name of the first user for the deployment. Only
|
||||
used if --first-user-trial is set.
|
||||
|
||||
--first-user-trial-job-title string, $CODER_FIRST_USER_TRIAL_JOB_TITLE
|
||||
Specifies the job title of the first user for the deployment. Only
|
||||
used if --first-user-trial is set.
|
||||
|
||||
--first-user-trial-last-name string, $CODER_FIRST_USER_TRIAL_LAST_NAME
|
||||
Specifies the last name of the first user for the deployment. Only
|
||||
used if --first-user-trial is set.
|
||||
|
||||
--first-user-trial-phone-number string, $CODER_FIRST_USER_TRIAL_PHONE_NUMBER
|
||||
Specifies the phone number of the first user for the deployment. Only
|
||||
used if --first-user-trial is set.
|
||||
|
||||
--first-user-username string, $CODER_FIRST_USER_USERNAME
|
||||
Specifies a username to use if creating the first user for the
|
||||
deployment.
|
||||
|
||||
Generated
+63
@@ -73,6 +73,69 @@ Specifies a password to use if creating the first user for the deployment.
|
||||
|
||||
Specifies whether a trial license should be provisioned for the Coder deployment or not.
|
||||
|
||||
### --first-user-trial-first-name
|
||||
|
||||
| | |
|
||||
|-------------|-------------------------------------------------|
|
||||
| Type | <code>string</code> |
|
||||
| Environment | <code>$CODER_FIRST_USER_TRIAL_FIRST_NAME</code> |
|
||||
|
||||
Specifies the first name of the first user for the deployment. Only used if --first-user-trial is set.
|
||||
|
||||
### --first-user-trial-last-name
|
||||
|
||||
| | |
|
||||
|-------------|------------------------------------------------|
|
||||
| Type | <code>string</code> |
|
||||
| Environment | <code>$CODER_FIRST_USER_TRIAL_LAST_NAME</code> |
|
||||
|
||||
Specifies the last name of the first user for the deployment. Only used if --first-user-trial is set.
|
||||
|
||||
### --first-user-trial-phone-number
|
||||
|
||||
| | |
|
||||
|-------------|---------------------------------------------------|
|
||||
| Type | <code>string</code> |
|
||||
| Environment | <code>$CODER_FIRST_USER_TRIAL_PHONE_NUMBER</code> |
|
||||
|
||||
Specifies the phone number of the first user for the deployment. Only used if --first-user-trial is set.
|
||||
|
||||
### --first-user-trial-job-title
|
||||
|
||||
| | |
|
||||
|-------------|------------------------------------------------|
|
||||
| Type | <code>string</code> |
|
||||
| Environment | <code>$CODER_FIRST_USER_TRIAL_JOB_TITLE</code> |
|
||||
|
||||
Specifies the job title of the first user for the deployment. Only used if --first-user-trial is set.
|
||||
|
||||
### --first-user-trial-company-name
|
||||
|
||||
| | |
|
||||
|-------------|---------------------------------------------------|
|
||||
| Type | <code>string</code> |
|
||||
| Environment | <code>$CODER_FIRST_USER_TRIAL_COMPANY_NAME</code> |
|
||||
|
||||
Specifies the company name of the first user for the deployment. Only used if --first-user-trial is set.
|
||||
|
||||
### --first-user-trial-country
|
||||
|
||||
| | |
|
||||
|-------------|----------------------------------------------|
|
||||
| Type | <code>string</code> |
|
||||
| Environment | <code>$CODER_FIRST_USER_TRIAL_COUNTRY</code> |
|
||||
|
||||
Specifies the country of the first user for the deployment. Only used if --first-user-trial is set.
|
||||
|
||||
### --first-user-trial-developers
|
||||
|
||||
| | |
|
||||
|-------------|-------------------------------------------------|
|
||||
| Type | <code>string</code> |
|
||||
| Environment | <code>$CODER_FIRST_USER_TRIAL_DEVELOPERS</code> |
|
||||
|
||||
Specifies the number of developers using the deployment. Only used if --first-user-trial is set.
|
||||
|
||||
### --use-token-as-session
|
||||
|
||||
| | |
|
||||
|
||||
Reference in New Issue
Block a user