Skip to content

[ci] check PowerShell scripts with PSScriptAnalyzer (part 1) #6704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .ci/lint-powershell.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
$settings = @{
Severity = @(
'Information',
'Warning',
'Error'
)
IncludeDefaultRules = $true
# Additional rules that are disabled by default
Rules = @{
PSAvoidExclaimOperator = @{
Enable = $true
}
PSAvoidLongLines = @{
Enable = $true
MaximumLineLength = 120
}
PSAvoidSemicolonsAsLineTerminators = @{
Enable = $true
}
PSPlaceCloseBrace = @{
Enable = $true
NoEmptyLineBefore = $true
IgnoreOneLineBlock = $true
NewLineAfter = $false
}
PSPlaceOpenBrace = @{
Enable = $true
OnSameLine = $true
NewLineAfter = $true
IgnoreOneLineBlock = $true
}
PSUseConsistentIndentation = @{
Enable = $true
IndentationSize = 4
PipelineIndentation = 'IncreaseIndentationAfterEveryPipeline'
Kind = 'space'
}
PSUseConsistentWhitespace = @{
Enable = $true
CheckInnerBrace = $true
CheckOpenBrace = $true
CheckOpenParen = $true
CheckOperator = $true
CheckSeparator = $true
CheckPipe = $true
CheckPipeForRedundantWhitespace = $true
CheckParameter = $true
IgnoreAssignmentOperatorInsideHashTable = $false
}
PSUseCorrectCasing = @{
Enable = $true
}
}
}

Invoke-ScriptAnalyzer -Path "$env:BUILD_DIRECTORY/.ci" -Recurse -EnableExit -Settings $settings
1 change: 0 additions & 1 deletion .ci/lint-r-code.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

loadNamespace("lintr")

