Get a folder / zip with files in changelist(s) in TFS with complete directory structure - deployment

Our team uses TFS to manage workflow in the following flow:
work item -> source control -> changelist -> manual deploy to servers
Is there any way to just get a list of files with complete directory structure for a given changelist? Ideally, I'd like to be able to select multiple changelists and/or work items to get all the changelists associated with a work item, and get a complete directory structure for files in the changelists/work items.
Any suggestions would be appreciated, thanks!

You can probably use the following code snippit to get started
Uri tfsUri = new Uri(#"http://server:8080/tfs");
string serverPath = #"$/Project";
//Connect to the project collection
var projectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(
tfsUri, new UICredentialsProvider());
//Get the source code control service.
var sourceControl = projectCollection.GetService<VersionControlServer>();
var history = sourceControl.QueryHistory(
serverPath, //Source control path to the item. Like $/Project/Path ...
LatestVersionSpec.Instance, //Search latest version
0, //No unique deletion id.
RecursionType.Full, //Full recursion on the path
null, //All users
new ChangesetVersionSpec("9829"), //From the 7 days ago ...
LatestVersionSpec.Instance, //To the current version ...
Int32.MaxValue, //Include all changes. Can limit the number you get back.
true, //Include details of items, not just metadata.
false //Slot mode is false.
);
//Enumerate of the changesets.
foreach (Changeset changeset in history.OfType<Changeset>().Take(1))
{
foreach (var change in changeset.Changes)
{
change.Item.ServerItem.Dump();
}
}

Related

What is the easiest way to import excel/google sheet spreadsheet to a Cloud Firestore database?

I need to import a large table of data into my database in one go. I currently have it as an Excel file but I am happy to copy it to Google sheets etc.
So far I've added a few entries manually directly via cloud firestore.
Is there a solution already out there to achieve this?
I think the easiest way to export table data into Firestore is to use a Google Apps Script Library (for Google Sheets).
Step 1
Make a Copy of THIS example Google Spreadsheet I created as an example
Step 2
From the menu of YOUR copy of the Example Google Spreadsheet from step 1, click Tools > Script Editor. This should open up the example Google App Script associated with the example spreadsheet.
Step 3
Follow the Steps for installing this library and then update the script with the following:
email
key
projectID
These variables are generated by going to the Google Service Accounts page. This will require that you already have a Firebase or Google Cloud account setup. I won't repeat all the steps that are already iterated in in the aforementioned Github writeup. Just follow them carefully, and realize that the private_key is THE ENTIRE KEY starting with -----BEGIN PRIVATE KEY-----\n, EVERYTHING in between, and ending with \n-----END PRIVATE KEY-----\n
Step 4
Insert a page on your spreadsheet that contains your data, and EDIT the script to use your new sheet name and your data. I have HEAVILY commented the script so it's pretty clear what almost every line of code is doing. For those of you that just want to peek at the Google App Script that's behind this spreadsheet, here's the code:
// Note this Script uses an external library as per this page:
// https://github.com/grahamearley/FirestoreGoogleAppsScript
// This solution requires a Google Spreadhseet and a Firebase Account
// FOLLOW THE INSTRUCTIONS ON THAT GITHUB REPO TO SETUP NEEDED API KEYS!!!
//Global Variables
const ss = SpreadsheetApp.getActiveSpreadsheet(); // Gets the active "workbook"
const sheet = ss.getSheetByName('Restaurants'); // CHANGE TO YOUR SHEET NAME
const headerRowNumber = 1; // If you have more than one row for your header, then change this value to number of header rows
// If you want to mark modified cells, then set up a trigger for the following function:
// Edit > Current Project Triggers > (+ Add Trigger) > On Edit Spreadsheet etc
function onEdit(e) {
var cell = ss.getActiveCell(); //This will also effectively get our row
var dataRange = sheet.getDataRange(); //This checks for all rows/columns with data
var modifiedCol = dataRange.getLastColumn()-1; //Our "modified" column should be the second to last
if (cell.getColumn() < modifiedCol && cell.getRow() > headerRowNumber) { //If we edit any cells to the left of our modified column and below our header...
var celltoMark = sheet.getRange(cell.getRowIndex(),modifiedCol) //Get the R/C cordinates of cell to place modified time
celltoMark.setValue(new Date()); //write timestamp to that cell
}
};
// This will parse any comma separated lists you create in any of your fields (useful for search words, or attributes, etc)
function listToArray(list) {
var ogArray = list.split(","); //Input is a comma separated list
let trimmedArr = ogArray.map(string => string.trim()); //Let's strip out the leading/trailing whitespaces if any
return trimmedArr; //return the cleaned array
}
function writeToFireStore() {
const email = 'sheets#yourprojectid.iam.gserviceaccount.com'; // CHANGE THIS!!!
const key = '-----BEGIN PRIVATE KEY-----\nYOURPRIVATEKEY\n-----END PRIVATE KEY-----\n'; // CHANGE THIS!!!
const projectID = 'yourprojectid'; // CHANGE THIS!!!
var firestore = FirestoreApp.getFirestore(email, key, projectID);
const collection = "MySpreadsheetData"; // Name of your Firestore Database "Collection"
var dataRange = sheet.getDataRange().offset(headerRowNumber, 0, sheet.getLastRow() - headerRowNumber); //this is your data range
var data = dataRange.getValues(); // this is an array of your datarange's values
var lastCol = dataRange.getLastColumn(); // this is the last column with a header
var newDoc = {}; // Instantiate your data object. Each one will become the data for your firestore documents
// r = row number in this case
for (let r = 0; r <= dataRange.getLastRow(); r++) {
//Logger.log("R = ",r);
var cellMod = dataRange.getCell(r+1, lastCol-1);
var cellFS = dataRange.getCell(r+1, lastCol);
var cellModVal = cellMod.getValue();
var cellFSVal = cellFS.getValue();
//
// IMPORTANT READ THIS IMPORTANT READ THIS IMPORTANT READ THIS IMPORTANT READ THIS IMPORTANT READ THIS!!!
// Well, read the line below...
if (r > 2) break; //Comment Out this line after you're done testing otherwise you'll write all your rows to firestore after every run
newDoc[r] = {
name : data[r][1],
category : data[r][2],
cuisine : data[r][3],
address: {
add1: data[r][4],
add2: data[r][5],
city: data[r][6],
state: data[r][7],
zip: data[r][8]
},
tel: data[r][9],
searchterms: listToArray(data[r][10]) //Let's turn a csv list into an array
}
// For the sake of efficiency and to save $, we WON'T create documents that have already been created...
// ...and we won't update documents that have a fireStore Timestamp that's newer than a Modified Timestamp
// If there's not firestore timestamp in our spreadsheet, then let's create firestore document and update firestore stamp:
if (!cellFSVal) {
var now = new Date(); //Generate timestamp right now
try {
firestore.createDocument(collection + "/" + data[r][0], newDoc[r]); // To Use Your Own Document ID
//Now let's insert a timestamp in our Firestore TS column of the sheet so we know it's been added to Firestore
cellFS.setValue(now);
Logger.log("Row ",r,"(",data[r][1],") is NEW and was added to FireStore Successfully");
} catch (e) {
Logger.log("Error: ",e," : Document with same name already existed in Firestore.");
}
}
//var if FS Timestamp exists but, the modified time stamp is greater, let's update the Firstore Document
else if ((cellFSVal) && (cellModVal > cellFSVal)) {
try {
firestore.updateDocument(collection + "/" + data[r][0], newDoc[r]);
//Now let's insert a timestamp in our Firestore TS column of the sheet so we know it's been updated to Firestore
cellFS.setValue(now);
Logger.log("Row ",r,"(",data[r][1],") updated/edited.");
} catch (e) {
Logger.log("Error: ",e," : Document existed, we tried updating it, but jack shit happened.");
}
}
else {
Logger.log("Row ",r,"(",data[r][1],") Already in Firestore & hasn't been modified. Skipped.");
}
}
}
Step 5
Once your script is modified to your needs, it's time to run the script. Simply save it (File > Save), then choose the function "writeToFireStore" from the "Select function" dropdown selector in the menu bar (in between the icon of the bug, and the lightbulb), then hit the PLAY icon (to the left of the bug icon). At this point, you will likely be prompted to accept permissions to run the script, (which you need to accept if you want to run the script). Once you've accepted the permissions, then run the "writeToFireStore" function again if it hasn't already run, and voila!
NOTES:
I created a function that automatically writes a Modified Timestamp to the second to last column in the target sheet, and when you run the function, writes a Firestore Timestamp (so you know which rows have been successfully exported to Firestore). This way, if you run the firestore function again, and you haven't changed the data on your sheet, it won't bother updating the database with the same data (and will save you money and/or server resources). For this functionality to work, you must setup project Triggers (which is explained inside the script in the comments).

Is there a REST endpoint to get a list of a project's task assignments in Project Server?

I've tried sending a request to
"/_api/ProjectServer/Projects('projectid')/Assignments", but all it does is return duplicates of the last assignment which is weird because the number of objects it returns is always equal to the number of assignments there are in the project.
Basically if I assign a resource to each of a hundred different tasks, the call returns 100 duplicates of the last task's assignment in the list.
I suspect it might be a bug, I'd appreciate it if someone could confirm or deny my assumption and/or let me know if there's any other way to retrieve the list of assignments in a project.
I didn't exactly know how to work with rest but I would like to provide you with litle lines of code using CSOM that, if I understood properly the question, it might help you:
private static void ListPublishedProjects()
{
// Get the list of projects on the server.
projContext.Load(projContext.Projects);
projContext.ExecuteQuery();
var proj = projContext.Projects.First(p => p.Name == "<project name>");
projContext.ExecuteQuery();
//You must ckeck out the project and load it's tasks
var draftProj = proj.CheckOut();
projContext.Load(draftProj.Tasks);
projContext.ExecuteQuery();
//Loop between all tasks
foreach (DraftTask task in draftProj.Tasks)
{
// Load all assignments in that task
projContext.Load(task.Assignments);
projContext.ExecuteQuery();
//Loop between al assignments
foreach (var assignment in task.Assignments)
{
projContext.Load(assignment.Owner, temp => temp.LoginName, temp => temp.Email);
projContext.Load(assignment.Resource);
projContext.ExecuteQuery();
Console.WriteLine("\n\t RESOURCE NAME:" + assignment.Resource.Name + " => " + assignment.ActualWork);
}
}
//Remember to publish and checkin the project when you finish your TODOs
draftProj.Publish(true);
draftProj.CheckIn(true);
QueueJob qJob = projContext.Projects.Update();
JobState jobState = projContext.WaitForQueue(qJob, 200);
}
}
Hope it helps,

