{"action":"created","comment":{"url":"https://api.github.com/repos/sailfishos/sfmf/pulls/comments/29663683","id":29663683,"diff_hunk":"@@ -0,0 +1,725 @@\n+/**\n+ * Sailfish OS Factory Snapshot Update\n+ * Copyright (C) 2015 Jolla Ltd.\n+ * Contact: Thomas Perl <thomas.perl@jolla.com>\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 as\n+ * published by the Free Software Foundation.\n+ *\n+ * This program 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\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License along\n+ * with this program; if not, write to the Free Software Foundation, Inc.,\n+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n+ **/\n+\n+#include \"logging.h\"\n+#include \"cleanup.h\"\n+#include \"privileged.h\"\n+\n+#include <unistd.h>\n+#include <stdlib.h>\n+#include <string.h>\n+\n+#include <glib.h>\n+#include <gio/gio.h>\n+\n+#define SFMF_DEPLOY \"/usr/bin/sfmf-deploy\"\n+#define SAILFISH_SNAPSHOT \"/usr/bin/sailfish-snapshot\"\n+\n+#define FACTORY_NAME \"factory\"\n+#define FACTORY_RENAME \"factory-old\"\n+#define SNAPSHOT_NAME \"factory-slipstream-tmp\"\n+\n+#define UFS_DBUS_NAME \"org.sailfishos.sfmf.ufs\"\n+#define UFS_DBUS_INTERFACE \"org.sailfishos.sfmf.ufs\"\n+#define UFS_DBUS_PATH \"/\"\n+\n+#define IDLE_TIMEOUT_SEC 60\n+\n+\n+struct DeployTask {\n+    gchar *name;\n+    gchar **cmd;\n+    int checked;\n+};\n+\n+void deploy_task_handle_exit_status(struct DeployTask *task, int exit_status);\n+\n+\n+struct DeployTaskQueue {\n+    const char *name;\n+\n+    struct DeployTask *tasks;\n+    int current;\n+    int total;\n+    int finished;\n+\n+    void (*finished_callback)(struct DeployTaskQueue *, void *); // callback when everything is done\n+    void (*task_done_callback)(struct DeployTaskQueue *, void *); // callback when one task is done\n+    void *callback_user_data; // user_data pointer passed to finished_callback and task_done_callback\n+};\n+\n+int deploy_task_queue_next(struct DeployTaskQueue *queue, int async);\n+void deploy_task_queue_abort(struct DeployTaskQueue *queue);\n+struct DeployTask *deploy_task_queue_get_current_task(struct DeployTaskQueue *queue);\n+void deploy_task_queue_run_sync(struct DeployTaskQueue *queue);\n+void deploy_task_queue_run_async(struct DeployTaskQueue *queue);\n+\n+// Callbacks\n+void deploy_task_queue_on_subprocess_finished_cb(GPid pid, gint status, gpointer user_data);\n+\n+\n+struct UpgradeFactorySnapshot {\n+    GMainLoop *mainloop;\n+    GDBusConnection *system_bus;\n+    guint own_name;\n+    guint object_registration;\n+    guint idle_timer_source_id;\n+\n+    gboolean running;\n+\n+    struct DeployTaskQueue *deploy_queue;\n+    struct DeployTaskQueue *cleanup_queue;\n+\n+    gchar **partitions;\n+    int partitions_length;\n+\n+    struct {\n+        gchar *partition;\n+        int progress;\n+        gchar *message;\n+        int partition_current;\n+        int partition_total;\n+    } status;\n+};\n+\n+void upgrade_factory_snapshot_init(struct UpgradeFactorySnapshot *ufs);\n+void upgrade_factory_snapshot_run(struct UpgradeFactorySnapshot *ufs);\n+void upgrade_factory_snapshot_quit(struct UpgradeFactorySnapshot *ufs);\n+void upgrade_factory_snapshot_broadcast_status(struct UpgradeFactorySnapshot *ufs,\n+        const char *partition, int progress, const char *message);\n+void upgrade_factory_snapshot_schedule_quit(struct UpgradeFactorySnapshot *ufs);\n+void upgrade_factory_snapshot_set_running(struct UpgradeFactorySnapshot *ufs, gboolean running);\n+\n+// Callbacks\n+void upgrade_factory_snapshot_bus_acquired_cb(GDBusConnection *connection, const gchar *name, gpointer user_data);\n+void upgrade_factory_snapshot_name_acquired_cb(GDBusConnection *connection, const gchar *name, gpointer user_data);\n+void upgrade_factory_snapshot_name_lost_cb(GDBusConnection *connection, const gchar *name, gpointer user_data);\n+void upgrade_factory_snapshot_cleanup_cb(void *user_data);\n+gboolean upgrade_factory_snapshot_do_next_entry_cb(gpointer user_data);\n+gboolean upgrade_factory_snapshot_delayed_finish_cb(gpointer user_data);\n+void upgrade_factory_snapshot_finished_cb(struct DeployTaskQueue *queue, void *user_data);\n+void upgrade_factory_snapshot_task_done_cb(struct DeployTaskQueue *queue, void *user_data);\n+void upgrade_factory_snapshot_dbus_signal_cb(GDBusConnection *connection,\n+        const gchar *sender_name, const gchar *object_path, const gchar *interface_name,\n+        const gchar *signal_name, GVariant *parameters, gpointer user_data);\n+void upgrade_factory_snapshot_method_call_cb(GDBusConnection *connection, const gchar *sender,\n+        const gchar *object_path, const gchar *interface_name, const gchar *method_name,\n+        GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data);\n+GVariant *upgrade_factory_snapshot_get_property_cb(GDBusConnection *connection, const gchar *sender,\n+        const gchar *object_path, const gchar *interface_name, const gchar *property_name,\n+        GError **error, gpointer user_data);\n+gboolean upgrade_factory_snapshot_set_property_cb(GDBusConnection *connection, const gchar *sender,\n+        const gchar *object_path, const gchar *interface_name, const gchar *property_name,\n+        GVariant *value, GError **error, gpointer user_data);\n+\n+\n+static gchar *cmd_list[] = { SAILFISH_SNAPSHOT, \"list\", NULL };\n+static gchar *cmd_delete_snapshot[] = { SAILFISH_SNAPSHOT, \"delete\", SNAPSHOT_NAME, NULL };\n+static gchar *cmd_deploy[] = { SAILFISH_SNAPSHOT, \"deploy\", SFMF_DEPLOY, SNAPSHOT_NAME, NULL };\n+static gchar *cmd_delete_factory_rename[] = { SAILFISH_SNAPSHOT, \"delete\", FACTORY_RENAME, NULL };\n+static gchar *cmd_rename_factory[] = { SAILFISH_SNAPSHOT, \"rename\", FACTORY_NAME, FACTORY_RENAME, NULL };\n+static gchar *cmd_rename_snapshot[] = { SAILFISH_SNAPSHOT, \"rename\", SNAPSHOT_NAME, FACTORY_NAME, NULL };\n+static gchar *sbj_partitions[] = { \"@\", \"@home\", NULL };\n+\n+static struct DeployTask g_deploy_tasks[] = {\n+    { \"Checking for existing snapshots\", cmd_list, 1 },\n+    { \"Removing temporary snapshot\", cmd_delete_snapshot, 0 },\n+    { \"Deploying new factory snapshot\", cmd_deploy, 1 },\n+    { \"Removing renamed factory snapshot\", cmd_delete_factory_rename, 0 },\n+    { \"Renaming factory snapshot\", cmd_rename_factory, 1 },\n+    { \"Activating new factory snapshot\", cmd_rename_snapshot, 1 },\n+    { \"Removing renamed factory snapshot\", cmd_delete_factory_rename, 0 },\n+    { \"Enumerating snapshots\", cmd_list, 0 },\n+    { NULL, NULL, 0 },\n+};\n+\n+static struct DeployTaskQueue g_deploy_queue = {\n+    \"deploy\",\n+\n+    g_deploy_tasks,\n+    -1,\n+    -1,\n+    0,\n+\n+    upgrade_factory_snapshot_finished_cb,\n+    upgrade_factory_snapshot_task_done_cb,\n+    NULL,\n+};\n+\n+static struct DeployTask g_cleanup_tasks[] = {\n+    { \"Deleting temporary snapshot\", cmd_delete_snapshot, 0 },\n+    { \"Enumerating snapshots\", cmd_list, 0 },\n+    { NULL, NULL, 0 },\n+};\n+\n+static struct DeployTaskQueue g_cleanup_queue = {\n+    \"cleanup\",\n+\n+    g_cleanup_tasks,\n+    -1,\n+    -1,\n+    0,\n+\n+    NULL,\n+    NULL,\n+    NULL,\n+};\n+\n+static struct UpgradeFactorySnapshot g_ufs = {\n+    NULL,\n+    NULL,\n+    0,\n+    0,\n+    0,\n+\n+    FALSE,\n+\n+    &g_deploy_queue,\n+    &g_cleanup_queue,\n+\n+    sbj_partitions,\n+    -1,\n+\n+    { NULL, 0, NULL, 0, 0 },\n+};\n+\n+static const GDBusInterfaceVTable upgrade_factory_snapshot_vtable = {\n+    upgrade_factory_snapshot_method_call_cb,\n+    upgrade_factory_snapshot_get_property_cb,\n+    upgrade_factory_snapshot_set_property_cb,\n+};\n+\n+\n+void deploy_task_handle_exit_status(struct DeployTask *task, int exit_status)\n+{\n+    GError *error = NULL;\n+    if (!g_spawn_check_exit_status(exit_status, &error)) {\n+        if (task->checked) {\n+            SFMF_FAIL(\"Failed to run command: %s\\n\", error->message);\n+        } else {\n+            SFMF_WARN(\"Failure (ignored): %s\\n\", error->message);\n+        }\n+        g_error_free(error);\n+    }\n+}\n+\n+struct DeployTask *deploy_task_queue_get_current_task(struct DeployTaskQueue *queue)\n+{\n+    if (queue->current >= 0) {\n+        return &(queue->tasks[queue->current]);\n+    }\n+\n+    return NULL;\n+}\n+\n+void deploy_task_queue_run_sync(struct DeployTaskQueue *queue)\n+{\n+    struct DeployTask *task = deploy_task_queue_get_current_task(queue);\n+\n+    if (!task) {\n+        SFMF_FAIL(\"Current task is invalid\\n\");\n+    }\n+\n+    gint exit_status = 0;\n+    GError *error = NULL;\n+    if (g_spawn_sync(NULL, task->cmd, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, &exit_status, &error)) {\n+        deploy_task_handle_exit_status(task, exit_status);\n+    } else {\n+        SFMF_FAIL(\"Failed to run command: %s\\n\", error->message);\n+        g_error_free(error);\n+    }\n+}\n+\n+void deploy_task_queue_on_subprocess_finished_cb(GPid pid, gint status, gpointer user_data)\n+{\n+    struct DeployTaskQueue *queue = user_data;\n+    struct DeployTask *task = deploy_task_queue_get_current_task(queue);\n+\n+    if (!task) {\n+        SFMF_FAIL(\"Current task is invalid\\n\");\n+    }\n+","path":"src/tools/upgrade-factory-snapshot.c","position":238,"original_position":257,"commit_id":"0ebb0e8b7700363cc2dd560d3a479523e9d9624f","original_commit_id":"5bbe01e262222fe0d29eb48edfa0dbc512d4309b","user":{"login":"thp","id":135241,"avatar_url":"https://avatars.githubusercontent.com/u/135241?v=3","gravatar_id":"","url":"https://api.github.com/users/thp","html_url":"https://github.com/thp","followers_url":"https://api.github.com/users/thp/followers","following_url":"https://api.github.com/users/thp/following{/other_user}","gists_url":"https://api.github.com/users/thp/gists{/gist_id}","starred_url":"https://api.github.com/users/thp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thp/subscriptions","organizations_url":"https://api.github.com/users/thp/orgs","repos_url":"https://api.github.com/users/thp/repos","events_url":"https://api.github.com/users/thp/events{/privacy}","received_events_url":"https://api.github.com/users/thp/received_events","type":"User","site_admin":false},"body":"Will fix that as part of whatever we decide to do with `SFMF_FAIL`","created_at":"2015-05-05T12:30:32Z","updated_at":"2015-05-05T12:30:32Z","html_url":"https://github.com/sailfishos/sfmf/pull/1#discussion_r29663683","pull_request_url":"https://api.github.com/repos/sailfishos/sfmf/pulls/1","_links":{"self":{"href":"https://api.github.com/repos/sailfishos/sfmf/pulls/comments/29663683"},"html":{"href":"https://github.com/sailfishos/sfmf/pull/1#discussion_r29663683"},"pull_request":{"href":"https://api.github.com/repos/sailfishos/sfmf/pulls/1"}}},"pull_request":{"url":"https://api.github.com/repos/sailfishos/sfmf/pulls/1","id":32322138,"html_url":"https://github.com/sailfishos/sfmf/pull/1","diff_url":"https://github.com/sailfishos/sfmf/pull/1.diff","patch_url":"https://github.com/sailfishos/sfmf/pull/1.patch","issue_url":"https://api.github.com/repos/sailfishos/sfmf/issues/1","number":1,"state":"open","locked":false,"title":"D-Bus support","user":{"login":"thp","id":135241,"avatar_url":"https://avatars.githubusercontent.com/u/135241?v=3","gravatar_id":"","url":"https://api.github.com/users/thp","html_url":"https://github.com/thp","followers_url":"https://api.github.com/users/thp/followers","following_url":"https://api.github.com/users/thp/following{/other_user}","gists_url":"https://api.github.com/users/thp/gists{/gist_id}","starred_url":"https://api.github.com/users/thp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thp/subscriptions","organizations_url":"https://api.github.com/users/thp/orgs","repos_url":"https://api.github.com/users/thp/repos","events_url":"https://api.github.com/users/thp/events{/privacy}","received_events_url":"https://api.github.com/users/thp/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-03-31T13:31:12Z","updated_at":"2015-05-05T12:30:32Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e127b1a6d11f4668af33620056aa3d460c05c8c1","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/sailfishos/sfmf/pulls/1/commits","review_comments_url":"https://api.github.com/repos/sailfishos/sfmf/pulls/1/comments","review_comment_url":"https://api.github.com/repos/sailfishos/sfmf/pulls/comments{/number}","comments_url":"https://api.github.com/repos/sailfishos/sfmf/issues/1/comments","statuses_url":"https://api.github.com/repos/sailfishos/sfmf/statuses/0ebb0e8b7700363cc2dd560d3a479523e9d9624f","head":{"label":"thp:dbus","ref":"dbus","sha":"0ebb0e8b7700363cc2dd560d3a479523e9d9624f","user":{"login":"thp","id":135241,"avatar_url":"https://avatars.githubusercontent.com/u/135241?v=3","gravatar_id":"","url":"https://api.github.com/users/thp","html_url":"https://github.com/thp","followers_url":"https://api.github.com/users/thp/followers","following_url":"https://api.github.com/users/thp/following{/other_user}","gists_url":"https://api.github.com/users/thp/gists{/gist_id}","starred_url":"https://api.github.com/users/thp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thp/subscriptions","organizations_url":"https://api.github.com/users/thp/orgs","repos_url":"https://api.github.com/users/thp/repos","events_url":"https://api.github.com/users/thp/events{/privacy}","received_events_url":"https://api.github.com/users/thp/received_events","type":"User","site_admin":false},"repo":{"id":33187033,"name":"sfmf","full_name":"thp/sfmf","owner":{"login":"thp","id":135241,"avatar_url":"https://avatars.githubusercontent.com/u/135241?v=3","gravatar_id":"","url":"https://api.github.com/users/thp","html_url":"https://github.com/thp","followers_url":"https://api.github.com/users/thp/followers","following_url":"https://api.github.com/users/thp/following{/other_user}","gists_url":"https://api.github.com/users/thp/gists{/gist_id}","starred_url":"https://api.github.com/users/thp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thp/subscriptions","organizations_url":"https://api.github.com/users/thp/orgs","repos_url":"https://api.github.com/users/thp/repos","events_url":"https://api.github.com/users/thp/events{/privacy}","received_events_url":"https://api.github.com/users/thp/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/thp/sfmf","description":"Sailfish OS Factory Snapshot Update","fork":true,"url":"https://api.github.com/repos/thp/sfmf","forks_url":"https://api.github.com/repos/thp/sfmf/forks","keys_url":"https://api.github.com/repos/thp/sfmf/keys{/key_id}","collaborators_url":"https://api.github.com/repos/thp/sfmf/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/thp/sfmf/teams","hooks_url":"https://api.github.com/repos/thp/sfmf/hooks","issue_events_url":"https://api.github.com/repos/thp/sfmf/issues/events{/number}","events_url":"https://api.github.com/repos/thp/sfmf/events","assignees_url":"https://api.github.com/repos/thp/sfmf/assignees{/user}","branches_url":"https://api.github.com/repos/thp/sfmf/branches{/branch}","tags_url":"https://api.github.com/repos/thp/sfmf/tags","blobs_url":"https://api.github.com/repos/thp/sfmf/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/thp/sfmf/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/thp/sfmf/git/refs{/sha}","trees_url":"https://api.github.com/repos/thp/sfmf/git/trees{/sha}","statuses_url":"https://api.github.com/repos/thp/sfmf/statuses/{sha}","languages_url":"https://api.github.com/repos/thp/sfmf/languages","stargazers_url":"https://api.github.com/repos/thp/sfmf/stargazers","contributors_url":"https://api.github.com/repos/thp/sfmf/contributors","subscribers_url":"https://api.github.com/repos/thp/sfmf/subscribers","subscription_url":"https://api.github.com/repos/thp/sfmf/subscription","commits_url":"https://api.github.com/repos/thp/sfmf/commits{/sha}","git_commits_url":"https://api.github.com/repos/thp/sfmf/git/commits{/sha}","comments_url":"https://api.github.com/repos/thp/sfmf/comments{/number}","issue_comment_url":"https://api.github.com/repos/thp/sfmf/issues/comments{/number}","contents_url":"https://api.github.com/repos/thp/sfmf/contents/{+path}","compare_url":"https://api.github.com/repos/thp/sfmf/compare/{base}...{head}","merges_url":"https://api.github.com/repos/thp/sfmf/merges","archive_url":"https://api.github.com/repos/thp/sfmf/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/thp/sfmf/downloads","issues_url":"https://api.github.com/repos/thp/sfmf/issues{/number}","pulls_url":"https://api.github.com/repos/thp/sfmf/pulls{/number}","milestones_url":"https://api.github.com/repos/thp/sfmf/milestones{/number}","notifications_url":"https://api.github.com/repos/thp/sfmf/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/thp/sfmf/labels{/name}","releases_url":"https://api.github.com/repos/thp/sfmf/releases{/id}","created_at":"2015-03-31T13:30:35Z","updated_at":"2015-03-31T13:30:35Z","pushed_at":"2015-05-05T12:08:55Z","git_url":"git://github.com/thp/sfmf.git","ssh_url":"git@github.com:thp/sfmf.git","clone_url":"https://github.com/thp/sfmf.git","svn_url":"https://github.com/thp/sfmf","homepage":null,"size":184,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":true,"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":"sailfishos:master","ref":"master","sha":"3c672f300cb3b5d9ab34361022d03ea1df637f17","user":{"login":"sailfishos","id":6198659,"avatar_url":"https://avatars.githubusercontent.com/u/6198659?v=3","gravatar_id":"","url":"https://api.github.com/users/sailfishos","html_url":"https://github.com/sailfishos","followers_url":"https://api.github.com/users/sailfishos/followers","following_url":"https://api.github.com/users/sailfishos/following{/other_user}","gists_url":"https://api.github.com/users/sailfishos/gists{/gist_id}","starred_url":"https://api.github.com/users/sailfishos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sailfishos/subscriptions","organizations_url":"https://api.github.com/users/sailfishos/orgs","repos_url":"https://api.github.com/users/sailfishos/repos","events_url":"https://api.github.com/users/sailfishos/events{/privacy}","received_events_url":"https://api.github.com/users/sailfishos/received_events","type":"Organization","site_admin":false},"repo":{"id":31022024,"name":"sfmf","full_name":"sailfishos/sfmf","owner":{"login":"sailfishos","id":6198659,"avatar_url":"https://avatars.githubusercontent.com/u/6198659?v=3","gravatar_id":"","url":"https://api.github.com/users/sailfishos","html_url":"https://github.com/sailfishos","followers_url":"https://api.github.com/users/sailfishos/followers","following_url":"https://api.github.com/users/sailfishos/following{/other_user}","gists_url":"https://api.github.com/users/sailfishos/gists{/gist_id}","starred_url":"https://api.github.com/users/sailfishos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sailfishos/subscriptions","organizations_url":"https://api.github.com/users/sailfishos/orgs","repos_url":"https://api.github.com/users/sailfishos/repos","events_url":"https://api.github.com/users/sailfishos/events{/privacy}","received_events_url":"https://api.github.com/users/sailfishos/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/sailfishos/sfmf","description":"Sailfish OS Factory Snapshot Update","fork":false,"url":"https://api.github.com/repos/sailfishos/sfmf","forks_url":"https://api.github.com/repos/sailfishos/sfmf/forks","keys_url":"https://api.github.com/repos/sailfishos/sfmf/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sailfishos/sfmf/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sailfishos/sfmf/teams","hooks_url":"https://api.github.com/repos/sailfishos/sfmf/hooks","issue_events_url":"https://api.github.com/repos/sailfishos/sfmf/issues/events{/number}","events_url":"https://api.github.com/repos/sailfishos/sfmf/events","assignees_url":"https://api.github.com/repos/sailfishos/sfmf/assignees{/user}","branches_url":"https://api.github.com/repos/sailfishos/sfmf/branches{/branch}","tags_url":"https://api.github.com/repos/sailfishos/sfmf/tags","blobs_url":"https://api.github.com/repos/sailfishos/sfmf/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sailfishos/sfmf/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sailfishos/sfmf/git/refs{/sha}","trees_url":"https://api.github.com/repos/sailfishos/sfmf/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sailfishos/sfmf/statuses/{sha}","languages_url":"https://api.github.com/repos/sailfishos/sfmf/languages","stargazers_url":"https://api.github.com/repos/sailfishos/sfmf/stargazers","contributors_url":"https://api.github.com/repos/sailfishos/sfmf/contributors","subscribers_url":"https://api.github.com/repos/sailfishos/sfmf/subscribers","subscription_url":"https://api.github.com/repos/sailfishos/sfmf/subscription","commits_url":"https://api.github.com/repos/sailfishos/sfmf/commits{/sha}","git_commits_url":"https://api.github.com/repos/sailfishos/sfmf/git/commits{/sha}","comments_url":"https://api.github.com/repos/sailfishos/sfmf/comments{/number}","issue_comment_url":"https://api.github.com/repos/sailfishos/sfmf/issues/comments{/number}","contents_url":"https://api.github.com/repos/sailfishos/sfmf/contents/{+path}","compare_url":"https://api.github.com/repos/sailfishos/sfmf/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sailfishos/sfmf/merges","archive_url":"https://api.github.com/repos/sailfishos/sfmf/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sailfishos/sfmf/downloads","issues_url":"https://api.github.com/repos/sailfishos/sfmf/issues{/number}","pulls_url":"https://api.github.com/repos/sailfishos/sfmf/pulls{/number}","milestones_url":"https://api.github.com/repos/sailfishos/sfmf/milestones{/number}","notifications_url":"https://api.github.com/repos/sailfishos/sfmf/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sailfishos/sfmf/labels{/name}","releases_url":"https://api.github.com/repos/sailfishos/sfmf/releases{/id}","created_at":"2015-02-19T15:37:16Z","updated_at":"2015-04-18T12:09:40Z","pushed_at":"2015-05-05T12:08:59Z","git_url":"git://github.com/sailfishos/sfmf.git","ssh_url":"git@github.com:sailfishos/sfmf.git","clone_url":"https://github.com/sailfishos/sfmf.git","svn_url":"https://github.com/sailfishos/sfmf","homepage":null,"size":243,"stargazers_count":2,"watchers_count":2,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":2,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/sailfishos/sfmf/pulls/1"},"html":{"href":"https://github.com/sailfishos/sfmf/pull/1"},"issue":{"href":"https://api.github.com/repos/sailfishos/sfmf/issues/1"},"comments":{"href":"https://api.github.com/repos/sailfishos/sfmf/issues/1/comments"},"review_comments":{"href":"https://api.github.com/repos/sailfishos/sfmf/pulls/1/comments"},"review_comment":{"href":"https://api.github.com/repos/sailfishos/sfmf/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/sailfishos/sfmf/pulls/1/commits"},"statuses":{"href":"https://api.github.com/repos/sailfishos/sfmf/statuses/0ebb0e8b7700363cc2dd560d3a479523e9d9624f"}}}}