NuGet - disallow overwriting packages (with same name and version number) - nuget-server

I have setup a custom NuGet server for my company. It all works great - I can publish, view packages, etc.
My only concern is that I can publish a package with the same name and version number, thereby overwriting the existing package. This is not ideal and I would like the NuGet server to return an error if a package with the same name and version already exists.
Any clues on how I can accomplish this?

I would also greatly appreciate disallowing to overwrite existing packages. However, it does not seem to be possible using the NuGet server out of the box. A similar feature request has been closed about two years ago.
But looking at the source code opens some options. Have a look at the CreatePackage()-method. It uses an IPackageAuthenticationService to check if the specified package is allowed to be added (only checks the API Key) and a IServerPackageRepository to actually add the package:
// Make sure they can access this package
if (Authenticate(context, apiKey, package.Id))
{
_serverRepository.AddPackage(package);
WriteStatus(context, HttpStatusCode.Created, "");
}
Both are passed in using constructor injection so it is easy to extend the behaviour by passing custom implementations (Modify the Ninject bindings for that).
At first sight i would go for a custom IServerPackageRepository. The current implementation uses IFileSystem.AddFile(...) to add the package. You can use IFileSystem.FileExists(...) to check whether the package already exists.
From a continuous integration perspective it makes totally sense to disallow overwriting an existing package since NuGet follows Semantic Versioning. Thus, a new build should incorporate a bugfix, a new feature or a breaking change.
I would choose to allow overwriting snapshots/pre-releases, however.
Update: It seems v2.8 will have an option allowOverrideExistingPackageOnPush which defaults to true for backwards compatibility. It has been comitted with 1e7345624d. I realized that after forking. Seems i was too late again ;-)

I ran into the same problem. I run my own SymbolSource server. I decided to maintain a log of published packages. Before I publish a package, I can check the log to see if it has already been published and then not publish it. This is all done in an MS-DOS batch file. See below.
#echo off
rem Requires that the Visual Studio directory is in your
rem PATH environment variable. It will be something like:
rem C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE
rem API key for publishing to SymbolSource server
set apiKey=<<<GUID>>>
rem URL of the SymbolSource web app
set lib=http://<<<address>>>
rem Path to a simple text file on a file share - which happens to be the
rem same place that the SymbolSource server web app is published.
set log=\\<<<path>>>\publish_log.txt
rem Path to the Visual Studio solution that contains the projects to be published.
set sln=..\<<<solution name>>>.sln
rem Build all projects in the solution.
devenv %sln% /rebuild Debug
rem Delete packages produced during last run.
del *.nupkg
rem Each line in projects.txt is a path to a .csproj file that we want to
rem create a nuget package for. Each .csproj file has a corresponding .nuspec
rem file that lives in the same directory.
for /F %%i in (projects.txt) do nuget.exe pack %%i -IncludeReferencedProjects -Prop Configuration=Debug -Symbols
rem Delete any local packages that have already been published.
for /F %%i in (%log%) do if exist %%i del %%i
for %%F in (".\*.symbols.nupkg") do nuget push %%~nxF %apiKey% -source %lib%
rem Log information about published packages so, in the next run,
rem we can tell what has been published and what has not.
for %%F in (".\*.symbols.nupkg") do echo %%~nxF >> %log%

I wrote a PowerShell script that deletes the existing package version but only if it matches the version which I wish to push:
param (
[string]$buildconfiguration = "Debug"
)
function Update-Package ([string]$package,[string]$version,[string]$path)
{
dotnet nuget delete $package $version -s https://<mynugetserver>/nuget -k <my access code if used> --non-interactive
dotnet nuget push "$path\bin\$buildconfiguration\$package.$version.nupkg" -s https://<mynugetserver>/nuget -k <my access code if used>
}
Update-Package -package "My.Package" -version "2.2.0" -path "MyPackage"
The major drawback to this is the possibility of a change to the package while forgetting to update the package version in the NuSpec or vsproj package section as well as forgetting to change the version number in the script file.
I would never use this technique on a public NuGet server.
I also use a version of this file that doesn't push, just deletes which is used as a PowerShell task in my Azure DevOps (VSTS) build.
I know that NuGet says it will list versions available, but I didn't really feel like writing a script which could read back the result of a list to determine if the version number that I'm building already exists.
The one good thing is that if the new version number doesn't exist, the CLI call to delete doesn't complain too much and no package version is affected.

Related

Use VS Online "Command Line" task with relative paths

I'm trying to use the new VS Online Build process and, specifically, package my app as a NuGet package.
To do so I have to execute something like "nuget pack". My source code is in GitHub and there, under the root folder, I have a folder called .nuget with all necessary to do the packaging.
I've added to my build template a "Command Line" step with this parameters:
tool: C:\a\9ea8689c\myusername\myproject.nuget\nuget.exe
arguments: pack
Everything else is defaults, included working folder. This works. However when I've tried to replace the absolute path for something like ".nuget/nuget.exe" or .nuget/nuget.exe or even changing the working folder to .nuget and from there just type "nuget.exe" it repeatedly fails because it can't find the tool.
Am I missing something? Should the task work with relative paths?
You should use absolute paths leveraging TFS Environment Variables like TF_BUILD_BUILDDIRECTORY.
In a batch file it is something like
echo Launching my own NuGet copy
%TF_BUILD_BUILDDIRECTORY%\myproject.nuget\nuget.exe
This worked for me: $(BUILD.SOURCESDIRECTORY)\NuGet.exe.
Notice the dot instead of underscore. I have put my NuGet.exe in the main repository folder, in my case NuGet 3.5 Beta.

