{"action":"created","comment":{"url":"https://api.github.com/repos/endlessm/eos-updater/pulls/comments/67574117","id":67574117,"diff_hunk":"@@ -0,0 +1,309 @@\n+/* -*- mode: C; c-file-style: \"gnu\"; indent-tabs-mode: nil; -*-\n+ *\n+ * Copyright 2016 Kinvolk GmbH\n+ *\n+ * This library is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU Lesser General Public\n+ * License as published by the Free Software Foundation; either\n+ * version 2 of the License, or (at your option) any later version.\n+ *\n+ * This library is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ * Lesser General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Lesser General Public\n+ * License along with this library; if not, write to the\n+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,\n+ * Boston, MA 02111-1307, USA.\n+ *\n+ * Author: Krzesimir Nowak <krzesimir@kinvolk.io>\n+ */\n+\n+#include \"eos-updater-branch-file.h\"\n+\n+#include \"eos-util.h\"\n+\n+static void\n+eos_branch_file_dispose_impl (EosBranchFile *bf)\n+{\n+  g_clear_pointer (&bf->raw_contents, g_bytes_unref);\n+  g_clear_pointer (&bf->raw_signature, g_bytes_unref);\n+  g_clear_pointer (&bf->branch_file, g_key_file_unref);\n+  g_clear_pointer (&bf->download_time, g_date_time_unref);\n+}\n+\n+static void\n+eos_branch_file_finalize_impl (EosBranchFile *bf)\n+{\n+  g_free (bf->contents_sha512sum);\n+}\n+\n+EOS_DEFINE_REFCOUNTED (EOS_BRANCH_FILE,\n+                       EosBranchFile,\n+                       eos_branch_file,\n+                       eos_branch_file_dispose_impl,\n+                       eos_branch_file_finalize_impl)\n+\n+static GDateTime *\n+get_download_time (GFile *file,\n+                   GCancellable *cancellable,\n+                   GError **error)\n+{\n+  g_autoptr(GFileInfo) info = NULL;\n+  guint64 mod_time;\n+\n+  info = g_file_query_info (file,\n+                            G_FILE_ATTRIBUTE_TIME_MODIFIED,\n+                            G_FILE_QUERY_INFO_NONE,\n+                            cancellable,\n+                            error);\n+  if (info == NULL)\n+    return NULL;\n+\n+  mod_time = g_file_info_get_attribute_uint64 (info,\n+                                               G_FILE_ATTRIBUTE_TIME_MODIFIED);\n+  return g_date_time_new_from_unix_utc (mod_time);\n+}\n+\n+static gboolean\n+read_file_to_bytes (GFile *file,\n+                    GCancellable *cancellable,\n+                    GBytes **out_bytes,\n+                    GError **error)\n+{\n+  g_autofree gchar *contents = NULL;\n+  gsize len = 0;\n+\n+  if (!g_file_query_exists (file, cancellable))\n+    return TRUE;\n+\n+  if (!g_file_load_contents (file,\n+                             cancellable,\n+                             &contents,\n+                             &len,\n+                             NULL,\n+                             error))\n+    return FALSE;\n+\n+  *out_bytes = g_bytes_new_take (g_steal_pointer (&contents), len);\n+  return TRUE;\n+}\n+\n+EosBranchFile *\n+eos_branch_file_new_empty (void)\n+{\n+  return g_object_new (EOS_TYPE_BRANCH_FILE, NULL);\n+};\n+\n+// TODO: put it in util header\n+static GFile *\n+get_eos_extensions_dir (OstreeRepo *repo)\n+{\n+  g_autofree gchar *rel_path = g_build_filename (\"extensions\", \"eos\", NULL);\n+\n+  return g_file_get_child (ostree_repo_get_path (repo), rel_path);\n+}\n+\n+static void\n+get_branch_file_paths (GFile *ext_path,\n+                       GFile **branch_file,\n+                       GFile **branch_file_sig)\n+{\n+  *branch_file = g_file_get_child (ext_path, \"branch_file\");\n+  *branch_file_sig = g_file_get_child (ext_path, \"branch_file.sig\");\n+}\n+\n+EosBranchFile *\n+eos_branch_file_new_from_repo (OstreeRepo *repo,\n+                               GCancellable *cancellable,\n+                               GError **error)\n+{\n+  g_autoptr(GFile) ext_dir = get_eos_extensions_dir (repo);\n+  g_autoptr(GFile) branch_file = NULL;\n+  g_autoptr(GFile) signature = NULL;\n+\n+  get_branch_file_paths (ext_dir, &branch_file, &signature);\n+  return eos_branch_file_new_from_files (branch_file, signature, cancellable, error);\n+}\n+\n+EosBranchFile *\n+eos_branch_file_new_from_files (GFile *branch_file,\n+                                GFile *signature,\n+                                GCancellable *cancellable,\n+                                GError **error)\n+{\n+  g_autoptr(GBytes) branch_file_bytes = NULL;\n+  g_autoptr(GBytes) signature_bytes = NULL;\n+  g_autoptr(GDateTime) download_time = NULL;\n+\n+  g_return_val_if_fail (G_IS_FILE (branch_file), NULL);\n+  g_return_val_if_fail (G_IS_FILE (signature), NULL);\n+\n+  if (!read_file_to_bytes (branch_file,\n+                           cancellable,\n+                           &branch_file_bytes,\n+                           error))\n+    return NULL;\n+  if (branch_file_bytes == NULL)\n+    {\n+      g_autofree gchar *path = g_file_get_path (branch_file);\n+\n+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,\n+                   \"cached branch file in %s not found\", path);\n+      return NULL;\n+    }\n+  if (!read_file_to_bytes (signature,\n+                           cancellable,\n+                           &signature_bytes,\n+                           error))\n+    return NULL;\n+  if (signature_bytes == NULL)\n+    {\n+      download_time = get_download_time (branch_file,\n+                                         cancellable,\n+                                         error);\n+      if (download_time == NULL)\n+        return NULL;\n+    }\n+\n+  return eos_branch_file_new_from_raw (branch_file_bytes,\n+                                       signature_bytes,\n+                                       download_time,\n+                                       error);\n+}\n+\n+EosBranchFile *\n+eos_branch_file_new_from_raw (GBytes *contents,\n+                              GBytes *signature,\n+                              GDateTime *download_time,\n+                              GError **error)\n+{\n+  g_autoptr(EosBranchFile) bf = NULL;\n+  gconstpointer contents_bytes;\n+  gsize contents_len;\n+  g_autoptr(GChecksum) sha512 = NULL;\n+\n+  g_return_val_if_fail (contents != NULL, NULL);\n+  g_return_val_if_fail (signature != NULL || download_time != NULL, NULL);\n+\n+  bf = eos_branch_file_new_empty ();\n+\n+  bf->raw_contents = g_bytes_ref (contents);\n+\n+  if (signature != NULL)\n+    bf->raw_signature = g_bytes_ref (signature);\n+\n+  bf->branch_file = g_key_file_new ();\n+  contents_bytes = g_bytes_get_data (bf->raw_contents, &contents_len);\n+  if (!g_key_file_load_from_data (bf->branch_file,\n+                                  contents_bytes,\n+                                  contents_len,\n+                                  G_KEY_FILE_NONE,\n+                                  error))\n+    return NULL;\n+\n+  sha512 = g_checksum_new (G_CHECKSUM_SHA512);\n+  g_checksum_update (sha512, contents_bytes, contents_len);\n+  bf->contents_sha512sum = g_strdup (g_checksum_get_string (sha512));\n+\n+  if (signature != NULL)\n+    {\n+      if (!eos_updater_get_timestamp_from_branch_file_keyfile (bf->branch_file,\n+                                                               &bf->download_time,\n+                                                               error))\n+        return NULL;\n+    }\n+  else\n+    bf->download_time = g_date_time_ref (download_time);\n+\n+  return g_steal_pointer (&bf);\n+}\n+\n+gboolean\n+eos_branch_file_save_to_repo (EosBranchFile *branch_file,\n+                              OstreeRepo *repo,\n+                              GCancellable *cancellable,\n+                              GError **error)\n+{\n+  g_autoptr(GFile) ext_dir = get_eos_extensions_dir (repo);\n+  g_autoptr(GFile) branch_file_path = NULL;\n+  g_autoptr(GFile) signature_path = NULL;\n+\n+  get_branch_file_paths (ext_dir, &branch_file_path, &signature_path);\n+  return eos_branch_file_save (branch_file,\n+                               branch_file_path,\n+                               signature_path,\n+                               cancellable,\n+                               error);\n+}\n+\n+gboolean\n+eos_branch_file_save (EosBranchFile *branch_file,\n+                      GFile *target,\n+                      GFile *target_signature,\n+                      GCancellable *cancellable,\n+                      GError **error)\n+{\n+  gconstpointer contents;\n+  gsize len;\n+\n+  g_return_val_if_fail (EOS_IS_BRANCH_FILE (branch_file), FALSE);\n+\n+  contents = g_bytes_get_data (branch_file->raw_contents,\n+                               &len);\n+  if (!g_file_replace_contents (target,\n+                                contents,\n+                                len,\n+                                NULL,\n+                                FALSE,\n+                                G_FILE_CREATE_NONE,\n+                                NULL,\n+                                cancellable,\n+                                error))\n+    return FALSE;\n+\n+  if (branch_file->raw_signature == NULL)\n+    {\n+      gint64 unix_time = g_date_time_to_unix (branch_file->download_time);\n+\n+      if (unix_time < 0)\n+        {\n+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,\n+                       \"invalid download time of the branch file\");","path":"src/eos-updater-branch-file.c","position":273,"original_position":273,"commit_id":"11801770675d6ec76b594a27f8212aa01541ce61","original_commit_id":"9c62faff990ba54fecc0cf9b9f37007688d7fb91","user":{"login":"smspillaz","id":775309,"avatar_url":"https://avatars.githubusercontent.com/u/775309?v=3","gravatar_id":"","url":"https://api.github.com/users/smspillaz","html_url":"https://github.com/smspillaz","followers_url":"https://api.github.com/users/smspillaz/followers","following_url":"https://api.github.com/users/smspillaz/following{/other_user}","gists_url":"https://api.github.com/users/smspillaz/gists{/gist_id}","starred_url":"https://api.github.com/users/smspillaz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/smspillaz/subscriptions","organizations_url":"https://api.github.com/users/smspillaz/orgs","repos_url":"https://api.github.com/users/smspillaz/repos","events_url":"https://api.github.com/users/smspillaz/events{/privacy}","received_events_url":"https://api.github.com/users/smspillaz/received_events","type":"User","site_admin":false},"body":"Is `G_IO_ERROR_NOT_FOUND` the best error code to use here?","created_at":"2016-06-17T20:55:13Z","updated_at":"2016-06-17T20:55:13Z","html_url":"https://github.com/endlessm/eos-updater/pull/37#discussion_r67574117","pull_request_url":"https://api.github.com/repos/endlessm/eos-updater/pulls/37","_links":{"self":{"href":"https://api.github.com/repos/endlessm/eos-updater/pulls/comments/67574117"},"html":{"href":"https://github.com/endlessm/eos-updater/pull/37#discussion_r67574117"},"pull_request":{"href":"https://api.github.com/repos/endlessm/eos-updater/pulls/37"}}},"pull_request":{"url":"https://api.github.com/repos/endlessm/eos-updater/pulls/37","id":72008936,"html_url":"https://github.com/endlessm/eos-updater/pull/37","diff_url":"https://github.com/endlessm/eos-updater/pull/37.diff","patch_url":"https://github.com/endlessm/eos-updater/pull/37.patch","issue_url":"https://api.github.com/repos/endlessm/eos-updater/issues/37","number":37,"state":"open","locked":false,"title":"LAN client and server, tests","user":{"login":"krnowak","id":96081,"avatar_url":"https://avatars.githubusercontent.com/u/96081?v=3","gravatar_id":"","url":"https://api.github.com/users/krnowak","html_url":"https://github.com/krnowak","followers_url":"https://api.github.com/users/krnowak/followers","following_url":"https://api.github.com/users/krnowak/following{/other_user}","gists_url":"https://api.github.com/users/krnowak/gists{/gist_id}","starred_url":"https://api.github.com/users/krnowak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/krnowak/subscriptions","organizations_url":"https://api.github.com/users/krnowak/orgs","repos_url":"https://api.github.com/users/krnowak/repos","events_url":"https://api.github.com/users/krnowak/events{/privacy}","received_events_url":"https://api.github.com/users/krnowak/received_events","type":"User","site_admin":false},"body":"Sorry about the code-droppy nature of this PR. The commits are basically per file as the work grew a bit. The changes done are:\r\n\r\n- add eos-updater daemon config\r\n  - for now it only has the \"Download\" group with an \"Order\" field being a comma-separated list of download sources in order of preference (currently the only possible values are \"lan\" and \"main\").\r\n- add polling for updates on LAN\r\n  - it starts EosAvahiDiscoverer to get the list of servers\r\n  - there are two versions of txt records possible:\r\n    - version 1 is mostly for servers that do not have signed branch file, so in txt records it contains:\r\n      - sha512sum of the branch file\r\n      - download time of the branch file from the main server\r\n      - ostree path\r\n    - version 2 is for the servers that do have signed branch file, so in txt records it contains:\r\n      - ostree path (should be exposed in a branch file too, so we can verify it, not yet implemented)\r\n      - branch file timestamp (verified against a timestamp in the branch file)\r\n    - version 2 does not expose hashed sum in txt records, \r\n      - signature serves the role of the hash checksum\r\n      - branch file has the new \"UnixUTCTimestamp\" field in the \"main\" group\r\n      - branch file would have the new \"OstreePath\" field in the \"main\" group, not implemented\r\n  - once the services are discovered, it downloads the newest branch file\r\n    - signed branch files are preferred over the unsigned ones, \r\n    - for version 1 of txt records we skip downloading the branch file if the timestamp was older or the sha512sum was the same\r\n    - if there are no signed branch files, we search the newest unsigned one\r\n  - it processes the branch file to learn about OstreeRef for the device\r\n    - if OnHold field is set to true, we stop the update\r\n  - it downloads ref->commit information from every service to learn what commit they have for upgrade\r\n    - this might be a lot of churn, maybe ref->commit information should be exposed in avahi txt records too?\r\n  - it filters the services so it has a list of services that offer the newest commit\r\n  - it prepares the update info and the metrics info\r\n- update polling from the main server\r\n  - now it can download the signature of the branch file\r\n  - it queries the main server about the latest commit from the ref->commit mapping\r\n  - it prepares the update info and the metrics info\r\n- update fetching the update\r\n  - the update info prepared in poll stage contains an optional list of url overrides\r\n  - the url actually used for fetching the updates is selected randomly \r\n- update applying the update\r\n  - store the extra files in ostree repo's extensions directory\r\n    - this is basically /ostree/repo/extensions/eos\r\n    - there we store the branch file with the signature and ref->commit mappings with signatures\r\n  - generate avahi service file\r\n- add LAN update server\r\n  - it serves the files in ostree repository\r\n    - repo config is faked to make ostree pull to believe it is pulling from archive-z2 repository\r\n    - refs/heads/\\<ref\\> are handled specially by serving /refs/remotes/\\<remote\\>/\\<ref\\>\r\n    - requests for /objects/\\<checksum\\>.filez are handled by zipping the objects with requested checksum on the fly\r\n    - rest of the files are served as is\r\n  - the server can listen on local port or on socket given to it by systemd\r\n  - the server shuts itself down after some period of inactivity\r\n- add some tests\r\n  - one is checking if updating from the main server works\r\n  - one is checking if updating from the lan server works","created_at":"2016-05-31T12:45:14Z","updated_at":"2016-06-17T20:55:13Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5bc567ac10b57e86d9b25da9f89e9d485ba8e99b","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/endlessm/eos-updater/pulls/37/commits","review_comments_url":"https://api.github.com/repos/endlessm/eos-updater/pulls/37/comments","review_comment_url":"https://api.github.com/repos/endlessm/eos-updater/pulls/comments{/number}","comments_url":"https://api.github.com/repos/endlessm/eos-updater/issues/37/comments","statuses_url":"https://api.github.com/repos/endlessm/eos-updater/statuses/11801770675d6ec76b594a27f8212aa01541ce61","head":{"label":"kinvolk:krnowak/avahi","ref":"krnowak/avahi","sha":"11801770675d6ec76b594a27f8212aa01541ce61","user":{"login":"kinvolk","id":14073052,"avatar_url":"https://avatars.githubusercontent.com/u/14073052?v=3","gravatar_id":"","url":"https://api.github.com/users/kinvolk","html_url":"https://github.com/kinvolk","followers_url":"https://api.github.com/users/kinvolk/followers","following_url":"https://api.github.com/users/kinvolk/following{/other_user}","gists_url":"https://api.github.com/users/kinvolk/gists{/gist_id}","starred_url":"https://api.github.com/users/kinvolk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kinvolk/subscriptions","organizations_url":"https://api.github.com/users/kinvolk/orgs","repos_url":"https://api.github.com/users/kinvolk/repos","events_url":"https://api.github.com/users/kinvolk/events{/privacy}","received_events_url":"https://api.github.com/users/kinvolk/received_events","type":"Organization","site_admin":false},"repo":{"id":56606373,"name":"eos-updater","full_name":"kinvolk/eos-updater","owner":{"login":"kinvolk","id":14073052,"avatar_url":"https://avatars.githubusercontent.com/u/14073052?v=3","gravatar_id":"","url":"https://api.github.com/users/kinvolk","html_url":"https://github.com/kinvolk","followers_url":"https://api.github.com/users/kinvolk/followers","following_url":"https://api.github.com/users/kinvolk/following{/other_user}","gists_url":"https://api.github.com/users/kinvolk/gists{/gist_id}","starred_url":"https://api.github.com/users/kinvolk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kinvolk/subscriptions","organizations_url":"https://api.github.com/users/kinvolk/orgs","repos_url":"https://api.github.com/users/kinvolk/repos","events_url":"https://api.github.com/users/kinvolk/events{/privacy}","received_events_url":"https://api.github.com/users/kinvolk/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/kinvolk/eos-updater","description":"System components of the OS Tree based updater","fork":true,"url":"https://api.github.com/repos/kinvolk/eos-updater","forks_url":"https://api.github.com/repos/kinvolk/eos-updater/forks","keys_url":"https://api.github.com/repos/kinvolk/eos-updater/keys{/key_id}","collaborators_url":"https://api.github.com/repos/kinvolk/eos-updater/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/kinvolk/eos-updater/teams","hooks_url":"https://api.github.com/repos/kinvolk/eos-updater/hooks","issue_events_url":"https://api.github.com/repos/kinvolk/eos-updater/issues/events{/number}","events_url":"https://api.github.com/repos/kinvolk/eos-updater/events","assignees_url":"https://api.github.com/repos/kinvolk/eos-updater/assignees{/user}","branches_url":"https://api.github.com/repos/kinvolk/eos-updater/branches{/branch}","tags_url":"https://api.github.com/repos/kinvolk/eos-updater/tags","blobs_url":"https://api.github.com/repos/kinvolk/eos-updater/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/kinvolk/eos-updater/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/kinvolk/eos-updater/git/refs{/sha}","trees_url":"https://api.github.com/repos/kinvolk/eos-updater/git/trees{/sha}","statuses_url":"https://api.github.com/repos/kinvolk/eos-updater/statuses/{sha}","languages_url":"https://api.github.com/repos/kinvolk/eos-updater/languages","stargazers_url":"https://api.github.com/repos/kinvolk/eos-updater/stargazers","contributors_url":"https://api.github.com/repos/kinvolk/eos-updater/contributors","subscribers_url":"https://api.github.com/repos/kinvolk/eos-updater/subscribers","subscription_url":"https://api.github.com/repos/kinvolk/eos-updater/subscription","commits_url":"https://api.github.com/repos/kinvolk/eos-updater/commits{/sha}","git_commits_url":"https://api.github.com/repos/kinvolk/eos-updater/git/commits{/sha}","comments_url":"https://api.github.com/repos/kinvolk/eos-updater/comments{/number}","issue_comment_url":"https://api.github.com/repos/kinvolk/eos-updater/issues/comments{/number}","contents_url":"https://api.github.com/repos/kinvolk/eos-updater/contents/{+path}","compare_url":"https://api.github.com/repos/kinvolk/eos-updater/compare/{base}...{head}","merges_url":"https://api.github.com/repos/kinvolk/eos-updater/merges","archive_url":"https://api.github.com/repos/kinvolk/eos-updater/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/kinvolk/eos-updater/downloads","issues_url":"https://api.github.com/repos/kinvolk/eos-updater/issues{/number}","pulls_url":"https://api.github.com/repos/kinvolk/eos-updater/pulls{/number}","milestones_url":"https://api.github.com/repos/kinvolk/eos-updater/milestones{/number}","notifications_url":"https://api.github.com/repos/kinvolk/eos-updater/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/kinvolk/eos-updater/labels{/name}","releases_url":"https://api.github.com/repos/kinvolk/eos-updater/releases{/id}","deployments_url":"https://api.github.com/repos/kinvolk/eos-updater/deployments","created_at":"2016-04-19T14:57:08Z","updated_at":"2016-04-19T14:57:08Z","pushed_at":"2016-06-14T12:48:36Z","git_url":"git://github.com/kinvolk/eos-updater.git","ssh_url":"git@github.com:kinvolk/eos-updater.git","clone_url":"https://github.com/kinvolk/eos-updater.git","svn_url":"https://github.com/kinvolk/eos-updater","homepage":null,"size":259,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"endlessm:master","ref":"master","sha":"2c569772f36a29189b9c36bff330c3648fb7dc95","user":{"login":"endlessm","id":1930958,"avatar_url":"https://avatars.githubusercontent.com/u/1930958?v=3","gravatar_id":"","url":"https://api.github.com/users/endlessm","html_url":"https://github.com/endlessm","followers_url":"https://api.github.com/users/endlessm/followers","following_url":"https://api.github.com/users/endlessm/following{/other_user}","gists_url":"https://api.github.com/users/endlessm/gists{/gist_id}","starred_url":"https://api.github.com/users/endlessm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/endlessm/subscriptions","organizations_url":"https://api.github.com/users/endlessm/orgs","repos_url":"https://api.github.com/users/endlessm/repos","events_url":"https://api.github.com/users/endlessm/events{/privacy}","received_events_url":"https://api.github.com/users/endlessm/received_events","type":"Organization","site_admin":false},"repo":{"id":13253303,"name":"eos-updater","full_name":"endlessm/eos-updater","owner":{"login":"endlessm","id":1930958,"avatar_url":"https://avatars.githubusercontent.com/u/1930958?v=3","gravatar_id":"","url":"https://api.github.com/users/endlessm","html_url":"https://github.com/endlessm","followers_url":"https://api.github.com/users/endlessm/followers","following_url":"https://api.github.com/users/endlessm/following{/other_user}","gists_url":"https://api.github.com/users/endlessm/gists{/gist_id}","starred_url":"https://api.github.com/users/endlessm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/endlessm/subscriptions","organizations_url":"https://api.github.com/users/endlessm/orgs","repos_url":"https://api.github.com/users/endlessm/repos","events_url":"https://api.github.com/users/endlessm/events{/privacy}","received_events_url":"https://api.github.com/users/endlessm/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/endlessm/eos-updater","description":"System components of the OS Tree based updater","fork":false,"url":"https://api.github.com/repos/endlessm/eos-updater","forks_url":"https://api.github.com/repos/endlessm/eos-updater/forks","keys_url":"https://api.github.com/repos/endlessm/eos-updater/keys{/key_id}","collaborators_url":"https://api.github.com/repos/endlessm/eos-updater/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/endlessm/eos-updater/teams","hooks_url":"https://api.github.com/repos/endlessm/eos-updater/hooks","issue_events_url":"https://api.github.com/repos/endlessm/eos-updater/issues/events{/number}","events_url":"https://api.github.com/repos/endlessm/eos-updater/events","assignees_url":"https://api.github.com/repos/endlessm/eos-updater/assignees{/user}","branches_url":"https://api.github.com/repos/endlessm/eos-updater/branches{/branch}","tags_url":"https://api.github.com/repos/endlessm/eos-updater/tags","blobs_url":"https://api.github.com/repos/endlessm/eos-updater/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/endlessm/eos-updater/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/endlessm/eos-updater/git/refs{/sha}","trees_url":"https://api.github.com/repos/endlessm/eos-updater/git/trees{/sha}","statuses_url":"https://api.github.com/repos/endlessm/eos-updater/statuses/{sha}","languages_url":"https://api.github.com/repos/endlessm/eos-updater/languages","stargazers_url":"https://api.github.com/repos/endlessm/eos-updater/stargazers","contributors_url":"https://api.github.com/repos/endlessm/eos-updater/contributors","subscribers_url":"https://api.github.com/repos/endlessm/eos-updater/subscribers","subscription_url":"https://api.github.com/repos/endlessm/eos-updater/subscription","commits_url":"https://api.github.com/repos/endlessm/eos-updater/commits{/sha}","git_commits_url":"https://api.github.com/repos/endlessm/eos-updater/git/commits{/sha}","comments_url":"https://api.github.com/repos/endlessm/eos-updater/comments{/number}","issue_comment_url":"https://api.github.com/repos/endlessm/eos-updater/issues/comments{/number}","contents_url":"https://api.github.com/repos/endlessm/eos-updater/contents/{+path}","compare_url":"https://api.github.com/repos/endlessm/eos-updater/compare/{base}...{head}","merges_url":"https://api.github.com/repos/endlessm/eos-updater/merges","archive_url":"https://api.github.com/repos/endlessm/eos-updater/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/endlessm/eos-updater/downloads","issues_url":"https://api.github.com/repos/endlessm/eos-updater/issues{/number}","pulls_url":"https://api.github.com/repos/endlessm/eos-updater/pulls{/number}","milestones_url":"https://api.github.com/repos/endlessm/eos-updater/milestones{/number}","notifications_url":"https://api.github.com/repos/endlessm/eos-updater/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/endlessm/eos-updater/labels{/name}","releases_url":"https://api.github.com/repos/endlessm/eos-updater/releases{/id}","deployments_url":"https://api.github.com/repos/endlessm/eos-updater/deployments","created_at":"2013-10-01T19:23:58Z","updated_at":"2016-03-02T21:58:39Z","pushed_at":"2016-06-14T12:48:36Z","git_url":"git://github.com/endlessm/eos-updater.git","ssh_url":"git@github.com:endlessm/eos-updater.git","clone_url":"https://github.com/endlessm/eos-updater.git","svn_url":"https://github.com/endlessm/eos-updater","homepage":null,"size":234,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/endlessm/eos-updater/pulls/37"},"html":{"href":"https://github.com/endlessm/eos-updater/pull/37"},"issue":{"href":"https://api.github.com/repos/endlessm/eos-updater/issues/37"},"comments":{"href":"https://api.github.com/repos/endlessm/eos-updater/issues/37/comments"},"review_comments":{"href":"https://api.github.com/repos/endlessm/eos-updater/pulls/37/comments"},"review_comment":{"href":"https://api.github.com/repos/endlessm/eos-updater/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/endlessm/eos-updater/pulls/37/commits"},"statuses":{"href":"https://api.github.com/repos/endlessm/eos-updater/statuses/11801770675d6ec76b594a27f8212aa01541ce61"}}}}
{"actor":{"display_login":"smspillaz"}}