diff --git a/images/linux/scripts/SoftwareReport/SoftwareReport.Android.psm1 b/images/linux/scripts/SoftwareReport/SoftwareReport.Android.psm1
new file mode 100644
index 000000000..7f34143a9
--- /dev/null
+++ b/images/linux/scripts/SoftwareReport/SoftwareReport.Android.psm1
@@ -0,0 +1,147 @@
+function Split-TableRowByColumns {
+ param(
+ [string] $Row
+ )
+ return $Row.Split("|") | ForEach-Object { $_.trim() }
+}
+
+function Get-AndroidSDKRoot {
+ return "/usr/local/lib/android/sdk"
+}
+
+function Get-AndroidSDKManagerPath {
+ $androidSDKDir = Get-AndroidSDKRoot
+ return Join-Path $androidSDKDir "tools" "bin" "sdkmanager"
+}
+
+function Get-AndroidInstalledPackages {
+ $androidSDKManagerPath = Get-AndroidSDKManagerPath
+ $androidSDKManagerList = Invoke-Expression "$androidSDKManagerPath --list --include_obsolete"
+ $androidInstalledPackages = @()
+ foreach($packageInfo in $androidSDKManagerList) {
+ if($packageInfo -Match "Available Packages:") {
+ break
+ }
+
+ $androidInstalledPackages += $packageInfo
+ }
+ return $androidInstalledPackages
+}
+
+
+function Build-AndroidTable {
+ $packageInfo = Get-AndroidInstalledPackages
+ return @(
+ @{
+ "Package" = "Android SDK Platform-Tools"
+ "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android SDK Platform-Tools"
+ },
+ @{
+ "Package" = "Android SDK Tools"
+ "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android SDK Tools"
+ },
+ @{
+ "Package" = "Android SDK Platforms"
+ "Version" = Get-AndroidPlatformVersions -PackageInfo $packageInfo
+ },
+ @{
+ "Package" = "Android SDK Build-tools"
+ "Version" = Get-AndroidBuildToolVersions -PackageInfo $packageInfo
+ },
+ @{
+ "Package" = "Google APIs"
+ "Version" = Get-AndroidGoogleAPIsVersions -PackageInfo $packageInfo
+ },
+ @{
+ "Package" = "Android Support Repository"
+ "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android Support Repository"
+ },
+ @{
+ "Package" = "Google Play services"
+ "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Google Play services"
+ },
+ @{
+ "Package" = "Google Repository"
+ "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Google Repository"
+ },
+ @{
+ "Package" = "SDK Patch Applier v4"
+ "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "SDK Patch Applier v4"
+ },
+ @{
+ "Package" = "CMake"
+ "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "cmake"
+ },
+ @{
+ "Package" = "NDK"
+ "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "ndk-bundle"
+ }
+ ) | Where-Object { $_.Version } | ForEach-Object {
+ [PSCustomObject] @{
+ "Package Name" = $_.Package
+ "Version" = $_.Version
+ }
+ }
+}
+
+function Get-AndroidPackageVersions {
+ param (
+ [Parameter(Mandatory)]
+ [object] $PackageInfo,
+ [Parameter(Mandatory)]
+ [object] $MatchedString
+ )
+
+ $versions = $packageInfo | Where-Object { $_ -Match $MatchedString } | ForEach-Object {
+ $packageInfoParts = Split-TableRowByColumns $_
+ return $packageInfoParts[1]
+ }
+ return ($versions -Join "
")
+}
+
+function Get-AndroidPlatformVersions {
+ param (
+ [Parameter(Mandatory)]
+ [object] $PackageInfo
+ )
+
+ $versions = $packageInfo | Where-Object { $_ -Match "Android SDK Platform " } | ForEach-Object {
+ $packageInfoParts = Split-TableRowByColumns $_
+ $revision = $packageInfoParts[1]
+ $version = $packageInfoParts[0].split(";")[1]
+ return "$version, (rev $revision)"
+ }
+ [array]::Reverse($versions)
+ return ($versions -Join "
")
+}
+
+function Get-AndroidBuildToolVersions {
+ param (
+ [Parameter(Mandatory)]
+ [object] $PackageInfo
+ )
+
+ $versions = $packageInfo | Where-Object { $_ -Match "Android SDK Build-Tools" } | ForEach-Object {
+ $packageInfoParts = Split-TableRowByColumns $_
+ return $packageInfoParts[1]
+ }
+ $groupVersions = @()
+ $versions | ForEach-Object {
+ $majorVersion = $_.Split(".")[0]
+ $groupVersions += $versions | Where-Object { $_.StartsWith($majorVersion) } | Join-String -Separator " "
+ }
+ return ($groupVersions | Sort-Object -Descending -Unique | Join-String -Separator "
")
+}
+
+function Get-AndroidGoogleAPIsVersions {
+ param (
+ [Parameter(Mandatory)]
+ [object] $PackageInfo
+ )
+
+ $versions = $packageInfo | Where-Object { $_ -Match "Google APIs" } | ForEach-Object {
+ $packageInfoParts = Split-TableRowByColumns $_
+ return $packageInfoParts[0].split(";")[1]
+ }
+ return ($versions -Join "
")
+}
\ No newline at end of file
diff --git a/images/linux/scripts/SoftwareReport/SoftwareReport.Browsers.psm1 b/images/linux/scripts/SoftwareReport/SoftwareReport.Browsers.psm1
new file mode 100644
index 000000000..96d05ee08
--- /dev/null
+++ b/images/linux/scripts/SoftwareReport/SoftwareReport.Browsers.psm1
@@ -0,0 +1,19 @@
+function Get-ChromeVersion {
+ $googleChromeVersion = google-chrome --version | Take-Part -Part 2
+ return "Google Chrome $googleChromeVersion"
+}
+
+function Get-ChromeDriverVersion {
+ $chromeDriverVersion = chromedriver --version | Take-Part -Part 1
+ return "ChromeDriver $chromeDriverVersion"
+}
+
+function Get-FirefoxVersion {
+ $firefoxVersion = firefox --version
+ return $firefoxVersion
+}
+
+function Get-GeckodriverVersion {
+ $geckodriverVersion = geckodriver --version | Select-Object -First 1 | Take-Part -Part 1
+ return "Geckodriver $geckodriverVersion"
+}
\ No newline at end of file
diff --git a/images/linux/scripts/SoftwareReport/SoftwareReport.CachedTools.psm1 b/images/linux/scripts/SoftwareReport/SoftwareReport.CachedTools.psm1
new file mode 100644
index 000000000..536b31255
--- /dev/null
+++ b/images/linux/scripts/SoftwareReport/SoftwareReport.CachedTools.psm1
@@ -0,0 +1,65 @@
+function Get-ToolcacheRubyVersions {
+ $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Ruby"
+ return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ }
+}
+
+function Get-ToolcachePythonVersions {
+ $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Python"
+ return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ }
+}
+
+function Get-ToolcachePyPyVersions {
+ $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "PyPy"
+ Get-ChildItem -Path $toolcachePath -Name | Sort-Object { [Version] $_ } | ForEach-Object {
+ $pypyRootPath = Join-Path $toolcachePath $_ "x64"
+ [string]$pypyVersionOutput = & "$pypyRootPath/bin/python" -c "import sys;print(sys.version)"
+ $pypyVersionOutput -match "^([\d\.]+) \(.+\) \[PyPy ([\d\.]+) .+]$" | Out-Null
+ return "{0} [PyPy {1}]" -f $Matches[1], $Matches[2]
+ }
+}
+
+function Get-ToolcacheNodeVersions {
+ $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "node"
+ return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ }
+}
+
+function Get-ToolcacheGoVersions {
+ $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "go"
+ return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ }
+}
+
+function Get-ToolcacheBoostVersions {
+ $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "boost"
+ if (-not (Test-Path $toolcachePath)) {
+ return @()
+ }
+ return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ }
+}
+
+function Build-CachedToolsSection {
+ $output = ""
+
+ $output += New-MDHeader "Ruby" -Level 4
+ $output += New-MDList -Lines (Get-ToolcacheRubyVersions) -Style Unordered
+
+ $output += New-MDHeader "Python" -Level 4
+ $output += New-MDList -Lines (Get-ToolcachePythonVersions) -Style Unordered
+
+ $output += New-MDHeader "PyPy" -Level 4
+ $output += New-MDList -Lines (Get-ToolcachePyPyVersions) -Style Unordered
+
+ $output += New-MDHeader "Node.js" -Level 4
+ $output += New-MDList -Lines (Get-ToolcacheNodeVersions) -Style Unordered
+
+ $output += New-MDHeader "Go" -Level 4
+ $output += New-MDList -Lines (Get-ToolcacheGoVersions) -Style Unordered
+
+ $boostVersions = Get-ToolcacheBoostVersions
+ if ($boostVersions.Count -gt 0) {
+ $output += New-MDHeader "Boost" -Level 4
+ $output += New-MDList -Lines $boostVersions -Style Unordered
+ }
+
+
+ return $output
+}
\ No newline at end of file
diff --git a/images/linux/scripts/SoftwareReport/SoftwareReport.Common.psm1 b/images/linux/scripts/SoftwareReport/SoftwareReport.Common.psm1
new file mode 100644
index 000000000..1d96f5550
--- /dev/null
+++ b/images/linux/scripts/SoftwareReport/SoftwareReport.Common.psm1
@@ -0,0 +1,296 @@
+Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Helpers.psm1") -DisableNameChecking
+
+function Get-OSName {
+ lsb_release -ds
+}
+
+function Get-CPPVersions {
+ $cppVersions = & apt list --installed | Where-Object { $_ -match "g\+\+-\d+"} | ForEach-Object {
+ $_ -match "now (?\d+\.\d+\.\d+)-" | Out-Null
+ $Matches.version
+ }
+ return "GNU C++ " + ($cppVersions -Join ", ")
+}
+
+function Get-FortranVersions {
+ $fortranVersions = & apt list --installed | Where-Object { $_ -match "^gfortran-\d+"} | ForEach-Object {
+ $_ -match "now (?\d+\.\d+\.\d+)-" | Out-Null
+ $Matches.version
+ }
+ return "GNU Fortran " + ($fortranVersions -Join ", ")
+}
+
+function Get-ClangVersions {
+ $clangVersions = @()
+ $clangVersions = & apt list --installed | Where-Object { $_ -match "^clang-\d+"} | ForEach-Object {
+ $clangCommand = ($_ -Split "/")[0]
+ Invoke-Expression "$clangCommand --version" | Where-Object { $_ -match "clang version" } | ForEach-Object {
+ $_ -match "clang version (?\d+\.\d+\.\d+)-" | Out-Null
+ $Matches.version
+ }
+ }
+ return "Clang " + ($clangVersions -Join ", ")
+}
+
+function Get-ErlangVersion {
+ $result = Get-CommandResult "erl -version"
+ $result.Output -match "version (?\d+\.\d+\.\d+)" | Out-Null
+ $version = $Matches.version
+ return "Erlang $version"
+}
+
+function Get-MonoVersion {
+ $monoVersion = $(mono --version) | Out-String | Take-Part -Part 4
+ return "Mono $monoVersion"
+}
+
+function Get-NodeVersion {
+ $nodeVersion = $(node --version).Substring(1)
+ return "Node $nodeVersion"
+}
+
+function Get-PythonVersion {
+ $result = Get-CommandResult "python --version"
+ $version = $result.Output | Take-Part -Part 1
+ return "Python $version"
+}
+
+function Get-Python3Version {
+ $result = Get-CommandResult "python3 --version"
+ $version = $result.Output | Take-Part -Part 1
+ return "Python3 $version"
+}
+
+function Get-PowershellVersion {
+ $(pwsh --version)
+}
+
+function Get-RubyVersion {
+ $rubyVersion = $(ruby --version) | Out-String | Take-Part -Part 1
+ return "Ruby $rubyVersion"
+}
+
+function Get-SwiftVersion {
+ $swiftVersion = $(swift --version) | Out-String | Take-Part -Part 2
+ return "Swift $swiftVersion"
+}
+
+function Get-JuliaVersion {
+ $juliaVersion = $(julia --version) | Out-String | Take-Part -Part 2
+ return "Julia $juliaVersion"
+}
+
+function Get-HomebrewVersion {
+ $result = Get-CommandResult "brew -v"
+ $result.Output -match "Homebrew (?\d+\.\d+\.\d+)" | Out-Null
+ $version = $Matches.version
+ return "Homebrew $version"
+}
+
+function Get-GemVersion {
+ $(gem --version) -match "(?\d+\.\d+\.\d+)" | Out-Null
+ $gemVersion = $Matches.version
+ return "Gem $gemVersion"
+}
+
+function Get-MinicondaVersion {
+ $condaVersion = $(conda --version)
+ return "Mini$condaVersion"
+}
+
+function Get-HelmVersion {
+ $(helm version) -match 'Version:"v(?\d+\.\d+\.\d+)"' | Out-Null
+ $helmVersion = $Matches.version
+ return "Helm $helmVersion"
+}
+
+function Get-NpmVersion {
+ $npmVersion = $(npm --version)
+ return "Npm $npmVersion"
+}
+
+function Get-YarnVersion {
+ $yarnVersion = $(yarn --version)
+ return "Yarn $yarnVersion"
+}
+
+function Get-PipVersion {
+ $result = Get-CommandResult "pip --version"
+ $result.Output -match "pip (?\d+\.\d+\.\d+)" | Out-Null
+ $pipVersion = $Matches.version
+ return "Pip $pipVersion"
+}
+
+function Get-Pip3Version {
+ $result = Get-CommandResult "pip3 --version"
+ $result.Output -match "pip (?\d+\.\d+\.\d+)" | Out-Null
+ $pipVersion = $Matches.version
+ return "Pip3 $pipVersion"
+}
+
+function Get-VcpkgVersion {
+ $result = Get-CommandResult "vcpkg version"
+ $result.Output -match "version (?\d+\.\d+\.\d+)" | Out-Null
+ $vcpkgVersion = $Matches.version
+ return "Vcpkg $vcpkgVersion"
+}
+
+function Get-AntVersion {
+ $result = $(ant -version) | Out-String
+ $result -match "version (?\d+\.\d+\.\d+)" | Out-Null
+ $antVersion = $Matches.version
+ return "Ant $antVersion"
+}
+
+function Get-GradleVersion {
+ $result = $(gradle -v) | Out-String
+ $result -match "Gradle (?\d+\.\d+\.\d+)" | Out-Null
+ $gradleVersion = $Matches.version
+ return "Gradle $gradleVersion"
+}
+function Get-MavenVersion {
+ $result = $(mvn -version) | Out-String
+ $result -match "Apache Maven (?\d+\.\d+\.\d+)" | Out-Null
+ $mavenVersion = $Matches.version
+ return "Maven $mavenVersion"
+}
+function Get-SbtVersion {
+ $result = $(sbt -version) | Out-String
+ $result -match "sbt script version: (?\d+\.\d+\.\d+)" | Out-Null
+ $sbtVersion = $Matches.version
+ return "Sbt $sbtVersion"
+}
+
+function Get-PHPVersions {
+ return $(apt list --installed) | Where-Object { $_ -match "^php\d+\.\d+/"} | ForEach-Object {
+ $_ -match "now (?\d+\.\d+\.\d+)-" | Out-Null
+ $Matches.version
+ }
+}
+
+function Get-ComposerVersion {
+ $(composer --version) -match "Composer version (?\d+\.\d+\.\d+)\s" | Out-Null
+ return $Matches.version
+}
+
+function Get-PHPUnitVersion {
+ $(phpunit --version | Out-String) -match "PHPUnit (?\d+\.\d+\.\d+)\s" | Out-Null
+ return $Matches.version
+}
+
+function Build-PHPTable {
+ $php = @{
+ "Tool" = "PHP"
+ "Version" = "$(Get-PHPVersions -Join '
')"
+ }
+ $composer = @{
+ "Tool" = "Composer"
+ "Version" = Get-ComposerVersion
+ }
+ $phpunit = @{
+ "Tool" = "PHPUnit"
+ "Version" = Get-PHPUnitVersion
+ }
+ return @($php, $composer, $phpunit) | ForEach-Object {
+ [PSCustomObject] @{
+ "Tool" = $_.Tool
+ "Version" = $_.Version
+ }
+ }
+}
+
+function Get-GHCVersion {
+ $(ghc --version) -match "version (?\d+\.\d+\.\d+)" | Out-Null
+ $ghcVersion = $Matches.version
+ return "GHC $ghcVersion"
+}
+
+function Get-CabalVersion {
+ $(cabal --version | Out-String) -match "cabal-install version (?\d+\.\d+\.\d+\.\d+)" | Out-Null
+ $cabalVersion = $Matches.version
+ return "Cabal $cabalVersion"
+}
+
+function Get-StackVersion {
+ $(stack --version | Out-String) -match "Version (?\d+\.\d+\.\d+)" | Out-Null
+ $stackVersion = $Matches.version
+ return "Stack $stackVersion"
+}
+
+function Get-RustVersion {
+ $rustVersion = $(rustc --version) | Take-Part -Part 1
+ return "Rust $rustVersion"
+}
+
+function Get-BindgenVersion {
+ $bindgenVersion = $(bindgen --version) | Take-Part -Part 1
+ return "Bindgen $bindgenVersion"
+}
+
+function Get-CargoVersion {
+ $cargoVersion = $(cargo --version) | Take-Part -Part 1
+ return "Cargo $cargoVersion"
+}
+
+function Get-CargoAuditVersion {
+ $cargoAuditVersion = $(cargo audit --version) | Take-Part -Part 1
+ return "Cargo audit $cargoAuditVersion"
+}
+
+function Get-CargoOutdatedVersion {
+ $cargoOutdatedVersion = $(cargo outdated --version) | Take-Part -Part 1 -Delimiter "v"
+ return "Cargo outdated $cargoOutdatedVersion"
+}
+
+function Get-CargoClippyVersion {
+ $cargoClippyVersion = $(cargo-clippy --version) | Take-Part -Part 1
+ return "Cargo clippy $cargoClippyVersion"
+}
+
+function Get-CbindgenVersion {
+ $cbindgenVersion = $(cbindgen --version) | Take-Part -Part 1
+ return "Cbindgen $cbindgenVersion"
+}
+
+function Get-RustupVersion {
+ $rustupVersion = $(rustup --version) | Take-Part -Part 1
+ return "Rustup $rustupVersion"
+}
+
+function Get-RustdocVersion {
+ $rustdocVersion = $(rustdoc --version) | Take-Part -Part 1
+ return "Rustdoc $rustdocVersion"
+}
+
+function Get-RustfmtVersion {
+ $rustfmtVersion = $(rustfmt --version) | Take-Part -Part 1 | Take-Part -Part 0 -Delimiter "-"
+ return "Rustfmt $rustfmtVersion"
+}
+
+function Get-AzModuleVersions {
+ $azModuleVersions = Get-ChildItem /usr/share | Where-Object { $_ -match "az_\d+" } | Foreach-Object {
+ $_.Name.Split("_")[1]
+ }
+
+ $azModuleVersions = $azModuleVersions -join " "
+ return $azModuleVersions
+}
+
+function Get-DotNetCoreSdkVersions {
+ $unsortedDotNetCoreSdkVersion = dotnet --list-sdks list | ForEach-Object { $_ | Take-Part -Part 0 }
+ $dotNetCoreSdkVersion = $unsortedDotNetCoreSdkVersion -join " "
+ return $dotNetCoreSdkVersion
+}
+
+function Get-CachedDockerImages {
+ $toolsetJson = Get-ToolsetContent
+ $images = $toolsetJson.docker.images
+ return $images
+}
+
+function Get-AptPackages {
+ $toolsetJson = Get-ToolsetContent
+ $apt = $toolsetJson.apt
+ $pkgs = ($apt.common_packages + $apt.cmd_packages | Sort-Object) -join ", "
+ return $pkgs
+}
\ No newline at end of file
diff --git a/images/linux/scripts/SoftwareReport/SoftwareReport.Databases.psm1 b/images/linux/scripts/SoftwareReport/SoftwareReport.Databases.psm1
new file mode 100644
index 000000000..6c274184e
--- /dev/null
+++ b/images/linux/scripts/SoftwareReport/SoftwareReport.Databases.psm1
@@ -0,0 +1,19 @@
+function Get-PostgreSqlVersion {
+ $postgreSQLVersion = psql --version | Take-Part -Part 2
+ return "Postgre SQL $postgreSQLVersion"
+}
+
+function Get-MongoDbVersion {
+ $mongoDBVersion = mongod --version | Select-Object -First 1 | Take-Part -Part 2 -Delimiter "v"
+ return "MongoDB $mongoDBVersion"
+}
+
+function Get-SqliteVersion {
+ $sqliteVersion = sqlite3 --version | Take-Part -Part 0
+ return "sqlite3 $sqliteVersion"
+}
+
+function Get-MySqlVersion {
+ $mySqlVersion = (mysql --version).Split("/usr/bin/")[1]
+ return "MySQL ($mySqlVersion)"
+}
\ No newline at end of file
diff --git a/images/linux/scripts/SoftwareReport/SoftwareReport.Generator.ps1 b/images/linux/scripts/SoftwareReport/SoftwareReport.Generator.ps1
new file mode 100644
index 000000000..e63f3719a
--- /dev/null
+++ b/images/linux/scripts/SoftwareReport/SoftwareReport.Generator.ps1
@@ -0,0 +1,221 @@
+param (
+ [Parameter(Mandatory)][string]
+ $OutputDirectory
+)
+
+# Install MarkdownPS module for software report generation
+Install-Module MarkdownPS -Force -Scope CurrentUser
+Import-Module MarkdownPS
+Import-Module (Join-Path $PSScriptRoot "SoftwareReport.CachedTools.psm1") -DisableNameChecking
+Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Common.psm1") -DisableNameChecking
+Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Helpers.psm1") -DisableNameChecking
+Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Tools.psm1") -DisableNameChecking
+Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Java.psm1") -DisableNameChecking
+Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Databases.psm1") -DisableNameChecking
+Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Browsers.psm1") -DisableNameChecking
+Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Android.psm1") -DisableNameChecking
+
+$markdown = ""
+
+if ($env:ANNOUNCEMENTS) {
+ $markdown += $env:ANNOUNCEMENTS
+ $markdown += New-MDNewLine
+ $markdown += "***"
+ $markdown += New-MDNewLine
+}
+
+$OSName = Get-OSName
+$markdown += New-MDHeader "$OSName" -Level 1
+
+$markdown += New-MDList -Style Unordered -Lines @(
+ "Image Version: $env:ImageVersion"
+)
+
+$markdown += New-MDHeader "Installed Software" -Level 2
+$markdown += New-MDHeader "Language and Runtime" -Level 3
+
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-CPPVersions),
+ (Get-FortranVersions),
+ (Get-ClangVersions),
+ (Get-ErlangVersion),
+ (Get-MonoVersion),
+ (Get-NodeVersion),
+ (Get-PythonVersion),
+ (Get-Python3Version),
+ (Get-PowershellVersion),
+ (Get-RubyVersion),
+ (Get-SwiftVersion),
+ (Get-JuliaVersion)
+)
+
+$markdown += New-MDHeader "Package Management" -Level 3
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-HomebrewVersion),
+ (Get-GemVersion),
+ (Get-MinicondaVersion),
+ (Get-HelmVersion),
+ (Get-NpmVersion),
+ (Get-YarnVersion),
+ (Get-PipVersion),
+ (Get-Pip3Version),
+ (Get-VcpkgVersion)
+)
+
+$markdown += New-MDHeader "Project Management" -Level 3
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-AntVersion),
+ (Get-GradleVersion),
+ (Get-MavenVersion),
+ (Get-SbtVersion)
+)
+
+$markdown += New-MDHeader "Tools" -Level 3
+$toolsList = @(
+ (Get-7zipVersion),
+ (Get-AnsibleVersion),
+ (Get-AzCopy7Version),
+ (Get-AzCopy10Version),
+ (Get-BazelVersion),
+ (Get-BazeliskVersion),
+ (Get-CMakeVersion),
+ (Get-CurlVersion),
+ (Get-DockerComposeVersion),
+ (Get-DockerMobyVersion),
+ (Get-DockerBuildxVersion),
+ (Get-GitVersion),
+ (Get-GitLFSVersion),
+ (Get-GitFTPVersion),
+ (Get-GoogleCloudSDKVersion),
+ (Get-HavegedVersion),
+ (Get-HerokuVersion),
+ (Get-HHVMVersion),
+ (Get-SVNVersion),
+ (Get-JqVersion),
+ (Get-KindVersion),
+ (Get-KubectlVersion),
+ (Get-KustomizeVersion),
+ (Get-LeiningenVersion),
+ (Get-M4Version),
+ (Get-HGVersion),
+ (Get-MinikubeVersion),
+ (Get-NewmanVersion),
+ (Get-NvmVersion),
+ (Get-PackerVersion),
+ (Get-PhantomJSVersion),
+ (Get-SwigVersion),
+ (Get-TerraformVersion),
+ (Get-UnZipVersion),
+ (Get-WgetVersion),
+ (Get-ZipVersion),
+ (Get-ZstdVersion)
+)
+
+if (-not (Test-IsUbuntu16)) {
+ $toolsList += @(
+ (Get-PodManVersion),
+ (Get-BuildahVersion),
+ (Get-SkopeoVersion)
+ )
+}
+
+$markdown += New-MDList -Style Unordered -Lines ($toolsList | Sort-Object)
+
+$markdown += New-MDHeader "CLI Tools" -Level 3
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-AlibabaCloudCliVersion),
+ (Get-AWSCliVersion),
+ (Get-AWSCliSessionManagerPluginVersion),
+ (Get-AWSSAMVersion),
+ (Get-AzureCliVersion),
+ (Get-AzureDevopsVersion),
+ (Get-GitHubCliVersion),
+ (Get-HubCliVersion),
+ (Get-NetlifyCliVersion),
+ (Get-OCCliVersion),
+ (Get-ORASCliVersion),
+ (Get-VerselCliversion)
+)
+
+$markdown += New-MDHeader "Java" -Level 3
+$markdown += Get-JavaVersions | New-MDTable
+$markdown += New-MDNewLine
+
+$markdown += New-MDHeader "PHP" -Level 3
+$markdown += Build-PHPTable | New-MDTable
+$markdown += New-MDNewLine
+
+$markdown += New-MDHeader "Haskell" -Level 3
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-GHCVersion),
+ (Get-CabalVersion),
+ (Get-StackVersion)
+)
+
+$markdown += New-MDHeader "Rust Tools" -Level 3
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-RustVersion),
+ (Get-RustupVersion),
+ (Get-RustdocVersion),
+ (Get-CargoVersion)
+)
+
+$markdown += New-MDHeader "Packages" -Level 4
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-BindgenVersion),
+ (Get-CargoAuditVersion),
+ (Get-CargoOutdatedVersion),
+ (Get-CargoClippyVersion),
+ (Get-CbindgenVersion),
+ (Get-RustfmtVersion)
+)
+
+$markdown += New-MDHeader "Browsers and Drivers" -Level 3
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-ChromeVersion),
+ (Get-ChromeDriverVersion),
+ (Get-FirefoxVersion),
+ (Get-GeckodriverVersion)
+)
+
+$markdown += New-MDHeader ".NET Core SDK" -Level 3
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-DotNetCoreSdkVersions)
+)
+
+$markdown += New-MDHeader "Az Module" -Level 3
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-AzModuleVersions)
+)
+
+$markdown += New-MDHeader "Databases" -Level 3
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-PostgreSqlVersion),
+ (Get-MongoDbVersion),
+ (Get-SqliteVersion)
+)
+
+$markdown += New-MDHeader "MySQL" -Level 4
+$markdown += New-MDList -Style Unordered -Lines @(
+ (Get-MySqlVersion),
+ "MySQL Server (user:root password:root)",
+ "MS SQL Server Client Tools"
+)
+$markdown += New-MDCode -Lines @(
+ "MySQL service is disabled by default. Use the following command as a part of your job to start the service: 'sudo systemctl start mysql.service'"
+)
+
+$markdown += New-MDHeader "Cached Tools" -Level 3
+$markdown += Build-CachedToolsSection
+
+$markdown += New-MDHeader "Android" -Level 3
+$markdown += Build-AndroidTable | New-MDTable
+$markdown += New-MDNewLine
+
+$markdown += New-MDHeader "Cached Docker images" -Level 3
+$markdown += New-MDList -Style Unordered -Lines @(Get-CachedDockerImages)
+
+$markdown += New-MDHeader "Installed apt packages" -Level 3
+$markdown += New-MDList -Style Unordered -Lines @(Get-AptPackages)
+
+$markdown | Out-File -FilePath "${OutputDirectory}/Ubuntu-Readme.md"
diff --git a/images/linux/scripts/SoftwareReport/SoftwareReport.Helpers.psm1 b/images/linux/scripts/SoftwareReport/SoftwareReport.Helpers.psm1
new file mode 100644
index 000000000..6f2701c92
--- /dev/null
+++ b/images/linux/scripts/SoftwareReport/SoftwareReport.Helpers.psm1
@@ -0,0 +1,52 @@
+function Get-CommandResult {
+ param (
+ [Parameter(Mandatory=$true)]
+ [string] $Command,
+ [switch] $Multiline
+ )
+ # Bash trick to suppress and show error output because some commands write to stderr (for example, "python --version")
+ $stdout = & bash -c "$Command 2>&1"
+ $exitCode = $LASTEXITCODE
+ return @{
+ Output = If ($Multiline -eq $true) { $stdout } else { [string]$stdout }
+ ExitCode = $exitCode
+ }
+}
+
+function Take-Part {
+ param (
+ [Parameter(ValueFromPipeline)]
+ [string] $toolOutput,
+ [string] $Delimiter = " ",
+ [int[]] $Part
+ )
+ $parts = $toolOutput.Split($Delimiter, [System.StringSplitOptions]::RemoveEmptyEntries)
+ $selectedParts = $parts[$Part]
+ return [string]::Join($Delimiter, $selectedParts)
+}
+
+function Test-IsUbuntu16 {
+ return (lsb_release -rs) -eq "16.04"
+}
+
+function Test-IsUbuntu18 {
+ return (lsb_release -rs) -eq "18.04"
+}
+
+function Test-IsUbuntu20 {
+ return (lsb_release -rs) -eq "20.04"
+}
+
+function Get-ToolsetContent
+{
+ $toolset = Join-Path $env:INSTALLER_SCRIPT_FOLDER "toolset.json"
+ Get-Content $toolset -Raw | ConvertFrom-Json
+}
+
+function New-MDNewLine {
+ param (
+ [int] $Count = 1
+ )
+ $newLineSymbol = [System.Environment]::NewLine
+ return $newLineSymbol * $Count
+}
diff --git a/images/linux/scripts/SoftwareReport/SoftwareReport.Java.psm1 b/images/linux/scripts/SoftwareReport/SoftwareReport.Java.psm1
new file mode 100644
index 000000000..4e54661ab
--- /dev/null
+++ b/images/linux/scripts/SoftwareReport/SoftwareReport.Java.psm1
@@ -0,0 +1,30 @@
+function Get-JavaFullVersion {
+ param($JavaRootPath)
+
+ $javaBinPath = Join-Path $javaRootPath "/bin/java"
+ $javaVersionOutput = (Get-CommandResult "$javaBinPath -version").Output
+ $matchResult = $javaVersionOutput | Select-String '^openjdk version \"([\d\._]+)\"'
+ return $matchResult.Matches.Groups[1].Value
+}
+
+function Get-JavaVersions {
+ $defaultJavaPath = $env:JAVA_HOME
+ $javaVersions = Get-Item env:JAVA_HOME_*_X64
+ $sortRules = @{
+ Expression = { [Int32]$_.Name.Split("_")[2] }
+ Descending = $false
+ }
+
+ return $javaVersions | Sort-Object $sortRules | ForEach-Object {
+ $javaPath = $_.Value
+ $version = Get-JavaFullVersion $javaPath
+ $vendor = $version.StartsWith("1.7") ? "Zulu" : "AdoptOpenJDK"
+ $defaultPostfix = ($javaPath -eq $defaultJavaPath) ? " (default)" : ""
+
+ [PSCustomObject] @{
+ "Version" = $version + $defaultPostfix
+ "Vendor" = $vendor
+ "Environment Variable" = $_.Name
+ }
+ }
+}
\ No newline at end of file
diff --git a/images/linux/scripts/SoftwareReport/SoftwareReport.Tools.psm1 b/images/linux/scripts/SoftwareReport/SoftwareReport.Tools.psm1
new file mode 100644
index 000000000..f66f2e4fc
--- /dev/null
+++ b/images/linux/scripts/SoftwareReport/SoftwareReport.Tools.psm1
@@ -0,0 +1,249 @@
+function Get-7zipVersion {
+ $7zVersion = 7z i | Select-String "7-Zip" | Take-Part -Part 2
+ return "7-Zip $7zVersion"
+}
+
+function Get-AnsibleVersion {
+ $ansibleVersion = ansible --version | Select-Object -First 1 | Take-Part -Part 1
+ return "Ansible $ansibleVersion"
+}
+
+function Get-AzCopy7Version {
+ $azcopy7Version = azcopy --version | Take-Part -Part 1 | Take-Part -Part 0 -Delimiter "-"
+ return "AzCopy7 (available by azcopy alias) $azcopy7Version"
+}
+
+function Get-AzCopy10Version {
+ $azcopy10Version = azcopy10 --version | Take-Part -Part 2
+ return "AzCopy10 (available by azcopy10 alias) $azcopy10Version"
+}
+
+function Get-BazelVersion {
+ $bazelVersion = bazel --version | Select-String "bazel" | Take-Part -Part 1
+ return "Bazel $bazelVersion"
+}
+
+function Get-BazeliskVersion {
+ $bazeliskVersion = bazelisk version 2>&1 | Select-String "Bazelisk version:" | Take-Part -Part 2 | Take-Part -Part 0 -Delimiter "v"
+ return "Bazelisk $bazeliskVersion"
+}
+
+function Get-PodManVersion {
+ $podmanVersion = podman --version | Take-Part -Part 2
+ return "Podman $podmanVersion"
+}
+
+function Get-BuildahVersion {
+ $buildahVersion = buildah --version | Take-Part -Part 2
+ return "Buildah $buildahVersion"
+}
+
+function Get-SkopeoVersion {
+ $skopeoVersion = skopeo --version | Take-Part -Part 2
+ return "Skopeo $skopeoVersion"
+}
+
+function Get-CMakeVersion {
+ $cmakeVersion = cmake --version | Select-Object -First 1 | Take-Part -Part 2
+ return "CMake $cmakeVersion"
+}
+
+function Get-CurlVersion {
+ $curlVersion = curl --version | Select-Object -First 1 | Take-Part -Part 0,1
+ return $curlVersion
+}
+
+function Get-DockerComposeVersion {
+ $composeVersion = docker-compose -v | Take-Part -Part 2 | Take-Part -Part 0 -Delimiter ","
+ return "Docker Compose $composeVersion"
+}
+
+function Get-DockerMobyVersion {
+ $dockerVersion = docker -v | Take-Part -Part 2 | Take-Part -Part 0 -Delimiter "+"
+ return "Docker-Moby $dockerVersion"
+}
+
+function Get-DockerBuildxVersion {
+ $buildxVersion = docker buildx version | Take-Part -Part 1 | Take-Part -Part 0 -Delimiter "+"
+ return "Docker-Buildx $buildxVersion"
+}
+
+function Get-GitVersion {
+ $gitVersion = git --version 2>&1 | Take-Part -Part 2
+ return "Git $gitVersion"
+}
+
+function Get-GitLFSVersion {
+ $gitlfsversion = git-lfs --version 2>&1 | Take-Part -Part 0 | Take-Part -Part 1 -Delimiter "/"
+ return "Git LFS $gitlfsversion"
+}
+
+function Get-GitFTPVersion {
+ $gitftpVersion = git-ftp --version | Take-Part -Part 2
+ return "Git-ftp $gitftpVersion"
+}
+
+function Get-GoogleCloudSDKVersion {
+ return "$(gcloud --version | Select-Object -First 1)"
+}
+
+function Get-HavegedVersion {
+ $havegedVersion = dpkg-query --showformat='${Version}' --show haveged | Take-Part -Part 0 -Delimiter "-"
+ return "Haveged $havegedVersion"
+}
+
+function Get-HerokuVersion {
+ $herokuVersion = heroku version | Take-Part -Part 0 | Take-Part -Part 1 -Delimiter "/"
+ return "Heroku $herokuVersion"
+}
+
+function Get-HHVMVersion {
+ $hhvmVersion = hhvm --version | Select-Object -First 1 | Take-Part -Part 2
+ return "HHVM (HipHop VM) $hhvmVersion"
+}
+
+function Get-SVNVersion {
+ $svnVersion = svn --version | Select-Object -First 1 | Take-Part -Part 2
+ return "SVN $svnVersion"
+}
+
+function Get-KustomizeVersion {
+ $kustomizeVersion = kustomize version --short | Take-Part -Part 0 | Take-Part -Part 1 -Delimiter "v"
+ return "Kustomize $kustomizeVersion"
+}
+
+function Get-KindVersion {
+ $kindVersion = kind version | Take-Part -Part 1 | Take-Part -Part 0 -Delimiter "v"
+ return "Kind $kindVersion"
+}
+
+function Get-KubectlVersion {
+ $kubectlVersion = kubectl version --client --short | Take-Part -Part 2 | Take-Part -Part 0 -Delimiter "v"
+ return "Kubectl $kubectlVersion"
+}
+
+function Get-MinikubeVersion {
+ $minikubeVersion = minikube version --short | Take-Part -Part 2 | Take-Part -Part 0 -Delimiter "v"
+ return "Minikube $minikubeVersion"
+}
+
+function Get-HGVersion {
+ $hgVersion = hg --version | Select-Object -First 1 | Take-Part -Part -1 | Take-Part -Part 0 -Delimiter ")"
+ return "Mercurial $hgVersion"
+}
+
+function Get-M4Version {
+ $m4Version = m4 --version | Select-Object -First 1 | Take-Part -Part -1
+ return "m4 $m4Version"
+}
+
+function Get-LeiningenVersion {
+ return "$(lein -v | Take-Part -Part 0,1)"
+}
+
+function Get-NewmanVersion {
+ return "Newman $(newman --version)"
+}
+
+function Get-NvmVersion {
+ $nvmVersion = bash -c "source $HOME/.nvm/nvm.sh && nvm --version"
+ return "nvm $nvmVersion"
+}
+
+function Get-PackerVersion {
+ return "Packer $(packer --version)"
+}
+
+function Get-PhantomJSVersion {
+ return "PhantomJS $(phantomjs --version)"
+}
+
+function Get-SwigVersion {
+ $swigVersion = swig -version | Select-String "SWIG Version" | Take-Part -Part 2
+ return "Swig $swigVersion"
+}
+
+function Get-TerraformVersion {
+ return (terraform version | Select-String "^Terraform").Line.Replace('v','')
+}
+
+function Get-UnZipVersion {
+ $unzipVersion = unzip -v | Select-Object -First 1 | Take-Part -Part 1
+ return "unzip $unzipVersion"
+}
+
+function Get-WgetVersion {
+ $wgetVersion = wget --version | Select-Object -First 1 | Take-Part -Part 2
+ return "wget $wgetVersion"
+}
+
+function Get-ZipVersion {
+ $zipVersion = zip -v | Select-String "This is Zip" | Take-Part -Part 3
+ return "zip $zipVersion"
+}
+
+function Get-ZstdVersion {
+ $zstdVersion = (zstd --version).Split() -match "v\d+" | ForEach-Object {$_.Replace("v","").Replace(",","")}
+ return "zstd $zstdVersion"
+}
+
+function Get-JqVersion {
+ $jqVersion = jq --version | Take-Part -Part 1 -Delimiter "-"
+ return "jq $jqVersion"
+}
+
+function Get-AzureCliVersion {
+ $azcliVersion = az -v | Select-String "azure-cli" | Take-Part -Part -1
+ return "Azure CLI (azure-cli) $azcliVersion"
+}
+
+function Get-AzureDevopsVersion {
+ $azdevopsVersion = az -v | Select-String "azure-devops" | Take-Part -Part -1
+ return "Azure CLI (azure-devops) $azdevopsVersion"
+}
+
+function Get-AlibabaCloudCliVersion {
+ return "Alibaba Cloud CLI $(aliyun version)"
+}
+
+function Get-AWSCliVersion {
+ $awsVersion = aws --version 2>&1 | Take-Part -Part 0 | Take-Part -Part 1 -Delimiter "/"
+ return "AWS CLI $awsVersion"
+}
+
+function Get-AWSCliSessionManagerPluginVersion {
+ return "AWS CLI Session manager plugin $(session-manager-plugin --version 2>&1)"
+}
+
+function Get-AWSSAMVersion {
+ return "AWS SAM CLI $(sam --version | Take-Part -Part -1)"
+}
+
+function Get-HubCliVersion {
+ $hubVersion = hub --version | Select-String "hub version" | Take-Part -Part 2
+ return "Hub CLI $hubVersion"
+}
+
+function Get-GitHubCliVersion {
+ $ghVersion = gh --version | Select-String "gh version" | Take-Part -Part 2
+ return "GitHub CLI $ghVersion"
+}
+
+function Get-NetlifyCliVersion {
+ $netlifyVersion = netlify --version | Take-Part -Part 0 | Take-Part -Part 1 -Delimiter "/"
+ return "Netlify CLI $netlifyVersion"
+}
+
+function Get-OCCliVersion {
+ $ocVersion = oc version | Take-Part -Part 2 | Take-Part -Part 0 -Delimiter "-"
+ return "oc CLI $ocVersion"
+}
+
+function Get-ORASCliVersion {
+ $orasVersion = oras version | Select-String "^Version:" | Take-Part -Part 1
+ return "ORAS CLI $orasVersion"
+}
+
+function Get-VerselCliversion {
+ return "$(vercel --version 2>&1 | Select-Object -First 1)"
+}
diff --git a/images/linux/ubuntu1604.json b/images/linux/ubuntu1604.json
index 9d8a11ba7..7ef3122a6 100644
--- a/images/linux/ubuntu1604.json
+++ b/images/linux/ubuntu1604.json
@@ -88,6 +88,11 @@
"source": "{{template_dir}}/scripts/installers",
"destination": "{{user `installer_script_folder`}}"
},
+ {
+ "type": "file",
+ "source": "{{ template_dir }}/scripts/SoftwareReport",
+ "destination": "{{user `image_folder`}}"
+ },
{
"type": "file",
"source": "{{template_dir}}/toolsets/toolcache-1604.json",
@@ -287,9 +292,19 @@
],
"execute_command": "sudo sh -c '{{ .Vars }} {{ .Path }}'"
},
+ {
+ "type": "powershell",
+ "inline": [
+ "pwsh -File '{{user `image_folder`}}/SoftwareReport/SoftwareReport.Generator.ps1' -OutputDirectory '{{user `image_folder`}}'"
+ ],
+ "environment_vars":[
+ "INSTALLER_SCRIPT_FOLDER={{user `installer_script_folder`}}",
+ "ANNOUNCEMENTS={{user `announcements`}}"
+ ]
+ },
{
"type": "file",
- "source": "{{user `metadata_file`}}",
+ "source": "{{user `image_folder`}}/Ubuntu-Readme.md",
"destination": "{{template_dir}}/Ubuntu1604-README.md",
"direction": "download"
},
diff --git a/images/linux/ubuntu1804.json b/images/linux/ubuntu1804.json
index aacfe4eea..5f280636d 100644
--- a/images/linux/ubuntu1804.json
+++ b/images/linux/ubuntu1804.json
@@ -91,6 +91,11 @@
"source": "{{template_dir}}/scripts/installers",
"destination": "{{user `installer_script_folder`}}"
},
+ {
+ "type": "file",
+ "source": "{{ template_dir }}/scripts/SoftwareReport",
+ "destination": "{{user `image_folder`}}"
+ },
{
"type": "file",
"source": "{{template_dir}}/toolsets/toolcache-1804.json",
@@ -291,9 +296,19 @@
],
"execute_command": "sudo sh -c '{{ .Vars }} {{ .Path }}'"
},
+ {
+ "type": "powershell",
+ "inline": [
+ "pwsh -File '{{user `image_folder`}}/SoftwareReport/SoftwareReport.Generator.ps1' -OutputDirectory '{{user `image_folder`}}'"
+ ],
+ "environment_vars":[
+ "INSTALLER_SCRIPT_FOLDER={{user `installer_script_folder`}}",
+ "ANNOUNCEMENTS={{user `announcements`}}"
+ ]
+ },
{
"type": "file",
- "source": "{{user `metadata_file`}}",
+ "source": "{{user `image_folder`}}/Ubuntu-Readme.md",
"destination": "{{template_dir}}/Ubuntu1804-README.md",
"direction": "download"
},
diff --git a/images/linux/ubuntu2004.json b/images/linux/ubuntu2004.json
index 17e9143d2..604b863a2 100644
--- a/images/linux/ubuntu2004.json
+++ b/images/linux/ubuntu2004.json
@@ -93,6 +93,11 @@
"source": "{{template_dir}}/scripts/installers",
"destination": "{{user `installer_script_folder`}}"
},
+ {
+ "type": "file",
+ "source": "{{ template_dir }}/scripts/SoftwareReport",
+ "destination": "{{user `image_folder`}}"
+ },
{
"type": "file",
"source": "{{template_dir}}/toolsets/toolcache-2004.json",
@@ -293,9 +298,19 @@
],
"execute_command": "sudo sh -c '{{ .Vars }} {{ .Path }}'"
},
+ {
+ "type": "powershell",
+ "inline": [
+ "pwsh -File '{{user `image_folder`}}/SoftwareReport/SoftwareReport.Generator.ps1' -OutputDirectory '{{user `image_folder`}}'"
+ ],
+ "environment_vars":[
+ "INSTALLER_SCRIPT_FOLDER={{user `installer_script_folder`}}",
+ "ANNOUNCEMENTS={{user `announcements`}}"
+ ]
+ },
{
"type": "file",
- "source": "{{user `metadata_file`}}",
+ "source": "{{user `image_folder`}}/Ubuntu-Readme.md",
"destination": "{{template_dir}}/Ubuntu2004-README.md",
"direction": "download"
},