From 1081547151528aa36eda16789a2108ffdf9aaba7 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Thu, 4 Jun 2026 19:10:22 +1000 Subject: [PATCH] feat(actions): remove auto-commit option from actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strip the per-action auto-commit toggle end-to-end. Actions now only execute their command and never create git commits. - UI: drop the auto-commit checkbox and `autoCommit` form state from the action editor, and remove the field from create/update/detection flows. - TS bindings: remove `autoCommit` from `ProjectAction`/`SuggestedAction` and the `createRepoAction`/`updateProjectAction` signatures, and delete the unused `ActionAutoCommitEvent`/`listenToActionAutoCommit`. - Tauri commands: drop the `auto_commit` parameter from `create_repo_action` and `update_project_action` in `lib.rs` and `web_server.rs`. - Store: remove `RepoAction::auto_commit` and `with_auto_commit`, drop the column from all SQL statements, and add migration 0016 to drop the `repo_actions.auto_commit` column. - Execution: stop propagating the flag into `ActionMetadata` on the local and remote paths. - builderbot-actions crate: remove the auto-commit machinery — the `AutoCommitContext` enum, `git` module, `ExecutionEvent::AutoCommit`, the post-success commit block, and the `autoCommit` field from the detector prompt and `SuggestedAction`. Verified with cargo check/clippy, the crate and store migration tests, and svelte-check/tsc. Signed-off-by: Matt Toohey --- apps/staged/src-tauri/src/actions/commands.rs | 14 +-- apps/staged/src-tauri/src/actions/events.rs | 14 --- apps/staged/src-tauri/src/branches.rs | 4 +- apps/staged/src-tauri/src/lib.rs | 7 +- apps/staged/src-tauri/src/store/actions.rs | 20 ++-- .../src-tauri/src/store/migration_tests.rs | 14 ++- .../0016-drop-action-auto-commit/up.sql | 4 + apps/staged/src-tauri/src/store/models.rs | 7 -- apps/staged/src-tauri/src/web_server.rs | 6 +- apps/staged/src/lib/commands.ts | 9 +- .../src/lib/features/actions/actions.ts | 19 ---- .../settings/ActionsSettingsPanel.svelte | 26 +---- crates/builderbot-actions/src/detector.rs | 13 +-- crates/builderbot-actions/src/executor.rs | 77 +-------------- crates/builderbot-actions/src/git.rs | 99 ------------------- crates/builderbot-actions/src/lib.rs | 3 - crates/builderbot-actions/src/models.rs | 6 -- 17 files changed, 39 insertions(+), 303 deletions(-) create mode 100644 apps/staged/src-tauri/src/store/migrations/0016-drop-action-auto-commit/up.sql delete mode 100644 crates/builderbot-actions/src/git.rs diff --git a/apps/staged/src-tauri/src/actions/commands.rs b/apps/staged/src-tauri/src/actions/commands.rs index 98fa1db21..961c289bb 100644 --- a/apps/staged/src-tauri/src/actions/commands.rs +++ b/apps/staged/src-tauri/src/actions/commands.rs @@ -221,7 +221,6 @@ pub(crate) async fn run_branch_action_impl( let metadata = ActionMetadata { action_id: action.id.clone(), action_name: action.name.clone(), - auto_commit: action.auto_commit, }; // Clone values needed after execute() moves them. @@ -309,15 +308,8 @@ pub(crate) async fn run_branch_action_impl( shell_command, ]; - // Provide auto-commit context so that after a successful action, - // git commands run on the remote workspace via `sq blox ws exec`. - // When there's no resolved path we can't determine the git working - // directory, so auto-commit is skipped (unlikely for remote branches). - let auto_commit_info = resolved_repo_path - .map(|resolved| (sq_binary.clone(), workspace_name.to_string(), resolved)); - let eid = executor - .execute_remote(sq_binary, args, metadata, listener, auto_commit_info) + .execute_remote(sq_binary, args, metadata, listener) .await .map_err(|e| format!("Failed to execute remote action: {e}"))?; @@ -653,8 +645,7 @@ pub(crate) async fn run_prerun_actions_impl( suggestion.command, suggestion.action_type, next_sort_order, - ) - .with_auto_commit(suggestion.auto_commit); + ); store .create_repo_action(&action) .map_err(|e| format!("Failed to create detected action: {e}"))?; @@ -715,7 +706,6 @@ pub(crate) async fn run_prerun_actions_impl( let metadata = ActionMetadata { action_id: action.id.clone(), action_name: action.name.clone(), - auto_commit: action.auto_commit, }; let execution_id = executor diff --git a/apps/staged/src-tauri/src/actions/events.rs b/apps/staged/src-tauri/src/actions/events.rs index 4f0a4e1f1..578c420e9 100644 --- a/apps/staged/src-tauri/src/actions/events.rs +++ b/apps/staged/src-tauri/src/actions/events.rs @@ -155,20 +155,6 @@ impl ExecutionListener for TauriExecutionListener { }, ); } - ExecutionEvent::AutoCommit { - execution_id, - action_name, - } => { - crate::web_server::emit_to_all( - &self.app, - "action_auto_commit", - serde_json::json!({ - "executionId": execution_id, - "branchId": self.branch_id, - "actionName": action_name, - }), - ); - } } } } diff --git a/apps/staged/src-tauri/src/branches.rs b/apps/staged/src-tauri/src/branches.rs index 68d0fff34..3dd6d8d91 100644 --- a/apps/staged/src-tauri/src/branches.rs +++ b/apps/staged/src-tauri/src/branches.rs @@ -2450,8 +2450,7 @@ pub(crate) async fn run_prerun_actions_for_branch( suggestion.command, suggestion.action_type, next_sort_order, - ) - .with_auto_commit(suggestion.auto_commit); + ); store .create_repo_action(&action) .map_err(|e| format!("Failed to create detected action: {e}"))?; @@ -2516,7 +2515,6 @@ pub(crate) async fn run_prerun_actions_for_branch( let metadata = ActionMetadata { action_id: action.id.clone(), action_name: action.name.clone(), - auto_commit: action.auto_commit, }; // execute_and_wait runs the action and waits for it to finish, diff --git a/apps/staged/src-tauri/src/lib.rs b/apps/staged/src-tauri/src/lib.rs index f277605ec..a6cb6841f 100644 --- a/apps/staged/src-tauri/src/lib.rs +++ b/apps/staged/src-tauri/src/lib.rs @@ -1662,7 +1662,6 @@ fn update_project_action( command: String, action_type: String, sort_order: i32, - auto_commit: bool, ) -> Result<(), String> { let store = get_store(&store)?; let action = store @@ -1678,7 +1677,6 @@ fn update_project_action( action_type: builderbot_actions::ActionType::parse(&action_type) .ok_or_else(|| format!("Invalid action type: {action_type}"))?, sort_order, - auto_commit, run_detection_mode: action.run_detection_mode, created_at: action.created_at, updated_at: store::now_timestamp(), @@ -1724,7 +1722,6 @@ fn list_repo_actions( } #[tauri::command(rename_all = "camelCase")] -#[allow(clippy::too_many_arguments)] fn create_repo_action( store: tauri::State<'_, Mutex>>>, github_repo: String, @@ -1733,7 +1730,6 @@ fn create_repo_action( command: String, action_type: String, sort_order: i32, - auto_commit: bool, ) -> Result { let store = get_store(&store)?; let context = store @@ -1741,8 +1737,7 @@ fn create_repo_action( .map_err(|e| e.to_string())?; let parsed_type = builderbot_actions::ActionType::parse(&action_type) .ok_or_else(|| format!("Invalid action type: {action_type}"))?; - let action = store::models::RepoAction::new(context.id, name, command, parsed_type, sort_order) - .with_auto_commit(auto_commit); + let action = store::models::RepoAction::new(context.id, name, command, parsed_type, sort_order); store .create_repo_action(&action) .map_err(|e| e.to_string())?; diff --git a/apps/staged/src-tauri/src/store/actions.rs b/apps/staged/src-tauri/src/store/actions.rs index e233e84ce..b570693ca 100644 --- a/apps/staged/src-tauri/src/store/actions.rs +++ b/apps/staged/src-tauri/src/store/actions.rs @@ -118,8 +118,8 @@ impl Store { .transpose() .map_err(|e| StoreError(format!("Failed to serialize run_detection_mode: {e}")))?; conn.execute( - "INSERT INTO repo_actions (id, context_id, name, command, action_type, sort_order, auto_commit, run_detection_mode, created_at, updated_at) - VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)", + "INSERT INTO repo_actions (id, context_id, name, command, action_type, sort_order, run_detection_mode, created_at, updated_at) + VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)", params![ action.id, action.context_id, @@ -127,7 +127,6 @@ impl Store { action.command, action.action_type.as_str(), action.sort_order, - action.auto_commit as i32, run_detection_mode_json, action.created_at, action.updated_at, @@ -139,7 +138,7 @@ impl Store { pub fn get_repo_action(&self, id: &str) -> Result, StoreError> { let conn = self.conn.lock().unwrap(); conn.query_row( - "SELECT id, context_id, name, command, action_type, sort_order, auto_commit, run_detection_mode, created_at, updated_at + "SELECT id, context_id, name, command, action_type, sort_order, run_detection_mode, created_at, updated_at FROM repo_actions WHERE id = ?1", params![id], Self::row_to_repo_action, @@ -151,7 +150,7 @@ impl Store { pub fn list_repo_actions(&self, context_id: &str) -> Result, StoreError> { let conn = self.conn.lock().unwrap(); let mut stmt = conn.prepare( - "SELECT id, context_id, name, command, action_type, sort_order, auto_commit, run_detection_mode, created_at, updated_at + "SELECT id, context_id, name, command, action_type, sort_order, run_detection_mode, created_at, updated_at FROM repo_actions WHERE context_id = ?1 ORDER BY sort_order ASC", )?; let rows = stmt.query_map(params![context_id], Self::row_to_repo_action)?; @@ -167,13 +166,12 @@ impl Store { .transpose() .map_err(|e| StoreError(format!("Failed to serialize run_detection_mode: {e}")))?; conn.execute( - "UPDATE repo_actions SET name = ?1, command = ?2, action_type = ?3, sort_order = ?4, auto_commit = ?5, run_detection_mode = ?6, updated_at = ?7 WHERE id = ?8", + "UPDATE repo_actions SET name = ?1, command = ?2, action_type = ?3, sort_order = ?4, run_detection_mode = ?5, updated_at = ?6 WHERE id = ?7", params![ action.name, action.command, action.action_type.as_str(), action.sort_order, - action.auto_commit as i32, run_detection_mode_json, now_timestamp(), action.id, @@ -234,8 +232,7 @@ impl Store { fn row_to_repo_action(row: &rusqlite::Row) -> rusqlite::Result { let action_type_str: String = row.get(4)?; - let auto_commit: i32 = row.get(6)?; - let run_detection_mode_str: Option = row.get(7)?; + let run_detection_mode_str: Option = row.get(6)?; let run_detection_mode: Option = run_detection_mode_str .as_deref() .and_then(|s| serde_json::from_str(s).ok()); @@ -246,10 +243,9 @@ impl Store { command: row.get(3)?, action_type: ActionType::parse(&action_type_str).unwrap_or(ActionType::Run), sort_order: row.get(5)?, - auto_commit: auto_commit != 0, run_detection_mode, - created_at: row.get(8)?, - updated_at: row.get(9)?, + created_at: row.get(7)?, + updated_at: row.get(8)?, }) } } diff --git a/apps/staged/src-tauri/src/store/migration_tests.rs b/apps/staged/src-tauri/src/store/migration_tests.rs index ede38aa0f..f3777dfeb 100644 --- a/apps/staged/src-tauri/src/store/migration_tests.rs +++ b/apps/staged/src-tauri/src/store/migration_tests.rs @@ -145,7 +145,7 @@ fn test_store_bootstraps_fresh_database_with_baseline_migration() { ) .unwrap(); - assert_eq!(version, 15); + assert_eq!(version, 16); assert_eq!(app_version, super::APP_VERSION); assert!(table_exists(&conn, "projects")); assert!(table_exists(&conn, "project_notes")); @@ -193,6 +193,10 @@ fn test_store_repairs_github_comment_tracking_user_version() { github_comment_type TEXT, github_comment_stale INTEGER NOT NULL DEFAULT 0 ); + CREATE TABLE repo_actions ( + id TEXT PRIMARY KEY, + auto_commit INTEGER NOT NULL DEFAULT 0 + ); ", ) .unwrap(); @@ -205,7 +209,7 @@ fn test_store_repairs_github_comment_tracking_user_version() { let version: i64 = conn .query_row("PRAGMA user_version", [], |row| row.get(0)) .unwrap(); - assert_eq!(version, 15); + assert_eq!(version, 16); assert!(column_exists(&conn, "sessions", "pipeline")); cleanup_db(&path); @@ -236,6 +240,10 @@ fn test_store_repairs_pipeline_user_version() { PRIMARY KEY (github_repo, subpath) ); CREATE TABLE comments (id TEXT PRIMARY KEY); + CREATE TABLE repo_actions ( + id TEXT PRIMARY KEY, + auto_commit INTEGER NOT NULL DEFAULT 0 + ); ", ) .unwrap(); @@ -248,7 +256,7 @@ fn test_store_repairs_pipeline_user_version() { let version: i64 = conn .query_row("PRAGMA user_version", [], |row| row.get(0)) .unwrap(); - assert_eq!(version, 15); + assert_eq!(version, 16); assert!(column_exists(&conn, "comments", "github_comment_id")); assert!(column_exists(&conn, "comments", "github_comment_type")); assert!(column_exists(&conn, "comments", "github_comment_stale")); diff --git a/apps/staged/src-tauri/src/store/migrations/0016-drop-action-auto-commit/up.sql b/apps/staged/src-tauri/src/store/migrations/0016-drop-action-auto-commit/up.sql new file mode 100644 index 000000000..9be6d7a7f --- /dev/null +++ b/apps/staged/src-tauri/src/store/migrations/0016-drop-action-auto-commit/up.sql @@ -0,0 +1,4 @@ +-- The auto-commit option has been removed from project actions. Actions now +-- only execute their command and never create git commits, so the column that +-- backed the per-action toggle is no longer needed. +ALTER TABLE repo_actions DROP COLUMN auto_commit; diff --git a/apps/staged/src-tauri/src/store/models.rs b/apps/staged/src-tauri/src/store/models.rs index b6c6d6a00..0b43d71d5 100644 --- a/apps/staged/src-tauri/src/store/models.rs +++ b/apps/staged/src-tauri/src/store/models.rs @@ -934,7 +934,6 @@ pub struct RepoAction { pub command: String, pub action_type: ActionType, pub sort_order: i32, - pub auto_commit: bool, pub run_detection_mode: Option, pub created_at: i64, pub updated_at: i64, @@ -956,17 +955,11 @@ impl RepoAction { command, action_type, sort_order, - auto_commit: false, run_detection_mode: None, created_at: now, updated_at: now, } } - - pub fn with_auto_commit(mut self, auto_commit: bool) -> Self { - self.auto_commit = auto_commit; - self - } } // ============================================================================= diff --git a/apps/staged/src-tauri/src/web_server.rs b/apps/staged/src-tauri/src/web_server.rs index e0775802c..f880de8ed 100644 --- a/apps/staged/src-tauri/src/web_server.rs +++ b/apps/staged/src-tauri/src/web_server.rs @@ -2025,7 +2025,6 @@ async fn dispatch(command: &str, args: Value, state: &WebAppState) -> Result Result Result Result { return invokeCommand('update_project_action', { actionId, @@ -475,7 +473,6 @@ export function updateProjectAction( command, actionType, sortOrder, - autoCommit, }); } @@ -507,8 +504,7 @@ export function createRepoAction( name: string, command: string, actionType: string, - sortOrder: number, - autoCommit: boolean + sortOrder: number ): Promise { return invokeCommand('create_repo_action', { githubRepo, @@ -517,7 +513,6 @@ export function createRepoAction( command, actionType, sortOrder, - autoCommit, }); } diff --git a/apps/staged/src/lib/features/actions/actions.ts b/apps/staged/src/lib/features/actions/actions.ts index fd92d5cad..2d751c6c6 100644 --- a/apps/staged/src/lib/features/actions/actions.ts +++ b/apps/staged/src/lib/features/actions/actions.ts @@ -44,7 +44,6 @@ export interface ProjectAction { command: string; actionType: ActionType; sortOrder: number; - autoCommit: boolean; runDetectionMode?: RunDetectionMode; createdAt: number; updatedAt: number; @@ -55,7 +54,6 @@ export interface SuggestedAction { name: string; command: string; actionType: ActionType; - autoCommit: boolean; source: string; // e.g., "package.json", "justfile" } @@ -86,13 +84,6 @@ export interface ActionStatusEvent { completedAt?: number; } -/** Event payload for auto-commit notifications. */ -export interface ActionAutoCommitEvent { - executionId: string; - branchId: string; - actionName: string; -} - /** Event payload for repo action detection status (header badge). */ export interface RepoActionsDetectionEvent { githubRepo: string; @@ -225,16 +216,6 @@ export function listenToActionStatus(callback: (event: ActionStatusEvent) => voi return listenToEvent('action_status', callback); } -/** - * Listen for action auto-commit events. - * Returns an unlisten function to stop listening. - */ -export function listenToActionAutoCommit( - callback: (event: ActionAutoCommitEvent) => void -): UnlistenFn { - return listenToEvent('action_auto_commit', callback); -} - /** Listen for repo action detection start/stop updates. */ export function listenToRepoActionsDetection( callback: (event: RepoActionsDetectionEvent) => void diff --git a/apps/staged/src/lib/features/settings/ActionsSettingsPanel.svelte b/apps/staged/src/lib/features/settings/ActionsSettingsPanel.svelte index 2aab36809..afdcf4728 100644 --- a/apps/staged/src/lib/features/settings/ActionsSettingsPanel.svelte +++ b/apps/staged/src/lib/features/settings/ActionsSettingsPanel.svelte @@ -67,7 +67,6 @@ name: '', command: '', actionType: 'run' as ActionType, - autoCommit: false, }); let badgeEditName = $state(''); let badgeEditHue = $state(0); @@ -313,8 +312,7 @@ suggestion.name, suggestion.command, suggestion.actionType, - nextSortOrder++, - suggestion.autoCommit + nextSortOrder++ ); if (selectedRepoKey === entryKey) { actions = [...actions, newAction]; @@ -336,7 +334,7 @@ } function startAddAction() { - editForm = { name: '', command: '', actionType: 'run', autoCommit: false }; + editForm = { name: '', command: '', actionType: 'run' }; editingAction = {} as ProjectAction; } @@ -345,7 +343,6 @@ name: action.name, command: action.command, actionType: action.actionType as ActionType, - autoCommit: action.autoCommit, }; editingAction = action; } @@ -372,8 +369,7 @@ editForm.name, editForm.command, editForm.actionType, - nextSortOrder, - editForm.autoCommit + nextSortOrder ); if (selectedRepoKey === entryKey) { actions = [...actions, newAction]; @@ -385,8 +381,7 @@ editForm.name, editForm.command, editForm.actionType, - editingAction.sortOrder, - editForm.autoCommit + editingAction.sortOrder ); actions = actions.map((a) => a.id === actionId @@ -395,7 +390,6 @@ name: editForm.name, command: editForm.command, actionType: editForm.actionType, - autoCommit: editForm.autoCommit, } : a ); @@ -774,10 +768,6 @@ -