Skip to content

Commit 8db6918

Browse files
committed
fix(ci): inline API create SSH key seeding
1 parent 92b7935 commit 8db6918

3 files changed

Lines changed: 38 additions & 67 deletions

File tree

packages/api/src/services/projects.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
type AppError,
33
buildCreateCommand,
4+
defaultTemplateConfig,
45
createProject,
56
formatParseError,
67
applyAllDockerGitProjects,
@@ -17,6 +18,7 @@ import { CommandFailedError } from "@effect-template/lib/shell/errors"
1718
import { defaultProjectsRoot, resolvePathFromCwd } from "@effect-template/lib/usecases/path-helpers"
1819
import { deleteDockerGitProject } from "@effect-template/lib/usecases/projects"
1920
import type { RawOptions } from "@effect-template/lib/core/command-options"
21+
import type { CreateCommand as LibCreateCommand } from "@effect-template/lib/core/domain"
2022
import type { ProjectItem } from "@effect-template/lib/usecases/projects"
2123
import { Effect, Either } from "effect"
2224

@@ -217,6 +219,20 @@ const mergeAuthorizedKeys = (
217219
return merged.length === 0 ? "" : `${merged.join("\n")}\n`
218220
}
219221

222+
const withManagedAuthorizedKeysForCreate = (
223+
command: LibCreateCommand,
224+
authorizedKeysContents: string | undefined
225+
) =>
226+
authorizedKeysContents === undefined
227+
? command
228+
: {
229+
...command,
230+
config: {
231+
...command.config,
232+
authorizedKeysPath: defaultTemplateConfig.authorizedKeysPath
233+
}
234+
}
235+
220236
export const seedAuthorizedKeysForCreate = (
221237
outDir: string,
222238
authorizedKeysContents: string | undefined
@@ -336,18 +352,20 @@ export const createProjectFromRequest = (
336352
)
337353
}
338354

