github as builtin

This commit is contained in:
Michael Zhang 2018-09-01 00:04:04 -05:00
parent 134bfab8cd
commit 9b5c7a42b2
No known key found for this signature in database
GPG key ID: A1B65B603268116B
7 changed files with 101 additions and 55 deletions

View file

@ -1,28 +1,27 @@
language: rust language: rust
sudo: false sudo: required
rust: rust:
- stable - stable
- beta
- nightly - nightly
os: linux os: linux
matrix: matrix:
fast_finish: true fast_finish: true
services:
- docker
cache: cargo cache: cargo
script: script:
- cargo test --all - cargo test --all
before_deploy: before_deploy:
- cargo build --release --all - ./ci/build-release.sh dip ${TRAVIS_TAG}-${TRAVIS_OS_NAME}
- cargo build --release --examples
deploy: deploy:
- provider: releases - provider: releases
api_key: $AUTH_TOKEN api_key: $AUTH_TOKEN
file: file:
- target/release/dip - dip-*
- target/release/examples/github
on: on:
condition: $TRAVIS_RUST_VERSION = stable condition: $TRAVIS_RUST_VERSION = stable
tags: true tags: true

View file

@ -4,26 +4,22 @@ description = "Configurable webhook server."
version = "0.1.0" version = "0.1.0"
authors = ["Michael Zhang <failed.down@gmail.com>"] authors = ["Michael Zhang <failed.down@gmail.com>"]
[[example]]
name = "github"
[dev_dependencies]
generic-array = "0.9"
hmac = "0.6"
secstr = "0.3"
sha-1 = "0.7"
[dependencies] [dependencies]
failure = "0.1" failure = "0.1"
futures = "0.1" futures = "0.1"
generic-array = "0.9"
hmac = "0.6"
hyper = "0.12" hyper = "0.12"
mktemp = "0.3" mktemp = "0.3"
lazy_static = "1.1" lazy_static = "1.1"
notify = "4.0" notify = "4.0"
owning_ref = "0.3" owning_ref = "0.3"
regex = "1.0" regex = "1.0"
secstr = "0.3"
serde = "1.0" serde = "1.0"
serde_derive = "1.0" serde_derive = "1.0"
serde_json = "1.0" serde_json = "1.0"
sha-1 = "0.7"
structopt = "0.2" structopt = "0.2"
tokio = "0.1" tokio = "0.1"
tokio-process = "0.2" tokio-process = "0.2"

34
ci/build-release.sh Normal file
View file

@ -0,0 +1,34 @@
#!/bin/bash
#
# Usage: ./build-release <PROJECT> ${TRAVIS_TAG}-${TRAVIS_OS_NAME}
#
# The latest version of this script is available at
# https://github.com/emk/rust-musl-builder/blob/master/examples/build-release
#
# Called by `.travis.yml` to build release binaries. We use
# ekidd/rust-musl-builder to make the Linux binaries so that we can run
# them unchanged on any distro, including tiny distros like Alpine (which
# is heavily used for Docker containers). Other platforms get regular
# binaries, which will generally be dynamically linked against libc.
#
# If you have a platform which supports static linking of libc, and this
# would be generally useful, please feel free to submit patches.
set -euo pipefail
case `uname -s` in
Linux)
echo "Building static binaries using ekidd/rust-musl-builder"
docker build -t build-"$1"-image .
docker run -it --name build-"$1" build-"$1"-image
docker cp build-"$1":/home/rust/src/target/x86_64-unknown-linux-musl/release/"$1" "$1"
docker rm build-"$1"
docker rmi build-"$1"-image
zip "$1"-"$2".zip "$1"
;;
*)
echo "Building standard release binaries"
cargo build --release
zip -j "$1"-"$2".zip target/release/"$1"
;;
esac

View file

