{"action":"created","comment":{"url":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/comments/82544903","pull_request_review_id":3444362,"id":82544903,"diff_hunk":"@@ -1,55 +1,286 @@\n-# Proposal for meta/run.yaml content being present in the composed image\n+# Proposal for OSv image RUN being compatible with OpenStack\n \n-Currently, Capstan runtime support makes use of `$CWD/meta/run.yaml` for composing and running an OSv image. Although this is fine for the composing, it gets tricky for running because `capstan run <IMAGE>` generally does not require user to be inside package root directory.\n+### Problem description\n+Currently, Capstan runtime support makes use of `$CWD/meta/run.yaml` for composing (COMPOSE) and\n+running (RUN) an OSv image. Although this is fine for the composing, it gets tricky for running\n+because `capstan run <IMAGE>` generally does not require user to be inside package root directory.\n+\n+Furthermore, you currently need Capstan if you want to pick a desired named configuration to RUN\n+pre-composed OSv image with. That's because capstan is able to:\n+* understand your meta/run.yaml\n+* generate run command from it\n+* set run command to the existing OSv image\n+\n+Other infrastructures (OpenStack, Amazon) are unable to perform first and second bullet, but can do\n+the third one.\n+\n+### Executive summary\n+This proposal is about what actions will Capstan perform on COMPOSE time based on user-friendly\n+input file that is provided in `$CWD/meta/run.yaml`. The format of this file was decided [here](https://github.com/mikelangelo-project/capstan/issues/3#issuecomment-245210949) and is not to be changed much with this proposal\n+(values starting with $ will be parsed as placeholder name, that's the only difference).\n+\n+The idea is that Capstan would prepare everything in COMPOSE time so that the first and second bullet\n+(see Problem description section) would not be needed at the RUN time. This way OpenStack would be\n+able to make use of named runtimes by not even having Capstan installed.\n \n I propose the following solution:\n \n+### User aspect\n+OSv packaging with runtime support ends up with four user roles:\n+\n+##### Application user\n+Downloads a pre-composed image and RUN it without modifications. She uses EXTERNAL packages and\n+EXTERNAL application. She does not perform COMPOSE step. She must be provided with this information:\n+ * what configuration names are available\n+ * what parameters are needed for each configuration name\n+\n+Example: Hdfs. User downloads it and runs it with customized parameters.\n+\n+##### Runtime user\n+Downloads pre-prepared Capstan packages. She uses EXTERNAL packages and INTERNAL application. She\n+needs to provide the `meta/run.yaml` file. There she needs to pick one of the supported runtimes that suites her best. Then she needs to either provide runtime-specific values either define placeholder\n+names for them (explained in following sections). She creates arbitrary number of configurations and\n+names them.\n+\n+To successfully perform the COMPOSE step, she must be provided with this information:\n+ * what runtimes are available\n+ * what runtime-specific attributes are supported\n+\n+Once she successfully composes the image, she takes the role of Application user.\n+\n+Example: NodeJS. User uses 'node' runtime (which downloads pre-prepared node package), but provides\n+her own NodeJS JavaScript application code.\n+\n+##### Runtime developer\n+Extends Capstan tool by implementing runtime.Runtime interface (Golang programmer). She must be\n+provided with this information:\n+ * what packages are available\n+ * how to use package\n+\n+It must be as easy as possible to add new runtime since we may expect PRs for new runtimes!\n+\n+QUICK THOUGHT: Is it really neccessary that runtime is hard-coded in Capstan? Would it be better to\n+support runtimes written in yaml and included in package itself? Not sure this is possible, though.\n+\n+##### Package developer\n+Statically compiles new languages from source into shared objects and bundles them into Capstan\n+package.\n+\n+NOTE: There is a mess with the hierarchy of packages. Would it make sense to introduce \"package type\"?\n+We could have:\n+ * *shared-object*: Packages that only provide shared objects\n+ * *runtime*: Packages that provide runtime (may require multiple *shared-object*). These are\n+ packages that doesn't work per-se; user needs to provide application code to compose with.\n+ * *application*: Packages that user should just download, compose without modifications and run.\n+    Only for this packages it's possible to provide the already-compiled image. These packages must\n+    make use of Agent functionality.\n+\n+Role 'Package developer' is the one that prepares *shared-object* packages, role 'Runtime developer'\n+prepares *runtime* packages and role 'Runtime user' prepares *application* packages. Role\n+'Application user' depends on Runtime user to publish their application.\n+\n+\n+\n ### Composing image\n-File meta/run.yaml is present at package root directory (same as until now)\n+When user runs `capstan package compose`, the content of meta/run.yaml file is processed and new\n+files are generated and uploaded to the OSv image (assume we upload them to folder `/osv-runtime`):\n \n-When user runs `capstan package compose` the content of meta/run.yaml file is processed and new files are generated inside the OSv image folder `/osv-runtime`:\n+```\n+user-friendly attributes of meta/run.yaml ---> complex run command for each named config\n+```\n \n-Capstan should create a separate file for each named configuration e.g.\n+A separate file containing only run command string is created for each named configuration e.g.\n ```\n /osv-runtime\n    |--burek\n-   |--pizza   <--------\\\n-   |                    |\n-   |--default-runtime  /\n+   |--pizza\n ```\n-Each runtime file should contain only one line representing the run command that is to be run inside OSv:\n+Example:\n ```\n # content of file /osv-runtime/burek:\n node burek.js\n ```\n+It is neccessary to use Capstan to generate run command since runtime user does not have enough\n+information to generate it on her own (e.g. what binariy to call, what flags to use). The idea\n+is that runtime user provides application specific data (e.g. what is her main file) and the rest is\n+automatized.\n+\n+#### Placeholders\n+Generation of capstan run command will be updated with placeholder capability.\n+If a *value* in meta/run.yaml will start with dollar sign *$* it will be interpreted as placeholder\n+name. E.g.\n+```yaml\n+# meta/run.yaml\n+\n+runtime: node\n+\n+main: app.js\n+my_ip: $IP\n+output_dir: $THE_OUTPUT_DIR\n+```\n+In the example above, Capstan would parse $IP and $THE_OUTPUT_DIR as placeholder names and insert\n+them into run command as `___<placeholder-name>___`. Lets suppose that\n+node runtime builds runtime command with the following pattern:\n+```\n+node <main> -ip <my_ip> -out <output_dir>\n+```\n+\n+Then the run command for our example above would be:\n+```\n+node app.js -ip ___IP___ -out ___THE_OUTPUT_DIR___\n+```\n+OSv Agent would then replace these placeholders with actual values just before the run command would\n+be run.\n \n-When user runs `capstan package compose` the boot command is set so that the agent is run. A capstan package must be implemented that provides agent funcionality.\n \n #### OSv Agent\n-Agent reads *cloud-init* file. Based on cloud-init data it spawns a thread with appropriate run command.\n+A Capstan package must be implemented that provides Agent funcionality.\n+Agent is the first application to be run when OSv instance starts, therefore the OSv boot command\n+should look like:\n+```\n+agent run <name-of-config> [<key-value>, <key-value>, ...]\n \n-TODO: Agent should be able to do more than just pick runtime! Since cloud-init script could contain some runtime-specific settings, say master-IP, and agent should be able to use them. At least it should be able to replace placeholders within run command. Must be careful, though, that agent needn't be adjusted when new runtime is added!\n+Example:\n+agent run pizza IP=192.168.0.55 THE_OUTPUT_DIR=/etc/out\n+```\n+Agent reads run command from `/osv-runtime/<name-of-config>` and replaces placeholders with the\n+values obtained as arguments.\n+\n+*CONCERN #1*: How to deal with optional arguments (they might or might not be provided)?<br>\n+SOLUTION: We don't! User can provide a separate named configuration for each set of arguments, all of\n+which are required.\n+\n+*CONCERN #2*: How to deal with non-string arguments e.g. list?<br>\n+SOLUTION: We don't! Runtime should be written in a way that list could be provided as a\n+comma-separated string. Remember, we are building a string here!\n+\n+*CONCERN #3*: What if runtime needs some changes to be made inside OSv prior running? E.g. Java runtime\n+creates file /java/mains with specific content.<br>\n+SOLUTION: This is where Agent complexity could explode. We should therefore prohibit such\n+requirement of a runtime. For Java we could write a small program (and include it in\n+the Java package) that would prepare these files for us. Then we would extend run command for Java\n+runtime like this:\n+```\n+Before (expected files already present):\n+java <args>\n+\n+Then (nothing expected to be done instead of us)\n+prepare-files <args_prepare> && java <args>\n+```\n+Naturally, same placeholders could be used both in args and args_prepare.\n \n-#### Default runtime\n-Symbolic link named `default-runtime` should be present in /osv-runtime folder. It should point to the runtime file that should be used if no runconfig is present in cloud-init.\n+*CONCERN #4*: OK, we handle runtime-specific placeholders. What about application-specific ones? E.g.\n+if our NodeJS application needs to have `mongo_database_url` to be set, which is not part of node\n+runtime parameters?<br>\n+SOLUTION: We should prohibit application-specific arguments for run command. Instead, application\n+should use environment variables for application-specific settings. To support this, we introduce a\n+new attribute, `env_vars` (a list of required environment variable names) for each named configuration. E.g.\n+```yaml\n+# meta/run.yaml\n \n+runtime: node\n+\n+main: app.js\n+my_ip: $IP\n+output_dir: $THE_OUTPUT_DIR\n+env_vars:  <------------------------ the new attribute\n+ - MONGO_DB_URL\n+```\n+Capstan would then prepend `--env key=value` option during run command creation where the value\n+would be a placeholder. Run command for the example above would then become:\n+```\n+--env MONGO_DB_URL=___MONGO_DB_URL___ node app.js ...\n+|----------- the new part -----------|\n+```\n+Agent would remain unchanged and would be run exactly same as before:\n+```\n+agent run <name-of-config> [<key-value>, <key-value>, ...]\n+                            ----both: params and env ----\n+\n+Example:\n+agent run pizza IP=192.168.0.55 THE_OUTPUT_DIR=/etc/out MONGO_DB_URL=my.mongo.local\n+                |----------- params ------------------| |-------- env ------------|\n+```\n \n ### Running image\n-With changes above, Capstan shouldn't be required to run image. OpenStack could run it with arbitrary hypervisor as long as it provides cloud-init.\n+When user decides to **RUN** an OSv image, she will have to set a proper boot command. This will be\n+done using `bootcmd` cloud-init [property](https://cloudinit.readthedocs.io/en/latest/topics/examples.html#run-commands-on-first-boot). The boot command will be as simple as:\n+```yaml\n+# cloud-init\n+bootcmd: agent run <name-of-config> [<key-value>, <key-value>, ...]\n+```\n+Although the boot command is simple enough to be written manually by user (and it requires no other\n+arguments but the placeholder values) it will probably be written by Heat or Murano.\n \n-The `capstan run` command could therefore be changed. Parameter --runconfig would result in init-file be created and passed to the OSv while run command of the composed image should remain untouched (i.e. Agent will be run).\n+SIDE NOTE: We cannot rely on boot command to be set on COMPOSE time since it can easily be overwritten by\n+mistake e.g. when sysadmin tests it prior uploading. This is why we can not implement an OSv Agent\n+that would be run 'by default' and wait on configuration name to be passed later. Therefore this\n+proposal introduces an OSv Agent that is fully defined by boot command (all parameters are there).\n+\n+#### Image independency\n+With changes above, Capstan shouldn't be required to run image. OpenStack could run it with\n+arbitrary hypervisor as long as it provides cloud-init support.\n+\n+#### Running image locally\n+User provides cloud-init file for the new instance, but it needs to be served somehow.\n+OpenStack automates cloud-init paradigm execution by using EC2 mechanism, namely it provides a\n+\"dummy\" IP and service from where the guest OS can get the information from the cloud-init file.\n+\n+However, if we are to support a local environment using the same cloud-init approach, it needs to\n+get the data somehow. Since Captan invokes QEMU, the only viable option is to attach a second volume\n+to it.\n+\n+Although attaching the second volume locally might be done using `file2img` script from\n+[this](https://github.com/cloudius-systems/osv/commit/56c6195) commit, there is no need for this.\n+Capstan is able to set the boot command without the cloud-init support. So, to run image locally, one\n+would use:\n+```\n+capstan run --execute \"agent run <name-of-config> [<key-value>, <key-value>, ...]\"\n+\n+Example:\n+capstan run --execute \"agent run pizza IP=192.168.0.55 THE_OUTPUT_DIR=/etc/out MONGO_DB_URL=my.mongo.local\"\n+```\n+Note that --execute argument is exactly the same as the bootcmd int the cloud-init.\n \n \n ### Accessing description of a composed image\n+When user downloads an already composed OSv image, she is not aware of what configuration names are\n+used and what parameters are required for them. It must be possible to obtain this information from\n+the image.\n \n-It will be possible to print runtime options of a composed image using capstan:\n+I propose the following solution:\n+\n+### Result\n+\n+OSv Agent should support some additional commands. `list` command would print all available named\n+configurations into OSv console:\n+```\n+$ agent list\n+\n+Available configuration names:\n+burek\n+pizza\n ```\n-$ capstan runtime info mike/my-app\n+Then `describe` command would print detailed information about the provided configuration name.\n ```\n-This command will print all the available config_set names and details only by using data from the image itself (as opposed to reading meta/run.yaml from the current directory on the host).\n+$ agent describe <name-of-config>\n+```\n+With the command above user would get enough information to be able to provide complete boot command.\n+\n+Commands `agent list` and `agent describe` could be set as boot command. This way user would be able\n+to use them directly on OpenStack. But ofcorse, we can simplify things for local environment by\n+implementing additional command for Capstan tool that would be setting the two boot commands for us\n+and only showed us result.\n+\n+### Implementation details\n+Printing list of named configurations is as simple as listing all filenames from `/osv-runtime`\n+folder.\n \n-#### Proposed solution\n-While composing, Capstan could store the original meta/run.yaml file into OSv image. Then the `capstan runtime info` command could access this file and print it.\n+But in OSv image there is currently no documentation data about named configurations stored. We could\n+fix this by upgrading Capstan: on COMPOSE time it would not only generate `/osv-runtime` files, but\n+also corresponding `/osv-runtime-docs` files. OSv agent would then simply read and print appropriate","path":"Documentation/proposal-runconfig_inside_osv.md","position":null,"original_position":302,"commit_id":"2c7631ac22a4d0477faef99aed2ddd9005cfc40a","original_commit_id":"2c7631ac22a4d0477faef99aed2ddd9005cfc40a","user":{"login":"gberginc","id":1437960,"avatar_url":"https://avatars.githubusercontent.com/u/1437960?v=3","gravatar_id":"","url":"https://api.github.com/users/gberginc","html_url":"https://github.com/gberginc","followers_url":"https://api.github.com/users/gberginc/followers","following_url":"https://api.github.com/users/gberginc/following{/other_user}","gists_url":"https://api.github.com/users/gberginc/gists{/gist_id}","starred_url":"https://api.github.com/users/gberginc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gberginc/subscriptions","organizations_url":"https://api.github.com/users/gberginc/orgs","repos_url":"https://api.github.com/users/gberginc/repos","events_url":"https://api.github.com/users/gberginc/events{/privacy}","received_events_url":"https://api.github.com/users/gberginc/received_events","type":"User","site_admin":false},"body":"What if we make use of some `/usr/bin` for runtimes and `/usr/man` for the docs?","created_at":"2016-10-10T05:22:38Z","updated_at":"2016-10-10T05:23:45Z","html_url":"https://github.com/mikelangelo-project/capstan/pull/6#discussion_r82544903","pull_request_url":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/6","_links":{"self":{"href":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/comments/82544903"},"html":{"href":"https://github.com/mikelangelo-project/capstan/pull/6#discussion_r82544903"},"pull_request":{"href":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/6"}}},"pull_request":{"url":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/6","id":88318852,"html_url":"https://github.com/mikelangelo-project/capstan/pull/6","diff_url":"https://github.com/mikelangelo-project/capstan/pull/6.diff","patch_url":"https://github.com/mikelangelo-project/capstan/pull/6.patch","issue_url":"https://api.github.com/repos/mikelangelo-project/capstan/issues/6","number":6,"state":"open","locked":false,"title":"Proposal for meta/run.yaml content being present in the composed image","user":{"login":"miha-plesko","id":8102426,"avatar_url":"https://avatars.githubusercontent.com/u/8102426?v=3","gravatar_id":"","url":"https://api.github.com/users/miha-plesko","html_url":"https://github.com/miha-plesko","followers_url":"https://api.github.com/users/miha-plesko/followers","following_url":"https://api.github.com/users/miha-plesko/following{/other_user}","gists_url":"https://api.github.com/users/miha-plesko/gists{/gist_id}","starred_url":"https://api.github.com/users/miha-plesko/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/miha-plesko/subscriptions","organizations_url":"https://api.github.com/users/miha-plesko/orgs","repos_url":"https://api.github.com/users/miha-plesko/repos","events_url":"https://api.github.com/users/miha-plesko/events{/privacy}","received_events_url":"https://api.github.com/users/miha-plesko/received_events","type":"User","site_admin":false},"body":"Signed-off-by: Miha Pleško <miha.plesko@xlab.si>","created_at":"2016-10-06T18:46:44Z","updated_at":"2016-10-10T05:23:45Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e1948f01f9448568c5dea406f8df3994946805ef","assignee":{"login":"gberginc","id":1437960,"avatar_url":"https://avatars.githubusercontent.com/u/1437960?v=3","gravatar_id":"","url":"https://api.github.com/users/gberginc","html_url":"https://github.com/gberginc","followers_url":"https://api.github.com/users/gberginc/followers","following_url":"https://api.github.com/users/gberginc/following{/other_user}","gists_url":"https://api.github.com/users/gberginc/gists{/gist_id}","starred_url":"https://api.github.com/users/gberginc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gberginc/subscriptions","organizations_url":"https://api.github.com/users/gberginc/orgs","repos_url":"https://api.github.com/users/gberginc/repos","events_url":"https://api.github.com/users/gberginc/events{/privacy}","received_events_url":"https://api.github.com/users/gberginc/received_events","type":"User","site_admin":false},"assignees":[{"login":"gberginc","id":1437960,"avatar_url":"https://avatars.githubusercontent.com/u/1437960?v=3","gravatar_id":"","url":"https://api.github.com/users/gberginc","html_url":"https://github.com/gberginc","followers_url":"https://api.github.com/users/gberginc/followers","following_url":"https://api.github.com/users/gberginc/following{/other_user}","gists_url":"https://api.github.com/users/gberginc/gists{/gist_id}","starred_url":"https://api.github.com/users/gberginc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gberginc/subscriptions","organizations_url":"https://api.github.com/users/gberginc/orgs","repos_url":"https://api.github.com/users/gberginc/repos","events_url":"https://api.github.com/users/gberginc/events{/privacy}","received_events_url":"https://api.github.com/users/gberginc/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/6/commits","review_comments_url":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/6/comments","review_comment_url":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/comments{/number}","comments_url":"https://api.github.com/repos/mikelangelo-project/capstan/issues/6/comments","statuses_url":"https://api.github.com/repos/mikelangelo-project/capstan/statuses/2c7631ac22a4d0477faef99aed2ddd9005cfc40a","head":{"label":"miha-plesko:proposal-runconfig-inside-osv","ref":"proposal-runconfig-inside-osv","sha":"2c7631ac22a4d0477faef99aed2ddd9005cfc40a","user":{"login":"miha-plesko","id":8102426,"avatar_url":"https://avatars.githubusercontent.com/u/8102426?v=3","gravatar_id":"","url":"https://api.github.com/users/miha-plesko","html_url":"https://github.com/miha-plesko","followers_url":"https://api.github.com/users/miha-plesko/followers","following_url":"https://api.github.com/users/miha-plesko/following{/other_user}","gists_url":"https://api.github.com/users/miha-plesko/gists{/gist_id}","starred_url":"https://api.github.com/users/miha-plesko/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/miha-plesko/subscriptions","organizations_url":"https://api.github.com/users/miha-plesko/orgs","repos_url":"https://api.github.com/users/miha-plesko/repos","events_url":"https://api.github.com/users/miha-plesko/events{/privacy}","received_events_url":"https://api.github.com/users/miha-plesko/received_events","type":"User","site_admin":false},"repo":{"id":70180532,"name":"capstan","full_name":"miha-plesko/capstan","owner":{"login":"miha-plesko","id":8102426,"avatar_url":"https://avatars.githubusercontent.com/u/8102426?v=3","gravatar_id":"","url":"https://api.github.com/users/miha-plesko","html_url":"https://github.com/miha-plesko","followers_url":"https://api.github.com/users/miha-plesko/followers","following_url":"https://api.github.com/users/miha-plesko/following{/other_user}","gists_url":"https://api.github.com/users/miha-plesko/gists{/gist_id}","starred_url":"https://api.github.com/users/miha-plesko/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/miha-plesko/subscriptions","organizations_url":"https://api.github.com/users/miha-plesko/orgs","repos_url":"https://api.github.com/users/miha-plesko/repos","events_url":"https://api.github.com/users/miha-plesko/events{/privacy}","received_events_url":"https://api.github.com/users/miha-plesko/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/miha-plesko/capstan","description":"Capstan, a tool for packaging and running your application on OSv.","fork":true,"url":"https://api.github.com/repos/miha-plesko/capstan","forks_url":"https://api.github.com/repos/miha-plesko/capstan/forks","keys_url":"https://api.github.com/repos/miha-plesko/capstan/keys{/key_id}","collaborators_url":"https://api.github.com/repos/miha-plesko/capstan/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/miha-plesko/capstan/teams","hooks_url":"https://api.github.com/repos/miha-plesko/capstan/hooks","issue_events_url":"https://api.github.com/repos/miha-plesko/capstan/issues/events{/number}","events_url":"https://api.github.com/repos/miha-plesko/capstan/events","assignees_url":"https://api.github.com/repos/miha-plesko/capstan/assignees{/user}","branches_url":"https://api.github.com/repos/miha-plesko/capstan/branches{/branch}","tags_url":"https://api.github.com/repos/miha-plesko/capstan/tags","blobs_url":"https://api.github.com/repos/miha-plesko/capstan/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/miha-plesko/capstan/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/miha-plesko/capstan/git/refs{/sha}","trees_url":"https://api.github.com/repos/miha-plesko/capstan/git/trees{/sha}","statuses_url":"https://api.github.com/repos/miha-plesko/capstan/statuses/{sha}","languages_url":"https://api.github.com/repos/miha-plesko/capstan/languages","stargazers_url":"https://api.github.com/repos/miha-plesko/capstan/stargazers","contributors_url":"https://api.github.com/repos/miha-plesko/capstan/contributors","subscribers_url":"https://api.github.com/repos/miha-plesko/capstan/subscribers","subscription_url":"https://api.github.com/repos/miha-plesko/capstan/subscription","commits_url":"https://api.github.com/repos/miha-plesko/capstan/commits{/sha}","git_commits_url":"https://api.github.com/repos/miha-plesko/capstan/git/commits{/sha}","comments_url":"https://api.github.com/repos/miha-plesko/capstan/comments{/number}","issue_comment_url":"https://api.github.com/repos/miha-plesko/capstan/issues/comments{/number}","contents_url":"https://api.github.com/repos/miha-plesko/capstan/contents/{+path}","compare_url":"https://api.github.com/repos/miha-plesko/capstan/compare/{base}...{head}","merges_url":"https://api.github.com/repos/miha-plesko/capstan/merges","archive_url":"https://api.github.com/repos/miha-plesko/capstan/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/miha-plesko/capstan/downloads","issues_url":"https://api.github.com/repos/miha-plesko/capstan/issues{/number}","pulls_url":"https://api.github.com/repos/miha-plesko/capstan/pulls{/number}","milestones_url":"https://api.github.com/repos/miha-plesko/capstan/milestones{/number}","notifications_url":"https://api.github.com/repos/miha-plesko/capstan/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/miha-plesko/capstan/labels{/name}","releases_url":"https://api.github.com/repos/miha-plesko/capstan/releases{/id}","deployments_url":"https://api.github.com/repos/miha-plesko/capstan/deployments","created_at":"2016-10-06T18:16:12Z","updated_at":"2016-10-06T18:16:13Z","pushed_at":"2016-10-08T13:28:06Z","git_url":"git://github.com/miha-plesko/capstan.git","ssh_url":"git@github.com:miha-plesko/capstan.git","clone_url":"https://github.com/miha-plesko/capstan.git","svn_url":"https://github.com/miha-plesko/capstan","homepage":"http://osv.io/capstan/","size":507,"stargazers_count":0,"watchers_count":0,"language":"Go","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":"develop"}},"base":{"label":"mikelangelo-project:develop","ref":"develop","sha":"45d1786bfc1d227a60b6f440ef06ce15431b001d","user":{"login":"mikelangelo-project","id":19607578,"avatar_url":"https://avatars.githubusercontent.com/u/19607578?v=3","gravatar_id":"","url":"https://api.github.com/users/mikelangelo-project","html_url":"https://github.com/mikelangelo-project","followers_url":"https://api.github.com/users/mikelangelo-project/followers","following_url":"https://api.github.com/users/mikelangelo-project/following{/other_user}","gists_url":"https://api.github.com/users/mikelangelo-project/gists{/gist_id}","starred_url":"https://api.github.com/users/mikelangelo-project/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikelangelo-project/subscriptions","organizations_url":"https://api.github.com/users/mikelangelo-project/orgs","repos_url":"https://api.github.com/users/mikelangelo-project/repos","events_url":"https://api.github.com/users/mikelangelo-project/events{/privacy}","received_events_url":"https://api.github.com/users/mikelangelo-project/received_events","type":"Organization","site_admin":false},"repo":{"id":59827827,"name":"capstan","full_name":"mikelangelo-project/capstan","owner":{"login":"mikelangelo-project","id":19607578,"avatar_url":"https://avatars.githubusercontent.com/u/19607578?v=3","gravatar_id":"","url":"https://api.github.com/users/mikelangelo-project","html_url":"https://github.com/mikelangelo-project","followers_url":"https://api.github.com/users/mikelangelo-project/followers","following_url":"https://api.github.com/users/mikelangelo-project/following{/other_user}","gists_url":"https://api.github.com/users/mikelangelo-project/gists{/gist_id}","starred_url":"https://api.github.com/users/mikelangelo-project/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikelangelo-project/subscriptions","organizations_url":"https://api.github.com/users/mikelangelo-project/orgs","repos_url":"https://api.github.com/users/mikelangelo-project/repos","events_url":"https://api.github.com/users/mikelangelo-project/events{/privacy}","received_events_url":"https://api.github.com/users/mikelangelo-project/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mikelangelo-project/capstan","description":"Capstan, a tool for packaging and running your application on OSv.","fork":true,"url":"https://api.github.com/repos/mikelangelo-project/capstan","forks_url":"https://api.github.com/repos/mikelangelo-project/capstan/forks","keys_url":"https://api.github.com/repos/mikelangelo-project/capstan/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mikelangelo-project/capstan/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mikelangelo-project/capstan/teams","hooks_url":"https://api.github.com/repos/mikelangelo-project/capstan/hooks","issue_events_url":"https://api.github.com/repos/mikelangelo-project/capstan/issues/events{/number}","events_url":"https://api.github.com/repos/mikelangelo-project/capstan/events","assignees_url":"https://api.github.com/repos/mikelangelo-project/capstan/assignees{/user}","branches_url":"https://api.github.com/repos/mikelangelo-project/capstan/branches{/branch}","tags_url":"https://api.github.com/repos/mikelangelo-project/capstan/tags","blobs_url":"https://api.github.com/repos/mikelangelo-project/capstan/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mikelangelo-project/capstan/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mikelangelo-project/capstan/git/refs{/sha}","trees_url":"https://api.github.com/repos/mikelangelo-project/capstan/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mikelangelo-project/capstan/statuses/{sha}","languages_url":"https://api.github.com/repos/mikelangelo-project/capstan/languages","stargazers_url":"https://api.github.com/repos/mikelangelo-project/capstan/stargazers","contributors_url":"https://api.github.com/repos/mikelangelo-project/capstan/contributors","subscribers_url":"https://api.github.com/repos/mikelangelo-project/capstan/subscribers","subscription_url":"https://api.github.com/repos/mikelangelo-project/capstan/subscription","commits_url":"https://api.github.com/repos/mikelangelo-project/capstan/commits{/sha}","git_commits_url":"https://api.github.com/repos/mikelangelo-project/capstan/git/commits{/sha}","comments_url":"https://api.github.com/repos/mikelangelo-project/capstan/comments{/number}","issue_comment_url":"https://api.github.com/repos/mikelangelo-project/capstan/issues/comments{/number}","contents_url":"https://api.github.com/repos/mikelangelo-project/capstan/contents/{+path}","compare_url":"https://api.github.com/repos/mikelangelo-project/capstan/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mikelangelo-project/capstan/merges","archive_url":"https://api.github.com/repos/mikelangelo-project/capstan/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mikelangelo-project/capstan/downloads","issues_url":"https://api.github.com/repos/mikelangelo-project/capstan/issues{/number}","pulls_url":"https://api.github.com/repos/mikelangelo-project/capstan/pulls{/number}","milestones_url":"https://api.github.com/repos/mikelangelo-project/capstan/milestones{/number}","notifications_url":"https://api.github.com/repos/mikelangelo-project/capstan/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mikelangelo-project/capstan/labels{/name}","releases_url":"https://api.github.com/repos/mikelangelo-project/capstan/releases{/id}","deployments_url":"https://api.github.com/repos/mikelangelo-project/capstan/deployments","created_at":"2016-05-27T10:51:12Z","updated_at":"2016-08-24T08:29:17Z","pushed_at":"2016-10-08T13:28:06Z","git_url":"git://github.com/mikelangelo-project/capstan.git","ssh_url":"git@github.com:mikelangelo-project/capstan.git","clone_url":"https://github.com/mikelangelo-project/capstan.git","svn_url":"https://github.com/mikelangelo-project/capstan","homepage":"http://osv.io/capstan/","size":498,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":7,"forks":2,"open_issues":7,"watchers":0,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/6"},"html":{"href":"https://github.com/mikelangelo-project/capstan/pull/6"},"issue":{"href":"https://api.github.com/repos/mikelangelo-project/capstan/issues/6"},"comments":{"href":"https://api.github.com/repos/mikelangelo-project/capstan/issues/6/comments"},"review_comments":{"href":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/6/comments"},"review_comment":{"href":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/mikelangelo-project/capstan/pulls/6/commits"},"statuses":{"href":"https://api.github.com/repos/mikelangelo-project/capstan/statuses/2c7631ac22a4d0477faef99aed2ddd9005cfc40a"}}}}
{"actor":{"display_login":"gberginc"}}