Index: appengine/chromium_rietveld/static/script.js |
diff --git a/appengine/chromium_rietveld/static/script.js b/appengine/chromium_rietveld/static/script.js |
index eb4805fec1c2c9843aace1217a72e1fe96d25f00..0a7da1edd3b26ea4ee580762fdc3b0a05dad7a99 100644 |
--- a/appengine/chromium_rietveld/static/script.js |
+++ b/appengine/chromium_rietveld/static/script.js |
@@ -12,6 +12,18 @@ |
// See the License for the specific language governing permissions and |
// limitations under the License. |
+// Global Rietveld namespace. |
+var rietveld = { |
+ preferredDomainName: document.location.hostname, |
+ 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
|
+ // Current rietveld user. |
+ user: null, |
+ gapi: { |
+ // Client ID to use. |
+ clientId: null, |
+ } |
+}; |
+ |
// Generic helpers |
/** |
@@ -120,7 +132,7 @@ function M_editPatchsetTitle(issue, patchset, xsrf_token, |
var new_patchset_title = prompt( |
'Please enter the new title of Patch Set ' + patch_count, |
original_patchset_title); |
- if (new_patchset_title == null) { |
+ if (new_patchset_title == null) { |
return false; |
} else if (new_patchset_title == original_patchset_title) { |
// Do not make an HTTP req if the new specified title is exactly the same. |
@@ -627,7 +639,7 @@ function M_editFlags(issue) { |
} |
/** |
- * Edit the list of pending try jobs for the given patchset. |
+ * Edit the list of pending try jobs for the given patchset. |
* @param {String} patchset The patchset key. |
*/ |
function M_editPendingTryJobs(patchset) { |
@@ -656,38 +668,60 @@ function M_editPendingTryJobs(patchset) { |
// Move popup as need to be on screen. |
var popupRect = popupElement.getBoundingClientRect(); |
if (popupRect.bottom > window.innerHeight) |
- popupElement.style.top = (anchorRect.bottom - (popupRect.bottom - window.innerHeight) - scrollbarWidth) + 'px'; |
+ popupElement.style.top = (anchorRect.bottom - (popupRect.bottom - window.innerHeight) - scrollbarWidth) + 'px'; |
if (popupRect.right > window.innerWidth) |
- popupElement.style.left = (anchorRect.left - (popupRect.right - window.innerWidth) - scrollbarWidth) + 'px'; |
+ popupElement.style.left = (anchorRect.left - (popupRect.right - window.innerWidth) - scrollbarWidth) + 'px'; |
} |
/** |
* Updates the pending builders for the patchset. |
* @param {String} issue The issue key. |
* @param {String} patchset The patchset key. |
- * @param {String} xsrf_token Security token. |
+ * @param {String} project Issue project. |
*/ |
-function M_updatePendingTrybots(issue, patchset, xsrf_token) { |
+function M_updatePendingTrybots(issue, patchset, project) { |
// Find which builder are checked. |
- var builders = []; |
+ var builds = []; |
var popup = jQuery('#trybot-popup'); |
jQuery('input:checkbox', popup).each(function(i) { |
var self = jQuery(this); |
- if (self.attr('checked')) |
- builders.push(self.attr('name')); |
+ if (!self.attr('checked')) { |
+ return; |
+ } |
+ var name = self.attr('name'); |
+ var nameParts = name.split(":", 2); |
+ builds.push({ |
+ name: name, |
+ master: nameParts[0], |
+ builder: nameParts[1] |
+ }); |
}); |
- |
- // Build POST data for request. |
- var data = []; |
- data.push('xsrf_token=' + xsrf_token); |
- data.push('last_patchset=' + patchset); |
- data.push('builders=' + builders.join(',')); |
- |
- M_sendEditFlagsRequest(issue, data.join("&"), function(xhr) { |
- if (xhr.status == 200) |
- window.location.reload(); |
+ |
+ var buildPromises = []; |
+ var alerted403 = false; |
+ |
+ for (var i = 0; i < builds.length; i++) { |
+ var build = builds[i]; |
+ var buildPromise = M_scheduleBuild( |
esprehn
2015/04/14 01:00:11
Is this making one http call per builder?
|
+ build.master, build.builder, issue, patchset, project); |
+ buildPromise.then(null, function (reason) { |
+ console.log('Failed to schedule build ' + build.name + ":", reason); |
+ if (reason.status == 403 && !alerted403) { |
+ alerted403 = true; |
+ alert( |
+ "Permission denied. User " + rietveld.user.email + " does not " + |
+ "have access to build bucket for master " + build.master |
+ ); |
+ } |
+ return Promise.reject(reason); |
+ }); |
+ buildPromises.push(buildPromise); |
+ } |
+ // Reload after all builds are scheduled. |
+ Promise.all(buildPromises).then(function () { |
+ window.location.reload(); |
}); |
- |
+ |
// Hide the popup. |
jQuery('#trybot-popup').css('display', 'none'); |
return true; |
@@ -701,7 +735,7 @@ function M_closePendingTrybots() { |
} |
/** |
- * Show or hide older try bot results. |
+ * Show or hide older try bot results. |
* @param {String} id The id of the div elements that holds all the try job |
* a elements. |
* @param makeVisible If true, makes older try bots visible. |
@@ -3664,3 +3698,99 @@ M_draftMessage.prototype.discard = function(cb) { |
M_draftMessage.prototype.get_dialog_ = function() { |
return document.getElementById(this.id_dlg_container); |
} |
+ |
+/** |
+ * Schedules a build on buildbucket. |
esprehn
2015/04/14 01:00:11
This makes N http requests now when scheduling a b
|
+ * @param {String} masterName Master name where the build will be scheduled, |
+ * without "master." prefix. |
+ * @param {String} builderName Builder name where the build will be scheduled. |
+ * @return A scheduled build as a promise. |
+ */ |
+function M_scheduleBuild(masterName, builderName, issue, patchset, project) { |
+ console.log("Scheduling a build: ", masterName, builderName); |
+ return M_ensureBuildBucketLoaded().then(function () { |
+ // Buildset tag: see https://cr-buildbucket.appspot.com/#docs/conventions |
+ var buildset = ( |
+ "patch/rietveld/" + rietveld.preferredDomainName + |
+ "/" + issue + "/" + patchset |
+ ); |
+ var callPut = gapi.client.buildbucket.put({ |
nodir
2015/04/13 16:56:48
This call has to be compatible with existing recip
|
+ bucket: "master." + masterName, |
+ tags: [ |
+ "builder:" + builderName, |
+ "buildset:" + buildset, |
+ "master:" + masterName, |
+ "user_agent:rietveld", |
+ ], |
+ parameters_json: JSON.stringify({ |
+ builder_name: builderName, |
+ changes: [{ |
+ author: {email: rietveld.user.email } |
+ }], |
+ properties: { |
+ clobber: false, |
+ issue: issue, |
+ master: masterName, |
+ patch_project: project, |
+ patch_storage: "rietveld", |
+ patchset: patchset, |
+ revision: "HEAD", |
+ rietveld: "https://" + rietveld.preferredDomainName |
+ } |
+ }) |
+ }); |
+ |
+ return callPut.then(function (resp) { |
+ var build = resp.result.build; |
+ console.log("Scheduled build", build.id); |
+ return build; |
+ }); |
+ }); |
+} |
+ |
+/** |
+ * Loads buildbucket api. |
+ * @return A promise with no value. |
+ */ |
+function M_ensureBuildBucketLoaded() { |
+ // remove this |
+ return M_ensureGapiAuthorized().then(function () { |
+ var root = "https://" + rietveld.buildbucketHostname + "/_ah/api"; |
+ return gapi.client.load("buildbucket", "v1", null, root); |
+ }); |
+} |
+ |
+/** |
+ * Acquires an access token to talk to Google APIs. |
+ * @return A promise with no value. |
+ */ |
+function M_ensureGapiAuthorized() { |
+ // API authentication and Rietveld authentication are orthogonal |
+ // but we try to merge them so users don't notice the difference. |
+ // Rietveld authentication is considered primary: |
+ // * API authentication does not happen until Rietveld user is not logged in. |
+ // * When authenticating to Google APIs, we enforce API user to match |
+ // Rietveld user by email. |
+ if (!rietveld.user) { |
+ throw "This function may not be called if Rietveld user is not logged in"; |
+ } |
+ if (!rietveld.gapi.clientId) { |
+ var msg = "ClientID is not configured for this Rietveld instance"; |
+ alert(msg); |
+ throw msg; |
+ } |
+ |
+ var authOptions = { |
+ client_id: rietveld.gapi.clientId, |
+ scope: "email", |
+ immediate: true, |
+ // Enfoce API user to match Rietveld user. |
+ login_hint: rietveld.user.email |
+ }; |
+ return gapi.auth.authorize(authOptions).then(null, function () { |
+ console.log("non-immediate gapi.auth.authorize failed"); |
+ // If immediate (windowless) authorization fails, try non-immediate one. |
+ authOptions.immediate = false; |
+ return gapi.auth.authorize(authOptions); |
+ }); |
+} |