mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-07 08:27:12 +00:00
Reject automatic pull requests for Local runs
This commit is contained in:
parent
efcf8a0d93
commit
18cb752b61
4 changed files with 137 additions and 0 deletions
|
|
@ -557,6 +557,8 @@ The `sandbox` transport runs the MCP server inside the workflow's sandbox. This
|
|||
|
||||
Automatically open a GitHub pull request when the workflow run completes successfully. Requires a [GitHub App](/integrations/github) to be configured.
|
||||
|
||||
Automatic pull requests require a clone-based Docker or Daytona environment. For in-place Local runs, leave pull requests disabled; run creation rejects a Local environment when `enabled = true`.
|
||||
|
||||
```toml title="run.toml"
|
||||
[run.pull_request]
|
||||
enabled = true
|
||||
|
|
|
|||
|
|
@ -133,6 +133,10 @@ pub(crate) enum EnvironmentSelectionError {
|
|||
NotFound { id: EnvironmentId },
|
||||
#[error("{detail}")]
|
||||
TargetUnsupported { detail: &'static str },
|
||||
#[error(
|
||||
"automatic pull requests require a clone-based Docker or Daytona environment; disable run.pull_request.enabled for Local execution"
|
||||
)]
|
||||
AutomaticPullRequestUnsupported,
|
||||
#[error("{detail}")]
|
||||
ProviderDisabled {
|
||||
provider: SandboxProviderKind,
|
||||
|
|
|
|||
|
|
@ -1041,6 +1041,11 @@ fn run_intent_admission_error(error: RunIntentAdmissionError) -> Response {
|
|||
error.to_string(),
|
||||
"target_environment_unsupported",
|
||||
),
|
||||
EnvironmentSelectionError::AutomaticPullRequestUnsupported => intent_error(
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
error.to_string(),
|
||||
"pull_request_environment_unsupported",
|
||||
),
|
||||
EnvironmentSelectionError::ProviderDisabled { .. }
|
||||
| EnvironmentSelectionError::MissingCredential { .. } => intent_error(
|
||||
StatusCode::SERVICE_UNAVAILABLE,
|
||||
|
|
@ -1118,6 +1123,15 @@ async fn validate_intent_environment(
|
|||
if image_incompatible || target_incompatible {
|
||||
return Err(EnvironmentSelectionError::TargetUnsupported { detail });
|
||||
}
|
||||
if configured_provider == SandboxProviderKind::Local
|
||||
&& settings
|
||||
.run
|
||||
.pull_request
|
||||
.as_ref()
|
||||
.is_some_and(|pull_request| pull_request.enabled)
|
||||
{
|
||||
return Err(EnvironmentSelectionError::AutomaticPullRequestUnsupported);
|
||||
}
|
||||
if let Some(detail) =
|
||||
run_manifest::sandbox_provider_policy_error(&state.server_settings(), effective_provider)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4251,6 +4251,123 @@ async fn post_runs_run_intent_canonicalizes_and_persists_a_local_folder_target()
|
|||
assert!(projection.spec.definition_blob.is_some());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn post_runs_run_intent_rejects_automatic_pull_requests_for_local_environment() {
|
||||
let target = tempfile::tempdir().unwrap();
|
||||
let state = local_test_app_state();
|
||||
let app = crate::test_support::build_test_router(Arc::clone(&state));
|
||||
let workflow_version_id = store_workflow_version(
|
||||
&state,
|
||||
MINIMAL_DOT,
|
||||
Some("_version = 1\n[run.pull_request]\nenabled = true\n"),
|
||||
)
|
||||
.await;
|
||||
|
||||
let response =
|
||||
post_run_intent_response(&app, folder_intent(workflow_version_id, target.path())).await;
|
||||
let body = response_json!(response, StatusCode::UNPROCESSABLE_ENTITY).await;
|
||||
|
||||
assert_eq!(
|
||||
body["errors"][0]["code"],
|
||||
"pull_request_environment_unsupported"
|
||||
);
|
||||
assert_eq!(
|
||||
body["errors"][0]["detail"],
|
||||
"automatic pull requests require a clone-based Docker or Daytona environment; disable run.pull_request.enabled for Local execution"
|
||||
);
|
||||
assert!(state.runs.lock().expect("runs lock poisoned").is_empty());
|
||||
assert!(
|
||||
state
|
||||
.stores
|
||||
.run_summaries
|
||||
.list_identities()
|
||||
.await
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn post_runs_run_intent_accepts_disabled_pull_requests_for_local_environment() {
|
||||
let target = tempfile::tempdir().unwrap();
|
||||
let state = local_test_app_state();
|
||||
let app = crate::test_support::build_test_router(Arc::clone(&state));
|
||||
let workflow_version_id = store_workflow_version(
|
||||
&state,
|
||||
MINIMAL_DOT,
|
||||
Some("_version = 1\n[run.pull_request]\nenabled = false\n"),
|
||||
)
|
||||
.await;
|
||||
|
||||
let body = post_run_manifest(&app, folder_intent(workflow_version_id, target.path())).await;
|
||||
let run_id = body["id"].as_str().unwrap().parse::<RunId>().unwrap();
|
||||
let projection = state
|
||||
.stores
|
||||
.runs
|
||||
.open_run_reader(&run_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.state()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
projection.spec.settings.run.environment.provider,
|
||||
EnvironmentProvider::Local
|
||||
);
|
||||
assert!(projection.spec.settings.run.pull_request.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn post_runs_run_intent_accepts_automatic_pull_requests_for_configured_docker_dry_run() {
|
||||
let state = test_app_state();
|
||||
let app = crate::test_support::build_test_router(Arc::clone(&state));
|
||||
let workflow_version_id = store_workflow_version(
|
||||
&state,
|
||||
MINIMAL_DOT,
|
||||
Some("_version = 1\n[run.pull_request]\nenabled = true\n"),
|
||||
)
|
||||
.await;
|
||||
|
||||
let body = post_run_manifest(
|
||||
&app,
|
||||
json!({
|
||||
"workflow_version_id": workflow_version_id,
|
||||
"target": {
|
||||
"kind": "git",
|
||||
"repo": "fabro-sh/fabro",
|
||||
"branch": "main"
|
||||
},
|
||||
"args": { "dry_run": true }
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let run_id = body["id"].as_str().unwrap().parse::<RunId>().unwrap();
|
||||
let projection = state
|
||||
.stores
|
||||
.runs
|
||||
.open_run_reader(&run_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.state()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
projection.spec.settings.run.environment.provider,
|
||||
EnvironmentProvider::Docker
|
||||
);
|
||||
assert_eq!(projection.spec.settings.run.execution.mode, RunMode::DryRun);
|
||||
assert!(
|
||||
projection
|
||||
.spec
|
||||
.settings
|
||||
.run
|
||||
.pull_request
|
||||
.is_some_and(|pull_request| pull_request.enabled)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn post_runs_run_intent_observes_folder_git_metadata_without_a_remote_call() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue