{"action":"created","issue":{"url":"https://api.github.com/repos/buildbot/buildbot/issues/1476","labels_url":"https://api.github.com/repos/buildbot/buildbot/issues/1476/labels{/name}","comments_url":"https://api.github.com/repos/buildbot/buildbot/issues/1476/comments","events_url":"https://api.github.com/repos/buildbot/buildbot/issues/1476/events","html_url":"https://github.com/buildbot/buildbot/pull/1476","id":53511086,"number":1476,"title":"Dynamic build","user":{"login":"thedylman","id":3179843,"avatar_url":"https://avatars.githubusercontent.com/u/3179843?v=3","gravatar_id":"","url":"https://api.github.com/users/thedylman","html_url":"https://github.com/thedylman","followers_url":"https://api.github.com/users/thedylman/followers","following_url":"https://api.github.com/users/thedylman/following{/other_user}","gists_url":"https://api.github.com/users/thedylman/gists{/gist_id}","starred_url":"https://api.github.com/users/thedylman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thedylman/subscriptions","organizations_url":"https://api.github.com/users/thedylman/orgs","repos_url":"https://api.github.com/users/thedylman/repos","events_url":"https://api.github.com/users/thedylman/events{/privacy}","received_events_url":"https://api.github.com/users/thedylman/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/buildbot/buildbot/labels/needs+more+review","name":"needs more review","color":"bfe5bf"},{"url":"https://api.github.com/repos/buildbot/buildbot/labels/needs+work","name":"needs work","color":"fad8c7"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":10,"created_at":"2015-01-06T12:41:19Z","updated_at":"2015-01-07T12:00:14Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/buildbot/buildbot/pulls/1476","html_url":"https://github.com/buildbot/buildbot/pull/1476","diff_url":"https://github.com/buildbot/buildbot/pull/1476.diff","patch_url":"https://github.com/buildbot/buildbot/pull/1476.patch"},"body":"One of the things I am trying to do is move the logic for building a project out of Buildbot, so that when you run on a build locally on a command line or on Buildbot they are as close as possible. Also I don't need to restart Buildbot every time the build commands change.\r\n\r\nYou could just have a single shell script which is run by Buildbot in a single stage, but it makes it difficult to see where something has gone wrong. This change allows you to add steps during the build, which allows the list of steps to change per changeset.\r\n\r\nThe workflow would be something like:\r\n* Build starts\r\n* clone a tree\r\n* Run a command to generate the list of stages/commands\r\n* Use the output from the command to generate and add steps to the build\r\n* Run newly added commands\r\n* Run upload/cleanup steps\r\n\r\nStill need to add test, but I would like your comments on if this is the right way to do it.\r\n\r\nDemo config:\r\n```python\r\nfrom buildbot.plugins import buildslave, schedulers, util, steps\r\nc = BuildmasterConfig = {}\r\nc['slaves'] = [buildslave.BuildSlave(\"example-slave\", \"pass\")]\r\nc['protocols'] = {'pb': {'port': 9989}}\r\nc['schedulers'] = []\r\nc['schedulers'].append(schedulers.ForceScheduler(name=\"force\", builderNames=[\"runtests\"]))\r\n\r\nfrom buildbot.status.results import SUCCESS\r\nclass GetStagesCommand(steps.BuildStep):\r\n    def run(self):\r\n        # e.g. run a command to get list of stages from source,\r\n        # then build the list of commands. (hard coded here for demo)\r\n        self.build.addStepsAfterCurrentStep([\r\n            steps.ShellCommand(name=\"command1\", command=[\"sleep\", \"5\"]),\r\n            steps.ShellCommand(name=\"command2\", command=[\"sleep\", \"5\"]),\r\n            steps.ShellCommand(name=\"commandx\", command=[\"sleep\", \"5\"]),\r\n            steps.ShellCommand(name=\"commandx\", command=[\"sleep\", \"5\"]),\r\n        ])\r\n        return SUCCESS\r\n\r\nfactory = util.BuildFactory()\r\nfactory.addStep(steps.ShellCommand(name=\"first step\", command=[\"echo\", \"first\"]))\r\nfactory.addStep(GetStagesCommand(name=\"dynamic step\"))\r\nfactory.addStep(steps.ShellCommand(name=\"last step\", command=[\"echo\", \"last\"]))\r\n\r\nc['builders'] = []\r\nc['builders'].append(util.BuilderConfig(name=\"runtests\", slavenames=[\"example-slave\"],\r\n                                        factory=factory))\r\nc['title'] = \"Pyflakes\"\r\nc['titleURL'] = \"https://launchpad.net/pyflakes\"\r\nc['buildbotURL'] = \"http://localhost:8010/\"\r\nc['www'] = {\"port\": 8020, \"plugins\" : {\"waterfall_view\": {\"num_builds\": 50}}}\r\nc['db'] = {\"db_url\" : \"sqlite:///state.sqlite\"}\r\n```\r\n![dynamic-stages](https://cloud.githubusercontent.com/assets/3179843/5628525/4e1926b0-95a0-11e4-8958-6fc36262653e.png)\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/buildbot/buildbot/issues/comments/69012942","html_url":"https://github.com/buildbot/buildbot/pull/1476#issuecomment-69012942","issue_url":"https://api.github.com/repos/buildbot/buildbot/issues/1476","id":69012942,"user":{"login":"thedylman","id":3179843,"avatar_url":"https://avatars.githubusercontent.com/u/3179843?v=3","gravatar_id":"","url":"https://api.github.com/users/thedylman","html_url":"https://github.com/thedylman","followers_url":"https://api.github.com/users/thedylman/followers","following_url":"https://api.github.com/users/thedylman/following{/other_user}","gists_url":"https://api.github.com/users/thedylman/gists{/gist_id}","starred_url":"https://api.github.com/users/thedylman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thedylman/subscriptions","organizations_url":"https://api.github.com/users/thedylman/orgs","repos_url":"https://api.github.com/users/thedylman/repos","events_url":"https://api.github.com/users/thedylman/events{/privacy}","received_events_url":"https://api.github.com/users/thedylman/received_events","type":"User","site_admin":false},"created_at":"2015-01-07T12:00:14Z","updated_at":"2015-01-07T12:00:14Z","body":"I've split the relnotes into a separate commit."}}