Using tf.exe to get code for different workspaces - tfs

I'm developing code on my machine using one local workspace to get and check in code from TFS. I'm also building an automated CI app which uses my machine and needs to get the latest code. I use the following to get latest for the CI app:
tf get $/abc/abc /recursive /all
I want to use a different local workspace, a CI workspace, to get latest to avoid conflict with dev code. BUT using tf, I'm not able to specify a workspace when using the get command and the MSDN doc doesn't show anyway to do this. How can I use "tf get" to specify the workspace I want to use?

Prior to the tf get, change directory to one of the directories mapped in the CI workspace. tf get will base the server and workspace off of the details for the mapped drive.

I am also creating an automated build process and as part of this performing a 'clean' get i.e. renaming the current local workfolder and then re-getting the files from tfs to a clean copy of the work folder prior to performing full build and unit testing. I am using a Nant script in a separate folder and so couldn't just change directory (changing the current directory within Nant doesn't work for this problem). My solution was to create and run a batch file. Sample Nant script below:
<!--do a clean get to the solution folder-->
<target name="clean_get">
<!--Create the batch file-->
<property name ="batch.file" value="clean_get.bat" />
<echo file="${batch.file}" append="True">md ${workfolder.path}</echo>
<echo file="${batch.file}" append="True">${environment::newline()}cd ${workfolder.path}</echo>
<echo file="${batch.file}" append="True">${environment::newline()}tf get $/${project.name} /recursive</echo>
<!--Run the batch file-->
<exec program="clean_get.bat" >
</exec>
<!--Delete the batch file - we're done with it-->
<delete file="${batch.file}" failonerror="false" />

Related

TF unable to determine workspace

I get this error:
Unable to determine the workspace. You may be able to correct this by running 'tf workspaces /collection:TeamProjectCollectionUrl'.
I have checkout the workspace from Jenkins plugin (TFS-plugin). Then I run a MSBuild script where I want to checkout a file with this target:
<!-- Checkout before building -->
<Target Name="CheckoutUpdateVersion">
<Message Text="***** Checkout $(UpdateVersionFile) *****" />
<Exec WorkingDirectory="E:\WS\Jenkins\workspace\TD_-_Build_TFS\" Command="$(tf) checkout E:\WS\Jenkins\workspace\TD_-_Build_TFS\src\rubin\Scripts\update_version.sql" />
</Target>
I have checked so my workspace I mapped and it is mapped to the WorkingDirectory that is above.
Workspaces are tied to a machine and user. I suppose that the Jenkins agent is running with another user account which has no proper workspace definition.
Some tips:
you cannot use the same local directory in different workspaces
to define a workspace for build user account, logon using the latter or run-as Team Explorer (or run-as a Command Prompt and use TF to define the workspace)

How to generate document output from text files in TFS

We store various design documents within TFS in multimarkdown format. We also have an EXE process that can run to take those MMD files and generate PDF's from them - but just by getting the files from a local folder.
What we'd like to do is to have a process run "on-checkin", just as if you'd run an automatic build on checkin (i.e., ultimately calling msbuild to compile an application) but in our case we'd like it to be able to get a list of the checked in files and to process and generate an output of them. The result doesn't need to be in TFS because they're a build output, not the source.
I'm sure this should be somehow possible by taking the same approach as must be taken by the workflow for a "normal" build.
Has anybody done anything like this or can point me in a suitable direction please ?
You could use the exec task in MSBuild to invoke the exe and "build" your output. Create a file called something like buildDocs.proj and check it in to TFS possibly in a folder under the things you want to build. Use the MSbuild below as a guide.
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build">
<Exec Command='"My.exe" -My Paramiters' />
<ItemGroup>
<CopyItems Include="[path to output]\*.*" />
</ItemGroup>
<Copy SourceFiles="#(CopyItems)" DestinationFolder="$(OutDir)\SomeDir" />
</Target>
</Project>
The trick will be in identifying the various paths involved.
Use the default template to build the proj, just as you would a c# project. If you need to pass in additional Parameters to MSBuild you can do this from within the advanced section of the build definition process tab.

Specifying Tf.exe and MsBuild version in CC.Net