@ -51,6 +51,7 @@ where
let mut programs = PROGRAMS.lock().unwrap(); let mut programs = PROGRAMS.lock().unwrap();
// TODO: some kind of smart diff // TODO: some kind of smart diff
programs.clear(); programs.clear();
let programs_dir = { let programs_dir = {
let mut p = root.as_ref().to_path_buf(); let mut p = root.as_ref().to_path_buf();
p.push("handlers"); p.push("handlers");

View file

@ -1,28 +1,18 @@
extern crate dip;
extern crate hmac;
extern crate secstr;
extern crate serde_json;
extern crate sha1;
#[macro_use]
extern crate serde_derive;
extern crate failure;
extern crate generic_array;
#[macro_use]
extern crate structopt;
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use std::io::{self, Read};
use std::iter::FromIterator; use std::iter::FromIterator;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use failure::err_msg; use failure::{err_msg, Error};
use generic_array::GenericArray; use generic_array::GenericArray;
use hmac::{Hmac, Mac}; use hmac::{Hmac, Mac};
use secstr::*; use secstr::*;
use serde::Serialize;
use serde_json::{self, Serializer as JsonSerializer, Value as JsonValue};
use sha1::Sha1; use sha1::Sha1;
use structopt::StructOpt; use structopt::StructOpt;
use toml::Value as TomlValue;
#[derive(StructOpt)] #[derive(StructOpt)]
struct Opt { struct Opt {
@ -60,16 +50,19 @@ fn default_path() -> PathBuf {
PathBuf::from(".") PathBuf::from(".")
} }
fn main() { pub fn main(config: &TomlValue, input: &JsonValue) -> Result<JsonValue, Error> {
let args = Opt::from_args(); let config_str = {
let config: Config = serde_json::from_str(&args.config).expect("Could not parse config."); let mut buf: Vec<u8> = Vec::new();
{
let mut serializer = JsonSerializer::new(&mut buf);
TomlValue::serialize(&config, &mut serializer).unwrap();
}
String::from_utf8(buf).unwrap()
};
let config: Config = serde_json::from_str(&config_str)?;
let mut payload = String::new(); let payload_str = format!("{}", input);
io::stdin() let payload: Payload = serde_json::from_str(&payload_str)?;
.read_to_string(&mut payload)
.expect("Could not read from stdin");
let payload: Payload = serde_json::from_str(&payload)
.expect(&format!("Could not parse stdin into json: '{}'", payload));
if !config.disable_hmac_verify { if !config.disable_hmac_verify {
let secret = GenericArray::from_iter(config.secret.bytes()); let secret = GenericArray::from_iter(config.secret.bytes());
@ -113,4 +106,5 @@ fn main() {
.arg(&target_path) .arg(&target_path)
.output() .output()
.expect("Could not spawn process to clone"); .expect("Could not spawn process to clone");
Ok(json!(1))
} }

View file

@ -1,3 +1,4 @@
use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
@ -12,7 +13,7 @@ use tokio::io::write_all;
use tokio_process::CommandExt; use tokio_process::CommandExt;
use toml::Value as TomlValue; use toml::Value as TomlValue;
use PROGRAMS; use github;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Handler { pub struct Handler {
@ -20,10 +21,20 @@ pub struct Handler {
pub action: Action, pub action: Action,
} }
#[derive(Clone, Debug)] #[derive(Clone)]
pub enum Action { pub enum Action {
Builtin(fn(&TomlValue, &JsonValue) -> Result<JsonValue, Error>),
Command(String), Command(String),
Exec(PathBuf), Program(String),
}
impl fmt::Debug for Action {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
Action::Builtin(_) => write!(f, "Builtin"),
_ => write!(f, "{:?}", self),
}
}
} }
impl Handler { impl Handler {
@ -45,17 +56,18 @@ impl Handler {
.ok_or(err_msg("'command' is not a string."))?; .ok_or(err_msg("'command' is not a string."))?;
Action::Command(command.to_owned()) Action::Command(command.to_owned())
} }
"github" => Action::Builtin(github::main),
handler => { handler => {
let programs = PROGRAMS.lock().unwrap(); // let programs = HANDLERS.lock().unwrap();
let program = programs // let program = programs
.get(handler) // .get(handler)
.ok_or(err_msg(format!("'{}' is not a valid executable", handler))) // .ok_or(err_msg(format!("'{}' is not a valid executable", handler)))
.and_then(|value| { // .and_then(|value| {
value // value
.canonicalize() // .canonicalize()
.map_err(|_| err_msg("failed to canonicalize the path")) // .map_err(|_| err_msg("failed to canonicalize the path"))
}).map(|value| value.clone())?; // }).map(|value| value.clone())?;
Action::Exec(program) Action::Program(handler.to_owned())
} }
}; };
let config = config.clone(); let config = config.clone();
@ -69,7 +81,7 @@ impl Handler {
input: JsonValue, input: JsonValue,
) -> impl Future<Item = (PathBuf, JsonValue), Error = Error> { ) -> impl Future<Item = (PathBuf, JsonValue), Error = Error> {
let temp_path_cp = temp_path.clone(); let temp_path_cp = temp_path.clone();
let config = { let config_str = {
let mut buf: Vec<u8> = Vec::new(); let mut buf: Vec<u8> = Vec::new();
{ {
let mut serializer = JsonSerializer::new(&mut buf); let mut serializer = JsonSerializer::new(&mut buf);
@ -85,6 +97,10 @@ impl Handler {
}; };
let output: Box<Future<Item = JsonValue, Error = Error> + Send> = match action { let output: Box<Future<Item = JsonValue, Error = Error> + Send> = match action {
Action::Builtin(ref func) => {
let result = func(&config, &input);
Box::new(future::result(result))
}
Action::Command(ref cmd) => { Action::Command(ref cmd) => {
// TODO: allow some kind of simple variable replacement // TODO: allow some kind of simple variable replacement
let mut command = Command::new("/bin/bash"); let mut command = Command::new("/bin/bash");
@ -111,13 +127,13 @@ impl Handler {
}); });
Box::new(result) Box::new(result)
} }
Action::Exec(ref path) => { Action::Program(ref path) => {
let mut command = Command::new(&path); let mut command = Command::new(&path);
command_helper(&mut command); command_helper(&mut command);
let mut child = command let mut child = command
.env("DIP_ROOT", "") .env("DIP_ROOT", "")
.arg("--config") .arg("--config")
.arg(config) .arg(config_str)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::piped()) .stderr(Stdio::piped())

View file

@ -1,7 +1,13 @@
//! # Dip //! # Dip
extern crate hmac;
extern crate secstr;
extern crate sha1;
#[macro_use]
extern crate serde_derive;
extern crate failure; extern crate failure;
extern crate futures; extern crate futures;
extern crate generic_array;
extern crate hyper; extern crate hyper;
extern crate mktemp; extern crate mktemp;
extern crate owning_ref; extern crate owning_ref;
@ -20,6 +26,7 @@ extern crate toml;
extern crate walkdir; extern crate walkdir;
pub mod config; pub mod config;
pub mod github;
pub mod handler; pub mod handler;
pub mod hook; pub mod hook;
pub mod service; pub mod service;
@ -48,9 +55,8 @@ const URIPATTERN_STR: &str = r"/webhook/(?P<name>[A-Za-z._][A-Za-z0-9._]*)";
lazy_static! { lazy_static! {
static ref URIPATTERN: Regex = Regex::new(URIPATTERN_STR).unwrap(); static ref URIPATTERN: Regex = Regex::new(URIPATTERN_STR).unwrap();
static ref HANDLERS: Arc<Mutex<HashMap<String, Handler>>> = static ref PROGRAMS: Arc<Mutex<HashMap<String, PathBuf>>> =
Arc::new(Mutex::new(HashMap::new())); Arc::new(Mutex::new(HashMap::new()));
static ref PROGRAMS: Mutex<HashMap<String, PathBuf>> = Mutex::new(HashMap::new());
static ref HOOKS: Arc<Mutex<HashMap<String, Hook>>> = Arc::new(Mutex::new(HashMap::new())); static ref HOOKS: Arc<Mutex<HashMap<String, Hook>>> = Arc::new(Mutex::new(HashMap::new()));
} }