339-
const command = {
355+
const parsedCommand = {
340356
...parsed.right,
341357
openSsh: false,
342358
waitForClone: request.waitForClone ?? parsed.right.waitForClone
343359
}
344360

345361
const resolvedAuthorizedKeysContents = request.authorizedKeysContents ?? (
346362
request.useManagedAuthorizedKeys === true
347-
? yield* _(resolveCreateAuthorizedKeysContents(command.outDir, command.config.authorizedKeysPath))
363+
? yield* _(resolveCreateAuthorizedKeysContents(parsedCommand.outDir, parsedCommand.config.authorizedKeysPath))
348364
: undefined
349365
)
350366

367+
const command = withManagedAuthorizedKeysForCreate(parsedCommand, resolvedAuthorizedKeysContents)
368+
351369
yield* _(seedAuthorizedKeysForCreate(command.outDir, resolvedAuthorizedKeysContents))
352370

353371
yield* _(ensureGithubAuthForCreate(command.config))
Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,14 @@
1-
import { Effect } from "effect"
2-
3-
import { request } from "./api-http.js"
4-
import { asObject, type JsonRequest, type JsonValue } from "./api-json.js"
5-
import { decodeProjectDetails } from "./api-project-codec.js"
6-
import type { CreateCommand } from "./frontend-lib/core/domain.js"
1+
import type { JsonRequest } from "./api-json.js"
2+
import { type CreateCommand, defaultTemplateConfig } from "./frontend-lib/core/domain.js"
73

84
type ResolvedCreateRequestPaths = {
95
readonly authorizedKeysPath: string
106
readonly authorizedKeysContents?: string | undefined
117
}
128

13-
const projectPath = (projectId: string, suffix = ""): string => `/projects/${encodeURIComponent(projectId)}${suffix}`
14-
15-
export const decodeProjectResponse = (payload: JsonValue) => {
16-
const object = asObject(payload)
17-
return object === null
18-
? decodeProjectDetails(payload)
19-
: decodeProjectDetails(object["project"] ?? payload)
20-
}
21-
22-
export const createProjectRequestNeedsFollowUpUp = (
23-
command: CreateCommand,
24-
resolvedPaths: ResolvedCreateRequestPaths
25-
): boolean => command.runUp && resolvedPaths.authorizedKeysContents !== undefined
26-
27-
export const createProjectRequestAllowsImmediateUp = (
28-
command: CreateCommand,
29-
resolvedPaths: ResolvedCreateRequestPaths
30-
): boolean => command.runUp && resolvedPaths.authorizedKeysContents === undefined
31-
329
export const buildCreateProjectRequest = (
3310
command: CreateCommand,
34-
resolvedPaths: ResolvedCreateRequestPaths,
35-
shouldRunUpInCreateRequest: boolean
11+
resolvedPaths: ResolvedCreateRequestPaths
3612
) => {
3713
const config = command.config
3814
return {
@@ -44,7 +20,9 @@ export const buildCreateProjectRequest = (
4420
containerName: config.containerName,
4521
serviceName: config.serviceName,
4622
volumeName: config.volumeName,
47-
authorizedKeysPath: resolvedPaths.authorizedKeysPath,
23+
authorizedKeysPath: resolvedPaths.authorizedKeysContents === undefined
24+
? resolvedPaths.authorizedKeysPath
25+
: defaultTemplateConfig.authorizedKeysPath,
4826
authorizedKeysContents: resolvedPaths.authorizedKeysContents,
4927
envGlobalPath: config.envGlobalPath,
5028
envProjectPath: config.envProjectPath,
@@ -62,21 +40,10 @@ export const buildCreateProjectRequest = (
6240
codexTokenLabel: config.codexAuthLabel,
6341
claudeTokenLabel: config.claudeAuthLabel,
6442
agentAutoMode: config.agentAuto ? (config.agentMode ?? "auto") : undefined,
65-
up: shouldRunUpInCreateRequest,
43+
up: command.runUp,
6644
openSsh: false,
6745
force: command.force,
6846
forceEnv: command.forceEnv,
6947
waitForClone: command.waitForClone
7048
} satisfies JsonRequest
7149
}
72-
73-
export const upCreatedProjectWithAuthorizedKeys = (
74-
projectId: string,
75-
authorizedKeysContents: string
76-
) =>
77-
request("POST", projectPath(projectId, "/up"), {
78-
authorizedKeysContents,
79-
useManagedAuthorizedKeys: true
80-
}).pipe(
81-
Effect.map((payload) => decodeProjectResponse(payload))
82-
)

packages/app/src/docker-git/api-client.ts

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,10 @@ import * as FsPlatform from "@effect/platform/FileSystem"
22
import * as PathPlatform from "@effect/platform/Path"
33
import { Effect } from "effect"
44

5-
import {
6-
buildCreateProjectRequest,
7-
createProjectRequestAllowsImmediateUp,
8-
createProjectRequestNeedsFollowUpUp,
9-
decodeProjectResponse,
10-
upCreatedProjectWithAuthorizedKeys
11-
} from "./api-client-create.js"
5+
import { buildCreateProjectRequest } from "./api-client-create.js"
126
import { readProjectOutput, resolveCreateRequestPaths } from "./api-client-helpers.js"
137
import { request, requestTextStream, requestVoid } from "./api-http.js"
14-
import { asArray, asObject } from "./api-json.js"
8+
import { asArray, asObject, type JsonValue } from "./api-json.js"
159
import { decodeProjectDetails, decodeProjectSummary } from "./api-project-codec.js"
1610
import { decodeTerminalSession } from "./api-terminal-codec.js"
1711
import type {
@@ -44,6 +38,13 @@ const projectPath = (projectId: string, suffix = ""): string => `/projects/${enc
4438
const codexLoginSuccessMarker = "__DOCKER_GIT_CODEX_LOGIN_STATUS__:ok"
4539
const codexLoginErrorMarkerPrefix = "__DOCKER_GIT_CODEX_LOGIN_STATUS__:error:"
4640

41+
const decodeProjectResponse = (payload: JsonValue) => {
42+
const object = asObject(payload)
43+
return object === null
44+
? decodeProjectDetails(payload)
45+
: decodeProjectDetails(object["project"] ?? payload)
46+
}
47+
4748
const codexLoginFailureMessage = (output: string, exitCode: string | null): string => {
4849
if (output.includes("429 Too Many Requests")) {
4950
return "Codex device auth is rate-limited by OpenAI (429 Too Many Requests). Wait a few minutes and retry."
@@ -97,24 +98,9 @@ const createProjectWithResolvedPaths = (
9798
}
9899
) =>
99100
Effect.gen(function*(_) {
100-
const createRequest = buildCreateProjectRequest(
101-
command,
102-
resolvedPaths,
103-
createProjectRequestAllowsImmediateUp(command, resolvedPaths)
104-
)
101+
const createRequest = buildCreateProjectRequest(command, resolvedPaths)
105102
const payload = yield* _(request("POST", "/projects", createRequest))
106-
const createdProject = decodeProjectResponse(payload)
107-
if (
108-
createdProject === null ||
109-
resolvedPaths.authorizedKeysContents === undefined ||
110-
!createProjectRequestNeedsFollowUpUp(command, resolvedPaths)
111-
) {
112-
return createdProject
113-
}
114-
115-
return yield* _(
116-
upCreatedProjectWithAuthorizedKeys(createdProject.id, resolvedPaths.authorizedKeysContents)
117-
)
103+
return decodeProjectResponse(payload)
118104
})
119105

120106
export const createProject = (command: CreateCommand) =>

0 commit comments

Comments
 (0)