Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ jobs:
e2e-runtime-volumes-ssh:
name: E2E (Runtime volumes + SSH)
runs-on: ubuntu-latest
timeout-minutes: 40
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- name: Install dependencies
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions packages/api/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import {
} from "./services/federation.js"
import {
applyAllProjects,
applyProjectById,
createProjectFromRequest,
deleteProjectById,
downAllProjects,
Expand Down Expand Up @@ -972,6 +973,14 @@ export const makeRouter = () => {
Effect.catchAll(errorResponse)
)
),
HttpRouter.post(
"/projects/:projectId/apply",
projectParams.pipe(
Effect.flatMap(({ projectId }) => applyProjectById(projectId)),
Effect.flatMap((project) => jsonResponse({ ok: true, project }, 200)),
Effect.catchAll(errorResponse)
)
),
HttpRouter.post(
"/projects/:projectId/down",
projectParams.pipe(
Expand Down
23 changes: 23 additions & 0 deletions packages/api/src/services/projects.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
type AppError,
applyProjectConfig,
buildCreateCommand,
defaultTemplateConfig,
createProject,
Expand Down Expand Up @@ -588,6 +589,28 @@ export const applyAllProjects = (activeOnly: boolean) =>
activeOnly
})

export const applyProjectById = (
projectId: string
) =>
Effect.gen(function*(_) {
const project = yield* _(findProjectById(projectId))
yield* _(markDeployment(projectId, "apply", "docker-git apply"))
yield* _(
runWithProjectEventLogs(
projectId,
applyProjectConfig({
_tag: "Apply",
projectDir: project.projectDir,
runUp: true
})
)
)
const details = yield* _(runtimeProjectDetails(project))
yield* _(recordProjectStartedFromDetails(project, details, "up"))
yield* _(markDeployment(projectId, "running", "Apply completed"))
return details
}).pipe(Effect.mapError(toProjectApiError))

export const downAllProjects = () => downAllDockerGitProjects

export const getProject = (
Expand Down
30 changes: 30 additions & 0 deletions packages/app/eslint.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,36 @@ export default defineConfig(
}]
}
},
{
files: [
"src/docker-git/menu-create-shared.ts",
"src/docker-git/menu-render.ts",
"src/web/actions-projects.ts",
"src/web/app-ready-controller.ts",
"src/web/app-ready-main-panels.tsx",
"src/web/app-ready-ssh-link-hook.ts",
"src/web/app-ready-terminal-screen.tsx",
"src/web/app-ready-url.ts",
"src/web/panel-content.tsx",
"src/web/panel-create-select.tsx",
"src/web/panel-project-details.tsx",
"src/web/panel-terminal.tsx",
"src/web/terminal-mobile-controls.ts",
"src/web/terminal-panel-runtime-core.ts"
],
rules: {
"complexity": ["error", 15],
"max-lines": [
"error",
{ max: 650, skipBlankLines: true, skipComments: true }
],
"max-lines-per-function": [
"error",
{ max: 160, skipBlankLines: true, skipComments: true }
],
"max-params": ["error", 6]
}
},
{
files: ['**/*.{test,spec}.{ts,tsx}', 'tests/**', '**/__tests__/**'],
...vitest.configs.all,
Expand Down
42 changes: 42 additions & 0 deletions packages/app/src/web/actions-projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
} from "./actions-shared.js"
import { loadSelectedProjectTasks } from "./actions-tasks.js"
import {
applyAllProjects,
applyProject,
createProjectTerminalSession,
deleteProject,
downAllProjects,
Expand Down Expand Up @@ -120,6 +122,31 @@ export const connectProjectById = (
})
}

export const applyProjectById = (
projectId: string,
context: BrowserActionContext
) => {
context.setSelectedProjectId(projectId)
withBusy({
context,
effect: applyProject(projectId),
label: "Applying project",
onSuccess: (project) => {
context.reloadDashboard()
context.setSelectedProject(project)
context.setMessage(`Applied ${project.displayName}.`)
}
})
}

export const applySelectedProject = (context: BrowserActionContext) => {
const projectId = requireSelectedProjectId(context)
if (projectId === null) {
return
}
applyProjectById(projectId, context)
}

export const attachProjectTerminalById = (
projectId: string,
projectKey: string,
Expand Down Expand Up @@ -223,6 +250,21 @@ const runDownAllProjects = (context: BrowserActionContext) => {
})
}

export const runApplyAllProjects = (context: BrowserActionContext) => {
if (!confirmAction("Apply docker-git config to all projects?")) {
return
}
withBusy({
context,
effect: applyAllProjects(false),
label: "Applying all projects",
onSuccess: () => {
context.reloadDashboard()
context.setMessage("Applied docker-git config to all projects.")
}
})
}