The problem
I am porting a build to a newer version of CC.Net (from 1.4.4 to a very recent version of 1.6). Previously the build mixed <msbuild /> tags with <exec /> tags that call batch files with tf.exe commands in them.
The build must have relied on the source control version being the same between the msbuild and tf.exe commands, because now I get an error on the tf.exe command:
Unable to determine the workspace.
When I launch a command prompt, with runas as the same user the service is using, and call the same command the batch file used (from the same directory), I get the same error.
However if I launch a Visual Studio 2010 command prompt, with runas, and call that command (from the same directory), I don't get the error.
This also makes sense, because the %UserProfile%\Local Settings\Application Data\Microsoft\Team Foundation\3.0\Cache\VersionControl.config file has the workspace mapping, but the ...\1.0\Cache\VersionControl.config file does not.
The solution I'd like to try
I can't simply change the PATH for the batch file because it actually needs to build against an older version of Visual Studio, due to licensing.
I think I could solve this by specifying the TFS/msbuild version used with the <msbuild /> tag. But I'm not sure how to go about this. I think it would be some sort of server configuration, but I haven't found anything about this in the docs.
My question is: How do I specify the version of TFS that Cruise Control uses for <msbuild /> tags? Or is there another way to solve this?
As far as I know, there is no way to specify this directly with msbuild. The way it is solved at our place is that we set the executable for msbuild to a bat-file that's basically a copy of the desired vcvars-script (used by Visual Studio Command Prompt to set up the environment).
<tasks>
<msbuild>
<executable>vcvars_VC10_amd64.bat</executable>
<workingDirectory>SomeFolder</workingDirectory>
<environment>
<variable name="foo" value="bar" />
</environment>
<projectFile>msbuild.proj</projectFile>
<buildArgs>/m:2 /p:Configuration=Release /p:Platform=x64 /v:normal</buildArgs>
<targets>Clean;Build;Test</targets>
<timeout>3600</timeout>
</msbuild>
</tasks>
Maybe you could get away with setting up your desired paths using the <environment> property as well.
I won't accept this solution, in case people have other solutions (or can solve the problem I directly asked about), but here's how I ended up solving my problem:
I added a workspace sync task to the top of the build tasks:
<tasks>
<exec>
<description>Sync the workspaces with the TFS server, for the version of TFS used in exec commands</description>
<executable>Tf.exe</executable>
<buildArgs>workspaces /s:$(TfsServer)</buildArgs>
</exec>
This works because in my scenario, I don't care about the versioning as much as I do about the workspaces getting out of sync.

Overriding the PostBuildEvent in TFSBuild.proj

