mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-15 23:32:46 +00:00
Merge remote-tracking branch 'origin/main' into production-web-ui
This commit is contained in:
commit
552d46398e
6 changed files with 55 additions and 44 deletions
|
|
@ -139,16 +139,17 @@ fn generate_mtls_certs(dir: &Path) -> Result<()> {
|
|||
let server_key_path = dir.join("server.key");
|
||||
std::fs::write(&server_key_path, &server_key)?;
|
||||
|
||||
let csr = run_openssl_with_stdin(
|
||||
let csr = run_openssl(
|
||||
&[
|
||||
"req",
|
||||
"-new",
|
||||
"-key",
|
||||
"/dev/stdin",
|
||||
server_key_path
|
||||
.to_str()
|
||||
.context("server key path is not valid UTF-8")?,
|
||||
"-subj",
|
||||
"/CN=localhost",
|
||||
],
|
||||
&server_key,
|
||||
"generate server CSR",
|
||||
)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -91,17 +91,26 @@ pub(crate) async fn attach_run_with_client(
|
|||
attach_live_run_with_client(
|
||||
client,
|
||||
run_id,
|
||||
auto_approve,
|
||||
verbose,
|
||||
replay_events,
|
||||
stream,
|
||||
kill_on_detach,
|
||||
styles,
|
||||
json_output,
|
||||
AttachOptions {
|
||||
auto_approve,
|
||||
verbose,
|
||||
kill_on_detach,
|
||||
json_output,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
struct AttachOptions {
|
||||
auto_approve: bool,
|
||||
verbose: bool,
|
||||
kill_on_detach: bool,
|
||||
json_output: bool,
|
||||
}
|
||||
|
||||
fn replay_run_with_client(
|
||||
verbose: bool,
|
||||
events: Vec<EventEnvelope>,
|
||||
|
|
@ -124,31 +133,28 @@ fn replay_run_with_client(
|
|||
async fn attach_live_run_with_client(
|
||||
client: &server_client::ServerStoreClient,
|
||||
run_id: &RunId,
|
||||
auto_approve: bool,
|
||||
verbose: bool,
|
||||
existing_events: Vec<EventEnvelope>,
|
||||
mut stream: server_client::RunAttachEventStream,
|
||||
kill_on_detach: bool,
|
||||
styles: &'static Styles,
|
||||
json_output: bool,
|
||||
opts: AttachOptions,
|
||||
) -> Result<ExitCode> {
|
||||
let is_tty = std::io::stderr().is_terminal();
|
||||
let mut progress_ui = run_progress::ProgressUI::new(is_tty, verbose);
|
||||
let mut progress_ui = run_progress::ProgressUI::new(is_tty, opts.verbose);
|
||||
let ctrl_c_signal = ctrl_c();
|
||||
tokio::pin!(ctrl_c_signal);
|
||||
|
||||
for event in existing_events {
|
||||
let line = event_payload_line(&event)?;
|
||||
emit_progress_line(&mut progress_ui, &line, json_output)?;
|
||||
emit_progress_line(&mut progress_ui, &line, opts.json_output)?;
|
||||
}
|
||||
|
||||
if let Some(exit_code) = handle_pending_server_interview(
|
||||
client,
|
||||
run_id,
|
||||
auto_approve,
|
||||
opts.auto_approve,
|
||||
&mut progress_ui,
|
||||
styles,
|
||||
json_output,
|
||||
opts.json_output,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
|
|
@ -158,23 +164,23 @@ async fn attach_live_run_with_client(
|
|||
loop {
|
||||
let next_event = tokio::select! {
|
||||
_ = &mut ctrl_c_signal => {
|
||||
handle_detach_signal(client, run_id, kill_on_detach).await;
|
||||
finish_progress(&mut progress_ui, json_output);
|
||||
handle_detach_signal(client, run_id, opts.kill_on_detach).await;
|
||||
finish_progress(&mut progress_ui, opts.json_output);
|
||||
return Ok(ExitCode::from(1));
|
||||
}
|
||||
result = stream.next_event() => result?,
|
||||
};
|
||||
|
||||
let Some(event) = next_event else {
|
||||
finish_progress(&mut progress_ui, json_output);
|
||||
finish_progress(&mut progress_ui, opts.json_output);
|
||||
return Err(anyhow::anyhow!(ATTACH_PREMATURE_EOF_MESSAGE));
|
||||
};
|
||||
|
||||
let line = event_payload_line(&event)?;
|
||||
emit_progress_line(&mut progress_ui, &line, json_output)?;
|
||||
emit_progress_line(&mut progress_ui, &line, opts.json_output)?;
|
||||
|
||||
if let Some(exit_code) = event_exit_code(&event) {
|
||||
finish_progress(&mut progress_ui, json_output);
|
||||
finish_progress(&mut progress_ui, opts.json_output);
|
||||
return Ok(exit_code);
|
||||
}
|
||||
|
||||
|
|
@ -182,10 +188,10 @@ async fn attach_live_run_with_client(
|
|||
if let Some(exit_code) = handle_pending_server_interview(
|
||||
client,
|
||||
run_id,
|
||||
auto_approve,
|
||||
opts.auto_approve,
|
||||
&mut progress_ui,
|
||||
styles,
|
||||
json_output,
|
||||
opts.json_output,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
|
|
|
|||
|
|
@ -63,8 +63,11 @@ pub(crate) async fn execute(
|
|||
.run
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("Run {run_id} has no run record in store"))?;
|
||||
let artifact_uploader =
|
||||
build_artifact_uploader(run_id, client.clone_for_reuse(), artifact_upload_token);
|
||||
let artifact_uploader = Some(build_artifact_uploader(
|
||||
run_id,
|
||||
client.clone_for_reuse(),
|
||||
artifact_upload_token,
|
||||
));
|
||||
let interviewer = Arc::new(ControlInterviewer::new());
|
||||
let cancel_token = Arc::new(AtomicBool::new(false));
|
||||
tokio::spawn(read_worker_control_stream(
|
||||
|
|
@ -156,17 +159,15 @@ fn build_artifact_uploader(
|
|||
run_id: RunId,
|
||||
client: server_client::ServerStoreClient,
|
||||
artifact_upload_token: Option<String>,
|
||||
) -> Option<Arc<dyn StageArtifactUploader>> {
|
||||
let uploader: Arc<dyn StageArtifactUploader> = match artifact_upload_token {
|
||||
) -> Arc<dyn StageArtifactUploader> {
|
||||
match artifact_upload_token {
|
||||
Some(token) => Arc::new(HttpArtifactUploader {
|
||||
run_id,
|
||||
client,
|
||||
bearer_token: token,
|
||||
}),
|
||||
None => Arc::new(MissingArtifactUploadTokenUploader { run_id }),
|
||||
};
|
||||
|
||||
Some(uploader)
|
||||
}
|
||||
}
|
||||
|
||||
struct HttpArtifactUploader {
|
||||
|
|
|
|||
|
|
@ -498,16 +498,11 @@ impl SlackService {
|
|||
return;
|
||||
};
|
||||
|
||||
let pending = match load_pending_interview(state.as_ref(), run_id, &submission.qid).await {
|
||||
Ok(pending) => pending,
|
||||
Err(_) => return,
|
||||
};
|
||||
if submit_pending_interview_answer(state.as_ref(), &pending, submission.answer)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
let Ok(pending) = load_pending_interview(state.as_ref(), run_id, &submission.qid).await
|
||||
else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let _ = submit_pending_interview_answer(state.as_ref(), &pending, submission.answer).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -262,13 +262,22 @@ fn ensure_parent_dir(path: &Path) {
|
|||
}
|
||||
|
||||
fn with_session_lock<T>(root: &Path, f: impl FnOnce() -> T) -> T {
|
||||
std::fs::create_dir_all(root)
|
||||
.unwrap_or_else(|err| panic!("failed to create {}: {err}", root.display()));
|
||||
let lock_path = session_lock_path(root);
|
||||
ensure_parent_dir(&lock_path);
|
||||
let lock_file = File::create(&lock_path)
|
||||
.unwrap_or_else(|err| panic!("failed to create {}: {err}", lock_path.display()));
|
||||
// Retry create-dir + create-file as a unit: another process's
|
||||
// cleanup_session_root can remove_dir_all between the two calls.
|
||||
let deadline = std::time::Instant::now() + SESSION_LOCK_TIMEOUT;
|
||||
let lock_file = loop {
|
||||
std::fs::create_dir_all(root)
|
||||
.unwrap_or_else(|err| panic!("failed to create {}: {err}", root.display()));
|
||||
ensure_parent_dir(&lock_path);
|
||||
match File::create(&lock_path) {
|
||||
Ok(f) => break f,
|
||||
Err(_) if std::time::Instant::now() < deadline => {
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
Err(err) => panic!("failed to create {}: {err}", lock_path.display()),
|
||||
}
|
||||
};
|
||||
while !fabro_proc::try_flock_exclusive(&lock_file)
|
||||
.unwrap_or_else(|err| panic!("failed to lock {}: {err}", lock_path.display()))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ impl InterviewQuestionType {
|
|||
"yes_no" => Self::YesNo,
|
||||
"multiple_choice" => Self::MultipleChoice,
|
||||
"multi_select" => Self::MultiSelect,
|
||||
"freeform" => Self::Freeform,
|
||||
"confirmation" => Self::Confirmation,
|
||||
_ => Self::Freeform,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue