Skip to content

Commit 743cda2

Browse files
authored
Merge pull request #227 from Open-VCS/Fix-bugs
2 parents bcc3b87 + 492802d commit 743cda2

35 files changed

Lines changed: 177 additions & 324 deletions

.github/workflows/opencode-review.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111
permissions:
1212
id-token: write
13-
contents: read
13+
contents: write
1414
pull-requests: write
1515
issues: write
1616

@@ -26,11 +26,12 @@ jobs:
2626
env:
2727
OPENCODE_API_KEY: ${{ secrets.ZEN_API_KEY }}
2828
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
29-
#GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3030
with:
3131
model: ${{ vars.OPENCODE_REVIEW_MODEL }}
32-
use_github_token: false
32+
use_github_token: true
3333
prompt: |
34+
Before anything else, read and follow AGENTS.md for this repository and module.
3435
Review this pull request:
3536
- Check for code quality issues
3637
- Look for potential bugs
@@ -43,11 +44,12 @@ jobs:
4344
env:
4445
OPENCODE_API_KEY: ${{ secrets.ZEN_API_KEY }}
4546
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
46-
#GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
47+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4748
with:
4849
model: ${{ vars.OPENCODE_REVIEW_MODEL_FALLBACK }}
49-
use_github_token: false
50+
use_github_token: true
5051
prompt: |
52+
Before anything else, read and follow AGENTS.md for this repository and module.
5153
Review this pull request:
5254
- Check for code quality issues
5355
- Look for potential bugs

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- `Backend/`: Rust + Tauri backend (`src/`), commands (`src/tauri_commands/`), plugin runtime (`src/plugin_runtime/`), and config-driven plugin sync support (`scripts/`).
66
- `openvcs.plugins.json`: built-in plugin source list used to materialize shipped plugins during client builds.
77
- `Frontend/`: TypeScript + Vite UI code (`src/scripts/`, `src/styles/`, `src/modals/`), with Vitest tests colocated as `*.test.ts` files.
8+
- OpenVCS is desktop-only: there is no web app, no standalone browser mode, and no supported web browser/WebView deployment target.
89
- `docs/`: UX docs, plugin architecture notes, and plugin/theme packaging guides referenced by contributors.
910
- `packaging/flatpak/`: Flatpak manifests and Flatpak-specific build notes.
1011
- Supporting files at the repo root include the workspace `Cargo.toml`, `Justfile`, `README.md`, `ARCHITECTURE.md`, `SECURITY.md`, and installer scripts.
@@ -47,7 +48,7 @@
4748
### Development servers
4849

4950
- `cargo tauri dev`: run the desktop app in dev mode (`Backend/` directory).
50-
- `npm --prefix Frontend run dev`: run the frontend-only Vite dev server.
51+
- `npm --prefix Frontend run dev`: run the frontend-only Vite dev server for desktop UI development only; it is not a web app/browser deployment.
5152

5253
## Plugin runtime & host expectations
5354

Backend/src/state.rs

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,44 +18,6 @@ use serde::{Deserialize, Serialize};
1818
/// Default number of recent repositories stored when settings are missing or invalid.
1919
pub const MAX_RECENTS: usize = 10;
2020

