{"action":"created","comment":{"url":"https://api.github.com/repos/hail-is/hail/pulls/comments/213769249","pull_request_review_id":150668172,"id":213769249,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxMzc2OTI0OQ==","diff_hunk":"@@ -0,0 +1,559 @@\n+#include \"hail/NativeModule.h\"\n+#include \"hail/NativeObj.h\"\n+#include \"hail/NativePtr.h\"\n+#include <cassert>\n+#include <cstdio>\n+#include <cstdlib>\n+#include <cstring>\n+#include <jni.h>\n+#include <dlfcn.h>\n+#include <fcntl.h>\n+#include <sys/file.h>\n+#include <sys/stat.h>\n+#include <sys/time.h>\n+#include <unistd.h>\n+#include <atomic>\n+#include <condition_variable>\n+#include <map>\n+#include <memory>\n+#include <mutex>\n+#include <iostream>\n+#include <queue>\n+#include <sstream>\n+#include <string>\n+#include <unordered_map>\n+#include <vector>\n+\n+namespace hail {\n+\n+namespace {\n+\n+// Top-level NativeModule methods lock this mutex.  Constructors, and helper methods\n+// with names ending in \"_locked\", must be called only while holding the mutex.\n+//\n+// That makes everything single-threaded.\n+\n+std::mutex big_mutex;\n+\n+// A simple way to get a hash of two strings, take 80bits,\n+// and produce a 20byte string of hex digits.  We also sprinkle\n+// in some \"salt\" from a checksum of a tar of all header files, so\n+// that any change to header files will force recompilation.\n+//\n+// The shorter string (corresponding to options), may have only a\n+// few distinct values, so we need to mix it up with the longer\n+// string in various ways.\n+\n+std::string even_bytes(const std::string& a) {\n+  std::stringstream ss;\n+  size_t len = a.length();\n+  for (size_t j = 0; j < len; j += 2) {\n+    ss << a[j];\n+  }\n+  return ss.str();\n+}\n+\n+std::string hash_two_strings(const std::string& a, const std::string& b) {\n+  bool a_shorter = (a.length() < b.length());\n+  const std::string* shorter = (a_shorter ? &a : &b);\n+  const std::string* longer  = (a_shorter ? &b : &a);\n+  uint64_t hashA = std::hash<std::string>()(*longer);\n+  uint64_t hashB = std::hash<std::string>()(*shorter + even_bytes(*longer));\n+  if (sizeof(size_t) < 8) {\n+    // On a 32bit machine we need to work harder to get 80 bits\n+    uint64_t hashC = std::hash<std::string>()(*longer + \"SmallChangeForThirdHash\");\n+    hashA += (hashC << 32);\n+  }\n+  if (a_shorter) hashA ^= 0xff; // order of strings should change result\n+  hashA ^= ALL_HEADER_CKSUM; // checksum from all header files\n+  hashA ^= (0x3ac5*hashB); // mix low bits of hashB into hashA\n+  hashB &= 0xffff;\n+  char buf[128];\n+  char* out = buf;\n+  for (int pos = 80; (pos -= 4) >= 0;) {\n+    int64_t nibble = ((pos >= 64) ? (hashB >> (pos-64)) : (hashA >> pos)) & 0xf;\n+    *out++ = ((nibble < 10) ? nibble+'0' : nibble-10+'a');\n+  }\n+  *out = 0;\n+  return std::string(buf);\n+}\n+\n+bool file_exists(const std::string& name) {\n+  struct stat st;\n+  int rc = ::stat(name.c_str(), &st);\n+  return (rc == 0);\n+}\n+\n+std::string read_file_as_string(const std::string& name) {\n+  FILE* f = fopen(name.c_str(), \"r\");\n+  if (!f) return std::string(\"\");\n+  std::stringstream ss;\n+  for (;;) {\n+    int c = fgetc(f);\n+    if (c == EOF) break;\n+    ss << (char)c;\n+  }\n+  fclose(f);\n+  return ss.str();\n+}\n+\n+std::string get_module_dir() {\n+  // This gives us a distinct temp directory for each process, so that\n+  // we can manage synchronization of threads accessing files in\n+  // the temp directory using only the in-memory big_mutex, rather than\n+  // dealing with the complexity of file locking.\n+  char buf[512];\n+  strcpy(buf, \"/tmp/hail_XXXXXX\");\n+  return ::mkdtemp(buf);\n+}\n+\n+class ModuleConfig {\n+ public:\n+  bool is_darwin_;\n+  std::string java_md_;\n+  std::string ext_cpp_;\n+  std::string ext_lib_;\n+  std::string ext_mak_;\n+  std::string module_dir_;\n+\n+ public:\n+  ModuleConfig() :\n+#if defined(__APPLE__) && defined(__MACH__)\n+    is_darwin_(true),\n+#else\n+    is_darwin_(false),\n+#endif\n+    java_md_(is_darwin_ ? \"darwin\" : \"linux\"),\n+    ext_cpp_(\".cpp\"),\n+    ext_lib_(is_darwin_ ? \".dylib\" : \".so\"),\n+    ext_mak_(\".mak\"),\n+    module_dir_(get_module_dir()) {\n+  }\n+  \n+  std::string get_lib_name(const std::string& key) {\n+    std:: stringstream ss;\n+    ss << module_dir_ << \"/hm_\" << key << ext_lib_;\n+    return ss.str();\n+  }\n+  \n+  void ensure_module_dir_exists() {\n+    int rc = ::access(module_dir_.c_str(), R_OK);\n+    if (rc < 0) { // create it\n+      rc = ::mkdir(module_dir_.c_str(), 0666);\n+      if (rc < 0) perror(module_dir_.c_str());\n+      rc = ::chmod(module_dir_.c_str(), 0755);\n+    }\n+  }\n+};\n+\n+ModuleConfig config;\n+\n+// module_table contains a single NativeModulePtr for each\n+// key that we have ever seen.  We never delete a NativeModule\n+// or unload its dynamically-loaded code.\n+\n+std::unordered_map<std::string, std::weak_ptr<NativeModule>> module_table;\n+\n+NativeModulePtr module_find_or_make(\n+  const char* options,\n+  const char* source,\n+  const char* include\n+) {\n+  std::lock_guard<std::mutex> mylock(big_mutex);\n+  auto key = hash_two_strings(options, source);\n+  auto mod = module_table[key].lock();\n+  if (!mod) { // make it while holding the lock\n+    mod = std::make_shared<NativeModule>(options, source, include);\n+    module_table[key] = mod; // save a weak_ptr\n+  }\n+  return mod;\n+}\n+\n+NativeModulePtr module_find_or_make(\n+  bool is_global,\n+  const char* key,\n+  ssize_t binary_size,\n+  const void* binary\n+) {\n+  std::lock_guard<std::mutex> mylock(big_mutex);\n+  auto mod = module_table[key].lock();\n+  if (!mod) { // make it while holding the lock\n+    mod = std::make_shared<NativeModule>(is_global, key, binary_size, binary);\n+    module_table[key] = mod; // save a weak_ptr\n+  }\n+  return mod;\n+}\n+\n+} // end anon\n+\n+// ModuleBuilder deals with compiling/linking source code to a DLL,\n+// and providing the binary DLL as an Array[Byte] which can be broadcast\n+// to all workers.\n+\n+class ModuleBuilder {\n+ private:\n+  std::string options_;\n+  std::string source_;\n+  std::string include_;\n+  std::string key_;\n+  std::string hm_base_;\n+  std::string hm_mak_;\n+  std::string hm_cpp_;\n+  std::string hm_lib_;\n+  \n+public:\n+  ModuleBuilder(\n+    const std::string& options,\n+    const std::string& source,\n+    const std::string& include,\n+    const std::string& key\n+  ) :\n+    options_(options),\n+    source_(source),\n+    include_(include),\n+    key_(key) {\n+    // To start with, put dynamic code in $HOME/hail_modules\n+    auto base = (config.module_dir_ + \"/hm_\") + key_;\n+    hm_base_ = base;\n+    hm_mak_ = (base + config.ext_mak_);\n+    hm_cpp_ = (base + config.ext_cpp_);\n+    hm_lib_ = (base + config.ext_lib_);\n+  }\n+  \n+  virtual ~ModuleBuilder() { }\n+  \n+private:\n+  void write_cpp() {\n+    FILE* f = fopen(hm_cpp_.c_str(), \"w\");\n+    if (!f) { perror(\"fopen\"); return; }\n+    fwrite(source_.data(), 1, source_.length(), f);\n+    fclose(f);\n+  }\n+  \n+  void write_mak() {\n+    FILE* f = fopen(hm_mak_.c_str(), \"w\");\n+    if (!f) { perror(\"fopen\"); return; }\n+    fprintf(f, \".PHONY: FORCE\\n\");\n+    fprintf(f, \"\\n\");\n+    fprintf(f, \"MODULE    := hm_%s\\n\", key_.c_str());\n+    fprintf(f, \"MODULE_SO := $(MODULE)%s\\n\", config.ext_lib_.c_str());\n+    fprintf(f, \"ifndef JAVA_HOME\\n\");\n+    fprintf(f, \"  TMP :=$(shell java -XshowSettings:properties -version 2>&1 | fgrep -i java.home)\\n\");\n+    fprintf(f, \"  JAVA_HOME :=$(shell dirname $(filter-out java.home =,$(TMP)))\\n\");\n+    fprintf(f, \"endif\\n\");\n+    fprintf(f, \"JAVA_INCLUDE :=$(JAVA_HOME)/include\\n\");\n+    fprintf(f, \"CXXFLAGS  := \\\\\\n\");\n+    fprintf(f, \"  -std=c++11 -fPIC -march=native -fno-strict-aliasing -Wall \\\\\\n\");\n+    fprintf(f, \"  -I$(JAVA_INCLUDE) \\\\\\n\");\n+    fprintf(f, \"  -I$(JAVA_INCLUDE)/%s \\\\\\n\", config.java_md_.c_str());\n+    fprintf(f, \"  -I%s \\\\\\n\", include_.c_str());\n+    bool have_oflag = (strstr(options_.c_str(), \"-O\") != nullptr);\n+    fprintf(f, \"  %s%s \\\\\\n\", have_oflag ? \"\" : \"-O3\", options_.c_str());\n+    fprintf(f, \"  -DHAIL_MODULE=$(MODULE)\\n\");\n+    fprintf(f, \"LIBFLAGS := -fvisibility=default %s\\n\", \n+      config.is_darwin_ ? \"-dynamiclib -Wl,-undefined,dynamic_lookup\"\n+                         : \"-rdynamic -shared\");\n+    fprintf(f, \"\\n\");\n+    // build .so from .cpp\n+    fprintf(f, \"$(MODULE_SO): FORCE\\n\");\n+    fprintf(f, \"\\t$(CXX) $(CXXFLAGS) -o $(MODULE).o -c $(MODULE).cpp 2> $(MODULE).err\\n\");\n+    fprintf(f, \"\\t$(CXX) $(CXXFLAGS) $(LIBFLAGS) -o $@ $(MODULE).o 2>> $(MODULE).err\\n\");\n+    fprintf(f, \"\\n\");\n+    fclose(f);\n+  }\n+\n+public:\n+  bool try_to_build() {\n+    // Try to create the .new file, we hold the global lock so there is no race\n+    int fd = ::open(hm_lib_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666);\n+    if (fd < 0) {\n+      perror(\"open\");\n+      assert(false);\n+    }\n+    ::close(fd);\n+    write_mak();\n+    write_cpp();\n+    std::stringstream ss;\n+    ss << \"make -B -C \" << config.module_dir_ << \" -f \" << hm_mak_ << \" 1>/dev/null\";\n+    // run the command synchronously, while holding the big_mutex\n+    int rc = system(ss.str().c_str());\n+    if (rc != 0) {\n+      std::string base(config.module_dir_ + \"/hm_\" + key_);\n+      fprintf(stderr, \"makefile:\\n%s\", read_file_as_string(base+\".mak\").c_str());\n+      fprintf(stderr, \"errors:\\n%s\",   read_file_as_string(base+\".err\").c_str());\n+    }\n+    return (rc == 0);\n+  }\n+};\n+\n+NativeModule::NativeModule(\n+  const char* options,\n+  const char* source,\n+  const char* include\n+) :\n+  build_state_(kInit),\n+  load_state_(kInit),\n+  key_(hash_two_strings(options, source)),\n+  is_global_(false),\n+  dlopen_handle_(nullptr),\n+  lib_name_(config.get_lib_name(key_)) {\n+  // Master constructor - try to get module built in local file\n+  config.ensure_module_dir_exists();\n+  if (file_exists(lib_name_)) {\n+    build_state_ = kPass;\n+  } else {\n+    // The file doesn't exist, let's build it\n+    ModuleBuilder builder(options, source, include, key_);\n+    build_state_ = (builder.try_to_build() ? kPass : kFail);\n+  }\n+}\n+\n+NativeModule::NativeModule(\n+  bool is_global,\n+  const char* key,\n+  ssize_t binary_size,\n+  const void* binary\n+) :\n+  build_state_(is_global ? kPass : kInit),\n+  load_state_(is_global ? kPass : kInit),\n+  key_(key),\n+  is_global_(is_global),\n+  dlopen_handle_(nullptr),\n+  lib_name_(config.get_lib_name(key_)) {\n+  // Worker constructor - try to get the binary written to local file\n+  if (is_global_) return;\n+  int rc = 0;\n+  config.ensure_module_dir_exists();\n+  if (!file_exists(lib_name_)) {\n+    // We hold big_mutex so there is no race\n+    int fd = open(lib_name_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666);\n+    if (fd < 0) {\n+      perror(\"open\");\n+      assert(0);\n+    }\n+    rc = write(fd, binary, binary_size);\n+    assert(rc == binary_size);\n+    ::close(fd);\n+    ::chmod(lib_name_.c_str(), 0644);\n+  }\n+  build_state_ = kPass;\n+  try_load_locked();\n+}\n+\n+NativeModule::~NativeModule() {\n+  if (!is_global_ && dlopen_handle_) {\n+    dlclose(dlopen_handle_);\n+  }\n+}\n+\n+bool NativeModule::try_load_locked() {\n+  if (load_state_ == kInit) {\n+    if (is_global_) {\n+      load_state_ = kPass;\n+    } else {\n+      auto handle = dlopen(lib_name_.c_str(), RTLD_GLOBAL|RTLD_NOW);\n+      if (!handle) {\n+        fprintf(stderr, \"ERROR: dlopen failed: %s\\n\", dlerror());\n+      }\n+      load_state_ = (handle ? kPass : kFail);\n+      if (handle) dlopen_handle_ = handle;\n+    }\n+  }\n+  return (load_state_ == kPass);\n+}\n+\n+std::vector<char> NativeModule::get_binary() {\n+  std::lock_guard<std::mutex> mylock(big_mutex);\n+  int fd = open(config.get_lib_name(key_).c_str(), O_RDONLY, 0666);\n+  std::vector<char> vec;\n+  if (fd < 0) {\n+    return vec; // build failed, no lib, return empty\n+  }\n+  struct stat st;\n+  int rc = fstat(fd, &st);\n+  assert(rc == 0);\n+  size_t file_size = st.st_size;\n+  vec.resize(file_size);\n+  rc = read(fd, &vec[0], file_size);\n+  assert(rc == (int)file_size);\n+  close(fd);\n+  return vec;\n+}\n+\n+static std::string to_qualified_name(\n+  JNIEnv* env,\n+  const std::string& key,\n+  jstring nameJ,\n+  int numArgs,\n+  bool is_global,\n+  bool is_longfunc\n+) {\n+  JString name(env, nameJ);\n+  char argTypeCodes[32];\n+  for (int j = 0; j < numArgs; ++j) argTypeCodes[j] = 'l';\n+  argTypeCodes[numArgs] = 0;\n+  char buf[512];","path":"src/main/c/NativeModule.cpp","position":395,"original_position":395,"commit_id":"198294028403e43a383f7175a9f0c54d400d8324","original_commit_id":"198294028403e43a383f7175a9f0c54d400d8324","user":{"login":"cseed","id":1244990,"node_id":"MDQ6VXNlcjEyNDQ5OTA=","avatar_url":"https://avatars3.githubusercontent.com/u/1244990?v=4","gravatar_id":"","url":"https://api.github.com/users/cseed","html_url":"https://github.com/cseed","followers_url":"https://api.github.com/users/cseed/followers","following_url":"https://api.github.com/users/cseed/following{/other_user}","gists_url":"https://api.github.com/users/cseed/gists{/gist_id}","starred_url":"https://api.github.com/users/cseed/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cseed/subscriptions","organizations_url":"https://api.github.com/users/cseed/orgs","repos_url":"https://api.github.com/users/cseed/repos","events_url":"https://api.github.com/users/cseed/events{/privacy}","received_events_url":"https://api.github.com/users/cseed/received_events","type":"User","site_admin":false},"body":"This screams buffer overflow bug.  At least, use `snprintf` and the assert the output fit.","created_at":"2018-08-29T17:34:40Z","updated_at":"2018-08-29T17:41:18Z","html_url":"https://github.com/hail-is/hail/pull/4211#discussion_r213769249","pull_request_url":"https://api.github.com/repos/hail-is/hail/pulls/4211","author_association":"COLLABORATOR","_links":{"self":{"href":"https://api.github.com/repos/hail-is/hail/pulls/comments/213769249"},"html":{"href":"https://github.com/hail-is/hail/pull/4211#discussion_r213769249"},"pull_request":{"href":"https://api.github.com/repos/hail-is/hail/pulls/4211"}}},"pull_request":{"url":"https://api.github.com/repos/hail-is/hail/pulls/4211","id":210881571,"node_id":"MDExOlB1bGxSZXF1ZXN0MjEwODgxNTcx","html_url":"https://github.com/hail-is/hail/pull/4211","diff_url":"https://github.com/hail-is/hail/pull/4211.diff","patch_url":"https://github.com/hail-is/hail/pull/4211.patch","issue_url":"https://api.github.com/repos/hail-is/hail/issues/4211","number":4211,"state":"open","locked":false,"title":"Cpp5 - support for Scala-generated C++","user":{"login":"rcownie","id":10273990,"node_id":"MDQ6VXNlcjEwMjczOTkw","avatar_url":"https://avatars3.githubusercontent.com/u/10273990?v=4","gravatar_id":"","url":"https://api.github.com/users/rcownie","html_url":"https://github.com/rcownie","followers_url":"https://api.github.com/users/rcownie/followers","following_url":"https://api.github.com/users/rcownie/following{/other_user}","gists_url":"https://api.github.com/users/rcownie/gists{/gist_id}","starred_url":"https://api.github.com/users/rcownie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rcownie/subscriptions","organizations_url":"https://api.github.com/users/rcownie/orgs","repos_url":"https://api.github.com/users/rcownie/repos","events_url":"https://api.github.com/users/rcownie/events{/privacy}","received_events_url":"https://api.github.com/users/rcownie/received_events","type":"User","site_admin":false},"body":"New PR for NativeModule etc\r\n\r\n- NativeModule now has a single big_mutex, so that it is single-threaded (releasing big_mutex\r\n  only while sleeping between polling file state)\r\n\r\n- The run_shell_get_first_line() has been removed, moving almost all configuration into the\r\n   module-build makefile\r\n \r\n- Simplified Makefile\r\n\r\n- Remove some historical code\r\n","created_at":"2018-08-25T02:45:59Z","updated_at":"2018-08-29T17:41:17Z","closed_at":null,"merged_at":null,"merge_commit_sha":"dd3dbd35981d8370225601304b379323e85e57b2","assignee":{"login":"cseed","id":1244990,"node_id":"MDQ6VXNlcjEyNDQ5OTA=","avatar_url":"https://avatars3.githubusercontent.com/u/1244990?v=4","gravatar_id":"","url":"https://api.github.com/users/cseed","html_url":"https://github.com/cseed","followers_url":"https://api.github.com/users/cseed/followers","following_url":"https://api.github.com/users/cseed/following{/other_user}","gists_url":"https://api.github.com/users/cseed/gists{/gist_id}","starred_url":"https://api.github.com/users/cseed/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cseed/subscriptions","organizations_url":"https://api.github.com/users/cseed/orgs","repos_url":"https://api.github.com/users/cseed/repos","events_url":"https://api.github.com/users/cseed/events{/privacy}","received_events_url":"https://api.github.com/users/cseed/received_events","type":"User","site_admin":false},"assignees":[{"login":"cseed","id":1244990,"node_id":"MDQ6VXNlcjEyNDQ5OTA=","avatar_url":"https://avatars3.githubusercontent.com/u/1244990?v=4","gravatar_id":"","url":"https://api.github.com/users/cseed","html_url":"https://github.com/cseed","followers_url":"https://api.github.com/users/cseed/followers","following_url":"https://api.github.com/users/cseed/following{/other_user}","gists_url":"https://api.github.com/users/cseed/gists{/gist_id}","starred_url":"https://api.github.com/users/cseed/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cseed/subscriptions","organizations_url":"https://api.github.com/users/cseed/orgs","repos_url":"https://api.github.com/users/cseed/repos","events_url":"https://api.github.com/users/cseed/events{/privacy}","received_events_url":"https://api.github.com/users/cseed/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/hail-is/hail/pulls/4211/commits","review_comments_url":"https://api.github.com/repos/hail-is/hail/pulls/4211/comments","review_comment_url":"https://api.github.com/repos/hail-is/hail/pulls/comments{/number}","comments_url":"https://api.github.com/repos/hail-is/hail/issues/4211/comments","statuses_url":"https://api.github.com/repos/hail-is/hail/statuses/198294028403e43a383f7175a9f0c54d400d8324","head":{"label":"rcownie:cpp5","ref":"cpp5","sha":"198294028403e43a383f7175a9f0c54d400d8324","user":{"login":"rcownie","id":10273990,"node_id":"MDQ6VXNlcjEwMjczOTkw","avatar_url":"https://avatars3.githubusercontent.com/u/10273990?v=4","gravatar_id":"","url":"https://api.github.com/users/rcownie","html_url":"https://github.com/rcownie","followers_url":"https://api.github.com/users/rcownie/followers","following_url":"https://api.github.com/users/rcownie/following{/other_user}","gists_url":"https://api.github.com/users/rcownie/gists{/gist_id}","starred_url":"https://api.github.com/users/rcownie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rcownie/subscriptions","organizations_url":"https://api.github.com/users/rcownie/orgs","repos_url":"https://api.github.com/users/rcownie/repos","events_url":"https://api.github.com/users/rcownie/events{/privacy}","received_events_url":"https://api.github.com/users/rcownie/received_events","type":"User","site_admin":false},"repo":{"id":122372524,"node_id":"MDEwOlJlcG9zaXRvcnkxMjIzNzI1MjQ=","name":"hail","full_name":"rcownie/hail","owner":{"login":"rcownie","id":10273990,"node_id":"MDQ6VXNlcjEwMjczOTkw","avatar_url":"https://avatars3.githubusercontent.com/u/10273990?v=4","gravatar_id":"","url":"https://api.github.com/users/rcownie","html_url":"https://github.com/rcownie","followers_url":"https://api.github.com/users/rcownie/followers","following_url":"https://api.github.com/users/rcownie/following{/other_user}","gists_url":"https://api.github.com/users/rcownie/gists{/gist_id}","starred_url":"https://api.github.com/users/rcownie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rcownie/subscriptions","organizations_url":"https://api.github.com/users/rcownie/orgs","repos_url":"https://api.github.com/users/rcownie/repos","events_url":"https://api.github.com/users/rcownie/events{/privacy}","received_events_url":"https://api.github.com/users/rcownie/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/rcownie/hail","description":"Scalable genomic data analysis.","fork":true,"url":"https://api.github.com/repos/rcownie/hail","forks_url":"https://api.github.com/repos/rcownie/hail/forks","keys_url":"https://api.github.com/repos/rcownie/hail/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rcownie/hail/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rcownie/hail/teams","hooks_url":"https://api.github.com/repos/rcownie/hail/hooks","issue_events_url":"https://api.github.com/repos/rcownie/hail/issues/events{/number}","events_url":"https://api.github.com/repos/rcownie/hail/events","assignees_url":"https://api.github.com/repos/rcownie/hail/assignees{/user}","branches_url":"https://api.github.com/repos/rcownie/hail/branches{/branch}","tags_url":"https://api.github.com/repos/rcownie/hail/tags","blobs_url":"https://api.github.com/repos/rcownie/hail/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rcownie/hail/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rcownie/hail/git/refs{/sha}","trees_url":"https://api.github.com/repos/rcownie/hail/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rcownie/hail/statuses/{sha}","languages_url":"https://api.github.com/repos/rcownie/hail/languages","stargazers_url":"https://api.github.com/repos/rcownie/hail/stargazers","contributors_url":"https://api.github.com/repos/rcownie/hail/contributors","subscribers_url":"https://api.github.com/repos/rcownie/hail/subscribers","subscription_url":"https://api.github.com/repos/rcownie/hail/subscription","commits_url":"https://api.github.com/repos/rcownie/hail/commits{/sha}","git_commits_url":"https://api.github.com/repos/rcownie/hail/git/commits{/sha}","comments_url":"https://api.github.com/repos/rcownie/hail/comments{/number}","issue_comment_url":"https://api.github.com/repos/rcownie/hail/issues/comments{/number}","contents_url":"https://api.github.com/repos/rcownie/hail/contents/{+path}","compare_url":"https://api.github.com/repos/rcownie/hail/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rcownie/hail/merges","archive_url":"https://api.github.com/repos/rcownie/hail/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rcownie/hail/downloads","issues_url":"https://api.github.com/repos/rcownie/hail/issues{/number}","pulls_url":"https://api.github.com/repos/rcownie/hail/pulls{/number}","milestones_url":"https://api.github.com/repos/rcownie/hail/milestones{/number}","notifications_url":"https://api.github.com/repos/rcownie/hail/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rcownie/hail/labels{/name}","releases_url":"https://api.github.com/repos/rcownie/hail/releases{/id}","deployments_url":"https://api.github.com/repos/rcownie/hail/deployments","created_at":"2018-02-21T17:57:56Z","updated_at":"2018-08-25T20:37:42Z","pushed_at":"2018-08-29T17:25:34Z","git_url":"git://github.com/rcownie/hail.git","ssh_url":"git@github.com:rcownie/hail.git","clone_url":"https://github.com/rcownie/hail.git","svn_url":"https://github.com/rcownie/hail","homepage":"https://hail.is","size":48494,"stargazers_count":0,"watchers_count":0,"language":"Scala","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"hail-is:master","ref":"master","sha":"02b7ad0299d7758b20d5b2ecfbaaa04dcaca29ea","user":{"login":"hail-is","id":20861792,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIwODYxNzky","avatar_url":"https://avatars1.githubusercontent.com/u/20861792?v=4","gravatar_id":"","url":"https://api.github.com/users/hail-is","html_url":"https://github.com/hail-is","followers_url":"https://api.github.com/users/hail-is/followers","following_url":"https://api.github.com/users/hail-is/following{/other_user}","gists_url":"https://api.github.com/users/hail-is/gists{/gist_id}","starred_url":"https://api.github.com/users/hail-is/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hail-is/subscriptions","organizations_url":"https://api.github.com/users/hail-is/orgs","repos_url":"https://api.github.com/users/hail-is/repos","events_url":"https://api.github.com/users/hail-is/events{/privacy}","received_events_url":"https://api.github.com/users/hail-is/received_events","type":"Organization","site_admin":false},"repo":{"id":45069467,"node_id":"MDEwOlJlcG9zaXRvcnk0NTA2OTQ2Nw==","name":"hail","full_name":"hail-is/hail","owner":{"login":"hail-is","id":20861792,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIwODYxNzky","avatar_url":"https://avatars1.githubusercontent.com/u/20861792?v=4","gravatar_id":"","url":"https://api.github.com/users/hail-is","html_url":"https://github.com/hail-is","followers_url":"https://api.github.com/users/hail-is/followers","following_url":"https://api.github.com/users/hail-is/following{/other_user}","gists_url":"https://api.github.com/users/hail-is/gists{/gist_id}","starred_url":"https://api.github.com/users/hail-is/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hail-is/subscriptions","organizations_url":"https://api.github.com/users/hail-is/orgs","repos_url":"https://api.github.com/users/hail-is/repos","events_url":"https://api.github.com/users/hail-is/events{/privacy}","received_events_url":"https://api.github.com/users/hail-is/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/hail-is/hail","description":"Scalable genomic data analysis.","fork":false,"url":"https://api.github.com/repos/hail-is/hail","forks_url":"https://api.github.com/repos/hail-is/hail/forks","keys_url":"https://api.github.com/repos/hail-is/hail/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hail-is/hail/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hail-is/hail/teams","hooks_url":"https://api.github.com/repos/hail-is/hail/hooks","issue_events_url":"https://api.github.com/repos/hail-is/hail/issues/events{/number}","events_url":"https://api.github.com/repos/hail-is/hail/events","assignees_url":"https://api.github.com/repos/hail-is/hail/assignees{/user}","branches_url":"https://api.github.com/repos/hail-is/hail/branches{/branch}","tags_url":"https://api.github.com/repos/hail-is/hail/tags","blobs_url":"https://api.github.com/repos/hail-is/hail/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hail-is/hail/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hail-is/hail/git/refs{/sha}","trees_url":"https://api.github.com/repos/hail-is/hail/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hail-is/hail/statuses/{sha}","languages_url":"https://api.github.com/repos/hail-is/hail/languages","stargazers_url":"https://api.github.com/repos/hail-is/hail/stargazers","contributors_url":"https://api.github.com/repos/hail-is/hail/contributors","subscribers_url":"https://api.github.com/repos/hail-is/hail/subscribers","subscription_url":"https://api.github.com/repos/hail-is/hail/subscription","commits_url":"https://api.github.com/repos/hail-is/hail/commits{/sha}","git_commits_url":"https://api.github.com/repos/hail-is/hail/git/commits{/sha}","comments_url":"https://api.github.com/repos/hail-is/hail/comments{/number}","issue_comment_url":"https://api.github.com/repos/hail-is/hail/issues/comments{/number}","contents_url":"https://api.github.com/repos/hail-is/hail/contents/{+path}","compare_url":"https://api.github.com/repos/hail-is/hail/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hail-is/hail/merges","archive_url":"https://api.github.com/repos/hail-is/hail/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hail-is/hail/downloads","issues_url":"https://api.github.com/repos/hail-is/hail/issues{/number}","pulls_url":"https://api.github.com/repos/hail-is/hail/pulls{/number}","milestones_url":"https://api.github.com/repos/hail-is/hail/milestones{/number}","notifications_url":"https://api.github.com/repos/hail-is/hail/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hail-is/hail/labels{/name}","releases_url":"https://api.github.com/repos/hail-is/hail/releases{/id}","deployments_url":"https://api.github.com/repos/hail-is/hail/deployments","created_at":"2015-10-27T20:55:42Z","updated_at":"2018-08-29T15:37:36Z","pushed_at":"2018-08-29T17:00:36Z","git_url":"git://github.com/hail-is/hail.git","ssh_url":"git@github.com:hail-is/hail.git","clone_url":"https://github.com/hail-is/hail.git","svn_url":"https://github.com/hail-is/hail","homepage":"https://hail.is","size":40663,"stargazers_count":360,"watchers_count":360,"language":"Scala","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":104,"mirror_url":null,"archived":false,"open_issues_count":150,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":104,"open_issues":150,"watchers":360,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/hail-is/hail/pulls/4211"},"html":{"href":"https://github.com/hail-is/hail/pull/4211"},"issue":{"href":"https://api.github.com/repos/hail-is/hail/issues/4211"},"comments":{"href":"https://api.github.com/repos/hail-is/hail/issues/4211/comments"},"review_comments":{"href":"https://api.github.com/repos/hail-is/hail/pulls/4211/comments"},"review_comment":{"href":"https://api.github.com/repos/hail-is/hail/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/hail-is/hail/pulls/4211/commits"},"statuses":{"href":"https://api.github.com/repos/hail-is/hail/statuses/198294028403e43a383f7175a9f0c54d400d8324"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"cseed"}}