Skip to content

Commit 37badc3

Browse files
committed
Merge branch 'main' into remove-goreleaser
2 parents 4a94e25 + a82c0eb commit 37badc3

File tree

239 files changed

+9470
-3696
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

239 files changed

+9470
-3696
lines changed

.gitattributes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ coderd/database/dump.sql linguist-generated=true
33
peerbroker/proto/*.go linguist-generated=true
44
provisionerd/proto/*.go linguist-generated=true
55
provisionersdk/proto/*.go linguist-generated=true
6+
*.tfplan.json linguist-generated=true
7+
*.tfstate.json linguist-generated=true
8+
*.tfstate.dot linguist-generated=true
9+
*.tfplan.dot linguist-generated=true

.github/workflows/coder.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ jobs:
208208
run: gotestsum --junitfile="gotests.xml" --packages="./..." --
209209
-covermode=atomic -coverprofile="gotests.coverage"
210210
-coverpkg=./...,github.com/coder/coder/codersdk
211-
-timeout=3m -count=$GOCOUNT -short -failfast
211+
-timeout=5m -count=$GOCOUNT -short -failfast
212212

213213
- name: Upload DataDog Trace
214214
if: always() && github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
@@ -315,7 +315,7 @@ jobs:
315315
deploy:
316316
name: "deploy"
317317
runs-on: ubuntu-latest
318-
timeout-minutes: 20
318+
timeout-minutes: 30
319319
if: github.ref == 'refs/heads/main' && !github.event.pull_request.head.repo.fork
320320
permissions:
321321
contents: read

.vscode/settings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"kirsle",
3939
"ldflags",
4040
"manifoldco",
41+
"mapstructure",
4142
"mattn",
4243
"mitchellh",
4344
"moby",
@@ -62,14 +63,17 @@
6263
"sdktrace",
6364
"Signup",
6465
"sourcemapped",
66+
"Srcs",
6567
"stretchr",
6668
"TCGETS",
6769
"tcpip",
6870
"TCSETS",
6971
"templateversions",
72+
"testdata",
7073
"testid",
7174
"tfexec",
7275
"tfjson",
76+
"tfplan",
7377
"tfstate",
7478
"trimprefix",
7579
"turnconn",

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,9 @@ test: test-clean
168168

169169
test-postgres: test-clean
170170
DB=ci gotestsum --junitfile="gotests.xml" --packages="./..." -- \
171-
-covermode=atomic -coverprofile="gotests.coverage" -timeout=5m \
171+
-covermode=atomic -coverprofile="gotests.coverage" -timeout=30m \
172172
-coverpkg=./...,github.com/coder/coder/codersdk \
173-
-count=1 -parallel=1 -race -failfast
173+
-count=1 -race -failfast
174174
.PHONY: test-postgres
175175

176176
test-postgres-docker:

README.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Coder creates remote development machines so your team can develop from anywhere
3333
> **Note**:
3434
> Coder is in an alpha state. [Report issues here](https://github.com/coder/coder/issues/new).
3535
36-
There are a few ways to install Coder: [install script](./docs/install.md#installsh) (macOS, Linux), [docker-compose](./docs/install.md#docker-compose), or [manually](./docs/install.md#manual) via the latest release (macOS, Windows, and Linux).
36+
There are a few ways to install Coder: [install script](https://coder.com/docs/coder-oss/latest/install#installsh) (macOS, Linux), [docker-compose](https://coder.com/docs/coder-oss/latest/install#docker-compose), or [manually](https://coder.com/docs/coder-oss/latest/install#manual) via the latest release (macOS, Windows, and Linux).
3737

3838
If you use the install script, you can preview what occurs during the install process:
3939

@@ -47,21 +47,25 @@ To install, run:
4747
curl -fsSL https://coder.com/install.sh | sh
4848
```
4949

50-
Once installed, you can run a temporary deployment in dev mode (all data is in-memory and destroyed on exit):
50+
Once installed, you can start a production deployment with a single command:
5151

5252
```sh
53-
coder server --dev
53+
# Automatically sets up an external access URL on *.try.coder.app
54+
coder server --tunnel
55+
56+
# Requires a PostgreSQL instance and external access URL
57+
coder server --postgres-url <url> --access-url <url>
5458
```
5559

56-
Use `coder --help` to get a complete list of flags and environment variables. Use our [quickstart guide](./docs/quickstart.md) for a full walkthrough.
60+
Use `coder --help` to get a complete list of flags and environment variables. Use our [quickstart guide](https://coder.com/docs/coder-oss/latest/quickstart) for a full walkthrough.
5761

5862
## Documentation
5963

60-
Visit our docs [here](./docs/index.md).
64+
Visit our docs [here](https://coder.com/docs/coder-oss).
6165

6266
## Comparison
6367

64-
Please file [an issue](https://github.com/coder/coder/issues/new) if any information is out of date. Also refer to: [What Coder is not](./docs/about.md#what-coder-is-not).
68+
Please file [an issue](https://github.com/coder/coder/issues/new) if any information is out of date. Also refer to: [What Coder is not](https://coder.com/docs/coder-oss/latest/index#what-coder-is-not).
6569

6670
| Tool | Type | Delivery Model | Cost | Environments |
6771
| :---------------------------------------------------------- | :------- | :----------------- | :---------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -82,6 +86,6 @@ Join our community on [Discord](https://discord.gg/coder) and [Twitter](https://
8286

8387
## Contributing
8488

85-
Read the [contributing docs](./docs/CONTRIBUTING.md).
89+
Read the [contributing docs](https://coder.com/docs/coder-oss/latest/CONTRIBUTING).
8690

8791
Find our list of contributors [here](./docs/CONTRIBUTORS.md).

cli/autostart.go

Lines changed: 123 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,41 @@ package cli
22

33
import (
44
"fmt"
5-
"os"
5+
"strings"
66
"time"
77

88
"github.com/spf13/cobra"
9+
"golang.org/x/xerrors"
910

1011
"github.com/coder/coder/coderd/autobuild/schedule"
12+
"github.com/coder/coder/coderd/util/ptr"
13+
"github.com/coder/coder/coderd/util/tz"
1114
"github.com/coder/coder/codersdk"
1215
)
1316

1417
const autostartDescriptionLong = `To have your workspace build automatically at a regular time you can enable autostart.
15-
When enabling autostart, provide the minute, hour, and day(s) of week.
16-
The default schedule is at 09:00 in your local timezone (TZ env, UTC by default).
18+
When enabling autostart, enter a schedule in the format: <start-time> [day-of-week] [location].
19+
* Start-time (required) is accepted either in 12-hour (hh:mm{am|pm}) format, or 24-hour format hh:mm.
20+
* Day-of-week (optional) allows specifying in the cron format, e.g. 1,3,5 or Mon-Fri.
21+
Aliases such as @daily are not supported.
22+
Default: * (every day)
23+
* Location (optional) must be a valid location in the IANA timezone database.
24+
If omitted, we will fall back to either the TZ environment variable or /etc/localtime.
25+
You can check your corresponding location by visiting https://ipinfo.io - it shows in the demo widget on the right.
1726
`
1827

1928
func autostart() *cobra.Command {
2029
autostartCmd := &cobra.Command{
2130
Annotations: workspaceCommand,
22-
Use: "autostart enable <workspace>",
31+
Use: "autostart set <workspace> <start-time> [day-of-week] [location]",
2332
Short: "schedule a workspace to automatically start at a regular time",
2433
Long: autostartDescriptionLong,
25-
Example: "coder autostart enable my-workspace --minute 30 --hour 9 --days 1-5 --tz Europe/Dublin",
34+
Example: "coder autostart set my-workspace 9:30AM Mon-Fri Europe/Dublin",
2635
}
2736

2837
autostartCmd.AddCommand(autostartShow())
29-
autostartCmd.AddCommand(autostartEnable())
30-
autostartCmd.AddCommand(autostartDisable())
38+
autostartCmd.AddCommand(autostartSet())
39+
autostartCmd.AddCommand(autostartUnset())
3140

3241
return autostartCmd
3342
}
@@ -60,13 +69,12 @@ func autostartShow() *cobra.Command {
6069
}
6170

6271
next := validSchedule.Next(time.Now())
63-
loc, _ := time.LoadLocation(validSchedule.Timezone())
6472

6573
_, _ = fmt.Fprintf(cmd.OutOrStdout(),
6674
"schedule: %s\ntimezone: %s\nnext: %s\n",
6775
validSchedule.Cron(),
68-
validSchedule.Timezone(),
69-
next.In(loc),
76+
validSchedule.Location(),
77+
next.In(validSchedule.Location()),
7078
)
7179

7280
return nil
@@ -75,23 +83,17 @@ func autostartShow() *cobra.Command {
7583
return cmd
7684
}
7785

78-
func autostartEnable() *cobra.Command {
79-
// yes some of these are technically numbers but the cron library will do that work
80-
var autostartMinute string
81-
var autostartHour string
82-
var autostartDayOfWeek string
83-
var autostartTimezone string
86+
func autostartSet() *cobra.Command {
8487
cmd := &cobra.Command{
85-
Use: "enable <workspace_name> <schedule>",
86-
Args: cobra.ExactArgs(1),
88+
Use: "set <workspace_name> <start-time> [day-of-week] [location]",
89+
Args: cobra.RangeArgs(2, 4),
8790
RunE: func(cmd *cobra.Command, args []string) error {
8891
client, err := createClient(cmd)
8992
if err != nil {
9093
return err
9194
}
9295

93-
spec := fmt.Sprintf("CRON_TZ=%s %s %s * * %s", autostartTimezone, autostartMinute, autostartHour, autostartDayOfWeek)
94-
validSchedule, err := schedule.Weekly(spec)
96+
sched, err := parseCLISchedule(args[1:]...)
9597
if err != nil {
9698
return err
9799
}
@@ -102,32 +104,30 @@ func autostartEnable() *cobra.Command {
102104
}
103105

104106
err = client.UpdateWorkspaceAutostart(cmd.Context(), workspace.ID, codersdk.UpdateWorkspaceAutostartRequest{
105-
Schedule: &spec,
107+
Schedule: ptr.Ref(sched.String()),
106108
})
107109
if err != nil {
108110
return err
109111
}
110112

111-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace will automatically start at %s.\n\n", workspace.Name, validSchedule.Next(time.Now()))
112-
113+
schedNext := sched.Next(time.Now())
114+
_, _ = fmt.Fprintf(cmd.OutOrStdout(),
115+
"%s will automatically start at %s %s (%s)\n",
116+
workspace.Name,
117+
schedNext.In(sched.Location()).Format(time.Kitchen),
118+
sched.DaysOfWeek(),
119+
sched.Location().String(),
120+
)
113121
return nil
114122
},
115123
}
116124

117-
cmd.Flags().StringVar(&autostartMinute, "minute", "0", "autostart minute")
118-
cmd.Flags().StringVar(&autostartHour, "hour", "9", "autostart hour")
119-
cmd.Flags().StringVar(&autostartDayOfWeek, "days", "1-5", "autostart day(s) of week")
120-
tzEnv := os.Getenv("TZ")
121-
if tzEnv == "" {
122-
tzEnv = "UTC"
123-
}
124-
cmd.Flags().StringVar(&autostartTimezone, "tz", tzEnv, "autostart timezone")
125125
return cmd
126126
}
127127

128-
func autostartDisable() *cobra.Command {
128+
func autostartUnset() *cobra.Command {
129129
return &cobra.Command{
130-
Use: "disable <workspace_name>",
130+
Use: "unset <workspace_name>",
131131
Args: cobra.ExactArgs(1),
132132
RunE: func(cmd *cobra.Command, args []string) error {
133133
client, err := createClient(cmd)
@@ -147,9 +147,98 @@ func autostartDisable() *cobra.Command {
147147
return err
148148
}
149149

150-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace will no longer automatically start.\n\n", workspace.Name)
150+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s will no longer automatically start.\n", workspace.Name)
151151

152152
return nil
153153
},
154154
}
155155
}
156+
157+
var errInvalidScheduleFormat = xerrors.New("Schedule must be in the format Mon-Fri 09:00AM America/Chicago")
158+
var errInvalidTimeFormat = xerrors.New("Start time must be in the format hh:mm[am|pm] or HH:MM")
159+
var errUnsupportedTimezone = xerrors.New("The location you provided looks like a timezone. Check https://ipinfo.io for your location.")
160+
161+
// parseCLISchedule parses a schedule in the format HH:MM{AM|PM} [DOW] [LOCATION]
162+
func parseCLISchedule(parts ...string) (*schedule.Schedule, error) {
163+
// If the user was careful and quoted the schedule, un-quote it.
164+
// In the case that only time was specified, this will be a no-op.
165+
if len(parts) == 1 {
166+
parts = strings.Fields(parts[0])
167+
}
168+
var loc *time.Location
169+
dayOfWeek := "*"
170+
t, err := parseTime(parts[0])
171+
if err != nil {
172+
return nil, err
173+
}
174+
hour, minute := t.Hour(), t.Minute()
175+
176+
// Any additional parts get ignored.
177+
switch len(parts) {
178+
case 3:
179+
dayOfWeek = parts[1]
180+
loc, err = time.LoadLocation(parts[2])
181+
if err != nil {
182+
_, err = time.Parse("MST", parts[2])
183+
if err == nil {
184+
return nil, errUnsupportedTimezone
185+
}
186+
return nil, xerrors.Errorf("Invalid timezone %q specified: a valid IANA timezone is required", parts[2])
187+
}
188+
case 2:
189+
// Did they provide day-of-week or location?
190+
if maybeLoc, err := time.LoadLocation(parts[1]); err != nil {
191+
// Assume day-of-week.
192+
dayOfWeek = parts[1]
193+
} else {
194+
loc = maybeLoc
195+
}
196+
case 1: // already handled
197+
default:
198+
return nil, errInvalidScheduleFormat
199+
}
200+
201+
// If location was not specified, attempt to automatically determine it as a last resort.
202+
if loc == nil {
203+
loc, err = tz.TimezoneIANA()
204+
if err != nil {
205+
return nil, xerrors.Errorf("Could not automatically determine your timezone")
206+
}
207+
}
208+
209+
sched, err := schedule.Weekly(fmt.Sprintf(
210+
"CRON_TZ=%s %d %d * * %s",
211+
loc.String(),
212+
minute,
213+
hour,
214+
dayOfWeek,
215+
))
216+
if err != nil {
217+
// This will either be an invalid dayOfWeek or an invalid timezone.
218+
return nil, xerrors.Errorf("Invalid schedule: %w", err)
219+
}
220+
221+
return sched, nil
222+
}
223+
224+
func parseTime(s string) (time.Time, error) {
225+
// Try a number of possible layouts.
226+
for _, layout := range []string{
227+
time.Kitchen, // 03:04PM
228+
"03:04pm",
229+
"3:04PM",
230+
"3:04pm",
231+
"15:04",
232+
"1504",
233+
"03PM",
234+
"03pm",
235+
"3PM",
236+
"3pm",
237+
} {
238+
t, err := time.Parse(layout, s)
239+
if err == nil {
240+
return t, nil
241+
}
242+
}
243+
return time.Time{}, errInvalidTimeFormat
244+
}

0 commit comments

Comments
 (0)