From f86270976da0eaa960c5bc9b69a3ce7857bfa1c6 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Wed, 19 Apr 2017 14:27:47 -0700 Subject: [PATCH] Simplified development setup * Renamed `Enable-LocalTestme.ps1` to `Setup-DevEnvironment.ps1` * Made `Setup-DevEnvironment.ps1` run migrations * Updated README * Removed Windows SDK Dependency * Changed hostname to localhost instead of nuget.localtest.me * Updated the Gallery's version of ServerCommon --- .vs/config/applicationhost.config | 4 +- README.md | 98 ++++++------------ build.ps1 | 6 +- src/NuGetGallery/NuGetGallery.csproj | 2 +- src/NuGetGallery/Web.config | 2 +- tools/Enable-LocalTestMe.ps1 | 147 --------------------------- tools/Setup-DevEnvironment.ps1 | 120 ++++++++++++++++++++++ 7 files changed, 157 insertions(+), 222 deletions(-) delete mode 100644 tools/Enable-LocalTestMe.ps1 create mode 100644 tools/Setup-DevEnvironment.ps1 diff --git a/.vs/config/applicationhost.config b/.vs/config/applicationhost.config index ebd2bbfc2f..8c1b6087eb 100644 --- a/.vs/config/applicationhost.config +++ b/.vs/config/applicationhost.config @@ -158,8 +158,8 @@ - - + + diff --git a/README.md b/README.md index 0aa8e2ae8a..43a262c1e1 100644 --- a/README.md +++ b/README.md @@ -10,79 +10,42 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope ## Build and Run the Gallery in (arbitrary number) easy steps -1. Prerequisites. Install these if you don't already have them: - 1. Visual Studio 2015 - Custom install so that you may also install Microsoft SQL Server Data Tools. This will provide the LocalDB that Windows Azure SDK requires. - 2. PowerShell 2.0 (comes with Windows 7+) - 3. [NuGet](http://docs.nuget.org/docs/start-here/installing-nuget) - 4. [Windows Azure SDK](http://www.microsoft.com/windowsazure/sdk/) +1. Prerequisites: + 1. Visual Studio 2017 - Install the following `Workloads`: + * ASP.NET and web development + * Azure development + 2. PowerShell 4.0 2. Clone it! - ```git clone git@github.com:NuGet/NuGetGallery.git``` + ```PS C:\Code> git clone git@github.com:NuGet/NuGetGallery.git``` 3. Build it! ``` - cd NuGetGallery - .\build + PS C:\Code> cd NuGetGallery + PS C:\Code\NuGetGallery> .\build ``` -4. Set up the website in IIS Express! - 1. We highly recommend using IIS Express. Use the [Web Platform Installer](http://microsoft.com/web) to install it if you don't have it already (it comes with recent versions of VS and WebMatrix though). Make sure to at least once run IIS Express as an administrator. - 2. In an ADMIN powershell prompt, run the `.\tools\Enable-LocalTestMe.ps1` file. It allows non-admins to host websites at: `http(s)://nuget.localtest.me`, it configures an IIS Express site at that URL and creates a self-signed SSL certificate. For more information on `localtest.me`, check out [readme.localtest.me](http://readme.localtest.me). However, because [Server Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication) is not supported in the Network Shell on versions of Windows before 8, you must have at least Windows 8 to run this script successfully. - 3. If you're having trouble, go to the _Project Properties_ for the Website project, click on the _Web_ tab and change the URL to `localhost:port` where _port_ is some port number above 1024. - -5. Create the Database! - - There are two ways you can create the databases. From Visual Studio 2015 or from the command line. - - 1. From Visual Studio 2015 - 1. Open Visual Studio 2015 - 2. Open the Package Manager Console window - 3. Ensure that the Default Project is set to `NuGetGallery` - 4. Open the NuGetGallery.sln solution from the root of this repository. ***Important:*** Make sure the Package Manager Console has been opened once before you open the solution. If the solution was already open, open the package manager console and then close and re-open the solution (from the file menu) - 5. Run the following command in the Package Manager Console: - - ``` powershell - Update-Database -StartUpProjectName NuGetGallery -ConfigurationTypeName MigrationsConfiguration - ``` - If this fails, you are likely to get more useful output by passing `-Debug` than `-Verbose`. - 2. From the command line. ***Important:*** You must have successfully built the Gallery (step 3) for this to succeed. - * Run `Update-Databases.ps1` in the `tools` folder to migrate the databases to the latest version. - * To Update both databases, Nuget Gallery and Support Request, run this command - ``` powershell - .\tools\Update-Databases.ps1 -MigrationTargets NugetGallery,NugetGallerySupportRequest - ``` - * To update only the Nuget Gallery DB, run this - ``` powershell - .\tools\Update-Databases.ps1 -MigrationTargets NugetGallery - ``` - * And to update only the Support Request DB, run this - ``` powershell - .\tools\Update-Databases.ps1 -MigrationTargets NugetGallerySupportRequest - ``` - * Additionally you can provide a `-NugetGallerySitePath` parameter to the `Update-Databases.ps1` script to indicate that you want to perform the migration on a site other than the one that is built with this repository. - -6. When working with the gallery, e-mail messages are saved to the file system (under `~/App_Data`). - * To change this to use an SMTP server, edit `src\NuGetGallery\Web.Config` and add a `Gallery.SmtpUri` setting. Its value should be an SMTP connection string, for example `smtp://user:password@smtpservername:25`. - * To turn off e-mail confirmations, edit `src\NuGetGallery\Web.Config` and change the value of `Gallery.ConfirmEmailAddresses` to `false`. - -7. Ensure the 'NuGetGallery' project (under the Frontend folder) is set to the Startup Project - - -That's it! You should now be able to press Ctrl-F5 to run the site! - -Be aware that you might detect a change in the __applicationhost.config__: - -Unfortunately Visual Studio will replace the relative path with an absolute path. The committed applicationhost.config-file is currently the easiest way to setup the localtest.me-binding for IIS Express. - -However, you can force Git to ignore the change with this command: +4. Set up the website! + + ```PS C:\Code\NuGetGallery> .\tools\Setup-DevEnvironment.ps1``` +5. Ensure the `NugetGallery` project is the StartUp Project and press `F5` to run the site! That's it! + +When working with the gallery, e-mail messages are saved to the file system (under `~/App_Data`). +You can use an SMTP server instead by editing `src\NuGetGallery\Web.Config` and adding a `Gallery.SmtpUri` +setting. Its value should be an SMTP connection string, such as: `smtp://user:password@smtpservername:25`. +You can also turn off e-email confirmations by changing the value of `Gallery.ConfirmEmailAddresses` to `false` +in the `src\NugetGallery\Web.Config` file. + +Visual Studio may modify the `applicationhost.config` file. You can force git to ignore changes to this file +with: git update-index --assume-unchanged .vs/config/applicationhost.config You can undo this with this command: - git update-index --no-assume-unchanged .vs/config/applicationhost.config + git update-index --no-assume-unchanged .vs/config/applicationhost.config -This should help to prevent unwanted file commits. - +This should help prevent unwanted file commits. + ## Contribute If you find a bug with the gallery, please visit the [Issue tracker](https://github.com/NuGet/NuGetGallery/issues) and create an issue. If you're feeling generous, please search to see if the issue is already logged before creating a @@ -122,8 +85,7 @@ This is the Git workflow we're currently using: ### Setting up -1. Clone and checkout the following branches (to make sure local copies are made): ' -2. '. +Clone and checkout the `dev` branch. ### When starting a new feature/unit of work. @@ -136,13 +98,13 @@ This is the Git workflow we're currently using: git pull dev 2. __Create a topic branch to do your work.__ - You must work in topic branches, in order to help us keep our features isolated and easily moved between branches. + You must work in topic branches to help us keep our features isolated and easily moved between branches. Our policy is to start all topic branches off of the 'dev' branch. - Branch names should use the following format '[user]-[bugnumber]-[shortdescription]'. If there is no bug yet, + Branch names should use the following format '[user]-[bugnumber]'. If there is no bug yet, create one and assign it to yourself! git checkout dev - git checkout -b anurse-123-makesuckless + git checkout -b anurse-123 3. __Do your work.__ Now, do your work using the following highly accurate and efficient algorithm :) @@ -160,7 +122,7 @@ This is the Git workflow we're currently using: 5. if (moreWorkToDo) go to #3.1 else go to #4. 4. __Start a code review.__ - Start a code review by pushing your branch up to GitHub (```git push origin anurse-123-makesuckless```) and + Start a code review by pushing your branch up to GitHub (```git push origin anurse-123```) and creating a Pull Request from your branch to ***dev***. Wait for at least someone on the team to respond with: ":shipit:" (that's called the "Ship-It Squirrel" and you can put it in your own comments by typing ```:shipit:```). @@ -171,7 +133,7 @@ This is the Git workflow we're currently using: git checkout dev git pull origin dev - git merge anurse-123-makesuckless + git merge anurse-123 ... resolve conflicts ... git push origin dev diff --git a/build.ps1 b/build.ps1 index 587eae59bf..87cae72f03 100644 --- a/build.ps1 +++ b/build.ps1 @@ -9,10 +9,10 @@ param ( [string]$SemanticVersion = '1.0.0-zlocal', [string]$Branch, [string]$CommitSHA, - [string]$BuildBranch = '1c479a7381ebbc0fe1fded765de70d513b8bd68e' + [string]$BuildBranch = '37ff6e758c38b3f513af39f881399ce85f4ff20b' ) -# For TeamCity - If any issue occurs, this script fail the build. - By default, TeamCity returns an exit code of 0 for all powershell scripts, even if they fail +# This script should fail the build if any issue occurs. trap { Write-Host "BUILD FAILED: $_" -ForegroundColor Red Write-Host "ERROR DETAILS:" -ForegroundColor Red @@ -81,7 +81,7 @@ Invoke-BuildStep 'Set version metadata in AssemblyInfo.cs' { Invoke-BuildStep 'Building solution' { $SolutionPath = Join-Path $PSScriptRoot "NuGetGallery.sln" - Build-Solution $Configuration $BuildNumber -MSBuildVersion "14" $SolutionPath -SkipRestore:$SkipRestore -MSBuildProperties "/p:MvcBuildViews=true" ` + Build-Solution $Configuration $BuildNumber -MSBuildVersion "15" $SolutionPath -SkipRestore:$SkipRestore -MSBuildProperties "/p:MvcBuildViews=true" ` } ` -ev +BuildErrors diff --git a/src/NuGetGallery/NuGetGallery.csproj b/src/NuGetGallery/NuGetGallery.csproj index 7748f904e2..e726a26a20 100644 --- a/src/NuGetGallery/NuGetGallery.csproj +++ b/src/NuGetGallery/NuGetGallery.csproj @@ -2091,7 +2091,7 @@ True 80 / - https://nuget.localtest.me + https://localhost False False diff --git a/src/NuGetGallery/Web.config b/src/NuGetGallery/Web.config index 83ac6c197b..5816b78c90 100644 --- a/src/NuGetGallery/Web.config +++ b/src/NuGetGallery/Web.config @@ -51,7 +51,7 @@ - + diff --git a/tools/Enable-LocalTestMe.ps1 b/tools/Enable-LocalTestMe.ps1 deleted file mode 100644 index 14b63eb921..0000000000 --- a/tools/Enable-LocalTestMe.ps1 +++ /dev/null @@ -1,147 +0,0 @@ -param([string]$Subdomain="nuget", [string]$SiteName = "NuGet Gallery", [string]$SitePhysicalPath, [string]$MakeCertPath, [string]$AppCmdPath) - -function Get-SiteFQDN() {return "$Subdomain.localtest.me"} -function Get-SiteHttpHost() {return "$(Get-SiteFQDN):80"} -function Get-SiteHttpsHost() {return "$(Get-SiteFQDN):443"} - -function Get-SiteCertificate([string] $Store, [switch] $UseLocalMachine) { - [string] $level = if($UseLocalMachine) {'LocalMachine'} else {'CurrentUser'} - return @(dir -l "Cert:\$level\$Store" | where {$_.Subject -eq "CN=$(Get-SiteFQDN)"}) -} - -function Find-MakeCert() { - # Find Windows SDK - $SDKVersion = dir 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows' | - where { $_.PSChildName -match "v(?\d+\.\d+)" } | - foreach { New-Object System.Version $($matches["ver"]) } | - sort -desc | - select -first 1 - if(!$SDKVersion) { - throw "Could not find Windows SDK. Please install the Windows SDK before running this script, or use -MakeCertPath to specify the path to makecert.exe" - } - $SDKRegKey = (Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v$SDKVersion" -ErrorAction SilentlyContinue) - if ($SDKRegKey -eq $null) { - $SDKRegKey = (Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v$SDKVersion`A" -ErrorAction SilentlyContinue) - } - $WinSDKDir = $SDKRegKey.InstallationFolder - $xArch = "x86" - if($env:PROCESSOR_ARCHITECTURE -eq "AMD64") { - $xArch = "x64" - } - - [string] $candidatePath = Join-Path $WinSDKDir "bin\$xArch\makecert.exe" - - # If registry scan finds wrong path, try the Azure extension's makecert - if(!(Test-Path $candidatePath)) { - if($xArch = "x64"){ - $candidatePath = "${env:ProgramFiles(x86)}/Microsoft Visual Studio 14.0\Common7\IDE\Extensions\Microsoft\Microsoft Azure Data Lake Tools for Visual Studio 2015\2.0.6000.0\CppSDK\SDK\bin\makecert.exe" - } else { - $candidatePath = "${env:ProgramFiles}/Microsoft Visual Studio 14.0\Common7\IDE\Extensions\Microsoft\Microsoft Azure Data Lake Tools for Visual Studio 2015\2.0.6000.0\CppSDK\SDK\bin\makecert.exe" - } - } - - return $candidatePath -} - -function Initialize-SiteCertificate() { - Write-Host "Generating a Self-Signed SSL Certificate for $(Get-SiteFQDN)" - & $MakeCertPath -r -pe -n "CN=$Subdomain.localtest.me" -b "`"$([DateTime]::Now.ToString("MM\/dd\/yyy"))`"" -e "`"$([DateTime]::Now.AddYears(10).ToString("MM\/dd\/yyy"))`"" -eku '1.3.6.1.5.5.7.3.1' -ss root -sr localMachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 - - [object] $cert = Get-SiteCertificate -Store 'Root' -UseLocalMachine - if($cert -eq $null) { - throw "Failed to create an SSL Certificate" - } - - return $cert -} - -function Invoke-Netsh() { - $argStr = $([String]::Join(" ", $args)) - Write-Verbose "netsh $argStr" - $result = netsh @args - $parsed = [Regex]::Match($result, ".*Error: (\d+).*") - if($parsed.Success) { - $err = $parsed.Groups[1].Value - if($err -ne "183") { - throw $result - } - } else { - Write-Host $result - } -} - -if(!(([Security.Principal.WindowsPrincipal]([System.Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator"))) { - throw "This script must be run as an admin." -} - -if(!$SitePhysicalPath) { - $ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path; - $SitePhysicalPath = Join-Path $ScriptRoot "..\src\NuGetGallery" -} -if(!(Test-Path $SitePhysicalPath)) { - throw "Could not find site at $SitePhysicalPath. Use -SitePhysicalPath argument to specify the path." -} -$SitePhysicalPath = Convert-Path $SitePhysicalPath - -# Check for a cert -$siteCert = Get-SiteCertificate -Store 'Root' -if($siteCert.Length -eq 0) { - $siteCert = Get-SiteCertificate -Store 'Root' -UseLocalMachine - if($siteCert.Length -eq 0) { - Write-Host $MakeCertPath - if(!$MakeCertPath) { $MakeCertPath = Find-MakeCert } - Write-Host $MakeCertPath - - if(!(Test-Path $MakeCertPath)) { - throw "Could not find makecert.exe in $MakeCertPath!" - } - } -} - -# Find IIS Express -if(!$AppCmdPath) { - $IISXVersion = dir 'HKLM:\Software\Microsoft\IISExpress' | - foreach { New-Object System.Version ($_.PSChildName) } | - sort -desc | - select -first 1 - if(!$IISXVersion) { - throw "Could not find IIS Express. Please install IIS Express before running this script, or use -AppCmdPath to specify the path to appcmd.exe for your IIS environment" - } - $IISRegKey = (Get-ItemProperty "HKLM:\Software\Microsoft\IISExpress\$IISXVersion") - $IISExpressDir = $IISRegKey.InstallPath - if(!(Test-Path $IISExpressDir)) { - throw "Can't find IIS Express in $IISExpressDir. Please install IIS Express" - } - $AppCmdPath = "$IISExpressDir\appcmd.exe" -} - -if(!(Test-Path $AppCmdPath)) { - throw "Could not find appcmd.exe in $AppCmdPath!" -} - -# Enable access to the necessary URLs -# S-1-1-0 is the unlocalized version for: user=Everyone -Invoke-Netsh http add urlacl "url=http://$(Get-SiteHttpHost)/" "sddl=D:(A;;GX;;;S-1-1-0)" -Invoke-Netsh http add urlacl "url=https://$(Get-SiteHttpsHost)/" "sddl=D:(A;;GX;;;S-1-1-0)" - -$SiteFullName = "$SiteName ($(Get-SiteFQDN))" -echo $AppCmdPath list site $SiteFullName -$sites = @(&$AppCmdPath list site $SiteFullName) -if($sites.Length -gt 0) { - Write-Warning "Site '$SiteFullName' already exists. Deleting and recreating." - &$AppCmdPath delete site "$SiteFullName" -} - -&$AppCmdPath add site /name:"$SiteFullName" /bindings:"http://$(Get-SiteHttpHost),https://$(Get-SiteHttpsHost)" /physicalPath:$SitePhysicalPath - -if ($siteCert -eq $null) { - # Generate one - $siteCert = Initialize-SiteCertificate -} - -Write-Host "Using SSL Certificate: $($siteCert.Thumbprint)" - -# Set the Certificate -Invoke-Netsh http add sslcert hostnameport="$(Get-SiteHttpsHost)" certhash="$($siteCert.Thumbprint)" certstorename=Root appid="{$([Guid]::NewGuid().ToString())}" - -Write-Host "Ready! All you have to do now is go to your website project properties and set 'http://$(Get-SiteFQDN)' as your Project URL!" diff --git a/tools/Setup-DevEnvironment.ps1 b/tools/Setup-DevEnvironment.ps1 new file mode 100644 index 0000000000..d940c113c0 --- /dev/null +++ b/tools/Setup-DevEnvironment.ps1 @@ -0,0 +1,120 @@ +param([string]$SiteName = "NuGet Gallery", [string]$SitePhysicalPath, [string]$AppCmdPath) + +function Get-SiteFQDN() {return "localhost"} +function Get-SiteHttpHost() {return "$(Get-SiteFQDN):80"} +function Get-SiteHttpsHost() {return "$(Get-SiteFQDN):443"} + +function Get-SiteCertificate() { + return @(dir -l "Cert:\LocalMachine\Root" | where {$_.Subject -eq "CN=$(Get-SiteFQDN)"}) +} + +function Initialize-SiteCertificate() { + Write-Host "Generating a Self-Signed SSL Certificate for $(Get-SiteFQDN)" + + # Create a new self-signed certificate. New-SelfSignedCertificate can only create + # certificates into the My certificate store. + $myCert = New-SelfSignedCertificate -DnsName $(Get-SiteFQDN) -CertStoreLocation "Cert:\LocalMachine\My" + + # Move the newly created self-signed certificate to the Root store. + $rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root", "LocalMachine") + $rootStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) + $rootStore.Add($myCert) + $rootStore.Close() + + # Return the self-signed certificate from the Root store. + $cert = Get-SiteCertificate + + if($cert -eq $null) { + throw "Failed to create an SSL Certificate" + } + + return $cert +} + +function Invoke-Netsh() { + $argStr = $([String]::Join(" ", $args)) + $result = netsh @args + $parsed = [Regex]::Match($result, ".*Error: (\d+).*") + + if($parsed.Success) { + $err = $parsed.Groups[1].Value + if($err -ne "183") { + throw $result + } + } else { + Write-Host $result + } +} + +if(!(([Security.Principal.WindowsPrincipal]([System.Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator"))) { + throw "This script must be run as an admin." +} + +Write-Host "[BEGIN] Setting up IIS Express" -ForegroundColor Cyan + +$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +if(!$SitePhysicalPath) { + $SitePhysicalPath = Join-Path $ScriptRoot "..\src\NuGetGallery" +} +if(!(Test-Path $SitePhysicalPath)) { + throw "Could not find site at $SitePhysicalPath. Use -SitePhysicalPath argument to specify the path." +} +$SitePhysicalPath = Convert-Path $SitePhysicalPath + +# Find IIS Express +if(!$AppCmdPath) { + $IISXVersion = dir 'HKLM:\Software\Microsoft\IISExpress' | + foreach { New-Object System.Version ($_.PSChildName) } | + sort -desc | + select -first 1 + if(!$IISXVersion) { + throw "Could not find IIS Express. Please install IIS Express before running this script, or use -AppCmdPath to specify the path to appcmd.exe for your IIS environment" + } + $IISRegKey = (Get-ItemProperty "HKLM:\Software\Microsoft\IISExpress\$IISXVersion") + $IISExpressDir = $IISRegKey.InstallPath + if(!(Test-Path $IISExpressDir)) { + throw "Can't find IIS Express in $IISExpressDir. Please install IIS Express" + } + $AppCmdPath = "$IISExpressDir\appcmd.exe" +} + +if(!(Test-Path $AppCmdPath)) { + throw "Could not find appcmd.exe in $AppCmdPath!" +} + +# Enable access to the necessary URLs +# S-1-1-0 is the unlocalized version for: user=Everyone +Invoke-Netsh http add urlacl "url=http://$(Get-SiteHttpHost)/" "sddl=D:(A;;GX;;;S-1-1-0)" +Invoke-Netsh http add urlacl "url=https://$(Get-SiteHttpsHost)/" "sddl=D:(A;;GX;;;S-1-1-0)" + +$SiteFullName = "$SiteName ($(Get-SiteFQDN))" + +$sites = @(&$AppCmdPath list site $SiteFullName) +if($sites.Length -gt 0) { + Write-Warning "Site '$SiteFullName' already exists. Deleting and recreating." + &$AppCmdPath delete site "$SiteFullName" +} + +&$AppCmdPath add site /name:"$SiteFullName" /bindings:"http://$(Get-SiteHttpHost),https://$(Get-SiteHttpsHost)" /physicalPath:$SitePhysicalPath + +Write-Host "[DONE] Setting up IIS Express" -ForegroundColor Cyan +Write-Host "[BEGIN] Setting SSL Certificate" -ForegroundColor Cyan + +# Ensure a certificate is bound to localhost's port 443. Generate a new +# self-signed certificate if necessary. +$siteCert = Get-SiteCertificate + +if ($siteCert -eq $null) { + $siteCert = Initialize-SiteCertificate +} + +Write-Host "Using SSL Certificate: $($siteCert.Thumbprint)" + +Invoke-Netsh http add sslcert hostnameport="$(Get-SiteHttpsHost)" certhash="$($siteCert.Thumbprint)" certstorename=Root appid="{$([Guid]::NewGuid().ToString())}" + +Write-Host "[DONE] Setting SSL Certificate" -ForegroundColor Cyan +Write-Host "[BEGIN] Running Migrations" -ForegroundColor Cyan + +& "$ScriptRoot\Update-Databases.ps1" -MigrationTargets NugetGallery,NugetGallerySupportRequest -NugetGallerySitePath $SitePhysicalPath + +Write-Host "[DONE] Running Migrations" -ForegroundColor Cyan \ No newline at end of file