Compare commits

..

1 Commits

Author SHA1 Message Date
Image generation service account 030286659e Updating readme file for win16 version 20220131.1 2022-02-01 09:18:57 +00:00
332 changed files with 8571 additions and 13610 deletions
+1 -1
View File
@@ -1 +1 @@
* @actions/runner-images-team
* @actions/virtual-environments-owners
+6 -10
View File
@@ -28,21 +28,17 @@ body:
required: true
- type: checkboxes
attributes:
label: Platforms affected
options:
- label: Azure DevOps
- label: GitHub Actions
- type: checkboxes
attributes:
label: Runner images affected
label: Virtual environments affected
options:
- label: Ubuntu 18.04
- label: Ubuntu 20.04
- label: Ubuntu 22.04
- label: macOS 10.15
- label: macOS 11
- label: macOS 12
- label: macOS 13
- label: Windows Server 2016
- label: Windows Server 2019
- label: Windows Server 2022
validations:
required: true
- type: textarea
attributes:
label: Mitigation ways
+13 -24
View File
@@ -1,6 +1,6 @@
name: Bug Report
description: Submit a bug report.
labels: [bug report, needs triage]
description: Submit a bug report
labels: [needs triage]
body:
- type: textarea
attributes:
@@ -10,30 +10,25 @@ body:
required: true
- type: checkboxes
attributes:
label: Platforms affected
options:
- label: Azure DevOps
- label: GitHub Actions - Standard Runners
- label: GitHub Actions - Larger Runners
- type: checkboxes
attributes:
label: Runner images affected
label: Virtual environments affected
options:
- label: Ubuntu 18.04
- label: Ubuntu 20.04
- label: Ubuntu 22.04
- label: macOS 10.15
- label: macOS 11
- label: macOS 12
- label: macOS 13
- label: Windows Server 2016
- label: Windows Server 2019
- label: Windows Server 2022
validations:
required: true
- type: textarea
attributes:
label: Image version and build link
description: |
Image version where you are experiencing the issue. Where to find image version in build logs:
1. For GitHub Actions, under "Set up job" -> "Runner Image" -> "Version".
2. For Azure DevOps, under "Initialize job" -> "Runner Image" -> "Version".
1. For GitHub Actions, under "Set up job" -> "Virtual Environment" -> "Version".
2. For Azure DevOps, under "Initialize job" -> "Virtual Environment" -> "Version".
If you have a public example, please, provide a link to the failed build.
validations:
required: true
@@ -41,20 +36,14 @@ body:
attributes:
label: Is it regression?
description: If yes, please, provide the latest image version where the issue didn't persist, and a link to the latest successful build.
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: A description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Actual behavior
description: A description of what is actually happening.
validations:
required: true
- type: textarea
attributes:
label: Repro steps
@@ -62,5 +51,5 @@ body:
A description with steps to reproduce the issue.
1. Step 1
2. Step 2
validations:
required: true
validations:
required: true
+1 -6
View File
@@ -1,6 +1 @@
blank_issues_enabled: false
contact_links:
- name: Get help in GitHub Discussions
url: https://github.com/actions/runner-images/discussions
about: Have a question? Feel free to ask in the runner-images GitHub Discussions!
blank_issues_enabled: false
+7 -11
View File
@@ -44,26 +44,22 @@ body:
label: URL for tool's homepage
- type: textarea
attributes:
label: Provide a basic test case to validate the tool's functionality.
label: Provide a basic test case to validate the tool's functionality.
description: This will be automatically formatted into code.
render: bash
- type: checkboxes
attributes:
label: Platforms where you need the tool
options:
- label: Azure DevOps
- label: GitHub Actions
- type: checkboxes
attributes:
label: Runner images where you need the tool
label: Virtual environments affected
options:
- label: Ubuntu 18.04
- label: Ubuntu 20.04
- label: Ubuntu 22.04
- label: macOS 10.15
- label: macOS 11
- label: macOS 12
- label: macOS 13
- label: Windows Server 2016
- label: Windows Server 2019
- label: Windows Server 2022
validations:
required: true
- type: textarea
attributes:
label: Can this tool be installed during the build?
+2 -2
View File
@@ -1,6 +1,6 @@
# Description
New tool, Bug fixing, or Improvement?
Please include a summary of the change and which issue is fixed. Also include relevant motivation and context.
New tool, Bug fixing, or Improvement?
Please include a summary of the change and which issue is fixed. Also include relevant motivation and context.
**For new tools, please provide total size and installation time.**
<!-- Currently, we can't accept external contributions to macOS source. Please find more details in [CONTRIBUTING.md](CONTRIBUTING.md#macOS) guide -->
-72
View File
@@ -1,72 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '32 4 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
# only required for workflows in private repositories
actions: read
contents: read
# required for all workflows
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'python', 'ruby' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Create release for ${{ github.event.client_payload.ReleaseBranchName }}
uses: actions/create-release@v1.1.1
+3 -3
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
@@ -45,5 +45,5 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: ${{ steps.create-pr.outputs.result }},
team_reviewers: ['runner-images-team']
})
team_reviewers: ['virtual-environments-akvelon']
})
-69
View File
@@ -1,69 +0,0 @@
name: Create and upload a SBOM to release assets
# Inherited variables:
# github.event.client_payload.agentSpec - Current YAML Label
# github.event.client_payload.ReleaseID - Current release ID
# github.event.client_payload.imageVersion - AzDO image version "major.minor"
# github.event.client_payload.ReleaseBranchName - Necessary to identify workflow run
#
# Current SYFT tool issues:
# macOS (minor): very long cataloging process (more than 6 hours) (https://github.com/anchore/syft/issues/1328),
# macOS (major): prompt privilegies that blocking process indefinetely (https://github.com/anchore/syft/issues/1367)
on:
repository_dispatch:
types: [generate-sbom]
defaults:
run:
shell: pwsh
jobs:
#Checking image version on available runner
version-check:
runs-on: ${{ github.event.client_payload.agentSpec }}
steps:
- name: Available image version check for ${{ github.event.client_payload.ReleaseBranchName }}
run: |
if ($env:ImageVersion -ne '${{ github.event.client_payload.imageVersion }}') {
throw "Current runner $env:ImageVersion image version don't match ${{ github.event.client_payload.imageVersion }}."
}
#Install and run SYFT, compress SBOM, upload it to release assets
create-sbom:
needs: version-check
runs-on: ${{ github.event.client_payload.agentSpec }}
steps:
#Installation section
- name: Install SYFT tool on Windows
if: ${{ runner.os == 'Windows' }}
run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b D:/syft
- name: Install SYFT tool on Ubuntu or macOS
if: ${{ runner.os != 'Windows' }}
run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
#Running section.
- name: Run SYFT on Windows
if: ${{ runner.os == 'Windows' }}
run: D:/syft/syft dir:C:/ -vv -o spdx-json=sbom.json
- name: Run SYFT on Ubuntu
if: ${{ runner.os == 'Linux' }}
run: syft dir:/ -vv -o spdx-json=sbom.json
- name: Run SYFT on macOS
if: ${{ runner.os == 'macOS' }}
run: syft dir:/ -vv -o spdx-json=sbom.json --exclude ./Users --exclude ./System/Volumes --exclude ./private
shell: bash
#Preparing artifact (raw SBOM.json is too big)
- name: Compress SBOM file
run: Compress-Archive sbom.json sbom.json.zip
#Upload artifact action
- uses: actions/upload-artifact@v3
with:
name: sbom-${{ github.event.client_payload.agentSpec }}-${{ github.event.client_payload.imageVersion }}
path: sbom.json.zip
if-no-files-found: warn
#Upload release asset action
#Might be changed to softprops/action-gh-release after additional check
- name: Upload release asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: "https://uploads.github.com/repos/actions/runner-images/releases/${{ github.event.client_payload.ReleaseID }}/assets{?name,label}"
asset_path: ./sbom.json.zip
asset_name: sbom.${{ github.event.client_payload.agentSpec }}.json.zip
asset_content_type: application/zip
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
fetch-depth: 0
-207
View File
@@ -1,207 +0,0 @@
name: macOS image generation
on:
workflow_call:
inputs:
image_label:
type: string
description: macOS codename
required: true
base_image_name:
type: string
description: Base clean image
required: true
template_path:
type: string
description: Packer template path
required: true
target_datastore:
type: string
description: Image datastore
required: true
custom_repo:
type: string
description: Custom repo to checkout
required: false
custom_repo_commit_hash:
type: string
description: Custom repo commit hash
required: false
env:
KEYVAULT: imagegeneration
ESXI_CLUSTER: mcv2-build-unstable
VCENTER_DATACENTER: imagegen
OUTPUT_FOLDER: mms-output
BUILD_DATASTORE: ds-image
defaults:
run:
shell: pwsh
jobs:
build:
#
# "macos-vmware" is dedicated runner not available in forks.
# to reduce undesired run attempts in forks, stick jobs to "actions" organization only
#
runs-on: macos-vmware
if: ${{ github.repository_owner == 'actions' }}
timeout-minutes: 1200
steps:
- name: Set image variables
run: |
$currentDate = Get-Date -Format "yyyyMMdd"
$templatePath = "${{ inputs.template_path }}"
$osName = $(($templatePath.Split("/")[-1]).Split(".")[0])
$virtualMachineName = "${osName}_${currentDate}_unstable.${{ github.run_id }}.${{ github.run_attempt }}"
"VM_NAME=$virtualMachineName" | Out-File -Append -FilePath $env:GITHUB_ENV
- name: Determine checkout type
run: |
if ("${{ inputs.custom_repo }}" -and "${{ inputs.custom_repo_commit_hash }}") {
$checkoutType = "custom_repo"
} elseif (("${{ github.event_name }}" -eq "pull_request_target") -and ("${{ github.event.action }}" -eq "labeled" )) {
$checkoutType = "pull_request"
} else {
$checkoutType = "main"
}
"CHECKOUT_TYPE=$checkoutType" | Out-File -Append $env:GITHUB_ENV
- name: Checkout repository
if: ${{ env.CHECKOUT_TYPE == 'main' }}
uses: actions/checkout@v3
with:
repository: actions/runner-images
- name: Checkout PR
if: ${{ env.CHECKOUT_TYPE == 'pull_request' }}
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Checkout custom repository
if: ${{ env.CHECKOUT_TYPE == 'custom_repo' }}
uses: actions/checkout@v3
with:
repository: '${{ inputs.custom_repo }}'
ref: '${{ inputs.custom_repo_commit_hash }}'
- name: Validate contributor permissions
if: ${{ github.event_name == 'pull_request_target' }}
run: |
[string]$contributorAllowList = "${{ vars.CONTRIBUTOR_ALLOWLIST }}"
./images.CI/macos/validate-contributor.ps1 `
-RepositoryName ${{ github.repository }} `
-AccessToken ${{ secrets.GH_FEED }} `
-SourceBranch "refs/pull/${{ github.event.pull_request.number }}/merge" `
-ContributorAllowList $contributorAllowList
- name: Select datastore
run: |
./images.CI/macos/select-datastore.ps1 `
-VMName "${{ env.VM_NAME }}" `
-VIServer ${{ secrets.VISERVER_V2 }} `
-VIUserName ${{ secrets.VI_USER_NAME }} `
-VIPassword ${{ secrets.VI_PASSWORD }} `
-Cluster ${{ env.ESXI_CLUSTER }}
- name: Build VM
run: |
$SensitiveData = @(
'IP address:',
'Using ssh communicator to connect:'
)
packer build -on-error=abort `
-var="vcenter_server=${{ secrets.VISERVER_V2 }}" `
-var="vcenter_username=${{ secrets.VI_USER_NAME }}" `
-var="vcenter_password=${{ secrets.VI_PASSWORD }}" `
-var="vcenter_datacenter=${{ env.VCENTER_DATACENTER }}" `
-var="cluster_or_esxi_host=${{ env.ESXI_CLUSTER }}" `
-var="esxi_datastore=${{ env.BUILD_DATASTORE }}" `
-var="output_folder=${{ env.OUTPUT_FOLDER }}" `
-var="vm_username=${{ secrets.VM_USERNAME }}" `
-var="vm_password=${{ secrets.VM_PASSWORD }}" `
-var="github_api_pat=${{ secrets.GH_FEED_TOKEN }}" `
-var="build_id=${{ env.VM_NAME }}" `
-var="baseimage_name=${{ inputs.base_image_name }}" `
-var="xcode_install_user=${{ secrets.XCODE_USER }}" `
-var="xcode_install_password=${{ secrets.XCODE_PASSWORD }}" `
-color=false `
${{ inputs.template_path }} `
| Where-Object {
#Filter sensitive data from Packer logs
$currentString = $_
$sensitiveString = $SensitiveData | Where-Object { $currentString -match $_ }
$sensitiveString -eq $null
}
working-directory: images/macos
env:
PACKER_LOG: 1
PACKER_LOG_PATH: ${{ runner.temp }}/packer-log.txt
- name: Prepare artifact
shell: bash
run: |
echo "Preparing artifact directory"
mkdir -p ${{ runner.temp }}/artifacts
echo "Copy image output files"
cp -R "images/image-output/software-report/." "${{ runner.temp }}/artifacts"
echo "Put VM name to 'VM_Done_Name' file"
echo "${{ env.VM_NAME }}" > "${{ runner.temp }}/artifacts/VM_Done_Name"
- name: Print markdown software report
run: |
Get-Content "${{ runner.temp }}/artifacts/systeminfo.md"
- name: Print json software report
run: |
Get-Content "${{ runner.temp }}/artifacts/systeminfo.json"
- name: Publish Artifacts
uses: actions/upload-artifact@v3
with:
name: Built_VM_Artifacts
path: ${{ runner.temp }}/artifacts/
- name: Print provisioners duration
run: |
./images.CI/measure-provisioners-duration.ps1 `
-PackerLogPath "${{ runner.temp }}/packer-log.txt" `
-PrintTopNLongest 25
- name: Move vm to cold storage and clear datastore tag
if: ${{ always() }}
run: |
./images.CI/macos/move-vm.ps1 `
-VMName "${{ env.VM_NAME }}" `
-TargetDataStore "${{ inputs.target_datastore }}" `
-VIServer "${{ secrets.VISERVER_V2 }}" `
-VIUserName "${{ secrets.VI_USER_NAME }}" `
-VIPassword "${{ secrets.VI_PASSWORD }}" `
-JobStatus "${{ job.status }}"
- name: Set VM size
run: |
$cpuCount = 3
$coresPerSocketCount = 3
$memory = 14336
./images.CI/macos/set-vm-size.ps1 `
-VMName "${{ env.VM_NAME }}" `
-CpuCount "$cpuCount" `
-CoresPerSocketCount "$coresPerSocketCount" `
-Memory "$memory" `
-VIServer "${{ secrets.VISERVER_V2 }}" `
-VIUserName "${{ secrets.VI_USER_NAME }}" `
-VIPassword "${{ secrets.VI_PASSWORD }}"
- name: Destroy VM (if build canceled only)
if: ${{ cancelled() }}
run: |
./images.CI/macos/destroy-vm.ps1 `
-VMName "${{ env.VM_NAME }}" `
-VIServer "${{ secrets.VISERVER_V2 }}" `
-VIUserName "${{ secrets.VI_USER_NAME }}" `
-VIPassword "${{ secrets.VI_PASSWORD }}"
-30
View File
@@ -1,30 +0,0 @@
run-name: macOS-11_unstable.${{ github.run_id }}.${{ github.run_attempt }}
on:
workflow_dispatch:
inputs:
CUSTOM_REPOSITORY:
description: 'Custom repository (owner/repo)'
required: false
CUSTOM_REPOSITORY_COMMIT_HASH:
description: 'Commit hash'
required: false
pull_request_target:
types: labeled
paths:
- 'images/macos/**'
schedule:
- cron: '45 0 * * *'
jobs:
macOS_11:
if: ${{ (github.event.label.name == 'CI macos-all') || (github.event.label.name == 'CI macos-11') || (github.event_name == 'workflow_dispatch') || (github.event_name == 'schedule') }}
name: macOS-11_unstable.${{ github.run_id }}.${{ github.run_attempt }}
uses: ./.github/workflows/macos-generation.yml
with:
image_label: 'macOS Big Sur'
base_image_name: 'clean-macOS-11-380Gb-runner'
template_path: 'templates/macOS-11.json'
target_datastore: 'ds-image'
custom_repo: ${{ github.event.inputs.CUSTOM_REPOSITORY }}
custom_repo_commit_hash: ${{ github.event.inputs.CUSTOM_REPOSITORY_COMMIT_HASH }}
secrets: inherit
-30
View File
@@ -1,30 +0,0 @@
run-name: macOS-12_unstable.${{ github.run_id }}.${{ github.run_attempt }}
on:
workflow_dispatch:
inputs:
CUSTOM_REPOSITORY:
description: 'Custom repository (owner/repo)'
required: false
CUSTOM_REPOSITORY_COMMIT_HASH:
description: 'Commit hash'
required: false
pull_request_target:
types: labeled
paths:
- 'images/macos/**'
schedule:
- cron: '0 0 * * *'
jobs:
macOS_12:
if: ${{ (github.event.label.name == 'CI macos-all') || (github.event.label.name == 'CI macos-12') || (github.event_name == 'workflow_dispatch') || (github.event_name == 'schedule') }}
name: macOS-12_unstable.${{ github.run_id }}.${{ github.run_attempt }}
uses: ./.github/workflows/macos-generation.yml
with:
image_label: 'macOS Monterey'
base_image_name: 'clean-macOS-12-380Gb-runner'
template_path: 'templates/macOS-12.json'
target_datastore: 'ds-image'
custom_repo: ${{ github.event.inputs.CUSTOM_REPOSITORY }}
custom_repo_commit_hash: ${{ github.event.inputs.CUSTOM_REPOSITORY_COMMIT_HASH }}
secrets: inherit
+12 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
with:
fetch-depth: 0
@@ -46,3 +46,14 @@ jobs:
pull_number: ${{ github.event.client_payload.PullRequestNumber }},
merge_method: "squash"
})
- name: Delete docs branch ${{ github.event.client_payload.ReleaseBranchName }}-docs
uses: actions/github-script@v2
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: "heads/${{ github.event.client_payload.ReleaseBranchName }}-docs"
})
-25
View File
@@ -1,25 +0,0 @@
# CI Validation
name: PowerShell Tests
on:
pull_request:
branches: [ main ]
paths:
- 'helpers/software-report-base/**'
jobs:
powershell-tests:
name: PowerShell tests
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Run Software Report module tests
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
Invoke-Pester -Output Detailed "helpers/software-report-base/tests"
-133
View File
@@ -1,133 +0,0 @@
name: MMS image generation
on:
workflow_call:
inputs:
image_name:
type: string
description: An OS image to build
required: true
image_readme_name:
type: string
description: README file path
required: true
custom_repo:
type: string
description: Custom repo to checkout
required: false
custom_repo_commit_hash:
type: string
description: Custom repo commit hash
required: false
defaults:
run:
shell: pwsh
jobs:
build:
#
# "azure-builds" is dedicated runner not available in forks.
# to reduce undesired run attempts in forks, stick jobs to "actions" organization only
#
runs-on: azure-builds
if: ${{ github.repository_owner == 'actions' }}
timeout-minutes: 1200
steps:
- name: Determine checkout type
run: |
if ("${{ inputs.custom_repo }}" -and "${{ inputs.custom_repo_commit_hash }}") {
$checkoutType = "custom_repo"
} elseif (("${{ github.event_name }}" -eq "pull_request_target") -and ("${{ github.event.action }}" -eq "labeled" )) {
$checkoutType = "pull_request"
} else {
$checkoutType = "main"
}
"CHECKOUT_TYPE=$checkoutType" | Out-File -Append $env:GITHUB_ENV
- name: Checkout repository
if: ${{ env.CHECKOUT_TYPE == 'main' }}
uses: actions/checkout@v3
with:
repository: actions/runner-images
- name: Checkout PR
if: ${{ env.CHECKOUT_TYPE == 'pull_request' }}
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Checkout custom repository
if: ${{ env.CHECKOUT_TYPE == 'custom_repo' }}
uses: actions/checkout@v3
with:
repository: '${{ inputs.custom_repo }}'
ref: '${{ inputs.custom_repo_commit_hash }}'
- name: Set image varibles
run: |
$ImageType = "${{ inputs.image_name }}"
if ($ImageType.StartsWith("ubuntu")) { $TemplateDirectoryName = "linux" } else { $TemplateDirectoryName = "win" }
$TemplateDirectoryPath = Join-Path "images" $TemplateDirectoryName | Resolve-Path
$TemplatePath = Join-Path $TemplateDirectoryPath "$ImageType.pkr.hcl"
if ( -not (Test-Path $TemplatePath) ) {
$TemplatePath = Join-Path $TemplateDirectoryPath "$ImageType.json"
}
"TemplatePath=$TemplatePath" | Out-File -Append -FilePath $env:GITHUB_ENV
"TemplateDirectoryPath=$TemplateDirectoryPath" | Out-File -Append -FilePath $env:GITHUB_ENV
"ImageType=$ImageType" | Out-File -Append -FilePath $env:GITHUB_ENV
- name: Build image
run: |
./images.CI/linux-and-win/build-image.ps1 `
-TemplatePath ${{ env.TemplatePath }} `
-ClientId ${{ secrets.CLIENT_ID }} `
-ClientSecret ${{ secrets.CLIENT_SECRET }} `
-Location ${{ secrets.AZURE_LOCATION }} `
-ResourcesNamePrefix ${{ github.run_number }} `
-ResourceGroup ${{ secrets.AZURE_RESOURCE_GROUP }} `
-StorageAccount ${{ secrets.AZURE_STORAGE_ACCOUNT }} `
-SubscriptionId ${{ secrets.AZURE_SUBSCRIPTION }} `
-TenantId ${{ secrets.AZURE_TENANT }} `
-VirtualNetworkName ${{ secrets.BUILD_AGENT_VNET_NAME }} `
-VirtualNetworkSubnet ${{ secrets.BUILD_AGENT_SUBNET_NAME }} `
-VirtualNetworkRG ${{ secrets.BUILD_AGENT_VNET_RESOURCE_GROUP }} `
env:
PACKER_LOG: 1
PACKER_LOG_PATH: ${{ runner.temp }}/packer-log.txt
RUN_VALIDATION_FLAG: true
- name: Output Readme file content
run: |
Get-Content -Path (Join-Path "$env:TemplateDirectoryPath" "${{ inputs.image_readme_name }}")
- name: Print provisioners duration
run: |
./images.CI/measure-provisioners-duration.ps1 `
-PackerLogPath "${{ runner.temp }}/packer-log.txt" `
-PrefixToPathTrim ${{ env.TemplateDirectoryPath }} `
-PrintTopNLongest 25
- name: Create release for VM deployment
run: |
./images.CI/linux-and-win/create-release.ps1 `
-BuildId ${{ github.run_number }} `
-Organization ${{ secrets.RELEASE_TARGET_ORGANIZATION }} `
-DefinitionId ${{ secrets.RELEASE_TARGET_DEFINITION_ID }} `
-Project ${{ secrets.RELEASE_TARGET_PROJECT }} `
-ImageName ${{ env.ImageType }} `
-AccessToken ${{ secrets.RELEASE_TARGET_TOKEN }}
- name: Clean up resources
if: ${{ always() }}
run: |
./images.CI/linux-and-win/cleanup.ps1 `
-ResourcesNamePrefix ${{ github.run_number }} `
-Image ${{ env.ImageType }} `
-StorageAccount ${{ secrets.AZURE_STORAGE_ACCOUNT }} `
-SubscriptionId ${{ secrets.AZURE_SUBSCRIPTION }} `
-ClientId ${{ secrets.CLIENT_ID }} `
-ClientSecret ${{ secrets.CLIENT_SECRET }} `
-TenantId ${{ secrets.AZURE_TENANT }}
-27
View File
@@ -1,27 +0,0 @@
run-name: Ubuntu20.04 - ${{ (github.event.pull_request.title || 'scheduled/manual run') }}
on:
workflow_dispatch:
inputs:
CUSTOM_REPOSITORY:
description: 'Custom repository (owner/repo)'
required: false
CUSTOM_REPOSITORY_COMMIT_HASH:
description: 'Commit hash'
required: false
pull_request_target:
types: labeled
paths:
- 'images/linux/**'
schedule:
- cron: '0 0 * * *'
jobs:
Ubuntu_2004:
if: ${{ (github.event.label.name == 'CI ubuntu-all') || (github.event.label.name == 'CI ubuntu-2004') || (github.event_name == 'workflow_dispatch') || (github.event_name == 'schedule') }}
uses: ./.github/workflows/ubuntu-win-generation.yml
with:
image_name: 'ubuntu2004'
image_readme_name: 'Ubuntu2004-Readme.md'
custom_repo: ${{ github.event.inputs.CUSTOM_REPOSITORY }}
custom_repo_commit_hash: ${{ github.event.inputs.CUSTOM_REPOSITORY_COMMIT_HASH }}
secrets: inherit
-27
View File
@@ -1,27 +0,0 @@
run-name: Ubuntu22.04 - ${{ (github.event.pull_request.title || 'scheduled/manual run') }}
on:
workflow_dispatch:
inputs:
CUSTOM_REPOSITORY:
description: 'Custom repository (owner/repo)'
required: false
CUSTOM_REPOSITORY_COMMIT_HASH:
description: 'Commit hash'
required: false
pull_request_target:
types: labeled
paths:
- 'images/linux/**'
schedule:
- cron: '0 0 * * *'
jobs:
Ubuntu_2204:
if: ${{ (github.event.label.name == 'CI ubuntu-all') || (github.event.label.name == 'CI ubuntu-2204') || (github.event_name == 'workflow_dispatch') || (github.event_name == 'schedule') }}
uses: ./.github/workflows/ubuntu-win-generation.yml
with:
image_name: 'ubuntu2204'
image_readme_name: 'Ubuntu2204-Readme.md'
custom_repo: ${{ github.event.inputs.CUSTOM_REPOSITORY }}
custom_repo_commit_hash: ${{ github.event.inputs.CUSTOM_REPOSITORY_COMMIT_HASH }}
secrets: inherit
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Update release for ${{ github.event.client_payload.ReleaseBranchName }}
uses: actions/github-script@v2
-27
View File
@@ -1,27 +0,0 @@
run-name: Windows 2019 - ${{ (github.event.pull_request.title || 'scheduled/manual run') }}
on:
workflow_dispatch:
inputs:
CUSTOM_REPOSITORY:
description: 'Custom repository (owner/repo)'
required: false
CUSTOM_REPOSITORY_COMMIT_HASH:
description: 'Commit hash'
required: false
pull_request_target:
types: labeled
paths:
- 'images/win/**'
schedule:
- cron: '0 0 * * *'
jobs:
Windows_2019:
if: ${{ (github.event.label.name == 'CI windows-all') || (github.event.label.name == 'CI windows-2019') || (github.event_name == 'workflow_dispatch') || (github.event_name == 'schedule') }}
uses: ./.github/workflows/ubuntu-win-generation.yml
with:
image_name: 'windows2019'
image_readme_name: 'Windows2019-Readme.md'
custom_repo: ${{ github.event.inputs.CUSTOM_REPOSITORY }}
custom_repo_commit_hash: ${{ github.event.inputs.CUSTOM_REPOSITORY_COMMIT_HASH }}
secrets: inherit
-27
View File
@@ -1,27 +0,0 @@
run-name: Windows 2022 - ${{ (github.event.pull_request.title || 'scheduled/manual run') }}
on:
workflow_dispatch:
inputs:
CUSTOM_REPOSITORY:
description: 'Custom repository (owner/repo)'
required: false
CUSTOM_REPOSITORY_COMMIT_HASH:
description: 'Commit hash'
required: false
pull_request_target:
types: labeled
paths:
- 'images/win/**'
schedule:
- cron: '0 0 * * *'
jobs:
Windows_2022:
if: ${{ (github.event.label.name == 'CI windows-all') || (github.event.label.name == 'CI windows-2022') || (github.event_name == 'workflow_dispatch') || (github.event_name == 'schedule') }}
uses: ./.github/workflows/ubuntu-win-generation.yml
with:
image_name: 'windows2022'
image_readme_name: 'Windows2022-Readme.md'
custom_repo: ${{ github.event.inputs.CUSTOM_REPOSITORY }}
custom_repo_commit_hash: ${{ github.event.inputs.CUSTOM_REPOSITORY_COMMIT_HASH }}
secrets: inherit
+1 -7
View File
@@ -267,9 +267,6 @@ paket-files/
.idea/
*.sln.iml
# VSCode settings
.vscode/settings.json
# CodeRush
.cr/
@@ -390,7 +387,4 @@ public
.dynamodb/
# visual studio code launch configuration
launch.json
# Ignore dynamic template
images/*/*-temp.json
launch.json
+3
View File
@@ -0,0 +1,3 @@
{
"files.trimTrailingWhitespace": false
}
+5 -5
View File
@@ -1,7 +1,7 @@
## Contributing
[fork]: https://github.com/actions/runner-images/fork
[pr]: https://github.com//actions/runner-images/compare
[fork]: https://github.com/actions/virtual-environments/fork
[pr]: https://github.com//actions/virtual-environments/compare
[code-of-conduct]: CODE_OF_CONDUCT.md
Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
@@ -32,13 +32,13 @@ Here are a few things you can do that will increase the likelihood of your pull
### General rules
- For every new tool add validation scripts and update software report script to make sure that it is included to documentation
- If the tool is available in other platforms (macOS, Windows, Linux), make sure you include it in as many as possible.
- If installing a few versions of the tool, consider putting the list of versions in the corresponding `toolset.json` file. It will help other customers to configure their builds flexibly. See [toolset-windows-2019.json](images/win/toolsets/toolset-2019.json) as example.
- If installing a few versions of the tool, consider putting the list of versions in the corresponding `toolset.json` file. It will help other customers to configure their builds flexibly. See [toolset-windows-2016.json](images/win/toolsets/toolset-2019.json) as example.
- Use consistent naming across all files
- Validation scripts should be simple and shouldn't change image content
### Windows
- Add a script that will install the tool and put the script in the `scripts/Installers` folder.
There are a bunch of helper functions that could simplify your code: `Choco-Install`, `Install-Binary`, `Install-VsixExtension`, `Start-DownloadWithRetry`, `Test-IsWin19`, `Test-IsWin22` (find the full list of helpers in [ImageHelpers.psm1](images/win/scripts/ImageHelpers/ImageHelpers.psm1)).
There are a bunch of helper functions that could simplify your code: `Choco-Install`, `Install-Binary`, `Install-VsixExtension`, `Start-DownloadWithRetry`, `Test-IsWin16`, `Test-IsWin19` (find the full list of helpers in [ImageHelpers.psm1](images/win/scripts/ImageHelpers/ImageHelpers.psm1)).
- Add a script that will validate the tool installation and put the script in the `scripts/Tests` folder.
We use [Pester v5](https://github.com/pester/pester) for validation scripts. If the tests for the tool are complex enough, create a separate `*.Tests.ps1`. Otherwise, use `Tools.Tests.ps1` for simple tests.
Add `Invoke-PesterTests -TestFile <testFileName> [-TestName <describeName>]` at the end of the installation script to make sure that your tests will be run.
@@ -49,7 +49,7 @@ Add `Invoke-PesterTests -TestFile <testFileName> [-TestName <describeName>]` at
Use existing scripts such as [github-cli.sh](images/linux/scripts/installers/github-cli.sh) as a starting point.
- Use [helpers](images/linux/scripts/helpers/install.sh) to simplify installation process.
- Validation part should `exit 1` if any issue with installation.
- Add changes to the software report generator `images/linux/scripts/SoftwareReport/SoftwareReport.Generator.ps1`. The software report generator is used to generate an image's README file, e.g. [Ubuntu2004-Readme.md](images/linux/Ubuntu2004-README.md) and it uses [MarkdownPS](https://github.com/Sarafian/MarkdownPS).
- Add changes to the software report generator `images/linux/scripts/SoftwareReport/SoftwareReport.Generator.ps1`. The software report generator is used to generate an image's README file, e.g. [Ubuntu1804-Readme.md](images/linux/Ubuntu1804-README.md) and it uses [MarkdownPS](https://github.com/Sarafian/MarkdownPS).
### macOS
macOS source lives in this repository and available for everyone. However, macOS image-generation CI doesn't support external contributions yet so we are not able to accept pull-requests for now.
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2023 GitHub
Copyright (c) 2021 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+42 -185
View File
@@ -1,202 +1,59 @@
# GitHub Actions Virtual Environments
This repository contains the source used to create the [virtual environments](https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners) for GitHub Actions hosted runners, as well as the VM images of [Microsoft-hosted agents](https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops#use-a-microsoft-hosted-agent) used for Azure Pipelines. To build a VM machine from this repo's source, see the [instructions](docs/create-image-and-azure-resources.md).
# GitHub Actions Runner Images
How to get in touch with us:
- To file a bug report, or request tools to be added/updated, please [open an issue using the appropriate template](https://github.com/actions/virtual-environments/issues/new/choose)
- If you want to share your thoughts about image configuration, installed software, or bring some idea, please, create a new topic in a [discussions section](https://github.com/actions/virtual-environments/discussions) for a corresponding category. Before making a new discussion please make sure no similar topics were created earlier.
**Table of Contents**
For general questions about using the virtual environments or writing your Actions workflow, please open requests in the [GitHub Actions Community Forum](https://github.community/c/github-actions/41).
- [About](#about)
- [Available Images](#available-images)
- [Announcements](#announcements)
- [Image Definitions](#image-definitions)
- [Image Releases](#image-releases)
- [Software and Image Support](#software-and-image-support)
- [How to Interact with the Repo](#how-to-interact-with-the-repo)
- [FAQs](#faqs)
## About
This repository contains the source code used to create the VM images for [GitHub-hosted runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners) used for Actions, as well as for [Microsoft-hosted agents](https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops#use-a-microsoft-hosted-agent) used for Azure Pipelines.
To build a VM machine from this repo's source, see the [instructions](docs/create-image-and-azure-resources.md).
## Available Images
| Image | YAML Label | Included Software | Rollout Progress of Latest Image Release |
## Available Environments
| Environment | YAML Label | Included Software | Latest Release & Rollout Progress |
| --------------------|---------------------|--------------------|---------------------|
| Ubuntu 22.04 | `ubuntu-latest` or `ubuntu-22.04` | [ubuntu-22.04] | [![status22](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=ubuntu22&badge=1)](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=ubuntu22&redirect=1)
| Ubuntu 20.04 | `ubuntu-20.04` | [ubuntu-20.04] | [![status20](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=ubuntu20&badge=1)](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=ubuntu20&redirect=1)
| macOS 13 [beta] | `macos-13` or `macos-13-xl`| [macOS-13] | [![statusumac13](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=macos-13&badge=1)](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=macos-13&redirect=1)
| macOS 12 | `macos-latest`, `macos-latest-xl`, `macos-12`, or `macos-12-xl`| [macOS-12] | [![statusumac12](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=macos-12&badge=1)](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=macos-12&redirect=1)
| macOS 11 | `macos-11`| [macOS-11] | [![statusmac11](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=macos-11&badge=1)](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=macos-11&redirect=1)
| Windows Server 2022 | `windows-latest` or `windows-2022` | [windows-2022] | [![statuswin22](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=windows-2022&badge=1)](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=windows-2022&redirect=1) |
| Windows Server 2019 | `windows-2019` | [windows-2019] | [![statuswin19](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=windows-2019&badge=1)](https://runnerimagesdeploymentstatus.azurewebsites.net/api/status?imageName=windows-2019&redirect=1)
| Ubuntu 20.04 | `ubuntu-latest` or `ubuntu-20.04` | [ubuntu-20.04] | [![](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=ubuntu20&badge=1)](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=ubuntu20&redirect=1)
| Ubuntu 18.04 | `ubuntu-18.04` | [ubuntu-18.04] | [![](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=ubuntu18&badge=1)](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=ubuntu18&redirect=1)
| macOS 11 | `macos-latest` or `macos-11`| [macOS-11] | [![](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=macos-11&badge=1)](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=macos-11&redirect=1)
| macOS 10.15 | `macos-10.15` | [macOS-10.15] | [![](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=macos-10.15&badge=1)](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=macos-10.15&redirect=1)
| Windows Server 2022 | `windows-2022` | [windows-2022] | [![](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=windows-2022&badge=1)](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=windows-2022&redirect=1) |
| Windows Server 2019 | `windows-latest` or `windows-2019` | [windows-2019] | [![](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=windows-2019&badge=1)](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=windows-2019&redirect=1)
| Windows Server 2016 | `windows-2016` | [windows-2016] | [![](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=windows-2016&badge=1)](https://actionvirtualenvironmentsstatus.azurewebsites.net/api/status?imageName=windows-2016&redirect=1)
### Label scheme
<b>Note:</b> Beta and Preview images are provided "as-is", "with all faults" and "as available" and are excluded from the service level agreement and warranty. Beta and Preview images may not be covered by customer support.
- In general the `-latest` label is used for the latest OS image version that is GA
- Before moving the`-latest` label to a new OS version we will announce the change and give sufficient lead time for users to update their workflows
[ubuntu-22.04]: https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md
[ubuntu-20.04]: https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2004-Readme.md
[windows-2022]: https://github.com/actions/runner-images/blob/main/images/win/Windows2022-Readme.md
[windows-2019]: https://github.com/actions/runner-images/blob/main/images/win/Windows2019-Readme.md
[macOS-11]: https://github.com/actions/runner-images/blob/main/images/macos/macos-11-Readme.md
[macOS-12]: https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md
[macOS-13]: https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md
[macOS-10.15]: https://github.com/actions/runner-images/blob/main/images/macos/macos-10.15-Readme.md
[self-hosted runners]: https://help.github.com/en/actions/hosting-your-own-runners
## Announcements
See notable upcoming changes by viewing issues with the [Announcement](https://github.com/actions/runner-images/labels/Announcement) label.
## Image Definitions
### Beta
The purpose of a Beta is to collect feedback on an image before it is released to GA. The goal of a Beta is to identify and fix any potential issues that exist on that
image. Images are updated on a weekly cadence. Any workflows that run on a beta image do not fall under the customer [SLA](https://github.com/customer-terms/github-online-services-sla) in place for Actions.
Customers choosing to use Beta images are encouraged to provide feedback in the runner-images repo by creating an issue. A Beta may take on different availability, i.e. public vs private.
### GA
A GA (General Availability) image has been through a Beta period and is deemed ready for general use. Images are updated on a weekly cadence. In order to be moved to
GA the image must meet the following criteria:
1. Has been through a Beta period (public or private)
2. Most major software we install on the image has a compatible
version for the underlying OS and
3. All major bugs reported during the Beta period have been addressed.
This image type falls under the customer [SLA](https://github.com/customer-terms/github-online-services-sla) for actions. GA images are eventually deprecated according to our guidelines as we only support the
latest 2 versions of an OS.
#### Latest Migration Process
GitHub Actions and Azure DevOps use the `-latest` YAML label (ex: `ubuntu-latest`, `windows-latest`, and `macos-latest`). These labels point towards the newest stable OS version available.
The `-latest` migration process is gradual and happens over 1-2 months in order to allow customers to adapt their workflows to the newest OS version. During this process, any workflow using the `-latest` label, may see changes in the OS version in their workflows or pipelines. To avoid unwanted migration, users can specify a specific OS version in the yaml file (ex: macos-12, windows-2022, ubuntu-22.04).
## Image Releases
*How to best follow along with changes*
1. Find the latest releases for this repository [here.](https://github.com/actions/runner-images/releases)
2. Subscribe to the releases coming out of this repository, instructions [here.](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository)
3. Upcoming changes: A pre-release is created when the deployment of an image has started. As soon as the deployment is finished, the pre-release is converted to a release. If you have subscribed to releases, you will get notified of pre-releases as well.
- You can also track upcoming changes using the [awaiting-deployment](https://github.com/actions/runner-images/labels/awaiting-deployment) label.
4. For high impact changes, we will post these in advance to the GitHub Changelog on our [blog](https://github.blog/changelog/) and on [twitter](https://twitter.com/GHchangelog).
- Ex: breaking changes, GA or deprecation of images
*Cadence*
- We typically deploy weekly updates to the software on the runner images.
## Software and Image Support
### Support Policy
- Tools and versions will typically be removed 6 months after they are deprecated or have reached end-of-life
- We support (at maximum) 2 GA images and 1 beta image at a time. We begin the deprecation process of the oldest image label once the newest OS image label has been released to GA.
- The images generally contain the latest versions of packages installed except for Ubuntu LTS where we mostly rely on the Canonical-provided repositories.
- Popular tools can have several versions installed side-by-side with the following strategy:
| Tool name | Installation strategy |
|-----------|-----------------------|
| Docker images | not more than 3 latest LTS OS\tool versions. New images or new versions of current images are added using the standard tool request process |
| Java | all LTS versions |
| Node.js | 3 latest LTS versions |
| Go | 3 latest minor versions |
| Python <br/> Ruby | 5 most popular `major.minor` versions |
| PyPy | 3 most popular `major.minor` versions |
| .NET Core | 2 latest LTS versions and 1 latest version. For each feature version only latest patch is installed |
| GCC <br/> GNU Fortran <br/> Clang <br/> GNU C++ | 3 latest major versions |
| Android NDK | 1 latest non-LTS, 2 latest LTS versions |
| Xcode | - all OS compatible versions side-by-side <br/> - for beta, GM versions - latest beta only <br/> - old patch versions are deprecated in 3 months |
### Package managers usage
We use third-party package managers to install software during the image generation process. The table below lists the package managers and the software installed.
> **Note**: third-party repositories are re-evaluated every year to identify if they are still useful and secure.
| Operating system | Package manager | Third-party repos and packages |
| :--- | :---: | ---: |
| Ubuntu | [APT](https://wiki.debian.org/Apt) | [Eclipse-Temurin (Adoptium)](https://packages.adoptium.net/artifactory/deb) </br> [Erlang](https://packages.erlang-solutions.com/ubuntu) </br>[Firefox](https://launchpad.net/~mozillateam/+archive/ubuntu/ppa) </br> [gcc, gfortran](https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test) </br> [git](https://launchpad.net/~git-core/+archive/ubuntu/ppa) </br> [HHvm](https://dl.hhvm.com/ubuntu) </br> [PHP](https://launchpad.net/~ondrej/+archive/ubuntu/php) (Ubuntu 20 only) </br> [Mono](https://download.mono-project.com/repo/ubuntu) </br> [PostgreSQL](https://apt.postgresql.org/pub/repos/apt) </br> [R](https://cloud.r-project.org/bin/linux/ubuntu) |
| | [pipx](https://pypa.github.io/pipx) | ansible-core </br>yamllint |
| Windows | [Chocolatey](https://chocolatey.org) | No third-party repos installed |
| macOS | [Homebrew](https://brew.sh) | [aws-cli v2](https://github.com/aws/homebrew-tap) </br> [azure/bicep](https://github.com/Azure/homebrew-bicep) </br> [mongodb/brew](https://github.com/mongodb/homebrew-brew) |
| | [pipx](https://pypa.github.io/pipx/) | yamllint |
### Image Deprecation Policy
- Images begin the deprecation process of the oldest image label once a new GA OS version has been released.
- Deprecation process begins with an announcement that sets a date for deprecation
- As it gets closer to the date, GitHub begins doing scheduled brownouts of the image
- During this time there will be an Announcement pinned in the repo to remind users of the deprecation.
- Finally GitHub will deprecate the image and it will no longer be available
### Preinstallation Policy
In general, these are the guidelines we follow when deciding what to pre-install on our images:
- Popularity: widely-used tools and ecosystems will be given priority.
- Latest Technology: recent versions of tools will be given priority.
- Deprecation: end-of-life tools and versions will not be added.
- Licensing: MIT, Apache, or GNU licenses are allowed.
- Time & Space on the Image: we will evaluate how much time is saved and how much space is used by having the tool pre-installed.
- Support: If a tool requires the support of more than one version, we will consider the cost of this maintenance.
### Default Version Update Policy
- In general, once a new version is installed on the image, we announce the default version update 2 weeks prior to deploying it.
- For potentially dangerous updates, we may extend the timeline up to 1 month between the announcement and deployment.
## How to Interact with the Repo
- **Issues**: To file a bug report, or request tools to be added/updated, please [open an issue using the appropriate template](https://github.com/actions/runner-images/issues/new/choose)
- **Discussions**: If you want to share your thoughts about image configuration, installed software, or bring a new idea, please create a new topic in a [discussion](https://github.com/actions/runner-images/discussions) for a corresponding category. Before making a new discussion please make sure no similar topics were created earlier.
- For general questions about using the runner images or writing your Actions workflow, please open requests in the [GitHub Actions Community Forum](https://github.community/c/github-actions/41).
## FAQs
<details>
<summary><b><i>What images are available for GitHub Actions and Azure DevOps?</b></i></summary>
The availability of images for GitHub Actions and Azure DevOps is the same. However, deprecation policies may differ. See documentation for more details:
***What images are available for GitHub Actions and Azure DevOps?***
The availability of images for GitHub Actions and Azure DevOps is different. See documentation for more details:
- [GitHub Actions](https://docs.github.com/en/free-pro-team@latest/actions/reference/specifications-for-github-hosted-runners#supported-runners-and-hardware-resources)
- [Azure DevOps](https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml#software)
</details>
<details>
<summary><b><i>What image version is used in my build?</b></i></summary>
***What image version is used in my build?*** Usually, image deployment takes 3-4 days, and documentation in the `main` branch is only updated when deployment is finished. To find out which image version and what software versions are used in a specific build, see `Set up job` (GitHub Actions) or `Initialize job` (Azure DevOps) step log.
Usually, image deployment takes 2-3 days, and documentation in the `main` branch is only updated when deployment is finished. To find out which image version and what software versions are used in a specific build, see `Set up job` (GitHub Actions) or `Initialize job` (Azure DevOps) step log.
<img width="1440" alt="actions-runner-image" src="https://user-images.githubusercontent.com/56982181/169595536-91a8a79b-d5e0-47d1-a736-510cff6cfb83.png">
</details>
***Looking for other Linux distributions?*** We do not plan to offer other Linux distributions. We recommend using Docker if you'd like to build using other distributions with the hosted virtual environments. Alternatively, you can leverage [self-hosted runners] and fully customize your environment to your needs.
<details>
<summary><b><i>Looking for other Linux distributions?</b></i></summary>
***How to contribute to macOS source?*** macOS source lives in this repository and available for everyone. However, MacOS image-generation CI doesn't support external contributions yet so we are not able to accept pull-requests for now.
We are in the process of preparing MacOS CI to accept contributions. Until then, we appreciate your patience and ask you continue to make tool requests by filing issues.
We do not plan to offer other Linux distributions. We recommend using Docker if you'd like to build using other distributions with the hosted runner images. Alternatively, you can leverage [self-hosted runners] and fully customize your VM image to your needs.
</details>
## Updates to virtual environments
*Cadence*
<details>
<summary><b><i>How do I contribute to the macOS source?</b></i></summary>
We typically deploy weekly updates to the software on the virtual environments.
For some tools, we always install the latest at the time of the deployment; for others,
we pin the tool to specific version(s).
macOS source lives in this repository and is available for everyone. However, macOS image-generation CI doesn't support external contributions yet so we are not able to accept pull-requests for now.
*Following Along / Change Notifications*
We are in the process of preparing macOS CI to accept contributions. Until then, we appreciate your patience and ask you to continue to make tool requests by filing issues.
</details>
* **High Impact Changes** (ex. breaking changes, new or deprecated environments) will be posted to the GitHub Changelog on our [blog](https://github.blog/changelog/) and on [twitter](https://twitter.com/GHchangelog).
* **Low Impact Changes** will be pinned in this repository and marked with the [Announcement](https://github.com/actions/virtual-environments/labels/Announcement) label.
* **Regular Weekly Rhythm** can be followed by watching [Releases](https://github.com/actions/virtual-environments/releases). Pre-release is created when deployment is started. As soon as deployment is finished, it is converted to release.
You can also track upcoming changes using the [awaiting-deployment](https://github.com/actions/virtual-environments/labels/awaiting-deployment) label.
<details>
<summary><b><i>How does GitHub determine what tools are installed on the images?</b></i></summary>
[ubuntu-20.04]: https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-Readme.md
[ubuntu-18.04]: https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-Readme.md
[windows-2022]: https://github.com/actions/virtual-environments/blob/main/images/win/Windows2022-Readme.md
[windows-2019]: https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md
[windows-2016]: https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md
[macOS-11]: https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11-Readme.md
[macOS-10.15]: https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md
[self-hosted runners]: https://help.github.com/en/actions/hosting-your-own-runners
For some tools, we always install the latest at the time of the deployment; for others, we pin the tool to specific version(s). For more details please see the [Preinstallation Policy](#preinstallation-policy)
</details>
<details>
<summary><b><i>How do I request that a new tool be pre-installed on the image?</b></i></summary>
Please create an issue and get an approval from us to add this tool to the image before creating the pull request.
</details>
## Software and image guidelines
To learn more about tools and images support policy, see the [guidelines](./docs/software-and-images-guidelines.md).
+89 -189
View File
@@ -1,178 +1,93 @@
# GitHub Actions Runner Images
# Virtual-Environments
The virtual-environments project uses [Packer](https://www.packer.io/) to generate disk images for the following platforms: Windows 2016/2019/2022, Ubuntu 18.04/20.04.
Each image is configured through a JSON template that Packer understands and which specifies where to build the image (Azure in this case), and what scripts to run to install software and prepare the disk.
The Packer process initializes a connection to Azure subscription via Azure CLI, and automatically creates the temporary Azure resources required to build the source VM(temporary resource group, network interfaces, and VM from the "clean" image specified in the template).
If the VM deployment succeeds, the build agent connects to the VM and starts to execute installation steps from the JSON template.
If any step in the JSON template fails, image generation will be aborted and the temporary VM will be terminated. Packer will also attempt to cleanup all the temporary resources it created (unless otherwise told).
After successful image generation, a snapshot of the temporary VM will be converted to VHD image and then uploaded to the specified Azure Storage Account.
The runner-images project uses [Packer](https://www.packer.io/) to generate disk images for Windows 2019/2022 and Ubuntu 20.04/22.04.
## Prerequisites and Image-generation
### Build Agent requirements
- `OS` - Windows/Linux
- `packer` - Can be downloaded from https://www.packer.io/downloads
- `PowerShell 5.0 or higher` or `PSCore` for linux distributes.
- `Azure CLI ` - https://docs.microsoft.com/en-us/cli/azure/install-azure-cli
- `Azure Az Powershell module` - https://docs.microsoft.com/en-us/powershell/azure/install-az-ps
- `Git for Windows` - https://gitforwindows.org/
Each image is configured by a JSON or HCL2 Packer template that specifies where to build the image (Azure in this case)
and what steps to run to install software and prepare the disk.
> To connect to a temporary VM packer uses WinRM or SSH connections on public IP interfaces.
If you use a build agent located in an Azure subscription, please make sure that HTTPS/SSH ports are allowed for incoming/outgoing connections.
In case of firewall restrictions, prohibiting connections from public addresses, private virtual network resources can be deployed and passed as arguments to the packer. This approach allows virtual machines to use private connections inside VLAN.
The Packer process initializes a connection to Azure subscription using Azure CLI and creates temporary resources
required for the build process: resource group, network interfaces and virtual machine from the "clean" image specified in the template.
### Service principal
Packer uses Service Principal to authorize in Azure infrastructure. To setup image-generation CI or use packer manually — SP with full read-write permissions for selected Azure subscription needed.
Detailed instruction can be found in [Azure documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal)
If the VM deployment succeeds, Packer connects it using ssh or WinRM and begins executing installation steps from the template one-by-one.
If any step fails, image generation is aborted and the temporary VM is terminated.
Packer also attempts to cleanup all the temporary resources it created (unless otherwise configured).
### Prepare environment and image deployment
#### How to prepare Windows build agent
Local machine or [Azure VM](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/quick-create-cli) can be used as a build agent.
After successful completion of all installation steps Packer converts snapshot of the temporary VM to VHD image
and uploads it to the specified Azure Storage Account.
## Build agent preparation
Build agent is a machine where Packer process will be started.
You can use any physical or virtual machine running OS Windows or Linux.
Of course you may also use [Azure VM](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/quick-create-cli).
In any case you will need these software installed:
- Packer 1.8.2 or higher.
Download and install it manually from [here](https://www.packer.io/downloads) or use [Chocolatey](https://chocolatey.org/):
```powershell
choco install packer
```
- Git.
For Linux - install the latest version from your distro's package repo.
For Windows - download and install it from [here](https://gitforwindows.org/) of use [Chocolatey](https://chocolatey.org/):
```powershell
choco install git -params '"/GitAndUnixToolsOnPath"'
```
- Powershell 5.0 or higher.
In Windows you already have it.
For Linux follow instructions [here](https://learn.microsoft.com/en-us/windows-server/administration/linux-package-repository-for-microsoft-software)
to add Microsoft's Linux Software Repository and then install package `powershell`.
- Azure CLI.
Follow instructions [here](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli).
Or if you use Windows you may run this command in Powershell instead:
```powershell
Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi
Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet'; rm .\AzureCLI.msi
```
- [Az Powershell module](https://docs.microsoft.com/en-us/powershell/azure/install-az-ps).
Run this command in Powershell:
```powershell
Install-Module -Name Az -Repository PSGallery -Force
```
## Automated image generation
This repo bundles script that automates image generation process.
You only need a build agent configured as described above and active Azure subsctiption.
We suggest to start with UbuntuMinimal image because it includes only a minimal set of required software and builds in less then half an hour.
All steps here are supposed to run in Powershell.
First, clone runner-images repository and change directory:
```powershell
git clone https://github.com/actions/runner-images.git
Set-Location runner-images
Download `packer` from https://www.packer.io/downloads, or install it via Chocolately.
```
choco install packer
```
Then import [GenerateResourcesAndImage](../helpers/GenerateResourcesAndImage.ps1) script from `helpers` subdirectory:
Install the Azure Az PowerShell module - https://docs.microsoft.com/en-us/powershell/azure/install-az-ps.
```
Install-Module -Name Az -Repository PSGallery -Force
```
Install Azure CLI - https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-windows?view=azure-cli-latest&tabs=azure-cli.
```
Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi; Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet'; rm .\AzureCLI.msi
```
Download Virtual-Environments repository.
```
Set-Location c:\
git clone https://github.com/actions/virtual-environments.git
```
Import [GenerateResourcesAndImage](../helpers/GenerateResourcesAndImage.ps1) script from `/helpers` folder, and run `GenerateResourcesAndImage` function via Powershell.
```
Set-Location C:\virtual-environments
```powershell
Import-Module .\helpers\GenerateResourcesAndImage.ps1
GenerateResourcesAndImage -SubscriptionId {YourSubscriptionId} -ResourceGroupName "myTestResourceGroup" -ImageGenerationRepositoryRoot "$pwd" -ImageType Ubuntu1804 -AzureLocation "East US"
```
Where:
- `SubscriptionId` - The Azure subscription Id where resources will be created.
- `ResourceGroupName` - The Azure resource group name where the Azure resources will be created.
- `ImageGenerationRepositoryRoot` - The root path of the image generation repository source.
- `ImageType` - The type of the image being generated. Valid options are: "Windows2016", "Windows2019", "Windows2022", "Ubuntu1804", "Ubuntu2004".
- `AzureLocation` - The location of the resources being created in Azure. For example "East US".
The function automatically creates all required Azure resources and kicks off packer image generation for the selected image type.
For optional authentication via service principal make sure to provide the following params — `AzureClientId`, `AzureClientSecret`, `AzureTenantId`, so the whole command will be:
```
GenerateResourcesAndImage -SubscriptionId {YourSubscriptionId} -ResourceGroupName "myTestResourceGroup" -ImageGenerationRepositoryRoot "$pwd" -ImageType Ubuntu1804 -AzureLocation "East US" -AzureClientId {AADApplicationID} -AzureClientSecret {AADApplicationSecret} -AzureTenantId {AADTenantID}
```
Finally, run `GenerateResourcesAndImage` function setting mandatory arguments: image type and where to create resources:
- `SubscriptionId` - your Azure Subscription ID
- `ResourceGroupName` - name of the resource group that will be created within your subscription (e.g. "imagegen-test")
- `AzureLocation` - location where resources will be created (e.g. "East US")
- `ImageType` - what image to build (we suggest choosing "UbuntuMinimal" here, other valid options are "Windows2019", "Windows2022", "Ubuntu2004", "Ubuntu2204")
> :warning: When running `GenerateResourcesAndImage` in PowerShell 7.3, following command should be executed first:
> ```powershell
> $PSNativeCommandArgumentPassing = 'Legacy'
> ```
This function automatically creates all required Azure resources and kicks off packer image generation for the selected image type.
When image is ready you may proceed to [deployment](#generated-machine-deployment)
## Image generation customization
Function `GenerateResourcesAndImage` accepts a bunch of arguments that may help you generating image in your specific environment.
For example, you may want that all the resources involved in image generation process are tagged.
In this case pass a HashTable of tags as a value for `Tags` parameter.
If you don't want function to authenticate interactively, you should create Service Principal and invoke the function with parameters `AzureClientId`, `AzureClientSecret` and `AzureTenantId`.
You can find more details [in corresponding section below](#azure-subscription-authentication).
Use `get-help GenerateResourcesAndImage -Detailed` for the complete list of parameters available.
### Network security
To connect to a temporary virtual machine Packer uses WinRM or SSH.
If your build agent is located outside of the Azure subscription where temporary VM is created, the public network interface and public IP address is used.
Make sure that firewalls are configured properly and WinRM (tcp port 5986) and ssh (tcp port 22) connections are allowed both outgoing for build agent and incoming for temporary VM.
Also if you don't want temporary VM to be accessible from everywhere, set `RestrictToAgentIpAddress` parameter value to `$true`
to setup firewall rules allowing access only from your build agent public IP address.
If your build agent and temporary VM are in the same subscription you can configure Packer to connect using private virtual network.
To achieve that set proper values for environment variables `VNET_RESOURCE_GROUP`, `VNET_NAME` and `VNET_SUBNET`.
### Azure subscription authentication
Packer uses Service Principal to authenticate in Azure infrastructure.
For more information about Service Principals refer to
[Azure documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal).
Function `GenerateResourcesAndImage` is able to create Service Principle to be used by Packer.
It uses Connect-AzAccount cmdlet that invokes interactive authentication process by default.
If you don't want to use interactive authentication you should create Service Principal with full read-write permissions for selected Azure subscription on your own
and provide proper values for parameters `AzureClientId`, `AzureClientSecret` and `AzureTenantId`.
Here is an example of how to create Service Principle using Az Powershell module:
```powershell
$credentials = [Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPasswordCredential]@{
StartDateTime = Get-Date
EndDateTime = (Get-Date).AddDays(7)
}
$sp = New-AzADServicePrincipal -DisplayName "imagegen-app"
$appCred = New-AzADAppCredential -ApplicationId $sp.AppId -PasswordCredentials $credentials
Start-Sleep -Seconds 30
New-AzRoleAssignment -RoleDefinitionName "Contributor" -PrincipalId $sp.Id
Start-Sleep -Seconds 30
@{
ClientId = $sp.AppId
ClientSecret = $appCred.SecretText
TenantId = (Get-AzSubscription -SubscriptionId $SubscriptionId).TenantId
}
```
## Generated machine deployment
*Please, check synopsis of `GenerateResourcesAndImage` for details about non-mandatory parameters.*
#### Generated VM Deployment
After the successful image generation, Virtual Machine can be created from the generated VHD using [CreateAzureVMFromPackerTemplate](../helpers/CreateAzureVMFromPackerTemplate.ps1) script.
```powershell
```
Set-Location C:\virtual-environments
Import-Module .\helpers\CreateAzureVMFromPackerTemplate.ps1
CreateAzureVMFromPackerTemplate -SubscriptionId {YourSubscriptionId} -ResourceGroupName {ResourceGroupName} -TemplateFile "C:\BuildVmImages\temporaryTemplate.json" -VirtualMachineName "testvm1" -AdminUsername "shady1" -AdminPassword "SomeSecurePassword1" -AzureLocation "eastus"
```
Where:
- `SubscriptionId` - The Azure subscription Id where resources will be created.
- `ResourceGroupName` - The Azure resource group name where the Azure virtual machine will be created.
- `TemplateFilePath` - The path to the json ARM-template generated by packer during image generation locally.*
- `TemplateFilePath` - The path to the json ARM-template generated by packer during image generation locally.*
- `VirtualMachineName` - The name of the virtual machine to be generated.
- `AdminUserName` - The administrator username for the virtual machine to be created.
- `AdminPassword` - The administrator password for the virtual machine to be created.
@@ -182,27 +97,19 @@ Where:
The function creates an Azure VM from a template and generates network resources in Azure to make the VM accessible.
## Manual image generation
If you want more control over image generation process you may run Packer directly. This section describes variables defined in Packer template. Some of them may be set using environment variabes.
### Required variables
| Template var | Env var | Description
| ------------ | ------- | -----------
| `subscription_id` | `ARM_SUBSCRIPTION_ID` | Subscription under which the build will be performed.
| `client_id` | `ARM_CLIENT_ID` | The Active Directory service principal associated with your builder.
| `client_secret` | `ARM_CLIENT_SECRET` | The password or secret for your service principal; may be omitted if `client_cert_path` is set.
| `client_cert_path` | `ARM_CLIENT_CERT_PATH` | The location of a PEM file containing a certificate and private key for service principal; may be omitted if `client_secret` is set.
| `location` | `ARM_RESOURCE_LOCATION` | Azure datacenter in which your VM will build.
| `resource_group` | `ARM_RESOURCE_GROUP` | Resource group under which the final artifact will be stored.
| `storage_account` | `ARM_STORAGE_ACCOUNT` | Storage account under which the final artifact will be stored.
### Optional variables
## Additional
### User variables
The Packer template includes `variables` section containing user variables used in image generation. Each variable is defined as a key/value strings. User variables can be passed to packer via predefined environment variables, or as direct arguments, in case if packer started manually.
- `build_resource_group_name` - Specify an existing resource group to run the build in it. By default, a temporary resource group will be created and destroyed as part of the build. If you do not have permission to do so, use build_resource_group_name to specify an existing resource group to run the build in it.
- `client_id` - The application ID of the AAD Service Principal. Requires `client_secret`.
- `object_id` - The object ID for the AAD SP. Will be derived from the oAuth token if empty.
- `client_secret` - A password/secret registered for the AAD SP.
- `subscription_id` - The subscription to use.
- `tenant_id` - The Active Directory tenant identifier with which your `client_id` and `subscription_id` are associated. If not specified, `tenant_id` will be looked up using `subscription_id`.
- `resource_group` - Resource group under which the final artifact will be stored.
- `storage_account` - Storage account under which the final artifact will be stored.
- `location` - Azure datacenter in which your VM will be built.
- `temp_resource_group_name` - Name assigned to the temporary resource group created during the build. If this value is not set, a random value will be assigned. This resource group is deleted at the end of the build.
- `private_virtual_network_with_public_ip` - This value allows you to set a `virtual_network_name` and obtain a public IP. If this value is not set and `virtual_network_name` is defined Packer is only allowed to be executed from a host on the same subnet / virtual network.
- `virtual_network_name` - Use a pre-existing virtual network for the VM. This option enables private communication with the VM, no public IP address is used or provisioned (unless you set `private_virtual_network_with_public_ip`).
@@ -211,7 +118,6 @@ If you want more control over image generation process you may run Packer direct
- `capture_name_prefix` - VHD prefix. The final artifacts will be named PREFIX-osDisk.UUID and PREFIX-vmTemplate.UUID.
### Builder variables
The `builders` section contains variables for the `azure-arm` builder used in the project. Most of the builder variables are inherited from the `user variables` section, however, the variables can be overwritten to adjust image-generation performance.
- `vm_size` - Size of the VM used for building. This can be changed when you deploy a VM from your VHD.
@@ -221,11 +127,9 @@ The `builders` section contains variables for the `azure-arm` builder used in th
**Detailed Azure builders documentation can be found in [packer documentation](https://www.packer.io/docs/builders/azure).**
### Toolset
Configuration for some installed software is located in `toolset.json` files. These files define the list of Ruby, Python, Go versions, the list of PowerShell modules and VS components that will be installed to image. They can be changed if these tools are not required to reduce image generation time or image size.
Generated tool versions and details can be found in related projects:
- [Python](https://github.com/actions/python-versions/)
- [Go](https://github.com/actions/go-versions)
- [Node](https://github.com/actions/node-versions)
@@ -235,30 +139,24 @@ Generated tool versions and details can be found in related projects:
> :warning: These scripts are intended to run on a VM deployed in Azure
The user, created during the image generation, does not exist in the result VHD hence some configuration files related to the user's home directory need to be changed as well as the file permissions for some directories. Scripts for that are located in the `post-generation` folder in the repository:
- Windows: <https://github.com/actions/runner-images/tree/main/images/win/post-generation>
- Linux: <https://github.com/actions/runner-images/tree/main/images/linux/post-generation>
- Windows: https://github.com/actions/virtual-environments/tree/main/images/win/post-generation
- Linux: https://github.com/actions/virtual-environments/tree/main/images/linux/post-generation
**Note:** The default user for Linux should have `sudo privileges`.
The scripts are copied to the VHD during the image generation process to the following paths:
- Windows: `C:\post-generation`
- Linux: `/opt/post-generation`
- Linux: `/opt/post-generation`
#### Running scripts
- Ubuntu
##### Ubuntu
```bash
sudo su -c "find /opt/post-generation -mindepth 1 -maxdepth 1 -type f -name '*.sh' -exec bash {} \;"
```
sudo su -c "find /opt/post-generation -mindepth 1 -maxdepth 1 -type f -name '*.sh' -exec bash {} \;"
- Windows
##### Windows
```powershell
Get-ChildItem C:\post-generation -Filter *.ps1 | ForEach-Object { & $_.FullName }
```
Get-ChildItem C:\post-generation -Filter *.ps1 | ForEach-Object { & $_.FullName }
#### Script details
@@ -267,11 +165,13 @@ The scripts are copied to the VHD during the image generation process to the fol
- **cleanup-logs.sh** - removes all build process logs from the machine
- **environment-variables.sh** - replaces `$HOME` with the default user's home directory for environmental variables related to the default user home directory
- **homebrew-permissions.sh** - Resets homebrew repository directory by running `git reset --hard` to make the working tree clean after chmoding /home and changes the repository directory owner to the current user
- **rust-permissions.sh** - fixes permissions for the Rust folder. Detailed issue explanation is provided in [runner-images/issues/572](https://github.com/actions/runner-images/issues/572).
- **rust-permissions.sh** - fixes permissions for the Rust folder. Detailed issue explanation is provided in [virtual-environments/issues/572](https://github.com/actions/virtual-environments/issues/572).
##### Windows
- **GenerateIISExpressCertificate.ps1** - generates and imports a certificate to run applications with IIS Express through HTTPS
- **Choco.ps1** - contains dummy command to cleanup orphaned packages to avoid initial delay for future choco commands
- **Dotnet.ps1** - adds `$env:USERPROFILE\.dotnet\tools` directory to the PATH
- **InternetExplorerConfiguration** - turns off the Internet Explorer Enhanced Security feature
- **Msys2FirstLaunch.ps1** - initializes bash user profile in MSYS2
- **RustJunction.ps1** - creates Rust junction points to cargo and rustup folders
- **VSConfiguration.ps1** - performs initial Visual Studio configuration
+38
View File
@@ -0,0 +1,38 @@
# Software and image guidelines
## Software preinstallation policy
In general, these are the guidelines we consider when deciding what to pre-install:
- Tools and ecosystems that are broadly popular and widely-used will be given priority.
- Recent versions of tools will be given priority over older versions.
- Tools and versions that are deprecated or have reached end-of-life will not be added.
- If a tool can be installed during the build, we will evaluate how much time is saved
and how much space is used by having the tool pre-installed.
- MIT, Apache, and GNU licenses are ok, anything else we'll have to check with lawyers.
- If a tool takes much space we will evaluate space usage and provide a decision if this tool can be pre-installed.
- If a tool requires the support of more than one version, we will consider the cost of this maintenance, how often new versions bring dangerous updates.
**Note:** For new tools, please, create an issue and get an approval from us to add this tool to the image before creating the pull request.
## Software and images support policy
These are the guidelines we follow in software and images supporting routine:
- Tools and versions will typically be removed 6 months after they are deprecated or have reached end-of-life.
- We support at least 2 latest OS versions (LTS only for Ubuntu) and initiate deprecation process for the oldest one when image usage drops below 5%.
- The images generally contain the latest versions of packages installed except for Ubuntu LTS where we rely on the Canonical-provided repositories mostly.
- Popular tools can have several versions installed side-by-side with the following strategy:
| Tool name | Installation strategy |
|-----------|-----------------------|
| Docker images | not more than 3 latest LTS OS\tool versions. New images or new versions of current images are added using the standard tool request process |
| Java | all LTS versions |
| Node.js | 3 latest LTS versions |
| Go | 3 latest minor versions |
| Python <br/> Ruby | 5 most popular `major.minor` versions |
| PyPy | 3 most popular `major.minor` versions |
| .NET Core | 2 latest LTS versions and 1 latest version. For each feature version only latest patch is installed |
| GCC <br/> GNU Fortran <br/> Clang <br/> GNU C++ | 3 latest major versions |
| Android NDK | 1 latest non-LTS, 2 latest LTS versions |
| Xcode | - all OS compatible versions side-by-side <br/> - for beta, GM versions - latest beta only <br/> - old patch versions are deprecated in 3 months |
## Software default versions update policy for tools with multiple versions installed
In general, once a new version is installed on the image, we announce the default version update 2 weeks prior to deploying it to give time to adapt to upcoming changes. For potentially dangerous updates, we can extend the timeline up to 1 month between the announcement and deployment.
+5 -5
View File
@@ -28,7 +28,7 @@ Function CreateAzureVMFromPackerTemplate {
The location where the Azure virtual machine will be provisioned. Example: "eastus"
.EXAMPLE
CreateAzureVMFromPackerTemplate -SubscriptionId {YourSubscriptionId} -ResourceGroupName {ResourceGroupName} -TemplateFile "C:\BuildVmImages\temporaryTemplate.json" -VirtualMachineName "testvm1" -AdminUsername "shady1" -AdminPassword "SomeSecurePassword1" -AzureLocation "eastus"
CreateAzureVMFromPackerTemplate -SubscriptionId {YourSubscriptionId} -ResourceGroupName {ResourceGroupName} -TemplateFile "C:\BuildVmImages\temporaryTemplate.json" -VirtualMachineName "testvm1" -AdminUsername "shady1" -AdminPassword "SomeSecurePassword1" -AzureLocation "eastus"
#>
param (
[Parameter(Mandatory = $True)]
@@ -63,14 +63,14 @@ Function CreateAzureVMFromPackerTemplate {
$networkId = ($nic | ConvertFrom-Json).NewNIC.id
Write-Host "`nCreating a public IP address"
($publicIp = az network public-ip create -g $ResourceGroupName -l $AzureLocation -n $publicIpName --allocation-method Static --sku Basic --version IPv4 --subscription $subscriptionId -o json)
($publicIp = az network public-ip create -g $ResourceGroupName -l $AzureLocation -n $publicIpName --allocation-method Static --sku Standard --version IPv4 --subscription $subscriptionId -o json)
$publicIpId = ($publicIp | ConvertFrom-Json).publicIp.id
Write-Host "`nAdding the public IP to the NIC"
az network nic ip-config update -g $ResourceGroupName -n ipconfig1 --nic-name $nicName --public-ip-address $publicIpId --subscription $subscriptionId
Write-Host "`nCreating the VM"
az deployment group create -g $ResourceGroupName -n $VirtualMachineName --subscription $subscriptionId --template-file $templateFilePath --parameters vmSize=$vmSize vmName=$VirtualMachineName adminUserName=$AdminUsername adminPassword=$AdminPassword networkInterfaceId=$networkId
Write-Host "`nCreated in ${ResourceGroupName}:`n vnet ${vnetName}`n subnet ${subnetName}`n nic ${nicName}`n publicip ${publicIpName}`n vm ${VirtualMachineName}"
az group deployment create -g $ResourceGroupName -n $VirtualMachineName --subscription $subscriptionId --template-file $templateFilePath --parameters vmSize=$vmSize vmName=$VirtualMachineName adminUserName=$AdminUsername adminPassword=$AdminPassword networkInterfaceId=$networkId
Write-Host "`nCreated in $(ResourceGroupName):`n vnet $(vnetName)`n subnet $(subnetName)`n nic $(nicName)`n publicip $(publicIpName)`n vm $(VirtualMachineName)"
}
+158 -209
View File
@@ -1,11 +1,11 @@
$ErrorActionPreference = 'Stop'
enum ImageType {
Windows2016 = 0
Windows2019 = 1
Windows2022 = 2
Ubuntu2004 = 3
Ubuntu2204 = 4
UbuntuMinimal = 5
Ubuntu1804 = 3
Ubuntu2004 = 4
}
Function Get-PackerTemplatePath {
@@ -17,21 +17,21 @@ Function Get-PackerTemplatePath {
)
switch ($ImageType) {
([ImageType]::Windows2016) {
$relativeTemplatePath = Join-Path "win" "windows2016.json"
}
([ImageType]::Windows2019) {
$relativeTemplatePath = Join-Path "win" "windows2019.json"
}
([ImageType]::Windows2022) {
$relativeTemplatePath = Join-Path "win" "windows2022.json"
}
([ImageType]::Ubuntu1804) {
$relativeTemplatePath = Join-Path "linux" "ubuntu1804.json"
}
([ImageType]::Ubuntu2004) {
$relativeTemplatePath = Join-Path "linux" "ubuntu2004.json"
}
([ImageType]::Ubuntu2204) {
$relativeTemplatePath = Join-Path "linux" "ubuntu2204.pkr.hcl"
}
([ImageType]::UbuntuMinimal) {
$relativeTemplatePath = Join-Path "linux" "ubuntuminimal.pkr.hcl"
}
default { throw "Unknown type of image" }
}
@@ -58,37 +58,46 @@ Function GenerateResourcesAndImage {
<#
.SYNOPSIS
A helper function to help generate an image.
.DESCRIPTION
Creates Azure resources and kicks off a packer image generation for the selected image type.
.PARAMETER SubscriptionId
The Azure subscription Id where resources will be created.
.PARAMETER ResourceGroupName
The Azure resource group name where the Azure resources will be created.
.PARAMETER ImageGenerationRepositoryRoot
The root path of the image generation repository source.
.PARAMETER ImageType
The type of the image being generated. Valid options are: {"Windows2019", "Windows2022", "Ubuntu2004", "Ubuntu2204", "UbuntuMinimal"}.
The type of the image being generated. Valid options are: {"Windows2016", "Windows2019", "Windows2022", "Ubuntu1804", "Ubuntu2004"}.
.PARAMETER AzureLocation
The location of the resources being created in Azure. For example "East US".
.PARAMETER Force
Delete the resource group if it exists without user confirmation.
.PARAMETER AzureClientId
Client id needs to be provided for optional authentication via service principal. Example: "11111111-1111-1111-1111-111111111111"
.PARAMETER AzureClientSecret
Client secret needs to be provided for optional authentication via service principal. Example: "11111111-1111-1111-1111-111111111111"
.PARAMETER AzureTenantId
Tenant needs to be provided for optional authentication via service principal. Example: "11111111-1111-1111-1111-111111111111"
.PARAMETER RestrictToAgentIpAddress
If set, access to the VM used by packer to generate the image is restricted to the public IP address this script is run from.
This parameter cannot be used in combination with the virtual_network_name packer parameter.
.PARAMETER AllowBlobPublicAccess
The Azure storage account will be created with this option.
.PARAMETER EnableHttpsTrafficOnly
The Azure storage account will be created with this option.
.PARAMETER OnError
Specify how packer handles an error during image creation.
.EXAMPLE
GenerateResourcesAndImage -SubscriptionId {YourSubscriptionId} -ResourceGroupName "shsamytest1" -ImageGenerationRepositoryRoot "C:\runner-images" -ImageType Ubuntu2004 -AzureLocation "East US"
GenerateResourcesAndImage -SubscriptionId {YourSubscriptionId} -ResourceGroupName "shsamytest1" -ImageGenerationRepositoryRoot "C:\virtual-environments" -ImageType Ubuntu1804 -AzureLocation "East US"
#>
param (
[Parameter(Mandatory = $True)]
@@ -116,207 +125,147 @@ Function GenerateResourcesAndImage {
[Parameter(Mandatory = $False)]
[bool] $AllowBlobPublicAccess = $False,
[Parameter(Mandatory = $False)]
[bool] $EnableHttpsTrafficOnly = $False,
[Parameter(Mandatory = $False)]
[ValidateSet("abort","ask","cleanup","run-cleanup-provisioner")]
[string] $OnError = "ask",
[Parameter(Mandatory = $False)]
[hashtable] $Tags
[bool] $EnableHttpsTrafficOnly = $False
)
$builderScriptPath = Get-PackerTemplatePath -RepositoryRoot $ImageGenerationRepositoryRoot -ImageType $ImageType
$ServicePrincipalClientSecret = $env:UserName + [System.GUID]::NewGuid().ToString().ToUpper()
$InstallPassword = $env:UserName + [System.GUID]::NewGuid().ToString().ToUpper()
if ([string]::IsNullOrEmpty($AzureClientId))
{
Connect-AzAccount
} else {
$AzSecureSecret = ConvertTo-SecureString $AzureClientSecret -AsPlainText -Force
$AzureAppCred = New-Object System.Management.Automation.PSCredential($AzureClientId, $AzSecureSecret)
Connect-AzAccount -ServicePrincipal -Credential $AzureAppCred -Tenant $AzureTenantId
}
Set-AzContext -SubscriptionId $SubscriptionId
$alreadyExists = $true;
try {
$builderScriptPath = Get-PackerTemplatePath -RepositoryRoot $ImageGenerationRepositoryRoot -ImageType $ImageType
$ServicePrincipalClientSecret = $env:UserName + [System.GUID]::NewGuid().ToString().ToUpper()
$InstallPassword = $env:UserName + [System.GUID]::NewGuid().ToString().ToUpper()
if ([string]::IsNullOrEmpty($AzureClientId))
{
Connect-AzAccount
} else {
$AzSecureSecret = ConvertTo-SecureString $AzureClientSecret -AsPlainText -Force
$AzureAppCred = New-Object System.Management.Automation.PSCredential($AzureClientId, $AzSecureSecret)
Connect-AzAccount -ServicePrincipal -Credential $AzureAppCred -Tenant $AzureTenantId
}
Set-AzContext -SubscriptionId $SubscriptionId
$alreadyExists = $true;
try {
Get-AzResourceGroup -Name $ResourceGroupName
Write-Verbose "Resource group was found, will delete and recreate it."
}
catch {
Write-Verbose "Resource group was not found, will create it."
$alreadyExists = $false;
}
if ($alreadyExists) {
if($Force -eq $true) {
# Cleanup the resource group if it already exitsted before
Remove-AzResourceGroup -Name $ResourceGroupName -Force
New-AzResourceGroup -Name $ResourceGroupName -Location $AzureLocation -Tag $tags
} else {
$title = "Delete Resource Group"
$message = "The resource group you specified already exists. Do you want to clean it up?"
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
"Delete the resource group including all resources."
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
"Keep the resource group and continue."
$stop = New-Object System.Management.Automation.Host.ChoiceDescription "&Stop", `
"Stop the current action."
$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no, $stop)
$result = $host.ui.PromptForChoice($title, $message, $options, 0)
switch ($result)
{
0 { Remove-AzResourceGroup -Name $ResourceGroupName -Force; New-AzResourceGroup -Name $ResourceGroupName -Location $AzureLocation -Tag $tags }
1 { <# Do nothing #> }
2 { exit }
}
}
} else {
New-AzResourceGroup -Name $ResourceGroupName -Location $AzureLocation -Tag $tags
}
# This script should follow the recommended naming conventions for azure resources
$storageAccountName = if($ResourceGroupName.EndsWith("-rg")) {
$ResourceGroupName.Substring(0, $ResourceGroupName.Length -3)
} else { $ResourceGroupName }
# Resource group names may contain special characters, that are not allowed in the storage account name
$storageAccountName = $storageAccountName.Replace("-", "").Replace("_", "").Replace("(", "").Replace(")", "").ToLower()
$storageAccountName += "001"
# Storage Account Name can only be 24 characters long
if ($storageAccountName.Length -gt 24){
$storageAccountName = $storageAccountName.Substring(0, 24)
}
if ($tags) {
New-AzStorageAccount -ResourceGroupName $ResourceGroupName -AccountName $storageAccountName -Location $AzureLocation -SkuName "Standard_LRS" -AllowBlobPublicAccess $AllowBlobPublicAccess -EnableHttpsTrafficOnly $EnableHttpsTrafficOnly -Tag $tags
} else {
New-AzStorageAccount -ResourceGroupName $ResourceGroupName -AccountName $storageAccountName -Location $AzureLocation -SkuName "Standard_LRS" -AllowBlobPublicAccess $AllowBlobPublicAccess -EnableHttpsTrafficOnly $EnableHttpsTrafficOnly
}
if ([string]::IsNullOrEmpty($AzureClientId)) {
# Interactive authentication: A service principal is created during runtime.
$spDisplayName = [System.GUID]::NewGuid().ToString().ToUpper()
$startDate = Get-Date
$endDate = $startDate.AddYears(1)
if ('Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential' -as [type]) {
$credentials = [Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential]@{
StartDate = $startDate
EndDate = $endDate
Password = $ServicePrincipalClientSecret
}
$sp = New-AzADServicePrincipal -DisplayName $spDisplayName -PasswordCredential $credentials
$spClientId = $sp.ApplicationId
$azRoleParam = @{
RoleDefinitionName = "Contributor"
ServicePrincipalName = $spClientId
}
}
if ('Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPasswordCredential' -as [type]) {
$credentials = [Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPasswordCredential]@{
StartDateTime = $startDate
EndDateTime = $endDate
}
$sp = New-AzADServicePrincipal -DisplayName $spDisplayName
$appCred = New-AzADAppCredential -ApplicationId $sp.AppId -PasswordCredentials $credentials
$spClientId = $sp.AppId
$azRoleParam = @{
RoleDefinitionName = "Contributor"
PrincipalId = $sp.Id
}
$ServicePrincipalClientSecret = $appCred.SecretText
}
Start-Sleep -Seconds $SecondsToWaitForServicePrincipalSetup
New-AzRoleAssignment @azRoleParam
Start-Sleep -Seconds $SecondsToWaitForServicePrincipalSetup
$sub = Get-AzSubscription -SubscriptionId $SubscriptionId
$tenantId = $sub.TenantId
# Remove ADPrincipal after the script completed
$isCleanupADPrincipal = $true
} else {
# Parametrized Authentication via given service principal: The service principal with the data provided via the command line
# is used for all authentication purposes.
$spClientId = $AzureClientId
$credentials = $AzureAppCred
$ServicePrincipalClientSecret = $AzureClientSecret
$tenantId = $AzureTenantId
}
Get-LatestCommit -ErrorAction SilentlyContinue
$packerBinary = Get-Command "packer"
if (-not ($packerBinary)) {
throw "'packer' binary is not found on PATH"
}
if ($RestrictToAgentIpAddress) {
$AgentIp = (Invoke-RestMethod http://ipinfo.io/json).ip
Write-Host "Restricting access to packer generated VM to agent IP Address: $AgentIp"
}
if ($builderScriptPath.Contains("pkr.hcl")) {
if ($AgentIp) {
$AgentIp = '[ \"{0}\" ]' -f $AgentIp
} else {
$AgentIp = "[]"
}
if (-not $Tags) {
$Tags = @{}
}
}
if ($builderScriptPath.Contains(".json")) {
if ($Tags) {
$builderScriptPath_temp = $builderScriptPath.Replace(".json", "-temp.json")
$packer_script = Get-Content -Path $builderScriptPath | ConvertFrom-Json
$packer_script.builders | Add-Member -Name "azure_tags" -Value $Tags -MemberType NoteProperty
$packer_script | ConvertTo-Json -Depth 3 | Out-File -Encoding Ascii $builderScriptPath_temp
$builderScriptPath = $builderScriptPath_temp
}
}
& $packerBinary build -on-error="$($OnError)" `
-var "client_id=$($spClientId)" `
-var "client_secret=$($ServicePrincipalClientSecret)" `
-var "subscription_id=$($SubscriptionId)" `
-var "tenant_id=$($tenantId)" `
-var "location=$($AzureLocation)" `
-var "resource_group=$($ResourceGroupName)" `
-var "storage_account=$($storageAccountName)" `
-var "install_password=$($InstallPassword)" `
-var "allowed_inbound_ip_addresses=$($AgentIp)" `
-var "azure_tags=$($Tags | ConvertTo-Json -Compress)" `
$builderScriptPath
Get-AzResourceGroup -Name $ResourceGroupName
Write-Verbose "Resource group was found, will delete and recreate it."
}
catch {
Write-Error $_
Write-Verbose "Resource group was not found, will create it."
$alreadyExists = $false;
}
finally {
# Remove ADServicePrincipal and ADApplication
if ($isCleanupADPrincipal) {
Write-Host "`nRemoving ${spDisplayName}/${spClientId}:"
if (Get-AzADServicePrincipal -DisplayName $spDisplayName) {
Write-Host " [+] ADServicePrincipal"
Remove-AzADServicePrincipal -DisplayName $spDisplayName -Confirm:$false
}
if (Get-AzADApplication -DisplayName $spDisplayName) {
Write-Host " [+] ADApplication"
Remove-AzADApplication -DisplayName $spDisplayName -Confirm:$false
if ($alreadyExists) {
if($Force -eq $true) {
# Cleanup the resource group if it already exitsted before
Remove-AzResourceGroup -Name $ResourceGroupName -Force
New-AzResourceGroup -Name $ResourceGroupName -Location $AzureLocation
} else {
$title = "Delete Resource Group"
$message = "The resource group you specified already exists. Do you want to clean it up?"
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
"Delete the resource group including all resources."
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
"Keep the resource group and continue."
$stop = New-Object System.Management.Automation.Host.ChoiceDescription "&Stop", `
"Stop the current action."
$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no, $stop)
$result = $host.ui.PromptForChoice($title, $message, $options, 0)
switch ($result)
{
0 { Remove-AzResourceGroup -Name $ResourceGroupName -Force; New-AzResourceGroup -Name $ResourceGroupName -Location $AzureLocation }
1 { <# Do nothing #> }
2 { exit }
}
}
} else {
New-AzResourceGroup -Name $ResourceGroupName -Location $AzureLocation
}
}
# This script should follow the recommended naming conventions for azure resources
$storageAccountName = if($ResourceGroupName.EndsWith("-rg")) {
$ResourceGroupName.Substring(0, $ResourceGroupName.Length -3)
} else { $ResourceGroupName }
# Resource group names may contain special characters, that are not allowed in the storage account name
$storageAccountName = $storageAccountName.Replace("-", "").Replace("_", "").Replace("(", "").Replace(")", "").ToLower()
$storageAccountName += "001"
New-AzStorageAccount -ResourceGroupName $ResourceGroupName -AccountName $storageAccountName -Location $AzureLocation -SkuName "Standard_LRS" -AllowBlobPublicAccess $AllowBlobPublicAccess -EnableHttpsTrafficOnly $EnableHttpsTrafficOnly
if ([string]::IsNullOrEmpty($AzureClientId)) {
# Interactive authentication: A service principal is created during runtime.
$spDisplayName = [System.GUID]::NewGuid().ToString().ToUpper()
$startDate = Get-Date
$endDate = $startDate.AddYears(1)
if ('Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential' -as [type]) {
$credentials = [Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential]@{
StartDate = $startDate
EndDate = $endDate
Password = $ServicePrincipalClientSecret
}
$sp = New-AzADServicePrincipal -DisplayName $spDisplayName -PasswordCredential $credentials
$spClientId = $sp.ApplicationId
$azRoleParam = @{
RoleDefinitionName = "Contributor"
ServicePrincipalName = $spClientId
}
}
if ('Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPasswordCredential' -as [type]) {
$credentials = [Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPasswordCredential]@{
StartDateTime = $startDate
EndDateTime = $endDate
}
$sp = New-AzADServicePrincipal -DisplayName $spDisplayName
$appCred = New-AzADAppCredential -ApplicationId $sp.AppId -PasswordCredentials $credentials
$spClientId = $sp.AppId
$azRoleParam = @{
RoleDefinitionName = "Contributor"
PrincipalId = $sp.Id
}
$ServicePrincipalClientSecret = $appCred.SecretText
}
Start-Sleep -Seconds $SecondsToWaitForServicePrincipalSetup
New-AzRoleAssignment @azRoleParam
Start-Sleep -Seconds $SecondsToWaitForServicePrincipalSetup
$sub = Get-AzSubscription -SubscriptionId $SubscriptionId
$tenantId = $sub.TenantId
# "", "Note this variable-setting script for running Packer with these Azure resources in the future:", "==============================================================================================", "`$spClientId = `"$spClientId`"", "`$ServicePrincipalClientSecret = `"$ServicePrincipalClientSecret`"", "`$SubscriptionId = `"$SubscriptionId`"", "`$tenantId = `"$tenantId`"", "`$spObjectId = `"$spObjectId`"", "`$AzureLocation = `"$AzureLocation`"", "`$ResourceGroupName = `"$ResourceGroupName`"", "`$storageAccountName = `"$storageAccountName`"", "`$install_password = `"$install_password`"", ""
} else {
# Parametrized Authentication via given service principal: The service principal with the data provided via the command line
# is used for all authentication purposes.
$spClientId = $AzureClientId
$credentials = $AzureAppCred
$ServicePrincipalClientSecret = $AzureClientSecret
$tenantId = $AzureTenantId
}
Get-LatestCommit -ErrorAction SilentlyContinue
$packerBinary = Get-Command "packer"
if (-not ($packerBinary)) {
throw "'packer' binary is not found on PATH"
}
if($RestrictToAgentIpAddress -eq $true) {
$AgentIp = (Invoke-RestMethod http://ipinfo.io/json).ip
Write-Host "Restricting access to packer generated VM to agent IP Address: $AgentIp"
}
& $packerBinary build -on-error=ask `
-var "client_id=$($spClientId)" `
-var "client_secret=$($ServicePrincipalClientSecret)" `
-var "subscription_id=$($SubscriptionId)" `
-var "tenant_id=$($tenantId)" `
-var "location=$($AzureLocation)" `
-var "resource_group=$($ResourceGroupName)" `
-var "storage_account=$($storageAccountName)" `
-var "install_password=$($InstallPassword)" `
-var "allowed_inbound_ip_addresses=$($AgentIp)" `
$builderScriptPath
}
@@ -1,66 +0,0 @@
using module ./SoftwareReport.psm1
using module ./SoftwareReport.DifferenceCalculator.psm1
<#
.SYNOPSIS
Calculates the difference between two software reports and saves it to a file.
.PARAMETER PreviousJsonReportPath
Path to the previous software report.
.PARAMETER CurrentJsonReportPath
Path to the current software report.
.PARAMETER OutputFile
Path to the file where the difference will be saved.
.PARAMETER ReleaseBranchName
Name of the release branch to build image docs URL.
.PARAMETER ReadmePath
Path to the README file in repository to build image docs URL.
#>
Param (
[Parameter(Mandatory=$true)]
[string] $PreviousJsonReportPath,
[Parameter(Mandatory=$true)]
[string] $CurrentJsonReportPath,
[Parameter(Mandatory=$true)]
[string] $OutputFile,
[Parameter(Mandatory=$false)]
[string] $ReleaseBranchName,
[Parameter(Mandatory=$false)]
[string] $ReadmePath
)
$ErrorActionPreference = "Stop"
$global:ErrorView = "NormalView"
function Read-SoftwareReport {
Param (
[Parameter(Mandatory=$true)]
[string] $JsonReportPath
)
if (-not (Test-Path $JsonReportPath)) {
throw "File '$JsonReportPath' does not exist"
}
$jsonReport = Get-Content -Path $JsonReportPath -Raw
$report = [SoftwareReport]::FromJson($jsonReport)
return $report
}
$previousReport = Read-SoftwareReport -JsonReportPath $PreviousJsonReportPath
$currentReport = Read-SoftwareReport -JsonReportPath $CurrentJsonReportPath
$comparer = [SoftwareReportDifferenceCalculator]::new($previousReport, $currentReport)
$comparer.CompareReports()
$diff = $comparer.GetMarkdownReport()
if ($ReleaseBranchName -and $ReadmePath) {
# https://github.com/actions/runner-images/blob/releases/macOS-12/20221215/images/macos/macos-12-Readme.md
$ImageDocsUrl = "https://github.com/actions/runner-images/blob/${ReleaseBranchName}/${ReadmePath}"
$diff += "`n`n`nFor comprehensive list of software installed on this image please click [here]($ImageDocsUrl)."
}
$parentDirectory = Split-Path $OutputFile -Parent
if (-not (Test-Path $parentDirectory)) { New-Item -Path $parentDirectory -ItemType Directory | Out-Null }
$diff | Out-File -Path $OutputFile -Encoding utf8NoBOM
@@ -1,56 +0,0 @@
############################
### Abstract base nodes ####
############################
# Abstract base class for all nodes
class BaseNode {
[Boolean] ShouldBeIncludedToDiff() {
return $false
}
[String] ToMarkdown() {
return $this.ToMarkdown(1)
}
[String] ToMarkdown([Int32] $Level) {
throw "Abtract method 'ToMarkdown(level)' is not implemented for '$($this.GetType().Name)'"
}
[Boolean] IsSimilarTo([BaseNode] $OtherNode) {
throw "Abtract method 'IsSimilarTo' is not implemented for '$($this.GetType().Name)'"
}
[Boolean] IsIdenticalTo([BaseNode] $OtherNode) {
throw "Abtract method 'IsIdenticalTo' is not implemented for '$($this.GetType().Name)'"
}
}
# Abstract base class for all nodes that describe a tool and should be rendered inside diff table
class BaseToolNode: BaseNode {
[ValidateNotNullOrEmpty()]
[String] $ToolName
BaseToolNode([String] $ToolName) {
$this.ToolName = $ToolName
}
[Boolean] ShouldBeIncludedToDiff() {
return $true
}
[String] GetValue() {
throw "Abtract method 'GetValue' is not implemented for '$($this.GetType().Name)'"
}
[Boolean] IsSimilarTo([BaseNode] $OtherNode) {
if ($this.GetType() -ne $OtherNode.GetType()) {
return $false
}
return $this.ToolName -eq $OtherNode.ToolName
}
[Boolean] IsIdenticalTo([BaseNode] $OtherNode) {
return $this.IsSimilarTo($OtherNode) -and ($this.GetValue() -eq $OtherNode.GetValue())
}
}
@@ -1,136 +0,0 @@
using module ./SoftwareReport.psm1
using module ./SoftwareReport.BaseNodes.psm1
using module ./SoftwareReport.Nodes.psm1
using module ./SoftwareReport.DifferenceRender.psm1
class SoftwareReportDifferenceCalculator {
[ValidateNotNullOrEmpty()]
hidden [SoftwareReport] $PreviousReport
[ValidateNotNullOrEmpty()]
hidden [SoftwareReport] $CurrentReport
hidden [Collections.Generic.List[ReportDifferenceItem]] $AddedItems
hidden [Collections.Generic.List[ReportDifferenceItem]] $ChangedItems
hidden [Collections.Generic.List[ReportDifferenceItem]] $DeletedItems
SoftwareReportDifferenceCalculator([SoftwareReport] $PreviousReport, [SoftwareReport] $CurrentReport) {
$this.PreviousReport = $PreviousReport
$this.CurrentReport = $CurrentReport
}
[void] CompareReports() {
$this.AddedItems = @()
$this.ChangedItems = @()
$this.DeletedItems = @()
$this.CompareInternal($this.PreviousReport.Root, $this.CurrentReport.Root, @())
}
[String] GetMarkdownReport() {
$reporter = [SoftwareReportDifferenceRender]::new()
$report = $reporter.GenerateMarkdownReport($this.CurrentReport, $this.PreviousReport, $this.AddedItems, $this.ChangedItems, $this.DeletedItems)
return $report
}
hidden [void] CompareInternal([HeaderNode] $previousReportPointer, [HeaderNode] $currentReportPointer, [String[]] $Headers) {
$currentReportPointer.Children ?? @() | Where-Object { $_.ShouldBeIncludedToDiff() -and $this.FilterExcludedNodes($_) } | ForEach-Object {
$currentReportNode = $_
$sameNodeInPreviousReport = $previousReportPointer ? $previousReportPointer.FindSimilarChildNode($currentReportNode) : $null
if ($currentReportNode -is [HeaderNode]) {
# Compare HeaderNode recursively
$this.CompareInternal($sameNodeInPreviousReport, $currentReportNode, $Headers + $currentReportNode.Title)
} else {
if ($sameNodeInPreviousReport -and ($currentReportNode.IsIdenticalTo($sameNodeInPreviousReport))) {
# Nodes are identical, nothing changed, just ignore it
} elseif ($sameNodeInPreviousReport) {
# Nodes are equal but not identical, something was changed
if ($currentReportNode -is [TableNode]) {
$this.CompareSimilarTableNodes($sameNodeInPreviousReport, $currentReportNode, $Headers)
} elseif ($currentReportNode -is [ToolVersionsListNode]) {
$this.CompareSimilarToolVersionsListNodes($sameNodeInPreviousReport, $currentReportNode, $Headers)
} else {
$this.ChangedItems.Add([ReportDifferenceItem]::new($sameNodeInPreviousReport, $currentReportNode, $Headers))
}
} else {
# Node was not found in previous report, new node was added
$this.AddedItems.Add([ReportDifferenceItem]::new($null, $currentReportNode, $Headers))
}
}
}
# Detecting nodes that were removed
$previousReportPointer.Children ?? @() | Where-Object { $_.ShouldBeIncludedToDiff() -and $this.FilterExcludedNodes($_) } | ForEach-Object {
$previousReportNode = $_
$sameNodeInCurrentReport = $currentReportPointer ? $currentReportPointer.FindSimilarChildNode($previousReportNode) : $null
if (-not $sameNodeInCurrentReport) {
if ($previousReportNode -is [HeaderNode]) {
# Compare removed HeaderNode recursively
$this.CompareInternal($previousReportNode, $null, $Headers + $previousReportNode.Title)
} else {
# Node was not found in current report, node was removed
$this.DeletedItems.Add([ReportDifferenceItem]::new($previousReportNode, $null, $Headers))
}
}
}
}
hidden [void] CompareSimilarTableNodes([TableNode] $PreviousReportNode, [TableNode] $CurrentReportNode, [String[]] $Headers) {
$addedRows = $CurrentReportNode.Rows | Where-Object { $_ -notin $PreviousReportNode.Rows }
$deletedRows = $PreviousReportNode.Rows | Where-Object { $_ -notin $CurrentReportNode.Rows }
if (($addedRows.Count -eq 0) -and ($deletedRows.Count -eq 0)) {
# Unexpected state: TableNodes are identical
return
}
if ($PreviousReportNode.Headers -ne $CurrentReportNode.Headers) {
# If headers are changed and rows are changed at the same time, we should track it as removing table and adding new one
$this.DeletedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $null, $Headers))
$this.AddedItems.Add([ReportDifferenceItem]::new($null, $CurrentReportNode, $Headers))
} elseif (($addedRows.Count -gt 0) -and ($deletedRows.Count -eq 0)) {
# If new rows were added and no rows were deleted, then it is AddedItem
$this.AddedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $CurrentReportNode, $Headers))
} elseif (($deletedRows.Count -gt 0) -and ($addedRows.Count -eq 0)) {
# If no rows were added and some rows were deleted, then it is DeletedItem
$this.DeletedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $CurrentReportNode, $Headers))
} else {
# If some rows were added and some rows were removed, then it is UpdatedItem
$this.ChangedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $CurrentReportNode, $Headers))
}
}
hidden [void] CompareSimilarToolVersionsListNodes([ToolVersionsListNode] $PreviousReportNode, [ToolVersionsListNode] $CurrentReportNode, [String[]] $Headers) {
$previousReportMajorVersions = $PreviousReportNode.Versions | ForEach-Object { $PreviousReportNode.ExtractMajorVersion($_) }
$currentReportMajorVersion = $CurrentReportNode.Versions | ForEach-Object { $CurrentReportNode.ExtractMajorVersion($_) }
$addedVersions = $CurrentReportNode.Versions | Where-Object { $CurrentReportNode.ExtractMajorVersion($_) -notin $previousReportMajorVersions }
$deletedVersions = $PreviousReportNode.Versions | Where-Object { $PreviousReportNode.ExtractMajorVersion($_) -notin $currentReportMajorVersion }
$changedPreviousVersions = $PreviousReportNode.Versions | Where-Object { ($PreviousReportNode.ExtractMajorVersion($_) -in $currentReportMajorVersion) -and ($_ -notin $CurrentReportNode.Versions) }
$changedCurrentVersions = $CurrentReportNode.Versions | Where-Object { ($CurrentReportNode.ExtractMajorVersion($_) -in $previousReportMajorVersions) -and ($_ -notin $PreviousReportNode.Versions) }
if ($addedVersions.Count -gt 0) {
$this.AddedItems.Add([ReportDifferenceItem]::new($null, [ToolVersionsListNode]::new($CurrentReportNode.ToolName, $addedVersions, $CurrentReportNode.MajorVersionRegex, "List"), $Headers))
}
if ($deletedVersions.Count -gt 0) {
$this.DeletedItems.Add([ReportDifferenceItem]::new([ToolVersionsListNode]::new($PreviousReportNode.ToolName, $deletedVersions, $PreviousReportNode.MajorVersionRegex, "List"), $null, $Headers))
}
$previousChangedNode = ($changedPreviousVersions.Count -gt 0) ? [ToolVersionsListNode]::new($PreviousReportNode.ToolName, $changedPreviousVersions, $PreviousReportNode.MajorVersionRegex, "List") : $null
$currentChangedNode = ($changedCurrentVersions.Count -gt 0) ? [ToolVersionsListNode]::new($CurrentReportNode.ToolName, $changedCurrentVersions, $CurrentReportNode.MajorVersionRegex, "List") : $null
if ($previousChangedNode -and $currentChangedNode) {
$this.ChangedItems.Add([ReportDifferenceItem]::new($previousChangedNode, $currentChangedNode, $Headers))
}
}
hidden [Boolean] FilterExcludedNodes([BaseNode] $Node) {
# We shouldn't show "Image Version" diff because it is already shown in report header
if (($Node -is [ToolVersionNode]) -and ($Node.ToolName -eq "Image Version:")) {
return $false
}
return $true
}
}
@@ -1,225 +0,0 @@
using module ./SoftwareReport.psm1
using module ./SoftwareReport.BaseNodes.psm1
using module ./SoftwareReport.Nodes.psm1
class SoftwareReportDifferenceRender {
[String] GenerateMarkdownReport([SoftwareReport] $CurrentReport, [SoftwareReport] $PreviousReport, [ReportDifferenceItem[]] $AddedItems, [ReportDifferenceItem[]] $ChangedItems, [ReportDifferenceItem[]] $DeletedItems) {
$sb = [System.Text.StringBuilder]::new()
$rootNode = $CurrentReport.Root
$imageVersion = $CurrentReport.GetImageVersion()
$previousImageVersion = $PreviousReport.GetImageVersion()
#############################
### Render report header ####
#############################
$sb.AppendLine("# :desktop_computer: Actions Runner Image: $($rootNode.Title)")
# ToolVersionNodes on root level contains main image description so just copy-paste them to final report
$rootNode.Children | Where-Object { $_ -is [ToolVersionNode] } | ForEach-Object {
$sb.AppendLine($_.ToMarkdown())
}
$sb.AppendLine()
$sb.AppendLine("## :mega: What's changed?").AppendLine()
###########################
### Render added items ####
###########################
[ReportDifferenceItem[]] $addedItemsBaseTools = $AddedItems | Where-Object { $_.IsBaseToolNode() }
[ReportDifferenceItem[]] $addedItemsTables = $AddedItems | Where-Object { $_.IsTableNode() }
if ($addedItemsBaseTools.Count + $addedItemsTables.Count -gt 0) {
$sb.AppendLine("### Added :heavy_plus_sign:").AppendLine()
}
if ($addedItemsBaseTools.Count -gt 0) {
$tableItems = $addedItemsBaseTools | ForEach-Object {
[PSCustomObject]@{
"Category" = $this.RenderCategory($_.Headers, $true);
"Tool name" = $this.RenderToolName($_.CurrentReportNode.ToolName);
"Current ($imageVersion)" = $_.CurrentReportNode.GetValue();
}
}
$sb.AppendLine($this.RenderHtmlTable($tableItems, "Category"))
}
if ($addedItemsTables.Count -gt 0) {
$addedItemsTables | ForEach-Object {
$sb.AppendLine($this.RenderTableNodesDiff($_))
}
}
#############################
### Render deleted items ####
#############################
[ReportDifferenceItem[]] $deletedItemsBaseTools = $DeletedItems | Where-Object { $_.IsBaseToolNode() }
[ReportDifferenceItem[]] $deletedItemsTables = $DeletedItems | Where-Object { $_.IsTableNode() }
if ($deletedItemsBaseTools.Count + $deletedItemsTables.Count -gt 0) {
$sb.AppendLine("### Deleted :heavy_minus_sign:").AppendLine()
}
if ($deletedItemsBaseTools.Count -gt 0) {
$tableItems = $deletedItemsBaseTools | ForEach-Object {
[PSCustomObject]@{
"Category" = $this.RenderCategory($_.Headers, $true);
"Tool name" = $this.RenderToolName($_.PreviousReportNode.ToolName);
"Previous ($previousImageVersion)" = $_.PreviousReportNode.GetValue();
}
}
$sb.AppendLine($this.RenderHtmlTable($tableItems, "Category"))
}
if ($deletedItemsTables.Count -gt 0) {
$deletedItemsTables | ForEach-Object {
$sb.AppendLine($this.RenderTableNodesDiff($_))
}
}
#############################
### Render updated items ####
#############################
[ReportDifferenceItem[]] $changedItemsBaseTools = $ChangedItems | Where-Object { $_.IsBaseToolNode() }
[ReportDifferenceItem[]] $changedItemsTables = $ChangedItems | Where-Object { $_.IsTableNode() }
if ($changedItemsBaseTools.Count + $changedItemsTables.Count -gt 0) {
$sb.AppendLine("### Updated").AppendLine()
}
if ($changedItemsBaseTools.Count -gt 0) {
$tableItems = $changedItemsBaseTools | ForEach-Object {
[PSCustomObject]@{
"Category" = $this.RenderCategory($_.Headers, $true);
"Tool name" = $this.RenderToolName($_.CurrentReportNode.ToolName);
"Previous ($previousImageVersion)" = $_.PreviousReportNode.GetValue();
"Current ($imageVersion)" = $_.CurrentReportNode.GetValue();
}
}
$sb.AppendLine($this.RenderHtmlTable($tableItems, "Category"))
}
if ($changedItemsTables.Count -gt 0) {
$changedItemsTables | ForEach-Object {
$sb.AppendLine($this.RenderTableNodesDiff($_))
}
}
return $sb.ToString()
}
[String] RenderHtmlTable([PSCustomObject[]] $Table, [String] $RowSpanColumnName) {
$headers = $Table[0].PSObject.Properties.Name
$sb = [System.Text.StringBuilder]::new()
$sb.AppendLine("<table>")
$sb.AppendLine(" <thead>")
$headers | ForEach-Object {
$sb.AppendLine(" <th>$_</th>")
}
$sb.AppendLine(" </thead>")
$sb.AppendLine(" <tbody>")
$tableRowSpans = $this.CalculateHtmlTableRowSpan($Table, $RowSpanColumnName)
for ($rowIndex = 0; $rowIndex -lt $Table.Count; $rowIndex++) {
$row = $Table[$rowIndex]
$sb.AppendLine(" <tr>")
$headers | ForEach-Object {
if ($_ -eq $RowSpanColumnName) {
if ($tableRowSpans[$rowIndex] -gt 0) {
$sb.AppendLine(" <td rowspan=`"$($tableRowSpans[$rowIndex])`">$($row.$_)</td>")
} else {
# Skip rendering this cell at all
}
} else {
$sb.AppendLine(" <td>$($row.$_)</td>")
}
}
$sb.AppendLine(" </tr>")
}
$sb.AppendLine(" </tbody>")
$sb.AppendLine("</table>")
return $sb.ToString()
}
[int[]] CalculateHtmlTableRowSpan([PSCustomObject[]] $Table, [String] $keyColumn) {
$result = @(0) * $Table.Count
for ($rowIndex = $Table.Count - 1; $rowIndex -ge 0; $rowIndex--) {
if (($rowIndex -lt ($Table.Count - 1)) -and ($Table[$rowIndex].$keyColumn -eq $Table[$rowIndex + 1].$keyColumn)) {
# If the current row is the same as the next row
# Then rowspan of current row should be equal to rowspan of the next row + 1
# And rowspan of the next row should be 0 because it is already included in the rowspan of the current row
$result[$rowIndex] = $result[$rowIndex + 1] + 1
$result[$rowIndex + 1] = 0
} else {
$result[$rowIndex] = 1
}
}
return $result
}
[String] RenderTableNodesDiff([ReportDifferenceItem] $DiffItem) {
# Use the simplest approach for now: first, print all removed lines. Then print added lines
# It will work well for most cases like changing existing rows, adding new rows and removing rows
# But can produce not so pretty results for cases when some rows are changed and some rows are added at the same time
# Let's see how it works in practice and improve it later if needed
[String] $tableHeaders = ($DiffItem.CurrentReportNode ?? $DiffItem.PreviousReportNode).Headers
[Collections.Generic.List[String]] $tableRows = @()
$DiffItem.PreviousReportNode.Rows ?? @() | Where-Object { $_ -notin $DiffItem.CurrentReportNode.Rows } | ForEach-Object {
$tableRows.Add($this.StrikeTableRow($_))
}
$DiffItem.CurrentReportNode.Rows ?? @() | Where-Object { $_ -notin $DiffItem.PreviousReportNode.Rows } | ForEach-Object {
$tableRows.Add($_)
}
$sb = [System.Text.StringBuilder]::new()
$sb.AppendLine("#### $($this.RenderCategory($DiffItem.Headers, $false))")
$sb.AppendLine([TableNode]::new($tableHeaders, $tableRows).ToMarkdown())
return $sb.ToString()
}
[String] RenderCategory([String[]] $Headers, [Boolean] $AddLineSeparator) {
# Always skip the first header because it is "Installed Software"
[String[]] $takeHeaders = $Headers | Select-Object -Skip 1
if ($takeHeaders.Count -eq 0) {
return ""
}
$lineSeparator = $AddLineSeparator ? "<br>": ""
return [String]::Join(" >$lineSeparator ", $takeHeaders)
}
[String] RenderToolName([String] $ToolName) {
return $ToolName.TrimEnd(":")
}
[String] StrikeTableRow([String] $Row) {
# Convert "a|b|c" to "~~a~~|~~b~~|~~c~~
$cells = $Row.Split("|")
$strikedCells = $cells | ForEach-Object { "~~$($_)~~"}
return [String]::Join("|", $strikedCells)
}
}
# Temporary structure to store the single difference between two reports
class ReportDifferenceItem {
[BaseNode] $PreviousReportNode
[BaseNode] $CurrentReportNode
[String[]] $Headers
ReportDifferenceItem([BaseNode] $PreviousReportNode, [BaseNode] $CurrentReportNode, [String[]] $Headers) {
$this.PreviousReportNode = $PreviousReportNode
$this.CurrentReportNode = $CurrentReportNode
$this.Headers = $Headers
}
[Boolean] IsBaseToolNode() {
$node = $this.CurrentReportNode ?? $this.PreviousReportNode
return $node -is [BaseToolNode]
}
[Boolean] IsTableNode() {
$node = $this.CurrentReportNode ?? $this.PreviousReportNode
return $node -is [TableNode]
}
}
@@ -1,429 +0,0 @@
using module ./SoftwareReport.BaseNodes.psm1
#########################################
### Nodes to describe image software ####
#########################################
# NodesFactory is used to simplify parsing different types of notes
# Every node has own logic of parsing and this method just invokes "FromJsonObject" of correct node type
class NodesFactory {
static [BaseNode] ParseNodeFromObject([object] $JsonObj) {
if ($JsonObj.NodeType -eq [HeaderNode].Name) {
return [HeaderNode]::FromJsonObject($JsonObj)
} elseif ($JsonObj.NodeType -eq [ToolVersionNode].Name) {
return [ToolVersionNode]::FromJsonObject($JsonObj)
} elseif ($JsonObj.NodeType -eq [ToolVersionsListNode].Name) {
return [ToolVersionsListNode]::FromJsonObject($JsonObj)
} elseif ($JsonObj.NodeType -eq [TableNode].Name) {
return [TableNode]::FromJsonObject($JsonObj)
} elseif ($JsonObj.NodeType -eq [NoteNode].Name) {
return [NoteNode]::FromJsonObject($JsonObj)
}
throw "Unknown node type in ParseNodeFromObject '$($JsonObj.NodeType)'"
}
}
class HeaderNode: BaseNode {
[ValidateNotNullOrEmpty()]
[String] $Title
[Collections.Generic.List[BaseNode]] $Children
HeaderNode([String] $Title) {
$this.Title = $Title
$this.Children = @()
}
[Boolean] ShouldBeIncludedToDiff() {
return $true
}
[void] AddNode([BaseNode] $node) {
$similarNode = $this.FindSimilarChildNode($node)
if ($similarNode) {
throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.`nFound node: $($similarNode.ToJsonObject() | ConvertTo-Json)`nNew node: $($node.ToJsonObject() | ConvertTo-Json)"
}
if (-not $this.IsNodeHasMarkdownHeader($node)) {
# If the node doesn't print own header to markdown, we should check that there is no other nodes that print header to markdown before it.
# It is done to avoid unexpected situation like this:
#
# HeaderNode A -> # A
# HeaderNode B -> ## B
# ToolVersionNode C -> - C
# ToolVersionNode D -> - D
#
# In this example, we add 'HeaderNode B" to 'HeaderNode A' and add 'ToolVersionNode C' to 'HeaderNode B'.
# Then we add 'ToolVersionNode D' to 'HeaderNode A'.
# But the result markdown will look like 'ToolVersionNode D' belongs to 'HeaderNode B' instead of 'HeaderNode A'.
$this.Children | Where-Object { $this.IsNodeHasMarkdownHeader($_) } | ForEach-Object {
throw "It is not allowed to add the non-header node after the header node. Consider adding the separate HeaderNode for this node"
}
}
$this.Children.Add($node)
}
[void] AddNodes([BaseNode[]] $nodes) {
$nodes | ForEach-Object {
$this.AddNode($_)
}
}
[HeaderNode] AddHeader([String] $Title) {
$node = [HeaderNode]::new($Title)
$this.AddNode($node)
return $node
}
[void] AddToolVersion([String] $ToolName, [String] $Version) {
$this.AddNode([ToolVersionNode]::new($ToolName, $Version))
}
[void] AddToolVersionsList([String] $ToolName, [String[]] $Version, [String] $MajorVersionRegex) {
$this.AddNode([ToolVersionsListNode]::new($ToolName, $Version, $MajorVersionRegex, "List"))
}
[void] AddToolVersionsListInline([String] $ToolName, [String[]] $Version, [String] $MajorVersionRegex) {
$this.AddNode([ToolVersionsListNode]::new($ToolName, $Version, $MajorVersionRegex, "Inline"))
}
[void] AddTable([PSCustomObject[]] $Table) {
$this.AddNode([TableNode]::FromObjectsArray($Table))
}
[void] AddNote([String] $Content) {
$this.AddNode([NoteNode]::new($Content))
}
[String] ToMarkdown([Int32] $Level) {
$sb = [System.Text.StringBuilder]::new()
$sb.AppendLine()
$sb.AppendLine("$("#" * $Level) $($this.Title)")
$this.Children | ForEach-Object {
$sb.AppendLine($_.ToMarkdown($Level + 1))
}
return $sb.ToString().TrimEnd()
}
[PSCustomObject] ToJsonObject() {
return [PSCustomObject]@{
NodeType = $this.GetType().Name
Title = $this.Title
Children = $this.Children | ForEach-Object { $_.ToJsonObject() }
}
}
static [HeaderNode] FromJsonObject([Object] $JsonObj) {
$node = [HeaderNode]::new($JsonObj.Title)
$JsonObj.Children | Where-Object { $_ } | ForEach-Object { $node.AddNode([NodesFactory]::ParseNodeFromObject($_)) }
return $node
}
[Boolean] IsSimilarTo([BaseNode] $OtherNode) {
if ($OtherNode.GetType() -ne [HeaderNode]) {
return $false
}
return $this.Title -eq $OtherNode.Title
}
[Boolean] IsIdenticalTo([BaseNode] $OtherNode) {
return $this.IsSimilarTo($OtherNode)
}
[BaseNode] FindSimilarChildNode([BaseNode] $Find) {
foreach ($childNode in $this.Children) {
if ($childNode.IsSimilarTo($Find)) {
return $childNode
}
}
return $null
}
hidden [Boolean] IsNodeHasMarkdownHeader([BaseNode] $node) {
if ($node -is [HeaderNode]) {
return $true
}
if (($node -is [ToolVersionsListNode]) -and ($node.ListType -eq "List")) {
return $true
}
return $false
}
}
class ToolVersionNode: BaseToolNode {
[ValidateNotNullOrEmpty()]
[String] $Version
ToolVersionNode([String] $ToolName, [String] $Version): base($ToolName) {
$this.Version = $Version
}
[String] ToMarkdown([Int32] $Level) {
return "- $($this.ToolName) $($this.Version)"
}
[String] GetValue() {
return $this.Version
}
[PSCustomObject] ToJsonObject() {
return [PSCustomObject]@{
NodeType = $this.GetType().Name
ToolName = $this.ToolName
Version = $this.Version
}
}
static [BaseNode] FromJsonObject([Object] $JsonObj) {
return [ToolVersionNode]::new($JsonObj.ToolName, $JsonObj.Version)
}
}
class ToolVersionsListNode: BaseToolNode {
[ValidateNotNullOrEmpty()]
[String[]] $Versions
[Regex] $MajorVersionRegex
[ValidateSet("List", "Inline")]
[String] $ListType
ToolVersionsListNode([String] $ToolName, [String[]] $Versions, [String] $MajorVersionRegex, [String] $ListType): base($ToolName) {
$this.Versions = $Versions
$this.MajorVersionRegex = [Regex]::new($MajorVersionRegex)
$this.ListType = $ListType
$this.ValidateMajorVersionRegex()
}
[String] ToMarkdown([Int32] $Level) {
if ($this.ListType -eq "Inline") {
return "- $($this.ToolName): $($this.Versions -join ', ')"
}
$sb = [System.Text.StringBuilder]::new()
$sb.AppendLine()
$sb.AppendLine("$("#" * $Level) $($this.ToolName)")
$this.Versions | ForEach-Object {
$sb.AppendLine("- $_")
}
return $sb.ToString().TrimEnd()
}
[String] GetValue() {
return $this.Versions -join ', '
}
[String] ExtractMajorVersion([String] $Version) {
$match = $this.MajorVersionRegex.Match($Version)
if (($match.Success -ne $true) -or [String]::IsNullOrEmpty($match.Groups[0].Value)) {
throw "Version '$Version' doesn't match regex '$($this.PrimaryVersionRegex)'"
}
return $match.Groups[0].Value
}
[PSCustomObject] ToJsonObject() {
return [PSCustomObject]@{
NodeType = $this.GetType().Name
ToolName = $this.ToolName
Versions = $this.Versions
MajorVersionRegex = $this.MajorVersionRegex.ToString()
ListType = $this.ListType
}
}
static [ToolVersionsListNode] FromJsonObject([Object] $JsonObj) {
return [ToolVersionsListNode]::new($JsonObj.ToolName, $JsonObj.Versions, $JsonObj.MajorVersionRegex, $JsonObj.ListType)
}
hidden [void] ValidateMajorVersionRegex() {
$this.Versions | Group-Object { $this.ExtractMajorVersion($_) } | ForEach-Object {
if ($_.Count -gt 1) {
throw "Multiple versions from list '$($this.GetValue())' return the same result from regex '$($this.MajorVersionRegex)': $($_.Name)"
}
}
}
}
class TableNode: BaseNode {
# It is easier to store the table as rendered lines because it will simplify finding differences in rows later
[ValidateNotNullOrEmpty()]
[String] $Headers
[ValidateNotNullOrEmpty()]
[String[]] $Rows
TableNode([String] $Headers, [String[]] $Rows) {
$this.Headers = $Headers
$this.Rows = $Rows
$columnsCount = $this.Headers.Split("|").Count
$this.Rows | ForEach-Object {
if ($_.Split("|").Count -ne $columnsCount) {
throw "Table has different number of columns in different rows"
}
}
}
[Boolean] ShouldBeIncludedToDiff() {
return $true
}
[String] ToMarkdown([Int32] $Level) {
$maxColumnWidths = $this.CalculateColumnsWidth()
$columnsCount = $maxColumnWidths.Count
$delimiterLine = [String]::Join("|", @("-") * $columnsCount)
$sb = [System.Text.StringBuilder]::new()
@($this.Headers) + @($delimiterLine) + $this.Rows | ForEach-Object {
$sb.Append("|")
$row = $_.Split("|")
for ($colIndex = 0; $colIndex -lt $columnsCount; $colIndex++) {
$padSymbol = $row[$colIndex] -eq "-" ? "-" : " "
$cellContent = $row[$colIndex].PadRight($maxColumnWidths[$colIndex], $padSymbol)
$sb.Append(" $($cellContent) |")
}
$sb.AppendLine()
}
return $sb.ToString().TrimEnd()
}
hidden [Int32[]] CalculateColumnsWidth() {
$maxColumnWidths = $this.Headers.Split("|") | ForEach-Object { $_.Length }
$columnsCount = $maxColumnWidths.Count
$this.Rows | ForEach-Object {
$columnWidths = $_.Split("|") | ForEach-Object { $_.Length }
for ($colIndex = 0; $colIndex -lt $columnsCount; $colIndex++) {
$maxColumnWidths[$colIndex] = [Math]::Max($maxColumnWidths[$colIndex], $columnWidths[$colIndex])
}
}
return $maxColumnWidths
}
[PSCustomObject] ToJsonObject() {
return [PSCustomObject]@{
NodeType = $this.GetType().Name
Headers = $this.Headers
Rows = $this.Rows
}
}
static [TableNode] FromJsonObject([Object] $JsonObj) {
return [TableNode]::new($JsonObj.Headers, $JsonObj.Rows)
}
[Boolean] IsSimilarTo([BaseNode] $OtherNode) {
if ($OtherNode.GetType() -ne [TableNode]) {
return $false
}
# We don't support having multiple TableNode instances on the same header level so such check is fine
return $true
}
[Boolean] IsIdenticalTo([BaseNode] $OtherNode) {
if (-not $this.IsSimilarTo($OtherNode)) {
return $false
}
# We don't compare $this.Headers intentionally
# It is fine to ignore the tables where headers are changed but rows are not changed
if ($this.Rows.Count -ne $OtherNode.Rows.Count) {
return $false
}
for ($rowIndex = 0; $rowIndex -lt $this.Rows.Count; $rowIndex++) {
if ($this.Rows[$rowIndex] -ne $OtherNode.Rows[$rowIndex]) {
return $false
}
}
return $true
}
static [TableNode] FromObjectsArray([PSCustomObject[]] $Table) {
if ($Table.Count -eq 0) {
throw "Failed to create TableNode from empty objects array"
}
[String] $tableHeaders = [TableNode]::ArrayToTableRow($Table[0].PSObject.Properties.Name)
[Collections.Generic.List[String]] $tableRows = @()
$Table | ForEach-Object {
$rowHeaders = [TableNode]::ArrayToTableRow($_.PSObject.Properties.Name)
if (($rowHeaders -ne $tableHeaders)) {
throw "Failed to create TableNode from objects array because objects have different properties"
}
$tableRows.Add([TableNode]::ArrayToTableRow($_.PSObject.Properties.Value))
}
return [TableNode]::new($tableHeaders, $tableRows)
}
hidden static [String] ArrayToTableRow([String[]] $Values) {
if ($Values.Count -eq 0) {
throw "Failed to create TableNode because some objects are empty"
}
$Values | ForEach-Object {
if ($_.Contains("|")) {
throw "Failed to create TableNode because some cells '$_' contains forbidden symbol '|'"
}
}
return [String]::Join("|", $Values)
}
}
class NoteNode: BaseNode {
[ValidateNotNullOrEmpty()]
[String] $Content
NoteNode([String] $Content) {
$this.Content = $Content
}
[String] ToMarkdown([Int32] $Level) {
return @(
'```',
$this.Content,
'```'
) -join "`n"
}
[PSCustomObject] ToJsonObject() {
return [PSCustomObject]@{
NodeType = $this.GetType().Name
Content = $this.Content
}
}
static [NoteNode] FromJsonObject([Object] $JsonObj) {
return [NoteNode]::new($JsonObj.Content)
}
[Boolean] IsSimilarTo([BaseNode] $OtherNode) {
if ($OtherNode.GetType() -ne [NoteNode]) {
return $false
}
return $this.Content -eq $OtherNode.Content
}
[Boolean] IsIdenticalTo([BaseNode] $OtherNode) {
return $this.IsSimilarTo($OtherNode)
}
}
@@ -1,34 +0,0 @@
using module ./SoftwareReport.BaseNodes.psm1
using module ./SoftwareReport.Nodes.psm1
class SoftwareReport {
[ValidateNotNullOrEmpty()]
[HeaderNode] $Root
SoftwareReport([String] $Title) {
$this.Root = [HeaderNode]::new($Title)
}
SoftwareReport([HeaderNode] $Root) {
$this.Root = $Root
}
[String] ToJson() {
return $this.Root.ToJsonObject() | ConvertTo-Json -Depth 10
}
static [SoftwareReport] FromJson([String] $JsonString) {
$jsonObj = $JsonString | ConvertFrom-Json
$rootNode = [NodesFactory]::ParseNodeFromObject($jsonObj)
return [SoftwareReport]::new($rootNode)
}
[String] ToMarkdown() {
return $this.Root.ToMarkdown().Trim()
}
[String] GetImageVersion() {
$imageVersionNode = $this.Root.Children ?? @() | Where-Object { ($_ -is [ToolVersionNode]) -and ($_.ToolName -eq "Image Version:") } | Select-Object -First 1
return $imageVersionNode.Version ?? "Unknown version"
}
}
@@ -1,558 +0,0 @@
using module ../SoftwareReport.psm1
using module ../SoftwareReport.DifferenceCalculator.psm1
Describe "Comparer.E2E" {
It "Some tools are updated" {
# Previous report
$prevSoftwareReport = [SoftwareReport]::new("macOS 11")
$prevSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.1 (20G817)")
$prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1")
$prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software")
$prevTools = $prevInstalledSoftware.AddHeader("Tools")
$prevTools.AddToolVersion("ToolWillBeUpdated1", "1.0.0")
$prevTools.AddToolVersion("ToolWillBeUpdated2", "3.0.1")
$prevTools.AddToolVersionsList("ToolWillBeUpdated3", @("14.0.0", "15.5.1"), "^\d+")
# Next report
$nextSoftwareReport = [SoftwareReport]::new("macOS 11")
$nextSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.1 (20G817)")
$nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.1")
$nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software")
$nextTools = $nextInstalledSoftware.AddHeader("Tools")
$nextTools.AddToolVersion("ToolWillBeUpdated1", "2.5.0")
$nextTools.AddToolVersion("ToolWillBeUpdated2", "3.0.2")
$nextTools.AddToolVersionsList("ToolWillBeUpdated3", @("14.2.0", "15.5.1"), "^\d+")
# Compare reports
$comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)
$comparer.CompareReports()
$comparer.GetMarkdownReport() | Should -BeExactly @'
# :desktop_computer: Actions Runner Image: macOS 11
- OS Version: macOS 11.7.1 (20G817)
- Image Version: 20220922.1
## :mega: What's changed?
### Updated
<table>
<thead>
<th>Category</th>
<th>Tool name</th>
<th>Previous (20220918.1)</th>
<th>Current (20220922.1)</th>
</thead>
<tbody>
<tr>
<td rowspan="3">Tools</td>
<td>ToolWillBeUpdated1</td>
<td>1.0.0</td>
<td>2.5.0</td>
</tr>
<tr>
<td>ToolWillBeUpdated2</td>
<td>3.0.1</td>
<td>3.0.2</td>
</tr>
<tr>
<td>ToolWillBeUpdated3</td>
<td>14.0.0</td>
<td>14.2.0</td>
</tr>
</tbody>
</table>
'@
}
It "Some tools are updated, added and removed" {
# Previous report
$prevSoftwareReport = [SoftwareReport]::new("macOS 11")
$prevSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.1 (20G817)")
$prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1")
$prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software")
$prevLanguagesAndRuntimes = $prevInstalledSoftware.AddHeader("Language and Runtime")
$prevLanguagesAndRuntimes.AddToolVersion("ToolWillBeRemoved", "5.1.16(1)-release")
$prevLanguagesAndRuntimes.AddToolVersionsListInline("ToolWithMultipleVersions3", @("1.2.100", "1.2.200", "1.3.500", "1.4.100", "1.4.200"), "^\d+\.\d+\.\d")
$prevLanguagesAndRuntimes.AddToolVersion("ToolWithoutChanges", "5.34.0")
$prevLanguagesAndRuntimes.AddToolVersion("ToolWillBeUpdated", "8.1.0")
$prevCachedTools = $prevInstalledSoftware.AddHeader("Cached Tools")
$prevCachedTools.AddToolVersionsList("ToolWithMultipleVersions1", @("2.7.3", "2.8.1", "3.1.2"), "^\d+\.\d+")
$prevCachedTools.AddToolVersionsList("ToolWithMultipleVersions2", @("14.8.0", "15.1.0", "16.4.2"), "^\d+")
$prevSQLSection = $prevInstalledSoftware.AddHeader("Databases")
$prevSQLSection.AddToolVersion("MineSQL", "6.1.0")
$prevSQLSection.AddNote("First Note")
# Next report
$nextSoftwareReport = [SoftwareReport]::new("macOS 11")
$nextSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.2 (20G922)")
$nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.0")
$nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software")
$nextLanguagesAndRuntimes = $nextInstalledSoftware.AddHeader("Language and Runtime")
$nextLanguagesAndRuntimes.AddToolVersion("ToolWillBeAdded", "16.18.0")
$nextLanguagesAndRuntimes.AddToolVersionsListInline("ToolWithMultipleVersions3", @("1.2.200", "1.3.515", "1.4.100", "1.4.200", "1.5.800"), "^\d+\.\d+\.\d")
$nextLanguagesAndRuntimes.AddToolVersion("ToolWithoutChanges", "5.34.0")
$nextLanguagesAndRuntimes.AddToolVersion("ToolWillBeUpdated", "8.3.0")
$nextCachedTools = $nextInstalledSoftware.AddHeader("Cached Tools")
$nextCachedTools.AddToolVersionsList("ToolWithMultipleVersions1", @("2.7.3", "2.8.1", "3.1.2"), "^\d+\.\d+")
$nextCachedTools.AddToolVersionsList("ToolWithMultipleVersions2", @("15.1.0", "16.4.2", "17.0.1"), "^\d+")
$nextSQLSection = $nextInstalledSoftware.AddHeader("Databases")
$nextSQLSection.AddToolVersion("MineSQL", "6.1.1")
$nextSQLSection.AddNote("Second Note")
# Compare reports
$comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)
$comparer.CompareReports()
$comparer.GetMarkdownReport() | Should -BeExactly @'
# :desktop_computer: Actions Runner Image: macOS 11
- OS Version: macOS 11.7.2 (20G922)
- Image Version: 20220922.0
## :mega: What's changed?
### Added :heavy_plus_sign:
<table>
<thead>
<th>Category</th>
<th>Tool name</th>
<th>Current (20220922.0)</th>
</thead>
<tbody>
<tr>
<td rowspan="2">Language and Runtime</td>
<td>ToolWillBeAdded</td>
<td>16.18.0</td>
</tr>
<tr>
<td>ToolWithMultipleVersions3</td>
<td>1.5.800</td>
</tr>
<tr>
<td rowspan="1">Cached Tools</td>
<td>ToolWithMultipleVersions2</td>
<td>17.0.1</td>
</tr>
</tbody>
</table>
### Deleted :heavy_minus_sign:
<table>
<thead>
<th>Category</th>
<th>Tool name</th>
<th>Previous (20220918.1)</th>
</thead>
<tbody>
<tr>
<td rowspan="2">Language and Runtime</td>
<td>ToolWithMultipleVersions3</td>
<td>1.2.100</td>
</tr>
<tr>
<td>ToolWillBeRemoved</td>
<td>5.1.16(1)-release</td>
</tr>
<tr>
<td rowspan="1">Cached Tools</td>
<td>ToolWithMultipleVersions2</td>
<td>14.8.0</td>
</tr>
</tbody>
</table>
### Updated
<table>
<thead>
<th>Category</th>
<th>Tool name</th>
<th>Previous (20220918.1)</th>
<th>Current (20220922.0)</th>
</thead>
<tbody>
<tr>
<td rowspan="1"></td>
<td>OS Version</td>
<td>macOS 11.7.1 (20G817)</td>
<td>macOS 11.7.2 (20G922)</td>
</tr>
<tr>
<td rowspan="2">Language and Runtime</td>
<td>ToolWithMultipleVersions3</td>
<td>1.3.500</td>
<td>1.3.515</td>
</tr>
<tr>
<td>ToolWillBeUpdated</td>
<td>8.1.0</td>
<td>8.3.0</td>
</tr>
<tr>
<td rowspan="1">Databases</td>
<td>MineSQL</td>
<td>6.1.0</td>
<td>6.1.1</td>
</tr>
</tbody>
</table>
'@
}
It "Header tree changes" {
# Previous report
$prevSoftwareReport = [SoftwareReport]::new("macOS 11")
$prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1")
$prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software")
$prevInstalledSoftware.AddToolVersion("ToolWithoutChanges", "5.34.0")
$prevInstalledSoftware.AddHeader("HeaderWillBeRemoved").AddHeader("SubheaderWillBeRemoved").AddToolVersion("ToolWillBeRemoved", "1.0.0")
$prevInstalledSoftware.AddHeader("Header1").AddToolVersion("ToolWillBeMovedToAnotherHeader", "3.0.0")
# Next report
$nextSoftwareReport = [SoftwareReport]::new("macOS 11")
$nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.0")
$nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software")
$nextInstalledSoftware.AddToolVersion("ToolWithoutChanges", "5.34.0")
$nextInstalledSoftware.AddHeader("HeaderWillBeAdded").AddHeader("SubheaderWillBeAdded").AddToolVersion("ToolWillBeAdded", "5.0.0")
$nextInstalledSoftware.AddHeader("Header2").AddToolVersion("ToolWillBeMovedToAnotherHeader", "3.0.0")
# Compare reports
$comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)
$comparer.CompareReports()
$comparer.GetMarkdownReport() | Should -BeExactly @'
# :desktop_computer: Actions Runner Image: macOS 11
- Image Version: 20220922.0
## :mega: What's changed?
### Added :heavy_plus_sign:
<table>
<thead>
<th>Category</th>
<th>Tool name</th>
<th>Current (20220922.0)</th>
</thead>
<tbody>
<tr>
<td rowspan="1">HeaderWillBeAdded ><br> SubheaderWillBeAdded</td>
<td>ToolWillBeAdded</td>
<td>5.0.0</td>
</tr>
<tr>
<td rowspan="1">Header2</td>
<td>ToolWillBeMovedToAnotherHeader</td>
<td>3.0.0</td>
</tr>
</tbody>
</table>
### Deleted :heavy_minus_sign:
<table>
<thead>
<th>Category</th>
<th>Tool name</th>
<th>Previous (20220918.1)</th>
</thead>
<tbody>
<tr>
<td rowspan="1">HeaderWillBeRemoved ><br> SubheaderWillBeRemoved</td>
<td>ToolWillBeRemoved</td>
<td>1.0.0</td>
</tr>
<tr>
<td rowspan="1">Header1</td>
<td>ToolWillBeMovedToAnotherHeader</td>
<td>3.0.0</td>
</tr>
</tbody>
</table>
'@
}
It "Tables are added and removed" {
# Previous report
$prevSoftwareReport = [SoftwareReport]::new("macOS 11")
$prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1")
$prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software")
$prevInstalledSoftware.AddHeader("HeaderWillExist").AddTable(@(
[PSCustomObject]@{TableInExistingHeaderWillBeRemoved = "Q"; Value = "25"},
[PSCustomObject]@{TableInExistingHeaderWillBeRemoved = "O"; Value = "24"}
))
$prevTools = $prevInstalledSoftware.AddHeader("Tools")
$prevTools.AddHeader("HeaderWillBeRemoved").AddTable(@(
[PSCustomObject]@{TableWillBeRemovedWithHeader = "Z"; Value = "30"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "W"; Value = "29"}
))
# Next report
$nextSoftwareReport = [SoftwareReport]::new("macOS 11")
$nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.1")
$nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software")
$nextInstalledSoftware.AddHeader("HeaderWillExist")
$nextTools = $nextInstalledSoftware.AddHeader("Tools")
$nextTools.AddToolVersion("ToolWillBeAdded", "3.0.1")
$nextTools.AddTable(@(
[PSCustomObject]@{NewTableInExistingHeader = "A"; Value = "1"},
[PSCustomObject]@{NewTableInExistingHeader = "B"; Value = "2"}
))
$nextTools.AddHeader("NewHeaderWithTable").AddTable(@(
[PSCustomObject]@{NewTableInNewHeader = "C"; Value = "3"},
[PSCustomObject]@{NewTableInNewHeader = "D"; Value = "4"}
))
# Compare reports
$comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)
$comparer.CompareReports()
$comparer.GetMarkdownReport() | Should -BeExactly @'
# :desktop_computer: Actions Runner Image: macOS 11
- Image Version: 20220922.1
## :mega: What's changed?
### Added :heavy_plus_sign:
<table>
<thead>
<th>Category</th>
<th>Tool name</th>
<th>Current (20220922.1)</th>
</thead>
<tbody>
<tr>
<td rowspan="1">Tools</td>
<td>ToolWillBeAdded</td>
<td>3.0.1</td>
</tr>
</tbody>
</table>
#### Tools
| NewTableInExistingHeader | Value |
| ------------------------ | ----- |
| A | 1 |
| B | 2 |
#### Tools > NewHeaderWithTable
| NewTableInNewHeader | Value |
| ------------------- | ----- |
| C | 3 |
| D | 4 |
### Deleted :heavy_minus_sign:
#### HeaderWillExist
| TableInExistingHeaderWillBeRemoved | Value |
| ---------------------------------- | ------ |
| ~~Q~~ | ~~25~~ |
| ~~O~~ | ~~24~~ |
#### Tools > HeaderWillBeRemoved
| TableWillBeRemovedWithHeader | Value |
| ---------------------------- | ------ |
| ~~Z~~ | ~~30~~ |
| ~~W~~ | ~~29~~ |
'@
}
It "Tables are changed" {
# Previous report
$prevSoftwareReport = [SoftwareReport]::new("macOS 11")
$prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1")
$prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software")
$prevTools = $prevInstalledSoftware.AddHeader("Tools")
$prevTools.AddHeader("TableWithAddedRows").AddTable(@(
[PSCustomObject]@{TableWillBeRemovedWithHeader = "AA"; Value = "10"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "AB"; Value = "11"}
))
$prevTools.AddHeader("TableWithRemovedRows").AddTable(@(
[PSCustomObject]@{TableWillBeRemovedWithHeader = "BA"; Value = "32"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "BB"; Value = "33"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "BC"; Value = "34"}
))
$prevTools.AddHeader("TableWithUpdatedRow").AddTable(@(
[PSCustomObject]@{TableWillBeRemovedWithHeader = "CA"; Value = "42"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "CB"; Value = "43"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "CC"; Value = "44"}
))
$prevTools.AddHeader("TableWithUpdatedRows").AddTable(@(
[PSCustomObject]@{TableWillBeRemovedWithHeader = "DA"; Value = "50"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "DB"; Value = "51"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "DC"; Value = "52"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "DD"; Value = "53"}
))
$prevTools.AddHeader("TableWithComplexChanges").AddTable(@(
[PSCustomObject]@{TableWillBeRemovedWithHeader = "EA"; Value = "62"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "EB"; Value = "63"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "EC"; Value = "64"}
[PSCustomObject]@{TableWillBeRemovedWithHeader = "ED"; Value = "65"}
))
$prevTools.AddHeader("TableWithOnlyHeaderChanged").AddTable(@(
[PSCustomObject]@{TableWithOnlyHeaderChanged = "FA"; Value = "72"},
[PSCustomObject]@{TableWithOnlyHeaderChanged = "FB"; Value = "73"}
))
$prevTools.AddHeader("TableWithHeaderAndRowsChanges").AddTable(@(
[PSCustomObject]@{TableWithHeaderAndRowsChanges = "GA"; Value = "82"},
[PSCustomObject]@{TableWithHeaderAndRowsChanges = "GB"; Value = "83"},
[PSCustomObject]@{TableWithHeaderAndRowsChanges = "GC"; Value = "84"}
))
# Next report
$nextSoftwareReport = [SoftwareReport]::new("macOS 11")
$nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.1")
$nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software")
$nextTools = $nextInstalledSoftware.AddHeader("Tools")
$nextTools.AddHeader("TableWithAddedRows").AddTable(@(
[PSCustomObject]@{TableWillBeRemovedWithHeader = "AA"; Value = "10"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "AB"; Value = "11"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "AC"; Value = "12"}
))
$nextTools.AddHeader("TableWithRemovedRows").AddTable(@(
[PSCustomObject]@{TableWillBeRemovedWithHeader = "BB"; Value = "33"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "BC"; Value = "34"}
))
$nextTools.AddHeader("TableWithUpdatedRow").AddTable(@(
[PSCustomObject]@{TableWillBeRemovedWithHeader = "CA"; Value = "42"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "CB"; Value = "500"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "CC"; Value = "44"}
))
$nextTools.AddHeader("TableWithUpdatedRows").AddTable(@(
[PSCustomObject]@{TableWillBeRemovedWithHeader = "DA"; Value = "50"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "DB"; Value = "5100"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "DC"; Value = "5200"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "DD"; Value = "53"}
))
$nextTools.AddHeader("TableWithComplexChanges").AddTable(@(
[PSCustomObject]@{TableWillBeRemovedWithHeader = "EB"; Value = "63"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "EC"; Value = "640"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "ED"; Value = "65"},
[PSCustomObject]@{TableWillBeRemovedWithHeader = "EE"; Value = "66"}
))
$nextTools.AddHeader("TableWithOnlyHeaderChanged").AddTable(@(
[PSCustomObject]@{TableWithOnlyHeaderChanged2 = "FA"; Value = "72"},
[PSCustomObject]@{TableWithOnlyHeaderChanged2 = "FB"; Value = "73"}
))
$nextTools.AddHeader("TableWithHeaderAndRowsChanges").AddTable(@(
[PSCustomObject]@{TableWithHeaderAndRowsChanges2 = "GA"; Value = "82"},
[PSCustomObject]@{TableWithHeaderAndRowsChanges2 = "GE"; Value = "850"},
[PSCustomObject]@{TableWithHeaderAndRowsChanges2 = "GC"; Value = "840"}
))
# Compare reports
$comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)
$comparer.CompareReports()
$comparer.GetMarkdownReport() | Should -BeExactly @'
# :desktop_computer: Actions Runner Image: macOS 11
- Image Version: 20220922.1
## :mega: What's changed?
### Added :heavy_plus_sign:
#### Tools > TableWithAddedRows
| TableWillBeRemovedWithHeader | Value |
| ---------------------------- | ----- |
| AC | 12 |
#### Tools > TableWithHeaderAndRowsChanges
| TableWithHeaderAndRowsChanges2 | Value |
| ------------------------------ | ----- |
| GA | 82 |
| GE | 850 |
| GC | 840 |
### Deleted :heavy_minus_sign:
#### Tools > TableWithRemovedRows
| TableWillBeRemovedWithHeader | Value |
| ---------------------------- | ------ |
| ~~BA~~ | ~~32~~ |
#### Tools > TableWithHeaderAndRowsChanges
| TableWithHeaderAndRowsChanges | Value |
| ----------------------------- | ------ |
| ~~GA~~ | ~~82~~ |
| ~~GB~~ | ~~83~~ |
| ~~GC~~ | ~~84~~ |
### Updated
#### Tools > TableWithUpdatedRow
| TableWillBeRemovedWithHeader | Value |
| ---------------------------- | ------ |
| ~~CB~~ | ~~43~~ |
| CB | 500 |
#### Tools > TableWithUpdatedRows
| TableWillBeRemovedWithHeader | Value |
| ---------------------------- | ------ |
| ~~DB~~ | ~~51~~ |
| ~~DC~~ | ~~52~~ |
| DB | 5100 |
| DC | 5200 |
#### Tools > TableWithComplexChanges
| TableWillBeRemovedWithHeader | Value |
| ---------------------------- | ------ |
| ~~EA~~ | ~~62~~ |
| ~~EC~~ | ~~64~~ |
| EC | 640 |
| EE | 66 |
'@
}
It "Reports are identical" {
# Previous report
$prevSoftwareReport = [SoftwareReport]::new("macOS 11")
$prevSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.1 (20G817)")
$prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1")
$prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software")
$prevTools = $prevInstalledSoftware.AddHeader("Tools")
$prevTools.AddToolVersion("ToolA", "1.0.0")
$prevTools.AddToolVersion("ToolB", "3.0.1")
# Next report
$nextSoftwareReport = [SoftwareReport]::new("macOS 11")
$nextSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.1 (20G817)")
$nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.1")
$nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software")
$nextTools = $nextInstalledSoftware.AddHeader("Tools")
$nextTools.AddToolVersion("ToolA", "1.0.0")
$nextTools.AddToolVersion("ToolB", "3.0.1")
# Compare reports
$comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)
$comparer.CompareReports()
$comparer.GetMarkdownReport() | Should -BeExactly @'
# :desktop_computer: Actions Runner Image: macOS 11
- OS Version: macOS 11.7.1 (20G817)
- Image Version: 20220922.1
## :mega: What's changed?
'@
}
}
@@ -1,603 +0,0 @@
using module ../SoftwareReport.Nodes.psm1
using module ../SoftwareReport.DifferenceCalculator.psm1
BeforeDiscovery {
Import-Module $(Join-Path $PSScriptRoot "TestHelpers.psm1") -DisableNameChecking
}
Describe "Comparer.UnitTests" {
Describe "Headers Tree" {
It "Add Node to existing header" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddToolVersion("MyTool1", "2.1.3")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 1
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 0
$comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty
$comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.AddedItems[0].CurrentReportNode.Version | Should -Be "2.1.3"
$comparer.AddedItems[0].Headers | Should -BeArray @("MyHeader")
}
It "Add new header with Node" {
$prevReport = [HeaderNode]::new("Version 1")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddHeader("MySubHeader").AddToolVersion("MyTool1", "2.1.3")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 1
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 0
$comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty
$comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.AddedItems[0].CurrentReportNode.Version | Should -Be "2.1.3"
$comparer.AddedItems[0].Headers | Should -BeArray @("MyHeader", "MySubHeader")
}
It "Remove Node from existing header" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddToolVersion("MyTool1", "2.1.3")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 1
$comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be "2.1.3"
$comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty
$comparer.DeletedItems[0].Headers | Should -BeArray @("MyHeader")
}
It "Remove header with Node" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.3")
$nextReport = [HeaderNode]::new("Version 2")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 1
$comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be "2.1.3"
$comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty
$comparer.DeletedItems[0].Headers | Should -BeArray @("MyHeader", "MySubheader")
}
It "Node with minor changes" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.3")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.4")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 1
$comparer.DeletedItems | Should -HaveCount 0
$comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].PreviousReportNode.Version | Should -Be "2.1.3"
$comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].CurrentReportNode.Version | Should -Be "2.1.4"
$comparer.ChangedItems[0].Headers | Should -BeArray @("MyHeader", "MySubHeader")
}
It "Node without changes" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.3")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.3")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 0
}
It "Node is moved to different header" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.3")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddHeader("MySubheader2").AddToolVersion("MyTool1", "2.1.3")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 1
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 1
$comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty
$comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.AddedItems[0].CurrentReportNode.Version | Should -Be "2.1.3"
$comparer.AddedItems[0].Headers | Should -BeArray @("MyHeader", "MySubheader2")
$comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be "2.1.3"
$comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty
$comparer.DeletedItems[0].Headers | Should -BeArray @("MyHeader", "MySubheader")
}
It "Complex structure" {
$prevReport = [HeaderNode]::new("Version 1")
$prevSubHeader = $prevReport.AddHeader("MyHeader").AddHeader("MySubheader")
$prevSubHeader.AddToolVersion("MyTool1", "2.1.3")
$prevSubHeader.AddHeader("MySubSubheader").AddToolVersion("MyTool2", "2.9.1")
$prevReport.AddHeader("MyHeader2")
$prevReport.AddHeader("MyHeader3").AddHeader("MySubheader3").AddToolVersion("MyTool3", "14.2.1")
$nextReport = [HeaderNode]::new("Version 2")
$nextSubHeader = $nextReport.AddHeader("MyHeader").AddHeader("MySubheader")
$nextSubHeader.AddToolVersion("MyTool1", "2.1.4")
$nextSubSubHeader = $nextSubHeader.AddHeader("MySubSubheader")
$nextSubSubHeader.AddToolVersion("MyTool2", "2.9.1")
$nextSubSubHeader.AddToolVersion("MyTool4", "2.7.6")
$nextReport.AddHeader("MyHeader2")
$nextReport.AddHeader("MyHeader3")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 1
$comparer.ChangedItems | Should -HaveCount 1
$comparer.DeletedItems | Should -HaveCount 1
$comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty
$comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool4"
$comparer.AddedItems[0].CurrentReportNode.Version | Should -Be "2.7.6"
$comparer.AddedItems[0].Headers | Should -BeArray @("MyHeader", "MySubheader", "MySubSubheader")
$comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].PreviousReportNode.Version | Should -Be "2.1.3"
$comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].CurrentReportNode.Version | Should -Be "2.1.4"
$comparer.ChangedItems[0].Headers | Should -BeArray @("MyHeader", "MySubheader")
$comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool3"
$comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be "14.2.1"
$comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty
$comparer.DeletedItems[0].Headers | Should -BeArray @("MyHeader3", "MySubheader3")
}
}
Describe "ToolVersionNode" {
It "ToolVersionNode is updated" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddToolVersion("MyTool1", "2.1.3")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddToolVersion("MyTool1", "2.1.4")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 1
$comparer.DeletedItems | Should -HaveCount 0
$comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].PreviousReportNode.Version | Should -Be "2.1.3"
$comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])
$comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].CurrentReportNode.Version | Should -Be "2.1.4"
$comparer.ChangedItems[0].Headers | Should -BeArray @("MyHeader")
}
}
Describe "ToolVersionsListNode" {
It "Single version is not changed" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3"), "^.+")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3"), "^.+")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 0
}
It "Single version is changed" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3"), "^\d+")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.4"), "^\d+")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 1
$comparer.DeletedItems | Should -HaveCount 0
$comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @("2.1.3")
$comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @("2.1.4")
}
It "Major version is added" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3"), "^\d+")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3", "3.1.4"), "^\d+")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 1
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 0
$comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty
$comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @("3.1.4")
}
It "Major version is removed" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3", "3.1.4"), "^\d+")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("3.1.4"), "^\d+")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 1
$comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @("2.1.3")
$comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty
}
It "Major version is changed" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("3.1.4"), "^\d+")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("3.2.0"), "^\d+")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 1
$comparer.DeletedItems | Should -HaveCount 0
$comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @("3.1.4")
$comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @("3.2.0")
}
It "Major version is added, removed and updated at the same time" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("1.0.0", "2.1.3", "3.1.4", "4.0.2"), "^\d+")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3", "3.2.0", "4.0.2", "5.1.0"), "^\d+")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 1
$comparer.ChangedItems | Should -HaveCount 1
$comparer.DeletedItems | Should -HaveCount 1
$comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty
$comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @("5.1.0")
$comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @("3.1.4")
$comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @("3.2.0")
$comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @("1.0.0")
$comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty
}
It "Minor version is added, removed and updated at the same time" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.3.8", "2.4.9", "2.5.3", "2.6.0", "2.7.4", "2.8.0"), "^\d+\.\d+")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.5.3", "2.6.2", "2.7.5", "2.8.0", "2.9.2", "2.10.3"), "^\d+\.\d+")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 1
$comparer.ChangedItems | Should -HaveCount 1
$comparer.DeletedItems | Should -HaveCount 1
$comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty
$comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @("2.9.2", "2.10.3")
$comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @("2.6.0", "2.7.4")
$comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @("2.6.2", "2.7.5")
$comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @("2.3.8", "2.4.9")
$comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty
}
It "Patch version is added, removed and updated at the same time" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.3.8", "2.4.9", "2.5.3", "2.6.0", "2.7.4"), "^\d+\.\d+\.\d+")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.4.9", "2.5.4", "2.6.0", "2.7.5", "2.8.2"), "^\d+\.\d+\.\d+")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 1
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 1
$comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty
$comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1"
$comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @("2.5.4", "2.7.5", "2.8.2")
$comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])
$comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1"
$comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @("2.3.8", "2.5.3", "2.7.4")
$comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty
}
}
Describe "TableNode" {
It "Rows are added" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2")))
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2", "C1|C2", "D1|D2")))
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 1
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 0
$comparer.AddedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])
$comparer.AddedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value"
$comparer.AddedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2")
$comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])
$comparer.AddedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value"
$comparer.AddedItems[0].CurrentReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2", "C1|C2", "D1|D2")
}
It "Rows are deleted" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2", "C1|C2", "D1|D2")))
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("C1|C2", "D1|D2")))
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 1
$comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])
$comparer.DeletedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value"
$comparer.DeletedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2", "C1|C2", "D1|D2")
$comparer.DeletedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])
$comparer.DeletedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value"
$comparer.DeletedItems[0].CurrentReportNode.Rows | Should -BeArray @("C1|C2", "D1|D2")
}
It "Rows are changed" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2")))
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B3|B4")))
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 1
$comparer.DeletedItems | Should -HaveCount 0
$comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])
$comparer.ChangedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value"
$comparer.ChangedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2")
$comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])
$comparer.ChangedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value"
$comparer.ChangedItems[0].CurrentReportNode.Rows | Should -BeArray @("A1|A2", "B3|B4")
}
It "Rows are changed and updated at the same time" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2")))
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B3|B4", "C1|C2")))
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 1
$comparer.DeletedItems | Should -HaveCount 0
$comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])
$comparer.ChangedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value"
$comparer.ChangedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2")
$comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])
$comparer.ChangedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value"
$comparer.ChangedItems[0].CurrentReportNode.Rows | Should -BeArray @("A1|A2", "B3|B4", "C1|C2")
}
It "Rows are changed and removed at the same time" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2", "C1|C2")))
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B3|B4")))
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 1
$comparer.DeletedItems | Should -HaveCount 0
$comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])
$comparer.ChangedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value"
$comparer.ChangedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2", "C1|C2")
$comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])
$comparer.ChangedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value"
$comparer.ChangedItems[0].CurrentReportNode.Rows | Should -BeArray @("A1|A2", "B3|B4")
}
It "Rows are not changed" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2")))
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2")))
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 0
}
It "Rows are not changed but header is changed" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2")))
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value2", @("A1|A2", "B1|B2")))
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 0
}
It "Rows are changed and header is changed at the same time" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2")))
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value2", @("A1|A2", "B1|B2", "C1|C2")))
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 1
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 1
$comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty
$comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])
$comparer.AddedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value2"
$comparer.AddedItems[0].CurrentReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2", "C1|C2")
$comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])
$comparer.DeletedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value"
$comparer.DeletedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2")
$comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty
}
}
Describe "NoteNode" {
It "NoteNode is ignored from report" {
$prevReport = [HeaderNode]::new("Version 1")
$prevReport.AddNote("MyFirstNote")
$prevReport.AddHeader("MyFirstHeader").AddNote("MyFirstSubNote")
$nextReport = [HeaderNode]::new("Version 2")
$nextReport.AddNote("MySecondNote")
$nextReport.AddHeader("MySecondHeader").AddNote("MySecondSubNote")
$comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)
$comparer.CompareReports()
$comparer.AddedItems | Should -HaveCount 0
$comparer.ChangedItems | Should -HaveCount 0
$comparer.DeletedItems | Should -HaveCount 0
}
}
}
@@ -1,291 +0,0 @@
using module ../SoftwareReport.Nodes.psm1
using module ../SoftwareReport.DifferenceRender.psm1
BeforeDiscovery {
Import-Module $(Join-Path $PSScriptRoot "TestHelpers.psm1") -DisableNameChecking
}
Describe "ComparerReport.UnitTests" {
BeforeAll {
$script:DifferenceRender = [SoftwareReportDifferenceRender]::new()
}
Context "CalculateHtmlTableRowSpan" {
It "Without the equal cells" {
$table = @(
[PSCustomObject]@{ Key = "A"; Value = "1" }
[PSCustomObject]@{ Key = "B"; Value = "2" }
[PSCustomObject]@{ Key = "C"; Value = "3" }
)
$actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, "Key")
$actual | Should -BeArray @(1, 1, 1)
}
It "Only equal cells" {
$table = @(
[PSCustomObject]@{ Key = "A"; Value = "D" }
[PSCustomObject]@{ Key = "B"; Value = "D" }
[PSCustomObject]@{ Key = "C"; Value = "D" }
)
$actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, "Value")
$actual | Should -BeArray @(3, 0, 0)
}
It "Single row" {
$table = @(
[PSCustomObject]@{ Key = "A"; Value = "1" }
)
$actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, "Key")
$actual | Should -BeArray @(1)
}
It "Different cells" {
$table = @(
[PSCustomObject]@{ Key = "A"; Value = "1" }
[PSCustomObject]@{ Key = "B"; Value = "2" }
[PSCustomObject]@{ Key = "B"; Value = "3" }
[PSCustomObject]@{ Key = "C"; Value = "4" }
[PSCustomObject]@{ Key = "C"; Value = "5" }
[PSCustomObject]@{ Key = "C"; Value = "6" }
[PSCustomObject]@{ Key = "D"; Value = "7" }
[PSCustomObject]@{ Key = "E"; Value = "8" }
[PSCustomObject]@{ Key = "E"; Value = "9" }
[PSCustomObject]@{ Key = "F"; Value = "10" }
)
$actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, "Key")
$actual | Should -BeArray @(1, 2, 0, 3, 0, 0, 1, 2, 0, 1)
}
}
Context "RenderCategory" {
It "With line separator" {
$actual = $DifferenceRender.RenderCategory(@("Header 1", "Header 2", "Header 3"), $true)
$actual | Should -Be "Header 2 ><br> Header 3"
}
It "Without line separator" {
$actual = $DifferenceRender.RenderCategory(@("Header 1", "Header 2", "Header 3"), $false)
$actual | Should -Be "Header 2 > Header 3"
}
It "One header" {
$actual = $DifferenceRender.RenderCategory(@("Header 1"), $false)
$actual | Should -Be ""
}
It "Empty headers" {
$actual = $DifferenceRender.RenderCategory(@(), $false)
$actual | Should -Be ""
}
}
Context "RenderToolName" {
It "Clear tool name" {
$actual = $DifferenceRender.RenderToolName("My Tool 1")
$actual | Should -Be "My Tool 1"
}
It "Name with colon symbol" {
$actual = $DifferenceRender.RenderToolName("My Tool 1:")
$actual | Should -Be "My Tool 1"
}
}
Context "StrikeTableRow" {
It "Simple row" {
$actual = $DifferenceRender.StrikeTableRow("Test1|Test2|Test3")
$actual | Should -Be "~~Test1~~|~~Test2~~|~~Test3~~"
}
It "Row with spaces" {
$actual = $DifferenceRender.StrikeTableRow("Test 1|Test 2|Test 3")
$actual | Should -Be "~~Test 1~~|~~Test 2~~|~~Test 3~~"
}
}
Context "RenderHtmlTable" {
It "Simple table" {
$table = @(
[PSCustomObject]@{ "Category" = "A"; "Tool name" = "My Tool 1"; "Version" = "1.0" },
[PSCustomObject]@{ "Category" = "B"; "Tool name" = "My Tool 2"; "Version" = "2.0" },
[PSCustomObject]@{ "Category" = "C"; "Tool name" = "My Tool 3"; "Version" = "3.0" }
)
$renderedTable = $DifferenceRender.RenderHtmlTable($table, "Category")
$renderedTable | Should -Be @'
<table>
<thead>
<th>Category</th>
<th>Tool name</th>
<th>Version</th>
</thead>
<tbody>
<tr>
<td rowspan="1">A</td>
<td>My Tool 1</td>
<td>1.0</td>
</tr>
<tr>
<td rowspan="1">B</td>
<td>My Tool 2</td>
<td>2.0</td>
</tr>
<tr>
<td rowspan="1">C</td>
<td>My Tool 3</td>
<td>3.0</td>
</tr>
</tbody>
</table>
'@
}
It "Table with the same category" {
$table = @(
[PSCustomObject]@{ "Category" = "A"; "Tool name" = "My Tool 1"; "Version" = "1.0" },
[PSCustomObject]@{ "Category" = "A"; "Tool name" = "My Tool 2"; "Version" = "2.0" },
[PSCustomObject]@{ "Category" = "A"; "Tool name" = "My Tool 3"; "Version" = "3.0" },
[PSCustomObject]@{ "Category" = "B"; "Tool name" = "My Tool 4"; "Version" = "4.0" }
)
$renderedTable = $DifferenceRender.RenderHtmlTable($table, "Category")
$renderedTable | Should -Be @'
<table>
<thead>
<th>Category</th>
<th>Tool name</th>
<th>Version</th>
</thead>
<tbody>
<tr>
<td rowspan="3">A</td>
<td>My Tool 1</td>
<td>1.0</td>
</tr>
<tr>
<td>My Tool 2</td>
<td>2.0</td>
</tr>
<tr>
<td>My Tool 3</td>
<td>3.0</td>
</tr>
<tr>
<td rowspan="1">B</td>
<td>My Tool 4</td>
<td>4.0</td>
</tr>
</tbody>
</table>
'@
}
}
Context "RenderTableNodesDiff" {
It "Add new table" {
$previousNode = $null
$currentNode = [TableNode]::new("Name|Value", @("A|1", "B|2"))
$reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3"))
$actual = $DifferenceRender.RenderTableNodesDiff($reportItem)
$actual | Should -Be @'
#### Header 2 > Header 3
| Name | Value |
| ---- | ----- |
| A | 1 |
| B | 2 |
'@
}
It "Remove existing table" {
$previousNode = [TableNode]::new("Name|Value", @("A|1", "B|2"))
$currentNode = $null
$reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3"))
$actual = $DifferenceRender.RenderTableNodesDiff($reportItem)
$actual | Should -Be @'
#### Header 2 > Header 3
| Name | Value |
| ----- | ----- |
| ~~A~~ | ~~1~~ |
| ~~B~~ | ~~2~~ |
'@
}
It "Add new rows to existing table" {
$previousNode = [TableNode]::new("Name|Value", @("A|1", "B|2"))
$currentNode = [TableNode]::new("Name|Value", @("A|1", "B|2", "C|3", "D|4"))
$reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3"))
$actual = $DifferenceRender.RenderTableNodesDiff($reportItem)
$actual | Should -Be @'
#### Header 2 > Header 3
| Name | Value |
| ---- | ----- |
| C | 3 |
| D | 4 |
'@
}
It "Remove rows from existing table" {
$previousNode = [TableNode]::new("Name|Value", @("A|1", "B|2", "C|3", "D|4"))
$currentNode = [TableNode]::new("Name|Value", @("C|3", "D|4"))
$reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3"))
$actual = $DifferenceRender.RenderTableNodesDiff($reportItem)
$actual | Should -Be @'
#### Header 2 > Header 3
| Name | Value |
| ----- | ----- |
| ~~A~~ | ~~1~~ |
| ~~B~~ | ~~2~~ |
'@
}
It "Row is changed in existing table" {
$previousNode = [TableNode]::new("Name|Value", @("A|1", "B|2"))
$currentNode = [TableNode]::new("Name|Value", @("A|1", "B|3"))
$reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3"))
$actual = $DifferenceRender.RenderTableNodesDiff($reportItem)
$actual | Should -Be @'
#### Header 2 > Header 3
| Name | Value |
| ----- | ----- |
| ~~B~~ | ~~2~~ |
| B | 3 |
'@
}
It "Row is changed, added and removed at the same time in existing table" {
$previousNode = [TableNode]::new("Name|Value", @("A|1", "B|2", "C|3", "D|4"))
$currentNode = [TableNode]::new("Name|Value", @("B|2", "C|4", "D|4", "E|5"))
$reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3"))
$actual = $DifferenceRender.RenderTableNodesDiff($reportItem)
$actual | Should -Be @'
#### Header 2 > Header 3
| Name | Value |
| ----- | ----- |
| ~~A~~ | ~~1~~ |
| ~~C~~ | ~~3~~ |
| C | 4 |
| E | 5 |
'@
}
}
}
@@ -1,93 +0,0 @@
using module ../SoftwareReport.psm1
using module ../SoftwareReport.Nodes.psm1
Describe "SoftwareReport.E2E" {
Context "Report example 1" {
BeforeEach {
$softwareReport = [SoftwareReport]::new("macOS 11")
$softwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7 (20G817)")
$softwareReport.Root.AddToolVersion("Image Version:", "20220918.1")
$installedSoftware = $softwareReport.Root.AddHeader("Installed Software")
$languagesAndRuntimes = $installedSoftware.AddHeader("Language and Runtime")
$languagesAndRuntimes.AddToolVersion("Bash", "5.1.16(1)-release")
$languagesAndRuntimes.AddToolVersionsListInline(".NET Core SDK", @("1.2.100", "1.2.200", "3.1.414"), "^\d+\.\d+\.\d")
$languagesAndRuntimes.AddNode([ToolVersionNode]::new("Perl", "5.34.0"))
$cachedTools = $installedSoftware.AddHeader("Cached Tools")
$cachedTools.AddToolVersionsList("Ruby", @("2.7.3", "2.8.1", "3.1.2"), "^\d+\.\d+")
$cachedTools.AddToolVersionsList("Node.js", @("14.8.0", "15.1.0", "16.4.2"), "^\d+")
$javaSection = $installedSoftware.AddHeader("Java")
$javaSection.AddTable(@(
[PSCustomObject] @{ Version = "8.0.125"; Vendor = "My Vendor"; "Environment Variable" = "JAVA_HOME_8_X64" },
[PSCustomObject] @{ Version = "11.3.103"; Vendor = "My Vendor"; "Environment Variable" = "JAVA_HOME_11_X64" }
))
$sqlSection = $installedSoftware.AddHeader("MySQL")
$sqlSection.AddToolVersion("MySQL", "6.1.0")
$sqlSection.AddNote("MySQL service is disabled by default.`nUse the following command as a part of your job to start the service: 'sudo systemctl start mysql.service'")
$expectedMarkdown = @'
# macOS 11
- OS Version: macOS 11.7 (20G817)
- Image Version: 20220918.1
## Installed Software
### Language and Runtime
- Bash 5.1.16(1)-release
- .NET Core SDK: 1.2.100, 1.2.200, 3.1.414
- Perl 5.34.0
### Cached Tools
#### Ruby
- 2.7.3
- 2.8.1
- 3.1.2
#### Node.js
- 14.8.0
- 15.1.0
- 16.4.2
### Java
| Version | Vendor | Environment Variable |
| -------- | --------- | -------------------- |
| 8.0.125 | My Vendor | JAVA_HOME_8_X64 |
| 11.3.103 | My Vendor | JAVA_HOME_11_X64 |
### MySQL
- MySQL 6.1.0
```
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'
```
'@
}
It "ToMarkdown" {
$softwareReport.ToMarkdown() | Should -Be $expectedMarkdown
}
It "Serialization + Deserialization" {
$json = $softwareReport.ToJson()
$deserializedReport = [SoftwareReport]::FromJson($json)
$deserializedReport.ToMarkdown() | Should -Be $expectedMarkdown
}
}
Context "GetImageVersion" {
It "Image version exists" {
$softwareReport = [SoftwareReport]::new("MyReport")
$softwareReport.Root.AddToolVersion("Image Version:", "123.4")
$softwareReport.GetImageVersion() | Should -Be "123.4"
}
It "Empty report" {
$softwareReport = [SoftwareReport]::new("MyReport")
$softwareReport.GetImageVersion() | Should -Be "Unknown version"
}
}
}
@@ -1,511 +0,0 @@
using module ../SoftwareReport.Nodes.psm1
BeforeDiscovery {
Import-Module $(Join-Path $PSScriptRoot "TestHelpers.psm1") -DisableNameChecking
}
Describe "Nodes.UnitTests" {
Context "ToolVersionNode" {
It "ToMarkdown" {
$node = [ToolVersionNode]::new("MyTool", "2.1.3")
$node.ToMarkdown() | Should -Be "- MyTool 2.1.3"
}
It "GetValue" {
$node = [ToolVersionNode]::new("MyTool", "2.1.3")
$node.GetValue() | Should -Be "2.1.3"
}
It "Serialization" {
$node = [ToolVersionNode]::new("MyTool", "2.1.3")
$json = $node.ToJsonObject()
$json.NodeType | Should -Be "ToolVersionNode"
$json.ToolName | Should -Be "MyTool"
$json.Version | Should -Be "2.1.3"
}
It "Deserialization" {
{ [ToolVersionNode]::FromJsonObject(@{ NodeType = "ToolVersionNode"; ToolName = ""; Version = "2.1.3" }) } | Should -Throw '*Exception setting "ToolName": "The argument is null or empty.*'
{ [ToolVersionNode]::FromJsonObject(@{ NodeType = "ToolVersionNode"; ToolName = "MyTool"; Version = "" }) } | Should -Throw '*Exception setting "Version": "The argument is null or empty.*'
{ [ToolVersionNode]::FromJsonObject(@{ NodeType = "ToolVersionNode"; ToolName = "MyTool"; Version = "2.1.3" }) } | Should -Not -Throw
}
It "Serialization + Deserialization" {
$node = [ToolVersionNode]::new("MyTool", "2.1.3")
$json = $node.ToJsonObject()
$node2 = [ToolVersionNode]::FromJsonObject($json)
$json2 = $node2.ToJsonObject()
$($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json)
}
It "IsSimilarTo" {
[ToolVersionNode]::new("MyTool", "2.1.3").IsSimilarTo([ToolVersionNode]::new("MyTool", "2.1.3")) | Should -BeTrue
[ToolVersionNode]::new("MyTool", "2.1.3").IsSimilarTo([ToolVersionNode]::new("MyTool", "1.0.0")) | Should -BeTrue
[ToolVersionNode]::new("MyTool", "2.1.3").IsSimilarTo([ToolVersionNode]::new("MyTool2", "2.1.3")) | Should -BeFalse
}
It "IsIdenticalTo" {
[ToolVersionNode]::new("MyTool", "2.1.3").IsIdenticalTo([ToolVersionNode]::new("MyTool", "2.1.3")) | Should -BeTrue
[ToolVersionNode]::new("MyTool", "2.1.3").IsIdenticalTo([ToolVersionNode]::new("MyTool", "1.0.0")) | Should -BeFalse
[ToolVersionNode]::new("MyTool", "2.1.3").IsIdenticalTo([ToolVersionNode]::new("MyTool2", "2.1.3")) | Should -BeFalse
}
}
Context "ToolVersionsListNode" {
It "ToMarkdown - List" {
$node = [ToolVersionsListNode]::new("MyTool", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "List")
$expected = @(
"",
"# MyTool"
"- 2.7.7"
"- 3.0.5"
"- 3.1.3"
) -join "`n"
$node.ToMarkdown() | Should -Be $expected
}
It "ToMarkdown - Inline" {
$node = [ToolVersionsListNode]::new("MyTool", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "Inline")
$node.ToMarkdown() | Should -Be "- MyTool: 2.7.7, 3.0.5, 3.1.3"
}
It "GetValue" {
$node = [ToolVersionsListNode]::new("MyTool", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "List")
$node.GetValue() | Should -Be "2.7.7, 3.0.5, 3.1.3"
}
It "Serialization - List" {
$node = [ToolVersionsListNode]::new("Ruby", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "List")
$json = $node.ToJsonObject()
$json.NodeType | Should -Be "ToolVersionsListNode"
$json.ToolName | Should -Be "Ruby"
$json.Versions | Should -BeArray @("2.7.7", "3.0.5", "3.1.3")
$json.MajorVersionRegex | Should -Be "^.+"
$json.ListType | Should -Be "List"
}
It "Serialization - Inline" {
$node = [ToolVersionsListNode]::new("Ruby", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "Inline")
$json = $node.ToJsonObject()
$json.NodeType | Should -Be "ToolVersionsListNode"
$json.ToolName | Should -Be "Ruby"
$json.Versions | Should -BeArray @("2.7.7", "3.0.5", "3.1.3")
$json.MajorVersionRegex | Should -Be "^.+"
$json.ListType | Should -Be "Inline"
}
It "Deserialization" {
{ [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = ""; Versions = @("2.1.3", "3.1.4"); MajorVersionRegex = "^\d+"; ListType = "List" }) } | Should -Throw '*Exception setting "ToolName": "The argument is null or empty.*'
{ [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; MajorVersionRegex = "^\d+"; ListType = "List" }) } | Should -Throw '*Exception setting "Versions": "The argument is null or empty.*'
{ [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @(); MajorVersionRegex = "^\d+"; ListType = "List" }) } | Should -Throw '*Exception setting "Versions": "The argument is null, empty,*'
{ [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @("2.1.3", '2.2.4'); MajorVersionRegex = "^\d+"; ListType = "List" }) } | Should -Throw 'Multiple versions from list * return the same result from regex *'
{ [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @("2.1.3", "3.1.4"); MajorVersionRegex = ""; ListType = "List" }) } | Should -Throw 'Version * doesn''t match regex *'
{ [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @("2.1.3", "3.1.4"); MajorVersionRegex = "^\d+"; ListType = "Fake" }) } | Should -Throw '*Exception setting "ListType": "The argument * does not belong to the set*'
{ [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @("2.1.3", "3.1.4"); MajorVersionRegex = "^\d+"; ListType = "List" }) } | Should -Not -Throw
{ [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @("2.1.3", "3.1.4"); MajorVersionRegex = "^\d+"; ListType = "Inline" }) } | Should -Not -Throw
}
It "Serialization + Deserialization" {
$node = [ToolVersionsListNode]::new("Ruby", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "List")
$json = $node.ToJsonObject()
$node2 = [ToolVersionsListNode]::FromJsonObject($json)
$json2 = $node2.ToJsonObject()
$($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json)
}
It "IsSimilarTo" {
[ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsSimilarTo(
[ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List")
) | Should -BeTrue
[ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsSimilarTo(
[ToolVersionsListNode]::new("MyTool", @("2.1.5", "5.0.0"), "^.+", "List")
) | Should -BeTrue
[ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsSimilarTo(
[ToolVersionsListNode]::new("MyTool2", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List")
) | Should -BeFalse
}
It "IsIdenticalTo" {
[ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsIdenticalTo(
[ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List")
) | Should -BeTrue
[ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsIdenticalTo(
[ToolVersionsListNode]::new("MyTool", @("2.1.5", "5.0.0"), "^.+", "List")
) | Should -BeFalse
[ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsIdenticalTo(
[ToolVersionsListNode]::new("MyTool2", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List")
) | Should -BeFalse
}
It "ExtractMajorVersion" {
$node = [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^\d+\.\d+", "List")
$node.ExtractMajorVersion("2.1.3") | Should -Be "2.1"
$node.ExtractMajorVersion("3.1.5") | Should -Be "3.1"
$node.ExtractMajorVersion("4.0.0") | Should -Be "4.0"
}
Context "ValidateMajorVersionRegex" {
It "Major version regex - unique versions" {
$node = [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^\d+", "List")
$node.Versions | Should -BeArray @("2.1.3", "3.1.5", "4.0.0")
}
It "Major version regex - non-unique versions" {
{ [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "3.2.0", "4.0.0"), "^\d+", "List") } | Should -Throw "Multiple versions from list * return the same result from regex *"
}
It "Minor version regex - unique versions" {
$node = [ToolVersionsListNode]::new("MyTool", @("2.1.3", "2.4.0", "3.1.2"), "^\d+\.\d+", "List")
$node.Versions | Should -BeArray @("2.1.3", "2.4.0", "3.1.2")
}
It "Minor version regex - non-unique versions" {
{ [ToolVersionsListNode]::new("MyTool", @("2.1.3", "2.1.4", "3.1.2"), "^\d+\.\d+", "List") } | Should -Throw "Multiple versions from list * return the same result from regex *"
}
It "Patch version regex - unique versions" {
$node = [ToolVersionsListNode]::new("MyTool", @("2.1.3", "2.1.4", "2.1.5"), "^\d+\.\d+\.\d+", "List")
$node.Versions | Should -BeArray @("2.1.3", "2.1.4", "2.1.5")
}
It "Patch version regex - non-unique versions" {
{ [ToolVersionsListNode]::new("MyTool", @("2.1.3", "2.1.4", "2.1.4"), "^\d+\.\d+\.\d+", "List") } | Should -Throw "Multiple versions from list * return the same result from regex *"
}
It ".NET Core version regex - unique versions" {
$node = [ToolVersionsListNode]::new("MyTool", @("2.1.100", "2.1.205", "2.1.303"), "^\d+\.\d+\.\d", "List")
$node.Versions | Should -BeArray @("2.1.100", "2.1.205", "2.1.303")
}
It ".NET Core version regex - non-unique versions" {
{ [ToolVersionsListNode]::new("MyTool", @("2.1.100", "2.1.205", "2.1.230", "3.1.0"), "^\d+\.\d+\.\d", "List") } | Should -Throw "Multiple versions from list * return the same result from regex *"
}
}
}
Context "TableNode" {
It "ToMarkdown (Simple table)" {
$node = [TableNode]::new("Name|Value", @("A|B", "C|D"))
$node.ToMarkdown() | Should -Be @'
| Name | Value |
| ---- | ----- |
| A | B |
| C | D |
'@
}
It "ToMarkdown (Wide cells)" {
$node = [TableNode]::new("Name|Value", @("Very long value here|B", "C|And very long value here too"))
$node.ToMarkdown() | Should -Be @'
| Name | Value |
| -------------------- | ---------------------------- |
| Very long value here | B |
| C | And very long value here too |
'@
}
It "CalculateColumnsWidth" {
[TableNode]::new("Name|Value", @("A|B", "C|D")).CalculateColumnsWidth() | Should -BeArray @(4, 5)
[TableNode]::new("Name|Value", @("Very long value here|B", "C|And very long value here too")).CalculateColumnsWidth() | Should -BeArray @(20, 28)
}
It "Serialization" {
$node = [TableNode]::new("Name|Value", @("A|B", "C|D"))
$json = $node.ToJsonObject()
$json.NodeType | Should -Be "TableNode"
$json.Headers | Should -Be "Name|Value"
$json.Rows | Should -BeArray @("A|B", "C|D")
}
It "Deserialization" {
{ [TableNode]::FromJsonObject(@{ NodeType = "TableNode"; Headers = ""; Rows = @("A|1", "B|2") }) } | Should -Throw 'Exception setting "Headers": "The argument is null or empty. *'
{ [TableNode]::FromJsonObject(@{ NodeType = "TableNode"; Headers = "Name|Value"; Rows = @() }) } | Should -Throw 'Exception setting "Rows": "The argument is null, empty, *'
{ [TableNode]::FromJsonObject(@{ NodeType = "TableNode"; Headers = "Name|Value"; Rows = @("A|1", "B|2|T", "C|3") }) } | Should -Throw 'Table has different number of columns in different rows'
{ [TableNode]::FromJsonObject(@{ NodeType = "TableNode"; Headers = "Name|Value"; Rows = @("A|1", "B|2") }) } | Should -Not -Throw
}
It "Serialization + Deserialization" {
$node = [TableNode]::new("Name|Value", @("A|B", "C|D"))
$json = $node.ToJsonObject()
$node2 = [TableNode]::FromJsonObject($json)
$json2 = $node2.ToJsonObject()
$($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json)
}
It "IsSimilarTo" {
[TableNode]::new("Name|Value", @("A|B", "C|D")).IsSimilarTo([TableNode]::new("Name|Value", @("A|B", "C|D"))) | Should -BeTrue
[TableNode]::new("Name|Value", @("A|B", "C|D")).IsSimilarTo([TableNode]::new("Name|Value", @("A|B", "C|D", "F|W"))) | Should -BeTrue
[TableNode]::new("Name|Value", @("A|B", "C|D")).IsSimilarTo([TableNode]::new("Name|Value", @("A|B", "C|E"))) | Should -BeTrue
[TableNode]::new("Name|Value", @("A|B", "C|D")).IsSimilarTo([TableNode]::new("Name|Key", @("A|B", "C|D"))) | Should -BeTrue
}
It "IsIdenticalTo" {
[TableNode]::new("Name|Value", @("A|B", "C|D")).IsIdenticalTo([TableNode]::new("Name|Value", @("A|B", "C|D"))) | Should -BeTrue
[TableNode]::new("Name|Value", @("A|B", "C|D")).IsIdenticalTo([TableNode]::new("Name|Key", @("A|B", "C|D"))) | Should -BeTrue
[TableNode]::new("Name|Value", @("A|B", "C|D")).IsIdenticalTo([TableNode]::new("Name|Value", @("A|B", "C|D", "F|W"))) | Should -BeFalse
[TableNode]::new("Name|Value", @("A|B", "C|D")).IsIdenticalTo([TableNode]::new("Name|Value", @("A|B", "C|E"))) | Should -BeFalse
}
Context "FromObjectsArray" {
It "Correct table" {
$table = @(
[PSCustomObject]@{Name = "A"; Value = "B"}
[PSCustomObject]@{Name = "C"; Value = "D"}
)
$tableNode = [TableNode]::FromObjectsArray($table)
$tableNode.Headers | Should -Be "Name|Value"
$tableNode.Rows | Should -BeArray @("A|B", "C|D")
}
It "Correct table with spaces" {
$table = @(
[PSCustomObject]@{Name = "A B"; "My Value" = "1 2"}
[PSCustomObject]@{Name = "C D"; "My Value" = "3 4"}
)
$tableNode = [TableNode]::FromObjectsArray($table)
$tableNode.Headers | Should -Be "Name|My Value"
$tableNode.Rows | Should -BeArray @("A B|1 2", "C D|3 4")
}
It "Throw on empty table" {
{ [TableNode]::FromObjectsArray(@()) } | Should -Throw "Failed to create TableNode from empty objects array"
}
It "Throw on table with different columns" {
$table = @(
[PSCustomObject]@{Name = "A"; Value = "B"}
[PSCustomObject]@{Name = "C"; Value2 = "D"}
)
{ [TableNode]::FromObjectsArray($table) } | Should -Throw "Failed to create TableNode from objects array because objects have different properties"
}
It "Throw on empty row" {
$table = @(
[PSCustomObject]@{Name = "A"; Value = "B"},
[PSCustomObject]@{},
[PSCustomObject]@{Name = "C"; Value2 = "D"}
)
{ [TableNode]::FromObjectsArray($table) } | Should -Throw "Failed to create TableNode because some objects are empty"
}
It "Throw on incorrect symbols in table column names" {
$table = @(
[PSCustomObject]@{"Name|War" = "A"; Value = "B"}
[PSCustomObject]@{"Name|War" = "C"; Value = "D"}
)
{ [TableNode]::FromObjectsArray($table) } | Should -Throw "Failed to create TableNode because some cells * contains forbidden symbol*"
}
It "Throw on incorrect symbols in table rows" {
$table = @(
[PSCustomObject]@{Name = "A"; Value = "B|AA"}
[PSCustomObject]@{Name = "C"; Value = "D"}
)
{ [TableNode]::FromObjectsArray($table) } | Should -Throw "Failed to create TableNode because some cells * contains forbidden symbol*"
}
}
}
Context "NoteNode" {
It "ToMarkdown" {
$node = [NoteNode]::new("Hello world`nGood Bye world")
$node.ToMarkdown() | Should -Be @'
```
hello world
Good Bye world
```
'@
}
It "Serialization" {
$node = [NoteNode]::new("MyContent`nMyContent2")
$json = $node.ToJsonObject()
$json.NodeType | Should -Be "NoteNode"
$json.Content | Should -Be "MyContent`nMyContent2"
}
It "Deserialization" {
{ [NoteNode]::FromJsonObject(@{ NodeType = "NoteNode" }) } | Should -Throw '*Exception setting "Content": "The argument is null or empty.*'
{ [NoteNode]::FromJsonObject(@{ NodeType = "NoteNode"; Content = "" }) } | Should -Throw '*Exception setting "Content": "The argument is null or empty.*'
{ [NoteNode]::FromJsonObject(@{ NodeType = "NoteNode"; Content = "MyTool" }) } | Should -Not -Throw
}
It "Serialization + Deserialization" {
$node = [NoteNode]::new("MyContent`nMyContent2")
$json = $node.ToJsonObject()
$node2 = [NoteNode]::FromJsonObject($json)
$json2 = $node2.ToJsonObject()
$($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json)
}
It "IsSimilarTo" {
[NoteNode]::new("MyContent").IsSimilarTo([NoteNode]::new("MyContent")) | Should -BeTrue
[NoteNode]::new("MyContent").IsSimilarTo([NoteNode]::new("MyContent2")) | Should -BeFalse
}
It "IsIdenticalTo" {
[NoteNode]::new("MyContent").IsIdenticalTo([NoteNode]::new("MyContent")) | Should -BeTrue
[NoteNode]::new("MyContent").IsIdenticalTo([NoteNode]::new("MyContent2")) | Should -BeFalse
}
}
Context "HeaderNode" {
It "ToMarkdown" {
$node = [HeaderNode]::new("MyHeader")
$node.AddToolVersion("MyTool", "2.1.3")
$node.ToMarkdown(1) | Should -Be @'
# MyHeader
- MyTool 2.1.3
'@
}
It "ToMarkdown (level 3)" {
$node = [HeaderNode]::new("MyHeader")
$node.AddToolVersion("MyTool", "2.1.3")
$node.ToMarkdown(3) | Should -Be @'
### MyHeader
- MyTool 2.1.3
'@
}
It "ToMarkdown (multiple levels)" {
$node = [HeaderNode]::new("MyHeader")
$node.AddHeader("MyHeader 2").AddHeader("MyHeader 3").AddHeader("MyHeader 4").AddToolVersion("MyTool", "2.1.3")
$node.ToMarkdown(1) | Should -Be @'
# MyHeader
## MyHeader 2
### MyHeader 3
#### MyHeader 4
- MyTool 2.1.3
'@
}
It "Serialization" {
$node = [HeaderNode]::new("MyHeader")
$node.AddToolVersion("MyTool", "2.1.3")
$json = $node.ToJsonObject()
$json.NodeType | Should -Be "HeaderNode"
$json.Title | Should -Be "MyHeader"
$json.Children | Should -HaveCount 1
}
It "Deserialization" {
{ [HeaderNode]::FromJsonObject(@{ NodeType = "HeaderNode" }) } | Should -Throw '*Exception setting "Title": "The argument is null or empty.*'
{ [HeaderNode]::FromJsonObject(@{ NodeType = "HeaderNode"; Title = "" }) } | Should -Throw '*Exception setting "Title": "The argument is null or empty.*'
{ [HeaderNode]::FromJsonObject(@{ NodeType = "HeaderNode"; Title = "MyHeader" }) } | Should -Not -Throw
}
It "Serialization + Deserialization" {
$node = [HeaderNode]::new("MyHeader")
$node.AddToolVersion("MyTool", "2.1.3")
$json = $node.ToJsonObject()
$node2 = [HeaderNode]::FromJsonObject($json)
$json2 = $node2.ToJsonObject()
$($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json)
}
It "IsSimilarTo" {
[HeaderNode]::new("MyHeader").IsSimilarTo([HeaderNode]::new("MyHeader")) | Should -BeTrue
[HeaderNode]::new("MyHeader").IsSimilarTo([HeaderNode]::new("MyHeader2")) | Should -BeFalse
}
It "IsIdenticalTo" {
[HeaderNode]::new("MyHeader").IsIdenticalTo([HeaderNode]::new("MyHeader")) | Should -BeTrue
[HeaderNode]::new("MyHeader").IsIdenticalTo([HeaderNode]::new("MyHeader2")) | Should -BeFalse
}
It "FindSimilarChildNode" {
$node = [HeaderNode]::new("MyHeader")
$node.AddToolVersion("MyTool", "2.1.3")
$node.FindSimilarChildNode([ToolVersionNode]::new("MyTool", "1.0.0")) | Should -Not -BeNullOrEmpty
$node.FindSimilarChildNode([ToolVersionNode]::New("MyTool2", "1.0.0")) | Should -BeNullOrEmpty
}
Context "Detect node duplicates" {
It "Similar HeaderNode on the same header" {
$node = [HeaderNode]::new("MyHeader")
$node.AddHeader("MySubHeader1")
$node.AddHeader("MySubHeader2")
{ $node.AddHeader("MySubHeader1") } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*"
}
It "Similar ToolVersionNode on the same header" {
$node = [HeaderNode]::new("MyHeader")
$node.AddToolVersion("MyTool", "2.1.3")
$node.AddToolVersion("MyTool2", "2.1.3")
{ $node.AddToolVersion("MyTool", "2.1.3") } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*"
}
It "Similar ToolVersionsListNode on the same header" {
$node = [HeaderNode]::new("MyHeader")
$node.AddToolVersionsListInline("MyTool", @("2.1.3", "3.0.0"), "^\d+")
$node.AddToolVersionsListInline("MyTool2", @("2.1.3", "3.0.0"), "^\d+")
{ $node.AddToolVersionsList("MyTool", @("2.1.3", "3.0.0"), "^\d+") } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*"
}
It "Similar TableNode on the same header" {
$node = [HeaderNode]::new("MyHeader")
$node.AddTable(@(
[PSCustomObject]@{Name = "Value1"},
[PSCustomObject]@{Name = "Value2"}
))
{
$node.AddTable(@(
[PSCustomObject]@{Name = "Value1"},
[PSCustomObject]@{Name = "Value2"}
))
} | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*"
}
It "Similar NoteNode on the same header" {
$node = [HeaderNode]::new("MyHeader")
$node.AddNote("MyContent")
$node.AddNote("MyContent2")
{ $node.AddNote("MyContent") } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*"
}
It "AddNode detects duplicates" {
$node = [HeaderNode]::new("MyHeader")
$node.AddNode([ToolVersionNode]::new("MyTool", "2.1.3"))
{ $node.AddNode([ToolVersionNode]::new("MyTool", "2.1.3")) } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*"
}
It "AddNodes detects duplicates" {
$node = [HeaderNode]::new("MyHeader")
$node.AddNodes(@(
[ToolVersionNode]::new("MyTool", "2.1.3"),
[ToolVersionNode]::new("MyTool2", "2.1.4")
))
{
$node.AddNodes(@(
[ToolVersionNode]::new("MyTool3", "2.1.5"),
[ToolVersionNode]::new("MyTool", "2.1.3")
))
} | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*"
}
It "Doesn't allow adding non-header nodes after header node" {
$node = [HeaderNode]::new("MyHeader")
{ $node.AddToolVersion("MyTool", "2.1.3") } | Should -Not -Throw
{ $node.AddHeader("MySubHeader") } | Should -Not -Throw
{ $node.AddToolVersion("MyTool2", "2.1.4") } | Should -Throw "It is not allowed to add the non-header node after the header node. Consider adding the separate HeaderNode for this node"
{ $node.AddHeader("MySubHeader2") } | Should -Not -Throw
{ $node.AddToolVersionsListInline("MyTool3", @("2.1.4", "2.1.5"), "^.+") } | Should -Throw "It is not allowed to add the non-header node after the header node. Consider adding the separate HeaderNode for this node"
{ $node.AddToolVersionsList("MyTool4", @("2.1.4", "2.1.5"), "^.+") } | Should -Not -Throw
}
}
}
}
@@ -1,34 +0,0 @@
function ShouldBeArray([Array] $ActualValue, [Array]$ExpectedValue, [Switch] $Negate, [String] $Because) {
if ($Negate) {
throw "Negation is not supported for Should-BeArray"
}
if ($ExpectedValue.Count -eq 0) {
throw "Expected array cannot be empty. Use Should-BeNullOrEmpty instead."
}
$ExpectedValue | ForEach-Object {
if ($_.GetType() -notin @([String], [Int32])) {
throw "Only string or int arrays are supported in Should-BeArray"
}
}
$actualValueJson = $ActualValue | ConvertTo-Json
$expectedValueJson = $ExpectedValue | ConvertTo-Json
$succeeded = ($ActualValue.Count -eq $ExpectedValue.Count) -and ($actualValueJson -eq $expectedValueJson)
if (-not $succeeded) {
$failureMessage = "Expected array '$actualValueJson' to be equal to '$expectedValueJson'"
}
return [PSCustomObject]@{
Succeeded = $succeeded
FailureMessage = $failureMessage
}
}
Add-ShouldOperator -Name BeArray `
-InternalName 'ShouldBeArray' `
-Test ${function:ShouldBeArray} `
-SupportsArrayInput
@@ -4,46 +4,16 @@
# - https://github.community/t5/GitHub-Actions/GitHub-Actions-Manual-Trigger-Approvals/td-p/31504
# - https://github.community/t5/GitHub-Actions/Protecting-github-workflows/td-p/30290
parameters:
- name: job_id
type: string
default: 'generate_image'
- name: image_type
type: string
- name: image_readme_name
type: string
- name: agent_pool
type: object
default:
name: 'ci-agent-pool'
- name: variable_group_name
type: string
default: 'Image Generation Variables'
- name: create_release
type: boolean
default: true
- name: repository_ref
type: string
default: 'self'
jobs:
- job: ${{ parameters.job_id }}
- job:
displayName: Image Generation (${{ parameters.image_type }})
timeoutInMinutes: 600
cancelTimeoutInMinutes: 30
pool: ${{ parameters.agent_pool }}
pool: ci-agent-pool
variables:
- group: ${{ parameters.variable_group_name }}
- group: Image Generation Variables
steps:
- checkout: ${{ parameters.repository_ref }}
- task: PowerShell@2
displayName: 'Download custom repository'
condition: and(ne(variables['CUSTOM_REPOSITORY_URL'], ''), ne(variables['CUSTOM_REPOSITORY_BRANCH'], ''))
@@ -61,10 +31,7 @@ jobs:
$ImageType = "${{ parameters.image_type }}"
$TemplateDirectoryName = if ($ImageType.StartsWith("ubuntu")) { "linux" } else { "win" }
$TemplateDirectoryPath = Join-Path "images" $TemplateDirectoryName | Resolve-Path
$TemplatePath = Join-Path $TemplateDirectoryPath "$ImageType.pkr.hcl"
if ( -not (Test-Path $TemplatePath) ) {
$TemplatePath = Join-Path $TemplateDirectoryPath "$ImageType.json"
}
$TemplatePath = Join-Path $TemplateDirectoryPath "$ImageType.json"
Write-Host "##vso[task.setvariable variable=TemplateDirectoryPath;]$TemplateDirectoryPath"
Write-Host "##vso[task.setvariable variable=TemplatePath;]$TemplatePath"
@@ -88,64 +55,35 @@ jobs:
env:
PACKER_LOG: 1
PACKER_LOG_PATH: "$(Agent.TempDirectory)/packer-log.txt"
PACKER_LOG_PATH: $(Build.ArtifactStagingDirectory)/packer-log.txt
- task: PowerShell@2
displayName: 'Copy image artifacts to the separate directory'
displayName: 'Output Readme file content'
inputs:
targetType: 'inline'
script: |
$readmePath = Join-Path "$(TemplateDirectoryPath)" "${{ parameters.image_readme_name }}"
$softwareReportPath = Join-Path "$(TemplateDirectoryPath)" "software-report.json"
Copy-Item -Path $readmePath -Destination "$(Build.ArtifactStagingDirectory)/"
if (Test-Path $softwareReportPath) {
Copy-Item -Path $softwareReportPath -Destination "$(Build.ArtifactStagingDirectory)/"
}
- task: PowerShell@2
displayName: 'Print markdown software report'
inputs:
targetType: 'inline'
script: |
Get-Content -Path "$(Build.ArtifactStagingDirectory)/${{ parameters.image_readme_name }}"
- task: PowerShell@2
displayName: 'Print json software report'
inputs:
targetType: 'inline'
script: |
$softwareReportPath = "$(Build.ArtifactStagingDirectory)/software-report.json"
if (Test-Path $softwareReportPath) {
Get-Content -Path $softwareReportPath
}
- task: PublishBuildArtifacts@1
inputs:
ArtifactName: 'Built_VM_Artifacts'
displayName: Publish Artifacts
Get-Content -Path (Join-Path "$(TemplateDirectoryPath)" "${{ parameters.image_readme_name }}")
- task: PowerShell@2
displayName: 'Print provisioners duration'
inputs:
targetType: 'filePath'
filePath: ./images.CI/measure-provisioners-duration.ps1
arguments: -PackerLogPath "$(Agent.TempDirectory)/packer-log.txt" `
arguments: -PackerLogPath "$(Build.ArtifactStagingDirectory)/packer-log.txt" `
-PrefixToPathTrim "$(TemplateDirectoryPath)" `
-PrintTopNLongest 25
- ${{ if eq(parameters.create_release, true) }}:
- task: PowerShell@2
displayName: 'Create release for VM deployment'
inputs:
targetType: filePath
filePath: ./images.CI/linux-and-win/create-release.ps1
arguments: -BuildId $(Build.BuildId) `
-Organization $(RELEASE_TARGET_ORGANIZATION) `
-DefinitionId $(RELEASE_TARGET_DEFINITION_ID) `
-Project $(RELEASE_TARGET_PROJECT) `
-ImageName ${{ parameters.image_type }} `
-AccessToken $(RELEASE_TARGET_TOKEN)
- task: PowerShell@2
displayName: 'Create release for VM deployment'
inputs:
targetType: filePath
filePath: ./images.CI/linux-and-win/create-release.ps1
arguments: -BuildId $(Build.BuildId) `
-Organization $(RELEASE_TARGET_ORGANIZATION) `
-DefinitionId $(RELEASE_TARGET_DEFINITION_ID) `
-Project $(RELEASE_TARGET_PROJECT) `
-ImageName ${{ parameters.image_type }} `
-AccessToken $(RELEASE_TARGET_TOKEN)
- task: PowerShell@2
displayName: 'Clean up resources'
@@ -0,0 +1,20 @@
schedules:
- cron: "0 0 * * *"
displayName: Daily
branches:
include:
- main
always: true
trigger: none
pr:
autoCancel: true
branches:
include:
- main
jobs:
- template: image-generation.yml
parameters:
image_type: ubuntu1804
image_readme_name: Ubuntu1804-Readme.md
@@ -1,3 +1,11 @@
schedules:
- cron: "0 0 * * *"
displayName: Daily
branches:
include:
- main
always: true
trigger: none
pr:
autoCancel: true
@@ -1,12 +0,0 @@
trigger: none
pr:
autoCancel: true
branches:
include:
- main
jobs:
- template: image-generation.yml
parameters:
image_type: ubuntu2204
image_readme_name: Ubuntu2204-Readme.md
@@ -0,0 +1,20 @@
schedules:
- cron: "0 0 * * *"
displayName: Daily
branches:
include:
- main
always: true
trigger: none
pr:
autoCancel: true
branches:
include:
- main
jobs:
- template: image-generation.yml
parameters:
image_type: windows2016
image_readme_name: Windows2016-Readme.md
@@ -1,3 +1,11 @@
schedules:
- cron: "0 0 * * *"
displayName: Daily
branches:
include:
- main
always: true
trigger: none
pr:
autoCancel: true
@@ -1,3 +1,11 @@
schedules:
- cron: "0 0 * * *"
displayName: Daily
branches:
include:
- main
always: true
trigger: none
pr:
autoCancel: true
+5 -6
View File
@@ -8,9 +8,9 @@ param(
[String] [Parameter (Mandatory=$true)] $StorageAccount,
[String] [Parameter (Mandatory=$true)] $SubscriptionId,
[String] [Parameter (Mandatory=$true)] $TenantId,
[String] [Parameter (Mandatory=$false)] $VirtualNetworkName,
[String] [Parameter (Mandatory=$false)] $VirtualNetworkRG,
[String] [Parameter (Mandatory=$false)] $VirtualNetworkSubnet
[String] [Parameter (Mandatory=$true)] $VirtualNetworkName,
[String] [Parameter (Mandatory=$true)] $VirtualNetworkRG,
[String] [Parameter (Mandatory=$true)] $VirtualNetworkSubnet
)
if (-not (Test-Path $TemplatePath))
@@ -19,7 +19,7 @@ if (-not (Test-Path $TemplatePath))
exit 1
}
$Image = [io.path]::GetFileName($TemplatePath).Split(".")[0]
$Image = [io.path]::GetFileNameWithoutExtension($TemplatePath)
$TempResourceGroupName = "${ResourcesNamePrefix}_${Image}"
$InstallPassword = [System.GUID]::NewGuid().ToString().ToUpper()
@@ -53,11 +53,10 @@ packer build -var "capture_name_prefix=$ResourcesNamePrefix" `
-var "virtual_network_resource_group_name=$VirtualNetworkRG" `
-var "virtual_network_subnet_name=$VirtualNetworkSubnet" `
-var "run_validation_diskspace=$env:RUN_VALIDATION_FLAG" `
-color=false `
$TemplatePath `
| Where-Object {
#Filter sensitive data from Packer logs
$currentString = $_
$sensitiveString = $SensitiveData | Where-Object { $currentString -match $_ }
$sensitiveString -eq $null
}
}
+2 -2
View File
@@ -14,10 +14,10 @@ $TempResourceGroupName = "${ResourcesNamePrefix}_${Image}"
$groupExist = az group exists --name $TempResourceGroupName --subscription $SubscriptionId
if ($groupExist -eq "true") {
$osDiskName = az deployment group list --resource-group $TempResourceGroupName --query "[].properties.parameters.osDiskName.value" -o tsv
$osDiskName = az group deployment list --resource-group $TempResourceGroupName --query "[].properties.parameters.osDiskName.value" -o tsv
Write-Host "Found a match, deleting temporary files"
az group delete --name $TempResourceGroupName --subscription $SubscriptionId --yes | Out-Null
Write-Host "Temporary group was deleted successfully"
Write-Host "Temporary group was deleted succesfully"
Write-Host "Deleting OS disk"
az storage remove --account-name $StorageAccount -c "images" -n "$osDiskName.vhd" --only-show-errors | Out-Null
Write-Host "OS disk deleted"
@@ -26,7 +26,6 @@ $headers = @{
Authorization = "Basic ${base64AuthInfo}"
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13
$NewRelease = Invoke-RestMethod $URL -Body $Body -Method "POST" -Headers $headers -ContentType "application/json"
Write-Host "Created release: $($NewRelease._links.web.href)"
+6 -14
View File
@@ -14,16 +14,15 @@ function Push-AnkaTemplateToRegistry {
)
# if registry uuid doesn't match than delete an image in registry
$AnkaCaCrtPath="$HOME/.config/anka/certs/anka-ca-crt.pem"
$images = anka --machine-readable registry --cacert $AnkaCaCrtPath --registry-path $RegistryUrl list | ConvertFrom-Json | ForEach-Object body
$images = anka --machine-readable registry --registry-path $RegistryUrl list | ConvertFrom-Json | ForEach-Object body
$images | Where-Object name -eq $TemplateName | ForEach-Object {
$id = $_.uuid
$id = $_.id
Show-StringWithFormat "Deleting '$TemplateName[$id]' VM and '$TagName' tag"
$curlCommand='curl -s -X DELETE -k "{0}/registry/vm?id={1}"' -f $RegistryUrl, $id
Invoke-AnkaCommand -Command $curlCommand
$uri = '{0}/registry/vm?id={1}' -f $RegistryUrl, $id
Invoke-WebRequest -Uri $uri -Method Delete | Out-Null
}
$command = "anka registry --cacert $AnkaCaCrtPath --registry-path $RegistryUrl push --force --tag $TagName $TemplateName"
$command = "anka registry --registry-path $RegistryUrl push --force --tag $TagName $TemplateName"
Invoke-AnkaCommand -Command $command
}
@@ -198,14 +197,7 @@ function Wait-AnkaVMIPAddress {
[int] $Seconds = 60
)
$condition = {
$vmStatus = Get-AnkaVMStatus -VMName $VMName
if ($vmStatus -eq "failed") {
Write-Host "`t [-] $VMName is in failed status"
exit 1
}
Get-AnkaVMIPAddress -VMName $VMName
}
$condition = { Get-AnkaVMIPAddress -VMName $VMName }
$null = Invoke-WithRetry -BreakCondition $condition -RetryCount $RetryCount -Seconds $Seconds
}
@@ -58,12 +58,6 @@ function Invoke-EnableAutoLogon {
}
function Invoke-SoftwareUpdate {
param (
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string] $Password
)
if (-not $InstallSoftwareUpdate) {
Write-Host "`t[*] Skip installing software updates"
return
@@ -87,32 +81,26 @@ function Invoke-SoftwareUpdate {
Write-Host "`t[*] Fetching Software Updates ready to install on '$TemplateName' VM:"
Show-StringWithFormat $newUpdates
$listOfNewUpdates = $($($newUpdates.Split("*")).Split("Title") | Where-Object {$_ -match "Label:"}).Replace("Label: ", '')
Write-Host "`t[*] Installing Software Updates on '$TemplateName' VM:"
Install-SoftwareUpdate -HostName $ipAddress -listOfUpdates $listOfNewUpdates -Password $Password | Show-StringWithFormat
Install-SoftwareUpdate -HostName $ipAddress | Show-StringWithFormat
# Check if Action: restart
# Make an array of updates
$listOfNewUpdates = $newUpdates.split('*').Trim('')
foreach ($newupdate in $listOfNewUpdates) {
# Will be True if the value is not Venture, not empty, and contains "Action: restart" words
if ($newupdate.Contains("Action: restart") -and !$newupdate.Contains("macOS Ventura") -and (-not [String]::IsNullOrEmpty($newupdate))) {
Write-Host "`t[*] Sleep 60 seconds before the software updates have been installed"
Start-Sleep -Seconds 60
if ($newUpdates.Contains("Action: restart")) {
Write-Host "`t[*] Sleep 60 seconds before the software updates have been installed"
Start-Sleep -Seconds 60
Write-Host "`t[*] Waiting for loginwindow process"
Wait-LoginWindow -HostName $ipAddress | Show-StringWithFormat
Write-Host "`t[*] Waiting for loginwindow process"
Wait-LoginWindow -HostName $ipAddress | Show-StringWithFormat
# Re-enable AutoLogon after installing a new security software update
Invoke-EnableAutoLogon
# Re-enable AutoLogon after installing a new security software update
Invoke-EnableAutoLogon
# Check software updates have been installed
$updates = Get-SoftwareUpdate -HostName $ipAddress
if ($updates.Contains("Action: restart")) {
Write-Host "`t[x] Software updates failed to install: "
Show-StringWithFormat $updates
exit 1
}
# Check software updates have been installed
$updates = Get-SoftwareUpdate -HostName $ipAddress
if ($updates.Contains("Action: restart")) {
Write-Host "`t[x] Software updates failed to install: $updates"
Show-StringWithFormat $updates
exit 1
}
}
@@ -122,11 +110,6 @@ function Invoke-SoftwareUpdate {
}
function Invoke-UpdateSettings {
param (
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string] $Password
)
$isConfRequired = $InstallSoftwareUpdate -or $EnableAutoLogon
if (-not $isConfRequired) {
Write-Host "`t[*] Skip additional configuration"
@@ -143,7 +126,7 @@ function Invoke-UpdateSettings {
Invoke-EnableAutoLogon
# Install software updates
Invoke-SoftwareUpdate -Password $Password
Invoke-SoftwareUpdate
Write-Host "`t[*] Stopping '$TemplateName' VM"
Stop-AnkaVM -VMName $TemplateName
@@ -162,16 +145,10 @@ $env:SSHUSER = $TemplateUsername
$env:SSHPASS = $TemplatePassword
Write-Host "`n[#1] Download macOS application installer:"
$macOSInstaller = Get-MacOSInstaller -MacOSVersion $MacOSVersion -DownloadLatestVersion $DownloadLatestVersion -BetaSearch $BetaSearch
$shortMacOSVersion = Get-ShortMacOSVersion -MacOSVersion $MacOSVersion
if ([string]::IsNullOrEmpty($TemplateName)) {
$osArch = $(arch)
if ($osArch -eq "arm64") {
$macOSInstaller = Get-MacOSIPSWInstaller -MacOSVersion $MacOSVersion -DownloadLatestVersion $DownloadLatestVersion -BetaSearch $BetaSearch
$TemplateName = "clean_macos_${shortMacOSVersion}_${osArch}_${DiskSizeGb}gb"
} else {
$macOSInstaller = Get-MacOSInstaller -MacOSVersion $MacOSVersion -DownloadLatestVersion $DownloadLatestVersion -BetaSearch $BetaSearch
$TemplateName = "clean_macos_${shortMacOSVersion}_${DiskSizeGb}gb"
}
$TemplateName = "clean_macos_${shortMacOSVersion}_${DiskSizeGb}gb"
}
Write-Host "`n[#2] Create a VM template:"
@@ -189,7 +166,7 @@ New-AnkaVMTemplate -InstallerPath $macOSInstaller `
-DiskSizeGb $DiskSizeGb | Show-StringWithFormat
Write-Host "`n[#3] Configure AutoLogon and/or install software updates:"
Invoke-UpdateSettings -Password $TemplatePassword
Invoke-UpdateSettings
Write-Host "`n[#4] Finalization '$TemplateName' configuration and push to the registry:"
Write-Host "`t[*] The '$TemplateName' VM status is stopped"
+12 -128
View File
@@ -13,28 +13,12 @@ function Enable-AutoLogon {
[string] $Password
)
$url = "https://raw.githubusercontent.com/actions/runner-images/main/images/macos/provision/bootstrap-provisioner/setAutoLogin.sh"
$url = "https://raw.githubusercontent.com/actions/virtual-environments/main/images/macos/provision/bootstrap-provisioner/kcpassword.py"
$script = Invoke-RestMethod -Uri $url
$base64 = [Convert]::ToBase64String($script.ToCharArray())
$command = "echo $base64 | base64 --decode > ./setAutoLogin.sh;sudo bash ./setAutoLogin.sh '${UserName}' '${Password}';rm ./setAutoLogin.sh"
Invoke-SSHPassCommand -HostName $HostName -Command $command
}
function Invoke-SoftwareUpdateArm64 {
param (
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string] $HostName,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string] $Password
)
$url = "https://raw.githubusercontent.com/actions/runner-images/main/images/macos/provision/configuration/auto-software-update-arm64.exp"
$script = Invoke-RestMethod -Uri $url
$base64 = [Convert]::ToBase64String($script.ToCharArray())
$command = "echo $base64 | base64 --decode > ./auto-software-update-arm64.exp;chmod +x ./auto-software-update-arm64.exp; ./auto-software-update-arm64.exp ${Password};rm ./auto-software-update-arm64.exp"
$kcpassword = "echo $base64 | base64 --decode > ~/kcpassword;sudo python ./kcpassword '${Password}';rm ./kcpassword"
$loginwindow = "sudo /usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser '${UserName}';sudo /usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUserScreenLocked -bool false"
$command = "${kcpassword};$loginwindow"
Invoke-SSHPassCommand -HostName $HostName -Command $command
}
@@ -55,84 +39,6 @@ function Get-AvailableVersions {
$allVersions
}
function Get-AvailableIPSWVersions {
param (
[bool] $IsBeta = $false,
[bool] $IsLatest = $true,
[string] $MacOSCodeNameOrVersion
)
if ($IsBeta) {
$command = { mist list installer "$MacOSCodeNameOrVersion" --include-betas --latest --export "/Applications/export.json"}
} elseif ($IsLatest) {
$command = { mist list installer "$MacOSCodeNameOrVersion" --latest --export "/Applications/export.json" }
} else {
$command = { mist list installer "$MacOSCodeNameOrVersion" --export "/Applications/export.json" }
}
$condition = { $LASTEXITCODE -eq 0 }
Invoke-WithRetry -Command $command -BreakCondition $condition | Out-Null
$softwareList = get-content -Path "/Applications/export.json"
$turgetVersion = ($softwareList | ConvertFrom-Json).version
if ($null -eq $turgetVersion) {
Write-Host "Requested macOS '$MacOSCodeNameOrVersion' version not found in the list of available installers."
$command = { mist list installer "$($MacOSCodeNameOrVersion.split('.')[0])" }
Invoke-WithRetry -Command $command -BreakCondition $condition
exit 1
}
return $turgetVersion
}
function Get-MacOSIPSWInstaller {
param (
[Parameter(Mandatory)]
[version] $MacOSVersion,
[bool] $DownloadLatestVersion = $false,
[bool] $BetaSearch = $false
)
if ($MacOSVersion -eq [version] "12.0") {
$MacOSName = "macOS Monterey"
} elseif ($MacOSVersion -eq [version] "13.0") {
$MacOSName = "macOS Ventura"
} else {
$MacOSName = $MacOSVersion.ToString()
}
Write-Host "`t[*] Finding available full installers"
if ($DownloadLatestVersion -eq $true) {
$targetVersion = Get-AvailableIPSWVersions -IsLatest $true -MacOSCodeNameOrVersion $MacOSName
Write-host "`t[*] The 'DownloadLatestVersion' flag is set to true. Latest macOS version is '$MacOSName' - '$targetVersion' now"
} elseif ($BetaSearch -eq $true) {
$targetVersion = Get-AvailableIPSWVersions -IsBeta $true -MacOSCodeNameOrVersion $MacOSName
Write-host "`t[*] The 'BetaSearch' flag is set to true. Latestbeta macOS version is '$MacOSName' - '$targetVersion' now"
} else {
$targetVersion = Get-AvailableIPSWVersions -MacOSCodeNameOrVersion $MacOSName
Write-host "`t[*] The exact version was specified - '$MacOSName' "
}
$installerPathPattern = "/Applications/Install ${macOSName}*.ipsw"
if (Test-Path $installerPathPattern) {
$previousInstallerPath = Get-Item -Path $installerPathPattern
Write-Host "`t[*] Removing '$previousInstallerPath' installation app before downloading the new one"
sudo rm -rf "$previousInstallerPath"
}
# Download macOS installer
$installerDir = "/Applications/"
$installerName = "Install ${macOSName}.ipsw"
Write-Host "`t[*] Requested macOS '$targetVersion' version installer found, fetching it from mist database"
Invoke-WithRetry { mist download firmware "$targetVersion" --output-directory $installerDir --firmware-name "$installerName" } {$LASTEXITCODE -eq 0} | Out-Null
if (Test-Path "$installerDir$installerName") {
$result = "$installerDir$installerName"
} else {
Write-host "`t[*] Requested macOS '$targetVersion' version installer failed to download"
exit 1
}
return $result
}
function Get-MacOSInstaller {
param (
[Parameter(Mandatory)]
@@ -167,20 +73,20 @@ function Get-MacOSInstaller {
Show-StringWithFormat $availableVersions
exit 1
}
Show-StringWithFormat $filteredVersions
Show-StringWithFormat $filteredVersions
$osVersions = $filteredVersions.OSVersion | Sort-Object {[version]$_}
$MacOSVersion = $osVersions | Select-Object -Last 1
Write-Host "`t[*] The 'DownloadLatestVersion' flag is set. Latest macOS version is '$MacOSVersion' now"
}
$macOSName = $availableVersions.Where{ $MacOSVersion -eq $_.OSVersion }.OSName.Split(" ")[1]
$macOSName = $availableVersions.Where{ $MacOSVersion -eq $_.OSVersion }.OSName
if (-not $macOSName) {
Write-Host "`t[x] Requested macOS '$MacOSVersion' version not found in the list of available installers. Available versions are:`n$($availableVersions.OSVersion)"
Write-Host "`t[x] Make sure to pass '-BetaSearch `$true' if you need a beta version installer"
exit 1
}
$installerPathPattern = "/Applications/Install macOS ${macOSName}*.app"
$installerPathPattern = "/Applications/Install*${macOSName}.app"
if (Test-Path $installerPathPattern) {
$previousInstallerPath = Get-Item -Path $installerPathPattern
Write-Host "`t[*] Removing '$previousInstallerPath' installation app before downloading the new one"
@@ -200,10 +106,6 @@ function Get-MacOSInstaller {
}
$installerPath = (Get-Item -Path $installerPathPattern).FullName
if (-not $installerPath) {
Write-Host "`t[x] Path not found using '$installerPathPattern'"
exit 1
}
Write-Host "`t[*] Installer successfully downloaded to '$installerPath'"
$installerPath
@@ -246,29 +148,11 @@ function Install-SoftwareUpdate {
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string] $HostName,
[array] $listOfUpdates,
[string] $Password
[string] $HostName
)
$osVersion = [Environment]::OSVersion
# If an update is happening on macOS 12 we will use the prepared list of updates, otherwise, we will install all updates.
if ($osVersion.Version.Major -eq "12") {
foreach ($update in $listOfUpdates){
# Filtering updates that contain "Ventura" word
if ($update -notmatch "Ventura") {
$command = "sudo /usr/sbin/softwareupdate --restart --verbose --install '$($update.trim())'"
Invoke-SSHPassCommand -HostName $HostName -Command $command
}
}
} else {
$osArch = $(arch)
if ($osArch -eq "arm64") {
Invoke-SoftwareUpdateArm64 -HostName $HostName -Password $Password
} else {
$command = "sudo /usr/sbin/softwareupdate --all --install --restart --verbose"
Invoke-SSHPassCommand -HostName $HostName -Command $command
}
}
$command = "sudo /usr/sbin/softwareupdate --all --install --restart --verbose"
Invoke-SSHPassCommand -HostName $HostName -Command $command
}
function Invoke-SSHPassCommand {
@@ -346,7 +230,7 @@ function Show-StringWithFormat {
[Parameter(ValuefromPipeline)]
[object] $string
)
process {
($string | Out-String).Trim().split("`n") | ForEach-Object { Write-Host "`t $_" }
}
@@ -55,8 +55,7 @@ jobs:
arguments: -VMName "$(VirtualMachineName)" `
-VIServer "$(vcenter-server-v2)" `
-VIUserName "$(vcenter-username-v2)" `
-VIPassword '$(vcenter-password-v2)' `
-Cluster "$(esxi-cluster-v2)"
-VIPassword "$(vcenter-password-v2)"
- pwsh: |
$SensitiveData = @(
@@ -67,14 +66,13 @@ jobs:
packer build -on-error=abort `
-var="vcenter_server=$(vcenter-server-v2)" `
-var="vcenter_username=$(vcenter-username-v2)" `
-var='vcenter_password=$(vcenter-password-v2)' `
-var="vcenter_password=$(vcenter-password-v2)" `
-var="vcenter_datacenter=$(vcenter-datacenter-v2)" `
-var="cluster_or_esxi_host=$(esxi-cluster-v2)" `
-var="esxi_datastore=$(buildDatastore)" `
-var="output_folder=$(output-folder)" `
-var="vm_username=$(vm-username)" `
-var="vm_password=$(vm-password)" `
-var="github_api_pat=$(github_api_pat)" `
-var="build_id=$(VirtualMachineName)" `
-var="baseimage_name=${{ parameters.base_image_name }}" `
-var="xcode_install_user=$(xcode-installation-user)" `
@@ -107,11 +105,7 @@ jobs:
- bash: |
cat "$(Build.ArtifactStagingDirectory)/systeminfo.md"
displayName: Print markdown software report
- bash: |
cat "$(Build.ArtifactStagingDirectory)/systeminfo.json"
displayName: Print json software report
displayName: Print software report
- task: PublishBuildArtifacts@1
inputs:
@@ -136,7 +130,7 @@ jobs:
-TargetDataStore "${{ parameters.target_datastore }}" `
-VIServer "$(vcenter-server-v2)" `
-VIUserName "$(vcenter-username-v2)" `
-VIPassword '$(vcenter-password-v2)' `
-VIPassword "$(vcenter-password-v2)"
-CpuCount "$(cpu-count)" `
-CoresPerSocketCount "$(cores-per-socket-count)" `
-Memory "$(memory)"
@@ -150,4 +144,4 @@ jobs:
arguments: -VMName "$(VirtualMachineName)" `
-VIServer "$(vcenter-server-v2)" `
-VIUserName "$(vcenter-username-v2)" `
-VIPassword '$(vcenter-password-v2)'
-VIPassword "$(vcenter-password-v2)"
@@ -0,0 +1,23 @@
name: macOS-10.15_$(date:yyyyMMdd)$(rev:.r)_unstable
schedules:
- cron: "0 0 * * *"
displayName: Daily
branches:
include:
- main
always: true
trigger: none
pr:
autoCancel: true
branches:
include:
- main
jobs:
- template: image-generation.yml
parameters:
image_label: 'macOS Catalina'
base_image_name: 'clean-macOS-10.15-380Gb-runner'
template_path: 'templates/macOS-10.15.json'
target_datastore: 'ds-image'
+1 -1
View File
@@ -1,6 +1,6 @@
name: macOS-11_$(date:yyyyMMdd)$(rev:.r)_unstable
schedules:
- cron: '45 0 * * *'
- cron: "0 0 * * *"
displayName: Daily
branches:
include:
+9 -6
View File
@@ -41,8 +41,6 @@ param(
[ValidateNotNullOrEmpty()]
[string]$VIPassword,
[string]$JobStatus,
[int32]$CpuCount,
[int32]$CoresPerSocketCount,
@@ -64,16 +62,15 @@ try {
}
$vm = Get-VM $VMName
if (($env:AGENT_JOBSTATUS -and $env:AGENT_JOBSTATUS -eq 'Failed') -or ($JobStatus -and $JobStatus -eq 'failure')) {
if ($env:AGENT_JOBSTATUS -eq 'Failed') {
try {
if ($vm.PowerState -ne "PoweredOff") {
if($vm.PowerState -ne "PoweredOff") {
Stop-VM -VM $vm -Confirm:$false -ErrorAction Stop | Out-Null
}
Set-VM -VM $vm -Name "${VMName}_failed" -Confirm:$false -ErrorAction Stop | Out-Null
Write-Host "VM has been successfully powered off and renamed to [${VMName}_failed]"
} catch {
Write-Host "##vso[task.LogIssue type=error;]Failed to power off and rename VM '$VMName'"
exit 1
}
}
@@ -82,5 +79,11 @@ try {
Write-Host "VM has been moved successfully to target datastore '$TargetDataStore'"
} catch {
Write-Host "##vso[task.LogIssue type=error;]Failed to move VM '$VMName' to target datastore '$TargetDataStore'"
exit 1
}
try {
Write-Host "Change CPU count to $CpuCount, cores count to $CoresPerSocketCount, amount of RAM to $Memory"
$vm | Set-VM -NumCPU $CpuCount -CoresPerSocket $CoresPerSocketCount -MemoryMB $Memory -Confirm:$false -ErrorAction Stop | Out-Null
} catch {
Write-Host "##vso[task.LogIssue type=error;]Failed to change specs for VM '$VMName'"
}
+5 -11
View File
@@ -36,10 +36,6 @@ param(
[ValidateNotNullOrEmpty()]
[string]$VIPassword,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Cluster,
[string]$TagCategory = "Busy"
)
@@ -51,7 +47,6 @@ function Select-DataStore {
[string]$VMName,
[string]$TagCategory,
[string]$TemplateDatastore = "ds-local-Datastore-*",
[string]$Cluster,
[int]$ThresholdInGb = 400,
[int]$VMCount = 2,
[int]$Retries = 5
@@ -62,12 +57,11 @@ function Select-DataStore {
# 3. Choose a datastore with the minimal VM count < 2
Write-Host "Start Datastore selection process..."
$clusterHosts = Get-Cluster -Name $Cluster | Get-VMHost
$availableClusterDatastores = $clusterHosts | Get-Datastore -Name $TemplateDatastore | Where-Object -Property State -eq "Available"
$availableDatastores = $availableClusterDatastores `
$allDatastores = Get-Datastore -Name $templateDatastore | Where-Object { $_.State -eq "Available" }
$availableDatastores = $allDatastores `
| Where-Object { $_.FreeSpaceGB -ge $thresholdInGb } `
| Where-Object {
$vmOnDatastore = @((Get-ChildItem -Path $_.DatastoreBrowserPath).Name -notmatch '(^\.|vmkdump)').Count
$vmOnDatastore = @((Get-ChildItem -Path $_.DatastoreBrowserPath).Name -notmatch "^\.").Count
$vmOnDatastore -lt $vmCount } `
| Group-Object -Property { $vmOnDatastore }
@@ -107,11 +101,11 @@ function Select-DataStore {
}
Write-Host "Datastore select failed, $retries left"
Select-DataStore -VMName $VMName -Cluster $Cluster -TagCategory $TagCategory -Retries $retries
Select-DataStore -VMName $VMName -TagCategory $TagCategory -Retries $retries
}
# Connection to a vCenter Server system
Connect-VCServer -VIServer $VIServer -VIUserName $VIUserName -VIPassword $VIPassword
# Get a target datastore for current deployment
Select-DataStore -VMName $VMName -Cluster $Cluster -TagCategory $TagCategory
Select-DataStore -VMName $VMName -TagCategory $TagCategory
-73
View File
@@ -1,73 +0,0 @@
<#
.SYNOPSIS
This script sets resources for VM
.PARAMETER VMName
VM name to resize (Example "macOS-10.15_20201012.4")
.PARAMETER VIServer
vCenter address (Example "10.0.1.16")
.PARAMETER VIUserName
vCenter username (Example "Administrator")
.PARAMETER VIPassword
vCenter password (Example "12345678")
.PARAMETER CpuCount
Target number of CPUs (Example "3")
.PARAMETER CoresPerSocketCount
Target number of cores per socket (Example "3")
.PARAMETER Memory
Target amount of memory in MB (Example "14336")
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$VMName,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$VIServer,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$VIUserName,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$VIPassword,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[int32]$CpuCount,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[int32]$CoresPerSocketCount,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[int64]$Memory
)
# Import helpers module
Import-Module $PSScriptRoot\helpers.psm1 -DisableNameChecking
# Connection to a vCenter Server system
Connect-VCServer -VIServer $VIServer -VIUserName $VIUserName -VIPassword $VIPassword
$vm = Get-VM $VMName
try {
Write-Host "Change CPU count to $CpuCount, cores count to $CoresPerSocketCount, amount of RAM to $Memory"
$vm | Set-VM -NumCPU $CpuCount -CoresPerSocket $CoresPerSocketCount -MemoryMB $Memory -Confirm:$false -ErrorAction Stop | Out-Null
} catch {
Write-Host "##vso[task.LogIssue type=error;]Failed to change specs for VM '$VMName'"
exit 1
}
+376
View File
@@ -0,0 +1,376 @@
| Announcements |
|-|
| [[All OSs] .NET 2.1 will be removed from the images on February, 21](https://github.com/actions/virtual-environments/issues/4871) |
| [[All OSs] Az PowerShell Module will be upgraded to 7.1.0 on January, 17](https://github.com/actions/virtual-environments/issues/4841) |
| [[Ubuntu] Issue with libstdc++ cannot allocate memory in static TLS block](https://github.com/actions/virtual-environments/issues/4799) |
| [[All OSs] Python version 3.5 will be removed from the images on January 24, 2022](https://github.com/actions/virtual-environments/issues/4744) |
***
# Ubuntu 18.04.6 LTS
- Linux kernel version: 5.4.0-1067-azure
- Image Version: 20220123.1
## Installed Software
### Language and Runtime
- Bash 4.4.20(1)-release
- Clang 9.0.0
- Clang-format 9.0.0
- Erlang 24.2 (Eshell 12.2)
- Erlang rebar3 3.18.0
- GNU C++ 7.5.0, 9.4.0, 10.3.0
- GNU Fortran 7.5.0, 9.4.0, 10.3.0
- Julia 1.7.1
- Kotlin 1.6.0-release-798
- Mono 6.12.0.122 (apt source repository: https://download.mono-project.com/repo/ubuntu stable-bionic main)
- MSBuild 16.6.0.15201 (from /usr/lib/mono/msbuild/15.0/bin/MSBuild.dll)
- Node 16.13.2
- Perl 5.26.1
- Python 2.7.17
- Python3 3.6.9
- Ruby 2.5.1p57
- Swift 5.5.2
### Package Management
- cpan 1.64
- Helm 3.7.2
- Homebrew 3.3.11
- Miniconda 4.10.3
- Npm 8.1.2
- Pip 9.0.1
- Pip3 9.0.1
- Pipx 1.0.0
- RubyGems 2.7.6
- Vcpkg (build from master \<df40d1c47>)
- Yarn 1.22.17
#### Environment variables
| Name | Value |
| ----------------------- | ---------------------- |
| CONDA | /usr/share/miniconda |
| VCPKG_INSTALLATION_ROOT | /usr/local/share/vcpkg |
### Project Management
- Ant 1.10.5
- Gradle 7.3.3
- Maven 3.8.4
- Sbt 1.6.1
### Tools
- Ansible 2.11.7
- apt-fast 1.9.12
- AzCopy 10.13.0 (available by `azcopy` and `azcopy10` aliases)
- Bazel 5.0.0
- Bazelisk 1.11.0
- Bicep 0.4.1124
- Buildah 1.19.6 (apt source repository: https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable)
- CMake 3.22.1
- CodeQL Action Bundle 2.7.5
- Docker Compose v1 1.29.2
- Docker Compose v2 2.2.3+azure-1
- Docker-Buildx 0.7.1
- Docker-Moby Client 20.10.11+azure-3
- Docker-Moby Server 20.10.11+azure-3
- Git 2.34.1 (apt source repository: ppa:git-core/ppa)
- Git LFS 3.0.2 (apt source repository: https://packagecloud.io/install/repositories/github/git-lfs)
- Git-ftp 1.3.1
- Haveged 1.9.1
- Heroku 7.59.2
- HHVM (HipHop VM) 4.145.0
- jq 1.5
- Kind 0.11.1
- Kubectl 1.23.2
- Kustomize 4.4.1
- Leiningen 2.9.8
- MediaInfo 17.12
- Mercurial 4.5.3
- Minikube 1.25.1
- n 8.0.2
- Newman 5.3.1
- nvm 0.39.1
- OpenSSL 1.1.1 11 Sep 2018
- Packer 1.7.9
- Parcel 2.2.1
- PhantomJS 2.1.1
- Podman 3.0.1 (apt source repository: https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable)
- Pulumi 3.22.1
- R 4.1.2
- Skopeo 1.2.2 (apt source repository: https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable)
- Sphinx Open Source Search Server 2.2.11
- SVN 1.9.7
- Terraform 1.1.4
- yamllint 1.26.3
- yq 4.17.2
- zstd 1.5.2 (homebrew)
### CLI Tools
- Alibaba Cloud CLI 3.0.104
- AWS CLI 1.22.41
- AWS CLI Session manager plugin 1.2.295.0
- AWS SAM CLI 1.37.0
- Azure CLI (azure-cli) 2.32.0 (installation method: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-linux?pivots=apt)
- Azure CLI (azure-devops) 0.22.0
- GitHub CLI 2.4.0
- Google Cloud SDK 369.0.0 (apt source repository: https://packages.cloud.google.com/apt)
- Hub CLI 2.14.2
- Netlify CLI 8.13.0
- OpenShift CLI 4.9.15
- ORAS CLI 0.12.0
- Vercel CLI 23.1.2
### Java
| Version | Vendor | Environment Variable |
| ------------------- | --------------- | -------------------- |
| 8.0.312+7 (default) | Eclipse Temurin | JAVA_HOME_8_X64 |
| 11.0.13+8 | Eclipse Temurin | JAVA_HOME_11_X64 |
| 12.0.2+10 | Adopt OpenJDK | JAVA_HOME_12_X64 |
| 17.0.1+12 | Eclipse Temurin | JAVA_HOME_17_X64 |
### PHP
| Tool | Version |
| -------- | ---------------------------------------- |
| PHP | 7.1.33 7.2.34 7.3.33 7.4.27 8.0.14 8.1.1 |
| Composer | 2.2.5 |
| PHPUnit | 8.5.23 |
```
Both Xdebug and PCOV extensions are installed, but only Xdebug is enabled.
```
### Haskell
- Cabal 3.6.2.0
- GHC 9.2.1
- GHCup 0.1.17.4
- Stack 2.7.3
### Rust Tools
- Cargo 1.58.0
- Rust 1.58.1
- Rustdoc 1.58.1
- Rustup 1.24.3
#### Packages
- Bindgen 0.59.2
- Cargo audit 0.16.0
- Cargo clippy 0.1.58
- Cargo outdated 0.10.2
- Cbindgen 0.20.0
- Rustfmt 1.4.38
### Browsers and Drivers
- Google Chrome 97.0.4692.99
- ChromeDriver 97.0.4692.71
- Mozilla Firefox 96.0
- Geckodriver 0.30.0
- Chromium 97.0.4692.0
- Selenium server 4.1.0
#### Environment variables
| Name | Value |
| ----------------- | ----------------------------------- |
| CHROMEWEBDRIVER | /usr/local/share/chrome_driver |
| GECKOWEBDRIVER | /usr/local/share/gecko_driver |
| SELENIUM_JAR_PATH | /usr/share/java/selenium-server.jar |
### .NET Core SDK
- 2.1.302 2.1.403 2.1.526 2.1.617 2.1.701 2.1.818 3.1.120 3.1.202 3.1.302 3.1.416 5.0.104 5.0.210 5.0.303 5.0.404
### .NET tools
- nbgv 3.4.255+06fb9182bf
### Databases
- MongoDB 5.0.5 (apt source repository: https://repo.mongodb.org/apt/ubuntu)
- PostgreSQL 14.1 (apt source repository: https://apt.postgresql.org/pub/repos/apt/)
- sqlite3 3.22.0
#### MySQL
- MySQL 5.7.36
- MySQL Server (user:root password:root)
```
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'
```
#### MS SQL Server Client Tools
- sqlcmd 17.8.0001.1
- SqlPackage 15.0.5282.3
### Cached Tools
#### Go
- 1.15.15
- 1.16.13
- 1.17.6
#### Node.js
- 12.22.9
- 14.18.3
- 16.13.2
#### PyPy
- 2.7.18 [PyPy 7.3.6]
- 3.6.12 [PyPy 7.3.3]
#### Python
- 2.7.18
- 3.6.15
- 3.7.12
- 3.8.12
- 3.9.10
- 3.10.2
#### Ruby
- 2.4.10
- 2.5.9
- 2.6.9
- 2.7.5
- 3.0.3
#### Environment variables
| Name | Value | Architecture |
| --------------- | ----------------------------------- | ------------ |
| GOROOT_1_15_X64 | /opt/hostedtoolcache/go/1.15.15/x64 | x64 |
| GOROOT_1_16_X64 | /opt/hostedtoolcache/go/1.16.13/x64 | x64 |
| GOROOT_1_17_X64 | /opt/hostedtoolcache/go/1.17.6/x64 | x64 |
### PowerShell Tools
- PowerShell 7.2.1
#### PowerShell Modules
| Module | Version |
| ---------- | ------- |
| MarkdownPS | 1.9 |
| Pester | 5.3.1 |
#### Az PowerShell Modules
- 7.1.0 3.1.0.zip 4.4.0.zip 5.9.0.zip 6.6.0.zip
### Web Servers
| Name | Version | ConfigFile | ServiceStatus | ListenPort |
| ------- | ------- | ------------------------- | ------------- | ---------- |
| apache2 | 2.4.29 | /etc/apache2/apache2.conf | inactive | 80 |
| nginx | 1.14.0 | /etc/nginx/nginx.conf | inactive | 80 |
### Android
| Package Name | Version |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Android Command Line Tools | 4.0 |
| Android Emulator | 31.1.4 |
| Android SDK Build-tools | 32.0.0<br>31.0.0<br>30.0.0 30.0.1 30.0.2 30.0.3<br>29.0.0 29.0.1 29.0.2 29.0.3<br>28.0.0 28.0.1 28.0.2 28.0.3<br>27.0.0 27.0.1 27.0.2 27.0.3<br>26.0.0 26.0.1 26.0.2 26.0.3<br>25.0.0 25.0.1 25.0.2 25.0.3<br>24.0.0 24.0.1 24.0.2 24.0.3<br>23.0.1 23.0.2 23.0.3 |
| Android SDK Platform-Tools | 31.0.3 |
| Android SDK Platforms | android-32 (rev 1)<br>android-31 (rev 1)<br>android-30 (rev 3)<br>android-29 (rev 5)<br>android-28 (rev 6)<br>android-27 (rev 3)<br>android-26 (rev 2)<br>android-25 (rev 3)<br>android-24 (rev 2)<br>android-23 (rev 3) |
| Android SDK Tools | 26.1.1 |
| Android Support Repository | 47.0.0 |
| CMake | 3.10.2<br>3.18.1 |
| Google APIs | addon-google_apis-google-21<br>addon-google_apis-google-22<br>addon-google_apis-google-23<br>addon-google_apis-google-24 |
| Google Play services | 49 |
| Google Repository | 58 |
| NDK | 21.4.7075529 (default)<br>23.1.7779620 |
| SDK Patch Applier v4 | 1 |
#### Environment variables
| Name | Value |
| ----------------------- | ------------------------------------------------------------------------------------ |
| ANDROID_HOME | /usr/local/lib/android/sdk |
| ANDROID_NDK_HOME | /usr/local/lib/android/sdk/ndk-bundle -> /usr/local/lib/android/sdk/ndk/21.4.7075529 |
| ANDROID_NDK_LATEST_HOME | /usr/local/lib/android/sdk/ndk/23.1.7779620 |
| ANDROID_NDK_ROOT | /usr/local/lib/android/sdk/ndk-bundle -> /usr/local/lib/android/sdk/ndk/21.4.7075529 |
| ANDROID_SDK_ROOT | /usr/local/lib/android/sdk |
### Cached Docker images
| Repository:Tag | Digest | Created |
| ----------------------- | ------------------------------------------------------------------------ | ---------- |
| alpine:3.12 | sha256:d9459083f962de6bd980ae6a05be2a4cf670df6a1d898157bceb420342bec280 | 2021-11-12 |
| alpine:3.13 | sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c | 2021-11-12 |
| alpine:3.14 | sha256:635f0aa53d99017b38d1a0aa5b2082f7812b03e3cdb299103fe77b5c8a07f1d2 | 2021-11-12 |
| buildpack-deps:bullseye | sha256:8cca6f729d85d572000a436eac498ff5358eeee5a0fa7b3e2b6668620c6ac06e | 2021-12-21 |
| buildpack-deps:buster | sha256:24cf4668129a284b9b960ffaa5e79f9654d7d040708721c8e7708811e362a9d9 | 2021-12-21 |
| buildpack-deps:stretch | sha256:19610095f7d1718c20d79e5109c42f64dabea9fdf104221394acdf2999170c51 | 2021-12-21 |
| debian:10 | sha256:94ccfd1c5115a6903cbb415f043a0b04e307be3f37b768cf6d6d3edff0021da3 | 2021-12-21 |
| debian:11 | sha256:2906804d2a64e8a13a434a1a127fe3f6a28bf7cf3696be4223b06276f32f1f2d | 2021-12-21 |
| debian:9 | sha256:54284a7a45383b407c1968657e8f1c50dc25b82d8d56b3812e46ad19f8bcfd83 | 2021-12-21 |
| moby/buildkit:latest | sha256:d6c89b7085b106301645ddcc77cf64eb7b705ab507b72d52d130ac33f1300417 | 2021-11-18 |
| node:12 | sha256:04334b2efca8f5f23d246ce8818fe358c2c0116819e5fd043e7929cae6ca7398 | 2022-01-11 |
| node:12-alpine | sha256:c3e7817c8fd124f1597cd66124d247db8e138e8ef311ba085b7104d900129d0b | 2022-01-12 |
| node:14 | sha256:50d4540794db9bdbd423a5823c4d840fc2ba0b9c82d71b059ff34b4aeb3e328f | 2022-01-11 |
| node:14-alpine | sha256:a9b200d25469261a4cccde176db08ec9f0b5799fa4220d8edfe57e69684c6dad | 2022-01-11 |
| node:16 | sha256:4b0b5c3db44f567d5d25c80a6fe33a981d911cdae20b39d2395be268aea2cb97 | 2022-01-11 |
| node:16-alpine | sha256:2f50f4a428f8b5280817c9d4d896dbee03f072e93f4e0c70b90cc84bd1fcfe0d | 2022-01-12 |
| ubuntu:16.04 | sha256:0f71fa8d4d2d4292c3c617fda2b36f6dabe5c8b6e34c3dc5b0d17d4e704bd39c | 2021-08-31 |
| ubuntu:18.04 | sha256:37b7471c1945a2a12e5a57488ee4e3e216a8369d0b9ee1ec2e41db9c2c1e3d22 | 2022-01-07 |
| ubuntu:20.04 | sha256:b5a61709a9a44284d88fb12e5c48db0409cfad5b69d4ff8224077c57302df9cf | 2022-01-07 |
### Installed apt packages
| Name | Version |
| ----------------- | --------------------------------- |
| aria2 | 1.33.1-1 |
| binutils | 2.30-21ubuntu1\~18.04.7 |
| bison | 2:3.0.4.dfsg-1build1 |
| brotli | 1.0.3-1ubuntu1.3 |
| build-essential | 12.4ubuntu1 |
| bzip2 | 1.0.6-8.1ubuntu0.2 |
| coreutils | 8.28-1ubuntu1 |
| curl | 7.58.0-2ubuntu3.16 |
| dbus | 1.12.2-1ubuntu1.2 |
| dnsutils | 1:9.11.3+dfsg-1ubuntu1.16 |
| dpkg | 1.19.0.5ubuntu2.3 |
| fakeroot | 1.22-2ubuntu1 |
| file | 1:5.32-2ubuntu0.4 |
| flex | 2.6.4-6 |
| ftp | 0.17-34 |
| gnupg2 | 2.2.4-1ubuntu1.4 |
| haveged | 1.9.1-6 |
| imagemagick | 8:6.9.7.4+dfsg-16ubuntu6.12 |
| iproute2 | 4.15.0-2ubuntu1.3 |
| iputils-ping | 3:20161105-1ubuntu3 |
| jq | 1.5+dfsg-2 |
| lib32z1 | 1:1.2.11.dfsg-0ubuntu2 |
| libc++-dev | 6.0-2 |
| libc++abi-dev | 6.0-2 |
| libcurl3 | 7.58.0-2ubuntu3.16 |
| libgbm-dev | 20.0.8-0ubuntu1\~18.04.1 |
| libgconf-2-4 | 3.2.6-4ubuntu1 |
| libgsl-dev | 2.4+dfsg-6 |
| libgtk-3-0 | 3.22.30-1ubuntu4 |
| libmagic-dev | 1:5.32-2ubuntu0.4 |
| libmagickcore-dev | 8:6.9.7.4+dfsg-16ubuntu6.12 |
| libmagickwand-dev | 8:6.9.7.4+dfsg-16ubuntu6.12 |
| libsecret-1-dev | 0.18.6-1 |
| libsqlite3-dev | 3.22.0-1ubuntu0.4 |
| libunwind8 | 1.2.1-8 |
| libxkbfile-dev | 1:1.0.9-2 |
| libxss1 | 1:1.2.2-1 |
| locales | 2.27-3ubuntu1.4 |
| m4 | 1.4.18-1 |
| mediainfo | 17.12-1 |
| mercurial | 4.5.3-1ubuntu2.2 |
| net-tools | 1.60+git20161116.90da8a0-1ubuntu1 |
| netcat | 1.10-41.1 |
| openssh-client | 1:7.6p1-4ubuntu0.5 |
| p7zip-full | 16.02+dfsg-6 |
| p7zip-rar | 16.02-2 |
| parallel | 20161222-1 |
| pass | 1.7.1-3 |
| patchelf | 0.9-1 |
| pkg-config | 0.29.1-0ubuntu2 |
| pollinate | 4.33-0ubuntu1\~18.04.2 |
| python-setuptools | 39.0.1-2 |
| rpm | 4.14.1+dfsg1-2 |
| rsync | 3.1.2-2.1ubuntu1.2 |
| shellcheck | 0.4.6-1 |
| sphinxsearch | 2.2.11-2 |
| sqlite3 | 3.22.0-1ubuntu0.4 |
| ssh | 1:7.6p1-4ubuntu0.5 |
| sshpass | 1.06-1 |
| subversion | 1.9.7-4ubuntu1 |
| sudo | 1.8.21p2-3ubuntu1.4 |
| swig | 3.0.12-1 |
| telnet | 0.17-41 |
| texinfo | 6.5.0.dfsg.1-2 |
| time | 1.7-25.1build1 |
| tk | 8.6.0+9 |
| tzdata | 2021e-0ubuntu0.18.04 |
| unzip | 6.0-21ubuntu1.1 |
| upx | 3.94-4 |
| wget | 1.19.4-1ubuntu2.2 |
| xorriso | 1.4.8-3 |
| xvfb | 2:1.19.6-1ubuntu4.10 |
| xz-utils | 5.2.2-1.3 |
| zip | 3.0-11build1 |
| zsync | 0.6.2-3ubuntu1 |
+233 -250
View File
@@ -1,49 +1,46 @@
| Announcements |
|-|
| [python2.7 will be removed from the images on May 15, 2023](https://github.com/actions/runner-images/issues/7401) |
| [[All OSs] .NET 2.1 will be removed from the images on February, 21](https://github.com/actions/virtual-environments/issues/4871) |
| [[All OSs] Az PowerShell Module will be upgraded to 7.1.0 on January, 17](https://github.com/actions/virtual-environments/issues/4841) |
| [[Ubuntu] Issue with libstdc++ cannot allocate memory in static TLS block](https://github.com/actions/virtual-environments/issues/4799) |
| [[All OSs] Python version 3.5 will be removed from the images on January 24, 2022](https://github.com/actions/virtual-environments/issues/4744) |
***
# Ubuntu 20.04
- OS Version: 20.04.6 LTS
- Kernel Version: 5.15.0-1037-azure
- Image Version: 20230507.1
- Systemd version: 245.4-4ubuntu3.21
# Ubuntu 20.04.3 LTS
- Linux kernel version: 5.11.0-1027-azure
- Image Version: 20220123.1
## Installed Software
### Language and Runtime
- Bash 5.0.17(1)-release
- Clang: 10.0.0, 11.0.0, 12.0.0
- Clang-format: 10.0.0, 11.0.0, 12.0.0
- Clang-tidy: 10.0.0, 11.0.0, 12.0.0
- Dash 0.5.10.2-6
- Erlang 25.3 (Eshell 13.2)
- Erlang rebar3 3.20.0
- GNU C++: 9.4.0, 10.3.0
- GNU Fortran: 9.4.0, 10.3.0
- Julia 1.8.5
- Kotlin 1.8.21-release-380
- Mono 6.12.0.182
- MSBuild 16.10.1.31701 (Mono 6.12.0.182)
- Node.js 18.16.0
- Clang 10.0.0, 11.0.0, 12.0.0
- Clang-format 10.0.0, 11.0.0, 12.0.0
- Erlang 24.2 (Eshell 12.2)
- Erlang rebar3 3.18.0
- GNU C++ 9.3.0, 10.3.0
- GNU Fortran 9.3.0, 10.3.0
- Julia 1.7.1
- Kotlin 1.6.0-release-798
- Mono 6.12.0.122 (apt source repository: https://download.mono-project.com/repo/ubuntu stable-focal main)
- MSBuild 16.6.0.15201 (from /usr/lib/mono/msbuild/15.0/bin/MSBuild.dll)
- Node 16.13.2
- Perl 5.30.0
- Python 3.8.10
- Python3 3.8.10
- Ruby 2.7.0p0
- Swift 5.8
- Swift 5.5.2
### Package Management
- cpan 1.64
- Helm 3.11.3
- Homebrew 4.0.16
- Miniconda 23.3.1
- Npm 9.5.1
- NuGet 6.3.1.1
- Helm 3.7.2
- Homebrew 3.3.11
- Miniconda 4.10.3
- Npm 8.1.2
- Pip 20.0.2
- Pip3 20.0.2
- Pipx 1.2.0
- Pipx 1.0.0
- RubyGems 3.1.2
- Vcpkg (build from commit 6a3dd0874)
- Yarn 1.22.19
- Vcpkg (build from master \<df40d1c47>)
- Yarn 1.22.17
#### Environment variables
| Name | Value |
@@ -51,212 +48,204 @@
| CONDA | /usr/share/miniconda |
| VCPKG_INSTALLATION_ROOT | /usr/local/share/vcpkg |
#### Homebrew note
```
Location: /home/linuxbrew
Note: Homebrew is pre-installed on image but not added to PATH.
run the eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" command
to accomplish this.
```
### Project Management
- Ant 1.10.7
- Gradle 8.1.1
- Lerna 6.6.2
- Maven 3.8.8
- Sbt 1.8.2
- Gradle 7.3.3
- Lerna 4.0.0
- Maven 3.8.4
- Sbt 1.6.1
### Tools
- Ansible 2.13.9
- Ansible 2.12.1
- apt-fast 1.9.12
- AzCopy 10.18.1 - available by `azcopy` and `azcopy10` aliases
- Bazel 6.1.2
- Bazelisk 1.13.2
- Bicep 0.17.1
- Buildah 1.22.3
- CMake 3.26.3
- CodeQL Action Bundles 2.13.0 2.13.1
- Docker Amazon ECR Credential Helper 0.7.0
- AzCopy 10.13.0 (available by `azcopy` and `azcopy10` aliases)
- Bazel 5.0.0
- Bazelisk 1.11.0
- Bicep 0.4.1124
- Buildah 1.21.3 (apt source repository: https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable)
- CMake 3.22.1
- CodeQL Action Bundle 2.7.5
- Docker Compose v1 1.29.2
- Docker Compose v2 2.17.3+azure-1
- Docker-Buildx 0.10.4
- Docker-Moby Client 20.10.24+azure-1
- Docker-Moby Server 20.10.24+azure-1
- Fastlane 2.212.2
- Git 2.40.1
- Git LFS 3.3.0
- Docker Compose v2 2.2.3+azure-1
- Docker-Buildx 0.7.1
- Docker-Moby Client 20.10.11+azure-3
- Docker-Moby Server 20.10.11+azure-3
- Fastlane 2.201.1
- Git 2.34.1 (apt source repository: ppa:git-core/ppa)
- Git LFS 3.0.2 (apt source repository: https://packagecloud.io/install/repositories/github/git-lfs)
- Git-ftp 1.6.0
- Haveged 1.9.1
- Heroku 8.1.3
- HHVM (HipHop VM) 4.172.1
- Heroku 7.59.2
- HHVM (HipHop VM) 4.145.0
- jq 1.6
- Kind 0.18.0
- Kubectl 1.27.1
- Kustomize 5.0.2
- Leiningen 2.10.0
- Kind 0.11.1
- Kubectl 1.23.2
- Kustomize 4.4.1
- Leiningen 2.9.8
- MediaInfo 19.09
- Mercurial 5.3.1
- Minikube 1.30.1
- n 9.1.0
- Newman 5.3.2
- nvm 0.39.3
- OpenSSL 1.1.1f-1ubuntu2.18
- Packer 1.8.7
- Parcel 2.8.3
- PhantomJS 2.1.1 2.1.1
- Podman 3.4.2
- Pulumi 3.66.0
- R 4.3.0
- Skopeo 1.5.0
- Minikube 1.25.1
- n 8.0.2
- Newman 5.3.1
- nvm 0.39.1
- OpenSSL 1.1.1f 31 Mar 2020
- Packer 1.7.9
- Parcel 2.2.1
- PhantomJS 2.1.1
- Podman 3.4.2 (apt source repository: https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable)
- Pulumi 3.22.1
- R 4.1.2
- Skopeo 1.3.0 (apt source repository: https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable)
- Sphinx Open Source Search Server 2.2.11
- SVN 1.13.0
- Terraform 1.4.6
- yamllint 1.31.0
- yq 4.33.3
- zstd 1.5.5
- Terraform 1.1.4
- yamllint 1.26.3
- yq 4.17.2
- zstd 1.5.2 (homebrew)
### CLI Tools
- Alibaba Cloud CLI 3.0.163
- AWS CLI 2.11.18
- AWS CLI Session Manager Plugin 1.2.463.0
- AWS SAM CLI 1.82.0
- Azure CLI 2.48.1
- Azure CLI (azure-devops) 0.26.0
- GitHub CLI 2.28.0
- Google Cloud SDK 429.0.0
- Alibaba Cloud CLI 3.0.104
- AWS CLI 2.4.13
- AWS CLI Session manager plugin 1.2.295.0
- AWS SAM CLI 1.37.0
- Azure CLI (azure-cli) 2.32.0 (installation method: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-linux?pivots=apt)
- Azure CLI (azure-devops) 0.22.0
- GitHub CLI 2.4.0
- Google Cloud SDK 369.0.0 (apt source repository: https://packages.cloud.google.com/apt)
- Hub CLI 2.14.2
- Netlify CLI 15.0.0
- OpenShift CLI 4.12.15
- ORAS CLI 1.0.0
- Vercel CLI 29.1.1
- Netlify CLI 8.13.0
- OpenShift CLI 4.9.15
- ORAS CLI 0.12.0
- Vercel CLI 23.1.2
### Java
| Version | Vendor | Environment Variable |
| ------------------- | --------------- | -------------------- |
| 8.0.362+9 | Eclipse Temurin | JAVA_HOME_8_X64 |
| 11.0.19+7 (default) | Eclipse Temurin | JAVA_HOME_11_X64 |
| 17.0.7+7 | Eclipse Temurin | JAVA_HOME_17_X64 |
| 8.0.312+7 | Eclipse Temurin | JAVA_HOME_8_X64 |
| 11.0.13+8 (default) | Eclipse Temurin | JAVA_HOME_11_X64 |
| 17.0.1+12 | Eclipse Temurin | JAVA_HOME_17_X64 |
### PHP Tools
- PHP: 7.4.33, 8.0.28, 8.1.18, 8.2.5
- Composer 2.5.5
- PHPUnit 8.5.33
```
Both Xdebug and PCOV extensions are installed, but only Xdebug is enabled.
```
### GraalVM
| Version | Environment variables |
| --------- | --------------------- |
| CE 21.3.0 | GRAALVM_11_ROOT |
### Haskell Tools
- Cabal 3.10.1.0
- GHC 9.6.1
- GHCup 0.1.19.2
- Stack 2.9.3
### PHP
| Tool | Version |
| -------- | ------------------- |
| PHP | 7.4.27 8.0.14 8.1.1 |
| Composer | 2.2.5 |
| PHPUnit | 8.5.23 |
```
Both Xdebug and PCOV extensions are installed, but only Xdebug is enabled.
```
### Haskell
- Cabal 3.6.2.0
- GHC 9.2.1
- GHCup 0.1.17.4
- Stack 2.7.3
### Rust Tools
- Cargo 1.69.0
- Rust 1.69.0
- Rustdoc 1.69.0
- Rustup 1.26.0
- Cargo 1.58.0
- Rust 1.58.1
- Rustdoc 1.58.1
- Rustup 1.24.3
#### Packages
- Bindgen 0.65.1
- Cargo audit 0.17.5
- Cargo clippy 0.1.69
- Cargo outdated 0.11.2
- Cbindgen 0.24.3
- Rustfmt 1.5.2
- Bindgen 0.59.2
- Cargo audit 0.16.0
- Cargo clippy 0.1.58
- Cargo outdated 0.10.2
- Cbindgen 0.20.0
- Rustfmt 1.4.38
### Browsers and Drivers
- Google Chrome 113.0.5672.63
- ChromeDriver 113.0.5672.63
- Chromium 113.0.5672.0
- Microsoft Edge 113.0.1774.35
- Microsoft Edge WebDriver 113.0.1774.35
- Selenium server 4.9.0
- Mozilla Firefox 113.0
- Geckodriver 0.33.0
- Google Chrome 97.0.4692.99
- ChromeDriver 97.0.4692.71
- Mozilla Firefox 96.0
- Geckodriver 0.30.0
- Chromium 97.0.4692.0
- Selenium server 4.1.0
#### Environment variables
| Name | Value |
| ----------------- | ----------------------------------- |
| CHROMEWEBDRIVER | /usr/local/share/chrome_driver |
| EDGEWEBDRIVER | /usr/local/share/edge_driver |
| GECKOWEBDRIVER | /usr/local/share/gecko_driver |
| SELENIUM_JAR_PATH | /usr/share/java/selenium-server.jar |
### .NET Tools
- .NET Core SDK: 3.1.120, 3.1.202, 3.1.302, 3.1.426, 6.0.408, 7.0.105, 7.0.203
- nbgv 3.6.128+518ee610d6
### .NET Core SDK
- 2.1.302 2.1.403 2.1.526 2.1.617 2.1.701 2.1.818 3.1.120 3.1.202 3.1.302 3.1.416 5.0.104 5.0.210 5.0.303 5.0.404
### .NET tools
- nbgv 3.4.255+06fb9182bf
### Databases
- MongoDB 5.0.17
- MongoDB 5.0.5 (apt source repository: https://repo.mongodb.org/apt/ubuntu)
- PostgreSQL 14.1 (apt source repository: https://apt.postgresql.org/pub/repos/apt/)
- sqlite3 3.31.1
#### PostgreSQL
- PostgreSQL 14.7
```
User: postgres
PostgreSQL service is disabled by default.
Use the following command as a part of your job to start the service: 'sudo systemctl start postgresql.service'
```
#### MySQL
- MySQL 8.0.32-0ubuntu0.20.04.2
```
User: root
Password: root
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'
```
- MySQL 8.0.26
- MySQL Server (user:root password:root)
#### MS SQL
- sqlcmd 17.10.0001.1
- SqlPackage 16.1.8089.0
```
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'
```
#### MS SQL Server Client Tools
- sqlcmd 17.8.0001.1
- SqlPackage 15.0.5282.3
### Cached Tools
#### Go
- 1.18.10
- 1.19.9
- 1.20.4
- 1.15.15
- 1.16.13
- 1.17.6
#### Node.js
- 14.21.3
- 16.20.0
- 18.16.0
- 12.22.9
- 14.18.3
- 16.13.2
#### PyPy
- 2.7.18 [PyPy 7.3.6]
- 3.6.12 [PyPy 7.3.3]
- 3.7.12 [PyPy 7.3.7]
- 3.8.12 [PyPy 7.3.7]
#### Python
- 2.7.18
- 3.6.15
- 3.7.16
- 3.8.16
- 3.9.16
- 3.10.11
- 3.11.3
#### PyPy
- 2.7.18 [PyPy 7.3.11]
- 3.6.12 [PyPy 7.3.3]
- 3.7.13 [PyPy 7.3.9]
- 3.8.16 [PyPy 7.3.11]
- 3.9.16 [PyPy 7.3.11]
- 3.7.12
- 3.8.12
- 3.9.10
- 3.10.2
#### Ruby
- 2.5.9
- 2.6.10
- 2.7.8
- 3.0.6
- 3.1.4
- 2.6.9
- 2.7.5
- 3.0.3
#### Environment variables
| Name | Value | Architecture |
| --------------- | ----------------------------------- | ------------ |
| GOROOT_1_15_X64 | /opt/hostedtoolcache/go/1.15.15/x64 | x64 |
| GOROOT_1_16_X64 | /opt/hostedtoolcache/go/1.16.13/x64 | x64 |
| GOROOT_1_17_X64 | /opt/hostedtoolcache/go/1.17.6/x64 | x64 |
### PowerShell Tools
- PowerShell 7.2.11
- PowerShell 7.2.1
#### PowerShell Modules
- Az: 9.3.0
- Az (Cached): 3.1.0.zip, 4.4.0.zip, 5.9.0.zip, 6.6.0.zip, 7.5.0.zip
- MarkdownPS: 1.9
- Microsoft.Graph: 1.27.0
- Pester: 5.4.1
- PSScriptAnalyzer: 1.21.0
| Module | Version |
| ---------------- | ------- |
| MarkdownPS | 1.9 |
| Pester | 5.3.1 |
| PSScriptAnalyzer | 1.20.0 |
#### Az PowerShell Modules
- 7.1.0 3.1.0.zip 4.4.0.zip 5.9.0.zip 6.6.0.zip
### Web Servers
| Name | Version | ConfigFile | ServiceStatus | ListenPort |
@@ -266,108 +255,102 @@ Use the following command as a part of your job to start the service: 'sudo syst
| nginx | 1.18.0 | /etc/nginx/nginx.conf | inactive | 80 |
### Android
| Package Name | Version |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Android Command Line Tools | 9.0 |
| Android Emulator | 32.1.12 |
| Android SDK Build-tools | 33.0.0 33.0.1 33.0.2<br>32.0.0<br>31.0.0<br>30.0.0 30.0.1 30.0.2 30.0.3<br>29.0.0 29.0.1 29.0.2 29.0.3<br>28.0.0 28.0.1 28.0.2 28.0.3<br>27.0.0 27.0.1 27.0.2 27.0.3 |
| Android SDK Platform-Tools | 34.0.1 |
| Android SDK Platforms | android-33-ext5 (rev 1)<br>android-33-ext4 (rev 1)<br>android-33 (rev 2)<br>android-32 (rev 1)<br>android-31 (rev 1)<br>android-30 (rev 3)<br>android-29 (rev 5)<br>android-28 (rev 6)<br>android-27 (rev 3) |
| Android SDK Tools | 26.1.1 |
| Android Support Repository | 47.0.0 |
| CMake | 3.10.2<br>3.18.1<br>3.22.1 |
| Google Play services | 49 |
| Google Repository | 58 |
| NDK | 23.2.8568313<br>24.0.8215888<br>25.2.9519653 (default) |
| SDK Patch Applier v4 | 1 |
| Package Name | Version |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Android Command Line Tools | 4.0 |
| Android Emulator | 31.1.4 |
| Android SDK Build-tools | 32.0.0<br>31.0.0<br>30.0.0 30.0.1 30.0.2 30.0.3<br>29.0.0 29.0.1 29.0.2 29.0.3<br>28.0.0 28.0.1 28.0.2 28.0.3<br>27.0.0 27.0.1 27.0.2 27.0.3 |
| Android SDK Platform-Tools | 31.0.3 |
| Android SDK Platforms | android-32 (rev 1)<br>android-31 (rev 1)<br>android-30 (rev 3)<br>android-29 (rev 5)<br>android-28 (rev 6)<br>android-27 (rev 3) |
| Android SDK Tools | 26.1.1 |
| Android Support Repository | 47.0.0 |
| CMake | 3.10.2<br>3.18.1 |
| Google Play services | 49 |
| Google Repository | 58 |
| NDK | 21.4.7075529 (default)<br>22.1.7171670<br>23.1.7779620 |
| SDK Patch Applier v4 | 1 |
#### Environment variables
| Name | Value |
| ----------------------- | ------------------------------------------- |
| ANDROID_HOME | /usr/local/lib/android/sdk |
| ANDROID_NDK | /usr/local/lib/android/sdk/ndk/25.2.9519653 |
| ANDROID_NDK_HOME | /usr/local/lib/android/sdk/ndk/25.2.9519653 |
| ANDROID_NDK_LATEST_HOME | /usr/local/lib/android/sdk/ndk/25.2.9519653 |
| ANDROID_NDK_ROOT | /usr/local/lib/android/sdk/ndk/25.2.9519653 |
| ANDROID_SDK_ROOT | /usr/local/lib/android/sdk |
| Name | Value |
| ----------------------- | ------------------------------------------------------------------------------------ |
| ANDROID_HOME | /usr/local/lib/android/sdk |
| ANDROID_NDK_HOME | /usr/local/lib/android/sdk/ndk-bundle -> /usr/local/lib/android/sdk/ndk/21.4.7075529 |
| ANDROID_NDK_LATEST_HOME | /usr/local/lib/android/sdk/ndk/23.1.7779620 |
| ANDROID_NDK_ROOT | /usr/local/lib/android/sdk/ndk-bundle -> /usr/local/lib/android/sdk/ndk/21.4.7075529 |
| ANDROID_SDK_ROOT | /usr/local/lib/android/sdk |
### Cached Docker images
| Repository:Tag | Digest | Created |
| ----------------------- | ------------------------------------------------------------------------ | ---------- |
| alpine:3.14 | sha256:0f2d5c38dd7a4f4f733e688e3a6733cb5ab1ac6e3cb4603a5dd564e5bfb80eed | 2023-03-29 |
| alpine:3.15 | sha256:ecbdce53b2c2f43ab1b19418bcbd3f120a23547d9497030c8d978114512b883e | 2023-03-29 |
| alpine:3.16 | sha256:c2b622f6e510a0d25bccaffa9e67b75a6860cb09b74bb58cfc36a9ef4331109f | 2023-03-29 |
| alpine:3.17 | sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126 | 2023-03-29 |
| buildpack-deps:bullseye | sha256:373587e78550f0918d16b793d1ead24c3f489a14c3af85da1096e2a683908e0c | 2023-05-03 |
| buildpack-deps:buster | sha256:bfefe2afb02a82c76d955f2c158b3b5aa96e7afcf262727e8f37ccdea60a39c5 | 2023-05-03 |
| buildpack-deps:stretch | sha256:78e995165a5788c2f55aed6e548d8f6c1534830d4310c870408fccb2da8c5b2e | 2022-06-23 |
| debian:10 | sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 | 2023-05-02 |
| debian:11 | sha256:63d62ae233b588d6b426b7b072d79d1306bfd02a72bff1fc045b8511cc89ee09 | 2023-05-02 |
| debian:9 | sha256:c5c5200ff1e9c73ffbf188b4a67eb1c91531b644856b4aefe86a58d2f0cb05be | 2022-06-23 |
| moby/buildkit:latest | sha256:d6fa89830c26919acba23c5cafa09df0c3ec1fbde20bb2a15ff349e0795241f4 | 2023-04-20 |
| node:14 | sha256:a158d3b9b4e3fa813fa6c8c590b8f0a860e015ad4e59bbce5744d2f6fd8461aa | 2023-04-12 |
| node:14-alpine | sha256:434215b487a329c9e867202ff89e704d3a75e554822e07f3e0c0f9e606121b33 | 2023-03-29 |
| node:16 | sha256:550f484fc5f314b575f5e397c9e2c71d7f218e59729fcda9ffa7ea1fc825dce7 | 2023-05-04 |
| node:16-alpine | sha256:f1657204d3463bce763cefa5b25e48c28af6fe0cdb0f68b354f0f8225ef61be7 | 2023-03-29 |
| node:18 | sha256:3f567a26b6b6d601fb2b168d4f987b50697617ead15bfc0e0152e600ac48d0fe | 2023-05-04 |
| node:18-alpine | sha256:1ccc70acda680aa4ba47f53e7c40b2d4d6892de74817128e0662d32647dd7f4d | 2023-04-13 |
| ubuntu:16.04 | sha256:1f1a2d56de1d604801a9671f301190704c25d604a416f59e03c04f5c6ffee0d6 | 2021-08-31 |
| ubuntu:18.04 | sha256:8aa9c2798215f99544d1ce7439ea9c3a6dfd82de607da1cec3a8a2fae005931b | 2023-03-08 |
| ubuntu:20.04 | sha256:db8bf6f4fb351aa7a26e27ba2686cf35a6a409f65603e59d4c203e58387dc6b3 | 2023-04-13 |
| alpine:3.12 | sha256:d9459083f962de6bd980ae6a05be2a4cf670df6a1d898157bceb420342bec280 | 2021-11-12 |
| alpine:3.13 | sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c | 2021-11-12 |
| alpine:3.14 | sha256:635f0aa53d99017b38d1a0aa5b2082f7812b03e3cdb299103fe77b5c8a07f1d2 | 2021-11-12 |
| buildpack-deps:bullseye | sha256:8cca6f729d85d572000a436eac498ff5358eeee5a0fa7b3e2b6668620c6ac06e | 2021-12-21 |
| buildpack-deps:buster | sha256:24cf4668129a284b9b960ffaa5e79f9654d7d040708721c8e7708811e362a9d9 | 2021-12-21 |
| buildpack-deps:stretch | sha256:19610095f7d1718c20d79e5109c42f64dabea9fdf104221394acdf2999170c51 | 2021-12-21 |
| debian:10 | sha256:94ccfd1c5115a6903cbb415f043a0b04e307be3f37b768cf6d6d3edff0021da3 | 2021-12-21 |
| debian:11 | sha256:2906804d2a64e8a13a434a1a127fe3f6a28bf7cf3696be4223b06276f32f1f2d | 2021-12-21 |
| debian:9 | sha256:54284a7a45383b407c1968657e8f1c50dc25b82d8d56b3812e46ad19f8bcfd83 | 2021-12-21 |
| moby/buildkit:latest | sha256:d6c89b7085b106301645ddcc77cf64eb7b705ab507b72d52d130ac33f1300417 | 2021-11-18 |
| node:12 | sha256:04334b2efca8f5f23d246ce8818fe358c2c0116819e5fd043e7929cae6ca7398 | 2022-01-11 |
| node:12-alpine | sha256:c3e7817c8fd124f1597cd66124d247db8e138e8ef311ba085b7104d900129d0b | 2022-01-12 |
| node:14 | sha256:50d4540794db9bdbd423a5823c4d840fc2ba0b9c82d71b059ff34b4aeb3e328f | 2022-01-11 |
| node:14-alpine | sha256:a9b200d25469261a4cccde176db08ec9f0b5799fa4220d8edfe57e69684c6dad | 2022-01-11 |
| node:16 | sha256:4b0b5c3db44f567d5d25c80a6fe33a981d911cdae20b39d2395be268aea2cb97 | 2022-01-11 |
| node:16-alpine | sha256:2f50f4a428f8b5280817c9d4d896dbee03f072e93f4e0c70b90cc84bd1fcfe0d | 2022-01-12 |
| ubuntu:16.04 | sha256:0f71fa8d4d2d4292c3c617fda2b36f6dabe5c8b6e34c3dc5b0d17d4e704bd39c | 2021-08-31 |
| ubuntu:18.04 | sha256:37b7471c1945a2a12e5a57488ee4e3e216a8369d0b9ee1ec2e41db9c2c1e3d22 | 2022-01-07 |
| ubuntu:20.04 | sha256:b5a61709a9a44284d88fb12e5c48db0409cfad5b69d4ff8224077c57302df9cf | 2022-01-07 |
### Installed apt packages
| Name | Version |
| ---------------------- | --------------------------------- |
| acl | 2.2.53-6 |
| aria2 | 1.35.0-1build1 |
| autoconf | 2.69-11.1 |
| automake | 1:1.16.1-4ubuntu6 |
| binutils | 2.34-6ubuntu1.4 |
| binutils | 2.34-6ubuntu1.3 |
| bison | 2:3.5.1+dfsg-1 |
| brotli | 1.0.7-6ubuntu0.1 |
| build-essential | 12.8ubuntu1.1 |
| bzip2 | 1.0.8-2 |
| coreutils | 8.30-3ubuntu2 |
| curl | 7.68.0-1ubuntu2.18 |
| dbus | 1.12.16-2ubuntu2.3 |
| dnsutils | 1:9.16.1-0ubuntu2.14 |
| dpkg | 1.19.7ubuntu3.2 |
| curl | 7.68.0-1ubuntu2.7 |
| dbus | 1.12.16-2ubuntu2.1 |
| dnsutils | 1:9.16.1-0ubuntu2.9 |
| dpkg | 1.19.7ubuntu3 |
| fakeroot | 1.24-1 |
| file | 1:5.38-4 |
| flex | 2.6.4-6.2 |
| fonts-noto-color-emoji | 0\~20200916-1\~ubuntu20.04.1 |
| ftp | 0.17-34.1 |
| gnupg2 | 2.2.19-3ubuntu2.2 |
| gnupg2 | 2.2.19-3ubuntu2.1 |
| haveged | 1.9.1-6ubuntu1 |
| imagemagick | 8:6.9.10.23+dfsg-2.1ubuntu11.7 |
| imagemagick | 8:6.9.10.23+dfsg-2.1ubuntu11.4 |
| iproute2 | 5.5.0-1ubuntu1 |
| iputils-ping | 3:20190709-3 |
| jq | 1.6-1ubuntu0.20.04.1 |
| lib32z1 | 1:1.2.11.dfsg-2ubuntu1.5 |
| lib32z1 | 1:1.2.11.dfsg-2ubuntu1.2 |
| libc++-dev | 1:10.0-50\~exp1 |
| libc++abi-dev | 1:10.0-50\~exp1 |
| libcurl4 | 7.68.0-1ubuntu2.18 |
| libgbm-dev | 21.2.6-0ubuntu0.1\~20.04.2 |
| libcurl4 | 7.68.0-1ubuntu2.7 |
| libgbm-dev | 21.0.3-0ubuntu0.3\~20.04.5 |
| libgconf-2-4 | 3.2.6-6ubuntu1 |
| libgsl-dev | 2.5+dfsg-6build1 |
| libgtk-3-0 | 3.24.20-0ubuntu1.1 |
| libgtk-3-0 | 3.24.20-0ubuntu1 |
| libmagic-dev | 1:5.38-4 |
| libmagickcore-dev | 8:6.9.10.23+dfsg-2.1ubuntu11.7 |
| libmagickwand-dev | 8:6.9.10.23+dfsg-2.1ubuntu11.7 |
| libmagickcore-dev | 8:6.9.10.23+dfsg-2.1ubuntu11.4 |
| libmagickwand-dev | 8:6.9.10.23+dfsg-2.1ubuntu11.4 |
| libsecret-1-dev | 0.20.4-0ubuntu1 |
| libsqlite3-dev | 3.31.1-4ubuntu0.5 |
| libtool | 2.4.6-14 |
| libunwind8 | 1.2.1-9ubuntu0.1 |
| libsqlite3-dev | 3.31.1-4ubuntu0.2 |
| libunwind8 | 1.2.1-9build1 |
| libxkbfile-dev | 1:1.1.0-1 |
| libxss1 | 1:1.2.3-1 |
| libyaml-dev | 0.2.2-1 |
| locales | 2.31-0ubuntu9.9 |
| locales | 2.31-0ubuntu9.2 |
| m4 | 1.4.18-4 |
| mediainfo | 19.09-1build1 |
| mercurial | 5.3.1-1ubuntu1 |
| net-tools | 1.60+git20180626.aebd88e-1ubuntu1 |
| netcat | 1.206-1ubuntu1 |
| openssh-client | 1:8.2p1-4ubuntu0.5 |
| openssh-client | 1:8.2p1-4ubuntu0.4 |
| p7zip-full | 16.02+dfsg-7build1 |
| p7zip-rar | 16.02-3build1 |
| parallel | 20161222-1.1 |
@@ -377,27 +360,27 @@ Use the following command as a part of your job to start the service: 'sudo syst
| pollinate | 4.33-3ubuntu1.20.04.1 |
| python-is-python3 | 3.8.2-4 |
| rpm | 4.14.2.1+dfsg1-1build2 |
| rsync | 3.1.3-8ubuntu0.5 |
| rsync | 3.1.3-8ubuntu0.1 |
| shellcheck | 0.7.0-2build2 |
| sphinxsearch | 2.2.11-2ubuntu2 |
| sqlite3 | 3.31.1-4ubuntu0.5 |
| ssh | 1:8.2p1-4ubuntu0.5 |
| sqlite3 | 3.31.1-4ubuntu0.2 |
| ssh | 1:8.2p1-4ubuntu0.4 |
| sshpass | 1.06-1 |
| subversion | 1.13.0-3ubuntu0.2 |
| sudo | 1.8.31-1ubuntu1.5 |
| subversion | 1.13.0-3 |
| sudo | 1.8.31-1ubuntu1.2 |
| swig | 4.0.1-5build1 |
| tar | 1.30+dfsg-7ubuntu0.20.04.3 |
| telnet | 0.17-41.2build1 |
| texinfo | 6.7.0.dfsg.2-5 |
| time | 1.7-25.1build1 |
| tk | 8.6.9+1 |
| tzdata | 2023c-0ubuntu0.20.04.1 |
| unzip | 6.0-25ubuntu1.1 |
| tzdata | 2021e-0ubuntu0.20.04 |
| unzip | 6.0-25ubuntu1 |
| upx | 3.95-2build1 |
| wget | 1.20.3-1ubuntu2 |
| xorriso | 1.5.2-1 |
| xvfb | 2:1.20.13-1ubuntu1\~20.04.8 |
| xz-utils | 5.2.4-1ubuntu1.1 |
| xvfb | 2:1.20.13-1ubuntu1\~20.04.2 |
| xz-utils | 5.2.4-1ubuntu1 |
| zip | 3.0-11build1 |
| zsync | 0.6.2-3ubuntu1 |
-385
View File
@@ -1,385 +0,0 @@
| Announcements |
|-|
| [python2.7 will be removed from the images on May 15, 2023](https://github.com/actions/runner-images/issues/7401) |
***
# Ubuntu 22.04
- OS Version: 22.04.2 LTS
- Kernel Version: 5.15.0-1037-azure
- Image Version: 20230507.1
- Systemd version: 249.11-0ubuntu3.9
## Installed Software
### Language and Runtime
- Bash 5.1.16(1)-release
- Clang: 12.0.1, 13.0.1, 14.0.0
- Clang-format: 12.0.1, 13.0.1, 14.0.0
- Clang-tidy: 12.0.1, 13.0.1, 14.0.0
- Dash 0.5.11+git20210903+057cd650a4ed-3build1
- GNU C++: 9.5.0, 10.4.0, 11.3.0, 12.1.0
- GNU Fortran: 9.5.0, 10.4.0, 11.3.0, 12.1.0
- Julia 1.8.5
- Kotlin 1.8.21-release-380
- Mono 6.12.0.182
- MSBuild 16.10.1.31701 (Mono 6.12.0.182)
- Node.js 18.16.0
- Perl 5.34.0
- Python 3.10.6
- Python3 3.10.6
- Ruby 3.0.2p107
- Swift 5.8
### Package Management
- cpan 1.64
- Helm 3.11.3
- Homebrew 4.0.16
- Miniconda 23.3.1
- Npm 9.5.1
- NuGet 6.3.1.1
- Pip 22.0.2
- Pip3 22.0.2
- Pipx 1.2.0
- RubyGems 3.3.5
- Vcpkg (build from commit 6a3dd0874)
- Yarn 1.22.19
#### Environment variables
| Name | Value |
| ----------------------- | ---------------------- |
| CONDA | /usr/share/miniconda |
| VCPKG_INSTALLATION_ROOT | /usr/local/share/vcpkg |
#### Homebrew note
```
Location: /home/linuxbrew
Note: Homebrew is pre-installed on image but not added to PATH.
run the eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" command
to accomplish this.
```
### Project Management
- Lerna 6.6.2
- Maven 3.8.8
### Tools
- Ansible 2.14.5
- apt-fast 1.9.12
- AzCopy 10.18.1 - available by `azcopy` and `azcopy10` aliases
- Bazel 6.1.2
- Bazelisk 1.13.2
- Bicep 0.17.1
- Buildah 1.23.1
- CMake 3.26.3
- CodeQL Action Bundles 2.13.0 2.13.1
- Docker Amazon ECR Credential Helper 0.7.0
- Docker Compose v1 1.29.2
- Docker Compose v2 2.17.3+azure-1
- Docker-Buildx 0.10.4
- Docker-Moby Client 20.10.24+azure-1
- Docker-Moby Server 20.10.24+azure-1
- Fastlane 2.212.2
- Git 2.40.1
- Git LFS 3.3.0
- Git-ftp 1.6.0
- Haveged 1.9.14
- Heroku 8.1.3
- jq 1.6
- Kind 0.18.0
- Kubectl 1.27.1
- Kustomize 5.0.2
- Leiningen 2.10.0
- MediaInfo 21.09
- Mercurial 6.1.1
- Minikube 1.30.1
- n 9.1.0
- Newman 5.3.2
- nvm 0.39.3
- OpenSSL 3.0.2-0ubuntu1.9
- Packer 1.8.7
- Parcel 2.8.3
- Podman 3.4.4
- Pulumi 3.66.0
- R 4.3.0
- Skopeo 1.4.1
- Sphinx Open Source Search Server 2.2.11
- SVN 1.14.1
- Terraform 1.4.6
- yamllint 1.31.0
- yq 4.33.3
- zstd 1.5.5
### CLI Tools
- Alibaba Cloud CLI 3.0.163
- AWS CLI 2.11.18
- AWS CLI Session Manager Plugin 1.2.463.0
- AWS SAM CLI 1.82.0
- Azure CLI 2.48.1
- Azure CLI (azure-devops) 0.26.0
- GitHub CLI 2.28.0
- Google Cloud SDK 429.0.0
- Hub CLI 2.14.2
- Netlify CLI 15.0.0
- OpenShift CLI 4.12.15
- ORAS CLI 1.0.0
- Vercel CLI 29.1.1
### Java
| Version | Vendor | Environment Variable |
| ------------------- | --------------- | -------------------- |
| 8.0.362+9 | Eclipse Temurin | JAVA_HOME_8_X64 |
| 11.0.19+7 (default) | Eclipse Temurin | JAVA_HOME_11_X64 |
| 17.0.7+7 | Eclipse Temurin | JAVA_HOME_17_X64 |
### PHP Tools
- PHP: 8.1.2
- Composer 2.5.5
- PHPUnit 8.5.33
```
Both Xdebug and PCOV extensions are installed, but only Xdebug is enabled.
```
### Haskell Tools
- Cabal 3.10.1.0
- GHC 9.6.1
- GHCup 0.1.19.2
- Stack 2.9.3
### Rust Tools
- Cargo 1.69.0
- Rust 1.69.0
- Rustdoc 1.69.0
- Rustup 1.26.0
#### Packages
- Bindgen 0.65.1
- Cargo audit 0.17.5
- Cargo clippy 0.1.69
- Cargo outdated 0.11.2
- Cbindgen 0.24.3
- Rustfmt 1.5.2
### Browsers and Drivers
- Google Chrome 113.0.5672.63
- ChromeDriver 113.0.5672.63
- Chromium 113.0.5672.0
- Microsoft Edge 113.0.1774.35
- Microsoft Edge WebDriver 113.0.1774.35
- Selenium server 4.9.0
- Mozilla Firefox 113.0
- Geckodriver 0.33.0
#### Environment variables
| Name | Value |
| ----------------- | ----------------------------------- |
| CHROMEWEBDRIVER | /usr/local/share/chrome_driver |
| EDGEWEBDRIVER | /usr/local/share/edge_driver |
| GECKOWEBDRIVER | /usr/local/share/gecko_driver |
| SELENIUM_JAR_PATH | /usr/share/java/selenium-server.jar |
### .NET Tools
- .NET Core SDK: 6.0.408, 7.0.105, 7.0.203
- nbgv 3.6.128+518ee610d6
### Databases
- sqlite3 3.37.2
#### PostgreSQL
- PostgreSQL 14.7
```
User: postgres
PostgreSQL service is disabled by default.
Use the following command as a part of your job to start the service: 'sudo systemctl start postgresql.service'
```
#### MySQL
- MySQL 8.0.32-0ubuntu0.22.04.2
```
User: root
Password: root
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'
```
#### MS SQL
- sqlcmd 17.10.0001.1
- SqlPackage 16.1.8089.0
### Cached Tools
#### Go
- 1.18.10
- 1.19.9
- 1.20.4
#### Node.js
- 14.21.3
- 16.20.0
- 18.16.0
#### Python
- 3.7.16
- 3.8.16
- 3.9.16
- 3.10.11
- 3.11.3
#### PyPy
- 3.7.13 [PyPy 7.3.9]
- 3.8.16 [PyPy 7.3.11]
- 3.9.16 [PyPy 7.3.11]
#### Ruby
- 3.1.4
### PowerShell Tools
- PowerShell 7.2.11
#### PowerShell Modules
- Az: 9.3.0
- MarkdownPS: 1.9
- Microsoft.Graph: 1.27.0
- Pester: 5.4.1
- PSScriptAnalyzer: 1.21.0
### Web Servers
| Name | Version | ConfigFile | ServiceStatus | ListenPort |
| ------- | ------- | ------------------------- | ------------- | ---------- |
| apache2 | 2.4.52 | /etc/apache2/apache2.conf | inactive | 80 |
| nginx | 1.18.0 | /etc/nginx/nginx.conf | inactive | 80 |
### Android
| Package Name | Version |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Android Command Line Tools | 9.0 |
| Android Emulator | 32.1.12 |
| Android SDK Build-tools | 33.0.0 33.0.1 33.0.2<br>32.0.0<br>31.0.0<br>30.0.0 30.0.1 30.0.2 30.0.3<br>29.0.0 29.0.1 29.0.2 29.0.3<br>28.0.0 28.0.1 28.0.2 28.0.3<br>27.0.0 27.0.1 27.0.2 27.0.3 |
| Android SDK Platform-Tools | 34.0.1 |
| Android SDK Platforms | android-33-ext5 (rev 1)<br>android-33-ext4 (rev 1)<br>android-33 (rev 2)<br>android-32 (rev 1)<br>android-31 (rev 1)<br>android-30 (rev 3)<br>android-29 (rev 5)<br>android-28 (rev 6)<br>android-27 (rev 3) |
| Android SDK Tools | 26.1.1 |
| Android Support Repository | 47.0.0 |
| CMake | 3.10.2<br>3.18.1<br>3.22.1 |
| Google Play services | 49 |
| Google Repository | 58 |
| NDK | 23.2.8568313<br>24.0.8215888<br>25.2.9519653 (default) |
| SDK Patch Applier v4 | 1 |
#### Environment variables
| Name | Value |
| ----------------------- | ------------------------------------------- |
| ANDROID_HOME | /usr/local/lib/android/sdk |
| ANDROID_NDK | /usr/local/lib/android/sdk/ndk/25.2.9519653 |
| ANDROID_NDK_HOME | /usr/local/lib/android/sdk/ndk/25.2.9519653 |
| ANDROID_NDK_LATEST_HOME | /usr/local/lib/android/sdk/ndk/25.2.9519653 |
| ANDROID_NDK_ROOT | /usr/local/lib/android/sdk/ndk/25.2.9519653 |
| ANDROID_SDK_ROOT | /usr/local/lib/android/sdk |
### Cached Docker images
| Repository:Tag | Digest | Created |
| ----------------------- | ------------------------------------------------------------------------ | ---------- |
| alpine:3.14 | sha256:0f2d5c38dd7a4f4f733e688e3a6733cb5ab1ac6e3cb4603a5dd564e5bfb80eed | 2023-03-29 |
| alpine:3.15 | sha256:ecbdce53b2c2f43ab1b19418bcbd3f120a23547d9497030c8d978114512b883e | 2023-03-29 |
| alpine:3.16 | sha256:c2b622f6e510a0d25bccaffa9e67b75a6860cb09b74bb58cfc36a9ef4331109f | 2023-03-29 |
| alpine:3.17 | sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126 | 2023-03-29 |
| buildpack-deps:bullseye | sha256:373587e78550f0918d16b793d1ead24c3f489a14c3af85da1096e2a683908e0c | 2023-05-03 |
| buildpack-deps:buster | sha256:bfefe2afb02a82c76d955f2c158b3b5aa96e7afcf262727e8f37ccdea60a39c5 | 2023-05-03 |
| debian:10 | sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 | 2023-05-02 |
| debian:11 | sha256:63d62ae233b588d6b426b7b072d79d1306bfd02a72bff1fc045b8511cc89ee09 | 2023-05-02 |
| moby/buildkit:latest | sha256:d6fa89830c26919acba23c5cafa09df0c3ec1fbde20bb2a15ff349e0795241f4 | 2023-04-20 |
| node:14 | sha256:a158d3b9b4e3fa813fa6c8c590b8f0a860e015ad4e59bbce5744d2f6fd8461aa | 2023-04-12 |
| node:14-alpine | sha256:434215b487a329c9e867202ff89e704d3a75e554822e07f3e0c0f9e606121b33 | 2023-03-29 |
| node:16 | sha256:550f484fc5f314b575f5e397c9e2c71d7f218e59729fcda9ffa7ea1fc825dce7 | 2023-05-04 |
| node:16-alpine | sha256:f1657204d3463bce763cefa5b25e48c28af6fe0cdb0f68b354f0f8225ef61be7 | 2023-03-29 |
| node:18 | sha256:3f567a26b6b6d601fb2b168d4f987b50697617ead15bfc0e0152e600ac48d0fe | 2023-05-04 |
| node:18-alpine | sha256:1ccc70acda680aa4ba47f53e7c40b2d4d6892de74817128e0662d32647dd7f4d | 2023-04-13 |
| ubuntu:18.04 | sha256:8aa9c2798215f99544d1ce7439ea9c3a6dfd82de607da1cec3a8a2fae005931b | 2023-03-08 |
| ubuntu:20.04 | sha256:db8bf6f4fb351aa7a26e27ba2686cf35a6a409f65603e59d4c203e58387dc6b3 | 2023-04-13 |
| ubuntu:22.04 | sha256:dfd64a3b4296d8c9b62aa3309984f8620b98d87e47492599ee20739e8eb54fbf | 2023-04-25 |
### Installed apt packages
| Name | Version |
| ---------------------- | ----------------------------------- |
| acl | 2.3.1-1 |
| aria2 | 1.36.0-1 |
| autoconf | 2.71-2 |
| automake | 1:1.16.5-1.3 |
| binutils | 2.38-4ubuntu2.1 |
| bison | 2:3.8.2+dfsg-1build1 |
| brotli | 1.0.9-2build6 |
| build-essential | 12.9ubuntu3 |
| bzip2 | 1.0.8-5build1 |
| coreutils | 8.32-4.1ubuntu1 |
| curl | 7.81.0-1ubuntu1.10 |
| dbus | 1.12.20-2ubuntu4.1 |
| dnsutils | 1:9.18.12-0ubuntu0.22.04.1 |
| dpkg | 1.21.1ubuntu2.1 |
| fakeroot | 1.28-1ubuntu1 |
| file | 1:5.41-3 |
| flex | 2.6.4-8build2 |
| fonts-noto-color-emoji | 2.038-0ubuntu1 |
| ftp | 20210827-4build1 |
| gnupg2 | 2.2.27-3ubuntu2.1 |
| haveged | 1.9.14-1ubuntu1 |
| imagemagick | 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.3 |
| iproute2 | 5.15.0-1ubuntu2 |
| iputils-ping | 3:20211215-1 |
| jq | 1.6-2.1ubuntu3 |
| lib32z1 | 1:1.2.11.dfsg-2ubuntu9.2 |
| libc++-dev | 1:14.0-55\~exp2 |
| libc++abi-dev | 1:14.0-55\~exp2 |
| libcurl4 | 7.81.0-1ubuntu1.10 |
| libgbm-dev | 22.2.5-0ubuntu0.1\~22.04.1 |
| libgconf-2-4 | 3.2.6-7ubuntu2 |
| libgsl-dev | 2.7.1+dfsg-3 |
| libgtk-3-0 | 3.24.33-1ubuntu2 |
| libmagic-dev | 1:5.41-3 |
| libmagickcore-dev | 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.3 |
| libmagickwand-dev | 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.3 |
| libsecret-1-dev | 0.20.5-2 |
| libsqlite3-dev | 3.37.2-2ubuntu0.1 |
| libssl-dev | 3.0.2-0ubuntu1.9 |
| libtool | 2.4.6-15build2 |
| libunwind8 | 1.3.2-2build2 |
| libxkbfile-dev | 1:1.1.0-1build3 |
| libxss1 | 1:1.2.3-1build2 |
| libyaml-dev | 0.2.2-1build2 |
| locales | 2.35-0ubuntu3.1 |
| lz4 | 1.9.3-2build2 |
| m4 | 1.4.18-5ubuntu2 |
| mediainfo | 22.03-1 |
| mercurial | 6.1.1-1ubuntu1 |
| net-tools | 1.60+git20181103.0eebece-1ubuntu5 |
| netcat | 1.218-4ubuntu1 |
| openssh-client | 1:8.9p1-3ubuntu0.1 |
| p7zip-full | 16.02+dfsg-8 |
| p7zip-rar | 16.02-3build1 |
| parallel | 20210822+ds-2 |
| pass | 1.7.4-5 |
| patchelf | 0.14.3-1 |
| pkg-config | 0.29.2-1ubuntu3 |
| pollinate | 4.33-3ubuntu2 |
| python-is-python3 | 3.9.2-2 |
| rpm | 4.17.0+dfsg1-4build1 |
| rsync | 3.2.7-0ubuntu0.22.04.2 |
| shellcheck | 0.8.0-2 |
| sphinxsearch | 2.2.11-8 |
| sqlite3 | 3.37.2-2ubuntu0.1 |
| ssh | 1:8.9p1-3ubuntu0.1 |
| sshpass | 1.09-1 |
| subversion | 1.14.1-3ubuntu0.22.04.1 |
| sudo | 1.9.9-1ubuntu2.4 |
| swig | 4.0.2-1ubuntu1 |
| tar | 1.34+dfsg-1ubuntu0.1.22.04.1 |
| telnet | 0.17-44build1 |
| texinfo | 6.8-4build1 |
| time | 1.9-0.1build2 |
| tk | 8.6.11+1build2 |
| tzdata | 2023c-0ubuntu0.22.04.1 |
| unzip | 6.0-26ubuntu3.1 |
| upx | 3.96-3 |
| wget | 1.21.2-2ubuntu1 |
| xorriso | 1.5.4-2 |
| xvfb | 2:21.1.4-2ubuntu1.7\~22.04.1 |
| xz-utils | 5.2.5-2ubuntu1 |
| zip | 3.0-12build2 |
| zsync | 0.6.2-3ubuntu1 |
@@ -1,2 +1,2 @@
# Name of pool supported by this image
POOL_NAME="Ubuntu 2204"
POOL_NAME="Ubuntu 1804"
@@ -1,5 +0,0 @@
#!/bin/bash
# Enable user session on boot, not on login
UserId=$(cut -d: -f3 /etc/passwd | tail -1)
loginctl enable-linger $UserId
@@ -166,9 +166,9 @@ function Get-AndroidNDKVersions {
}
function Build-AndroidEnvironmentTable {
$androidVersions = Get-Item env:ANDROID_*
$androidVersions = Get-Item env:ANDROID_*
$shouldResolveLink = 'ANDROID_NDK', 'ANDROID_NDK_HOME', 'ANDROID_NDK_ROOT', 'ANDROID_NDK_LATEST_HOME'
$shouldResolveLink = 'ANDROID_NDK_PATH', 'ANDROID_NDK_HOME', 'ANDROID_NDK_ROOT', 'ANDROID_NDK_LATEST_HOME'
return $androidVersions | Sort-Object -Property Name | ForEach-Object {
[PSCustomObject] @{
"Name" = $_.Name
@@ -1,61 +1,52 @@
function Get-ChromeVersion {
$googleChromeVersion = google-chrome --version | Take-OutputPart -Part 2
return $googleChromeVersion
return "Google Chrome $googleChromeVersion"
}
function Get-ChromeDriverVersion {
$chromeDriverVersion = chromedriver --version | Take-OutputPart -Part 1
return $chromeDriverVersion
return "ChromeDriver $chromeDriverVersion"
}
function Get-FirefoxVersion {
$firefoxVersion = $(firefox --version) | Take-OutputPart -Part 2
$firefoxVersion = firefox --version
return $firefoxVersion
}
function Get-GeckodriverVersion {
$geckodriverVersion = geckodriver --version | Select-Object -First 1 | Take-OutputPart -Part 1
return $geckodriverVersion
return "Geckodriver $geckodriverVersion"
}
function Get-ChromiumVersion {
$chromiumVersion = chromium-browser --version | Take-OutputPart -Part 1
$chromiumVersion = chromium-browser --version | Take-OutputPart -Part 0,1
return $chromiumVersion
}
function Get-EdgeVersion {
$edgeVersion = (microsoft-edge --version).Trim() | Take-OutputPart -Part 2
return $edgeVersion
}
function Get-EdgeDriverVersion {
$edgeDriverVersion = msedgedriver --version | Take-OutputPart -Part 3
return $edgeDriverVersion
}
function Get-SeleniumVersion {
$seleniumBinaryName = Get-ToolsetValue "selenium.binary_name"
$fullSeleniumVersion = (Get-ChildItem "/usr/share/java/${seleniumBinaryName}-*").Name -replace "${seleniumBinaryName}-"
return $fullSeleniumVersion
return "Selenium server $fullSeleniumVersion"
}
function Build-BrowserWebdriversEnvironmentTable {
return @(
[PSCustomObject] @{
@{
"Name" = "CHROMEWEBDRIVER"
"Value" = $env:CHROMEWEBDRIVER
},
[PSCustomObject] @{
"Name" = "EDGEWEBDRIVER"
"Value" = $env:EDGEWEBDRIVER
},
[PSCustomObject] @{
@{
"Name" = "GECKOWEBDRIVER"
"Value" = $env:GECKOWEBDRIVER
},
[PSCustomObject] @{
@{
"Name" = "SELENIUM_JAR_PATH"
"Value" = $env:SELENIUM_JAR_PATH
}
)
) | ForEach-Object {
[PSCustomObject] @{
"Name" = $_.Name
"Value" = $_.Value
}
}
}
@@ -27,3 +27,37 @@ function Get-ToolcacheGoVersions {
$toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "go"
return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ }
}
function Build-GoEnvironmentTable {
return Get-CachedToolInstances -Name "go" -VersionCommand "version" | ForEach-Object {
$Version = [System.Version]($_.Version -Split(" "))[0]
$Name = "GOROOT_$($Version.major)_$($Version.minor)_X64"
$Value = (Get-Item env:\$Name).Value
[PSCustomObject] @{
"Name" = $Name
"Value" = (Get-Item env:\$Name).Value
"Architecture" = $_. Architecture
}
}
}
function Build-CachedToolsSection {
$output = ""
$output += New-MDHeader "Go" -Level 4
$output += New-MDList -Lines (Get-ToolcacheGoVersions) -Style Unordered
$output += New-MDHeader "Node.js" -Level 4
$output += New-MDList -Lines (Get-ToolcacheNodeVersions) -Style Unordered
$output += New-MDHeader "PyPy" -Level 4
$output += New-MDList -Lines (Get-ToolcachePyPyVersions) -Style Unordered
$output += New-MDHeader "Python" -Level 4
$output += New-MDList -Lines (Get-ToolcachePythonVersions) -Style Unordered
$output += New-MDHeader "Ruby" -Level 4
$output += New-MDList -Lines (Get-ToolcacheRubyVersions) -Style Unordered
return $output
}
@@ -1,11 +1,6 @@
function Get-BashVersion {
$version = bash -c 'echo ${BASH_VERSION}'
return $version
}
function Get-DashVersion {
$version = dpkg-query -W -f '${Version}' dash
return $version
return "Bash $version"
}
function Get-CPPVersions {
@@ -13,7 +8,7 @@ function Get-CPPVersions {
$cppVersions = $result.Output | Where-Object { $_ -match "g\+\+-\d+"} | ForEach-Object {
& $_.Split("/")[0] --version | Select-Object -First 1 | Take-OutputPart -Part 3
} | Sort-Object {[Version]$_}
return $cppVersions
return "GNU C++ " + ($cppVersions -Join ", ")
}
function Get-FortranVersions {
@@ -22,212 +17,227 @@ function Get-FortranVersions {
$_ -match "now (?<version>\d+\.\d+\.\d+)-" | Out-Null
$Matches.version
} | Sort-Object {[Version]$_}
return $fortranVersions
return "GNU Fortran " + ($fortranVersions -Join ", ")
}
function Get-ClangToolVersions {
param (
[Parameter(Mandatory = $true)]
[string] $ToolName,
[string] $VersionLineMatcher = "${ToolName} version",
[string] $VersionPattern = "\d+\.\d+\.\d+)-"
)
$result = Get-CommandResult "apt list --installed" -Multiline
$toolVersions = $result.Output | Where-Object { $_ -match "^${ToolName}-\d+"} | ForEach-Object {
$clangCommand = ($_ -Split "/")[0]
Invoke-Expression "$clangCommand --version" | Where-Object { $_ -match "${VersionLineMatcher}" } | ForEach-Object {
$_ -match "${VersionLineMatcher} (?<version>${VersionPattern}" | Out-Null
Invoke-Expression "$clangCommand --version" | Where-Object { $_ -match "${ToolName} version" } | ForEach-Object {
$_ -match "${ToolName} version (?<version>${VersionPattern}" | Out-Null
$Matches.version
}
} | Sort-Object {[Version]$_}
return $toolVersions
return $toolVersions -Join ", "
}
function Get-ClangVersions {
$clangVersions = Get-ClangToolVersions -ToolName "clang"
return "Clang " + $clangVersions
}
function Get-ClangTidyVersions {
return Get-ClangToolVersions -ToolName "clang-tidy" -VersionLineMatcher "LLVM version" -VersionPattern "\d+\.\d+\.\d+)"
function Get-ClangFormatVersions {
$clangFormatVersions = Get-ClangToolVersions -ToolName "clang-format"
return "Clang-format " + $clangFormatVersions
}
function Get-ErlangVersion {
$erlangVersion = (erl -eval '{ok, Version} = file:read_file(filename:join([code:root_dir(), "releases", erlang:system_info(otp_release), ''OTP_VERSION''])), io:fwrite(Version), halt().' -noshell)
$shellVersion = (erl -eval 'erlang:display(erlang:system_info(version)), halt().' -noshell).Trim('"')
return "$erlangVersion (Eshell $shellVersion)"
return "Erlang $erlangVersion (Eshell $shellVersion)"
}
function Get-ErlangRebar3Version {
$result = Get-CommandResult "rebar3 --version"
$result.Output -match "rebar (?<version>(\d+.){2}\d+)" | Out-Null
return $Matches.version
$rebarVersion = $Matches.version
return "Erlang rebar3 $rebarVersion"
}
function Get-MonoVersion {
$monoVersion = mono --version | Out-String | Take-OutputPart -Part 4
return $monoVersion
$aptSourceRepo = Get-AptSourceRepository -PackageName "mono"
return "Mono $monoVersion (apt source repository: $aptSourceRepo)"
}
function Get-MsbuildVersion {
$msbuildVersion = msbuild -version | Select-Object -Last 1
$monoVersion = Get-MonoVersion
return "$msbuildVersion (Mono $monoVersion)"
}
function Get-NuGetVersion {
$nugetVersion = nuget help | Select-Object -First 1 | Take-OutputPart -Part 2
return $nugetVersion
$result = Select-String -Path (Get-Command msbuild).Source -Pattern "msbuild"
$result -match "(?<path>\/\S*\.dll)" | Out-Null
$msbuildPath = $Matches.path
return "MSBuild $msbuildVersion (from $msbuildPath)"
}
function Get-NodeVersion {
$nodeVersion = $(node --version).Substring(1)
return $nodeVersion
return "Node $nodeVersion"
}
function Get-OpensslVersion {
return $(dpkg-query -W -f '${Version}' openssl)
return $(openssl version)
}
function Get-PerlVersion {
$version = $(perl -e 'print substr($^V,1)')
return $version
return "Perl $version"
}
function Get-PythonVersion {
$result = Get-CommandResult "python --version"
$version = $result.Output | Take-OutputPart -Part 1
return $version
return "Python $version"
}
function Get-Python3Version {
$result = Get-CommandResult "python3 --version"
$version = $result.Output | Take-OutputPart -Part 1
return $version
return "Python3 $version"
}
function Get-PowershellVersion {
return $(pwsh --version) | Take-OutputPart -Part 1
return $(pwsh --version)
}
function Get-RubyVersion {
$rubyVersion = ruby --version | Out-String | Take-OutputPart -Part 1
return $rubyVersion
return "Ruby $rubyVersion"
}
function Get-SwiftVersion {
$swiftVersion = swift --version | Out-String | Take-OutputPart -Part 2
return $swiftVersion
return "Swift $swiftVersion"
}
function Get-KotlinVersion {
$kotlinVersion = kotlin -version | Out-String | Take-OutputPart -Part 2
return $kotlinVersion
return "Kotlin $kotlinVersion"
}
function Get-JuliaVersion {
$juliaVersion = julia --version | Take-OutputPart -Part 2
return $juliaVersion
return "Julia $juliaVersion"
}
function Get-LernaVersion {
$version = lerna -v
return $version
return "Lerna $version"
}
function Get-HomebrewVersion {
$result = Get-CommandResult "/home/linuxbrew/.linuxbrew/bin/brew -v"
$result = Get-CommandResult "brew -v"
$result.Output -match "Homebrew (?<version>\d+\.\d+\.\d+)" | Out-Null
return $Matches.version
$version = $Matches.version
return "Homebrew $version"
}
function Get-CpanVersion {
$result = Get-CommandResult "cpan --version" -ExpectExitCode @(25, 255)
$result.Output -match "version (?<version>\d+\.\d+) " | Out-Null
return $Matches.version
$cpanVersion = $Matches.version
return "cpan $cpanVersion"
}
function Get-GemVersion {
$result = Get-CommandResult "gem --version"
$result.Output -match "(?<version>\d+\.\d+\.\d+)" | Out-Null
return $Matches.version
$gemVersion = $Matches.version
return "RubyGems $gemVersion"
}
function Get-MinicondaVersion {
$condaVersion = conda --version | Take-OutputPart -Part 1
return $condaVersion
$condaVersion = conda --version
return "Mini$condaVersion"
}
function Get-HelmVersion {
$(helm version) -match 'Version:"v(?<version>\d+\.\d+\.\d+)"' | Out-Null
return $Matches.version
$helmVersion = $Matches.version
return "Helm $helmVersion"
}
function Get-NpmVersion {
$npmVersion = npm --version
return $npmVersion
return "Npm $npmVersion"
}
function Get-YarnVersion {
$yarnVersion = yarn --version
return $yarnVersion
return "Yarn $yarnVersion"
}
function Get-ParcelVersion {
$parcelVersion = parcel --version
return $parcelVersion
return "Parcel $parcelVersion"
}
function Get-PipVersion {
$result = Get-CommandResult "pip --version"
$result.Output -match "pip (?<version>\d+\.\d+\.\d+)" | Out-Null
return $Matches.version
$pipVersion = $Matches.version
return "Pip $pipVersion"
}
function Get-Pip3Version {
$result = Get-CommandResult "pip3 --version"
$result.Output -match "pip (?<version>\d+\.\d+\.\d+)" | Out-Null
return $Matches.version
$pipVersion = $Matches.version
return "Pip3 $pipVersion"
}
function Get-VcpkgVersion {
$result = Get-CommandResult "vcpkg version"
$result.Output -match "version (?<version>\d+\.\d+\.\d+)" | Out-Null
$vcpkgVersion = $Matches.version
$commitId = git -C "/usr/local/share/vcpkg" rev-parse --short HEAD
return "(build from commit $commitId)"
return "Vcpkg $vcpkgVersion (build from master \<$commitId>)"
}
function Get-AntVersion {
$result = ant -version | Out-String
$result -match "version (?<version>\d+\.\d+\.\d+)" | Out-Null
return $Matches.version
$antVersion = $Matches.version
return "Ant $antVersion"
}
function Get-GradleVersion {
$gradleVersion = (gradle -v) -match "^Gradle \d" | Take-OutputPart -Part 1
return $gradleVersion
return "Gradle $gradleVersion"
}
function Get-MavenVersion {
$result = mvn -version | Out-String
$result -match "Apache Maven (?<version>\d+\.\d+\.\d+)" | Out-Null
return $Matches.version
$mavenVersion = $Matches.version
return "Maven $mavenVersion"
}
function Get-SbtVersion {
$result = Get-CommandResult "sbt -version"
$result.Output -match "sbt script version: (?<version>\d+\.\d+\.\d+)" | Out-Null
return $Matches.version
$sbtVersion = $Matches.version
return "Sbt $sbtVersion"
}
function Get-PHPVersions {
$result = Get-CommandResult "apt list --installed" -Multiline
return $result.Output | Where-Object { $_ -match "^php\d+\.\d+/"} | ForEach-Object {
$_ -match "now (\d+:)?(?<version>\d+\.\d+\.\d+)" | Out-Null
$_ -match "now (?<version>\d+\.\d+\.\d+)-" | Out-Null
$Matches.version
}
}
function Get-ComposerVersion {
$composerVersion = (composer --version) -replace " version" | Take-OutputPart -Part 1
return $composerVersion
$(composer --version) -match "Composer version (?<version>\d+\.\d+\.\d+)\s" | Out-Null
return $Matches.version
}
function Get-PHPUnitVersion {
@@ -235,24 +245,60 @@ function Get-PHPUnitVersion {
return $Matches.version
}
function Build-PHPTable {
$php = @{
"Tool" = "PHP"
"Version" = "$(Get-PHPVersions -Join '<br>')"
}
$composer = @{
"Tool" = "Composer"
"Version" = Get-ComposerVersion
}
$phpunit = @{
"Tool" = "PHPUnit"
"Version" = Get-PHPUnitVersion
}
return @($php, $composer, $phpunit) | ForEach-Object {
[PSCustomObject] @{
"Tool" = $_.Tool
"Version" = $_.Version
}
}
}
function Build-PHPSection {
$output = ""
$output += New-MDHeader "PHP" -Level 3
$output += Build-PHPTable | New-MDTable
$output += New-MDCode -Lines @(
"Both Xdebug and PCOV extensions are installed, but only Xdebug is enabled."
)
return $output
}
function Get-GHCVersion {
$(ghc --version) -match "version (?<version>\d+\.\d+\.\d+)" | Out-Null
return $Matches.version
$ghcVersion = $Matches.version
return "GHC $ghcVersion"
}
function Get-GHCupVersion {
$(ghcup --version) -match "version (?<version>\d+(\.\d+){2,})" | Out-Null
return $Matches.version
$(ghcup --version) -match "version v(?<version>\d+(\.\d+){2,})" | Out-Null
$ghcVersion = $Matches.version
return "GHCup $ghcVersion"
}
function Get-CabalVersion {
$(cabal --version | Out-String) -match "cabal-install version (?<version>\d+\.\d+\.\d+\.\d+)" | Out-Null
return $Matches.version
$cabalVersion = $Matches.version
return "Cabal $cabalVersion"
}
function Get-StackVersion {
$(stack --version | Out-String) -match "Version (?<version>\d+\.\d+\.\d+)" | Out-Null
return $Matches.version
$stackVersion = $Matches.version
return "Stack $stackVersion"
}
function Get-AzModuleVersions {
@@ -265,40 +311,38 @@ function Get-AzModuleVersions {
}
function Get-PowerShellModules {
[Array] $result = @()
$modules = (Get-ToolsetContent).powershellModules.name
[Array] $azureInstalledModules = Get-ChildItem -Path "/usr/share/az_*" -Directory | ForEach-Object { $_.Name.Split("_")[1] }
if ($azureInstalledModules.Count -gt 0) {
$result += [ToolVersionsListNode]::new("Az", $azureInstalledModules, "^\d+\.\d+", "Inline")
$psModules = Get-Module -Name $modules -ListAvailable | Sort-Object Name | Group-Object Name
$psModules | ForEach-Object {
$moduleName = $_.Name
$moduleVersions = ($_.group.Version | Sort-Object -Unique) -join '<br>'
[PSCustomObject]@{
Module = $moduleName
Version = $moduleVersions
}
}
[Array] $azureCachedModules = Get-ChildItem /usr/share/az_*.zip -File | ForEach-Object { $_.Name.Split("_")[1] }
if ($azureCachedModules.Count -gt 0) {
$result += [ToolVersionsListNode]::new("Az (Cached)", $azureCachedModules, "^\d+\.\d+", "Inline")
}
(Get-ToolsetContent).powershellModules.name | ForEach-Object {
$moduleName = $_
$moduleVersions = Get-Module -Name $moduleName -ListAvailable | Select-Object -ExpandProperty Version | Sort-Object -Unique
$result += [ToolVersionsListNode]::new($moduleName, $moduleVersions, "^\d+", "Inline")
}
return $result
}
function Get-DotNetCoreSdkVersions {
$dotNetCoreSdkVersion = dotnet --list-sdks list | ForEach-Object { $_ | Take-OutputPart -Part 0 }
$unsortedDotNetCoreSdkVersion = dotnet --list-sdks list | ForEach-Object { $_ | Take-OutputPart -Part 0 }
$dotNetCoreSdkVersion = $unsortedDotNetCoreSdkVersion -join " "
return $dotNetCoreSdkVersion
}
function Get-DotnetTools {
$env:PATH = "/etc/skel/.dotnet/tools:$($env:PATH)"
$dotnetTools = (Get-ToolsetContent).dotnet.tools
return $dotnetTools | ForEach-Object {
$version = Invoke-Expression $_.getversion
return [ToolVersionNode]::new($_.name, $version)
$toolsList = @()
ForEach ($dotnetTool in $dotnetTools) {
$toolsList += $dotnetTool.name + " " + (Invoke-Expression $dotnetTool.getversion)
}
return $toolsList
}
function Get-CachedDockerImages {
@@ -341,24 +385,39 @@ function Get-AptPackages {
function Get-PipxVersion {
$result = (Get-CommandResult "pipx --version").Output
$result -match "(?<version>\d+\.\d+\.\d+\.?\d*)" | Out-Null
return $Matches.Version
$pipxVersion = $Matches.Version
return "Pipx $pipxVersion"
}
function Get-GraalVMVersion {
$version = & "$env:GRAALVM_11_ROOT\bin\java" --version | Select-String -Pattern "GraalVM" | Take-OutputPart -Part 5,6
return $version
}
function Build-GraalVMTable {
$version = Get-GraalVMVersion
$envVariables = "GRAALVM_11_ROOT"
return [PSCustomObject] @{
"Version" = $version
"Environment variables" = $envVariables
}
}
function Build-PackageManagementEnvironmentTable {
return @(
[PSCustomObject] @{
@{
"Name" = "CONDA"
"Value" = $env:CONDA
},
[PSCustomObject] @{
@{
"Name" = "VCPKG_INSTALLATION_ROOT"
"Value" = $env:VCPKG_INSTALLATION_ROOT
}
)
) | ForEach-Object {
[PSCustomObject] @{
"Name" = $_.Name
"Value" = $_.Value
}
}
}
function Get-SystemdVersion {
$matches = [regex]::Matches((systemctl --version | head -n 1), "\((.*?)\)")
$result = foreach ($match in $matches) {$match.Groups[1].Value}
return $result
}
@@ -1,61 +1,61 @@
function Get-PostgreSqlVersion {
$postgreSQLVersion = psql --version | Take-OutputPart -Part 2
return $postgreSQLVersion
$aptSourceRepo = Get-AptSourceRepository -PackageName "postgresql"
return "PostgreSQL $postgreSQLVersion (apt source repository: $aptSourceRepo)"
}
function Get-MongoDbVersion {
$mongoDBVersion = mongod --version | Select-Object -First 1 | Take-OutputPart -Part 2 -Delimiter "v"
return $mongoDBVersion
$aptSourceRepo = Get-AptSourceRepository -PackageName "mongodb"
return "MongoDB $mongoDBVersion (apt source repository: $aptSourceRepo)"
}
function Get-SqliteVersion {
$sqliteVersion = sqlite3 --version | Take-OutputPart -Part 0
return $sqliteVersion
return "sqlite3 $sqliteVersion"
}
function Get-MySQLVersion {
$mySQLVersion = mysqld --version | Take-OutputPart -Part 2
return $mySQLVersion
if (-not (Test-IsUbuntu20)) {
$mySQLVersion = $mySQLVersion | Take-OutputPart -Part 0 -Delimiter "-"
}
return "MySQL $mySQLVersion"
}
function Get-SQLCmdVersion {
$sqlcmdVersion = sqlcmd -? | Select-String -Pattern "Version" | Take-OutputPart -Part 1
return $sqlcmdVersion
return "sqlcmd $sqlcmdVersion"
}
function Get-SqlPackageVersion {
$sqlPackageVersion = sqlpackage /version
return $sqlPackageVersion
}
function Build-PostgreSqlSection {
$node = [HeaderNode]::new("PostgreSQL")
$node.AddToolVersion("PostgreSQL", $(Get-PostgreSqlVersion))
$node.AddNote(@(
"User: postgres",
"PostgreSQL service is disabled by default.",
"Use the following command as a part of your job to start the service: 'sudo systemctl start postgresql.service'"
) -join "`n")
return $node
return "SqlPackage $sqlPackageVersion"
}
function Build-MySQLSection {
$node = [HeaderNode]::new("MySQL")
$node.AddToolVersion("MySQL", $(Get-MySQLVersion))
$node.AddNote(@(
"User: root",
"Password: root",
"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'"
) -join "`n")
$output = ""
return $node
$output += New-MDHeader "MySQL" -Level 4
$output += New-MDList -Style Unordered -Lines @(
(Get-MySQLVersion ),
"MySQL Server (user:root password:root)"
)
$output += 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'"
)
return $output
}
function Build-MSSQLToolsSection {
$node = [HeaderNode]::new("MS SQL")
$node.AddToolVersion("sqlcmd", $(Get-SQLCmdVersion))
$node.AddToolVersion("SqlPackage", $(Get-SqlPackageVersion))
return $node
$output = ""
$output += New-MDHeader "MS SQL Server Client Tools" -Level 4
$output += New-MDList -Style Unordered -Lines @(
(Get-SQLCmdVersion),
(Get-SqlPackageVersion)
)
return $output
}
@@ -1,6 +1,3 @@
using module ./software-report-base/SoftwareReport.psm1
using module ./software-report-base/SoftwareReport.Nodes.psm1
param (
[Parameter(Mandatory)][string]
$OutputDirectory
@@ -10,6 +7,7 @@ $global:ErrorActionPreference = "Stop"
$global:ErrorView = "NormalView"
Set-StrictMode -Version Latest
Import-Module MarkdownPS
Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Android.psm1") -DisableNameChecking
Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Browsers.psm1") -DisableNameChecking
Import-Module (Join-Path $PSScriptRoot "SoftwareReport.CachedTools.psm1") -DisableNameChecking
@@ -25,219 +23,261 @@ Import-Module (Join-Path $PSScriptRoot "SoftwareReport.WebServers.psm1") -Disabl
# Restore file owner in user profile
Restore-UserOwner
# Software report
$softwareReport = [SoftwareReport]::new("Ubuntu $(Get-OSVersionShort)")
$softwareReport.Root.AddToolVersion("OS Version:", $(Get-OSVersionFull))
$softwareReport.Root.AddToolVersion("Kernel Version:", $(Get-KernelVersion))
$softwareReport.Root.AddToolVersion("Image Version:", $env:IMAGE_VERSION)
$softwareReport.Root.AddToolVersion("Systemd version:", $(Get-SystemdVersion))
$markdown = ""
$installedSoftware = $softwareReport.Root.AddHeader("Installed Software")
$OSName = Get-OSName
$markdown += New-MDHeader "$OSName" -Level 1
$kernelVersion = Get-KernelVersion
$markdown += New-MDList -Style Unordered -Lines @(
"$kernelVersion"
"Image Version: $env:IMAGE_VERSION"
)
$markdown += New-MDHeader "Installed Software" -Level 2
$markdown += New-MDHeader "Language and Runtime" -Level 3
$runtimesList = @(
(Get-BashVersion),
(Get-CPPVersions),
(Get-FortranVersions),
(Get-ErlangVersion),
(Get-ErlangRebar3Version),
(Get-MonoVersion),
(Get-MsbuildVersion),
(Get-NodeVersion),
(Get-PerlVersion),
(Get-PythonVersion),
(Get-Python3Version),
(Get-RubyVersion),
(Get-SwiftVersion),
(Get-JuliaVersion),
(Get-KotlinVersion),
(Get-ClangVersions),
(Get-ClangFormatVersions)
)
$markdown += New-MDList -Style Unordered -Lines ($runtimesList | Sort-Object)
$markdown += New-MDHeader "Package Management" -Level 3
$packageManagementList = @(
(Get-HomebrewVersion),
(Get-CpanVersion),
(Get-GemVersion),
(Get-MinicondaVersion),
(Get-HelmVersion),
(Get-NpmVersion),
(Get-YarnVersion),
(Get-PipxVersion),
(Get-PipVersion),
(Get-Pip3Version),
(Get-VcpkgVersion)
)
$markdown += New-MDList -Style Unordered -Lines ($packageManagementList | Sort-Object)
$markdown += New-MDHeader "Environment variables" -Level 4
$markdown += Build-PackageManagementEnvironmentTable | New-MDTable
$markdown += New-MDNewLine
$markdown += New-MDHeader "Project Management" -Level 3
$projectManagementList = @(
(Get-AntVersion),
(Get-GradleVersion),
(Get-MavenVersion),
(Get-SbtVersion)
)
# Language and Runtime
$languageAndRuntime = $installedSoftware.AddHeader("Language and Runtime")
$languageAndRuntime.AddToolVersion("Bash", $(Get-BashVersion))
$languageAndRuntime.AddToolVersionsListInline("Clang", $(Get-ClangToolVersions -ToolName "clang"), "^\d+")
$languageAndRuntime.AddToolVersionsListInline("Clang-format", $(Get-ClangToolVersions -ToolName "clang-format"), "^\d+")
$languageAndRuntime.AddToolVersionsListInline("Clang-tidy", $(Get-ClangTidyVersions), "^\d+")
$languageAndRuntime.AddToolVersion("Dash", $(Get-DashVersion))
if (Test-IsUbuntu20) {
$languageAndRuntime.AddToolVersion("Erlang", $(Get-ErlangVersion))
$languageAndRuntime.AddToolVersion("Erlang rebar3", $(Get-ErlangRebar3Version))
$projectManagementList += @(
(Get-LernaVersion)
)
}
$languageAndRuntime.AddToolVersionsListInline("GNU C++", $(Get-CPPVersions), "^\d+")
$languageAndRuntime.AddToolVersionsListInline("GNU Fortran", $(Get-FortranVersions), "^\d+")
$languageAndRuntime.AddToolVersion("Julia", $(Get-JuliaVersion))
$languageAndRuntime.AddToolVersion("Kotlin", $(Get-KotlinVersion))
$languageAndRuntime.AddToolVersion("Mono", $(Get-MonoVersion))
$languageAndRuntime.AddToolVersion("MSBuild", $(Get-MsbuildVersion))
$languageAndRuntime.AddToolVersion("Node.js", $(Get-NodeVersion))
$languageAndRuntime.AddToolVersion("Perl", $(Get-PerlVersion))
$languageAndRuntime.AddToolVersion("Python", $(Get-PythonVersion))
$languageAndRuntime.AddToolVersion("Python3", $(Get-Python3Version))
$languageAndRuntime.AddToolVersion("Ruby", $(Get-RubyVersion))
$languageAndRuntime.AddToolVersion("Swift", $(Get-SwiftVersion))
$markdown += New-MDList -Style Unordered -Lines ($projectManagementList | Sort-Object)
# Package Management
$packageManagement = $installedSoftware.AddHeader("Package Management")
$packageManagement.AddToolVersion("cpan", $(Get-CpanVersion))
$packageManagement.AddToolVersion("Helm", $(Get-HelmVersion))
$packageManagement.AddToolVersion("Homebrew", $(Get-HomebrewVersion))
$packageManagement.AddToolVersion("Miniconda", $(Get-MinicondaVersion))
$packageManagement.AddToolVersion("Npm", $(Get-NpmVersion))
$packageManagement.AddToolVersion("NuGet", $(Get-NuGetVersion))
$packageManagement.AddToolVersion("Pip", $(Get-PipVersion))
$packageManagement.AddToolVersion("Pip3", $(Get-Pip3Version))
$packageManagement.AddToolVersion("Pipx", $(Get-PipxVersion))
$packageManagement.AddToolVersion("RubyGems", $(Get-GemVersion))
$packageManagement.AddToolVersion("Vcpkg", $(Get-VcpkgVersion))
$packageManagement.AddToolVersion("Yarn", $(Get-YarnVersion))
$packageManagement.AddHeader("Environment variables").AddTable($(Build-PackageManagementEnvironmentTable))
$packageManagement.AddHeader("Homebrew note").AddNote(@'
Location: /home/linuxbrew
Note: Homebrew is pre-installed on image but not added to PATH.
run the eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" command
to accomplish this.
'@)
$markdown += New-MDHeader "Tools" -Level 3
$toolsList = @(
(Get-AnsibleVersion),
(Get-AptFastVersion),
(Get-AzCopyVersion),
(Get-BazelVersion),
(Get-BazeliskVersion),
(Get-BicepVersion),
(Get-BuildahVersion),
(Get-CodeQLBundleVersion),
(Get-CMakeVersion),
(Get-DockerMobyClientVersion),
(Get-DockerMobyServerVersion),
(Get-DockerComposeV1Version),
(Get-DockerComposeV2Version),
(Get-DockerBuildxVersion),
(Get-GitVersion),
(Get-GitLFSVersion),
(Get-GitFTPVersion),
(Get-HavegedVersion),
(Get-HerokuVersion),
(Get-HHVMVersion),
(Get-SVNVersion),
(Get-JqVersion),
(Get-YqVersion),
(Get-KindVersion),
(Get-KubectlVersion),
(Get-KustomizeVersion),
(Get-LeiningenVersion),
(Get-MediainfoVersion),
(Get-HGVersion),
(Get-MinikubeVersion),
(Get-NewmanVersion),
(Get-NVersion),
(Get-NvmVersion),
(Get-OpensslVersion),
(Get-PackerVersion),
(Get-ParcelVersion),
(Get-PhantomJSVersion),
(Get-PodManVersion),
(Get-PulumiVersion),
(Get-RVersion),
(Get-SkopeoVersion),
(Get-SphinxVersion),
(Get-TerraformVersion),
(Get-YamllintVersion),
(Get-ZstdVersion)
)
# Project Management
$projectManagement = $installedSoftware.AddHeader("Project Management")
if (Test-IsUbuntu20) {
$projectManagement.AddToolVersion("Ant", $(Get-AntVersion))
$projectManagement.AddToolVersion("Gradle", $(Get-GradleVersion))
$toolsList += (Get-FastlaneVersion)
}
if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) {
$projectManagement.AddToolVersion("Lerna", $(Get-LernaVersion))
}
$projectManagement.AddToolVersion("Maven", $(Get-MavenVersion))
$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-GoogleCloudSDKVersion),
(Get-HubCliVersion),
(Get-NetlifyCliVersion),
(Get-OCCliVersion),
(Get-ORASCliVersion),
(Get-VerselCliversion)
) | Sort-Object
)
$markdown += New-MDHeader "Java" -Level 3
$markdown += Get-JavaVersions | New-MDTable
$markdown += New-MDNewLine
if (Test-IsUbuntu20) {
$projectManagement.AddToolVersion("Sbt", $(Get-SbtVersion))
$markdown += New-MDHeader "GraalVM" -Level 3
$markdown += Build-GraalVMTable | New-MDTable
$markdown += New-MDNewLine
}
# Tools
$tools = $installedSoftware.AddHeader("Tools")
$tools.AddToolVersion("Ansible", $(Get-AnsibleVersion))
$tools.AddToolVersion("apt-fast", $(Get-AptFastVersion))
$tools.AddToolVersion("AzCopy", $(Get-AzCopyVersion))
$tools.AddToolVersion("Bazel", $(Get-BazelVersion))
$tools.AddToolVersion("Bazelisk", $(Get-BazeliskVersion))
$tools.AddToolVersion("Bicep", $(Get-BicepVersion))
$tools.AddToolVersion("Buildah", $(Get-BuildahVersion))
$tools.AddToolVersion("CMake", $(Get-CMakeVersion))
$tools.AddToolVersion("CodeQL Action Bundles", $(Get-CodeQLBundleVersions))
$tools.AddToolVersion("Docker Amazon ECR Credential Helper", $(Get-DockerAmazonECRCredHelperVersion))
$tools.AddToolVersion("Docker Compose v1", $(Get-DockerComposeV1Version))
$tools.AddToolVersion("Docker Compose v2", $(Get-DockerComposeV2Version))
$tools.AddToolVersion("Docker-Buildx", $(Get-DockerBuildxVersion))
$tools.AddToolVersion("Docker-Moby Client", $(Get-DockerMobyClientVersion))
$tools.AddToolVersion("Docker-Moby Server", $(Get-DockerMobyServerVersion))
if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) {
$tools.AddToolVersion("Fastlane", $(Get-FastlaneVersion))
}
$tools.AddToolVersion("Git", $(Get-GitVersion))
$tools.AddToolVersion("Git LFS", $(Get-GitLFSVersion))
$tools.AddToolVersion("Git-ftp", $(Get-GitFTPVersion))
$tools.AddToolVersion("Haveged", $(Get-HavegedVersion))
$tools.AddToolVersion("Heroku", $(Get-HerokuVersion))
if (Test-IsUbuntu20) {
$tools.AddToolVersion("HHVM (HipHop VM)", $(Get-HHVMVersion))
}
$tools.AddToolVersion("jq", $(Get-JqVersion))
$tools.AddToolVersion("Kind", $(Get-KindVersion))
$tools.AddToolVersion("Kubectl", $(Get-KubectlVersion))
$tools.AddToolVersion("Kustomize", $(Get-KustomizeVersion))
$tools.AddToolVersion("Leiningen", $(Get-LeiningenVersion))
$tools.AddToolVersion("MediaInfo", $(Get-MediainfoVersion))
$tools.AddToolVersion("Mercurial", $(Get-HGVersion))
$tools.AddToolVersion("Minikube", $(Get-MinikubeVersion))
$tools.AddToolVersion("n", $(Get-NVersion))
$tools.AddToolVersion("Newman", $(Get-NewmanVersion))
$tools.AddToolVersion("nvm", $(Get-NvmVersion))
$tools.AddToolVersion("OpenSSL", $(Get-OpensslVersion))
$tools.AddToolVersion("Packer", $(Get-PackerVersion))
$tools.AddToolVersion("Parcel", $(Get-ParcelVersion))
if (Test-IsUbuntu20) {
$tools.AddToolVersion("PhantomJS", $(Get-PhantomJSVersion))
}
$tools.AddToolVersion("Podman", $(Get-PodManVersion))
$tools.AddToolVersion("Pulumi", $(Get-PulumiVersion))
$tools.AddToolVersion("R", $(Get-RVersion))
$tools.AddToolVersion("Skopeo", $(Get-SkopeoVersion))
$tools.AddToolVersion("Sphinx Open Source Search Server", $(Get-SphinxVersion))
$tools.AddToolVersion("SVN", $(Get-SVNVersion))
$tools.AddToolVersion("Terraform", $(Get-TerraformVersion))
$tools.AddToolVersion("yamllint", $(Get-YamllintVersion))
$tools.AddToolVersion("yq", $(Get-YqVersion))
$tools.AddToolVersion("zstd", $(Get-ZstdVersion))
$markdown += Build-PHPSection
# CLI Tools
$cliTools = $installedSoftware.AddHeader("CLI Tools")
$cliTools.AddToolVersion("Alibaba Cloud CLI", $(Get-AlibabaCloudCliVersion))
$cliTools.AddToolVersion("AWS CLI", $(Get-AWSCliVersion))
$cliTools.AddToolVersion("AWS CLI Session Manager Plugin", $(Get-AWSCliSessionManagerPluginVersion))
$cliTools.AddToolVersion("AWS SAM CLI", $(Get-AWSSAMVersion))
$cliTools.AddToolVersion("Azure CLI", $(Get-AzureCliVersion))
$cliTools.AddToolVersion("Azure CLI (azure-devops)", $(Get-AzureDevopsVersion))
$cliTools.AddToolVersion("GitHub CLI", $(Get-GitHubCliVersion))
$cliTools.AddToolVersion("Google Cloud SDK", $(Get-GoogleCloudSDKVersion))
$cliTools.AddToolVersion("Hub CLI", $(Get-HubCliVersion))
$cliTools.AddToolVersion("Netlify CLI", $(Get-NetlifyCliVersion))
$cliTools.AddToolVersion("OpenShift CLI", $(Get-OCCliVersion))
$cliTools.AddToolVersion("ORAS CLI", $(Get-ORASCliVersion))
$cliTools.AddToolVersion("Vercel CLI", $(Get-VerselCliversion))
$markdown += New-MDHeader "Haskell" -Level 3
$markdown += New-MDList -Style Unordered -Lines (@(
(Get-GHCVersion),
(Get-GHCupVersion),
(Get-CabalVersion),
(Get-StackVersion)
) | Sort-Object
)
$markdown += New-MDHeader "Rust Tools" -Level 3
$markdown += New-MDList -Style Unordered -Lines (@(
(Get-RustVersion),
(Get-RustupVersion),
(Get-RustdocVersion),
(Get-CargoVersion)
) | Sort-Object
)
$installedSoftware.AddHeader("Java").AddTable($(Get-JavaVersionsTable))
$markdown += New-MDHeader "Packages" -Level 4
$markdown += New-MDList -Style Unordered -Lines (@(
(Get-BindgenVersion),
(Get-CargoAuditVersion),
(Get-CargoOutdatedVersion),
(Get-CargoClippyVersion),
(Get-CbindgenVersion),
(Get-RustfmtVersion)
) | Sort-Object
)
$phpTools = $installedSoftware.AddHeader("PHP Tools")
$phpTools.AddToolVersionsListInline("PHP", $(Get-PHPVersions), "^\d+\.\d+")
$phpTools.AddToolVersion("Composer", $(Get-ComposerVersion))
$phpTools.AddToolVersion("PHPUnit", $(Get-PHPUnitVersion))
$phpTools.AddNote("Both Xdebug and PCOV extensions are installed, but only Xdebug is enabled.")
$markdown += New-MDHeader "Browsers and Drivers" -Level 3
$haskellTools = $installedSoftware.AddHeader("Haskell Tools")
$haskellTools.AddToolVersion("Cabal", $(Get-CabalVersion))
$haskellTools.AddToolVersion("GHC", $(Get-GHCVersion))
$haskellTools.AddToolVersion("GHCup", $(Get-GHCupVersion))
$haskellTools.AddToolVersion("Stack", $(Get-StackVersion))
$browsersAndDriversList = @(
(Get-ChromeVersion),
(Get-ChromeDriverVersion),
(Get-FirefoxVersion),
(Get-GeckodriverVersion),
(Get-ChromiumVersion),
(Get-SeleniumVersion)
)
Initialize-RustEnvironment
$rustTools = $installedSoftware.AddHeader("Rust Tools")
$rustTools.AddToolVersion("Cargo", $(Get-CargoVersion))
$rustTools.AddToolVersion("Rust", $(Get-RustVersion))
$rustTools.AddToolVersion("Rustdoc", $(Get-RustdocVersion))
$rustTools.AddToolVersion("Rustup", $(Get-RustupVersion))
$rustToolsPackages = $rustTools.AddHeader("Packages")
$rustToolsPackages.AddToolVersion("Bindgen", $(Get-BindgenVersion))
$rustToolsPackages.AddToolVersion("Cargo audit", $(Get-CargoAuditVersion))
$rustToolsPackages.AddToolVersion("Cargo clippy", $(Get-CargoClippyVersion))
$rustToolsPackages.AddToolVersion("Cargo outdated", $(Get-CargoOutdatedVersion))
$rustToolsPackages.AddToolVersion("Cbindgen", $(Get-CbindgenVersion))
$rustToolsPackages.AddToolVersion("Rustfmt", $(Get-RustfmtVersion))
$markdown += New-MDList -Style Unordered -Lines $browsersAndDriversList
$markdown += New-MDHeader "Environment variables" -Level 4
$markdown += Build-BrowserWebdriversEnvironmentTable | New-MDTable
$markdown += New-MDNewLine
$browsersTools = $installedSoftware.AddHeader("Browsers and Drivers")
$browsersTools.AddToolVersion("Google Chrome", $(Get-ChromeVersion))
$browsersTools.AddToolVersion("ChromeDriver", $(Get-ChromeDriverVersion))
$browsersTools.AddToolVersion("Chromium", $(Get-ChromiumVersion))
$browsersTools.AddToolVersion("Microsoft Edge", $(Get-EdgeVersion))
$browsersTools.AddToolVersion("Microsoft Edge WebDriver", $(Get-EdgeDriverVersion))
$browsersTools.AddToolVersion("Selenium server", $(Get-SeleniumVersion))
$browsersTools.AddToolVersion("Mozilla Firefox", $(Get-FirefoxVersion))
$browsersTools.AddToolVersion("Geckodriver", $(Get-GeckodriverVersion))
$browsersTools.AddHeader("Environment variables").AddTable($(Build-BrowserWebdriversEnvironmentTable))
$markdown += New-MDHeader ".NET Core SDK" -Level 3
$markdown += New-MDList -Style Unordered -Lines @(
(Get-DotNetCoreSdkVersions)
)
$netCoreTools = $installedSoftware.AddHeader(".NET Tools")
$netCoreTools.AddToolVersionsListInline(".NET Core SDK", $(Get-DotNetCoreSdkVersions), "^\d+\.\d+\.\d")
$netCoreTools.AddNodes($(Get-DotnetTools))
$markdown += New-MDHeader ".NET tools" -Level 3
$tools = Get-DotnetTools
$markdown += New-MDList -Lines $tools -Style Unordered
$databasesTools = $installedSoftware.AddHeader("Databases")
if (Test-IsUbuntu20) {
$databasesTools.AddToolVersion("MongoDB", $(Get-MongoDbVersion))
}
$databasesTools.AddToolVersion("sqlite3", $(Get-SqliteVersion))
$databasesTools.AddNode($(Build-PostgreSqlSection))
$databasesTools.AddNode($(Build-MySQLSection))
$databasesTools.AddNode($(Build-MSSQLToolsSection))
$markdown += New-MDHeader "Databases" -Level 3
$markdown += New-MDList -Style Unordered -Lines (@(
(Get-PostgreSqlVersion),
(Get-MongoDbVersion),
(Get-SqliteVersion)
) | Sort-Object
)
$cachedTools = $installedSoftware.AddHeader("Cached Tools")
$cachedTools.AddToolVersionsList("Go", $(Get-ToolcacheGoVersions), "^\d+\.\d+")
$cachedTools.AddToolVersionsList("Node.js", $(Get-ToolcacheNodeVersions), "^\d+")
$cachedTools.AddToolVersionsList("Python", $(Get-ToolcachePythonVersions), "^\d+\.\d+")
$cachedTools.AddToolVersionsList("PyPy", $(Get-ToolcachePyPyVersions), "^\d+\.\d+")
$cachedTools.AddToolVersionsList("Ruby", $(Get-ToolcacheRubyVersions), "^\d+\.\d+")
$markdown += Build-MySQLSection
$markdown += Build-MSSQLToolsSection
$powerShellTools = $installedSoftware.AddHeader("PowerShell Tools")
$powerShellTools.AddToolVersion("PowerShell", $(Get-PowershellVersion))
$powerShellTools.AddHeader("PowerShell Modules").AddNodes($(Get-PowerShellModules))
$markdown += New-MDHeader "Cached Tools" -Level 3
$markdown += Build-CachedToolsSection
$installedSoftware.AddHeader("Web Servers").AddTable($(Build-WebServersTable))
$markdown += New-MDHeader "Environment variables" -Level 4
$markdown += Build-GoEnvironmentTable | New-MDTable
$markdown += New-MDNewLine
$androidTools = $installedSoftware.AddHeader("Android")
$androidTools.AddTable($(Build-AndroidTable))
$androidTools.AddHeader("Environment variables").AddTable($(Build-AndroidEnvironmentTable))
$markdown += New-MDHeader "PowerShell Tools" -Level 3
$markdown += New-MDList -Lines (Get-PowershellVersion) -Style Unordered
$installedSoftware.AddHeader("Cached Docker images").AddTable($(Get-CachedDockerImagesTableData))
$installedSoftware.AddHeader("Installed apt packages").AddTable($(Get-AptPackages))
$markdown += New-MDHeader "PowerShell Modules" -Level 4
$markdown += Get-PowerShellModules | New-MDTable
$markdown += New-MDNewLine
$markdown += New-MDHeader "Az PowerShell Modules" -Level 4
$markdown += New-MDList -Style Unordered -Lines @(
(Get-AzModuleVersions)
)
$softwareReport.ToJson() | Out-File -FilePath "${OutputDirectory}/software-report.json" -Encoding UTF8NoBOM
$softwareReport.ToMarkdown() | Out-File -FilePath "${OutputDirectory}/software-report.md" -Encoding UTF8NoBOM
$markdown += Build-WebServersSection
$markdown += New-MDHeader "Android" -Level 3
$markdown += Build-AndroidTable | New-MDTable
$markdown += New-MDNewLine
$markdown += New-MDHeader "Environment variables" -Level 4
$markdown += Build-AndroidEnvironmentTable | New-MDTable
$markdown += New-MDNewLine
$markdown += New-MDHeader "Cached Docker images" -Level 3
$markdown += Get-CachedDockerImagesTableData | New-MDTable
$markdown += New-MDNewLine
$markdown += New-MDHeader "Installed apt packages" -Level 3
$markdown += Get-AptPackages | New-MDTable
Test-BlankElement
$markdown | Out-File -FilePath "${OutputDirectory}/Ubuntu-Readme.md"
@@ -1,4 +1,4 @@
function Get-JavaVersionsTable {
function Get-JavaVersions {
$javaToolcacheVersions = Get-ChildItem $env:AGENT_TOOLSDIRECTORY/Java*/* -Directory | Sort-Object { [int]$_.Name.Split(".")[0] }
$existingVersions = $javaToolcacheVersions | ForEach-Object {
@@ -5,51 +5,52 @@ function Initialize-RustEnvironment {
}
function Get-RustVersion {
Initialize-RustEnvironment
$rustVersion = $(rustc --version) | Take-OutputPart -Part 1
return $rustVersion
return "Rust $rustVersion"
}
function Get-BindgenVersion {
$bindgenVersion = $(bindgen --version) | Take-OutputPart -Part 1
return $bindgenVersion
return "Bindgen $bindgenVersion"
}
function Get-CargoVersion {
$cargoVersion = $(cargo --version) | Take-OutputPart -Part 1
return $cargoVersion
return "Cargo $cargoVersion"
}
function Get-CargoAuditVersion {
$cargoAuditVersion = $(cargo-audit --version) | Take-OutputPart -Part 1
return $cargoAuditVersion
$cargoAuditVersion = $(cargo audit --version) | Take-OutputPart -Part 1
return "Cargo audit $cargoAuditVersion"
}
function Get-CargoOutdatedVersion {
$cargoOutdatedVersion = cargo outdated --version | Take-OutputPart -Part 1
return $cargoOutdatedVersion
$cargoOutdatedVersion = $(cargo outdated --version) | Take-OutputPart -Part 1 -Delimiter "v"
return "Cargo outdated $cargoOutdatedVersion"
}
function Get-CargoClippyVersion {
$cargoClippyVersion = $(cargo-clippy --version) | Take-OutputPart -Part 1
return $cargoClippyVersion
return "Cargo clippy $cargoClippyVersion"
}
function Get-CbindgenVersion {
$cbindgenVersion = $(cbindgen --version) | Take-OutputPart -Part 1
return $cbindgenVersion
return "Cbindgen $cbindgenVersion"
}
function Get-RustupVersion {
$rustupVersion = $(rustup --version) | Take-OutputPart -Part 1
return $rustupVersion
return "Rustup $rustupVersion"
}
function Get-RustdocVersion {
$rustdocVersion = $(rustdoc --version) | Take-OutputPart -Part 1
return $rustdocVersion
return "Rustdoc $rustdocVersion"
}
function Get-RustfmtVersion {
$rustfmtVersion = $(rustfmt --version) | Take-OutputPart -Part 1 | Take-OutputPart -Part 0 -Delimiter "-"
return $rustfmtVersion
return "Rustfmt $rustfmtVersion"
}
@@ -1,294 +1,295 @@
function Get-AnsibleVersion {
$ansibleVersion = (ansible --version)[0] -replace "[^\d.]"
return $ansibleVersion
return "Ansible $ansibleVersion"
}
function Get-AptFastVersion {
$versionFileContent = Get-Content (which apt-fast) -Raw
$match = [Regex]::Match($versionFileContent, '# apt-fast v(.+)\n')
return $match.Groups[1].Value
$aptFastVersion = $match.Groups[1].Value
return "apt-fast $aptFastVersion"
}
function Get-AzCopyVersion {
$azcopyVersion = azcopy --version | Take-OutputPart -Part 2
return "$azcopyVersion - available by ``azcopy`` and ``azcopy10`` aliases"
return "AzCopy $azcopyVersion (available by ``azcopy`` and ``azcopy10`` aliases)"
}
function Get-BazelVersion {
$bazelVersion = bazel --version | Select-String "bazel" | Take-OutputPart -Part 1
return $bazelVersion
return "Bazel $bazelVersion"
}
function Get-BazeliskVersion {
$result = Get-CommandResult "bazelisk version" -Multiline
$bazeliskVersion = $result.Output | Select-String "Bazelisk version:" | Take-OutputPart -Part 2 | Take-OutputPart -Part 0 -Delimiter "v"
return $bazeliskVersion
return "Bazelisk $bazeliskVersion"
}
function Get-BicepVersion {
(bicep --version | Out-String) -match "bicep cli version (?<version>\d+\.\d+\.\d+)" | Out-Null
return $Matches.Version
$bicepVersion = $Matches.Version
return "Bicep $bicepVersion"
}
function Get-CodeQLBundleVersions {
function Get-CodeQLBundleVersion {
$CodeQLVersionsWildcard = Join-Path $Env:AGENT_TOOLSDIRECTORY -ChildPath "CodeQL" | Join-Path -ChildPath "*"
$CodeQLVersionPaths = Get-ChildItem $CodeQLVersionsWildcard
$CodeQlVersions=@()
foreach ($CodeQLVersionPath in $CodeQLVersionPaths) {
$FullCodeQLVersionPath = $CodeQLVersionPath | Select-Object -Expand FullName
$CodeQLPath = Join-Path $FullCodeQLVersionPath -ChildPath "x64" | Join-Path -ChildPath "codeql" | Join-Path -ChildPath "codeql"
$CodeQLVersion = & $CodeQLPath version --quiet
$CodeQLVersions += $CodeQLVersion
}
return $CodeQLVersions
$CodeQLVersionPath = Get-ChildItem $CodeQLVersionsWildcard | Select-Object -First 1 -Expand FullName
$CodeQLPath = Join-Path $CodeQLVersionPath -ChildPath "x64" | Join-Path -ChildPath "codeql" | Join-Path -ChildPath "codeql"
$CodeQLVersion = & $CodeQLPath version --quiet
return "CodeQL Action Bundle $CodeQLVersion"
}
function Get-PodManVersion {
$podmanVersion = podman --version | Take-OutputPart -Part 2
return $podmanVersion
$aptSourceRepo = Get-AptSourceRepository -PackageName "containers"
return "Podman $podmanVersion (apt source repository: $aptSourceRepo)"
}
function Get-BuildahVersion {
$buildahVersion = buildah --version | Take-OutputPart -Part 2
return $buildahVersion
$aptSourceRepo = Get-AptSourceRepository -PackageName "containers"
return "Buildah $buildahVersion (apt source repository: $aptSourceRepo)"
}
function Get-SkopeoVersion {
$skopeoVersion = skopeo --version | Take-OutputPart -Part 2
return $skopeoVersion
$aptSourceRepo = Get-AptSourceRepository -PackageName "containers"
return "Skopeo $skopeoVersion (apt source repository: $aptSourceRepo)"
}
function Get-CMakeVersion {
$cmakeVersion = cmake --version | Select-Object -First 1 | Take-OutputPart -Part 2
return $cmakeVersion
return "CMake $cmakeVersion"
}
function Get-DockerComposeV1Version {
$composeVersion = docker-compose -v | Take-OutputPart -Part 2 | Take-OutputPart -Part 0 -Delimiter ","
return $composeVersion
return "Docker Compose v1 $composeVersion"
}
function Get-DockerComposeV2Version {
$composeVersion = docker compose version | Take-OutputPart -Part 3
return $composeVersion
return "Docker Compose v2 $composeVersion"
}
function Get-DockerMobyClientVersion {
$dockerClientVersion = sudo docker version --format '{{.Client.Version}}'
return $dockerClientVersion
return "Docker-Moby Client $dockerClientVersion"
}
function Get-DockerMobyServerVersion {
$dockerServerVersion = sudo docker version --format '{{.Server.Version}}'
return $dockerServerVersion
return "Docker-Moby Server $dockerServerVersion"
}
function Get-DockerBuildxVersion {
$buildxVersion = docker buildx version | Take-OutputPart -Part 1 | Take-OutputPart -Part 0 -Delimiter "+"
return $buildxVersion
}
function Get-DockerAmazonECRCredHelperVersion {
$ecrVersion = docker-credential-ecr-login -v | Select-String "Version:" | Take-OutputPart -Part 1
return $ecrVersion
return "Docker-Buildx $buildxVersion"
}
function Get-GitVersion {
$gitVersion = git --version | Take-OutputPart -Part -1
return $gitVersion
$result = Get-CommandResult "git --version"
$gitVersion = $result.Output | Take-OutputPart -Part 2
$aptSourceRepo = Get-AptSourceRepository -PackageName "git-core"
return "Git $gitVersion (apt source repository: $aptSourceRepo)"
}
function Get-GitLFSVersion {
$result = Get-CommandResult "git-lfs --version"
$gitlfsversion = $result.Output | Take-OutputPart -Part 0 | Take-OutputPart -Part 1 -Delimiter "/"
return $gitlfsversion
$aptSourceRepo = Get-AptSourceRepository -PackageName "git-lfs"
return "Git LFS $gitlfsversion (apt source repository: $aptSourceRepo)"
}
function Get-GitFTPVersion {
$gitftpVersion = git-ftp --version | Take-OutputPart -Part 2
return $gitftpVersion
return "Git-ftp $gitftpVersion"
}
function Get-GoogleCloudSDKVersion {
return (gcloud --version | Select-Object -First 1) | Take-OutputPart -Part 3
$aptSourceRepo = Get-AptSourceRepository -PackageName "google-cloud-sdk"
return "$(gcloud --version | Select-Object -First 1) (apt source repository: $aptSourceRepo)"
}
function Get-HavegedVersion {
$havegedVersion = dpkg-query --showformat='${Version}' --show haveged | Take-OutputPart -Part 0 -Delimiter "-"
return $havegedVersion
return "Haveged $havegedVersion"
}
function Get-HerokuVersion {
$herokuVersion = heroku version | Take-OutputPart -Part 0 | Take-OutputPart -Part 1 -Delimiter "/"
return $herokuVersion
return "Heroku $herokuVersion"
}
function Get-HHVMVersion {
$hhvmVersion = hhvm --version | Select-Object -First 1 | Take-OutputPart -Part 2
return $hhvmVersion
return "HHVM (HipHop VM) $hhvmVersion"
}
function Get-SVNVersion {
$svnVersion = svn --version | Select-Object -First 1 | Take-OutputPart -Part 2
return $svnVersion
return "SVN $svnVersion"
}
function Get-KustomizeVersion {
$kustomizeVersion = kustomize version --short | Take-OutputPart -Part 0 | Take-OutputPart -Part 1 -Delimiter "v"
return $kustomizeVersion
return "Kustomize $kustomizeVersion"
}
function Get-KindVersion {
$kindVersion = kind version | Take-OutputPart -Part 1 | Take-OutputPart -Part 0 -Delimiter "v"
return $kindVersion
return "Kind $kindVersion"
}
function Get-KubectlVersion {
$kubectlVersion = (kubectl version --client --output=json | ConvertFrom-Json).clientVersion.gitVersion.Replace('v','')
return $kubectlVersion
$kubectlVersion = kubectl version --client --short | Take-OutputPart -Part 2 | Take-OutputPart -Part 0 -Delimiter "v"
return "Kubectl $kubectlVersion"
}
function Get-MinikubeVersion {
$minikubeVersion = minikube version --short | Take-OutputPart -Part 0 -Delimiter "v"
return $minikubeVersion
return "Minikube $minikubeVersion"
}
function Get-HGVersion {
$hgVersion = hg --version | Select-Object -First 1 | Take-OutputPart -Part -1 | Take-OutputPart -Part 0 -Delimiter ")"
return $hgVersion
return "Mercurial $hgVersion"
}
function Get-LeiningenVersion {
return "$(lein -v | Take-OutputPart -Part 1)"
return "$(lein -v | Take-OutputPart -Part 0,1)"
}
function Get-MediainfoVersion {
$mediainfoVersion = (mediainfo --version | Select-Object -Index 1 | Take-OutputPart -Part 2).Replace('v', '')
return $mediainfoVersion
return "MediaInfo $mediainfoVersion"
}
function Get-NewmanVersion {
return $(newman --version)
return "Newman $(newman --version)"
}
function Get-NVersion {
$nVersion = (n --version).Replace('v', '')
return $nVersion
return "n $nVersion"
}
function Get-NvmVersion {
$nvmVersion = bash -c "source /etc/skel/.nvm/nvm.sh && nvm --version"
return $nvmVersion
return "nvm $nvmVersion"
}
function Get-PackerVersion {
$packerVersion = packer --version
return $packerVersion
# Packer 1.7.1 has a bug and outputs version to stderr instead of stdout https://github.com/hashicorp/packer/issues/10855
$result = (Get-CommandResult "packer --version").Output
$packerVersion = [regex]::matches($result, "(\d+.){2}\d+").Value
return "Packer $packerVersion"
}
function Get-PhantomJSVersion {
$env:OPENSSL_CONF="/etc/ssl"; phantomjs --version
return $(phantomjs --version)
return "PhantomJS $(phantomjs --version)"
}
function Get-TerraformVersion {
return (terraform version | Select-String "^Terraform").Line.Replace('v','') | Take-OutputPart -Part 1
return (terraform version | Select-String "^Terraform").Line.Replace('v','')
}
function Get-JqVersion {
$jqVersion = jq --version | Take-OutputPart -Part 1 -Delimiter "-"
return $jqVersion
return "jq $jqVersion"
}
function Get-AzureCliVersion {
$azcliVersion = (az version | ConvertFrom-Json).'azure-cli'
return $azcliVersion
$aptSourceRepo = Get-AptSourceRepository -PackageName "azure-cli"
return "Azure CLI (azure-cli) $azcliVersion (installation method: $aptSourceRepo)"
}
function Get-AzureDevopsVersion {
$azdevopsVersion = (az version | ConvertFrom-Json).extensions.'azure-devops'
return $azdevopsVersion
return "Azure CLI (azure-devops) $azdevopsVersion"
}
function Get-AlibabaCloudCliVersion {
return $(aliyun version)
return "Alibaba Cloud CLI $(aliyun version)"
}
function Get-AWSCliVersion {
$result = Get-CommandResult "aws --version"
$awsVersion = $result.Output | Take-OutputPart -Part 0 | Take-OutputPart -Part 1 -Delimiter "/"
return $awsVersion
return "AWS CLI $awsVersion"
}
function Get-AWSCliSessionManagerPluginVersion {
$result = (Get-CommandResult "session-manager-plugin --version").Output
return $result
return "AWS CLI Session manager plugin $result"
}
function Get-AWSSAMVersion {
return $(sam --version | Take-OutputPart -Part -1)
return "AWS SAM CLI $(sam --version | Take-OutputPart -Part -1)"
}
function Get-FastlaneVersion {
$fastlaneVersion = fastlane --version | Select-String "^fastlane [0-9]" | Take-OutputPart -Part 1
return $fastlaneVersion
return "Fastlane $fastlaneVersion"
}
function Get-HubCliVersion {
$hubVersion = hub --version | Select-String "hub version" | Take-OutputPart -Part 2
return $hubVersion
return "Hub CLI $hubVersion"
}
function Get-GitHubCliVersion {
$ghVersion = gh --version | Select-String "gh version" | Take-OutputPart -Part 2
return $ghVersion
return "GitHub CLI $ghVersion"
}
function Get-NetlifyCliVersion {
$netlifyVersion = netlify --version | Take-OutputPart -Part 0 | Take-OutputPart -Part 1 -Delimiter "/"
return $netlifyVersion
return "Netlify CLI $netlifyVersion"
}
function Get-OCCliVersion {
$ocVersion = oc version -o=json | jq -r '.releaseClientVersion'
return $ocVersion
$ocVersion = oc version | Take-OutputPart -Part 2 | Take-OutputPart -Part 0 -Delimiter "-"
return "OpenShift CLI $ocVersion"
}
function Get-ORASCliVersion {
$orasVersion = oras version | Select-String "^Version:" | Take-OutputPart -Part 1
return $orasVersion
return "ORAS CLI $orasVersion"
}
function Get-VerselCliversion {
$result = Get-CommandResult "vercel --version" -Multiline
return $result.Output | Select-Object -Skip 1 -First 1
return $result.Output | Select-Object -First 1
}
function Get-PulumiVersion {
$pulumiVersion = pulumi version | Take-OutputPart -Part 0 -Delimiter "v"
return $pulumiVersion
return "Pulumi $pulumiVersion"
}
function Get-RVersion {
$rVersion = (Get-CommandResult "R --version | grep 'R version'").Output | Take-OutputPart -Part 2
return $rVersion
return "R $rVersion"
}
function Get-SphinxVersion {
$sphinxVersion = searchd -h | Select-Object -First 1 | Take-OutputPart -Part 1 | Take-OutputPart -Part 0 -Delimiter "-"
return $sphinxVersion
return "Sphinx Open Source Search Server $sphinxVersion"
}
function Get-YamllintVersion {
return $(yamllint --version) | Take-OutputPart -Part 1
return "$(yamllint --version)"
}
function Get-ZstdVersion {
$zstdVersion = zstd --version | Take-OutputPart -Part 1 -Delimiter "v" | Take-OutputPart -Part 0 -Delimiter ","
return "$zstdVersion"
return "zstd $zstdVersion (homebrew)"
}
function Get-YqVersion {
$yqVersion = $(yq -V) | Take-OutputPart -Part 3
return $yqVersion.TrimStart("v").Trim()
$yqVersion = ($(yq -V) -Split " ")[-1]
return "yq $yqVersion"
}
@@ -43,13 +43,19 @@ function Get-Xsp4Version {
}
}
function Build-WebServersTable {
$servers = @()
$servers += (Get-ApacheVersion)
function Build-WebServersSection {
$servers = @(
(Get-ApacheVersion),
(Get-NginxVersion)
)
if (Test-IsUbuntu20) {
$servers += (Get-Xsp4Version)
}
$servers += (Get-NginxVersion)
return $servers
$output = ""
$output += New-MDHeader "Web Servers" -Level 3
$output += $servers | Sort-Object Name | New-MDTable
$output += New-MDNewLine
return $output
}
@@ -1,12 +0,0 @@
#!/bin/bash -e
################################################################################
## File: apt-ubuntu-archive.sh
## Desc: Script for adding additional apt repo to /etc/apt/sources.list
################################################################################
echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -cs) main restricted" | tee -a /etc/apt/sources.list
echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -cs)-updates main restricted" | tee -a /etc/apt/sources.list
echo "deb http://security.ubuntu.com/ubuntu/ $(lsb_release -cs)-security main restricted" | tee -a /etc/apt/sources.list
echo "deb http://security.ubuntu.com/ubuntu/ $(lsb_release -cs)-security universe" | tee -a /etc/apt/sources.list
echo "deb http://security.ubuntu.com/ubuntu/ $(lsb_release -cs)-security multiverse" | tee -a /etc/apt/sources.list
+5 -8
View File
@@ -14,13 +14,6 @@ echo "APT::Acquire::Retries \"10\";" > /etc/apt/apt.conf.d/80-retries
# Configure apt to always assume Y
echo "APT::Get::Assume-Yes \"true\";" > /etc/apt/apt.conf.d/90assumeyes
# APT understands a field called Phased-Update-Percentage which can be used to control the rollout of a new version. It is an integer between 0 and 100.
# In case you have multiple systems that you want to receive the same set of updates,
# you can set APT::Machine-ID to a UUID such that they all phase the same,
# or set APT::Get::Never-Include-Phased-Updates or APT::Get::Always-Include-Phased-Updates to true such that APT will never/always consider phased updates.
# apt-cache policy pkgname
echo 'APT::Get::Always-Include-Phased-Updates "true";' > /etc/apt/apt.conf.d/99-phased-updates
# Fix bad proxy and http headers settings
cat <<EOF >> /etc/apt/apt.conf.d/99bad_proxy
Acquire::http::Pipeline-Depth 0;
@@ -31,7 +24,11 @@ EOF
# Uninstall unattended-upgrades
apt-get purge unattended-upgrades
echo 'APT sources'
# Need to limit arch for default apt repos due to
# https://github.com/actions/virtual-environments/issues/1961
sed -i'' -E 's/^deb http:\/\/(azure.archive|security).ubuntu.com/deb [arch=amd64,i386] http:\/\/\1.ubuntu.com/' /etc/apt/sources.list
echo 'APT sources limited to the actual architectures'
cat /etc/apt/sources.list
apt-get update
+7 -1
View File
@@ -9,8 +9,14 @@ LSB_RELEASE=$(lsb_release -rs)
# Install Microsoft repository
wget https://packages.microsoft.com/config/ubuntu/$LSB_RELEASE/packages-microsoft-prod.deb
dpkg -i packages-microsoft-prod.deb
apt-get install -y apt-transport-https ca-certificates curl software-properties-common
# Install Microsoft GPG public key
curl -L https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
# update
apt-get install -y apt-transport-https ca-certificates curl software-properties-common
apt-get -yq update
apt-get -yq dist-upgrade
@@ -29,27 +29,23 @@ function Get-CommandResult {
}
}
function Get-OSVersionShort {
$(Get-OSVersionFull) | Take-OutputPart -Delimiter '.' -Part 0,1
}
function Get-OSVersionFull {
lsb_release -ds | Take-OutputPart -Part 1, 2
function Get-OSName {
lsb_release -ds
}
function Get-KernelVersion {
$kernelVersion = uname -r
return $kernelVersion
return "Linux kernel version: $kernelVersion"
}
function Test-IsUbuntu18 {
return (lsb_release -rs) -eq "18.04"
}
function Test-IsUbuntu20 {
return (lsb_release -rs) -eq "20.04"
}
function Test-IsUbuntu22 {
return (lsb_release -rs) -eq "22.04"
}
function Get-ToolsetContent {
$toolset = Join-Path $env:INSTALLER_SCRIPT_FOLDER "toolset.json"
Get-Content $toolset -Raw | ConvertFrom-Json
@@ -73,7 +69,7 @@ function Get-ToolsetValue {
function Get-AndroidPackages {
$androidSDKManagerPath = "/usr/local/lib/android/sdk/cmdline-tools/latest/bin/sdkmanager"
$androidPackages = & $androidSDKManagerPath --list --verbose 2>&1
$androidPackages = & $androidSDKManagerPath --list --verbose
return $androidPackages
}
@@ -10,6 +10,14 @@ function Take-OutputPart {
return [string]::Join($Delimiter, $selectedParts)
}
function New-MDNewLine {
param (
[int] $Count = 1
)
$newLineSymbol = [System.Environment]::NewLine
return $newLineSymbol * $Count
}
function Restore-UserOwner {
sudo chown -R ${env:USER}: $env:HOME
}
@@ -33,9 +41,118 @@ function Get-PathWithLink {
return "${inputPath}${link}"
}
function Get-CachedToolInstances
{
<#
.SYNOPSIS
Returns hastable of installed cached tools.
.DESCRIPTION
Return hastable that contains versions and architectures for selected cached tool.
.PARAMETER Name
Name of cached tool.
.PARAMETER VersionCommand
Optional parameter. Command to return version of system default tool.
.EXAMPLE
Get-CachedToolInstances -Name "Python" -VersionCommand "--version"
#>
param
(
[String] $Name,
[String] $VersionCommand
)
$toolInstances = @()
$toolPath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath $Name
# Get all installed versions from TOOLSDIRECTORY folder
$versions = Get-ChildItem $toolPath | Sort-Object { [System.Version]$_.Name }
foreach ($version in $versions)
{
$instanceInfo = @{}
# Create instance hashtable
[string]$instanceInfo.Path = Join-Path -Path $toolPath -ChildPath $version.Name
[string]$instanceInfo.Version = $version.Name
# Get all architectures for current version
[array]$instanceInfo.Architecture_Array = Get-ChildItem $version.FullName -Name -Directory | Where-Object { $_ -match "^x[0-9]{2}$" }
[string]$instanceInfo.Architecture = $instanceInfo.Architecture_Array -Join ", "
# Add (default) postfix to version name, in case if current version is in environment path
if (-not ([string]::IsNullOrEmpty($VersionCommand)))
{
$defaultVersion = $(& ($Name.ToLower()) $VersionCommand 2>&1)
$defaultToolVersion = $defaultVersion | Select-String -Pattern "\d+\.\d+\.\d+" -AllMatches `
| ForEach-Object { $_.Matches.Value }
if ([version]$version.Name -eq [version]$defaultToolVersion)
{
$instanceInfo.Version += " (Default)"
}
}
$toolInstances += $instanceInfo
}
return $toolInstances
}
function Get-AptSourceRepository {
param([String] $PackageName)
$sourceUrl = Get-Content "$PSScriptRoot/../helpers/apt-sources.txt" | Select-String -Pattern $PackageName | Take-OutputPart -Part (1..3)
return $sourceUrl
}
function Test-BlankElement {
param(
[string] $Markdown
)
$splitByLines = $Markdown.Split("`n")
# Validate entry without version
$blankVersions = $splitByLines -match "^-" -notmatch "Image Version|MySQL Server|Vcpkg|\d\." | Out-String
# Validate tables with blank rows
$blankRows = ""
for($i = 0; $i -lt $splitByLines.Length; $i++) {
$addRows= $false
$table = @()
if ($splitByLines[$i].StartsWith("#") -and $splitByLines[$i+1].StartsWith("|")) {
$table += $splitByLines[$i,($i+1),($i+2)]
$i += 3
$current = $splitByLines[$i]
while ($current.StartsWith("|")) {
$isBlankRow = $current.Substring(1, $current.LastIndexOf("|") - 2).Split("|").Trim() -contains ""
if ($isBlankRow) {
$table += $current
$addRows = $true
}
$current = $splitByLines[++$i]
}
if ($addRows) {
$blankRows += $table | Out-String
}
}
}
# Display report
$isReport = $false
if ($blankVersions) {
Write-Host "Software list with blank version:`n${blankVersions}"
$isReport = $true
}
if ($blankRows) {
Write-Host "Tables with blank rows:`n${blankRows}"
$isReport = $true
}
if ($isReport) {
exit 1
}
}
@@ -20,10 +20,10 @@ function Invoke-PesterTests {
if (!(Get-Module "Pester")) {
Import-Module Pester
}
$configuration = [PesterConfiguration] @{
Run = @{ Path = $testPath; PassThru = $true }
Output = @{ Verbosity = "Detailed"; RenderMode = "Plaintext" }
Output = @{ Verbosity = "Detailed" }
}
if ($TestName) {
$configuration.Filter.FullName = $TestName
+3 -24
View File
@@ -28,14 +28,15 @@ download_with_retries() {
set +e
http_code=$(eval $COMMAND)
exit_code=$?
set -e
if [ $http_code -eq 200 ] && [ $exit_code -eq 0 ]; then
echo "Download completed"
return 0
else
echo "Error — Either HTTP response code for '$URL' is wrong - '$http_code' or exit code is not 0 - '$exit_code'. Waiting $interval seconds before the next attempt, $retries attempts left"
sleep $interval
sleep 30
fi
# Enable exit on error back
set -e
done
echo "Could not download $URL"
@@ -65,25 +66,3 @@ get_toolset_value() {
local query=$1
echo "$(jq -r "$query" $toolset_path)"
}
get_github_package_download_url() {
local REPO_ORG=$1
local FILTER=$2
local VERSION=$3
local SEARCH_IN_COUNT="100"
json=$(curl -s "https://api.github.com/repos/${REPO_ORG}/releases?per_page=${SEARCH_IN_COUNT}")
if [ -n "$VERSION" ]; then
tagName=$(echo $json | jq -r '.[] | select(.prerelease==false).tag_name' | sort --unique --version-sort | egrep -v ".*-[a-z]|beta" | egrep "\w*${VERSION}" | tail -1)
else
tagName=$(echo $json | jq -r '.[] | select((.prerelease==false) and (.assets | length > 0)).tag_name' | sort --unique --version-sort | egrep -v ".*-[a-z]|beta" | tail -1)
fi
downloadUrl=$(echo $json | jq -r ".[] | select(.tag_name==\"${tagName}\").assets[].browser_download_url | select(${FILTER})" | head -n 1)
if [ -z "$downloadUrl" ]; then
echo "Failed to parse a download url for the '${tagName}' tag using '${FILTER}' filter"
exit 1
fi
echo $downloadUrl
}
@@ -0,0 +1,193 @@
#!/bin/bash -e
#
# Temporary fix for the https://github.com/actions/virtual-environments/issues/4732 issue
# Taken from the official ubuntu:20.04 docker container
### BEGIN INIT INFO
# Provides: mysql
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Should-Start: $network $time
# Should-Stop: $network $time
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start and stop the mysql database server daemon
# Description: Controls the main MySQL database server daemon "mysqld"
# and its wrapper script "mysqld_safe".
### END INIT INFO
#
set -e
set -u
${DEBIAN_SCRIPT_DEBUG:+ set -v -x}
test -x /usr/bin/mysqld_safe || exit 0
. /lib/lsb/init-functions
SELF=$(cd $(dirname $0); pwd -P)/$(basename $0)
CONF=/etc/mysql/my.cnf
MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
# priority can be overriden and "-s" adds output to stderr
ERR_LOGGER="logger -p daemon.err -t /etc/init.d/mysql -i"
# Safeguard (relative paths, core dumps..)
cd /
umask 077
# mysqladmin likes to read /root/.my.cnf. This is usually not what I want
# as many admins e.g. only store a password without a username there and
# so break my scripts.
export HOME=/etc/mysql/
## Fetch a particular option from mysql's invocation.
#
# Usage: void mysqld_get_param option
mysqld_get_param() {
/usr/sbin/mysqld --print-defaults \
| tr " " "\n" \
| grep -- "--$1" \
| tail -n 1 \
| cut -d= -f2
}
## Do some sanity checks before even trying to start mysqld.
sanity_checks() {
# check for config file
if [ ! -r /etc/mysql/my.cnf ]; then
log_warning_msg "$0: WARNING: /etc/mysql/my.cnf cannot be read. See README.Debian.gz"
echo "WARNING: /etc/mysql/my.cnf cannot be read. See README.Debian.gz" | $ERR_LOGGER
fi
# check for diskspace shortage
datadir=`mysqld_get_param datadir`
if LC_ALL=C BLOCKSIZE= df --portability $datadir/. | tail -n 1 | awk '{ exit ($4>4096) }'; then
log_failure_msg "$0: ERROR: The partition with $datadir is too full!"
echo "ERROR: The partition with $datadir is too full!" | $ERR_LOGGER
exit 1
fi
}
## Checks if there is a server running and if so if it is accessible.
#
# check_alive insists on a pingable server
# check_dead also fails if there is a lost mysqld in the process list
#
# Usage: boolean mysqld_status [check_alive|check_dead] [warn|nowarn]
mysqld_status () {
ping_output=`$MYADMIN ping 2>&1`; ping_alive=$(( ! $? ))
ps_alive=0
pidfile=`mysqld_get_param pid-file`
if [ -f "$pidfile" ] && ps `cat $pidfile` >/dev/null 2>&1; then ps_alive=1; fi
if [ "$1" = "check_alive" -a $ping_alive = 1 ] ||
[ "$1" = "check_dead" -a $ping_alive = 0 -a $ps_alive = 0 ]; then
return 0 # EXIT_SUCCESS
else
if [ "$2" = "warn" ]; then
echo -e "$ps_alive processes alive and '$MYADMIN ping' resulted in\n$ping_output\n" | $ERR_LOGGER -p daemon.debug
fi
return 1 # EXIT_FAILURE
fi
}
#
# main()
#
case "${1:-''}" in
'start')
sanity_checks;
# Start daemon
log_daemon_msg "Starting MySQL database server" "mysqld"
if mysqld_status check_alive nowarn; then
log_progress_msg "already running"
log_end_msg 0
else
# Could be removed during boot
test -e /var/run/mysqld || install -m 755 -o mysql -g root -d /var/run/mysqld
# Start MySQL!
su - mysql -s /bin/sh -c "/usr/bin/mysqld_safe > /dev/null 2>&1 &"
# 6s was reported in #352070 to be too few when using ndbcluster
# 14s was reported in #736452 to be too few with large installs
for i in $(seq 1 30); do
sleep 1
if mysqld_status check_alive nowarn ; then break; fi
log_progress_msg "."
done
if mysqld_status check_alive warn; then
log_end_msg 0
# Now start mysqlcheck or whatever the admin wants.
output=$(/etc/mysql/debian-start)
[ -n "$output" ] && log_action_msg "$output"
else
log_end_msg 1
log_failure_msg "Please take a look at the syslog"
fi
fi
;;
'stop')
# * As a passwordless mysqladmin (e.g. via ~/.my.cnf) must be possible
# at least for cron, we can rely on it here, too. (although we have
# to specify it explicit as e.g. sudo environments points to the normal
# users home and not /root)
log_daemon_msg "Stopping MySQL database server" "mysqld"
if ! mysqld_status check_dead nowarn; then
set +e
shutdown_out=`$MYADMIN shutdown 2>&1`; r=$?
set -e
if [ "$r" -ne 0 ]; then
log_end_msg 1
[ "$VERBOSE" != "no" ] && log_failure_msg "Error: $shutdown_out"
log_daemon_msg "Killing MySQL database server by signal" "mysqld"
killall -15 mysqld
server_down=
for i in 1 2 3 4 5 6 7 8 9 10; do
sleep 1
if mysqld_status check_dead nowarn; then server_down=1; break; fi
done
if test -z "$server_down"; then killall -9 mysqld; fi
fi
fi
if ! mysqld_status check_dead warn; then
log_end_msg 1
log_failure_msg "Please stop MySQL manually and read /usr/share/doc/mysql-server-5.7/README.Debian.gz!"
exit -1
else
log_end_msg 0
fi
;;
'restart')
set +e; $SELF stop; set -e
$SELF start
;;
'reload'|'force-reload')
log_daemon_msg "Reloading MySQL database server" "mysqld"
$MYADMIN reload
log_end_msg 0
;;
'status')
if mysqld_status check_alive nowarn; then
log_action_msg "$($MYADMIN version)"
else
log_action_msg "MySQL is stopped."
exit 3
fi
;;
*)
echo "Usage: $SELF start|stop|restart|reload|force-reload|status"
exit 1
;;
esac
# Some success paths end up returning non-zero so exit 0 explicitly. See
# bug #739846.
exit 0
+5 -5
View File
@@ -4,16 +4,16 @@
## Desc: Helper functions for installing tools
################################################################################
function isUbuntu18
{
lsb_release -d | grep -q 'Ubuntu 18'
}
function isUbuntu20
{
lsb_release -d | grep -q 'Ubuntu 20'
}
function isUbuntu22
{
lsb_release -d | grep -q 'Ubuntu 22'
}
function getOSVersionLabel
{
lsb_release -cs
@@ -8,8 +8,8 @@
source $HELPER_SCRIPTS/install.sh
# Install Alibaba Cloud CLI
downloadUrl="https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz"
download_with_retries $downloadUrl "/tmp"
URL=$(curl -s https://api.github.com/repos/aliyun/aliyun-cli/releases/latest | jq -r '.assets[].browser_download_url | select(contains("aliyun-cli-linux") and endswith("amd64.tgz"))')
download_with_retries $URL "/tmp"
tar xzf /tmp/aliyun-cli-linux-*-amd64.tgz
mv aliyun /usr/local/bin
+14 -27
View File
@@ -34,37 +34,24 @@ function get_full_ndk_version {
# Set env variable for SDK Root (https://developer.android.com/studio/command-line/variables)
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
echo "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" | tee -a /etc/environment
# ANDROID_HOME is deprecated, but older versions of Gradle rely on it
echo "ANDROID_HOME=${ANDROID_SDK_ROOT}" | tee -a /etc/environment
# Set env variables for NDK Root
echo "ANDROID_NDK_HOME=${ANDROID_NDK_ROOT}" | tee -a /etc/environment
echo "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" | tee -a /etc/environment
# Create android sdk directory
mkdir -p ${ANDROID_SDK_ROOT}
cmdlineTools="android-cmdline-tools.zip"
# Download the latest command line tools so that we can accept all of the licenses.
# See https://developer.android.com/studio/#command-tools
cmdlineToolsVersion=$(get_toolset_value '.android."cmdline-tools"')
if [[ $cmdlineToolsVersion == "latest" ]]; then
repositoryXmlUrl="https://dl.google.com/android/repository/repository2-1.xml"
download_with_retries $repositoryXmlUrl "/tmp" "repository2-1.xml"
cmdlineToolsVersion=$(
yq -p=xml \
'.sdk-repository.remotePackage[] | select(."+@path" == "cmdline-tools;latest" and .channelRef."+@ref" == "channel-0").archives.archive[].complete.url | select(contains("commandlinetools-linux"))' \
/tmp/repository2-1.xml
)
if [[ -z $cmdlineToolsVersion ]]; then
echo "Failed to parse latest command-line tools version"
exit 1
fi
fi
download_with_retries "https://dl.google.com/android/repository/${cmdlineToolsVersion}" "." $cmdlineTools
cmdlineTools="android-cmdline-tools.zip"
download_with_retries https://dl.google.com/android/repository/commandlinetools-linux-7302050_latest.zip "." $cmdlineTools
unzip -qq $cmdlineTools -d ${ANDROID_SDK_ROOT}/cmdline-tools
# Command line tools need to be placed in ${ANDROID_SDK_ROOT}/sdk/cmdline-tools/latest to determine SDK root
mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/latest
@@ -87,6 +74,7 @@ addons=$(get_toolset_value '.android.addon_list[]|"add-ons;" + .')
additional=$(get_toolset_value '.android.additional_tools[]')
ANDROID_NDK_MAJOR_VERSIONS=($(get_toolset_value '.android.ndk.versions[]'))
ANDROID_NDK_MAJOR_DEFAULT=$(get_toolset_value '.android.ndk.default')
ndkDefaultFullVersion=$(get_full_ndk_version $ANDROID_NDK_MAJOR_DEFAULT)
components=("${extras[@]}" "${addons[@]}" "${additional[@]}")
for ndk_version in "${ANDROID_NDK_MAJOR_VERSIONS[@]}"
@@ -95,14 +83,13 @@ do
components+=("ndk;$ndk_full_version")
done
# This changes were added due to incompatibility with android ndk-bundle (ndk;22.0.7026061).
# Link issue virtual-environments: https://github.com/actions/virtual-environments/issues/2481
# Link issue xamarin-android: https://github.com/xamarin/xamarin-android/issues/5526
ln -s $ANDROID_SDK_ROOT/ndk/$ndkDefaultFullVersion $ANDROID_NDK_ROOT
ANDROID_NDK_MAJOR_LATEST=(${ANDROID_NDK_MAJOR_VERSIONS[-1]})
ndkDefaultFullVersion=$(get_full_ndk_version $ANDROID_NDK_MAJOR_DEFAULT)
ndkLatestFullVersion=$(get_full_ndk_version $ANDROID_NDK_MAJOR_LATEST)
ANDROID_NDK="$ANDROID_SDK_ROOT/ndk/$ndkDefaultFullVersion"
# ANDROID_NDK, ANDROID_NDK_HOME, and ANDROID_NDK_ROOT variables should be set as many customer builds depend on them https://github.com/actions/runner-images/issues/5879
echo "ANDROID_NDK=${ANDROID_NDK}" | tee -a /etc/environment
echo "ANDROID_NDK_HOME=${ANDROID_NDK}" | tee -a /etc/environment
echo "ANDROID_NDK_ROOT=${ANDROID_NDK}" | tee -a /etc/environment
echo "ANDROID_NDK_LATEST_HOME=$ANDROID_SDK_ROOT/ndk/$ndkLatestFullVersion" | tee -a /etc/environment
availablePlatforms=($($SDKMANAGER --list | sed -n '/Available Packages:/,/^$/p' | grep "platforms;android-[0-9]" | cut -d"|" -f 1))
@@ -115,7 +102,7 @@ filter_components_by_version $minimumBuildToolVersion "${availableBuildTools[@]}
echo "y" | $SDKMANAGER ${components[@]}
# Old skdmanager from sdk tools doesn't work with Java > 8, set version 8 explicitly
if isUbuntu20 || isUbuntu22; then
if isUbuntu20; then
sed -i "2i export JAVA_HOME=${JAVA_HOME_8_X64}" ${ANDROID_SDK_ROOT}/tools/bin/sdkmanager
fi
@@ -1,9 +0,0 @@
#!/bin/bash -e
################################################################################
## File: apt-vital.sh
## Desc: Installs vital command line utilities
################################################################################
source $HELPER_SCRIPTS/install.sh
vital_packages=$(get_toolset_value .apt.vital_packages[])
apt-get install -y --no-install-recommends $vital_packages
+13 -3
View File
@@ -8,9 +8,19 @@
source $HELPER_SCRIPTS/os.sh
source $HELPER_SCRIPTS/install.sh
download_with_retries "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" "/tmp" "awscliv2.zip"
unzip -qq /tmp/awscliv2.zip -d /tmp
/tmp/aws/install -i /usr/local/aws-cli -b /usr/local/bin
# Install the AWS CLI v1 Ubuntu18 and AWS CLI v2 on Ubuntu20
if isUbuntu20 ; then
download_with_retries "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" "/tmp" "awscliv2.zip"
unzip -qq /tmp/awscliv2.zip -d /tmp
/tmp/aws/install -i /usr/local/aws-cli -b /usr/local/bin
fi
# The installation should be run after python3 is installed as aws-cli V1 dropped python2 support
if isUbuntu18 ; then
download_with_retries "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" "/tmp" "awscli-bundle.zip"
unzip -qq /tmp/awscli-bundle.zip -d /tmp
python3 /tmp/awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws
fi
download_with_retries "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" "/tmp" "session-manager-plugin.deb"
apt install /tmp/session-manager-plugin.deb
@@ -1,6 +1,6 @@
#!/bin/bash -e
################################################################################
## File: apt-common.sh
## File: basic.sh
## Desc: Installs basic command line utilities and dev packages
################################################################################
source $HELPER_SCRIPTS/install.sh
+1 -1
View File
@@ -10,6 +10,6 @@ source $HELPER_SCRIPTS/install.sh
npm install -g @bazel/bazelisk
# run bazelisk once in order to instal /usr/local/bin/bazel binary
sudo -u $SUDO_USER bazel version
bazelisk
invoke_tests "Tools" "Bazel"
+1 -3
View File
@@ -11,7 +11,7 @@ function InstallClang {
local version=$1
echo "Installing clang-$version..."
apt-get install -y "clang-$version" "lldb-$version" "lld-$version" "clang-format-$version" "clang-tidy-$version"
apt-get install -y "clang-$version" "lldb-$version" "lld-$version" "clang-format-$version"
}
function SetDefaultClang {
@@ -21,8 +21,6 @@ function SetDefaultClang {
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${version} 100
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${version} 100
update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-${version} 100
update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-${version} 100
update-alternatives --install /usr/bin/run-clang-tidy run-clang-tidy /usr/bin/run-clang-tidy-${version} 100
}
versions=$(get_toolset_value '.clang.versions[]')
+4 -5
View File
@@ -4,16 +4,15 @@
## Desc: Installs CMake
################################################################################
# Source the helpers for use with the script
source $HELPER_SCRIPTS/install.sh
# Test to see if the software in question is already installed, if not install it
echo "Checking to see if the installer script has already been run"
if command -v cmake; then
echo "cmake is already installed"
else
downloadUrl=$(get_github_package_download_url "Kitware/CMake" "endswith(\"inux-x86_64.sh\")")
curl -sL ${downloadUrl} -o cmakeinstall.sh \
json=$(curl -s "https://api.github.com/repos/Kitware/CMake/releases")
latest_tag=$(echo $json | jq -r '.[] | select(.prerelease==false).tag_name' | sort --unique --version-sort | grep -v "rc" | tail -1)
sh_url=$(echo $json | jq -r ".[] | select(.tag_name==\"${latest_tag}\").assets[].browser_download_url | select(endswith(\"inux-x86_64.sh\"))")
curl -sL ${sh_url} -o cmakeinstall.sh \
&& chmod +x cmakeinstall.sh \
&& ./cmakeinstall.sh --prefix=/usr/local --exclude-subdir \
&& rm cmakeinstall.sh
@@ -7,39 +7,19 @@
source $HELPER_SCRIPTS/install.sh
# Retrieve the name of the CodeQL bundle preferred by the Action (in the format codeql-bundle-YYYYMMDD).
base_url="$(curl -sSL https://raw.githubusercontent.com/github/codeql-action/v2/src/defaults.json)"
codeql_tag_name="$(echo "$base_url" | jq -r '.bundleVersion')"
codeql_cli_version="$(echo "$base_url" | jq -r '.cliVersion')"
prior_codeql_tag_name="$(echo "$base_url" | jq -r '.priorBundleVersion')"
prior_codeql_cli_version="$(echo "$base_url" | jq -r '.priorCliVersion')"
codeql_bundle_name="$(curl -sSL https://raw.githubusercontent.com/github/codeql-action/v1/src/defaults.json | jq -r .bundleVersion)"
# Convert the bundle name to a version number (0.0.0-YYYYMMDD).
codeql_bundle_version="0.0.0-${codeql_bundle_name##*-}"
# Convert the tag names to bundles with a version number (x.y.z-YYYYMMDD).
codeql_bundle_version="${codeql_cli_version}-${codeql_tag_name##*-}"
prior_codeql_bundle_version="${prior_codeql_cli_version}-${prior_codeql_tag_name##*-}"
extraction_directory="$AGENT_TOOLSDIRECTORY/CodeQL/$codeql_bundle_version/x64"
mkdir -p "$extraction_directory"
# Download and name both CodeQL bundles.
codeql_bundle_versions=("${codeql_bundle_version}" "${prior_codeql_bundle_version}")
codeql_tag_names=("${codeql_tag_name}" "${prior_codeql_tag_name}")
echo "Downloading CodeQL bundle $codeql_bundle_version..."
download_with_retries "https://github.com/github/codeql-action/releases/download/$codeql_bundle_name/codeql-bundle.tar.gz" "/tmp" "codeql-bundle.tar.gz"
tar -xzf "/tmp/codeql-bundle.tar.gz" -C "$extraction_directory"
for index in "${!codeql_bundle_versions[@]}"; do
bundle_version="${codeql_bundle_versions[$index]}"
bundle_tag_name="${codeql_tag_names[$index]}"
echo "Downloading CodeQL bundle $bundle_version..."
download_with_retries "https://github.com/github/codeql-action/releases/download/$bundle_tag_name/codeql-bundle.tar.gz" "/tmp" "codeql-bundle.tar.gz"
codeql_archive="/tmp/codeql-bundle.tar.gz"
# Touch a special file that indicates to the CodeQL Action that this bundle was baked-in to the hosted runner images.
touch "$extraction_directory/pinned-version"
codeql_toolcache_path="$AGENT_TOOLSDIRECTORY/CodeQL/$bundle_version/x64"
mkdir -p "$codeql_toolcache_path"
echo "Unpacking the downloaded CodeQL bundle archive..."
tar -xzf "$codeql_archive" -C "$codeql_toolcache_path"
# We only pin the latest version in the toolcache, to support overriding the CodeQL version specified in defaults.json on GitHub Enterprise.
if [[ "$bundle_version" == "$codeql_bundle_version" ]]; then
touch "$codeql_toolcache_path/pinned-version"
fi
# Touch a file to indicate to the toolcache that setting up CodeQL is complete.
touch "$codeql_toolcache_path.complete"
done
# Touch a file to indicate to the toolcache that setting up CodeQL is complete.
touch "$extraction_directory.complete"
@@ -1,8 +1,5 @@
#!/bin/bash -e
# Source the helpers for use with the script
source $HELPER_SCRIPTS/os.sh
# Set ImageVersion and ImageOS env variables
echo ImageVersion=$IMAGE_VERSION | tee -a /etc/environment
echo ImageOS=$IMAGE_OS | tee -a /etc/environment
@@ -10,7 +7,7 @@ echo ImageOS=$IMAGE_OS | tee -a /etc/environment
# Set the ACCEPT_EULA variable to Y value to confirm your acceptance of the End-User Licensing Agreement
echo ACCEPT_EULA=Y | tee -a /etc/environment
# This directory is supposed to be created in $HOME and owned by user(https://github.com/actions/runner-images/issues/491)
# This directory is supposed to be created in $HOME and owned by user(https://github.com/actions/virtual-environments/issues/491)
mkdir -p /etc/skel/.config/configstore
echo 'XDG_CONFIG_HOME=$HOME/.config' | tee -a /etc/environment
@@ -32,10 +29,6 @@ chmod -R 777 $AGENT_TOOLSDIRECTORY
# https://www.suse.com/support/kb/doc/?id=000016692
echo 'vm.max_map_count=262144' | tee -a /etc/sysctl.conf
# https://kind.sigs.k8s.io/docs/user/known-issues/#pod-errors-due-to-too-many-open-files
echo 'fs.inotify.max_user_watches=655360' | tee -a /etc/sysctl.conf
echo 'fs.inotify.max_user_instances=1280' | tee -a /etc/sysctl.conf
# Create symlink for tests running
chmod +x $HELPER_SCRIPTS/invoke-tests.sh
ln -s $HELPER_SCRIPTS/invoke-tests.sh /usr/local/bin/invoke_tests
@@ -47,9 +40,3 @@ if [[ -f "/etc/fwupd/daemon.conf" ]]; then
sed -i 's/UpdateMotd=true/UpdateMotd=false/g' /etc/fwupd/daemon.conf
systemctl mask fwupd-refresh.timer
fi
# Disable to load providers
# https://github.com/microsoft/azure-pipelines-agent/issues/3834
if isUbuntu22; then
sed -i 's/openssl_conf = openssl_init/#openssl_conf = openssl_init/g' /etc/ssl/openssl.cnf
fi
+13 -18
View File
@@ -7,27 +7,22 @@
source $HELPER_SCRIPTS/os.sh
install_packages=(podman buildah skopeo)
REPO_URL="https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable"
# Packages is available in the official Ubuntu upstream starting from Ubuntu 21
if isUbuntu20; then
REPO_URL="https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable"
source /etc/os-release
sh -c "echo 'deb ${REPO_URL}/x${NAME}_${VERSION_ID}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list"
wget -nv https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/x${NAME}_${VERSION_ID}/Release.key -O Release.key
apt-key add Release.key
fi
# Install podman, buildah, scopeo container's tools
apt-get update
apt-get -y install ${install_packages[@]}
# Install podman, buildah, scopeo container's tools (on Ubuntu20 these tools can be installed without adding new repository)
source /etc/os-release
sh -c "echo 'deb ${REPO_URL}/x${NAME}_${VERSION_ID}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list"
wget -qnv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/x${NAME}_${VERSION_ID}/Release.key -O Release.key
apt-key add Release.key
apt-get update -qq
apt-get -qq -y install ${install_packages[@]}
mkdir -p /etc/containers
echo -e "[registries.search]\nregistries = ['docker.io', 'quay.io']" | tee /etc/containers/registries.conf
if isUbuntu20; then
# Remove source repo
rm /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
# Document source repo
echo "containers $REPO_URL" >> $HELPER_SCRIPTS/apt-sources.txt
fi
# Remove source repo
rm /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
# Document source repo
echo "containers $REPO_URL" >> $HELPER_SCRIPTS/apt-sources.txt
invoke_tests "Tools" "Containers"
+14 -27
View File
@@ -29,34 +29,21 @@ systemctl is-enabled --quiet docker.service || systemctl enable docker.service
sleep 10
docker info
if [ "${DOCKERHUB_PULL_IMAGES:-yes}" == "yes" ]; then
# If credentials are provided, attempt to log into Docker Hub
# with a paid account to avoid Docker Hub's rate limit.
if [ "${DOCKERHUB_LOGIN}" ] && [ "${DOCKERHUB_PASSWORD}" ]; then
docker login --username "${DOCKERHUB_LOGIN}" --password "${DOCKERHUB_PASSWORD}"
fi
# Pull images
images=$(get_toolset_value '.docker.images[]')
for image in $images; do
docker pull "$image"
done
# Always attempt to logout so we do not leave our credentials on the built
# image. Logout _should_ return a zero exit code even if no credentials were
# stored from earlier.
docker logout
else
echo "Skipping docker images pulling"
# If credentials are provided, attempt to log into Docker Hub
# with a paid account to avoid Docker Hub's rate limit.
if [ "${DOCKERHUB_LOGIN}" ] && [ "${DOCKERHUB_PASSWORD}" ]; then
docker login --username "${DOCKERHUB_LOGIN}" --password "${DOCKERHUB_PASSWORD}"
fi
# Install amazon-ecr-credential-helper
aws_latest_release_url="https://api.github.com/repos/awslabs/amazon-ecr-credential-helper/releases/latest"
aws_helper_url=$(curl "${authString[@]}" -sL $aws_latest_release_url | jq -r '.body' | awk -F'[()]' '/linux-amd64/ {print $2}')
download_with_retries "$aws_helper_url" "/usr/bin" docker-credential-ecr-login
chmod +x /usr/bin/docker-credential-ecr-login
# Pull images
images=$(get_toolset_value '.docker.images[]')
for image in $images; do
docker pull "$image"
done
# Always attempt to logout so we do not leave our credentials on the built
# image. Logout _should_ return a zero exit code even if no credentials were
# stored from earlier.
docker logout
invoke_tests "Tools" "Docker"
if [ "${DOCKERHUB_PULL_IMAGES:-yes}" -eq "yes" ]; then
invoke_tests "Tools" "Docker images"
fi
@@ -16,19 +16,6 @@ DOTNET_TOOLS=$(get_toolset_value '.dotnet.tools[].name')
# Disable telemetry
export DOTNET_CLI_TELEMETRY_OPTOUT=1
# There is a versions conflict, that leads to
# Microsoft <-> Canonical repos dependencies mix up.
# Give Microsoft's repo higher priority to avoid collisions.
# See: https://github.com/dotnet/core/issues/7699
cat << EOF > /etc/apt/preferences.d/dotnet
Package: *net*
Pin: origin packages.microsoft.com
Pin-Priority: 1001
EOF
apt-get update
for latest_package in ${LATEST_DOTNET_PACKAGES[@]}; do
echo "Determing if .NET Core ($latest_package) is installed"
if ! IsPackageInstalled $latest_package; then
@@ -39,10 +26,6 @@ for latest_package in ${LATEST_DOTNET_PACKAGES[@]}; do
fi
done
rm /etc/apt/preferences.d/dotnet
apt-get update
# Get list of all released SDKs from channels which are not end-of-life or preview
sdks=()
for version in ${DOTNET_VERSIONS[@]}; do
+8 -8
View File
@@ -8,22 +8,22 @@
source $HELPER_SCRIPTS/install.sh
source_list=/etc/apt/sources.list.d/eslerlang.list
source_key=/usr/share/keyrings/eslerlang.gpg
# Install Erlang
wget -q -O - https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc | gpg --dearmor > $source_key
echo "deb [signed-by=$source_key] https://packages.erlang-solutions.com/ubuntu $(lsb_release -cs) contrib" > $source_list
echo "deb https://binaries.erlang-solutions.com/debian $(lsb_release -cs) contrib" > $source_list
wget -q -O - https://binaries.erlang-solutions.com/debian/erlang_solutions.asc | apt-key add -
apt-get update
apt-get install --no-install-recommends esl-erlang
apt-get install -y --no-install-recommends esl-erlang
# Install rebar3
rebar3_url="https://github.com/erlang/rebar3/releases/latest/download/rebar3"
download_with_retries $rebar3_url "/usr/local/bin" "rebar3"
json=$(curl -sL "https://api.github.com/repos/erlang/rebar3/releases")
rebar3Version=$(echo $json | jq -r '.[] | select(.tag_name | contains("-") | not).tag_name' | sort -V | tail -n1)
rebar3DownloadUrl=$(echo $json | jq -r ".[] | select(.tag_name==\"$rebar3Version\").assets[].browser_download_url")
download_with_retries $rebar3DownloadUrl "/tmp"
mv /tmp/rebar3 /usr/local/bin/rebar3
chmod +x /usr/local/bin/rebar3
# Clean up source list
rm $source_list
rm $source_key
invoke_tests "Tools" "erlang"

Some files were not shown because too many files have changed in this diff Show More