Skip to content

Commit e16b1d7

Browse files
committed
merge main
2 parents 47da2bf + a72b460 commit e16b1d7

File tree

9 files changed

+40
-160
lines changed

9 files changed

+40
-160
lines changed

app/pages/project/instances/InstancesPage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { Tooltip } from '~/ui/lib/Tooltip'
3939
import { setDiff } from '~/util/array'
4040
import { toLocaleTimeString } from '~/util/date'
4141
import { pb } from '~/util/path-builder'
42+
import { pluralize } from '~/util/str'
4243

4344
import { useMakeInstanceActions } from './actions'
4445
import { ResizeInstanceModal } from './instance/InstancePage'
@@ -99,7 +100,8 @@ export function Component() {
99100
header: 'CPU',
100101
cell: (info) => (
101102
<>
102-
{info.getValue()} <span className="ml-1 text-tertiary">vCPU</span>
103+
{info.getValue()}{' '}
104+
<span className="ml-1 text-tertiary">{pluralize('vCPU', info.getValue())}</span>
103105
</>
104106
),
105107
}),

app/pages/project/instances/instance/InstancePage.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import { Spinner } from '~/ui/lib/Spinner'
5252
import { Tooltip } from '~/ui/lib/Tooltip'
5353
import { truncate } from '~/ui/lib/Truncate'
5454
import { pb } from '~/util/path-builder'
55+
import { pluralize } from '~/util/str'
5556
import { GiB } from '~/util/units'
5657

5758
import { useMakeInstanceActions } from '../actions'
@@ -221,7 +222,7 @@ export function InstancePage() {
221222
<PropertiesTable>
222223
<PropertiesTable.Row label="cpu">
223224
<span className="text-default">{instance.ncpus}</span>
224-
<span className="ml-1 text-tertiary"> vCPUs</span>
225+
<span className="ml-1 text-tertiary">{pluralize(' vCPU', instance.ncpus)}</span>
225226
</PropertiesTable.Row>
226227
<PropertiesTable.Row label="ram">
227228
<span className="text-default">{memory.value}</span>

app/pages/system/metrics-util.spec.ts

Lines changed: 0 additions & 89 deletions
This file was deleted.

app/pages/system/metrics-util.ts

Lines changed: 0 additions & 51 deletions
This file was deleted.

app/util/str.spec.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
extractText,
1515
kebabCase,
1616
normalizeName,
17+
pluralize,
1718
titleCase,
1819
} from './str'
1920

@@ -23,6 +24,14 @@ describe('capitalize', () => {
2324
})
2425
})
2526