I'm using currently VS 2010 and TFS 2008. In one of our solutions we have a .csproj file with the following:
<Target Name="BeforeBuild">
<TransformXml Source="..\..\..\ProjectX\ProjectXWebSite\ProjectXWebSite\Web.config" Transform="..\..\..\ProjectX\ProjectXWebSite\ProjectXWebSite\Web.$(Configuration).config" Destination="..\..\..\ProjectX\ProjectXWebSite\ProjectXWebSite\Web.$(Configuration).config.transformed" />
</Target>
<PropertyGroup>
<PostBuildEvent>xcopy "C:\Source\Projects\ProjectX\ProjectXWebSite\ProjectXWebSite\Web.$(Configuration).config.transformed" "C:\Source\Projects\ProjectX\ProjectXWebSite\ProjectXWebSite\Web.config" /R /Y</PostBuildEvent>
</PropertyGroup>
This works fine when building locally, but TFS is failing when it reaches <PostBuildEvent>, because this path is not available on the build machine. How can I get TFS to either skip the entire "BeforeBuild" or just set the <PostBuildEvent> to an empty string, so that the build will work successfully when built locally in VS2010 and via TFS 2008? I've tried within TFSBuild.proj to set <Target Name="BeforeBuild" />, but this doesn't work.
I'd recommend against using absolute pathnames in your projects - relative paths will make them relocatable and headaches like this will disappear.
i.e. In the post-build xcopy, just change C:\Source\Projects\ProjectX to ..\..\..\ProjectX
(You can't rely on always being able to put your code on C: - I've had to move my code from C: to D: or E: due to lack of disk space or installation of a new drive on several occasions, and using relative paths has made this process very easy. It's also very handy at times to be able to have multiple copies (different versions or branches) of your source code on your PC at once and still be able to build them all)

Can you do a TFS get without needing a workspace?

I'm trying to change our build scripts from using SourceSafe to TFS without using MsBuild (yet).
One hiccup is that the workspace directory is renamed and archived by the scripts, which makes TFS think it doesn't need to get any files. Even with the /force flag it just gets the directories without getting the source files.
I am currently using
TF.exe get "Product/Main/Project1" /force /recursive /noprompt
To save me managing workspaces in the scripts or using intermediate directories, does anyone know of a command that can get directories and code without needing a workspace?
It's not possible to run a tf get without a workspace. The reason is that the server needs to know the mapping between the server paths and the local paths.
If you are working with a large number of files, it is not a good idea to:
Create & Delete a new workspace every time
Or, Create a new workspace (and then never delete it)
The reason for this is that every time you do a Get, the server keeps track of which files, at which versions were downloaded to which workspace. If you never clean up these workspaces, then the table that stores this information will grow over time.
Additionally, if you are creating & deleting a workspace all the time, the server has to write all these rows, then delete them when you are done. This is unnecessary.
You really should try and reuse the same workspace each time. If you do, the server is very efficient about only sending you files that have changed since you last downloaded them. Even if your build is moving from one branch to another, you can use tf get /remap which is sometimes more efficient if the branches share common files.
Although it doesn't solve your problem, it is possible to list files and download files without a workspace.
To list files:
tf dir $/Product/Main/Project1 /R
To download a file:
tf view $/Product/Main/Project1/file.cs
With a creative batch file, you can string these two together with a FOR command. However I would recommend trying to solve your workspace problem first, since that is the way that TFS was intended to be used.
A workspace is a mapping between the source repository location and the filesystem location, so no you can't get away with not using a workspace. But you can easily set up and tear down a workspace when you need to.
Here is a simple TFS task i use to get my database source files from TFS prior to doing some text substitutions and building them into a database update package. You can easily translate this to whatever syntax your current build scripts require:
<Target Name="GetDatabaseSources">
<!-- clean out the folder before doing the fresh get of the database sources -->
<Folder.CleanFolder Path="$(DatabaseBuildBaseLocation)" Force="true"/>
<!-- create a workspace for the database source of the product -->
<CallTarget Targets="CreateDatabaseSourceWorkspace" />
<!-- get the database sources for the product -->
<Get TeamFoundationServerUrl="$(TeamFoundationServerUrl)" Workspace="$(DatabaseSourceWorkspaceName)" Recursive="true" Version="$(DatabaseSourceVersion)" Force="true" />
<!-- delete the workspace -->
<Exec Command="$(Tf) workspace /delete $(DatabaseSourceWorkspaceName) /server:$(TeamFoundationServerUrl) /noprompt " ContinueOnError="true" />
</Target>
<!-- creates and maps a temporary workspace for the database source of the product -->
<Target Name="CreateDatabaseSourceWorkspace">
<Exec Command="$(Tf) workspace /delete $(DatabaseSourceWorkspaceName) /server:$(TeamFoundationServerUrl) /noprompt " ContinueOnError="true" />
<Exec Command="$(Tf) workspace /new $(DatabaseSourceWorkspaceName) /server:$(TeamFoundationServerUrl) /noprompt" />
<Exec Command="$(Tf) workfold /unmap /workspace:$(DatabaseSourceWorkspaceName) $/" />
<Exec Command="$(Tf) workfold /map /workspace:$(DatabaseSourceWorkspaceName) /server:$(TeamFoundationServerUrl) $(DatabaseSourceLocation) "$(DatabaseBuildBaseLocation)"" />
</Target>
TFS is your source repository, but you didn't explicitly mention what your build scripts were designed for. You really should migrate them to a TFS build script, then you can simplify your build, for example you won't have to worry about mapping workspaces or getting the latest source code because TFS does that for you, all you have to worry about is any custom build steps and possibly archiving your build results.
Neno Loje created a small utility that does exactly what you need. To boot it can also remove any source control bindings from the solution and project file, should you need that.
C# has the VersionControlServer library and you can use VersionControlServer.GetItems call to fetch TFS Item. If the ItemType is File then call DownloadFile to retrieve the file.
I don't know why your buildscripts delete the workspace directories everytime. But to answer your question I don't think you can get source code from TFS without a workspace. Maybe you can try to create a worspace everytime before you to a get. The command is
tf.exe workspace /new
You could do this easily with SourceSafe: get any version to any specific directory you wanted. And it was very often very convenient. There are often occasions why one would want to do this. Shame (if) TFS does not support it, it is a missing functionality, imho.

Resources