How to get current changesetid on tfs build server?

I'm need to include changesetid in assemblyversions on TFS build server. I have created powershell script that updates [AssembliVersion] attributes is all assemblyinfo.cs.
How can I get current changeset for a folder in tfs?
currently I have something like this:
function GetLatestChangeset{
Param([string]$projectDir)
$changesetId = Get-TfsItemHistory $projectDir -Recurse -version:W | % ChangesetId | sort -Descending
if ($changesetId.Count -eq 0){
return 0
}
return $changesetId[0]
}
but it returns latest changeset id, not the one being built
If you are using the new build system you should be able to use the environment variable $env:BUILD_SOURCEVERSION
see : https://msdn.microsoft.com/en-us/library/vs/alm/build/scripts/variables
For XAML builds you could use TF_BUILD_SOURCEGETVERSION environment variable : https://msdn.microsoft.com/en-us/library/hh850448%28v=vs.120%29.aspx
If you are doing this within the scope of a gated check-in and the change has not yet been committed, there will be no 'current' changeset. We do something similar, but after the build has been committed and at that point then simply query the buildserver to get the latest build as below ;
var buildDetail = context.GetExtension<IBuildDetail>();
var vcserver = buildDetail.BuildServer.TeamProjectCollection.GetService<VersionControlServer>();
var queryHistoryResult = vcserver
.QueryHistory(versionControlPath, VersionSpec.Latest, 0,
RecursionType.Full, null, null, null, 1,
true, false)
.Cast<Changeset>()
.ToList();
if (!queryHistoryResult.Any())
{
throw new ChangesetNotFoundException("GetLatestChangesetActivity: No current changeset available.");
}
var changeset = queryHistoryResult.First();
which looks pretty much the same as your code.
To get the associated changeset summaries from the specified build, you can use InformationNodeConverters.GetAssociatedChangesets Method.