27+
describe('pluralize', () => {
28+
it('pluralizes correctly', () => {
29+
expect(pluralize('item', 0)).toBe('items')
30+
expect(pluralize('item', 1)).toBe('item')
31+
expect(pluralize('item', 2)).toBe('items')
32+
})
33+
})
34+
2635
describe('camelCase', () => {
2736
it('basic formats to camel case', () => {
2837
expect(camelCase('camelCase')).toBe('camelCase')
@@ -51,8 +60,9 @@ it('commaSeries', () => {
5160
expect(commaSeries([], 'or')).toBe('')
5261
expect(commaSeries(['a'], 'or')).toBe('a')
5362
expect(commaSeries(['a', 'b'], 'or')).toBe('a or b')
54-
expect(commaSeries(['a', 'b'], 'or')).toBe('a or b')
63+
expect(commaSeries(['a', 'b'], 'and')).toBe('a and b')
5564
expect(commaSeries(['a', 'b', 'c'], 'or')).toBe('a, b, or c')
65+
expect(commaSeries(['a', 'b', 'c'], 'and')).toBe('a, b, and c')
5666
})
5767

5868
describe('titleCase', () => {

app/util/str.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import React, { type ReactElement, type ReactNode } from 'react'
1010

1111
export const capitalize = (s: string) => s && s.charAt(0).toUpperCase() + s.slice(1)
1212

13-
export const pluralize = (s: string, n: number) => `${n} ${s}${n === 1 ? '' : 's'}`
13+
export const pluralize = (s: string, n: number) => `${s}${n === 1 ? '' : 's'}`
1414

1515
export const camelCase = (s: string) =>
1616
s

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"start": "API_MODE=msw vite",
1111
"start:msw": "API_MODE=msw vite",
1212
"start:nexus": "API_MODE=nexus vite",
13-
"start:dogfood": "API_MODE=dogfood vite",
13+
"start:dogfood": "API_MODE=remote EXT_HOST=oxide.sys.rack2.eng.oxide.computer vite",
14+
"start:colo": "API_MODE=remote EXT_HOST=oxide.sys.r3.oxide-preview.com vite",
1415
"preview": "API_MODE=msw npm run build && cp mockServiceWorker.js dist/ && vite preview",
1516
"dev": "API_MODE=msw vite",
1617
"start:mock-api": "tsx ./tools/start_mock_api.ts",

test/e2e/instance.e2e.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ test('can resize a failed or stopped instance', async ({ page }) => {
161161
// resize 'you-fail', currently in a failed state
162162
await expectRowVisible(table, {
163163
name: 'you-fail',
164-
CPU: '4 vCPU',
164+
CPU: '4 vCPUs',
165165
Memory: '6 GiB',
166166
state: expect.stringMatching(/^failed\d+s$/),
167167
})
@@ -173,15 +173,15 @@ test('can resize a failed or stopped instance', async ({ page }) => {
173173
await resizeModal.getByRole('button', { name: 'Resize' }).click()
174174
await expectRowVisible(table, {
175175
name: 'you-fail',
176-
CPU: '10 vCPU',
176+
CPU: '10 vCPUs',
177177
Memory: '20 GiB',
178178
state: expect.stringMatching(/^failed\d+s$/),
179179
})
180180

181181
// resize 'db1', which needs to be stopped first
182182
await expectRowVisible(table, {
183183
name: 'db1',
184-
CPU: '2 vCPU',
184+
CPU: '2 vCPUs',
185185
Memory: '4 GiB',
186186
state: expect.stringMatching(/^running\d+s$/),
187187
})
@@ -200,7 +200,7 @@ test('can resize a failed or stopped instance', async ({ page }) => {
200200
await resizeModal.getByRole('button', { name: 'Resize' }).click()
201201
await expectRowVisible(table, {
202202
name: 'db1',
203-
CPU: '8 vCPU',
203+
CPU: '8 vCPUs',
204204
Memory: '16 GiB',
205205
state: expect.stringMatching(/^stopped\d+s$/),
206206
})
@@ -224,19 +224,19 @@ test('instance table', async ({ page }) => {
224224
const table = page.getByRole('table')
225225
await expectRowVisible(table, {
226226
name: 'db1',
227-
CPU: '2 vCPU',
227+
CPU: '2 vCPUs',
228228
Memory: '4 GiB',
229229
state: expect.stringMatching(/^running\d+s$/),
230230
})
231231
await expectRowVisible(table, {
232232
name: 'you-fail',
233-
CPU: '4 vCPU',
233+
CPU: '4 vCPUs',
234234
Memory: '6 GiB',
235235
state: expect.stringMatching(/^failed\d+s$/),
236236
})
237237
await expectRowVisible(table, {
238238
name: 'not-there-yet',
239-
CPU: '2 vCPU',
239+
CPU: '2 vCPUs',
240240
Memory: '8 GiB',
241241
state: expect.stringMatching(/^starting\d+s$/),
242242
})

vite.config.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@ import vercelConfig from './vercel.json'
1818

1919
const KiB = 1024
2020

21-
const ApiMode = z.enum(['msw', 'dogfood', 'nexus'])
21+
const ApiMode = z.enum(['msw', 'remote', 'nexus'])
22+
23+
function bail(msg: string): never {
24+
console.error(msg)
25+
process.exit(1)
26+
}
2227

2328
const apiModeResult = ApiMode.default('nexus').safeParse(process.env.API_MODE)
2429
if (!apiModeResult.success) {
2530
const options = ApiMode.options.join(', ')
26-
console.error(`Error: API_MODE must be one of: [${options}]. If unset, default is "msw".`)
27-
process.exit(1)
31+
bail(`Error: API_MODE must be one of: [${options}]. If unset, default is "msw".`)
2832
}
2933
/**
3034
* What API are we talking to? Only relevant in development mode.
@@ -35,8 +39,11 @@ if (!apiModeResult.success) {
3539
*/
3640
const apiMode = apiModeResult.data
3741

38-
// if you want a different host you can override it with EXT_HOST
39-
const DOGFOOD_HOST = process.env.EXT_HOST || 'oxide.sys.rack2.eng.oxide.computer'
42+
if (apiMode === 'remote' && !process.env.EXT_HOST) {
43+
bail(`Error: EXT_HOST is required when API_MODE=remote. See package.json for examples.`)
44+
}
45+
46+
const EXT_HOST = process.env.EXT_HOST
4047

4148
const previewAnalyticsTag = {
4249
injectTo: 'head' as const,
@@ -126,7 +133,7 @@ export default defineConfig(({ mode }) => ({
126133
},
127134
}),
128135
react(),
129-
apiMode === 'dogfood' && basicSsl(),
136+
apiMode === 'remote' && basicSsl(),
130137
],
131138
html: {
132139
// don't include a placeholder nonce in production.
@@ -139,8 +146,7 @@ export default defineConfig(({ mode }) => ({
139146
// these only get hit when MSW doesn't intercept the request
140147
proxy: {
141148
'/v1': {
142-
target:
143-
apiMode === 'dogfood' ? `https://${DOGFOOD_HOST}` : 'http://localhost:12220',
149+
target: apiMode === 'remote' ? `https://${EXT_HOST}` : 'http://localhost:12220',
144150
changeOrigin: true,
145151
},
146152
},

0 commit comments

Comments
 (0)