feat: implement config profile git mounts

- Add git_mounts column to config_profiles table (JSONB)
- Create GitMount Pydantic models with validation
- Add repo validation in create/update endpoints
- Update profile resolver to merge git mounts from includes
- Implement auto-clone, branch checkout, and glob expansion
- Add parallel processing for git mount resolution
- Create GitMountEditor frontend component
- Update TypeScript types and API clients
- Add CSS styles for git mount UI
- Frontend type check and build pass

Implements tasks 1.1-7.9 of config-profile-git-mounts spec
This commit is contained in:
Alex Blank
2026-05-26 22:27:04 +02:00
parent adda76a2ff
commit 4c11163bff
15 changed files with 1118 additions and 3 deletions
+27
View File
@@ -19,8 +19,10 @@ import {
type ResolvedProfile,
} from "../api/config_profiles";
import { listProjects } from "../api/projects";
import { listRepositories, type GitRepository } from "../api/git_repositories";
import type { Project } from "../types";
import { listToolTypes, type ToolType } from "../api/tool_types";
import { GitMountEditor } from "../components/git-mount-editor";
type Status = "loading" | "ready" | "error";
type MobileView = "list" | "detail" | "edit";
@@ -32,6 +34,7 @@ export const ConfigProfilesPage = () => {
const [profiles, setProfiles] = useState<ConfigProfile[]>([]);
const [projects, setProjects] = useState<Project[]>([]);
const [toolTypes, setToolTypes] = useState<ToolType[]>([]);
const [repositories, setRepositories] = useState<GitRepository[]>([]);
const [selectedProfileId, setSelectedProfileId] = useState<string | null>(null);
const [isCreating, setIsCreating] = useState(false);
@@ -47,6 +50,7 @@ export const ConfigProfilesPage = () => {
env_vars: {},
runtime_hints: {},
mounts: [],
git_mounts: [],
files: {},
is_default: false,
});
@@ -67,6 +71,19 @@ export const ConfigProfilesPage = () => {
setProfiles(profs || []);
setProjects(projs || []);
setToolTypes(types || []);
// Load repositories from all projects
const allRepos: GitRepository[] = [];
for (const project of projs || []) {
try {
const repos = await listRepositories(project.id);
allRepos.push(...repos);
} catch {
// Skip projects we can't access
}
}
setRepositories(allRepos);
setStatus("ready");
} catch {
setStatus("error");
@@ -84,6 +101,7 @@ export const ConfigProfilesPage = () => {
env_vars: {},
runtime_hints: {},
mounts: [],
git_mounts: [],
files: {},
is_default: false,
});
@@ -102,6 +120,7 @@ export const ConfigProfilesPage = () => {
env_vars: profile.env_vars,
runtime_hints: profile.runtime_hints,
mounts: profile.mounts,
git_mounts: profile.git_mounts || [],
files: profile.files,
is_default: profile.is_default,
});
@@ -1239,6 +1258,14 @@ export const ConfigProfilesPage = () => {
</button>
</div>
<div className="form-section">
<GitMountEditor
mounts={formData.git_mounts || []}
repositories={repositories}
onChange={(git_mounts) => updateFormField("git_mounts", git_mounts)}
/>
</div>
<div className="dialog-actions" style={{ marginTop: "1rem", position: "sticky", bottom: "1rem", background: "var(--surface)", padding: "1rem", borderRadius: "0.5rem", border: "1px solid var(--border)" }}>
<button type="submit" disabled={saveStatus === "saving"}>
<Icon name={isCreating ? "add" : "save"} size="sm" />