export const runProjectMenuAction = (
currentMenu: Exclude<BrowserMenuTag, "Auth" | "ProjectAuth">,
context: BrowserActionContext
Expand Down
9 changes: 8 additions & 1 deletion packages/app/src/web/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ export {
saveSelectedDatabaseProfile
} from "./actions-databases.js"
export { closeSelectedProjectPort, loadSelectedProjectPorts, openSelectedProjectPort } from "./actions-port-forwards.js"
export { attachProjectTerminalById, connectProjectById, loadSelectedProjectInfo } from "./actions-projects.js"
export {
applyProjectById,
applySelectedProject,
attachProjectTerminalById,
connectProjectById,
loadSelectedProjectInfo,
runApplyAllProjects
} from "./actions-projects.js"
export { loadSelectedProjectTaskLogs, loadSelectedProjectTasks, stopSelectedProjectTask } from "./actions-tasks.js"

export const runBrowserMenuAction = (
Expand Down
9 changes: 9 additions & 0 deletions packages/app/src/web/api-project-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ export const loadProjectLogs = (projectId: string) =>
Effect.map((response) => response.output)
)

export const applyProject = (projectId: string) =>
requestJson(
"POST",
`/projects/${encodeURIComponent(projectId)}/apply`,
ProjectResponseSchema
).pipe(
Effect.map((response) => response.project)
)

export const createProject = (draft: CreateProjectDraft) =>
requestJson(
"POST",
Expand Down
12 changes: 11 additions & 1 deletion packages/app/src/web/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,14 @@ export {
restartProjectDatabaseEditor,
saveProjectDatabaseProfile
} from "./api-database.js"
export { createProject, loadProjectDetails, loadProjectLogs, loadProjectPs, upProject } from "./api-project-core.js"
export {
applyProject,
createProject,
loadProjectDetails,
loadProjectLogs,
loadProjectPs,
upProject
} from "./api-project-core.js"
export { loadProjectTaskLogs, loadProjectTasks, stopProjectTask } from "./api-tasks.js"

export type * from "./api-types.js"
Expand Down Expand Up @@ -204,6 +211,9 @@ export const deleteProject = (projectId: string) =>

export const downAllProjects = () => requestText("POST", "/projects/down-all").pipe(Effect.asVoid)

export const applyAllProjects = (activeOnly: boolean) =>
requestText("POST", "/projects/apply-all", { activeOnly }).pipe(Effect.asVoid)

export const loadGithubStatus = () =>
requestJson("GET", "/auth/github/status", GithubStatusResponseSchema).pipe(
Effect.map((response) => response.status)
Expand Down
12 changes: 12 additions & 0 deletions packages/app/src/web/app-ready-controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { updateActionPromptValue } from "./action-prompt.js"
import { withBusy } from "./actions-shared.js"
import {
applyProjectById,
applySelectedProject,
attachProjectTerminalById,
cancelBrowserActionPrompt,
closeSelectedProjectPort,
Expand All @@ -10,6 +12,7 @@ import {
openProjectBrowserById,
openSelectedProjectBrowser,
openSelectedProjectPort,
runApplyAllProjects,
submitBrowserActionPrompt
} from "./actions.js"
import { deleteProjectTerminalSession } from "./api.js"
Expand Down Expand Up @@ -254,6 +257,15 @@ const bindTerminalActions = (
actionContext: ReturnType<typeof createActionContext>,
state: ReturnType<typeof useReadyState>
) => ({
onApplyProjectById: (projectId: string) => {
applyProjectById(projectId, actionContext)
},
onApplySelectedProject: () => {
applySelectedProject(actionContext)
},
onApplyAllProjects: () => {
runApplyAllProjects(actionContext)
},
onOpenProjectTerminalById: (projectId: string, projectKey?: string) => {
connectProjectById(projectId, actionContext, projectKey)
},
Expand Down
3 changes: 3 additions & 0 deletions packages/app/src/web/app-ready-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export type ReadyLayoutProps = {
readonly onActionPromptCancel: () => void
readonly onActionPromptChange: (key: string, value: string) => void
readonly onActionPromptSubmit: () => void
readonly onApplyAllProjects: () => void
readonly onApplyProjectById: (projectId: string) => void
readonly onApplySelectedProject: () => void
readonly onAttachProjectTerminalSession: (
projectId: string,
projectKey: string,
Expand Down
44 changes: 36 additions & 8 deletions packages/app/src/web/app-ready-main-panels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,20 @@ const MainMenuRoute = (
const ProjectActionBar = (
{
currentMenu,
onApplyAllProjects,
onApplySelectedProject,
onRunCurrentMenuAction,
projectBrowser,
selectedProjectSummary
}: Pick<MainPanelsProps, "currentMenu" | "onRunCurrentMenuAction" | "projectBrowser" | "selectedProjectSummary">
}: Pick<
MainPanelsProps,
| "currentMenu"
| "onApplyAllProjects"
| "onApplySelectedProject"
| "onRunCurrentMenuAction"
| "projectBrowser"
| "selectedProjectSummary"
>
): JSX.Element => (
<Box
alignItems="center"
Expand All @@ -97,13 +107,29 @@ const ProjectActionBar = (
<Text fg="#aab7c4" wrap="truncate">
{selectedProjectSummary === undefined ? "No project selected." : selectedProjectSummary.displayName}
</Text>
{currentMenu === "Browser" && !canOpenProjectBrowser(projectBrowser, selectedProjectSummary?.id ?? null)
? <Text bold={true} fg="#8fa6c4">{actionLabel(currentMenu)}</Text>
: (
<Box onClick={onRunCurrentMenuAction} width="auto">
<Text bold={true} fg="#78f0a3">{actionLabel(currentMenu)}</Text>
</Box>
)}
<Box flexWrap="wrap" gap={1} justifyContent="flex-end" width="auto">
{currentMenu === "Select" && selectedProjectSummary !== undefined
? (
<Box onClick={onApplySelectedProject} width="auto">
<Text bold={true} fg="#78f0a3">Apply</Text>
</Box>
)
: null}
{currentMenu === "Select"
? (
<Box onClick={onApplyAllProjects} width="auto">
<Text bold={true} fg="#78f0a3">Apply all</Text>
</Box>
)
: null}
{currentMenu === "Browser" && !canOpenProjectBrowser(projectBrowser, selectedProjectSummary?.id ?? null)
? <Text bold={true} fg="#8fa6c4">{actionLabel(currentMenu)}</Text>
: (
<Box onClick={onRunCurrentMenuAction} width="auto">
<Text bold={true} fg="#78f0a3">{actionLabel(currentMenu)}</Text>
</Box>
)}
</Box>
</Box>
)

Expand Down Expand Up @@ -259,6 +285,8 @@ const ProjectPickerScreen = (props: MainPanelsProps): JSX.Element => (
</Box>
<ProjectActionBar
currentMenu={props.currentMenu}
onApplyAllProjects={props.onApplyAllProjects}
onApplySelectedProject={props.onApplySelectedProject}
onRunCurrentMenuAction={props.onRunCurrentMenuAction}
projectBrowser={props.projectBrowser}
selectedProjectSummary={props.selectedProjectSummary}
Expand Down
9 changes: 9 additions & 0 deletions packages/app/src/web/app-ready-terminal-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { ActiveTerminalSession } from "./terminal.js"
type TerminalScreenProps = Pick<
ReadyLayoutProps,
| "activeTerminalSessionId"
| "onApplyProjectById"
| "onOpenProjectBrowserById"
| "onOpenProjectTerminalById"
| "onSelectTerminal"
Expand All @@ -29,6 +30,7 @@ type TerminalPaneProps =
& Pick<
TerminalScreenProps,
| "onOpenProjectBrowserById"
| "onApplyProjectById"
| "onOpenProjectTerminalById"
| "onSetActiveScreen"
| "onTerminalClose"
Expand Down Expand Up @@ -206,6 +208,7 @@ const TerminalTabs = (

const TerminalPane = (
{
onApplyProjectById,
onOpenProjectBrowserById,
onOpenProjectTerminalById,
onSetActiveScreen,
Expand Down Expand Up @@ -250,6 +253,11 @@ const TerminalPane = (
: () => {
onOpenProjectBrowserById(browserProjectId)
}}
onApplyProject={browserProjectId === undefined
? undefined
: () => {
onApplyProjectById(browserProjectId)
}}
onOpenTerminal={browserProjectId === undefined
? undefined
: () => {
Expand Down Expand Up @@ -289,6 +297,7 @@ export const TerminalScreen = (props: TerminalScreenProps): JSX.Element | null =
<TerminalPane
key={terminalSessionId(activeSession)}
onOpenProjectBrowserById={props.onOpenProjectBrowserById}
onApplyProjectById={props.onApplyProjectById}
onOpenProjectTerminalById={props.onOpenProjectTerminalById}
onSetActiveScreen={props.onSetActiveScreen}
onTerminalClose={props.onTerminalClose}
Expand Down
Loading