21-
/// Applies Git SSH-related environment variables from current settings.
22-
///
23-
/// # Parameters
24-
/// - `cfg`: Current app configuration.
25-
///
26-
/// # Returns
27-
/// - `()`.
28-
fn apply_git_ssh_env(cfg: &AppConfig) {
29-
// Prefer config-driven runtime env so the VCS backend (in another crate) can read it.
30-
// Keep env var names stable for packaging and troubleshooting.
31-
unsafe {
32-
// Safety: OpenVCS sets these env vars during startup/config updates and treats them as
33-
// process-wide configuration for child processes (e.g. `git`).
34-
std::env::set_var(
35-
"OPENVCS_SSH_MODE",
36-
match cfg.git.ssh_binary {
37-
crate::settings::GitSshBinary::Auto => "auto",
38-
crate::settings::GitSshBinary::Host => "host",
39-
crate::settings::GitSshBinary::Bundled => "bundled",
40-
crate::settings::GitSshBinary::Custom => "custom",
41-
},
42-
);
43-
}
44-
if cfg.git.ssh_binary == crate::settings::GitSshBinary::Custom
45-
&& !cfg.git.ssh_path.trim().is_empty()
46-
{
47-
unsafe {
48-
// Safety: see comment above.
49-
std::env::set_var("OPENVCS_SSH", cfg.git.ssh_path.trim());
50-
}
51-
} else {
52-
unsafe {
53-
// Safety: see comment above.
54-
std::env::remove_var("OPENVCS_SSH");
55-
}
56-
}
57-
}
58-
5921
/// Central application state.
6022
/// Keeps track of the currently open repo and MRU recents.
6123
/// Backend choice is tied to each repo (via `Repo::id()`), not stored globally.
@@ -84,10 +46,9 @@ impl AppState {
8446
/// Creates app state by loading persisted settings and recent repositories.
8547
///
8648
/// # Returns
87-
/// - A fully initialized [`AppState`] with config, recents, and runtime env applied.
49+
/// - A fully initialized [`AppState`] with config and recent repositories loaded.
8850
pub fn new_with_config() -> Self {
8951
let cfg = AppConfig::load_or_default(); // reads ~/.config/openvcs/openvcs.conf
90-
apply_git_ssh_env(&cfg);
9152
let s = Self {
9253
config: RwLock::new(cfg),
9354
repo_config: RwLock::new(RepoConfig::default()),
@@ -125,7 +86,6 @@ impl AppState {
12586
next.migrate();
12687
next.validate();
12788
next.save().map_err(|e| e.to_string())?;
128-
apply_git_ssh_env(&next);
12989
crate::monitoring::sync_backend_monitoring(&next);
13090
*self.config.write() = next;
13191
self.enforce_recents_limit_and_persist();

Frontend/src/scripts/features/about.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export async function openAbout(): Promise<void> {
3535
if (!modal) return;
3636

3737
try {
38-
const info = (TAURI.has ? await TAURI.invoke("about_info").catch(() => null) : null) as
38+
const info = (await TAURI.invoke("about_info").catch(() => null)) as
3939
| {
4040
version?: string;
4141
build?: string;

Frontend/src/scripts/features/branches.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ function syncBranchLabelsFromState() {
4040
/* ---------------- data load ---------------- */
4141

4242
async function loadBranches() {
43-
if (!TAURI.has) return;
4443
try {
4544
const branches = await TAURI.invoke<Branch[]>('git_list_branches');
4645
state.branches = Array.isArray(branches) ? branches : [];
@@ -174,7 +173,7 @@ export function bindBranchUI() {
174173
return;
175174
}
176175
try {
177-
if (TAURI.has) await TAURI.invoke('git_checkout_branch', { name });
176+
await TAURI.invoke('git_checkout_branch', { name });
178177
await runHook('onSwitchBranch', hookData);
179178
await loadBranches(); // resync from backend instead of manual toggles
180179
if (options.closePopover) closeBranchPopover();
@@ -214,7 +213,7 @@ export function bindBranchUI() {
214213
const ok = await confirmBool(`Merge '${name}' into '${cur}'?`);
215214
if (!ok) return;
216215
try {
217-
if (TAURI.has) await TAURI.invoke('git_merge_branch', { name });
216+
await TAURI.invoke('git_merge_branch', { name });
218217
notify(`Merged branch '${name}' into '${cur}'`);
219218
await Promise.allSettled([renderList(), loadBranches()]);
220219
} catch (e) {
@@ -263,7 +262,7 @@ export function bindBranchUI() {
263262
notify(pre.reason || 'Delete cancelled');
264263
return;
265264
}
266-
if (TAURI.has) await TAURI.invoke('git_delete_branch', { name, force: wantForce });
265+
await TAURI.invoke('git_delete_branch', { name, force: wantForce });
267266
await runHook('onBranchDelete', hookData);
268267
notify(`${wantForce ? 'Force-deleted' : 'Deleted'} '${name}'`);
269268
await loadBranches();
@@ -286,7 +285,7 @@ export function bindBranchUI() {
286285
notify(pre.reason || 'Delete cancelled');
287286
return;
288287
}
289-
if (TAURI.has) await TAURI.invoke('git_delete_branch', { name, force: true });
288+
await TAURI.invoke('git_delete_branch', { name, force: true });
290289
await runHook('onBranchDelete', hookData);
291290
notify(`Force-deleted '${name}'`);
292291
await loadBranches();

Frontend/src/scripts/features/cherryPick.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ export function wireCherryPick() {
3131
const branch = (branchEl?.value || '').trim();
3232
if (!commit || !branch) return;
3333
try {
34-
if (!TAURI.has) {
35-
notify('Cherry-pick requires the desktop app');
36-
return;
37-
}
3834
await TAURI.invoke('git_cherry_pick_to_branch', { id: commit, branch });
3935
notify(`Cherry-picked onto ${branch}`);
4036
closeModal('cherry-pick-modal');
@@ -74,11 +70,6 @@ export async function openCherryPick(commit: CommitLike) {
7470
hydrate('cherry-pick-modal');
7571
wireCherryPick();
7672

77-
if (!TAURI.has) {
78-
notify('Cherry-pick requires the desktop app');
79-
return;
80-
}
81-
8273
await hydrateBranches();
8374
const branches = (state.branches || [])
8475
.filter((b: any) => {

Frontend/src/scripts/features/commandSheet.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ function setDisabled(id: string, on: boolean) {
3535
}
3636

3737
async function validateClone() {
38-
if (!TAURI.has) return;
3938
const url = cloneUrl?.value.trim();
4039
const dest = clonePath?.value.trim();
4140
try {
@@ -48,7 +47,6 @@ async function validateClone() {
4847
}
4948

5049
async function validateAdd() {
51-
if (!TAURI.has) return;
5250
const path = addPath?.value.trim();
5351
try {
5452
const res = await TAURI.invoke<{ ok: boolean; reason?: string }>("validate_add_path", { path });
@@ -186,7 +184,6 @@ export function bindCommandSheet() {
186184

187185
// Browse buttons
188186
el<HTMLButtonElement>("#browse-clone", root)?.addEventListener("click", async () => {
189-
if (!TAURI.has) return;
190187
try {
191188
const dir = await TAURI.invoke<string>("browse_directory", { purpose: "clone_dest" });
192189
if (dir && clonePath) {
@@ -197,7 +194,6 @@ export function bindCommandSheet() {
197194
});
198195

199196
el<HTMLButtonElement>("#browse-add", root)?.addEventListener("click", async () => {
200-
if (!TAURI.has) return;
201197
try {
202198
const dir = await TAURI.invoke<string>("browse_directory", { purpose: "add_repo" });
203199
if (dir && addPath) {
@@ -213,7 +209,7 @@ export function bindCommandSheet() {
213209
const dest = clonePath?.value.trim();
214210
if (!url || !dest) return;
215211
try {
216-
if (TAURI.has) await TAURI.invoke("clone_repo", { url, dest });
212+
await TAURI.invoke("clone_repo", { url, dest });
217213
await refreshRepoSummary(); // ensure state + event
218214
notify(`Cloned ${url}${dest}`);
219215
closeSheet();
@@ -226,7 +222,7 @@ export function bindCommandSheet() {
226222
const path = addPath?.value.trim();
227223
if (!path) return;
228224
try {
229-
if (TAURI.has) await TAURI.invoke("add_repo", { path });
225+
await TAURI.invoke("add_repo", { path });
230226
await refreshRepoSummary(); // ensure state + event
231227
notify(`Added ${path}`);
232228
closeSheet();

Frontend/src/scripts/features/conflicts.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ async function ensureMergeModal() {
2525

2626
const applyBtn = modal.querySelector<HTMLButtonElement>('#merge-apply');
2727
applyBtn?.addEventListener('click', async () => {
28-
if (!TAURI.has) { notify('Saving merges requires the desktop app.'); return; }
2928
if (!currentConflict) { notify('No conflict selected.'); return; }
3029
const textarea = modal.querySelector<HTMLTextAreaElement>('#merge-result');
3130
const content = textarea?.value ?? '';
@@ -85,7 +84,6 @@ async function ensureSummaryModal() {
8584
const contBtn = modal.querySelector<HTMLButtonElement>('#conflicts-continue');
8685

8786
abortBtn?.addEventListener('click', async () => {
88-
if (!TAURI.has) return;
8987
const ok = await confirmBool('Abort the merge? This will discard merge progress.');
9088
if (!ok) return;
9189
try {
@@ -99,7 +97,6 @@ async function ensureSummaryModal() {
9997
});
10098

10199
contBtn?.addEventListener('click', async () => {
102-
if (!TAURI.has) return;
103100
try {
104101
await TAURI.invoke('git_merge_continue');
105102
notify('Merge committed');
@@ -114,7 +111,6 @@ async function ensureSummaryModal() {
114111
}
115112

116113
export async function openConflictsSummary(files: FileStatus[]): Promise<void> {
117-
if (!TAURI.has) return;
118114
await ensureSummaryModal();
119115
const modal = document.getElementById('conflicts-summary-modal') as HTMLElement | null;
120116
if (!modal) return;
@@ -207,7 +203,6 @@ export async function openConflictsSummary(files: FileStatus[]): Promise<void> {
207203
}
208204

209205
export async function autoOpenFirstConflict(files: FileStatus[]): Promise<void> {
210-
if (!TAURI.has) return;
211206
if (!Array.isArray(files) || files.length === 0) return;
212207

213208
const conflicted = files.find((f) => String(f?.status || '').toUpperCase() === 'U' && !!f?.path);
@@ -230,7 +225,7 @@ export async function autoOpenFirstConflict(files: FileStatus[]): Promise<void>
230225
}
231226

232227
async function ensureExternalMergeConfig() {
233-
if (externalToolState.loaded || !TAURI.has) return;
228+
if (externalToolState.loaded) return;
234229
try {
235230
const cfg = await TAURI.invoke<GlobalSettings>('get_global_settings');
236231
const tool = cfg?.diff?.external_merge;
@@ -244,13 +239,11 @@ async function ensureExternalMergeConfig() {
244239
}
245240

246241
export async function hasExternalMergeTool(): Promise<boolean> {
247-
if (!TAURI.has) return false;
248242
await ensureExternalMergeConfig();
249243
return externalToolState.enabled;
250244
}
251245

252246
export async function launchExternalMergeTool(path: string): Promise<void> {
253-
if (!TAURI.has) { notify('Launching merge tools requires the desktop app.'); return; }
254247
if (!(await hasExternalMergeTool())) {
255248
notify('No custom merge tool configured');
256249
return;

Frontend/src/scripts/features/diff.ts

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -50,37 +50,36 @@ export function bindCommit() {
5050
const selLines = linesMap[path] || {};
5151
combinedPatch += buildPatchForSelected(path, lines, selHunks, selLines) + '\n';
5252
}
53-
if (TAURI.has) {
54-
if (combinedPatch.trim().length > 0 || selectedFiles.length > 0) {
55-
const hookData = {
56-
summary,
57-
description,
58-
branch: state.branch,
59-
files: selectedFiles,
60-
stagedFiles: stagePaths,
61-
partialFiles,
62-
patch: combinedPatch,
63-
};
64-
const pre = await runHook('preCommit', hookData);
65-
if (pre.cancelled) {
66-
notify(pre.reason || 'Commit cancelled');
67-
clearBusy('Ready');
68-
return;
69-
}
70-
summary = String(hookData.summary || '').trim() || summary;
71-
description = String(hookData.description || '');
72-
await TAURI.invoke('commit_patch_and_files', {
73-
summary,
74-
description,
75-
patch: combinedPatch,
76-
files: selectedFiles,
77-
stagePaths,
78-
});
79-
await runHook('onCommit', hookData);
80-
} else {
81-
notify('Select files or hunks to commit');
53+
if (combinedPatch.trim().length > 0 || selectedFiles.length > 0) {
54+
const hookData = {
55+
summary,
56+
description,
57+
branch: state.branch,
58+
files: selectedFiles,
59+
stagedFiles: stagePaths,
60+
partialFiles,
61+
patch: combinedPatch,
62+
};
63+
const pre = await runHook('preCommit', hookData);
64+
if (pre.cancelled) {
65+
notify(pre.reason || 'Commit cancelled');
66+
clearBusy('Ready');
8267
return;
8368
}
69+
summary = String(hookData.summary || '').trim() || summary;
70+
description = String(hookData.description || '');
71+
await TAURI.invoke('commit_patch_and_files', {
72+
summary,
73+
description,
74+
patch: combinedPatch,
75+
files: selectedFiles,
76+
stagePaths,
77+
});
78+
await runHook('onCommit', hookData);
79+
}
80+
else {
81+
notify('Select files or hunks to commit');
82+
return;
8483
}
8584
notify(`Committed to ${state.branch}: ${summary}`);
8685
if (commitSummary) commitSummary.value = '';

Frontend/src/scripts/features/newBranch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ export function wireNewBranch() {
128128
return;
129129
}
130130
}
131-
if (TAURI.has) await TAURI.invoke('git_create_branch', { name, from, checkout });
131+
await TAURI.invoke('git_create_branch', { name, from, checkout });
132132
await runHook('onBranchCreate', hookData);
133133
if (checkout) {
134134
await runHook('onSwitchBranch', { from: state.branch, to: name });

0 commit comments

Comments
 (0)