diff --git a/.ci/Export-NUnitXml.psm1 b/.ci/Export-NUnitXml.psm1 new file mode 100644 index 0000000000..dc73bdfdd9 --- /dev/null +++ b/.ci/Export-NUnitXml.psm1 @@ -0,0 +1,107 @@ +Function Export-NUnitXml { +<# +.SYNOPSIS + Takes results from PSScriptAnalyzer and exports them as a Pester test results file (NUnitXml format). + +.DESCRIPTION + Takes results from PSScriptAnalyzer and exports them as a Pester test results file (NUnit XML schema). + Because the generated file in NUnit-compatible, it can be consumed and published by most continuous integration tools. +#> + [CmdletBinding()] + Param ( + [Parameter(Mandatory, Position=0)] + [AllowNull()] + [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]$ScriptAnalyzerResult, + + [Parameter(Mandatory, Position=1)] + [string]$Path + ) + + $TotalNumber = If ($ScriptAnalyzerResult) { $ScriptAnalyzerResult.Count -as [string] } Else { '1' } + $FailedNumber = If ($ScriptAnalyzerResult) { $ScriptAnalyzerResult.Count -as [string] } Else { '0' } + $Now = Get-Date + $FormattedDate = Get-Date $Now -Format 'yyyy-MM-dd' + $FormattedTime = Get-Date $Now -Format 'T' + $User = $env:USERNAME + $MachineName = $env:COMPUTERNAME + $Cwd = $pwd.Path + $UserDomain = $env:USERDOMAIN + $OS = Get-CimInstance -ClassName Win32_OperatingSystem + $Platform = $OS.Caption + $OSVersion = $OS.Version + $ClrVersion = $PSVersionTable.CLRVersion.ToString() + $CurrentCulture = (Get-Culture).Name + $UICulture = (Get-UICulture).Name + + Switch ($ScriptAnalyzerResult) { + $Null { $TestResult = 'Success'; $TestSuccess = 'True'; Break} + Default { $TestResult = 'Failure'; $TestSuccess = 'False'} + } + + $Header = @" + + + + + + + + `n +"@ + + $Footer = @" + + + + + +"@ + + If ( -not($ScriptAnalyzerResult) ) { + + $TestDescription = 'All PowerShell files pass the specified PSScriptAnalyzer rules' + $TestName = "PSScriptAnalyzer.{0}" -f $TestDescription + + $Body = @" + `n +"@ + } + Else { # $ScriptAnalyzerResult is not null + $Body = [string]::Empty + Foreach ( $Result in $ScriptAnalyzerResult ) { + + $TestDescription = "Rule name : $($Result.RuleName)" + $TestName = "PSScriptAnalyzer.{0} - {1} - Line {2}" -f $TestDescription, $($Result.ScriptName), $($Result.Line.ToString()) + + # Need to Escape these otherwise we can end up with an invalid XML if the Stacktrace has non XML friendly chars like &, etc + $Line = [System.Security.SecurityElement]::Escape($Result.Line) + $ScriptPath = [System.Security.SecurityElement]::Escape($Result.ScriptPath) + $Text = [System.Security.SecurityElement]::Escape($Result.Extent.Text) + $Severity = [System.Security.SecurityElement]::Escape($Result.Severity) + + $TestCase = @" + + + $($Result.Message) + at line: $($Line) in $($ScriptPath) + $($Line): $($Text) + Rule severity : $($Severity) + + + `n +"@ + + $Body += $TestCase + } + } + $OutputXml = $Header + $Body + $Footer + + # Checking our output is a well formed XML document + Try { + $XmlCheck = [xml]$OutputXml + } + Catch { + Throw "There was an problem when attempting to cast the output to XML : $($_.Exception.Message)" + } + $OutputXml | Out-File -FilePath $Path -Encoding utf8 -Force +} diff --git a/.ci/Fudge.ps1 b/.ci/Fudge.ps1 new file mode 100644 index 0000000000..df4d407c29 --- /dev/null +++ b/.ci/Fudge.ps1 @@ -0,0 +1,448 @@ +<# + .SYNOPSIS + Fudge is a tool to help you manage and version control Chocolatey packages required for environments to function + + .DESCRIPTION + Fudge is a tool to help you manage and version control Chocolatey packages required for environments to function. + This is done via a Fudgefile which allows you to specify packages (and their versions) to install. You can also + specify dev-specific packages (like git, or fiddler) + + You are also able to define pre/post install/upgrade/downgrade/uninstall scripts for additional required functionality + + Furthermore, Fudge has a section to allow you to specify multiple nuspec files and pack the one you need + + .PARAMETER Action + The action that Fudge should undertake + Actions: install, upgrade, downgrade, uninstall, reinstall, pack, list, search, new, delete, prune, clean, rebuild, + which, help, renew, add, remove + [Alias: -a] + + .PARAMETER Key + The key represents a package/nuspec name in the Fudgefile + [Actions: install, upgrade, downgrade, uninstall, reinstall, pack, new, which, renew, add, remove] + [Alias: -k] + + .PARAMETER FudgefilePath + This will override looking for a default 'Fudgefile' at the root of the current path, and allow you to specify + other files instead. This allows you to have multiple Fudgefiles + [Actions: install, upgrade, downgrade, uninstall, reinstall, pack, list, new, delete, prune, rebuild, renew, add, remove] + [Default: ./Fudgefile] + [Alias: -fp] + + .PARAMETER Limit + This argument only applies for the 'search' action. It will limit the amount of packages returned when searching + If 0 is supplied, the full list is returned + [Actions: search] + [Default: 10] + [Alias: -l] + + .PARAMETER Source + Passing this argument will allow you to specify custom source locations to get/download packages for Chocolatey. + This allows you to install packages from local directories, or from custom Chocolatey servers. Passing this will + also override the source specified in any Fudgefiles + [Default: Chocolatey's server] + [Actions: install, upgrade, downgrade, reinstall, search, rebuild, add] + [Alias: -s] + + .PARAMETER Parameters + This argument allows you to pass parameters to a chocolatey package, as if you were using "--params" on choco. + For install/upgrade/downgrade/uninstall/reinstall, this argument only works when "-Adhoc" is also supplied + [Default: Empty] + [Actions: install, upgrade, downgrade, uninstall, reinstall, add] + [Alias: -p] + + .PARAMETER Arguments + This argument allows you to pass extra arguments to a chocolatey, such as "--x86" or "--ignore-checksum" + For install/upgrade/downgrade/uninstall/reinstall, this argument only works when "-Adhoc" is also supplied + [Default: Empty] + [Actions: install, upgrade, downgrade, uninstall, reinstall, add] + [Alias: -args] + + .PARAMETER Dev + Switch parameter, if supplied will also action upon the devPackages in the Fudgefile + [Actions: install, upgrade, downgrade, uninstall, reinstall, list, delete, prune, rebuild, add, remove] + [Alias: -d] + + .PARAMETER DevOnly + Switch parameter, if supplied will only action upon the devPackages in the Fudgefile + [Actions: install, upgrade, downgrade, uninstall, reinstall, list, delete, prune, rebuild] + [Alias: -do] + + .PARAMETER Install + Switch parameter, if supplied will install packages after creating a new Fudgefile + [Actions: new, renew, add] + [Alias: -i] + + .PARAMETER Uninstall + Switch parameter, if supplied will uninstall packages before deleting a Fudgefile + [Actions: delete, renew, remove] + [Alias: -u] + + .PARAMETER Adhoc + Switch parameter, if supplied will install software from Chocolatey whether or not + the package is in the Fudgefile + [Actions: install, upgrade, downgrade, uninstall, reinstall] + [Alias: -ad] + + .PARAMETER Version + Switch parameter, if supplied will just display the current version of Fudge installed + [Alias: -v] + + .PARAMETER Help + Switch parameter, if supplied will just display help output + [Alias: -h] + + .EXAMPLE + fudge install + + .EXAMPLE + fudge install -d # to also install devPackages (-do will only install devPackages) + + .EXAMPLE + fudge install git -ad # installs git dispite not being in the Fudgefile + + .EXAMPLE + fudge pack website + + .EXAMPLE + fudge list + + .EXAMPLE + fudge search checksum +#> +param ( + [Alias('a')] + [string] + $Action, + + [Alias('k')] + [string] + $Key, + + [Alias('fp')] + [string] + $FudgefilePath, + + [Alias('l')] + [int] + $Limit = 10, + + [Alias('s')] + [string] + $Source, + + [Alias('p')] + [string] + $Parameters, + + [Alias('args')] + [string] + $Arguments, + + [Alias('d')] + [switch] + $Dev, + + [Alias('do')] + [switch] + $DevOnly, + + [Alias('i')] + [switch] + $Install, + + [Alias('u')] + [switch] + $Uninstall, + + [Alias('v')] + [switch] + $Version, + + [Alias('h')] + [switch] + $Help, + + [Alias('ad')] + [switch] + $Adhoc +) + +# ensure if there's an error, we stop +$ErrorActionPreference = 'Stop' + + +# Import required modules +$root = Split-Path -Parent -Path $MyInvocation.MyCommand.Path +Import-Module "$($root)\Modules\FudgeTools.psm1" -Force -ErrorAction Stop + + +# output the version +$ver = 'v$version$' +Write-Success "Fudge $($ver)" + +# if we were only after the version, just return +if ($Version -or (@('v', 'version') -icontains $Action)) +{ + return +} + + +# if action is just to display Help, show it and return +if ($Help -or (@('h', 'help') -icontains $Action)) +{ + Write-Host "`nUsage: fudge " + Write-Host "`nWhere is one of:" + Write-Host " add, clean, delete, downgrade, help, install, list, new, pack," + Write-Host " prune, rebuild, reinstall, remove, renew, search, uninstall," + Write-Host " upgrade, version, which" + Write-Host "" + return +} + + +try +{ + # start timer + $timer = [DateTime]::UtcNow + + + # ensure we have a valid action + $packageActions = @('install', 'upgrade', 'uninstall', 'reinstall', 'list', 'rebuild', 'downgrade', 'add', 'remove') + $maintainActions = @('prune') + $packingActions = @('pack') + $miscActions = @('search', 'clean', 'which') + $newActions = @('new') + $alterActions = @('delete', 'renew') + + $actions = ($packageActions + $maintainActions + $packingActions + $miscActions + $newActions + $alterActions) + if ((Test-Empty $Action) -or $actions -inotcontains $Action) { + Write-Fail "Unrecognised action supplied '$($Action)', should be either: $($actions -join ', ')" + return + } + + + # actions that require chocolatey + $isChocoAction = (@('which', 'add', 'remove', 'delete') -inotcontains $Action) + if (!$isChocoAction -and ($Install -or $Uninstall)) { + $isChocoAction = $true + } + + + # if adhoc was supplied for an invalid action + if ($Adhoc -and @('install', 'uninstall', 'upgrade', 'downgrade', 'reinstall') -inotcontains $Action) { + Write-Fail "Adhoc supplied for invalid action: $($Action)" + return + } + + # if adhoc supplied with no package name, fail + if ($Adhoc -and [string]::IsNullOrWhiteSpace($Key)) { + Write-Fail "No package name supplied for adhoc $($Action)" + return + } + + + # if -devOnly is passed, set -dev to true + if ($DevOnly) { + $Dev = $true + } + + + # get the Fudgefile path, if adhoc is supplied set to empty + $FudgefilePath = Get-FudgefilePath $FudgefilePath -Adhoc:$Adhoc + + + # ensure that the Fudgefile exists (for certain actions), and deserialise it + if (($packageActions + $maintainActions + $packingActions + $alterActions) -icontains $Action) + { + $config = $null + + # if adhoc is supplied, we don't need to get the content + if (!$Adhoc) { + if (!(Test-Path $FudgefilePath)) { + Write-Fail "Path to Fudgefile does not exist: $($FudgefilePath)" + return + } + + $config = Get-FudgefileContent $FudgefilePath + } + + # if we have a custom source in the config and no CLI source, set the source + if ((Test-Empty $Source) -and ($null -ne $config) -and !(Test-Empty $config.source)) { + $Source = $config.source + } + } + + # ensure that the Fudgefile doesn't exist + elseif ($newActions -icontains $Action) + { + if (Test-Path $FudgefilePath) { + Write-Fail "Path to Fudgefile already exists: $($FudgefilePath)" + return + } + } + + + # if there are no packages to install or nuspecs to pack, just return + if ($null -ne $config) + { + # check nuspecs + if ($packingActions -icontains $Action) + { + if (Test-Empty $config.pack) { + Write-Notice "There are no nuspecs to $($Action)" + return + } + + if (![string]::IsNullOrWhiteSpace($Key) -and [string]::IsNullOrWhiteSpace($config.pack.$Key)) { + Write-Notice "Fudgefile does not contain a nuspec pack file for '$($Key)'" + return + } + } + + # check packages + elseif ($packageActions -icontains $Action) + { + if ((Test-Empty $config.packages) -and (!$Dev -or ($Dev -and (Test-Empty $config.devPackages)))) { + Write-Notice "There are no packages to $($Action)" + return + } + + if ($DevOnly -and (Test-Empty $config.devPackages)) { + Write-Notice "There are no devPackages to $($Action)" + return + } + } + } + + + # check to see if chocolatey is installed + if ($isChocoAction) { + $isChocoInstalled = Test-Chocolatey + } + + + # check if the console is elevated (only needs to be done for certain actions) + $isAdminAction = @('list', 'search', 'new', 'delete', 'renew', 'which', 'add', 'remove', 'pack') -inotcontains $Action + $actionNeedsAdmin = (@('delete', 'remove') -icontains $Action -and $Uninstall) -or (@('new', 'renew', 'add') -icontains $Action -and $Install) + + if (((!$isChocoInstalled -and $isChocoAction) -or $isAdminAction -or $actionNeedsAdmin) -and !(Test-AdminUser)) + { + Write-Notice 'Must be running with administrator privileges for Fudge to fully function' + return + } + + + # if chocolatey isn't installed, install it + if (!$isChocoInstalled -and $isChocoAction) { + Install-Chocolatey + } + + + # if we are using a global custom source, output it for info + if (!(Test-Empty $Source)) { + Write-Notice "Source: $($Source)" + } + + Write-Host ([string]::Empty) + + + # retrieve a local list of what's currently installed + if ($isChocoAction) { + $localList = Get-ChocolateyLocalList + } + + + # invoke chocolatey based on the action required + switch ($Action) + { + {($_ -ieq 'install') -or ($_ -ieq 'uninstall') -or ($_ -ieq 'upgrade') -or ($_ -ieq 'downgrade')} + { + Invoke-ChocolateyAction -Action $Action -Key $Key -Source $Source -Config $config -LocalList $localList ` + -Parameters $Parameters -Arguments $Arguments -Dev:$Dev -DevOnly:$DevOnly -Adhoc:$Adhoc + } + + {($_ -ieq 'reinstall')} + { + Invoke-ChocolateyAction -Action 'uninstall' -Key $Key -Source $Source -Config $config -LocalList $localList ` + -Parameters $Parameters -Arguments $Arguments -Dev:$Dev -DevOnly:$DevOnly -Adhoc:$Adhoc + + Invoke-ChocolateyAction -Action 'install' -Key $Key -Source $Source -Config $config -LocalList $localList ` + -Parameters $Parameters -Arguments $Arguments -Dev:$Dev -DevOnly:$DevOnly -Adhoc:$Adhoc + } + + {($_ -ieq 'pack')} + { + Invoke-ChocolateyAction -Action 'pack' -Key $Key -Config $config + } + + {($_ -ieq 'list')} + { + Invoke-FudgeLocalDetails -Config $config -Key $Key -LocalList $localList -Dev:$Dev -DevOnly:$DevOnly + } + + {($_ -ieq 'search')} + { + Invoke-Search -Key $Key -Limit $Limit -Source $Source -LocalList $localList + } + + {($_ -ieq 'new')} + { + New-Fudgefile -Path $FudgefilePath -Key $Key -LocalList $localList -Install:$Install -Dev:$Dev -DevOnly:$DevOnly + } + + {($_ -ieq 'renew')} + { + Restore-Fudgefile -Path $FudgefilePath -Key $Key -LocalList $localList -Install:$Install -Uninstall:$Uninstall -Dev:$Dev -DevOnly:$DevOnly + } + + {($_ -ieq 'delete')} + { + Remove-Fudgefile -Path $FudgefilePath -Uninstall:$Uninstall -Dev:$Dev -DevOnly:$DevOnly + } + + {($_ -ieq 'prune')} + { + Invoke-FudgePrune -Config $config -LocalList $localList -Dev:$Dev -DevOnly:$DevOnly + } + + {($_ -ieq 'clean')} + { + Invoke-FudgeClean -LocalList $localList + } + + {($_ -ieq 'add')} + { + Invoke-FudgeAdd -Path $FudgefilePath -Key $Key -Source $Source -Config $config -LocalList $localList ` + -Parameters $Parameters -Arguments $Arguments -Dev:$Dev -Install:$Install + } + + {($_ -ieq 'remove')} + { + Invoke-FudgeRemove -Path $FudgefilePath -Key $Key -Config $config -LocalList $localList ` + -Parameters $Parameters -Arguments $Arguments -Dev:$Dev -Uninstall:$Uninstall + } + + {($_ -ieq 'which')} + { + Invoke-FudgeWhich -Key $Key + } + + {($_ -ieq 'rebuild')} + { + Invoke-FudgeClean -LocalList $localList + Invoke-ChocolateyAction -Action 'install' -Key $Key -Source $Source -Config $config -Dev:$Dev -DevOnly:$DevOnly + } + + default + { + Write-Fail "Action not recognised: $($_)" + } + } +} +finally +{ + # output duration, and cleanup + Write-Details "`nDuration: $(([DateTime]::UtcNow - $timer).ToString())" + Remove-Module -Name 'FudgeTools' -ErrorAction SilentlyContinue | Out-Null +} \ No newline at end of file diff --git a/.ci/FudgeCI.ps1 b/.ci/FudgeCI.ps1 new file mode 100644 index 0000000000..8f834ce475 --- /dev/null +++ b/.ci/FudgeCI.ps1 @@ -0,0 +1,183 @@ +if (!($env:FudgeCI)) { + if (Test-Path 'assets/fudge/FudgeCI.ps1') { + $env:FudgeCI = 'assets/fudge/' + } + elseif (Test-Path '.ci/FudgeCI.ps1') { + $env:FudgeCI = '.ci/' + } +} + +. $env:FudgeCI/PrepareAVVM.ps1 + +Set-StrictMode -Version latest + +function Initialize-MinGW { + # TODO: Handle versions other than 8.1.0 + Move-Item C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64 C:\MinGW81-x64 +} + +function Initialize-AppVeyorPreinstalledProduct { + param( + [array] + $packages + ) + + foreach ($pkg in $packages) { + $name = $pkg.Name + + $product = $pkg.AppVeyor_ID + if ($product -eq $true) { + Write-Information "No version choices available for AppVeyor $name" + continue + } + + $version = $pkg.Version + + $version_parts = ($version.Split('.')) + + if (!($env:AppVeyor)) { + Write-Verbose "AppVeyor not set; skipping $product $version_parts" + continue + } + + if ($product -eq 'jdk') { + # 8 -> 1.8.0 + $version = "1." + $version_parts[0] + ".0" + } + elseif ($product -eq 'MinGW') { + Initialize-MinGW + } + elseif ($product -eq 'miniconda') { + # TODO improve translation of real miniconda versions + # into AppVeyor versions which are the python version + if ($version -eq '4.5.12') { + $version = '3.7' + } + + if ($version[0] -eq '2') { + Initialize-Miniconda27 + } + } + + # Allow the installed version of python to be over + if ($product -eq 'python') { + if ($env:PYTHON_VERSION) { + $version = $env:PYTHON_VERSION + } + } + + Add-Product $product $version $env:PLATFORM + if (Test-Path "C:\avvm\$product\$version\$env:PLATFORM") { + Install-Product $product $version $env:PLATFORM + } + elseif (Test-Path "C:\avvm\$product\$version") { + if ($env:PLATFORM -eq 'x86') { + $platform = 'x64' + } + else { + $platform = 'x86' + } + Install-Product $product $version $platform + } + } +} + +function Initialize-AppVeyorFakeChocoPackage { + param( + [array] + $packages + ) + + New-Item -ItemType Directory -Force ($env:FudgeCI + '\\nuspecs\\') > $null + + Remove-Item "$env:FudgeCI/nuspecs/*.nupkg" -Force > $null + + foreach ($pkg in $packages) { + . $env:FudgeCI/FudgeGenerateFake.ps1 + + GenerateFakeNuspec $pkg.Name $pkg.Version + + $name = $pkg.Name + + $pkg.Source = "$env:FudgeCI/nuspecs/" + + $filename = "$env:FudgeCI/nuspecs/$name.nuspec" + + Invoke-Chocolatey -Action pack -Package $pkg.Name -Version $filename + } +} + +function Get-Preinstalled { + try { + if ($config) { } + } + catch { + $config = Get-Content Fudgefile | + ConvertFrom-Json + } + + $appveyor_preinstalled = New-Object System.Collections.ArrayList + + foreach ($pkg in $config.packages) { + try { + if ($pkg.AppVeyor_ID) { + $appveyor_preinstalled.Add($pkg) > $null + } + } + catch { + continue + } + } + + $appveyor_preinstalled +} + +function Initialize-AppVeyorVM { + $appveyor_preinstalled = Get-Preinstalled + + if (!($env:AppVeyor)) { + Write-Notice "Not running on AppVeyor; skipping" + return + } + + Initialize-AppVeyorProductVersion + + Initialize-AppVeyorPreinstalledProduct $appveyor_preinstalled +} + +function Invoke-FudgeAppVeyor { + $appveyor_preinstalled = Get-Preinstalled + + if (!($env:AppVeyor)) { + Write-Notice "Not running on AppVeyor; skipping" + return + } + + Initialize-AppVeyorFakeChocoPackage $appveyor_preinstalled +} + +function Repair-Config { + foreach ($pkg in $config.packages) { + if (!($pkg.Source)) { + $pkg.Source = $config.Source + } + } +} + +function Invoke-FudgeCI { + if (!($env:CI)) { + Fix-Config + + Write-Notice "Not running on CI; skipping" + return + } + + Invoke-FudgeAppVeyor + + Repair-Config +} + +$old_EAP = $ErrorActionPreference +$ErrorActionPreference = 'SilentlyContinue'; +Export-ModuleMember -Function Prepare-AppVeyor-AVVM, Invoke-FudgeCI +$ErrorActionPreference = $old_EAP; diff --git a/.ci/FudgeGenerateFake.ps1 b/.ci/FudgeGenerateFake.ps1 new file mode 100644 index 0000000000..ac85e407f2 --- /dev/null +++ b/.ci/FudgeGenerateFake.ps1 @@ -0,0 +1,42 @@ +Set-StrictMode -Version latest + +$template = @' + + + + {name} + {version} + {name} {version} + AppVeyor + Fake generated {name} package to fulfil dependencies. + + + + + +'@ + +function GenerateFakeNuspec { + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $name, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $version + ) + + $content = $template -replace '{name}', $name + $content = $content -replace '{version}', $version + + $nuspec = ($env:FudgeCI + '\nuspecs\' + $name + '.nuspec') + + Set-Content $nuspec $content + + Write-Output "Created $nuspec" +} + +Export-ModuleMember -Function GenerateFakeNuspec diff --git a/.ci/FudgePostInstall.ps1 b/.ci/FudgePostInstall.ps1 new file mode 100644 index 0000000000..c8df753663 --- /dev/null +++ b/.ci/FudgePostInstall.ps1 @@ -0,0 +1,52 @@ +. $env:ChocolateyInstall\helpers\functions\Write-FunctionCallLogMessage.ps1 +. $env:ChocolateyInstall\helpers\functions\Get-EnvironmentVariable.ps1 +. $env:ChocolateyInstall\helpers\functions\Get-EnvironmentVariableNames.ps1 +. $env:ChocolateyInstall\helpers\functions\Start-ChocolateyProcessAsAdmin.ps1 +. $env:ChocolateyInstall\helpers\functions\Set-EnvironmentVariable.ps1 +. $env:ChocolateyInstall\helpers\functions\Set-PowerShellExitCode.ps1 +. $env:ChocolateyInstall\helpers\functions\Update-SessionEnvironment.ps1 +. $env:ChocolateyInstall\helpers\functions\Write-FunctionCallLogMessage.ps1 +. $env:ChocolateyInstall\helpers\functions\Install-ChocolateyPath.ps1 + +Set-StrictMode -Version latest + +$deps_base = $env:FudgeCI + +function Invoke-PostInstall { + choco list --local-only + + Update-SessionEnvironment + + Write-Host "PATH = $env:PATH" + + foreach ($pkg in $config.Packages) { + $name = $pkg.Name + + $glob = "$deps_base/deps.$name.ps1" + + if (Test-Path $glob) { + Write-Host "Running post-install for $name" + + . $glob + Complete-Install + } + } + + Update-SessionEnvironment + + Write-Host "PATH = $env:PATH" + + foreach ($pkg in $config.Packages) { + $name = $pkg.Name + + $glob = "$deps_base/deps.$name-packages.ps1" + if (Test-Path $glob) { + Write-Host "Running $name package installation" + + . $glob + Invoke-ExtraInstallation + } + } +} + +Export-ModuleMember -Function Invoke-PostInstall diff --git a/.ci/Modules/FudgeTools.psm1 b/.ci/Modules/FudgeTools.psm1 new file mode 100644 index 0000000000..4c51f2f95d --- /dev/null +++ b/.ci/Modules/FudgeTools.psm1 @@ -0,0 +1,1896 @@ + +function Write-Success +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Message, + + [switch] + $NoNewLine + ) + + Write-Host $Message -NoNewline:$NoNewLine -ForegroundColor Green +} + +function Write-Information +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Message, + + [switch] + $NoNewLine + ) + + Write-Host $Message -NoNewline:$NoNewLine -ForegroundColor Magenta +} + + +function Write-Details +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Message, + + [switch] + $NoNewLine + ) + + Write-Host $Message -NoNewline:$NoNewLine -ForegroundColor Cyan +} + + +function Write-Notice +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Message, + + [switch] + $NoNewLine + ) + + Write-Host $Message -NoNewline:$NoNewLine -ForegroundColor Yellow +} + + +function Write-Fail +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Message, + + [switch] + $NoNewLine + ) + + Write-Host $Message -NoNewline:$NoNewLine -ForegroundColor Red +} + + +# compares two versions, and returns a value specifying if they're equal or different +# -1: installed version is behind +# 0: installed version is latest +# 1: installed version is ahead +function Compare-Versions +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Installed, + + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $NewVersion + ) + + if (Test-VersionPassedIsLatest $NewVersion) { + return -1 + } + + $i_parts = $Installed -split '\.' + $o_parts = $NewVersion -split '\.' + + $count = $i_parts.Length + if ($o_parts.Length -gt $count) { + $count = $o_parts.Length + } + + for ($i = 0; $i -lt $count; $i++) + { + if ($i_parts[$i] -eq $o_parts[$i]) { + continue + } + + if ($i_parts[$i] -lt $o_parts[$i]) { + return -1 + } + else { + return 1 + } + } + + return 0 +} + + +# takes a package about to be installed, and checks if the version needs to be installed, +# or just a downgrade/upgrade of what's already installed +function Get-InstallAction +{ + param ( + [string] + $Package, + + [string] + $Version, + + $LocalList + ) + + # if it's not installed, it needs installing + $current = $LocalList[$Package] + if (Test-Empty $current) { + return 'install' + } + + # compare the version, and determine if we need to upgrade/downgrade + $action = Compare-Versions -Installed $current -NewVersion $Version + + switch ($action) + { + 1 { return 'downgrade' } + -1 { return 'upgrade' } + default { return 'install' } + } +} + + +# return the package name from a supplied key +function Get-PackageFromKey +{ + param ( + [string] + $Key + ) + + return ($Key -isplit '@')[0].Trim() +} + + +# return the version from a supplied key +function Get-VersionFromKey +{ + param ( + [string] + $Key + ) + + $parts = ($Key -isplit '@') + if ($parts.Length -le 1) { + return 'latest' + } + + return $parts[1].Trim() +} + + +# returns the levenshtein distance between two strings +function Get-Levenshtein +{ + param ( + [string] + $Value1, + + [string] + $Value2 + ) + + $len1 = $Value1.Length + $len2 = $Value2.Length + + if ($len1 -eq 0) { return $len2 } + if ($len2 -eq 0) { return $len1 } + + $Value1 = $Value1.ToLowerInvariant() + $Value2 = $Value2.ToLowerInvariant() + + $dist = New-Object -Type 'int[,]' -Arg ($len1 + 1), ($len2 + 1) + + 0..$len1 | ForEach-Object { $dist[$_, 0] = $_ } + 0..$len2 | ForEach-Object { $dist[0, $_] = $_ } + + $cost = 0 + + for ($i = 1; $i -le $len1; $i++) + { + for ($j = 1; $j -le $len2; $j++) + { + $cost = 1 + if ($Value2[$j - 1] -ceq $Value1[$i - 1]) + { + $cost = 0 + } + + $tempmin = [System.Math]::Min(([int]$dist[($i - 1), $j] + 1), ([int]$dist[$i, ($j - 1)] + 1)) + $dist[$i, $j] = [System.Math]::Min($tempmin, ([int]$dist[($i - 1), ($j - 1)] + $cost)) + } + } + + # the actual distance is stored in the bottom right cell + return $dist[$len1, $len2]; +} + + +# safeguards a string, by return empty or a default if empty +function Format-SafeguardString +{ + param ( + [string] + $Value, + + [string] + $Default = $null + ) + + if (!(Test-Empty $Value)) { + return $Value + } + + $Value = [string]::Empty + if (!(Test-Empty $Default)) { + $Value = $Default + } + + return $Value +} + + +# checks to see if a passed version is to use the latest version +function Test-VersionPassedIsLatest +{ + param ( + [string] + $Version + ) + + return ((Test-Empty $Version) -or ($Version.Trim() -ieq 'latest')) +} + + +# checks to see if a passed path is a valid nuspec (path, xml, and content) +function Test-Nuspec +{ + param ( + [string] + $Path + ) + + if (!(Test-NuspecPath $Path)) + { + Write-Fail "Path to nuspec file doesn't exist or is invalid: $($Path)" + return $false + } + + if (!(Test-XmlContent $Path)) + { + Write-Fail "Nuspec file fails to parse as a valid XML document: $($Path)" + return $false + } + + $nuspecData = Get-XmlContent $Path + + if (!(Test-NuspecContent $nuspecData)) + { + Write-Fail "Nuspec file is missing the package/metadata XML sections: $($Path)" + return $false + } + + return $true +} + + +# checks to see if a passed path is a valid nuspec file path +function Test-NuspecPath +{ + param ( + [string] + $Path + ) + + # ensure a path was passed + if ([string]::IsNullOrWhiteSpace($Path)) + { + return $false + } + + # ensure path is exists, or is not just a directory path + if (!(Test-Path $Path) -or (Test-PathDirectory $Path)) + { + return $false + } + + return $true +} + + +# checks to see if the file at passed path is a valid XML file +function Test-XmlContent +{ + param ( + [string] + $Path + ) + + # fail if the path doesn't exist + if ([string]::IsNullOrWhiteSpace($Path) -or !(Test-Path $Path)) + { + return $false + } + + # ensure the content parses as xml + try + { + [xml](Get-Content $Path) | Out-Null + return $true + } + catch [exception] + { + return $false + } +} + + +# checks to see if the passed XML content is a valid nuspec file +function Test-NuspecContent +{ + param ( + [xml] + $Content + ) + + return ($Content -ne $null -and $Content.package -ne $null -and $Content.package.metadata -ne $null) +} + + +# returns the XML content of the file at the passed path +function Get-XmlContent +{ + param ( + [string] + $Path + ) + + if (!(Test-XmlContent $Path)) + { + return $null + } + + return [xml](Get-Content $Path) +} + + +# checks to see if a passed path is a directory +function Test-PathDirectory +{ + param ( + [string] + $Path + ) + + if ([string]::IsNullOrWhiteSpace($Path) -or !(Test-Path $Path)) + { + return $false + } + + return ((Get-Item $Path) -is [System.IO.DirectoryInfo]) +} + + +# returns a path to a fidgefile based on a passed path +function Get-FudgefilePath +{ + param ( + [string] + $Path, + + [switch] + $Adhoc + ) + + if ($Adhoc) + { + return [string]::Empty + } + + $rootpath = './Fudgefile' + if (![string]::IsNullOrWhiteSpace($Path)) + { + if ((Test-Path $Path) -and (Test-PathDirectory $Path)) + { + $rootpath = Join-Path $Path 'Fudgefile' + } + else + { + $rootpath = $Path + } + } + + return $rootpath +} + + +# returns the content of a passed fudgefile path +function Get-FudgefileContent +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Path + ) + + if (!(Test-Path $Path)) { + throw "Path to Fudgefile does not exist: $($Path)" + } + + try { + $config = Get-Content -Path $Path -Raw | ConvertFrom-Json + } + catch { + throw "Failed to parse the Fudgefile at: $($Path)`n$($_.Exception)" + } + + return $config +} + + +# removes an existing fudgefile, and if passed attempting to uninstall the packages +function Remove-Fudgefile +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Path, + + $LocalList, + + [switch] + $Uninstall, + + [switch] + $Dev, + + [switch] + $DevOnly + ) + + # ensure the path actually exists + if (!(Test-Path $Path)) + { + Write-Fail "Path to Fudgefile does not exist: $($Path)" + return + } + + # uninstall packages first, if requested + if ($Uninstall) + { + $config = Get-FudgefileContent $Path + Invoke-ChocolateyAction -Action 'uninstall' -Key $null -Config $config -LocalList $LocalList -Dev:$Dev -DevOnly:$DevOnly + } + + # remove the fudgefile + Write-Information "> Deleting Fudgefile" -NoNewLine + Remove-Item -Path $Path -Force -Confirm:$false | Out-Null + Write-Success " > deleted" + Write-Details " > $($Path)" +} + + +# restores the packages of an existing fudgefile (either empty, from nuspec, or from local) +function Restore-Fudgefile +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Path, + + [string] + $Key, + + $LocalList, + + [switch] + $Install, + + [switch] + $Uninstall, + + [switch] + $Dev, + + [switch] + $DevOnly + ) + + # retrieve the content of the current fudgefile + $content = Get-FudgefileContent $Path + + # check to see if we're restoring this file using local packages + if ($Key -ieq 'local') + { + if (Test-Empty $LocalList) + { + Write-Fail "No packages installed locally to renew Fudgefile" + return + } + + Write-Information "> Renewing Fudgefile using $($LocalList.Count) local packages" -NoNewLine + } + + # check if there are any nuspecs in the pack section + elseif ($Key -ieq 'nuspec') + { + if (Test-Empty $content.pack) + { + Write-Fail "No nuspecs in pack section to renew Fudgefile" + return + } + + Write-Information "> Renewing Fudgefile using nuspecs" -NoNewLine + } + + # else if it's not empty, we don't know what they mean + elseif (!(Test-Empty $Key)) + { + Write-Fail "Renew command not recognised: $($Key). Should be blank, or either local/nuspec" + return + } + + # else we're renewing to an empty file + else + { + Write-Information "> Renewing Fudgefile" -NoNewLine + } + + # first, uninstall the packages, if requested + if ($Uninstall) + { + Invoke-ChocolateyAction -Action 'uninstall' -Key $null -Config $content -LocalList $LocalList -Dev:$Dev -DevOnly:$DevOnly + } + + # remove all current packages + if (!$DevOnly) + { + $content.packages = @() + } + + if ($Dev) + { + $content.devPackages = @() + } + + # insert packages from any nuspecs in the pack section + if ($Key -ieq 'nuspec') + { + $nuspecs = $content.pack.psobject.properties.name + $nuspecs | ForEach-Object { + $content = Add-PackagesFromNuspec -Content $content -Path $content.pack.$_ -Dev:$Dev -DevOnly:$DevOnly + } + } + + # insert any data from the local packages + if ($Key -ieq 'local') + { + $content = Add-PackagesFromLocal -Content $content -LocalList $LocalList -DevOnly:$DevOnly + } + + # save contents as json + $content | ConvertTo-Json | Out-File -FilePath $Path -Encoding utf8 -Force + Write-Success " > renewed" + Write-Details " > $($Path)" + + # now install the packages + if ($Install) + { + Invoke-ChocolateyAction -Action 'install' -Key $null -Config $content -LocalList $LocalList -Dev:$Dev -DevOnly:$DevOnly + } +} + + +# create a new empty fudgefile, or a new one from a nuspec file +function New-Fudgefile +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Path, + + [string] + $Key, + + $LocalList, + + [switch] + $Install, + + [switch] + $Dev, + + [switch] + $DevOnly + ) + + # check to see if we're making this file using local packages + if ($Key -ieq 'local') + { + if (Test-Empty $LocalList) + { + Write-Fail "No packages installed locally to create new Fudgefile" + return + } + + Write-Information "> Creating new Fudgefile using $($LocalList.Count) local packages" -NoNewLine + } + + # if the key is passed, ensure it's a valid nuspec file + elseif (!(Test-Empty $Key)) + { + if (!(Test-Nuspec $Key)) + { + return + } + + $nuspecName = Split-Path -Leaf -Path $Key + Write-Information "> Creating new Fudgefile using $($nuspecName)" -NoNewLine + } + + # else it's just an empty file + else + { + Write-Information "> Creating new Fudgefile" -NoNewLine + } + + # setup the empty fudgefile + $content = @{ + 'scripts' = @{ + 'pre' = @{ 'install'= ''; 'uninstall'= ''; 'upgrade'= ''; 'downgrade' = ''; 'pack'= ''; }; + 'post' = @{ 'install'= ''; 'uninstall'= ''; 'upgrade'= ''; 'downgrade' = ''; 'pack'= ''; }; + }; + 'packages' = @(); + 'devPackages' = @(); + 'pack' = @{}; + } + + # insert any data from the nuspec + if (!(Test-Empty $nuspecName)) + { + $content = Add-PackagesFromNuspec -Content $content -Path $Key -Dev:$Dev -DevOnly:$DevOnly + $name = [System.IO.Path]::GetFileNameWithoutExtension($nuspecName) + $content.pack[$name] = $Key + } + + # insert any data from the local packages + if ($Key -ieq 'local') + { + $content = Add-PackagesFromLocal -Content $content -LocalList $LocalList -DevOnly:$DevOnly + } + + # save contents as json + $content | ConvertTo-Json | Out-File -FilePath $Path -Encoding utf8 -Force + Write-Success " > created" + Write-Details " > $($Path)" + + # now install the packages + if ($Install) + { + $config = Get-FudgefileContent $Path + Invoke-ChocolateyAction -Action 'install' -Key $null -Config $config -LocalList $LocalList -Dev:$Dev -DevOnly:$DevOnly + } +} + + +# adds packages to a fudgefile from local packages +function Add-PackagesFromLocal +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNull()] + $Content, + + [Parameter(Mandatory=$true)] + [ValidateNotNull()] + $LocalList, + + [switch] + $DevOnly + ) + + $LocalList.Keys | ForEach-Object { + $package = @{ + 'name' = $_; + 'version' = $LocalList[$_]; + } + + if ($DevOnly) + { + $Content.devPackages += $package + } + else + { + $Content.packages += $package + } + } + + return $Content +} + + +# adds packages to a fudgefile from a nuspec file +function Add-PackagesFromNuspec +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNull()] + $Content, + + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + $Path, + + [switch] + $Dev, + + [switch] + $DevOnly + ) + + # get the content from the nuspec + $data = Get-XmlContent -Path $Path + $metadata = $data.package.metadata + + # if we have any dependencies, add them as packages + if (!$DevOnly -and $metadata.dependencies -ne $null) + { + $metadata.dependencies.dependency | ForEach-Object { + $version = Format-SafeguardString $_.version 'latest' + $Content.packages += @{ + 'name' = $_.id; + 'version' = $version; + } + } + } + + # if we have any devDependencies, add them as devPackages + if ($Dev -and $metadata.devDependencies -ne $null) + { + $metadata.devDependencies.dependency | ForEach-Object { + $version = Format-SafeguardString $_.version 'latest' + $Content.devPackages += @{ + 'name' = $_.id; + 'version' = $version; + } + } + } + + return $Content +} + + +# checks to see if the user has administrator privileges +function Test-AdminUser +{ + if ($PSVersionTable.Platform -ieq 'Unix') + { + Write-Notice 'Windows Admin check bypassed on Unix' + return $true + } + + try + { + $principal = New-Object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent()) + + if ($principal -eq $null) + { + return $false + } + + return $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) + } + catch [exception] + { + Write-Fail 'Error checking user administrator privileges' + Write-Fail $_.Exception.Message + return $false + } +} + + +# checks to see if the passed value is empty +function Test-Empty +{ + param ( + $Value + ) + + if ($Value -eq $null) + { + return $true + } + + if ($Value.GetType().Name -ieq 'string') + { + return [string]::IsNullOrWhiteSpace($Value) + } + + $type = $Value.GetType().BaseType.Name.ToLowerInvariant() + switch ($type) + { + 'valuetype' + { + return $false + } + + 'array' + { + return (($Value | Measure-Object).Count -eq 0 -or $Value.Count -eq 0) + } + } + + return ([string]::IsNullOrWhiteSpace($Value) -or ($Value | Measure-Object).Count -eq 0 -or $Value.Count -eq 0) +} + + +# check to see if chocolatey is installed on the current machine +function Test-Chocolatey +{ + try { + $output = Invoke-Expression -Command 'choco -v' + Write-Details "Chocolatey v$($output)" + return $true + } + catch { + return $false + } +} + + +# installs chocolatey +function Install-Chocolatey +{ + Write-Notice "Installing Chocolatey" + + $policies = @('Unrestricted', 'ByPass', 'AllSigned') + if ($policies -inotcontains (Get-ExecutionPolicy)) { + Set-ExecutionPolicy Bypass -Force + } + + Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) | Out-Null + Write-Success "Chocolatey installed`n" +} + + +# invoke scripts for pre/post actions +function Invoke-Script +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Action, + + [string] + $Stage, + + $Scripts + ) + + # if there is no stage, return + if (Test-Empty $Stage) { + return + } + + # if there are no scripts, return + if ((Test-Empty $Scripts) -or (Test-Empty $Scripts.$Stage)) { + return + } + + $script = $Scripts.$Stage.$Action + if (Test-Empty $script) { + return + } + + # run the script + Invoke-Expression -Command $script +} + + +# cycle through the passed packages, actioning upon them +function Start-ActionPackages +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Action, + + [string] + $Key, + + [string] + $Source, + + [array] + $Packages, + + [string] + $Arguments, + + $LocalList + ) + + # if there are no packages to deal with, return + if (Test-Empty $Packages) { + return + } + + # have we installed anything + $installed = $false + $haveKey = (![string]::IsNullOrWhiteSpace($Key)) + + # loop through each of the packages, and call chocolatey on them + foreach ($pkg in $Packages) + { + # skip if we forcing action on a specific package + if ($haveKey -and ($pkg.name -ine $Key)) { + continue + } + + # use a package specific custom source + if (![string]::IsNullOrWhiteSpace($pkg.source)) { + $Source = $pkg.source + } + + # force a specific action on the package + $_action = $Action + if (!(Test-Empty $pkg.action)) { + $_action = $pkg.action + } + + $installed = $true + + Invoke-Chocolatey -Action $_action -Package $pkg.name -Version $pkg.version ` + -Source $Source -Parameters $pkg.params -Arguments "$($pkg.args) $($Arguments)".Trim() -LocalList $LocalList + } + + # if we didn't install anything, and we have a key - say it isn't present in file + if (!$installed -and $haveKey) { + Write-Notice "Package not found in Fudgefile: $($Key)" + } +} + + +# cycle through the passed nuspecs for packing via nuget +function Start-ActionPack +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Action, + + [string] + $Key, + + $Nuspecs + ) + + # if there are no nuspecs to deal with, return + if (Test-Empty $Nuspecs) { + return + } + + # have we packed anything + $packed = $false + $haveKey = (![string]::IsNullOrWhiteSpace($Key)) + + # loop through each of the nuspecs, and call chocolatey on them for packing + foreach ($pkg in $Nuspecs.psobject.properties.name) + { + if ($haveKey -and ($pkg -ine $Key)) { + continue + } + + $packed = $true + Invoke-Chocolatey -Action $Action -Package $pkg -Version ($Nuspecs.$pkg) + } + + # if we didn't pavk anything, and we have a key - say it isn't present in file + if (!$packed -and $haveKey) { + Write-Notice "Nuspec not found in Fudgefile: $($Key)" + } +} + + +# invokes a chocolatey action, which also runs the pre/post scripts +function Invoke-ChocolateyAction +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Action, + + [string] + $Key, + + [string] + $Source, + + $Config, + + $LocalList, + + [string] + $Parameters, + + [string] + $Arguments, + + [switch] + $Dev, + + [switch] + $DevOnly, + + [switch] + $Adhoc + ) + + # ensure the config object exists, unless adhoc is supplied + if (!$Adhoc -and $null -eq $Config) { + throw "Invalid Fudge configuration supplied" + } + + # if we're running as adhoc, just call chocolatey directly + if ($Adhoc) + { + $pkg = Get-PackageFromKey -Key $Key + $vsn = Get-VersionFromKey -Key $Key + Invoke-Chocolatey -Action $Action -Package $pkg -Source $Source -Version $vsn -LocalList $LocalList -Parameters $Parameters -Arguments $Arguments + } + else + { + # invoke pre-script for current action + Invoke-Script -Action $Action -Stage 'pre' -Scripts $Config.scripts + + # invoke chocolatey based on the action + switch ($Action.ToLowerInvariant()) { + 'pack' { + # run pack on supplied nuspecs + Start-ActionPack -Action $Action -Key $Key -Nuspecs $Config.pack + } + + default { + # install normal packages, unless flagged as dev only + if (!$DevOnly) { + Start-ActionPackages -Action $Action -Key $Key -Packages $Config.packages -Source $Source -LocalList $LocalList -Arguments $Arguments + } + + # install dev packages + if ($Dev) { + Start-ActionPackages -Action $Action -Key $Key -Packages $Config.devPackages -Source $Source -LocalList $LocalList -Arguments $Arguments + } + } + } + + # invoke post-script for current action + Invoke-Script -Action $Action -Stage 'post' -Scripts $Config.scripts + } +} + + +# add the core chocolatey and fudge packages to a package map +function Add-CoreChocoPackages +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNull()] + $Packages + ) + + $Packages['chocolatey'] = 'latest' + $Packages['chocolatey-core.extension'] = 'latest' + $Packages['fudge'] = 'latest' + + return $Packages +} + + +# pruning the current machine's packages with what's in the fudgefile +function Invoke-FudgePrune +{ + param ( + $Config, + + $LocalList, + + [switch] + $Dev, + + [switch] + $DevOnly + ) + + # package map for what to leave installed + $packages = @{} + $pruned = $false + + if (!$DevOnly) + { + $Config.packages | ForEach-Object { $packages[$_.name] = $_.version } + } + + if ($Dev) + { + $Config.devPackages | ForEach-Object { $packages[$_.name] = $_.version } + } + + # add core chocolatey packages (and fudge) + $packages = Add-CoreChocoPackages $packages + + # loop through each local package, and remove if not in fudgefile + $LocalList.Keys | ForEach-Object { + if (!$packages.ContainsKey($_)) + { + $pruned = $true + Invoke-Chocolatey -Action 'uninstall' -Package $_ + } + } + + if(!$pruned) + { + Write-Notice 'Nothing to prune' + } +} + + +# cleaning a machine of all packages currently installed +function Invoke-FudgeClean +{ + param ( + $LocalList + ) + + # package map for what to leave installed + $packages = @{} + $cleaned = $false + + # add core chocolatey packages (and fudge) + $packages = Add-CoreChocoPackages $packages + + # loop through each local package, and remove it + $LocalList.Keys | ForEach-Object { + if (!$packages.ContainsKey($_)) + { + $cleaned = $true + Invoke-Chocolatey -Action 'uninstall' -Package $_ + } + } + + if(!$cleaned) + { + Write-Notice 'Nothing to clean' + } +} + + +# add a package to a fudgefile +function Invoke-FudgeAdd +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Path, + + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Key, + + [string] + $Source, + + $Config, + + $LocalList, + + [string] + $Parameters, + + [string] + $Arguments, + + [switch] + $Dev, + + [switch] + $Install + ) + + # get name and version from key + $KeyName = Get-PackageFromKey -Key $Key + $KeyVer = Get-VersionFromKey -Key $Key + + # ensure package isn't already present + $pkg_count = ($config.packages | Where-Object { $_.name -ieq $KeyName } | Measure-Object).Count + $dev_count = ($config.devPackages | Where-Object { $_.name -ieq $KeyName } | Measure-Object).Count + + if ($pkg_count -ne 0 -or $dev_count -ne 0) + { + Write-Notice "The package '$($KeyName)' already exists in the Fudgefile" + return + } + + # attempt to install if specified + if ($Install) + { + Invoke-Chocolatey -Action 'install' -Package $KeyName -Source $Source -Version $KeyVer -LocalList $LocalList -Parameters $Parameters -Arguments $Arguments + } + + Write-Information "> Adding $($KeyName)@$($KeyVer) to Fudgefile" -NoNewLine + + # create package json object + $obj = @{ 'name' = $KeyName; } + + if (!(Test-Empty $KeyVer)) + { + $obj['version'] = $KeyVer + } + + if (!(Test-Empty $Source) -and ($Source -ine $Config.source)) + { + $obj['source'] = $Source + } + + # add to config + if ($Dev) + { + [array]$Config.devPackages += $obj + } + else + { + [array]$Config.packages += $obj + } + + # save new config back + $Config | ConvertTo-Json | Out-File -FilePath $Path -Encoding utf8 -Force + Write-Success " > added" + Write-Details " > $($Path)" +} + + +# removes a package from a fudgefile +function Invoke-FudgeRemove +{ + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Path, + + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Key, + + $Config, + + $LocalList, + + [string] + $Parameters, + + [string] + $Arguments, + + [switch] + $Dev, + + [switch] + $Uninstall + ) + + # get name and version from key + $KeyName = Get-PackageFromKey -Key $Key + + # ensure package isn't already removed + $pkg_count = ($config.packages | Where-Object { $_.name -ieq $KeyName } | Measure-Object).Count + $dev_count = ($config.devPackages | Where-Object { $_.name -ieq $KeyName } | Measure-Object).Count + + if ($pkg_count -eq 0 -and $dev_count -eq 0) + { + Write-Notice "The package '$($KeyName)' is not present in the Fudgefile" + return + } + + # attempt to uninstall if specified + if ($Uninstall) + { + Invoke-Chocolatey -Action 'uninstall' -Package $KeyName -LocalList $LocalList -Parameters $Parameters -Arguments $Arguments + } + + Write-Information "> Removing $($KeyName) from Fudgefile" -NoNewLine + + # remove to config + if ($Dev) + { + $Config.devPackages = ($Config.devPackages | Where-Object { $_.name -ine $KeyName }) + } + else + { + $Config.packages = ($Config.packages | Where-Object { $_.name -ine $KeyName }) + } + + # save new config back + $Config | ConvertTo-Json | Out-File -FilePath $Path -Encoding utf8 -Force + Write-Success " > removed" + Write-Details " > $($Path)" +} + + +# returns the path for where a command is located +function Invoke-FudgeWhich +{ + param ( + [string] + $Key + ) + + if (Test-Empty $Key) + { + Write-Notice 'No command passed to find' + } + else + { + $path = (Get-Command -Name $Key -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Definition) + if (!(Test-Empty $path)) + { + Write-Host "> $($path)" + } + else + { + Write-Notice "Command not found: $($Key)" + } + } +} + + +# returns a source argument for chocolatey +function Format-ChocolateySource +{ + param ( + [string] + $Source + ) + + if (Test-Empty $Source) { + return [string]::Empty + } + + return "-s '$($Source)'" +} + + +# returns a params argument for chocolatey +function Format-ChocolateyParams +{ + param ( + [string] + $Parameters + ) + + if (Test-Empty $Parameters) { + return [string]::Empty + } + + return "--params='$($Parameters)'" +} + + +# returns a version argument for chocolatey +function Format-ChocolateyVersion +{ + param ( + [string] + $Version + ) + + # if the version is latest, return empty + if (Test-VersionPassedIsLatest -Version $Version) { + return [string]::Empty + } + + # return the specific version to install + return "--version $($Version)" +} + + +# returns the list of search returns from chocolatey +function Get-ChocolateySearchList +{ + param ( + [string] + $Key, + + [string] + $Source + ) + + # set the source if we have one + $Source = Format-ChocolateySource $Source + + $list = (choco search $Key $Source) + if (!$?) { + Write-Fail "$($list)" + throw 'Failed to retrieve search results from Chocolatey' + } + + return (Format-ChocolateyList -List $list) +} + + +# returns a list of packages installed localled +function Get-ChocolateyLocalList +{ + $list = (choco list -lo) + if (!$?) { + Write-Fail "$($list)" + throw 'Failed to retrieve local list of installed packages' + } + + return (Format-ChocolateyList -List $list) +} + + +# formats list/search results from chocolatey into a hash table +function Format-ChocolateyList +{ + param ( + [string[]] + $List + ) + + $map = @{} + + $List | ForEach-Object { + $row = $_ -ireplace ' Downloads cached for licensed users', '' + if ($row -imatch '^(?.*?)\s+(?[\d\.]+(\s+\[Approved\]){0,1}(\s+-\s+Possibly broken){0,1}).*?$') + { + $map[$Matches['name']] = $Matches['version'] + } + } + + return $map +} + +# get chocolatey search results, filtered by fudge +function Format-ChocolateySearchResults +{ + param ( + [Parameter()] + [string] + $Key, + + [Parameter()] + [int] + $Limit, + + [Parameter()] + [string] + $Source + ) + + # get search results from chocolatey + $results = Get-ChocolateySearchList -Key $Key -Source $Source + $OrganisedResults = @() + $count = 0 + + # if limit is 0, set to total results returned + if ($Limit -eq 0) { + $Limit = ($results | Measure-Object).Count + } + + # perfect match result + if ($results.ContainsKey($Key)) { + $count++ + $OrganisedResults += @{ 'Name' = $Key; 'Version' = $results[$Key]; } + $results.Remove($Key) + } + + # starts with for sub-packages + if ($count -lt $Limit) + { + $results.Clone().Keys | ForEach-Object { + if ($_.StartsWith("$($Key).")) { + $count++ + $OrganisedResults += @{ 'Name' = $_; 'Version' = $results[$_]; } + $results.Remove($_) + } + } + } + + # starts with for other packages + if ($count -lt $Limit) + { + $results.Clone().Keys | ForEach-Object { + if ($_.StartsWith($Key)) { + $count++ + $OrganisedResults += @{ 'Name' = $_; 'Version' = $results[$_]; } + $results.Remove($_) + } + } + } + + # contains the key + if ($count -lt $Limit) + { + $results.Clone().Keys | ForEach-Object { + if ($_.Contains($Key)) { + $count++ + $OrganisedResults += @{ 'Name' = $_; 'Version' = $results[$_]; } + $results.Remove($_) + } + } + } + + # levenshtein for remaining packages + if ($count -lt $Limit) + { + $leven = @() + + $results.Keys | ForEach-Object { + $leven += @{ 'Name' = $_; 'Version' = $results[$_]; 'Dist' = (Get-Levenshtein $Key $_) } + } + + $leven | Sort-Object { $_.Dist } | ForEach-Object { + $OrganisedResults += @{ 'Name' = $_.name; 'Version' = $_.Version; } + } + } + + return @($OrganisedResults) +} + + +# invokes fudge to search chocolatey for a package and display the results +function Invoke-Search +{ + param ( + [string] + $Key, + + [int] + $Limit, + + [string] + $Source, + + $LocalList + ) + + # validate the key/package name supplied + if ([string]::IsNullOrWhiteSpace($Key)) { + Write-Notice 'No search key provided' + return + } + + # validate the limit supplied + if ($Limit -lt 0) { + Write-Notice "Limit for searching must be 0 or greater, found: $($Limit)" + return + } + + # get search results from chocolatey + $results = Format-ChocolateySearchResults -Key $Key -Limit $Limit -Source $Source + + # display the search results + $results | Select-Object -First $Limit | ForEach-Object { + if ($LocalList.ContainsKey($_.Name)) + { + ($_.Version -imatch '^(?[\d\.]+).*?$') | Out-Null + + if ($Matches['version'] -eq $LocalList[$_.Name]) { + Write-Success ("{0,-30} {1,-40} (installed: {2})" -f $_.Name, $_.Version, $LocalList[$_.Name]) + } + else { + Write-Notice ("{0,-30} {1,-40} (installed: {2})" -f $_.Name, $_.Version, $LocalList[$_.Name]) + } + } + else { + Write-Host ("{0,-30} {1,-30}" -f $_.Name, $_.Version) + } + } + + # display the total + $total = ($results | Measure-Object).Count + Write-Notice "$($total) package(s) found" +} + + +# invokes fudge to display details of local packages +function Invoke-FudgeLocalDetails +{ + param ( + $Config, + + [string] + $Key, + + $LocalList, + + [switch] + $Dev, + + [switch] + $DevOnly + ) + + # maps for filtering packages + $installed = @{} + $updating = @{} + $missing = @{} + + # package map + $packages = @{} + + if (!$DevOnly) { + $Config.packages | ForEach-Object { $packages[$_.name] = $_.version } + } + + if ($Dev) { + $Config.devPackages | ForEach-Object { $packages[$_.name] = $_.version } + } + + # loop through packages + $packages.Keys | ForEach-Object { + if ([string]::IsNullOrWhiteSpace($Key) -or $_ -ieq $Key) + { + $version = $packages[$_] + + if ($LocalList.ContainsKey($_)) + { + if ($LocalList[$_] -ieq $version -or (Test-VersionPassedIsLatest $version)) { + $installed[$_] = $version + } + else { + $updating[$_] = $version + } + } + else { + $missing[$_] = $version + } + } + } + + if (![string]::IsNullOrWhiteSpace($Key) -and (Test-Empty $installed) -and (Test-Empty $updating) -and (Test-Empty $missing)) + { + $missing[$Key] = 'Not in Fudgefile' + } + + # output the details + Write-Host "Package details from Fudgefile:" + $installed.Keys | Sort-Object | ForEach-Object { Write-Success ("{0,-30} {1,-20} (installed: {2})" -f $_, $installed[$_], $LocalList[$_]) } + $updating.Keys | Sort-Object | ForEach-Object { Write-Notice ("{0,-30} {1,-20} (installed: {2})" -f $_, $updating[$_], $LocalList[$_]) } + $missing.Keys | Sort-Object | ForEach-Object { Write-Fail ("{0,-30} {1,-20}" -f $_, $missing[$_]) } +} + +function Get-ChocolateyLatestVersion +{ + param ( + [Parameter()] + [string] + $Package, + + [Parameter()] + [string] + $Source + ) + + $result = (Format-ChocolateySearchResults -Key $Package -Limit 1 -Source $Source | Select-Object -First 1) + if (!(Test-Empty $result)) { + return ($result.Version -split '\s+')[0] + } + + return 'latest' +} + +# invoke chocolate for the specific action +function Invoke-Chocolatey +{ + param ( + [Parameter()] + [string] + $Action, + + [Parameter()] + [string] + $Package, + + [Parameter()] + [string] + $Version, + + [Parameter()] + [string] + $Source, + + [Parameter()] + [string] + $Parameters, + + [Parameter()] + [string] + $Arguments, + + [Parameter()] + $LocalList + ) + + # if there was no package passed, return + if (Test-Empty $Package) { + return + } + + # set the source from which to install/upgrade/downgrade packages + $SourceArg = Format-ChocolateySource $Source + + # set the parameters to pass + $ParametersArg = Format-ChocolateyParams $Parameters + + $Arguments += ' --allow-unofficial' + + # if the version is latest, attempt to get the real current version + $latest = Test-VersionPassedIsLatest -Version $Version + if ($latest) { + $Version = Get-ChocolateyLatestVersion -Package $Package -Source $Source + } + + $Version = Format-SafeguardString $Version 'latest' + + # build a version string, so if latest we can show the latest version + $VersionStr = "$($Version)" + if ($latest) { + $VersionStr = "latest [$($Version)]" + } + + # set the version arg to pass + $VersionArg = Format-ChocolateyVersion $Version + + # if action is 'install', do we need to install, or upgrade/downgrade based on version? + if (($Action -ieq 'install') -and ($null -ne $LocalList)) { + $Action = Get-InstallAction -Package $Package -Version $Version -LocalList $LocalList + } + + # attempt 3 times - mostly do to help prevent timeouts + foreach ($i in 1..3) + { + $success = $true + + # run chocolatey for appropriate action + switch ($Action.ToLowerInvariant()) + { + 'install' + { + if ($i -eq 1) { + Write-Information "> Installing $($Package) ($($VersionStr))" -NoNewLine + } + $output = Invoke-Expression "choco install $($Package) $($VersionArg) -y $($SourceArg) $($ParametersArg) $($Arguments)" + } + + 'upgrade' + { + if ($i -eq 1) { + Write-Information "> Upgrading $($Package) to ($($VersionStr))" -NoNewLine + } + $output = Invoke-Expression "choco upgrade $($Package) $($VersionArg) -y $($SourceArg) $($ParametersArg) $($Arguments)" + } + + 'downgrade' + { + if ($i -eq 1) { + Write-Information "> Downgrading $($Package) to ($($VersionStr))" -NoNewLine + } + $output = Invoke-Expression "choco upgrade $($Package) $($VersionArg) -y $($SourceArg) $($ParametersArg) $($Arguments) --allow-downgrade" + } + + 'uninstall' + { + if ($i -eq 1) { + Write-Information "> Uninstalling $($Package)" -NoNewLine + } + $output = Invoke-Expression "choco uninstall $($Package) -y -x $($ParametersArg) $($Arguments)" + } + + 'pack' + { + Write-Information "> Packing $($Package)" -NoNewLine + $path = Split-Path -Parent -Path $Version + $name = Split-Path -Leaf -Path $Version + + try { + Push-Location $path + $output = Invoke-Expression "choco pack $($name) $($Arguments)" + } + finally { + Pop-Location + } + } + } + + # determine if we failed, or if the exit code indicates a reboot is needed, + # or we've timed out and need to try + $lastcode = $LASTEXITCODE + if (!$? -or (@(0, 3010) -notcontains $lastcode)) + { + # flag as failure + $success = $false + + # check if the package timed-out, so we can retry again + if ($output -ilike '*the operation has timed out*') { + Start-Sleep -Seconds 5 + continue + } + + # double check that the error isn't a false positive + switch ($Action) + { + {($_ -ieq 'uninstall')} + { + $success = ($output -ilike '*has been successfully uninstalled*' -or $output -ilike '*Cannot uninstall a non-existent package*') + } + + {($_ -ieq 'install') -or ($_ -ieq 'upgrade') -or ($_ -ieq 'downgrade')} + { + $success = ($output -ilike '*has been successfully installed*' -or $output -ilike '*has been installed*') + } + } + } + + # if we get here, break out + break + } + + # if not successful, output the error details + if (!$success) { + Write-Fail " > failed" + Write-Notice "`n`n$($output)`n" + throw "Failed to $($Action) package: $($Package)" + } + + $actionVerb = ("$($Action)ed" -ireplace 'eed$', 'ed') + + if ($output -ilike '*exit code 3010*' -or $lastcode -eq 3010) + { + Write-Success " > $($actionVerb)" -NoNewLine + Write-Notice " > Reboot Required" + } + else { + Write-Success " > $($actionVerb)" + } +} \ No newline at end of file diff --git a/.ci/PSLint.ps1 b/.ci/PSLint.ps1 new file mode 100644 index 0000000000..e989bb044c --- /dev/null +++ b/.ci/PSLint.ps1 @@ -0,0 +1,94 @@ +Set-StrictMode -Version Latest + +$ErrorActionPreference = 'Stop' + +Import-Module -Name 'PsScriptAnalyzer' -Force +$base = $global:PSScriptRoot + +$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False + +function Test-Skip-File { + param ( + [Parameter(Mandatory)] + [string] + $filename + ) + + # Ignore imported files + # https://github.com/pypa/virtualenv/issues/1371 + # https://github.com/ogrisel/python-appveyor-demo/issues/55 + # https://github.com/MathieuBuisson/PowerShell-DevOps/issues/6 + return ( + $filename -eq 'Export-NUnitXml.psm1' -or + $filename -eq 'Fudge.ps1' -or + $filename -eq 'FudgeTools.psm1' -or + $filename -eq 'install.ps1' -or + $filename -eq 'activate.ps1' -or + $filename.EndsWith('.jj2')) +} + +$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False + +$UNIXEOL = "`n" + +function ReformatFile { + param ( + [Parameter(Mandatory)] + [string] + $filename, + + [Parameter(Mandatory)] + [string] + $eol + ) + $orig = Get-Content -Raw $filename + $content = Invoke-Formatter -ScriptDefinition $orig -Settings $base\PSScriptAnalyzerSettings.psd1 -ErrorAction Stop + + if ($content) { + $content = $content -split "`r?`n" + if (!($content[-1])) { + $content = $content[0..($content.length - 2)] + } + $content = (($content -join $eol) + $eol) + [System.IO.File]::WriteAllText($filename, $content, $Utf8NoBomEncoding) + } +} + +[Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]$ScriptAnalyzerResult = $null + +Get-ChildItem -Recurse -Force '*.ps*' | ForEach-Object { + $path = $_.FullName + + if ( $path.Contains('.git') -or (Test-Skip-File($_.Name)) ) { + Write-Output "Skipping $path" + } + else { + $NewResult = Invoke-ScriptAnalyzer -IncludeDefaultRules -Setting $base\PSScriptAnalyzerSettings.psd1 -Path $path + if ($ScriptAnalyzerResult) { + $ScriptAnalyzerResult += $NewResult + } + else { + $ScriptAnalyzerResult = $NewResult + } + + ReformatFile $path $UNIXEOL + } +} + +if ($env:APPVEYOR) { + Import-Module "$base\Export-NUnitXml.psm1" + Export-NUnitXml -ScriptAnalyzerResult $ScriptAnalyzerResult -Path ".\ScriptAnalyzerResult.xml" + (New-Object 'System.Net.WebClient').UploadFile( + "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", + (Resolve-Path .\ScriptAnalyzerResult.xml) + ) +} + +git diff + +if ($ScriptAnalyzerResult) { + Write-Output $ScriptAnalyzerResult + + # Failing the build + Throw 'Build failed because there was one or more PSScriptAnalyzer violation. See test results for more information.' +} diff --git a/.ci/PSScriptAnalyzerSettings.psd1 b/.ci/PSScriptAnalyzerSettings.psd1 new file mode 100644 index 0000000000..1199c79dad --- /dev/null +++ b/.ci/PSScriptAnalyzerSettings.psd1 @@ -0,0 +1,120 @@ +@{ + Severity = @('Error', 'Warning') + + IncludeDefaultRules = $true + + # List all rules here, otherwise they are excluded. + # Some are inactive because they need to be configured before usable. + + IncludeRules = @( + 'PSAlignAssignmentStatement' + 'PSAvoidAlias' + 'PSAvoidAssignmentToAutomaticVariable' + 'PSAvoidDefaultTrueValueSwitchParameter' + 'PSAvoidDefaultValueForMandatoryParameter' + 'PSAvoidEmptyCatchBlock' + 'PSAvoidGlobalAliases' + 'PSAvoidGlobalFunctions' + 'PSAvoidGlobalVars' + 'PSAvoidInvokingEmptyMembers' + 'PSAvoidNullOrEmptyHelpMessageAttribute' + 'PSAvoidPositionalParameters' + 'PSAvoidReservedCharInCmdlet' + 'PSAvoidReservedParams' + 'PSAvoidShouldContinueWithoutForce' + 'PSAvoidTrailingWhitespace' + 'PSAvoidUserNameAndPasswordParams' + 'PSAvoidUsingComputerNameHardcoded' + 'PSAvoidUsingConvertToSecureStringWithPlainText' + 'PSAvoidUsingDeprecatedManifestFields' + 'PSAvoidUsingInvokeExpression' + 'PSAvoidUsingPlainTextForPassword' + 'PSAvoidUsingWMICmdlet' + # Easier to use inside Fudge + # 'PSAvoidUsingWriteHost' + 'PSDscExamplesPresent' + 'PSDscTestsPresent' + 'PSMisleadingBacktick' + 'PSMissingModuleManifestField' + 'PSPlaceCloseBrace' + 'PSPlaceOpenBrace' + 'PSPossibleIncorrectComparisonWithNull' + 'PSPossibleIncorrectUsageOfAssignmentOperator' + 'PSPossibleIncorrectUsageOfRedirectionOperator' + 'PSProvideCommentHelp' + 'PSReturnCorrectTypesForDSCFunctions' + 'PSUseApprovedVerbs' + # Not suitable for other OS + # 'PSUseBOMForUnicodeEncodedFile' + 'PSUseCmdletCorrectly' + 'PSUseCompatibleCmdlets' + 'PSUseConsistentIndentation' + 'PSUseConsistentWhitespace' + 'PSUseCorrectCasing' + 'PSUseDeclaredVarsMoreThanAssignments' + 'PSUseIdenticalMandatoryParametersDSC' + 'PSUseIdenticalParametersDSC' + 'PSUseLiteralInitializerForHashtable' + 'PSUseOutputTypeCorrectly' + 'PSUsePSCredentialType' + 'PSUseShouldProcessCorrectly' + 'PSUseShouldProcessForStateChangingFunctions' + 'PSUseSingularNouns' + 'PSUseStandardDSCFunctionsInResource' + 'PSUseSupportsShouldProcess' + 'PSUseToExportFieldsInManifest' + 'PSUseUTF8EncodingForHelpFile' + 'PSUseVerboseMessageInDSCResource' + # Compatibility subdirectory + 'PSUseCompatibleCommands' + 'PSUseCompatibleSyntax' + 'PSUseCompatibleTypes' + ) + + # The Rules here are mostly manually imported from + # https://github.com/PowerShell/PSScriptAnalyzer/blob/master/Engine/Settings/CodeFormatting.psd1 + # except `PSUseConsistentWhitespace.CheckOperator` is disabled as incompatible + # https://github.com/PowerShell/PSScriptAnalyzer/issues/769 + + Rules = @{ + PSPlaceOpenBrace = @{ + Enable = $true + OnSameLine = $true + NewLineAfter = $true + IgnoreOneLineBlock = $true + } + + PSPlaceCloseBrace = @{ + Enable = $true + NewLineAfter = $true + IgnoreOneLineBlock = $true + NoEmptyLineBefore = $false + } + + PSUseConsistentIndentation = @{ + Enable = $true + Kind = 'space' + PipelineIndentation = 'IncreaseIndentationForFirstPipeline' + IndentationSize = 4 + } + + PSUseConsistentWhitespace = @{ + Enable = $true + CheckInnerBrace = $true + CheckOpenBrace = $true + CheckOpenParen = $true + CheckOperator = $true + CheckPipe = $true + CheckSeparator = $true + } + + PSAlignAssignmentStatement = @{ + Enable = $true + CheckHashtable = $false + } + + PSUseCorrectCasing = @{ + Enable = $true + } + } +} diff --git a/.ci/PrepareAVVM.ps1 b/.ci/PrepareAVVM.ps1 new file mode 100644 index 0000000000..65cfd5fc4a --- /dev/null +++ b/.ci/PrepareAVVM.ps1 @@ -0,0 +1,308 @@ +Set-StrictMode -Version latest + +$PACKAGES_ROOT = "$env:SYSTEMDRIVE\avvm" +$REGISTRY_ROOT = 'HKLM:\Software\AppVeyor\VersionManager' + +# GetInstalledProductVersion and Get-Version are copied from AVVM.ps1 +function GetInstalledProductVersion ($product) { + $productRegPath = "$REGISTRY_ROOT\$product" + if (Test-Path $productRegPath) { + $ver = Get-ItemProperty -Path $productRegPath + @{ + Product = $product + version = $ver.version + Platform = $ver.Platform + } + } +} + +function Get-Version ([string]$str) { + $versionDigits = $str.Split('.') + $version = @{ + major = -1 + minor = -1 + build = -1 + revision = -1 + number = 0 + value = $null + } + + $version.value = $str + + if ($versionDigits -and $versionDigits.Length -gt 0) { + $version.major = [int]$versionDigits[0] + } + if ($versionDigits.Length -gt 1) { + $version.minor = [int]$versionDigits[1] + } + if ($versionDigits.Length -gt 2) { + $version.build = [int]$versionDigits[2] + } + if ($versionDigits.Length -gt 3) { + $version.revision = [int]$versionDigits[3] + } + + for ($i = 0; $i -lt $versionDigits.Length; $i++) { + $version.number += [long]$versionDigits[$i] -shl 16 * (3 - $i) + } + + return $version +} + +function SetInstalledProductVersion { + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $product, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $version, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $platform + ) + + $productRegPath = "$REGISTRY_ROOT\$product" + New-Item $productRegPath -Force | + Out-Null + + New-ItemProperty -Path $productRegPath -Name Version -PropertyType String -Value $version -Force | + Out-Null + + New-ItemProperty -Path $productRegPath -Name Platform -PropertyType String -Value $platform -Force | + Out-Null + + Write-Output "Creating $PACKAGES_ROOT\$product\$version\$platform" + + if (!(Test-Path "$PACKAGES_ROOT\$product\$version\$platform")) { + New-Item -ItemType Directory "$PACKAGES_ROOT\$product\$version\$platform" -Force > $null + } + + if (!(Test-Path "$PACKAGES_ROOT\$product\$version\$platform")) { + throw "Something went wrong" + } +} + +function Add-Product { + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $product, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $version, + + [string] + $platform + ) + + $name = $product + + if (!$platform) { + $platform = $env:platform + } + + $installed = GetInstalledProductVersion $Product + + $in_program_files = $false + + $version_parts = Get-Version $version + + if (($product -eq 'jdk') -or ($product -eq 'node')) { + $in_program_files = $true + + if (($product -eq 'jdk') -and ($version_parts.minor -gt 8)) { + # 1.9.0 -> 9 + $shortver = $version_parts.minor + } + else { + $shortver = $version + } + + if ($platform -eq 'x86') { + $dir_name = "Program Files (x86)" + } + else { + $dir_name = "Program Files" + } + if ($product -eq 'jdk') { + $dir_name = "$dir_name\Java" + } + $dir_name = "$dir_name\$name" + } + else { + $shortver = "{0}{1}" -f ($version_parts.major, $version_parts.minor) + $dir_name = $name + } + + + if (Test-Path "$PACKAGES_ROOT\$name\$version\") { + Write-Output "$PACKAGES_ROOT\$name\$version exists; skipping" + + if ($product -eq 'node') { + $base = 'https://appveyordownloads.blob.core.windows.net/avvm' + $versions_content = (New-Object Net.WebClient).DownloadString("$base/$name-versions.txt") + Set-Content "$PACKAGES_ROOT\$name-versions.txt" $versions_content + return + } + } + + if ($installed) { + $current_version = $installed.version + if ((Get-Version $current_version).number -gt $version_parts.number) { + $versions_content = "$current_version +$version +lts:$version +stable:$version +current:$current_version +" + } + else { + $versions_content = "$version +$current_version +lts:$version +stable:$version +current:$current_version +" + } + } + else { + $versions_content = "$version +lts:$version +stable:$version +" + } + Set-Content "$PACKAGES_ROOT\$name-versions.txt" $versions_content + + Write-Output "Wrote $PACKAGES_ROOT\$name-versions.txt" + + if ($product -eq 'MinGW') { + if (Test-Path C:\avvm\MinGW) { + # This is only needed for 5.3.0 x86, only vs2015 image + Write-Output "Deleting pre-existing C:\avvm\MinGW ..." + Remove-Item C:\avvm\MinGW + } + } + + if ($installed) { + if ($installed.version -eq $version) { + if ($installed.Platform -eq $platform) { + Write-Output "$product $version $env:platform already set up" + return; + } + } + } + + New-Item -ItemType Directory "$PACKAGES_ROOT\$name" -Force > $null + + New-Item -ItemType Directory "$PACKAGES_ROOT\$name\$version" -Force > $null + + Write-Verbose "Looking for C:\$dir_name$shortver .." + + if (!(Test-Path "C:\$dir_name$shortver")) { + if (!(Test-Path "C:\$dir_name$shortver-x64")) { + throw "Cant find $dir_name$shortver or C:\$dir_name$shortver-x64" + } + + # Use x64 if x86 not available + $platform = 'x64' + } + + New-Item -ItemType Directory "$PACKAGES_ROOT\$name\$version\$platform" -Force > $null + + if ($in_program_files) { + $dir = "C:\$dir_name$shortver" + } + else { + Write-Output "Looking for C:\$name$shortver-x64 .." + + $dir = '' + if (Test-Path "C:\$dir_name$shortver-x64") { + if ($platform -eq "x64") { + $dir = "C:\$dir_name$shortver-x64" + } + else { + $dir = "C:\$dir_name$shortver" + } + } + + # TODO: Re-arrange to look only for the needed platform + if (!$dir) { + Write-Output "Looking for C:\$dir_name$shortver-x86 .." + } + + if ((!($dir)) -and (Test-Path "C:\$dir_name$shortver-x86")) { + if ($platform -eq "x86") { + $dir = "C:\$dir_name$shortver-x86" + } + else { + $dir = "C:\$dir_name$shortver" + } + } + } + + if (!($dir)) { + throw 'couldnt find x86/x64 directories for $name' + } + + if (!(Test-Path $dir)) { + throw "Cant find $dir" + } + + Write-Output "Coping $dir to $PACKAGES_ROOT\$name\$version\$platform\$name ..." + + Move-Item $dir "$PACKAGES_ROOT\$name\$version\$platform\$name" + + $files_content = ('$files = @{ "' + $name + '" = "C:\' + $name + '" }') + $files_path = "$PACKAGES_ROOT\$name\$version\$platform\files.ps1" + + Set-Content $files_path $files_content + + Write-Output "Wrote $files_path" +} + +function Initialize-Miniconda27 { + # The algorithm above to prepare products for switching depends on a version + # in the directory name, which works for all cases except for Miniconda27. + # Use this if you need Miniconda27. + Write-Output "Moving C:\Miniconda(-x64) to C:\Miniconda27(-x64)" + Move-Item C:\Miniconda C:\Miniconda27 + Move-Item C:\Miniconda-x64 C:\Miniconda27-x64 +} + +function Initialize-AppVeyorProductVersion { + # TODO: Only set up default versions for products which are needed + + # This tells Install-Product to load product versions from $PACKAGES_ROOT + $env:AVVM_DOWNLOAD_URL = '../../avvm/' + Set-ItemProperty -Path 'HKCU:\Environment' -Name 'AVVM_DOWNLOAD_URL' -Value $env:AVVM_DOWNLOAD_URL + + # node already set to 8.x + SetInstalledProductVersion go 1.12.3 x64 + + # todo: Handle Python27 and Ruby193 + + SetInstalledProductVersion miniconda 2.7.15 x86 + SetInstalledProductVersion miniconda3 3.7.0 x86 + + SetInstalledProductVersion jdk 1.6.0 x86 + SetInstalledProductVersion perl 5.20.1 x86 + # /C/MinGW is only set on vs2013 and vs2015 images, and it isnt desirable + # SetInstalledProductVersion MinGW 5.3.0 x86 + + # hg 4.1.1 is pre-installed, but it does not need to be replaced, + # and is instead reported as 5.0 +} + +$old_EAP = $ErrorActionPreference +$ErrorActionPreference = 'SilentlyContinue'; +Export-ModuleMember -Function Initialize-AppVeyorProductVersion, Add-Product, Initialize-Miniconda27 +$ErrorActionPreference = $old_EAP; diff --git a/.ci/appveyor.yml b/.ci/appveyor.yml index ea56a828c7..61bdfe907a 100644 --- a/.ci/appveyor.yml +++ b/.ci/appveyor.yml @@ -1,100 +1,184 @@ +image: Visual Studio 2015 + environment: global: - # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the - # /E:ON and /V:ON options are not enabled in the batch script intepreter - # See: http://stackoverflow.com/a/13751649/163740 - CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\.ci\\run_with_env.cmd" PIP_CACHE_DIR: C:\pip_cache + PIP_DISABLE_PIP_VERSION_CHECK: 1 + # Needed if pip uninstall is used + PIP_YES: 1 + YARN_GPG: 'no' + FudgeCI: $(APPVEYOR_BUILD_FOLDER)\.ci + NUGET_EXE_NO_PROMPT: 1 + NUGET_HTTP_CACHE_PATH: C:\nuget_http_cache + CHOCO_CACHE_DIR: C:\choco_cache + MSYS_ROOT: C:\msys64 + MSYS_BIN: $(MSYS_ROOT)\usr\bin + JAVA_HOME: C:\jdk + MSSDK_ROOT: C:\Program Files\Microsoft SDKs + VS_ROOT: C:\Program Files (x86)\Microsoft Visual Studio + VIRTUALENV_NO_DOWNLOAD: 1 + VIRTUALENV_NO_PIP: 1 + VIRTUALENV_NO_SETUPTOOLS: 1 + # Uncomment to debug tox venv problems + # VIRTUALENV_VERBOSE: 1 + GOPATH: C:\gopath + # A problem with store_env_in_registry.py means this needs to be explicit + PSModulePath: >- + $(PSModulePath);C:\Program Files (x86)\WindowsPowerShell\Modules; + PATH: >- + C:\python;C:\python\Scripts;$(PATH);$(MSYS_BIN); + C:\tools\fudge\tools; + C:\ruby\bin; + C:\jdk\bin; + $(GOPATH)\bin; + $(APPVEYOR_BUILD_FOLDER)\node_modules\.bin; + $(APPVEYOR_BUILD_FOLDER)\vendor\bin; + JL_PKG: CoalaBears + JULIA_PROJECT: '@.' + TOX_FEATURES: check-noskip-codecov + BEAR_LIST: astyle cppcheck xmllint + TOX_TEST_SELECTORS: pip-noreqs-npm-gem-go-perl-php-java8-adhoc + TOXENV: py$(PYTHON_MINOR_NODOTS)-$(TOX_TEST_SELECTORS)-$(TOX_FEATURES)-win matrix: - - PYTHON: "C:\\Python34" - PYTHON_VERSION: "3.4.4" - PYTHON_ARCH: "32" - ruby_version: "24" + - PYTHON_VERSION: 3.6 + PYTHON_MINOR_NODOTS: 36 + - PYTHON_VERSION: 3.5 + PYTHON_MINOR_NODOTS: 35 + - PYTHON_VERSION: 3.4 + PYTHON_MINOR_NODOTS: 34 - - PYTHON: "C:\\Python34-x64" - PYTHON_VERSION: "3.4.4" - PYTHON_ARCH: "64" - ruby_version: "24-x64" +platform: + - x86 + - x64 cache: + - C:\nuget_http_cache + - C:\choco_cache - "C:\\pip_cache" - "node_modules" - "C:\\Users\\appveyor\\AppData\\Local\\coala-bears\\coala-bears" - "C:\\Users\\appveyor\\AppData\\Roaming\\nltk_data" + - "%LOCALAPPDATA%\\Composer" branches: except: - /^sils\/.*/ +# This forces unix style line endings in the clone, which is necessary to +# avoid warning regarding EOLs when running git diff on Windows +init: git config --global core.autocrlf false + install: - # Prepend newly installed Python to the PATH of this build (this cannot be - # done from inside the powershell script as it would require to restart - # the parent CMD process). - - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - - "SET PATH=C:\\Program\ Files\\Java\\jdk1.7.0\\bin;%PATH%" + # Show initial state + - powershell -c "$PSVersionTable" + # Uncomment to debug + # printenv + - python --version + - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" + - python -m pip --version + - python -c "import setuptools; print(setuptools.__version__)" + - node --version + - which npm + - npm --version + - npm config get prefix + - ruby --version + - bundler --version + - go version + - which java + - java -version + - which javac + - javac -version + # JAVA_HOME set above is not populated yet + - echo %JAVA_HOME% + - ls %JAVA_HOME%/bin/java & exit 0 + - which perl + - perl --version + - which gcc & exit 0 + - gcc --version & exit 0 + - which cl & exit 0 + + # Stores environment in registry, with minor tweaks + - python .ci/store_env_in_registry.py + - refreshenv + + # Set up AppVeyor product versions, and install dummy choco entries for them + - ps: . .ci/FudgeCI.ps1; Initialize-AppVeyorVM + - refreshenv + - echo %PATH% + # Avoid tools finding and using MinGW + - mv C:\MinGW %TEMP% + # TODO: Avoid tools finding and using Visual Studio + + # Show updated SOE; versions should be as defined in top of the Fudgefile + - python --version + - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" + - node --version + - which npm + - npm --version + - npm config get prefix + - go version + - ruby --version + - which java + - java -version + - which javac + - javac -version + - ls %JAVA_HOME%/bin/java + - perl --version + - which ppm + - ppm --version + - which gcc & exit 0 + - gcc --version & exit 0 + + - "%MSYS_BIN%\\date.exe" + - cp .ci/choco.config + %ChocolateyInstall%\config\chocolatey.config + # Install remainer of the Fudgefile with chocolatey using Fudge + - ps: . .ci/Fudge.ps1 install + - refreshenv + - echo %PATH% - # language-tool needs the registry tweaked here since it determines the java - # version wrong (since appveyor has both, 1.7 and 1.8 in x86 and x64). - - "SET KEY_NAME=HKLM\\Software\\JavaSoft\\Java Runtime Environment" - - "REG add \"%KEY_NAME%\" /v CurrentVersion /t REG_SZ /d 1.7 /f" + - ps: if ($env:APPVEYOR_JOB_NUMBER -eq 1) { . .ci/PSLint.ps1 } # Check that we have the expected version and architecture for Python - "python --version" - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" - - "%CMD_IN_ENV% python -m pip install --upgrade setuptools==21 pip==9" - - "%CMD_IN_ENV% python -m pip install -r test-requirements.txt \ - -r requirements.txt -r docs-requirements.txt" - - ps: "Install-Product node ''" # Use latest node v5.x.x - - "npm config set loglevel warn" - - "sed -i '/alex/d' package.json" - - "sed -i '/coffeelint/d' package.json" - - "sed -i '/csscomb/d' package.json" - - "sed -i '/docker/d' package.json" - - "sed -i '/elm/d' package.json" - - "sed -i '/gherkin/d' package.json" - - "sed -i '/jshint/d' package.json" - - "sed -i '/remark/d' package.json" - - "sed -i '/postcss/d' package.json" - - "sed -i '/sass/d' package.json" - - "sed -i '/textlint/d' package.json" - - "sed -i '/tslint/d' package.json" - - "npm install" - - "SET PATH=node_modules\\.bin;%PATH%" - - "DEL node_modules\\.bin\\eslint*" - - # Commands for Ruby - - "sed -i '/sqlint/d' Gemfile" - - "SET PATH=C:\\Ruby%ruby_version%\\bin;%PATH%" - - "bundle install" + # Confirm other versions + - node --version + - which npm + - npm --version + - npm config get prefix + - go version + - ruby --version + - perl --version + - which java + - java -version + - which javac + - javac -version + - ls %JAVA_HOME%/bin/java + # Newly installed versions + - which composer & exit 0 + - composer --version & exit 0 + - ppm --version & exit 0 + + + - "%MSYS_BIN%\\date.exe" build: false # Not a C# project, build stuff at the test step instead. test_script: - # Force DOS format, as Checkstyle configs enable NewlineAtEndOfFile, - # which defaults to CRLF on Windows, and Appveyor CI ignores .gitattributes - # http://help.appveyor.com/discussions/problems/5687-gitattributes-changes-dont-have-any-effect - - unix2dos tests/java/test_files/CheckstyleGood.java - # Clang DLLs x64 were nowadays installed, but the x64 version hangs, so we - # exclude according tests. See https://github.com/appveyor/ci/issues/495 and - # https://github.com/appveyor/ci/issues/688 - - > - "%CMD_IN_ENV% python -m pytest - --cov -k "not ClangASTPrintBear and not ClangCloneDetectionBear and - not ClangComplexityBear and not ClangCountVectorCreator and - not ClangCountingConditions and not RuboCopBearTest and - not CSVLintBearTest" - - "%CMD_IN_ENV% python setup.py install" - - "%CMD_IN_ENV% python -m pip install \ - git+https://github.com/coala/coala" - - sed -i '/ShellCheckBear/d' .coafile - - "%CMD_IN_ENV% coala --ci" - -on_success: - - codecov - -on_failure: - - codecov + - python -m pip --version + - python -c "import setuptools; print(setuptools.__version__)" + + - "sed -i 's/^envlist.*$/envlist: %TOXENV%/' tox.ini" + - python -m tox --sitepackages + + - python setup.py install + + + - rm -rf .tox + + - coala --ci matrix: fast_finish: true diff --git a/.ci/choco.config b/.ci/choco.config new file mode 100644 index 0000000000..026715ec07 --- /dev/null +++ b/.ci/choco.config @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.ci/constants.ps1 b/.ci/constants.ps1 new file mode 100644 index 0000000000..f8979db466 --- /dev/null +++ b/.ci/constants.ps1 @@ -0,0 +1,9 @@ + +New-Variable -Scope global -Name project_name -Value 'coala-bears' +New-Variable -Scope global -Name pip_version -Value '9.0.1' +New-Variable -Scope global -Name setuptools_version -Value '21.2.2' + +$old_EAP = $ErrorActionPreference +$ErrorActionPreference = 'SilentlyContinue' +Export-ModuleMember -Variable name, pip_version, setuptools_version +$ErrorActionPreference = $old_EAP diff --git a/.ci/deps.ActivePerl-packages.ps1 b/.ci/deps.ActivePerl-packages.ps1 new file mode 100644 index 0000000000..dd3e21058c --- /dev/null +++ b/.ci/deps.ActivePerl-packages.ps1 @@ -0,0 +1,9 @@ +function Install-Perl-Modules { + cpanm --quiet --installdeps --with-develop --notest . + + Remove-Item -Force MYMETA.yml -ErrorAction Ignore +} + +function Invoke-ExtraInstallation { + Install-Perl-Modules +} diff --git a/.ci/deps.ActivePerl.ps1 b/.ci/deps.ActivePerl.ps1 new file mode 100644 index 0000000000..4464207b19 --- /dev/null +++ b/.ci/deps.ActivePerl.ps1 @@ -0,0 +1,11 @@ +Set-StrictMode -Version latest + +function Install-PPM-cpanm { + ppm install App-cpanminus +} + +function Complete-Install { + Install-PPM-cpanm +} + +Export-ModuleMember -Function Install-PPM-cpanm, Complete-Install diff --git a/.ci/deps.alex.sh b/.ci/deps.alex.sh new file mode 100644 index 0000000000..255f474592 --- /dev/null +++ b/.ci/deps.alex.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e -x + +ALEX=$(which alex || true) +# Delete 'alex' if it is not in a node_modules directory, +# which means it is ghc-alex. +if [[ -n "$ALEX" && "${ALEX/node_modules/}" == "${ALEX}" ]]; then + echo "Removing $ALEX" + sudo rm -rf $ALEX +fi diff --git a/.ci/deps.apt.sh b/.ci/deps.apt.sh deleted file mode 100755 index ac2a6920e2..0000000000 --- a/.ci/deps.apt.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -set -e -set -x - -# This script is no longer used by Travis CI. -# Any related aspects can be removed if beneficial. - -# apt-get commands -export DEBIAN_FRONTEND=noninteractive - -deps="libclang1-3.4 astyle indent mono-mcs chktex r-base julia golang-go luarocks verilator cppcheck flawfinder devscripts mercurial" -deps_infer="m4 opam" - -case $CIRCLE_BUILD_IMAGE in - "ubuntu-12.04") - USE_PPAS="true" - # The Circle provided Go is too old - sudo mv /usr/local/go /usr/local/circleci-go - ;; - "ubuntu-14.04") - # Use xenial, needed to replace outdated julia provided by Circle CI - ADD_APT_UBUNTU_RELEASE=xenial - # Work around lack of systemd on trusty, which xenial's lxc-common expects - echo '#!/bin/sh' | sudo tee /usr/bin/systemd-detect-virt > /dev/null - sudo chmod a+x /usr/bin/systemd-detect-virt - - # The non-apt go provided by Circle CI is acceptable - deps=${deps/golang-go/} - # Add packages which are already in the precise image - deps="$deps g++-4.9 libxml2-utils php-cli php7.0-cli php-codesniffer" - # gfortran on CircleCI precise is 4.6 and R irlba compiles ok, - # but for reasons unknown it fails on trusty without gfortran-4.9 - deps="$deps gfortran-4.9" - # Add extra infer deps - deps_infer="$deps_infer ocaml camlp4" - # opam install --deps-only --yes infer fails with - # Fatal error: - # Stack overflow - # aspcud is an external dependency resolver, and is the recommended - # solution: https://github.com/ocaml/opam/issues/2507 - deps_infer="$deps_infer aspcud" - ;; -esac - -if [ -n "$ADD_APT_UBUNTU_RELEASE" ]; then - echo "deb http://archive.ubuntu.com/ubuntu/ $ADD_APT_UBUNTU_RELEASE main universe" | sudo tee -a /etc/apt/sources.list.d/$ADD_APT_UBUNTU_RELEASE.list > /dev/null -fi - -if [ "$USE_PPAS" = "true" ]; then - sudo add-apt-repository -y ppa:cs50/ppa - sudo add-apt-repository -y ppa:marutter/rdev - sudo add-apt-repository -y ppa:staticfloat/juliareleases - sudo add-apt-repository -y ppa:staticfloat/julia-deps - sudo add-apt-repository -y ppa:ondrej/golang - sudo add-apt-repository -y ppa:avsm/ppa -elif [ -n "$USE_PPAS" ]; then - for ppa in $USE_PPAS; do - sudo add-apt-repository -y ppa:$ppa - done -fi - -deps_perl="libperl-critic-perl" - -sudo apt-get -y update -sudo apt-get -y --no-install-recommends install $deps $deps_perl $deps_infer - -# On Trusty, g++ & gfortran 4.9 need activating for R lintr dependency irlba. -ls -al /usr/bin/gcc* /usr/bin/g++* /usr/bin/gfortran* || true -if [[ "$CIRCLE_BUILD_IMAGE" == "ubuntu-14.04" ]]; then - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 20 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 20 - sudo update-alternatives --install /usr/bin/gfortran gfortran /usr/bin/gfortran-4.9 20 -fi diff --git a/.ci/deps.apt_get.sh b/.ci/deps.apt_get.sh new file mode 100755 index 0000000000..faac6a05a8 --- /dev/null +++ b/.ci/deps.apt_get.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e -x + +if [ -f /usr/bin/flawfinder ]; then + sh .ci/deps.flawfinder.sh +fi diff --git a/.ci/deps.bakalint.sh b/.ci/deps.bakalint.sh new file mode 100755 index 0000000000..363e822eae --- /dev/null +++ b/.ci/deps.bakalint.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e -x + +# VHDL Bakalint Installation +if [ ! -e ~/.local/bin/bakalint.pl ]; then + BAKALINT_VERSION=0.4.0 + wget "http://downloads.sourceforge.net/project/fpgalibre/bakalint/0.4.0/bakalint-0.4.0.tar.gz?r=https%3A%2F%2Fsourceforge.net%2Fprojects%2Ffpgalibre%2Ffiles%2Fbakalint%2F0.4.0%2F&ts=1461844926&use_mirror=netcologne" -O ~/bl.tar.gz + tar xf ~/bl.tar.gz -C ~/ + mv ~/bakalint-$BAKALINT_VERSION/bakalint.pl ~/.local/bin/ +fi diff --git a/.ci/deps.cabal.sh b/.ci/deps.cabal.sh index b8980b7741..d103d91f2d 100755 --- a/.ci/deps.cabal.sh +++ b/.ci/deps.cabal.sh @@ -7,6 +7,8 @@ cabal --version cabal update +cabal install happy + cabal install --only-dependencies --avoid-reinstalls # Force ghc-mod to resolve its Cabal version diff --git a/.ci/deps.composer-packages.ps1 b/.ci/deps.composer-packages.ps1 new file mode 100644 index 0000000000..9a99c0d5b2 --- /dev/null +++ b/.ci/deps.composer-packages.ps1 @@ -0,0 +1,9 @@ +function Install-Composer-DependList { + $composer_phar = "C:\ProgramData\ComposerSetup\bin\composer.phar" + + php.exe $composer_phar install +} + +function Invoke-ExtraInstallation { + Install-Composer-DependList +} diff --git a/.ci/deps.composer.sh b/.ci/deps.composer.sh new file mode 100755 index 0000000000..61deea413a --- /dev/null +++ b/.ci/deps.composer.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +set -e -x + +composer install diff --git a/.ci/deps.elm.sh b/.ci/deps.elm.sh new file mode 100755 index 0000000000..e60bf614d0 --- /dev/null +++ b/.ci/deps.elm.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e -x + +# elm-format Installation +if [ ! -e ~/.local/bin/elm-format ]; then + curl -fsSL -o elm-format.tgz https://github.com/avh4/elm-format/releases/download/0.5.2-alpha/elm-format-0.17-0.5.2-alpha-linux-x64.tgz + tar -xvzf elm-format.tgz -C ~/.local/bin/ +fi + +if [ "$TRAVIS_ELM_VERSION" = "0.18.0" ]; then + touch elm-package.json +else + touch elm.json +fi diff --git a/.ci/deps.flawfinder.sh b/.ci/deps.flawfinder.sh new file mode 100755 index 0000000000..32df5db717 --- /dev/null +++ b/.ci/deps.flawfinder.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +set -e -x + +# Change environment for flawfinder from python to python2 +if [ ! -e ~/.local/bin/flawfinder ]; then + cp /usr/bin/flawfinder ~/.local/bin/flawfinder + sed -i '1s/.*/#!\/usr\/bin\/env python2/' ~/.local/bin/flawfinder + chmod +x ~/.local/bin/flawfinder +fi + +.ci/deps.python27.sh + +head ~/.local/bin/flawfinder + +~/.local/bin/flawfinder || true diff --git a/.ci/deps.generic.sh b/.ci/deps.generic.sh new file mode 100755 index 0000000000..e0cbeb80bd --- /dev/null +++ b/.ci/deps.generic.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +set -e -x + +# TODO implement DISABLE_BEARS here +if [ -n "$BEARS" ]; then + for item in $BEARS $BEAR_LIST; do + if [ -f ".ci/deps.$item.sh" ]; then + bash -e -x ".ci/deps.$item.sh" + fi + done +fi diff --git a/.ci/deps.ghc-mod.sh b/.ci/deps.ghc-mod.sh new file mode 100755 index 0000000000..42ef9fa42d --- /dev/null +++ b/.ci/deps.ghc-mod.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +set -e -x +# This file causes ghc-mod to try to install them all +# which fails https://github.com/coala/coala-bears/issues/1384 +rm coala-bears.cabal diff --git a/.ci/deps.ghc-packages.ps1 b/.ci/deps.ghc-packages.ps1 new file mode 100644 index 0000000000..f590fcdf1e --- /dev/null +++ b/.ci/deps.ghc-packages.ps1 @@ -0,0 +1,7 @@ +function Install-Cabal-Deps { + cabal install --only-dependencies --avoid-reinstalls +} + +function Invoke-ExtraInstallation { + Install-Cabal-Deps +} diff --git a/.ci/deps.go.sh b/.ci/deps.go.sh index f42575be59..7d0b582e1e 100755 --- a/.ci/deps.go.sh +++ b/.ci/deps.go.sh @@ -7,3 +7,5 @@ go get -u github.com/alecthomas/gometalinter gometalinter --install go get -u github.com/BurntSushi/toml/cmd/tomlv + +go get -u sourcegraph.com/sqs/goreturns diff --git a/.ci/deps.golang-packages.ps1 b/.ci/deps.golang-packages.ps1 new file mode 100644 index 0000000000..26d102032d --- /dev/null +++ b/.ci/deps.golang-packages.ps1 @@ -0,0 +1,10 @@ +function Install-GoMetalinter-Linters { + gometalinter.v2.exe '--install' +} + +function Invoke-ExtraInstallation { + Install-GoMetalinter-Linters + + go get -u github.com/BurntSushi/toml/cmd/tomlv + go get -u sourcegraph.com/sqs/goreturns +} diff --git a/.ci/deps.golang.ps1 b/.ci/deps.golang.ps1 new file mode 100644 index 0000000000..5adf27ab5b --- /dev/null +++ b/.ci/deps.golang.ps1 @@ -0,0 +1,18 @@ +Set-StrictMode -Version latest + +function Install-GoPM { + go.exe get -u github.com/gpmgo/gopm + go.exe install github.com/gpmgo/gopm +} + +function Install-GoMetaLinter { + go.exe get -u gopkg.in/alecthomas/gometalinter.v2 +} + +function Complete-Install { + Install-GoMetaLinter + + Install-GoPM +} + +Export-ModuleMember -Function Install-GoPM, Install-GoMetaLinter, Complete-Install diff --git a/.ci/deps.infer.sh b/.ci/deps.infer.sh new file mode 100755 index 0000000000..ff2f3d2102 --- /dev/null +++ b/.ci/deps.infer.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +set -e -x + +INFER_URL="https://github.com/facebook/infer/releases/download/v$INFER_VERSION/infer-linux64-v$INFER_VERSION.tar.xz" +curl -sSL "$INFER_URL" | tar -C ~/ -xJ diff --git a/.ci/deps.java.sh b/.ci/deps.java.sh index 236b136d76..7e8d8799d0 100755 --- a/.ci/deps.java.sh +++ b/.ci/deps.java.sh @@ -1,22 +1,17 @@ -#!/bin/sh +#!/bin/bash set -e set -x -# PMD commands -PMD_VERSION=5.4.1 -if [ ! -e ~/.local/bin/pmd ]; then - wget -nc -O ~/pmd.zip https://github.com/pmd/pmd/releases/download/pmd_releases%2F5.4.1/pmd-bin-5.4.1.zip - unzip ~/pmd.zip -d ~/ - cp -r ~/pmd-bin-$PMD_VERSION/* ~/.local/ +if [ -n "$TRAVIS_JDK_VERSION" ]; then + jdk_version=${TRAVIS_JDK_VERSION#openjdk} + jdk_version=${jdk_version#oraclejdk} fi -# Tailor (Swift) commands -# Comment out the hardcoded PREFIX, so we can put it into ~/.local -if [ ! -e ~/.local/tailor/tailor-latest ]; then - curl -fsSL -o install.sh https://tailor.sh/install.sh - sed -i 's/read -r CONTINUE < \/dev\/tty/CONTINUE=y/;;s/^PREFIX.*/# PREFIX=""/;' install.sh - PREFIX=$HOME/.local bash ./install.sh - # Provide a constant path for the executable - ln -s ~/.local/tailor/tailor-* ~/.local/tailor/tailor-latest +if [ -z "$jdk_version" ] || [ $jdk_version -eq 8 ]; then + .ci/deps.tailor.sh +fi + +if [ -z "$(which pmd || true)" ]; then + .ci/deps.pmd.sh fi diff --git a/.ci/deps.julia.jl b/.ci/deps.julia.jl new file mode 100644 index 0000000000..ec2e28fa51 --- /dev/null +++ b/.ci/deps.julia.jl @@ -0,0 +1,7 @@ +if VERSION < v"0.7.0-DEV.5183" + Pkg.clone(pwd(), ENV["JL_PKG"]) + Pkg.build(ENV["JL_PKG"]) +else + using Pkg + Pkg.build() +end diff --git a/.ci/deps.julia.sh b/.ci/deps.julia.sh new file mode 100755 index 0000000000..3608be2e59 --- /dev/null +++ b/.ci/deps.julia.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e -x + +# Normal package management not possible due to +# https://github.com/tonyhffong/Lint.jl/issues/254 +julia -e 'using Pkg; Pkg.add(PackageSpec(url="https://github.com/tonyhffong/Lint.jl", rev="v0.6.0"))' diff --git a/.ci/deps.lua.sh b/.ci/deps.lua.sh new file mode 100755 index 0000000000..7f7ab70029 --- /dev/null +++ b/.ci/deps.lua.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +set -e -x + +luarocks install --local --deps-mode=none luacheck diff --git a/.ci/deps.minimal.sh b/.ci/deps.minimal.sh new file mode 120000 index 0000000000..e78c42d4d0 --- /dev/null +++ b/.ci/deps.minimal.sh @@ -0,0 +1 @@ +deps.generic.sh \ No newline at end of file diff --git a/.ci/deps.node_js.sh b/.ci/deps.node_js.sh new file mode 100755 index 0000000000..81fe68945e --- /dev/null +++ b/.ci/deps.node_js.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e -x + +source .ci/deps.alex.sh + +source .ci/deps.elm.sh diff --git a/.ci/deps.nodejs-packages.ps1 b/.ci/deps.nodejs-packages.ps1 new file mode 100644 index 0000000000..5db9b8d665 --- /dev/null +++ b/.ci/deps.nodejs-packages.ps1 @@ -0,0 +1,33 @@ +function Install-Node-Packages { + npm config set loglevel warn + + # pnpm setting + npm config set reporter silent + + cp -force package.json package.json.bak + + # elm-platform should be added to Fudgefile + # https://github.com/coala/coala-bears/issues/2924 + sed -i '/elm/d' package.json + + # textlint-plugin-asciidoc-loose requires a compiler + # and should be replaced with textlint-plugin-asciidoctor + sed -i '/textlint-plugin-asciidoc-loose/d' package.json + + # If gyp fails, use npm config python to help locate Python 2.7 + + $old_EAP = $ErrorActionPreference + $ErrorActionPreference = 'Continue' + + npm install + + $ErrorActionPreference = $old_EAP + + mv -force package.json.bak package.json + + npm config set reporter default +} + +function Invoke-ExtraInstallation { + Install-Node-Packages +} diff --git a/.ci/deps.objective_c.sh b/.ci/deps.objective_c.sh new file mode 120000 index 0000000000..41a87f2ce0 --- /dev/null +++ b/.ci/deps.objective_c.sh @@ -0,0 +1 @@ +deps.tailor.sh \ No newline at end of file diff --git a/.ci/deps.opam.sh b/.ci/deps.opam.sh index a3f1daa3c6..c4a7a076e5 100755 --- a/.ci/deps.opam.sh +++ b/.ci/deps.opam.sh @@ -3,6 +3,8 @@ set -e set -x +.ci/deps.python27.sh + # Infer commands if [ ! -e ~/infer-linux64-v0.7.0/infer/bin ]; then wget -nc -O ~/infer.tar.xz https://github.com/facebook/infer/releases/download/v0.7.0/infer-linux64-v0.7.0.tar.xz diff --git a/.ci/deps.perl.sh b/.ci/deps.perl.sh new file mode 120000 index 0000000000..5fdd9fa4b3 --- /dev/null +++ b/.ci/deps.perl.sh @@ -0,0 +1 @@ +deps.bakalint.sh \ No newline at end of file diff --git a/.ci/deps.php.ps1 b/.ci/deps.php.ps1 new file mode 100644 index 0000000000..1991fc9314 --- /dev/null +++ b/.ci/deps.php.ps1 @@ -0,0 +1,123 @@ +Set-StrictMode -Version latest + +function Get-PHP-Root { + $PHP_ROOT = '' + Get-ChildItem -Directory 'C:\tools\' -Filter 'php*' | + ForEach-Object { + $PHP_ROOT = $_.FullName.ToString() + + Write-Host "Setting PHP_ROOT='$PHP_ROOT'" + + $env:PHP_ROOT = $PHP_ROOT + Set-ItemProperty -Path 'HKCU:\Environment' -Name 'PHP_ROOT' -Value $PHP_ROOT + } + + if ($PHP_ROOT) { + return $PHP_ROOT + } + + throw ('php not found in ' + $list) +} + +# This is not needed with the recent choco php packages +function Initialize-PHP-Ini { + param ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $PHP_ROOT + ) + + $PHP_INI = ($PHP_ROOT + '\php.ini') + + Write-Host 'Creating '$PHP_INI + + cp ($PHP_INI + '-production') $PHP_INI + sed -i 's/;date.timezone =.*/date.timezone=UTC/' $PHP_INI + + $list = (Get-ChildItem -Recurse $PHP_ROOT | + Out-String) + + Write-Verbose ('php dir ' + $list) + + Write-Host 'Enabling PHP openssl ...' + + $openssl_dll = '' + + Get-ChildItem $PHP_ROOT -Recurse -filter '*openssl*.dll' | + ForEach-Object { + $openssl_dll = $_.FullName + + Write-Host ' found '$openssl_dll + } + + if (! $openssl_dll) { + Write-Host ' not found' + + throw ('openssl not found in ' + $list) + } + + sed -i 's/;extension=openssl/extension=openssl/' $PHP_INI + + $dir = Split-Path -Path $openssl_dll + + Write-Host 'Setting extension directory: '$dir + + (Get-Content $PHP_INI) | + ForEach-Object { + $_ -replace ';extension_dir *=.*', ('extension_dir="' + $dir + '"') + } | + Set-Content $PHP_INI + + grep '^extension' $PHP_INI +} + +function Install-PEAR { + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $PHP_ROOT + ) + + $PHP_INI = "$PHP_ROOT\php.ini" + + grep '^extension' $PHP_INI + + $PHP = "$PHP_ROOT\php.exe" + + Write-Output 'Installing PEAR' + + $pear_install_url = 'http://pear.php.net/install-pear-nozlib.phar' + $phar = $env:TMP + '\install-pear.phar' + + curl -o $phar $pear_install_url + + php.exe $phar -b $PHP_ROOT -d $PHP_ROOT -p $PHP +} + +function Sync-PEAR { + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $PHP_ROOT + ) + + Write-Output 'Updating PEAR channel pear.php.net' + + php.exe $PHP_ROOT\pearcmd.php channel-update pear.php.net +} + +function Complete-Install { + $PHP_ROOT = Get-PHP-Root + + Write-Host "PHP_ROOT = $PHP_ROOT" + + $env:PATH = ($env:PATH + ';' + $PHP_ROOT) + + Install-PEAR $PHP_ROOT + Sync-PEAR $PHP_ROOT +} + +Export-ModuleMember -Function Get-PHP-Root, Initialize-PHP-Ini, Install-PEAR, Sync-PEAR diff --git a/.ci/deps.php.sh b/.ci/deps.php.sh new file mode 120000 index 0000000000..1133214314 --- /dev/null +++ b/.ci/deps.php.sh @@ -0,0 +1 @@ +deps.composer.sh \ No newline at end of file diff --git a/.ci/deps.pmd.sh b/.ci/deps.pmd.sh new file mode 100755 index 0000000000..bca89db899 --- /dev/null +++ b/.ci/deps.pmd.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e -x + +# PMD commands +PMD_VERSION=5.4.1 +if [ ! -e ~/.local/bin/pmd ]; then + wget -nc -O ~/pmd.zip https://github.com/pmd/pmd/releases/download/pmd_releases%2F5.4.1/pmd-bin-5.4.1.zip + unzip ~/pmd.zip -d ~/ + cp -r ~/pmd-bin-$PMD_VERSION/* ~/.local/ +fi diff --git a/.ci/deps.pyenv.sh b/.ci/deps.pyenv.sh new file mode 100755 index 0000000000..acb981b18b --- /dev/null +++ b/.ci/deps.pyenv.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# This file should be used with `source` + +set -e -x + +if [ -z "$(which pyenv)" ]; then + export PYENV_ROOT="$HOME/.pyenv" + export PATH="$PYENV_ROOT/bin:$PATH" + if [ ! -f "$PYENV_ROOT/bin/pyenv" ]; then + if [ -d "$PYENV_ROOT" ]; then + rm -rf "$PYENV_ROOT" + fi + git clone https://github.com/pyenv/pyenv.git $PYENV_ROOT + if [ -d ~/local/bin ]; then + rm ~/local/bin/pyenv + (cd ~/local/bin && ln -s $PYENV_ROOT/bin/pyenv .) + fi + fi +else + export PYENV_ROOT="$(pyenv root)" +fi diff --git a/.ci/deps.python-packages.ps1 b/.ci/deps.python-packages.ps1 new file mode 100644 index 0000000000..7055fe1a52 --- /dev/null +++ b/.ci/deps.python-packages.ps1 @@ -0,0 +1,191 @@ +$ci_directory = $env:FudgeCI + +if (!($ci_directory)) { + $ci_directory = '.ci' +} + +. $ci_directory/constants.ps1 + +function Checkpoint-Pip-Constraints { + python -m pip freeze --all > constraints.txt +} + +function Install-Pip-Requirement { + param ( + [parameter(Mandatory, ValueFromPipeline)] + [string] + $requirement + ) + + if ($requirement.EndsWith('.txt')) { + python -m pip install --constraint constraints.txt --constraint test-requirements.txt -r $requirement + } + else { + python -m pip install --constraint constraints.txt --constraint test-requirements.txt $requirement.Split() + } +} + +function Install-Binary-Packages { + # Install lxml needed by for coala-bears as a wheel as libxml2 and libxslt + # headers and library files are not available, and STATIC_DEPS=true often + # results in linter problems due to different VS compilers and MS runtimes. + # Also use cffi wheel to avoid need for VS compilers + python -m pip install --prefer-binary cffi lxml + # pycparser not a wheel, but ensure it is installable before proceeding + # https://github.com/eliben/pycparser/issues/251 + python -m pip --verbose install pycparser +} + +function Install-coala { + param ( + [string] + $stop_at + ) + + python -m pip install -U six pip==$pip_version setuptools==$setuptools_version + + if (!(Test-Path constraints.txt)) { + if ($stop_at -eq 'coala-bears') { + cp bear-requirements.txt constraints.txt + } + elseif (Test-Path 'requirements.txt') { + cp requirements.txt constraints.txt + } + else { + Checkpoint-Pip-Constraints + } + + # pip bales on encountering VCS or other imprecise requirements + sed -Ei '/(git|hg)+/d' constraints.txt + } + + if (!($stop_at -eq 'PyPrint')) { + Write-Output "Installing PyPrint" + Install-Pip-Requirement 'git+https://gitlab.com/coala/PyPrint#egg=PyPrint' + + if (!($stop_at -eq 'coala_utils')) { + Write-Output "Installing coala_utils" + + Checkpoint-Pip-Constraints + + Install-Pip-Requirement 'git+https://gitlab.com/coala/coala-utils#egg=coala-utils' + + if (!($stop_at -eq 'dependency-management')) { + Write-Output "Installing sarge with Windows support" + + Install-Pip-Requirement 'hg+https://bitbucket.org/jayvdb/sarge@win-reg-lookup#egg=sarge' + + if (!(Test-Path $env:TEMP/pm-master)) { + $PM_URL = "https://gitlab.com/coala/package_manager.git/" + git clone $PM_URL $env:TEMP/pm-master + } + rm $env:TEMP/pm-master/test-requirements.txt + rm $env:TEMP/pm-master/requirements.txt + touch $env:TEMP/pm-master/test-requirements.txt + touch $env:TEMP/pm-master/requirements.txt + + Install-Pip-Requirement "$env:TEMP/pm-master" + + if (!($stop_at -eq 'coala')) { + Write-Output "Installing coala" + + Checkpoint-Pip-Constraints + + Install-Pip-Requirement 'git+https://github.com/coala/coala#egg=coala' + + if (!($stop_at -eq 'coala-bears')) { + Write-Output "Installing coala-bears" + + Checkpoint-Pip-Constraints + + Install-Pip-Requirement 'git+https://github.com/coala/coala-bears#egg=coala-bears' + } + } + } + } + } +} + +function Install-Project-Dependency-Packages { + Write-Output "Installing dependencies of $project_name" + Install-coala $project_name +} + +function Install-Project { + if (Test-Path 'requirements.txt') { + Write-Output "Installing $project_name requirements.txt" + + Install-Pip-Requirement 'requirements.txt' + } + + if (Test-Path 'setup.py') { + Write-Output "Installing $project_name setup.py" + Install-Pip-Requirement '.' + } + + # coala-bears has an ignore.txt for optional dependencies that ordinary + # users may be unable to install. They are needed to reach 100% coverage. + if (Test-Path 'ignore.txt') { + Install-Pip-Requirement 'ignore.txt' + } +} + +function Install-Test-Packages { + if (Test-Path docs-requirements.txt) { + Write-Output "Installing docs-requirements.txt" + + Checkpoint-Pip-Constraints + + Install-Pip-Requirement 'docs-requirements.txt' + } + + Checkpoint-Pip-Constraints + + Write-Output "Installing test-requirements.txt" + + Install-Pip-Requirement 'test-requirements.txt' + Install-Pip-Requirement 'pytest-spec' + + if ($project_name -eq 'coala-bears') { + Write-Output "Installing tox" + # Avoid previous cache entry for setuptools, as it + # causes a deserialisation error + python -m pip install -U --no-cache-dir setuptools + + Checkpoint-Pip-Constraints + + # tox 3.13 uses pluggy 0.12.0 which is incompatible with a pytest 3.6.4 + Install-Pip-Requirement 'tox~=3.12.0 tox-backticks' + } +} + +function Invoke-ExtraInstallation { + + $old_pip_check_flag = 0 + if ($env:PIP_DISABLE_PIP_VERSION_CHECK) { + $old_pip_check_flag = 1 + } + $env:PIP_DISABLE_PIP_VERSION_CHECK = 1 + + Install-Binary-Packages + + if (!($env:PYTHON_VERSION -eq '2.7')) { + Install-Project-Dependency-Packages + } + + Install-Project + + Install-Test-Packages + + if (Test-Path constraints.txt) { + Move-Item constraints.txt $env:TEMP -Force + } + + if (!$old_pip_check_flag) { + Remove-Item Env:\PIP_DISABLE_PIP_VERSION_CHECK + } +} + +$ErrorActionPreference = 'SilentlyContinue'; +Export-ModuleMember -Function Invoke-ExtraInstallation -ErrorAction:Ignore +$ErrorActionPreference = 'Continue'; diff --git a/.ci/deps.python.ps1 b/.ci/deps.python.ps1 new file mode 100644 index 0000000000..e8f63683dc --- /dev/null +++ b/.ci/deps.python.ps1 @@ -0,0 +1,44 @@ +Set-StrictMode -Version latest + +function Add-EnvPythonVersion { + if ($env:TRAVIS -and $env:TRAVIS_PYTHON_VERSION) { + $env:PYTHON_VERSION = $env:TRAVIS_PYTHON_VERSION + } + else { + $env:PYTHON_VERSION = python -c 'import sys; print(sys.version[0:3])' + } +} + +function Add-EnvPythonMinorDotless { + if (!($env:PYTHON_MINOR_NODOTS)) { + $python_minor = $env:PYTHON_VERSION.Substring(0, 3) + + $env:PYTHON_MINOR_NODOTS = $python_minor -replace '.', '' + } +} + +function Add-PATHPythonRoaming { + $roaming_home = ( + $env:APPDATA + '/Python/Python' + $env:PYTHON_MINOR_NODOTS) + + Install-ChocolateyPath -PathToInstall $roaming_home + Install-ChocolateyPath -PathToInstall ($roaming_home + '/Scripts') +} + +function Add-EnvPipNonEagerUpgradeStrategy { + $env:PIP_UPGRADE_STRATEGY = 'only-if-needed' + + Set-ItemProperty -Path 'HKCU:\Environment' -Name 'PIP_UPGRADE_STRATEGY' -Value $env:PIP_UPGRADE_STRATEGY +} + +function Complete-Install { + Add-EnvPythonVersion + + Add-EnvPythonMinorDotless + + Add-EnvPipNonEagerUpgradeStrategy + + Add-PATHPythonRoaming +} + +Export-ModuleMember -Function Complete-Install diff --git a/.ci/deps.python27.sh b/.ci/deps.python27.sh new file mode 100755 index 0000000000..d7a1e784e4 --- /dev/null +++ b/.ci/deps.python27.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e -x + +source .ci/deps.pyenv.sh + +PYTHON27_BIN="$(which python2.7 || true)" +if [ -n "$PYTHON27_BIN" ]; then + if [ ! -d "$PYENV_ROOT/plugins/pyenv-register" ]; then + # https://github.com/doloopwhile/pyenv-register/pull/3 + git clone https://github.com/garyp/pyenv-register.git \ + "$PYENV_ROOT/plugins/pyenv-register" + fi + + pyenv register -f "$PYTHON27_BIN" || true +fi + +PYTHON27_VERSION=$(pyenv versions --bare | fgrep '2.7' --max-count 1) +PYTHON36_VERSION=$(pyenv versions --bare | fgrep '3.6' --max-count 1 || true) + +pyenv global "$PYTHON27_VERSION" "$PYTHON36_VERSION" +hash -r diff --git a/.ci/deps.python36.sh b/.ci/deps.python36.sh new file mode 100755 index 0000000000..5e264bf5b9 --- /dev/null +++ b/.ci/deps.python36.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +set -e -x + +source .ci/deps.pyenv.sh + +PYTHON36_BIN="$(which python3.6 || true)" +if [ -n "$PYTHON36_BIN" ]; then + if [ ! -d "$PYENV_ROOT/plugins/pyenv-register" ]; then + # https://github.com/doloopwhile/pyenv-register/pull/3 + git clone https://github.com/garyp/pyenv-register.git \ + "$PYENV_ROOT/plugins/pyenv-register" + fi + + pyenv register -f "$PYTHON36_BIN" || true +fi + +PYTHON36_VERSION=$(pyenv versions --bare | fgrep '3.6' --max-count 1 || true) + +if [ -z "$PYTHON36_VERSION" ]; then + PYTHON36_VERSION=3.6.3 + + pyenv install "$PYTHON36_VERSION"; +fi + +pyenv global "$PYTHON36_VERSION" + +hash -r diff --git a/.ci/deps.r.sh b/.ci/deps.r.sh deleted file mode 100755 index 8e32ff8057..0000000000 --- a/.ci/deps.r.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -set -e -set -x - -# R commands -echo '.libPaths( c( "'"$R_LIB_USER"'", .libPaths()) )' >> .Rprofile -echo 'options(repos=structure(c(CRAN="http://cran.rstudio.com")))' >> .Rprofile -R -q -e 'install.packages("lintr")' -R -q -e 'install.packages("formatR")' diff --git a/.ci/deps.ruby-packages.ps1 b/.ci/deps.ruby-packages.ps1 new file mode 100644 index 0000000000..6c987cf0bf --- /dev/null +++ b/.ci/deps.ruby-packages.ps1 @@ -0,0 +1,30 @@ +function Install-Gemfile { + cp -force Gemfile Gemfile.orig + cp -force Gemfile Gemfile.win + + # Unbuildable on Windows + sed -i '/sqlint/d' Gemfile.win + # https://github.com/coala/coala-bears/issues/2909 + sed -i '/csvlint/d' Gemfile.win + + # pusher-client 0.4.0 doesnt depend on json, which requires + # a compiler and the GMP library + Write-Output 'gem "pusher-client", "~>0.4.0", require: false' | + Out-File -FilePath Gemfile.win -Append -Encoding ascii + + cp -force Gemfile.win Gemfile + + # The build crawls if DevKit is included in the PATH + $old_PATH = $env:PATH + $env:PATH = ($env:ChocolateyToolsLocation + '\DevKit2\bin;' + $env:PATH) + + bundle install + + $env:PATH = $old_PATH + + mv -force Gemfile.orig Gemfile +} + +function Invoke-ExtraInstallation { + Install-Gemfile +} diff --git a/.ci/deps.ruby.ps1 b/.ci/deps.ruby.ps1 new file mode 100644 index 0000000000..429f970475 --- /dev/null +++ b/.ci/deps.ruby.ps1 @@ -0,0 +1,11 @@ +Set-StrictMode -Version latest + +function Install-Bundler { + gem install bundler +} + +function Complete-Install { + Install-Bundler +} + +Export-ModuleMember -Function Complete-Install diff --git a/.ci/deps.ruby.sh b/.ci/deps.ruby.sh new file mode 100755 index 0000000000..8454830ed7 --- /dev/null +++ b/.ci/deps.ruby.sh @@ -0,0 +1,15 @@ +# Remove Ruby directive from Gemfile as we test many versions +sed -i.bak '/^ruby/d' Gemfile + +if [ "$TRAVIS_RUBY_VERSION" != "2.1" ]; then + gem update --system +fi + +# Install maximum version of bundler for each Ruby version +if [ "$TRAVIS_RUBY_VERSION" = "2.1" ]; then + gem install bundler -v 1.16.1 +elif [ "$TRAVIS_RUBY_VERSION" = "2.2" ]; then + gem install bundler -v 1.17.3 +else + gem install bundler +fi diff --git a/.ci/deps.sh b/.ci/deps.sh deleted file mode 100755 index 7722a69ea5..0000000000 --- a/.ci/deps.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash - -set -e -set -x - -# NPM commands -ALEX=$(which alex || true) -# Delete 'alex' if it is not in a node_modules directory, -# which means it is ghc-alex. -if [[ -n "$ALEX" && "${ALEX/node_modules/}" == "${ALEX}" ]]; then - echo "Removing $ALEX" - sudo rm -rf $ALEX -fi -npm install -npm list --depth=0 - -# Ruby commands -bundle install --path=vendor/bundle --binstubs=vendor/bin --jobs=8 --retry=3 - -# Dart Lint commands -if ! dartanalyzer -v &> /dev/null; then - wget -nc -O ~/dart-sdk.zip https://storage.googleapis.com/dart-archive/channels/stable/release/1.14.2/sdk/dartsdk-linux-x64-release.zip - unzip -n ~/dart-sdk.zip -d ~/ - cp -rp ~/dart-sdk/* ~/.local/ -fi - -# VHDL Bakalint Installation -if [ ! -e ~/.local/bin/bakalint.pl ]; then - BAKALINT_VERSION=0.4.0 - wget "http://downloads.sourceforge.net/project/fpgalibre/bakalint/0.4.0/bakalint-0.4.0.tar.gz?r=https%3A%2F%2Fsourceforge.net%2Fprojects%2Ffpgalibre%2Ffiles%2Fbakalint%2F0.4.0%2F&ts=1461844926&use_mirror=netcologne" -O ~/bl.tar.gz - tar xf ~/bl.tar.gz -C ~/ - mv ~/bakalint-$BAKALINT_VERSION/bakalint.pl ~/.local/bin/ -fi - -# elm-format Installation -if [ ! -e ~/.local/bin/elm-format ]; then - curl -fsSL -o elm-format.tgz https://github.com/avh4/elm-format/releases/download/0.5.2-alpha/elm-format-0.17-0.5.2-alpha-linux-x64.tgz - tar -xvzf elm-format.tgz -C ~/.local/bin/ -fi - -# Julia commands -julia -e "Pkg.add(\"Lint\")" - -# Lua commands -luarocks install --local --deps-mode=none luacheck - -# PHPMD installation -if [ ! -e ~/.local/bin/phpmd ]; then - PHPMD='http://static.phpmd.org/php/latest/phpmd.phar' - mkdir -p ~/.local/bin - curl -fsSL -o ~/.local/bin/phpmd "$PHPMD" - chmod +x ~/.local/bin/phpmd -fi - -# Change environment for flawfinder from python to python2 -if [ ! -e ~/.local/bin/flawfinder ]; then - cp /usr/bin/flawfinder ~/.local/bin/flawfinder - sed -i '1s/.*/#!\/usr\/bin\/env python2/' ~/.local/bin/flawfinder - chmod +x ~/.local/bin/flawfinder -fi diff --git a/.ci/deps.tailor.sh b/.ci/deps.tailor.sh new file mode 100755 index 0000000000..2c681ab887 --- /dev/null +++ b/.ci/deps.tailor.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e +set -x + +# Tailor (Swift) commands +# Comment out the hardcoded PREFIX, so we can put it into ~/.local +if [ ! -e ~/.local/tailor/tailor-latest ]; then + curl -fsSL -o install.sh https://tailor.sh/install.sh + sed -i 's/read -r CONTINUE < \/dev\/tty/CONTINUE=y/;;s/^PREFIX.*/# PREFIX=""/;' install.sh + PREFIX=$HOME/.local bash ./install.sh + # Provide a constant path for the executable + ln -s ~/.local/tailor/tailor-* ~/.local/tailor/tailor-latest +fi diff --git a/.ci/generate_coverage_thresholds.py b/.ci/generate_coverage_thresholds.py new file mode 100755 index 0000000000..0d5bb8d814 --- /dev/null +++ b/.ci/generate_coverage_thresholds.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +import json +import os +import sys + +IS_WIN = os.name == 'nt' + + +def main(): + args = sys.argv[1:] + thresholds = {} + if args == ['none']: + if os.path.exists(".threshold.json"): + os.remove(".threshold.json") + return + + for test in args: + bear = test.replace('tests/', 'bears/') + bear = bear.replace('Test.py', '.py').replace('*', '.*') + + threshold = 100 + if IS_WIN: + bear = bear.replace('/', '\\\\') + if 'CheckstyleBear' in bear or 'CMakeLintBear' in bear: + threshold = 90 + + thresholds[bear] = threshold + + with open('.threshold.json', 'w') as f: + json.dump(thresholds, f) + + +if __name__ == '__main__': + main() diff --git a/.ci/get_bears.py b/.ci/get_bears.py new file mode 100755 index 0000000000..7bdb67093e --- /dev/null +++ b/.ci/get_bears.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +import glob +import os +import os.path +import sys + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + +PROJECT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) + +PROJECT_BEAR_DIR = os.path.abspath(os.path.join(PROJECT_DIR, 'bears')) + + +def main(): + args = sys.argv[1:] + do_missing = do_all = False + if args[0] == '--all': + do_all = True + args = args[1:] + elif args[0] == '--missing': + do_missing = True + args = args[1:] + + if do_all or do_missing: + all_bears = glob.glob('{}/**/*.py'.format(PROJECT_BEAR_DIR)) + all_bears = [ + bear[len(PROJECT_DIR) + 1:].replace(os.path.sep, '/') + for bear in all_bears + if not bear.endswith('__init__.py') + ] + if do_all: + print(' '.join(sorted(all_bears))) + return + + all_bears = set(all_bears) + + bears = set() + + for arg in args: + if arg.startswith('tests/'): + bear = arg.replace('tests/', 'bears/') + bear = bear[:bear.find('Test')] + '.py' + else: + bear = arg + bears.add(bear) + + if do_missing: + bears = all_bears - bears + + print(' '.join(sorted(bears))) + + +if __name__ == '__main__': + main() diff --git a/.ci/get_codecov_tags.py b/.ci/get_codecov_tags.py new file mode 100755 index 0000000000..d3b5edc768 --- /dev/null +++ b/.ci/get_codecov_tags.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import sys + +# Tags like list, check and collectonly shouldnt appear on codecov +# but they also shouldnt be submitted to codecov, so they are not +# removed here as that would hide a bug in tox.ini +REJECT_TAGS = set(['codecov', 'skip', 'noskip']) + + +def main(): + env_factors = set(sys.argv[1].split('-')) + + print(','.join(sorted(env_factors - REJECT_TAGS))) + + +if __name__ == '__main__': + main() diff --git a/.ci/get_cov_args.py b/.ci/get_cov_args.py new file mode 100755 index 0000000000..2331a3f7d7 --- /dev/null +++ b/.ci/get_cov_args.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +import sys + + +def main(): + args = sys.argv[1:] + + bears = set() + + for test in args: + bear = test.replace('tests/', 'bears/') + bear = bear[:bear.find('Test')] + '.py' + bears.add(bear) + + print('--cov=' + ' --cov='.join(sorted(bears))) + + +if __name__ == '__main__': + main() diff --git a/.ci/get_tests.py b/.ci/get_tests.py new file mode 100755 index 0000000000..3ea95d1f21 --- /dev/null +++ b/.ci/get_tests.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 + +import glob +import os +import os.path +import sys + +from ruamel.yaml import YAML + +yaml = YAML() +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + +PROJECT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) + +IS_WIN = os.name == 'nt' + +# Clang DLLs x64 were nowadays installed, but the x64 version hangs, so we +# exclude according tests. See https://github.com/appveyor/ci/issues/495 and +# https://github.com/appveyor/ci/issues/688 + +WINDOWS_BROKEN = set(( + 'bakalint', # not installed + 'phpcs', # https://github.com/coala/coala-bears/issues/2916 + 'pmd', 'cpd', # https://github.com/coala/coala-bears/issues/2908 + 'mcs', # choco mono isnt providing this in the PATH + 'tailor', # installer fails + 'shellcheck', # https://github.com/coala/coala-bears/issues/2920 + # pip + 'apertium_lint', # https://gitlab.com/jpsinghgoud/apertium-lint/issues/5 + 'bandit', # RuntimeError: Unable to output report using 'json' formatter + 'clang', # see note above + 'cppclean', # https://github.com/myint/cppclean/issues/120 + 'scspell', # https://github.com/coala/coala-bears/issues/2926 + 'vint', # https://github.com/Kuniwak/vint/issues/290 + # gem + 'csvlint', # https://github.com/coala/coala-bears/issues/2909 + 'sqlint', # https://github.com/coala/coala-bears/issues/2923 + # npm ; try different version + 'alex', # https://github.com/coala/coala-bears/issues/2922 + 'coffeelint', # Extra windows results + 'csscomb', # Linter errors + 'dockerfile_lint', # test case bug + 'elm-format', # https://github.com/coala/coala-bears/issues/2925 + 'gherkin', # result json decode exception + 'jshint', # test case bug + 'remark', # remark result text difference due to unicode + 'postcss', # https://github.com/coala/coala-bears/issues/2921 + 'sass-lint', # rule `!important not allowed` not trigger + 'textlint', # Unexpected extra result in test + # Also textlint plugin for asciidoc requires a compiler. + # and should be replaced with plugin asciidoctor which does + # not need a compiler + + # No information from linter bear + 'eslint', # Two of tests fail + 'tslint', # Half of tests fail +)) + + +DISABLE_BEARS = set(os.environ.get('DISABLE_BEARS', '').split(' ')) + + +def get_metadata(): + with open('bear-metadata.yaml') as f: + metadata = yaml.load(f) + + return metadata['bear_metadata'] + + +def get_bears(metadata, args, include_disabled=False): + bears = [] + + for arg in args: + for bear in metadata.values(): + tags = set(bear['tags']) + + if tags.intersection(DISABLE_BEARS): + tags.add('disabled') + + if IS_WIN and tags.intersection(WINDOWS_BROKEN): + tags.add('disabled') + + if arg in tags and (include_disabled or 'disabled' not in tags): + bears.append(bear) + + return bears + + +CLANG_EXTRA_TESTS = [ + 'tests/c_languages/codeclone_detection/ClangCountingConditionsTest.py', + 'tests/c_languages/codeclone_detection/ClangCountVectorCreatorTest.py', + 'tests/c_languages/codeclone_detection/CountVectorTest.py', + 'tests/c_languages/codeclone_detection/CloneDetectionRoutinesTest.py', +] + + +def get_tests(bears): + # Add 1 for the path separator after bears + project_dir_prefix_len = len(PROJECT_DIR) + 1 + + tests = set() + for bear in bears: + name = bear['name'] + if name.startswith('_'): + continue + subdir = bear['subdir'] + # A few test modules are FoobearSomethingTest.py, like + # PySafetyBearWithoutMockTest.py + testpath = os.path.join('tests', subdir, '{}*Test.py'.format(name)) + files = glob.glob(testpath) + for filename in files: + filename = filename.replace(os.path.sep, '/') + if filename.startswith('/'): + filename = filename[project_dir_prefix_len:] + tests.add(filename) + + if subdir == 'c_languages/codeclone_detection': + tests.update(CLANG_EXTRA_TESTS) + + elif subdir.startswith('vcs'): + tests.add('tests/vcs/CommitBearTest.py') + + return tests + + +def main(): + args_orig = sys.argv[1:] + metadata = get_metadata() + + include_disabled = False + if args_orig[0] == '--disabled': + include_disabled = True + args_orig = args_orig[1:] + + args = [] + for arg in args_orig: + if arg in ['ghc-mod', 'default-jre']: + args += [arg] + continue + args += arg.split('-') + + if 'java7' in args or 'java8' in args: + args.append('java') + + if 'pip' in args: + args.append('noreqs') + + # TODO: pass through any args which are literal test filenames + + bears = get_bears(metadata, args, include_disabled) + tests = get_tests(bears) + print(' '.join(sorted(tests))) + + +if __name__ == '__main__': + main() diff --git a/.ci/refreshenv.sh b/.ci/refreshenv.sh new file mode 100644 index 0000000000..49701d4348 --- /dev/null +++ b/.ci/refreshenv.sh @@ -0,0 +1,29 @@ +# shellcheck disable=SC2059,SC2154 +# as shellcheck believes the $ in the heredoc are shell variables + +function refreshenv +{ + powershell -NonInteractive - <<\EOF +Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" + +Update-SessionEnvironment + +# Round brackets in variable names cause problems with bash +Get-ChildItem env:* | %{ + if (!($_.Name.Contains('('))) { + $value = $_.Value + if ($_.Name -eq 'PATH') { + $value = $value -replace ';',':' + } + Write-Output ("export " + $_.Name + "='" + $value + "'") + } +} | Out-File -Encoding ascii $env:TEMP\refreshenv.sh + +EOF + + # shellcheck disable=SC1090 + # as shellcheck can not follow this `source` + source "$TEMP/refreshenv.sh" +} + +alias RefreshEnv=refreshenv diff --git a/.ci/run_with_env.cmd b/.ci/run_with_env.cmd deleted file mode 100644 index da8e020a54..0000000000 --- a/.ci/run_with_env.cmd +++ /dev/null @@ -1,88 +0,0 @@ -:: To build extensions for 64 bit Python 3, we need to configure environment -:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: -:: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) -:: -:: To build extensions for 64 bit Python 2, we need to configure environment -:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: -:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) -:: -:: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific -:: environment configurations. -:: -:: Note: this script needs to be run with the /E:ON and /V:ON flags for the -:: cmd interpreter, at least for (SDK v7.0) -:: -:: More details at: -:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows -:: http://stackoverflow.com/a/13751649/163740 -:: -:: Author: Olivier Grisel -:: License: CC0 1.0 Universal: https://creativecommons.org/publicdomain/zero/1.0/ -:: -:: Notes about batch files for Python people: -:: -:: Quotes in values are literally part of the values: -:: SET FOO="bar" -:: FOO is now five characters long: " b a r " -:: If you don't want quotes, don't include them on the right-hand side. -:: -:: The CALL lines at the end of this file look redundant, but if you move them -:: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y -:: case, I don't know why. -@ECHO OFF - -SET COMMAND_TO_RUN=%* -SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows -SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf - -:: Extract the major and minor versions, and allow for the minor version to be -:: more than 9. This requires the version number to have two dots in it. -SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1% -IF "%PYTHON_VERSION:~3,1%" == "." ( - SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1% -) ELSE ( - SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2% -) - -:: Based on the Python version, determine what SDK version to use, and whether -:: to set the SDK for 64-bit. -IF %MAJOR_PYTHON_VERSION% == 2 ( - SET WINDOWS_SDK_VERSION="v7.0" - SET SET_SDK_64=Y -) ELSE ( - IF %MAJOR_PYTHON_VERSION% == 3 ( - SET WINDOWS_SDK_VERSION="v7.1" - IF %MINOR_PYTHON_VERSION% LEQ 4 ( - SET SET_SDK_64=Y - ) ELSE ( - SET SET_SDK_64=N - IF EXIST "%WIN_WDK%" ( - :: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ - REN "%WIN_WDK%" 0wdf - ) - ) - ) ELSE ( - ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" - EXIT 1 - ) -) - -IF %PYTHON_ARCH% == 64 ( - IF %SET_SDK_64% == Y ( - ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture - SET DISTUTILS_USE_SDK=1 - SET MSSdk=1 - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 - ) ELSE ( - ECHO Using default MSVC build environment for 64 bit architecture - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 - ) -) ELSE ( - ECHO Using default MSVC build environment for 32 bit architecture - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 -) diff --git a/.ci/store_env_in_registry.py b/.ci/store_env_in_registry.py new file mode 100755 index 0000000000..6f766d5c03 --- /dev/null +++ b/.ci/store_env_in_registry.py @@ -0,0 +1,85 @@ +import os + +KEY = 'System\\CurrentControlSet\\Control\\Session Manager\\Environment' + +DISCARD_KEYWORDS = tuple([ + 'awscli', + 'azure', + 'coverity', + 'dnvm', + 'mspec', + 'nunit', + 'odbc', + 'privateassemblies', + 'python27', + 'ruby193', + 'service fabric', + 'sql', + 'subversion', + 'testwindow', + 'xunit', +]) + +# TODOs: +# - Also get raw unexpanded values from registry to reduce length, and +# merge them with the current PATH which AppVeyor has populated +# - also fetch and filter user env vars, also de-duplicate wrt system vars +# (see https://github.com/reider-roque/pathvar) +# - Convert to Windows path names, not /c/foo/bar +# - Replace \\ and \.\ with \ + + +def get_tidy_path(original): + parts = [] + dups = set() + discard_matches = set() + + for part in original.split(';'): + # This will break directories with a trailing space + part = part.strip().rstrip('\\') + if part in parts: + dups.add(part) + continue + + part_lower = part.lower() + for word in DISCARD_KEYWORDS: + if word in part_lower: + discard_matches.add(word) + break + else: + parts.append(part) + + if dups: + print('Discarded dups:\n {}'.format('\n '.join(sorted(dups)))) + + if discard_matches: + print('Discarded keyword matches: ' + '{}'.format(', '.join(sorted(discard_matches)))) + + return ';'.join(parts) + + +def set_envvar_in_registry(envvar, value): + try: + import winreg + except ImportError: + import _winreg as winreg + + reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + with winreg.OpenKey(reg, KEY, 0, winreg.KEY_ALL_ACCESS) as regkey: + winreg.SetValueEx(regkey, envvar, 0, winreg.REG_EXPAND_SZ, value) + + +def do_all_environ(): + for key, value in os.environ.items(): + if key.upper() in ['PWD', 'OLDPWD', 'CWD']: + continue + + if key.upper() in ['PATH', 'PSMODULEPATH']: + value = get_tidy_path(value) + print('%s (len %d) set to:\n%s' % (key, len(value), value)) + set_envvar_in_registry(key, value) + + +if __name__ == '__main__': + do_all_environ() diff --git a/.ci/travis_extra_globals.sh b/.ci/travis_extra_globals.sh new file mode 100644 index 0000000000..219fb6c730 --- /dev/null +++ b/.ci/travis_extra_globals.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# This must be `source`d, and kept very basic to avoid breaking travis shell + +# Uses tox.ini selectors so that multiple languages can be handled in one job +if [ -z "$TOX_TEST_SELECTORS" ] && [ "$TRAVIS_LANGUAGE" ]; then + if [ "$TRAVIS_LANGUAGE" = "ruby" ]; then + TOX_TEST_SELECTORS=gem + elif [ "$TRAVIS_LANGUAGE" = "node_js" ]; then + TOX_TEST_SELECTORS=npm + elif [ "${BEARS/lua/}" != "$BEARS" ]; then + TOX_TEST_SELECTORS="$BEARS" + else + TOX_TEST_SELECTORS="$TRAVIS_LANGUAGE" + fi + export TOX_TEST_SELECTORS +fi + +if [ "${TOX_TEST_SELECTORS/gem/}" != "$TOX_TEST_SELECTORS" ]; then + # https://travis-ci.community/t/bundle-path-disappears/4260 + export BUNDLE_PATH="$TRAVIS_BUILD_DIR/vendor/bundle" + export BUNDLE_BIN="$BUNDLE_PATH/bin" + EXTRA_PATH="$EXTRA_PATH:$BUNDLE_BIN" +fi + +if [ "${TOX_TEST_SELECTORS/java/}" != "$TOX_TEST_SELECTORS" ]; then + EXTRA_PATH="$EXTRA_PATH:$HOME/.local/tailor/tailor-latest/bin" +fi + +if [ "${TOX_TEST_SELECTORS/php/}" != "$TOX_TEST_SELECTORS" ]; then + EXTRA_PATH="$EXTRA_PATH:$TRAVIS_BUILD_DIR/vendor/bin" +fi + +if [ "${TOX_TEST_SELECTORS/npm/}" != "$TOX_TEST_SELECTORS" ]; then + # Travis adds relative ./node_modules/.bin , but some tests change directory + EXTRA_PATH="$EXTRA_PATH:$TRAVIS_BUILD_DIR/node_modules/.bin" +fi + +if [ "${TOX_TEST_SELECTORS/lua/}" != "$TOX_TEST_SELECTORS" ]; then + EXTRA_PATH="$EXTRA_PATH:$HOME/.luarocks/bin" +fi + +# Remove leading colons +EXTRA_PATH="${EXTRA_PATH##:}" + +if [ "$EXTRA_PATH" != "" ]; then + echo "EXTRA_PATH=$EXTRA_PATH" + export PATH="$PATH:$EXTRA_PATH" +fi diff --git a/.ci/travis_init.ps1 b/.ci/travis_init.ps1 new file mode 100644 index 0000000000..40b36d6682 --- /dev/null +++ b/.ci/travis_init.ps1 @@ -0,0 +1,5 @@ +$PSVersionTable + +Set-MpPreference -DisableRealtimeMonitoring $true +Set-MpPreference -DisableArchiveScanning $true +Set-MpPreference -DisableBehaviorMonitoring $true diff --git a/.coafile b/.coafile index f78c659676..ca7e004baa 100644 --- a/.coafile +++ b/.coafile @@ -1,7 +1,8 @@ [all] files = *.py, bears/**/*.py, tests/**/*.py, .moban.dt/*.py.in ignore = tests/python/test_files/pylint_test.py, tests/python/bandit_test_files/*, - tests/python/vulture_test_files/* + tests/python/vulture_test_files/*, + .ci/Fudge*.ps* max_line_length = 80 use_spaces = True @@ -70,6 +71,7 @@ ignore = *.py [all.yml] bears = YAMLLintBear files = *.yml, *.yaml, .ci/*.yml, tests/**/*.yml +ignore = MYMETA.yml [bash] bears = ShellCheckBear diff --git a/.moban.dt/bears-appveyor.yml.jj2 b/.moban.dt/bears-appveyor.yml.jj2 new file mode 100644 index 0000000000..bc8a95c9d8 --- /dev/null +++ b/.moban.dt/bears-appveyor.yml.jj2 @@ -0,0 +1,10 @@ +{% extends 'ci/appveyor.yml.jj2' %} + +{% set _ = appveyor_global_environment.__setitem__( + 'TOX_FEATURES', '-'.join(tox.features)) %} +{% set _ = appveyor_global_environment.__setitem__( + 'BEAR_LIST', ' '.join(tox.win.extra_bears)) %} +{% set _ = appveyor_global_environment.__setitem__( + 'TOX_TEST_SELECTORS', '-'.join(tox.win.selectors)) %} +{% set _ = appveyor_global_environment.__setitem__( + 'TOXENV', 'py$(PYTHON_MINOR_NODOTS)-$(TOX_TEST_SELECTORS)-$(TOX_FEATURES)-win') %} diff --git a/.moban.dt/bears-test-requirements.txt.jj2 b/.moban.dt/bears-test-requirements.txt.jj2 index f35988528c..4bbc504435 100644 --- a/.moban.dt/bears-test-requirements.txt.jj2 +++ b/.moban.dt/bears-test-requirements.txt.jj2 @@ -1,2 +1,9 @@ {% include 'test-requirements.txt.jj2' %} +pbr!=2.1.0,>=2.0.0 +pytest-error-for-skips +git+https://github.com/krkd/pytest-cov-threshold#egg=pytest-cov-threshold ; python_version > '3.4' twine~=1.7.4 +tox~=3.12.0 +tox-travis +tox-backticks +tox-pyenv diff --git a/.moban.dt/bears-travis.yml.jj2 b/.moban.dt/bears-travis.yml.jj2 new file mode 100644 index 0000000000..696c8aeb4e --- /dev/null +++ b/.moban.dt/bears-travis.yml.jj2 @@ -0,0 +1,267 @@ +{% extends 'ci/travis.yml.jj2' %} + +{% block custom_python_versions %} +python: 3.6.3 +{% endblock %} + +{% block stages %} +stages: + - name: sentinel + - test-languages + - test-other-versions + +cache: &global_cache + pip: true + directories: + - $HOME/.local/ + +{% endblock %} + +{% block jobs %} +{% macro job(language, version, seen, all_apt_packages) -%} +{% if language == 'python' and version.startswith('3.5') %} +{% set seen = False %} +{% endif %} +{% if language == 'python' and version.startswith('3.6') %} + - stage: sentinel +{% elif not seen %} + - stage: test-languages +{% else %} + - stage: test-other-versions +{% endif %} +{% set env = {} %} +{% set version_key = language %} +{% set dist = None %} +{% set packages = [] %} +{% set cacher = False %} +{% if language == 'lua' %} +{% set env = {'BEARS': language} %} +{% set version_key = None %} +{% set packages = ['luarocks'] %} +{% elif language == 'infer' %} +{% set version_key = None %} +{% set env = { + 'BEARS': 'opam', + 'INFER_VERSION': version, + 'PATH': '$PATH:$HOME/infer-linux64-v$INFER_VERSION/infer/bin', + } %} +{% set dist = 'trusty' %} +{% set packages = ['camlp4-extra', 'ocaml', 'opam'] %} +{% elif language == 'apt' %} +{% set version_key = None %} +{% set dist = version %} +{% if version in ['xenial', 'bionic'] %} +{% set _disable_bears = "shellcheck" %} +{% endif %} +{% set env = { + 'DIST': version, + 'BEARS': 'apt_get', + 'DISABLE_BEARS': _disable_bears, + } %} +{# https://travis-ci.community/t/apt-addon-broken-on-bionic/4061 #} +{% set _unsupported_packages = { + 'precise': ['astyle', 'hlint', 'php-codesniffer', 'phpmd', 'shellcheck'], + 'trusty': ['astyle'], + 'xenial': ['shellcheck'], + 'bionic': all_apt_packages, + } %} +{% for package in all_apt_packages %} +{% if package not in _unsupported_packages[dist] %} +{% set _ = packages.append(package) %} +{% endif %} +{% endfor %} +{% if 'flawfinder' in packages %} +{% set _ = packages.append('python') %} +{% endif %} +{% elif language == 'ruby' %} +{% set dist = 'trusty' %} +{% set version_key = 'rvm' %} +{% set cacher = 'bundler' %} +{% if version >= '2.5' %} +{% set env = {'DISABLE_BEARS': "csvlint"} %} +{% endif %} +{% elif language == 'python' %} +{# clang-3.4 is not available on xenial #} +{% set dist = 'trusty' %} +{% set packages = ['clang-3.4'] %} +{% elif language == 'perl' %} +{% set dist = 'trusty' %} +{% elif language == 'php' %} +{% set dist = 'trusty' %} +{% elif language == 'r' %} +{% set cacher = 'packages' %} +{% elif language == 'node_js' %} +{% set cacher = 'npm' %} +{% elif language == 'java' %} +{% set version_key = 'jdk' %} +{% if version == '7' %} +{% set dist = 'trusty' %} +{% set env = {'DISABLE_BEARS': "tailor"} %} +{% elif version != '8' %} +{% set env = {'DISABLE_BEARS': "languagetool tailor"} %} +{% endif %} +{% set version = 'openjdk' + version %} +{% elif language == 'mono' %} +{% set language = 'csharp' %} +{% endif %} +{% if dist %} + dist: {{ dist }} +{% endif %} +{% if language in ['infer', 'apt'] %} + language: generic +{% else %} + language: {{ language }} +{% endif %} +{% if version_key %} +{% if version.endswith('0') and not version.endswith('.0') %} + {{ version_key }}: '{{ version }}' +{% else %} + {{ version_key }}: {{ version }} +{% endif %} +{% endif %} +{% if env %} +{% if env | length == 1 %} +{% set name = [].__class__(env.keys())[0] %} +{% set _value = env[name] %} +{% endif %} +{% if _value %} + env: {{ name }}="{{ _value }}" +{% else %} + env: +{% for name, value in env.items() %} +{% if value %} + {{ name }}="{{ value }}" +{% endif %} +{% endfor %} +{% endif %} +{% endif %} +{% if cacher %} + cache: + <<: *global_cache + {{ cacher }}: true +{% endif %} +{% if packages %} + addons: + apt: +{% if 'opam' in packages %} + sources: + - avsm +{% endif %} +{% if packages | length == 1 %} + packages: {{ packages[0] }} +{% else %} + packages: +{% for package in packages %} + - {{ package }} +{% endfor %} +{% endif %} +{% endif %} +{% if language == 'go' %} + install: skip + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/.cache/go-build/ + - $HOME/gopath/pkg/mod/ +{% elif language == 'apt' and version == 'precise' %} + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/.pyenv/ +{% elif language == 'python' %} + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - docs/_build/ + - $HOME/nltk_data/ +{% elif language == 'lua' %} + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/.luarocks/ +{% elif language == 'perl' %} + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/perl5/ +{% elif language == 'scala' and version.startswith('2.12') %} + jdk: openjdk8 +{% elif language == 'julia' %} +{# default language is 0.6.4, and has problems with pre-installed Lint.jl #} +{# install not provided https://github.com/travis-ci/travis-build/pull/1571 #} + env: JL_PKG={{ appveyor_global_environment.JL_PKG }} + install: + - julia --color=yes .ci/deps.julia.jl + # Verify compilation works + - julia -e 'import Lint.lintfile' + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/.julia/ +{% endif %} +{% endmacro %} +{# reorder so python is first on Python 3.6 #} +{% set _supported_versions = {'python': python_versions} %} +{% set _ = _supported_versions.update(supported_versions) %} +{% set supported_versions = _supported_versions %} +jobs: + include: + # Manually added "language" entries should complete test coverage +{% macro manualmatrix() %}{% include 'travis-manual-matrix.yaml' %}{% endmacro %} + {{ manualmatrix() | indent(4) }} + # Entries generates from `supported_versions` +{% set seen_languages = {} %} +{% set apt_packages = [] %} +{% for requirement in distro_requirements.values() %} +{% for package_type, package_name in requirement.packages.items() %} +{% if package_type == 'apt_get' %} +{% if not package_name.startswith('r-') and package_name not in ['ghc-mod'] %} +{% set package_name = package_name.replace('php-cli', 'php5-cli') %} +{% set _ = apt_packages.append(package_name) %} +{% endif %} +{% endif %} +{% endfor %} +{% endfor %} +{% for language, versions in supported_versions.items() %} +{% for version in versions %} +{% set version = ''.__class__(version) %} +{{ job(language, version, language in seen_languages, apt_packages) }} +{% set _ = seen_languages.__setitem__(language, 1) %} +{% endfor %} +{% endfor %} + - *moban + allow_failures: + - *moban +{% endblock %} + +{% block before_install %} +{% macro beforeinstall() %}{% include 'travis-before-install.yaml' %}{% endmacro %} +{{ beforeinstall() }} +{% endblock %} + +{% block script %} + # Ensure metadata files are in sync with the bear metadata in the source + - if [ -n "$TRAVIS_PYTHON_VERSION" ]; then + PYTHONPATH=. .ci/generate_bear_metadata.py --debug --update; + fi + - python -m tox + - if [ -n "$TRAVIS_PYTHON_VERSION" ]; then + python setup.py docs; + fi + - if [ -n "$TRAVIS_PYTHON_VERSION" ]; then + coala --non-interactive; + fi +{% endblock %} + +{% block end %} + +branches: + exclude: + - /^sils\// +{% endblock %} diff --git a/.moban.dt/travis-before-install.yaml b/.moban.dt/travis-before-install.yaml new file mode 100644 index 0000000000..958ba51803 --- /dev/null +++ b/.moban.dt/travis-before-install.yaml @@ -0,0 +1,56 @@ +env: + global: + - TERM=dumb + - PATH="$HOME/.local/bin:$PATH" + # These are only needed by Windows + - NUGET_EXE_NO_PROMPT=true + - VIRTUALENV_NO_DOWNLOAD=1 + # Enable to debug tox + # VIRTUALENV_VERBOSE=1 + # This exceeds the travis maximum log length + # PIP_VERBOSE=1 + - PIP_DISABLE_PIP_VERSION_CHECK=1 + - PIP_YES=1 + - FudgeCI=${TRAVIS_BUILD_DIR}/.ci/ + - TOX_FEATURES="{{ '-'.join(tox.features) }}" + +before_install: + - printenv + - mkdir -p ~/bin ~/.local/bin + - source .ci/travis_extra_globals.sh + + - if [ -z "$TRAVIS_PYTHON_VERSION" ]; then + .ci/deps.python36.sh; + fi + - if [ -d "$HOME/.pyenv/bin" ]; then + export PATH="$HOME/.pyenv/bin:$PATH"; + fi + - hash -r && pyenv versions --bare && python --version + - if [ "${TRAVIS_PYTHON_VERSION/3.4/}" != "$TRAVIS_PYTHON_VERSION" ]; then + pip install pip==9.0.3 setuptools==21.2.2; + fi + + - if [ -f ".ci/deps.$TRAVIS_LANGUAGE.sh" ]; then + bash -e -x ".ci/deps.$TRAVIS_LANGUAGE.sh"; + fi + + # https://github.com/coala/coala/issues/3183 + - cp requirements.txt requirements.orig + - printf '%s\n%s\n%s\n' + "$(cat test-requirements.txt)" + "$(grep -v '^-r' docs-requirements.txt)" + "$(cat bear-requirements.txt requirements.txt)" + > requirements.txt + +before_script: + - mv requirements.orig requirements.txt + - if [ -n "$TRAVIS_PYTHON_VERSION" ]; then + python setup.py bdist_wheel && + pip install $(ls ./dist/*.whl)"[alldeps]"; + fi + - if [ -z "$TRAVIS_PYTHON_VERSION" -a "$TRAVIS_OS_NAME" = "linux" ]; then + python -m pip install --upgrade --user -r test-requirements.txt; + fi + - if [ "${TRAVIS_PYTHON_VERSION/3.4/}" != "$TRAVIS_PYTHON_VERSION" ]; then + pip install --upgrade setuptools; + fi diff --git a/.moban.dt/travis-manual-matrix.yaml b/.moban.dt/travis-manual-matrix.yaml new file mode 100644 index 0000000000..96a308ca77 --- /dev/null +++ b/.moban.dt/travis-manual-matrix.yaml @@ -0,0 +1,166 @@ +{# This file can include Jinja as long as it is valid YAML #} +- stage: test-languages + dist: xenial + # This is in generic image, as language: haskell isnt working yet + # https://github.com/coala/coala-bears/issues/1384 + language: generic + env: DIST=xenial BEARS=adhoc BEAR_LIST="ghc-mod" + # ghc-mod needs parts of ghc, specifically at least /usr/lib/ghc/settings + # ghc-mod needs cabal-install + cache: + pip: true + directories: + - $HOME/.local/ + - $HOME/.cabal/ + - $HOME/.ghc/ + - $HOME/.ghc-mod/ + addons: + apt: + packages: + - cabal-install + - ghc + - ghc-mod + +- stage: test-languages + dist: xenial + language: generic + env: DIST=xenial BEARS=adhoc BEAR_LIST="apt_get bakalint default-jre" + DISABLE_BEARS="astyle flawfinder ghc-mod r_script shellcheck" + # astyle and shellcheck are failing in xenial + # xenial doesnt have Python 2.7 needed for flawfinder + # R bears and ghc_mod use separate jobs + addons: + apt: + packages: + - chktex + - cppcheck + - devscripts + - hlint + - indent + - libperl-critic-perl + - libxml2-utils + - mono-mcs + - php7.0-cli + - phpmd + - php-codesniffer + - verilator + +- stage: test-languages + dist: trusty + language: generic + env: DIST=trusty BEARS=adhoc BEAR_LIST="astyle flawfinder shellcheck" + addons: + apt: + sources: + - sourceline: # astyle + deb http://ppa.launchpad.net/cs50/ppa/ubuntu trusty main + key_url: + https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x5BDA2E974A0E822C + packages: + - astyle + - flawfinder + - shellcheck + +- stage: test-other-versions + dist: trusty + language: generic + env: DIST=trusty BEARS=apt_get + addons: + apt: + packages: + - chktex + - cppcheck + - devscripts + - indent + - libperl-critic-perl + - libxml2-utils + - mono-mcs + - php5-cli + - php-codesniffer + - verilator + +# Additonal manual entries +- stage: test-other-versions + os: windows + language: bash + python: '{{ choco_requirements.python.version }}' + env: + BEAR_LIST="{{ ' '.join(tox.win.extra_bears) }}" + TOX_TEST_SELECTORS="{{ '-'.join(tox.win.selectors) }}" + cache: + directories: + - C:/nuget_http_cache/ + - C:/choco_cache/ + - C:/pip_cache/ + before_install: + - bash --version + - powershell -c "Set-ExecutionPolicy -ExecutionPolicy Unrestricted + -Scope LocalMachine" + - powershell .ci/travis_init.ps1 + + - export NUGET_HTTP_CACHE_PATH=/C/nuget_http_cache + - export CHOCO_CACHE_DIR=/C/choco_cache + - export PIP_CACHE_DIR=/C/pip_cache + + - source .ci/travis_extra_globals.sh + + - export TOXINI_SITEPACKAGES=true + - export VIRTUALENV_NO_PIP=1 + - export VIRTUALENV_NO_SETUPTOOLS=1 + + - export TOXENV="py36-${TOX_TEST_SELECTORS}-${TOX_FEATURES}-win" + + - printenv + + - cp .ci/choco.config $ChocolateyInstall/config/chocolatey.config + + - python .ci/store_env_in_registry.py + - source .ci/refreshenv.sh + + # TODO: Add support for disabling pre-installed vctools which is + # disabled in choco_requirements. Uninstalling vctools fails + install: + - powershell -c ". .ci/Fudge.ps1 install" + - refreshenv + + - taskkill -IM "gpg-agent.exe" || true + + - pip uninstall tox-venv || true + + # Avoid verify_gemfile_dependencies_are_found errors due to + # Gemfile modifications in .ci/deps.ruby-packages.ps1 + - cp Gemfile.win Gemfile + + - cp requirements.txt requirements.orig + - cp tox.ini tox.orig + - "sed -i 's/^envlist.*$/envlist: '$TOXENV/ tox.ini" + + after_script: + - cp tox.orig tox.ini + - cp Gemfile.orig Gemfile + +- python: 2.7 + stage: test-other-versions + env: PIP_NO_COMPILE=1 + before_install: true + install: pip install 3to2 + before_script: true + script: .ci/check_unsupported.sh + +- python: 3.3 + stage: test-other-versions + dist: trusty + env: PIP_NO_COMPILE=1 + before_install: true + install: true + before_script: true + script: .ci/check_unsupported.sh + +- python: 3.6 + stage: sentinel + before_install: false + install: pip install moban + before_script: false + script: .ci/check_moban.sh + after_success: false + after_failure: false diff --git a/.moban.yaml b/.moban.yaml index 6ac3d2a1b3..b9824460e7 100644 --- a/.moban.yaml +++ b/.moban.yaml @@ -12,22 +12,153 @@ package_module: bears docs_source_dir: API docs_dir: docs test_prevent_skips: false +lint_command: false +appveyor_global_environment: + # Needed for Julia + JL_PKG: CoalaBears + JULIA_PROJECT: "'@.'" + +python_versions: + - 3.6 + - 3.5 + - 3.4.4 + +tox: + features: + - check + - noskip + - codecov + win: + selectors: + - pip + - noreqs + - npm + - gem + - go + - perl + - php + - java8 + - adhoc + extra_bears: + - astyle + - cppcheck + - xmllint entry_points: coalabears: - coala_official_bears = bears +choco_requirements: + # overrides for package managers not yet used + visualstudio2017-workload-vctools: false + MinGW: false + miniconda3: false + R.Project: false # https://github.com/coala/coala-bears/issues/2919 + luarocks: false # https://github.com/coala/coala-bears/issues/2918 + julia: false # A bit tricky to get Lint.jl to build + ghc: false # Compiling from source broken on Travis + haskell-stack: false + rust: false # https://github.com/coala/coala-bears/issues/50 + bower: false + # extra deps for bears + elm-platform: false # https://github.com/coala/coala-bears/issues/2925 + astyle: true + cppcheck: true + xsltproc: true + ShellCheck: true # https://github.com/coala/coala-bears/issues/2920 + +# The first entry is the most supported +# Latter entries which are higher versions may have some bears disabled +supported_versions: + # python included from `python_version` + apt: + # xenial and trusty are manually crafted to balance the job matrix + # so that only one of each necessary dependency is done in first batch + - precise + - bionic + mono: + - 5.20.1 + r: + - release + - devel + - oldrel + node_js: + - 10 + - 9 + - 8 + - 7 + - 6 + julia: + - 1.1 + - 1.0 + - 0.7.0 + dart: + - 1.15.0 + - 1.14.2 + perl: + - '5.30' + - 5.22 + - 5.18 + - 5.14 + go: + - 1.11 + - '1.10' + ruby: + - 2.4 + - 2.3 + - 2.2 + - 2.1 + - 2.5 + php: + - 7.2 + - hhvm-3.18 + - 5.5 + scala: + - 2.11 + - 2.12.2 + java: + - 8 + - 7 + # openjdk 9, 10 and 12 are broken on Travis + # https://github.com/travis-ci/travis-cookbooks/issues/976 + # Their config is the same as 11 + - 11 + lua: + - default + infer: + - 0.7.0 + +requires: + - https://gitlab.com/coala/package_manager/ + - https://gitlab.com/coala/mobans + configuration: template_dir: - .moban.dt/ - - .ci/ - - ../coala-mobans/templates/ - - ../coala-mobans/assets/ + - 'mobans:templates/' + - 'mobans:assets/' + - 'package_manager:' configuration: .moban.yaml - configuration_dir: ../coala-mobans/ + configuration_dir: 'mobans:' targets: - setup.py: bears-setup.py.jj2 - setup.cfg: bears-setup.cfg.jj2 + - Fudgefile: Fudgefile.jj2 + # Powershell Linting + - .ci/PSLint.ps1: ci/PSLint.ps1 + - .ci/Export-NUnitXml.psm1: ci/Export-NUnitXml.psm1 + - .ci/PSScriptAnalyzerSettings.psd1: ci/PSScriptAnalyzerSettings.psd1 + # Fudge + - .ci/Fudge.ps1: fudge/Fudge.ps1 + - .ci/Modules/FudgeTools.psm1: fudge/Modules/FudgeTools.psm1 + - .ci/FudgeCI.ps1: fudge/FudgeCI.ps1 + - .ci/PrepareAVVM.ps1: fudge/PrepareAVVM.ps1 + - .ci/FudgeGenerateFake.ps1: fudge/FudgeGenerateFake.ps1 + - .ci/FudgePostInstall.ps1: fudge/FudgePostInstall.ps1 + - .ci/store_env_in_registry.py: ci/store_env_in_registry.py + - .ci/refreshenv.sh: ci/refreshenv.sh + - .ci/constants.ps1: constants.ps1.jj2 + - .ci/deps.python-packages.ps1: fudge/deps.python-packages.ps1 - requirements.txt: requirements.txt.jj2 - test-requirements.txt: bears-test-requirements.txt.jj2 - bears/VERSION: VERSION.jj2 @@ -35,8 +166,13 @@ targets: - DESCRIPTION: DESCRIPTION.jj2 - package.json: package.json.jj2 - bear-requirements.txt: bear-requirements.txt.jj2 - - .ci/appveyor.yml: ci/appveyor.yml.jj2 - - .ci/run_with_env.cmd: run_with_env.cmd + - .travis.yml: bears-travis.yml.jj2 + - .ci/appveyor.yml: bears-appveyor.yml.jj2 + # from package manager + - .ci/deps.golang.ps1: .ci/deps.golang.ps1 + - .ci/deps.ActivePerl.ps1: .ci/deps.ActivePerl.ps1 + - .ci/deps.php.ps1: .ci/deps.php.ps1 + # Other - .ci/check_unsupported.sh: ci/check_unsupported.sh.jj2 - runtime.txt: runtime.txt - netlify.toml: docs/netlify.toml diff --git a/.travis.yml b/.travis.yml index 9544d7517b..523d44a464 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,25 @@ sudo: false - +dist: xenial language: python -python: - - 3.4 - - 3.5 - - 3.6 +notifications: + email: false +python: 3.6.3 + +stages: + - name: sentinel + - test-languages + - test-other-versions + +cache: &global_cache + pip: true + directories: + - $HOME/.local/ .disable_global: &disable_global addons: false - cache: pip + cache: false + env: {} + python: false before_install: false install: false before_script: false @@ -18,160 +29,586 @@ python: before_deploy: false deploy: false -stages: - - name: sentinel - if: branch != master OR type = pull_request - - test - - moban - - name: unsupported - if: branch = master AND type = push - -.check_moban: &check_moban +.moban: &moban <<: *disable_global python: 3.6 stage: moban - install: pip install moban - script: .ci/check_moban.sh - if: branch != master OR type = pull_request + install: pip install moban>=0.0.4 + script: + - moban + - git diff --exit-code jobs: include: - - stage: sentinel - # All other jobs will be cancelled if the sentinel job fails - <<: *disable_global - python: 3.6 - install: pip install -r requirements.txt ".[alldeps]" - script: coala --non-interactive -V + # Manually added "language" entries should complete test coverage + - stage: test-languages + dist: xenial + # This is in generic image, as language: haskell isnt working yet + # https://github.com/coala/coala-bears/issues/1384 + language: generic + env: DIST=xenial BEARS=adhoc BEAR_LIST="ghc-mod" + # ghc-mod needs parts of ghc, specifically at least /usr/lib/ghc/settings + # ghc-mod needs cabal-install + cache: + pip: true + directories: + - $HOME/.local/ + - $HOME/.cabal/ + - $HOME/.ghc/ + - $HOME/.ghc-mod/ + addons: + apt: + packages: + - cabal-install + - ghc + - ghc-mod + + - stage: test-languages + dist: xenial + language: generic + env: DIST=xenial BEARS=adhoc BEAR_LIST="apt_get bakalint default-jre" + DISABLE_BEARS="astyle flawfinder ghc-mod r_script shellcheck" + # astyle and shellcheck are failing in xenial + # xenial doesnt have Python 2.7 needed for flawfinder + # R bears and ghc_mod use separate jobs + addons: + apt: + packages: + - chktex + - cppcheck + - devscripts + - hlint + - indent + - libperl-critic-perl + - libxml2-utils + - mono-mcs + - php7.0-cli + - phpmd + - php-codesniffer + - verilator + + - stage: test-languages + dist: trusty + language: generic + env: DIST=trusty BEARS=adhoc BEAR_LIST="astyle flawfinder shellcheck" + addons: + apt: + sources: + - sourceline: # astyle + deb http://ppa.launchpad.net/cs50/ppa/ubuntu trusty main + key_url: + https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x5BDA2E974A0E822C + packages: + - astyle + - flawfinder + - shellcheck + + - stage: test-other-versions + dist: trusty + language: generic + env: DIST=trusty BEARS=apt_get + addons: + apt: + packages: + - chktex + - cppcheck + - devscripts + - indent + - libperl-critic-perl + - libxml2-utils + - mono-mcs + - php5-cli + - php-codesniffer + - verilator + + # Additonal manual entries + - stage: test-other-versions + os: windows + language: bash + python: '3.6.8' + env: + BEAR_LIST="astyle cppcheck xmllint" + TOX_TEST_SELECTORS="pip-noreqs-npm-gem-go-perl-php-java8-adhoc" + cache: + directories: + - C:/nuget_http_cache/ + - C:/choco_cache/ + - C:/pip_cache/ + before_install: + - bash --version + - powershell -c "Set-ExecutionPolicy -ExecutionPolicy Unrestricted + -Scope LocalMachine" + - powershell .ci/travis_init.ps1 + + - export NUGET_HTTP_CACHE_PATH=/C/nuget_http_cache + - export CHOCO_CACHE_DIR=/C/choco_cache + - export PIP_CACHE_DIR=/C/pip_cache + + - source .ci/travis_extra_globals.sh + + - export TOXINI_SITEPACKAGES=true + - export VIRTUALENV_NO_PIP=1 + - export VIRTUALENV_NO_SETUPTOOLS=1 + + - export TOXENV="py36-${TOX_TEST_SELECTORS}-${TOX_FEATURES}-win" + + - printenv + + - cp .ci/choco.config $ChocolateyInstall/config/chocolatey.config + + - python .ci/store_env_in_registry.py + - source .ci/refreshenv.sh + + # TODO: Add support for disabling pre-installed vctools which is + # disabled in choco_requirements. Uninstalling vctools fails + install: + - powershell -c ". .ci/Fudge.ps1 install" + - refreshenv + + - taskkill -IM "gpg-agent.exe" || true + + - pip uninstall tox-venv || true + + # Avoid verify_gemfile_dependencies_are_found errors due to + # Gemfile modifications in .ci/deps.ruby-packages.ps1 + - cp Gemfile.win Gemfile + + - cp requirements.txt requirements.orig + - cp tox.ini tox.orig + - "sed -i 's/^envlist.*$/envlist: '$TOXENV/ tox.ini" + + after_script: + - cp tox.orig tox.ini + - cp Gemfile.orig Gemfile - python: 2.7 - stage: unsupported + stage: test-other-versions env: PIP_NO_COMPILE=1 - addons: false before_install: true install: pip install 3to2 before_script: true script: .ci/check_unsupported.sh + - python: 3.3 - stage: unsupported + stage: test-other-versions + dist: trusty env: PIP_NO_COMPILE=1 - addons: false before_install: true install: true before_script: true script: .ci/check_unsupported.sh + - python: 3.6 - stage: moban - addons: false - cache: pip + stage: sentinel before_install: false install: pip install moban before_script: false script: .ci/check_moban.sh after_success: false after_failure: false - if: branch = master AND type = push - - *check_moban + + # Entries generates from `supported_versions` + - stage: sentinel + dist: trusty + language: python + python: 3.6 + addons: + apt: + packages: clang-3.4 + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - docs/_build/ + - $HOME/nltk_data/ + + - stage: test-languages + dist: trusty + language: python + python: 3.5 + addons: + apt: + packages: clang-3.4 + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - docs/_build/ + - $HOME/nltk_data/ + + - stage: test-other-versions + dist: trusty + language: python + python: 3.4.4 + addons: + apt: + packages: clang-3.4 + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - docs/_build/ + - $HOME/nltk_data/ + + - stage: test-languages + dist: precise + language: generic + env: + DIST="precise" + BEARS="apt_get" + addons: + apt: + packages: + - chktex + - cppcheck + - default-jre + - devscripts + - flawfinder + - indent + - libperl-critic-perl + - libxml2-utils + - mono-mcs + - perl + - php5-cli + - ruby + - verilator + - python + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/.pyenv/ + + - stage: test-other-versions + dist: bionic + language: generic + env: + DIST="bionic" + BEARS="apt_get" + DISABLE_BEARS="shellcheck" + + - stage: test-languages + language: csharp + mono: 5.20.1 + + - stage: test-languages + language: r + r: release + cache: + <<: *global_cache + packages: true + + - stage: test-other-versions + language: r + r: devel + cache: + <<: *global_cache + packages: true + + - stage: test-other-versions + language: r + r: oldrel + cache: + <<: *global_cache + packages: true + + - stage: test-languages + language: node_js + node_js: '10' + cache: + <<: *global_cache + npm: true + + - stage: test-other-versions + language: node_js + node_js: 9 + cache: + <<: *global_cache + npm: true + + - stage: test-other-versions + language: node_js + node_js: 8 + cache: + <<: *global_cache + npm: true + + - stage: test-other-versions + language: node_js + node_js: 7 + cache: + <<: *global_cache + npm: true + + - stage: test-other-versions + language: node_js + node_js: 6 + cache: + <<: *global_cache + npm: true + + - stage: test-languages + language: julia + julia: 1.1 + env: JL_PKG=CoalaBears + install: + - julia --color=yes .ci/deps.julia.jl + # Verify compilation works + - julia -e 'import Lint.lintfile' + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/.julia/ + + - stage: test-other-versions + language: julia + julia: 1.0 + env: JL_PKG=CoalaBears + install: + - julia --color=yes .ci/deps.julia.jl + # Verify compilation works + - julia -e 'import Lint.lintfile' + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/.julia/ + + - stage: test-other-versions + language: julia + julia: 0.7.0 + env: JL_PKG=CoalaBears + install: + - julia --color=yes .ci/deps.julia.jl + # Verify compilation works + - julia -e 'import Lint.lintfile' + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/.julia/ + + - stage: test-languages + language: dart + dart: 1.15.0 + + - stage: test-other-versions + language: dart + dart: 1.14.2 + + - stage: test-languages + dist: trusty + language: perl + perl: '5.30' + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/perl5/ + + - stage: test-other-versions + dist: trusty + language: perl + perl: 5.22 + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/perl5/ + + - stage: test-other-versions + dist: trusty + language: perl + perl: 5.18 + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/perl5/ + + - stage: test-other-versions + dist: trusty + language: perl + perl: 5.14 + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/perl5/ + + - stage: test-languages + language: go + go: 1.11 + install: skip + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/.cache/go-build/ + - $HOME/gopath/pkg/mod/ + + - stage: test-other-versions + language: go + go: '1.10' + install: skip + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/.cache/go-build/ + - $HOME/gopath/pkg/mod/ + + - stage: test-languages + dist: trusty + language: ruby + rvm: 2.4 + cache: + <<: *global_cache + bundler: true + + - stage: test-other-versions + dist: trusty + language: ruby + rvm: 2.3 + cache: + <<: *global_cache + bundler: true + + - stage: test-other-versions + dist: trusty + language: ruby + rvm: 2.2 + cache: + <<: *global_cache + bundler: true + + - stage: test-other-versions + dist: trusty + language: ruby + rvm: 2.1 + cache: + <<: *global_cache + bundler: true + + - stage: test-other-versions + dist: trusty + language: ruby + rvm: 2.5 + env: DISABLE_BEARS="csvlint" + cache: + <<: *global_cache + bundler: true + + - stage: test-languages + dist: trusty + language: php + php: 7.2 + + - stage: test-other-versions + dist: trusty + language: php + php: hhvm-3.18 + + - stage: test-other-versions + dist: trusty + language: php + php: 5.5 + + - stage: test-languages + language: scala + scala: 2.11 + + - stage: test-other-versions + language: scala + scala: 2.12.2 + jdk: openjdk8 + + - stage: test-languages + language: java + jdk: openjdk8 + + - stage: test-other-versions + dist: trusty + language: java + jdk: openjdk7 + env: DISABLE_BEARS="tailor" + + - stage: test-other-versions + language: java + jdk: openjdk11 + env: DISABLE_BEARS="languagetool tailor" + + - stage: test-languages + language: lua + env: BEARS="lua" + addons: + apt: + packages: luarocks + cache: + <<: *global_cache + directories: + - $HOME/.local/ + - $HOME/.luarocks/ + + - stage: test-languages + dist: trusty + language: generic + env: + BEARS="opam" + INFER_VERSION="0.7.0" + PATH="$PATH:$HOME/infer-linux64-v$INFER_VERSION/infer/bin" + addons: + apt: + sources: + - avsm + packages: + - camlp4-extra + - ocaml + - opam + + - *moban allow_failures: - - *check_moban - -dist: trusty - -.apt_sources: &apt_sources - - ubuntu-toolchain-r-test - # avsm # OPAM stable - - hvr-ghc # Haskell - - sourceline: # R - deb https://cloud.r-project.org/bin/linux/ubuntu trusty-cran35/ - key_url: - https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x51716619E084DAB9 - - sourceline: # Julia - deb http://ppa.launchpad.net/staticfloat/juliareleases/ubuntu trusty main - key_url: - https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xCF979FFA3D3D3ACC - - sourceline: # astyle - deb http://ppa.launchpad.net/cs50/ppa/ubuntu trusty main - key_url: - https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x5BDA2E974A0E822C - -addons: - apt: - sources: *apt_sources - packages: - - aspcud - - astyle - - cabal-install-1.24 - - chktex - - clang-3.4 - - cppcheck - - devscripts - - flawfinder - - gfortran - - ghc - - happy - - indent - - julia - - libarpack2 - - libblas-dev - - libcolamd2.8.0 - - libfftw3-3 - - liblapack-dev - - libopenblas-base - - libpaper-utils - - libperl-critic-perl - - libumfpack5.6.2 - - libxml2-utils - - luarocks - - mercurial - # menhir - - mono-mcs - # ocaml - # opam - - php-codesniffer - - r-base - - verilator - -cache: - pip: true - directories: - - docs/_build - # Installed language package caches - - ~/.cabal - - ~/.ghc - - ~/.ghc-mod - - ~/R/Library - - ~/.julia - - ~/.luarocks - - $TRAVIS_BUILD_DIR/node_modules - - $TRAVIS_BUILD_DIR/.bundle - - $TRAVIS_BUILD_DIR/vendor - # coala managed data - - ~/nltk_data - # Installed linters - - ~/infer-linux64-v$INFER_VERSION - - ~/.local/ + - *moban + +stage: test env: global: - TERM=dumb - - R_LIB_USER=~/R/Library - - LINTR_COMMENT_BOT=false - - CABAL_VERSION=1.24 - - INFER_VERSION=0.7.0 - - PATH="$HOME/.local/bin:/opt/cabal/$CABAL_VERSION/bin:$PATH:$TRAVIS_BUILD_DIR/node_modules/.bin:$TRAVIS_BUILD_DIR/vendor/bin:$HOME/.cabal/bin:$HOME/infer-linux64-v$INFER_VERSION/infer/bin:$HOME/.local/tailor/tailor-latest/bin:$HOME/.luarocks/bin" + - PATH="$HOME/.local/bin:$PATH" + # These are only needed by Windows + - NUGET_EXE_NO_PROMPT=true + - VIRTUALENV_NO_DOWNLOAD=1 + # Enable to debug tox + # VIRTUALENV_VERBOSE=1 + # This exceeds the travis maximum log length + # PIP_VERBOSE=1 + - PIP_DISABLE_PIP_VERSION_CHECK=1 + - PIP_YES=1 + - FudgeCI=${TRAVIS_BUILD_DIR}/.ci/ + - TOX_FEATURES="check-noskip-codecov" before_install: - # Install latest stable version of Go using gimme - - gimme 1.11.5 > setup_go_root.sh - - source setup_go_root.sh - - nvm install 6.10.2 - # Remove Ruby directive from Gemfile as this image has 2.2.5 - - sed -i '/^ruby/d' Gemfile - - .ci/deps.sh - - .ci/deps.go.sh - - .ci/deps.cabal.sh - - .ci/deps.r.sh - # .ci/deps.opam.sh - - .ci/deps.java.sh + - printenv + - mkdir -p ~/bin ~/.local/bin + - source .ci/travis_extra_globals.sh + + - if [ -z "$TRAVIS_PYTHON_VERSION" ]; then + .ci/deps.python36.sh; + fi + - if [ -d "$HOME/.pyenv/bin" ]; then + export PATH="$HOME/.pyenv/bin:$PATH"; + fi + - hash -r && pyenv versions --bare && python --version + - if [ "${TRAVIS_PYTHON_VERSION/3.4/}" != "$TRAVIS_PYTHON_VERSION" ]; then + pip install pip==9.0.3 setuptools==21.2.2; + fi + + - if [ -f ".ci/deps.$TRAVIS_LANGUAGE.sh" ]; then + bash -e -x ".ci/deps.$TRAVIS_LANGUAGE.sh"; + fi + # https://github.com/coala/coala/issues/3183 - cp requirements.txt requirements.orig - printf '%s\n%s\n%s\n' @@ -182,22 +619,29 @@ before_install: before_script: - mv requirements.orig requirements.txt - - .ci/deps.coala-bears.sh + - if [ -n "$TRAVIS_PYTHON_VERSION" ]; then + python setup.py bdist_wheel && + pip install $(ls ./dist/*.whl)"[alldeps]"; + fi + - if [ -z "$TRAVIS_PYTHON_VERSION" -a "$TRAVIS_OS_NAME" = "linux" ]; then + python -m pip install --upgrade --user -r test-requirements.txt; + fi + - if [ "${TRAVIS_PYTHON_VERSION/3.4/}" != "$TRAVIS_PYTHON_VERSION" ]; then + pip install --upgrade setuptools; + fi script: - - python setup.py bdist_wheel - - pip install $(ls ./dist/*.whl)"[alldeps]" - # Ensure bear requirements are in sync with the bear PipRequirement - - .ci/generate_bear_requirements.py --check --update - - coala --non-interactive - - rm bears/java/InferBear.py tests/java/InferBearTest.py - - rm bears/go/GoReturnsBear.py tests/go/GoReturnsBearTest.py - - pytest - - codecov - - python setup.py docs - -notifications: - email: false + # Ensure metadata files are in sync with the bear metadata in the source + - if [ -n "$TRAVIS_PYTHON_VERSION" ]; then + PYTHONPATH=. .ci/generate_bear_metadata.py --debug --update; + fi + - python -m tox + - if [ -n "$TRAVIS_PYTHON_VERSION" ]; then + python setup.py docs; + fi + - if [ -n "$TRAVIS_PYTHON_VERSION" ]; then + coala --non-interactive; + fi branches: exclude: diff --git a/Fudgefile b/Fudgefile new file mode 100644 index 0000000000..ba5a989ea2 --- /dev/null +++ b/Fudgefile @@ -0,0 +1,111 @@ +{ + "pack": { + "ActivePerl": ".ci/nuspecs/ActivePerl.nuspec", + "adoptopenjdk": ".ci/nuspecs/adoptopenjdk.nuspec", + "golang": ".ci/nuspecs/golang.nuspec", + "hg": ".ci/nuspecs/hg.nuspec", + "maven": ".ci/nuspecs/maven.nuspec", + "nodejs": ".ci/nuspecs/nodejs.nuspec", + "python": ".ci/nuspecs/python.nuspec", + "ruby": ".ci/nuspecs/ruby.nuspec", + "ruby2.devkit": ".ci/nuspecs/ruby2.devkit.nuspec" + }, + "packages": [ + { + "name": "msys2", + "params": "/InstallDir:C:\\msys64 /NoUpdate", + "source": "" + }, + { + "appveyor_id": true, + "name": "hg", + "source": "", + "version": "5.0" + }, + { + "appveyor_id": "python", + "name": "python", + "source": "", + "version": "3.6.8" + }, + { + "appveyor_id": "node", + "name": "nodejs", + "source": "", + "version": "11.13.0" + }, + { + "appveyor_id": "ruby", + "name": "ruby", + "source": "", + "version": "2.5.3.1" + }, + { + "appveyor_id": true, + "name": "ruby2.devkit", + "source": "", + "version": "4.7.2.2013022403" + }, + { + "appveyor_id": "go", + "name": "golang", + "source": "", + "version": "1.9.7" + }, + { + "appveyor_id": "jdk", + "name": "adoptopenjdk", + "source": "", + "version": "8.192" + }, + { + "appveyor_id": true, + "name": "ActivePerl", + "source": "", + "version": "5.24.3.2404" + }, + { + "appveyor_id": true, + "name": "maven", + "source": "", + "version": "3.5.4" + }, + { + "name": "php", + "source": "" + }, + { + "name": "composer", + "source": "" + }, + { + "name": "PSScriptAnalyzer", + "source": "" + }, + { + "name": "astyle", + "source": "" + }, + { + "name": "cppcheck", + "source": "" + }, + { + "name": "xsltproc", + "source": "" + }, + { + "name": "ShellCheck", + "source": "" + } + ], + "scripts": { + "post": { + "install": ". $env:FudgeCI/FudgePostInstall.ps1; Invoke-PostInstall" + }, + "pre": { + "install": ". $env:FudgeCI/FudgeCI.ps1; Invoke-FudgeCI" + } + }, + "source": "https://chocolatey.org/api/v2/" +} diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000000..e889d73de6 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,6 @@ +use ExtUtils::MakeMaker; +WriteMakefile( + NAME => 'Coala::Bears', + VERSION => '0.10', + PREREQ_PM => {Perl::Critic => 1.126}, +); diff --git a/bear-requirements.yaml b/bear-requirements.yaml index 1e25b04766..9f860344ec 100644 --- a/bear-requirements.yaml +++ b/bear-requirements.yaml @@ -1,32 +1,91 @@ # This is an automatically generated file. # And should not be edited by hand. -overrides: coala-build.yaml -gem_requirements: - brakeman: - version: ~>4.1.1 - csvlint: - version: ~>0.4.0 - fasterer: - version: ~>0.4.1 - haml_lint: - version: ~>0.27.0 - puppet-lint: - version: ~>2.1.1 - reek: - version: ~>4.6 - rubocop: - version: ~>0.51.0 - scss_lint: - version: ~>0.56.0 - sqlint: - version: ~>0.1.5 - travis: - version: ~>1.8.8 -r_script_requirements: - formatR: - version: '>1.5' - lintr: - version: '>=1.0.2' +overrides: pm-requirements.yaml +pip_requirements: + HTTPolice: + version: ~=0.5.2 + aenum: + version: ~=2.0.8 + apertium-lint: + version: ~=0.29 + autoflake: + version: ~=0.7 + autopep8: + version: ~=1.2 + bandit: + version: ~=1.2 + bashate: + version: ~=0.5.1 + cmakelint: + version: ~=1.3 + cppclean: + version: ~=0.12.0 + cpplint: + version: ~=1.3 + dennis: + version: ~=0.9 + docutils-ast-writer: + version: ~=0.1.2 + eradicate: + version: ~=0.1.6 + git-url-parse: + version: ~=1.1 + guess-language-spirit: + version: ~=0.5.2 + html-linter: + version: ~=0.4.0 + isort: + version: ~=4.2 + language-check: + version: ~=1.0 + libclang-py3: + version: ~=3.4.0 + lxml: + version: '>=1.0' + memento-client: + version: ~=0.6.1 + munkres3: + version: ~=1.0 + mypy: + version: ==0.590 + nbformat: + version: ~=4.1 + nltk: + version: ~=3.2 + proselint: + version: ~=0.7.0 + pycodestyle: + version: ~=2.2 + pydocstyle: + version: ~=2.0 + pyflakes: + version: ~=2.0.0 + pylint: + version: ~=1.7.2 + pyroma: + version: ~=2.2.0 + pyyaml: + version: ~=3.12 + radon: + version: ==1.4.0 + restructuredtext-lint: + version: ~=1.0 + rstcheck: + version: ~=3.1 + safety: + version: ~=1.8.2 + scspell3k: + version: ~=2.0 + sqlparse: + version: ~=0.2.4 + vim-vint: + version: ~=0.3.12,!=0.3.19 + vulture: + version: ~=0.25.0 + yamllint: + version: ~=1.12.0 + yapf: + version: ~=0.21.0 npm_requirements: alex: version: ~3 @@ -136,91 +195,27 @@ npm_requirements: version: ~12.0.0 write-good: version: ~0.9.1 -pip_requirements: - HTTPolice: - version: ~=0.5.2 - aenum: - version: ~=2.0.8 - apertium-lint: - version: ~=0.29 - autoflake: - version: ~=0.7 - autopep8: - version: ~=1.2 - bandit: - version: ~=1.2 - bashate: - version: ~=0.5.1 - cmakelint: - version: ~=1.3 - cppclean: - version: ~=0.12.0 - cpplint: - version: ~=1.3 - dennis: - version: ~=0.9 - docutils-ast-writer: - version: ~=0.1.2 - eradicate: - version: ~=0.1.6 - git-url-parse: - version: ~=1.1 - guess-language-spirit: - version: ~=0.5.2 - html-linter: - version: ~=0.4.0 - isort: - version: ~=4.2 - language-check: - version: ~=1.0 - libclang-py3: - version: ~=3.4.0 - lxml: - version: '>=1.0' - memento-client: - version: ~=0.6.1 - munkres3: - version: ~=1.0 - mypy: - version: ==0.590 - nbformat: - version: ~=4.1 - nltk: - version: ~=3.2 - proselint: - version: ~=0.7.0 - pycodestyle: - version: ~=2.2 - pydocstyle: - version: ~=2.0 - pyflakes: - version: ~=2.0.0 - pylint: - version: ~=1.7.2 - pyroma: - version: ~=2.2.0 - pyyaml: - version: ~=3.12 - radon: - version: ==1.4.0 - restructuredtext-lint: - version: ~=1.0 - rstcheck: - version: ~=3.1 - safety: - version: ~=1.8.2 - scspell3k: - version: ~=2.0 - sqlparse: - version: ~=0.2.4 - vim-vint: - version: ~=0.3.12,!=0.3.19 - vulture: - version: ~=0.25.0 - yamllint: - version: ~=1.12.0 - yapf: - version: ~=0.21.0 +gem_requirements: + brakeman: + version: ~>4.1.1 + csvlint: + version: ~>0.4.0 + fasterer: + version: ~>0.4.1 + haml_lint: + version: ~>0.27.0 + puppet-lint: + version: ~>2.1.1 + reek: + version: ~>4.6 + rubocop: + version: ~>0.51.0 + scss_lint: + version: ~>0.56.0 + sqlint: + version: ~>0.1.5 + travis: + version: ~>1.8.8 composer_requirements: phpmd/phpmd: version: ~2.6.0 @@ -233,3 +228,189 @@ cabal_requirements: version: ==5.6.0.0 hlint: version: ==1.9.27 +r_script_requirements: + formatR: + lintr: +distro_requirements: + astyle: + packages: + apt_get: astyle + dnf: astyle + chktex: + packages: + apt_get: chktex + brew: chktex + dnf: chktex + pacman: chktex + pkg: chktex + portage: chktex + xbps: chktex + yum: chktex + zypper: texlive-chktex + cppcheck: + packages: + apt_get: cppcheck + brew: cppcheck + dnf: cppcheck + pacman: cppcheck + pkg: cppcheck + portage: cppcheck + xbps: cppcheck + yum: cppcheck + zypper: cppcheck + dart: + packages: + brew: dart + default-jre: + packages: + apt_get: default-jre + devscripts: + packages: + apt_get: devscripts + dnf: licensecheck + portage: + zypper: devscripts + flawfinder: + packages: + apt_get: flawfinder + brew: flawfinder + dnf: flawfinder + pacman: flawfinder + pkg: flawfinder + portage: flawfinder + xbps: flawfinder + yum: flawfinder + zypper: flawfinder + ghc-mod: + packages: + apt_get: ghc-mod + brew: ghc-mod + dnf: ghc-mod + pacman: ghc-mod + pkg: ghc-mod + portage: ghc-mod + xbps: ghc-mod + yum: ghc-mod + zypper: ghc-mod + hlint: + packages: + apt_get: hlint + indent: + packages: + apt_get: indent + brew: indent + dnf: indent + pacman: indent + pkg: indent + portage: indent + xbps: indent + yum: indent + zypper: indent + libperl-critic-perl: + packages: + apt_get: libperl-critic-perl + brew: + dnf: perl-Perl-Critic + portage: dev-perl/Perl-Critic + xbps: + yum: perl-Perl-Critic + zypper: perl-Perl-Critic + libxml2: + packages: + apt_get: libxml2-utils + brew: libxml2 + dnf: libxml2 + pacman: libxml2 + pkg: libxml2 + portage: dev-libs/libxml2 + xbps: libxml2 + yum: libxml2 + zypper: libxml2 + mono: + packages: + apt_get: mono-mcs + brew: mono + dnf: mono + pacman: mono + pkg: mono + portage: dev-lang/mono + xbps: mono + yum: mono + zypper: mono + perl: + packages: + apt_get: perl + brew: perl + dnf: perl + pacman: perl + pkg: perl + portage: perl + xbps: perl + yum: perl + zypper: perl + php-cli: + packages: + apt_get: php-cli + php-codesniffer: + packages: + apt_get: php-codesniffer + zypper: php-pear-php_codesniffer + phpmd: + packages: + apt_get: phpmd + dnf: php-phpmd-PHP-PMD + r-base: + packages: + apt_get: r-base + version: '>=3.1.1' + r-cran-formatr: + packages: + apt_get: r-cran-formatr + zypper: R-formatR + ruby: + packages: + apt_get: ruby + brew: ruby + dnf: ruby + pacman: ruby + pkg: ruby + portage: ruby + xbps: ruby + yum: ruby + zypper: ruby + shellcheck: + packages: + apt_get: shellcheck + brew: shellcheck + dnf: shellcheck + pacman: shellcheck + pkg: shellcheck + portage: shellcheck + xbps: shellcheck + yum: shellcheck + zypper: shellcheck + verilator: + packages: + apt_get: verilator + brew: + dnf: verilator + portage: + yum: verilator + zypper: verilator +julia_requirements: + Lint: +go_requirements: + github.com/BurntSushi/toml/cmd/tomlv: + github.com/golang/lint/golint: + github.com/kisielk/errcheck: + golang.org/cmd/gofmt: + golang.org/cmd/vet: + golang.org/x/tools/cmd/goimports: + golang.org/x/tools/cmd/gotype: + sourcegraph.com/sqs/goreturns: +luarocks_requirements: + luacheck: +conda_requirements: + ruby: + version: '>=2.2.3' +exe_requirements: {} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000000..d14f37262a --- /dev/null +++ b/composer.json @@ -0,0 +1,6 @@ +{ + "require-dev": { + "phpmd/phpmd": "^2.6", + "squizlabs/php_codesniffer": "^3.4" + } +} diff --git a/package.json b/package.json index 00f1517754..81ffb7fccd 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "stylelint": "~7", "stylint": "~1.5.9", "textlint": "~7.3.0", - "textlint-plugin-asciidoc-loose": "~1.0.1", + "textlint-plugin-asciidoctor": "~1.0.3", "textlint-plugin-html": "~0.1.5", "textlint-plugin-review": "~0.3.3", "textlint-plugin-rst": "~0.1.1", diff --git a/setup.cfg b/setup.cfg index 1c7ee45201..18c0be3a5c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -60,6 +60,7 @@ source = omit = tests/* setup.py + .ci/store_env_in_registry.py .ci/* [coverage:report] diff --git a/test-requirements.txt b/test-requirements.txt index 1643a4eb9a..fad513360a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -28,4 +28,11 @@ ipdb~=0.11 pip<10 six>=1.11.0 wheel~=0.29 +pbr!=2.1.0,>=2.0.0 +pytest-error-for-skips +git+https://github.com/krkd/pytest-cov-threshold#egg=pytest-cov-threshold ; python_version > '3.4' twine~=1.7.4 +tox~=3.12.0 +tox-travis +tox-backticks +tox-pyenv diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000000..3b72e9ed6d --- /dev/null +++ b/tox.ini @@ -0,0 +1,114 @@ +[tox] +envlist = py{34,35,36,37}-{all,pip,clang,npm,gem,go,perl,php,cabal,java,java7,java8,scala,elm,r,dart,julia,lua,infer,opam,apt_get,adhoc,disabled,win,swift,mono}-{list,check,collectonly,skip,noskip}-codecov +minversion = 3.4 + +[travis:env] +TRAVIS = + true: codecov +TRAVIS_LANGUAGE = + python: pip-noskip + node_js: py36-npm-noskip + ruby: py36-gem-noskip + haskell: py36-cabal-noskip + go: py36-go-noskip + perl: py36-perl-noskip + php: py36-php-noskip + scala: py36-scala-noskip + elm: py36-elm-noskip + r: py36-r-noskip + dart: py36-dart-noskip + julia: py36-julia-noskip + objective_c: py36-swift-noskip + swift: py36-swift-noskip + objectivec: py36-swift-noskip + objective-c: py36-swift-noskip + csharp: py36-mono-noskip + minimal,generic: py36-adhoc-noskip +TRAVIS_JDK_VERSION = + oraclejdk11: py36-java-skip + oraclejdk9: py36-java-skip + oraclejdk8: py36-java8-noskip + openjdk11: py36-java-skip + openjdk10: py36-java-skip + openjdk9: py36-java-skip + openjdk8: py36-java8-noskip + openjdk7: py36-java7-noskip +# apt_get is the only group allowed to skip +BEARS = + apt_get: py36-apt_get-skip + lua: py36-lua-noskip + infer: py36-infer-noskip + opam: py36-opam-noskip + adhoc: py36-adhoc-noskip + clang: py36-clang-noskip + disabled: py36-disabled-noskip + +[testenv] +passenv = + HOME + PATH + CI CI_* + TRAVIS TRAVIS_* + APPVEYOR APPVEYOR_* + TOX_* + PIP_* + VIRTUALENV_* + LOCALAPPDATA + GEM_HOME + GEM_PATH + BUNDLE_PATH + BUNDLE_BIN + JULIA_PROJECT + GOROOT + GOPATH + BEARS + BEAR_LIST + DISABLE_BEARS + R_PROFILE + R_LIBS_USER + R_LIBS_SITE + _R_CHECK_CRAN_INCOMING_ + NOT_CRAN + R_PROFILE +pip_version = 9.0.1 +alwayscopy = true +skipsdist = true +sitepackages={env:TOXINI_SITEPACKAGES:False} +skip_install = true +list_dependencies_command = python -m pip freeze --local +whitelist_externals = + pytest +deps = + git+https://github.com/coala/coala#egg=coala + pip: -rbear-requirements.txt + # aenum is needed during test collection + !pip: aenum + # PyYAML is needed for two gem bears, but it is already included for pip + gem-!pip: PyYAML + npm-!pip: docutils-ast-writer~=0.1.2 + {apt_get,clang,mono,adhoc}-!pip: libclang-py3~=3.4.0 + clang-!pip: munkres3~=1.0 + java{7,8}-!pip: language-check~=1.0 + java{7,8}-!pip: guess-language-spirit~=0.5.2 + -rtest-requirements.txt + # pytest-cov-threshold is incompatible with py34 + !py34: git+https://github.com/krkd/pytest-cov-threshold + noskip: pytest-error-for-skips +setenv = + LINTR_COMMENT_BOT=false + ENVNAMEBEARS=`python .ci/get_tests.py {envname}` + adhoc: ADHOCBEARS=`python .ci/get_tests.py {env:BEAR_LIST}` + disabled: DISABLEDBEARS=`python .ci/get_tests.py --disabled {env:BEAR_LIST}` + SELECTED={env:ENVNAMEBEARS:} {env:ADHOCBEARS:} {env:DISABLEDBEARS:} + noskip: PYTEST_ARGS=--error-for-skips + py34-noskip: PYTEST_ARGS=--error-for-skips -k 'not test_valid_async' + win-noskip: PYTEST_ARGS=--error-for-skips -k 'not test_language_french and not test_valid_async' + collectonly,list: PYTEST_ARGS=--collect-only + codecov: CODECOV_FLAGS=`python .ci/get_codecov_tags.py {envname}` +commands = + check,list,all: python .ci/get_bears.py --missing {env:SELECTED} + !py34,!apt_get: python .ci/generate_coverage_thresholds.py {posargs:{env:SELECTED}} + py34,apt_get: python .ci/generate_coverage_thresholds.py none + !list: pytest {env:PYTEST_ARGS:} --cov --cov-fail-under=0 --continue-on-collection-errors --cov-report term-missing:skip-covered --deselect=requirements.txt {posargs:{env:SELECTED}} +commands_post = + codecov: codecov --name={envname} --flags={env:CODECOV_FLAGS}