TF unable to determine workspace - jenkins

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)

Related

environment specific db automation with tfs ci cd

I am trying automate db deployment using TFS CI/CD.
MSBuild for building the db which in turn creates dacpac
Winrm for db deployment to an on premise sql server
During build I am passing
MSBuild Arguments: /t:build /t:publish /p:SqlPublishProfilePath=db.publish.xml
Contents of db.publish.xml file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<IncludeCompositeObjects>True</IncludeCompositeObjects>
<TargetDatabaseName>dev_DB</TargetDatabaseName>
<DeployScriptFileName>db.sql</DeployScriptFileName>
<TargetConnectionString>Data Source=xxxx;Persist Security Info=True;User ID=sa;password=xxx;Pooling=False;MultipleActiveResultSets=False;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True</TargetConnectionString>
<ProfileVersionNumber>1</ProfileVersionNumber>
</PropertyGroup>
</Project>
My release is currently configured to dev. And QA.. the release is working fine for dev. And it is not working for QA.
One observation is: the SqlPublishProfilePath in build is referring to publish file which is referring to Dev.
I would like to use same dacpack file for dev. And QA. As I have mentioned specific publish file in the release task.
release configuration
In Publish Profile argument, usually specify the path to the Publish profile XML file on the target machine or on a UNC share that is accessible by the machine administrator's credentials.
It seems variable $(devpublishprofile) point to a path on your dev machine. You have two option to fix your issue:
Put the Publish profile in a UNC share, and specify variable $(devpublishprofile) to the UNC share where both Dev and QA can access.
Copy the Publish profile from Dev to QA, and locate it in the same path as variable $(devpublishprofile) points to.

How to set nuget server apikey in TFS build server

Question
How to set nuget source api key, so it works with TFS Build running "Network Service"?
Scenario
I have TFS build server and Build Definition that executes as "Network Service".
during the build i'm trying to publish nuget packages:
<Exec Command='"nuget.exe" push "mypackages\*.nupkg" -source http://mynugetserver/'/>
however, I don't know how to set apikey for my nuget server.
What have I tried
nuget setapi "myapikey" -source http://mynugetserver/
when I log in to build server and runthis command It saves the apikey for current user and it is ignored in TFS build that runs as network service. build.proj publishes the packages when executed manually since the key is saved for current user, but it doesn't work when I queue new tfs build.
I have tried to create custom nuget.config in the root directory for my TFS project builds 'C:\Builds\1\MyProject\nuget.config` and run
nuget setapi "myapikey" -source http://mynugetserver/ -ConfigFile "C:\Builds\1\MyProject\nuget.config"
but the build ends with message:
"Key not valid for use in specified state"
You can set the apiKey with push command directly like following format:
nuget push <packages> <apiKey> -source <nuget server url>
You could also create an individual proj file with following contents and then create a build definition to build it once:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="SetApiKey">
<Exec Command='nuget.exe setapikey xxxxx -source '/>
</Target>
</Project>
After this, you should be able to push the packages without apikey under "Network Service" and you can delete the build definition and proj file. I just tried it under my environment and it works.

Jenkins Build is giving error "workspace directory not valid"

I am trying to run a ear task with ant on Jenkins. The ant task builds successfully on my local machine.
However, when I try to build using Jenkins, I get the following error: "specified workspace is not valid". I tried hardcoding the workspace value, I tried getting it from the environment variables and I also tried to define it in a custom workspace and then accessing it.
But I keep getting the following error:
[subProjEAR] $ cmd.exe /C '"F:\Build\Ant\apache-ant-1.8.4\bin\ant.bat && exit %%ERRORLEVEL%%"'
Buildfile: E:\Jenkins\jobs\ProjectDev\workspace\subProjEAR\build.xml
init.env:
init.typedefs:
Trying to override old definition of task apt
init:
BUILD FAILED
E:\Jenkins\jobs\ProjDev\workspace\subProjEAR\build.xml:81: Specified workspace directory "E:\Jenkins\jobs\ProjDev\workspace" is not valid.
Total time: 0 seconds
Build step 'Invoke Ant' marked build as failure`
The code snipped around line 81 is as below
<target name="init" depends="init.env,init.typedefs" unless="init.executed">
<property name="init.executed" value="true" />
<fail unless="workspace" message="The workspace property needs to be set!" />
<dirname property="project.dir" file="${ant.file}" />
<property name="echo.metadata" value="false" />
<!-- Line below is no 81 -->
<mdimport workspace="${workspace}" pjdir="${project.dir}" echo="${echo.metadata}">
</mdimport>
<property name="archive.name" value="${project.name}.ear"/>
<property name="uri" value=""/>
</target>
I have created an environment variable named as WORKSPACE in my "local machine". Echoing prints it correctly ( C:\bea\user_projects\w4WP_workspaces\myProjWS). I have also set the WORKSPACE environment variable on the Jenkins machine. This is the same workspace as Jenkins. Echo on Jenkins prints the below:
Buildfile: E:\Jenkins\jobs\ProjectDev\workspace\subProjEAR\build.xml
init.env:
init.typedefs:
Trying to override old definition of task apt
init:
[echo] workspace : E:\Jenkins\jobs\ProjectDev\workspace
[echo] weblogic workspace : ${env.WORKSHOP_WORKSPACE}
BUILD FAILED
E:\Jenkins\jobs\ProjectDev\workspace\subProjEAR\build.xml:85: Specified workspace directory "E:\Jenkins\jobs\ProjectDev\workspace" is not valid.
Please help.
Best way to access WORKSPACE from ANT is through:
<property environment="env" />
<echo message="${env.WORKSPACE}" />
It doesn't seem like a Jenkins error, as Jenkins is able to successfully load the build file from that workspace. What is on line: 81 of that build.xml? The cause of the problem is there. Paste a snippet of your build file around line: 81
Edit after author comments
So, as I guessed, it wasn't anything to do with Jenkins. The issue is with <mdimport> task. So far, I've been able to find zero documentation about it, other that it comes from Weblogic
It is this task that is throwing the error. And considering that Weblogic and Jenkins have little to do with each other, I doubt you should be passing Jenkins's workspace for this task. It is probably looking for some Weblogic workspace.
Do you have a Weblogic workspace checked out under a subfolder in Jenkins, like my_checkout_folder? If so, maybe you should use ${workspace}/my_checkout_folder for that task?
In this issue from someone, they are reading ${workspace}/workspace.xml file
In this example (which looks very similar to yours), the value of workspace comes from environment variable WORKSHOP_WORKSPACE. Do you have that environment variable set on your local machine (the one where it works)?
Probable cause
It just came to me, while reviewing your question.
You said it works on local machine, but not on Jenkins.
If you are using the same ant file on both, means you are expecting an environment variable workspace to be present on your local machine, right? (Otherwise the same ant script wouldn't have worked on local)
Jenkins, when executing a build, has its own environment variable workspace which has nothing to do with the one you might be having on your local machine. Jenkins's workspace variable defines its job's working directory.
Even if you manually set a global workspace environment variable on Jenkins machine, it will be overwritten by the job's workspace everytime it runs.
Do the following test:
echo workspace environment variable on your local machine
in Jenkins job, use "execute shell" (or "execute windows batch command") to echo workspace from within the job.
Paste the result here. But you can also compare them yourself.

Using tf.exe to get code for different workspaces

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" />

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