Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(407)

Side by Side Diff: appengine/chromium_rietveld/static/script.js

Issue 1058893004: Rietveld schedules builds on buildbucket (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2008 Google Inc. 1 // Copyright 2008 Google Inc.
2 // 2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License. 4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at 5 // You may obtain a copy of the License at
6 // 6 //
7 // http://www.apache.org/licenses/LICENSE-2.0 7 // http://www.apache.org/licenses/LICENSE-2.0
8 // 8 //
9 // Unless required by applicable law or agreed to in writing, software 9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, 10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 // Global Rietveld namespace.
16 var rietveld = {
17 preferredDomainName: document.location.hostname,
18 buildbucketHostname: "cr-buildbucket-test.appspot.com",
jrobbins 2015/04/16 18:09:27 It looks like you hardcoded a test value here.
nodir 2015/04/16 18:15:11 it is overwritten in base.html
19 // Current rietveld user.
20 user: null,
21 gapi: {
22 // Client ID to use.
23 clientId: null,
24 }
25 };
26
15 // Generic helpers 27 // Generic helpers
16 28
17 /** 29 /**
18 * Create a new XMLHttpRequest in a cross-browser-compatible way. 30 * Create a new XMLHttpRequest in a cross-browser-compatible way.
19 * @return XMLHttpRequest object 31 * @return XMLHttpRequest object
20 */ 32 */
21 function M_getXMLHttpRequest() { 33 function M_getXMLHttpRequest() {
22 try { 34 try {
23 return new XMLHttpRequest(); 35 return new XMLHttpRequest();
24 } catch (e) { } 36 } catch (e) { }
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
113 } 125 }
114 return y; 126 return y;
115 } 127 }
116 128
117 function M_editPatchsetTitle(issue, patchset, xsrf_token, 129 function M_editPatchsetTitle(issue, patchset, xsrf_token,
118 original_patchset_title, patch_count) { 130 original_patchset_title, patch_count) {
119 131
120 var new_patchset_title = prompt( 132 var new_patchset_title = prompt(
121 'Please enter the new title of Patch Set ' + patch_count, 133 'Please enter the new title of Patch Set ' + patch_count,
122 original_patchset_title); 134 original_patchset_title);
123 if (new_patchset_title == null) { 135 if (new_patchset_title == null) {
124 return false; 136 return false;
125 } else if (new_patchset_title == original_patchset_title) { 137 } else if (new_patchset_title == original_patchset_title) {
126 // Do not make an HTTP req if the new specified title is exactly the same. 138 // Do not make an HTTP req if the new specified title is exactly the same.
127 return false; 139 return false;
128 } 140 }
129 141
130 //Build POST data for request. 142 //Build POST data for request.
131 var data = []; 143 var data = [];
132 data.push('xsrf_token=' + xsrf_token); 144 data.push('xsrf_token=' + xsrf_token);
133 data.push('patchset_title=' + new_patchset_title); 145 data.push('patchset_title=' + new_patchset_title);
(...skipping 486 matching lines...) Expand 10 before | Expand all | Expand 10 after
620 } 632 }
621 633
622 M_sendEditFlagsRequest(issue, req.join("&"), function(xhr) { 634 M_sendEditFlagsRequest(issue, req.join("&"), function(xhr) {
623 if (xhr.status == 200) 635 if (xhr.status == 200)
624 window.location.reload(); 636 window.location.reload();
625 }); 637 });
626 return true; 638 return true;
627 } 639 }
628 640
629 /** 641 /**
630 * Edit the list of pending try jobs for the given patchset. 642 * Edit the list of pending try jobs for the given patchset.
631 * @param {String} patchset The patchset key. 643 * @param {String} patchset The patchset key.
632 */ 644 */
633 function M_editPendingTryJobs(patchset) { 645 function M_editPendingTryJobs(patchset) {
634 var checkboxContainer = document.getElementById('trybot-popup-checkboxes'); 646 var checkboxContainer = document.getElementById('trybot-popup-checkboxes');
635 if (! checkboxContainer) { 647 if (! checkboxContainer) {
636 checkboxContainer = document.getElementById( 648 checkboxContainer = document.getElementById(
637 'trybot-popup-checkboxes-with-categories') 649 'trybot-popup-checkboxes-with-categories')
638 } 650 }
639 var checkboxElements = checkboxContainer.getElementsByTagName('input'); 651 var checkboxElements = checkboxContainer.getElementsByTagName('input');
640 for (var checkbox, i = 0; checkbox = checkboxElements[i]; i++) { 652 for (var checkbox, i = 0; checkbox = checkboxElements[i]; i++) {
641 checkbox.checked = false; 653 checkbox.checked = false;
642 checkbox.disabled = false; 654 checkbox.disabled = false;
643 } 655 }
644 656
645 // Position popup below anchor. 657 // Position popup below anchor.
646 var anchor = document.getElementById('tryjobchange-' + patchset); 658 var anchor = document.getElementById('tryjobchange-' + patchset);
647 var anchorRect = anchor.getBoundingClientRect(); 659 var anchorRect = anchor.getBoundingClientRect();
648 var popupElement = document.getElementById('trybot-popup'); 660 var popupElement = document.getElementById('trybot-popup');
649 popupElement.style.left = anchorRect.left + 'px'; 661 popupElement.style.left = anchorRect.left + 'px';
650 popupElement.style.top = anchorRect.bottom + 'px'; 662 popupElement.style.top = anchorRect.bottom + 'px';
651 popupElement.style.display = ''; 663 popupElement.style.display = '';
652 664
653 // Extra padding to allow for scrollbars. 665 // Extra padding to allow for scrollbars.
654 var scrollbarWidth = 20; 666 var scrollbarWidth = 20;
655 667
656 // Move popup as need to be on screen. 668 // Move popup as need to be on screen.
657 var popupRect = popupElement.getBoundingClientRect(); 669 var popupRect = popupElement.getBoundingClientRect();
658 if (popupRect.bottom > window.innerHeight) 670 if (popupRect.bottom > window.innerHeight)
659 popupElement.style.top = (anchorRect.bottom - (popupRect.bottom - window.inn erHeight) - scrollbarWidth) + 'px'; 671 popupElement.style.top = (anchorRect.bottom - (popupRect.bottom - window.inn erHeight) - scrollbarWidth) + 'px';
660 if (popupRect.right > window.innerWidth) 672 if (popupRect.right > window.innerWidth)
661 popupElement.style.left = (anchorRect.left - (popupRect.right - window.inner Width) - scrollbarWidth) + 'px'; 673 popupElement.style.left = (anchorRect.left - (popupRect.right - window.inner Width) - scrollbarWidth) + 'px';
662 } 674 }
663 675
664 /** 676 /**
665 * Updates the pending builders for the patchset. 677 * Updates the pending builders for the patchset.
666 * @param {String} issue The issue key. 678 * @param {String} issue The issue key.
667 * @param {String} patchset The patchset key. 679 * @param {String} patchset The patchset key.
668 * @param {String} xsrf_token Security token. 680 * @param {String} project Issue project.
669 */ 681 */
670 function M_updatePendingTrybots(issue, patchset, xsrf_token) { 682 function M_updatePendingTrybots(issue, patchset, project) {
671 // Find which builder are checked. 683 // Find which builder are checked.
672 var builders = []; 684 var builds = [];
673 var popup = jQuery('#trybot-popup'); 685 var popup = jQuery('#trybot-popup');
674 jQuery('input:checkbox', popup).each(function(i) { 686 jQuery('input:checkbox', popup).each(function(i) {
675 var self = jQuery(this); 687 var self = jQuery(this);
676 if (self.attr('checked')) 688 if (!self.attr('checked')) {
677 builders.push(self.attr('name')); 689 return;
690 }
691 var name = self.attr('name');
692 var nameParts = name.split(":", 2);
693 builds.push({
694 name: name,
695 master: nameParts[0],
696 builder: nameParts[1]
697 });
678 }); 698 });
679 699
680 // Build POST data for request. 700 var buildPromises = [];
681 var data = []; 701 var alerted403 = false;
682 data.push('xsrf_token=' + xsrf_token); 702
683 data.push('last_patchset=' + patchset); 703 for (var i = 0; i < builds.length; i++) {
684 data.push('builders=' + builders.join(',')); 704 var build = builds[i];
685 705 var buildPromise = M_scheduleBuild(
esprehn 2015/04/14 01:00:11 Is this making one http call per builder?
686 M_sendEditFlagsRequest(issue, data.join("&"), function(xhr) { 706 build.master, build.builder, issue, patchset, project);
687 if (xhr.status == 200) 707 buildPromise.then(null, function (reason) {
688 window.location.reload(); 708 console.log('Failed to schedule build ' + build.name + ":", reason);
709 if (reason.status == 403 && !alerted403) {
710 alerted403 = true;
711 alert(
712 "Permission denied. User " + rietveld.user.email + " does not " +
713 "have access to build bucket for master " + build.master
714 );
715 }
716 return Promise.reject(reason);
717 });
718 buildPromises.push(buildPromise);
719 }
720 // Reload after all builds are scheduled.
721 Promise.all(buildPromises).then(function () {
722 window.location.reload();
689 }); 723 });
690 724
691 // Hide the popup. 725 // Hide the popup.
692 jQuery('#trybot-popup').css('display', 'none'); 726 jQuery('#trybot-popup').css('display', 'none');
693 return true; 727 return true;
694 } 728 }
695 729
696 /** 730 /**
697 * Hide the pending builders popup. 731 * Hide the pending builders popup.
698 */ 732 */
699 function M_closePendingTrybots() { 733 function M_closePendingTrybots() {
700 jQuery('#trybot-popup').css('display', 'none'); 734 jQuery('#trybot-popup').css('display', 'none');
701 } 735 }
702 736
703 /** 737 /**
704 * Show or hide older try bot results. 738 * Show or hide older try bot results.
705 * @param {String} id The id of the div elements that holds all the try job 739 * @param {String} id The id of the div elements that holds all the try job
706 * a elements. 740 * a elements.
707 * @param makeVisible If true, makes older try bots visible. 741 * @param makeVisible If true, makes older try bots visible.
708 */ 742 */
709 function M_showTryJobResult(id, makeVisible) { 743 function M_showTryJobResult(id, makeVisible) {
710 // This set keeps track of the first occurance of each try job result for 744 // This set keeps track of the first occurance of each try job result for
711 // a given builder. The try job results are ordered reverse chronologically, 745 // a given builder. The try job results are ordered reverse chronologically,
712 // so we visit them from newest to oldest. 746 // so we visit them from newest to oldest.
713 var firstBuilderSet = {}; 747 var firstBuilderSet = {};
714 var oldBuildersExist = false; 748 var oldBuildersExist = false;
(...skipping 2942 matching lines...) Expand 10 before | Expand all | Expand 10 after
3657 httpreq.open("DELETE", base_url + this.issue_id + "/draft_message", true); 3691 httpreq.open("DELETE", base_url + this.issue_id + "/draft_message", true);
3658 httpreq.send(""); 3692 httpreq.send("");
3659 } 3693 }
3660 3694
3661 /** 3695 /**
3662 * Helper function that returns the dialog's HTML container. 3696 * Helper function that returns the dialog's HTML container.
3663 */ 3697 */
3664 M_draftMessage.prototype.get_dialog_ = function() { 3698 M_draftMessage.prototype.get_dialog_ = function() {
3665 return document.getElementById(this.id_dlg_container); 3699 return document.getElementById(this.id_dlg_container);
3666 } 3700 }
3701
3702 /**
3703 * Schedules a build on buildbucket.
esprehn 2015/04/14 01:00:11 This makes N http requests now when scheduling a b
3704 * @param {String} masterName Master name where the build will be scheduled,
3705 * without "master." prefix.
3706 * @param {String} builderName Builder name where the build will be scheduled.
3707 * @return A scheduled build as a promise.
3708 */
3709 function M_scheduleBuild(masterName, builderName, issue, patchset, project) {
3710 console.log("Scheduling a build: ", masterName, builderName);
3711 return M_ensureBuildBucketLoaded().then(function () {
3712 // Buildset tag: see https://cr-buildbucket.appspot.com/#docs/conventions
3713 var buildset = (
3714 "patch/rietveld/" + rietveld.preferredDomainName +
3715 "/" + issue + "/" + patchset
3716 );
3717 var callPut = gapi.client.buildbucket.put({
nodir 2015/04/13 16:56:48 This call has to be compatible with existing recip
3718 bucket: "master." + masterName,
3719 tags: [
3720 "builder:" + builderName,
3721 "buildset:" + buildset,
3722 "master:" + masterName,
3723 "user_agent:rietveld",
3724 ],
3725 parameters_json: JSON.stringify({
3726 builder_name: builderName,
3727 changes: [{
3728 author: {email: rietveld.user.email }
3729 }],
3730 properties: {
3731 clobber: false,
3732 issue: issue,
3733 master: masterName,
3734 patch_project: project,
3735 patch_storage: "rietveld",
3736 patchset: patchset,
3737 revision: "HEAD",
3738 rietveld: "https://" + rietveld.preferredDomainName
3739 }
3740 })
3741 });
3742
3743 return callPut.then(function (resp) {
3744 var build = resp.result.build;
3745 console.log("Scheduled build", build.id);
3746 return build;
3747 });
3748 });
3749 }
3750
3751 /**
3752 * Loads buildbucket api.
3753 * @return A promise with no value.
3754 */
3755 function M_ensureBuildBucketLoaded() {
3756 // remove this
3757 return M_ensureGapiAuthorized().then(function () {
3758 var root = "https://" + rietveld.buildbucketHostname + "/_ah/api";
3759 return gapi.client.load("buildbucket", "v1", null, root);
3760 });
3761 }
3762
3763 /**
3764 * Acquires an access token to talk to Google APIs.
3765 * @return A promise with no value.
3766 */
3767 function M_ensureGapiAuthorized() {
3768 // API authentication and Rietveld authentication are orthogonal
3769 // but we try to merge them so users don't notice the difference.
3770 // Rietveld authentication is considered primary:
3771 // * API authentication does not happen until Rietveld user is not logged in.
3772 // * When authenticating to Google APIs, we enforce API user to match
3773 // Rietveld user by email.
3774 if (!rietveld.user) {
3775 throw "This function may not be called if Rietveld user is not logged in";
3776 }
3777 if (!rietveld.gapi.clientId) {
3778 var msg = "ClientID is not configured for this Rietveld instance";
3779 alert(msg);
3780 throw msg;
3781 }
3782
3783 var authOptions = {
3784 client_id: rietveld.gapi.clientId,
3785 scope: "email",
3786 immediate: true,
3787 // Enfoce API user to match Rietveld user.
3788 login_hint: rietveld.user.email
3789 };
3790 return gapi.auth.authorize(authOptions).then(null, function () {
3791 console.log("non-immediate gapi.auth.authorize failed");
3792 // If immediate (windowless) authorization fails, try non-immediate one.
3793 authOptions.immediate = false;
3794 return gapi.auth.authorize(authOptions);
3795 });
3796 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698