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
sudo: false
sudo: required
rust:
- stable
- beta
- nightly
os: linux
matrix:
fast_finish: true
services:
- docker
cache: cargo
script:
- cargo test --all
before_deploy:
- cargo build --release --all
- cargo build --release --examples
- ./ci/build-release.sh dip ${TRAVIS_TAG}-${TRAVIS_OS_NAME}
deploy:
- provider: releases
api_key: $AUTH_TOKEN
file:
- target/release/dip
- target/release/examples/github
- dip-*
on:
condition: $TRAVIS_RUST_VERSION = stable
tags: true

View file

@ -4,26 +4,22 @@ description = "Configurable webhook server."
version = "0.1.0"
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]
failure = "0.1"
futures = "0.1"
generic-array = "0.9"
hmac = "0.6"
hyper = "0.12"
mktemp = "0.3"
lazy_static = "1.1"
notify = "4.0"
owning_ref = "0.3"
regex = "1.0"
secstr = "0.3"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
sha-1 = "0.7"
structopt = "0.2"
tokio = "0.1"
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();
// TODO: some kind of smart diff
programs.clear();
let programs_dir = {
let mut p = root.as_ref().to_path_buf();
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::env;
use std::io::{self, Read};
use std::iter::FromIterator;
use std::path::PathBuf;
use std::process::Command;
use failure::err_msg;
use failure::{err_msg, Error};
use generic_array::GenericArray;
use hmac::{Hmac, Mac};
use secstr::*;
use serde::Serialize;
use serde_json::{self, Serializer as JsonSerializer, Value as JsonValue};
use sha1::Sha1;
use structopt::StructOpt;
use toml::Value as TomlValue;
#[derive(StructOpt)]
struct Opt {
@ -60,16 +50,19 @@ fn default_path() -> PathBuf {
PathBuf::from(".")
}
fn main() {
let args = Opt::from_args();
let config: Config = serde_json::from_str(&args.config).expect("Could not parse config.");
pub fn main(config: &TomlValue, input: &JsonValue) -> Result<JsonValue, Error> {
let config_str = {
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();
io::stdin()
.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));
let payload_str = format!("{}", input);
let payload: Payload = serde_json::from_str(&payload_str)?;
if !config.disable_hmac_verify {
let secret = GenericArray::from_iter(config.secret.bytes());
@ -113,4 +106,5 @@ fn main() {
.arg(&target_path)
.output()
.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::process::{Command, Stdio};
@ -12,7 +13,7 @@ use tokio::io::write_all;
use tokio_process::CommandExt;
use toml::Value as TomlValue;
use PROGRAMS;
use github;
#[derive(Clone, Debug)]
pub struct Handler {
@ -20,10 +21,20 @@ pub struct Handler {
pub action: Action,
}
#[derive(Clone, Debug)]
#[derive(Clone)]
pub enum Action {
Builtin(fn(&TomlValue, &JsonValue) -> Result<JsonValue, Error>),
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 {
@ -45,17 +56,18 @@ impl Handler {
.ok_or(err_msg("'command' is not a string."))?;
Action::Command(command.to_owned())
}
"github" => Action::Builtin(github::main),
handler => {
let programs = PROGRAMS.lock().unwrap();
let program = programs
.get(handler)
.ok_or(err_msg(format!("'{}' is not a valid executable", handler)))
.and_then(|value| {
value
.canonicalize()
.map_err(|_| err_msg("failed to canonicalize the path"))
}).map(|value| value.clone())?;
Action::Exec(program)
// let programs = HANDLERS.lock().unwrap();
// let program = programs
// .get(handler)
// .ok_or(err_msg(format!("'{}' is not a valid executable", handler)))
// .and_then(|value| {
// value
// .canonicalize()
// .map_err(|_| err_msg("failed to canonicalize the path"))
// }).map(|value| value.clone())?;
Action::Program(handler.to_owned())
}
};
let config = config.clone();
@ -69,7 +81,7 @@ impl Handler {
input: JsonValue,
) -> impl Future<Item = (PathBuf, JsonValue), Error = Error> {
let temp_path_cp = temp_path.clone();
let config = {
let config_str = {
let mut buf: Vec<u8> = Vec::new();
{
let mut serializer = JsonSerializer::new(&mut buf);
@ -85,6 +97,10 @@ impl Handler {
};
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) => {
// TODO: allow some kind of simple variable replacement
let mut command = Command::new("/bin/bash");
@ -111,13 +127,13 @@ impl Handler {
});
Box::new(result)
}
Action::Exec(ref path) => {
Action::Program(ref path) => {
let mut command = Command::new(&path);
command_helper(&mut command);
let mut child = command
.env("DIP_ROOT", "")
.arg("--config")
.arg(config)
.arg(config_str)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())

View file

@ -1,7 +1,13 @@
//! # Dip
extern crate hmac;
extern crate secstr;
extern crate sha1;
#[macro_use]
extern crate serde_derive;
extern crate failure;
extern crate futures;
extern crate generic_array;
extern crate hyper;
extern crate mktemp;
extern crate owning_ref;
@ -20,6 +26,7 @@ extern crate toml;
extern crate walkdir;
pub mod config;
pub mod github;
pub mod handler;
pub mod hook;
pub mod service;
@ -48,9 +55,8 @@ const URIPATTERN_STR: &str = r"/webhook/(?P<name>[A-Za-z._][A-Za-z0-9._]*)";
lazy_static! {
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()));
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()));
}