args <- commandArgs(
Expand Down
97 changes: 50 additions & 47 deletions .ci/test-r-package-windows.ps1
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Download a file and retry upon failure. This looks like
# an infinite loop but CI-level timeouts will kill it
function Download-File-With-Retries {
param(
[string]$url,
[string]$destfile
)
$ProgressPreference = "SilentlyContinue" # progress bar bug extremely slows down download speed
do {
Write-Output "Downloading ${url}"
sleep 5;
Invoke-WebRequest -Uri $url -OutFile $destfile
} while(!$?);
function Get-File-With-Tenacity {
param(
[Parameter(Mandatory = $true)][string]$url,
[Parameter(Mandatory = $true)][string]$destfile
)
$ProgressPreference = "SilentlyContinue" # progress bar bug extremely slows down download speed
do {
Write-Output "Downloading ${url}"
sleep 5
Invoke-WebRequest -Uri $url -OutFile $destfile
} while (-not $?)
}

# External utilities like R.exe / Rscript.exe writing to stderr (even for harmless
Expand All @@ -20,20 +20,23 @@ function Download-File-With-Retries {
# Using standard PowerShell redirection does not work to avoid these errors.
# This function uses R's built-in redirection mechanism, sink(). Any place where
# this function is used is a command that writes harmless messages to stderr
function Run-R-Code-Redirect-Stderr {
param(
[string]$rcode
)
$decorated_code = "out_file <- file(tempfile(), open = 'wt'); sink(out_file, type = 'message'); $rcode; sink()"
Rscript --vanilla -e $decorated_code
function Invoke-R-Code-Redirect-Stderr {
param(
[Parameter(Mandatory = $true)][string]$rcode
)
$decorated_code = "out_file <- file(tempfile(), open = 'wt'); sink(out_file, type = 'message'); $rcode; sink()"
Rscript --vanilla -e $decorated_code
}

# Remove all items matching some pattern from PATH environment variable
function Remove-From-Path {
param(
[string]$pattern_to_remove
)
$env:PATH = ($env:PATH.Split(';') | Where-Object { $_ -notmatch "$pattern_to_remove" }) -join ';'
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory = $true)][string]$pattern_to_remove
)
if ($PSCmdlet.ShouldProcess($env:PATH, "Removing ${pattern_to_remove}")) {
$env:PATH = ($env:PATH.Split(';') | Where-Object { $_ -notmatch "$pattern_to_remove" }) -join ';'
}
}

# remove some details that exist in the GitHub Actions images which might
Expand Down Expand Up @@ -87,7 +90,7 @@ if ($env:R_MAJOR_VERSION -eq "3") {
$env:R_WINDOWS_VERSION = "4.3.1"
} else {
Write-Output "[ERROR] Unrecognized R version: $env:R_VERSION"
Check-Output $false
Assert-Output $false
}
$env:CMAKE_VERSION = "3.30.0"

Expand Down Expand Up @@ -120,29 +123,29 @@ tzutil /s "GMT Standard Time"

# download R, RTools and CMake
Write-Output "Downloading R, Rtools and CMake"
Download-File-With-Retries -url "$env:CRAN_MIRROR/bin/windows/base/old/$env:R_WINDOWS_VERSION/R-$env:R_WINDOWS_VERSION-win.exe" -destfile "R-win.exe"
Download-File-With-Retries -url "https://github.com/microsoft/LightGBM/releases/download/v2.0.12/$env:RTOOLS_EXE_FILE" -destfile "Rtools.exe"
Download-File-With-Retries -url "https://github.com/Kitware/CMake/releases/download/v$env:CMAKE_VERSION/cmake-$env:CMAKE_VERSION-windows-x86_64.zip" -destfile "$env:CMAKE_PATH/cmake.zip"
Get-File-With-Tenacity -url "$env:CRAN_MIRROR/bin/windows/base/old/$env:R_WINDOWS_VERSION/R-$env:R_WINDOWS_VERSION-win.exe" -destfile "R-win.exe"
Get-File-With-Tenacity -url "https://github.com/microsoft/LightGBM/releases/download/v2.0.12/$env:RTOOLS_EXE_FILE" -destfile "Rtools.exe"
Get-File-With-Tenacity -url "https://github.com/Kitware/CMake/releases/download/v$env:CMAKE_VERSION/cmake-$env:CMAKE_VERSION-windows-x86_64.zip" -destfile "$env:CMAKE_PATH/cmake.zip"

# Install R
Write-Output "Installing R"
Start-Process -FilePath R-win.exe -NoNewWindow -Wait -ArgumentList "/VERYSILENT /DIR=$env:R_LIB_PATH/R /COMPONENTS=main,x64,i386" ; Check-Output $?
Start-Process -FilePath R-win.exe -NoNewWindow -Wait -ArgumentList "/VERYSILENT /DIR=$env:R_LIB_PATH/R /COMPONENTS=main,x64,i386" ; Assert-Output $?
Write-Output "Done installing R"

Write-Output "Installing Rtools"
Start-Process -FilePath Rtools.exe -NoNewWindow -Wait -ArgumentList "/VERYSILENT /SUPPRESSMSGBOXES /DIR=$RTOOLS_INSTALL_PATH" ; Check-Output $?
Start-Process -FilePath Rtools.exe -NoNewWindow -Wait -ArgumentList "/VERYSILENT /SUPPRESSMSGBOXES /DIR=$RTOOLS_INSTALL_PATH" ; Assert-Output $?
Write-Output "Done installing Rtools"

Write-Output "Installing CMake"
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory("$env:CMAKE_PATH/cmake.zip", "$env:CMAKE_PATH") ; Check-Output $?
[System.IO.Compression.ZipFile]::ExtractToDirectory("$env:CMAKE_PATH/cmake.zip", "$env:CMAKE_PATH") ; Assert-Output $?
# Remove old CMake shiped with RTools
Remove-Item "$env:RTOOLS_MINGW_BIN/cmake.exe" -Force -ErrorAction Ignore
Write-Output "Done installing CMake"

Write-Output "Installing dependencies"
$packages = "c('data.table', 'jsonlite', 'knitr', 'markdown', 'Matrix', 'processx', 'R6', 'RhpcBLASctl', 'testthat'), dependencies = c('Imports', 'Depends', 'LinkingTo')"
Run-R-Code-Redirect-Stderr "options(install.packages.check.source = 'no'); install.packages($packages, repos = '$env:CRAN_MIRROR', type = 'binary', lib = '$env:R_LIB_PATH', Ncpus = parallel::detectCores())" ; Check-Output $?
Invoke-R-Code-Redirect-Stderr "options(install.packages.check.source = 'no'); install.packages($packages, repos = '$env:CRAN_MIRROR', type = 'binary', lib = '$env:R_LIB_PATH', Ncpus = parallel::detectCores())" ; Assert-Output $?

Write-Output "Building R-package"

Expand All @@ -163,9 +166,9 @@ if ($env:COMPILER -ne "MSVC") {
$env:BUILD_R_FLAGS = "'--skip-install'"
} else {
Write-Output "[ERROR] Unrecognized toolchain: $env:TOOLCHAIN"
Check-Output $false
Assert-Output $false
}
Run-R-Code-Redirect-Stderr "commandArgs <- function(...){$env:BUILD_R_FLAGS}; source('build_r.R')"; Check-Output $?
Invoke-R-Code-Redirect-Stderr "commandArgs <- function(...){$env:BUILD_R_FLAGS}; source('build_r.R')"; Assert-Output $?
} elseif ($env:R_BUILD_TYPE -eq "cran") {
# NOTE: gzip and tar are needed to create a CRAN package on Windows, but
# some flavors of tar.exe can fail in some settings on Windows.
Expand All @@ -174,7 +177,7 @@ if ($env:COMPILER -ne "MSVC") {
if ($env:R_MAJOR_VERSION -eq "3") {
$env:PATH = "C:\msys64\usr\bin;" + $env:PATH
}
Run-R-Code-Redirect-Stderr "result <- processx::run(command = 'sh', args = 'build-cran-package.sh', echo = TRUE, windows_verbatim_args = FALSE, error_on_status = TRUE)" ; Check-Output $?
Invoke-R-Code-Redirect-Stderr "result <- processx::run(command = 'sh', args = 'build-cran-package.sh', echo = TRUE, windows_verbatim_args = FALSE, error_on_status = TRUE)" ; Assert-Output $?
Remove-From-Path ".*msys64.*"
# Test CRAN source .tar.gz in a directory that is not this repo or below it.
# When people install.packages('lightgbm'), they won't have the LightGBM
Expand All @@ -193,31 +196,31 @@ if ($env:COMPILER -ne "MSVC") {
} else {
$check_args = "c('CMD', 'check', '--no-multiarch', '--as-cran', '--run-donttest', '$PKG_FILE_NAME')"
}
Run-R-Code-Redirect-Stderr "result <- processx::run(command = 'R.exe', args = $check_args, echo = TRUE, windows_verbatim_args = FALSE, error_on_status = TRUE)" ; $check_succeeded = $?
Invoke-R-Code-Redirect-Stderr "result <- processx::run(command = 'R.exe', args = $check_args, echo = TRUE, windows_verbatim_args = FALSE, error_on_status = TRUE)" ; $check_succeeded = $?

Write-Output "R CMD check build logs:"
$INSTALL_LOG_FILE_NAME = "lightgbm.Rcheck\00install.out"
Get-Content -Path "$INSTALL_LOG_FILE_NAME"

Check-Output $check_succeeded
Assert-Output $check_succeeded

Write-Output "Looking for issues with R CMD check results"
if (Get-Content "$LOG_FILE_NAME" | Select-String -Pattern "NOTE|WARNING|ERROR" -CaseSensitive -Quiet) {
echo "NOTEs, WARNINGs, or ERRORs have been found by R CMD check"
Check-Output $False
Assert-Output $False
}

} else {
$INSTALL_LOG_FILE_NAME = "$env:BUILD_SOURCESDIRECTORY\00install_out.txt"
Run-R-Code-Redirect-Stderr "source('build_r.R')" 1> $INSTALL_LOG_FILE_NAME ; $install_succeeded = $?
Invoke-R-Code-Redirect-Stderr "source('build_r.R')" 1> $INSTALL_LOG_FILE_NAME ; $install_succeeded = $?
Write-Output "----- build and install logs -----"
Get-Content -Path "$INSTALL_LOG_FILE_NAME"
Write-Output "----- end of build and install logs -----"
Check-Output $install_succeeded
Assert-Output $install_succeeded
# some errors are not raised above, but can be found in the logs
if (Get-Content "$INSTALL_LOG_FILE_NAME" | Select-String -Pattern "ERROR" -CaseSensitive -Quiet) {
echo "ERRORs have been found installing lightgbm"
Check-Output $False
Assert-Output $False
}
}

Expand All @@ -231,7 +234,7 @@ if ($env:TOOLCHAIN -ne "MSVC") {
}
if ($checks_cnt -eq 0) {
Write-Output "Wrong R version was found (expected '$env:R_WINDOWS_VERSION'). Check the build logs."
Check-Output $False
Assert-Output $False
}

# Checking that we actually got the expected compiler. The R-package has some logic
Expand All @@ -241,7 +244,7 @@ if ($env:R_BUILD_TYPE -eq "cmake") {
$checks = Select-String -Path "${INSTALL_LOG_FILE_NAME}" -Pattern "Check for working CXX compiler.*$env:COMPILER"
if ($checks.Matches.length -eq 0) {
Write-Output "The wrong compiler was used. Check the build logs."
Check-Output $False
Assert-Output $False
}
}

Expand All @@ -251,7 +254,7 @@ if (($env:COMPILER -eq "MINGW") -and ($env:R_BUILD_TYPE -eq "cmake")) {
$checks = Select-String -Path "${INSTALL_LOG_FILE_NAME}" -Pattern "Trying to build with.*$env:TOOLCHAIN"
if ($checks.Matches.length -eq 0) {
Write-Output "The wrong toolchain was used. Check the build logs."
Check-Output $False
Assert-Output $False
}
}

Expand All @@ -267,7 +270,7 @@ if ($env:R_BUILD_TYPE -eq "cran") {
}
if ($checks_cnt -eq 0) {
Write-Output "MM_PREFETCH preprocessor definition wasn't used. Check the build logs."
Check-Output $False
Assert-Output $False
}

# Checking that MM_MALLOC preprocessor definition is actually used in CI builds.
Expand All @@ -282,25 +285,25 @@ if ($env:R_BUILD_TYPE -eq "cran") {
}
if ($checks_cnt -eq 0) {
Write-Output "MM_MALLOC preprocessor definition wasn't used. Check the build logs."
Check-Output $False
Assert-Output $False
}

# Checking that OpenMP is actually used in CMake builds.
if ($env:R_BUILD_TYPE -eq "cmake") {
$checks = Select-String -Path "${INSTALL_LOG_FILE_NAME}" -Pattern ".*Found OpenMP: TRUE.*"
if ($checks.Matches.length -eq 0) {
Write-Output "OpenMP wasn't found. Check the build logs."
Check-Output $False
Assert-Output $False
}
}

if ($env:COMPILER -eq "MSVC") {
Write-Output "Running tests with testthat.R"
cd R-package/tests
# NOTE: using Rscript.exe intentionally here, instead of Run-R-Code-Redirect-Stderr,
# because something about the interaction between Run-R-Code-Redirect-Stderr
# NOTE: using Rscript.exe intentionally here, instead of Invoke-R-Code-Redirect-Stderr,
# because something about the interaction between Invoke-R-Code-Redirect-Stderr
# and testthat results in failing tests not exiting with a non-0 exit code.
Rscript.exe --vanilla "testthat.R" ; Check-Output $?
Rscript.exe --vanilla "testthat.R" ; Assert-Output $?
}

Write-Output "No issues were found checking the R-package"
Loading
Loading