Using AsConfigured and still be able to get UnitTest results in TFS

So I am running into an issue when I go to build my projects using tfs build controller using the Output location "AsConfigred" it will not detect my unit tests. Let me give a little info on my setup.
TFS 2013 Update 2, Default Process Template
Here is a few screenshots that can hopefully help fill in what I can't in typing. I am copying my build out to a file share on our network so that we can use other utilities use the output. I don't want to use "PerProject" or "SingleFolder" because they mess up the file structure we have configured (These both will run the tests). So i have the files copy to folder names "SingleOutputFolder" which is a child of the DropLocation. I would like to be able to run from the drop folder or run from the bin folder for each of my tests (I don't care which). However it doesn't seem to detect/run ANY of the tests. Any help would be greatly appreciated. Please let me know if you need any additional information.
I have tried using ***test*.dll, Install\SingleFolderOutput**.test.dll, and $(TF_BUILD_DROPLOCATION)\Install\SingleFolderOutput*test*.dll
But I am not sure what variables are available and understand where the scope of its execution is.
Given that you're using Build Output location set to AsConfigured you have to change the default values of the Test sources spec setting to allow build to find the test libraries in the bin folders. Here's an example.
If the full path to the unit test libraries is:
E:\Builds\7\<TFS Team Project>\<Build Definition>\src\<Unit Test Project>\bin\Release\*test*.dll
use
..\src\*UnitTest*\bin\*\*test*.dll;
This question was asked on MSDN forums here.
MSDN Forums Suggested Workaround
The suggested workaround in the accepted answer (as of 8 a.m. on June 20) is to specify the full path to the test projects' binary folders: For example:
C:\Builds\{agentId}\{teamProjectName}\{buildDefinitionName}\src\{solutionName}\{testProjectName}\bin*\Debug\*test*.dll*
which really should have been shown as
{agentWorkingFolder}\src\{relativePathToTestProjectBinariesFolder}\*test*.dll
However this approach is very brittle, for the following reasons:
Any new test projects you add to the solution will not be executed until you add them to the build definition's list of test sources:
It will break under any of the following circumstances:
the build definition is renamed
the working folder in build agent properties is modified
you have multiple build agents, and a different agent than the one you specified in {id} runs the build
Improved Workaround
My workaround mitigates the issues listed in #2 (can't do anything about #1).
In the path specified above, replace the initial part:
{agentWorkingFolder}
with
..
so you have
..\src\{relativePathToTestProjectBinariesFolder}\*test*.dll
This works because the internal working directory is apparently the \binaries\ folder that is a sibling of the \src\ folder. Navigating up to the parent folder (whatever it is named, we don't care) and back in to \src\ before specifying the path to the test projects binaries does the trick.
Note: If you have multiple test projects, you add additional entries, separated with semicolons:
..\src\{relativePathToTestProjectONEBinariesFolder}\*test*.dll;..\src\{relativePathToTestProjectTWOBinariesFolder}\*test*.dll;..\src\{relativePathToTestProjectTHREEBinariesFolder}\*test*.dll;
What I ended up doing was adding a post build event to copy all of the test.dll into the staging location folder in the specific build that is basically equivalent to where it would go on a SingleFolder build and do that on each test project.
if "$(TeamBuildOutDir)" == "" (
echo "Building Interactively not in TFS"
) else (
echo "Building in TFS"
xcopy "$(TargetDir)*.*" "$(TeamBuildBinaries)\" /Y /E /S
)
MSBUILD parameter in the build def that told it to basically drop in the folder that TFS looks for them.
/p:TeamBuildBinaries="$(TF_BUILD_BINARIESDIRECTORY)"
Kept the default Test assembly file specification:
**\*test*.dll
View this link for the information on the variable that I used and what relative path it exists at.
Another solution is to do the reverse.
Leave all of the files in the root so that all of the built in functionality works. There is more than just test execution in there. What about static code analysis, impact analysis..among others. You would have to do something custom for them all.
Instead use a pre-drop powershell script to create your Install arrangement from the root files.
If it is an application then you can use the _ApplicationFolder Nuget package to create an _PublishApplications folder same as you get for web applications.

TFS changeset/revision information offline

I'm looking for a way to extract changeset number or revision number information from a TFS workspace at build time, while potentially offline from the TFS server.
The specific problem is as follows. I am attempting to port a cross-platform (Linux/g++/make and Windows/VS2010) C++ codebase from SVN to TFS. As part of the build process, the Makefile (on the Linux side) or VS build properties are set up to automatically create a 'version' file, that contains the filtered output of the svnversion or SubWCRev command. This information is used to report not only the revision number and modification state of the main executable, but also of the component static libraries that are built and are used to link against.
A complicating issue is that our TFS server is on the end of a VPN in another state, and is not reliably available. It is important to be able to build the code locally while not connected to the TFS server. The TF command-line tool, as far as I can tell, insists on attempting to establish a connection, or reports that I do not have permission to access the remote resource.
The current VS/TortoiseSVN approach just has a custom build step (executes after "PreBuildEvent"), which calls SubWCRev and then filters the output to make it compatible with the output from svnversion, finally writing it to a header file:
set bv=$(IntDir)\$(ProjectName)_bv.h
SubWCRev "$(SolutionDir)\..\$(ProjectName)" | sed "s/:/ /g" | gawk "/revision/ {r=%24(NF)} /modifications/ {m=\"M\"} END {print \"#define $(ProjectName)_bv \\\"\" r m \"\\\"\"}" > "%bv%.tmp"
fc "%bv%" "%bv%.tmp" 2>nul >nul || copy "%bv%.tmp" "%bv%"
Can anyone offer a similarly straightforward solution for use with intermittently-connected TFS?
Any help is much appreciated!

Embed a build number / build ID into a project in Visual Studio 2008

There has been confusion a few times with my testers somehow getting old builds of my project to test, and then reporting on fixed bugs.
How can I embed a build ID into my project? Current time of build, or simply starting at 1 and incrementing every time the program is built would work. Then in the game UI, the build number will be printed so there is no ambiguity as to which version of the software the tester is using.
I've googled around for an answer and asked on IRC, but everything I've found seems to pertain only to C#.
Any ideas? On Unix, I would just modify the Makefile, and have some oneliner insert the value I need into the source.
Right now my best idea is to figure out how the VS build process works, then write a python script to run first and edit the source to update the build number.
Ok, here is my quick and dirty and ugly solution.
I have a buildid.txt file, this is a text file with only an integer value for buildid.
I have increment-buildid.bat
#echo off
for /f %%a in (buildid.txt) do (
echo %%a
set /a num=%%a
)
echo %num%
set /a num += 1
echo %num% > buildid.txt
echo int buildid = %num%; > buildid.c
To muck about with the Visual Studio build process, right click solution in solution explorer, -> properties -> Build Events -> Pre-Build Events.
Now that I know how do do this, I can put in my zip + scp script in post build events for instant upload! (not sure of that's blocking or not, will have to test it)
edit: the upload is indeed blocking. that is, debugging does not start until the upload has finished. I just have another batch to upload using scp as I feel the need to. Double click it, and it is sent to remote server.
To solve the same problem, I wrote a small utility that generates a header file containing a date/time stamp in a #define. It runs as a pre-build step and the main project includes the generated header. Then you can include the stamp in a sign-on banner or the like.
In my projects I also generate a version resource, then move it into an .rc2 file (manually written resources) and modify it to include the generated header and update the version appropriately.

Batch/CMD: Adding files to Startup list

How can a batch file lists itself in the startup list of Windows???
It doesn't matter if it goes from the registry or not.
IF with the registry, please give also the command to DELETE the registry entry.
This should work under all versions from ME to 7 please.
Otherwise just XP/Vista/7.
Thanks.
Not sure i understand you, but if what you want is an easy way to execute a command/batch on startup, why not just put it in the All Users\Startup folder?
To do so programatically would just mean copying a file to that directory.
For example, in Windows Vista, the full path of that directory is:
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
(you can use replace the beginning of the line with %ProgramData% or %AllUsers%\ProgramData to make it more global - such as when Windows is installed on D:).
I do not use windows7 (might get a check at the beta shortly), but I think the correct place will always be better taken from the registry, because of the Windows versions being localized. My own version of C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup here looks more like "C:\Documents and Settings\All Users\Menu Démarrer\Programmes\Démarrage" (from XP, of course)
-10 for programmers using hard-coded directory names (yes, some installers will create english/different language directories at installation).
-1 for Microsoft localising directory names...
Anyhow here is a snipet for this, valid for XP at least:
commonstartup.cmd
#echo off
for /F "tokens=3 delims= " %%k in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" /v "Common Startup"^| findstr /i /c:"Common Startup"') do set StartUp=%%k
echo StartUp="%StartUp%"
___Notes_____
1: Because reg.exe from Windows2000 and XP have different command arguments, maybe the W7 one has changed too so test it before set and forget.
2: To get a list of all the system directories, issue the command: reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" and read the lines. You might want to change the "Common Startup" for something else, if things are so different with W7.
3: There is also a personal/user list within HKEY_CURRENT_USER if you want this to be usable by some users only.
xcopy C:\Users\NAME\Desktop\Batch.bat C:\ProgramData\Microsoft\Windows\"Start Menu"\Programs\StartUp /O /X /E /H /K
is the correct command for windows 10. simply change the the second path to your version, and remember whenever there is a space, place a " before the word before the space, and after the word after it.
however, it MUST be opened in administrator, so after some research, i found that a batch file could be used to start a different batch file and run it in administrative mode:
runas /user:administrator C:\data\mybatchfile.bat
that should work!