mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-02-01 15:20:49 +01:00
- Enhanced Git class to include method for fetching incoming changes from remote repository. - Implemented logic to retrieve and display incoming commits in the changes page. - Updated API routes to handle incoming changes and pull requests. - Modified UI components to show incoming changes and allow users to pull updates. - Improved actions bar to disable commit actions when there are incoming changes. - Added sync button to refresh repository status and check for updates.
303 lines
7.9 KiB
Markdown
303 lines
7.9 KiB
Markdown
# Manual Pull Handling for Databases
|
|
|
|
**Status: Complete**
|
|
|
|
## Summary
|
|
|
|
When `auto_pull = 0`, users receive notifications that updates are available but
|
|
have no way to review or pull them. This document plans extending the existing
|
|
`/databases/[id]/changes` page to support incoming changes (pull) alongside
|
|
outgoing changes (push).
|
|
|
|
---
|
|
|
|
## Current State
|
|
|
|
### The `/databases/[id]/changes` Page
|
|
|
|
Currently this page:
|
|
|
|
- Only accessible to developers (requires `personal_access_token`)
|
|
- Shows **outgoing changes** (uncommitted local ops)
|
|
- Allows: select files, write commit message, push to remote
|
|
- Allows: discard local changes
|
|
- Allows: switch branches
|
|
|
|
### The Gap
|
|
|
|
When `auto_pull = 0` and updates are found:
|
|
|
|
1. User receives notification "Updates available for X"
|
|
2. User has no way to see what those updates contain
|
|
3. User has no way to pull them from the UI
|
|
|
|
---
|
|
|
|
## Proposed Solution
|
|
|
|
Extend the changes page to show both directions:
|
|
|
|
| Section | Who Sees It | Description |
|
|
| ----------------- | ----------- | ---------------------------------- |
|
|
| Incoming Changes | Everyone | Commits available to pull |
|
|
| Outgoing Changes | Developers | Uncommitted local ops to push |
|
|
|
|
### User Flow
|
|
|
|
**For regular users (no PAT):**
|
|
|
|
1. Navigate to `/databases/[id]/changes`
|
|
2. See "Incoming Changes" section with commits behind
|
|
3. Review the changes (files modified in each commit)
|
|
4. Click "Pull Updates" to sync
|
|
|
|
**For developers (with PAT):**
|
|
|
|
1. Same as above, plus...
|
|
2. See "Outgoing Changes" section with uncommitted ops
|
|
3. Full commit/push/discard functionality
|
|
|
|
---
|
|
|
|
## Implementation Plan
|
|
|
|
### 1. Remove PAT Requirement for Page Access
|
|
|
|
**File:** `src/routes/databases/[id]/changes/+page.server.ts`
|
|
|
|
```typescript
|
|
export const load: PageServerLoad = async ({ parent }) => {
|
|
const { database } = await parent();
|
|
// Remove the PAT check - page is now accessible to everyone
|
|
// PAT only needed for push actions
|
|
return {
|
|
isDeveloper: !!database.personal_access_token
|
|
};
|
|
};
|
|
```
|
|
|
|
### 2. Update API to Return Incoming Changes
|
|
|
|
**File:** `src/routes/api/databases/[id]/changes/+server.ts`
|
|
|
|
Remove PAT requirement for GET. Add incoming changes data:
|
|
|
|
```typescript
|
|
export const GET: RequestHandler = async ({ params }) => {
|
|
const database = databaseInstancesQueries.getById(id);
|
|
const git = new Git(database.local_path);
|
|
|
|
// Fetch for everyone
|
|
const [status, incomingChanges, branches, repoInfo] = await Promise.all([
|
|
git.status(),
|
|
git.getIncomingChanges(),
|
|
git.getBranches(),
|
|
getRepoInfo(database.repository_url, database.personal_access_token)
|
|
]);
|
|
|
|
// Only fetch outgoing changes for developers
|
|
let uncommittedOps = null;
|
|
if (database.personal_access_token) {
|
|
uncommittedOps = await git.getUncommittedOps();
|
|
}
|
|
|
|
return json({
|
|
status,
|
|
incomingChanges,
|
|
branches,
|
|
repoInfo,
|
|
uncommittedOps
|
|
});
|
|
};
|
|
```
|
|
|
|
### 3. Add Git Function for Incoming Changes
|
|
|
|
**File:** `src/lib/server/utils/git/status.ts`
|
|
|
|
```typescript
|
|
export interface IncomingCommit {
|
|
hash: string;
|
|
shortHash: string;
|
|
message: string;
|
|
author: string;
|
|
date: string;
|
|
files: string[];
|
|
}
|
|
|
|
export interface IncomingChanges {
|
|
hasUpdates: boolean;
|
|
commitsBehind: number;
|
|
commits: IncomingCommit[];
|
|
}
|
|
|
|
export async function getIncomingChanges(repoPath: string): Promise<IncomingChanges> {
|
|
// Fetch latest from remote
|
|
await execGitSafe(['fetch'], repoPath);
|
|
|
|
const branch = await getBranch(repoPath);
|
|
const remoteBranch = `origin/${branch}`;
|
|
|
|
// Count commits behind
|
|
const countOutput = await execGitSafe(
|
|
['rev-list', '--count', `HEAD..${remoteBranch}`],
|
|
repoPath
|
|
);
|
|
const commitsBehind = parseInt(countOutput || '0') || 0;
|
|
|
|
if (commitsBehind === 0) {
|
|
return { hasUpdates: false, commitsBehind: 0, commits: [] };
|
|
}
|
|
|
|
// Get commit details for incoming commits
|
|
const logOutput = await execGitSafe(
|
|
['log', '--format=%H|%h|%s|%an|%aI', `HEAD..${remoteBranch}`],
|
|
repoPath
|
|
);
|
|
|
|
const commits: IncomingCommit[] = [];
|
|
for (const line of logOutput.split('\n').filter(Boolean)) {
|
|
const [hash, shortHash, message, author, date] = line.split('|');
|
|
|
|
// Get files changed in this commit
|
|
const filesOutput = await execGitSafe(
|
|
['diff-tree', '--no-commit-id', '--name-only', '-r', hash],
|
|
repoPath
|
|
);
|
|
const files = filesOutput.split('\n').filter(Boolean);
|
|
|
|
commits.push({ hash, shortHash, message, author, date, files });
|
|
}
|
|
|
|
return { hasUpdates: true, commitsBehind, commits };
|
|
}
|
|
```
|
|
|
|
### 4. Add Pull Action
|
|
|
|
**File:** `src/routes/databases/[id]/changes/+page.server.ts`
|
|
|
|
```typescript
|
|
export const actions: Actions = {
|
|
// ... existing actions ...
|
|
|
|
pull: async ({ params }) => {
|
|
const id = parseInt(params.id || '', 10);
|
|
const database = databaseInstancesQueries.getById(id);
|
|
|
|
if (!database) {
|
|
return { success: false, error: 'Database not found' };
|
|
}
|
|
|
|
try {
|
|
const result = await pcdManager.sync(id);
|
|
return result;
|
|
} catch (err) {
|
|
return {
|
|
success: false,
|
|
error: err instanceof Error ? err.message : 'Failed to pull'
|
|
};
|
|
}
|
|
}
|
|
};
|
|
```
|
|
|
|
### 5. Update Page UI
|
|
|
|
**File:** `src/routes/databases/[id]/changes/+page.svelte`
|
|
|
|
```svelte
|
|
<script lang="ts">
|
|
export let data: PageData;
|
|
|
|
let incomingChanges: IncomingChanges | null = null;
|
|
// ... existing state ...
|
|
|
|
$: isDeveloper = data.isDeveloper;
|
|
</script>
|
|
|
|
<!-- Incoming Changes Section (visible to everyone) -->
|
|
<section>
|
|
<h2>Incoming Changes</h2>
|
|
|
|
{#if incomingChanges?.hasUpdates}
|
|
<p>{incomingChanges.commitsBehind} commits available</p>
|
|
|
|
<!-- Expandable table showing commits -->
|
|
<ExpandableTable data={incomingChanges.commits} ...>
|
|
<!-- Show commit message, author, date -->
|
|
<!-- Expanded: show files changed -->
|
|
</ExpandableTable>
|
|
|
|
<Button on:click={handlePull}>Pull Updates</Button>
|
|
{:else}
|
|
<p>Up to date</p>
|
|
{/if}
|
|
</section>
|
|
|
|
<!-- Outgoing Changes Section (developers only) -->
|
|
{#if isDeveloper}
|
|
<section>
|
|
<h2>Outgoing Changes</h2>
|
|
<!-- Existing uncommitted ops table -->
|
|
<!-- Existing commit message + push UI -->
|
|
</section>
|
|
{/if}
|
|
```
|
|
|
|
---
|
|
|
|
## Sync Job Integration
|
|
|
|
When the sync job runs with `auto_pull = 0`:
|
|
|
|
1. `checkForUpdates()` already fetches and counts commits behind
|
|
2. This data is already available via `git.status()` (the `behind` field)
|
|
3. The `/api/databases/[id]/changes` endpoint will show this when user visits
|
|
|
|
No additional "store" needed - the git state IS the store. Each time the user
|
|
visits the changes page, we fetch fresh data from git.
|
|
|
|
---
|
|
|
|
## Files to Create/Modify
|
|
|
|
### Modified Files
|
|
|
|
- `src/routes/databases/[id]/changes/+page.server.ts` - Remove PAT requirement
|
|
for load, add pull action
|
|
- `src/routes/databases/[id]/changes/+page.svelte` - Add incoming changes UI
|
|
- `src/routes/api/databases/[id]/changes/+server.ts` - Remove PAT requirement
|
|
for GET, add incoming changes data
|
|
- `src/lib/server/utils/git/status.ts` - Add `getIncomingChanges()` function
|
|
- `src/lib/server/utils/git/types.ts` - Add `IncomingChanges` types
|
|
- `src/lib/server/utils/git/Git.ts` - Expose `getIncomingChanges()`
|
|
|
|
### New Components (optional)
|
|
|
|
- `src/routes/databases/[id]/changes/components/IncomingChangesTable.svelte`
|
|
- `src/routes/databases/[id]/changes/components/OutgoingChangesTable.svelte`
|
|
|
|
Could extract existing table into `OutgoingChangesTable` and create matching
|
|
`IncomingChangesTable` for consistency.
|
|
|
|
---
|
|
|
|
## Edge Cases
|
|
|
|
1. **No incoming changes**: Show "Up to date" message
|
|
2. **Pull fails**: Show error, allow retry
|
|
3. **Conflicts**: Shouldn't happen since user_ops are gitignored, but handle
|
|
gracefully if it does
|
|
4. **Large number of commits**: Paginate or limit to recent N commits
|
|
|
|
---
|
|
|
|
## UI Considerations
|
|
|
|
- StatusCard (repo info, branch switcher) visible for everyone
|
|
- Use consistent table styling between incoming/outgoing
|
|
- Incoming table is read-only (no checkboxes)
|
|
- Clear visual separation between sections
|
|
- Consider showing incoming changes count in the tab/nav if updates available
|