-
Notifications
You must be signed in to change notification settings - Fork 0
feat: git-backed pipeline audit trail #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
3b70eb1
feat(schema): add git integration fields to Environment model
TerrifiedBug 9ea5023
chore: add simple-git dependency for pipeline audit trail
TerrifiedBug a72083d
feat: add git sync service for pipeline audit trail
TerrifiedBug fd9ec14
feat: wire git sync into deploy agent flow
TerrifiedBug 12a153e
feat: wire git-sync delete into pipeline deletion flow
TerrifiedBug b2dde37
feat: add git integration config to environment router
TerrifiedBug 7af22ff
feat: add Git Integration settings UI to environment detail page
TerrifiedBug 178dc24
feat: show warning toast when git sync fails after deploy
TerrifiedBug a261103
docs: add git integration documentation
TerrifiedBug d9876b8
fix: address code review findings for git integration
TerrifiedBug 23c600b
fix: address Greptile review findings for git integration
TerrifiedBug 742f34a
fix: set git committer identity for clean server environments
TerrifiedBug 52fa212
fix: handle empty slug edge case and strip gitToken from update response
TerrifiedBug ac69f3e
fix: defer disconnect state reset until mutation succeeds
TerrifiedBug e38c3f6
fix: use context-specific toasts and prevent testing button flash
TerrifiedBug File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
4 changes: 4 additions & 0 deletions
4
prisma/migrations/20260306000000_add_environment_git_config/migration.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| -- AlterTable | ||
| ALTER TABLE "Environment" ADD COLUMN "gitRepoUrl" TEXT, | ||
| ADD COLUMN "gitBranch" TEXT DEFAULT 'main', | ||
| ADD COLUMN "gitToken" TEXT; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,220 @@ | ||
| "use client"; | ||
|
|
||
| import { useState } from "react"; | ||
| import { useTRPC } from "@/trpc/client"; | ||
| import { useMutation, useQueryClient } from "@tanstack/react-query"; | ||
| import { toast } from "sonner"; | ||
| import { GitBranch, Eye, EyeOff, Loader2 } from "lucide-react"; | ||
|
|
||
| import { Button } from "@/components/ui/button"; | ||
| import { Input } from "@/components/ui/input"; | ||
| import { Label } from "@/components/ui/label"; | ||
| import { | ||
| Card, | ||
| CardContent, | ||
| CardDescription, | ||
| CardHeader, | ||
| CardTitle, | ||
| } from "@/components/ui/card"; | ||
|
|
||
| interface GitSyncSectionProps { | ||
| environmentId: string; | ||
| gitRepoUrl: string | null; | ||
| gitBranch: string | null; | ||
| hasGitToken: boolean; | ||
| } | ||
|
|
||
| export function GitSyncSection({ | ||
| environmentId, | ||
| gitRepoUrl, | ||
| gitBranch, | ||
| hasGitToken, | ||
| }: GitSyncSectionProps) { | ||
| const trpc = useTRPC(); | ||
| const queryClient = useQueryClient(); | ||
|
|
||
| const [repoUrl, setRepoUrl] = useState(gitRepoUrl ?? ""); | ||
| const [branch, setBranch] = useState(gitBranch ?? "main"); | ||
| const [token, setToken] = useState(""); | ||
| const [showToken, setShowToken] = useState(false); | ||
| const [isTesting, setIsTesting] = useState(false); | ||
|
|
||
| const updateMutation = useMutation( | ||
| trpc.environment.update.mutationOptions({ | ||
| onSuccess: () => { | ||
| queryClient.invalidateQueries({ queryKey: trpc.environment.get.queryKey({ id: environmentId }) }); | ||
| }, | ||
| onError: (err) => toast.error(err.message || "Failed to save Git settings"), | ||
| }) | ||
| ); | ||
|
|
||
| const testMutation = useMutation( | ||
| trpc.environment.testGitConnection.mutationOptions({ | ||
| onSuccess: (result) => { | ||
| if (result.success) { | ||
| toast.success("Git connection successful"); | ||
| } else { | ||
| toast.error("Git connection failed", { description: result.error }); | ||
| } | ||
| setIsTesting(false); | ||
| }, | ||
| onError: (err) => { | ||
| toast.error("Connection test failed", { description: err.message }); | ||
| setIsTesting(false); | ||
| }, | ||
| }) | ||
| ); | ||
|
|
||
| function handleSave() { | ||
| updateMutation.mutate( | ||
| { | ||
| id: environmentId, | ||
| gitRepoUrl: repoUrl || null, | ||
| gitBranch: branch || null, | ||
| gitToken: token || undefined, // Only send if user entered a new token | ||
| }, | ||
| { | ||
| onSuccess: () => { | ||
| toast.success("Git integration settings saved"); | ||
| setToken(""); | ||
| }, | ||
| }, | ||
| ); | ||
| } | ||
|
|
||
| function handleTest() { | ||
| const testToken = token || undefined; | ||
| if (!repoUrl) { | ||
| toast.error("Enter a repository URL first"); | ||
| return; | ||
| } | ||
| if (!testToken && !hasGitToken) { | ||
| toast.error("Enter an access token first"); | ||
| return; | ||
| } | ||
| if (testToken) { | ||
| setIsTesting(true); | ||
| testMutation.mutate({ environmentId, repoUrl, branch, token: testToken }); | ||
| } else { | ||
| toast.warning("Enter a new token to test the connection"); | ||
| } | ||
| } | ||
|
|
||
| function handleDisconnect() { | ||
| updateMutation.mutate( | ||
| { | ||
| id: environmentId, | ||
| gitRepoUrl: null, | ||
| gitBranch: null, | ||
| gitToken: null, | ||
| }, | ||
| { | ||
| onSuccess: () => { | ||
| toast.success("Git integration disconnected"); | ||
| setRepoUrl(""); | ||
| setBranch("main"); | ||
| setToken(""); | ||
| }, | ||
| }, | ||
| ); | ||
| } | ||
TerrifiedBug marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| const hasChanges = repoUrl !== (gitRepoUrl ?? "") || branch !== (gitBranch ?? "main") || token !== ""; | ||
| const isConfigured = !!gitRepoUrl; | ||
|
|
||
| return ( | ||
| <Card> | ||
| <CardHeader> | ||
| <div className="flex items-center gap-2"> | ||
| <GitBranch className="h-5 w-5" /> | ||
| <div> | ||
| <CardTitle>Git Integration</CardTitle> | ||
| <CardDescription> | ||
| Automatically commit pipeline YAML to a Git repository on deploy and delete. | ||
| </CardDescription> | ||
| </div> | ||
| </div> | ||
| </CardHeader> | ||
| <CardContent className="space-y-4"> | ||
| <div className="space-y-2"> | ||
| <Label htmlFor="git-repo-url">Repository URL</Label> | ||
| <Input | ||
| id="git-repo-url" | ||
| type="url" | ||
| placeholder="https://github.com/org/pipeline-configs.git" | ||
| value={repoUrl} | ||
| onChange={(e) => setRepoUrl(e.target.value)} | ||
| /> | ||
| </div> | ||
|
|
||
| <div className="space-y-2"> | ||
| <Label htmlFor="git-branch">Branch</Label> | ||
| <Input | ||
| id="git-branch" | ||
| placeholder="main" | ||
| value={branch} | ||
| onChange={(e) => setBranch(e.target.value)} | ||
| /> | ||
| </div> | ||
|
|
||
| <div className="space-y-2"> | ||
| <Label htmlFor="git-token"> | ||
| Access Token {hasGitToken && "(saved \u2014 enter new value to replace)"} | ||
| </Label> | ||
| <div className="flex gap-2"> | ||
| <div className="relative flex-1"> | ||
| <Input | ||
| id="git-token" | ||
| type={showToken ? "text" : "password"} | ||
| placeholder={hasGitToken ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "ghp_xxxx or glpat-xxxx"} | ||
| value={token} | ||
| onChange={(e) => setToken(e.target.value)} | ||
| /> | ||
| <Button | ||
| type="button" | ||
| variant="ghost" | ||
| size="icon" | ||
| className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7" | ||
| onClick={() => setShowToken(!showToken)} | ||
| > | ||
| {showToken ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />} | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="flex gap-2 pt-2"> | ||
| <Button | ||
| onClick={handleSave} | ||
| disabled={updateMutation.isPending || !hasChanges} | ||
| > | ||
| {updateMutation.isPending ? "Saving..." : "Save"} | ||
| </Button> | ||
| <Button | ||
| variant="outline" | ||
| onClick={handleTest} | ||
| disabled={isTesting || !repoUrl} | ||
| > | ||
| {isTesting ? ( | ||
| <> | ||
| <Loader2 className="h-4 w-4 animate-spin mr-2" /> | ||
| Testing... | ||
| </> | ||
| ) : ( | ||
| "Test Connection" | ||
| )} | ||
| </Button> | ||
| {isConfigured && ( | ||
| <Button | ||
| variant="destructive" | ||
| onClick={handleDisconnect} | ||
| disabled={updateMutation.isPending} | ||
| > | ||
| Disconnect | ||
| </Button> | ||
| )} | ||
| </div> | ||
| </CardContent> | ||
| </Card> | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.