folder.getSheets() returning null?

I'm trying to go through and entire workspace and list each element in a treeview, but when I attempt to grab the sheets in the found folders the getSheets() method in the Folder class always returns null. Am I doing something wrong?
TreeItem<String> workspace = new TreeItem<>("Earls Workspace", workspaceIcon);
workspace.setExpanded(true);
try {
Workspace earlsWorkspace = SmartsheetConnector.getInstance().getSmartsheet().workspaces().getWorkspace(*****);
List<Sheet> sheets = earlsWorkspace.getSheets();
List<Folder> folders = earlsWorkspace.getFolders();
for (Folder folder : folders) {
TreeItem<String> item = new TreeItem<>(folder.getName(), new ImageView(folderIcon));
System.out.println("Folder: " + folder.getName());
List<Sheet> folderSheets = folder.getSheets(); <- problem is here
System.out.println(folderSheets.toArray());
for (Sheet sheet : folderSheets) {
TreeItem<String> subItem = new TreeItem<>(sheet.getName(), new ImageView(sheetIcon));
item.getChildren().add(subItem);
}
workspace.getChildren().add(item);
}
for (Sheet sheet : sheets) {
TreeItem<String> item = new TreeItem<>(sheet.getName(), new ImageView(sheetIcon));
workspace.getChildren().add(item);
}
sheetExplorer.setRoot(workspace);
} catch (SmartsheetException ex) {
ex.printStackTrace();
}
I've purposefully blocked out the workspace ID.
Since the Java SDK mimics the Smartsheet API, the GET workspace by ID function only returns high level folder information, and does not include the sheets within the folder, which is why it is returning null. There are two ways to solve your problem:
After getting a list of folders from your workspace, you can call the GET folder by ID function, and that will return a list of sheets within a folder.
Workspace earlsWorkspace = SmartsheetConnector.getInstance().getSmartsheet().workspaces().getWorkspace(*****);
List<Folder> folders = earlsWorkspace.getFolders();
for (Folder folder : folders) {
List<Sheet> folderSheets = SmartsheetConnector.getInstance().getSmartsheet().folders().getFolder(folder.getId()).getSheets();
for (Sheet sheet : folderSheets) {
System.out.println(sheet.getName());
}
}
Call the getHome() function. This will return a nested list of all Home objects, including sheets, workspaces and folders, and optionally reports and/or templates depending on the EnumSet that you pass in. You will have to go through the list of workspaces and find the one that you are interested in. From there, you can loop through the folders, and call folder.getSheets() to get a list of sheets within a folder.
Home home = SmartsheetConnector.getInstance().getSmartsheet().home().getHome(EnumSet.of(ObjectInclusion.TEMPLATES);
In the next release of the SDK, there will be an option to specify if you want to load all of the contents, including nested folders and nested sheets within a workspace, so you can easily call folder.getSheets(), just like you were doing, and get back a list of sheets.

How to copy the XPO files out of version control...partial code working, bizarre issue

I began upgrading our layers to Roll Up 7 while we still were developing in another environment with TFS turned on. We were at say version 1850, and by the time I finished, we were at 1900. So the goal is to merge in the 50 different check-ins into the completed RU7 environment. Each check-in can contain many different objects, and each object is stored in TFS as an XPO somewhere.
My code is 90% of the way there, but the issue arrises when copying the files out of the temp directory. When I look in the temp directory, the files aren't there, but somehow they're able to be accessed.
static void Job33(Args _args)
{
#File
SysVersionControlSystem sysVersionControlSystem = versioncontrol.parmSysVersionControlSystem();
SysVersionControlTmpItem contents;
SysVersionControlTmpChange change;
SysVersionControlTmpChange changes;
int i;
SysVersionControlTmpItem contentsAddition;
SysVersionControlTmpItem contentsItem;
str writePath;
Set permissionSet = new Set(Types::Class);
str fileName;
int n;
;
change = versioncontrol.getChangesHistory();
// BP deviation documented
changes.setTmp();
changes.checkRecord(false);
changes.setTmpData(change);
while select changes
order by changes.ChangeNumber asc
where changes.ChangeNumber > 1850
{
writePath = #'C:\TEMP\' + int2str(changes.ChangeNumber) + #'\';
contentsAddition = versioncontrol.getChangeNumberContents(changes.ChangeNumber);
n = 0;
while select contentsAddition
{
// HOW DOES THIS LINE ACCESS THE FILE BUT MY METHOD CAN NOT??
contentsAddition.viewFile();
//?????????????
// Write to appropriate directory
if(!WinAPI::pathExists(writePath))
WinAPI::createDirectory(writePath);
n++;
fileName = int2str(changes.ChangeNumber) + '_' + int2str(n) + '.xpo';
if (WinAPI::fileExists(contentsAddition.fileName(), false))
{
// Write to appropriate directory
if(!WinAPI::pathExists(writePath))
WinAPI::createDirectory(writePath);
WinAPI::copyFile(contentsAddition.fileName(), writePath + fileName, true);
info(strfmt("|%1|%2|", contentsAddition.fileName(), writePath + fileName));
}
}
info(strfmt("%1", changes.ChangeNumber));
}
}
Buried in Classes\SysVersionControlFilebasedBackEndTfs there is a .Net assembly that is used. I was able to use this to extract what I needed mixed in with the upper code. After I used this...my code from above started working strangely enough??
Somehow there was a file lock on the folder that I copied TO, that just wouldn't let me delete it until I closed AX...no big deal, but it suggests there is a tfsProxy.close() method or something I should have called.
Microsoft.Dynamics.Morphx.TeamFoundationServer.Proxy tfsProxy = new Microsoft.Dynamics.Morphx.TeamFoundationServer.Proxy();
;
tfsProxy.DownloadFile(contentsAddition.InternalFilename, changes.ChangeNumber, writePath + fileName);
So you are trying to just get the objects that were changed so you can import them into the new RU7 environment? Why not do this within Visual Studio directly? You can pull the XPOs from there based on the history of changesets since you started the RU7 upgrade.
Also, you should use branching for this. It would have been easy to just branch the new code in that way. Something you should look into for the future.