mirror of https://github.com/coder/coder.git
Merge branch 'coder:main' into feat/coder-login-secret
This commit is contained in:
commit
3b8dbc04fc
|
@ -142,7 +142,7 @@ jobs:
|
|||
|
||||
# Check for any typos
|
||||
- name: Check for typos
|
||||
uses: crate-ci/typos@v1.20.9
|
||||
uses: crate-ci/typos@v1.20.10
|
||||
with:
|
||||
config: .github/workflows/typos.toml
|
||||
|
||||
|
@ -909,7 +909,8 @@ jobs:
|
|||
uses: actions/checkout@v4
|
||||
- name: "Dependency Review"
|
||||
id: review
|
||||
uses: actions/dependency-review-action@v4
|
||||
# TODO: Replace this with the latest release once https://github.com/actions/dependency-review-action/pull/761 is merged.
|
||||
uses: actions/dependency-review-action@49fbbe0acb033b7824f26d00b005d7d598d76301
|
||||
with:
|
||||
allow-licenses: Apache-2.0, BSD-2-Clause, BSD-3-Clause, CC0-1.0, ISC, MIT, MIT-0, MPL-2.0
|
||||
allow-dependencies-licenses: "pkg:golang/github.com/pelletier/go-toml/v2"
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
},
|
||||
{
|
||||
"pattern": "tailscale.com"
|
||||
},
|
||||
{
|
||||
"pattern": "wireguard.com"
|
||||
}
|
||||
],
|
||||
"aliveStatusCodes": [200, 0]
|
||||
|
|
|
@ -128,6 +128,13 @@ jobs:
|
|||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
# Necessary for signing Windows binaries.
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: "zulu"
|
||||
java-version: "11.0"
|
||||
|
||||
- name: Install nsis and zstd
|
||||
run: sudo apt-get install -y nsis zstd
|
||||
|
||||
|
@ -161,10 +168,32 @@ jobs:
|
|||
AC_CERTIFICATE_PASSWORD: ${{ secrets.AC_CERTIFICATE_PASSWORD }}
|
||||
AC_APIKEY_P8_BASE64: ${{ secrets.AC_APIKEY_P8_BASE64 }}
|
||||
|
||||
- name: Setup Windows EV Signing Certificate
|
||||
run: |
|
||||
set -euo pipefail
|
||||
touch /tmp/ev_cert.pem
|
||||
chmod 600 /tmp/ev_cert.pem
|
||||
echo "$EV_SIGNING_CERT" > /tmp/ev_cert.pem
|
||||
wget https://github.com/ebourg/jsign/releases/download/6.0/jsign-6.0.jar -O /tmp/jsign-6.0.jar
|
||||
env:
|
||||
EV_SIGNING_CERT: ${{ secrets.EV_SIGNING_CERT }}
|
||||
|
||||
- name: Test migrations from current ref to main
|
||||
run: |
|
||||
make test-migrations
|
||||
|
||||
# Setup GCloud for signing Windows binaries.
|
||||
- name: Authenticate to Google Cloud
|
||||
id: gcloud_auth
|
||||
uses: google-github-actions/auth@v2
|
||||
with:
|
||||
workload_identity_provider: ${{ secrets.GCP_CODE_SIGNING_WORKLOAD_ID_PROVIDER }}
|
||||
service_account: ${{ secrets.GCP_CODE_SIGNING_SERVICE_ACCOUNT }}
|
||||
token_format: "access_token"
|
||||
|
||||
- name: Setup GCloud SDK
|
||||
uses: "google-github-actions/setup-gcloud@v2"
|
||||
|
||||
- name: Build binaries
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
@ -179,16 +208,26 @@ jobs:
|
|||
build/coder_helm_"$version".tgz \
|
||||
build/provisioner_helm_"$version".tgz
|
||||
env:
|
||||
CODER_SIGN_WINDOWS: "1"
|
||||
CODER_SIGN_DARWIN: "1"
|
||||
AC_CERTIFICATE_FILE: /tmp/apple_cert.p12
|
||||
AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt
|
||||
AC_APIKEY_ISSUER_ID: ${{ secrets.AC_APIKEY_ISSUER_ID }}
|
||||
AC_APIKEY_ID: ${{ secrets.AC_APIKEY_ID }}
|
||||
AC_APIKEY_FILE: /tmp/apple_apikey.p8
|
||||
EV_KEY: ${{ secrets.EV_KEY }}
|
||||
EV_KEYSTORE: ${{ secrets.EV_KEYSTORE }}
|
||||
EV_TSA_URL: ${{ secrets.EV_TSA_URL }}
|
||||
EV_CERTIFICATE_PATH: /tmp/ev_cert.pem
|
||||
GCLOUD_ACCESS_TOKEN: ${{ steps.gcloud_auth.outputs.access_token }}
|
||||
JSIGN_PATH: /tmp/jsign-6.0.jar
|
||||
|
||||
- name: Delete Apple Developer certificate and API key
|
||||
run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}
|
||||
|
||||
- name: Delete Windows EV Signing Cert
|
||||
run: rm /tmp/ev_cert.pem
|
||||
|
||||
- name: Determine base image tag
|
||||
id: image-base-tag
|
||||
run: |
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/coder/serpent"
|
||||
)
|
||||
|
||||
func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.TemplateVersionParameter) (string, error) {
|
||||
func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.TemplateVersionParameter, defaultOverrides map[string]string) (string, error) {
|
||||
label := templateVersionParameter.Name
|
||||
if templateVersionParameter.DisplayName != "" {
|
||||
label = templateVersionParameter.DisplayName
|
||||
|
@ -26,6 +26,11 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te
|
|||
_, _ = fmt.Fprintln(inv.Stdout, " "+strings.TrimSpace(strings.Join(strings.Split(templateVersionParameter.DescriptionPlaintext, "\n"), "\n "))+"\n")
|
||||
}
|
||||
|
||||
defaultValue := templateVersionParameter.DefaultValue
|
||||
if v, ok := defaultOverrides[templateVersionParameter.Name]; ok {
|
||||
defaultValue = v
|
||||
}
|
||||
|
||||
var err error
|
||||
var value string
|
||||
if templateVersionParameter.Type == "list(string)" {
|
||||
|
@ -58,7 +63,7 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te
|
|||
var richParameterOption *codersdk.TemplateVersionParameterOption
|
||||
richParameterOption, err = RichSelect(inv, RichSelectOptions{
|
||||
Options: templateVersionParameter.Options,
|
||||
Default: templateVersionParameter.DefaultValue,
|
||||
Default: defaultValue,
|
||||
HideSearch: true,
|
||||
})
|
||||
if err == nil {
|
||||
|
@ -69,7 +74,7 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te
|
|||
} else {
|
||||
text := "Enter a value"
|
||||
if !templateVersionParameter.Required {
|
||||
text += fmt.Sprintf(" (default: %q)", templateVersionParameter.DefaultValue)
|
||||
text += fmt.Sprintf(" (default: %q)", defaultValue)
|
||||
}
|
||||
text += ":"
|
||||
|
||||
|
@ -87,7 +92,7 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te
|
|||
|
||||
// If they didn't specify anything, use the default value if set.
|
||||
if len(templateVersionParameter.Options) == 0 && value == "" {
|
||||
value = templateVersionParameter.DefaultValue
|
||||
value = defaultValue
|
||||
}
|
||||
|
||||
return value, nil
|
||||
|
|
|
@ -165,6 +165,11 @@ func (r *RootCmd) create() *serpent.Command {
|
|||
return xerrors.Errorf("can't parse given parameter values: %w", err)
|
||||
}
|
||||
|
||||
cliBuildParameterDefaults, err := asWorkspaceBuildParameters(parameterFlags.richParameterDefaults)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("can't parse given parameter defaults: %w", err)
|
||||
}
|
||||
|
||||
var sourceWorkspaceParameters []codersdk.WorkspaceBuildParameter
|
||||
if copyParametersFrom != "" {
|
||||
sourceWorkspaceParameters, err = client.WorkspaceBuildParameters(inv.Context(), sourceWorkspace.LatestBuild.ID)
|
||||
|
@ -178,8 +183,9 @@ func (r *RootCmd) create() *serpent.Command {
|
|||
TemplateVersionID: templateVersionID,
|
||||
NewWorkspaceName: workspaceName,
|
||||
|
||||
RichParameterFile: parameterFlags.richParameterFile,
|
||||
RichParameters: cliBuildParameters,
|
||||
RichParameterFile: parameterFlags.richParameterFile,
|
||||
RichParameters: cliBuildParameters,
|
||||
RichParameterDefaults: cliBuildParameterDefaults,
|
||||
|
||||
SourceWorkspaceParameters: sourceWorkspaceParameters,
|
||||
})
|
||||
|
@ -262,6 +268,7 @@ func (r *RootCmd) create() *serpent.Command {
|
|||
cliui.SkipPromptOption(),
|
||||
)
|
||||
cmd.Options = append(cmd.Options, parameterFlags.cliParameters()...)
|
||||
cmd.Options = append(cmd.Options, parameterFlags.cliParameterDefaults()...)
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -276,9 +283,10 @@ type prepWorkspaceBuildArgs struct {
|
|||
PromptBuildOptions bool
|
||||
BuildOptions []codersdk.WorkspaceBuildParameter
|
||||
|
||||
PromptRichParameters bool
|
||||
RichParameters []codersdk.WorkspaceBuildParameter
|
||||
RichParameterFile string
|
||||
PromptRichParameters bool
|
||||
RichParameters []codersdk.WorkspaceBuildParameter
|
||||
RichParameterFile string
|
||||
RichParameterDefaults []codersdk.WorkspaceBuildParameter
|
||||
}
|
||||
|
||||
// prepWorkspaceBuild will ensure a workspace build will succeed on the latest template version.
|
||||
|
@ -311,7 +319,8 @@ func prepWorkspaceBuild(inv *serpent.Invocation, client *codersdk.Client, args p
|
|||
WithBuildOptions(args.BuildOptions).
|
||||
WithPromptRichParameters(args.PromptRichParameters).
|
||||
WithRichParameters(args.RichParameters).
|
||||
WithRichParametersFile(parameterFile)
|
||||
WithRichParametersFile(parameterFile).
|
||||
WithRichParametersDefaults(args.RichParameterDefaults)
|
||||
buildParameters, err := resolver.Resolve(inv, args.Action, templateVersionParameters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -315,6 +315,68 @@ func TestCreateWithRichParameters(t *testing.T) {
|
|||
<-doneChan
|
||||
})
|
||||
|
||||
t.Run("ParametersDefaults", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
owner := coderdtest.CreateFirstUser(t, client)
|
||||
member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, echoResponses)
|
||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||
|
||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
|
||||
|
||||
inv, root := clitest.New(t, "create", "my-workspace", "--template", template.Name,
|
||||
"--parameter-default", fmt.Sprintf("%s=%s", firstParameterName, firstParameterValue),
|
||||
"--parameter-default", fmt.Sprintf("%s=%s", secondParameterName, secondParameterValue),
|
||||
"--parameter-default", fmt.Sprintf("%s=%s", immutableParameterName, immutableParameterValue))
|
||||
clitest.SetupConfig(t, member, root)
|
||||
doneChan := make(chan struct{})
|
||||
pty := ptytest.New(t).Attach(inv)
|
||||
go func() {
|
||||
defer close(doneChan)
|
||||
err := inv.Run()
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
||||
matches := []string{
|
||||
firstParameterDescription, firstParameterValue,
|
||||
secondParameterDescription, secondParameterValue,
|
||||
immutableParameterDescription, immutableParameterValue,
|
||||
}
|
||||
for i := 0; i < len(matches); i += 2 {
|
||||
match := matches[i]
|
||||
defaultValue := matches[i+1]
|
||||
|
||||
pty.ExpectMatch(match)
|
||||
pty.ExpectMatch(`Enter a value (default: "` + defaultValue + `")`)
|
||||
pty.WriteLine("")
|
||||
}
|
||||
pty.ExpectMatch("Confirm create?")
|
||||
pty.WriteLine("yes")
|
||||
<-doneChan
|
||||
|
||||
// Verify that the expected default values were used.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
defer cancel()
|
||||
|
||||
workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
|
||||
Name: "my-workspace",
|
||||
})
|
||||
require.NoError(t, err, "can't list available workspaces")
|
||||
require.Len(t, workspaces.Workspaces, 1)
|
||||
|
||||
workspaceLatestBuild := workspaces.Workspaces[0].LatestBuild
|
||||
require.Equal(t, version.ID, workspaceLatestBuild.TemplateVersionID)
|
||||
|
||||
buildParameters, err := client.WorkspaceBuildParameters(ctx, workspaceLatestBuild.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, buildParameters, 3)
|
||||
require.Contains(t, buildParameters, codersdk.WorkspaceBuildParameter{Name: firstParameterName, Value: firstParameterValue})
|
||||
require.Contains(t, buildParameters, codersdk.WorkspaceBuildParameter{Name: secondParameterName, Value: secondParameterValue})
|
||||
require.Contains(t, buildParameters, codersdk.WorkspaceBuildParameter{Name: immutableParameterName, Value: immutableParameterValue})
|
||||
})
|
||||
|
||||
t.Run("RichParametersFile", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -18,14 +18,16 @@ type workspaceParameterFlags struct {
|
|||
promptBuildOptions bool
|
||||
buildOptions []string
|
||||
|
||||
richParameterFile string
|
||||
richParameters []string
|
||||
richParameterFile string
|
||||
richParameters []string
|
||||
richParameterDefaults []string
|
||||
|
||||
promptRichParameters bool
|
||||
}
|
||||
|
||||
func (wpf *workspaceParameterFlags) allOptions() []serpent.Option {
|
||||
options := append(wpf.cliBuildOptions(), wpf.cliParameters()...)
|
||||
options = append(options, wpf.cliParameterDefaults()...)
|
||||
return append(options, wpf.alwaysPrompt())
|
||||
}
|
||||
|
||||
|
@ -62,6 +64,17 @@ func (wpf *workspaceParameterFlags) cliParameters() []serpent.Option {
|
|||
}
|
||||
}
|
||||
|
||||
func (wpf *workspaceParameterFlags) cliParameterDefaults() []serpent.Option {
|
||||
return serpent.OptionSet{
|
||||
serpent.Option{
|
||||
Flag: "parameter-default",
|
||||
Env: "CODER_RICH_PARAMETER_DEFAULT",
|
||||
Description: `Rich parameter default values in the format "name=value".`,
|
||||
Value: serpent.StringArrayOf(&wpf.richParameterDefaults),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wpf *workspaceParameterFlags) alwaysPrompt() serpent.Option {
|
||||
return serpent.Option{
|
||||
Flag: "always-prompt",
|
||||
|
|
|
@ -26,9 +26,10 @@ type ParameterResolver struct {
|
|||
lastBuildParameters []codersdk.WorkspaceBuildParameter
|
||||
sourceWorkspaceParameters []codersdk.WorkspaceBuildParameter
|
||||
|
||||
richParameters []codersdk.WorkspaceBuildParameter
|
||||
richParametersFile map[string]string
|
||||
buildOptions []codersdk.WorkspaceBuildParameter
|
||||
richParameters []codersdk.WorkspaceBuildParameter
|
||||
richParametersDefaults map[string]string
|
||||
richParametersFile map[string]string
|
||||
buildOptions []codersdk.WorkspaceBuildParameter
|
||||
|
||||
promptRichParameters bool
|
||||
promptBuildOptions bool
|
||||
|
@ -59,6 +60,16 @@ func (pr *ParameterResolver) WithRichParametersFile(fileMap map[string]string) *
|
|||
return pr
|
||||
}
|
||||
|
||||
func (pr *ParameterResolver) WithRichParametersDefaults(params []codersdk.WorkspaceBuildParameter) *ParameterResolver {
|
||||
if pr.richParametersDefaults == nil {
|
||||
pr.richParametersDefaults = make(map[string]string)
|
||||
}
|
||||
for _, p := range params {
|
||||
pr.richParametersDefaults[p.Name] = p.Value
|
||||
}
|
||||
return pr
|
||||
}
|
||||
|
||||
func (pr *ParameterResolver) WithPromptRichParameters(promptRichParameters bool) *ParameterResolver {
|
||||
pr.promptRichParameters = promptRichParameters
|
||||
return pr
|
||||
|
@ -227,7 +238,7 @@ func (pr *ParameterResolver) resolveWithInput(resolved []codersdk.WorkspaceBuild
|
|||
(action == WorkspaceUpdate && tvp.Mutable && tvp.Required) ||
|
||||
(action == WorkspaceUpdate && !tvp.Mutable && firstTimeUse) ||
|
||||
(tvp.Mutable && !tvp.Ephemeral && pr.promptRichParameters) {
|
||||
parameterValue, err := cliui.RichParameter(inv, tvp)
|
||||
parameterValue, err := cliui.RichParameter(inv, tvp, pr.richParametersDefaults)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
18
cli/start.go
18
cli/start.go
|
@ -99,7 +99,12 @@ func buildWorkspaceStartRequest(inv *serpent.Invocation, client *codersdk.Client
|
|||
|
||||
cliRichParameters, err := asWorkspaceBuildParameters(parameterFlags.richParameters)
|
||||
if err != nil {
|
||||
return codersdk.CreateWorkspaceBuildRequest{}, xerrors.Errorf("unable to parse build options: %w", err)
|
||||
return codersdk.CreateWorkspaceBuildRequest{}, xerrors.Errorf("unable to parse rich parameters: %w", err)
|
||||
}
|
||||
|
||||
cliRichParameterDefaults, err := asWorkspaceBuildParameters(parameterFlags.richParameterDefaults)
|
||||
if err != nil {
|
||||
return codersdk.CreateWorkspaceBuildRequest{}, xerrors.Errorf("unable to parse rich parameter defaults: %w", err)
|
||||
}
|
||||
|
||||
buildParameters, err := prepWorkspaceBuild(inv, client, prepWorkspaceBuildArgs{
|
||||
|
@ -108,11 +113,12 @@ func buildWorkspaceStartRequest(inv *serpent.Invocation, client *codersdk.Client
|
|||
NewWorkspaceName: workspace.Name,
|
||||
LastBuildParameters: lastBuildParameters,
|
||||
|
||||
PromptBuildOptions: parameterFlags.promptBuildOptions,
|
||||
BuildOptions: buildOptions,
|
||||
PromptRichParameters: parameterFlags.promptRichParameters,
|
||||
RichParameters: cliRichParameters,
|
||||
RichParameterFile: parameterFlags.richParameterFile,
|
||||
PromptBuildOptions: parameterFlags.promptBuildOptions,
|
||||
BuildOptions: buildOptions,
|
||||
PromptRichParameters: parameterFlags.promptRichParameters,
|
||||
RichParameters: cliRichParameters,
|
||||
RichParameterFile: parameterFlags.richParameterFile,
|
||||
RichParameterDefaults: cliRichParameterDefaults,
|
||||
})
|
||||
if err != nil {
|
||||
return codersdk.CreateWorkspaceBuildRequest{}, err
|
||||
|
|
|
@ -20,6 +20,9 @@ OPTIONS:
|
|||
--parameter string-array, $CODER_RICH_PARAMETER
|
||||
Rich parameter value in the format "name=value".
|
||||
|
||||
--parameter-default string-array, $CODER_RICH_PARAMETER_DEFAULT
|
||||
Rich parameter default values in the format "name=value".
|
||||
|
||||
--rich-parameter-file string, $CODER_RICH_PARAMETER_FILE
|
||||
Specify a file path with values for rich parameters defined in the
|
||||
template.
|
||||
|
|
|
@ -19,6 +19,9 @@ OPTIONS:
|
|||
--parameter string-array, $CODER_RICH_PARAMETER
|
||||
Rich parameter value in the format "name=value".
|
||||
|
||||
--parameter-default string-array, $CODER_RICH_PARAMETER_DEFAULT
|
||||
Rich parameter default values in the format "name=value".
|
||||
|
||||
--rich-parameter-file string, $CODER_RICH_PARAMETER_FILE
|
||||
Specify a file path with values for rich parameters defined in the
|
||||
template.
|
||||
|
|
|
@ -19,6 +19,9 @@ OPTIONS:
|
|||
--parameter string-array, $CODER_RICH_PARAMETER
|
||||
Rich parameter value in the format "name=value".
|
||||
|
||||
--parameter-default string-array, $CODER_RICH_PARAMETER_DEFAULT
|
||||
Rich parameter default values in the format "name=value".
|
||||
|
||||
--rich-parameter-file string, $CODER_RICH_PARAMETER_FILE
|
||||
Specify a file path with values for rich parameters defined in the
|
||||
template.
|
||||
|
|
|
@ -21,6 +21,9 @@ OPTIONS:
|
|||
--parameter string-array, $CODER_RICH_PARAMETER
|
||||
Rich parameter value in the format "name=value".
|
||||
|
||||
--parameter-default string-array, $CODER_RICH_PARAMETER_DEFAULT
|
||||
Rich parameter default values in the format "name=value".
|
||||
|
||||
--rich-parameter-file string, $CODER_RICH_PARAMETER_FILE
|
||||
Specify a file path with values for rich parameters defined in the
|
||||
template.
|
||||
|
|
|
@ -8541,6 +8541,10 @@ const docTemplate = `{
|
|||
"description": "DashboardURL is the URL to hit the deployment's dashboard.\nFor external workspace proxies, this is the coderd they are connected\nto.",
|
||||
"type": "string"
|
||||
},
|
||||
"deployment_id": {
|
||||
"description": "DeploymentID is the unique identifier for this deployment.",
|
||||
"type": "string"
|
||||
},
|
||||
"external_url": {
|
||||
"description": "ExternalURL references the current Coder version.\nFor production builds, this will link directly to a release. For development builds, this will link to a commit.",
|
||||
"type": "string"
|
||||
|
|
|
@ -7599,6 +7599,10 @@
|
|||
"description": "DashboardURL is the URL to hit the deployment's dashboard.\nFor external workspace proxies, this is the coderd they are connected\nto.",
|
||||
"type": "string"
|
||||
},
|
||||
"deployment_id": {
|
||||
"description": "DeploymentID is the unique identifier for this deployment.",
|
||||
"type": "string"
|
||||
},
|
||||
"external_url": {
|
||||
"description": "ExternalURL references the current Coder version.\nFor production builds, this will link directly to a release. For development builds, this will link to a commit.",
|
||||
"type": "string"
|
||||
|
|
|
@ -64,7 +64,7 @@ func TestValidate(t *testing.T) {
|
|||
|
||||
func TestExpiresSoon(t *testing.T) {
|
||||
t.Parallel()
|
||||
const threshold = 2
|
||||
const threshold = 1
|
||||
|
||||
for _, c := range azureidentity.Certificates {
|
||||
block, rest := pem.Decode([]byte(c))
|
||||
|
|
|
@ -436,6 +436,15 @@ func New(options *Options) *API {
|
|||
|
||||
api.AppearanceFetcher.Store(&appearance.DefaultFetcher)
|
||||
api.PortSharer.Store(&portsharing.DefaultPortSharer)
|
||||
buildInfo := codersdk.BuildInfoResponse{
|
||||
ExternalURL: buildinfo.ExternalURL(),
|
||||
Version: buildinfo.Version(),
|
||||
AgentAPIVersion: AgentAPIVersionREST,
|
||||
DashboardURL: api.AccessURL.String(),
|
||||
WorkspaceProxy: false,
|
||||
UpgradeMessage: api.DeploymentValues.CLIUpgradeMessage.String(),
|
||||
DeploymentID: api.DeploymentID,
|
||||
}
|
||||
api.SiteHandler = site.New(&site.Options{
|
||||
BinFS: binFS,
|
||||
BinHashes: binHashes,
|
||||
|
@ -444,6 +453,7 @@ func New(options *Options) *API {
|
|||
OAuth2Configs: oauthConfigs,
|
||||
DocsURL: options.DeploymentValues.DocsURL.String(),
|
||||
AppearanceFetcher: &api.AppearanceFetcher,
|
||||
BuildInfo: buildInfo,
|
||||
})
|
||||
api.SiteHandler.Experiments.Store(&experiments)
|
||||
|
||||
|
@ -735,7 +745,7 @@ func New(options *Options) *API {
|
|||
// All CSP errors will be logged
|
||||
r.Post("/csp/reports", api.logReportCSPViolations)
|
||||
|
||||
r.Get("/buildinfo", buildInfo(api.AccessURL, api.DeploymentValues.CLIUpgradeMessage.String()))
|
||||
r.Get("/buildinfo", buildInfoHandler(buildInfo))
|
||||
// /regions is overridden in the enterprise version
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(apiKeyMiddleware)
|
||||
|
|
|
@ -2,9 +2,7 @@ package coderd
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/coder/coder/v2/buildinfo"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
|
@ -68,16 +66,10 @@ func (api *API) deploymentStats(rw http.ResponseWriter, r *http.Request) {
|
|||
// @Tags General
|
||||
// @Success 200 {object} codersdk.BuildInfoResponse
|
||||
// @Router /buildinfo [get]
|
||||
func buildInfo(accessURL *url.URL, upgradeMessage string) http.HandlerFunc {
|
||||
func buildInfoHandler(resp codersdk.BuildInfoResponse) http.HandlerFunc {
|
||||
// This is in a handler so that we can generate API docs info.
|
||||
return func(rw http.ResponseWriter, r *http.Request) {
|
||||
httpapi.Write(r.Context(), rw, http.StatusOK, codersdk.BuildInfoResponse{
|
||||
ExternalURL: buildinfo.ExternalURL(),
|
||||
Version: buildinfo.Version(),
|
||||
AgentAPIVersion: AgentAPIVersionREST,
|
||||
DashboardURL: accessURL.String(),
|
||||
WorkspaceProxy: false,
|
||||
UpgradeMessage: upgradeMessage,
|
||||
})
|
||||
httpapi.Write(r.Context(), rw, http.StatusOK, resp)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -563,6 +563,9 @@ func applyDefaultsToConfig(config *codersdk.ExternalAuthConfig) {
|
|||
|
||||
// Dynamic defaults
|
||||
switch codersdk.EnhancedExternalAuthProvider(config.Type) {
|
||||
case codersdk.EnhancedExternalAuthProviderGitLab:
|
||||
copyDefaultSettings(config, gitlabDefaults(config))
|
||||
return
|
||||
case codersdk.EnhancedExternalAuthProviderBitBucketServer:
|
||||
copyDefaultSettings(config, bitbucketServerDefaults(config))
|
||||
return
|
||||
|
@ -667,6 +670,44 @@ func bitbucketServerDefaults(config *codersdk.ExternalAuthConfig) codersdk.Exter
|
|||
return defaults
|
||||
}
|
||||
|
||||
// gitlabDefaults returns a static config if using the gitlab cloud offering.
|
||||
// The values are dynamic if using a self-hosted gitlab.
|
||||
// When the decision is not obvious, just defer to the cloud defaults.
|
||||
// Any user specific fields will override this if provided.
|
||||
func gitlabDefaults(config *codersdk.ExternalAuthConfig) codersdk.ExternalAuthConfig {
|
||||
cloud := codersdk.ExternalAuthConfig{
|
||||
AuthURL: "https://gitlab.com/oauth/authorize",
|
||||
TokenURL: "https://gitlab.com/oauth/token",
|
||||
ValidateURL: "https://gitlab.com/oauth/token/info",
|
||||
DisplayName: "GitLab",
|
||||
DisplayIcon: "/icon/gitlab.svg",
|
||||
Regex: `^(https?://)?gitlab\.com(/.*)?$`,
|
||||
Scopes: []string{"write_repository"},
|
||||
}
|
||||
|
||||
if config.AuthURL == "" || config.AuthURL == cloud.AuthURL {
|
||||
return cloud
|
||||
}
|
||||
|
||||
au, err := url.Parse(config.AuthURL)
|
||||
if err != nil || au.Host == "gitlab.com" {
|
||||
// If the AuthURL is not a valid URL or is using the cloud,
|
||||
// use the cloud static defaults.
|
||||
return cloud
|
||||
}
|
||||
|
||||
// At this point, assume it is self-hosted and use the AuthURL
|
||||
return codersdk.ExternalAuthConfig{
|
||||
DisplayName: cloud.DisplayName,
|
||||
Scopes: cloud.Scopes,
|
||||
DisplayIcon: cloud.DisplayIcon,
|
||||
AuthURL: au.ResolveReference(&url.URL{Path: "/oauth/authorize"}).String(),
|
||||
TokenURL: au.ResolveReference(&url.URL{Path: "/oauth/token"}).String(),
|
||||
ValidateURL: au.ResolveReference(&url.URL{Path: "/oauth/token/info"}).String(),
|
||||
Regex: fmt.Sprintf(`^(https?://)?%s(/.*)?$`, strings.ReplaceAll(au.Host, ".", `\.`)),
|
||||
}
|
||||
}
|
||||
|
||||
func jfrogArtifactoryDefaults(config *codersdk.ExternalAuthConfig) codersdk.ExternalAuthConfig {
|
||||
defaults := codersdk.ExternalAuthConfig{
|
||||
DisplayName: "JFrog Artifactory",
|
||||
|
@ -789,15 +830,6 @@ var staticDefaults = map[codersdk.EnhancedExternalAuthProvider]codersdk.External
|
|||
Regex: `^(https?://)?bitbucket\.org(/.*)?$`,
|
||||
Scopes: []string{"account", "repository:write"},
|
||||
},
|
||||
codersdk.EnhancedExternalAuthProviderGitLab: {
|
||||
AuthURL: "https://gitlab.com/oauth/authorize",
|
||||
TokenURL: "https://gitlab.com/oauth/token",
|
||||
ValidateURL: "https://gitlab.com/oauth/token/info",
|
||||
DisplayName: "GitLab",
|
||||
DisplayIcon: "/icon/gitlab.svg",
|
||||
Regex: `^(https?://)?gitlab\.com(/.*)?$`,
|
||||
Scopes: []string{"write_repository"},
|
||||
},
|
||||
codersdk.EnhancedExternalAuthProviderGitHub: {
|
||||
AuthURL: xgithub.Endpoint.AuthURL,
|
||||
TokenURL: xgithub.Endpoint.TokenURL,
|
||||
|
|
|
@ -8,6 +8,112 @@ import (
|
|||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
func TestGitlabDefaults(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// The default cloud setup. Copying this here as hard coded
|
||||
// values.
|
||||
cloud := codersdk.ExternalAuthConfig{
|
||||
Type: string(codersdk.EnhancedExternalAuthProviderGitLab),
|
||||
ID: string(codersdk.EnhancedExternalAuthProviderGitLab),
|
||||
AuthURL: "https://gitlab.com/oauth/authorize",
|
||||
TokenURL: "https://gitlab.com/oauth/token",
|
||||
ValidateURL: "https://gitlab.com/oauth/token/info",
|
||||
DisplayName: "GitLab",
|
||||
DisplayIcon: "/icon/gitlab.svg",
|
||||
Regex: `^(https?://)?gitlab\.com(/.*)?$`,
|
||||
Scopes: []string{"write_repository"},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input codersdk.ExternalAuthConfig
|
||||
expected codersdk.ExternalAuthConfig
|
||||
mutateExpected func(*codersdk.ExternalAuthConfig)
|
||||
}{
|
||||
// Cloud
|
||||
{
|
||||
name: "OnlyType",
|
||||
input: codersdk.ExternalAuthConfig{
|
||||
Type: string(codersdk.EnhancedExternalAuthProviderGitLab),
|
||||
},
|
||||
expected: cloud,
|
||||
},
|
||||
{
|
||||
// If someone was to manually configure the gitlab cli.
|
||||
name: "CloudByConfig",
|
||||
input: codersdk.ExternalAuthConfig{
|
||||
Type: string(codersdk.EnhancedExternalAuthProviderGitLab),
|
||||
AuthURL: "https://gitlab.com/oauth/authorize",
|
||||
},
|
||||
expected: cloud,
|
||||
},
|
||||
{
|
||||
// Changing some of the defaults of the cloud option
|
||||
name: "CloudWithChanges",
|
||||
input: codersdk.ExternalAuthConfig{
|
||||
Type: string(codersdk.EnhancedExternalAuthProviderGitLab),
|
||||
// Adding an extra query param intentionally to break simple
|
||||
// string comparisons.
|
||||
AuthURL: "https://gitlab.com/oauth/authorize?foo=bar",
|
||||
DisplayName: "custom",
|
||||
Regex: ".*",
|
||||
},
|
||||
expected: cloud,
|
||||
mutateExpected: func(config *codersdk.ExternalAuthConfig) {
|
||||
config.AuthURL = "https://gitlab.com/oauth/authorize?foo=bar"
|
||||
config.DisplayName = "custom"
|
||||
config.Regex = ".*"
|
||||
},
|
||||
},
|
||||
// Self-hosted
|
||||
{
|
||||
// Dynamically figures out the Validate, Token, and Regex fields.
|
||||
name: "SelfHostedOnlyAuthURL",
|
||||
input: codersdk.ExternalAuthConfig{
|
||||
Type: string(codersdk.EnhancedExternalAuthProviderGitLab),
|
||||
AuthURL: "https://gitlab.company.org/oauth/authorize?foo=bar",
|
||||
},
|
||||
expected: cloud,
|
||||
mutateExpected: func(config *codersdk.ExternalAuthConfig) {
|
||||
config.AuthURL = "https://gitlab.company.org/oauth/authorize?foo=bar"
|
||||
config.ValidateURL = "https://gitlab.company.org/oauth/token/info"
|
||||
config.TokenURL = "https://gitlab.company.org/oauth/token"
|
||||
config.Regex = `^(https?://)?gitlab\.company\.org(/.*)?$`
|
||||
},
|
||||
},
|
||||
{
|
||||
// Strange values
|
||||
name: "RandomValues",
|
||||
input: codersdk.ExternalAuthConfig{
|
||||
Type: string(codersdk.EnhancedExternalAuthProviderGitLab),
|
||||
AuthURL: "https://auth.com/auth",
|
||||
ValidateURL: "https://validate.com/validate",
|
||||
TokenURL: "https://token.com/token",
|
||||
Regex: "random",
|
||||
},
|
||||
expected: cloud,
|
||||
mutateExpected: func(config *codersdk.ExternalAuthConfig) {
|
||||
config.AuthURL = "https://auth.com/auth"
|
||||
config.ValidateURL = "https://validate.com/validate"
|
||||
config.TokenURL = "https://token.com/token"
|
||||
config.Regex = `random`
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, c := range tests {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
applyDefaultsToConfig(&c.input)
|
||||
if c.mutateExpected != nil {
|
||||
c.mutateExpected(&c.expected)
|
||||
}
|
||||
require.Equal(t, c.input, c.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_bitbucketServerConfigDefaults(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -2151,6 +2151,9 @@ type BuildInfoResponse struct {
|
|||
// UpgradeMessage is the message displayed to users when an outdated client
|
||||
// is detected.
|
||||
UpgradeMessage string `json:"upgrade_message"`
|
||||
|
||||
// DeploymentID is the unique identifier for this deployment.
|
||||
DeploymentID string `json:"deployment_id"`
|
||||
}
|
||||
|
||||
type WorkspaceProxyBuildInfo struct {
|
||||
|
|
|
@ -55,6 +55,7 @@ curl -X GET http://coder-server:8080/api/v2/buildinfo \
|
|||
{
|
||||
"agent_api_version": "string",
|
||||
"dashboard_url": "string",
|
||||
"deployment_id": "string",
|
||||
"external_url": "string",
|
||||
"upgrade_message": "string",
|
||||
"version": "string",
|
||||
|
|
|
@ -1178,6 +1178,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
{
|
||||
"agent_api_version": "string",
|
||||
"dashboard_url": "string",
|
||||
"deployment_id": "string",
|
||||
"external_url": "string",
|
||||
"upgrade_message": "string",
|
||||
"version": "string",
|
||||
|
@ -1191,6 +1192,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||
| ------------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `agent_api_version` | string | false | | Agent api version is the current version of the Agent API (back versions MAY still be supported). |
|
||||
| `dashboard_url` | string | false | | Dashboard URL is the URL to hit the deployment's dashboard. For external workspace proxies, this is the coderd they are connected to. |
|
||||
| `deployment_id` | string | false | | Deployment ID is the unique identifier for this deployment. |
|
||||
| `external_url` | string | false | | External URL references the current Coder version. For production builds, this will link directly to a release. For development builds, this will link to a commit. |
|
||||
| `upgrade_message` | string | false | | Upgrade message is the message displayed to users when an outdated client is detected. |
|
||||
| `version` | string | false | | Version returns the semantic version of the build. |
|
||||
|
|
|
@ -91,3 +91,12 @@ Rich parameter value in the format "name=value".
|
|||
| Environment | <code>$CODER_RICH_PARAMETER_FILE</code> |
|
||||
|
||||
Specify a file path with values for rich parameters defined in the template.
|
||||
|
||||
### --parameter-default
|
||||
|
||||
| | |
|
||||
| ----------- | ------------------------------------------ |
|
||||
| Type | <code>string-array</code> |
|
||||
| Environment | <code>$CODER_RICH_PARAMETER_DEFAULT</code> |
|
||||
|
||||
Rich parameter default values in the format "name=value".
|
||||
|
|
|
@ -55,6 +55,15 @@ Rich parameter value in the format "name=value".
|
|||
|
||||
Specify a file path with values for rich parameters defined in the template.
|
||||
|
||||
### --parameter-default
|
||||
|
||||
| | |
|
||||
| ----------- | ------------------------------------------ |
|
||||
| Type | <code>string-array</code> |
|
||||
| Environment | <code>$CODER_RICH_PARAMETER_DEFAULT</code> |
|
||||
|
||||
Rich parameter default values in the format "name=value".
|
||||
|
||||
### --always-prompt
|
||||
|
||||
| | |
|
||||
|
|
|
@ -55,6 +55,15 @@ Rich parameter value in the format "name=value".
|
|||
|
||||
Specify a file path with values for rich parameters defined in the template.
|
||||
|
||||
### --parameter-default
|
||||
|
||||
| | |
|
||||
| ----------- | ------------------------------------------ |
|
||||
| Type | <code>string-array</code> |
|
||||
| Environment | <code>$CODER_RICH_PARAMETER_DEFAULT</code> |
|
||||
|
||||
Rich parameter default values in the format "name=value".
|
||||
|
||||
### --always-prompt
|
||||
|
||||
| | |
|
||||
|
|
|
@ -53,6 +53,15 @@ Rich parameter value in the format "name=value".
|
|||
|
||||
Specify a file path with values for rich parameters defined in the template.
|
||||
|
||||
### --parameter-default
|
||||
|
||||
| | |
|
||||
| ----------- | ------------------------------------------ |
|
||||
| Type | <code>string-array</code> |
|
||||
| Environment | <code>$CODER_RICH_PARAMETER_DEFAULT</code> |
|
||||
|
||||
Rich parameter default values in the format "name=value".
|
||||
|
||||
### --always-prompt
|
||||
|
||||
| | |
|
||||
|
|
|
@ -157,7 +157,6 @@ resource "coder_agent" "dev" {
|
|||
os = "linux"
|
||||
dir = local.repo_dir
|
||||
env = {
|
||||
GITHUB_TOKEN : data.coder_external_auth.github.access_token,
|
||||
OIDC_TOKEN : data.coder_workspace.me.owner_oidc_access_token,
|
||||
}
|
||||
startup_script_behavior = "blocking"
|
||||
|
|
|
@ -147,13 +147,14 @@ func NewWithAPI(t *testing.T, options *Options) (
|
|||
}
|
||||
|
||||
type LicenseOptions struct {
|
||||
AccountType string
|
||||
AccountID string
|
||||
Trial bool
|
||||
AllFeatures bool
|
||||
GraceAt time.Time
|
||||
ExpiresAt time.Time
|
||||
Features license.Features
|
||||
AccountType string
|
||||
AccountID string
|
||||
DeploymentIDs []string
|
||||
Trial bool
|
||||
AllFeatures bool
|
||||
GraceAt time.Time
|
||||
ExpiresAt time.Time
|
||||
Features license.Features
|
||||
}
|
||||
|
||||
// AddFullLicense generates a license with all features enabled.
|
||||
|
@ -190,6 +191,7 @@ func GenerateLicense(t *testing.T, options LicenseOptions) string {
|
|||
LicenseExpires: jwt.NewNumericDate(options.GraceAt),
|
||||
AccountType: options.AccountType,
|
||||
AccountID: options.AccountID,
|
||||
DeploymentIDs: options.DeploymentIDs,
|
||||
Trial: options.Trial,
|
||||
Version: license.CurrentVersion,
|
||||
AllFeatures: options.AllFeatures,
|
||||
|
|
|
@ -257,14 +257,16 @@ type Claims struct {
|
|||
// the end of the grace period (identical to LicenseExpires if there is no grace period).
|
||||
// The reason we use the standard claim for the end of the grace period is that we want JWT
|
||||
// processing libraries to consider the token "valid" until then.
|
||||
LicenseExpires *jwt.NumericDate `json:"license_expires,omitempty"`
|
||||
AccountType string `json:"account_type,omitempty"`
|
||||
AccountID string `json:"account_id,omitempty"`
|
||||
Trial bool `json:"trial"`
|
||||
AllFeatures bool `json:"all_features"`
|
||||
Version uint64 `json:"version"`
|
||||
Features Features `json:"features"`
|
||||
RequireTelemetry bool `json:"require_telemetry,omitempty"`
|
||||
LicenseExpires *jwt.NumericDate `json:"license_expires,omitempty"`
|
||||
AccountType string `json:"account_type,omitempty"`
|
||||
AccountID string `json:"account_id,omitempty"`
|
||||
// DeploymentIDs enforces the license can only be used on a set of deployments.
|
||||
DeploymentIDs []string `json:"deployment_ids,omitempty"`
|
||||
Trial bool `json:"trial"`
|
||||
AllFeatures bool `json:"all_features"`
|
||||
Version uint64 `json:"version"`
|
||||
Features Features `json:"features"`
|
||||
RequireTelemetry bool `json:"require_telemetry,omitempty"`
|
||||
}
|
||||
|
||||
// ParseRaw consumes a license and returns the claims.
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -120,6 +121,15 @@ func (api *API) postLicense(rw http.ResponseWriter, r *http.Request) {
|
|||
// old licenses with a uuid.
|
||||
id = uuid.New()
|
||||
}
|
||||
if len(claims.DeploymentIDs) > 0 && !slices.Contains(claims.DeploymentIDs, api.AGPL.DeploymentID) {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "License cannot be used on this deployment!",
|
||||
Detail: fmt.Sprintf("The provided license is locked to the following deployments: %q. "+
|
||||
"Your deployment identifier is %q. Please contact sales.", claims.DeploymentIDs, api.AGPL.DeploymentID),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
dl, err := api.Database.InsertLicense(ctx, database.InsertLicenseParams{
|
||||
UploadedAt: dbtime.Now(),
|
||||
JWT: addLicense.License,
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
|
@ -36,6 +37,22 @@ func TestPostLicense(t *testing.T) {
|
|||
assert.EqualValues(t, 1, features[codersdk.FeatureAuditLog])
|
||||
})
|
||||
|
||||
t.Run("InvalidDeploymentID", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// The generated deployment will start out with a different deployment ID.
|
||||
client, _ := coderdenttest.New(t, &coderdenttest.Options{DontAddLicense: true})
|
||||
license := coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
|
||||
DeploymentIDs: []string{uuid.NewString()},
|
||||
})
|
||||
_, err := client.AddLicense(context.Background(), codersdk.AddLicenseRequest{
|
||||
License: license,
|
||||
})
|
||||
errResp := &codersdk.Error{}
|
||||
require.ErrorAs(t, err, &errResp)
|
||||
require.Equal(t, http.StatusBadRequest, errResp.StatusCode())
|
||||
require.Contains(t, errResp.Message, "License cannot be used on this deployment!")
|
||||
})
|
||||
|
||||
t.Run("Unauthorized", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _ := coderdenttest.New(t, &coderdenttest.Options{DontAddLicense: true})
|
||||
|
|
15
go.mod
15
go.mod
|
@ -42,7 +42,7 @@ replace github.com/dlclark/regexp2 => github.com/dlclark/regexp2 v1.7.0
|
|||
|
||||
// There are a few minor changes we make to Tailscale that we're slowly upstreaming. Compare here:
|
||||
// https://github.com/tailscale/tailscale/compare/main...coder:tailscale:main
|
||||
replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20240401202854-d329bbdb530d
|
||||
replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20240430122706-f586aa40c0c1
|
||||
|
||||
// Fixes a race-condition in coder/wgtunnel.
|
||||
// Upstream PR: https://github.com/WireGuard/wireguard-go/pull/85
|
||||
|
@ -144,7 +144,7 @@ require (
|
|||
github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02
|
||||
github.com/imulab/go-scim/pkg/v2 v2.2.0
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.0
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
github.com/jmoiron/sqlx v1.4.0
|
||||
github.com/justinas/nosurf v1.1.1
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f
|
||||
|
@ -153,7 +153,7 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mitchellh/go-wordwrap v1.0.1
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c
|
||||
github.com/moby/moby v26.0.1+incompatible
|
||||
github.com/moby/moby v26.1.0+incompatible
|
||||
github.com/muesli/termenv v0.15.2
|
||||
github.com/open-policy-agent/opa v0.58.0
|
||||
github.com/ory/dockertest/v3 v3.10.0
|
||||
|
@ -200,7 +200,7 @@ require (
|
|||
golang.org/x/tools v0.20.0
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028
|
||||
golang.zx2c4.com/wireguard v0.0.0-20230704135630-469159ecf7d1
|
||||
google.golang.org/api v0.175.0
|
||||
google.golang.org/api v0.176.1
|
||||
google.golang.org/grpc v1.63.2
|
||||
google.golang.org/protobuf v1.33.0
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.61.0
|
||||
|
@ -222,8 +222,8 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/auth v0.2.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.1 // indirect
|
||||
cloud.google.com/go/auth v0.3.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
|
||||
github.com/DataDog/go-libddwaf/v2 v2.3.1 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.13.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect
|
||||
|
@ -237,7 +237,7 @@ require (
|
|||
require (
|
||||
cloud.google.com/go/logging v1.9.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.5.5 // indirect
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/DataDog/appsec-internal-go v1.4.1 // indirect
|
||||
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 // indirect
|
||||
|
@ -307,7 +307,6 @@ require (
|
|||
github.com/go-openapi/swag v0.22.8 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||
github.com/go-test/deep v1.0.8 // indirect
|
||||
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
|
|
39
go.sum
39
go.sum
|
@ -1,18 +1,18 @@
|
|||
cdr.dev/slog v1.6.2-0.20240126064726-20367d4aede6 h1:KHblWIE/KHOwQ6lEbMZt6YpcGve2FEZ1sDtrW1Am5UI=
|
||||
cdr.dev/slog v1.6.2-0.20240126064726-20367d4aede6/go.mod h1:NaoTA7KwopCrnaSb0JXTC0PTp/O/Y83Lndnq0OEV3ZQ=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go/auth v0.2.2 h1:gmxNJs4YZYcw6YvKRtVBaF2fyUE6UrWPyzU8jHvYfmI=
|
||||
cloud.google.com/go/auth v0.2.2/go.mod h1:2bDNJWtWziDT3Pu1URxHHbkHE/BbOCuyUiKIGcNvafo=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.1 h1:VSPmMmUlT8CkIZ2PzD9AlLN+R3+D1clXMWHHa6vG/Ag=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.1/go.mod h1:tOdK/k+D2e4GEwfBRA48dKNQiDsqIXxLh7VU319eV0g=
|
||||
cloud.google.com/go/auth v0.3.0 h1:PRyzEpGfx/Z9e8+lHsbkoUVXD0gnu4MNmm7Gp8TQNIs=
|
||||
cloud.google.com/go/auth v0.3.0/go.mod h1:lBv6NKTWp8E3LPzmO1TbiiRKc4drLOfHsgmlH9ogv5w=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cloud.google.com/go/logging v1.9.0 h1:iEIOXFO9EmSiTjDmfpbRjOxECO7R8C7b8IXUGOj7xZw=
|
||||
cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE=
|
||||
cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg=
|
||||
cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s=
|
||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
filippo.io/mkcert v1.4.4 h1:8eVbbwfVlaqUM7OwuftKc2nuYOoTDQWqsoXmzoXZdbc=
|
||||
filippo.io/mkcert v1.4.4/go.mod h1:VyvOchVuAye3BoUsPUOOofKygVwLV2KQMVFJNRq+1dA=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ=
|
||||
|
@ -217,8 +217,8 @@ github.com/coder/serpent v0.7.0 h1:zGpD2GlF3lKIVkMjNGKbkip88qzd5r/TRcc30X/SrT0=
|
|||
github.com/coder/serpent v0.7.0/go.mod h1:REkJ5ZFHQUWFTPLExhXYZ1CaHFjxvGNRlLXLdsI08YA=
|
||||
github.com/coder/ssh v0.0.0-20231128192721-70855dedb788 h1:YoUSJ19E8AtuUFVYBpXuOD6a/zVP3rcxezNsoDseTUw=
|
||||
github.com/coder/ssh v0.0.0-20231128192721-70855dedb788/go.mod h1:aGQbuCLyhRLMzZF067xc84Lh7JDs1FKwCmF1Crl9dxQ=
|
||||
github.com/coder/tailscale v1.1.1-0.20240401202854-d329bbdb530d h1:IMvBC1GrCIiZFxpOYRQacZtdjnmsdWNAMilPz+kvdG4=
|
||||
github.com/coder/tailscale v1.1.1-0.20240401202854-d329bbdb530d/go.mod h1:L8tPrwSi31RAMEMV8rjb0vYTGs7rXt8rAHbqY/p41j4=
|
||||
github.com/coder/tailscale v1.1.1-0.20240430122706-f586aa40c0c1 h1:cu5YyztCk8FAOvP1sR3b/2D96EfvBAzKUu0B/Cqhg8U=
|
||||
github.com/coder/tailscale v1.1.1-0.20240430122706-f586aa40c0c1/go.mod h1:L8tPrwSi31RAMEMV8rjb0vYTGs7rXt8rAHbqY/p41j4=
|
||||
github.com/coder/terraform-provider-coder v0.21.0 h1:aoDmFJULYZpS66EIAZuNY4IxElaDkdRaWMWp9ScD2R8=
|
||||
github.com/coder/terraform-provider-coder v0.21.0/go.mod h1:hqxd15PJeftFBOnGBBPN6WfNQutZtnahwwPeV8U6TyA=
|
||||
github.com/coder/wgtunnel v0.1.13-0.20231127054351-578bfff9b92a h1:KhR9LUVllMZ+e9lhubZ1HNrtJDgH5YLoTvpKwmrGag4=
|
||||
|
@ -380,9 +380,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
|||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
|
@ -582,8 +581,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
|
|||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
|
||||
|
@ -638,7 +637,6 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kUL
|
|||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
|
@ -670,9 +668,8 @@ github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC
|
|||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
||||
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
|
||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||
|
@ -703,8 +700,8 @@ github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374
|
|||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/moby v26.0.1+incompatible h1:vCKs/AM0lLYnMxFwpf8ycsOekPPPcGn0s0Iczqv3/ec=
|
||||
github.com/moby/moby v26.0.1+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
|
||||
github.com/moby/moby v26.1.0+incompatible h1:mjepCwMH0KpCgPvrXjqqyCeTCHgzO7p9TwZ2nQMI2qU=
|
||||
github.com/moby/moby v26.1.0+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -1158,8 +1155,8 @@ golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvY
|
|||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
||||
google.golang.org/api v0.175.0 h1:9bMDh10V9cBuU8N45Wlc3cKkItfqMRV0Fi8UscLEtbY=
|
||||
google.golang.org/api v0.175.0/go.mod h1:Rra+ltKu14pps/4xTycZfobMgLpbosoaaL7c+SEMrO8=
|
||||
google.golang.org/api v0.176.1 h1:DJSXnV6An+NhJ1J+GWtoF2nHEuqB1VNoTfnIbjNvwD4=
|
||||
google.golang.org/api v0.176.1/go.mod h1:j2MaSDYcvYV1lkZ1+SMW4IeF90SrEyFA+tluDYWRrFg=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
|
|
|
@ -35,6 +35,7 @@ os="${GOOS:-linux}"
|
|||
arch="${GOARCH:-amd64}"
|
||||
slim="${CODER_SLIM_BUILD:-0}"
|
||||
sign_darwin="${CODER_SIGN_DARWIN:-0}"
|
||||
sign_windows="${CODER_SIGN_WINDOWS:-0}"
|
||||
output_path=""
|
||||
agpl="${CODER_BUILD_AGPL:-0}"
|
||||
boringcrypto=${CODER_BUILD_BORINGCRYPTO:-0}
|
||||
|
@ -106,6 +107,11 @@ if [[ "$sign_darwin" == 1 ]]; then
|
|||
requiredenvs AC_CERTIFICATE_FILE AC_CERTIFICATE_PASSWORD_FILE
|
||||
fi
|
||||
|
||||
if [[ "$sign_windows" == 1 ]]; then
|
||||
dependencies java
|
||||
requiredenvs JSIGN_PATH EV_KEYSTORE EV_KEY EV_CERTIFICATE_PATH EV_TSA_URL GCLOUD_ACCESS_TOKEN
|
||||
fi
|
||||
|
||||
ldflags=(
|
||||
-X "'github.com/coder/coder/v2/buildinfo.tag=$version'"
|
||||
)
|
||||
|
@ -176,4 +182,8 @@ if [[ "$sign_darwin" == 1 ]] && [[ "$os" == "darwin" ]]; then
|
|||
execrelative ./sign_darwin.sh "$output_path" 1>&2
|
||||
fi
|
||||
|
||||
if [[ "$sign_windows" == 1 ]] && [[ "$os" == "windows" ]]; then
|
||||
execrelative ./sign_windows.sh "$output_path" 1>&2
|
||||
fi
|
||||
|
||||
echo "$output_path"
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
# [#pr-deployments](https://codercom.slack.com/archives/C05DNE982E8) Slack channel
|
||||
|
||||
set -euo pipefail
|
||||
# shellcheck source=scripts/lib.sh
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
||||
cdroot
|
||||
|
||||
# default settings
|
||||
dryRun=false
|
||||
|
@ -64,6 +67,9 @@ if $confirm; then
|
|||
fi
|
||||
fi
|
||||
|
||||
# Authenticate gh CLI
|
||||
gh_auth
|
||||
|
||||
# get branch name and pr number
|
||||
branchName=$(gh pr view --json headRefName | jq -r .headRefName)
|
||||
prNumber=$(gh pr view --json number | jq -r .number)
|
||||
|
|
|
@ -130,6 +130,22 @@ requiredenvs() {
|
|||
fi
|
||||
}
|
||||
|
||||
gh_auth() {
|
||||
local fail=0
|
||||
if [[ "${CODER:-}" == "true" ]]; then
|
||||
if ! output=$(coder external-auth access-token github 2>&1); then
|
||||
log "ERROR: Could not authenticate with GitHub."
|
||||
log "$output"
|
||||
fail=1
|
||||
else
|
||||
GITHUB_TOKEN=$(coder external-auth access-token github)
|
||||
export GITHUB_TOKEN
|
||||
fi
|
||||
else
|
||||
log "Please authenticate gh CLI by running 'gh auth login'"
|
||||
fi
|
||||
}
|
||||
|
||||
# maybedryrun prints the given program and flags, and then, if the first
|
||||
# argument is 0, executes it. The reason the first argument should be 0 is that
|
||||
# it is expected that you have a dry_run variable in your script that is set to
|
||||
|
|
|
@ -113,6 +113,9 @@ done
|
|||
# Check dependencies.
|
||||
dependencies gh jq sort
|
||||
|
||||
# Authenticate gh CLI
|
||||
gh_auth
|
||||
|
||||
if [[ -z $increment ]]; then
|
||||
# Default to patch versions.
|
||||
increment="patch"
|
||||
|
|
|
@ -31,6 +31,9 @@ range="${from_ref}..${to_ref}"
|
|||
# Check dependencies.
|
||||
dependencies gh
|
||||
|
||||
# Authenticate gh CLI
|
||||
gh_auth
|
||||
|
||||
COMMIT_METADATA_BREAKING=0
|
||||
declare -a COMMIT_METADATA_COMMITS
|
||||
declare -A COMMIT_METADATA_TITLE COMMIT_METADATA_HUMAN_TITLE COMMIT_METADATA_CATEGORY COMMIT_METADATA_AUTHORS
|
||||
|
@ -145,7 +148,6 @@ main() {
|
|||
done
|
||||
} | sort -t- -n | head -n 1
|
||||
)
|
||||
|
||||
# Get the labels for all PRs merged since the last release, this is
|
||||
# inexact based on date, so a few PRs part of the previous release may
|
||||
# be included.
|
||||
|
|
|
@ -57,6 +57,9 @@ done
|
|||
# Check dependencies.
|
||||
dependencies gh sort
|
||||
|
||||
# Authticate gh CLI
|
||||
gh_auth
|
||||
|
||||
if [[ -z ${old_version} ]]; then
|
||||
error "No old version specified"
|
||||
fi
|
||||
|
|
|
@ -71,6 +71,9 @@ done
|
|||
# Check dependencies
|
||||
dependencies gh
|
||||
|
||||
# Authenticate gh CLI
|
||||
gh_auth
|
||||
|
||||
# Remove the "v" prefix.
|
||||
version="${version#v}"
|
||||
if [[ "$version" == "" ]]; then
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# This script signs the provided windows binary with an Extended Validation
|
||||
# code signing certificate.
|
||||
#
|
||||
# Usage: ./sign_windows.sh path/to/binary
|
||||
#
|
||||
# On success, the input file will be signed using the EV cert.
|
||||
#
|
||||
# Depends on the jsign utility (and thus Java). Requires the following environment variables
|
||||
# to be set:
|
||||
# - $JSIGN_PATH: The path to the jsign jar.
|
||||
# - $EV_KEYSTORE: The name of the keyring containing the private key
|
||||
# - $EV_KEY: The name of the key.
|
||||
# - $EV_CERTIFICATE_PATH: The path to the certificate.
|
||||
# - $EV_TSA_URL: The url of the timestamp server to use.
|
||||
|
||||
set -euo pipefail
|
||||
# shellcheck source=scripts/lib.sh
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
||||
|
||||
# Check dependencies
|
||||
dependencies java
|
||||
requiredenvs JSIGN_PATH EV_KEYSTORE EV_KEY EV_CERTIFICATE_PATH EV_TSA_URL GCLOUD_ACCESS_TOKEN
|
||||
|
||||
java -jar "$JSIGN_PATH" \
|
||||
--storetype GOOGLECLOUD \
|
||||
--storepass "$GCLOUD_ACCESS_TOKEN" \
|
||||
--keystore "$EV_KEYSTORE" \
|
||||
--alias "$EV_KEY" \
|
||||
--certfile "$EV_CERTIFICATE_PATH" \
|
||||
--tsmode RFC3161 \
|
||||
--tsaurl "$EV_TSA_URL" \
|
||||
"$@" \
|
||||
1>&2
|
|
@ -34,7 +34,6 @@ import (
|
|||
"golang.org/x/sync/singleflight"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/buildinfo"
|
||||
"github.com/coder/coder/v2/coderd/appearance"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
||||
|
@ -78,6 +77,7 @@ type Options struct {
|
|||
SiteFS fs.FS
|
||||
OAuth2Configs *httpmw.OAuth2Configs
|
||||
DocsURL string
|
||||
BuildInfo codersdk.BuildInfoResponse
|
||||
AppearanceFetcher *atomic.Pointer[appearance.Fetcher]
|
||||
}
|
||||
|
||||
|
@ -149,12 +149,7 @@ func New(opts *Options) *Handler {
|
|||
// static files.
|
||||
OnlyFiles(opts.SiteFS))),
|
||||
)
|
||||
|
||||
buildInfo := codersdk.BuildInfoResponse{
|
||||
ExternalURL: buildinfo.ExternalURL(),
|
||||
Version: buildinfo.Version(),
|
||||
}
|
||||
buildInfoResponse, err := json.Marshal(buildInfo)
|
||||
buildInfoResponse, err := json.Marshal(opts.BuildInfo)
|
||||
if err != nil {
|
||||
panic("failed to marshal build info: " + err.Error())
|
||||
}
|
||||
|
|
|
@ -165,6 +165,7 @@ export interface BuildInfoResponse {
|
|||
readonly workspace_proxy: boolean;
|
||||
readonly agent_api_version: string;
|
||||
readonly upgrade_message: string;
|
||||
readonly deployment_id: string;
|
||||
}
|
||||
|
||||
// From codersdk/insights.go
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import FormHelperText, {
|
||||
type FormHelperTextProps,
|
||||
} from "@mui/material/FormHelperText";
|
||||
import type { ComponentProps, FC } from "react";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
|
||||
/**
|
||||
* Use these components as the label in FormControlLabel when implementing radio
|
||||
* buttons, checkboxes, or switches to ensure proper styling.
|
||||
*/
|
||||
|
||||
export const StackLabel: FC<ComponentProps<typeof Stack>> = (props) => {
|
||||
return (
|
||||
<Stack
|
||||
spacing={0.5}
|
||||
css={{ paddingLeft: 12, fontWeight: 500 }}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const StackLabelHelperText: FC<FormHelperTextProps> = (props) => {
|
||||
return (
|
||||
<FormHelperText
|
||||
css={{
|
||||
marginTop: 0,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -12,9 +12,11 @@ import LaunchIcon from "@mui/icons-material/LaunchOutlined";
|
|||
import DocsIcon from "@mui/icons-material/MenuBook";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import type { FC } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import type * as TypesGen from "api/typesGenerated";
|
||||
import { CopyButton } from "components/CopyButton/CopyButton";
|
||||
import { ExternalImage } from "components/ExternalImage/ExternalImage";
|
||||
import { usePopover } from "components/Popover/Popover";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
|
@ -161,15 +163,51 @@ export const UserDropdownContent: FC<UserDropdownContentProps> = ({
|
|||
<Divider css={{ marginBottom: "0 !important" }} />
|
||||
|
||||
<Stack css={styles.info} spacing={0}>
|
||||
<a
|
||||
title="Browse Source Code"
|
||||
css={[styles.footerText, styles.buildInfo]}
|
||||
href={buildInfo?.external_url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{buildInfo?.version} <LaunchIcon />
|
||||
</a>
|
||||
<Tooltip title="Coder Version">
|
||||
<a
|
||||
title="Browse Source Code"
|
||||
css={[styles.footerText, styles.buildInfo]}
|
||||
href={buildInfo?.external_url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{buildInfo?.version} <LaunchIcon />
|
||||
</a>
|
||||
</Tooltip>
|
||||
|
||||
{Boolean(buildInfo?.deployment_id) && (
|
||||
<div
|
||||
css={css`
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`}
|
||||
>
|
||||
<Tooltip title="Deployment Identifier">
|
||||
<div
|
||||
css={css`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
`}
|
||||
>
|
||||
{buildInfo?.deployment_id}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<CopyButton
|
||||
text={buildInfo!.deployment_id}
|
||||
buttonStyles={css`
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div css={styles.footerText}>{Language.copyrightText}</div>
|
||||
</Stack>
|
||||
|
|
|
@ -21,12 +21,7 @@ export const TemplateScheduleAutostart: FC<TemplateScheduleAutostartProps> = ({
|
|||
onChange,
|
||||
}) => {
|
||||
return (
|
||||
<Stack
|
||||
direction="column"
|
||||
width="100%"
|
||||
alignItems="center"
|
||||
css={{ marginBottom: "20px" }}
|
||||
>
|
||||
<Stack width="100%" alignItems="start" spacing={1}>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={0}
|
||||
|
@ -49,6 +44,7 @@ export const TemplateScheduleAutostart: FC<TemplateScheduleAutostartProps> = ({
|
|||
}[]
|
||||
).map((day) => (
|
||||
<Button
|
||||
fullWidth
|
||||
key={day.key}
|
||||
css={{ borderRadius: 0 }}
|
||||
// TODO: Adding a background color would also help
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { useTheme } from "@emotion/react";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
|
@ -14,6 +13,10 @@ import {
|
|||
FormFields,
|
||||
} from "components/Form/Form";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import {
|
||||
StackLabel,
|
||||
StackLabelHelperText,
|
||||
} from "components/StackLabel/StackLabel";
|
||||
import { getFormHelpers } from "utils/formUtils";
|
||||
import {
|
||||
calculateAutostopRequirementDaysValue,
|
||||
|
@ -51,7 +54,8 @@ const DORMANT_AUTODELETION_DEFAULT = 30;
|
|||
* The default form field space is 4 but since this form is quite heavy I think
|
||||
* increase the space can make it feels lighter.
|
||||
*/
|
||||
const FORM_FIELDS_SPACING = 6;
|
||||
const FORM_FIELDS_SPACING = 8;
|
||||
const DORMANT_FIELDSET_SPACING = 4;
|
||||
|
||||
export interface TemplateScheduleForm {
|
||||
template: Template;
|
||||
|
@ -151,7 +155,6 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||
form,
|
||||
error,
|
||||
);
|
||||
const theme = useTheme();
|
||||
|
||||
const now = new Date();
|
||||
const weekFromNow = new Date(now);
|
||||
|
@ -404,34 +407,30 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||
/>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Checkbox
|
||||
id="allow-user-autostop"
|
||||
size="small"
|
||||
disabled={isSubmitting || !allowAdvancedScheduling}
|
||||
onChange={async () => {
|
||||
await form.setFieldValue(
|
||||
"allow_user_autostop",
|
||||
!form.values.allow_user_autostop,
|
||||
);
|
||||
}}
|
||||
name="allow_user_autostop"
|
||||
checked={form.values.allow_user_autostop}
|
||||
/>
|
||||
<Stack spacing={0.5}>
|
||||
<strong>Enforce these settings across all workspaces</strong>
|
||||
<span
|
||||
css={{
|
||||
fontSize: 12,
|
||||
color: theme.palette.text.secondary,
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
id="allow-user-autostop"
|
||||
size="small"
|
||||
disabled={isSubmitting || !allowAdvancedScheduling}
|
||||
onChange={async (_, checked) => {
|
||||
await form.setFieldValue("allow_user_autostop", checked);
|
||||
}}
|
||||
>
|
||||
Workspaces by default allow users to set custom autostop timers.
|
||||
Use this to apply the template settings to all workspaces under
|
||||
this template.
|
||||
</span>
|
||||
</Stack>
|
||||
</Stack>
|
||||
name="allow_user_autostop"
|
||||
checked={form.values.allow_user_autostop}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<StackLabel>
|
||||
Allow users to customize autostop duration for workspaces.
|
||||
<StackLabelHelperText>
|
||||
By default, workspaces will inherit the Autostop timer from
|
||||
this template. Enabling this option allows users to set custom
|
||||
Autostop timers on their workspaces or turn off the timer.
|
||||
</StackLabelHelperText>
|
||||
</StackLabel>
|
||||
}
|
||||
/>
|
||||
</FormFields>
|
||||
</FormSection>
|
||||
|
||||
|
@ -439,27 +438,30 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||
title="Autostart"
|
||||
description="Allow users to set custom autostart and autostop scheduling options for workspaces created from this template."
|
||||
>
|
||||
<Stack direction="column">
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Checkbox
|
||||
id="allow_user_autostart"
|
||||
size="small"
|
||||
disabled={isSubmitting || !allowAdvancedScheduling}
|
||||
onChange={async () => {
|
||||
await form.setFieldValue(
|
||||
"allow_user_autostart",
|
||||
!form.values.allow_user_autostart,
|
||||
);
|
||||
}}
|
||||
name="allow_user_autostart"
|
||||
checked={form.values.allow_user_autostart}
|
||||
/>
|
||||
<Stack spacing={0.5}>
|
||||
<strong>
|
||||
<Stack>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
id="allow_user_autostart"
|
||||
size="small"
|
||||
disabled={isSubmitting || !allowAdvancedScheduling}
|
||||
onChange={async () => {
|
||||
await form.setFieldValue(
|
||||
"allow_user_autostart",
|
||||
!form.values.allow_user_autostart,
|
||||
);
|
||||
}}
|
||||
name="allow_user_autostart"
|
||||
checked={form.values.allow_user_autostart}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<StackLabel>
|
||||
Allow users to automatically start workspaces on a schedule.
|
||||
</strong>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</StackLabel>
|
||||
}
|
||||
/>
|
||||
|
||||
{allowAdvancedScheduling && (
|
||||
<TemplateScheduleAutostart
|
||||
enabled={Boolean(form.values.allow_user_autostart)}
|
||||
|
@ -482,19 +484,20 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||
<>
|
||||
<FormSection
|
||||
title="Dormancy"
|
||||
description="Coder's Dormancy Threshold determines when workspaces become dormant due to inactivity, requiring manual activation for access."
|
||||
description="When enabled, Coder will mark workspaces as dormant after a period of time with no connections. Dormant workspaces can be auto-deleted (see below) or manually reviewed by the workspace owner or admins."
|
||||
>
|
||||
<FormFields spacing={FORM_FIELDS_SPACING}>
|
||||
<Stack>
|
||||
<Stack spacing={DORMANT_FIELDSET_SPACING}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
size="small"
|
||||
name="dormancyThreshold"
|
||||
checked={form.values.inactivity_cleanup_enabled}
|
||||
onChange={handleToggleInactivityCleanup}
|
||||
/>
|
||||
}
|
||||
label="Enable Dormancy Threshold"
|
||||
label={<StackLabel>Enable Dormancy Threshold</StackLabel>}
|
||||
/>
|
||||
<TextField
|
||||
{...getFieldHelpers("time_til_dormant_ms", {
|
||||
|
@ -514,16 +517,33 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||
/>
|
||||
</Stack>
|
||||
|
||||
<Stack>
|
||||
<Stack spacing={DORMANT_FIELDSET_SPACING}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
size="small"
|
||||
name="dormancyAutoDeletion"
|
||||
checked={form.values.dormant_autodeletion_cleanup_enabled}
|
||||
onChange={handleToggleDormantAutoDeletion}
|
||||
/>
|
||||
}
|
||||
label="Enable Dormancy Auto-Deletion"
|
||||
label={
|
||||
<StackLabel>
|
||||
Enable Dormancy Auto-Deletion
|
||||
<StackLabelHelperText>
|
||||
When enabled, Coder will permanently delete dormant
|
||||
workspaces after a period of time.{" "}
|
||||
<span
|
||||
css={(theme) => ({
|
||||
fontWeight: 500,
|
||||
color: theme.palette.text.primary,
|
||||
})}
|
||||
>
|
||||
Once a workspace is deleted it cannot be recovered.
|
||||
</span>
|
||||
</StackLabelHelperText>
|
||||
</StackLabel>
|
||||
}
|
||||
/>
|
||||
<TextField
|
||||
{...getFieldHelpers("time_til_dormant_autodelete_ms", {
|
||||
|
@ -544,16 +564,25 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||
/>
|
||||
</Stack>
|
||||
|
||||
<Stack>
|
||||
<Stack spacing={DORMANT_FIELDSET_SPACING}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
size="small"
|
||||
name="failureCleanupEnabled"
|
||||
checked={form.values.failure_cleanup_enabled}
|
||||
onChange={handleToggleFailureCleanup}
|
||||
/>
|
||||
}
|
||||
label="Enable Failure Cleanup"
|
||||
label={
|
||||
<StackLabel>
|
||||
Enable Failure Cleanup
|
||||
<StackLabelHelperText>
|
||||
When enabled, Coder will attempt to stop workspaces that
|
||||
are in a failed state after a specified number of days.
|
||||
</StackLabelHelperText>
|
||||
</StackLabel>
|
||||
}
|
||||
/>
|
||||
<TextField
|
||||
{...getFieldHelpers("failure_ttl_ms", {
|
||||
|
|
|
@ -201,6 +201,7 @@ export const MockBuildInfo: TypesGen.BuildInfoResponse = {
|
|||
dashboard_url: "https:///mock-url",
|
||||
workspace_proxy: false,
|
||||
upgrade_message: "My custom upgrade message",
|
||||
deployment_id: "510d407f-e521-4180-b559-eab4a6d802b8",
|
||||
};
|
||||
|
||||
export const MockSupportLinks: TypesGen.LinkConfig[] = [
|
||||
|
|
Loading…
Reference in New Issue