2019-10-11 23:51:17 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
*
|
|
|
|
* This software may be used and distributed according to the terms of the
|
2020-02-11 13:42:43 +03:00
|
|
|
* GNU General Public License version 2.
|
2019-10-11 23:51:17 +03:00
|
|
|
*/
|
2018-08-08 15:24:56 +03:00
|
|
|
|
|
|
|
#![deny(warnings)]
|
|
|
|
|
2020-04-16 12:12:29 +03:00
|
|
|
use anyhow::{Error, Result};
|
2018-08-08 15:24:56 +03:00
|
|
|
use blobrepo::BlobRepo;
|
2020-04-08 18:56:17 +03:00
|
|
|
use blobstore::Loadable;
|
2019-05-20 17:54:53 +03:00
|
|
|
use bookmarks::BookmarkName;
|
2019-04-08 12:49:27 +03:00
|
|
|
use cloned::cloned;
|
2018-11-30 21:11:37 +03:00
|
|
|
use context::CoreContext;
|
2020-04-16 12:12:29 +03:00
|
|
|
use futures::{
|
|
|
|
compat::{Future01CompatExt, Stream01CompatExt},
|
|
|
|
future::{self, FutureExt, TryFutureExt},
|
|
|
|
stream::{StreamExt, TryStreamExt},
|
|
|
|
};
|
2020-04-16 12:12:29 +03:00
|
|
|
use futures_ext::{BoxFuture, FutureExt as OldFutureExt};
|
|
|
|
use futures_old::Future as OldFuture;
|
2020-03-18 19:13:05 +03:00
|
|
|
use hooks::{hook_loader::load_hooks, HookManager, HookOutcome};
|
2020-04-11 14:25:18 +03:00
|
|
|
use hooks_content_stores::blobrepo_text_only_fetcher;
|
2019-03-15 00:16:15 +03:00
|
|
|
use mercurial_types::HgChangesetId;
|
2019-01-31 11:31:11 +03:00
|
|
|
use metaconfig_types::RepoConfig;
|
2018-09-26 14:46:41 +03:00
|
|
|
use mononoke_types::ChangesetId;
|
2018-08-08 15:24:56 +03:00
|
|
|
use revset::AncestorsNodeStream;
|
2019-11-15 18:04:51 +03:00
|
|
|
use scuba_ext::ScubaSampleBuilder;
|
2020-04-16 12:12:29 +03:00
|
|
|
use slog::debug;
|
2019-04-17 17:56:57 +03:00
|
|
|
use std::collections::HashSet;
|
2018-08-08 15:24:56 +03:00
|
|
|
use std::sync::Arc;
|
2019-11-15 09:02:57 +03:00
|
|
|
use thiserror::Error;
|
2020-04-16 12:12:29 +03:00
|
|
|
use tokio::task;
|
2018-08-08 15:24:56 +03:00
|
|
|
|
|
|
|
pub struct Tailer {
|
2018-11-30 21:11:37 +03:00
|
|
|
ctx: CoreContext,
|
2019-01-09 14:49:56 +03:00
|
|
|
repo: BlobRepo,
|
2018-08-08 15:24:56 +03:00
|
|
|
hook_manager: Arc<HookManager>,
|
2019-05-20 17:54:53 +03:00
|
|
|
bookmark: BookmarkName,
|
2019-04-17 17:56:57 +03:00
|
|
|
excludes: HashSet<ChangesetId>,
|
2018-08-08 15:24:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Tailer {
|
|
|
|
pub fn new(
|
2018-11-30 21:11:37 +03:00
|
|
|
ctx: CoreContext,
|
2019-01-09 14:49:56 +03:00
|
|
|
repo: BlobRepo,
|
2018-08-08 15:24:56 +03:00
|
|
|
config: RepoConfig,
|
2019-05-20 17:54:53 +03:00
|
|
|
bookmark: BookmarkName,
|
2019-04-17 17:56:57 +03:00
|
|
|
excludes: HashSet<ChangesetId>,
|
2019-07-11 16:38:11 +03:00
|
|
|
disabled_hooks: &HashSet<String>,
|
2018-08-08 15:24:56 +03:00
|
|
|
) -> Result<Tailer> {
|
2020-04-08 18:56:17 +03:00
|
|
|
let content_fetcher = blobrepo_text_only_fetcher(repo.clone(), config.hook_max_file_size);
|
2018-08-08 15:24:56 +03:00
|
|
|
|
|
|
|
let mut hook_manager = HookManager::new(
|
2020-01-27 15:28:33 +03:00
|
|
|
ctx.fb,
|
2020-04-08 18:56:17 +03:00
|
|
|
content_fetcher,
|
2018-11-22 14:28:28 +03:00
|
|
|
Default::default(),
|
2019-11-15 18:04:51 +03:00
|
|
|
ScubaSampleBuilder::with_discard(),
|
2018-08-08 15:24:56 +03:00
|
|
|
);
|
|
|
|
|
2019-09-14 06:16:08 +03:00
|
|
|
load_hooks(ctx.fb, &mut hook_manager, config, disabled_hooks)?;
|
2018-08-08 15:24:56 +03:00
|
|
|
|
|
|
|
Ok(Tailer {
|
2018-11-30 21:11:37 +03:00
|
|
|
ctx,
|
2018-08-08 15:24:56 +03:00
|
|
|
repo,
|
|
|
|
hook_manager: Arc::new(hook_manager),
|
|
|
|
bookmark,
|
2019-04-17 17:56:57 +03:00
|
|
|
excludes,
|
2018-08-08 15:24:56 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-04-09 18:36:33 +03:00
|
|
|
pub fn run_single_changeset(
|
|
|
|
&self,
|
2020-04-16 12:12:29 +03:00
|
|
|
changeset: ChangesetId,
|
2020-03-18 19:13:05 +03:00
|
|
|
) -> BoxFuture<Vec<HookOutcome>, Error> {
|
2019-10-16 21:02:19 +03:00
|
|
|
cloned!(self.ctx, self.repo, self.hook_manager, self.bookmark,);
|
2020-04-16 12:12:29 +03:00
|
|
|
run_hooks_for_changeset(ctx, repo, hook_manager, bookmark, changeset)
|
2020-03-18 19:13:05 +03:00
|
|
|
.map(|(_, result)| result)
|
2019-04-09 18:36:33 +03:00
|
|
|
.boxify()
|
|
|
|
}
|
|
|
|
|
2020-04-16 12:12:29 +03:00
|
|
|
pub async fn run_with_limit(&self, limit: usize) -> Result<Vec<HookOutcome>, Error> {
|
|
|
|
let bm_rev = self
|
|
|
|
.repo
|
|
|
|
.get_bonsai_bookmark(self.ctx.clone(), &self.bookmark)
|
|
|
|
.compat()
|
|
|
|
.await?
|
|
|
|
.ok_or_else(|| ErrorKind::NoSuchBookmark(self.bookmark.clone()))?;
|
|
|
|
|
|
|
|
let res =
|
|
|
|
AncestorsNodeStream::new(self.ctx.clone(), &self.repo.get_changeset_fetcher(), bm_rev)
|
|
|
|
.compat()
|
|
|
|
.take(limit)
|
|
|
|
.try_filter(|cs_id| future::ready(!self.excludes.contains(cs_id)))
|
|
|
|
.map(|cs_id| async move {
|
|
|
|
match cs_id {
|
|
|
|
Ok(cs_id) => {
|
|
|
|
let (_, outcomes) = task::spawn(
|
|
|
|
run_hooks_for_changeset(
|
|
|
|
self.ctx.clone(),
|
|
|
|
self.repo.clone(),
|
|
|
|
self.hook_manager.clone(),
|
|
|
|
self.bookmark.clone(),
|
|
|
|
cs_id,
|
|
|
|
)
|
|
|
|
.compat(),
|
|
|
|
)
|
|
|
|
.await??;
|
|
|
|
|
|
|
|
Ok(outcomes)
|
2018-10-22 15:20:40 +03:00
|
|
|
}
|
2020-04-16 12:12:29 +03:00
|
|
|
Err(e) => Err(e),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.buffered(100)
|
|
|
|
.try_concat()
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(res)
|
2018-10-22 15:20:40 +03:00
|
|
|
}
|
2018-08-08 15:24:56 +03:00
|
|
|
}
|
|
|
|
|
2018-10-17 18:03:39 +03:00
|
|
|
fn run_hooks_for_changeset(
|
2018-11-30 21:11:37 +03:00
|
|
|
ctx: CoreContext,
|
2019-01-09 14:49:56 +03:00
|
|
|
repo: BlobRepo,
|
2018-10-17 18:03:39 +03:00
|
|
|
hm: Arc<HookManager>,
|
2019-05-20 17:54:53 +03:00
|
|
|
bm: BookmarkName,
|
2020-04-08 18:56:17 +03:00
|
|
|
cs_id: ChangesetId,
|
2020-04-16 12:12:29 +03:00
|
|
|
) -> impl OldFuture<Item = (ChangesetId, Vec<HookOutcome>), Error = Error> {
|
2020-04-08 18:56:17 +03:00
|
|
|
cs_id
|
|
|
|
.load(ctx.clone(), repo.blobstore())
|
|
|
|
.from_err()
|
|
|
|
.and_then(move |cs| {
|
2020-03-18 19:13:05 +03:00
|
|
|
async move {
|
2020-04-08 18:56:17 +03:00
|
|
|
debug!(ctx.logger(), "Running hooks for changeset {:?}", cs);
|
2020-04-11 14:25:18 +03:00
|
|
|
let hook_results = hm
|
|
|
|
.run_hooks_for_bookmark(&ctx, vec![cs].iter(), &bm, None)
|
2020-04-08 18:56:17 +03:00
|
|
|
.await?;
|
|
|
|
Ok((cs_id, hook_results))
|
2020-03-18 19:13:05 +03:00
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
.compat()
|
2018-10-17 18:03:39 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-11-15 09:02:57 +03:00
|
|
|
#[derive(Debug, Error)]
|
2018-08-08 15:24:56 +03:00
|
|
|
pub enum ErrorKind {
|
2019-11-15 09:02:57 +03:00
|
|
|
#[error("No such bookmark '{0}'")]
|
2019-05-20 17:54:53 +03:00
|
|
|
NoSuchBookmark(BookmarkName),
|
2019-11-15 09:02:57 +03:00
|
|
|
#[error("Cannot find last revision in blobstore")]
|
2019-01-14 20:29:33 +03:00
|
|
|
NoLastRevision,
|
2019-11-15 09:02:57 +03:00
|
|
|
#[error("Cannot find bonsai for {0}")]
|
2019-01-14 20:29:33 +03:00
|
|
|
BonsaiNotFound(HgChangesetId),
|
2018-08-08 15:24:56 +03:00
|
|
|
}
|