bpci 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Bootstrap.js by @fat & @mdo
3
+ * Copyright 2012 Twitter, Inc.
4
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
5
+ */
6
+ !function(a){a(function(){"use strict",a.support.transition=function(){var b=document.body||document.documentElement,c=b.style,d=c.transition!==undefined||c.WebkitTransition!==undefined||c.MozTransition!==undefined||c.MsTransition!==undefined||c.OTransition!==undefined;return d&&{end:function(){var b="TransitionEnd";return a.browser.webkit?b="webkitTransitionEnd":a.browser.mozilla?b="transitionend":a.browser.opera&&(b="oTransitionEnd"),b}()}}()})}(window.jQuery),!function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype={constructor:c,close:function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),e.trigger("close"),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger("close").removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()}},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype={constructor:b,setState:function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},toggle:function(){var a=this.$element.parent('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")}},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.carousel.defaults,c),this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(){return this.interval=setInterval(a.proxy(this.next,this),this.options.interval),this},to:function(b){var c=this.$element.find(".active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(){return clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this;this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();if(e.hasClass("active"))return;return!a.support.transition&&this.$element.hasClass("slide")?(this.$element.trigger("slide"),d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")):(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.trigger("slide"),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})),f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=typeof c=="object"&&c;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):typeof c=="string"||(c=f.slide)?e[c]():e.cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find(".in"),e;d&&d.length&&(e=d.data("collapse"),d.collapse("hide"),e||d.data("collapse",null)),this.$element[b](0),this.transition("addClass","show","shown"),this.$element[b](this.$element[0][c])},hide:function(){var a=this.dimension();this.reset(this.$element[a]()),this.transition("removeClass","hide","hidden"),this.$element[a](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c=="show"&&e.reset(),e.$element.trigger(d)};this.$element.trigger(c)[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();a(e).collapse(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}"use strict";var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e=c.attr("data-target"),f,g;return e||(e=c.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,"")),f=a(e),f.length||(f=c.parent()),g=f.hasClass("open"),d(),!g&&f.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,a.proxy(f,this)):f.call(this)):b&&b()}function f(){this.$backdrop.remove(),this.$backdrop=null}function g(){var b=this;this.isShown&&this.options.keyboard?a(document).on("keyup.dismiss.modal",function(a){a.which==27&&b.hide()}):this.isShown||a(document).off("keyup.dismiss.modal")}"use strict";var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this))};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this;if(this.isShown)return;a("body").addClass("modal-open"),this.isShown=!0,this.$element.trigger("show"),g.call(this),e.call(this,function(){var c=a.support.transition&&b.$element.hasClass("fade");!b.$element.parent().length&&b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in"),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();if(!this.isShown)return;var e=this;this.isShown=!1,a("body").removeClass("modal-open"),g.call(this),this.$element.trigger("hide").removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?c.call(this):d.call(this)}},a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a(function(){a("body").on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({},e.data(),c.data());b.preventDefault(),e.modal(f)})})}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,this.options.trigger!="manual"&&(e=this.options.trigger=="hover"?"mouseenter":"focus",f=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(e,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f,this.options.selector,a.proxy(this.leave,this))),this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,b,this.$element.data()),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);!c.options.delay||!c.options.delay.show?c.show():(c.hoverState="in",setTimeout(function(){c.hoverState=="in"&&c.show()},c.options.delay.show))},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);!c.options.delay||!c.options.delay.hide?c.hide():(c.hoverState="out",setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide))},show:function(){var a,b,c,d,e,f,g;if(this.hasContent()&&this.enabled){a=this.tip(),this.setContent(),this.options.animation&&a.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,a[0],this.$element[0]):this.options.placement,b=/in/.test(f),a.remove().css({top:0,left:0,display:"block"}).appendTo(b?this.$element:document.body),c=this.getPosition(b),d=a[0].offsetWidth,e=a[0].offsetHeight;switch(b?f.split(" ")[1]:f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}a.css(g).addClass(f).addClass("in")}},setContent:function(){var a=this.tip();a.find(".tooltip-inner").html(this.getTitle()),a.removeClass("fade in top bottom left right")},hide:function(){function d(){var b=setTimeout(function(){c.off(a.support.transition.end).remove()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.remove()})}var b=this,c=this.tip();c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d():c.remove()},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(b){return a.extend({},b?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a=(a||"").toString().replace(/(^\s*|\s*$)/,""),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()}},a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,delay:0,selector:!1,placement:"top",trigger:"hover",title:"",template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'}}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var b=this.tip(),c=this.getTitle(),d=this.getContent();b.find(".popover-title")[a.type(c)=="object"?"append":"html"](c),b.find(".popover-content > *")[a.type(d)=="object"?"append":"html"](d),b.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-content")||(typeof c.content=="function"?c.content.call(b[0]):c.content),a=a.toString().replace(/(^\s*|\s*$)/,""),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip}}),a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(a){function b(b,c){var d=a.proxy(this.process,this),e=a(b).is("body")?a(window):a(b),f;this.options=a.extend({},a.fn.scrollspy.defaults,c),this.$scrollElement=e.on("scroll.scroll.data-api",d),this.selector=(this.options.target||(f=a(b).attr("href"))&&f.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=a("body").on("click.scroll.data-api",this.selector,d),this.refresh(),this.process()}"use strict",b.prototype={constructor:b,refresh:function(){this.targets=this.$body.find(this.selector).map(function(){var b=a(this).attr("href");return/^#\w/.test(b)&&a(b).length?b:null}),this.offsets=a.map(this.targets,function(b){return a(b).position().top})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.offsets,c=this.targets,d=this.activeTarget,e;for(e=b.length;e--;)d!=c[e]&&a>=b[e]&&(!b[e+1]||a<=b[e+1])&&this.activate(c[e])},activate:function(a){var b;this.activeTarget=a,this.$body.find(this.selector).parent(".active").removeClass("active"),b=this.$body.find(this.selector+'[href="'+a+'"]').parent("li").addClass("active"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active")}},a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a(function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),!function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active a").last()[0],b.trigger({type:"show",relatedTarget:e}),f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}},a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a(function(){a("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.$menu=a(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(a),this.$element.change(),this.hide()},show:function(){var b=a.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:b.top+b.height,left:b.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c=this,d,e;return this.query=this.$element.val(),this.query?(d=a.grep(this.source,function(a){if(c.matcher(a))return a}),d=this.sorter(d),d.length?this.render(d.slice(0,this.options.items)).show():this.shown?this.hide():this):this.shown?this.hide():this},matcher:function(a){return~a.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){var b=[],c=[],d=[],e;while(e=a.shift())e.toLowerCase().indexOf(this.query.toLowerCase())?~e.indexOf(this.query)?c.push(e):d.push(e):b.push(e);return b.concat(c,d)},highlighter:function(a){return a.replace(new RegExp("("+this.query+")","ig"),function(a,b){return"<strong>"+b+"</strong>"})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),(a.browser.webkit||a.browser.msie)&&this.$element.on("keydown",a.proxy(this.keypress,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this))},keyup:function(a){switch(a.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},keypress:function(a){if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:a.preventDefault(),this.prev();break;case 40:a.preventDefault(),this.next()}a.stopPropagation()},blur:function(a){var b=this;setTimeout(function(){b.hide()},150)},click:function(a){a.stopPropagation(),a.preventDefault(),this.select()},mouseenter:function(b){this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")}},a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>'},a.fn.typeahead.Constructor=b,a(function(){a("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;b.preventDefault(),c.typeahead(c.data())})})}(window.jQuery);
@@ -0,0 +1,55 @@
1
+ class BPCI
2
+ # An in memory queue used for maintaining an order list of requested
3
+ # builds.
4
+ class Queue
5
+ # enabled - determines whether builds should be queued or not.
6
+ def initialize(enabled, verbose=false)
7
+ @enabled = enabled
8
+ @verbose = verbose
9
+ @queue = []
10
+
11
+ log("Build queueing enabled") if enabled
12
+ end
13
+
14
+ # Public: Appends a branch to be built, unless it already exists
15
+ # within the queue.
16
+ #
17
+ # branch - the name of the branch to build or nil if the default
18
+ # should be built.
19
+ #
20
+ # Returns nothing
21
+ def append_unless_already_exists(branch)
22
+ return unless enabled?
23
+ unless @queue.include? branch
24
+ @queue << branch
25
+ log "#{Time.now.to_i}: Queueing #{branch}"
26
+ end
27
+ end
28
+
29
+ # Returns a String of the next branch to build
30
+ def next_branch_to_build
31
+ branch = @queue.shift
32
+ log "#{Time.now.to_i}: De-queueing #{branch}"
33
+ branch
34
+ end
35
+
36
+ # Returns true if there are requested builds waiting and false
37
+ # otherwise.
38
+ def waiting?
39
+ if enabled?
40
+ not @queue.empty?
41
+ else
42
+ false
43
+ end
44
+ end
45
+
46
+ def enabled?
47
+ @enabled
48
+ end
49
+
50
+ protected
51
+ def log(msg)
52
+ puts msg if @verbose
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,115 @@
1
+ require 'sinatra/base'
2
+ require 'erb'
3
+ require 'json'
4
+
5
+ class BPCI
6
+ class Server < Sinatra::Base
7
+ attr_reader :bpci
8
+
9
+ dir = File.dirname(File.expand_path(__FILE__))
10
+
11
+ set :views, "#{dir}/views"
12
+ set :public_folder, "#{dir}/public"
13
+ set :static, true
14
+ set :lock, true
15
+
16
+ before { bpci.restore }
17
+
18
+ get '/ping' do
19
+ if bpci.building? || !bpci.last_build
20
+ halt 412, "building"
21
+ end
22
+
23
+ "#{bpci.last_build.sha} #{bpci.last_build.status}"
24
+ end
25
+
26
+ get '/?' do
27
+ erb(:template, {}, :bpci => bpci)
28
+ end
29
+
30
+ post '/?' do
31
+ unless params[:rebuild]
32
+ payload = JSON.parse(params[:payload])
33
+ pushed_branch = payload["ref"].split('/').last
34
+ end
35
+
36
+ # Only build if we were given an explicit branch via `?branch=blah`
37
+ # or the payload exists and the "ref" property matches our
38
+ # specified build branch.
39
+ if params[:branch] || params[:rebuild] || pushed_branch == bpci.git_branch
40
+ bpci.build(params[:branch])
41
+ end
42
+
43
+ redirect request.path
44
+ end
45
+
46
+ get '/json/?' do
47
+ response = [200, {'Content-Type' => 'application/json'}]
48
+ response_json = erb(:json, {}, :bpci => bpci)
49
+ if params[:jsonp]
50
+ response << params[:jsonp] + '(' + response_json.strip + ')'
51
+ else
52
+ response << response_json.strip
53
+ end
54
+ response
55
+ end
56
+
57
+
58
+ helpers do
59
+ include Rack::Utils
60
+ alias_method :h, :escape_html
61
+
62
+ # thanks integrity!
63
+ def ansi_color_codes(string)
64
+ string.gsub("\e[0m", '</span>').
65
+ gsub(/\e\[(\d+)m/, "<span class=\"color\\1\">")
66
+ end
67
+
68
+ def pretty_time(time)
69
+ time.strftime("%Y-%m-%d %H:%M")
70
+ end
71
+
72
+ def bpci_root
73
+ root = request.path
74
+ root = "" if root == "/"
75
+ root
76
+ end
77
+ end
78
+
79
+ def initialize(*args)
80
+ super
81
+ @bpci = BPCI.new(settings.project_path)
82
+ check_project
83
+ end
84
+
85
+ def self.start(host, port, project_path)
86
+ set :project_path, project_path
87
+ $port = port # TODO: Unglobal this.
88
+ BPCI::Server.run! :host => host, :port => port
89
+ end
90
+
91
+ def self.rack_start(project_path)
92
+ set :project_path, project_path
93
+ self.new
94
+ end
95
+
96
+ def self.project_path=(project_path)
97
+ user, pass = Config.bpci(project_path).user.to_s, Config.bpci(project_path).pass.to_s
98
+ if user != '' && pass != ''
99
+ use Rack::Auth::Basic do |username, password|
100
+ [ username, password ] == [ user, pass ]
101
+ end
102
+ puts "Using HTTP basic auth"
103
+ end
104
+ set :project_path, Proc.new{project_path}, true
105
+ end
106
+
107
+ def check_project
108
+ if settings.project_path.nil? || !File.exists?(File.expand_path(settings.project_path))
109
+ puts "Whoops! I need the path to a Git repo."
110
+ puts " $ git clone git@github.com:username/project.git project"
111
+ abort " $ bpci project"
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,3 @@
1
+ class BPCI
2
+ Version = VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,14 @@
1
+ <% if bpci.last_build %>
2
+ {"name":"<%= bpci.project %>",
3
+ "url":"<%= bpci.url %>",
4
+ "status":"<%= bpci.last_build.status %>",
5
+ "started_at":"<%= pretty_time(bpci.last_build.started_at) %>",
6
+ "finished_at":"<%= pretty_time(bpci.last_build.finished_at) %>",
7
+ "duration":"<%= bpci.last_build.duration if bpci.last_build.duration %>",
8
+ "sha":"<%= bpci.last_build.sha %>",
9
+ "commit_url":"<%= bpci.last_build.commit.url if bpci.last_build.commit %>",
10
+ "branch":"<%= bpci.last_build.branch %>"
11
+ }
12
+ <% else %>
13
+ {"error": "No build has run yet."}
14
+ <% end %>
@@ -0,0 +1,135 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link href="<%= bpci_root %>/bootstrap/css/bootstrap.min.css" rel="stylesheet">
5
+ <link href="<%= bpci_root %>/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet">
6
+ <style>
7
+ body {
8
+ padding: 60px 0 60px 0;
9
+ }
10
+
11
+ .color31 {
12
+ color: red !important;
13
+ }
14
+
15
+ .color32 {
16
+ color: green !important;
17
+ }
18
+
19
+ .color33 {
20
+ color: yellow !important;
21
+ }
22
+
23
+ .bigbutton {
24
+ font-size: 25px;
25
+ text-align: center;
26
+ }
27
+ </style>
28
+ <script src="<%= bpci_root %>/bootstrap/js/bootstrap.min.js"></script>
29
+
30
+ <!--[if lt IE 9]>
31
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
32
+ <![endif]-->
33
+
34
+ <link rel="shortcut icon" href="<%= bpci_root %>/favicon.ico" type="image/x-icon" />
35
+ <title><%= h(bpci.project) %>: Breakpoint CI</title>
36
+ </head>
37
+ <body>
38
+
39
+ <div class="navbar navbar-fixed-top">
40
+ <div class="navbar-inner">
41
+ <div class="container">
42
+ <a class="brand" href="/">Breakpoint CI</a>
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <div class="container">
48
+ <div class="row">
49
+ <div class="span8">
50
+ <h1><%= bpci.project %></h1>
51
+ <% if bpci.current_build %>
52
+ <%# If there is a build happening, don't display anything except info about that build %>
53
+ <div class="alert alert-warning">
54
+ A build is in progress. When it is complete, the result will be displayed here.
55
+ </div>
56
+ <% else %>
57
+ <%# We are not currently in a build. %>
58
+ <% if bpci.last_build %>
59
+ <%# Has a build ever been done? %>
60
+ <% cssclass = case bpci.last_build.status
61
+ when :successful
62
+ 'success'
63
+ when :failed
64
+ 'error'
65
+ when :building
66
+ 'info'
67
+ end %>
68
+
69
+ <div class="alert alert-<%= cssclass %>">
70
+ <% if bpci.last_build.failed? %>
71
+ <%# Has the last build failed? %>
72
+ <div class="alert alert-error">
73
+ <strong>Oh crap! You broke it!</strong> That build failed! :-(. Check the log below for console output.
74
+ </div>
75
+ <% else %>
76
+ <%# Last build did not fail, but exists. %>
77
+ <span class="date"><%= pretty_time(bpci.last_build.finished_at) %></span> &raquo;
78
+ <% if bpci.last_build.sha %>
79
+ Built <%= bpci.last_build.branch %> at <a href="<%= bpci.last_build.commit.url %>"><%= bpci.last_build.short_sha %></a>
80
+ <% end %>
81
+ (<strong><%= bpci.last_build.status.upcase %></strong>)
82
+ <% if bpci.last_build.duration %>
83
+ in <strong><%= bpci.last_build.duration %></strong> seconds.
84
+ <% end %>
85
+ <% end %> <%# /!bpci.last_build.failed? %>
86
+
87
+ <%# Dislay the test output in all cases. %>
88
+ <pre><code><%= ansi_color_codes h(bpci.last_build.output) %></code></pre>
89
+ </div>
90
+
91
+ <% end %><%# /bpci.last_build %>
92
+ <% end %><%# !/bpci.current_build %>
93
+ </div><%# /.span8 %>
94
+
95
+ <div class="span4">
96
+ <div class="well">
97
+ <h3>Build Status</h3>
98
+ <% if bpci.current_build %>
99
+ <span class="date"><%= pretty_time(bpci.current_build.started_at) if bpci.current_build %></span> &raquo;
100
+ <% if bpci.current_build.sha %>
101
+ <p>Building <%= bpci.current_build.branch %> at <a href="<%= bpci.current_build.commit.url %>"><%= bpci.current_build.short_sha %></a> <small>(pid: <%= bpci.pid %>)</small></p>
102
+ <p><small>Reloading every 3 seconds until build is complete.</small></p>
103
+ <div class="progress progress-warning progress-striped active">
104
+ <div class="bar" style="width: 66%;"></div>
105
+ </div>
106
+ <script>setTimeout("location.reload();", 3000);</script>
107
+ <% else %>
108
+ <p>Starting project build...</p>
109
+ <p><small>Reloading every 3 seconds until build is complete.</small></p>
110
+ <div class="progress progress-warning progress-striped active">
111
+ <div class="bar" style="width: 33%;"></div>
112
+ </div>
113
+ <script>setTimeout("location.reload();", 3000);</script>
114
+ <% end %>
115
+ <% else %>
116
+ <form method="POST">
117
+ <input type="hidden" name="rebuild" value="true">
118
+ <div class="bigbutton">
119
+ <input type="submit" class="btn btn-primary btn-large" value="Force a Build" />
120
+ </div>
121
+ </form>
122
+ <% end %>
123
+ </div><%# /.well %>
124
+ </div><%# /.span4 %>
125
+ </div><%# /.row %>
126
+
127
+ <hr />
128
+
129
+ <footer>
130
+ <div style="float: left;">&copy; 2012-present</div>
131
+ <div style="float: right;"><a href="https://github.com/breakpoint-eval/bpci">source code</a></div>
132
+ </footer>
133
+ </div>
134
+ </body>
135
+ </html>
@@ -0,0 +1,52 @@
1
+ {
2
+ "after": "416cb2f7105e7f989bc223d1a975b93a6491b276",
3
+ "before": "0ae4da1eea2d191b33ebfbd5db48e3c1f91953ad",
4
+ "commits": [
5
+ {
6
+ "added": [
7
+
8
+ ],
9
+ "author": {
10
+ "email": "joshua.owens@gmail.com",
11
+ "name": "Josh Owens",
12
+ "username": "queso"
13
+ },
14
+ "id": "09293a1703b3bdb36aba70d38abd5e44396c50a5",
15
+ "message": "Update README",
16
+ "modified": [
17
+ "README.textile"
18
+ ],
19
+ "removed": [
20
+
21
+ ],
22
+ "timestamp": "2011-02-07T15:27:35-08:00",
23
+ "url": "https:\/\/github.com\/fourbeansoup\/broth\/commit\/09293a1703b3bdb36aba70d38abd5e44396c50a5"
24
+ }
25
+ ],
26
+ "compare": "https:\/\/github.com\/fourbeansoup\/broth\/compare\/0ae4da1...416cb2f",
27
+ "forced": false,
28
+ "ref": "refs\/heads\/master",
29
+ "repository": {
30
+ "created_at": "2009\/12\/05 10:44:57 -0800",
31
+ "description": "FourBeanSoup's Broth Application, a tasty starting point for every app",
32
+ "fork": true,
33
+ "forks": 4,
34
+ "has_downloads": true,
35
+ "has_issues": true,
36
+ "has_wiki": true,
37
+ "homepage": "",
38
+ "language": "JavaScript",
39
+ "name": "broth",
40
+ "open_issues": 2,
41
+ "organization": "fourbeansoup",
42
+ "owner": {
43
+ "email": "josh+scm@fourbeansoup.com",
44
+ "name": "fourbeansoup"
45
+ },
46
+ "private": false,
47
+ "pushed_at": "2011\/02\/07 17:17:00 -0800",
48
+ "size": 604,
49
+ "url": "https:\/\/github.com\/fourbeansoup\/broth",
50
+ "watchers": 10
51
+ }
52
+ }
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'mocha'
4
+
5
+ ENV['RACK_ENV'] = 'test'
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
+ require 'bpci'
10
+
11
+ BPCI::Server.set :project_path, "."
12
+ BPCI::Server.set :environment, "test"
13
+
14
+ TMP_DIR = '/tmp/bpci_test'
15
+
16
+ def tmp_dir
17
+ TMP_DIR
18
+ end
19
+
20
+ def setup_git_info(options = {})
21
+ @tmp_dirs ||= []
22
+ @tmp_dirs += [options[:tmp_dir]]
23
+ create_tmpdir!(options[:tmp_dir])
24
+ dir = options[:tmp_dir] || tmp_dir
25
+ `cd #{dir} && git init`
26
+ options[:config].each do |key, value|
27
+ `cd #{dir} && git config --add #{key} "#{value}"`
28
+ end
29
+ end
30
+
31
+ def teardown_git_info
32
+ remove_tmpdir!
33
+ @tmp_dirs.each do |dir|
34
+ remove_tmpdir!(dir)
35
+ end
36
+ end
37
+
38
+ def remove_tmpdir!(passed_dir = nil)
39
+ FileUtils.rm_rf(passed_dir || tmp_dir)
40
+ end
41
+
42
+ def create_tmpdir!(passed_dir = nil)
43
+ FileUtils.mkdir_p(passed_dir || tmp_dir)
44
+ end
45
+
46
+ class Test::Unit::TestCase
47
+ end
@@ -0,0 +1,17 @@
1
+ require 'helper'
2
+
3
+ class TestBPCI < Test::Unit::TestCase
4
+ def test_raise_error_on_invalid_command
5
+ assert_raise RuntimeError, LoadError do
6
+ BPCI::Config.new('--invalid').to_s
7
+ end
8
+ end
9
+
10
+ def test_return_value_of_config
11
+ assert_equal `git config blame`.chomp, BPCI::Config.new('blame').to_s
12
+ end
13
+
14
+ def test_return_empty_string_when_config_does_not_exist
15
+ assert_equal '', BPCI::Config.new('invalid').to_s
16
+ end
17
+ end