github as builtin
This commit is contained in:
parent
134bfab8cd
commit
9b5c7a42b2
7 changed files with 101 additions and 55 deletions
11
.travis.yml
11
.travis.yml
|
@ -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
|
||||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -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
34
ci/build-release.sh
Normal 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
|
|
@ -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");
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
|
@ -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())
|
||||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue