// // This origin of this script is somwhere from one of these links: // http://fogbugz.stackexchange.com/questions/4427/ // http://help.fogcreek.com/7868/integration-with-perforce?se_found=1 // // The original script did not have a copyright notice. // My modifications don't warrant copyright either. // // Permission to use, copy, modify, and distribute this software and its // documentation for any purpose, without fee, and without a written // agreement, is hereby granted, provided that the above notice, // this paragraph and the following two paragraphs appear in all copies, // modifications, and distributions. // // IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, // INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST // PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, // EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF // ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". THE AUTHOR HAS NO OBLIGATION // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. // /////////////////////////////////////////////////////////////////////////////// // begin configuration // // Change to top level URL for bugz server var BUGZ_URL = "https://YourFogBugzAccountName.fogbugz.com"; // ixRepository for this repo in FogBugz. var IXREPOSITORY = "3"; // FogBugz user id of user "Perforce" (create virtual account in FogBugz for edits by this trigger) var PerforceFbUserId = 23; // this is only used to automatically generate an API token var gFogBugzApiEmail = "YourFogBugzApiEmail"; var gFogBugzApiPassword = "YourFogBugzApiPassword"; // Perforce credentials var P4USERNAME = "YourP4UserName"; var P4PASSWORD = "YourP4Password"; var gFbApiTokenRegPath = "HKCU\\Software\\FbApi\\token"; // To debug, change 0 to 1 on the line below var LoggingEnabled = 0; // Localized strings used when sending the changelist to FogBugz. Translating // these will change how changelists appear in FogBugz var FB_CHANGELIST_LATIN = "Changelist"; var FB_FILES_LATIN = "Files"; var FB_FILE_LATIN = "File"; // // end configuration /////////////////////////////////////////////////////////////////////////////// var ChangeList = ""; var ServerPort = ""; var p4 = ""; var gFogBugzApiUrl = BUGZ_URL + "/api.asp"; var WshShell = new ActiveXObject("WScript.Shell"); var fso = new ActiveXObject("Scripting.FileSystemObject"); var tmpFile = GetTempFileName(); var sChangeLogInfo = ""; var bugIDlist; function ReadCmdLineAndInit() { var args = WScript.Arguments.UnNamed; if (args.length != 2) { WScript.Echo("usage cscript.exe [scriptname].js %changelist% %serverport% "); WScript.Quit(1); } ChangeList = args.item(0); ServerPort = args.item(1); p4 = "cmd /c p4 -p " + ServerPort + " -u " + P4USERNAME + " -P " + P4PASSWORD; // Get log information "p4 -p port -c client describe -s changenum" LogIt("ChangeList: " + ChangeList); LogIt("ServerPort: " + ServerPort); LogIt("P4USERNAME: " + P4USERNAME); LogIt("P4PASSWORD:" + P4PASSWORD); LogIt("p4: " + p4); } function GetChangeInfo() { // // Get Bug ID if it's there // WshShell.Run(p4 + " describe -s " + ChangeList + " > " + tmpFile, 0, true); var f = fso.opentextfile(tmpFile); if (!f.AtEndOfStream) { sChangeLogInfo = f.ReadAll(); sChangeLogInfo = sChangeLogInfo.replace("Affected files ...\r\n\r\n", ""); sChangeLogInfo = sChangeLogInfo.replace(/\.\.\. /g, ""); } f.close(); f = null; fso.DeleteFile(tmpFile); LogIt("sChangeLogInfo:"); LogIt(sChangeLogInfo); } function GetBugIdList() { var bugIDString = ""; var regex = /\s*\[CASE\s*[#:;= ]+((\d+[ ,:;#]*)+)\]/gim; var matches = regex.exec(sChangeLogInfo); while (matches != null) { LogIt("Found id matches in STDIN: " + matches.length); if (bugIDString.length > 0) bugIDString = bugIDString + ","; var curmatch = matches[1]; LogIt("curmatch: " + curmatch); bugIDString = bugIDString + curmatch; matches = regex.exec(sChangeLogInfo); } if (bugIDString.length == 0) { WScript.Quit(0); } LogIt("ixBug = " + bugIDString); bugIDString = bugIDString.replace(" ", ","); bugIDString = bugIDString.replace(":", ","); bugIDString = bugIDString.replace(";", ","); bugIDString = bugIDString.replace("#", ","); bugIDlist = bugIDString.split(","); } function GetChangelistFilesAndUpdateFbCheckins() { // // Get change description // WshShell.Run(p4 + " files @" + ChangeList + "," + ChangeList + " > " + tmpFile, 0, true); var sFile, sRev, sRev2; var http = new ActiveXObject("Microsoft.XMLHTTP"); if (http == undefined || http == null) http = new ActiveXObject("MSXML2.ServerXMLHTTP"); if (http == null || http == undefined) { LogIt("FAILURE! Couldn't create XMLHTTP object"); fso.DeleteFile(tmpFile); WScript.Quit(0); } var f = fso.OpenTextFile(tmpFile); LogIt("reading: " + tmpFile); while (!f.AtEndOfStream) { var sCurLine = f.ReadLine(); LogIt("Examining line: " + sCurLine); var regex = /([^#]*)#([^ ]*) -/g; var matches = regex.exec(sCurLine); while (matches != null) { LogIt("Match found!"); var sFile = matches[1]; sRev2 = matches[2]; sRev = sRev2 - 1; LogIt(sFile + " " + sRev + " " + sRev2); for (var ixBug in bugIDlist) { var curCase = bugIDlist[ixBug]; LogIt("cur case: " + curCase); if (!isNaN(curCase)) { var curCaseNum = new Number(curCase); if (curCaseNum > 0) { if (1) { LogIt("Adding files for Bug ID#" + curCaseNum + "..."); http.open("GET", BUGZ_URL + "/cvsSubmit.asp" + "?ixBug=" + curCaseNum + "&sFile=" + sFile + "&sPrev=" + sRev + "&sNew=" + sRev2 + "&ixRepository=" + IXREPOSITORY, false); http.Send(); if (http.responseText == "OK") { LogIt("SUCCESS! Bug change entered!"); } else { LogIt("FAILURE! Could not submit to server!"); LogIt("Status Code: " + http.status()); LogIt(http.responseText()); } } else { LogIt("skipped add..."); } } curCaseNum = null; } } matches = regex.exec(sCurLine); } } LogIt("finished reading"); f.Close(); f = null; fso.DeleteFile(tmpFile); http = null; } function PostChangelistToFogbugz() { var fbapi = new FogBugzAPI(); for (var ixBug in bugIDlist) { var curCase = bugIDlist[ixBug]; if (!isNaN(curCase)) { var curCaseNum = new Number(curCase); if (curCaseNum > 0) { if (1) { fbapi.updateCase(curCaseNum, sChangeLogInfo); } else { LogIt("skipped fb update..."); } } curCaseNum = null; } } // fbapi.logoff(); // don't logoff because it will invalidate the token fbapi = null; } function SimpleEscape(s) { return Replace(Replace(Replace(s, " ", "%20"), "(", "%28"), ")", "%29"); } function GetTempFileName() { var TemporaryFolder = 2; var tfolder = fso.GetSpecialFolder(TemporaryFolder); return tfolder + "\\" + fso.GetTempName(); } function LogIt(s) { if (LoggingEnabled) { if (0) { WScript.StdOut.WriteLine(s); } else { var TemporaryFolder = 2; var tfolder = fso.GetSpecialFolder(TemporaryFolder); var f = fso.OpenTextFile(tfolder + "\\p4log.txt", 8, true); f.WriteLine(s); f.Close(); f = null; } } } // FogBugz API wrapper class // http://fogbugz.stackexchange.com/fogbugz-xml-api function FogBugzAPI() { try { this.token = WshShell.RegRead(gFbApiTokenRegPath); } catch (e) { LogIt("fb ctor regread exception " + e); this.token = null; } this.fogBugzApiUrl = gFogBugzApiUrl; this.email = gFogBugzApiEmail; this.password = gFogBugzApiPassword; this.xhr = null; this.refcount = 0; if (null == this.token) this.logon(); } FogBugzAPI.prototype.logon = function() { var package = { cmd : "logon", email : this.email, password : this.password }; var xml = this.doFogBugzApiCall(package); var tokenNodes = xml.getElementsByTagName("token"); if (tokenNodes.length > 0) { this.token = tokenNodes[0].childNodes[0].nodeValue; try { WshShell.RegWrite(gFbApiTokenRegPath, this.token, "REG_SZ"); } catch (e) { LogIt("fb logon regwrite exception " + e); } LogIt("fb logon token: " + this.token); } xml = null; // release MSXML resources allocated in doFogBugzApiCall() } FogBugzAPI.prototype.logoff = function() { var package = { cmd: "logoff" }; var xml = this.doFogBugzApiCall(package); xml = null; } FogBugzAPI.prototype.updateCase = function(caseNumber, text) { var package = { cmd : "edit", ixBug : caseNumber, ixPersonEditedBy : PerforceFbUserId, sEvent : text }; try { LogIt("fb edit: " + caseNumber); var xml = this.doFogBugzApiCall(package); xml = null; // immediately release return value; we don't need it WScript.StdOut.WriteLine("FogBugz updated"); } catch (e) { LogIt("fb edit exception " + e); // email the case instead // SendMail(gFogBugzCaseEmail, // "Failed to update case " + caseNumber, // text); } } FogBugzAPI.prototype.doFogBugzApiCall = function(args) { this.refcount++; // add the token automatically for all calls except logon if (args["cmd"] != "logon") args.token = this.token; if (null == this.xhr) { this.xhr = new ActiveXObject("MSXML2.ServerXMLHTTP.6.0"); if (this.xhr == undefined || this.xhr == null) this.xhr = new ActiveXObject("MSXML2.ServerXMLHTTP"); if (this.xhr == undefined || this.xhr == null) this.xhr = new ActiveXObject("Microsoft.XMLHTTP"); if (this.xhr == null || this.xhr == undefined) { LogIt("FAILURE! FbApi couldn't create XMLHTTP object"); WScript.Quit(0); } } this.sendMultiFormRequest(args); var xmlResponse = this.xhr.responseXML; if (xmlResponse && xmlResponse.parsed) { // check for error var errorNodes = xmlResponse.getElementsByTagName("error"); if (errorNodes.length > 0) { var errorCode = errorNodes[0].attributes[0].childNodes[0].data; if (3 == errorCode) { // not logged on. LogIt("fbapi retry - not logged in."); xmlResponse = null; this.logon(); args.token = this.token; this.sendMultiFormRequest(args); xmlResponse = this.xhr.responseXML; } else { xmlResponse = null; LogIt(errorNodes[0].childNodes[0].nodeValue); } } } if (0 == --this.refcount) this.xhr = null; return xmlResponse; // set to null in the caller to release the object } FogBugzAPI.prototype.sendMultiFormRequest = function(args) { var MULTIPART_BOUNDARY = "x231IEEzGlxT781lt"; var CrLf = "\r\n"; var strRequest = ""; for (var property in args) { strRequest += "--" + MULTIPART_BOUNDARY + CrLf; strRequest += 'Content-Disposition: form-data; name="' + property + '"' + CrLf + CrLf + args[property] + CrLf; } strRequest += "--" + MULTIPART_BOUNDARY + "--"; this.xhr.open("POST", this.fogBugzApiUrl, false); this.xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + MULTIPART_BOUNDARY); this.xhr.setRequestHeader("Content-Length", strRequest.length); this.xhr.send(strRequest); } ReadCmdLineAndInit(); GetChangeInfo(); GetBugIdList(); GetChangelistFilesAndUpdateFbCheckins(); PostChangelistToFogbugz(); fso = null; WshShell = null;
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 12068 | sean | added a license of sorts to clear my inbox | ||
#2 | 10114 | sean |
final version. intall trigger as: FogBugzTrigger change-commit //... "cscript.exe /nologo %YourDir%\log BugDataP4.js %changelist% %serverport%" |
||
#1 | 10113 | sean | converted from vbs to js | ||
//guest/sean/p4FogBugzTrigger/logBugDataP4.vbs | |||||
#1 | 10112 | sean | original version of file that came from FogBugz support or FogBugz stackexchange |