From 479e5f4bd79703fb577bc207d6e84fdff8667866 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Sun, 11 Aug 2019 12:31:54 +0200 Subject: [PATCH 01/17] (GH-103) Pin Cake.Recipe to 1.0.0 --- setup.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cake b/setup.cake index 7f8f666..a825ba9 100644 --- a/setup.cake +++ b/setup.cake @@ -1,4 +1,4 @@ -#load nuget:https://www.myget.org/F/cake-contrib/api/v2?package=Cake.Recipe&prerelease +#load nuget:?package=Cake.Recipe&version=1.0.0 Environment.SetVariableNames(); From 2269d93915a114cd4e177c4780bc8a5b25ea4c47 Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Sun, 8 Sep 2019 20:43:38 +0100 Subject: [PATCH 02/17] (GH-105) Update NuGet metadata As per Twitter conversation with NuGet PM: https://twitter.com/adgrv/status/1127591787697061888 --- nuspec/nuget/Cake.Npm.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nuspec/nuget/Cake.Npm.nuspec b/nuspec/nuget/Cake.Npm.nuspec index 56dd83a..3a1a21d 100644 --- a/nuspec/nuget/Cake.Npm.nuspec +++ b/nuspec/nuget/Cake.Npm.nuspec @@ -12,7 +12,7 @@ https://github.com/cake-contrib/cake-npm https://cdn.jsdelivr.net/gh/cake-contrib/graphics@a5cf0f881c390650144b2243ae551d5b9f836196/png/cake-contrib-medium.png false - + cake npm cake-build cake-contrib https://github.com/cake-contrib/Cake.Npm/releases/tag/0.17.0 @@ -21,4 +21,4 @@ - \ No newline at end of file + From 7ec8b929d4b52677648abe6659bec7480c255d7e Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Mon, 9 Sep 2019 14:41:28 +0100 Subject: [PATCH 03/17] (GH-107) Switch to recipe.cake file This provides some immediate information that a repository is using Cake.Recipe, and it is a convention now followed by a number of other repositories. --- .appveyor.yml | 2 +- build.ps1 | 4 ++-- build.sh | 2 +- setup.cake => recipe.cake | 0 4 files changed, 4 insertions(+), 4 deletions(-) mode change 100644 => 100755 build.sh rename setup.cake => recipe.cake (100%) diff --git a/.appveyor.yml b/.appveyor.yml index 2b5f541..0d21fc4 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -27,4 +27,4 @@ branches: # Build Cache # #---------------------------------# cache: -- tools -> setup.cake \ No newline at end of file +- tools -> recipe.cake diff --git a/build.ps1 b/build.ps1 index bdfb32b..a6d30c4 100644 --- a/build.ps1 +++ b/build.ps1 @@ -35,7 +35,7 @@ http://cakebuild.net [CmdletBinding()] Param( - [string]$Script = "setup.cake", + [string]$Script = "recipe.cake", [string]$Target = "Default", [ValidateSet("Release", "Debug")] [string]$Configuration = "Release", @@ -181,4 +181,4 @@ if (!(Test-Path $CAKE_EXE)) { # Start Cake Write-Host "Running build script..." Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs" -exit $LASTEXITCODE \ No newline at end of file +exit $LASTEXITCODE diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 index a541ec1..d35bcf4 --- a/build.sh +++ b/build.sh @@ -11,7 +11,7 @@ NUGET_EXE=$TOOLS_DIR/nuget.exe CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe # Define default arguments. -SCRIPT="setup.cake" +SCRIPT="recipe.cake" TARGET="Default" CONFIGURATION="Release" VERBOSITY="verbose" diff --git a/setup.cake b/recipe.cake similarity index 100% rename from setup.cake rename to recipe.cake From a129ab9d8fa6be9a5ea9e109fb1556a467fd4aa7 Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Mon, 9 Sep 2019 14:42:26 +0100 Subject: [PATCH 04/17] (GH-107) Always run GitVersion So that build can run on other operating systems --- recipe.cake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/recipe.cake b/recipe.cake index a825ba9..413bf60 100644 --- a/recipe.cake +++ b/recipe.cake @@ -11,7 +11,8 @@ BuildParameters.SetParameters( repositoryName: "Cake.Npm", appVeyorAccountName: "cakecontrib", shouldRunDupFinder: false, - shouldRunCodecov: false); + shouldRunCodecov: false, + shouldRunGitVersion: true); BuildParameters.PrintParameters(Context); From 2264fc387ddda6c25bf7c5251390d40b39d5b2ab Mon Sep 17 00:00:00 2001 From: "D. Domig" Date: Tue, 1 Oct 2019 22:38:34 +0200 Subject: [PATCH 05/17] Fix typo --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 200e284..43a7ffd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Cake.Npm Addin This Addin for the Cake Build Automation System allows you to run [npm](https://www.npmjs.com/). -More about Cake at http://cakebuild.net +More about Cake at [cakebuild.net](http://cakebuild.net). [![License](http://img.shields.io/:license-mit-blue.svg)](http://cake-contrib.mit-license.org) @@ -28,7 +28,7 @@ More about Cake at http://cakebuild.net ## Chat Room -Come join in the conversation about Cake.Yeoman in our Gitter Chat Room +Come join in the conversation about Cake.Yeoman in our Gitter Chat Room. [![Join the chat at https://gitter.im/cake-contrib/Lobby](https://badges.gitter.im/cake-contrib/Lobby.svg)](https://gitter.im/cake-contrib/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -36,4 +36,4 @@ Come join in the conversation about Cake.Yeoman in our Gitter Chat Room Contributions are welcome. See [Contribution Guidelines]. -[Contribution Guidelines]: CONTRIBUTING.md \ No newline at end of file +[Contribution Guidelines]: CONTRIBUTING.md From b8bc0f8de47d6d4e7842c1b4ae9fd24cfdccd7e9 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Sun, 19 Jan 2020 16:06:20 +0100 Subject: [PATCH 06/17] Update bootstrapper --- build.ps1 | 121 ++++++++++++++++++++++++++++++++++++++---------------- build.sh | 89 ++++++++++++++++++++++++--------------- 2 files changed, 140 insertions(+), 70 deletions(-) diff --git a/build.ps1 b/build.ps1 index a6d30c4..9a6ade2 100644 --- a/build.ps1 +++ b/build.ps1 @@ -5,11 +5,14 @@ ########################################################################## <# + .SYNOPSIS This is a Powershell script to bootstrap a Cake build. + .DESCRIPTION This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) and execute your Cake build script with the parameters you provide. + .PARAMETER Script The build script to execute. .PARAMETER Target @@ -18,19 +21,22 @@ The build script target to run. The build configuration to use. .PARAMETER Verbosity Specifies the amount of information to be displayed. +.PARAMETER ShowDescription +Shows description about tasks. +.PARAMETER DryRun +Performs a dry run. .PARAMETER Experimental -Tells Cake to use the latest Roslyn release. -.PARAMETER WhatIf -Performs a dry run of the build script. -No tasks will be executed. +Uses the nightly builds of the Roslyn script engine. .PARAMETER Mono -Tells Cake to use the Mono scripting engine. +Uses the Mono compiler rather than the Roslyn script engine. .PARAMETER SkipToolPackageRestore Skips restoring of packages. .PARAMETER ScriptArgs Remaining arguments are added here. + .LINK -http://cakebuild.net +https://cakebuild.net + #> [CmdletBinding()] @@ -41,9 +47,10 @@ Param( [string]$Configuration = "Release", [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] [string]$Verbosity = "Verbose", + [switch]$ShowDescription, + [Alias("WhatIf", "Noop")] + [switch]$DryRun, [switch]$Experimental, - [Alias("DryRun","Noop")] - [switch]$WhatIf, [switch]$Mono, [switch]$SkipToolPackageRestore, [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] @@ -75,6 +82,15 @@ function MD5HashFile([string] $filePath) } } +function GetProxyEnabledWebClient +{ + $wc = New-Object System.Net.WebClient + $proxy = [System.Net.WebRequest]::GetSystemWebProxy() + $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials + $wc.Proxy = $proxy + return $wc +} + Write-Host "Preparing to run build script..." if(!$PSScriptRoot){ @@ -82,31 +98,15 @@ if(!$PSScriptRoot){ } $TOOLS_DIR = Join-Path $PSScriptRoot "tools" +$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" +$MODULES_DIR = Join-Path $TOOLS_DIR "Modules" $NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" $CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" $NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" $PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" $PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" - -# Should we use mono? -$UseMono = ""; -if($Mono.IsPresent) { - Write-Verbose -Message "Using the Mono based scripting engine." - $UseMono = "-mono" -} - -# Should we use the new Roslyn? -$UseExperimental = ""; -if($Experimental.IsPresent -and !($Mono.IsPresent)) { - Write-Verbose -Message "Using experimental version of Roslyn." - $UseExperimental = "-experimental" -} - -# Is this a dry run? -$UseDryRun = ""; -if($WhatIf.IsPresent) { - $UseDryRun = "-dryrun" -} +$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" +$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" # Make sure tools folder exists if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { @@ -116,8 +116,10 @@ if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { # Make sure that packages.config exist. if (!(Test-Path $PACKAGES_CONFIG)) { - Write-Verbose -Message "Downloading packages.config..." - try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { + Write-Verbose -Message "Downloading packages.config..." + try { + $wc = GetProxyEnabledWebClient + $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { Throw "Could not download packages.config." } } @@ -125,7 +127,7 @@ if (!(Test-Path $PACKAGES_CONFIG)) { # Try find NuGet.exe in path if not exists if (!(Test-Path $NUGET_EXE)) { Write-Verbose -Message "Trying to find nuget.exe in PATH..." - $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) } + $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." @@ -137,7 +139,8 @@ if (!(Test-Path $NUGET_EXE)) { if (!(Test-Path $NUGET_EXE)) { Write-Verbose -Message "Downloading NuGet.exe..." try { - (New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE) + $wc = GetProxyEnabledWebClient + $wc.DownloadFile($NUGET_URL, $NUGET_EXE) } catch { Throw "Could not download NuGet.exe." } @@ -160,16 +163,51 @@ if(-Not $SkipToolPackageRestore.IsPresent) { } Write-Verbose -Message "Restoring tools from NuGet..." - $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -PreRelease -OutputDirectory `"$TOOLS_DIR`" -Source https://www.myget.org/F/cake/api/v3/index.json" + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" if ($LASTEXITCODE -ne 0) { - Throw "An error occured while restoring NuGet tools." + Throw "An error occurred while restoring NuGet tools." } else { $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" } Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Restore addins from NuGet +if (Test-Path $ADDINS_PACKAGES_CONFIG) { + Push-Location + Set-Location $ADDINS_DIR + + Write-Verbose -Message "Restoring addins from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet addins." + } + + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Restore modules from NuGet +if (Test-Path $MODULES_PACKAGES_CONFIG) { + Push-Location + Set-Location $MODULES_DIR + + Write-Verbose -Message "Restoring modules from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet modules." + } + + Write-Verbose -Message ($NuGetOutput | out-string) + Pop-Location } @@ -178,7 +216,18 @@ if (!(Test-Path $CAKE_EXE)) { Throw "Could not find Cake.exe at $CAKE_EXE" } +# Build Cake arguments +$cakeArguments = @("$Script"); +if ($Target) { $cakeArguments += "-target=$Target" } +if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } +if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } +if ($ShowDescription) { $cakeArguments += "-showdescription" } +if ($DryRun) { $cakeArguments += "-dryrun" } +if ($Experimental) { $cakeArguments += "-experimental" } +if ($Mono) { $cakeArguments += "-mono" } +$cakeArguments += $ScriptArgs + # Start Cake Write-Host "Running build script..." -Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs" -exit $LASTEXITCODE +&$CAKE_EXE $cakeArguments +exit $LASTEXITCODE \ No newline at end of file diff --git a/build.sh b/build.sh index d35bcf4..4b690aa 100755 --- a/build.sh +++ b/build.sh @@ -1,14 +1,20 @@ #!/usr/bin/env bash -############################################################### -# This is the Cake bootstrapper script that is responsible for -# downloading Cake and all specified tools from NuGet. -############################################################### +########################################################################## +# This is the Cake bootstrapper script for Linux and OS X. +# This file was downloaded from https://github.com/cake-build/resources +# Feel free to change this file to fit your needs. +########################################################################## # Define directories. SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) TOOLS_DIR=$SCRIPT_DIR/tools NUGET_EXE=$TOOLS_DIR/nuget.exe -CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe +NUGET_URL=https://dist.nuget.org/win-x86-commandline/latest/nuget.exe +CAKE_VERSION=0.32.1 +CAKE_EXE=$TOOLS_DIR/Cake.$CAKE_VERSION/Cake.exe + +# Temporarily skip verification of addins. +export CAKE_SETTINGS_SKIPVERIFICATION='true' # Define default arguments. SCRIPT="recipe.cake" @@ -16,18 +22,15 @@ TARGET="Default" CONFIGURATION="Release" VERBOSITY="verbose" DRYRUN= -SHOW_VERSION=false SCRIPT_ARGUMENTS=() # Parse arguments. for i in "$@"; do case $1 in - -s|--script) SCRIPT="$2"; shift ;; -t|--target) TARGET="$2"; shift ;; -c|--configuration) CONFIGURATION="$2"; shift ;; -v|--verbosity) VERBOSITY="$2"; shift ;; -d|--dryrun) DRYRUN="-dryrun" ;; - --version) SHOW_VERSION=true ;; --) shift; SCRIPT_ARGUMENTS+=("$@"); break ;; *) SCRIPT_ARGUMENTS+=("$1") ;; esac @@ -35,48 +38,66 @@ for i in "$@"; do done # Make sure the tools folder exist. -if [ ! -d $TOOLS_DIR ]; then - mkdir $TOOLS_DIR +if [ ! -d "$TOOLS_DIR" ]; then + mkdir "$TOOLS_DIR" fi -# Make sure that packages.config exist. -if [ ! -f $TOOLS_DIR/packages.config ]; then - echo "Downloading packages.config..." - curl -Lsfo $TOOLS_DIR/packages.config http://cakebuild.net/bootstrapper/packages - if [ $? -ne 0 ]; then - echo "An error occured while downloading packages.config." - exit 1 - fi +# Print Mono version. +echo "Mono version:" +mono --version +echo "" + +########################################################################### +# INSTALL .NET CORE CLI +########################################################################### + +echo "Installing .NET CLI..." +if [ ! -d "$SCRIPT_DIR/.dotnet" ]; then + mkdir "$SCRIPT_DIR/.dotnet" fi +curl -Lsfo "$SCRIPT_DIR/.dotnet/dotnet-install.sh" https://dot.net/v1/dotnet-install.sh +sudo bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --version 2.1.400 --install-dir .dotnet --no-path +export PATH="$SCRIPT_DIR/.dotnet":$PATH +export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER=0 +"$SCRIPT_DIR/.dotnet/dotnet" --info + +########################################################################### +# INSTALL NUGET +########################################################################### # Download NuGet if it does not exist. -if [ ! -f $NUGET_EXE ]; then +if [ ! -f "$NUGET_EXE" ]; then echo "Downloading NuGet..." - curl -Lsfo $NUGET_EXE https://dist.nuget.org/win-x86-commandline/latest/nuget.exe + curl -Lsfo "$NUGET_EXE" $NUGET_URL if [ $? -ne 0 ]; then - echo "An error occured while downloading nuget.exe." + echo "An error occurred while downloading nuget.exe." exit 1 fi fi -# Restore tools from NuGet. -pushd $TOOLS_DIR >/dev/null -mono $NUGET_EXE install -ExcludeVersion -PreRelease -Source https://www.myget.org/F/cake/api/v3/index.json -if [ $? -ne 0 ]; then - echo "Could not restore NuGet packages." - exit 1 +########################################################################### +# INSTALL CAKE +########################################################################### + +if [ ! -f "$CAKE_EXE" ]; then + mono "$NUGET_EXE" install Cake -Version $CAKE_VERSION -OutputDirectory "$TOOLS_DIR" + if [ $? -ne 0 ]; then + echo "An error occurred while installing Cake." + exit 1 + fi fi -popd >/dev/null # Make sure that Cake has been installed. -if [ ! -f $CAKE_EXE ]; then +if [ ! -f "$CAKE_EXE" ]; then echo "Could not find Cake.exe at '$CAKE_EXE'." exit 1 fi +########################################################################### +# RUN BUILD SCRIPT +########################################################################### + # Start Cake -if $SHOW_VERSION; then - exec mono $CAKE_EXE -version -else - exec mono $CAKE_EXE $SCRIPT -verbosity=$VERBOSITY -configuration=$CONFIGURATION -target=$TARGET $DRYRUN "${SCRIPT_ARGUMENTS[@]}" -fi +exec mono "$CAKE_EXE" $SCRIPT --verbosity=$VERBOSITY --configuration=$CONFIGURATION --target=$TARGET $DRYRUN "${SCRIPT_ARGUMENTS[@]}" From b7b50079dacfe8cb4dcef482820b7bfc91dc8322 Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Mon, 10 Aug 2020 11:49:25 +0200 Subject: [PATCH 07/17] fixes GH-113 by setting RedirectStandardOutput as a default on NpmVersion. --- src/Cake.Npm/NpmVersionAliases.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Cake.Npm/NpmVersionAliases.cs b/src/Cake.Npm/NpmVersionAliases.cs index 6b16551..fc37253 100644 --- a/src/Cake.Npm/NpmVersionAliases.cs +++ b/src/Cake.Npm/NpmVersionAliases.cs @@ -33,7 +33,10 @@ public static string NpmVersion(this ICakeContext context) } AddinInformation.LogVersionInformation(context.Log); - var settings =new NpmVersionSettings(); + var settings = new NpmVersionSettings + { + RedirectStandardOutput = true + }; return new NpmVersionTool(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools, context.Log).Version(settings); } } From 3fd972d866d56be57bac0609ab5e9484a199786e Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Sat, 29 Aug 2020 11:09:31 +0200 Subject: [PATCH 08/17] Disable publishing to MyGet --- recipe.cake | 1 + 1 file changed, 1 insertion(+) diff --git a/recipe.cake b/recipe.cake index 413bf60..1a105f5 100644 --- a/recipe.cake +++ b/recipe.cake @@ -10,6 +10,7 @@ BuildParameters.SetParameters( repositoryOwner: "cake-contrib", repositoryName: "Cake.Npm", appVeyorAccountName: "cakecontrib", + shouldPublishMyGet: false, shouldRunDupFinder: false, shouldRunCodecov: false, shouldRunGitVersion: true); From 9c1d32342ede5702f9439d57fab4e23370a815f4 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Sat, 29 Aug 2020 10:56:38 +0200 Subject: [PATCH 09/17] (GH-115) Add overloads for NpmVersion which accept a settings object --- src/Cake.Npm/NpmVersionAliases.cs | 70 ++++++++++++++++++++-- src/Cake.Npm/Version/NpmVersionSettings.cs | 2 + 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/Cake.Npm/NpmVersionAliases.cs b/src/Cake.Npm/NpmVersionAliases.cs index fc37253..fbb5c51 100644 --- a/src/Cake.Npm/NpmVersionAliases.cs +++ b/src/Cake.Npm/NpmVersionAliases.cs @@ -33,11 +33,73 @@ public static string NpmVersion(this ICakeContext context) } AddinInformation.LogVersionInformation(context.Log); - var settings = new NpmVersionSettings + return context.NpmVersion(new NpmVersionSettings()); + } + + /// + /// Versions all packages for the project in the current working directory + /// using the settings returned by a configurator. + /// + /// The context. + /// The settings configurator. + /// + /// + /// settings.WithLogLevel(NpmLogLevel.Verbose)); + /// ]]> + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Version")] + public static string NpmVersion(this ICakeContext context, Action configurator) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (configurator == null) + { + throw new ArgumentNullException(nameof(configurator)); + } + + var settings = new NpmVersionSettings(); + configurator(settings); + return context.NpmVersion(settings); + } + + /// + /// Versions all packages for the project in the current working directory + /// using the specified settings. + /// + /// The context. + /// The settings. + /// + /// + /// + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Version")] + public static string NpmVersion(this ICakeContext context, NpmVersionSettings settings) + { + if (context == null) { - RedirectStandardOutput = true - }; - return new NpmVersionTool(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools, context.Log).Version(settings); + throw new ArgumentNullException(nameof(context)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + AddinInformation.LogVersionInformation(context.Log); + var tool = new NpmVersionTool(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools, context.Log); + return tool.Version(settings); } } } \ No newline at end of file diff --git a/src/Cake.Npm/Version/NpmVersionSettings.cs b/src/Cake.Npm/Version/NpmVersionSettings.cs index 52159ca..4c07ec0 100644 --- a/src/Cake.Npm/Version/NpmVersionSettings.cs +++ b/src/Cake.Npm/Version/NpmVersionSettings.cs @@ -14,6 +14,8 @@ public class NpmVersionSettings : NpmSettings public NpmVersionSettings() : base("version") { + // Since 'NpmVersion' returns a string we should redirect standard output. + RedirectStandardOutput = true; } } } From 9252fbed93c2e31db3ce0e2ce52b26ba25e757f1 Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Sun, 30 Aug 2020 00:08:53 +0200 Subject: [PATCH 10/17] (GH-124) Added the return-value to NpmPack(this ICakeContext context, Action configurator) --- src/Cake.Npm/NpmPackAliases.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Cake.Npm/NpmPackAliases.cs b/src/Cake.Npm/NpmPackAliases.cs index 9bde161..5bf4bf1 100644 --- a/src/Cake.Npm/NpmPackAliases.cs +++ b/src/Cake.Npm/NpmPackAliases.cs @@ -76,6 +76,7 @@ public static IEnumerable NpmPack(this ICakeContext context, string so /// /// The context. /// The settings configurator. + /// List of created packages. /// /// /// NpmPack(this ICakeContext context, string so /// [CakeMethodAlias] [CakeAliasCategory("Pack")] - public static void NpmPack(this ICakeContext context, Action configurator) + public static IEnumerable NpmPack(this ICakeContext context, Action configurator) { if (context == null) { @@ -99,7 +100,7 @@ public static void NpmPack(this ICakeContext context, Action co var settings = new NpmPackSettings(); configurator(settings); - context.NpmPack(settings); + return context.NpmPack(settings); } /// From 3bc107209115898308490048d65ed3b4ceef52e5 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Sat, 29 Aug 2020 11:23:07 +0200 Subject: [PATCH 11/17] Fix badge for AppVeyor master branch build status --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43a7ffd..fcaf258 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ More about Cake at [cakebuild.net](http://cakebuild.net). |Develop|Master| |:--:|:--:| -|[![Build status](https://ci.appveyor.com/api/projects/status/5fbkekpb5roh39m8/branch/develop?svg=true)](https://ci.appveyor.com/project/cakecontrib/cake-npm/branch/develop)|[![Build status](https://ci.appveyor.com/api/projects/status/5fbkekpb5roh39m8/branch/develop?svg=true)](https://ci.appveyor.com/project/cakecontrib/cake-npm/branch/master)| +|[![Build status](https://ci.appveyor.com/api/projects/status/5fbkekpb5roh39m8/branch/develop?svg=true)](https://ci.appveyor.com/project/cakecontrib/cake-npm/branch/develop)|[![Build status](https://ci.appveyor.com/api/projects/status/5fbkekpb5roh39m8/branch/master?svg=true)](https://ci.appveyor.com/project/cakecontrib/cake-npm/branch/master)| ## Code Coverage From 8ff34afbd723a480cba91c2a35eb6acf638f8eaf Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Sat, 29 Aug 2020 23:51:24 +0200 Subject: [PATCH 12/17] (GH-97) Added StandardErrorAction and StandardOutputAction in NpmSettings --- .../NpmSettingsExtensionsTests.cs | 30 ++++++++++++ src/Cake.Npm/NpmSettings.cs | 30 +++++++++++- src/Cake.Npm/NpmSettingsExtensions.cs | 46 +++++++++++++++++++ src/Cake.Npm/NpmTool.cs | 27 ++++++++++- 4 files changed, 129 insertions(+), 4 deletions(-) diff --git a/src/Cake.Npm.Tests/NpmSettingsExtensionsTests.cs b/src/Cake.Npm.Tests/NpmSettingsExtensionsTests.cs index 2e7805b..e911af0 100644 --- a/src/Cake.Npm.Tests/NpmSettingsExtensionsTests.cs +++ b/src/Cake.Npm.Tests/NpmSettingsExtensionsTests.cs @@ -1,5 +1,7 @@ namespace Cake.Npm.Tests { + using System; + using Cake.Npm.Install; using Shouldly; using Xunit; @@ -83,6 +85,34 @@ public void Should_Set_WorkingDirectory() // Then settings.WorkingDirectory.ToString().ShouldBe(@"c:/temp"); } + + [Fact] + public void Should_Set_StandardOutputAction() + { + // Given + var settings = new NpmInstallSettings(); + Action action = x => { }; + + // When + settings.SetRedirectedStandardOutputHandler(action); + + // Then + settings.StandardOutputAction.ShouldBe(action); + } + + [Fact] + public void Should_Set_StandardErrorAction() + { + // Given + var settings = new NpmInstallSettings(); + Action action = x => { }; + + // When + settings.SetRedirectedStandardErrorHandler(action); + + // Then + settings.StandardErrorAction.ShouldBe(action); + } } } } diff --git a/src/Cake.Npm/NpmSettings.cs b/src/Cake.Npm/NpmSettings.cs index cd37900..41907a2 100644 --- a/src/Cake.Npm/NpmSettings.cs +++ b/src/Cake.Npm/NpmSettings.cs @@ -1,5 +1,7 @@ namespace Cake.Npm { + using System; + using Core; using Core.Diagnostics; using Core.IO; @@ -27,15 +29,39 @@ protected NpmSettings(string command) public NpmLogLevel LogLevel { get; set; } /// - /// Gets or sets the process option to redirect standard error + /// Gets or sets the process option to redirect standard error output. /// + /// + /// To retrieve and process the standard error output + /// needs to be set. + /// public bool RedirectStandardError { get; set; } /// - /// Gets or sets the process option to redirect standard output + /// Gets or sets an action to retrieve and process standard error output. + /// + /// + /// Setting a standard error action implicitely set . + /// + public Action StandardErrorAction { get; set; } + + /// + /// Gets or sets the process option to redirect standard output. /// + /// + /// To retrieve and process the standard error output + /// needs to be set. + /// public bool RedirectStandardOutput { get; set; } + /// + /// Gets or sets an action to retrieve and process standard output. + /// + /// + /// Setting a standard error action implicitely set . + /// + public Action StandardOutputAction { get; set; } + /// /// Gets or sets the Log level set by Cake. /// diff --git a/src/Cake.Npm/NpmSettingsExtensions.cs b/src/Cake.Npm/NpmSettingsExtensions.cs index 3bbcc3d..0510a01 100644 --- a/src/Cake.Npm/NpmSettingsExtensions.cs +++ b/src/Cake.Npm/NpmSettingsExtensions.cs @@ -48,5 +48,51 @@ public static NpmSettings FromPath(this NpmSettings settings, DirectoryPath path return settings; } + + /// + /// Sets the StandardError-Action + /// + /// The settings. + /// The StandardError-Action. + /// The instance with set to . + public static NpmSettings SetRedirectedStandardErrorHandler(this NpmSettings settings, Action standardErrorAction) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + if (standardErrorAction == null) + { + throw new ArgumentNullException(nameof(standardErrorAction)); + } + + settings.StandardErrorAction = standardErrorAction; + + return settings; + } + + /// + /// Sets the StandardOutput-Action + /// + /// The settings. + /// The StandardOutput-Action. + /// The instance with set to . + public static NpmSettings SetRedirectedStandardOutputHandler(this NpmSettings settings, Action standardOutputAction) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + if (standardOutputAction == null) + { + throw new ArgumentNullException(nameof(standardOutputAction)); + } + + settings.StandardOutputAction = standardOutputAction; + + return settings; + } } } diff --git a/src/Cake.Npm/NpmTool.cs b/src/Cake.Npm/NpmTool.cs index 4dbd10a..1151e17 100644 --- a/src/Cake.Npm/NpmTool.cs +++ b/src/Cake.Npm/NpmTool.cs @@ -90,8 +90,31 @@ protected void RunCore(TSettings settings, ProcessSettings processSettings, Acti settings.CakeVerbosityLevel = CakeLog.Verbosity; } - processSettings.RedirectStandardError = settings.RedirectStandardError; - processSettings.RedirectStandardOutput = settings.RedirectStandardOutput; + processSettings.RedirectStandardOutput = + settings.RedirectStandardOutput || + settings.StandardOutputAction != null; + + if (settings.StandardOutputAction != null) + { + processSettings.RedirectedStandardOutputHandler = x => + { + settings.StandardOutputAction(x); + return x; + }; + } + + processSettings.RedirectStandardError = + settings.RedirectStandardError || + settings.StandardErrorAction != null; + + if (settings.StandardErrorAction != null) + { + processSettings.RedirectedStandardErrorHandler = x => + { + settings.StandardErrorAction(x); + return x; + }; + } var args = GetArguments(settings); Run(settings, args, processSettings, postAction); From 248162d1e1f49d4e288217d14e1160310a14a2c5 Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Thu, 14 Jan 2021 23:15:58 +0100 Subject: [PATCH 13/17] (GH-128) replaced link to Gitter with a link to GitHub discussions --- README.md | 6 +++--- docs/input/_Bottom.cshtml | 10 ++++----- docs/input/assets/css/override.less | 33 +++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index fcaf258..088067e 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,11 @@ More about Cake at [cakebuild.net](http://cakebuild.net). - [Documentation](https://cake-contrib.github.io/Cake.Npm) -## Chat Room +## Discussion -Come join in the conversation about Cake.Yeoman in our Gitter Chat Room. +If you have questions, search for an existing one, or create a new discussion on the Cake GitHub repository, using the `Extension Q&A` category. -[![Join the chat at https://gitter.im/cake-contrib/Lobby](https://badges.gitter.im/cake-contrib/Lobby.svg)](https://gitter.im/cake-contrib/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Join in the discussion on the Cake repository](https://img.shields.io/badge/GitHub-Discussions-green?logo=github)](https://github.com/cake-build/cake/discussions/categories/extension-q-a) ## Contributing diff --git a/docs/input/_Bottom.cshtml b/docs/input/_Bottom.cshtml index 9309d39..98b6ff5 100644 --- a/docs/input/_Bottom.cshtml +++ b/docs/input/_Bottom.cshtml @@ -1,12 +1,10 @@ - - + + + Discussion + diff --git a/docs/input/assets/css/override.less b/docs/input/assets/css/override.less index 74d8ed4..7e1f7ed 100644 --- a/docs/input/assets/css/override.less +++ b/docs/input/assets/css/override.less @@ -71,9 +71,9 @@ pre:hover .btn-copy { @font-family-sans-serif: "Roboto", Helvetica, Arial, sans-serif; -/* For Gitter and GitHub */ +/* For GitHub */ .bottom-footer { - margin-bottom: 40px !important; // Make room for Gitter and GitHub buttons + margin-bottom: 40px !important; // Make room for GitHub buttons } .gitter-open-chat-button { @@ -84,7 +84,7 @@ pre:hover .btn-copy { } .gitter-open-chat-button:focus, .gitter-open-chat-button:hover, -.github-button:focus, .github-button:hover, +.github-button:focus, .github-button:hover { background-color: #4EABDD; color: #fff; @@ -96,11 +96,36 @@ pre:hover .btn-copy { z-index: 10000; } +.discussion-button { + z-index: 100; + position: fixed; + bottom: 0px; + right: 80px; + padding: 1em 3em; + background-color: #367fa9; + border: 0; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; + font-family: sans-serif; + font-size: 9pt; + text-transform: uppercase; + text-align: center; + text-decoration: none; + cursor: pointer; + cursor: hand; + -webkit-transition: all .3s ease; + transition: all .3s ease; + color: #fff; + a, a:active, a:hover, a:focus { + color: #fff; + } +} + .github-button { z-index: 100; position: fixed; bottom: 0px; - right: 240px; + right: 255px; padding: 1em 3em; background-color: #367fa9; border: 0; From a98a7b788e1c6e43e47bc211b0ca1ac6c95643af Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Thu, 1 Oct 2020 10:05:09 +0200 Subject: [PATCH 14/17] (GH-96) Added aliases NpmBumpVersion and NpmViewVersion BumpVersion to use "npm version ..." to update the version of the current package. ViewVersion to use "npm view ... version" to get the version of some npm package. --- .../BumpVersion/NpmBumpVersionToolFixture.cs | 14 ++ .../BumpVersion/NpmBumpVersionToolTests.cs | 155 ++++++++++++++++++ .../ExceptionAssertExtensions.cs | 6 + .../ViewVersion/NpmViewVersionToolFixture.cs | 16 ++ .../ViewVersion/NpmViewVersionToolTests.cs | 113 +++++++++++++ .../BumpVersion/NpmBumpVersionSettings.cs | 58 +++++++ .../NpmBumpVersionSettingsExtensions.cs | 61 +++++++ .../BumpVersion/NpmBumpVersionTool.cs | 47 ++++++ src/Cake.Npm/Namespaces.cs | 24 +++ src/Cake.Npm/NpmBumpVersionAliases.cs | 110 +++++++++++++ src/Cake.Npm/NpmVersionAliases.cs | 8 +- src/Cake.Npm/NpmViewVersionAliases.cs | 116 +++++++++++++ .../ViewVersion/NpmViewVersionSettings.cs | 48 ++++++ .../NpmViewVersionSettingsExtensions.cs | 28 ++++ .../ViewVersion/NpmViewVersionTool.cs | 59 +++++++ 15 files changed, 862 insertions(+), 1 deletion(-) create mode 100644 src/Cake.Npm.Tests/BumpVersion/NpmBumpVersionToolFixture.cs create mode 100644 src/Cake.Npm.Tests/BumpVersion/NpmBumpVersionToolTests.cs create mode 100644 src/Cake.Npm.Tests/ViewVersion/NpmViewVersionToolFixture.cs create mode 100644 src/Cake.Npm.Tests/ViewVersion/NpmViewVersionToolTests.cs create mode 100644 src/Cake.Npm/BumpVersion/NpmBumpVersionSettings.cs create mode 100644 src/Cake.Npm/BumpVersion/NpmBumpVersionSettingsExtensions.cs create mode 100644 src/Cake.Npm/BumpVersion/NpmBumpVersionTool.cs create mode 100644 src/Cake.Npm/NpmBumpVersionAliases.cs create mode 100644 src/Cake.Npm/NpmViewVersionAliases.cs create mode 100644 src/Cake.Npm/ViewVersion/NpmViewVersionSettings.cs create mode 100644 src/Cake.Npm/ViewVersion/NpmViewVersionSettingsExtensions.cs create mode 100644 src/Cake.Npm/ViewVersion/NpmViewVersionTool.cs diff --git a/src/Cake.Npm.Tests/BumpVersion/NpmBumpVersionToolFixture.cs b/src/Cake.Npm.Tests/BumpVersion/NpmBumpVersionToolFixture.cs new file mode 100644 index 0000000..0926dc3 --- /dev/null +++ b/src/Cake.Npm.Tests/BumpVersion/NpmBumpVersionToolFixture.cs @@ -0,0 +1,14 @@ + +namespace Cake.Npm.Tests.BumpVersion +{ + using Cake.Npm.BumpVersion; + + internal sealed class NpmBumpVersionToolFixture : NpmFixture + { + protected override void RunTool() + { + var tool = new NpmBumpVersionTool(FileSystem, Environment, ProcessRunner, Tools, Log); + tool.BumpVersion(Settings); + } + } +} diff --git a/src/Cake.Npm.Tests/BumpVersion/NpmBumpVersionToolTests.cs b/src/Cake.Npm.Tests/BumpVersion/NpmBumpVersionToolTests.cs new file mode 100644 index 0000000..0abd09e --- /dev/null +++ b/src/Cake.Npm.Tests/BumpVersion/NpmBumpVersionToolTests.cs @@ -0,0 +1,155 @@ +namespace Cake.Npm.Tests.BumpVersion +{ + using Npm.BumpVersion; + + using System; + using System.Collections; + using System.Collections.Generic; + + using Xunit; + + public class NpmBumpVersionToolTests + { + public sealed class TheBumpVersionMethod + { + private NpmBumpVersionToolFixture fixture; + + public TheBumpVersionMethod() + { + fixture = new NpmBumpVersionToolFixture(); + } + + [Fact] + public void Should_Throw_If_Settings_Are_Null() + { + // Given + fixture.Settings = null; + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + result.IsArgumentNullException("settings"); + } + + [Theory] + [ClassData(typeof(ExtensionNullCheckData))] + public void Should_Throw_If_Settings_Are_Null_For_All_Extensions(Action extensionAction) + { + // Given + fixture.Settings = null; + + // When + var result = Record.Exception(() => extensionAction(fixture.Settings)); + + // Then + result.IsArgumentNullException("settings"); + } + + [Fact] + public void Should_Add_Mandatory_And_Default_Arguments() + { + // Given + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("version minor", result.Args); + } + + [Fact] + public void Should_Add_Version_Argument() + { + // Given + fixture.Settings.Version = "1.2.3"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("version 1.2.3", result.Args); + } + + [Fact] + public void Should_Add_Version_Argument_Using_Extensions() + { + // Given + fixture.Settings.WithVersion("1.2.3"); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("version 1.2.3", result.Args); + } + + [Fact] + public void Should_Add_Force_Switch() + { + // Given + fixture.Settings.Force = true; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("version minor -f", result.Args); + } + + [Fact] + public void Should_Add_Force_Switch_Using_Extension() + { + // Given + fixture.Settings.WithForce(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("version minor -f", result.Args); + } + + [Fact] + public void Should_Add_CommitMessage_Option() + { + // Given + fixture.Settings.CommitMessage = "Bumped minor version."; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("version minor -m \"Bumped minor version.\"", result.Args); + } + + [Fact] + public void Should_Add_CommitMessage_Option_Using_Extension() + { + // Given + fixture.Settings.WithCommitMessage("Bumped minor version."); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("version minor -m \"Bumped minor version.\"", result.Args); + } + + class ExtensionNullCheckData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { (Action)(x => x.WithForce()) }; + yield return new object[] { (Action)(x => x.WithCommitMessage("")) }; + yield return new object[] { (Action)(x => x.WithVersion("")) }; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + } + } +} diff --git a/src/Cake.Npm.Tests/ExceptionAssertExtensions.cs b/src/Cake.Npm.Tests/ExceptionAssertExtensions.cs index 024404f..0dd1777 100644 --- a/src/Cake.Npm.Tests/ExceptionAssertExtensions.cs +++ b/src/Cake.Npm.Tests/ExceptionAssertExtensions.cs @@ -21,5 +21,11 @@ public static void IsUriFormatException(this Exception exception) { Assert.IsType(exception); } + + public static void IsArgumentOutOfRangeException(this Exception exception, string parameterName) + { + Assert.IsType(exception); + Assert.Equal(parameterName, ((ArgumentOutOfRangeException)exception).ParamName); + } } } diff --git a/src/Cake.Npm.Tests/ViewVersion/NpmViewVersionToolFixture.cs b/src/Cake.Npm.Tests/ViewVersion/NpmViewVersionToolFixture.cs new file mode 100644 index 0000000..b9150b4 --- /dev/null +++ b/src/Cake.Npm.Tests/ViewVersion/NpmViewVersionToolFixture.cs @@ -0,0 +1,16 @@ + +namespace Cake.Npm.Tests.ViewVersion +{ + using Cake.Npm.ViewVersion; + + internal sealed class NpmViewVersionToolFixture : NpmFixture + { + internal string Version { get; private set; } + + protected override void RunTool() + { + var tool = new NpmViewVersionTool(FileSystem, Environment, ProcessRunner, Tools, Log); + Version = tool.Version(Settings); + } + } +} diff --git a/src/Cake.Npm.Tests/ViewVersion/NpmViewVersionToolTests.cs b/src/Cake.Npm.Tests/ViewVersion/NpmViewVersionToolTests.cs new file mode 100644 index 0000000..23adbda --- /dev/null +++ b/src/Cake.Npm.Tests/ViewVersion/NpmViewVersionToolTests.cs @@ -0,0 +1,113 @@ +namespace Cake.Npm.Tests.ViewVersion +{ + using Cake.Npm.ViewVersion; + + using Shouldly; + + using Xunit; + + public class NpmViewVersionToolTests + { + public sealed class TheViewVersionMethod + { + private NpmViewVersionToolFixture fixture; + + public TheViewVersionMethod() + { + fixture = new NpmViewVersionToolFixture(); + } + + [Fact] + public void Should_Throw_If_Settings_Are_Null() + { + // Given + fixture.Settings = null; + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + result.IsArgumentNullException("settings"); + } + + [Fact] + public void Should_Throw_If_Package_Is_Null() + { + // Given + fixture.Settings.Package = null; + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + result.IsArgumentOutOfRangeException("Package"); + } + + [Fact] + public void Should_Throw_If_Extension_Is_Called_On_Null() + { + // Given + fixture.Settings = null; + + // When + var result = Record.Exception(() => fixture.Settings.ForPackage("cakejs")); + + // Then + result.IsArgumentNullException("settings"); + } + + [Fact] + public void Should_Add_Mandatory_And_Default_Arguments() + { + // Given + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("view npm version", result.Args); + } + + [Fact] + public void Should_Add_Package_Argument() + { + // Given + fixture.Settings.Package = "cakejs"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("view cakejs version", result.Args); + } + + [Fact] + public void Should_Add_Package_Argument_Using_Extension() + { + // Given + fixture.Settings.ForPackage("cakejs"); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("view cakejs version", result.Args); + } + + [Fact] + public void Should_Return_Version_From_StandardOutput() + { + const string version = "1.1.0"; + + // Given + fixture.ProcessRunner.Process.SetStandardOutput(new[] { version }); + + // When + fixture.Run(); + + // Then + fixture.Version.ShouldBe(version); + } + } + } +} diff --git a/src/Cake.Npm/BumpVersion/NpmBumpVersionSettings.cs b/src/Cake.Npm/BumpVersion/NpmBumpVersionSettings.cs new file mode 100644 index 0000000..5031cb0 --- /dev/null +++ b/src/Cake.Npm/BumpVersion/NpmBumpVersionSettings.cs @@ -0,0 +1,58 @@ +namespace Cake.Npm.BumpVersion +{ + using Core; + using Core.IO; + + /// + /// Contains settings used by . + /// + public class NpmBumpVersionSettings : NpmSettings + { + /// + /// Initializes a new instance of the class. + /// + public NpmBumpVersionSettings() + : base("version") + { + Version = "minor"; + } + + /// + /// Gets or sets the force-option + /// + public bool Force { get; set; } + + /// + /// Gets or sets the commit message. + /// + public string CommitMessage { get; set; } + + /// + /// Gets or sets the version to bump to. Should be a valid semver + /// or one of "patch", "minor", "major", + /// "prepatch", "preminor", "premajor", + /// "prerelease" or "from-git". + /// + public string Version {get;set; } + + /// + /// Evaluates the settings and writes them to . + /// + /// The argument builder into which the settings should be written. + protected override void EvaluateCore(ProcessArgumentBuilder args) + { + base.EvaluateCore(args); + args.Append(Version); + + if (!string.IsNullOrEmpty(CommitMessage)) + { + args.AppendSwitchQuoted("-m", CommitMessage); + } + + if (Force) + { + args.Append("-f"); + } + } + } +} diff --git a/src/Cake.Npm/BumpVersion/NpmBumpVersionSettingsExtensions.cs b/src/Cake.Npm/BumpVersion/NpmBumpVersionSettingsExtensions.cs new file mode 100644 index 0000000..9d96ce9 --- /dev/null +++ b/src/Cake.Npm/BumpVersion/NpmBumpVersionSettingsExtensions.cs @@ -0,0 +1,61 @@ +namespace Cake.Npm.BumpVersion +{ + using System; + + /// + /// Extensions for . + /// + public static class NpmBumpVersionSettingsExtensions + { + /// + /// Defines that npm version should commit, even if the repository is not clean. + /// + /// The settings. + /// Whether to set force, or not. + /// The instance with set. + public static NpmBumpVersionSettings WithForce(this NpmBumpVersionSettings settings, bool force = true) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + settings.Force = force; + return settings; + } + + /// + /// Sets the commit message. + /// + /// The settings. + /// The commit message to set. + /// The instance with set. + public static NpmBumpVersionSettings WithCommitMessage(this NpmBumpVersionSettings settings, string message) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + settings.CommitMessage = message; + return settings; + } + + /// + /// Sets the version to bump. + /// + /// The settings. + /// The version to bump. + /// The instance with set. + public static NpmBumpVersionSettings WithVersion(this NpmBumpVersionSettings settings, string version) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + settings.Version = version; + return settings; + } + } +} diff --git a/src/Cake.Npm/BumpVersion/NpmBumpVersionTool.cs b/src/Cake.Npm/BumpVersion/NpmBumpVersionTool.cs new file mode 100644 index 0000000..1d8ca52 --- /dev/null +++ b/src/Cake.Npm/BumpVersion/NpmBumpVersionTool.cs @@ -0,0 +1,47 @@ +namespace Cake.Npm.BumpVersion +{ + using System; + + using Core; + using Core.Diagnostics; + using Core.IO; + using Core.Tooling; + + /// + /// Tool for bumping the version. + /// + public class NpmBumpVersionTool : NpmTool + { + /// + /// Initializes a new instance of the class. + /// + /// The file system. + /// The environment. + /// The process runner. + /// The tool locator. + /// Cake log instance. + public NpmBumpVersionTool( + IFileSystem fileSystem, + ICakeEnvironment environment, + IProcessRunner processRunner, + IToolLocator tools, + ICakeLog log) + : base(fileSystem, environment, processRunner, tools, log) + { + } + + /// + /// Calls npm version to bump a version. + /// + /// The settings. + public void BumpVersion(NpmBumpVersionSettings settings) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + RunCore(settings); + } + } +} diff --git a/src/Cake.Npm/Namespaces.cs b/src/Cake.Npm/Namespaces.cs index fe1572b..b2e0346 100644 --- a/src/Cake.Npm/Namespaces.cs +++ b/src/Cake.Npm/Namespaces.cs @@ -106,4 +106,28 @@ namespace Cake.Npm.Update internal class NamespaceDoc { } +} + +// ReSharper disable once CheckNamespace +namespace Cake.Npm.BumpVersion +{ + /// + /// This namespace contain types used for bumping the package version. + /// + [CompilerGenerated] + internal class NamespaceDoc + { + } +} + +// ReSharper disable once CheckNamespace +namespace Cake.Npm.ViewVersion +{ + /// + /// This namespace contain types used for viewing the package versions. + /// + [CompilerGenerated] + internal class NamespaceDoc + { + } } \ No newline at end of file diff --git a/src/Cake.Npm/NpmBumpVersionAliases.cs b/src/Cake.Npm/NpmBumpVersionAliases.cs new file mode 100644 index 0000000..e051ed4 --- /dev/null +++ b/src/Cake.Npm/NpmBumpVersionAliases.cs @@ -0,0 +1,110 @@ +namespace Cake.Npm +{ + using System; + using BumpVersion; + using Core; + using Core.Annotations; + + /// + /// Npm BumpVersion aliases. + /// Use this if you want to use 'npm version' to bump the version. + /// Use this to bump the version of the current package. + /// For other functions of npm version, see: + /// + /// + /// + /// + /// + [CakeAliasCategory("Npm")] + [CakeNamespaceImport("Cake.Npm.BumpVersion")] + public static class NpmBumpVersionAliases + { + /// + /// Bump the version of the package. + /// + /// The context. + /// + /// + /// + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("BumpVersion")] + public static void NpmBumpVersion(this ICakeContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.NpmBumpVersion(new NpmBumpVersionSettings()); + } + + /// + /// Bump the version of the package using the settings returned by a configurator. + /// + /// The context. + /// The settings configurator. + /// + /// + /// + /// settings.Version("major")); + /// ]]> + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("BumpVersion")] + public static void NpmBumpVersion(this ICakeContext context, Action configurator) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (configurator == null) + { + throw new ArgumentNullException(nameof(configurator)); + } + + var settings = new NpmBumpVersionSettings(); + configurator(settings); + context.NpmBumpVersion(settings); + } + + /// + /// Bump the version of the package using the specified settings. + /// + /// The context. + /// The settings. + /// + /// + /// + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("BumpVersion")] + public static void NpmBumpVersion(this ICakeContext context, NpmBumpVersionSettings settings) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + AddinInformation.LogVersionInformation(context.Log); + var tool = new NpmBumpVersionTool(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools, context.Log); + tool.BumpVersion(settings); + } + } +} \ No newline at end of file diff --git a/src/Cake.Npm/NpmVersionAliases.cs b/src/Cake.Npm/NpmVersionAliases.cs index fbb5c51..39d2a1e 100644 --- a/src/Cake.Npm/NpmVersionAliases.cs +++ b/src/Cake.Npm/NpmVersionAliases.cs @@ -6,7 +6,13 @@ namespace Cake.Npm using Core.Annotations; /// - /// Npm Version aliases + /// Npm Version aliases. + /// Use this to get the current npm version in use. + /// For other functions of npm version, see: + /// + /// + /// + /// /// [CakeAliasCategory("Npm")] [CakeNamespaceImport("Cake.Npm.Version")] diff --git a/src/Cake.Npm/NpmViewVersionAliases.cs b/src/Cake.Npm/NpmViewVersionAliases.cs new file mode 100644 index 0000000..c37d8ae --- /dev/null +++ b/src/Cake.Npm/NpmViewVersionAliases.cs @@ -0,0 +1,116 @@ +namespace Cake.Npm +{ + using System; + using Cake.Core; + using Cake.Core.Annotations; + using Cake.Npm.ViewVersion; + + /// + /// Npm ViewVersion aliases. + /// Use this to view package versions. + /// For other functions of npm version, see: + /// + /// + /// + /// + /// + [CakeAliasCategory("Npm")] + [CakeNamespaceImport("Cake.Npm.ViewVersion")] + public static class NpmViewVersionAliases + { + /// + /// View the version of a package. + /// + /// The context. + /// The Package. + /// + /// + /// + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Version")] + public static string NpmViewVersion(this ICakeContext context, string package) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + AddinInformation.LogVersionInformation(context.Log); + return context.NpmViewVersion(new NpmViewVersionSettings + { + Package = package + }); + } + + /// + /// View the version of a package + /// using the settings returned by a configurator. + /// + /// The context. + /// The settings configurator. + /// + /// + /// + /// settings.ForPackage("cakejs")); + /// ]]> + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Version")] + public static string NpmViewVersion(this ICakeContext context, Action configurator) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (configurator == null) + { + throw new ArgumentNullException(nameof(configurator)); + } + + var settings = new NpmViewVersionSettings(); + configurator(settings); + return context.NpmViewVersion(settings); + } + + /// + /// View the version of a package + /// using the specified settings. + /// + /// The context. + /// The settings. + /// + /// + /// + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Version")] + public static string NpmViewVersion(this ICakeContext context, NpmViewVersionSettings settings) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + AddinInformation.LogVersionInformation(context.Log); + var tool = new NpmViewVersionTool(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools, context.Log); + return tool.Version(settings); + } + } +} \ No newline at end of file diff --git a/src/Cake.Npm/ViewVersion/NpmViewVersionSettings.cs b/src/Cake.Npm/ViewVersion/NpmViewVersionSettings.cs new file mode 100644 index 0000000..8c337a5 --- /dev/null +++ b/src/Cake.Npm/ViewVersion/NpmViewVersionSettings.cs @@ -0,0 +1,48 @@ +namespace Cake.Npm.ViewVersion +{ + using Core; + using Core.IO; + + using System; + + /// + /// Contains settings used by . + /// + public class NpmViewVersionSettings : NpmSettings + { + /// + /// Initializes a new instance of the class. + /// + public NpmViewVersionSettings() + : base("view") + { + // Since 'NpmViewVersion' returns a string we should redirect standard output. + RedirectStandardOutput = true; + Package = "npm"; + } + + /// + /// Gets or sets the package to view the version of. + /// + /// + /// The package. + /// + public string Package { get; set; } + + /// + /// Evaluates the settings and writes them to . + /// + /// The argument builder into which the settings should be written. + protected override void EvaluateCore(ProcessArgumentBuilder args) + { + if (string.IsNullOrEmpty(Package)) + { + throw new ArgumentOutOfRangeException(nameof(Package)); + } + + base.EvaluateCore(args); + args.Append(Package); + args.Append("version"); + } + } +} diff --git a/src/Cake.Npm/ViewVersion/NpmViewVersionSettingsExtensions.cs b/src/Cake.Npm/ViewVersion/NpmViewVersionSettingsExtensions.cs new file mode 100644 index 0000000..628bf28 --- /dev/null +++ b/src/Cake.Npm/ViewVersion/NpmViewVersionSettingsExtensions.cs @@ -0,0 +1,28 @@ +namespace Cake.Npm.ViewVersion +{ + using System; + + /// + /// Extensions for . + /// + public static class NpmViewVersionSettingsExtensions + { + /// + /// Sets the package to view the version of. + /// exists on disk. + /// + /// The settings. + /// The package for which to view the version. + /// The instance with set. + public static NpmViewVersionSettings ForPackage(this NpmViewVersionSettings settings, string package) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + settings.Package = package; + return settings; + } + } +} diff --git a/src/Cake.Npm/ViewVersion/NpmViewVersionTool.cs b/src/Cake.Npm/ViewVersion/NpmViewVersionTool.cs new file mode 100644 index 0000000..6e4a619 --- /dev/null +++ b/src/Cake.Npm/ViewVersion/NpmViewVersionTool.cs @@ -0,0 +1,59 @@ +namespace Cake.Npm.ViewVersion +{ + using System; + using System.Linq; + + using Core; + using Core.Diagnostics; + using Core.IO; + using Core.Tooling; + + /// + /// Tool for viewing npm package versions. + /// + public class NpmViewVersionTool : NpmTool + { + /// + /// Initializes a new instance of the class. + /// + /// The file system. + /// The environment. + /// The process runner. + /// The tool locator. + /// Cake log instance. + public NpmViewVersionTool( + IFileSystem fileSystem, + ICakeEnvironment environment, + IProcessRunner processRunner, + IToolLocator tools, + ICakeLog log) + : base(fileSystem, environment, processRunner, tools, log) + { + } + + /// + /// Views the package version from the specified settings. + /// + /// The settings. + public string Version(NpmViewVersionSettings settings) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + var versionString = string.Empty; + + RunCore(settings, new ProcessSettings(), process => + { + var processOutput = process.GetStandardOutput()?.ToList(); + if (processOutput?.Any() ?? false) + { + versionString = processOutput.First(); + } + }); + + return versionString; + } + } +} From 38820d16632159b28d2aeac8f091d4487c4be19f Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Mon, 8 Feb 2021 21:36:35 +0100 Subject: [PATCH 15/17] (GH-130) Build against Cake 1.0.0 --- src/Cake.Npm.Tests/Cake.Npm.Tests.csproj | 4 ++-- src/Cake.Npm/Cake.Npm.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cake.Npm.Tests/Cake.Npm.Tests.csproj b/src/Cake.Npm.Tests/Cake.Npm.Tests.csproj index 84e37aa..bd815e1 100644 --- a/src/Cake.Npm.Tests/Cake.Npm.Tests.csproj +++ b/src/Cake.Npm.Tests/Cake.Npm.Tests.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/src/Cake.Npm/Cake.Npm.csproj b/src/Cake.Npm/Cake.Npm.csproj index a7cb039..119580b 100644 --- a/src/Cake.Npm/Cake.Npm.csproj +++ b/src/Cake.Npm/Cake.Npm.csproj @@ -21,6 +21,6 @@ - + \ No newline at end of file From 2550236617f0f0fff680e88033974038ab74adfd Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Fri, 12 Feb 2021 20:55:12 +0100 Subject: [PATCH 16/17] Update release notes link --- nuspec/nuget/Cake.Npm.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nuspec/nuget/Cake.Npm.nuspec b/nuspec/nuget/Cake.Npm.nuspec index 3a1a21d..411b5bf 100644 --- a/nuspec/nuget/Cake.Npm.nuspec +++ b/nuspec/nuget/Cake.Npm.nuspec @@ -14,7 +14,7 @@ false cake npm cake-build cake-contrib - https://github.com/cake-contrib/Cake.Npm/releases/tag/0.17.0 + https://github.com/cake-contrib/Cake.Npm/releases/tag/1.0.0 From eeab63e7edfc4ee8b1cc95c51545f74b20b577f1 Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Fri, 12 Feb 2021 21:02:46 +0100 Subject: [PATCH 17/17] (GH-132) Embed icon in NuGet package --- .appveyor.yml | 4 ++++ nuspec/nuget/Cake.Npm.nuspec | 3 ++- nuspec/nuget/icon.png | Bin 0 -> 15580 bytes 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 nuspec/nuget/icon.png diff --git a/.appveyor.yml b/.appveyor.yml index 0d21fc4..c180aec 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -6,6 +6,10 @@ image: Visual Studio 2017 #---------------------------------# # Build Script # #---------------------------------# +install: + # Update to latest NuGet version since we require 5.3.0 for embedded icon + - ps: nuget update -self + build_script: - ps: .\build.ps1 -Target AppVeyor diff --git a/nuspec/nuget/Cake.Npm.nuspec b/nuspec/nuget/Cake.Npm.nuspec index 411b5bf..6a8541c 100644 --- a/nuspec/nuget/Cake.Npm.nuspec +++ b/nuspec/nuget/Cake.Npm.nuspec @@ -10,13 +10,14 @@ A set of aliases for Cake to help with running Npm (Node Package Manager) commands MIT https://github.com/cake-contrib/cake-npm - https://cdn.jsdelivr.net/gh/cake-contrib/graphics@a5cf0f881c390650144b2243ae551d5b9f836196/png/cake-contrib-medium.png + icon.png false cake npm cake-build cake-contrib https://github.com/cake-contrib/Cake.Npm/releases/tag/1.0.0 + diff --git a/nuspec/nuget/icon.png b/nuspec/nuget/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9881edc4e937ff34999ecf375c550249d82acc32 GIT binary patch literal 15580 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 literal 0 HcmV?d00001