[
  {
    "path": ".gitattributes",
    "content": "*   text=auto eol=lf"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "* @actions/runner-images-team\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/announcement.yml",
    "content": "name: Announcement\ndescription: Submit an announcement\nlabels: [Announcement]\nbody:\n  - type: textarea\n    attributes:\n      label: Breaking changes\n      placeholder: Short description of the upcoming change\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Target date\n      placeholder: Date of changes propagation start\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: The motivation for the changes\n      placeholder: Description of main reasons for this change\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Possible impact\n      placeholder: Description of who might be impacted by this change\n    validations:\n      required: true\n  - type: checkboxes\n    attributes:\n      label: Platforms affected\n      options:\n        - label: Azure DevOps\n        - label: GitHub Actions\n  - type: checkboxes\n    attributes:\n      label: Runner images affected\n      options:\n        - label: Ubuntu 22.04\n        - label: Ubuntu 24.04\n        - label: Ubuntu Slim\n        - label: macOS 14\n        - label: macOS 14 Arm64\n        - label: macOS 15\n        - label: macOS 15 Arm64\n        - label: macOS 26\n        - label: macOS 26 Arm64\n        - label: Windows Server 2022\n        - label: Windows Server 2025\n        - label: Windows Server 2025 with Visual Studio 2026\n  - type: textarea\n    attributes:\n      label: Mitigation ways\n      description: Steps or options for impact mitigation\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "content": "name: Bug Report\ndescription: Submit a bug report.\nlabels: [bug report, needs triage]\nbody:\n  - type: textarea\n    attributes:\n      label: Description\n      description: A clear and concise description of what the bug is, and why you consider it to be a bug.\n    validations:\n      required: true\n  - type: checkboxes\n    attributes:\n      label: Platforms affected\n      options:\n        - label: Azure DevOps\n        - label: GitHub Actions - Standard Runners\n        - label: GitHub Actions - Larger Runners\n  - type: checkboxes\n    attributes:\n      label: Runner images affected\n      options:\n        - label: Ubuntu 22.04\n        - label: Ubuntu 24.04\n        - label: Ubuntu Slim\n        - label: macOS 14\n        - label: macOS 14 Arm64\n        - label: macOS 15\n        - label: macOS 15 Arm64\n        - label: macOS 26\n        - label: macOS 26 Arm64\n        - label: Windows Server 2022\n        - label: Windows Server 2025\n        - label: Windows Server 2025 with Visual Studio 2026\n  - type: textarea\n    attributes:\n      label: Image version and build link\n      description: |\n        Image version where you are experiencing the issue. Where to find image version in build logs:\n        1. For GitHub Actions, under \"Set up job\" -> \"Runner Image\" -> \"Version\".\n        2. For Azure DevOps, under \"Initialize job\" -> \"Runner Image\" -> \"Version\".\n\n        If you have a public example, please, provide a link to the failed build.\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Is it regression?\n      description: If yes, please, provide the latest image version where the issue didn't persist, and a link to the latest successful build.\n    validations:\n        required: true\n  - type: textarea\n    attributes:\n      label: Expected behavior\n      description: A description of what you expected to happen.\n    validations:\n        required: true\n  - type: textarea\n    attributes:\n      label: Actual behavior\n      description: A description of what is actually happening.\n    validations:\n        required: true\n  - type: textarea\n    attributes:\n      label: Repro steps\n      placeholder: |\n        A description with steps to reproduce the issue.\n        1. Step 1\n        2. Step 2\n    validations:\n        required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n\ncontact_links:\n  - name: Get help in GitHub Discussions\n    url: https://github.com/actions/runner-images/discussions\n    about: Have a question? Feel free to ask in the runner-images GitHub Discussions!\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/tool-request.yml",
    "content": "name: Tool request\ndescription: Request a new tool or update to a tool\ntitle: Update/Add [tool name]\nlabels: [feature request, needs triage]\nbody:\n  - type: markdown\n    attributes:\n      value: \"## Tool information\"\n  - type: input\n    attributes:\n      label: Tool name\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Tool license\n      description: Type of licensing for desired tool.\n    validations:\n      required: true\n  - type: checkboxes\n    attributes:\n      label: Add or update?\n      options:\n        - label: Add\n        - label: Update\n  - type: input\n    attributes:\n      label: Desired version\n      description: Let us know if you're requesting a specific version, dev/RC, whatever is latest, etc.\n    validations:\n      required: true\n  - type: input\n    attributes:\n      label: Approximate size\n      description: Leave blank if unknown.\n  - type: markdown\n    attributes:\n      value: \"## If this is an add request\"\n  - type: textarea\n    attributes:\n      label: Brief description of tool\n  - type: input\n    attributes:\n      label: URL for tool's homepage\n  - type: textarea\n    attributes:\n      label: Provide a basic test case to validate the tool's functionality.\n      description: This will be automatically formatted into code.\n      render: bash\n  - type: checkboxes\n    attributes:\n      label: Platforms where you need the tool\n      options:\n        - label: Azure DevOps\n        - label: GitHub Actions\n  - type: checkboxes\n    attributes:\n      label: Runner images where you need the tool\n      options:\n        - label: Ubuntu 22.04\n        - label: Ubuntu 24.04\n        - label: Ubuntu Slim\n        - label: macOS 14\n        - label: macOS 14 Arm64\n        - label: macOS 15\n        - label: macOS 15 Arm64\n        - label: macOS 26\n        - label: macOS 26 Arm64\n        - label: Windows Server 2022\n        - label: Windows Server 2025\n        - label: Windows Server 2025 with Visual Studio 2026\n  - type: textarea\n    attributes:\n      label: Can this tool be installed during the build?\n      description: If so, please provide a description with required steps. This will be automatically formatted into code.\n      render: bash\n  - type: input\n    attributes:\n      label: Tool installation time in runtime\n      description: How long does it take to install the tool?\n  - type: input\n    attributes:\n      label: Are you willing to submit a PR?\n      description: We accept contributions!\n"
  },
  {
    "path": ".github/copilot-instructions.md",
    "content": "# GitHub Copilot Instructions for Actions Runner Images Repository\n\n## Scope and goals\n\n- This repository serves as the source for building GitHub Actions runner and Azure DevOps agent images for Windows, Ubuntu, and macOS. You can find exact versions in the [Available Images](../README.md#available-images) section of README.md. Windows and Ubuntu images build on Azure infrastructure using Packer; macOS images use Anka virtualization.\n- Emphasize best practices for contributing to open-source projects, including code style, commit messages, and pull request etiquette.\n- Prefer clarity and correctness over creativity. If information is missing, ask clarifying questions or insert TODOs instead of guessing.\n\n## Code and command instructions\n\n- Follow the code style guide in [CONTRIBUTING.md](../CONTRIBUTING.md#code-style-guide) for Bash and PowerShell scripts, including naming conventions, file structure, and indentation rules.\n- Focus on re-using helpers when writing scripts. Windows, Linux and Ubuntu scripts have helper functions available to simplify installation and validation.\n- Always confirm versions and installation paths against existing toolset files and installation scripts.\n\n## Output format\n\n- Use GitHub Flavored Markdown only. Avoid raw HTML unless necessary.\n- One H1 (`#`) per page, followed by logical, sequential headings (`##`, `###`, …).\n- Use fenced code blocks with language identifiers (` ```bash `, ` ```json `, ` ```yaml `, etc.).\n- Use blockquote callouts for notes:\n  > [!NOTE] Context or nuance  \n  > [!TIP] Helpful hint  \n  > [!WARNING] Risks or breaking changes  \n  > [!IMPORTANT] Critical requirement for functionality\n\n## Style and tone\n\n- Audience: Open-source contributors, GitHub Actions maintainers, and developers building custom runner images. Assume familiarity with CI/CD concepts, Packer, and basic infrastructure provisioning, but explain platform-specific details (Azure for Windows/Ubuntu, Anka for macOS) when relevant.\n- Voice: Second person (\"you\"), active voice, imperative for operational steps.\n- Be concise: short paragraphs and sentences. Prefer lists and step-by-steps, especially for operational procedures and troubleshooting.\n- Use inclusive, accessible language. Avoid idioms, sarcasm, and culturally specific references.\n- English: en-US (spelling, punctuation, and units).\n\n## Safety and integrity\n\n- Do not expose sensitive credentials (API tokens, Azure subscription IDs, etc.) in code examples.\n- Do not fabricate tool versions, installation paths, or software availability without verifying against toolset files or actual installation scripts.\n- Always call out assumptions and limitations explicitly, especially for changes affecting runner image behavior or software availability.\n- If ambiguous requests are made about image modifications, ask clarifying questions about target OS, tool versions, and compatibility requirements before proceeding.\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "# Description\nNew tool, Bug fixing, or Improvement?\nPlease include a summary of the change and which issue is fixed. Also include relevant motivation and context.\n**For new tools, please provide total size and installation time.**\n\n<!-- Currently, we can't accept external contributions to macOS source. Please find more details in [CONTRIBUTING.md](CONTRIBUTING.md#macOS) guide -->\n\n#### Related issue:\n\n## Check list\n- [ ] Related issue / work item is attached\n- [ ] Tests are written (if applicable)\n- [ ] Documentation is updated (if applicable)\n- [ ] Changes are tested and related VM images are successfully generated\n"
  },
  {
    "path": ".github/workflows/check-pinned-versions.yml",
    "content": "name: Check Outdated Version Pinning\n\non:\n  schedule:\n    - cron: '0 12 * * 1'  # Run at 12:00 UTC every Monday\n\npermissions:\n  issues: write\n  contents: read\n\njobs:\n  check-pinning-dates:\n    runs-on: ubuntu-slim\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v5\n\n      - name: Validate JSON Schema\n        shell: pwsh\n        run: ./helpers/CheckOutdatedVersionPinning.ps1\n        env:\n          GH_TOKEN: ${{ github.token }}\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [ main ]\n  schedule:\n    - cron: '32 4 * * 0'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      # only required for workflows in private repositories\n      actions: read\n      contents: read\n      # required for all workflows\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'python' ]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]\n        # Learn more about CodeQL language support at https://git.io/codeql-language-support\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v5\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v3\n      with:\n        languages: ${{ matrix.language }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n        # queries: ./path/to/local/query, your-org/your-repo/queries@main\n\n    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n    # If this step fails, then you should remove it and run the build manually (see below)\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v3\n\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 https://git.io/JvXDl\n\n    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n    #    and modify them (or add more) to build your code if your project\n    #    uses a compiled language\n\n    #- run: |\n    #   make bootstrap\n    #   make release\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v3\n"
  },
  {
    "path": ".github/workflows/create_github_release.yml",
    "content": "name: Create GitHub release\n\non:\n  repository_dispatch:\n    types: [create-github-release]\n\n\njobs:\n  Create_GitHub_release:\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Create release for ${{ github.event.client_payload.ReleaseBranchName }}\n      uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b #v1.20.0\n      with:\n        tag: ${{ github.event.client_payload.ReleaseBranchName }}\n        name: ${{ github.event.client_payload.ReleaseTitle }}\n        body: ${{ github.event.client_payload.ReleaseBody }}\n        prerelease: ${{ github.event.client_payload.Prerelease }}\n        commit: ${{ github.event.client_payload.Commitish }}\n        allowUpdates: true\n"
  },
  {
    "path": ".github/workflows/create_pull_request.yml",
    "content": "name: Create Pull Request\n\non:\n  repository_dispatch:\n    types: [create-pr]\n\n\njobs:\n  Create_pull_request:\n    runs-on: ubuntu-slim\n\n    steps:\n    - uses: actions/checkout@v5\n      with:\n        fetch-depth: 0\n\n    - name: Clone release branch to create pull request\n      run: |\n        git checkout ${{ github.event.client_payload.ReleaseBranchName }}\n        git branch ${{ github.event.client_payload.ReleaseBranchName }}-docs\n        git push origin ${{ github.event.client_payload.ReleaseBranchName }}-docs --force\n\n    - name: Create pull request for ${{ github.event.client_payload.ReleaseBranchName }}\n      id: create-pr\n      uses: actions/github-script@v8\n      with:\n        github-token: ${{secrets.GITHUB_TOKEN}}\n        script: |\n          const pulls = await github.rest.pulls.list({\n            owner: context.repo.owner,\n            repo: context.repo.repo,\n            head: `${context.repo.owner}:${{ github.event.client_payload.ReleaseBranchName }}-docs`,\n            base: \"${{ github.event.client_payload.PullRequestBase }}\",\n            state: 'open'\n          });\n\n          if (pulls.data.length > 0) {\n            console.log(`Pull request already exists: ${pulls.data[0].html_url}`);\n            return pulls.data[0].number;\n          } else {\n            console.log('No existing pull request found, creating new one');\n            let response = await github.rest.pulls.create({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              title: \"${{ github.event.client_payload.PullRequestTitle }}\",\n              head: \"${{ github.event.client_payload.ReleaseBranchName }}-docs\",\n              base: \"${{ github.event.client_payload.PullRequestBase }}\",\n              body: `${{ github.event.client_payload.PullRequestBody }}`\n            });\n            return response.data.number;\n          }\n\n    - name: Request reviewers\n      uses: actions/github-script@v8\n      with:\n        github-token: ${{secrets.PRAPPROVAL_SECRET}}\n        script: |\n          github.rest.pulls.requestReviewers({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              pull_number: ${{ steps.create-pr.outputs.result }},\n              team_reviewers: ['runner-images-team']\n          })\n"
  },
  {
    "path": ".github/workflows/create_sbom_report.yml",
    "content": "name: Create SBOM for the release\n\nrun-name: Collecting SBOM for ${{ github.event.client_payload.agentSpec || 'unknown image' }} - ${{ github.event.client_payload.imageVersion || 'unknown version' }}\n\non:\n  repository_dispatch:\n    types: [generate-sbom]\n\ndefaults:\n  run:\n    shell: pwsh\n\njobs:\n  sbom-check:\n    outputs:\n      check_status: ${{ steps.check.outputs.status }}\n    runs-on: ubuntu-latest\n    env:\n      RELEASE_ID: ${{ github.event.client_payload.ReleaseID }}\n    steps:\n    - name: Check SBOM asset for release ${{ env.RELEASE_ID }}\n      id: check\n      shell: pwsh\n      run: |\n        $apiUrl = \"https://api.github.com/repos/actions/runner-images/releases/$env:RELEASE_ID\"\n        $response = Invoke-RestMethod -Uri $apiUrl -Method Get -SkipHttpErrorCheck\n        if ($response.message -ilike \"Not Found\") {\n          echo \"status=release_not_found\" >> $env:GITHUB_OUTPUT\n          Write-Error \"Release $env:RELEASE_ID wasn't found\"\n          exit 1\n        }\n        foreach ($asset in $response.assets) {\n          if ($asset.name -like '*sbom*') {\n            echo \"status=sbom_exists\" >> $env:GITHUB_OUTPUT\n            return \"Release $env:RELEASE_ID already contains a SBOM\"\n          }\n        }\n        Write-Host \"Release has been found, SBOM is not attached, starting generation.\"\n        echo \"status=okay\" >> $env:GITHUB_OUTPUT\n\n  building-sbom:\n    needs: sbom-check\n    if: ${{ needs.sbom-check.outputs.check_status == 'okay' }}\n    runs-on: ${{ github.event.client_payload.agentSpec }}\n    env:\n      AGENT_SPEC: ${{ github.event.client_payload.agentSpec }}\n      RELEASE_ID: ${{ github.event.client_payload.ReleaseID }}\n      IMAGE_VERSION: ${{ github.event.client_payload.imageVersion }}\n    steps:\n      - name: Available image version check\n        run: |\n          $expectedVersion = $env:IMAGE_VERSION\n          $runnerVersion = $env:ImageVersion\n          \n          # Split versions by dot\n          $expectedParts = $expectedVersion.Split('.')\n          $runnerParts = $runnerVersion.Split('.')\n\n          # Determine what parts to compare\n          $minLength = [Math]::Min($expectedParts.Length, $runnerParts.Length)\n          $expectedComparable = $expectedParts[0..($minLength-1)] -join '.'\n          $runnerComparable = $runnerParts[0..($minLength-1)] -join '.'\n          \n          # Perform the comparison\n          if ($expectedComparable -ne $runnerComparable) {\n            throw \"Version mismatch: Expected version '$expectedVersion' doesn't match runner version '$runnerVersion'\"\n          }\n\n      - name: Install SYFT tool on Windows\n        if: ${{ runner.os == 'Windows' }}\n        run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b C:/syft\n\n      - name: Install SYFT tool on Ubuntu \n        if: ${{ runner.os == 'Linux' }}\n        run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin\n\n      - name: Install SYFT v1.24.0 on macOS\n        if: ${{ runner.os == 'macOS' }}\n        run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin v1.24.0\n      \n      - name: Run SYFT on Windows\n        if: ${{ runner.os == 'Windows' }}\n        run: C:/syft/syft dir:C:/ -vv -o spdx-json=sbom.json\n\n      - name: Run SYFT on Ubuntu\n        if: ${{ runner.os == 'Linux' }}\n        run: syft dir:/ -vv -o spdx-json=sbom.json\n\n      - name: Run SYFT on macOS\n        if: ${{ runner.os == 'macOS' }}\n        # Skip protected folders to avoid prompt privileges that block process indefinitely (https://github.com/anchore/syft/issues/1367)\n        run: sudo syft dir:/ -vv -o spdx-json=sbom.json --exclude ./Users --exclude ./System/Volumes --exclude ./private\n        shell: bash\n\n      - name: Compress SBOM file\n        run: Compress-Archive sbom.json sbom.json.zip\n\n      - uses: actions/upload-artifact@v4\n        with:\n          name: sbom-${{ env.AGENT_SPEC }}-${{ env.IMAGE_VERSION }}\n          path: sbom.json.zip\n          if-no-files-found: warn\n\n      - name: Upload release asset\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: \"https://uploads.github.com/repos/actions/runner-images/releases/${{ env.RELEASE_ID }}/assets{?name,label}\"\n          asset_path: ./sbom.json.zip\n          asset_name: sbom.${{ env.AGENT_SPEC }}.json.zip\n          asset_content_type: application/zip\n"
  },
  {
    "path": ".github/workflows/docker-images.yml",
    "content": "name: Test Docker Images\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      - 'images/ubuntu-slim/**'\n      - '.github/workflows/docker-images.yml'\n  pull_request:\n    paths:\n      - 'images/ubuntu-slim/**'\n      - '.github/workflows/docker-images.yml'\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\njobs:\n  test-images:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        directory:\n          - images/ubuntu-slim\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Run test.sh\n        working-directory: ${{ matrix.directory }}\n        run: ./test.sh\n"
  },
  {
    "path": ".github/workflows/linter.yml",
    "content": "# CI Validation\n\nname: Linter\n\non:\n  pull_request:\n    branches: [ main ]\n    paths:\n      - '**.json'\n      - '**.md'\n      - '**.sh'\n\njobs:\n  build:\n    name: Lint JSON & MD files\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout Code\n        uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n\n      - name: Lint Code Base\n        uses: github/super-linter/slim@v7\n        env:\n          VALIDATE_ALL_CODEBASE: false\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          VALIDATE_JSON: true\n          VALIDATE_MARKDOWN: true\n          DEFAULT_BRANCH: ${{ github.base_ref }}\n          FILTER_REGEX_EXCLUDE: .*images/*/.*-Readme.md\n\n      - name: Checking shebang lines in MacOS and Ubuntu releases.\n        run: ./images.CI/shebang-linter.ps1\n        shell: pwsh\n"
  },
  {
    "path": ".github/workflows/merge_pull_request.yml",
    "content": "name: Merge pull request\n\non:\n  repository_dispatch:\n    types: [merge-pr]\n\n\njobs:\n  Merge_pull_request:\n    runs-on: ubuntu-slim\n\n    steps:\n    - uses: actions/checkout@v5\n      with:\n        fetch-depth: 0\n\n    - name: Resolve possible conflicts ${{ github.event.client_payload.ReleaseBranchName }} with main\n      run: |\n        git config --global user.email \"no-reply@github.com\"\n        git config --global user.name \"Actions service account\"\n        git checkout ${{ github.event.client_payload.ReleaseBranchName }}-docs\n        git merge --no-edit --strategy-option=ours main\n        git push origin ${{ github.event.client_payload.ReleaseBranchName }}-docs\n        sleep 30\n\n    - name: Approve pull request by GitHub-Actions bot\n      uses: actions/github-script@v8\n      with:\n        github-token: ${{secrets.PRAPPROVAL_SECRET}}\n        script: |\n          github.rest.pulls.createReview({\n            owner: context.repo.owner,\n            repo: context.repo.repo,\n            pull_number: ${{ github.event.client_payload.PullRequestNumber }},\n            event: \"APPROVE\"\n          });\n\n    - name: Merge pull request for ${{ github.event.client_payload.ReleaseBranchName }}\n      uses: actions/github-script@v8\n      with:\n        github-token: ${{secrets.GITHUB_TOKEN}}\n        script: |\n          github.rest.pulls.merge({\n            owner: context.repo.owner,\n            repo: context.repo.repo,\n            pull_number: ${{ github.event.client_payload.PullRequestNumber }},\n            merge_method: \"squash\"\n          })\n"
  },
  {
    "path": ".github/workflows/powershell-tests.yml",
    "content": "# CI Validation\n\nname: PowerShell Tests\n\non:\n  pull_request:\n    branches: [ main ]\n    paths:\n      - 'helpers/software-report-base/**'\n\njobs:\n  powershell-tests:\n    name: PowerShell tests\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@v5\n\n      - name: Run Software Report module tests\n        shell: pwsh\n        run: |\n            $ErrorActionPreference = \"Stop\"\n            Invoke-Pester -Output Detailed \"helpers/software-report-base/tests\"\n        "
  },
  {
    "path": ".github/workflows/trigger-ubuntu-win-build.yml",
    "content": "name: Trigger Build workflow\n\non:\n  workflow_call:\n    inputs:\n      image_type:\n        required: true\n        type: string\n\ndefaults:\n  run:\n    shell: pwsh\n\njobs:\n  trigger-workflow:\n    runs-on: ubuntu-latest\n    outputs:\n      ci_workflow_run_id: ${{ steps.resolve.outputs.ci_workflow_run_id }}\n      ci_workflow_run_url: ${{ steps.resolve.outputs.ci_workflow_run_url }}\n    env:\n      CI_PR_TOKEN: ${{ secrets.CI_PR_TOKEN }}\n      PR_TITLE: ${{ github.event.pull_request.title }}\n      CI_REPO: ${{ vars.CI_REPO }}\n    steps:\n      - name: Checkout Code\n        uses: actions/checkout@v5\n\n      - name: Trigger Build workflow\n        run: |\n          Import-Module ./helpers/GitHubApi.psm1\n          $gitHubApi = Get-GithubApi -Repository \"${env:CI_REPO}\" -AccessToken \"${env:CI_PR_TOKEN}\"\n\n          $eventType = \"trigger-${{ inputs.image_type }}-build\"\n          [string] $prGuid = New-Guid\n          $clientPayload = @{\n              pr_title                = \"${env:PR_TITLE} - \" + $prGuid\n              custom_repo             = \"${{ github.event.pull_request.head.repo.full_name }}\"\n              custom_repo_commit_hash = \"${{ github.event.pull_request.head.sha }}\"\n          }\n\n          $gitHubApi.DispatchWorkflow($eventType, $clientPayload)\n          \"PR_GUID=$prGuid\" | Out-File -Append -FilePath $env:GITHUB_ENV\n\n      - name: Resolve Workflow Run ID\n        id: resolve\n        run: |\n          Import-Module ./helpers/GitHubApi.psm1\n          $gitHubApi = Get-GithubApi -Repository \"${env:CI_REPO}\" -AccessToken \"${env:CI_PR_TOKEN}\"\n\n          $workflowFileName = $(\"{0}.yml\" -f \"${{ inputs.image_type }}\").ToLower()\n          $WorkflowSearchPattern = \"${env:PR_GUID}\"\n\n          # It might take a few minutes for the action to start\n          $attempt = 1\n          do {\n            $workflowRuns = $gitHubApi.GetWorkflowRuns($WorkflowFileName).workflow_runs\n            $workflowRunId = ($workflowRuns | Where-Object {$_.display_title -match $WorkflowSearchPattern}).id | Select-Object -First 1\n\n            if (-not ([string]::IsNullOrEmpty($workflowRunId))) {\n              $workflowRun = $gitHubApi.GetWorkflowRun($workflowRunId)\n              Write-Host \"Found the workflow run with ID $workflowRunId on attempt $attempt. Workflow run link: $($workflowRun.html_url)\"\n              \"ci_workflow_run_id=$workflowRunId\" | Out-File -Append -FilePath $env:GITHUB_OUTPUT\n              \"ci_workflow_run_url=$($workflowRun.html_url)\" | Out-File -Append -FilePath $env:GITHUB_OUTPUT\n              break\n            }\n\n            Write-Host \"Workflow run for $WorkflowSearchPattern pattern not found on attempt $attempt.\"\n            $attempt += 1\n            Start-Sleep 30\n          } until ($attempt -eq 10)\n\n          if ([string]::IsNullOrEmpty($workflowRunId)) {\n              throw \"Failed to find a workflow run for '$WorkflowSearchPattern'.\"\n          }\n\n  wait-completion:\n    runs-on: ubuntu-latest\n    needs: trigger-workflow\n    steps:\n      - name: Checkout Code\n        uses: actions/checkout@v5\n\n      - name: Wait for workflow completion\n        env:\n          CI_PR_TOKEN: ${{ secrets.CI_PR_TOKEN }}\n          CI_REPO: ${{ vars.CI_REPO }}\n        run: |\n          ./helpers/WaitWorkflowCompletion.ps1 `\n            -WorkflowRunId \"${{ needs.trigger-workflow.outputs.ci_workflow_run_id }}\" `\n            -Repository \"${env:CI_REPO}\" `\n            -AccessToken \"${env:CI_PR_TOKEN}\"\n\n      - name: Add Summary\n        if: always()\n        run: |\n          \"# Test Partner Image\" >> $env:GITHUB_STEP_SUMMARY\n          \"| Key | Value |\" >> $env:GITHUB_STEP_SUMMARY\n          \"| :-----------: | :--------: |\" >> $env:GITHUB_STEP_SUMMARY\n          \"| Workflow Run | [Link](${{ needs.trigger-workflow.outputs.ci_workflow_run_url }}) |\" >> $env:GITHUB_STEP_SUMMARY\n          \"| Workflow Result | $env:CI_WORKFLOW_RUN_RESULT |\" >> $env:GITHUB_STEP_SUMMARY\n          \"  \" >> $env:GITHUB_STEP_SUMMARY\n\n  cancel-workflow:\n    runs-on: ubuntu-latest\n    needs: [trigger-workflow, wait-completion]\n    if: cancelled()\n    steps:\n      - name: Checkout Code\n        uses: actions/checkout@v5\n\n      - name: Cancel workflow\n        env:\n          CI_PR_TOKEN: ${{ secrets.CI_PR_TOKEN }}\n          CI_REPO: ${{ vars.CI_REPO }}\n        run: |\n          Import-Module ./helpers/GitHubApi.psm1\n\n          $gitHubApi = Get-GithubApi -Repository \"${env:CI_REPO}\" -AccessToken \"${env:CI_PR_TOKEN}\"\n          $gitHubApi.CancelWorkflowRun(\"${{ needs.trigger-workflow.outputs.ci_workflow_run_id }}\")\n"
  },
  {
    "path": ".github/workflows/ubuntu2204.yml",
    "content": "name: Trigger Ubuntu22.04 CI\nrun-name: Ubuntu22.04 - ${{ github.event.pull_request.title }}\n\non:\n  pull_request_target:\n    types: labeled\n    paths:\n    - 'images/ubuntu/**'\n\ndefaults:\n  run:\n    shell: pwsh\n\njobs:\n  Ubuntu_2204:\n    if: github.event.label.name == 'CI ubuntu-all' || github.event.label.name == 'CI ubuntu-2204'\n    uses: ./.github/workflows/trigger-ubuntu-win-build.yml\n    with:\n      image_type: 'ubuntu2204'\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/ubuntu2404.yml",
    "content": "name: Trigger Ubuntu24.04 CI\nrun-name: Ubuntu24.04 - ${{ github.event.pull_request.title }}\n\non:\n  pull_request_target:\n    types: labeled\n    paths:\n    - 'images/ubuntu/**'\n\ndefaults:\n  run:\n    shell: pwsh\n\njobs:\n  Ubuntu_2404:\n    if: github.event.label.name == 'CI ubuntu-all' || github.event.label.name == 'CI ubuntu-2404'\n    uses: ./.github/workflows/trigger-ubuntu-win-build.yml\n    with:\n      image_type: 'ubuntu2404'\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/update_github_release.yml",
    "content": "name: Update release\n\non:\n  repository_dispatch:\n    types: [update-github-release]\n\n\njobs:\n  Update_GitHub_release:\n    runs-on: ubuntu-slim\n\n    steps:\n    - name: Update release for ${{ github.event.client_payload.ReleaseBranchName }}\n      uses: actions/github-script@v8\n      with:\n        github-token: ${{secrets.GITHUB_TOKEN}}\n        script: |\n            const response = await github.rest.repos.getReleaseByTag({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              tag: \"${{ github.event.client_payload.ReleaseBranchName }}\"\n            });\n            github.rest.repos.updateRelease({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              release_id: response.data.id,\n              prerelease: ${{ github.event.client_payload.Prerelease }}\n            });\n"
  },
  {
    "path": ".github/workflows/validate-json-schema.yml",
    "content": "name: Validate JSON Schema\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  validate-json-schema:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v5\n\n      - name: Validate JSON Schema\n        shell: pwsh\n        run: ./helpers/CheckJsonSchema.ps1\n"
  },
  {
    "path": ".github/workflows/windows2022.yml",
    "content": "name: Trigger Windows22 CI\nrun-name: Windows2022 - ${{ github.event.pull_request.title }}\n\non:\n  pull_request_target:\n    types: labeled\n    paths:\n    - 'images/windows/**'\n\ndefaults:\n  run:\n    shell: pwsh\n\njobs:\n  Windows_2022:\n    if: github.event.label.name == 'CI windows-all' || github.event.label.name == 'CI windows-2022'\n    uses: ./.github/workflows/trigger-ubuntu-win-build.yml\n    with:\n      image_type: 'windows2022'\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/windows2025-vs2026.yml",
    "content": "name: Trigger Windows25 with VS 2026 CI\nrun-name: Windows2025 with VS 2026 - ${{ github.event.pull_request.title }}\n\non:\n  pull_request_target:\n    types: labeled\n    paths:\n    - 'images/windows/**'\n\ndefaults:\n  run:\n    shell: pwsh\n\njobs:\n  Windows_2025_vs_2026:\n    if: github.event.label.name == 'CI windows-all' || github.event.label.name == 'CI windows-2025-vs2026'\n    uses: ./.github/workflows/trigger-ubuntu-win-build.yml\n    with:\n      image_type: 'windows2025-vs2026'\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/windows2025.yml",
    "content": "name: Trigger Windows25 CI\nrun-name: Windows2025 - ${{ github.event.pull_request.title }}\n\non:\n  pull_request_target:\n    types: labeled\n    paths:\n    - 'images/windows/**'\n\ndefaults:\n  run:\n    shell: pwsh\n\njobs:\n  Windows_2025:\n    if: github.event.label.name == 'CI windows-all' || github.event.label.name == 'CI windows-2025'\n    uses: ./.github/workflows/trigger-ubuntu-win-build.yml\n    with:\n      image_type: 'windows2025'\n    secrets: inherit\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n**/Properties/launchSettings.json\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Typescript v1 declaration files\ntypings/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# VSCode settings\n.vscode/**\n!.vscode/extensions.json\n!.vscode/settings.json\n!.vscode/tasks.json\n\n# CodeRush\n.cr/\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# Ignore files generated by packer\nInstalledSoftware.md\n\n# Desktop Service Store\n.DS_Store\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n\n# next.js build output\n.next\n\n# nuxt.js build output\n.nuxt\n\n# gatsby files\n.cache/\npublic\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# visual studio code launch configuration\nlaunch.json\n\n# Ignore dynamic template\nimages/*/*-temp.json\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\n    \"streetsidesoftware.code-spell-checker\",\n    \"hashicorp.hcl\",\n    \"davidanson.vscode-markdownlint\",\n    \"ms-vscode.powershell\",\n    \"timonwong.shellcheck\"\n  ]\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"files.trimFinalNewlines\": true,\n  \"files.insertFinalNewline\": true,\n  \"powershell.codeFormatting.addWhitespaceAroundPipe\": true,\n  \"powershell.codeFormatting.alignPropertyValuePairs\": true,\n  \"powershell.codeFormatting.autoCorrectAliases\": true,\n  \"powershell.codeFormatting.newLineAfterCloseBrace\": false,\n  \"powershell.codeFormatting.newLineAfterOpenBrace\": true,\n  \"powershell.codeFormatting.openBraceOnSameLine\": true,\n  \"powershell.codeFormatting.pipelineIndentationStyle\": \"IncreaseIndentationForFirstPipeline\",\n  \"powershell.codeFormatting.preset\": \"OTBS\",\n  \"powershell.codeFormatting.trimWhitespaceAroundPipe\": true,\n  \"powershell.codeFormatting.whitespaceAfterSeparator\": true,\n  \"powershell.codeFormatting.whitespaceAroundOperator\": true,\n  \"powershell.codeFormatting.whitespaceBeforeOpenBrace\": true,\n  \"powershell.codeFormatting.whitespaceBeforeOpenParen\": true,\n  \"powershell.codeFormatting.whitespaceBetweenParameters\": true,\n  \"powershell.codeFormatting.whitespaceInsideBrace\": true,\n  \"shellcheck.exclude\": [\n    \"SC1090\",\"SC2096\"\n  ],\n  \"shellcheck.customArgs\": [\n    \"-x\"\n  ],\n  \"json.schemas\": [\n    {\n        \"fileMatch\": [\n            \"**/toolset-*.json\"\n        ],\n        \"url\": \"./schemas/toolset-schema.json\"\n    }\n]\n\n}\n"
  },
  {
    "path": ".vscode/tasks.json",
    "content": "// Available variables which can be used inside of strings.\n// ${workspaceRoot}: the root folder of the team\n// ${file}: the current opened file\n// ${relativeFile}: the current opened file relative to workspaceRoot\n// ${fileBasename}: the current opened file's basename\n// ${fileDirname}: the current opened file's dirname\n// ${fileExtname}: the current opened file's extension\n// ${cwd}: the current working directory of the spawned process\n{\n    // See https://go.microsoft.com/fwlink/?LinkId=733558\n    // for the documentation about the tasks.json format\n\t\"version\": \"2.0.0\",\n\n    // Start PowerShell\n    \"windows\": {\n        \"command\": \"${env:windir}/System32/WindowsPowerShell/v1.0/powershell.exe\",\n        //\"command\": \"${env:ProgramFiles}/PowerShell/6.0.0/powershell.exe\",\n        \"args\": [ \"-NoProfile\", \"-ExecutionPolicy\", \"Bypass\" ]\n    },\n    \"linux\": {\n        \"command\": \"/usr/bin/powershell\",\n        \"args\": [ \"-NoProfile\" ]\n    },\n    \"osx\": {\n        \"command\": \"/usr/local/bin/powershell\",\n        \"args\": [ \"-NoProfile\" ]\n    },\n\n    // Associate with test task runner\n    \"tasks\": [\n        {\n            \"taskName\": \"Test\",\n            \"suppressTaskName\": true,\n            \"isTestCommand\": true,\n            \"args\": [\n                \"Write-Host 'Invoking Pester...'; $ProgressPreference = 'SilentlyContinue'; Invoke-Pester -Script test -PesterOption @{IncludeVSCodeMarker=$true};\",\n                \"Invoke-Command { Write-Host 'Completed Test task in task runner.' }\"\n            ],\n            \"problemMatcher\": \"$pester\"\n        }\n\t]\n}\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to make participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies within all project spaces, and it also applies when\nan individual is representing the project or its community in public spaces.\nExamples of representing a project or community include using an official\nproject e-mail address, posting via an official social media account, or acting\nas an appointed representative at an online or offline event. Representation of\na project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at opensource@github.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n[fork]: https://github.com/actions/runner-images/fork\n[pr]: https://github.com//actions/runner-images/compare\n[code-of-conduct]: CODE_OF_CONDUCT.md\n\nHi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.\n\nContributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [MIT](LICENSE.md) license.\n\nPlease note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project, you agree to abide by its terms.\n\n## Contents\n\n- [Submitting a pull request](#submitting-a-pull-request)\n- [Adding a new tool to an image](#adding-a-new-tool-to-an-image)\n- [Code style guide](#code-style-guide)\n\n## Submitting a pull request\n\n1. [Fork][fork] and clone the repository.\n1. Create a new branch: `git checkout -b my-branch-name`.\n1. Make your changes, ensuring that they include steps to install, validate post-install, and update the software report (please see [Adding a new tool to an image](#adding-a-new-tool-to-an-image) for details).\n1. Test your changes by [creating an image and deploying a VM](docs/create-image-and-azure-resources.md).\n1. Push to your fork and [submit a pull request][pr].\n\nHere are a few things you can do that will increase the likelihood of your pull request being accepted:\n\n- Follow the style guide for [Powershell](https://github.com/PoshCode/PowerShellPracticeAndStyle) when writing Windows scripts. There is currently no set style for the Shell scripts that run Linux installs :soon:.\n- Include complete details of why this is needed in the PR description.\n- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.\n- Write [good commit messages](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).\n- For new tools:\n  - Make sure that the tool satisfies the [Software Guidelines](README.md#software-guidelines).\n  - Create an issue and get approval from us to add this tool to the image before creating the pull request.\n\n## Adding a new tool to an image\n\n### General rules\n\n- For every new tool, add validation scripts and update the software report script to ensure that it is included in the documentation.\n- If the tool is available on multiple platforms (macOS, Windows, Linux), make sure you include it on as many as possible.\n- If installing multiple versions of the tool, consider putting the list of versions in the corresponding `toolset.json` file. This will help other customers configure their builds flexibly. See [toolset-windows-2022.json](images/windows/toolsets/toolset-2022.json) as an example.\n- Use consistent naming across all files.\n- Validation scripts should be simple and shouldn't change the image content.\n\n### Windows\n\n- Add a script that will install the tool and put the script in the `scripts/build` folder.\nThere are a bunch of helper functions that could simplify your code: `Install-ChocoPackage`, `Install-Binary`, `Install-VSIXFromFile`, `Install-VSIXFromUrl`, `Invoke-DownloadWithRetry`, `Test-IsWin22`, `Test-IsWin25` (find the full list of helpers in [ImageHelpers.psm1](images/windows/scripts/helpers/ImageHelpers.psm1)).\n- Add a script that will validate the tool installation and put the script in the `scripts/tests` folder.\nWe 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.\nAdd `Invoke-PesterTests -TestFile <testFileName> [-TestName <describeName>]` at the end of the installation script to ensure that your tests will be run.\n- Add changes to the software report generator `images/windows/scripts/docs-gen/Generate-SoftwareReport.ps1`. The software report generator is used to generate an image's README file, e.g. [Windows2022-Readme.md](images/windows/Windows2022-Readme.md) and uses [MarkdownPS](https://github.com/Sarafian/MarkdownPS).\n\n### Ubuntu\n\n- Add a script that will install and validate the tool and put the script in the `scripts/build` folder.\nUse existing scripts such as [github-cli.sh](images/ubuntu/scripts/build/github-cli.sh) as a starting point.\n  - Use [helpers](images/ubuntu/scripts/helpers/install.sh) to simplify the installation process.\n  - The validation part should `exit 1` if there is any issue with the installation.\n- Add changes to the software report generator `images/ubuntu/scripts/docs-gen/Generate-SoftwareReport.ps1`. The software report generator is used to generate an image's README file, e.g. [Ubuntu2204-Readme.md](images/ubuntu/Ubuntu2204-Readme.md)   and it uses [MarkdownPS](https://github.com/Sarafian/MarkdownPS).\n\n### macOS\n\nThe macOS source lives in this repository and is available for everyone. However, the macOS image-generation CI doesn't support external contributions yet, so we are not able to accept pull requests for now.\nWe are in the process of preparing the macOS CI to accept contributions. Until then, we appreciate your patience and ask that you continue to make tool requests by filing issues.\n\n## Code style guide\n\nThe principles of clean code apply to all languages. The main points are:\n\n- Use meaningful names for variables, functions, files, etc.\n- Keep functions short and simple.\n- Use comments to explain what the code does.\n- Use a consistent code style, naming convention, and file structure.\n\n### File structure\n\n- Each file should have a header with a title and a short description of the file.\n- Each file should have a newline at the end.\n- Use blank lines to separate logical blocks of code, but don't abuse blank lines:\n  - Don't add a blank line in the beginning and end of a block or function.\n  - Don't add blank lines between logically connected statements.\n- Avoid trailing whitespace.\n\n### Bash scripts\n\n#### Naming convention for bash scripts\n\n- Use lowercase letters for variable names.\n- Use uppercase letters for constants.\n- Use underscores to separate words in variable names.\n\n#### Bash script structure\n\nEach script should start with the following shebang:\n\n```bash\n#!/bin/bash -e\n```\n\n> TODO: do we need to set pipefail?\n\nThis will make the script exit if any command fails.\n\nAfter the shebang, add a header with the following format:\n\n```bash\n################################################################################\n##  File:  <filename>\n##  Desc:  <short description of what the script does>\n################################################################################\n```\n\nThen import helpers that are used in the script.\n\nFor Linux:\n\n```bash\nsource $HELPER_SCRIPTS/os.sh\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n```\n\nFor macOS:\n\n```bash\nsource ~/utils/utils.sh\n```\n\n> [!NOTE]\n> You don't need to import all helpers, only the ones that are used in the script.\n\nAfter that, add the script code.\n\n### Indentations and line breaks in bash scripts\n\n- Use 4 spaces for indentation.\n- Use 1 space between `if`/`for`/`while` and `[[` and between `[[` and the condition.\n- Place `then`/`do` on the new line.\n- For short `if`/`for`/`while` statements, use the one-line format.\n- Break long pipelines using `\\`.\n\n### Other recommendations for bash scripts\n\n- For command substitution, use `$()` instead of backticks.\n- Use `[[` instead of `[` for conditional expressions.\n- Prefer using long options instead of short keys, but there are exceptions, e.g.:\n  - `tar -xzf`\n  - `apt-get -yqq`\n  - `curl -sSLf`\n  - `wget -qO-`\n\n### PowerShell scripts\n\n#### Naming convention for PowerShell scripts\n\n- Use camelCase for variable names.\n- Use uppercase letters for constants.\n- Use `Verb-Noun` and PascalCase for function names.\n\n### PowerShell script structure\n\nEach script should start with the following header:\n\n```powershell\n################################################################################\n##  File:  <filename>\n##  Desc:  <short description of what the script does>\n################################################################################\n```\n\nThen declare functions that are used in the script.\n\n> TODO: do we need to set the error action preference and progress preference?\n>\n> ```powershell\n> $ErrorActionPreference = \"Stop\"\n> $ProgressPreference = \"SilentlyContinue\"\n> ```\n\nFor Linux and macOS, import helpers that are used in the script:\n\nFor Linux:\n\n```powershell\nImport-Module \"$env:HELPER_SCRIPTS/Tests.Helpers.psm1\" -DisableNameChecking\n```\n\nFor macOS:\n\n```powershell\nImport-Module \"$env:HOME/image-generation/helpers/Common.Helpers.psm1\"\nImport-Module \"$env:HOME/image-generation/helpers/Xcode.Helpers.psm1\" -DisableNameChecking\n```\n\n> [!NOTE]\n> You don't need to import all helpers, only the ones that are used in the script.\n\nAfter that, add the script code.\n\n### Indentations and line breaks in PowerShell scripts\n\n- Use 4 spaces for indentation.\n- Use 1 space between `if`/`elseif`/`foreach` and `(` but not between `(` and the condition.\n- Add a space before and after pipe `|` and redirection `>` operators.\n- Align properties in hash tables.\n- Use [1TBS](https://en.wikipedia.org/wiki/Indentation_style#Variant:_1TBS_(OTBS)) style for curly braces:\n  - If block of statement is long, then place it on the new line, indent it, and add a closing curly brace on the new line.\n  - If block of statement is short, then place it on the same line as the statement.\n\n  ```powershell\n  function Show-Example1 {\n      $exampleVariable = Get-ChildItem $env:TEMP\n      $exampleVariable | ForEach-Object {\n          $itemName = $_.Name\n          $itemPath = $_.FullName\n      }\n  }\n\n  $Example2 | Some-Function -Arguments @{Parameter1 = \"Disabled\"}\n  ```\n\n- Avoid using aliases.\n- Break long pipelines using backticks or use [splatting](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_splatting?view=powershell-7.3):\n\n  ```powershell\n  # Instead of this\n  Copy-Item -Path \"test.txt\" -Destination \"test2.txt\" -WhatIf\n\n  # you can use this\n  $HashArguments = @{\n    Path = \"test.txt\"\n    Destination = \"test2.txt\"\n    WhatIf = $true\n  }\n  Copy-Item @HashArguments\n  ```\n\n  When using backticks be extra careful with trailing whitespace as they can cause errors.\n\n### Other recommendations for PowerShell scripts\n\n- Verify exit codes of commands.\n- When writing a function, provide a docstring that describes what the function does.\n\n## Resources\n\n- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)\n- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)\n- [GitHub Help](https://help.github.com)\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2026 GitHub\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# GitHub Actions Runner Images\n\n**Table of Contents**\n\n- [About](#about)\n- [Available Images](#available-images)\n- [Announcements](#announcements)\n- [Image Definitions](#image-definitions)\n- [Image Releases](#image-releases)\n- [Software and Image Support](#software-and-image-support)\n- [How to Interact with the Repo](#how-to-interact-with-the-repo)\n- [FAQs](#faqs)\n\n## About\n\nThis 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.\nTo build a VM machine from this repo's source, see the [instructions](docs/create-image-and-azure-resources.md).\n\n## Available Images\n\n| Image | Architecture | YAML Label | Included Software |\n| --------------------|--------------|---------------------|------------------|\n| Ubuntu 24.04<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fubuntu24.json) | x64 | `ubuntu-latest` or `ubuntu-24.04` | [ubuntu-24.04] |\n| Ubuntu 22.04<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fubuntu22.json) | x64 | `ubuntu-22.04` | [ubuntu-22.04] |\n| Ubuntu Slim ![preview](https://img.shields.io/badge/preview-0969DA?style=flat&logoColor=white)<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fubuntu-slim.json) | x64 | `ubuntu-slim` | [ubuntu-slim] |\n| macOS 26 Arm64<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fmacos-26-arm64.json) | arm64 | `macos-26` or `macos-26-xlarge` | [macOS-26-arm64] |\n| macOS 26<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fmacos-26.json) | x64 | `macos-26-intel`, `macos-26-large` | [macOS-26] |\n| macOS 15<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fmacos-15.json) | x64 | `macos-latest-large`, `macos-15-large`, or `macos-15-intel` | [macOS-15] |\n| macOS 15 Arm64<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fmacos-15-arm64.json) | arm64 | `macos-latest`, `macos-15`, or `macos-15-xlarge` | [macOS-15-arm64] |\n| macOS 14<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fmacos-14.json) | x64 | `macos-14-large`| [macOS-14] |\n| macOS 14 Arm64<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fmacos-14-arm64.json) | arm64 | `macos-14` or `macos-14-xlarge`| [macOS-14-arm64] |\n| Windows Server 2025 with Visual Studio 2026 ![beta](https://img.shields.io/badge/beta-yellow)<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fwin25-vs2026.json) | x64 | `windows-2025-vs2026` | [windows-2025-vs2026] |\n| Windows Server 2025<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fwin25.json) | x64 | `windows-latest` or `windows-2025` | [windows-2025] |\n| Windows Server 2022<br>![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fhosted-runners-images-bot%2F79267492faab096d04cdd25ce7014cec%2Fraw%2Fwin22.json) | x64 | `windows-2022` | [windows-2022] |\n\n### Label scheme\n\n- In general the `-latest` label is used for the latest OS image version that is GA.\n- 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.\n- The `-xlarge` and `-large` suffixes are unique to macOS images and are only available for GitHub Actions. Learn more about [GitHub Actions larger runners](https://docs.github.com/en/actions/reference/runners/larger-runners#available-macos-larger-runners-and-labels).\n\n[ubuntu-24.04]: https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md\n[ubuntu-22.04]: https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md\n[ubuntu-slim]: https://github.com/actions/runner-images/blob/main/images/ubuntu-slim/ubuntu-slim-Readme.md\n[windows-2025]: https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-Readme.md\n[windows-2025-vs2026]: https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-VS2026-Readme.md\n[windows-2022]: https://github.com/actions/runner-images/blob/main/images/windows/Windows2022-Readme.md\n[macOS-14]: https://github.com/actions/runner-images/blob/main/images/macos/macos-14-Readme.md\n[macOS-14-arm64]: https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md\n[macOS-15]: https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md\n[macOS-15-arm64]: https://github.com/actions/runner-images/blob/main/images/macos/macos-15-arm64-Readme.md\n[macOS-26]: https://github.com/actions/runner-images/blob/main/images/macos/macos-26-Readme.md\n[macOS-26-arm64]: https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md\n[self-hosted runners]: https://help.github.com/en/actions/hosting-your-own-runners\n\n## Announcements\n\nSee notable upcoming changes by viewing issues with the [Announcement](https://github.com/actions/runner-images/labels/Announcement) label.\n\n## Image Definitions\n\n### Beta\n\nThe 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\nimage. 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.\nCustomers 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.\n\n### GA\n\nA 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\nGA the image must meet the following criteria:\n\n1. Has been through a Beta period (public or private)\n2. Most major software we install on the image has a compatible\nversion for the underlying OS and\n3. All major bugs reported during the Beta period have been addressed.\n\nThis 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\nlatest 2 versions of an OS.\n\n#### Latest Migration Process\n\nGitHub 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.\n\nThe `-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-14, windows-2022, ubuntu-22.04).\n\n## Image Releases\n\n*How to best follow along with changes*\n\n1. Find the latest releases for this repository [here](https://github.com/actions/runner-images/releases).\n2. 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).\n3. 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.\n\n   - You can also track upcoming changes using the [awaiting-deployment](https://github.com/actions/runner-images/labels/awaiting-deployment) label.\n4. For high impact changes, we will post these in advance to the GitHub Changelog on our [blog](https://github.blog/changelog/) and on [X](https://x.com/GHchangelog).\n   - Ex: breaking changes, GA or deprecation of images\n\n*Cadence*\n\n- We typically deploy weekly updates to the software on the runner images.\n\n## Software and Image Support\n\n### Support Policy\n\n- Tools and versions will typically be removed 6 months after they are deprecated or have reached end-of-life\n- 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.\n- The images generally contain the latest versions of packages installed except for Ubuntu LTS where we mostly rely on the Canonical-provided repositories.\n\n- Popular tools can have several versions installed side-by-side with the following strategy:\n\n| Tool name | Installation strategy |\n|-----------|-----------------------|\n| 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 |\n| Java      | all LTS versions |\n| Node.js   | 3 latest LTS versions |\n| Go        | 3 latest minor versions |\n| Python <br/> Ruby | 5 most popular `major.minor` versions |\n| PyPy      | 3 most popular `major.minor` versions |\n| .NET Core | 2 latest LTS versions and 1 latest version. For each feature version only latest patch is installed. Note for [Ubuntu images see details.](./docs/dotnet-ubuntu.md) |\n| GCC <br/> GNU Fortran <br/> Clang <br/> GNU C++ | 3 latest major versions |\n| Android NDK | 1 latest non-LTS, 2 latest LTS versions |\n| Xcode     | - only one major version of Xcode will be supported per macOS version <br/> - all minor versions of the supported major version will be available <br/> - beta and RC versions will be provided \"as-is\" in the latest available macOS image only no matter of beta/GA status of the image <br/> - when a new patch version is released, the previous patch version will be replaced |\n| Xcode Platforms | - only three major.minor versions of platform tools and simulator runtimes will be available for installed Xcode, including beta/RC versions |\n\n### Package managers usage\n\nWe use third-party package managers to install software during the image generation process. The table below lists the package managers and the software installed.\n> [!NOTE]\n> Third-party repositories are re-evaluated every year to identify if they are still useful and secure.\n\n| Operating system | Package manager                       | Third-party repos and packages |\n| :---             |        :---:                          |                           ---: |\n| Ubuntu           | [APT](https://wiki.debian.org/Apt)    | [docker](https://download.docker.com/linux/ubuntu) <br/> [Eclipse-Temurin (Adoptium)](https://packages.adoptium.net/artifactory/deb/) <br/> [Erlang](https://packages.erlang-solutions.com/ubuntu) <br/> [Firefox](https://ppa.launchpad.net/mozillateam/ppa/ubuntu) <br/> [git-lfs](https://packagecloud.io/install/repositories/github/git-lfs) <br/> [git](https://launchpad.net/~git-core/+archive/ubuntu/ppa) <br/> [Google Cloud CLI](https://packages.cloud.google.com/apt) <br/> [Heroku](https://cli-assets.heroku.com/channels/stable/apt) <br/> [HHvm](https://dl.hhvm.com/ubuntu) <br/> [MongoDB](https://repo.mongodb.org/apt/ubuntu) <br/> [Mono](https://download.mono-project.com/repo/ubuntu) <br/> [MS Edge](https://packages.microsoft.com/repos/edge) <br/> [PostgreSQL](https://apt.postgresql.org/pub/repos/apt/) <br/> [R](https://cloud.r-project.org/bin/linux/ubuntu)                                      |\n|                  | [pipx](https://pypa.github.io/pipx)   | ansible-core <br/>yamllint     |\n| Windows          | [Chocolatey](https://chocolatey.org)  | No third-party repos installed |\n| 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)                                                  |\n|                  | [pipx](https://pypa.github.io/pipx/)  | yamllint                       |\n\n### Image Deprecation Policy\n\n- Images begin the deprecation process of the oldest image label once a new GA OS version has been released.\n- Deprecation process begins with an announcement that sets a date for deprecation.\n- As it gets closer to the date, GitHub begins doing scheduled brownouts of the image.\n- During this time there will be an Announcement pinned in the repo to remind users of the deprecation.\n- Finally, GitHub will deprecate the image and it will no longer be available.\n\n### Preinstallation Policy\n\nIn general, these are the guidelines we follow when deciding what to pre-install on our images:\n\n- Popularity: widely-used tools and ecosystems will be given priority.\n- Latest Technology: recent versions of tools will be given priority.\n- Deprecation: end-of-life tools and versions will not be added.\n- Licensing: MIT, Apache, or GNU licenses are allowed.\n- 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.\n- Support: If a tool requires the support of more than one version, we will consider the cost of this maintenance.\n\n### Default Version Update Policy\n\n- In general, once a new version is installed on the image, we announce the default version update 2 weeks prior to deploying it.\n- For potentially dangerous updates, we may extend the timeline up to 1 month between the announcement and deployment.\n\n## How to Interact with the Repo\n\n- **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)\n- **Discussions**: If you want to share your thoughts about image configuration, installed software, or bring a new idea, please create a [new discussion](https://github.com/orgs/community/discussions/new?category=actions). Before making a new discussion, please make sure no similar topics were created earlier in the [actions category](https://github.com/orgs/community/discussions/categories/actions).\n- For general questions about using the runner images or writing your Actions workflow, please open requests in the [GitHub Community discussion Actions category](https://github.com/orgs/community/discussions/categories/actions).\n\n## FAQs\n\n<details>\n   <summary><b><i>What images are available for GitHub Actions and Azure DevOps?</b></i></summary>\n\nThe availability of images for GitHub Actions and Azure DevOps is the same. However, deprecation policies may differ. See documentation for more details:\n\n- [GitHub Actions](https://docs.github.com/en/free-pro-team@latest/actions/reference/specifications-for-github-hosted-runners#supported-runners-and-hardware-resources)\n- [Azure DevOps](https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml#software)\n\n</details>\n\n<details>\n   <summary><b><i>What image version is used in my build?</b></i></summary>\n\nUsually, 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.\n<img width=\"1440\" alt=\"actions-runner-image\" src=\"https://github.com/actions/runner-images/assets/88318005/922a8bf5-3e4d-4265-9527-b3b51e6bf9c8\">\n</details>\n\n<details>\n   <summary><b><i>Looking for other Linux distributions?</b></i></summary>\n\nWe 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.\n</details>\n\n<details>\n   <summary><b><i>How do I contribute to the macOS source?</b></i></summary>\n\nmacOS 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.\n\nWe 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.\n</details>\n\n<details>\n   <summary><b><i>How does GitHub determine what tools are installed on the images?</b></i></summary>\n\nFor 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)\n</details>\n\n<details>\n   <summary><b><i>How do I request that a new tool be pre-installed on the image?</b></i></summary>\nPlease create an issue and get an approval from us to add this tool to the image before creating the pull request.\n</details>\n\n<details>\n   <summary><b><i>What branch should I use to build custom image?</b></i></summary>\nWe strongly encourage customers to build their own images using the main branch.\nThis repository contains multiple branches and releases that serve as document milestones to reflect what software is installed in the images at certain point of time. Current builds are not idempotent and if one tries to build a runner image using the specific tag it is not guaranteed that the build will succeed.\n</details>\n"
  },
  {
    "path": "SECURITY.md",
    "content": "If you discover a security issue in this repo, please submit it through the [GitHub Security Bug Bounty](https://hackerone.com/github)\n\nThanks for helping make GitHub Actions safe for everyone.\n"
  },
  {
    "path": "docs/create-image-and-azure-resources.md",
    "content": "# GitHub Actions Runner Images\n\nThe runner-images project uses [Packer](https://www.packer.io/) to generate disk images for Windows 2022/2025 and Ubuntu 22.04/24.04.\n\nEach image is configured by a HCL2 Packer template that specifies where to build the image (Azure, in this case),\nand what steps to run to install software and prepare the disk.\n\nThe Packer process initializes a connection to the Azure subscription using Azure CLI and creates temporary resources\nrequired for the build process: a resource group, network interfaces and a virtual machine from the \"clean\" image specified in the template.\n\nIf the VM deployment succeeds, Packer connects to it using SSH or WinRM and begins executing installation steps from the template one-by-one.\nIf any step fails, image generation is aborted, and the temporary VM is terminated.\nPacker also attempts to clean up all the temporary resources it created (unless otherwise configured).\n\nAfter successful completion of all installation steps, Packer creates a managed image from the temporary VM's disk and deletes the VM.\n\n- [Build Agent Preparation](#build-agent-preparation)\n- [Manual image generation](#manual-image-generation)\n- [Manual Image Generation Customization](#manual-image-generation-customization)\n  - [Network Security](#network-security)\n  - [Azure Subscription Authentication](#azure-subscription-authentication)\n- [Generated Machine Deployment](#generated-machine-deployment)\n- [Automated image generation](#automated-image-generation)\n  - [Required variables](#required-variables)\n  - [Optional variables](#optional-variables)\n- [Builder variables](#builder-variables)\n- [Toolset](#toolset)\n- [Post-generation scripts](#post-generation-scripts)\n  - [Running scripts](#running-scripts)\n  - [Script Details: Ubuntu](#script-details-ubuntu)\n  - [Script Details: Windows](#script-details-windows)\n\n## Build Agent Preparation\n\nThe build agent is a machine where the Packer process will be started.\nYou can use any physical or virtual machine running Windows or Linux OS.\nOf course, you may also use an [Azure VM](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/quick-create-cli).\nIn any case, you will need these software installed:\n\n- Packer 1.8.2 or higher.\n\n  Download and install it manually from [here](https://www.packer.io/downloads) or use [Chocolatey](https://chocolatey.org/):\n\n  ```powershell\n  choco install packer\n  ```\n\n- Git.\n\n  For Linux - install the latest version from your distro's package repo.\n\n  For Windows - download and install it from [here](https://gitforwindows.org/) or use [Chocolatey](https://chocolatey.org/):\n\n  ```powershell\n  choco install git -params '\"/GitAndUnixToolsOnPath\"'\n  ```\n\n- Powershell 5.0 or higher.\n\n  In Windows you already have it.\n\n  For Linux follow instructions [here](https://learn.microsoft.com/en-us/windows-server/administration/linux-package-repository-for-microsoft-software)\n  to add Microsoft's Linux Software Repository and then install the `powershell` package.\n\n- Azure CLI.\n\n  Follow the instructions [here](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli).\n  Or if you use Windows, you may run this command in Powershell instead:\n\n  ```powershell\n  Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\\AzureCLI.msi\n  Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet'; rm .\\AzureCLI.msi\n  ```\n\n## Manual image generation\n\nThis repository includes a script that assists in generating images in Azure.\nAll you need is an Azure subscription, a resource group in that subscription and a build agent configured as described above.\n\nAll the commands below should be executed in PowerShell.\n\nFirst, clone the runner-images repository and set the current directory to it:\n\n```powershell\ngit clone https://github.com/actions/runner-images.git\nSet-Location runner-images\n```\n\nThen, import the [GenerateResourcesAndImage](../helpers/GenerateResourcesAndImage.ps1) script from the `helpers` subdirectory:\n\n```powershell\nImport-Module .\\helpers\\GenerateResourcesAndImage.ps1\n```\n\nFinally, run the `GenerateResourcesAndImage` function, setting the mandatory arguments: image type and where to build and store the resulting managed image:\n\n- `SubscriptionId` - your Azure Subscription ID;\n- `ResourceGroupName` - the name of the resource group that will store the resulting artifact (e.g., \"imagegen-test\").\n    The resource group must already exist in your Azure subscription;\n- `AzureLocation` - the location where resources will be created (e.g., \"East US\");\n- `ImageType` - the type of image to build (valid options are \"Windows2022\", \"Windows2025\", \"Ubuntu2204\", \"Ubuntu2404\").\n\nThis function automatically creates all required Azure resources and initiates the Packer image generation for the selected image type.\n\nWhen the image is ready, you may proceed to [deployment](#generated-machine-deployment).\n\n## Manual Image Generation Customization\n\nThe `GenerateResourcesAndImage` function accepts a number of arguments that may assist you in generating an image in your specific environment.\n\nFor example, you may want all the resources involved in the image generation process to be tagged.\nIn this case, pass a HashTable of tags as a value for the `Tags` parameter.\n\nIf you don't want the function to authenticate interactively, you should create a Service Principal and invoke the function with the parameters `AzureClientId`, `AzureClientSecret` and `AzureTenantId`.\nYou can find more details in the [corresponding section below](#azure-subscription-authentication).\n\nUse `get-help GenerateResourcesAndImage -Detailed` for the complete list of available parameters.\n\n### Network Security\n\nTo connect to a temporary virtual machine, Packer uses WinRM or SSH.\n\nIf your build agent is located outside of the Azure subscription where the temporary VM is created, a public network interface and public IP address are used.\nMake sure that firewalls are configured properly and that WinRM (TCP port 5986) and SSH (TCP port 22) connections are allowed both outgoing for the build agent and incoming for the temporary VM.\nAlso, if you don't want the temporary VM to be accessible from everywhere, set the `RestrictToAgentIpAddress` parameter value to `$true`\nto set up firewall rules allowing access only from your build agent's public IP address.\n\nIf your build agent and temporary VM are in the same subscription, you can configure Packer to connect using a private virtual network.\nTo achieve this, set proper values for the environment variables `VNET_RESOURCE_GROUP`, `VNET_NAME` and `VNET_SUBNET`.\n\n### Azure Subscription Authentication\n\nPacker uses a Service Principal to authenticate in Azure infrastructure.\nFor more information about Service Principals, refer to the\n[Azure documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal).\n\nThe `GenerateResourcesAndImage` function is able to create a Service Principal to be used by Packer.\nIt uses the Connect-AzAccount cmdlet that invokes an interactive authentication process by default.\nIf you don't want to use interactive authentication, you should create a Service Principal with full read-write permissions for the selected Azure subscription on your own\nand provide proper values for the parameters `AzureClientId`, `AzureClientSecret` and `AzureTenantId`.\n\nHere is an example of how to create a Service Principal using the Az PowerShell module:\n\n```powershell\n$credentials = [Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPasswordCredential]@{\n  StartDateTime = Get-Date\n  EndDateTime = (Get-Date).AddDays(7)\n}\n\n$sp = New-AzADServicePrincipal -DisplayName \"imagegen-app\"\n$appCred = New-AzADAppCredential -ApplicationId $sp.AppId -PasswordCredentials $credentials\n\nStart-Sleep -Seconds 30\nNew-AzRoleAssignment -RoleDefinitionName \"Contributor\" -PrincipalId $sp.Id\nStart-Sleep -Seconds 30\n\n@{\n  ClientId = $sp.AppId\n  ClientSecret = $appCred.SecretText\n  TenantId = (Get-AzSubscription -SubscriptionId $SubscriptionId).TenantId\n}\n```\n\n## Generated Machine Deployment\n\nAfter successful image generation, a Virtual Machine can be created from the generated image using the [CreateAzureVMFromPackerTemplate](../helpers/CreateAzureVMFromPackerTemplate.ps1) script.\n\n```powershell\nImport-Module .\\helpers\\CreateAzureVMFromPackerTemplate.ps1\n\nCreateAzureVMFromPackerTemplate -SubscriptionId {YourSubscriptionId} -ResourceGroupName {ResourceGroupName} -ManagedImageName \"Runner-Image-Ubuntu2204\" -VirtualMachineName \"testvm1\" -AdminUsername \"shady1\" -AdminPassword \"SomeSecurePassword1\" -AzureLocation \"eastus\"\n```\n\nWhere:\n\n- `SubscriptionId` - the Azure subscription ID where resources will be created;\n- `ResourceGroupName` - the Azure resource group name where the Azure virtual machine will be created;\n- `ManagedImageName` - the name of the managed image to be used for the virtual machine creation;\n- `VirtualMachineName` - the name of the virtual machine to be generated;\n- `AdminUserName` - the administrator username for the virtual machine to be created;\n- `AdminPassword` - the administrator password for the virtual machine to be created;\n- `AzureLocation` - the location where the Azure virtual machine will be provisioned (e.g., \"eastus\").\n\nThis function creates an Azure VM and generates network resources in Azure to make the VM accessible.\n\n## Automated image generation\n\nIf you want to generate images automatically (e.g., as a part of a CI/CD pipeline),\nyou can use Packer directly. To do this, you will need:\n\n- a build agent configured as described in the\n  [Build agent preparation](#build-agent-preparation) section;\n- an Azure subscription and Service Principal configured as described in the\n  [Azure subscription authentication](#azure-subscription-authentication) section;\n- a resource group created in your Azure subscription where the managed image will be stored;\n- a string to be used as a password for the user used to install software (Windows only).\n\nThen, you can invoke Packer in your CI/CD pipeline using the following commands:\n\n```powershell\npacker plugins install github.com/hashicorp/azure 2.2.1\n\npacker build -only \"$BuildName*\" `\n             -var \"subscription_id=$SubscriptionId\" `\n             -var \"client_id=$ClientId\" `\n             -var \"client_secret=$ClientSecret\" `\n             -var \"install_password=$InstallPassword\" `\n             -var \"location=$Location\" `\n             -var \"image_os=$ImageOS\" `\n             -var \"managed_image_name=$ImageName\" `\n             -var \"managed_image_resource_group_name=$ImageResourceGroupName\" `\n             -var \"tenant_id=$TenantId\" `\n             $TemplatePath\n```\n\nWhere:\n\n- `BuildName` - name of the build defined in Packer template's `build{}` block (e.g. \"ubuntu-24_04\", \"windows-2025\");\n- `SubscriptionId` - your Azure Subscription ID;\n- `ClientId` and `ClientSecret` - Service Principal credentials;\n- `TenantId` - Azure Tenant ID;\n- `InstallPassword` - password for the user used to install software (Windows only);\n- `Location` - location where resources will be created (e.g., \"East US\");\n- `ImageOS` - the type of OS that will be deployed as a temporary VM (e.g. \"ubuntu24\", \"win25\");\n- `ImageName` and `ImageResourceGroupName` - name of the resource group where the managed image will be stored;\n- `TemplatePath` - path to the folder with Packer template files (e.g., \"images/windows/templates\").\n\n### Required variables\n\nThe following variables are required to be passed to the Packer process:\n\n| Template var | Env var | Description\n| ------------ | ------- | -----------\n| `subscription_id` | `ARM_SUBSCRIPTION_ID` | The subscription under which the build will be performed.\n| `client_id` | `ARM_CLIENT_ID` | The Active Directory service principal associated with your builder.\n| `client_secret` | `ARM_CLIENT_SECRET` | The password or secret for your service principal; may be omitted if `client_cert_path` is set.\n| `client_cert_path` | `ARM_CLIENT_CERT_PATH` | The location of a PEM file containing a certificate and private key for the service principal; may be omitted if `client_secret` is set.\n| `location` | `ARM_RESOURCE_LOCATION` | The Azure datacenter in which your VM will be built.\n| `managed_image_resource_group_name` | `ARM_RESOURCE_GROUP` | The resource group under which the final artifact will be stored.\n\n### Optional variables\n\nThe following variables are optional:\n\n- `managed_image_name` - the name of the managed image to create. If not specified, \"Runner-Image-{{ImageType}}\" will be used;\n- `build_resource_group_name` - specify an existing resource group to run the build in; 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;\n- `object_id` - the object ID for the AAD SP; will be derived from the oAuth token if empty;\n- `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`;\n- `temp_resource_group_name` - the 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;\n- `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;\n- `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`);\n- `virtual_network_resource_group_name` - if `virtual_network_name` is set, this value may also be set; if `virtual_network_name` is set, and this value is not set, the builder attempts to determine the resource group containing the virtual network; if the resource group cannot be found, or it cannot be disambiguated, this value should be set;\n- `virtual_network_subnet_name` - if `virtual_network_name` is set, this value may also be set; if `virtual_network_name` is set, and this value is not set, the builder attempts to determine the subnet to use with the virtual network; if the subnet cannot be found, or it cannot be disambiguated, this value should be set.\n\n## Builder variables\n\nThe `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.\n\n- `vm_size` - the size of the VM used for building; this can be changed when you deploy a VM from your image;\n- `image_os` - the type of OS that will be deployed as a temporary VM;\n- `image_version` - specify the version of an OS to boot from.\n\n**Detailed Azure builders documentation can be found in the [packer documentation](https://www.packer.io/docs/builders/azure).**\n\n## Toolset\n\nThe 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 on the image. They can be changed if these tools are not required, to reduce image generation time or image size.\n\nGenerated tool versions and details can be found in related projects:\n\n- [Python](https://github.com/actions/python-versions/)\n- [Go](https://github.com/actions/go-versions)\n- [Node](https://github.com/actions/node-versions)\n\n## Post-generation scripts\n\n> :warning: These scripts are intended to be run on a VM deployed in Azure\n\nThe user, created during the image generation, does not exist in the resulting image. 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-gen` folder in the repository:\n\n- Windows: <https://github.com/actions/runner-images/tree/main/images/windows/assets/post-gen>\n- Linux: <https://github.com/actions/runner-images/tree/main/images/ubuntu/assets/post-gen>\n\n**Note:** The default user for Linux should have `sudo privileges`.\n\nThe scripts are copied to the image during the generation process to the following paths:\n\n- Windows: `C:\\post-generation`\n- Linux:  `/opt/post-generation`\n\n### Running scripts\n\n- Ubuntu\n\n  ```bash\n  sudo su -c \"find /opt/post-generation -mindepth 1 -maxdepth 1 -type f -name '*.sh' -exec bash {} \\;\"\n  ```\n\n- Windows\n\n  ```powershell\n  Get-ChildItem C:\\post-generation -Filter *.ps1 | ForEach-Object { & $_.FullName }\n  ```\n\n### Script Details: Ubuntu\n\n- **cleanup-logs.sh** - removes all build process logs from the machine;\n- **environment-variables.sh** - replaces `$HOME` with the default user's home directory for environment variables related to the default user home directory;\n- **homebrew-permissions.sh** - resets the Homebrew repository directory by running `git reset --hard` to make the working tree clean after changing permissions in /home and changes the repository directory owner to the current user;\n- **rust-permissions.sh** - fixes permissions for the Rust folder; a detailed issue explanation is provided in [runner-images/issues/572](https://github.com/actions/runner-images/issues/572).\n\n### Script Details: Windows\n\n- **GenerateIISExpressCertificate.ps1** - generates and imports a certificate to run applications with IIS Express through HTTPS;\n- **InternetExplorerConfiguration.ps1** - turns off the Internet Explorer Enhanced Security feature;\n- **Msys2FirstLaunch.ps1** - initializes the bash user profile in MSYS2;\n- **VSConfiguration.ps1** - performs initial Visual Studio configuration.\n"
  },
  {
    "path": "docs/dotnet-ubuntu.md",
    "content": "# Ubuntu .NET Core Versions\n\n.NET has changed the recommended install methods for Ubuntu from 2404.\n\nThis document gives an overview of these change and the impact this has on the `runner-images`.\n\n## .NET Core for Ubuntu 2204\n\n2204 uses the [Microsoft Package repository](https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu-install?tabs=dotnet8&pivots=os-linux-ubuntu-2204) to install .NET deb files built and published by the .NET team.\n\n## .NET Core Versions from Ubuntu 2404\n\nThe .NET Core team have worked with Canonical and Ubuntu now provides its own .NET packages.\n\nThese are the recommended install path and, as-such what is installed on the image.\n\n> The release of Ubuntu 24.04 is just around the corner. Canonical-produced .NET 6, 7, and 8 packages will be available on day one, for \"Noble Numbat\". Microsoft will not be publishing .NET packages to the 24.04 feed at packages.microsoft.com.\n\nYou can read the [full announcement from .NET team here](https://github.com/dotnet/core/discussions/9258). We'll briefly summarize how this change may impact users of the image.\n\n### [`Feature Bands`](https://learn.microsoft.com/dotnet/core/porting/versioning-sdk-msbuild-vs)\n\nGoing forward only the `1xx` feature band will be present in the image as Ubuntu only build and publish this band.\n\n> Most distros, including Ubuntu, stick to the .1xx feature band for the lifetime of a major .NET version. They make this choice because .1xx is (effectively) the \"compatibility band\". Higher bands can have breaking changes.\n> This means there will no longer be packages available for .2xx and later feature bands. Such packages have been exclusively available from Microsoft. If users see an incompatibility between .1xx and higher feature bands, we ask that you please report it in the dotnet/sdk repo. [link: dotnet/core discussion](https://github.com/dotnet/core/discussions/9258)\n\nIf you need a higher feature band for your Actions the recommendation is to use the [`setup-dotnet`](https://github.com/actions/setup-dotnet) action to install the desired version.\n\n### .NET MAUI\n\n.NET MAUI is [not included](https://github.com/dotnet/core/discussions/9258#discussioncomment-9548857) in the Ubuntu .NET package. There is work [ongoing to fix.](https://github.com/dotnet/core/discussions/9258#discussioncomment-9548857)\n\nYou should be able to resolve this by using the [`setup-dotnet`](https://github.com/actions/setup-dotnet) action to install the desired version.\n"
  },
  {
    "path": "helpers/CheckJsonSchema.ps1",
    "content": "$ErrorActionPreference = 'Stop'\n\n# A JSON schema validator which supports outputting line numbers for errors\n# this allows us to put annotations on builds for errors in the JSON files\n# `Test-Json` built in cmdline doesn't. No existing cli tool supports this\n# that I could find either. See: https://github.com/lawrencegripper/gripdev-json-schema-validator\nInstall-Module -Name GripDevJsonSchemaValidator -Force -Scope CurrentUser\n\n# Find all toolset JSON files\n$toolsetFiles = Get-ChildItem -Recurse -Filter \"toolset-*.json\" | Where-Object { $_.Name -notlike \"*schema.json\" }\n$schemaFilePath = \"./schemas/toolset-schema.json\"\n\n$toolsetHasErrors = $false\nforeach ($file in $toolsetFiles) {\n    Write-Host \"\"\n    Write-Host \"🔍 Validating $($file.FullName)\" -ForegroundColor Cyan\n\n    $validationResult = Test-JsonSchema -SchemaPath $schemaFilePath -JsonPath $file.FullName -PrettyPrint $false\n\n    if ($validationResult.Valid) {\n        Write-Host \"✅ JSON is valid.\" -ForegroundColor Green\n    } else {\n        # File has been modified since the commit, enforce validation\n        $toolsetHasErrors = $true\n        Write-Host \"`n❌ JSON validation failed!\" -ForegroundColor Red\n        Write-Host \"   Found the following errors:`n\" -ForegroundColor Yellow\n\n        $validationResult.Errors | ForEach-Object {\n            Write-Host $_.UserMessage\n            if ($env:GITHUB_ACTIONS -eq 'true') {\n                Write-Host \"Adding annotation\"\n                Write-Host \"::error file=$($file.Name),line=$($_.LineNumber)::$($_.UserMessage.Replace(\"`n\", '%0A'))\"\n            }\n        }\n    }\n}\n\nif ($toolsetHasErrors) {\n    Write-Error \"One or more toolset JSON files failed schema validation. See the error output above for more details.\"\n} else {\n    Write-Host \"Schema validation completed successfully\"\n}\n"
  },
  {
    "path": "helpers/CheckOutdatedVersionPinning.ps1",
    "content": "$ErrorActionPreference = 'Stop'\n\n# Find all toolset JSON files\n$toolsetFiles = Get-ChildItem -Recurse -Filter \"toolset-*.json\" | Where-Object { $_.Name -notlike \"*schema.json\" }\n\n$expiringPins = @()\n$now = Get-Date\n$warningDays = 30 # Warn if expiring within 30 days\n\nforeach ($file in $toolsetFiles) {\n    Write-Host \"Processing $($file.Name)\"\n    $content = Get-Content $file.FullName | ConvertFrom-Json\n\n    # Recursively search for pinnedDetails in the JSON\n    function Search-PinnedDetails {\n        param($obj, $path)\n        \n        $foundPins = @()\n        \n        if ($obj -is [System.Management.Automation.PSCustomObject]) {\n            foreach ($prop in $obj.PSObject.Properties) {\n                if ($prop.Name -eq \"pinnedDetails\") {\n                    Write-Host \"Found pinned version at $path\"\n                    $reviewAt = [DateTime]::Parse($prop.Value.'review-at')\n                    $daysUntilExpiry = ($reviewAt - $now).Days\n                    \n                    if ($daysUntilExpiry -lt $warningDays) {\n                        Write-Host \"Adding to expiringPins array\"\n                        $foundPins += @{\n                            Path = $path\n                            File = $file.Name\n                            ReviewAt = $reviewAt\n                            DaysUntilExpiry = $daysUntilExpiry\n                            Reason = $prop.Value.reason\n                            Link = $prop.Value.link\n                        }\n                    }\n                } else {\n                    $foundPins += Search-PinnedDetails -obj $prop.Value -path \"$path.$($prop.Name)\"\n                }\n            }\n        } elseif ($obj -is [Array]) {\n            for ($i = 0; $i -lt $obj.Count; $i++) {\n                $foundPins += Search-PinnedDetails -obj $obj[$i] -path \"$path[$i]\"\n            }\n        }\n        \n        return $foundPins\n    }\n\n    $expiringPins += Search-PinnedDetails -obj $content -path $file.Name\n}\n\nif ($expiringPins) {\n    $issueBody = \"# Version Pinning Review Required`n`n\"\n    $issueBody += \"The following pinned versions need review:`n`n\"\n    \n    foreach ($pin in $expiringPins) {\n        $status = if ($pin.DaysUntilExpiry -lt 0) { \"EXPIRED\" } else { \"Expiring Soon\" }\n        $issueBody += \"## $($status) - $($pin.Path)`n\"\n        $issueBody += \"- **File**: $($pin.File)`n\"\n        $issueBody += \"- **Review Date**: $($pin.ReviewAt.ToString('yyyy-MM-dd'))`n\"\n        $issueBody += \"- **Days until expiry**: $($pin.DaysUntilExpiry)`n\"\n        $issueBody += \"- **Reason**: $($pin.Reason)`n\"\n        $issueBody += \"- **Original PR**: $($pin.Link)`n`n\"\n    }\n\n    if ($env:GITHUB_ACTIONS -eq 'true') {\n        # In GitHub Actions, create an issue\n        Write-Host \"Creating issue\"\n        $tempFile = [System.IO.Path]::GetTempFileName()\n        Set-Content -Path $tempFile -Value $issueBody\n        gh issue create --title \"Version Pinning Review Found Expired Pinned Versions\" --body-file $tempFile\n        Remove-Item -Path $tempFile\n    }\n    \n    Write-Host \"`nIssue Content:`n\"\n    Write-Host $issueBody\n}\nelse {\n    Write-Host \"No expiring pins found.\"\n    if ($env:GITHUB_ACTIONS -eq 'true') {\n        \"expired_pins=0\" >> $env:GITHUB_OUTPUT\n    }\n}\n"
  },
  {
    "path": "helpers/CreateAzureVMFromPackerTemplate.ps1",
    "content": "Function CreateAzureVMFromPackerTemplate {\n    <#\n        .SYNOPSIS\n            A helper function to deploy a VM from a generated image.\n\n        .DESCRIPTION\n             Creates an Azure VM from a template. Also generates network resources in Azure to make the VM accessible.\n\n        .PARAMETER SubscriptionId\n            The Azure subscription Id where resources will be created.\n\n        .PARAMETER ResourceGroupName\n            The Azure resource group name where the Azure virtual machine will be created.\n\n        .PARAMETER ManagedImageName\n            The name of the managed image to be used to create the virtual machine.\n\n        .PARAMETER VirtualMachineName\n            The name of the virtual machine to be generated.\n\n        .PARAMETER AdminUserName\n            The administrator username for the virtual machine to be created.\n\n        .PARAMETER AdminPassword\n            The administrator password for the virtual machine to be created.\n\n        .PARAMETER AzureLocation\n            The location where the Azure virtual machine will be provisioned. Example: \"eastus\"\n\n        .EXAMPLE\n            CreateAzureVMFromPackerTemplate -SubscriptionId {SubscriptionId} -ResourceGroupName {ResourceGroupName} -VirtualMachineName \"testvm1\" -ManagedImageName {ManagedImageName} -AdminUsername \"shady1\" -AdminPassword \"SomeSecurePassword1\" -AzureLocation \"eastus\"\n    #>\n    param (\n        [Parameter(Mandatory = $True)]\n        [string] $SubscriptionId,\n        [Parameter(Mandatory = $True)]\n        [string] $ResourceGroupName,\n        [Parameter(Mandatory = $True)]\n        [string] $ManagedImageName,\n        [Parameter(Mandatory = $True)]\n        [string] $VirtualMachineName,\n        [Parameter(Mandatory = $True)]\n        [string] $AdminUsername,\n        [Parameter(Mandatory = $True)]\n        [string] $AdminPassword,\n        [Parameter(Mandatory = $True)]\n        [string] $AzureLocation\n    )\n\n    $vmSize = \"Standard_DS2_v2\"\n    $guid = [System.GUID]::NewGuid().ToString().ToUpper()\n    $vnetName = $env:UserName + \"vnet-\" + $guid\n    $subnetName = $env:UserName + \"subnet-\" + $guid\n    $nicName = $env:UserName + \"nic-\" + $guid\n    $publicIpName = $env:UserName + \"pip-\" + $guid\n\n    Write-Host \"Creating a virtual network and subnet\"\n    ($vnet = az network vnet create -g $ResourceGroupName -l $AzureLocation -n $vnetName --address-prefixes 10.0.0.0/16 --subnet-name $subnetName --subnet-prefixes 10.0.1.0/24 --subscription $subscriptionId -o json)\n    $subnetId = ($vnet | ConvertFrom-Json).newVNet.subnets[0].id\n\n    Write-Host \"`nCreating a network interface controller (NIC)\"\n    ($nic = az network nic create -g $ResourceGroupName -l $AzureLocation -n $nicName --subnet $subnetId --subscription $subscriptionId -o json)\n    $networkId = ($nic | ConvertFrom-Json).NewNIC.id\n\n    Write-Host \"`nCreating a public IP address\"\n    ($publicIp = az network public-ip create -g $ResourceGroupName -l $AzureLocation -n $publicIpName --allocation-method Static --sku Basic --version IPv4 --subscription $subscriptionId -o json)\n    $publicIpId = ($publicIp | ConvertFrom-Json).publicIp.id\n\n    Write-Host \"`nAdding the public IP to the NIC\"\n    az network nic ip-config update -g $ResourceGroupName -n ipconfig1 --nic-name $nicName --public-ip-address $publicIpId --subscription $subscriptionId\n\n    Write-Host \"`nCreating the VM\"\n    az vm create `\n        --resource-group $ResourceGroupName `\n        --name $VirtualMachineName `\n        --image $ManagedImageName `\n        --size $vmSize `\n        --admin-username $AdminUsername `\n        --admin-password $AdminPassword `\n        --nics $networkId `\n        --subscription $subscriptionId `\n        --location $AzureLocation\n\n    Write-Host \"`nCreated in ${ResourceGroupName}:`n  vnet ${vnetName}`n  subnet ${subnetName}`n  nic ${nicName}`n  publicip ${publicIpName}`n  vm ${VirtualMachineName}\"\n}\n"
  },
  {
    "path": "helpers/GenerateResourcesAndImage.ps1",
    "content": "$ErrorActionPreference = 'Stop'\n\nenum ImageType {\n    Windows2022         = 1\n    Windows2025         = 2\n    Windows2025_vs2026  = 3\n    Ubuntu2204          = 4\n    Ubuntu2404          = 5\n}\n\nFunction Get-PackerTemplate {\n    param (\n        [Parameter(Mandatory = $True)]\n        [string] $RepositoryRoot,\n        [Parameter(Mandatory = $True)]\n        [ImageType] $ImageType\n    )\n\n    switch ($ImageType) {\n        # Note: Double Join-Path is required to support PowerShell 5.1\n        ([ImageType]::Windows2022) {\n            $relativeTemplatePath = Join-Path (Join-Path \"windows\" \"templates\") \"build.windows-2022.pkr.hcl\"\n            $imageOS = \"win22\"\n        }\n        ([ImageType]::Windows2025) {\n            $relativeTemplatePath = Join-Path (Join-Path \"windows\" \"templates\") \"build.windows-2025.pkr.hcl\"\n            $imageOS = \"win25\"\n        }\n        ([ImageType]::Windows2025_vs2026) {\n            $relativeTemplatePath = Join-Path (Join-Path \"windows\" \"templates\") \"build.windows-2025-vs2026.pkr.hcl\"\n            $imageOS = \"win25-vs2026\"\n        }\n        ([ImageType]::Ubuntu2204) {\n            $relativeTemplatePath = Join-Path (Join-Path \"ubuntu\" \"templates\") \"build.ubuntu-22_04.pkr.hcl\"\n            $imageOS = \"ubuntu22\"\n        }\n        ([ImageType]::Ubuntu2404) {\n            $relativeTemplatePath = Join-Path (Join-Path \"ubuntu\" \"templates\") \"build.ubuntu-24_04.pkr.hcl\"\n            $imageOS = \"ubuntu24\"\n        }\n        default { throw \"Unknown type of image\" }\n    }\n\n    $imageTemplatePath = [IO.Path]::Combine($RepositoryRoot, \"images\", $relativeTemplatePath)\n    # Specific template selection using Packer's \"-only\" functionality\n    $buildName = [IO.Path]::GetFileName($imageTemplatePath).Split(\".\")[1]\n\n    if (-not (Test-Path $imageTemplatePath)) {\n        throw \"Template for image '$ImageType' doesn't exist on path '$imageTemplatePath'.\"\n    }\n\n    return [PSCustomObject] @{\n        \"BuildName\" = $buildName\n        \"ImageOS\"   = $imageOS\n        \"Path\"      = [IO.Path]::GetDirectoryName($imageTemplatePath)\n    }\n}\n\nFunction Show-LatestCommit {\n    [CmdletBinding()]\n    param()\n\n    process {\n        $latestCommit = (git --no-pager log --pretty=format:\"Date: %cd; Commit: %H - %s; Author: %an <%ae>\" -1)\n        Write-Host \"Latest commit: $latestCommit.\"\n    }\n}\n\nfunction Get-GitHubActionsOidcIdToken {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory = $True)]\n        [string] $RequestUrl,\n        [Parameter(Mandatory = $True)]\n        [string] $RequestToken,\n        [Parameter(Mandatory = $False)]\n        [string] $Audience = 'api://AzureADTokenExchange'\n    )\n\n    $separator = if ($RequestUrl -match '\\?') { '&' } else { '?' }\n    $urlWithAudience = \"${RequestUrl}${separator}audience=$([System.Uri]::EscapeDataString($Audience))\"\n    $headers = @{ Authorization = \"Bearer $RequestToken\" }\n\n    try {\n        $response = Invoke-RestMethod -Method Get -Uri $urlWithAudience -Headers $headers\n    }\n    catch {\n        throw \"Failed to request GitHub Actions OIDC ID token. Ensure workflow permissions include 'id-token: write'. Details: $($_.Exception.Message)\"\n    }\n\n    if ([string]::IsNullOrEmpty($response.value)) {\n        throw \"GitHub Actions OIDC token response did not contain a 'value' field.\"\n    }\n\n    return $response.value\n}\n\nfunction Start-Sleep($seconds) {\n    $doneDT = (Get-Date).AddSeconds($seconds)\n    while ($doneDT -gt (Get-Date)) {\n        $secondsLeft = $doneDT.Subtract((Get-Date)).TotalSeconds\n        $percent = ($seconds - $secondsLeft) / $seconds * 100\n        Write-Progress -Activity \"Sleeping\" -Status \"Sleeping...\" -SecondsRemaining $secondsLeft -PercentComplete $percent\n        [System.Threading.Thread]::Sleep(500)\n    }\n    Write-Progress -Activity \"Sleeping\" -Status \"Sleeping...\" -SecondsRemaining 0 -Completed\n}\n\nFunction GenerateResourcesAndImage {\n    <#\n        .SYNOPSIS\n            A helper function to help generate an image.\n        .DESCRIPTION\n            This function will generate the Azure resources and image for the specified image type.\n        .PARAMETER SubscriptionId\n            The Azure subscription id where the Azure resources will be created.\n        .PARAMETER ResourceGroupName\n            The name of the resource group to store the resulting artifact. Resource group must already exist.\n        .PARAMETER ImageType\n            The type of image to generate. Valid values are: Windows2022, Windows2025, Windows2025_vs2026, Ubuntu2204, Ubuntu2404.\n        .PARAMETER ManagedImageName\n            The name of the managed image to create. The default is \"Runner-Image-{{ImageType}}\".\n        .PARAMETER AzureLocation\n            The Azure location where the Azure resources will be created. For example: \"East US\"\n        .PARAMETER ImageGenerationRepositoryRoot\n            The root directory of the image generation repository. This is used to locate the packer template.\n        .PARAMETER SecondsToWaitForServicePrincipalSetup\n            The number of seconds to wait for the service principal to be setup. The default is 120 seconds.\n        .PARAMETER AzureClientId\n            The Azure client id to use to authenticate with Azure. If not specified, the current user's credentials will be used.\n        .PARAMETER AzureClientSecret\n            The Azure client secret to use to authenticate with Azure. If not specified, the current user's credentials will be used.\n        .PARAMETER AzureTenantId\n            The Azure tenant id to use to authenticate with Azure. If not specified, the current user's credentials will be used.\n        .PARAMETER UseOidc\n            If set, authenticate using GitHub Actions OIDC (federated credentials) instead of a client secret.\n            Requires AzureClientId and AzureTenantId, and OidcRequestToken/OidcRequestUrl parameters.\n        .PARAMETER OidcRequestToken\n            GitHub Actions OIDC request token.\n        .PARAMETER OidcRequestUrl\n            GitHub Actions OIDC request URL.\n        .PARAMETER RestrictToAgentIpAddress\n            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. \n            This parameter cannot be used in combination with the virtual_network_name packer parameter.\n        .PARAMETER OnError\n            Specify how packer handles an error during image creation.\n            Options:\n                abort - abort immediately\n                ask - ask user for input\n                cleanup - attempt to cleanup and then abort\n                run-cleanup-provisioner - run the cleanup provisioner and then abort\n            The default is 'ask'.\n        .PARAMETER Tags\n            Tags to be applied to the Azure resources created.\n        .PARAMETER PluginVersion\n            Specify the version of the packer Azure plugin to use. The default is \"2.2.1\".\n        .EXAMPLE\n            GenerateResourcesAndImage -SubscriptionId {YourSubscriptionId} -ResourceGroupName \"shsamytest1\" -ImageGenerationRepositoryRoot \"C:\\runner-images\" -ImageType Ubuntu2204 -AzureLocation \"East US\"\n    #>\n    param (\n        [Parameter(Mandatory = $True)]\n        [string] $SubscriptionId,\n        [Parameter(Mandatory = $True)]\n        [string] $ResourceGroupName,\n        [Parameter(Mandatory = $True)]\n        [ImageType] $ImageType,\n        [Parameter(Mandatory = $False)]\n        [string] $ManagedImageName = \"Runner-Image-$($ImageType)\",\n        [Parameter(Mandatory = $True)]\n        [string] $AzureLocation,\n        [Parameter(Mandatory = $False)]\n        [string] $ImageGenerationRepositoryRoot = $pwd,\n        [Parameter(Mandatory = $False)]\n        [int] $SecondsToWaitForServicePrincipalSetup = 120,\n        [Parameter(Mandatory = $False)]\n        [string] $AzureClientId,\n        [Parameter(Mandatory = $False)]\n        [string] $AzureClientSecret,\n        [Parameter(Mandatory = $False)]\n        [string] $AzureTenantId,\n        [Parameter(Mandatory = $False)]\n        [switch] $UseOidc,\n        [Parameter(Mandatory = $False)]\n        [ValidateNotNullOrEmpty()]\n        [string] $OidcRequestToken,\n        [Parameter(Mandatory = $False)]\n        [ValidateNotNullOrEmpty()]\n        [string] $OidcRequestUrl,\n        [Parameter(Mandatory = $False)]\n        [string] $PluginVersion = \"2.2.1\",\n        [Parameter(Mandatory = $False)]\n        [switch] $RestrictToAgentIpAddress,\n        [Parameter(Mandatory = $False)]\n        [ValidateSet(\"abort\", \"ask\", \"cleanup\", \"run-cleanup-provisioner\")]\n        [string] $OnError = \"ask\",\n        [Parameter(Mandatory = $False)]\n        [hashtable] $Tags = @{}\n    )\n\n    Show-LatestCommit -ErrorAction SilentlyContinue\n\n    # Validate packer is installed\n    $PackerBinary = Get-Command \"packer\"\n    if (-not ($PackerBinary)) {\n        throw \"'packer' binary is not found on PATH.\"\n    }\n\n    # Get template path\n    $PackerTemplate = Get-PackerTemplate -RepositoryRoot $ImageGenerationRepositoryRoot -ImageType $ImageType\n    Write-Debug \"Template path: $($PackerTemplate.Path).\"\n\n    # Prepare list of allowed inbound IP addresses\n    if ($RestrictToAgentIpAddress) {\n        $AgentIp = (Invoke-RestMethod https://ipinfo.io/json).ip\n        if (-not $AgentIp) {\n            throw \"Unable to determine agent IP address.\"\n        }\n\n        Write-Host \"Access to packer generated VM will be restricted to agent IP Address: $AgentIp.\"\n        if ($PSVersionTable.PSVersion.Major -eq 5) {\n            Write-Verbose \"PowerShell 5 detected. Replacing double quotes with escaped double quotes in allowed inbound IP addresses.\"\n            $AllowedInboundIpAddresses = '[\\\"{0}\\\"]' -f $AgentIp\n        }\n        elseif ($PSVersionTable.PSVersion.Major -eq 7 -and $PSVersionTable.PSVersion.Minor -le 2) {\n            Write-Verbose \"PowerShell 7.0-7.2 detected. Replacing double quotes with escaped double quotes in allowed inbound IP addresses.\"\n            $AllowedInboundIpAddresses = '[\\\"{0}\\\"]' -f $AgentIp\n        }\n        else {\n            $AllowedInboundIpAddresses = '[\"{0}\"]' -f $AgentIp\n        }\n    }\n    else {\n        $AllowedInboundIpAddresses = \"[]\"\n    }\n    Write-Debug \"Allowed inbound IP addresses: $AllowedInboundIpAddresses.\"\n\n    # Prepare tags\n    $TagsList = $Tags.GetEnumerator() | ForEach-Object { \"$($_.Key)=$($_.Value)\" }\n    Write-Debug \"Tags list: $TagsList.\"\n    $TagsJson = $Tags | ConvertTo-Json -Compress\n    if ($PSVersionTable.PSVersion.Major -eq 5) {\n        Write-Verbose \"PowerShell 5 detected. Replacing double quotes with escaped double quotes in tags JSON.\"\n        $TagsJson = $TagsJson -replace '\"', '\\\"'\n    }\n    elseif ($PSVersionTable.PSVersion.Major -eq 7 -and $PSVersionTable.PSVersion.Minor -le 2) {\n        Write-Verbose \"PowerShell 7.0-7.2 detected. Replacing double quotes with escaped double quotes in tags JSON.\"\n        $TagsJson = $TagsJson -replace '\"', '\\\"'\n    }\n    Write-Debug \"Tags JSON: $TagsJson.\"\n\n    $InstallPassword = $env:UserName + [System.GUID]::NewGuid().ToString().ToUpper()\n\n    Write-Host \"Downloading packer plugins...\"\n    & $PackerBinary plugins install github.com/hashicorp/azure $PluginVersion\n\n    if ($LastExitCode -ne 0) {\n        throw \"Packer plugins download failed.\"\n    }\n\n    Write-Host \"Validating packer template...\"\n    $validateClientSecret = \"fake\"\n    if ($UseOidc) {\n        $validateClientSecret = \"\"\n    }\n\n    & $PackerBinary validate `\n        \"-only=$($PackerTemplate.BuildName).*\" `\n        \"-var=client_id=fake\" `\n        \"-var=client_secret=$($validateClientSecret)\" `\n        \"-var=oidc_request_token=fake\" `\n        \"-var=oidc_request_url=fake\" `\n        \"-var=subscription_id=$($SubscriptionId)\" `\n        \"-var=tenant_id=fake\" `\n        \"-var=location=$($AzureLocation)\" `\n        \"-var=image_os=$($PackerTemplate.ImageOS)\" `\n        \"-var=managed_image_name=$($ManagedImageName)\" `\n        \"-var=managed_image_resource_group_name=$($ResourceGroupName)\" `\n        \"-var=install_password=$($InstallPassword)\" `\n        \"-var=allowed_inbound_ip_addresses=$($AllowedInboundIpAddresses)\" `\n        \"-var=azure_tags=$($TagsJson)\" `\n        $PackerTemplate.Path\n\n    if ($LastExitCode -ne 0) {\n        throw \"Packer template validation failed.\"\n    }\n\n    try {\n        # Login to Azure subscription\n        if ([string]::IsNullOrEmpty($AzureClientId)) {\n            Write-Verbose \"No AzureClientId was provided, will use interactive login.\"\n            az login --output none\n        }\n        elseif ($UseOidc) {\n            if ([string]::IsNullOrEmpty($AzureTenantId)) {\n                throw \"AzureTenantId is required for OIDC authentication.\"\n            }\n\n            Write-Verbose \"Using OIDC service principal login (federated credentials).\"\n            $idToken = Get-GitHubActionsOidcIdToken -RequestUrl $OidcRequestUrl -RequestToken $OidcRequestToken\n            az login --service-principal --username $AzureClientId --tenant $AzureTenantId --federated-token $idToken --output none\n        }\n        else {\n            if ([string]::IsNullOrEmpty($AzureClientSecret) -or [string]::IsNullOrEmpty($AzureTenantId)) {\n                throw \"AzureClientSecret and AzureTenantId are required for service principal login unless -UseOidc is specified.\"\n            }\n            Write-Verbose \"AzureClientId was provided, will use service principal login (client secret).\"\n            az login --service-principal --username $AzureClientId --password=$AzureClientSecret --tenant $AzureTenantId --output none\n        }\n\n        az account set --subscription $SubscriptionId\n        if ($LastExitCode -ne 0) {\n            throw \"Failed to login to Azure subscription '$SubscriptionId'.\"\n        }\n\n        # Check resource group\n        $ResourceGroupExists = [System.Convert]::ToBoolean((az group exists --name $ResourceGroupName));\n        if ($ResourceGroupExists) {\n            Write-Verbose \"Resource group '$ResourceGroupName' already exists.\"\n        }\n        else {\n            throw \"Resource group '$ResourceGroupName' does not exist.\"\n        }\n\n        # Create / choose authentication for packer\n        if ([string]::IsNullOrEmpty($AzureClientId)) {\n            Write-Host \"Creating service principal for packer...\"\n            $ADCleanupRequired = $true\n\n            $ServicePrincipalName = \"packer-\" + [System.GUID]::NewGuid().ToString().ToUpper()\n            $ServicePrincipal = az ad sp create-for-rbac --name $ServicePrincipalName --role Contributor --scopes /subscriptions/$SubscriptionId --only-show-errors | ConvertFrom-Json\n            if ($LastExitCode -ne 0) {\n                throw \"Failed to create service principal '$ServicePrincipalName'.\"\n            }\n\n            $ServicePrincipalAppId = $ServicePrincipal.appId\n            $ServicePrincipalPassword = $ServicePrincipal.password\n            $TenantId = $ServicePrincipal.tenant\n\n            Write-Verbose \"Waiting for service principal to propagate...\"\n            Start-Sleep $SecondsToWaitForServicePrincipalSetup\n            Write-Host \"Service principal created with id '$ServicePrincipalAppId'. It will be deleted after the build.\"\n        }\n        else {\n            if ($UseOidc) {\n                if ([string]::IsNullOrEmpty($AzureTenantId)) {\n                    throw \"AzureTenantId is required for OIDC authentication.\"\n                }\n\n                $ServicePrincipalAppId = $AzureClientId\n                $ServicePrincipalPassword = \"\"\n                $TenantId = $AzureTenantId\n                # Avoid leaking OIDC request values via command line arguments.\n                $env:PKR_VAR_oidc_request_token = $OidcRequestToken\n                $env:PKR_VAR_oidc_request_url = $OidcRequestUrl\n            }\n            else {\n                if ([string]::IsNullOrEmpty($AzureClientSecret) -or [string]::IsNullOrEmpty($AzureTenantId)) {\n                    throw \"AzureClientSecret and AzureTenantId are required for service principal authentication unless -UseOidc is specified.\"\n                }\n                $ServicePrincipalAppId = $AzureClientId\n                $ServicePrincipalPassword = $AzureClientSecret\n                $TenantId = $AzureTenantId\n            }\n        }\n        Write-Debug \"Service principal app id: $ServicePrincipalAppId.\"\n        Write-Debug \"Tenant id: $TenantId.\"\n\n        & $PackerBinary build -on-error=\"$($OnError)\" `\n            -only \"$($PackerTemplate.BuildName).*\" `\n            -var \"client_id=$($ServicePrincipalAppId)\" `\n            -var \"client_secret=$($ServicePrincipalPassword)\" `\n            -var \"oidc_request_token=$($env:PKR_VAR_oidc_request_token)\" `\n            -var \"oidc_request_url=$($env:PKR_VAR_oidc_request_url)\" `\n            -var \"subscription_id=$($SubscriptionId)\" `\n            -var \"tenant_id=$($TenantId)\" `\n            -var \"location=$($AzureLocation)\" `\n            -var \"image_os=$($PackerTemplate.ImageOS)\" `\n            -var \"managed_image_name=$($ManagedImageName)\" `\n            -var \"managed_image_resource_group_name=$($ResourceGroupName)\" `\n            -var \"install_password=$($InstallPassword)\" `\n            -var \"allowed_inbound_ip_addresses=$($AllowedInboundIpAddresses)\" `\n            -var \"azure_tags=$($TagsJson)\" `\n            $PackerTemplate.Path\n\n        if ($LastExitCode -ne 0) {\n            throw \"Failed to build image.\"\n        }\n    } catch {\n        Write-Error $_\n    } finally {\n        Write-Verbose \"`nCleaning up...\"\n\n        # Remove ADServicePrincipal and ADApplication\n        if ($ADCleanupRequired) {\n            Write-Host \"Removing ADServicePrincipal...\"\n            if (az ad sp show --id $ServicePrincipalAppId --query id) {\n                az ad sp delete --id $ServicePrincipalAppId\n            }\n\n            Write-Host \"Removing ADApplication...\"\n            if (az ad app show --id $ServicePrincipalAppId --query id) {\n                az ad app delete --id $ServicePrincipalAppId\n            }\n        }\n        Write-Verbose \"Cleanup completed.\"\n    }\n}\n"
  },
  {
    "path": "helpers/GitHubApi.psm1",
    "content": "class GithubApi\n{\n    [string] $Repository\n    [object] hidden $AuthHeader\n\n    GithubApi(\n        [string] $Repository,\n        [string] $AccessToken\n    ) {\n        $this.Repository = $Repository\n        $this.AuthHeader = $this.BuildAuth($AccessToken)\n    }\n\n    [object] hidden BuildAuth([string]$AccessToken) {\n        if ([string]::IsNullOrEmpty($AccessToken)) {\n            return $null\n        }\n        $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(\"'':${AccessToken}\"))\n        return @{\n            Authorization = \"Basic ${base64AuthInfo}\"\n        }\n    }\n\n    [string] hidden BuildBaseUrl([string]$Repository, [string]$ApiPrefix) {\n        return \"https://$ApiPrefix.github.com/repos/$Repository\"\n    }\n\n    [object] GetWorkflowRuns([string]$WorkflowId) {\n        $url = \"actions/workflows/$WorkflowId/runs\"\n        $response =  $this.InvokeRestMethod($url, 'GET', $null, $null)\n        return $response\n    }\n\n    [object] GetWorkflowRun([string]$WorkflowRunId) {\n        $url = \"actions/runs/$WorkflowRunId\"\n        $response =  $this.InvokeRestMethod($url, 'GET', $null, $null)\n        return $response\n    }\n\n    [object] DispatchWorkflow([string]$EventType, [object]$EventPayload) {\n        $url = \"dispatches\"\n        $body = @{\n            \"event_type\"     = $EventType\n            \"client_payload\" = $EventPayload\n        } | ConvertTo-Json\n        $response =  $this.InvokeRestMethod($url, 'POST', $null, $body)\n        return $response\n    }\n\n    [object] CancelWorkflowRun([string]$workflowRunId) {\n        $url = \"actions/runs/$workflowRunId/cancel\"\n        $response =  $this.InvokeRestMethod($url, 'POST', $null, $null)\n        return $response\n    }\n\n    [string] hidden BuildUrl([string]$url, [string]$RequestParams, [string]$ApiPrefix) {\n        $baseUrl = $this.BuildBaseUrl($this.Repository, $ApiPrefix)\n        if ([string]::IsNullOrEmpty($RequestParams)) {\n            return \"$($baseUrl)/$($url)\"\n        } else {\n            return \"$($baseUrl)/$($url)?$($requestParams)\"\n        }\n    }\n\n    [object] hidden InvokeRestMethod(\n        [string] $url,\n        [string] $Method,\n        [string] $RequestParams,\n        [string] $body\n    ) {\n        $requestUrl = $this.BuildUrl($url, $RequestParams, \"api\")\n        $params = @{\n            Method      = $Method\n            ContentType = \"application/json\"\n            Uri         = $requestUrl\n            Headers     = @{}\n        }\n        if ($this.AuthHeader) {\n            $params.Headers += $this.AuthHeader\n        }\n        if (![string]::IsNullOrEmpty($body)) {\n            $params.Body = $body\n        }\n\n        $response = Invoke-RestMethod @params\n        return $response\n    }\n}\n\nfunction Get-GithubApi {\n    param (\n        [string] $Repository,\n        [string] $AccessToken\n    )\n\n    return [GithubApi]::New($Repository, $AccessToken)\n}\n"
  },
  {
    "path": "helpers/WaitWorkflowCompletion.ps1",
    "content": "Param (\n    [Parameter(Mandatory)]\n    [string] $WorkflowRunId,\n    [Parameter(Mandatory)]\n    [string] $Repository,\n    [Parameter(Mandatory)]\n    [string] $AccessToken,\n    [int] $RetryIntervalSeconds = 300,\n    [int] $MaxRetryCount = 0\n)\n\nImport-Module (Join-Path $PSScriptRoot \"GitHubApi.psm1\")\n\nfunction Wait-ForWorkflowCompletion($WorkflowRunId, $RetryIntervalSeconds) {\n    do {\n        Start-Sleep -Seconds $RetryIntervalSeconds\n        $workflowRun = $gitHubApi.GetWorkflowRun($WorkflowRunId)\n    } until ($workflowRun.status -eq \"completed\")\n\n    return $workflowRun\n}\n\n$gitHubApi = Get-GithubApi -Repository $Repository -AccessToken $AccessToken\n\n$attempt = 1\ndo {\n    $finishedWorkflowRun = Wait-ForWorkflowCompletion -WorkflowRunId $WorkflowRunId -RetryIntervalSeconds $RetryIntervalSeconds\n    Write-Host \"Workflow run finished with result: $($finishedWorkflowRun.conclusion)\"\n    if ($finishedWorkflowRun.conclusion -in (\"success\", \"cancelled\", \"timed_out\")) {\n        break\n    } elseif ($finishedWorkflowRun.conclusion -eq \"failure\") {\n        if ($attempt -le $MaxRetryCount) {\n            Write-Host \"Workflow run will be restarted. Attempt $attempt of $MaxRetryCount\"\n            $gitHubApi.ReRunFailedJobs($WorkflowRunId)\n            $attempt += 1\n        } else {\n            break\n        }\n    }\n} while ($true)\n\nWrite-Host \"Last result: $($finishedWorkflowRun.conclusion).\"\n\"CI_WORKFLOW_RUN_RESULT=$($finishedWorkflowRun.conclusion)\" | Out-File -Append -FilePath $env:GITHUB_ENV\n\nif ($finishedWorkflowRun.conclusion -in (\"failure\", \"cancelled\", \"timed_out\")) {\n    exit 1\n}\n"
  },
  {
    "path": "helpers/software-report-base/Calculate-ImagesDifference.ps1",
    "content": "using module ./SoftwareReport.psm1\nusing module ./SoftwareReport.DifferenceCalculator.psm1\n\n<#\n.SYNOPSIS\n    Calculates the difference between two software reports and saves it to a file.\n.PARAMETER PreviousJsonReportPath\n    Path to the previous software report.\n.PARAMETER CurrentJsonReportPath\n    Path to the current software report.\n.PARAMETER OutputFile\n    Path to the file where the difference will be saved.\n.PARAMETER ReleaseBranchName\n    Name of the release branch to build image docs URL.\n.PARAMETER ReadmePath\n    Path to the README file in repository to build image docs URL.\n#>\n\nParam (\n    [Parameter(Mandatory=$true)]\n    [string] $PreviousJsonReportPath,\n    [Parameter(Mandatory=$true)]\n    [string] $CurrentJsonReportPath,\n    [Parameter(Mandatory=$true)]\n    [string] $OutputFile,\n    [Parameter(Mandatory=$false)]\n    [string] $ReleaseBranchName,\n    [Parameter(Mandatory=$false)]\n    [string] $ReadmePath\n)\n\n$ErrorActionPreference = \"Stop\"\n$global:ErrorView = \"NormalView\"\n\nfunction Read-SoftwareReport {\n    Param (\n        [Parameter(Mandatory=$true)]\n        [string] $JsonReportPath\n    )\n\n    if (-not (Test-Path $JsonReportPath)) {\n        throw \"File '$JsonReportPath' does not exist\"\n    }\n\n    $jsonReport = Get-Content -Path $JsonReportPath -Raw\n    $report = [SoftwareReport]::FromJson($jsonReport)\n    return $report\n}\n\n$previousReport = Read-SoftwareReport -JsonReportPath $PreviousJsonReportPath\n$currentReport = Read-SoftwareReport -JsonReportPath $CurrentJsonReportPath\n\n$comparer = [SoftwareReportDifferenceCalculator]::new($previousReport, $currentReport)\n$comparer.CompareReports()\n$diff = $comparer.GetMarkdownReport()\n\nif ($ReleaseBranchName -and $ReadmePath) {\n    # https://github.com/actions/runner-images/blob/releases/macOS-12/20221215/images/macos/macos-12-Readme.md\n    $ImageDocsUrl = \"https://github.com/actions/runner-images/blob/${ReleaseBranchName}/${ReadmePath}\"\n    $diff += \"`n`n`nFor comprehensive list of software installed on this image please click [here]($ImageDocsUrl).\"\n}\n\n$parentDirectory = Split-Path $OutputFile -Parent\nif (-not (Test-Path $parentDirectory)) { New-Item -Path $parentDirectory -ItemType Directory | Out-Null }\n\n$diff | Out-File -Path $OutputFile -Encoding utf8NoBOM\n"
  },
  {
    "path": "helpers/software-report-base/SoftwareReport.BaseNodes.psm1",
    "content": "############################\n### Abstract base nodes ####\n############################\n\n# Abstract base class for all nodes\nclass BaseNode {\n    [Boolean] ShouldBeIncludedToDiff() {\n        return $false\n    }\n\n    [String] ToMarkdown() {\n        return $this.ToMarkdown(1)\n    }\n\n    [String] ToMarkdown([Int32] $Level) {\n        throw \"Abstract method 'ToMarkdown(level)' is not implemented for '$($this.GetType().Name)'\"\n    }\n\n    [Boolean] IsSimilarTo([BaseNode] $OtherNode) {\n        throw \"Abstract method 'IsSimilarTo' is not implemented for '$($this.GetType().Name)'\"\n    }\n\n    [Boolean] IsIdenticalTo([BaseNode] $OtherNode) {\n        throw \"Abstract method 'IsIdenticalTo' is not implemented for '$($this.GetType().Name)'\"\n    }\n}\n\n# Abstract base class for all nodes that describe a tool and should be rendered inside diff table\nclass BaseToolNode: BaseNode {\n    [ValidateNotNullOrEmpty()]\n    [String] $ToolName\n\n    BaseToolNode([String] $ToolName) {\n        $this.ToolName = $ToolName\n    }\n\n    [Boolean] ShouldBeIncludedToDiff() {\n        return $true\n    }\n\n    [String] GetValue() {\n        throw \"Abstract method 'GetValue' is not implemented for '$($this.GetType().Name)'\"\n    }\n\n    [Boolean] IsSimilarTo([BaseNode] $OtherNode) {\n        if ($this.GetType() -ne $OtherNode.GetType()) {\n            return $false\n        }\n\n        return $this.ToolName -eq $OtherNode.ToolName\n    }\n\n    [Boolean] IsIdenticalTo([BaseNode] $OtherNode) {\n        return $this.IsSimilarTo($OtherNode) -and ($this.GetValue() -eq $OtherNode.GetValue())\n    }\n}\n"
  },
  {
    "path": "helpers/software-report-base/SoftwareReport.DifferenceCalculator.psm1",
    "content": "using module ./SoftwareReport.psm1\nusing module ./SoftwareReport.BaseNodes.psm1\nusing module ./SoftwareReport.Nodes.psm1\nusing module ./SoftwareReport.DifferenceRender.psm1\n\nclass SoftwareReportDifferenceCalculator {\n    [ValidateNotNullOrEmpty()]\n    hidden [SoftwareReport] $PreviousReport\n    [ValidateNotNullOrEmpty()]\n    hidden [SoftwareReport] $CurrentReport\n\n    hidden [Collections.Generic.List[ReportDifferenceItem]] $AddedItems\n    hidden [Collections.Generic.List[ReportDifferenceItem]] $ChangedItems\n    hidden [Collections.Generic.List[ReportDifferenceItem]] $DeletedItems\n\n    SoftwareReportDifferenceCalculator([SoftwareReport] $PreviousReport, [SoftwareReport] $CurrentReport) {\n        $this.PreviousReport = $PreviousReport\n        $this.CurrentReport = $CurrentReport\n    }\n\n    [void] CompareReports() {\n        $this.AddedItems = @()\n        $this.ChangedItems = @()\n        $this.DeletedItems = @()\n\n        $this.CompareInternal($this.PreviousReport.Root, $this.CurrentReport.Root, @())\n    }\n\n    [String] GetMarkdownReport() {\n        $reporter = [SoftwareReportDifferenceRender]::new()\n        $report = $reporter.GenerateMarkdownReport($this.CurrentReport, $this.PreviousReport, $this.AddedItems, $this.ChangedItems, $this.DeletedItems)\n        return $report\n    }\n\n    hidden [void] CompareInternal([HeaderNode] $previousReportPointer, [HeaderNode] $currentReportPointer, [String[]] $Headers) {\n        $currentReportPointer.Children ?? @() | Where-Object { $_.ShouldBeIncludedToDiff() -and $this.FilterExcludedNodes($_) } | ForEach-Object {\n            $currentReportNode = $_\n            $sameNodeInPreviousReport = $previousReportPointer ? $previousReportPointer.FindSimilarChildNode($currentReportNode) : $null\n\n            if ($currentReportNode -is [HeaderNode]) {\n                # Compare HeaderNode recursively\n                $this.CompareInternal($sameNodeInPreviousReport, $currentReportNode, $Headers + $currentReportNode.Title)\n            } else {\n                if ($sameNodeInPreviousReport -and ($currentReportNode.IsIdenticalTo($sameNodeInPreviousReport))) {\n                    # Nodes are identical, nothing changed, just ignore it\n                } elseif ($sameNodeInPreviousReport) {\n                    # Nodes are equal but not identical, something was changed\n                    if ($currentReportNode -is [TableNode]) {\n                        $this.CompareSimilarTableNodes($sameNodeInPreviousReport, $currentReportNode, $Headers)\n                    } elseif ($currentReportNode -is [ToolVersionsListNode]) {\n                        $this.CompareSimilarToolVersionsListNodes($sameNodeInPreviousReport, $currentReportNode, $Headers)\n                    } else {\n                        $this.ChangedItems.Add([ReportDifferenceItem]::new($sameNodeInPreviousReport, $currentReportNode, $Headers))\n                    }\n                } else {\n                    # Node was not found in previous report, new node was added\n                    $this.AddedItems.Add([ReportDifferenceItem]::new($null, $currentReportNode, $Headers))\n                }\n            }\n        }\n\n        # Detecting nodes that were removed\n        $previousReportPointer.Children ?? @() | Where-Object { $_.ShouldBeIncludedToDiff() -and $this.FilterExcludedNodes($_) } | ForEach-Object {\n            $previousReportNode = $_\n            $sameNodeInCurrentReport = $currentReportPointer ? $currentReportPointer.FindSimilarChildNode($previousReportNode) : $null\n\n            if (-not $sameNodeInCurrentReport) {\n                if ($previousReportNode -is [HeaderNode]) {\n                    # Compare removed HeaderNode recursively\n                    $this.CompareInternal($previousReportNode, $null, $Headers + $previousReportNode.Title)\n                } else {\n                    # Node was not found in current report, node was removed\n                    $this.DeletedItems.Add([ReportDifferenceItem]::new($previousReportNode, $null, $Headers))\n                }\n            }\n        }\n    }\n\n    hidden [void] CompareSimilarTableNodes([TableNode] $PreviousReportNode, [TableNode] $CurrentReportNode, [String[]] $Headers) {\n        $addedRows = $CurrentReportNode.Rows | Where-Object { $_ -notin $PreviousReportNode.Rows }\n        $deletedRows = $PreviousReportNode.Rows | Where-Object { $_ -notin $CurrentReportNode.Rows }\n\n        if (($addedRows.Count -eq 0) -and ($deletedRows.Count -eq 0)) {\n            # Unexpected state: TableNodes are identical\n            return\n        }\n\n        if ($PreviousReportNode.Headers -ne $CurrentReportNode.Headers) {\n            # If headers are changed and rows are changed at the same time, we should track it as removing table and adding new one\n            $this.DeletedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $null, $Headers))\n            $this.AddedItems.Add([ReportDifferenceItem]::new($null, $CurrentReportNode, $Headers))\n        } elseif (($addedRows.Count -gt 0) -and ($deletedRows.Count -eq 0)) {\n            # If new rows were added and no rows were deleted, then it is AddedItem\n            $this.AddedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $CurrentReportNode, $Headers))\n        } elseif (($deletedRows.Count -gt 0) -and ($addedRows.Count -eq 0)) {\n            # If no rows were added and some rows were deleted, then it is DeletedItem\n            $this.DeletedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $CurrentReportNode, $Headers))\n        } else {\n            # If some rows were added and some rows were removed, then it is UpdatedItem \n            $this.ChangedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $CurrentReportNode, $Headers))\n        }\n    }\n\n    hidden [void] CompareSimilarToolVersionsListNodes([ToolVersionsListNode] $PreviousReportNode, [ToolVersionsListNode] $CurrentReportNode, [String[]] $Headers) {\n        $previousReportMajorVersions = $PreviousReportNode.Versions | ForEach-Object { $PreviousReportNode.ExtractMajorVersion($_) }\n        $currentReportMajorVersion = $CurrentReportNode.Versions | ForEach-Object { $CurrentReportNode.ExtractMajorVersion($_) }\n\n        $addedVersions = $CurrentReportNode.Versions | Where-Object { $CurrentReportNode.ExtractMajorVersion($_) -notin $previousReportMajorVersions }\n        $deletedVersions = $PreviousReportNode.Versions | Where-Object { $PreviousReportNode.ExtractMajorVersion($_) -notin $currentReportMajorVersion }\n        $changedPreviousVersions = $PreviousReportNode.Versions | Where-Object { ($PreviousReportNode.ExtractMajorVersion($_) -in $currentReportMajorVersion) -and ($_ -notin $CurrentReportNode.Versions) }\n        $changedCurrentVersions = $CurrentReportNode.Versions | Where-Object { ($CurrentReportNode.ExtractMajorVersion($_) -in $previousReportMajorVersions) -and ($_ -notin $PreviousReportNode.Versions) }\n\n        if ($addedVersions.Count -gt 0) {\n            $this.AddedItems.Add([ReportDifferenceItem]::new($null, [ToolVersionsListNode]::new($CurrentReportNode.ToolName, $addedVersions, $CurrentReportNode.MajorVersionRegex, \"List\"), $Headers))\n        }\n\n        if ($deletedVersions.Count -gt 0) {\n            $this.DeletedItems.Add([ReportDifferenceItem]::new([ToolVersionsListNode]::new($PreviousReportNode.ToolName, $deletedVersions, $PreviousReportNode.MajorVersionRegex, \"List\"), $null, $Headers))\n        }\n\n        $previousChangedNode = ($changedPreviousVersions.Count -gt 0) ? [ToolVersionsListNode]::new($PreviousReportNode.ToolName, $changedPreviousVersions, $PreviousReportNode.MajorVersionRegex, \"List\") : $null\n        $currentChangedNode = ($changedCurrentVersions.Count -gt 0) ? [ToolVersionsListNode]::new($CurrentReportNode.ToolName, $changedCurrentVersions, $CurrentReportNode.MajorVersionRegex, \"List\") : $null\n        if ($previousChangedNode -and $currentChangedNode) {\n            $this.ChangedItems.Add([ReportDifferenceItem]::new($previousChangedNode, $currentChangedNode, $Headers))\n        }\n    }\n\n    hidden [Boolean] FilterExcludedNodes([BaseNode] $Node) {\n        # We shouldn't show \"Image Version\" diff because it is already shown in report header\n        if (($Node -is [ToolVersionNode]) -and ($Node.ToolName -eq \"Image Version:\")) {\n            return $false\n        }\n\n        return $true\n    }\n}"
  },
  {
    "path": "helpers/software-report-base/SoftwareReport.DifferenceRender.psm1",
    "content": "using module ./SoftwareReport.psm1\nusing module ./SoftwareReport.BaseNodes.psm1\nusing module ./SoftwareReport.Nodes.psm1\n\nclass SoftwareReportDifferenceRender {\n    [String] GenerateMarkdownReport([SoftwareReport] $CurrentReport, [SoftwareReport] $PreviousReport, [ReportDifferenceItem[]] $AddedItems, [ReportDifferenceItem[]] $ChangedItems, [ReportDifferenceItem[]] $DeletedItems) {\n        $sb = [System.Text.StringBuilder]::new()\n\n        $rootNode = $CurrentReport.Root\n        $imageVersion = $CurrentReport.GetImageVersion()\n        $previousImageVersion = $PreviousReport.GetImageVersion()\n\n        #############################\n        ### Render report header ####\n        #############################\n\n        $sb.AppendLine(\"# :desktop_computer: Actions Runner Image: $($rootNode.Title)\")\n\n        # ToolVersionNodes on root level contains main image description so just copy-paste them to final report\n        $rootNode.Children | Where-Object { $_ -is [ToolVersionNode] } | ForEach-Object {\n            $sb.AppendLine($_.ToMarkdown())\n        }\n        $sb.AppendLine()\n\n        $sb.AppendLine(\"## :mega: What's changed?\").AppendLine()\n\n        ###########################\n        ### Render added items ####\n        ###########################\n\n        [ReportDifferenceItem[]] $addedItemsBaseTools = $AddedItems | Where-Object { $_.IsBaseToolNode() }\n        [ReportDifferenceItem[]] $addedItemsTables = $AddedItems | Where-Object { $_.IsTableNode() }\n        if ($addedItemsBaseTools.Count + $addedItemsTables.Count -gt 0) {\n            $sb.AppendLine(\"### Added :heavy_plus_sign:\").AppendLine()\n        }\n        if ($addedItemsBaseTools.Count -gt 0) {\n            $tableItems = $addedItemsBaseTools | ForEach-Object {\n                [PSCustomObject]@{\n                    \"Category\" = $this.RenderCategory($_.Headers, $true);\n                    \"Tool name\" = $this.RenderToolName($_.CurrentReportNode.ToolName);\n                    \"Current ($imageVersion)\" = $_.CurrentReportNode.GetValue();\n                }\n            }\n            $sb.AppendLine($this.RenderHtmlTable($tableItems, \"Category\"))\n        }\n        if ($addedItemsTables.Count -gt 0) {\n            $addedItemsTables | ForEach-Object {\n                $sb.AppendLine($this.RenderTableNodesDiff($_))\n            }\n        }\n\n        #############################\n        ### Render deleted items ####\n        #############################\n\n        [ReportDifferenceItem[]] $deletedItemsBaseTools = $DeletedItems | Where-Object { $_.IsBaseToolNode() }\n        [ReportDifferenceItem[]] $deletedItemsTables = $DeletedItems | Where-Object { $_.IsTableNode() }\n        if ($deletedItemsBaseTools.Count + $deletedItemsTables.Count -gt 0) {\n            $sb.AppendLine(\"### Deleted :heavy_minus_sign:\").AppendLine()\n        }\n        if ($deletedItemsBaseTools.Count -gt 0) {\n            $tableItems = $deletedItemsBaseTools | ForEach-Object {\n                [PSCustomObject]@{\n                    \"Category\" = $this.RenderCategory($_.Headers, $true);\n                    \"Tool name\" = $this.RenderToolName($_.PreviousReportNode.ToolName);\n                    \"Previous ($previousImageVersion)\" = $_.PreviousReportNode.GetValue();\n                }\n            }\n            $sb.AppendLine($this.RenderHtmlTable($tableItems, \"Category\"))\n        }\n        if ($deletedItemsTables.Count -gt 0) {\n            $deletedItemsTables | ForEach-Object {\n                $sb.AppendLine($this.RenderTableNodesDiff($_))\n            }\n        }\n\n        #############################\n        ### Render updated items ####\n        #############################\n\n        [ReportDifferenceItem[]] $changedItemsBaseTools = $ChangedItems | Where-Object { $_.IsBaseToolNode() }\n        [ReportDifferenceItem[]] $changedItemsTables = $ChangedItems | Where-Object { $_.IsTableNode() }\n        if ($changedItemsBaseTools.Count + $changedItemsTables.Count -gt 0) {\n            $sb.AppendLine(\"### Updated\").AppendLine()\n        }\n        if ($changedItemsBaseTools.Count -gt 0) {\n            $tableItems = $changedItemsBaseTools | ForEach-Object {\n                [PSCustomObject]@{\n                    \"Category\" = $this.RenderCategory($_.Headers, $true);\n                    \"Tool name\" = $this.RenderToolName($_.CurrentReportNode.ToolName);\n                    \"Previous ($previousImageVersion)\" = $_.PreviousReportNode.GetValue();\n                    \"Current ($imageVersion)\" = $_.CurrentReportNode.GetValue();\n                }\n            }\n            $sb.AppendLine($this.RenderHtmlTable($tableItems, \"Category\"))\n        }\n        if ($changedItemsTables.Count -gt 0) {\n            $changedItemsTables | ForEach-Object {\n                $sb.AppendLine($this.RenderTableNodesDiff($_))\n            }\n        }\n\n        return $sb.ToString()\n    }\n\n    [String] RenderHtmlTable([PSCustomObject[]] $Table, [String] $RowSpanColumnName) {\n        $headers = $Table[0].PSObject.Properties.Name\n\n        $sb = [System.Text.StringBuilder]::new()\n        $sb.AppendLine(\"<table>\")\n        $sb.AppendLine(\"    <thead>\")\n        $headers | ForEach-Object {\n            $sb.AppendLine(\"        <th>$_</th>\")\n        }\n        $sb.AppendLine(\"    </thead>\")\n        $sb.AppendLine(\"    <tbody>\")\n\n        $tableRowSpans = $this.CalculateHtmlTableRowSpan($Table, $RowSpanColumnName)\n        for ($rowIndex = 0; $rowIndex -lt $Table.Count; $rowIndex++) {\n            $row = $Table[$rowIndex]\n\n            $sb.AppendLine(\"        <tr>\")\n            $headers | ForEach-Object {\n                if ($_ -eq $RowSpanColumnName) {\n                    if ($tableRowSpans[$rowIndex] -gt 0) {\n                        $sb.AppendLine(\"            <td rowspan=`\"$($tableRowSpans[$rowIndex])`\">$($row.$_)</td>\")\n                    } else {\n                        # Skip rendering this cell at all\n                    }\n                } else {\n                    $sb.AppendLine(\"            <td>$($row.$_)</td>\")\n                }\n            }\n            $sb.AppendLine(\"        </tr>\")\n        }\n        $sb.AppendLine(\"    </tbody>\")\n        $sb.AppendLine(\"</table>\")\n\n        return $sb.ToString()\n    }\n\n    [int[]] CalculateHtmlTableRowSpan([PSCustomObject[]] $Table, [String] $keyColumn) {\n        $result = @(0) * $Table.Count\n\n        for ($rowIndex = $Table.Count - 1; $rowIndex -ge 0; $rowIndex--) {\n            if (($rowIndex -lt ($Table.Count - 1)) -and ($Table[$rowIndex].$keyColumn -eq $Table[$rowIndex + 1].$keyColumn)) {\n                # If the current row is the same as the next row\n                # Then rowspan of current row should be equal to rowspan of the next row + 1\n                # And rowspan of the next row should be 0 because it is already included in the rowspan of the current row\n                $result[$rowIndex] = $result[$rowIndex + 1] + 1\n                $result[$rowIndex + 1] = 0\n            } else {\n                $result[$rowIndex] = 1\n            }\n        }\n\n        return $result\n    }\n\n    [String] RenderTableNodesDiff([ReportDifferenceItem] $DiffItem) {\n        # Use the simplest approach for now: first, print all removed lines. Then print added lines\n        # It will work well for most cases like changing existing rows, adding new rows and removing rows\n        # But can produce not so pretty results for cases when some rows are changed and some rows are added at the same time\n        # Let's see how it works in practice and improve it later if needed\n\n        [String] $tableHeaders = ($DiffItem.CurrentReportNode ?? $DiffItem.PreviousReportNode).Headers\n        [Collections.Generic.List[String]] $tableRows = @()\n        $DiffItem.PreviousReportNode.Rows ?? @() | Where-Object { $_ -notin $DiffItem.CurrentReportNode.Rows } | ForEach-Object {\n            $tableRows.Add($this.StrikeTableRow($_))\n        }\n        $DiffItem.CurrentReportNode.Rows ?? @() | Where-Object { $_ -notin $DiffItem.PreviousReportNode.Rows } | ForEach-Object {\n            $tableRows.Add($_)\n        }\n\n        $sb = [System.Text.StringBuilder]::new()\n        $sb.AppendLine(\"#### $($this.RenderCategory($DiffItem.Headers, $false))\")\n        $sb.AppendLine([TableNode]::new($tableHeaders, $tableRows).ToMarkdown())\n        return $sb.ToString()\n    }\n\n    [String] RenderCategory([String[]] $Headers, [Boolean] $AddLineSeparator) {\n        # Always skip the first header because it is \"Installed Software\"\n        [String[]] $takeHeaders = $Headers | Select-Object -Skip 1\n        if ($takeHeaders.Count -eq 0) {\n            return \"\"\n        }\n\n        $lineSeparator = $AddLineSeparator ? \"<br>\": \"\"\n        return [String]::Join(\" >$lineSeparator \", $takeHeaders)\n    }\n\n    [String] RenderToolName([String] $ToolName) {\n        return $ToolName.TrimEnd(\":\")\n    }\n\n    [String] StrikeTableRow([String] $Row) {\n        # Convert \"a|b|c\" to \"~~a~~|~~b~~|~~c~~\n        $cells = $Row.Split(\"|\")\n        $strikedCells = $cells | ForEach-Object { \"~~$($_)~~\"}\n        return [String]::Join(\"|\", $strikedCells)\n    }\n}\n\n# Temporary structure to store the single difference between two reports\nclass ReportDifferenceItem {\n    [BaseNode] $PreviousReportNode\n    [BaseNode] $CurrentReportNode\n    [String[]] $Headers\n\n    ReportDifferenceItem([BaseNode] $PreviousReportNode, [BaseNode] $CurrentReportNode, [String[]] $Headers) {\n        $this.PreviousReportNode = $PreviousReportNode\n        $this.CurrentReportNode = $CurrentReportNode\n        $this.Headers = $Headers\n    }\n\n    [Boolean] IsBaseToolNode() {\n        $node = $this.CurrentReportNode ?? $this.PreviousReportNode\n        return $node -is [BaseToolNode]\n    }\n\n    [Boolean] IsTableNode() {\n        $node = $this.CurrentReportNode ?? $this.PreviousReportNode\n        return $node -is [TableNode]\n    }\n}"
  },
  {
    "path": "helpers/software-report-base/SoftwareReport.Nodes.psm1",
    "content": "using module ./SoftwareReport.BaseNodes.psm1\n\n#########################################\n### Nodes to describe image software ####\n#########################################\n\n# NodesFactory is used to simplify parsing different types of notes\n# Every node has own logic of parsing and this method just invokes \"FromJsonObject\" of correct node type\nclass NodesFactory {\n    static [BaseNode] ParseNodeFromObject([object] $JsonObj) {\n        if ($JsonObj.NodeType -eq [HeaderNode].Name) {\n            return [HeaderNode]::FromJsonObject($JsonObj)\n        } elseif ($JsonObj.NodeType -eq [ToolVersionNode].Name) {\n            return [ToolVersionNode]::FromJsonObject($JsonObj)\n        } elseif ($JsonObj.NodeType -eq [ToolVersionsListNode].Name) {\n            return [ToolVersionsListNode]::FromJsonObject($JsonObj)\n        } elseif ($JsonObj.NodeType -eq [TableNode].Name) {\n            return [TableNode]::FromJsonObject($JsonObj)\n        } elseif ($JsonObj.NodeType -eq [NoteNode].Name) {\n            return [NoteNode]::FromJsonObject($JsonObj)\n        }\n\n        throw \"Unknown node type in ParseNodeFromObject '$($JsonObj.NodeType)'\"\n    }\n}\n\nclass HeaderNode: BaseNode {\n    [ValidateNotNullOrEmpty()]\n    [String] $Title\n    [Collections.Generic.List[BaseNode]] $Children\n\n    HeaderNode([String] $Title) {\n        $this.Title = $Title\n        $this.Children = @()\n    }\n\n    [Boolean] ShouldBeIncludedToDiff() {\n        return $true\n    }\n\n    [void] AddNode([BaseNode] $node) {\n        $similarNode = $this.FindSimilarChildNode($node)\n        if ($similarNode) {\n            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)\"\n        }\n\n        if (-not $this.IsNodeHasMarkdownHeader($node)) {\n            # 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.\n            # It is done to avoid unexpected situation like this:\n            #\n            # HeaderNode A                 -> # A\n            #   HeaderNode B               -> ## B\n            #   ToolVersionNode C          -> - C\n            # ToolVersionNode D            -> - D\n            # \n            # In this example, we add 'HeaderNode B\" to 'HeaderNode A' and add 'ToolVersionNode C' to 'HeaderNode B'.\n            # Then we add 'ToolVersionNode D' to 'HeaderNode A'.\n            # But the result markdown will look like 'ToolVersionNode D' belongs to 'HeaderNode B' instead of 'HeaderNode A'.\n            $this.Children | Where-Object { $this.IsNodeHasMarkdownHeader($_) } | ForEach-Object {\n                throw \"It is not allowed to add the non-header node after the header node. Consider adding the separate HeaderNode for this node\"\n            }\n        }\n\n        $this.Children.Add($node)\n    }\n\n    [void] AddNodes([BaseNode[]] $nodes) {\n        $nodes | ForEach-Object {\n            $this.AddNode($_)\n        }\n    }\n\n    [HeaderNode] AddHeader([String] $Title) {\n        $node = [HeaderNode]::new($Title)\n        $this.AddNode($node)\n        return $node\n    }\n\n    [void] AddToolVersion([String] $ToolName, [String] $Version) {\n        $this.AddNode([ToolVersionNode]::new($ToolName, $Version))\n    }\n\n    [void] AddToolVersionsList([String] $ToolName, [String[]] $Version, [String] $MajorVersionRegex) {\n        $this.AddNode([ToolVersionsListNode]::new($ToolName, $Version, $MajorVersionRegex, \"List\"))\n    }\n\n    [void] AddToolVersionsListInline([String] $ToolName, [String[]] $Version, [String] $MajorVersionRegex) {\n        $this.AddNode([ToolVersionsListNode]::new($ToolName, $Version, $MajorVersionRegex, \"Inline\"))\n    }\n     \n    [void] AddTable([PSCustomObject[]] $Table) {\n        $this.AddNode([TableNode]::FromObjectsArray($Table))\n    }\n\n    [void] AddNote([String] $Content) {\n        $this.AddNode([NoteNode]::new($Content))\n    }\n\n    [String] ToMarkdown([Int32] $Level) {\n        $sb = [System.Text.StringBuilder]::new()\n        $sb.AppendLine()\n        $sb.AppendLine(\"$(\"#\" * $Level) $($this.Title)\")\n        $this.Children  | ForEach-Object {\n            $sb.AppendLine($_.ToMarkdown($Level + 1))\n        }\n\n        return $sb.ToString().TrimEnd()\n    }\n\n    [PSCustomObject] ToJsonObject() {\n        return [PSCustomObject]@{\n            NodeType = $this.GetType().Name\n            Title = $this.Title\n            Children = $this.Children | ForEach-Object { $_.ToJsonObject() }\n        }\n    }\n\n    static [HeaderNode] FromJsonObject([Object] $JsonObj) {\n        $node = [HeaderNode]::new($JsonObj.Title)\n        $JsonObj.Children | Where-Object { $_ } | ForEach-Object { $node.AddNode([NodesFactory]::ParseNodeFromObject($_)) }\n        return $node\n    }\n\n    [Boolean] IsSimilarTo([BaseNode] $OtherNode) {\n        if ($OtherNode.GetType() -ne [HeaderNode]) {\n            return $false\n        }\n\n        return $this.Title -eq $OtherNode.Title\n    }\n\n    [Boolean] IsIdenticalTo([BaseNode] $OtherNode) {\n        return $this.IsSimilarTo($OtherNode)\n    }\n\n    [BaseNode] FindSimilarChildNode([BaseNode] $Find) {\n        foreach ($childNode in $this.Children) {\n            if ($childNode.IsSimilarTo($Find)) {\n                return $childNode\n            }\n        }\n\n        return $null\n    }\n\n    hidden [Boolean] IsNodeHasMarkdownHeader([BaseNode] $node) {\n        if ($node -is [HeaderNode]) {\n            return $true\n        }\n\n        if (($node -is [ToolVersionsListNode]) -and ($node.ListType -eq \"List\")) {\n            return $true\n        }\n\n        return $false\n    }\n}\n\nclass ToolVersionNode: BaseToolNode {\n    [ValidateNotNullOrEmpty()]\n    [String] $Version\n\n    ToolVersionNode([String] $ToolName, [String] $Version): base($ToolName) {\n\n        if ([String]::IsNullOrEmpty($Version)) {\n            throw \"ToolVersionNode '$($this.ToolName)' has empty version\"\n        }\n\n        $this.Version = $Version\n    }\n\n    [String] ToMarkdown([Int32] $Level) {\n        return \"- $($this.ToolName) $($this.Version)\"\n    }\n\n    [String] GetValue() {\n        return $this.Version\n    }\n\n    [PSCustomObject] ToJsonObject() {\n        return [PSCustomObject]@{\n            NodeType = $this.GetType().Name\n            ToolName = $this.ToolName\n            Version = $this.Version\n        }\n    }\n\n    static [BaseNode] FromJsonObject([Object] $JsonObj) {\n        return [ToolVersionNode]::new($JsonObj.ToolName, $JsonObj.Version)\n    }\n}\n\nclass ToolVersionsListNode: BaseToolNode {\n    [ValidateNotNullOrEmpty()]\n    [String[]] $Versions\n\n    [Regex] $MajorVersionRegex\n\n    [ValidateSet(\"List\", \"Inline\")]\n    [String] $ListType\n\n    ToolVersionsListNode([String] $ToolName, [String[]] $Versions, [String] $MajorVersionRegex, [String] $ListType): base($ToolName) {\n        $this.Versions = $Versions\n\n         if ([String]::IsNullOrEmpty($Versions)) {\n            throw \"ToolVersionsListNode '$($this.ToolName)' has empty versions list\"\n        }\n\n        $this.MajorVersionRegex = [Regex]::new($MajorVersionRegex)\n        $this.ListType = $ListType\n        $this.ValidateMajorVersionRegex()\n    }\n\n    [String] ToMarkdown([Int32] $Level) {\n        if ($this.ListType -eq \"Inline\") {\n            return \"- $($this.ToolName): $($this.Versions -join ', ')\"\n        }\n\n        $sb = [System.Text.StringBuilder]::new()\n        $sb.AppendLine()\n        $sb.AppendLine(\"$(\"#\" * $Level) $($this.ToolName)\")\n        $this.Versions | ForEach-Object {\n            $sb.AppendLine(\"- $_\")\n        }\n\n        return $sb.ToString().TrimEnd()\n    }\n\n    [String] GetValue() {\n        return $this.Versions -join ', '\n    }\n\n    [String] ExtractMajorVersion([String] $Version) {\n        $match = $this.MajorVersionRegex.Match($Version)\n        if (($match.Success -ne $true) -or [String]::IsNullOrEmpty($match.Groups[0].Value)) {\n            throw \"Version '$Version' doesn't match regex '$($this.PrimaryVersionRegex)'\"\n        }\n\n        return $match.Groups[0].Value\n    }\n\n    [PSCustomObject] ToJsonObject() {\n        return [PSCustomObject]@{\n            NodeType = $this.GetType().Name\n            ToolName = $this.ToolName\n            Versions = $this.Versions\n            MajorVersionRegex = $this.MajorVersionRegex.ToString()\n            ListType = $this.ListType\n        }\n    }\n\n    static [ToolVersionsListNode] FromJsonObject([Object] $JsonObj) {\n        return [ToolVersionsListNode]::new($JsonObj.ToolName, $JsonObj.Versions, $JsonObj.MajorVersionRegex, $JsonObj.ListType)\n    }\n\n    hidden [void] ValidateMajorVersionRegex() {\n        $this.Versions | Group-Object { $this.ExtractMajorVersion($_) } | ForEach-Object {\n            if ($_.Count -gt 1) {\n                throw \"Multiple versions from list '$($this.GetValue())' return the same result from regex '$($this.MajorVersionRegex)': $($_.Name)\"\n            }\n        }\n    }\n}\n\nclass TableNode: BaseNode {\n    # It is easier to store the table as rendered lines because it will simplify finding differences in rows later\n    [ValidateNotNullOrEmpty()]\n    [String] $Headers\n    [ValidateNotNullOrEmpty()]\n    [String[]] $Rows\n\n    TableNode([String] $Headers, [String[]] $Rows) {\n        $this.Headers = $Headers\n        $this.Rows = $Rows\n        \n        $columnsCount = $this.Headers.Split(\"|\").Count\n        $this.Rows | ForEach-Object {\n            if ($_.Split(\"|\").Count -ne $columnsCount) {\n                throw \"Table has different number of columns in different rows\"\n            }\n        }\n    }\n\n    [Boolean] ShouldBeIncludedToDiff() {\n        return $true\n    }\n\n    [String] ToMarkdown([Int32] $Level) {\n        $maxColumnWidths = $this.CalculateColumnsWidth()\n        $columnsCount = $maxColumnWidths.Count\n\n        $delimiterLine = [String]::Join(\"|\", @(\"-\") * $columnsCount)\n\n        $sb = [System.Text.StringBuilder]::new()\n        @($this.Headers) + @($delimiterLine) + $this.Rows | ForEach-Object {\n            $sb.Append(\"|\")\n            $row = $_.Split(\"|\")\n\n            for ($colIndex = 0; $colIndex -lt $columnsCount; $colIndex++) {\n                $padSymbol = $row[$colIndex] -eq \"-\" ? \"-\" : \" \"\n                $cellContent = $row[$colIndex].PadRight($maxColumnWidths[$colIndex], $padSymbol)\n                $sb.Append(\" $($cellContent) |\")\n            }\n            \n            $sb.AppendLine()\n        }\n\n        return $sb.ToString().TrimEnd()\n    }\n\n    hidden [Int32[]] CalculateColumnsWidth() {\n        $maxColumnWidths = $this.Headers.Split(\"|\") | ForEach-Object { $_.Length }\n        $columnsCount = $maxColumnWidths.Count\n\n        $this.Rows | ForEach-Object {\n            $columnWidths = $_.Split(\"|\") | ForEach-Object { $_.Length }\n            for ($colIndex = 0; $colIndex -lt $columnsCount; $colIndex++) {\n                $maxColumnWidths[$colIndex] = [Math]::Max($maxColumnWidths[$colIndex], $columnWidths[$colIndex])\n            }\n        }\n\n        return $maxColumnWidths\n    }\n\n    [PSCustomObject] ToJsonObject() {\n        return [PSCustomObject]@{\n            NodeType = $this.GetType().Name\n            Headers = $this.Headers\n            Rows = $this.Rows\n        }\n    }\n\n    static [TableNode] FromJsonObject([Object] $JsonObj) {\n        return [TableNode]::new($JsonObj.Headers, $JsonObj.Rows)\n    }\n\n    [Boolean] IsSimilarTo([BaseNode] $OtherNode) {\n        if ($OtherNode.GetType() -ne [TableNode]) {\n            return $false\n        }\n\n        # We don't support having multiple TableNode instances on the same header level so such check is fine\n        return $true\n    }\n\n    [Boolean] IsIdenticalTo([BaseNode] $OtherNode) {\n        if (-not $this.IsSimilarTo($OtherNode)) {\n            return $false\n        }\n\n        # We don't compare $this.Headers intentionally\n        # It is fine to ignore the tables where headers are changed but rows are not changed\n\n        if ($this.Rows.Count -ne $OtherNode.Rows.Count) {\n            return $false\n        }\n\n        for ($rowIndex = 0; $rowIndex -lt $this.Rows.Count; $rowIndex++) {\n            if ($this.Rows[$rowIndex] -ne $OtherNode.Rows[$rowIndex]) {\n                return $false\n            }\n        }\n\n        return $true\n    }\n\n    static [TableNode] FromObjectsArray([PSCustomObject[]] $Table) {\n        if ($Table.Count -eq 0) {\n            throw \"Failed to create TableNode from empty objects array\"\n        }\n\n        [String] $tableHeaders = [TableNode]::ArrayToTableRow($Table[0].PSObject.Properties.Name)\n        [Collections.Generic.List[String]] $tableRows = @()\n\n        $Table | ForEach-Object {\n            $rowHeaders = [TableNode]::ArrayToTableRow($_.PSObject.Properties.Name)\n            if (($rowHeaders -ne $tableHeaders)) {\n                throw \"Failed to create TableNode from objects array because objects have different properties\"\n            }\n\n            $tableRows.Add([TableNode]::ArrayToTableRow($_.PSObject.Properties.Value))\n        }\n\n        return [TableNode]::new($tableHeaders, $tableRows)\n    }\n\n    hidden static [String] ArrayToTableRow([String[]] $Values) {\n        if ($Values.Count -eq 0) {\n            throw \"Failed to create TableNode because some objects are empty\"\n        }\n        $Values | ForEach-Object {\n            if ($_.Contains(\"|\")) {\n                throw \"Failed to create TableNode because some cells '$_' contains forbidden symbol '|'\"\n            }\n        }\n\n        return [String]::Join(\"|\", $Values)\n    }\n}\n\nclass NoteNode: BaseNode {\n    [ValidateNotNullOrEmpty()]\n    [String] $Content\n\n    NoteNode([String] $Content) {\n        $this.Content = $Content\n    }\n\n    [String] ToMarkdown([Int32] $Level) {\n        return @(\n            '```',\n            $this.Content,\n            '```'\n        ) -join \"`n\"\n    }\n\n    [PSCustomObject] ToJsonObject() {\n        return [PSCustomObject]@{\n            NodeType = $this.GetType().Name\n            Content = $this.Content\n        }\n    }\n\n    static [NoteNode] FromJsonObject([Object] $JsonObj) {\n        return [NoteNode]::new($JsonObj.Content)\n    }\n\n    [Boolean] IsSimilarTo([BaseNode] $OtherNode) {\n        if ($OtherNode.GetType() -ne [NoteNode]) {\n            return $false\n        }\n\n        return $this.Content -eq $OtherNode.Content\n    }\n\n    [Boolean] IsIdenticalTo([BaseNode] $OtherNode) {\n        return $this.IsSimilarTo($OtherNode)\n    }\n}"
  },
  {
    "path": "helpers/software-report-base/SoftwareReport.psm1",
    "content": "using module ./SoftwareReport.BaseNodes.psm1\nusing module ./SoftwareReport.Nodes.psm1\n\nclass SoftwareReport {\n    [ValidateNotNullOrEmpty()]\n    [HeaderNode] $Root\n\n    SoftwareReport([String] $Title) {\n        $this.Root = [HeaderNode]::new($Title)\n    }\n\n    SoftwareReport([HeaderNode] $Root) {\n        $this.Root = $Root\n    }\n\n    [String] ToJson() {\n        return $this.Root.ToJsonObject() | ConvertTo-Json -Depth 10\n    }\n\n    static [SoftwareReport] FromJson([String] $JsonString) {\n        $jsonObj = $JsonString | ConvertFrom-Json\n        $rootNode = [NodesFactory]::ParseNodeFromObject($jsonObj)\n        return [SoftwareReport]::new($rootNode)\n    }\n\n    [String] ToMarkdown() {\n        return $this.Root.ToMarkdown().Trim()\n    }\n\n    [String] GetImageVersion() {\n        $imageVersionNode = $this.Root.Children ?? @() | Where-Object { ($_ -is [ToolVersionNode]) -and ($_.ToolName -eq \"Image Version:\") } | Select-Object -First 1\n        return $imageVersionNode.Version ?? \"Unknown version\"\n    }\n}"
  },
  {
    "path": "helpers/software-report-base/tests/SoftwareReport.Difference.E2E.Tests.ps1",
    "content": "using module ../SoftwareReport.psm1\nusing module ../SoftwareReport.DifferenceCalculator.psm1\n\nDescribe \"Comparer.E2E\" {\n    It \"Some tools are updated\" {\n        # Previous report\n        $prevSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $prevSoftwareReport.Root.AddToolVersion(\"OS Version:\", \"macOS 11.7.1 (20G817)\")\n        $prevSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220918.1\")\n        $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader(\"Installed Software\")\n        $prevTools = $prevInstalledSoftware.AddHeader(\"Tools\")\n        $prevTools.AddToolVersion(\"ToolWillBeUpdated1\", \"1.0.0\")\n        $prevTools.AddToolVersion(\"ToolWillBeUpdated2\", \"3.0.1\")\n        $prevTools.AddToolVersionsList(\"ToolWillBeUpdated3\", @(\"14.0.0\", \"15.5.1\"), \"^\\d+\")\n\n        # Next report\n        $nextSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $nextSoftwareReport.Root.AddToolVersion(\"OS Version:\", \"macOS 11.7.1 (20G817)\")\n        $nextSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220922.1\")\n        $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader(\"Installed Software\")\n        $nextTools = $nextInstalledSoftware.AddHeader(\"Tools\")\n        $nextTools.AddToolVersion(\"ToolWillBeUpdated1\", \"2.5.0\")\n        $nextTools.AddToolVersion(\"ToolWillBeUpdated2\", \"3.0.2\")\n        $nextTools.AddToolVersionsList(\"ToolWillBeUpdated3\", @(\"14.2.0\", \"15.5.1\"), \"^\\d+\")\n\n        # Compare reports\n        $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)\n        $comparer.CompareReports()\n        $comparer.GetMarkdownReport() | Should -BeExactly @'\n# :desktop_computer: Actions Runner Image: macOS 11\n- OS Version: macOS 11.7.1 (20G817)\n- Image Version: 20220922.1\n\n## :mega: What's changed?\n\n### Updated\n\n<table>\n    <thead>\n        <th>Category</th>\n        <th>Tool name</th>\n        <th>Previous (20220918.1)</th>\n        <th>Current (20220922.1)</th>\n    </thead>\n    <tbody>\n        <tr>\n            <td rowspan=\"3\">Tools</td>\n            <td>ToolWillBeUpdated1</td>\n            <td>1.0.0</td>\n            <td>2.5.0</td>\n        </tr>\n        <tr>\n            <td>ToolWillBeUpdated2</td>\n            <td>3.0.1</td>\n            <td>3.0.2</td>\n        </tr>\n        <tr>\n            <td>ToolWillBeUpdated3</td>\n            <td>14.0.0</td>\n            <td>14.2.0</td>\n        </tr>\n    </tbody>\n</table>\n\n\n'@\n    }\n    \n    It \"Some tools are updated, added and removed\" {\n        # Previous report\n        $prevSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $prevSoftwareReport.Root.AddToolVersion(\"OS Version:\", \"macOS 11.7.1 (20G817)\")\n        $prevSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220918.1\")\n        $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader(\"Installed Software\")\n\n        $prevLanguagesAndRuntimes = $prevInstalledSoftware.AddHeader(\"Language and Runtime\")\n        $prevLanguagesAndRuntimes.AddToolVersion(\"ToolWillBeRemoved\", \"5.1.16(1)-release\")\n        $prevLanguagesAndRuntimes.AddToolVersionsListInline(\"ToolWithMultipleVersions3\", @(\"1.2.100\", \"1.2.200\", \"1.3.500\", \"1.4.100\", \"1.4.200\"), \"^\\d+\\.\\d+\\.\\d\")\n        $prevLanguagesAndRuntimes.AddToolVersion(\"ToolWithoutChanges\", \"5.34.0\")\n        $prevLanguagesAndRuntimes.AddToolVersion(\"ToolWillBeUpdated\", \"8.1.0\")\n\n        $prevCachedTools = $prevInstalledSoftware.AddHeader(\"Cached Tools\")\n        $prevCachedTools.AddToolVersionsList(\"ToolWithMultipleVersions1\", @(\"2.7.3\", \"2.8.1\", \"3.1.2\"), \"^\\d+\\.\\d+\")\n        $prevCachedTools.AddToolVersionsList(\"ToolWithMultipleVersions2\", @(\"14.8.0\", \"15.1.0\", \"16.4.2\"), \"^\\d+\")\n\n        $prevSQLSection = $prevInstalledSoftware.AddHeader(\"Databases\")\n        $prevSQLSection.AddToolVersion(\"MineSQL\", \"6.1.0\")\n        $prevSQLSection.AddNote(\"First Note\")\n\n        # Next report\n        $nextSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $nextSoftwareReport.Root.AddToolVersion(\"OS Version:\", \"macOS 11.7.2 (20G922)\")\n        $nextSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220922.0\")\n        $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader(\"Installed Software\")\n\n        $nextLanguagesAndRuntimes = $nextInstalledSoftware.AddHeader(\"Language and Runtime\")\n        $nextLanguagesAndRuntimes.AddToolVersion(\"ToolWillBeAdded\", \"16.18.0\")\n        $nextLanguagesAndRuntimes.AddToolVersionsListInline(\"ToolWithMultipleVersions3\", @(\"1.2.200\", \"1.3.515\", \"1.4.100\", \"1.4.200\", \"1.5.800\"), \"^\\d+\\.\\d+\\.\\d\")\n        $nextLanguagesAndRuntimes.AddToolVersion(\"ToolWithoutChanges\", \"5.34.0\")\n        $nextLanguagesAndRuntimes.AddToolVersion(\"ToolWillBeUpdated\", \"8.3.0\")\n\n        $nextCachedTools = $nextInstalledSoftware.AddHeader(\"Cached Tools\")\n        $nextCachedTools.AddToolVersionsList(\"ToolWithMultipleVersions1\", @(\"2.7.3\", \"2.8.1\", \"3.1.2\"), \"^\\d+\\.\\d+\")\n        $nextCachedTools.AddToolVersionsList(\"ToolWithMultipleVersions2\", @(\"15.1.0\", \"16.4.2\", \"17.0.1\"), \"^\\d+\")\n\n        $nextSQLSection = $nextInstalledSoftware.AddHeader(\"Databases\")\n        $nextSQLSection.AddToolVersion(\"MineSQL\", \"6.1.1\")\n        $nextSQLSection.AddNote(\"Second Note\")\n\n        # Compare reports\n        $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)\n        $comparer.CompareReports()\n        $comparer.GetMarkdownReport() | Should -BeExactly @'\n# :desktop_computer: Actions Runner Image: macOS 11\n- OS Version: macOS 11.7.2 (20G922)\n- Image Version: 20220922.0\n\n## :mega: What's changed?\n\n### Added :heavy_plus_sign:\n\n<table>\n    <thead>\n        <th>Category</th>\n        <th>Tool name</th>\n        <th>Current (20220922.0)</th>\n    </thead>\n    <tbody>\n        <tr>\n            <td rowspan=\"2\">Language and Runtime</td>\n            <td>ToolWillBeAdded</td>\n            <td>16.18.0</td>\n        </tr>\n        <tr>\n            <td>ToolWithMultipleVersions3</td>\n            <td>1.5.800</td>\n        </tr>\n        <tr>\n            <td rowspan=\"1\">Cached Tools</td>\n            <td>ToolWithMultipleVersions2</td>\n            <td>17.0.1</td>\n        </tr>\n    </tbody>\n</table>\n\n### Deleted :heavy_minus_sign:\n\n<table>\n    <thead>\n        <th>Category</th>\n        <th>Tool name</th>\n        <th>Previous (20220918.1)</th>\n    </thead>\n    <tbody>\n        <tr>\n            <td rowspan=\"2\">Language and Runtime</td>\n            <td>ToolWithMultipleVersions3</td>\n            <td>1.2.100</td>\n        </tr>\n        <tr>\n            <td>ToolWillBeRemoved</td>\n            <td>5.1.16(1)-release</td>\n        </tr>\n        <tr>\n            <td rowspan=\"1\">Cached Tools</td>\n            <td>ToolWithMultipleVersions2</td>\n            <td>14.8.0</td>\n        </tr>\n    </tbody>\n</table>\n\n### Updated\n\n<table>\n    <thead>\n        <th>Category</th>\n        <th>Tool name</th>\n        <th>Previous (20220918.1)</th>\n        <th>Current (20220922.0)</th>\n    </thead>\n    <tbody>\n        <tr>\n            <td rowspan=\"1\"></td>\n            <td>OS Version</td>\n            <td>macOS 11.7.1 (20G817)</td>\n            <td>macOS 11.7.2 (20G922)</td>\n        </tr>\n        <tr>\n            <td rowspan=\"2\">Language and Runtime</td>\n            <td>ToolWithMultipleVersions3</td>\n            <td>1.3.500</td>\n            <td>1.3.515</td>\n        </tr>\n        <tr>\n            <td>ToolWillBeUpdated</td>\n            <td>8.1.0</td>\n            <td>8.3.0</td>\n        </tr>\n        <tr>\n            <td rowspan=\"1\">Databases</td>\n            <td>MineSQL</td>\n            <td>6.1.0</td>\n            <td>6.1.1</td>\n        </tr>\n    </tbody>\n</table>\n\n\n'@\n    }\n\n    It \"Header tree changes\" {\n        # Previous report\n        $prevSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $prevSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220918.1\")\n        $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader(\"Installed Software\")\n        $prevInstalledSoftware.AddToolVersion(\"ToolWithoutChanges\", \"5.34.0\")\n        $prevInstalledSoftware.AddHeader(\"HeaderWillBeRemoved\").AddHeader(\"SubheaderWillBeRemoved\").AddToolVersion(\"ToolWillBeRemoved\", \"1.0.0\")\n        $prevInstalledSoftware.AddHeader(\"Header1\").AddToolVersion(\"ToolWillBeMovedToAnotherHeader\", \"3.0.0\")\n\n        # Next report\n        $nextSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $nextSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220922.0\")\n        $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader(\"Installed Software\")\n        $nextInstalledSoftware.AddToolVersion(\"ToolWithoutChanges\", \"5.34.0\")\n        $nextInstalledSoftware.AddHeader(\"HeaderWillBeAdded\").AddHeader(\"SubheaderWillBeAdded\").AddToolVersion(\"ToolWillBeAdded\", \"5.0.0\")\n        $nextInstalledSoftware.AddHeader(\"Header2\").AddToolVersion(\"ToolWillBeMovedToAnotherHeader\", \"3.0.0\")\n\n        # Compare reports\n        $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)\n        $comparer.CompareReports()\n        $comparer.GetMarkdownReport() | Should -BeExactly @'\n# :desktop_computer: Actions Runner Image: macOS 11\n- Image Version: 20220922.0\n\n## :mega: What's changed?\n\n### Added :heavy_plus_sign:\n\n<table>\n    <thead>\n        <th>Category</th>\n        <th>Tool name</th>\n        <th>Current (20220922.0)</th>\n    </thead>\n    <tbody>\n        <tr>\n            <td rowspan=\"1\">HeaderWillBeAdded ><br> SubheaderWillBeAdded</td>\n            <td>ToolWillBeAdded</td>\n            <td>5.0.0</td>\n        </tr>\n        <tr>\n            <td rowspan=\"1\">Header2</td>\n            <td>ToolWillBeMovedToAnotherHeader</td>\n            <td>3.0.0</td>\n        </tr>\n    </tbody>\n</table>\n\n### Deleted :heavy_minus_sign:\n\n<table>\n    <thead>\n        <th>Category</th>\n        <th>Tool name</th>\n        <th>Previous (20220918.1)</th>\n    </thead>\n    <tbody>\n        <tr>\n            <td rowspan=\"1\">HeaderWillBeRemoved ><br> SubheaderWillBeRemoved</td>\n            <td>ToolWillBeRemoved</td>\n            <td>1.0.0</td>\n        </tr>\n        <tr>\n            <td rowspan=\"1\">Header1</td>\n            <td>ToolWillBeMovedToAnotherHeader</td>\n            <td>3.0.0</td>\n        </tr>\n    </tbody>\n</table>\n\n\n'@\n    }\n\n    It \"Tables are added and removed\" {\n        # Previous report\n        $prevSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $prevSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220918.1\")\n        $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader(\"Installed Software\")\n        $prevInstalledSoftware.AddHeader(\"HeaderWillExist\").AddTable(@(\n            [PSCustomObject]@{TableInExistingHeaderWillBeRemoved = \"Q\"; Value = \"25\"},\n            [PSCustomObject]@{TableInExistingHeaderWillBeRemoved = \"O\"; Value = \"24\"}\n        ))\n            \n        $prevTools = $prevInstalledSoftware.AddHeader(\"Tools\")\n        $prevTools.AddHeader(\"HeaderWillBeRemoved\").AddTable(@(\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"Z\"; Value = \"30\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"W\"; Value = \"29\"}\n        ))\n\n        # Next report\n        $nextSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $nextSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220922.1\")\n        $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader(\"Installed Software\")\n        $nextInstalledSoftware.AddHeader(\"HeaderWillExist\")\n        $nextTools = $nextInstalledSoftware.AddHeader(\"Tools\")\n        $nextTools.AddToolVersion(\"ToolWillBeAdded\", \"3.0.1\")\n        $nextTools.AddTable(@(\n            [PSCustomObject]@{NewTableInExistingHeader = \"A\"; Value = \"1\"},\n            [PSCustomObject]@{NewTableInExistingHeader = \"B\"; Value = \"2\"}\n        ))\n        $nextTools.AddHeader(\"NewHeaderWithTable\").AddTable(@(\n            [PSCustomObject]@{NewTableInNewHeader = \"C\"; Value = \"3\"},\n            [PSCustomObject]@{NewTableInNewHeader = \"D\"; Value = \"4\"}\n        ))\n\n        # Compare reports\n        $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)\n        $comparer.CompareReports()\n        $comparer.GetMarkdownReport() | Should -BeExactly @'\n# :desktop_computer: Actions Runner Image: macOS 11\n- Image Version: 20220922.1\n\n## :mega: What's changed?\n\n### Added :heavy_plus_sign:\n\n<table>\n    <thead>\n        <th>Category</th>\n        <th>Tool name</th>\n        <th>Current (20220922.1)</th>\n    </thead>\n    <tbody>\n        <tr>\n            <td rowspan=\"1\">Tools</td>\n            <td>ToolWillBeAdded</td>\n            <td>3.0.1</td>\n        </tr>\n    </tbody>\n</table>\n\n#### Tools\n| NewTableInExistingHeader | Value |\n| ------------------------ | ----- |\n| A                        | 1     |\n| B                        | 2     |\n\n#### Tools > NewHeaderWithTable\n| NewTableInNewHeader | Value |\n| ------------------- | ----- |\n| C                   | 3     |\n| D                   | 4     |\n\n### Deleted :heavy_minus_sign:\n\n#### HeaderWillExist\n| TableInExistingHeaderWillBeRemoved | Value  |\n| ---------------------------------- | ------ |\n| ~~Q~~                              | ~~25~~ |\n| ~~O~~                              | ~~24~~ |\n\n#### Tools > HeaderWillBeRemoved\n| TableWillBeRemovedWithHeader | Value  |\n| ---------------------------- | ------ |\n| ~~Z~~                        | ~~30~~ |\n| ~~W~~                        | ~~29~~ |\n\n\n'@\n    }\n\n    It \"Tables are changed\" {\n        # Previous report\n        $prevSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $prevSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220918.1\")\n        $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader(\"Installed Software\")\n        $prevTools = $prevInstalledSoftware.AddHeader(\"Tools\")\n        $prevTools.AddHeader(\"TableWithAddedRows\").AddTable(@(\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"AA\"; Value = \"10\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"AB\"; Value = \"11\"}\n        ))\n        $prevTools.AddHeader(\"TableWithRemovedRows\").AddTable(@(\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"BA\"; Value = \"32\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"BB\"; Value = \"33\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"BC\"; Value = \"34\"}\n        ))\n        $prevTools.AddHeader(\"TableWithUpdatedRow\").AddTable(@(\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"CA\"; Value = \"42\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"CB\"; Value = \"43\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"CC\"; Value = \"44\"}\n        ))\n        $prevTools.AddHeader(\"TableWithUpdatedRows\").AddTable(@(\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"DA\"; Value = \"50\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"DB\"; Value = \"51\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"DC\"; Value = \"52\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"DD\"; Value = \"53\"}\n        ))\n        $prevTools.AddHeader(\"TableWithComplexChanges\").AddTable(@(\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"EA\"; Value = \"62\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"EB\"; Value = \"63\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"EC\"; Value = \"64\"}\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"ED\"; Value = \"65\"}\n        ))\n\n        $prevTools.AddHeader(\"TableWithOnlyHeaderChanged\").AddTable(@(\n            [PSCustomObject]@{TableWithOnlyHeaderChanged = \"FA\"; Value = \"72\"},\n            [PSCustomObject]@{TableWithOnlyHeaderChanged = \"FB\"; Value = \"73\"}\n        ))\n\n        $prevTools.AddHeader(\"TableWithHeaderAndRowsChanges\").AddTable(@(\n            [PSCustomObject]@{TableWithHeaderAndRowsChanges = \"GA\"; Value = \"82\"},\n            [PSCustomObject]@{TableWithHeaderAndRowsChanges = \"GB\"; Value = \"83\"},\n            [PSCustomObject]@{TableWithHeaderAndRowsChanges = \"GC\"; Value = \"84\"}\n        ))\n\n        # Next report\n        $nextSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $nextSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220922.1\")\n        $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader(\"Installed Software\")\n        $nextTools = $nextInstalledSoftware.AddHeader(\"Tools\")\n        $nextTools.AddHeader(\"TableWithAddedRows\").AddTable(@(\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"AA\"; Value = \"10\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"AB\"; Value = \"11\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"AC\"; Value = \"12\"}\n        ))\n        $nextTools.AddHeader(\"TableWithRemovedRows\").AddTable(@(\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"BB\"; Value = \"33\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"BC\"; Value = \"34\"}\n        ))\n        $nextTools.AddHeader(\"TableWithUpdatedRow\").AddTable(@(\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"CA\"; Value = \"42\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"CB\"; Value = \"500\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"CC\"; Value = \"44\"}\n        ))\n        $nextTools.AddHeader(\"TableWithUpdatedRows\").AddTable(@(\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"DA\"; Value = \"50\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"DB\"; Value = \"5100\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"DC\"; Value = \"5200\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"DD\"; Value = \"53\"}\n        ))\n        $nextTools.AddHeader(\"TableWithComplexChanges\").AddTable(@(\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"EB\"; Value = \"63\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"EC\"; Value = \"640\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"ED\"; Value = \"65\"},\n            [PSCustomObject]@{TableWillBeRemovedWithHeader = \"EE\"; Value = \"66\"}\n        ))\n\n        $nextTools.AddHeader(\"TableWithOnlyHeaderChanged\").AddTable(@(\n            [PSCustomObject]@{TableWithOnlyHeaderChanged2 = \"FA\"; Value = \"72\"},\n            [PSCustomObject]@{TableWithOnlyHeaderChanged2 = \"FB\"; Value = \"73\"}\n        ))\n\n        $nextTools.AddHeader(\"TableWithHeaderAndRowsChanges\").AddTable(@(\n            [PSCustomObject]@{TableWithHeaderAndRowsChanges2 = \"GA\"; Value = \"82\"},\n            [PSCustomObject]@{TableWithHeaderAndRowsChanges2 = \"GE\"; Value = \"850\"},\n            [PSCustomObject]@{TableWithHeaderAndRowsChanges2 = \"GC\"; Value = \"840\"}\n        ))\n\n        # Compare reports\n        $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)\n        $comparer.CompareReports()\n        $comparer.GetMarkdownReport() | Should -BeExactly @'\n# :desktop_computer: Actions Runner Image: macOS 11\n- Image Version: 20220922.1\n\n## :mega: What's changed?\n\n### Added :heavy_plus_sign:\n\n#### Tools > TableWithAddedRows\n| TableWillBeRemovedWithHeader | Value |\n| ---------------------------- | ----- |\n| AC                           | 12    |\n\n#### Tools > TableWithHeaderAndRowsChanges\n| TableWithHeaderAndRowsChanges2 | Value |\n| ------------------------------ | ----- |\n| GA                             | 82    |\n| GE                             | 850   |\n| GC                             | 840   |\n\n### Deleted :heavy_minus_sign:\n\n#### Tools > TableWithRemovedRows\n| TableWillBeRemovedWithHeader | Value  |\n| ---------------------------- | ------ |\n| ~~BA~~                       | ~~32~~ |\n\n#### Tools > TableWithHeaderAndRowsChanges\n| TableWithHeaderAndRowsChanges | Value  |\n| ----------------------------- | ------ |\n| ~~GA~~                        | ~~82~~ |\n| ~~GB~~                        | ~~83~~ |\n| ~~GC~~                        | ~~84~~ |\n\n### Updated\n\n#### Tools > TableWithUpdatedRow\n| TableWillBeRemovedWithHeader | Value  |\n| ---------------------------- | ------ |\n| ~~CB~~                       | ~~43~~ |\n| CB                           | 500    |\n\n#### Tools > TableWithUpdatedRows\n| TableWillBeRemovedWithHeader | Value  |\n| ---------------------------- | ------ |\n| ~~DB~~                       | ~~51~~ |\n| ~~DC~~                       | ~~52~~ |\n| DB                           | 5100   |\n| DC                           | 5200   |\n\n#### Tools > TableWithComplexChanges\n| TableWillBeRemovedWithHeader | Value  |\n| ---------------------------- | ------ |\n| ~~EA~~                       | ~~62~~ |\n| ~~EC~~                       | ~~64~~ |\n| EC                           | 640    |\n| EE                           | 66     |\n\n\n'@\n    }\n\n    It \"Reports are identical\" {\n        # Previous report\n        $prevSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $prevSoftwareReport.Root.AddToolVersion(\"OS Version:\", \"macOS 11.7.1 (20G817)\")\n        $prevSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220918.1\")\n        $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader(\"Installed Software\")\n        $prevTools = $prevInstalledSoftware.AddHeader(\"Tools\")\n        $prevTools.AddToolVersion(\"ToolA\", \"1.0.0\")\n        $prevTools.AddToolVersion(\"ToolB\", \"3.0.1\")\n\n        # Next report\n        $nextSoftwareReport = [SoftwareReport]::new(\"macOS 11\")\n        $nextSoftwareReport.Root.AddToolVersion(\"OS Version:\", \"macOS 11.7.1 (20G817)\")\n        $nextSoftwareReport.Root.AddToolVersion(\"Image Version:\", \"20220922.1\")\n        $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader(\"Installed Software\")\n        $nextTools = $nextInstalledSoftware.AddHeader(\"Tools\")\n        $nextTools.AddToolVersion(\"ToolA\", \"1.0.0\")\n        $nextTools.AddToolVersion(\"ToolB\", \"3.0.1\")\n\n        # Compare reports\n        $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport)\n        $comparer.CompareReports()\n        $comparer.GetMarkdownReport() | Should -BeExactly @'\n# :desktop_computer: Actions Runner Image: macOS 11\n- OS Version: macOS 11.7.1 (20G817)\n- Image Version: 20220922.1\n\n## :mega: What's changed?\n\n\n'@\n    }\n}"
  },
  {
    "path": "helpers/software-report-base/tests/SoftwareReport.DifferenceCalculator.Unit.Tests.ps1",
    "content": "using module ../SoftwareReport.Nodes.psm1\nusing module ../SoftwareReport.DifferenceCalculator.psm1\n\nBeforeDiscovery {\n    Import-Module $(Join-Path $PSScriptRoot \"TestHelpers.psm1\") -DisableNameChecking\n}\n\nDescribe \"Comparer.UnitTests\" {\n    Describe \"Headers Tree\" {\n        It \"Add Node to existing header\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddToolVersion(\"MyTool1\", \"2.1.3\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 1\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 0\n\n            $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty\n            $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.AddedItems[0].CurrentReportNode.Version | Should -Be \"2.1.3\"\n            $comparer.AddedItems[0].Headers | Should -BeArray @(\"MyHeader\")\n        }\n\n        It \"Add new header with Node\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddHeader(\"MySubHeader\").AddToolVersion(\"MyTool1\", \"2.1.3\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 1\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 0\n\n            $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty\n            $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.AddedItems[0].CurrentReportNode.Version | Should -Be \"2.1.3\"\n            $comparer.AddedItems[0].Headers | Should -BeArray @(\"MyHeader\", \"MySubHeader\")\n        }\n\n        It \"Remove Node from existing header\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddToolVersion(\"MyTool1\", \"2.1.3\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 1\n\n            $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be \"2.1.3\"\n            $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty\n            $comparer.DeletedItems[0].Headers | Should -BeArray @(\"MyHeader\")\n        }\n\n        It \"Remove header with Node\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddHeader(\"MySubheader\").AddToolVersion(\"MyTool1\", \"2.1.3\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 1\n\n            $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be \"2.1.3\"\n            $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty\n            $comparer.DeletedItems[0].Headers | Should -BeArray @(\"MyHeader\", \"MySubheader\")\n        }\n\n        It \"Node with minor changes\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddHeader(\"MySubheader\").AddToolVersion(\"MyTool1\", \"2.1.3\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddHeader(\"MySubheader\").AddToolVersion(\"MyTool1\", \"2.1.4\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 1\n            $comparer.DeletedItems | Should -HaveCount 0\n\n            $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].PreviousReportNode.Version | Should -Be \"2.1.3\"\n            $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].CurrentReportNode.Version | Should -Be \"2.1.4\"\n            $comparer.ChangedItems[0].Headers | Should -BeArray @(\"MyHeader\", \"MySubHeader\")\n        }\n\n        It \"Node without changes\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddHeader(\"MySubheader\").AddToolVersion(\"MyTool1\", \"2.1.3\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddHeader(\"MySubheader\").AddToolVersion(\"MyTool1\", \"2.1.3\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 0\n        }\n\n        It \"Node is moved to different header\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddHeader(\"MySubheader\").AddToolVersion(\"MyTool1\", \"2.1.3\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddHeader(\"MySubheader2\").AddToolVersion(\"MyTool1\", \"2.1.3\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 1\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 1\n\n            $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty\n            $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.AddedItems[0].CurrentReportNode.Version | Should -Be \"2.1.3\"\n            $comparer.AddedItems[0].Headers | Should -BeArray @(\"MyHeader\", \"MySubheader2\")\n\n            $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be \"2.1.3\"\n            $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty\n            $comparer.DeletedItems[0].Headers | Should -BeArray @(\"MyHeader\", \"MySubheader\")\n        }\n\n        It \"Complex structure\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevSubHeader = $prevReport.AddHeader(\"MyHeader\").AddHeader(\"MySubheader\")\n            $prevSubHeader.AddToolVersion(\"MyTool1\", \"2.1.3\")\n            $prevSubHeader.AddHeader(\"MySubSubheader\").AddToolVersion(\"MyTool2\", \"2.9.1\")\n            $prevReport.AddHeader(\"MyHeader2\")\n            $prevReport.AddHeader(\"MyHeader3\").AddHeader(\"MySubheader3\").AddToolVersion(\"MyTool3\", \"14.2.1\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextSubHeader = $nextReport.AddHeader(\"MyHeader\").AddHeader(\"MySubheader\")\n            $nextSubHeader.AddToolVersion(\"MyTool1\", \"2.1.4\")\n            $nextSubSubHeader = $nextSubHeader.AddHeader(\"MySubSubheader\")\n            $nextSubSubHeader.AddToolVersion(\"MyTool2\", \"2.9.1\")\n            $nextSubSubHeader.AddToolVersion(\"MyTool4\", \"2.7.6\")\n            $nextReport.AddHeader(\"MyHeader2\")\n            $nextReport.AddHeader(\"MyHeader3\")\n\n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 1\n            $comparer.ChangedItems | Should -HaveCount 1\n            $comparer.DeletedItems | Should -HaveCount 1\n\n            $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty\n            $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool4\"\n            $comparer.AddedItems[0].CurrentReportNode.Version | Should -Be \"2.7.6\"\n            $comparer.AddedItems[0].Headers | Should -BeArray @(\"MyHeader\", \"MySubheader\", \"MySubSubheader\")\n\n            $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].PreviousReportNode.Version | Should -Be \"2.1.3\"\n            $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].CurrentReportNode.Version | Should -Be \"2.1.4\"\n            $comparer.ChangedItems[0].Headers | Should -BeArray @(\"MyHeader\", \"MySubheader\")\n\n            $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool3\"\n            $comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be \"14.2.1\"\n            $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty\n            $comparer.DeletedItems[0].Headers | Should -BeArray @(\"MyHeader3\", \"MySubheader3\")\n        }\n    }\n\n    Describe \"ToolVersionNode\" {\n        It \"ToolVersionNode is updated\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddToolVersion(\"MyTool1\", \"2.1.3\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddToolVersion(\"MyTool1\", \"2.1.4\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 1\n            $comparer.DeletedItems | Should -HaveCount 0\n\n            $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].PreviousReportNode.Version | Should -Be \"2.1.3\"\n            $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode])\n            $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].CurrentReportNode.Version | Should -Be \"2.1.4\"\n            $comparer.ChangedItems[0].Headers | Should -BeArray @(\"MyHeader\")\n        }\n    }\n\n    Describe \"ToolVersionsListNode\" {\n        It \"Single version is not changed\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"2.1.3\"), \"^.+\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"2.1.3\"), \"^.+\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 0\n        }\n\n        It \"Single version is changed\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"2.1.3\"), \"^\\d+\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"2.1.4\"), \"^\\d+\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 1\n            $comparer.DeletedItems | Should -HaveCount 0\n\n            $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @(\"2.1.3\")\n            $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @(\"2.1.4\")\n        }\n\n        It \"Major version is added\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"2.1.3\"), \"^\\d+\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"2.1.3\", \"3.1.4\"), \"^\\d+\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 1\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 0\n\n            $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty\n            $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @(\"3.1.4\")\n        }\n\n        It \"Major version is removed\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"2.1.3\", \"3.1.4\"), \"^\\d+\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"3.1.4\"), \"^\\d+\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 1\n\n            $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @(\"2.1.3\")\n            $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty\n        }\n\n        It \"Major version is changed\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"3.1.4\"), \"^\\d+\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"3.2.0\"), \"^\\d+\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 1\n            $comparer.DeletedItems | Should -HaveCount 0\n\n            $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @(\"3.1.4\")\n            $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @(\"3.2.0\")\n        }\n\n        It \"Major version is added, removed and updated at the same time\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"1.0.0\", \"2.1.3\", \"3.1.4\", \"4.0.2\"), \"^\\d+\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"2.1.3\", \"3.2.0\", \"4.0.2\", \"5.1.0\"), \"^\\d+\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 1\n            $comparer.ChangedItems | Should -HaveCount 1\n            $comparer.DeletedItems | Should -HaveCount 1\n\n            $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty\n            $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @(\"5.1.0\")\n\n            $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @(\"3.1.4\")\n            $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @(\"3.2.0\")\n\n            $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @(\"1.0.0\")\n            $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty\n        }\n\n        It \"Minor version is added, removed and updated at the same time\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $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+\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $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+\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 1\n            $comparer.ChangedItems | Should -HaveCount 1\n            $comparer.DeletedItems | Should -HaveCount 1\n\n            $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty\n            $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @(\"2.9.2\", \"2.10.3\")\n\n            $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @(\"2.6.0\", \"2.7.4\")\n            $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @(\"2.6.2\", \"2.7.5\")\n\n            $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @(\"2.3.8\", \"2.4.9\")\n            $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty\n        }\n\n        It \"Patch version is added, removed and updated at the same time\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"2.3.8\", \"2.4.9\", \"2.5.3\", \"2.6.0\", \"2.7.4\"), \"^\\d+\\.\\d+\\.\\d+\")\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddToolVersionsList(\"MyTool1\", @(\"2.4.9\", \"2.5.4\", \"2.6.0\", \"2.7.5\", \"2.8.2\"), \"^\\d+\\.\\d+\\.\\d+\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 1\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 1\n\n            $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty\n            $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @(\"2.5.4\", \"2.7.5\", \"2.8.2\")\n\n            $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode])\n            $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be \"MyTool1\"\n            $comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @(\"2.3.8\", \"2.5.3\", \"2.7.4\")\n            $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty\n        }\n    }\n\n    Describe \"TableNode\" {\n        It \"Rows are added\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B1|B2\")))\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B1|B2\", \"C1|C2\", \"D1|D2\")))\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 1\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 0\n\n            $comparer.AddedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])\n            $comparer.AddedItems[0].PreviousReportNode.Headers | Should -Be \"Name|Value\"\n            $comparer.AddedItems[0].PreviousReportNode.Rows | Should -BeArray @(\"A1|A2\", \"B1|B2\")\n            $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])\n            $comparer.AddedItems[0].CurrentReportNode.Headers | Should -Be \"Name|Value\"\n            $comparer.AddedItems[0].CurrentReportNode.Rows | Should -BeArray @(\"A1|A2\", \"B1|B2\", \"C1|C2\", \"D1|D2\")\n        }\n\n        It \"Rows are deleted\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B1|B2\", \"C1|C2\", \"D1|D2\")))\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"C1|C2\", \"D1|D2\")))\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 1\n\n            $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])\n            $comparer.DeletedItems[0].PreviousReportNode.Headers | Should -Be \"Name|Value\"\n            $comparer.DeletedItems[0].PreviousReportNode.Rows | Should -BeArray @(\"A1|A2\", \"B1|B2\", \"C1|C2\", \"D1|D2\")\n            $comparer.DeletedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])\n            $comparer.DeletedItems[0].CurrentReportNode.Headers | Should -Be \"Name|Value\"\n            $comparer.DeletedItems[0].CurrentReportNode.Rows | Should -BeArray @(\"C1|C2\", \"D1|D2\")\n        }\n\n        It \"Rows are changed\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B1|B2\")))\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B3|B4\")))\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 1\n            $comparer.DeletedItems | Should -HaveCount 0\n\n            $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])\n            $comparer.ChangedItems[0].PreviousReportNode.Headers | Should -Be \"Name|Value\"\n            $comparer.ChangedItems[0].PreviousReportNode.Rows | Should -BeArray @(\"A1|A2\", \"B1|B2\")\n            $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])\n            $comparer.ChangedItems[0].CurrentReportNode.Headers | Should -Be \"Name|Value\"\n            $comparer.ChangedItems[0].CurrentReportNode.Rows | Should -BeArray @(\"A1|A2\", \"B3|B4\")\n        }\n\n        It \"Rows are changed and updated at the same time\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B1|B2\")))\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B3|B4\", \"C1|C2\")))\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 1\n            $comparer.DeletedItems | Should -HaveCount 0\n\n            $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])\n            $comparer.ChangedItems[0].PreviousReportNode.Headers | Should -Be \"Name|Value\"\n            $comparer.ChangedItems[0].PreviousReportNode.Rows | Should -BeArray @(\"A1|A2\", \"B1|B2\")\n            $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])\n            $comparer.ChangedItems[0].CurrentReportNode.Headers | Should -Be \"Name|Value\"\n            $comparer.ChangedItems[0].CurrentReportNode.Rows | Should -BeArray @(\"A1|A2\", \"B3|B4\", \"C1|C2\")\n        }\n\n        It \"Rows are changed and removed at the same time\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B1|B2\", \"C1|C2\")))\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B3|B4\")))\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 1\n            $comparer.DeletedItems | Should -HaveCount 0\n\n            $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])\n            $comparer.ChangedItems[0].PreviousReportNode.Headers | Should -Be \"Name|Value\"\n            $comparer.ChangedItems[0].PreviousReportNode.Rows | Should -BeArray @(\"A1|A2\", \"B1|B2\", \"C1|C2\")\n            $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])\n            $comparer.ChangedItems[0].CurrentReportNode.Headers | Should -Be \"Name|Value\"\n            $comparer.ChangedItems[0].CurrentReportNode.Rows | Should -BeArray @(\"A1|A2\", \"B3|B4\")\n        }\n\n        It \"Rows are not changed\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B1|B2\")))\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B1|B2\")))\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 0\n        }\n\n        It \"Rows are not changed but header is changed\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B1|B2\")))\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value2\", @(\"A1|A2\", \"B1|B2\")))\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 0\n        }\n\n        It \"Rows are changed and header is changed at the same time\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value\", @(\"A1|A2\", \"B1|B2\")))\n\n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddHeader(\"MyHeader\").AddNode([TableNode]::new(\"Name|Value2\", @(\"A1|A2\", \"B1|B2\", \"C1|C2\")))\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n\n            $comparer.AddedItems | Should -HaveCount 1\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 1\n\n            $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty\n            $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([TableNode])\n            $comparer.AddedItems[0].CurrentReportNode.Headers | Should -Be \"Name|Value2\"\n            $comparer.AddedItems[0].CurrentReportNode.Rows | Should -BeArray @(\"A1|A2\", \"B1|B2\", \"C1|C2\")\n\n            $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([TableNode])\n            $comparer.DeletedItems[0].PreviousReportNode.Headers | Should -Be \"Name|Value\"\n            $comparer.DeletedItems[0].PreviousReportNode.Rows | Should -BeArray @(\"A1|A2\", \"B1|B2\")\n            $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty\n        }\n    }\n\n    Describe \"NoteNode\" {\n        It \"NoteNode is ignored from report\" {\n            $prevReport = [HeaderNode]::new(\"Version 1\")\n            $prevReport.AddNote(\"MyFirstNote\")\n            $prevReport.AddHeader(\"MyFirstHeader\").AddNote(\"MyFirstSubNote\")\n    \n            $nextReport = [HeaderNode]::new(\"Version 2\")\n            $nextReport.AddNote(\"MySecondNote\")\n            $nextReport.AddHeader(\"MySecondHeader\").AddNote(\"MySecondSubNote\")\n            \n            $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport)\n            $comparer.CompareReports()\n    \n            $comparer.AddedItems | Should -HaveCount 0\n            $comparer.ChangedItems | Should -HaveCount 0\n            $comparer.DeletedItems | Should -HaveCount 0\n        }\n    }\n}"
  },
  {
    "path": "helpers/software-report-base/tests/SoftwareReport.DifferenceRender.Unit.Tests.ps1",
    "content": "using module ../SoftwareReport.Nodes.psm1\nusing module ../SoftwareReport.DifferenceRender.psm1\n\nBeforeDiscovery {\n    Import-Module $(Join-Path $PSScriptRoot \"TestHelpers.psm1\") -DisableNameChecking\n}\n\nDescribe \"ComparerReport.UnitTests\" {\n    BeforeAll {\n        $script:DifferenceRender = [SoftwareReportDifferenceRender]::new()\n    }\n\n    Context \"CalculateHtmlTableRowSpan\" {\n        It \"Without the equal cells\" {\n            $table = @(\n                [PSCustomObject]@{ Key = \"A\"; Value = \"1\" }\n                [PSCustomObject]@{ Key = \"B\"; Value = \"2\" }\n                [PSCustomObject]@{ Key = \"C\"; Value = \"3\" }\n            )\n            \n            $actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, \"Key\")\n            $actual | Should -BeArray @(1, 1, 1)\n        }\n\n        It \"Only equal cells\" {\n            $table = @(\n                [PSCustomObject]@{ Key = \"A\"; Value = \"D\" }\n                [PSCustomObject]@{ Key = \"B\"; Value = \"D\" }\n                [PSCustomObject]@{ Key = \"C\"; Value = \"D\" }\n            )\n            \n            $actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, \"Value\")\n            $actual | Should -BeArray @(3, 0, 0)\n        }\n\n        It \"Single row\" {\n            $table = @(\n                [PSCustomObject]@{ Key = \"A\"; Value = \"1\" }\n            )\n            \n            $actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, \"Key\")\n            $actual | Should -BeArray @(1)\n        }\n\n        It \"Different cells\" {\n            $table = @(\n                [PSCustomObject]@{ Key = \"A\"; Value = \"1\" }\n                [PSCustomObject]@{ Key = \"B\"; Value = \"2\" }\n                [PSCustomObject]@{ Key = \"B\"; Value = \"3\" }\n                [PSCustomObject]@{ Key = \"C\"; Value = \"4\" }\n                [PSCustomObject]@{ Key = \"C\"; Value = \"5\" }\n                [PSCustomObject]@{ Key = \"C\"; Value = \"6\" }\n                [PSCustomObject]@{ Key = \"D\"; Value = \"7\" }\n                [PSCustomObject]@{ Key = \"E\"; Value = \"8\" }\n                [PSCustomObject]@{ Key = \"E\"; Value = \"9\" }\n                [PSCustomObject]@{ Key = \"F\"; Value = \"10\" }\n            )\n            \n            $actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, \"Key\")\n            $actual | Should -BeArray @(1, 2, 0, 3, 0, 0, 1, 2, 0, 1)\n        }\n    }\n\n    Context \"RenderCategory\" {\n        It \"With line separator\" {\n            $actual = $DifferenceRender.RenderCategory(@(\"Header 1\", \"Header 2\", \"Header 3\"), $true)\n            $actual | Should -Be \"Header 2 ><br> Header 3\"\n        }\n\n        It \"Without line separator\" {\n            $actual = $DifferenceRender.RenderCategory(@(\"Header 1\", \"Header 2\", \"Header 3\"), $false)\n            $actual | Should -Be \"Header 2 > Header 3\"\n        }\n\n        It \"One header\" {\n            $actual = $DifferenceRender.RenderCategory(@(\"Header 1\"), $false)\n            $actual | Should -Be \"\"\n        }\n\n        It \"Empty headers\" {\n            $actual = $DifferenceRender.RenderCategory(@(), $false)\n            $actual | Should -Be \"\"\n        }\n    }\n\n    Context \"RenderToolName\" {\n        It \"Clear tool name\" {\n            $actual = $DifferenceRender.RenderToolName(\"My Tool 1\")\n            $actual | Should -Be \"My Tool 1\"\n        }\n\n        It \"Name with colon symbol\" {\n            $actual = $DifferenceRender.RenderToolName(\"My Tool 1:\")\n            $actual | Should -Be \"My Tool 1\"\n        }\n    }\n\n    Context \"StrikeTableRow\" {\n        It \"Simple row\" {\n            $actual = $DifferenceRender.StrikeTableRow(\"Test1|Test2|Test3\")\n            $actual | Should -Be \"~~Test1~~|~~Test2~~|~~Test3~~\"\n        }\n\n        It \"Row with spaces\" {\n            $actual = $DifferenceRender.StrikeTableRow(\"Test 1|Test 2|Test 3\")\n            $actual | Should -Be \"~~Test 1~~|~~Test 2~~|~~Test 3~~\"\n        }\n    }\n\n    Context \"RenderHtmlTable\" {\n        It \"Simple table\" {\n            $table = @(\n                [PSCustomObject]@{ \"Category\" = \"A\"; \"Tool name\" = \"My Tool 1\"; \"Version\" = \"1.0\" },\n                [PSCustomObject]@{ \"Category\" = \"B\"; \"Tool name\" = \"My Tool 2\"; \"Version\" = \"2.0\" },\n                [PSCustomObject]@{ \"Category\" = \"C\"; \"Tool name\" = \"My Tool 3\"; \"Version\" = \"3.0\" }\n            )\n\n            $renderedTable = $DifferenceRender.RenderHtmlTable($table, \"Category\")\n            $renderedTable | Should -Be @'\n<table>\n    <thead>\n        <th>Category</th>\n        <th>Tool name</th>\n        <th>Version</th>\n    </thead>\n    <tbody>\n        <tr>\n            <td rowspan=\"1\">A</td>\n            <td>My Tool 1</td>\n            <td>1.0</td>\n        </tr>\n        <tr>\n            <td rowspan=\"1\">B</td>\n            <td>My Tool 2</td>\n            <td>2.0</td>\n        </tr>\n        <tr>\n            <td rowspan=\"1\">C</td>\n            <td>My Tool 3</td>\n            <td>3.0</td>\n        </tr>\n    </tbody>\n</table>\n\n'@\n\n        }\n\n        It \"Table with the same category\" {\n            $table = @(\n                [PSCustomObject]@{ \"Category\" = \"A\"; \"Tool name\" = \"My Tool 1\"; \"Version\" = \"1.0\" },\n                [PSCustomObject]@{ \"Category\" = \"A\"; \"Tool name\" = \"My Tool 2\"; \"Version\" = \"2.0\" },\n                [PSCustomObject]@{ \"Category\" = \"A\"; \"Tool name\" = \"My Tool 3\"; \"Version\" = \"3.0\" },\n                [PSCustomObject]@{ \"Category\" = \"B\"; \"Tool name\" = \"My Tool 4\"; \"Version\" = \"4.0\" }\n            )\n\n            $renderedTable = $DifferenceRender.RenderHtmlTable($table, \"Category\")\n            $renderedTable | Should -Be @'\n<table>\n    <thead>\n        <th>Category</th>\n        <th>Tool name</th>\n        <th>Version</th>\n    </thead>\n    <tbody>\n        <tr>\n            <td rowspan=\"3\">A</td>\n            <td>My Tool 1</td>\n            <td>1.0</td>\n        </tr>\n        <tr>\n            <td>My Tool 2</td>\n            <td>2.0</td>\n        </tr>\n        <tr>\n            <td>My Tool 3</td>\n            <td>3.0</td>\n        </tr>\n        <tr>\n            <td rowspan=\"1\">B</td>\n            <td>My Tool 4</td>\n            <td>4.0</td>\n        </tr>\n    </tbody>\n</table>\n\n'@\n\n        }\n    }\n\n    Context \"RenderTableNodesDiff\" {\n        It \"Add new table\" {\n            $previousNode = $null\n            $currentNode = [TableNode]::new(\"Name|Value\", @(\"A|1\", \"B|2\"))\n            $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @(\"Header 1\", \"Header 2\", \"Header 3\"))\n\n            $actual = $DifferenceRender.RenderTableNodesDiff($reportItem)\n            $actual | Should -Be @'\n#### Header 2 > Header 3\n| Name | Value |\n| ---- | ----- |\n| A    | 1     |\n| B    | 2     |\n\n'@\n        }\n\n        It \"Remove existing table\" {\n            $previousNode = [TableNode]::new(\"Name|Value\", @(\"A|1\", \"B|2\"))\n            $currentNode = $null\n            $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @(\"Header 1\", \"Header 2\", \"Header 3\"))\n\n            $actual = $DifferenceRender.RenderTableNodesDiff($reportItem)\n            $actual | Should -Be @'\n#### Header 2 > Header 3\n| Name  | Value |\n| ----- | ----- |\n| ~~A~~ | ~~1~~ |\n| ~~B~~ | ~~2~~ |\n\n'@\n        }\n\n        It \"Add new rows to existing table\" {\n            $previousNode = [TableNode]::new(\"Name|Value\", @(\"A|1\", \"B|2\"))\n            $currentNode = [TableNode]::new(\"Name|Value\", @(\"A|1\", \"B|2\", \"C|3\", \"D|4\"))\n            $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @(\"Header 1\", \"Header 2\", \"Header 3\"))\n\n            $actual = $DifferenceRender.RenderTableNodesDiff($reportItem)\n            $actual | Should -Be @'\n#### Header 2 > Header 3\n| Name | Value |\n| ---- | ----- |\n| C    | 3     |\n| D    | 4     |\n\n'@\n        }\n\n        It \"Remove rows from existing table\" {\n            $previousNode = [TableNode]::new(\"Name|Value\", @(\"A|1\", \"B|2\", \"C|3\", \"D|4\"))\n            $currentNode = [TableNode]::new(\"Name|Value\", @(\"C|3\", \"D|4\"))\n            $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @(\"Header 1\", \"Header 2\", \"Header 3\"))\n\n            $actual = $DifferenceRender.RenderTableNodesDiff($reportItem)\n            $actual | Should -Be @'\n#### Header 2 > Header 3\n| Name  | Value |\n| ----- | ----- |\n| ~~A~~ | ~~1~~ |\n| ~~B~~ | ~~2~~ |\n\n'@\n        }\n\n        It \"Row is changed in existing table\" {\n            $previousNode = [TableNode]::new(\"Name|Value\", @(\"A|1\", \"B|2\"))\n            $currentNode = [TableNode]::new(\"Name|Value\", @(\"A|1\", \"B|3\"))\n            $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @(\"Header 1\", \"Header 2\", \"Header 3\"))\n\n            $actual = $DifferenceRender.RenderTableNodesDiff($reportItem)\n            $actual | Should -Be @'\n#### Header 2 > Header 3\n| Name  | Value |\n| ----- | ----- |\n| ~~B~~ | ~~2~~ |\n| B     | 3     |\n\n'@\n        }\n\n        It \"Row is changed, added and removed at the same time in existing table\" {\n            $previousNode = [TableNode]::new(\"Name|Value\", @(\"A|1\", \"B|2\", \"C|3\", \"D|4\"))\n            $currentNode = [TableNode]::new(\"Name|Value\", @(\"B|2\", \"C|4\", \"D|4\", \"E|5\"))\n            $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @(\"Header 1\", \"Header 2\", \"Header 3\"))\n\n            $actual = $DifferenceRender.RenderTableNodesDiff($reportItem)\n            $actual | Should -Be @'\n#### Header 2 > Header 3\n| Name  | Value |\n| ----- | ----- |\n| ~~A~~ | ~~1~~ |\n| ~~C~~ | ~~3~~ |\n| C     | 4     |\n| E     | 5     |\n\n'@\n        }\n    }\n}"
  },
  {
    "path": "helpers/software-report-base/tests/SoftwareReport.E2E.Tests.ps1",
    "content": "using module ../SoftwareReport.psm1\nusing module ../SoftwareReport.Nodes.psm1\n\nDescribe \"SoftwareReport.E2E\" {\n    Context \"Report example 1\" {\n        BeforeEach {\n            $softwareReport = [SoftwareReport]::new(\"macOS 11\")\n            $softwareReport.Root.AddToolVersion(\"OS Version:\", \"macOS 11.7 (20G817)\")\n            $softwareReport.Root.AddToolVersion(\"Image Version:\", \"20220918.1\")\n            $installedSoftware = $softwareReport.Root.AddHeader(\"Installed Software\")\n\n            $languagesAndRuntimes = $installedSoftware.AddHeader(\"Language and Runtime\")\n            $languagesAndRuntimes.AddToolVersion(\"Bash\", \"5.1.16(1)-release\")\n            $languagesAndRuntimes.AddToolVersionsListInline(\".NET Core SDK\", @(\"1.2.100\", \"1.2.200\", \"3.1.414\"), \"^\\d+\\.\\d+\\.\\d\")\n            $languagesAndRuntimes.AddNode([ToolVersionNode]::new(\"Perl\", \"5.34.0\"))\n\n            $cachedTools = $installedSoftware.AddHeader(\"Cached Tools\")\n            $cachedTools.AddToolVersionsList(\"Ruby\", @(\"2.7.3\", \"2.8.1\", \"3.1.2\"), \"^\\d+\\.\\d+\")\n            $cachedTools.AddToolVersionsList(\"Node.js\", @(\"14.8.0\", \"15.1.0\", \"16.4.2\"), \"^\\d+\")\n\n            $javaSection = $installedSoftware.AddHeader(\"Java\")\n            $javaSection.AddTable(@(\n                [PSCustomObject] @{ Version = \"8.0.125\"; Vendor = \"My Vendor\"; \"Environment Variable\" = \"JAVA_HOME_8_X64\" },\n                [PSCustomObject] @{ Version = \"11.3.103\"; Vendor = \"My Vendor\"; \"Environment Variable\" = \"JAVA_HOME_11_X64\" }\n            ))\n\n            $sqlSection = $installedSoftware.AddHeader(\"MySQL\")\n            $sqlSection.AddToolVersion(\"MySQL\", \"6.1.0\")\n            $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'\")\n\n            $expectedMarkdown = @'\n# macOS 11\n- OS Version: macOS 11.7 (20G817)\n- Image Version: 20220918.1\n\n## Installed Software\n\n### Language and Runtime\n- Bash 5.1.16(1)-release\n- .NET Core SDK: 1.2.100, 1.2.200, 3.1.414\n- Perl 5.34.0\n\n### Cached Tools\n\n#### Ruby\n- 2.7.3\n- 2.8.1\n- 3.1.2\n\n#### Node.js\n- 14.8.0\n- 15.1.0\n- 16.4.2\n\n### Java\n| Version  | Vendor    | Environment Variable |\n| -------- | --------- | -------------------- |\n| 8.0.125  | My Vendor | JAVA_HOME_8_X64      |\n| 11.3.103 | My Vendor | JAVA_HOME_11_X64     |\n\n### MySQL\n- MySQL 6.1.0\n```\nMySQL service is disabled by default.\nUse the following command as a part of your job to start the service: 'sudo systemctl start mysql.service'\n```\n'@\n        }\n\n        It \"ToMarkdown\" {\n            $softwareReport.ToMarkdown() | Should -Be $expectedMarkdown\n        }\n\n        It \"Serialization + Deserialization\" {\n            $json = $softwareReport.ToJson()\n            $deserializedReport = [SoftwareReport]::FromJson($json)\n            $deserializedReport.ToMarkdown() | Should -Be $expectedMarkdown\n        }\n    }\n\n    Context \"GetImageVersion\" {\n        It \"Image version exists\" {\n            $softwareReport = [SoftwareReport]::new(\"MyReport\")\n            $softwareReport.Root.AddToolVersion(\"Image Version:\", \"123.4\")\n            $softwareReport.GetImageVersion() | Should -Be \"123.4\"\n        }\n\n        It \"Empty report\" {\n            $softwareReport = [SoftwareReport]::new(\"MyReport\")\n            $softwareReport.GetImageVersion() | Should -Be \"Unknown version\"\n        }\n    }\n}"
  },
  {
    "path": "helpers/software-report-base/tests/SoftwareReport.Nodes.Unit.Tests.ps1",
    "content": "using module ../SoftwareReport.Nodes.psm1\n\nBeforeDiscovery {\n    Import-Module $(Join-Path $PSScriptRoot \"TestHelpers.psm1\") -DisableNameChecking\n}\n\nDescribe \"Nodes.UnitTests\" {\n    Context \"ToolVersionNode\" {\n        It \"ToMarkdown\" {\n            $node = [ToolVersionNode]::new(\"MyTool\", \"2.1.3\")\n            $node.ToMarkdown() | Should -Be \"- MyTool 2.1.3\"\n        }\n\n        It \"GetValue\" {\n            $node = [ToolVersionNode]::new(\"MyTool\", \"2.1.3\")\n            $node.GetValue() | Should -Be \"2.1.3\"\n        }\n\n        It \"Serialization\" {\n            $node = [ToolVersionNode]::new(\"MyTool\", \"2.1.3\")\n            $json = $node.ToJsonObject()\n            $json.NodeType | Should -Be \"ToolVersionNode\"\n            $json.ToolName | Should -Be \"MyTool\"\n            $json.Version | Should -Be \"2.1.3\"\n        }\n\n        It \"Deserialization\" {\n            { [ToolVersionNode]::FromJsonObject(@{ NodeType = \"ToolVersionNode\"; ToolName = \"\"; Version = \"2.1.3\" }) } | Should -Throw '*Exception setting \"ToolName\": \"The argument is null or empty.*'\n            { [ToolVersionNode]::FromJsonObject(@{ NodeType = \"ToolVersionNode\"; ToolName = \"MyTool\"; Version = \"\" }) } | Should -Throw 'ToolVersionNode ''MyTool'' has empty version'\n            { [ToolVersionNode]::FromJsonObject(@{ NodeType = \"ToolVersionNode\"; ToolName = \"MyTool\"; Version = \"2.1.3\" }) } | Should -Not -Throw\n        }\n\n        It \"Serialization + Deserialization\" {\n            $node = [ToolVersionNode]::new(\"MyTool\", \"2.1.3\")\n            $json = $node.ToJsonObject()\n            $node2 = [ToolVersionNode]::FromJsonObject($json)\n            $json2 = $node2.ToJsonObject()\n            $($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json)\n        }\n\n        It \"IsSimilarTo\" {\n            [ToolVersionNode]::new(\"MyTool\", \"2.1.3\").IsSimilarTo([ToolVersionNode]::new(\"MyTool\", \"2.1.3\")) | Should -BeTrue\n            [ToolVersionNode]::new(\"MyTool\", \"2.1.3\").IsSimilarTo([ToolVersionNode]::new(\"MyTool\", \"1.0.0\")) | Should -BeTrue\n            [ToolVersionNode]::new(\"MyTool\", \"2.1.3\").IsSimilarTo([ToolVersionNode]::new(\"MyTool2\", \"2.1.3\")) | Should -BeFalse\n        }\n\n        It \"IsIdenticalTo\" {\n            [ToolVersionNode]::new(\"MyTool\", \"2.1.3\").IsIdenticalTo([ToolVersionNode]::new(\"MyTool\", \"2.1.3\")) | Should -BeTrue\n            [ToolVersionNode]::new(\"MyTool\", \"2.1.3\").IsIdenticalTo([ToolVersionNode]::new(\"MyTool\", \"1.0.0\")) | Should -BeFalse\n            [ToolVersionNode]::new(\"MyTool\", \"2.1.3\").IsIdenticalTo([ToolVersionNode]::new(\"MyTool2\", \"2.1.3\")) | Should -BeFalse\n        }\n    }\n\n    Context \"ToolVersionsListNode\" {\n        It \"ToMarkdown - List\" {\n            $node = [ToolVersionsListNode]::new(\"MyTool\", @(\"2.7.7\", \"3.0.5\", \"3.1.3\"), \"^.+\", \"List\")\n            $expected = @(\n                \"\",\n                \"# MyTool\"\n                \"- 2.7.7\"\n                \"- 3.0.5\"\n                \"- 3.1.3\"\n            ) -join \"`n\"\n            $node.ToMarkdown() | Should -Be $expected\n        }\n\n        It \"ToMarkdown - Inline\" {\n            $node = [ToolVersionsListNode]::new(\"MyTool\", @(\"2.7.7\", \"3.0.5\", \"3.1.3\"), \"^.+\", \"Inline\")\n            $node.ToMarkdown() | Should -Be \"- MyTool: 2.7.7, 3.0.5, 3.1.3\"\n        }\n\n        It \"GetValue\" {\n            $node = [ToolVersionsListNode]::new(\"MyTool\", @(\"2.7.7\", \"3.0.5\", \"3.1.3\"), \"^.+\", \"List\")\n            $node.GetValue() | Should -Be \"2.7.7, 3.0.5, 3.1.3\"\n        }\n\n        It \"Serialization - List\" {\n            $node = [ToolVersionsListNode]::new(\"Ruby\", @(\"2.7.7\", \"3.0.5\", \"3.1.3\"), \"^.+\", \"List\")\n            $json = $node.ToJsonObject()\n            $json.NodeType | Should -Be \"ToolVersionsListNode\"\n            $json.ToolName | Should -Be \"Ruby\"\n            $json.Versions | Should -BeArray @(\"2.7.7\", \"3.0.5\", \"3.1.3\")\n            $json.MajorVersionRegex | Should -Be \"^.+\"\n            $json.ListType | Should -Be \"List\"\n        }\n\n        It \"Serialization - Inline\" {\n            $node = [ToolVersionsListNode]::new(\"Ruby\", @(\"2.7.7\", \"3.0.5\", \"3.1.3\"), \"^.+\", \"Inline\")\n            $json = $node.ToJsonObject()\n            $json.NodeType | Should -Be \"ToolVersionsListNode\"\n            $json.ToolName | Should -Be \"Ruby\"\n            $json.Versions | Should -BeArray @(\"2.7.7\", \"3.0.5\", \"3.1.3\")\n            $json.MajorVersionRegex | Should -Be \"^.+\"\n            $json.ListType | Should -Be \"Inline\"\n        }\n\n        It \"Deserialization\" {\n            { [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.*'\n            { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = \"ToolVersionsListNode\"; ToolName = \"MyTool\"; MajorVersionRegex = \"^\\d+\"; ListType = \"List\" }) } | Should -Throw '*Exception setting \"Versions\": \"The argument is null or empty.*'\n            { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = \"ToolVersionsListNode\"; ToolName = \"MyTool\"; Versions = @(); MajorVersionRegex = \"^\\d+\"; ListType = \"List\" }) } | Should -Throw '*Exception setting \"Versions\": \"The argument is null, empty,*'\n            { [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 *'\n            { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = \"ToolVersionsListNode\"; ToolName = \"MyTool\"; Versions = @(\"2.1.3\", \"3.1.4\"); MajorVersionRegex = \"\"; ListType = \"List\" }) } | Should -Throw 'Version * doesn''t match regex *'\n            { [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*'\n            { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = \"ToolVersionsListNode\"; ToolName = \"MyTool\"; Versions = @(\"2.1.3\", \"3.1.4\"); MajorVersionRegex = \"^\\d+\"; ListType = \"List\" }) } | Should -Not -Throw\n            { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = \"ToolVersionsListNode\"; ToolName = \"MyTool\"; Versions = @(\"2.1.3\", \"3.1.4\"); MajorVersionRegex = \"^\\d+\"; ListType = \"Inline\" }) } | Should -Not -Throw\n        }\n\n        It \"Serialization + Deserialization\" {\n            $node = [ToolVersionsListNode]::new(\"Ruby\", @(\"2.7.7\", \"3.0.5\", \"3.1.3\"), \"^.+\", \"List\")\n            $json = $node.ToJsonObject()\n            $node2 = [ToolVersionsListNode]::FromJsonObject($json)\n            $json2 = $node2.ToJsonObject()\n            $($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json)\n        }\n\n        It \"IsSimilarTo\" {\n            [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^.+\", \"List\").IsSimilarTo(\n                [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^.+\", \"List\")\n            ) | Should -BeTrue\n            [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^.+\", \"List\").IsSimilarTo(\n                [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.5\", \"5.0.0\"), \"^.+\", \"List\")\n            ) | Should -BeTrue\n            [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^.+\", \"List\").IsSimilarTo(\n                [ToolVersionsListNode]::new(\"MyTool2\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^.+\", \"List\")\n            ) | Should -BeFalse\n        }\n\n        It \"IsIdenticalTo\" {\n            [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^.+\", \"List\").IsIdenticalTo(\n                [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^.+\", \"List\")\n            ) | Should -BeTrue\n            [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^.+\", \"List\").IsIdenticalTo(\n                [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.5\", \"5.0.0\"), \"^.+\", \"List\")\n            ) | Should -BeFalse\n            [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^.+\", \"List\").IsIdenticalTo(\n                [ToolVersionsListNode]::new(\"MyTool2\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^.+\", \"List\")\n            ) | Should -BeFalse\n        }\n\n        It \"ExtractMajorVersion\" {\n            $node = [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^\\d+\\.\\d+\", \"List\")\n            $node.ExtractMajorVersion(\"2.1.3\") | Should -Be \"2.1\"\n            $node.ExtractMajorVersion(\"3.1.5\") | Should -Be \"3.1\"\n            $node.ExtractMajorVersion(\"4.0.0\") | Should -Be \"4.0\"\n        }\n\n        Context \"ValidateMajorVersionRegex\" {\n            It \"Major version regex - unique versions\" {\n                $node = [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"3.1.5\", \"4.0.0\"), \"^\\d+\", \"List\")\n                $node.Versions | Should -BeArray @(\"2.1.3\", \"3.1.5\", \"4.0.0\")\n            }\n\n            It \"Major version regex - non-unique versions\" {\n                { [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 *\"\n            }\n\n            It \"Minor version regex - unique versions\" {\n                $node = [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"2.4.0\", \"3.1.2\"), \"^\\d+\\.\\d+\", \"List\")\n                $node.Versions | Should -BeArray @(\"2.1.3\", \"2.4.0\", \"3.1.2\")\n            }\n\n            It \"Minor version regex - non-unique versions\" {\n                { [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 *\"\n            }\n\n            It \"Patch version regex - unique versions\" {\n                $node = [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.3\", \"2.1.4\", \"2.1.5\"), \"^\\d+\\.\\d+\\.\\d+\", \"List\")\n                $node.Versions | Should -BeArray @(\"2.1.3\", \"2.1.4\", \"2.1.5\")\n            }\n\n            It \"Patch version regex - non-unique versions\" {\n                { [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 *\"\n            }\n\n            It \".NET Core version regex - unique versions\" {\n                $node = [ToolVersionsListNode]::new(\"MyTool\", @(\"2.1.100\", \"2.1.205\", \"2.1.303\"), \"^\\d+\\.\\d+\\.\\d\", \"List\")\n                $node.Versions | Should -BeArray @(\"2.1.100\", \"2.1.205\", \"2.1.303\")\n            }\n\n            It \".NET Core version regex - non-unique versions\" {\n                { [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 *\"\n            }\n        }\n    }\n\n    Context \"TableNode\" {\n        It \"ToMarkdown (Simple table)\" {\n            $node = [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\"))\n            $node.ToMarkdown() | Should -Be @'\n| Name | Value |\n| ---- | ----- |\n| A    | B     |\n| C    | D     |\n'@\n        }\n\n        It \"ToMarkdown (Wide cells)\" {\n            $node = [TableNode]::new(\"Name|Value\", @(\"Very long value here|B\", \"C|And very long value here too\"))\n            $node.ToMarkdown() | Should -Be @'\n| Name                 | Value                        |\n| -------------------- | ---------------------------- |\n| Very long value here | B                            |\n| C                    | And very long value here too |\n'@\n        }\n\n        It \"CalculateColumnsWidth\" {\n            [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\")).CalculateColumnsWidth() | Should -BeArray @(4, 5)\n            [TableNode]::new(\"Name|Value\", @(\"Very long value here|B\", \"C|And very long value here too\")).CalculateColumnsWidth() | Should -BeArray @(20, 28)\n        }\n\n        It \"Serialization\" {\n            $node = [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\"))\n            $json = $node.ToJsonObject()\n            $json.NodeType | Should -Be \"TableNode\"\n            $json.Headers | Should -Be \"Name|Value\"\n            $json.Rows | Should -BeArray @(\"A|B\", \"C|D\")\n        }\n\n        It \"Deserialization\" {\n            { [TableNode]::FromJsonObject(@{ NodeType = \"TableNode\"; Headers = \"\"; Rows = @(\"A|1\", \"B|2\") }) } | Should -Throw 'Exception setting \"Headers\": \"The argument is null or empty. *'\n            { [TableNode]::FromJsonObject(@{ NodeType = \"TableNode\"; Headers = \"Name|Value\"; Rows = @() }) } | Should -Throw 'Exception setting \"Rows\": \"The argument is null, empty, *'\n            { [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'\n            { [TableNode]::FromJsonObject(@{ NodeType = \"TableNode\"; Headers = \"Name|Value\"; Rows = @(\"A|1\", \"B|2\") }) } | Should -Not -Throw\n        }\n\n        It \"Serialization + Deserialization\" {\n            $node = [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\"))\n            $json = $node.ToJsonObject()\n            $node2 = [TableNode]::FromJsonObject($json)\n            $json2 = $node2.ToJsonObject()\n            $($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json)\n        }\n\n        It \"IsSimilarTo\" {\n            [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\")).IsSimilarTo([TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\"))) | Should -BeTrue\n            [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\")).IsSimilarTo([TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\", \"F|W\"))) | Should -BeTrue\n            [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\")).IsSimilarTo([TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|E\"))) | Should -BeTrue\n            [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\")).IsSimilarTo([TableNode]::new(\"Name|Key\", @(\"A|B\", \"C|D\"))) | Should -BeTrue\n        }\n\n        It \"IsIdenticalTo\" {\n            [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\")).IsIdenticalTo([TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\"))) | Should -BeTrue\n            [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\")).IsIdenticalTo([TableNode]::new(\"Name|Key\", @(\"A|B\", \"C|D\"))) | Should -BeTrue\n            [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\")).IsIdenticalTo([TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\", \"F|W\"))) | Should -BeFalse\n            [TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|D\")).IsIdenticalTo([TableNode]::new(\"Name|Value\", @(\"A|B\", \"C|E\"))) | Should -BeFalse\n        }\n\n        Context \"FromObjectsArray\" {\n            It \"Correct table\" {\n                $table = @(\n                    [PSCustomObject]@{Name = \"A\"; Value = \"B\"}\n                    [PSCustomObject]@{Name = \"C\"; Value = \"D\"}\n                )\n\n                $tableNode = [TableNode]::FromObjectsArray($table)\n                $tableNode.Headers | Should -Be \"Name|Value\"\n                $tableNode.Rows | Should -BeArray @(\"A|B\", \"C|D\")\n            }\n\n            It \"Correct table with spaces\" {\n                $table = @(\n                    [PSCustomObject]@{Name = \"A B\"; \"My Value\" = \"1 2\"}\n                    [PSCustomObject]@{Name = \"C D\"; \"My Value\" = \"3 4\"}\n                )\n\n                $tableNode = [TableNode]::FromObjectsArray($table)\n                $tableNode.Headers | Should -Be \"Name|My Value\"\n                $tableNode.Rows | Should -BeArray @(\"A B|1 2\", \"C D|3 4\")\n            }\n\n            It \"Throw on empty table\" {\n                { [TableNode]::FromObjectsArray(@()) } | Should -Throw \"Failed to create TableNode from empty objects array\"\n            }\n\n            It \"Throw on table with different columns\" {\n                $table = @(\n                    [PSCustomObject]@{Name = \"A\"; Value = \"B\"}\n                    [PSCustomObject]@{Name = \"C\"; Value2 = \"D\"}\n                )\n\n                { [TableNode]::FromObjectsArray($table) } | Should -Throw \"Failed to create TableNode from objects array because objects have different properties\"\n            }\n\n            It \"Throw on empty row\" {\n                $table = @(\n                    [PSCustomObject]@{Name = \"A\"; Value = \"B\"},\n                    [PSCustomObject]@{},\n                    [PSCustomObject]@{Name = \"C\"; Value2 = \"D\"}\n                )\n\n                { [TableNode]::FromObjectsArray($table) } | Should -Throw \"Failed to create TableNode because some objects are empty\"\n            }\n\n            It \"Throw on incorrect symbols in table column names\" {\n                $table = @(\n                    [PSCustomObject]@{\"Name|War\" = \"A\"; Value = \"B\"}\n                    [PSCustomObject]@{\"Name|War\" = \"C\"; Value = \"D\"}\n                )\n\n                { [TableNode]::FromObjectsArray($table) } | Should -Throw \"Failed to create TableNode because some cells * contains forbidden symbol*\"\n            }\n\n            It \"Throw on incorrect symbols in table rows\" {\n                $table = @(\n                    [PSCustomObject]@{Name = \"A\"; Value = \"B|AA\"}\n                    [PSCustomObject]@{Name = \"C\"; Value = \"D\"}\n                )\n\n                { [TableNode]::FromObjectsArray($table) } | Should -Throw \"Failed to create TableNode because some cells * contains forbidden symbol*\"\n            }\n        }\n    }\n\n    Context \"NoteNode\" {\n        It \"ToMarkdown\" {\n            $node = [NoteNode]::new(\"Hello world`nGood Bye world\")\n            $node.ToMarkdown() | Should -Be @'\n```\nhello world\nGood Bye world\n```\n'@\n        }\n\n        It \"Serialization\" {\n            $node = [NoteNode]::new(\"MyContent`nMyContent2\")\n            $json = $node.ToJsonObject()\n            $json.NodeType | Should -Be \"NoteNode\"\n            $json.Content | Should -Be \"MyContent`nMyContent2\"\n        }\n\n        It \"Deserialization\" {\n            { [NoteNode]::FromJsonObject(@{ NodeType = \"NoteNode\" }) } | Should -Throw '*Exception setting \"Content\": \"The argument is null or empty.*'\n            { [NoteNode]::FromJsonObject(@{ NodeType = \"NoteNode\"; Content = \"\" }) } | Should -Throw '*Exception setting \"Content\": \"The argument is null or empty.*'\n            { [NoteNode]::FromJsonObject(@{ NodeType = \"NoteNode\"; Content = \"MyTool\" }) } | Should -Not -Throw\n        }\n\n        It \"Serialization + Deserialization\" {\n            $node = [NoteNode]::new(\"MyContent`nMyContent2\")\n            $json = $node.ToJsonObject()\n            $node2 = [NoteNode]::FromJsonObject($json)\n            $json2 = $node2.ToJsonObject()\n            $($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json)\n        }\n\n        It \"IsSimilarTo\" {\n            [NoteNode]::new(\"MyContent\").IsSimilarTo([NoteNode]::new(\"MyContent\")) | Should -BeTrue\n            [NoteNode]::new(\"MyContent\").IsSimilarTo([NoteNode]::new(\"MyContent2\")) | Should -BeFalse\n        }\n\n        It \"IsIdenticalTo\" {\n            [NoteNode]::new(\"MyContent\").IsIdenticalTo([NoteNode]::new(\"MyContent\")) | Should -BeTrue\n            [NoteNode]::new(\"MyContent\").IsIdenticalTo([NoteNode]::new(\"MyContent2\")) | Should -BeFalse\n        }\n    }\n\n    Context \"HeaderNode\" {\n        It \"ToMarkdown\" {\n            $node = [HeaderNode]::new(\"MyHeader\")\n            $node.AddToolVersion(\"MyTool\", \"2.1.3\")\n            $node.ToMarkdown(1) | Should -Be @'\n\n# MyHeader\n- MyTool 2.1.3\n'@\n        }\n\n        It \"ToMarkdown (level 3)\" {\n            $node = [HeaderNode]::new(\"MyHeader\")\n            $node.AddToolVersion(\"MyTool\", \"2.1.3\")\n            $node.ToMarkdown(3) | Should -Be @'\n\n### MyHeader\n- MyTool 2.1.3\n'@\n        }\n\n        It \"ToMarkdown (multiple levels)\" {\n            $node = [HeaderNode]::new(\"MyHeader\")\n            $node.AddHeader(\"MyHeader 2\").AddHeader(\"MyHeader 3\").AddHeader(\"MyHeader 4\").AddToolVersion(\"MyTool\", \"2.1.3\")\n            $node.ToMarkdown(1) | Should -Be @'\n\n# MyHeader\n\n## MyHeader 2\n\n### MyHeader 3\n\n#### MyHeader 4\n- MyTool 2.1.3\n'@\n        }\n\n        It \"Serialization\" {\n            $node = [HeaderNode]::new(\"MyHeader\")\n            $node.AddToolVersion(\"MyTool\", \"2.1.3\")\n            $json = $node.ToJsonObject()\n            $json.NodeType | Should -Be \"HeaderNode\"\n            $json.Title | Should -Be \"MyHeader\"\n            $json.Children | Should -HaveCount 1\n        }\n\n        It \"Deserialization\" {\n            { [HeaderNode]::FromJsonObject(@{ NodeType = \"HeaderNode\" }) } | Should -Throw '*Exception setting \"Title\": \"The argument is null or empty.*'\n            { [HeaderNode]::FromJsonObject(@{ NodeType = \"HeaderNode\"; Title = \"\" }) } | Should -Throw '*Exception setting \"Title\": \"The argument is null or empty.*'\n            { [HeaderNode]::FromJsonObject(@{ NodeType = \"HeaderNode\"; Title = \"MyHeader\" }) } | Should -Not -Throw\n        }\n\n        It \"Serialization + Deserialization\" {\n            $node = [HeaderNode]::new(\"MyHeader\")\n            $node.AddToolVersion(\"MyTool\", \"2.1.3\")\n            $json = $node.ToJsonObject()\n            $node2 = [HeaderNode]::FromJsonObject($json)\n            $json2 = $node2.ToJsonObject()\n            $($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json)\n        }\n\n        It \"IsSimilarTo\" {\n            [HeaderNode]::new(\"MyHeader\").IsSimilarTo([HeaderNode]::new(\"MyHeader\")) | Should -BeTrue\n            [HeaderNode]::new(\"MyHeader\").IsSimilarTo([HeaderNode]::new(\"MyHeader2\")) | Should -BeFalse\n        }\n\n        It \"IsIdenticalTo\" {\n            [HeaderNode]::new(\"MyHeader\").IsIdenticalTo([HeaderNode]::new(\"MyHeader\")) | Should -BeTrue\n            [HeaderNode]::new(\"MyHeader\").IsIdenticalTo([HeaderNode]::new(\"MyHeader2\")) | Should -BeFalse\n        }\n\n        It \"FindSimilarChildNode\" {\n            $node = [HeaderNode]::new(\"MyHeader\")\n            $node.AddToolVersion(\"MyTool\", \"2.1.3\")\n\n            $node.FindSimilarChildNode([ToolVersionNode]::new(\"MyTool\", \"1.0.0\")) | Should -Not -BeNullOrEmpty\n            $node.FindSimilarChildNode([ToolVersionNode]::New(\"MyTool2\", \"1.0.0\")) | Should -BeNullOrEmpty\n        }\n\n        Context \"Detect node duplicates\" {\n            It \"Similar HeaderNode on the same header\" {\n                $node = [HeaderNode]::new(\"MyHeader\")\n                $node.AddHeader(\"MySubHeader1\")\n                $node.AddHeader(\"MySubHeader2\")\n                { $node.AddHeader(\"MySubHeader1\") } | Should -Throw \"This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*\"\n            }\n\n            It \"Similar ToolVersionNode on the same header\" {\n                $node = [HeaderNode]::new(\"MyHeader\")\n                $node.AddToolVersion(\"MyTool\", \"2.1.3\")\n                $node.AddToolVersion(\"MyTool2\", \"2.1.3\")\n                { $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.*\"\n            }\n\n            It \"Similar ToolVersionsListNode on the same header\" {\n                $node = [HeaderNode]::new(\"MyHeader\")\n                $node.AddToolVersionsListInline(\"MyTool\", @(\"2.1.3\", \"3.0.0\"), \"^\\d+\")\n                $node.AddToolVersionsListInline(\"MyTool2\", @(\"2.1.3\", \"3.0.0\"), \"^\\d+\")\n                { $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.*\"\n            }\n\n            It \"Similar TableNode on the same header\" {\n                $node = [HeaderNode]::new(\"MyHeader\")\n                $node.AddTable(@(\n                    [PSCustomObject]@{Name = \"Value1\"},\n                    [PSCustomObject]@{Name = \"Value2\"}\n                ))\n                {\n                    $node.AddTable(@(\n                        [PSCustomObject]@{Name = \"Value1\"},\n                        [PSCustomObject]@{Name = \"Value2\"}\n                    ))\n                } | Should -Throw \"This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*\"\n            }\n\n            It \"Similar NoteNode on the same header\" {\n                $node = [HeaderNode]::new(\"MyHeader\")\n                $node.AddNote(\"MyContent\")\n                $node.AddNote(\"MyContent2\")\n                { $node.AddNote(\"MyContent\") } | Should -Throw \"This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*\"\n            }\n\n            It \"AddNode detects duplicates\" {\n                $node = [HeaderNode]::new(\"MyHeader\")\n                $node.AddNode([ToolVersionNode]::new(\"MyTool\", \"2.1.3\"))\n                { $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.*\"\n            }\n\n            It \"AddNodes detects duplicates\" {\n                $node = [HeaderNode]::new(\"MyHeader\")\n                $node.AddNodes(@(\n                    [ToolVersionNode]::new(\"MyTool\", \"2.1.3\"),\n                    [ToolVersionNode]::new(\"MyTool2\", \"2.1.4\")\n                ))\n                {\n                    $node.AddNodes(@(\n                        [ToolVersionNode]::new(\"MyTool3\", \"2.1.5\"),\n                        [ToolVersionNode]::new(\"MyTool\", \"2.1.3\")\n                    ))\n                } | Should -Throw \"This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*\"\n            }\n\n            It \"Doesn't allow adding non-header nodes after header node\" {\n                $node = [HeaderNode]::new(\"MyHeader\")\n                { $node.AddToolVersion(\"MyTool\", \"2.1.3\") } | Should -Not -Throw\n                { $node.AddHeader(\"MySubHeader\") } | Should -Not -Throw\n                { $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\"\n                { $node.AddHeader(\"MySubHeader2\") } | Should -Not -Throw\n                { $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\"\n                { $node.AddToolVersionsList(\"MyTool4\", @(\"2.1.4\", \"2.1.5\"), \"^.+\") } | Should -Not -Throw\n            }\n        }\n    }\n}"
  },
  {
    "path": "helpers/software-report-base/tests/TestHelpers.psm1",
    "content": "function ShouldBeArray([Array] $ActualValue, [Array]$ExpectedValue, [Switch] $Negate, [String] $Because) {\n    if ($Negate) {\n        throw \"Negation is not supported for Should-BeArray\"\n    }\n\n    if ($ExpectedValue.Count -eq 0) {\n        throw \"Expected array cannot be empty. Use Should-BeNullOrEmpty instead.\"\n    }\n\n    $ExpectedValue | ForEach-Object {\n        if ($_.GetType() -notin @([String], [Int32])) {\n            throw \"Only string or int arrays are supported in Should-BeArray\"\n        }\n    }\n\n    $actualValueJson = $ActualValue | ConvertTo-Json\n    $expectedValueJson = $ExpectedValue | ConvertTo-Json\n\n    $succeeded = ($ActualValue.Count -eq $ExpectedValue.Count) -and ($actualValueJson -eq $expectedValueJson)\n\n    if (-not $succeeded) {\n        $failureMessage = \"Expected array '$actualValueJson' to be equal to '$expectedValueJson'\"\n    }\n\n    return [PSCustomObject]@{\n        Succeeded = $succeeded\n        FailureMessage = $failureMessage\n    }\n}\n\nAdd-ShouldOperator -Name BeArray `\n    -InternalName 'ShouldBeArray' `\n    -Test ${function:ShouldBeArray} `\n    -SupportsArrayInput"
  },
  {
    "path": "images/macos/assets/add-certificate.swift",
    "content": "import Foundation\nimport Security\n\nlet certInfo: CFDictionary\n\nenum SecurityError: Error {\n    case generalError\n}\n\nfunc deleteCertificateFromKeyChain(_ certificateLabel: String) -> Bool {\n    let delQuery: [NSString: Any] = [\n        kSecClass: kSecClassCertificate,\n        kSecAttrLabel: certificateLabel,\n    ]\n    let delStatus: OSStatus = SecItemDelete(delQuery as CFDictionary)\n\n    return delStatus == errSecSuccess\n}\n\nfunc saveCertificateToKeyChain(_ certificate: SecCertificate, certificateLabel: String) throws {\n    SecKeychainSetPreferenceDomain(SecPreferencesDomain.system)\n    deleteCertificateFromKeyChain(certificateLabel)\n\n    let setQuery: [NSString: AnyObject] = [\n        kSecClass: kSecClassCertificate,\n        kSecValueRef: certificate,\n        kSecAttrLabel: certificateLabel as AnyObject,\n        kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,\n    ]\n    let addStatus: OSStatus = SecItemAdd(setQuery as CFDictionary, nil)\n\n    guard addStatus == errSecSuccess else {\n        throw SecurityError.generalError\n    }\n\n    var status = SecTrustSettingsSetTrustSettings(certificate, SecTrustSettingsDomain.admin, nil)\n}\n\nfunc getCertificateFromString(stringData: String) throws -> SecCertificate {\n    if let data = NSData(base64Encoded: stringData, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) {\n        if let certificate = SecCertificateCreateWithData(kCFAllocatorDefault, data) {\n            return certificate\n        }\n    }\n    throw SecurityError.generalError\n}\n\nif CommandLine.arguments.count > 1 {\n    let fileURL = URL(fileURLWithPath: CommandLine.arguments[1])\n    do {\n        let certData = try Data(contentsOf: fileURL)\n        let certificate = SecCertificateCreateWithData(nil, certData as CFData)\n        if certificate != nil {\n            print(\"Saving certificate\")\n            try? saveCertificateToKeyChain(certificate!, certificateLabel: \"Test\")\n        } else {\n            print(\"Certificate can't be read\")\n        }\n    } catch {\n        print(\"Unable to read the file \\(CommandLine.arguments[1])\")\n    }\n} else {\n    print(\"Usage: \\(CommandLine.arguments[0]) [cert.file]\")\n}\n"
  },
  {
    "path": "images/macos/assets/auto-software-update-arm64.exp",
    "content": "#! /usr/bin/expect -f\n\nset timeout -1\nspawn sudo /usr/sbin/softwareupdate --restart --verbose --install \"MACOSUPDATE\"\nexpect \"Password*\"\nsend \"[lindex $argv 0]\\r\"\nexpect eof\n"
  },
  {
    "path": "images/macos/assets/bashprofile",
    "content": "[ -f $HOME/.bashrc ] && source $HOME/.bashrc\n"
  },
  {
    "path": "images/macos/assets/bashrc",
    "content": "export LC_CTYPE=en_US.UTF-8\nexport LC_ALL=en_US.UTF-8\nexport LANG=en_US.UTF-8\n\nexport ANDROID_HOME=${HOME}/Library/Android/sdk\nexport ANDROID_SDK_ROOT=${HOME}/Library/Android/sdk\n\nexport VM_ASSETS=/usr/local/opt/$USER/scripts\n\nexport NUNIT_BASE_PATH=/Library/Developer/nunit\nexport NUNIT3_PATH=/Library/Developer/nunit/3.6.0\n\nexport AGENT_TOOLSDIRECTORY=$HOME/hostedtoolcache\nexport RUNNER_TOOL_CACHE=$HOME/hostedtoolcache\nexport ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE=$HOME/actionarchivecache\n\nexport PATH=/Library/Frameworks/Mono.framework/Versions/Current/Commands:$PATH\nexport PATH=/Library/Frameworks/Python.framework/Versions/Current/bin:$PATH\nexport PATH=$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$PATH\nexport PATH=/usr/local/bin:/usr/local/sbin:~/bin:~/.yarn/bin:$PATH\nexport PATH=\"/usr/local/opt/curl/bin:$PATH\"\nexport PATH=$HOME/.cargo/bin:$PATH\n\nexport RCT_NO_LAUNCH_PACKAGER=1\nexport DOTNET_MULTILEVEL_LOOKUP=0\nexport DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1\nexport DOTNET_NOLOGO=1\n\nexport HOMEBREW_NO_AUTO_UPDATE=1\nexport HOMEBREW_NO_INSTALL_CLEANUP=1\nexport HOMEBREW_CASK_OPTS=\"--no-quarantine\"\n\nexport BOOTSTRAP_HASKELL_NONINTERACTIVE=1\nexport BOOTSTRAP_HASKELL_INSTALL_NO_STACK_HOOK=1\n"
  },
  {
    "path": "images/macos/assets/bootstrap-provisioner/change_password",
    "content": "#!/bin/bash\nUSERNAME=\"$1\"\nOLD_PASSWD=\"$2\"\nNEW_PASSWD=\"$3\"\nUPDATE_LOGIN_KEYCHAIN=\"${4:-true}\"\n\nsudo /usr/sbin/sysadminctl -resetPasswordFor $USERNAME -newPassword \"$NEW_PASSWD\" -adminUser $USERNAME -adminPassword \"$OLD_PASSWD\"\nsudo /usr/bin/python3 /Users/$USERNAME/bootstrap/kcpassword.py \"$NEW_PASSWD\"\nsudo /usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser \"$USERNAME\"\n\nif [[ $UPDATE_LOGIN_KEYCHAIN == \"true\" ]]; then\n    /usr/bin/security set-keychain-password -o \"$OLD_PASSWD\" -p \"$NEW_PASSWD\" /Users/$USERNAME/Library/Keychains/login.keychain\nfi\n"
  },
  {
    "path": "images/macos/assets/bootstrap-provisioner/installNewProvisioner.sh",
    "content": "#!/bin/bash -e -o pipefail\nBOOTSTRAP_PATH=\"$1\"\nProvisionerPackageUri=\"$2\"\nProvisionerScriptUri=\"$3\"\nScriptName=\"$4\"\nScriptParam=\"$5\"\nUsername=\"$6\"\narch=$(arch)\n\nif [[ $arch == \"arm64\" ]]; then\n  export PATH=/usr/bin:/usr/sbin:/usr/local/bin:/bin:/sbin:/opt/homebrew/bin\nelse\n  export PATH=/usr/bin:/usr/sbin:/usr/local/bin:/bin:/sbin\nfi\n\nPROVISIONER_ROOT=/usr/local/opt/${Username}\nsudo mkdir -p ${PROVISIONER_ROOT}\nsudo chown ${Username} ${PROVISIONER_ROOT}\n\ntee -a ${PROVISIONER_ROOT}/runprovisioner.sh > /dev/null <<\\EOF\n#!/bin/bash\n\n. ${HOME}/.bashrc\n\n/usr/local/opt/$USER/provisioner/provisioner\nEOF\n\nchmod +x $PROVISIONER_ROOT/runprovisioner.sh\n\naria2c \\\n  --enable-color=false \\\n  --file-allocation=none \\\n  -d ${BOOTSTRAP_PATH} \"${ProvisionerPackageUri}\" >> ${BOOTSTRAP_PATH}/download.log\n\naria2c \\\n  --enable-color=false \\\n  --file-allocation=none \\\n  -d ${BOOTSTRAP_PATH} \"${ProvisionerScriptUri}\" >> ${BOOTSTRAP_PATH}/download.log\n\nchmod +x ${BOOTSTRAP_PATH}/${ScriptName}\n\n# Install Provisioner with provided scripts\neval \"$BOOTSTRAP_PATH/$ScriptName $BOOTSTRAP_PATH/$ScriptParam $Username\" 2>&1 | tee \"$BOOTSTRAP_PATH/install.log\"\n# State File\ntouch $BOOTSTRAP_PATH/provisionerDone"
  },
  {
    "path": "images/macos/assets/bootstrap-provisioner/kcpassword.py",
    "content": "#!/usr/bin/env python3\n\n# Port of Gavin Brock's Perl kcpassword generator to Python, by Tom Taylor\n# <tom@tomtaylor.co.uk>.\n# Perl version: https://www.brock-family.org/gavin/perl/kcpassword.html\n# This script was taken from https://github.com/timsutton/osx-vm-templates/blob/master/scripts/support/set_kcpassword.py\n# Distributed by MIT license, license can be found at the bottom of this script\n\nimport sys\nimport os\n\ndef encode_data(passwd):\n    # The magic 11 bytes - these are just repeated\n    # 0x7D 0x89 0x52 0x23 0xD2 0xBC 0xDD 0xEA 0xA3 0xB9 0x1F\n    key = [125,137,82,35,210,188,221,234,163,185,31]\n    key_len = len(key)\n\n    passwd = [ord(x) for x in list(passwd)]\n    # pad passwd length out to an even multiple of key length\n    r = len(passwd) % key_len\n    if len(passwd) == 11:\n        passwd += [0]\n    elif (r > 0):\n        passwd = passwd + [0] * (key_len - r)\n\n    for n in range(0, len(passwd), len(key)):\n        ki = 0\n        for j in range(n, min(n+len(key), len(passwd))):\n            passwd[j] = passwd[j] ^ key[ki]\n            ki += 1\n\n    return bytearray(passwd)\n\nif __name__ == \"__main__\":\n    runner_pwd_encoded = encode_data(sys.argv[1])\n    fd = os.open('/etc/kcpassword', os.O_WRONLY | os.O_CREAT, 0o600)\n    file = os.fdopen(fd, 'wb')\n    file.truncate(0)\n    file.write(runner_pwd_encoded)\n    file.close()\n\n\"\"\"\nThe MIT License (MIT)\nCopyright (c) 2013-2017 Timothy Sutton\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\"\"\"\n"
  },
  {
    "path": "images/macos/assets/bootstrap-provisioner/setAutoLogin.sh",
    "content": "#!/bin/bash -e -o pipefail\n: <<-LICENSE_BLOCK\nsetAutoLogin (20210911) - Copyright (c) 2021 Joel Bruner (https://github.com/brunerd)\nLicensed under the MIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nLICENSE_BLOCK\n\nUSERNAME=\"${1}\"\nPW=\"${2}\"\n\nkcpasswordEncode() {\n    #ascii string\n    local thisString=\"${1}\"\n    local i\n\n    #macOS cipher hex ascii representation array\n    local cipherHex_array=( 7D 89 52 23 D2 BC DD EA A3 B9 1F )\n\n    #converted to hex representation with spaces\n    local thisStringHex_array=( $(echo -n \"${thisString}\" | xxd -p -u | sed 's/../& /g') )\n\n    #get padding by subtraction if under 11\n    local r=$(( ${#thisStringHex_array[@]} % 11 ))\n    local padding=0\n    if [ ${#thisStringHex_array[@]} -eq 11 ]; then\n        local padding=1\n    elif [ $r -gt 0 ]; then\n        local padding=$(( 11 - $r ))\n    fi\n\n    #cycle through each element of the array + padding\n    for ((i=0; i < $(( ${#thisStringHex_array[@]} + ${padding})); i++)); do\n        #use modulus to loop through the cipher array elements\n        local charHex_cipher=${cipherHex_array[$(( $i % 11 ))]}\n\n        #get the current hex representation element\n        local charHex=${thisStringHex_array[$i]}\n\n        #use $(( shell Aritmethic )) to ^ XOR the two 0x## values (extra padding is 0x00) \n        #take decimal value and printf convert to two char hex value\n        #use xxd to convert hex to actual value and append to the encodedString variable\n        local encodedString+=$(printf \"%02X\" \"$(( 0x${charHex_cipher} ^ 0x${charHex:-00} ))\")\n    done\n\n    #return the string without a newline\n    echo -n \"${encodedString}\"\n}\n\n#encode password and write file\nkcpasswordEncode \"${PW}\" | xxd -r -p > /etc/kcpassword\n\n#ensure ownership and permissions (600)\nchown root:wheel /etc/kcpassword\nchmod u=rw,go= /etc/kcpassword\n\n#turn on auto login\n/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser -string \"${USERNAME}\"\n/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUserScreenLocked -bool false\necho \"Auto login enabled for '${USERNAME}'\"\n"
  },
  {
    "path": "images/macos/macos-14-Readme.md",
    "content": "| Announcements |\n|-|\n| [macOS 26 (Tahoe) is now generally available in GitHub Actions](https://github.com/actions/runner-images/issues/13739) |\n| [[macOS] The macOS 14 Sonoma based runner images will begin deprecation on July 6th and will be fully unsupported by November 2nd for GitHub Actions and Azure DevOps](https://github.com/actions/runner-images/issues/13518) |\n***\n# macOS 14\n- OS Version: macOS 14.8.4 (23J319)\n- Kernel Version: Darwin 23.6.0\n- Image Version: 20260302.0252.1\n\n## Installed Software\n\n### Language and Runtime\n- .NET Core SDK: 8.0.101, 8.0.204, 8.0.303, 8.0.418, 9.0.102, 9.0.203, 9.0.311, 10.0.103\n- Bash 3.2.57(1)-release\n- Clang/LLVM 15.0.0\n- Clang/LLVM (Homebrew) 15.0.7 - available on `$(brew --prefix llvm@15)/bin/clang`\n- GCC 13 (Homebrew GCC 13.4.0) - available by `gcc-13` alias\n- GCC 14 (Homebrew GCC 14.3.0) - available by `gcc-14` alias\n- GCC 15 (Homebrew GCC 15.2.0_1) - available by `gcc-15` alias\n- GNU Fortran 13 (Homebrew GCC 13.4.0) - available by `gfortran-13` alias\n- GNU Fortran 14 (Homebrew GCC 14.3.0) - available by `gfortran-14` alias\n- GNU Fortran 15 (Homebrew GCC 15.2.0_1) - available by `gfortran-15` alias\n- Kotlin 2.3.10-release-465\n- Mono 6.12.0.188\n- Node.js 20.20.0\n- Perl 5.42.0\n- PHP 8.5.3\n- Python3 3.14.3\n- Ruby 3.3.10\n\n### Package Management\n- Bundler 4.0.7\n- Carthage 0.40.0\n- CocoaPods 1.16.2\n- Composer 2.9.5\n- Homebrew 5.0.15\n- NPM 10.8.2\n- NuGet 6.3.1.1\n- Pip3 26.0 (python 3.14)\n- Pipx 1.8.0\n- RubyGems 4.0.7\n- Vcpkg 2026 (build from commit 62159a45e1)\n- Yarn 1.22.22\n\n### Project Management\n- Apache Ant 1.10.15\n- Apache Maven 3.9.12\n- Gradle 9.3.1\n\n### Utilities\n- 7-Zip 17.05\n- aria2 1.37.0\n- azcopy 10.32.1\n- bazel 9.0.0\n- bazelisk 1.28.1\n- bsdtar 3.5.3 - available by 'tar' alias\n- Curl 8.18.0\n- Git 2.53.0\n- Git LFS 3.7.1\n- GitHub CLI 2.87.3\n- GNU Tar 1.35 - available by 'gtar' alias\n- GNU Wget 1.25.0\n- gpg (GnuPG) 2.4.9\n- jq 1.8.1\n- OpenSSL 1.1.1w  11 Sep 2023\n- Packer 1.15.0\n- pkgconf 2.5.1\n- Unxip 3.3\n- yq 4.52.4\n- zstd 1.5.7\n- Ninja 1.13.2\n\n### Tools\n- AWS CLI 2.34.0\n- AWS SAM CLI 1.154.0\n- AWS Session Manager CLI 1.2.779.0\n- Azure CLI 2.83.0\n- Azure CLI (azure-devops) 1.0.2\n- Bicep CLI 0.41.2\n- Cmake 4.2.3\n- CodeQL Action Bundle 2.24.2\n- Fastlane 2.232.2\n- SwiftFormat 0.59.1\n- Xcbeautify 3.1.4\n- Xcode Command Line Tools 16.2.0.0.1.1733547573\n- Xcodes 1.6.2\n\n### Linters\n- SwiftLint 0.63.2\n\n### Browsers\n- Safari 26.3 (19623.2.7.18.1)\n- SafariDriver 26.3 (19623.2.7.18.1)\n- Google Chrome 145.0.7632.117\n- Google Chrome for Testing 145.0.7632.117\n- ChromeDriver 145.0.7632.117\n- Microsoft Edge 145.0.3800.82\n- Microsoft Edge WebDriver 145.0.3800.82\n- Mozilla Firefox 148.0\n- geckodriver 0.36.0\n- Selenium server 4.41.0\n\n#### Environment variables\n| Name            | Value                                 |\n| --------------- | ------------------------------------- |\n| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-x64 |\n| EDGEWEBDRIVER   | /usr/local/share/edge_driver          |\n| GECKOWEBDRIVER  | /usr/local/opt/geckodriver/bin        |\n\n### Java\n| Version               | Environment Variable |\n| --------------------- | -------------------- |\n| 8.0.482+8             | JAVA_HOME_8_X64      |\n| 11.0.30+7             | JAVA_HOME_11_X64     |\n| 17.0.18+8             | JAVA_HOME_17_X64     |\n| 21.0.10+7.0 (default) | JAVA_HOME_21_X64     |\n| 25.0.2+10.0           | JAVA_HOME_25_X64     |\n\n### Cached Tools\n\n#### Ruby\n- 3.2.10\n- 3.3.10\n- 3.4.8\n- 4.0.1\n\n#### Python\n- 3.10.19\n- 3.11.9\n- 3.12.10\n- 3.13.12\n- 3.14.3\n\n#### Node.js\n- 20.20.0\n- 22.22.0\n- 24.14.0\n\n#### Go\n- 1.22.12\n- 1.23.12\n- 1.24.13\n- 1.25.7\n\n### Rust Tools\n- Cargo 1.93.1\n- Rust 1.93.1\n- Rustdoc 1.93.1\n- Rustup 1.28.2\n\n#### Packages\n- Clippy 0.1.93\n- Rustfmt 1.8.0-stable\n\n### PowerShell Tools\n- PowerShell 7.4.13\n\n#### PowerShell Modules\n- Az: 14.6.0\n- Pester: 5.7.1\n- PSScriptAnalyzer: 1.24.0\n\n### Xcode\n| Version        | Build    | Path                           | Symlinks                                                  |\n| -------------- | -------- | ------------------------------ | --------------------------------------------------------- |\n| 16.2           | 16C5032a | /Applications/Xcode_16.2.app   | /Applications/Xcode_16.2.0.app                            |\n| 16.1           | 16B40    | /Applications/Xcode_16.1.app   | /Applications/Xcode_16.1.0.app                            |\n| 15.4 (default) | 15F31d   | /Applications/Xcode_15.4.app   | /Applications/Xcode_15.4.0.app<br>/Applications/Xcode.app |\n| 15.3           | 15E204a  | /Applications/Xcode_15.3.app   | /Applications/Xcode_15.3.0.app                            |\n| 15.2           | 15C500b  | /Applications/Xcode_15.2.app   | /Applications/Xcode_15.2.0.app                            |\n| 15.1           | 15C65    | /Applications/Xcode_15.1.app   | /Applications/Xcode_15.1.0.app                            |\n| 15.0.1         | 15A507   | /Applications/Xcode_15.0.1.app | /Applications/Xcode_15.0.app                              |\n\n#### Installed SDKs\n| SDK                      | SDK Name             | Xcode Version |\n| ------------------------ | -------------------- | ------------- |\n| macOS 14.0               | macosx14.0           | 15.0.1        |\n| macOS 14.2               | macosx14.2           | 15.1, 15.2    |\n| macOS 14.4               | macosx14.4           | 15.3          |\n| macOS 14.5               | macosx14.5           | 15.4          |\n| macOS 15.1               | macosx15.1           | 16.1          |\n| macOS 15.2               | macosx15.2           | 16.2          |\n| iOS 17.0                 | iphoneos17.0         | 15.0.1        |\n| iOS 17.2                 | iphoneos17.2         | 15.1, 15.2    |\n| iOS 17.4                 | iphoneos17.4         | 15.3          |\n| iOS 17.5                 | iphoneos17.5         | 15.4          |\n| iOS 18.1                 | iphoneos18.1         | 16.1          |\n| iOS 18.2                 | iphoneos18.2         | 16.2          |\n| Simulator - iOS 17.0     | iphonesimulator17.0  | 15.0.1        |\n| Simulator - iOS 17.2     | iphonesimulator17.2  | 15.1, 15.2    |\n| Simulator - iOS 17.4     | iphonesimulator17.4  | 15.3          |\n| Simulator - iOS 17.5     | iphonesimulator17.5  | 15.4          |\n| Simulator - iOS 18.1     | iphonesimulator18.1  | 16.1          |\n| Simulator - iOS 18.2     | iphonesimulator18.2  | 16.2          |\n| tvOS 17.0                | appletvos17.0        | 15.0.1        |\n| tvOS 17.2                | appletvos17.2        | 15.1, 15.2    |\n| tvOS 17.4                | appletvos17.4        | 15.3          |\n| tvOS 17.5                | appletvos17.5        | 15.4          |\n| tvOS 18.1                | appletvos18.1        | 16.1          |\n| tvOS 18.2                | appletvos18.2        | 16.2          |\n| Simulator - tvOS 17.0    | appletvsimulator17.0 | 15.0.1        |\n| Simulator - tvOS 17.2    | appletvsimulator17.2 | 15.1, 15.2    |\n| Simulator - tvOS 17.4    | appletvsimulator17.4 | 15.3          |\n| Simulator - tvOS 17.5    | appletvsimulator17.5 | 15.4          |\n| Simulator - tvOS 18.1    | appletvsimulator18.1 | 16.1          |\n| Simulator - tvOS 18.2    | appletvsimulator18.2 | 16.2          |\n| watchOS 10.0             | watchos10.0          | 15.0.1        |\n| watchOS 10.2             | watchos10.2          | 15.1, 15.2    |\n| watchOS 10.4             | watchos10.4          | 15.3          |\n| watchOS 10.5             | watchos10.5          | 15.4          |\n| watchOS 11.1             | watchos11.1          | 16.1          |\n| watchOS 11.2             | watchos11.2          | 16.2          |\n| Simulator - watchOS 10.0 | watchsimulator10.0   | 15.0.1        |\n| Simulator - watchOS 10.2 | watchsimulator10.2   | 15.1, 15.2    |\n| Simulator - watchOS 10.4 | watchsimulator10.4   | 15.3          |\n| Simulator - watchOS 10.5 | watchsimulator10.5   | 15.4          |\n| Simulator - watchOS 11.1 | watchsimulator11.1   | 16.1          |\n| Simulator - watchOS 11.2 | watchsimulator11.2   | 16.2          |\n| visionOS 1.0             | xros1.0              | 15.2          |\n| visionOS 1.1             | xros1.1              | 15.3          |\n| visionOS 1.2             | xros1.2              | 15.4          |\n| visionOS 2.1             | xros2.1              | 16.1          |\n| visionOS 2.2             | xros2.2              | 16.2          |\n| Simulator - visionOS 1.0 | xrsimulator1.0       | 15.2          |\n| Simulator - visionOS 1.1 | xrsimulator1.1       | 15.3          |\n| Simulator - visionOS 1.2 | xrsimulator1.2       | 15.4          |\n| Simulator - visionOS 2.1 | xrsimulator2.1       | 16.1          |\n| Simulator - visionOS 2.2 | xrsimulator2.2       | 16.2          |\n| DriverKit 23.0           | driverkit23.0        | 15.0.1        |\n| DriverKit 23.2           | driverkit23.2        | 15.1, 15.2    |\n| DriverKit 23.4           | driverkit23.4        | 15.3          |\n| DriverKit 23.5           | driverkit23.5        | 15.4          |\n| DriverKit 24.1           | driverkit24.1        | 16.1          |\n| DriverKit 24.2           | driverkit24.2        | 16.2          |\n\n#### Installed Simulators\n| Name         | OS     | Simulators                                                                                                                                                                                                                                                                                                                                                               |\n| ------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| iOS 17.0     | 17.0.1 | iPhone 15<br>iPhone 15 Plus<br>iPhone 15 Pro<br>iPhone 15 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air (5th generation)<br>iPad mini (6th generation)<br>iPad Pro (11-inch) (4th generation)<br>iPad Pro (12.9-inch) (6th generation)                                                                                                     |\n| iOS 17.2     | 17.2   | iPhone 15<br>iPhone 15 Plus<br>iPhone 15 Pro<br>iPhone 15 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air (5th generation)<br>iPad mini (6th generation)<br>iPad Pro (11-inch) (4th generation)<br>iPad Pro (12.9-inch) (6th generation)                                                                                                     |\n| iOS 17.4     | 17.4   | iPhone 15<br>iPhone 15 Plus<br>iPhone 15 Pro<br>iPhone 15 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air (5th generation)<br>iPad Air 11-inch (M2)<br>iPad Air 13-inch (M2)<br>iPad mini (6th generation)<br>iPad Pro (11-inch) (4th generation)<br>iPad Pro (12.9-inch) (6th generation)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4) |\n| iOS 17.5     | 17.5   | iPhone 15<br>iPhone 15 Plus<br>iPhone 15 Pro<br>iPhone 15 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air 11-inch (M2)<br>iPad Air 13-inch (M2)<br>iPad mini (6th generation)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4)                                                                                                              |\n| iOS 18.1     | 18.1   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air 11-inch (M2)<br>iPad Air 13-inch (M2)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4)                                                                                                                     |\n| iOS 18.2     | 18.2   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air 11-inch (M2)<br>iPad Air 13-inch (M2)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4)                                                                                                                     |\n| tvOS 17.0    | 17.0   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| tvOS 17.2    | 17.2   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| tvOS 17.4    | 17.4   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| tvOS 17.5    | 17.5   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| tvOS 18.1    | 18.1   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| tvOS 18.2    | 18.2   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| watchOS 10.0 | 10.0   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 5 (40mm)<br>Apple Watch Series 5 (44mm)<br>Apple Watch Series 6 (40mm)<br>Apple Watch Series 6 (44mm)<br>Apple Watch Series 7 (41mm)<br>Apple Watch Series 7 (45mm)<br>Apple Watch Series 9 (41mm)<br>Apple Watch Series 9 (45mm)<br>Apple Watch Ultra 2 (49mm)   |\n| watchOS 10.2 | 10.2   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 5 (40mm)<br>Apple Watch Series 5 (44mm)<br>Apple Watch Series 6 (40mm)<br>Apple Watch Series 6 (44mm)<br>Apple Watch Series 7 (41mm)<br>Apple Watch Series 7 (45mm)<br>Apple Watch Series 9 (41mm)<br>Apple Watch Series 9 (45mm)<br>Apple Watch Ultra 2 (49mm)   |\n| watchOS 10.4 | 10.4   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 5 (40mm)<br>Apple Watch Series 5 (44mm)<br>Apple Watch Series 6 (40mm)<br>Apple Watch Series 6 (44mm)<br>Apple Watch Series 7 (41mm)<br>Apple Watch Series 7 (45mm)<br>Apple Watch Series 9 (41mm)<br>Apple Watch Series 9 (45mm)<br>Apple Watch Ultra 2 (49mm)   |\n| watchOS 10.5 | 10.5   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 5 (40mm)<br>Apple Watch Series 5 (44mm)<br>Apple Watch Series 6 (40mm)<br>Apple Watch Series 6 (44mm)<br>Apple Watch Series 7 (41mm)<br>Apple Watch Series 7 (45mm)<br>Apple Watch Series 9 (41mm)<br>Apple Watch Series 9 (45mm)<br>Apple Watch Ultra 2 (49mm)   |\n| watchOS 11.1 | 11.1   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 10 (42mm)<br>Apple Watch Series 10 (46mm)<br>Apple Watch Ultra 2 (49mm)                                                                                                                                                                                           |\n| watchOS 11.2 | 11.2   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 10 (42mm)<br>Apple Watch Series 10 (46mm)<br>Apple Watch Ultra 2 (49mm)                                                                                                                                                                                           |\n\n### Android\n| Package Name               | Version                                                                                                                                                                                                                                                                                                               |\n| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Android Command Line Tools | 11.0                                                                                                                                                                                                                                                                                                                  |\n| Android Emulator           | 36.4.9                                                                                                                                                                                                                                                                                                                |\n| Android SDK Build-tools    | 36.0.0 36.1.0<br>35.0.0 35.0.1<br>34.0.0                                                                                                                                                                                                                                                                              |\n| Android SDK Platforms      | android-36.1 (rev 1)<br>android-36-ext19 (rev 1)<br>android-36-ext18 (rev 1)<br>android-36 (rev 2)<br>android-35-ext15 (rev 1)<br>android-35-ext14 (rev 1)<br>android-35 (rev 2)<br>android-34-ext8 (rev 1)<br>android-34-ext12 (rev 1)<br>android-34-ext11 (rev 1)<br>android-34-ext10 (rev 1)<br>android-34 (rev 3) |\n| Android SDK Platform-Tools | 36.0.2                                                                                                                                                                                                                                                                                                                |\n| Android Support Repository | 47.0.0                                                                                                                                                                                                                                                                                                                |\n| CMake                      | 3.31.5<br>4.1.2                                                                                                                                                                                                                                                                                                       |\n| Google Play services       | 49                                                                                                                                                                                                                                                                                                                    |\n| Google Repository          | 58                                                                                                                                                                                                                                                                                                                    |\n| NDK                        | 27.3.13750724 (default)<br>28.2.13676358<br>29.0.14206865                                                                                                                                                                                                                                                             |\n\n#### Environment variables\n| Name                    | Value                                               |\n| ----------------------- | --------------------------------------------------- |\n| ANDROID_HOME            | /Users/runner/Library/Android/sdk                   |\n| ANDROID_NDK             | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_HOME        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/29.0.14206865 |\n| ANDROID_NDK_ROOT        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_SDK_ROOT        | /Users/runner/Library/Android/sdk                   |\n\n### Miscellaneous\n- Tcl/Tk 8.6.17\n\n#### Environment variables\n| Name              | Value                                                                                     |\n| ----------------- | ----------------------------------------------------------------------------------------- |\n| PARALLELS_DMG_URL | https://download.parallels.com/desktop/v26/26.2.2-57373/ParallelsDesktop-26.2.2-57373.dmg |\n\n##### Notes\n```\nIf you want to use Parallels Desktop you should download a package from URL stored in\nPARALLELS_DMG_URL environment variable. A system extension is allowed for this version.\n```\n\n"
  },
  {
    "path": "images/macos/macos-14-arm64-Readme.md",
    "content": "| Announcements |\n|-|\n| [macOS 26 (Tahoe) is now generally available in GitHub Actions](https://github.com/actions/runner-images/issues/13739) |\n| [[macOS] The macOS 14 Sonoma based runner images will begin deprecation on July 6th and will be fully unsupported by November 2nd for GitHub Actions and Azure DevOps](https://github.com/actions/runner-images/issues/13518) |\n***\n# macOS 14\n- OS Version: macOS 14.8.4 (23J319)\n- Kernel Version: Darwin 23.6.0\n- Image Version: 20260302.0147.1\n\n## Installed Software\n\n### Language and Runtime\n- .NET Core SDK: 8.0.101, 8.0.204, 8.0.303, 8.0.418, 9.0.102, 9.0.203, 9.0.311, 10.0.103\n- Bash 3.2.57(1)-release\n- Clang/LLVM 15.0.0\n- Clang/LLVM (Homebrew) 15.0.7 - available on `$(brew --prefix llvm@15)/bin/clang`\n- GCC 13 (Homebrew GCC 13.4.0) - available by `gcc-13` alias\n- GCC 14 (Homebrew GCC 14.3.0) - available by `gcc-14` alias\n- GCC 15 (Homebrew GCC 15.2.0_1) - available by `gcc-15` alias\n- GNU Fortran 13 (Homebrew GCC 13.4.0) - available by `gfortran-13` alias\n- GNU Fortran 14 (Homebrew GCC 14.3.0) - available by `gfortran-14` alias\n- GNU Fortran 15 (Homebrew GCC 15.2.0_1) - available by `gfortran-15` alias\n- Kotlin 2.3.10-release-465\n- Mono 6.12.0.188\n- Node.js 20.20.0\n- Perl 5.42.0\n- Python3 3.14.3\n- Ruby 3.3.10\n\n### Package Management\n- Bundler 4.0.7\n- Carthage 0.40.0\n- CocoaPods 1.16.2\n- Homebrew 5.0.15\n- NPM 10.8.2\n- NuGet 6.3.1.1\n- Pip3 26.0 (python 3.14)\n- Pipx 1.8.0\n- RubyGems 4.0.7\n- Vcpkg 2026 (build from commit 62159a45e1)\n- Yarn 1.22.22\n\n### Project Management\n- Apache Ant 1.10.15\n- Apache Maven 3.9.12\n- Gradle 9.3.1\n\n### Utilities\n- 7-Zip 17.05\n- aria2 1.37.0\n- azcopy 10.32.1\n- bazel 9.0.0\n- bazelisk 1.28.1\n- bsdtar 3.5.3 - available by 'tar' alias\n- Curl 8.7.1\n- Git 2.53.0\n- Git LFS 3.7.1\n- GitHub CLI 2.87.3\n- GNU Tar 1.35 - available by 'gtar' alias\n- GNU Wget 1.25.0\n- gpg (GnuPG) 2.4.9\n- jq 1.8.1\n- OpenSSL 1.1.1w  11 Sep 2023\n- Packer 1.15.0\n- pkgconf 2.5.1\n- Unxip 3.3\n- yq 4.52.4\n- zstd 1.5.7\n- Ninja 1.13.2\n\n### Tools\n- AWS CLI 2.34.0\n- AWS SAM CLI 1.154.0\n- AWS Session Manager CLI 1.2.779.0\n- Azure CLI 2.83.0\n- Azure CLI (azure-devops) 1.0.2\n- Bicep CLI 0.41.2\n- Cmake 4.2.3\n- CodeQL Action Bundle 2.24.2\n- Fastlane 2.232.2\n- SwiftFormat 0.59.1\n- Xcbeautify 3.1.4\n- Xcode Command Line Tools 16.2.0.0.1.1733547573\n- Xcodes 1.6.2\n\n### Browsers\n- Safari 26.3 (19623.2.7.18.1)\n- SafariDriver 26.3 (19623.2.7.18.1)\n- Google Chrome 145.0.7632.117\n- Google Chrome for Testing 145.0.7632.117\n- ChromeDriver 145.0.7632.117\n- Microsoft Edge 145.0.3800.82\n- Microsoft Edge WebDriver 145.0.3800.82\n- Mozilla Firefox 148.0\n- geckodriver 0.36.0\n- Selenium server 4.41.0\n\n#### Environment variables\n| Name            | Value                                   |\n| --------------- | --------------------------------------- |\n| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-arm64 |\n| EDGEWEBDRIVER   | /usr/local/share/edge_driver            |\n| GECKOWEBDRIVER  | /opt/homebrew/opt/geckodriver/bin       |\n\n### Java\n| Version               | Environment Variable |\n| --------------------- | -------------------- |\n| 11.0.30+7             | JAVA_HOME_11_arm64   |\n| 17.0.18+8             | JAVA_HOME_17_arm64   |\n| 21.0.10+7.0 (default) | JAVA_HOME_21_arm64   |\n| 25.0.2+10.0           | JAVA_HOME_25_arm64   |\n\n### Cached Tools\n\n#### Ruby\n- 3.2.10\n- 3.3.10\n- 3.4.8\n- 4.0.1\n\n#### Python\n- 3.11.9\n- 3.12.10\n- 3.13.12\n- 3.14.3\n\n#### Node.js\n- 20.20.0\n- 22.22.0\n- 24.14.0\n\n#### Go\n- 1.22.12\n- 1.23.12\n- 1.24.13\n- 1.25.7\n\n### Rust Tools\n- Cargo 1.93.1\n- Rust 1.93.1\n- Rustdoc 1.93.1\n- Rustup 1.28.2\n\n#### Packages\n- Clippy 0.1.93\n- Rustfmt 1.8.0-stable\n\n### PowerShell Tools\n- PowerShell 7.4.13\n\n#### PowerShell Modules\n- Az: 14.6.0\n- Pester: 5.7.1\n- PSScriptAnalyzer: 1.24.0\n\n### Xcode\n| Version        | Build    | Path                           | Symlinks                                                  |\n| -------------- | -------- | ------------------------------ | --------------------------------------------------------- |\n| 16.2           | 16C5032a | /Applications/Xcode_16.2.app   | /Applications/Xcode_16.2.0.app                            |\n| 16.1           | 16B40    | /Applications/Xcode_16.1.app   | /Applications/Xcode_16.1.0.app                            |\n| 15.4 (default) | 15F31d   | /Applications/Xcode_15.4.app   | /Applications/Xcode_15.4.0.app<br>/Applications/Xcode.app |\n| 15.3           | 15E204a  | /Applications/Xcode_15.3.app   | /Applications/Xcode_15.3.0.app                            |\n| 15.2           | 15C500b  | /Applications/Xcode_15.2.app   | /Applications/Xcode_15.2.0.app                            |\n| 15.1           | 15C65    | /Applications/Xcode_15.1.app   | /Applications/Xcode_15.1.0.app                            |\n| 15.0.1         | 15A507   | /Applications/Xcode_15.0.1.app | /Applications/Xcode_15.0.app                              |\n\n#### Installed SDKs\n| SDK                      | SDK Name             | Xcode Version |\n| ------------------------ | -------------------- | ------------- |\n| macOS 14.0               | macosx14.0           | 15.0.1        |\n| macOS 14.2               | macosx14.2           | 15.1, 15.2    |\n| macOS 14.4               | macosx14.4           | 15.3          |\n| macOS 14.5               | macosx14.5           | 15.4          |\n| macOS 15.1               | macosx15.1           | 16.1          |\n| macOS 15.2               | macosx15.2           | 16.2          |\n| iOS 17.0                 | iphoneos17.0         | 15.0.1        |\n| iOS 17.2                 | iphoneos17.2         | 15.1, 15.2    |\n| iOS 17.4                 | iphoneos17.4         | 15.3          |\n| iOS 17.5                 | iphoneos17.5         | 15.4          |\n| iOS 18.1                 | iphoneos18.1         | 16.1          |\n| iOS 18.2                 | iphoneos18.2         | 16.2          |\n| Simulator - iOS 17.0     | iphonesimulator17.0  | 15.0.1        |\n| Simulator - iOS 17.2     | iphonesimulator17.2  | 15.1, 15.2    |\n| Simulator - iOS 17.4     | iphonesimulator17.4  | 15.3          |\n| Simulator - iOS 17.5     | iphonesimulator17.5  | 15.4          |\n| Simulator - iOS 18.1     | iphonesimulator18.1  | 16.1          |\n| Simulator - iOS 18.2     | iphonesimulator18.2  | 16.2          |\n| tvOS 17.0                | appletvos17.0        | 15.0.1        |\n| tvOS 17.2                | appletvos17.2        | 15.1, 15.2    |\n| tvOS 17.4                | appletvos17.4        | 15.3          |\n| tvOS 17.5                | appletvos17.5        | 15.4          |\n| tvOS 18.1                | appletvos18.1        | 16.1          |\n| tvOS 18.2                | appletvos18.2        | 16.2          |\n| Simulator - tvOS 17.0    | appletvsimulator17.0 | 15.0.1        |\n| Simulator - tvOS 17.2    | appletvsimulator17.2 | 15.1, 15.2    |\n| Simulator - tvOS 17.4    | appletvsimulator17.4 | 15.3          |\n| Simulator - tvOS 17.5    | appletvsimulator17.5 | 15.4          |\n| Simulator - tvOS 18.1    | appletvsimulator18.1 | 16.1          |\n| Simulator - tvOS 18.2    | appletvsimulator18.2 | 16.2          |\n| watchOS 10.0             | watchos10.0          | 15.0.1        |\n| watchOS 10.2             | watchos10.2          | 15.1, 15.2    |\n| watchOS 10.4             | watchos10.4          | 15.3          |\n| watchOS 10.5             | watchos10.5          | 15.4          |\n| watchOS 11.1             | watchos11.1          | 16.1          |\n| watchOS 11.2             | watchos11.2          | 16.2          |\n| Simulator - watchOS 10.0 | watchsimulator10.0   | 15.0.1        |\n| Simulator - watchOS 10.2 | watchsimulator10.2   | 15.1, 15.2    |\n| Simulator - watchOS 10.4 | watchsimulator10.4   | 15.3          |\n| Simulator - watchOS 10.5 | watchsimulator10.5   | 15.4          |\n| Simulator - watchOS 11.1 | watchsimulator11.1   | 16.1          |\n| Simulator - watchOS 11.2 | watchsimulator11.2   | 16.2          |\n| visionOS 1.0             | xros1.0              | 15.2          |\n| visionOS 1.1             | xros1.1              | 15.3          |\n| visionOS 1.2             | xros1.2              | 15.4          |\n| visionOS 2.1             | xros2.1              | 16.1          |\n| visionOS 2.2             | xros2.2              | 16.2          |\n| Simulator - visionOS 1.0 | xrsimulator1.0       | 15.2          |\n| Simulator - visionOS 1.1 | xrsimulator1.1       | 15.3          |\n| Simulator - visionOS 1.2 | xrsimulator1.2       | 15.4          |\n| Simulator - visionOS 2.1 | xrsimulator2.1       | 16.1          |\n| Simulator - visionOS 2.2 | xrsimulator2.2       | 16.2          |\n| DriverKit 23.0           | driverkit23.0        | 15.0.1        |\n| DriverKit 23.2           | driverkit23.2        | 15.1, 15.2    |\n| DriverKit 23.4           | driverkit23.4        | 15.3          |\n| DriverKit 23.5           | driverkit23.5        | 15.4          |\n| DriverKit 24.1           | driverkit24.1        | 16.1          |\n| DriverKit 24.2           | driverkit24.2        | 16.2          |\n\n#### Installed Simulators\n| Name         | OS     | Simulators                                                                                                                                                                                                                                                                                                                                                               |\n| ------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| iOS 17.0     | 17.0.1 | iPhone 15<br>iPhone 15 Plus<br>iPhone 15 Pro<br>iPhone 15 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air (5th generation)<br>iPad mini (6th generation)<br>iPad Pro (11-inch) (4th generation)<br>iPad Pro (12.9-inch) (6th generation)                                                                                                     |\n| iOS 17.2     | 17.2   | iPhone 15<br>iPhone 15 Plus<br>iPhone 15 Pro<br>iPhone 15 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air (5th generation)<br>iPad mini (6th generation)<br>iPad Pro (11-inch) (4th generation)<br>iPad Pro (12.9-inch) (6th generation)                                                                                                     |\n| iOS 17.4     | 17.4   | iPhone 15<br>iPhone 15 Plus<br>iPhone 15 Pro<br>iPhone 15 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air (5th generation)<br>iPad Air 11-inch (M2)<br>iPad Air 13-inch (M2)<br>iPad mini (6th generation)<br>iPad Pro (11-inch) (4th generation)<br>iPad Pro (12.9-inch) (6th generation)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4) |\n| iOS 17.5     | 17.5   | iPhone 15<br>iPhone 15 Plus<br>iPhone 15 Pro<br>iPhone 15 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air 11-inch (M2)<br>iPad Air 13-inch (M2)<br>iPad mini (6th generation)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4)                                                                                                              |\n| iOS 18.1     | 18.1   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air 11-inch (M2)<br>iPad Air 13-inch (M2)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4)                                                                                                                     |\n| iOS 18.2     | 18.2   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad Air 11-inch (M2)<br>iPad Air 13-inch (M2)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4)                                                                                                                     |\n| tvOS 17.0    | 17.0   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| tvOS 17.2    | 17.2   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| tvOS 17.4    | 17.4   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| tvOS 17.5    | 17.5   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| tvOS 18.1    | 18.1   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| tvOS 18.2    | 18.2   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                      |\n| watchOS 10.0 | 10.0   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 5 (40mm)<br>Apple Watch Series 5 (44mm)<br>Apple Watch Series 6 (40mm)<br>Apple Watch Series 6 (44mm)<br>Apple Watch Series 7 (41mm)<br>Apple Watch Series 7 (45mm)<br>Apple Watch Series 9 (41mm)<br>Apple Watch Series 9 (45mm)<br>Apple Watch Ultra 2 (49mm)   |\n| watchOS 10.2 | 10.2   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 5 (40mm)<br>Apple Watch Series 5 (44mm)<br>Apple Watch Series 6 (40mm)<br>Apple Watch Series 6 (44mm)<br>Apple Watch Series 7 (41mm)<br>Apple Watch Series 7 (45mm)<br>Apple Watch Series 9 (41mm)<br>Apple Watch Series 9 (45mm)<br>Apple Watch Ultra 2 (49mm)   |\n| watchOS 10.4 | 10.4   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 5 (40mm)<br>Apple Watch Series 5 (44mm)<br>Apple Watch Series 6 (40mm)<br>Apple Watch Series 6 (44mm)<br>Apple Watch Series 7 (41mm)<br>Apple Watch Series 7 (45mm)<br>Apple Watch Series 9 (41mm)<br>Apple Watch Series 9 (45mm)<br>Apple Watch Ultra 2 (49mm)   |\n| watchOS 10.5 | 10.5   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 5 (40mm)<br>Apple Watch Series 5 (44mm)<br>Apple Watch Series 6 (40mm)<br>Apple Watch Series 6 (44mm)<br>Apple Watch Series 7 (41mm)<br>Apple Watch Series 7 (45mm)<br>Apple Watch Series 9 (41mm)<br>Apple Watch Series 9 (45mm)<br>Apple Watch Ultra 2 (49mm)   |\n| watchOS 11.1 | 11.1   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 10 (42mm)<br>Apple Watch Series 10 (46mm)<br>Apple Watch Ultra 2 (49mm)                                                                                                                                                                                           |\n| watchOS 11.2 | 11.2   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 10 (42mm)<br>Apple Watch Series 10 (46mm)<br>Apple Watch Ultra 2 (49mm)                                                                                                                                                                                           |\n| visionOS 1.0 | 1.0    | Apple Vision Pro                                                                                                                                                                                                                                                                                                                                                         |\n| visionOS 1.1 | 1.1    | Apple Vision Pro                                                                                                                                                                                                                                                                                                                                                         |\n| visionOS 1.2 | 1.2    | Apple Vision Pro                                                                                                                                                                                                                                                                                                                                                         |\n| visionOS 2.1 | 2.1    | Apple Vision Pro                                                                                                                                                                                                                                                                                                                                                         |\n| visionOS 2.2 | 2.2    | Apple Vision Pro                                                                                                                                                                                                                                                                                                                                                         |\n\n### Android\n| Package Name               | Version                                                                                                                                                                                                                                                                                                               |\n| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Android Command Line Tools | 11.0                                                                                                                                                                                                                                                                                                                  |\n| Android Emulator           | 36.4.9                                                                                                                                                                                                                                                                                                                |\n| Android SDK Build-tools    | 36.0.0 36.1.0<br>35.0.0 35.0.1<br>34.0.0                                                                                                                                                                                                                                                                              |\n| Android SDK Platforms      | android-36.1 (rev 1)<br>android-36-ext19 (rev 1)<br>android-36-ext18 (rev 1)<br>android-36 (rev 2)<br>android-35-ext15 (rev 1)<br>android-35-ext14 (rev 1)<br>android-35 (rev 2)<br>android-34-ext8 (rev 1)<br>android-34-ext12 (rev 1)<br>android-34-ext11 (rev 1)<br>android-34-ext10 (rev 1)<br>android-34 (rev 3) |\n| Android SDK Platform-Tools | 36.0.2                                                                                                                                                                                                                                                                                                                |\n| Android Support Repository | 47.0.0                                                                                                                                                                                                                                                                                                                |\n| CMake                      | 3.31.5<br>4.1.2                                                                                                                                                                                                                                                                                                       |\n| Google Play services       | 49                                                                                                                                                                                                                                                                                                                    |\n| Google Repository          | 58                                                                                                                                                                                                                                                                                                                    |\n| NDK                        | 27.3.13750724 (default)<br>28.2.13676358<br>29.0.14206865                                                                                                                                                                                                                                                             |\n\n#### Environment variables\n| Name                    | Value                                               |\n| ----------------------- | --------------------------------------------------- |\n| ANDROID_HOME            | /Users/runner/Library/Android/sdk                   |\n| ANDROID_NDK             | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_HOME        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/29.0.14206865 |\n| ANDROID_NDK_ROOT        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_SDK_ROOT        | /Users/runner/Library/Android/sdk                   |\n\n### Miscellaneous\n- Tcl/Tk 8.6.17\n\n"
  },
  {
    "path": "images/macos/macos-15-Readme.md",
    "content": "| Announcements |\n|-|\n| [macOS 26 (Tahoe) is now generally available in GitHub Actions](https://github.com/actions/runner-images/issues/13739) |\n| [[macOS] The macOS 14 Sonoma based runner images will begin deprecation on July 6th and will be fully unsupported by November 2nd for GitHub Actions and Azure DevOps](https://github.com/actions/runner-images/issues/13518) |\n***\n# macOS 15\n- OS Version: macOS 15.7.4 (24G517)\n- Kernel Version: Darwin 24.6.0\n- Image Version: 20260303.0227.1\n\n## Installed Software\n\n### Language and Runtime\n- .NET Core SDK: 8.0.101, 8.0.204, 8.0.303, 8.0.418, 9.0.102, 9.0.203, 9.0.311, 10.0.103\n- Bash 3.2.57(1)-release\n- Clang/LLVM 17.0.0\n- Clang/LLVM (Homebrew) 18.1.8 - available on `$(brew --prefix llvm@18)/bin/clang`\n- GCC 13 (Homebrew GCC 13.4.0) - available by `gcc-13` alias\n- GCC 14 (Homebrew GCC 14.3.0) - available by `gcc-14` alias\n- GCC 15 (Homebrew GCC 15.2.0_1) - available by `gcc-15` alias\n- GNU Fortran 13 (Homebrew GCC 13.4.0) - available by `gfortran-13` alias\n- GNU Fortran 14 (Homebrew GCC 14.3.0) - available by `gfortran-14` alias\n- GNU Fortran 15 (Homebrew GCC 15.2.0_1) - available by `gfortran-15` alias\n- Kotlin 2.3.10-release-465\n- Node.js 22.22.0\n- Perl 5.42.0\n- PHP 8.5.3\n- Python3 3.14.3\n- Ruby 3.3.10\n\n### Package Management\n- Bundler 4.0.7\n- Carthage 0.40.0\n- CocoaPods 1.16.2\n- Composer 2.9.5\n- Homebrew 5.0.16\n- NPM 10.9.4\n- Pip3 26.0 (python 3.14)\n- Pipx 1.8.0\n- RubyGems 4.0.7\n- Vcpkg 2026 (build from commit 39a6cc0e44)\n- Yarn 1.22.22\n\n### Project Management\n- Apache Ant 1.10.15\n- Apache Maven 3.9.12\n- Gradle 9.3.1\n\n### Utilities\n- 7-Zip 17.05\n- aria2 1.37.0\n- azcopy 10.32.1\n- bazel 9.0.0\n- bazelisk 1.28.1\n- bsdtar 3.5.3 - available by 'tar' alias\n- Curl 8.18.0\n- Git 2.53.0\n- Git LFS 3.7.1\n- GitHub CLI 2.87.3\n- GNU Tar 1.35 - available by 'gtar' alias\n- GNU Wget 1.25.0\n- gpg (GnuPG) 2.4.9\n- jq 1.8.1\n- OpenSSL 1.1.1w  11 Sep 2023\n- Packer 1.15.0\n- pkgconf 2.5.1\n- Unxip 3.3\n- yq 4.52.4\n- zstd 1.5.7\n- Ninja 1.13.2\n\n### Tools\n- AWS CLI 2.34.0\n- AWS SAM CLI 1.154.0\n- AWS Session Manager CLI 1.2.779.0\n- Azure CLI 2.84.0\n- Azure CLI (azure-devops) 1.0.2\n- Bicep CLI 0.41.2\n- Cmake 4.2.3\n- CodeQL Action Bundle 2.24.2\n- Fastlane 2.232.2\n- SwiftFormat 0.59.1\n- Xcbeautify 3.1.4\n- Xcode Command Line Tools 16.4.0.0.1.1747106510\n- Xcodes 1.6.2\n\n### Linters\n- SwiftLint 0.63.2\n\n### Browsers\n- Safari 26.3 (20623.2.7.18.1)\n- SafariDriver 26.3 (20623.2.7.18.1)\n- Google Chrome 145.0.7632.117\n- Google Chrome for Testing 145.0.7632.117\n- ChromeDriver 145.0.7632.117\n- Microsoft Edge 145.0.3800.82\n- Microsoft Edge WebDriver 145.0.3800.82\n- Mozilla Firefox 148.0\n- geckodriver 0.36.0\n- Selenium server 4.41.0\n\n#### Environment variables\n| Name            | Value                                 |\n| --------------- | ------------------------------------- |\n| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-x64 |\n| EDGEWEBDRIVER   | /usr/local/share/edge_driver          |\n| GECKOWEBDRIVER  | /usr/local/opt/geckodriver/bin        |\n\n### Java\n| Version               | Environment Variable |\n| --------------------- | -------------------- |\n| 11.0.30+7             | JAVA_HOME_11_X64     |\n| 17.0.18+8             | JAVA_HOME_17_X64     |\n| 21.0.10+7.0 (default) | JAVA_HOME_21_X64     |\n| 25.0.2+10.0           | JAVA_HOME_25_X64     |\n\n### Cached Tools\n\n#### Ruby\n- 3.2.10\n- 3.3.10\n- 3.4.8\n- 4.0.1\n\n#### Python\n- 3.10.19\n- 3.11.9\n- 3.12.10\n- 3.13.12\n- 3.14.3\n\n#### Node.js\n- 20.20.0\n- 22.22.0\n- 24.14.0\n\n#### Go\n- 1.22.12\n- 1.23.12\n- 1.24.13\n- 1.25.7\n\n### Rust Tools\n- Cargo 1.93.1\n- Rust 1.93.1\n- Rustdoc 1.93.1\n- Rustup 1.28.2\n\n#### Packages\n- Clippy 0.1.93\n- Rustfmt 1.8.0-stable\n\n### PowerShell Tools\n- PowerShell 7.4.13\n\n#### PowerShell Modules\n- Az: 14.6.0\n- Pester: 5.7.1\n- PSScriptAnalyzer: 1.24.0\n\n### Xcode\n| Version        | Build    | Path                           | Symlinks                                                       |\n| -------------- | -------- | ------------------------------ | -------------------------------------------------------------- |\n| 26.3           | 17C529   | /Applications/Xcode_26.3.app   | /Applications/Xcode_26.3.0.app                                 |\n| 26.2           | 17C52    | /Applications/Xcode_26.2.app   | /Applications/Xcode_26.2.0.app                                 |\n| 26.1.1         | 17B100   | /Applications/Xcode_26.1.1.app | /Applications/Xcode_26.1.app                                   |\n| 26.0.1         | 17A400   | /Applications/Xcode_26.0.1.app | /Applications/Xcode_26.0.app                                   |\n| 16.4 (default) | 16F6     | /Applications/Xcode_16.4.app   | /Applications/Xcode_16.4.0.app<br>/Applications/Xcode.app      |\n| 16.3           | 16E140   | /Applications/Xcode_16.3.app   | /Applications/Xcode_16.3.0.app                                 |\n| 16.2           | 16C5032a | /Applications/Xcode_16.2.app   | /Applications/Xcode_16.2.0.app                                 |\n| 16.1           | 16B40    | /Applications/Xcode_16.1.app   | /Applications/Xcode_16.1.0.app                                 |\n| 16.0           | 16A242d  | /Applications/Xcode_16.app     | /Applications/Xcode_16.0.0.app<br>/Applications/Xcode_16.0.app |\n\n#### Installed SDKs\n| SDK                       | SDK Name             | Xcode Version |\n| ------------------------- | -------------------- | ------------- |\n| macOS 15.0                | macosx15.0           | 16.0          |\n| macOS 15.1                | macosx15.1           | 16.1          |\n| macOS 15.2                | macosx15.2           | 16.2          |\n| macOS 15.4                | macosx15.4           | 16.3          |\n| macOS 15.5                | macosx15.5           | 16.4          |\n| macOS 26.0                | macosx26.0           | 26.0.1        |\n| macOS 26.1                | macosx26.1           | 26.1.1        |\n| macOS 26.2                | macosx26.2           | 26.2, 26.3    |\n| iOS 18.0                  | iphoneos18.0         | 16.0          |\n| iOS 18.1                  | iphoneos18.1         | 16.1          |\n| iOS 18.2                  | iphoneos18.2         | 16.2          |\n| iOS 18.4                  | iphoneos18.4         | 16.3          |\n| iOS 18.5                  | iphoneos18.5         | 16.4          |\n| iOS 26.0                  | iphoneos26.0         | 26.0.1        |\n| iOS 26.1                  | iphoneos26.1         | 26.1.1        |\n| iOS 26.2                  | iphoneos26.2         | 26.2, 26.3    |\n| Simulator - iOS 18.0      | iphonesimulator18.0  | 16.0          |\n| Simulator - iOS 18.1      | iphonesimulator18.1  | 16.1          |\n| Simulator - iOS 18.2      | iphonesimulator18.2  | 16.2          |\n| Simulator - iOS 18.4      | iphonesimulator18.4  | 16.3          |\n| Simulator - iOS 18.5      | iphonesimulator18.5  | 16.4          |\n| Simulator - iOS 26.0      | iphonesimulator26.0  | 26.0.1        |\n| Simulator - iOS 26.1      | iphonesimulator26.1  | 26.1.1        |\n| Simulator - iOS 26.2      | iphonesimulator26.2  | 26.2, 26.3    |\n| tvOS 18.0                 | appletvos18.0        | 16.0          |\n| tvOS 18.1                 | appletvos18.1        | 16.1          |\n| tvOS 18.2                 | appletvos18.2        | 16.2          |\n| tvOS 18.4                 | appletvos18.4        | 16.3          |\n| tvOS 18.5                 | appletvos18.5        | 16.4          |\n| tvOS 26.0                 | appletvos26.0        | 26.0.1        |\n| tvOS 26.1                 | appletvos26.1        | 26.1.1        |\n| tvOS 26.2                 | appletvos26.2        | 26.2, 26.3    |\n| Simulator - tvOS 18.0     | appletvsimulator18.0 | 16.0          |\n| Simulator - tvOS 18.1     | appletvsimulator18.1 | 16.1          |\n| Simulator - tvOS 18.2     | appletvsimulator18.2 | 16.2          |\n| Simulator - tvOS 18.4     | appletvsimulator18.4 | 16.3          |\n| Simulator - tvOS 18.5     | appletvsimulator18.5 | 16.4          |\n| Simulator - tvOS 26.0     | appletvsimulator26.0 | 26.0.1        |\n| Simulator - tvOS 26.1     | appletvsimulator26.1 | 26.1.1        |\n| Simulator - tvOS 26.2     | appletvsimulator26.2 | 26.2, 26.3    |\n| watchOS 11.0              | watchos11.0          | 16.0          |\n| watchOS 11.1              | watchos11.1          | 16.1          |\n| watchOS 11.2              | watchos11.2          | 16.2          |\n| watchOS 11.4              | watchos11.4          | 16.3          |\n| watchOS 11.5              | watchos11.5          | 16.4          |\n| watchOS 26.0              | watchos26.0          | 26.0.1        |\n| watchOS 26.1              | watchos26.1          | 26.1.1        |\n| watchOS 26.2              | watchos26.2          | 26.2, 26.3    |\n| Simulator - watchOS 11.0  | watchsimulator11.0   | 16.0          |\n| Simulator - watchOS 11.1  | watchsimulator11.1   | 16.1          |\n| Simulator - watchOS 11.2  | watchsimulator11.2   | 16.2          |\n| Simulator - watchOS 11.4  | watchsimulator11.4   | 16.3          |\n| Simulator - watchOS 11.5  | watchsimulator11.5   | 16.4          |\n| Simulator - watchOS 26.0  | watchsimulator26.0   | 26.0.1        |\n| Simulator - watchOS 26.1  | watchsimulator26.1   | 26.1.1        |\n| Simulator - watchOS 26.2  | watchsimulator26.2   | 26.2, 26.3    |\n| visionOS 2.0              | xros2.0              | 16.0          |\n| visionOS 2.1              | xros2.1              | 16.1          |\n| visionOS 2.2              | xros2.2              | 16.2          |\n| visionOS 2.4              | xros2.4              | 16.3          |\n| visionOS 2.5              | xros2.5              | 16.4          |\n| visionOS 26.0             | xros26.0             | 26.0.1        |\n| visionOS 26.1             | xros26.1             | 26.1.1        |\n| visionOS 26.2             | xros26.2             | 26.2, 26.3    |\n| Simulator - visionOS 2.0  | xrsimulator2.0       | 16.0          |\n| Simulator - visionOS 2.1  | xrsimulator2.1       | 16.1          |\n| Simulator - visionOS 2.2  | xrsimulator2.2       | 16.2          |\n| Simulator - visionOS 2.4  | xrsimulator2.4       | 16.3          |\n| Simulator - visionOS 2.5  | xrsimulator2.5       | 16.4          |\n| Simulator - visionOS 26.0 | xrsimulator26.0      | 26.0.1        |\n| Simulator - visionOS 26.1 | xrsimulator26.1      | 26.1.1        |\n| Simulator - visionOS 26.2 | xrsimulator26.2      | 26.2, 26.3    |\n| DriverKit 24.0            | driverkit24.0        | 16.0          |\n| DriverKit 24.1            | driverkit24.1        | 16.1          |\n| DriverKit 24.2            | driverkit24.2        | 16.2          |\n| DriverKit 24.4            | driverkit24.4        | 16.3          |\n| DriverKit 24.5            | driverkit24.5        | 16.4          |\n| DriverKit 25.0            | driverkit25.0        | 26.0.1        |\n| DriverKit 25.1            | driverkit25.1        | 26.1.1        |\n| DriverKit 25.2            | driverkit25.2        | 26.2, 26.3    |\n\n#### Installed Simulators\n| Name         | OS     | Simulators                                                                                                                                                                                                                                                                                                                                                                                                                                            |\n| ------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| iOS 18.5     | 18.5   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone 16e<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad (A16)<br>iPad Air 11-inch (M2)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M2)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4)                                                                                                                    |\n| iOS 18.6     | 18.6   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone 16e<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad (A16)<br>iPad Air 11-inch (M2)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M2)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4)                                                                                                                    |\n| iOS 26.0     | 26.0.1 | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad (A16)<br>iPad Air 11-inch (M2)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M2)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M4)<br>iPad Pro 13-inch (M5) |\n| iOS 26.1     | 26.1   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad (A16)<br>iPad Air 11-inch (M2)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M2)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M4)<br>iPad Pro 13-inch (M5) |\n| iOS 26.2     | 26.2   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad (A16)<br>iPad Air 11-inch (M2)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M2)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M4)<br>iPad Pro 13-inch (M5) |\n| tvOS 18.5    | 18.5   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                                                                                                   |\n| tvOS 26.1    | 26.1   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                                                                                                   |\n| tvOS 26.2    | 26.2   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                                                                                                   |\n| watchOS 11.5 | 11.5   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 10 (42mm)<br>Apple Watch Series 10 (46mm)<br>Apple Watch Ultra 2 (49mm)                                                                                                                                                                                                                                                                        |\n| watchOS 26.1 | 26.1   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch SE 3 (40mm)<br>Apple Watch SE 3 (44mm)<br>Apple Watch Series 10 (42mm)<br>Apple Watch Series 10 (46mm)<br>Apple Watch Series 11 (42mm)<br>Apple Watch Series 11 (46mm)<br>Apple Watch Ultra 2 (49mm)<br>Apple Watch Ultra 3 (49mm)                                                                                                                    |\n| watchOS 26.2 | 26.2   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch SE 3 (40mm)<br>Apple Watch SE 3 (44mm)<br>Apple Watch Series 10 (42mm)<br>Apple Watch Series 10 (46mm)<br>Apple Watch Series 11 (42mm)<br>Apple Watch Series 11 (46mm)<br>Apple Watch Ultra 2 (49mm)<br>Apple Watch Ultra 3 (49mm)                                                                                                                    |\n\n### Android\n| Package Name               | Version                                                                                                                                                                                                                                                                                                               |\n| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Android Command Line Tools | 16.0                                                                                                                                                                                                                                                                                                                  |\n| Android Emulator           | 36.4.9                                                                                                                                                                                                                                                                                                                |\n| Android SDK Build-tools    | 36.0.0 36.1.0<br>35.0.0 35.0.1                                                                                                                                                                                                                                                                                        |\n| Android SDK Platforms      | android-36.1 (rev 1)<br>android-36-ext19 (rev 1)<br>android-36-ext18 (rev 1)<br>android-36 (rev 2)<br>android-35-ext15 (rev 1)<br>android-35-ext14 (rev 1)<br>android-35 (rev 2)<br>android-34-ext8 (rev 1)<br>android-34-ext12 (rev 1)<br>android-34-ext11 (rev 1)<br>android-34-ext10 (rev 1)<br>android-34 (rev 3) |\n| Android SDK Platform-Tools | 37.0.0                                                                                                                                                                                                                                                                                                                |\n| Android Support Repository | 47.0.0                                                                                                                                                                                                                                                                                                                |\n| CMake                      | 3.31.5<br>4.1.2                                                                                                                                                                                                                                                                                                       |\n| Google Play services       | 49                                                                                                                                                                                                                                                                                                                    |\n| Google Repository          | 58                                                                                                                                                                                                                                                                                                                    |\n| NDK                        | 27.3.13750724 (default)<br>28.2.13676358<br>29.0.14206865                                                                                                                                                                                                                                                             |\n\n#### Environment variables\n| Name                    | Value                                               |\n| ----------------------- | --------------------------------------------------- |\n| ANDROID_HOME            | /Users/runner/Library/Android/sdk                   |\n| ANDROID_NDK             | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_HOME        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/29.0.14206865 |\n| ANDROID_NDK_ROOT        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_SDK_ROOT        | /Users/runner/Library/Android/sdk                   |\n\n### Miscellaneous\n- Tcl/Tk 8.6.17\n\n#### Environment variables\n| Name              | Value                                                                                     |\n| ----------------- | ----------------------------------------------------------------------------------------- |\n| PARALLELS_DMG_URL | https://download.parallels.com/desktop/v26/26.2.2-57373/ParallelsDesktop-26.2.2-57373.dmg |\n\n##### Notes\n```\nIf you want to use Parallels Desktop you should download a package from URL stored in\nPARALLELS_DMG_URL environment variable. A system extension is allowed for this version.\n```\n\n"
  },
  {
    "path": "images/macos/macos-15-arm64-Readme.md",
    "content": "| Announcements |\n|-|\n| [macOS 26 (Tahoe) is now generally available in GitHub Actions](https://github.com/actions/runner-images/issues/13739) |\n| [[macOS] The macOS 14 Sonoma based runner images will begin deprecation on July 6th and will be fully unsupported by November 2nd for GitHub Actions and Azure DevOps](https://github.com/actions/runner-images/issues/13518) |\n***\n# macOS 15\n- OS Version: macOS 15.7.4 (24G517)\n- Kernel Version: Darwin 24.6.0\n- Image Version: 20260303.0188.2\n\n## Installed Software\n\n### Language and Runtime\n- .NET Core SDK: 8.0.101, 8.0.204, 8.0.303, 8.0.418, 9.0.102, 9.0.203, 9.0.311, 10.0.103\n- Bash 3.2.57(1)-release\n- Clang/LLVM 17.0.0\n- Clang/LLVM (Homebrew) 18.1.8 - available on `$(brew --prefix llvm@18)/bin/clang`\n- GCC 13 (Homebrew GCC 13.4.0) - available by `gcc-13` alias\n- GCC 14 (Homebrew GCC 14.3.0) - available by `gcc-14` alias\n- GCC 15 (Homebrew GCC 15.2.0_1) - available by `gcc-15` alias\n- GNU Fortran 13 (Homebrew GCC 13.4.0) - available by `gfortran-13` alias\n- GNU Fortran 14 (Homebrew GCC 14.3.0) - available by `gfortran-14` alias\n- GNU Fortran 15 (Homebrew GCC 15.2.0_1) - available by `gfortran-15` alias\n- Kotlin 2.3.10-release-465\n- Node.js 22.22.0\n- Perl 5.42.0\n- Python3 3.14.3\n- Ruby 3.3.10\n\n### Package Management\n- Bundler 4.0.7\n- Carthage 0.40.0\n- CocoaPods 1.16.2\n- Homebrew 5.0.16\n- NPM 10.9.4\n- Pip3 26.0 (python 3.14)\n- Pipx 1.8.0\n- RubyGems 4.0.7\n- Vcpkg 2026 (build from commit 39a6cc0e44)\n- Yarn 1.22.22\n\n### Project Management\n- Apache Ant 1.10.15\n- Apache Maven 3.9.12\n- Gradle 9.3.1\n\n### Utilities\n- 7-Zip 17.05\n- aria2 1.37.0\n- azcopy 10.32.1\n- bazel 9.0.0\n- bazelisk 1.28.1\n- bsdtar 3.5.3 - available by 'tar' alias\n- Curl 8.7.1\n- Git 2.53.0\n- Git LFS 3.7.1\n- GitHub CLI 2.87.3\n- GNU Tar 1.35 - available by 'gtar' alias\n- GNU Wget 1.25.0\n- gpg (GnuPG) 2.4.9\n- jq 1.8.1\n- OpenSSL 1.1.1w  11 Sep 2023\n- Packer 1.15.0\n- pkgconf 2.5.1\n- Unxip 3.3\n- yq 4.52.4\n- zstd 1.5.7\n- Ninja 1.13.2\n\n### Tools\n- AWS CLI 2.34.0\n- AWS SAM CLI 1.154.0\n- AWS Session Manager CLI 1.2.779.0\n- Azure CLI 2.84.0\n- Azure CLI (azure-devops) 1.0.2\n- Bicep CLI 0.41.2\n- Cmake 4.2.3\n- CodeQL Action Bundle 2.24.2\n- Fastlane 2.232.2\n- SwiftFormat 0.59.1\n- Xcbeautify 3.1.4\n- Xcode Command Line Tools 16.4.0.0.1.1747106510\n- Xcodes 1.6.2\n\n### Browsers\n- Safari 26.3 (20623.2.7.18.1)\n- SafariDriver 26.3 (20623.2.7.18.1)\n- Google Chrome 145.0.7632.117\n- Google Chrome for Testing 145.0.7632.117\n- ChromeDriver 145.0.7632.117\n- Microsoft Edge 145.0.3800.82\n- Microsoft Edge WebDriver 145.0.3800.82\n- Mozilla Firefox 148.0\n- geckodriver 0.36.0\n- Selenium server 4.41.0\n\n#### Environment variables\n| Name            | Value                                   |\n| --------------- | --------------------------------------- |\n| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-arm64 |\n| EDGEWEBDRIVER   | /usr/local/share/edge_driver            |\n| GECKOWEBDRIVER  | /opt/homebrew/opt/geckodriver/bin       |\n\n### Java\n| Version               | Environment Variable |\n| --------------------- | -------------------- |\n| 11.0.30+7             | JAVA_HOME_11_arm64   |\n| 17.0.18+8             | JAVA_HOME_17_arm64   |\n| 21.0.10+7.0 (default) | JAVA_HOME_21_arm64   |\n| 25.0.2+10.0           | JAVA_HOME_25_arm64   |\n\n### Cached Tools\n\n#### Ruby\n- 3.2.10\n- 3.3.10\n- 3.4.8\n- 4.0.1\n\n#### Python\n- 3.11.9\n- 3.12.10\n- 3.13.12\n- 3.14.3\n\n#### Node.js\n- 20.20.0\n- 22.22.0\n- 24.14.0\n\n#### Go\n- 1.22.12\n- 1.23.12\n- 1.24.13\n- 1.25.7\n\n### Rust Tools\n- Cargo 1.93.1\n- Rust 1.93.1\n- Rustdoc 1.93.1\n- Rustup 1.28.2\n\n#### Packages\n- Clippy 0.1.93\n- Rustfmt 1.8.0-stable\n\n### PowerShell Tools\n- PowerShell 7.4.13\n\n#### PowerShell Modules\n- Az: 14.6.0\n- Pester: 5.7.1\n- PSScriptAnalyzer: 1.24.0\n\n### Xcode\n| Version        | Build    | Path                           | Symlinks                                                       |\n| -------------- | -------- | ------------------------------ | -------------------------------------------------------------- |\n| 26.3           | 17C529   | /Applications/Xcode_26.3.app   | /Applications/Xcode_26.3.0.app                                 |\n| 26.2           | 17C52    | /Applications/Xcode_26.2.app   | /Applications/Xcode_26.2.0.app                                 |\n| 26.1.1         | 17B100   | /Applications/Xcode_26.1.1.app | /Applications/Xcode_26.1.app                                   |\n| 26.0.1         | 17A400   | /Applications/Xcode_26.0.1.app | /Applications/Xcode_26.0.app                                   |\n| 16.4 (default) | 16F6     | /Applications/Xcode_16.4.app   | /Applications/Xcode_16.4.0.app<br>/Applications/Xcode.app      |\n| 16.3           | 16E140   | /Applications/Xcode_16.3.app   | /Applications/Xcode_16.3.0.app                                 |\n| 16.2           | 16C5032a | /Applications/Xcode_16.2.app   | /Applications/Xcode_16.2.0.app                                 |\n| 16.1           | 16B40    | /Applications/Xcode_16.1.app   | /Applications/Xcode_16.1.0.app                                 |\n| 16.0           | 16A242d  | /Applications/Xcode_16.app     | /Applications/Xcode_16.0.0.app<br>/Applications/Xcode_16.0.app |\n\n#### Installed SDKs\n| SDK                       | SDK Name             | Xcode Version |\n| ------------------------- | -------------------- | ------------- |\n| macOS 15.0                | macosx15.0           | 16.0          |\n| macOS 15.1                | macosx15.1           | 16.1          |\n| macOS 15.2                | macosx15.2           | 16.2          |\n| macOS 15.4                | macosx15.4           | 16.3          |\n| macOS 15.5                | macosx15.5           | 16.4          |\n| macOS 26.0                | macosx26.0           | 26.0.1        |\n| macOS 26.1                | macosx26.1           | 26.1.1        |\n| macOS 26.2                | macosx26.2           | 26.2, 26.3    |\n| iOS 18.0                  | iphoneos18.0         | 16.0          |\n| iOS 18.1                  | iphoneos18.1         | 16.1          |\n| iOS 18.2                  | iphoneos18.2         | 16.2          |\n| iOS 18.4                  | iphoneos18.4         | 16.3          |\n| iOS 18.5                  | iphoneos18.5         | 16.4          |\n| iOS 26.0                  | iphoneos26.0         | 26.0.1        |\n| iOS 26.1                  | iphoneos26.1         | 26.1.1        |\n| iOS 26.2                  | iphoneos26.2         | 26.2, 26.3    |\n| Simulator - iOS 18.0      | iphonesimulator18.0  | 16.0          |\n| Simulator - iOS 18.1      | iphonesimulator18.1  | 16.1          |\n| Simulator - iOS 18.2      | iphonesimulator18.2  | 16.2          |\n| Simulator - iOS 18.4      | iphonesimulator18.4  | 16.3          |\n| Simulator - iOS 18.5      | iphonesimulator18.5  | 16.4          |\n| Simulator - iOS 26.0      | iphonesimulator26.0  | 26.0.1        |\n| Simulator - iOS 26.1      | iphonesimulator26.1  | 26.1.1        |\n| Simulator - iOS 26.2      | iphonesimulator26.2  | 26.2, 26.3    |\n| tvOS 18.0                 | appletvos18.0        | 16.0          |\n| tvOS 18.1                 | appletvos18.1        | 16.1          |\n| tvOS 18.2                 | appletvos18.2        | 16.2          |\n| tvOS 18.4                 | appletvos18.4        | 16.3          |\n| tvOS 18.5                 | appletvos18.5        | 16.4          |\n| tvOS 26.0                 | appletvos26.0        | 26.0.1        |\n| tvOS 26.1                 | appletvos26.1        | 26.1.1        |\n| tvOS 26.2                 | appletvos26.2        | 26.2, 26.3    |\n| Simulator - tvOS 18.0     | appletvsimulator18.0 | 16.0          |\n| Simulator - tvOS 18.1     | appletvsimulator18.1 | 16.1          |\n| Simulator - tvOS 18.2     | appletvsimulator18.2 | 16.2          |\n| Simulator - tvOS 18.4     | appletvsimulator18.4 | 16.3          |\n| Simulator - tvOS 18.5     | appletvsimulator18.5 | 16.4          |\n| Simulator - tvOS 26.0     | appletvsimulator26.0 | 26.0.1        |\n| Simulator - tvOS 26.1     | appletvsimulator26.1 | 26.1.1        |\n| Simulator - tvOS 26.2     | appletvsimulator26.2 | 26.2, 26.3    |\n| watchOS 11.0              | watchos11.0          | 16.0          |\n| watchOS 11.1              | watchos11.1          | 16.1          |\n| watchOS 11.2              | watchos11.2          | 16.2          |\n| watchOS 11.4              | watchos11.4          | 16.3          |\n| watchOS 11.5              | watchos11.5          | 16.4          |\n| watchOS 26.0              | watchos26.0          | 26.0.1        |\n| watchOS 26.1              | watchos26.1          | 26.1.1        |\n| watchOS 26.2              | watchos26.2          | 26.2, 26.3    |\n| Simulator - watchOS 11.0  | watchsimulator11.0   | 16.0          |\n| Simulator - watchOS 11.1  | watchsimulator11.1   | 16.1          |\n| Simulator - watchOS 11.2  | watchsimulator11.2   | 16.2          |\n| Simulator - watchOS 11.4  | watchsimulator11.4   | 16.3          |\n| Simulator - watchOS 11.5  | watchsimulator11.5   | 16.4          |\n| Simulator - watchOS 26.0  | watchsimulator26.0   | 26.0.1        |\n| Simulator - watchOS 26.1  | watchsimulator26.1   | 26.1.1        |\n| Simulator - watchOS 26.2  | watchsimulator26.2   | 26.2, 26.3    |\n| visionOS 2.0              | xros2.0              | 16.0          |\n| visionOS 2.1              | xros2.1              | 16.1          |\n| visionOS 2.2              | xros2.2              | 16.2          |\n| visionOS 2.4              | xros2.4              | 16.3          |\n| visionOS 2.5              | xros2.5              | 16.4          |\n| visionOS 26.0             | xros26.0             | 26.0.1        |\n| visionOS 26.1             | xros26.1             | 26.1.1        |\n| visionOS 26.2             | xros26.2             | 26.2, 26.3    |\n| Simulator - visionOS 2.0  | xrsimulator2.0       | 16.0          |\n| Simulator - visionOS 2.1  | xrsimulator2.1       | 16.1          |\n| Simulator - visionOS 2.2  | xrsimulator2.2       | 16.2          |\n| Simulator - visionOS 2.4  | xrsimulator2.4       | 16.3          |\n| Simulator - visionOS 2.5  | xrsimulator2.5       | 16.4          |\n| Simulator - visionOS 26.0 | xrsimulator26.0      | 26.0.1        |\n| Simulator - visionOS 26.1 | xrsimulator26.1      | 26.1.1        |\n| Simulator - visionOS 26.2 | xrsimulator26.2      | 26.2, 26.3    |\n| DriverKit 24.0            | driverkit24.0        | 16.0          |\n| DriverKit 24.1            | driverkit24.1        | 16.1          |\n| DriverKit 24.2            | driverkit24.2        | 16.2          |\n| DriverKit 24.4            | driverkit24.4        | 16.3          |\n| DriverKit 24.5            | driverkit24.5        | 16.4          |\n| DriverKit 25.0            | driverkit25.0        | 26.0.1        |\n| DriverKit 25.1            | driverkit25.1        | 26.1.1        |\n| DriverKit 25.2            | driverkit25.2        | 26.2, 26.3    |\n\n#### Installed Simulators\n| Name          | OS     | Simulators                                                                                                                                                                                                                                                                                                                                                                                                                                            |\n| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| iOS 18.5      | 18.5   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone 16e<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad (A16)<br>iPad Air 11-inch (M2)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M2)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4)                                                                                                                    |\n| iOS 18.6      | 18.6   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone 16e<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad (A16)<br>iPad Air 11-inch (M2)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M2)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 13-inch (M4)                                                                                                                    |\n| iOS 26.0      | 26.0.1 | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad (A16)<br>iPad Air 11-inch (M2)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M2)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M4)<br>iPad Pro 13-inch (M5) |\n| iOS 26.1      | 26.1   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad (A16)<br>iPad Air 11-inch (M2)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M2)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M4)<br>iPad Pro 13-inch (M5) |\n| iOS 26.2      | 26.2   | iPhone 16<br>iPhone 16 Plus<br>iPhone 16 Pro<br>iPhone 16 Pro Max<br>iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPhone SE (3rd generation)<br>iPad (10th generation)<br>iPad (A16)<br>iPad Air 11-inch (M2)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M2)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M4)<br>iPad Pro 13-inch (M5) |\n| tvOS 18.5     | 18.5   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                                                                                                   |\n| tvOS 26.1     | 26.1   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                                                                                                   |\n| tvOS 26.2     | 26.2   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                                                                                                                                                                                                   |\n| watchOS 11.5  | 11.5   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch Series 10 (42mm)<br>Apple Watch Series 10 (46mm)<br>Apple Watch Ultra 2 (49mm)                                                                                                                                                                                                                                                                        |\n| watchOS 26.1  | 26.1   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch SE 3 (40mm)<br>Apple Watch SE 3 (44mm)<br>Apple Watch Series 10 (42mm)<br>Apple Watch Series 10 (46mm)<br>Apple Watch Series 11 (42mm)<br>Apple Watch Series 11 (46mm)<br>Apple Watch Ultra 2 (49mm)<br>Apple Watch Ultra 3 (49mm)                                                                                                                    |\n| watchOS 26.2  | 26.2   | Apple Watch SE (40mm) (2nd generation)<br>Apple Watch SE (44mm) (2nd generation)<br>Apple Watch SE 3 (40mm)<br>Apple Watch SE 3 (44mm)<br>Apple Watch Series 10 (42mm)<br>Apple Watch Series 10 (46mm)<br>Apple Watch Series 11 (42mm)<br>Apple Watch Series 11 (46mm)<br>Apple Watch Ultra 2 (49mm)<br>Apple Watch Ultra 3 (49mm)                                                                                                                    |\n| visionOS 2.3  | 2.3    | Apple Vision Pro                                                                                                                                                                                                                                                                                                                                                                                                                                      |\n| visionOS 2.4  | 2.4    | Apple Vision Pro                                                                                                                                                                                                                                                                                                                                                                                                                                      |\n| visionOS 2.5  | 2.5    | Apple Vision Pro                                                                                                                                                                                                                                                                                                                                                                                                                                      |\n| visionOS 26.1 | 26.1   | Apple Vision Pro                                                                                                                                                                                                                                                                                                                                                                                                                                      |\n| visionOS 26.2 | 26.2   | Apple Vision Pro                                                                                                                                                                                                                                                                                                                                                                                                                                      |\n\n### Android\n| Package Name               | Version                                                                                                                                                                                                                                                                                                               |\n| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Android Command Line Tools | 16.0                                                                                                                                                                                                                                                                                                                  |\n| Android Emulator           | 36.4.9                                                                                                                                                                                                                                                                                                                |\n| Android SDK Build-tools    | 36.0.0 36.1.0<br>35.0.0 35.0.1                                                                                                                                                                                                                                                                                        |\n| Android SDK Platforms      | android-36.1 (rev 1)<br>android-36-ext19 (rev 1)<br>android-36-ext18 (rev 1)<br>android-36 (rev 2)<br>android-35-ext15 (rev 1)<br>android-35-ext14 (rev 1)<br>android-35 (rev 2)<br>android-34-ext8 (rev 1)<br>android-34-ext12 (rev 1)<br>android-34-ext11 (rev 1)<br>android-34-ext10 (rev 1)<br>android-34 (rev 3) |\n| Android SDK Platform-Tools | 37.0.0                                                                                                                                                                                                                                                                                                                |\n| Android Support Repository | 47.0.0                                                                                                                                                                                                                                                                                                                |\n| CMake                      | 3.31.5<br>4.1.2                                                                                                                                                                                                                                                                                                       |\n| Google Play services       | 49                                                                                                                                                                                                                                                                                                                    |\n| Google Repository          | 58                                                                                                                                                                                                                                                                                                                    |\n| NDK                        | 27.3.13750724 (default)<br>28.2.13676358<br>29.0.14206865                                                                                                                                                                                                                                                             |\n\n#### Environment variables\n| Name                    | Value                                               |\n| ----------------------- | --------------------------------------------------- |\n| ANDROID_HOME            | /Users/runner/Library/Android/sdk                   |\n| ANDROID_NDK             | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_HOME        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/29.0.14206865 |\n| ANDROID_NDK_ROOT        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_SDK_ROOT        | /Users/runner/Library/Android/sdk                   |\n\n### Miscellaneous\n- Tcl/Tk 8.6.17\n\n"
  },
  {
    "path": "images/macos/macos-26-Readme.md",
    "content": "| Announcements |\n|-|\n| [macOS 26 (Tahoe) is now generally available in GitHub Actions](https://github.com/actions/runner-images/issues/13739) |\n| [[macOS] The macOS 14 Sonoma based runner images will begin deprecation on July 6th and will be fully unsupported by November 2nd for GitHub Actions and Azure DevOps](https://github.com/actions/runner-images/issues/13518) |\n***\n# macOS 26\n- OS Version: macOS 26.3 (25D125)\n- Kernel Version: Darwin 25.3.0\n- Image Version: 20260303.0134.1\n\n## Installed Software\n\n### Language and Runtime\n- .NET Core SDK: 8.0.101, 8.0.204, 8.0.303, 8.0.418, 9.0.102, 9.0.203, 9.0.311, 10.0.103\n- Bash 3.2.57(1)-release\n- Clang/LLVM 17.0.0\n- Clang/LLVM (Homebrew) 20.1.8 - available on `$(brew --prefix llvm@20)/bin/clang`\n- GCC 13 (Homebrew GCC 13.4.0) - available by `gcc-13` alias\n- GCC 14 (Homebrew GCC 14.3.0) - available by `gcc-14` alias\n- GCC 15 (Homebrew GCC 15.2.0_1) - available by `gcc-15` alias\n- GNU Fortran 13 (Homebrew GCC 13.4.0) - available by `gfortran-13` alias\n- GNU Fortran 14 (Homebrew GCC 14.3.0) - available by `gfortran-14` alias\n- GNU Fortran 15 (Homebrew GCC 15.2.0_1) - available by `gfortran-15` alias\n- Kotlin 2.3.10-release-465\n- Node.js 24.14.0\n- Perl 5.42.0\n- PHP 8.5.3\n- Python3 3.14.3\n- Ruby 3.4.8\n\n### Package Management\n- Bundler 4.0.7\n- Carthage 0.40.0\n- CocoaPods 1.16.2\n- Composer 2.9.5\n- Homebrew 5.0.16\n- NPM 11.9.0\n- Pip3 26.0 (python 3.14)\n- Pipx 1.8.0\n- RubyGems 4.0.7\n- Vcpkg 2026 (build from commit 39a6cc0e44)\n- Yarn 1.22.22\n\n### Project Management\n- Apache Ant 1.10.15\n- Apache Maven 3.9.12\n- Gradle 9.3.1\n\n### Utilities\n- 7-Zip 17.05\n- aria2 1.37.0\n- azcopy 10.32.1\n- bazel 9.0.0\n- bazelisk 1.28.1\n- bsdtar 3.5.3 - available by 'tar' alias\n- Curl 8.18.0\n- Git 2.53.0\n- Git LFS 3.7.1\n- GitHub CLI 2.87.3\n- GNU Tar 1.35 - available by 'gtar' alias\n- GNU Wget 1.25.0\n- gpg (GnuPG) 2.4.9\n- jq 1.8.1\n- OpenSSL 3.6.1 27 Jan 2026 (Library: OpenSSL 3.6.1 27 Jan 2026)\n- Packer 1.15.0\n- pkgconf 2.5.1\n- Unxip 3.3\n- yq 4.52.4\n- zstd 1.5.7\n- Ninja 1.13.2\n\n### Tools\n- AWS CLI 2.34.0\n- AWS SAM CLI 1.154.0\n- AWS Session Manager CLI 1.2.779.0\n- Azure CLI 2.84.0\n- Azure CLI (azure-devops) 1.0.2\n- Bicep CLI 0.41.2\n- Cmake 4.2.3\n- CodeQL Action Bundle 2.24.2\n- Fastlane 2.232.2\n- SwiftFormat 0.59.1\n- Xcbeautify 3.1.4\n- Xcode Command Line Tools 26.3.0.0.1.1771626560\n- Xcodes 1.6.2\n\n### Linters\n- SwiftLint 0.63.2\n\n### Browsers\n- Safari 26.3 (21623.2.7.11.6)\n- SafariDriver 26.3 (21623.2.7.11.6)\n- Google Chrome 145.0.7632.117\n- Google Chrome for Testing 145.0.7632.117\n- ChromeDriver 145.0.7632.117\n- Microsoft Edge 145.0.3800.82\n- Microsoft Edge WebDriver 145.0.3800.82\n- Mozilla Firefox 148.0\n- geckodriver 0.36.0\n- Selenium server 4.41.0\n\n#### Environment variables\n| Name            | Value                                 |\n| --------------- | ------------------------------------- |\n| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-x64 |\n| EDGEWEBDRIVER   | /usr/local/share/edge_driver          |\n| GECKOWEBDRIVER  | /usr/local/opt/geckodriver/bin        |\n\n### Java\n| Version               | Environment Variable |\n| --------------------- | -------------------- |\n| 11.0.30+7             | JAVA_HOME_11_X64     |\n| 17.0.18+8             | JAVA_HOME_17_X64     |\n| 21.0.10+7.0 (default) | JAVA_HOME_21_X64     |\n| 25.0.2+10.0           | JAVA_HOME_25_X64     |\n\n### Cached Tools\n\n#### Ruby\n- 3.2.10\n- 3.3.10\n- 3.4.8\n- 4.0.1\n\n#### Python\n- 3.11.9\n- 3.12.10\n- 3.13.12\n- 3.14.3\n\n#### Node.js\n- 20.20.0\n- 22.22.0\n- 24.14.0\n\n#### Go\n- 1.23.12\n- 1.24.13\n- 1.25.7\n\n### Rust Tools\n- Cargo 1.93.1\n- Rust 1.93.1\n- Rustdoc 1.93.1\n- Rustup 1.28.2\n\n#### Packages\n- Clippy 0.1.93\n- Rustfmt 1.8.0-stable\n\n### PowerShell Tools\n- PowerShell 7.4.13\n\n#### PowerShell Modules\n- Az: 14.6.0\n- Pester: 5.7.1\n- PSScriptAnalyzer: 1.24.0\n\n### Xcode\n| Version        | Build    | Path                                | Symlinks                                                       |\n| -------------- | -------- | ----------------------------------- | -------------------------------------------------------------- |\n| 26.4 (beta)    | 17E5170d | /Applications/Xcode_26.4_beta_2.app | /Applications/Xcode_26.4.0.app<br>/Applications/Xcode_26.4.app |\n| 26.3           | 17C529   | /Applications/Xcode_26.3.app        | /Applications/Xcode_26.3.0.app                                 |\n| 26.2 (default) | 17C52    | /Applications/Xcode_26.2.app        | /Applications/Xcode_26.2.0.app<br>/Applications/Xcode.app      |\n| 26.1.1         | 17B100   | /Applications/Xcode_26.1.1.app      | /Applications/Xcode_26.1.app                                   |\n| 26.0.1         | 17A400   | /Applications/Xcode_26.0.1.app      | /Applications/Xcode_26.0.app                                   |\n\n#### Installed SDKs\n| SDK                       | SDK Name             | Xcode Version |\n| ------------------------- | -------------------- | ------------- |\n| macOS 26.0                | macosx26.0           | 26.0.1        |\n| macOS 26.1                | macosx26.1           | 26.1.1        |\n| macOS 26.2                | macosx26.2           | 26.2, 26.3    |\n| macOS 26.4                | macosx26.4           | 26.4          |\n| iOS 26.0                  | iphoneos26.0         | 26.0.1        |\n| iOS 26.1                  | iphoneos26.1         | 26.1.1        |\n| iOS 26.2                  | iphoneos26.2         | 26.2, 26.3    |\n| iOS 26.4                  | iphoneos26.4         | 26.4          |\n| Simulator - iOS 26.0      | iphonesimulator26.0  | 26.0.1        |\n| Simulator - iOS 26.1      | iphonesimulator26.1  | 26.1.1        |\n| Simulator - iOS 26.2      | iphonesimulator26.2  | 26.2, 26.3    |\n| Simulator - iOS 26.4      | iphonesimulator26.4  | 26.4          |\n| tvOS 26.0                 | appletvos26.0        | 26.0.1        |\n| tvOS 26.1                 | appletvos26.1        | 26.1.1        |\n| tvOS 26.2                 | appletvos26.2        | 26.2, 26.3    |\n| tvOS 26.4                 | appletvos26.4        | 26.4          |\n| Simulator - tvOS 26.0     | appletvsimulator26.0 | 26.0.1        |\n| Simulator - tvOS 26.1     | appletvsimulator26.1 | 26.1.1        |\n| Simulator - tvOS 26.2     | appletvsimulator26.2 | 26.2, 26.3    |\n| Simulator - tvOS 26.4     | appletvsimulator26.4 | 26.4          |\n| watchOS 26.0              | watchos26.0          | 26.0.1        |\n| watchOS 26.1              | watchos26.1          | 26.1.1        |\n| watchOS 26.2              | watchos26.2          | 26.2, 26.3    |\n| watchOS 26.4              | watchos26.4          | 26.4          |\n| Simulator - watchOS 26.0  | watchsimulator26.0   | 26.0.1        |\n| Simulator - watchOS 26.1  | watchsimulator26.1   | 26.1.1        |\n| Simulator - watchOS 26.2  | watchsimulator26.2   | 26.2, 26.3    |\n| Simulator - watchOS 26.4  | watchsimulator26.4   | 26.4          |\n| visionOS 26.0             | xros26.0             | 26.0.1        |\n| visionOS 26.1             | xros26.1             | 26.1.1        |\n| visionOS 26.2             | xros26.2             | 26.2, 26.3    |\n| visionOS 26.4             | xros26.4             | 26.4          |\n| Simulator - visionOS 26.0 | xrsimulator26.0      | 26.0.1        |\n| Simulator - visionOS 26.1 | xrsimulator26.1      | 26.1.1        |\n| Simulator - visionOS 26.2 | xrsimulator26.2      | 26.2, 26.3    |\n| Simulator - visionOS 26.4 | xrsimulator26.4      | 26.4          |\n| DriverKit 25.0            | driverkit25.0        | 26.0.1        |\n| DriverKit 25.1            | driverkit25.1        | 26.1.1        |\n| DriverKit 25.2            | driverkit25.2        | 26.2, 26.3    |\n| DriverKit 25.4            | driverkit25.4        | 26.4          |\n\n#### Installed Simulators\n| Name         | OS     | Simulators                                                                                                                                                                                                                                                             |\n| ------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| iOS 26.0     | 26.0.1 | iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPad (A16)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M4)<br>iPad Pro 13-inch (M5) |\n| iOS 26.1     | 26.1   | iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPad (A16)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M5)                                                   |\n| iOS 26.2     | 26.2   | iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPad (A16)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M5)                                                   |\n| tvOS 26.0    | 26.0   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                    |\n| tvOS 26.1    | 26.1   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                    |\n| tvOS 26.2    | 26.2   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                    |\n| watchOS 26.0 | 26.0   | Apple Watch SE 3 (40mm)<br>Apple Watch SE 3 (44mm)<br>Apple Watch Series 11 (42mm)<br>Apple Watch Series 11 (46mm)<br>Apple Watch Ultra 3 (49mm)                                                                                                                       |\n| watchOS 26.1 | 26.1   | Apple Watch SE 3 (40mm)<br>Apple Watch SE 3 (44mm)<br>Apple Watch Series 11 (42mm)<br>Apple Watch Series 11 (46mm)<br>Apple Watch Ultra 3 (49mm)                                                                                                                       |\n| watchOS 26.2 | 26.2   | Apple Watch SE 3 (40mm)<br>Apple Watch SE 3 (44mm)<br>Apple Watch Series 11 (42mm)<br>Apple Watch Series 11 (46mm)<br>Apple Watch Ultra 3 (49mm)                                                                                                                       |\n\n### Android\n| Package Name               | Version                                                                                                                                                                          |\n| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Android Command Line Tools | 16.0                                                                                                                                                                             |\n| Android Emulator           | 36.4.9                                                                                                                                                                           |\n| Android SDK Build-tools    | 36.0.0 36.1.0<br>35.0.0 35.0.1                                                                                                                                                   |\n| Android SDK Platforms      | android-36.1 (rev 1)<br>android-36-ext19 (rev 1)<br>android-36-ext18 (rev 1)<br>android-36 (rev 2)<br>android-35-ext15 (rev 1)<br>android-35-ext14 (rev 1)<br>android-35 (rev 2) |\n| Android SDK Platform-Tools | 37.0.0                                                                                                                                                                           |\n| Android Support Repository | 47.0.0                                                                                                                                                                           |\n| CMake                      | 3.31.5<br>4.1.2                                                                                                                                                                  |\n| Google Play services       | 49                                                                                                                                                                               |\n| Google Repository          | 58                                                                                                                                                                               |\n| NDK                        | 27.3.13750724 (default)<br>28.2.13676358<br>29.0.14206865                                                                                                                        |\n\n#### Environment variables\n| Name                    | Value                                               |\n| ----------------------- | --------------------------------------------------- |\n| ANDROID_HOME            | /Users/runner/Library/Android/sdk                   |\n| ANDROID_NDK             | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_HOME        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/29.0.14206865 |\n| ANDROID_NDK_ROOT        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_SDK_ROOT        | /Users/runner/Library/Android/sdk                   |\n\n### Miscellaneous\n- Tcl/Tk 8.6.17\n\n"
  },
  {
    "path": "images/macos/macos-26-arm64-Readme.md",
    "content": "| Announcements |\n|-|\n| [macOS 26 (Tahoe) is now generally available in GitHub Actions](https://github.com/actions/runner-images/issues/13739) |\n| [[macOS] The macOS 14 Sonoma based runner images will begin deprecation on July 6th and will be fully unsupported by November 2nd for GitHub Actions and Azure DevOps](https://github.com/actions/runner-images/issues/13518) |\n***\n# macOS 26\n- OS Version: macOS 26.3 (25D125)\n- Kernel Version: Darwin 25.3.0\n- Image Version: 20260303.0251.1\n\n## Installed Software\n\n### Language and Runtime\n- .NET Core SDK: 8.0.101, 8.0.204, 8.0.303, 8.0.418, 9.0.102, 9.0.203, 9.0.311, 10.0.103\n- Bash 3.2.57(1)-release\n- Clang/LLVM 17.0.0\n- Clang/LLVM (Homebrew) 20.1.8 - available on `$(brew --prefix llvm@20)/bin/clang`\n- GCC 13 (Homebrew GCC 13.4.0) - available by `gcc-13` alias\n- GCC 14 (Homebrew GCC 14.3.0) - available by `gcc-14` alias\n- GCC 15 (Homebrew GCC 15.2.0_1) - available by `gcc-15` alias\n- GNU Fortran 13 (Homebrew GCC 13.4.0) - available by `gfortran-13` alias\n- GNU Fortran 14 (Homebrew GCC 14.3.0) - available by `gfortran-14` alias\n- GNU Fortran 15 (Homebrew GCC 15.2.0_1) - available by `gfortran-15` alias\n- Kotlin 2.3.10-release-465\n- Node.js 24.14.0\n- Perl 5.42.0\n- Python3 3.14.3\n- Ruby 3.4.8\n\n### Package Management\n- Bundler 4.0.7\n- Carthage 0.40.0\n- CocoaPods 1.16.2\n- Homebrew 5.0.16\n- NPM 11.9.0\n- Pip3 26.0 (python 3.14)\n- Pipx 1.8.0\n- RubyGems 4.0.7\n- Vcpkg 2026 (build from commit 39a6cc0e44)\n- Yarn 1.22.22\n\n### Project Management\n- Apache Ant 1.10.15\n- Apache Maven 3.9.12\n- Gradle 9.3.1\n\n### Utilities\n- 7-Zip 17.05\n- aria2 1.37.0\n- azcopy 10.32.1\n- bazel 9.0.0\n- bazelisk 1.28.1\n- bsdtar 3.5.3 - available by 'tar' alias\n- Curl 8.7.1\n- Git 2.53.0\n- Git LFS 3.7.1\n- GitHub CLI 2.87.3\n- GNU Tar 1.35 - available by 'gtar' alias\n- GNU Wget 1.25.0\n- gpg (GnuPG) 2.4.9\n- jq 1.8.1\n- OpenSSL 3.6.1 27 Jan 2026 (Library: OpenSSL 3.6.1 27 Jan 2026)\n- Packer 1.15.0\n- pkgconf 2.5.1\n- Unxip 3.3\n- yq 4.52.4\n- zstd 1.5.7\n- Ninja 1.13.2\n\n### Tools\n- AWS CLI 2.34.0\n- AWS SAM CLI 1.154.0\n- AWS Session Manager CLI 1.2.779.0\n- Azure CLI 2.84.0\n- Azure CLI (azure-devops) 1.0.2\n- Bicep CLI 0.41.2\n- Cmake 4.2.3\n- CodeQL Action Bundle 2.24.2\n- Fastlane 2.232.2\n- SwiftFormat 0.59.1\n- Xcbeautify 3.1.4\n- Xcode Command Line Tools 26.3.0.0.1.1771626560\n- Xcodes 1.6.2\n\n### Browsers\n- Safari 26.3 (21623.2.7.11.6)\n- SafariDriver 26.3 (21623.2.7.11.6)\n- Google Chrome 145.0.7632.117\n- Google Chrome for Testing 145.0.7632.117\n- ChromeDriver 145.0.7632.117\n- Microsoft Edge 145.0.3800.82\n- Microsoft Edge WebDriver 145.0.3800.82\n- Mozilla Firefox 148.0\n- geckodriver 0.36.0\n- Selenium server 4.41.0\n\n#### Environment variables\n| Name            | Value                                   |\n| --------------- | --------------------------------------- |\n| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-arm64 |\n| EDGEWEBDRIVER   | /usr/local/share/edge_driver            |\n| GECKOWEBDRIVER  | /opt/homebrew/opt/geckodriver/bin       |\n\n### Java\n| Version               | Environment Variable |\n| --------------------- | -------------------- |\n| 11.0.30+7             | JAVA_HOME_11_arm64   |\n| 17.0.18+8             | JAVA_HOME_17_arm64   |\n| 21.0.10+7.0 (default) | JAVA_HOME_21_arm64   |\n| 25.0.2+10.0           | JAVA_HOME_25_arm64   |\n\n### Cached Tools\n\n#### Ruby\n- 3.2.10\n- 3.3.10\n- 3.4.8\n- 4.0.1\n\n#### Python\n- 3.11.9\n- 3.12.10\n- 3.13.12\n- 3.14.3\n\n#### Node.js\n- 20.20.0\n- 22.22.0\n- 24.14.0\n\n#### Go\n- 1.23.12\n- 1.24.13\n- 1.25.7\n\n### Rust Tools\n- Cargo 1.93.1\n- Rust 1.93.1\n- Rustdoc 1.93.1\n- Rustup 1.28.2\n\n#### Packages\n- Clippy 0.1.93\n- Rustfmt 1.8.0-stable\n\n### PowerShell Tools\n- PowerShell 7.4.13\n\n#### PowerShell Modules\n- Az: 14.6.0\n- Pester: 5.7.1\n- PSScriptAnalyzer: 1.24.0\n\n### Xcode\n| Version        | Build    | Path                                | Symlinks                                                       |\n| -------------- | -------- | ----------------------------------- | -------------------------------------------------------------- |\n| 26.4 (beta)    | 17E5170d | /Applications/Xcode_26.4_beta_2.app | /Applications/Xcode_26.4.0.app<br>/Applications/Xcode_26.4.app |\n| 26.3           | 17C529   | /Applications/Xcode_26.3.app        | /Applications/Xcode_26.3.0.app                                 |\n| 26.2 (default) | 17C52    | /Applications/Xcode_26.2.app        | /Applications/Xcode_26.2.0.app<br>/Applications/Xcode.app      |\n| 26.1.1         | 17B100   | /Applications/Xcode_26.1.1.app      | /Applications/Xcode_26.1.app                                   |\n| 26.0.1         | 17A400   | /Applications/Xcode_26.0.1.app      | /Applications/Xcode_26.0.app                                   |\n\n#### Installed SDKs\n| SDK                       | SDK Name             | Xcode Version |\n| ------------------------- | -------------------- | ------------- |\n| macOS 26.0                | macosx26.0           | 26.0.1        |\n| macOS 26.1                | macosx26.1           | 26.1.1        |\n| macOS 26.2                | macosx26.2           | 26.2, 26.3    |\n| macOS 26.4                | macosx26.4           | 26.4          |\n| iOS 26.0                  | iphoneos26.0         | 26.0.1        |\n| iOS 26.1                  | iphoneos26.1         | 26.1.1        |\n| iOS 26.2                  | iphoneos26.2         | 26.2, 26.3    |\n| iOS 26.4                  | iphoneos26.4         | 26.4          |\n| Simulator - iOS 26.0      | iphonesimulator26.0  | 26.0.1        |\n| Simulator - iOS 26.1      | iphonesimulator26.1  | 26.1.1        |\n| Simulator - iOS 26.2      | iphonesimulator26.2  | 26.2, 26.3    |\n| Simulator - iOS 26.4      | iphonesimulator26.4  | 26.4          |\n| tvOS 26.0                 | appletvos26.0        | 26.0.1        |\n| tvOS 26.1                 | appletvos26.1        | 26.1.1        |\n| tvOS 26.2                 | appletvos26.2        | 26.2, 26.3    |\n| tvOS 26.4                 | appletvos26.4        | 26.4          |\n| Simulator - tvOS 26.0     | appletvsimulator26.0 | 26.0.1        |\n| Simulator - tvOS 26.1     | appletvsimulator26.1 | 26.1.1        |\n| Simulator - tvOS 26.2     | appletvsimulator26.2 | 26.2, 26.3    |\n| Simulator - tvOS 26.4     | appletvsimulator26.4 | 26.4          |\n| watchOS 26.0              | watchos26.0          | 26.0.1        |\n| watchOS 26.1              | watchos26.1          | 26.1.1        |\n| watchOS 26.2              | watchos26.2          | 26.2, 26.3    |\n| watchOS 26.4              | watchos26.4          | 26.4          |\n| Simulator - watchOS 26.0  | watchsimulator26.0   | 26.0.1        |\n| Simulator - watchOS 26.1  | watchsimulator26.1   | 26.1.1        |\n| Simulator - watchOS 26.2  | watchsimulator26.2   | 26.2, 26.3    |\n| Simulator - watchOS 26.4  | watchsimulator26.4   | 26.4          |\n| visionOS 26.0             | xros26.0             | 26.0.1        |\n| visionOS 26.1             | xros26.1             | 26.1.1        |\n| visionOS 26.2             | xros26.2             | 26.2, 26.3    |\n| visionOS 26.4             | xros26.4             | 26.4          |\n| Simulator - visionOS 26.0 | xrsimulator26.0      | 26.0.1        |\n| Simulator - visionOS 26.1 | xrsimulator26.1      | 26.1.1        |\n| Simulator - visionOS 26.2 | xrsimulator26.2      | 26.2, 26.3    |\n| Simulator - visionOS 26.4 | xrsimulator26.4      | 26.4          |\n| DriverKit 25.0            | driverkit25.0        | 26.0.1        |\n| DriverKit 25.1            | driverkit25.1        | 26.1.1        |\n| DriverKit 25.2            | driverkit25.2        | 26.2, 26.3    |\n| DriverKit 25.4            | driverkit25.4        | 26.4          |\n\n#### Installed Simulators\n| Name          | OS     | Simulators                                                                                                                                                                                                                                                             |\n| ------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| iOS 26.0      | 26.0.1 | iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPad (A16)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M4)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M4)<br>iPad Pro 13-inch (M5) |\n| iOS 26.1      | 26.1   | iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPad (A16)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M5)                                                   |\n| iOS 26.2      | 26.2   | iPhone 16e<br>iPhone 17<br>iPhone 17 Pro<br>iPhone 17 Pro Max<br>iPhone Air<br>iPad (A16)<br>iPad Air 11-inch (M3)<br>iPad Air 13-inch (M3)<br>iPad mini (A17 Pro)<br>iPad Pro 11-inch (M5)<br>iPad Pro 13-inch (M5)                                                   |\n| tvOS 26.0     | 26.0   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                    |\n| tvOS 26.1     | 26.1   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                    |\n| tvOS 26.2     | 26.2   | Apple TV<br>Apple TV 4K (3rd generation)<br>Apple TV 4K (3rd generation) (at 1080p)                                                                                                                                                                                    |\n| watchOS 26.0  | 26.0   | Apple Watch SE 3 (40mm)<br>Apple Watch SE 3 (44mm)<br>Apple Watch Series 11 (42mm)<br>Apple Watch Series 11 (46mm)<br>Apple Watch Ultra 3 (49mm)                                                                                                                       |\n| watchOS 26.1  | 26.1   | Apple Watch SE 3 (40mm)<br>Apple Watch SE 3 (44mm)<br>Apple Watch Series 11 (42mm)<br>Apple Watch Series 11 (46mm)<br>Apple Watch Ultra 3 (49mm)                                                                                                                       |\n| watchOS 26.2  | 26.2   | Apple Watch SE 3 (40mm)<br>Apple Watch SE 3 (44mm)<br>Apple Watch Series 11 (42mm)<br>Apple Watch Series 11 (46mm)<br>Apple Watch Ultra 3 (49mm)                                                                                                                       |\n| visionOS 26.0 | 26.0   | Apple Vision Pro                                                                                                                                                                                                                                                       |\n| visionOS 26.1 | 26.1   | Apple Vision Pro                                                                                                                                                                                                                                                       |\n| visionOS 26.2 | 26.2   | Apple Vision Pro                                                                                                                                                                                                                                                       |\n\n### Android\n| Package Name               | Version                                                                                                                                                                          |\n| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Android Command Line Tools | 16.0                                                                                                                                                                             |\n| Android Emulator           | 36.4.9                                                                                                                                                                           |\n| Android SDK Build-tools    | 36.0.0 36.1.0<br>35.0.0 35.0.1                                                                                                                                                   |\n| Android SDK Platforms      | android-36.1 (rev 1)<br>android-36-ext19 (rev 1)<br>android-36-ext18 (rev 1)<br>android-36 (rev 2)<br>android-35-ext15 (rev 1)<br>android-35-ext14 (rev 1)<br>android-35 (rev 2) |\n| Android SDK Platform-Tools | 37.0.0                                                                                                                                                                           |\n| Android Support Repository | 47.0.0                                                                                                                                                                           |\n| CMake                      | 3.31.5<br>4.1.2                                                                                                                                                                  |\n| Google Play services       | 49                                                                                                                                                                               |\n| Google Repository          | 58                                                                                                                                                                               |\n| NDK                        | 27.3.13750724 (default)<br>28.2.13676358<br>29.0.14206865                                                                                                                        |\n\n#### Environment variables\n| Name                    | Value                                               |\n| ----------------------- | --------------------------------------------------- |\n| ANDROID_HOME            | /Users/runner/Library/Android/sdk                   |\n| ANDROID_NDK             | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_HOME        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/29.0.14206865 |\n| ANDROID_NDK_ROOT        | /Users/runner/Library/Android/sdk/ndk/27.3.13750724 |\n| ANDROID_SDK_ROOT        | /Users/runner/Library/Android/sdk                   |\n\n### Miscellaneous\n- Tcl/Tk 8.6.17\n\n"
  },
  {
    "path": "images/macos/scripts/build/Configure-Toolset.ps1",
    "content": "################################################################################\n##  File:  Configure-Toolset.ps1\n##  Team:  CI-Build\n##  Desc:  Configure toolset\n################################################################################\n\nImport-Module \"~/image-generation/helpers/Common.Helpers.psm1\"\n\nfunction Get-ToolsetToolFullPath {\n    param\n    (\n        [Parameter(Mandatory)] [string] $ToolName,\n        [Parameter(Mandatory)] [string] $ToolVersion,\n        [Parameter(Mandatory)] [string] $ToolArchitecture\n    )\n\n    $toolPath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath $toolName\n    $toolPathVersion = Join-Path -Path $toolPath -ChildPath $toolVersion\n    $foundVersion = Get-Item $toolPathVersion | Sort-Object -Property {[version]$_.name} -Descending | Select-Object -First 1\n    $installationDir = Join-Path -Path $foundVersion -ChildPath $toolArchitecture\n    return $installationDir\n}\n\n$arch = Get-Architecture\n$toolcache = (Get-ToolsetContent).toolcache\n\nforeach ($tool in $toolcache) {\n    $toolName = $tool.name\n    $toolEnvironment = $tool.arch.$arch.variable_template\n\n    if (-not $toolEnvironment) {\n        continue\n    }\n\n    foreach ($toolVersion in $tool.arch.$arch.versions) {\n        Write-Host \"Set $toolName $toolVersion environment variable...\"\n        $toolPath = Get-ToolsetToolFullPath -ToolName $toolName -ToolVersion $toolVersion -ToolArchitecture $arch\n        $envName = $toolEnvironment -f $toolVersion.split(\".\")\n\n        # Add environment variable name=value\n        $envVar = \"export {0}={1}\" -f $envName, $toolPath\n        Add-Content -Path \"${env:HOME}/.bashrc\" -Value $envVar\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/build/Configure-Xcode-Simulators.ps1",
    "content": "################################################################################\n##  File:  Configure-Xcode-Simulators.ps1\n##  Team:  CI-Build\n##  Desc:  CHeck and remove duplicate simulators\n################################################################################\n\nImport-Module \"~/image-generation/helpers/Common.Helpers.psm1\"\nImport-Module \"~/image-generation/helpers/Xcode.Helpers.psm1\"\n$arch = Get-Architecture\n$xcodeVersions = (Get-ToolsetContent).xcode.${arch}.versions\n$defaultXcode = (Get-ToolsetContent).xcode.default\n\n# Switch to each Xcode version\nforeach ($xcodeVersion in $xcodeVersions.link) {\n    Write-Host \"Switching to Xcode $xcodeVersion\"\n    Switch-Xcode -Version $XcodeVersion\n\n    # Make object of all simulators\n    $devicesList = $(xcrun simctl list -j devices | ConvertFrom-Json)\n    $devicesObject = [System.Collections.ArrayList]@()\n    foreach ($runtime in $devicesList.devices.psobject.Properties.name) {\n        foreach ($device in $devicesList.devices.$runtime) {\n            $devicesObject += [PSCustomObject]@{\n                runtime = $runtime\n                DeviceName = $($device.name)\n                DeviceId = $($device.udid)\n                DeviceCreationTime = (Get-Item $HOME/Library/Developer/CoreSimulator/Devices/$($device.udid)).CreationTime\n            }\n        }\n    }\n\n    # Remove duplicates\n    foreach ($simRuntume in $devicesObject.runtime | Sort-Object -Unique) {\n        [System.Collections.ArrayList]$sameRuntimeDevices = [array]$($devicesObject | Where-Object {$_.runtime -eq $simRuntume} | Sort-Object -Property DeviceName)\n        Write-Host \"///////////////////////////////////////////////////////////////////\"\n        Write-Host \"// Checking for duplicates in $simRuntume \"\n        $devicesAsHashTable =  $sameRuntimeDevices | Group-Object -Property DeviceName -AsHashTable -AsString\n        foreach ($key in $devicesAsHashTable.Keys) {\n            if ( $devicesAsHashTable[$key].count -gt 1) {\n                Write-Host \"// Duplicates for $key - $($devicesAsHashTable[$key].count)\"\n            }\n        }\n        Write-Host \"///////////////////////////////////////////////////////////////////\"\n        for ($i = 0; $i -lt $sameRuntimeDevices.Count; $i++) {\n            if ( [string]::IsNullOrEmpty($($sameRuntimeDevices[$i+1].DeviceName)) ){\n                Write-Host \"No more devices to compare in $simRuntume\"\n                Write-Host \"-------------------------------------------------------------------\"\n                continue\n            }\n            Write-Host \"$($sameRuntimeDevices[$i].DeviceName) - DeviceId $($sameRuntimeDevices[$i].DeviceId) comparing with\"\n            Write-Host \"$($sameRuntimeDevices[$i+1].DeviceName) - DeviceId $($sameRuntimeDevices[$i+1].DeviceId)\"\n            Write-Host \"-------------------------------------------------------------------\"\n            if ($sameRuntimeDevices[$i].DeviceName -eq $sameRuntimeDevices[$i+1].DeviceName) {\n                Write-Host \"*******************************************************************\"\n                Write-Host \"** Duplicate found\"\n                if ($sameRuntimeDevices[$i].DeviceCreationTime -lt $sameRuntimeDevices[$i+1].DeviceCreationTime) {\n                    Write-Host \"** will be removed $($sameRuntimeDevices[$i+1].DeviceName) with id $($sameRuntimeDevices[$i+1].DeviceId)\"\n                    xcrun simctl delete $sameRuntimeDevices[$i+1].DeviceId\n                    $sameRuntimeDevices.RemoveAt($i+1)\n                } else {\n                    Write-Host \"** will be removed $($sameRuntimeDevices[$i].DeviceName) with id $($sameRuntimeDevices[$i].DeviceId)\"\n                    xcrun simctl delete $sameRuntimeDevices[$i].DeviceId\n                    $sameRuntimeDevices.RemoveAt($i)\n                }\n                Write-Host \"*******************************************************************\"\n            }\n        }\n    }\n}\n\n# Restore default Xcode\nWrite-Host \"Restoring default Xcode to $defaultXcode\"\nSwitch-Xcode -Version $defaultXcode\n"
  },
  {
    "path": "images/macos/scripts/build/Install-Toolset.ps1",
    "content": "################################################################################\n##  File:  Install-Toolset.ps1\n##  Team:  CI-Build\n##  Desc:  Install toolset\n################################################################################\n\nImport-Module \"~/image-generation/tests/Helpers.psm1\"\nImport-Module \"~/image-generation/helpers/Common.Helpers.psm1\"\n\nFunction Install-Asset {\n    param(\n        [Parameter(Mandatory=$true)]\n        [object] $ReleaseAsset\n    )\n\n    Write-Host \"Download $($ReleaseAsset.filename) archive...\"\n    $assetArchivePath = Invoke-DownloadWithRetry $ReleaseAsset.download_url\n\n    Write-Host \"Extract $($ReleaseAsset.filename) content...\"\n    $assetFolderPath = Join-Path \"/tmp\" \"$($ReleaseAsset.filename)-temp-dir\"\n    New-Item -ItemType Directory -Path $assetFolderPath | Out-Null\n    tar -xzf $assetArchivePath -C $assetFolderPath\n\n    Write-Host \"Invoke installation script...\"\n    Push-Location -Path $assetFolderPath\n    Invoke-Expression \"bash ./setup.sh\"\n    Pop-Location\n}\n\n$arch = Get-Architecture\n\n# Get toolcache content from toolset\n$toolsToInstall = @(\"Python\", \"Node\", \"Go\")\n$tools = (Get-ToolsetContent).toolcache | Where-Object {$toolsToInstall -contains $_.Name}\n\nforeach ($tool in $tools) {\n    # Get versions manifest for current tool\n    $assets = Invoke-RestMethod $tool.url -MaximumRetryCount 10 -RetryIntervalSec 30\n\n    # Get github release asset for each version\n    foreach ($version in $tool.arch.$arch.versions) {\n        $asset = $assets | Where-Object version -like $version `\n                         | Select-Object -ExpandProperty files `\n                         | Where-Object { ($_.platform -eq $tool.platform) -and ($_.arch -eq $arch)} `\n                         | Select-Object -First 1\n\n        Write-Host \"Installing $($tool.name) $version...\"\n        if ($null -ne $asset) {\n            Install-Asset -ReleaseAsset $asset\n        } else {\n            Write-Host \"Asset was not found in versions manifest\"\n            exit 1\n        }\n    }\n}\n\n\n# Ensure python3 and pip3 point to the latest installed Python version\n# Fix for ./setup.sh script behavior for python3 and pip3 symlinks\n# Only Intel images are affected since /usr/local/bin is used for Intel\n# ARM images use /opt/homebrew/bin which is managed by Homebrew\nWrite-Host \"Ensuring python3 and pip3 point to the latest installed Python version from Homebrew\"\nbrew unlink python@3.14 && brew link python@3.14 --force --overwrite\nSplit-Path (readlink (which python3)) \n\nInvoke-PesterTests \"Toolcache\"\n"
  },
  {
    "path": "images/macos/scripts/build/Install-Xcode.ps1",
    "content": "################################################################################\n##  File:  Install-Xcode.ps1\n##  Desc:  Install Xcode\n################################################################################\n\n$ErrorActionPreference = \"Stop\"\n\nImport-Module \"$env:HOME/image-generation/helpers/Common.Helpers.psm1\"\nImport-Module \"$env:HOME/image-generation/helpers/Xcode.Installer.psm1\" -DisableNameChecking\n\n$os = Get-OSVersion\n$arch = Get-Architecture\n[Array]$xcodeVersions = (Get-ToolsetContent).xcode.$arch.versions\nWrite-Host $xcodeVersions\n$defaultXcode = (Get-ToolsetContent).xcode.default\n[Array]::Reverse($xcodeVersions)\n$threadCount = \"5\"\n\nWrite-Host \"Installing Xcode versions...\"\n$xcodeVersions | ForEach-Object -ThrottleLimit $threadCount -Parallel {\n    $ErrorActionPreference = \"Stop\"\n    Import-Module \"$env:HOME/image-generation/helpers/Common.Helpers.psm1\"\n    Import-Module \"$env:HOME/image-generation/helpers/Xcode.Installer.psm1\" -DisableNameChecking\n\n    Install-XcodeVersion -Version $_.version -LinkTo $_.link -Sha256Sum $_.sha256\n    Confirm-XcodeIntegrity -Version $_.link\n}\n\n$xcodeVersions | ForEach-Object {\n    Approve-XcodeLicense -Version $_.link\n}\n\nWrite-Host \"Configuring Xcode versions...\"\n$xcodeVersions | ForEach-Object {\n    Write-Host \"Configuring Xcode $($_.link) ...\"\n    Invoke-XcodeRunFirstLaunch -Version $_.link\n    Install-XcodeAdditionalSimulatorRuntimes -Version $_.link -Arch $arch -Runtimes $_.install_runtimes\n    if ($_.link -match '^(\\d+)\\.(\\d+)(?:\\.(\\d+))?$' -and [int]$matches[1] -ge 26) {\n        Install-XcodeAdditionalComponents -Version $_.link\n        Update-DyldCache -Version $_.link\n    }\n}\n\nInvoke-XcodeRunFirstLaunch -Version $defaultXcode\n\nWrite-Host \"Configuring Xcode symlinks...\"\n$xcodeVersions | ForEach-Object {\n    Build-XcodeSymlinks -Version $_.link -Symlinks $_.symlinks\n\n    # Skip creating symlink to install multiple releases of the same Xcode version side-by-side\n    if ($_.\"skip-symlink\" -ne \"true\") {\n        Build-ProvisionatorSymlink -Version $_.link\n    }\n}\n\nWrite-Host \"Rebuilding Launch Services database ...\"\n$xcodeVersions | ForEach-Object {\n    Initialize-XcodeLaunchServicesDb -Version $_.link\n}\n\nWrite-Host \"Setting default Xcode to $defaultXcode\"\nSwitch-Xcode -Version $defaultXcode\nNew-Item -Path \"/Applications/Xcode.app\" -ItemType SymbolicLink -Value (Get-XcodeRootPath -Version $defaultXcode) | Out-Null\n\nWrite-Host \"Setting environment variables 'XCODE_<VERSION>_DEVELOPER_DIR'\"\nSet-XcodeDeveloperDirEnvironmentVariables -XcodeList $xcodeVersions.link\n"
  },
  {
    "path": "images/macos/scripts/build/Update-XcodeSimulators.ps1",
    "content": "################################################################################\n##  File:  Update-XcodeSimulators.ps1\n##  Desc:  Check available Xcode simulators and create missing ones\n################################################################################\n\n$ErrorActionPreference = \"Stop\"\n\nImport-Module \"$env:HOME/image-generation/helpers/Xcode.Helpers.psm1\" -DisableNameChecking\nImport-Module \"$env:HOME/image-generation/software-report/SoftwareReport.Xcode.psm1\" -DisableNameChecking\n\nfunction Test-SimulatorInstalled {\n    param(\n        [Parameter(Mandatory)]\n        [string] $RuntimeId,\n        [Parameter(Mandatory)]\n        [string] $DeviceId,\n        [Parameter(Mandatory)]\n        [string] $SimulatorName,\n        [Parameter(Mandatory)]\n        [string] $XcodeVersion\n    )\n\n    $simctlPath = Get-XcodeToolPath -Version $XcodeVersion -ToolName \"simctl\"\n    if (-not (Test-Path $simctlPath)) {\n        Write-Host \"Skip validating simulator '$SimulatorName [$RuntimeId]' because Xcode $XcodeVersion is not installed\"\n        return\n    }\n\n    $simulatorFullNameDebug = \"$SimulatorName [$RuntimeId]\"\n    Write-Host \"Checking Xcode simulator '$simulatorFullNameDebug' (Xcode $XcodeVersion)...\"\n\n    # Get all available devices\n    [string]$rawDevicesInfo = Invoke-Expression \"$simctlPath list devices --json\"\n    $jsonDevicesInfo = ($rawDevicesInfo | ConvertFrom-Json).devices\n\n    # Checking if simulator already exists\n    $existingSimulator = $jsonDevicesInfo.$RuntimeId | Where-Object { $_.deviceTypeIdentifier -eq  $DeviceId } | Select-Object -First 1\n\n    if ($null -eq $existingSimulator) {\n        Write-Host \"Simulator '$simulatorFullNameDebug' is missed. Creating it...\"\n        Invoke-Expression \"$simctlPath create '$SimulatorName' '$DeviceId' '$RuntimeId'\"\n    } elseif ($existingSimulator.name -ne $SimulatorName) {\n        Write-Host \"Simulator '$simulatorFullNameDebug' is named incorrectly. Renaming it from '$($existingSimulator.name)' to '$SimulatorName'...\"\n        Invoke-Expression \"$simctlPath rename '$($existingSimulator.udid)' '$SimulatorName'\"\n    } else {\n        Write-Host \"Simulator '$simulatorFullNameDebug' is installed correctly.\"\n    }\n}\n\n# First run doesn't provide full data about devices\nGet-XcodeInfoList | Out-Null\n"
  },
  {
    "path": "images/macos/scripts/build/configure-auto-updates.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  configure-auto-updates.sh\n##  Desc:  Disabling automatic updates\n################################################################################\n\nsudo softwareupdate --schedule off\ndefaults write com.apple.SoftwareUpdate AutomaticDownload -int 0\ndefaults write com.apple.SoftwareUpdate CriticalUpdateInstall -int 0\ndefaults write com.apple.commerce AutoUpdate -bool false\ndefaults write com.apple.SoftwareUpdate AutomaticCheckEnabled -bool false\n"
  },
  {
    "path": "images/macos/scripts/build/configure-autologin.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:       configure-autologin.sh\n##  Desc:       add a Daemon to re-detect the attached network interfaces after vm is booted.\n##  Maintainer: @timsutton\n##              script was taken from https://github.com/timsutton/osx-vm-templates/blob/master/scripts/autologin.sh\n################################################################################\n\necho \"Enabling automatic GUI login for the '$USERNAME' user..\"\npython3 $HOME/bootstrap/kcpassword.py \"$PASSWORD\"\n/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser \"$USERNAME\"\n/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUserScreenLocked -bool false\n\n: '\nThe MIT License (MIT)\nCopyright (c) 2013-2017 Timothy Sutton\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n'\n"
  },
  {
    "path": "images/macos/scripts/build/configure-hostname.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  configure-hostname.sh\n##  Desc:  Change the hostname at startup to prevent duplicates\n##         Hostname and Computername should contain .local in name to avoid name resolution issues\n################################################################################\n\ntee -a /usr/local/bin/change_hostname.sh > /dev/null <<\\EOF\n#!/bin/bash -e -o pipefail\n\nname=\"Mac-$(python3 -c 'from time import time; print(int(round(time() * 1000)))')\"\nscutil --set HostName \"${name}.local\"\nscutil --set LocalHostName $name\nscutil --set ComputerName \"${name}.local\"\nEOF\n\nchmod +x /usr/local/bin/change_hostname.sh\n\nsudo tee -a /Library/LaunchDaemons/change_hostname.plist > /dev/null <<\\EOF\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"https://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n        <dict>\n                <key>Label</key>\n                <string>change-hostname</string>\n                <key>Program</key>\n                <string>/usr/local/bin/change_hostname.sh</string>\n                <key>RunAtLoad</key>\n                <true/>\n        </dict>\n</plist>\nEOF\n"
  },
  {
    "path": "images/macos/scripts/build/configure-machine.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  configure-machine.sh\n##  Desc:  Configure guest OS settings\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Enabling developer mode...\"\nsudo /usr/sbin/DevToolsSecurity --enable\n\n# Turn off hibernation and get rid of the sleepimage\nsudo pmset -a hibernatemode 0\nsudo rm -f /var/vm/sleepimage\n\n# Set computer, disk, and display sleep to never\nsudo pmset -a sleep 0 disksleep 0 displaysleep 0\n\n# Disable App Nap System Wide\ndefaults write NSGlobalDomain NSAppSleepDisabled -bool YES\n\n# Disable Keyboard Setup Assistant window\nsudo defaults write /Library/Preferences/com.apple.keyboardtype \"keyboardtype\" -dict-add \"3-7582-0\" -int 40\n\n# Update VoiceOver Utility to allow VoiceOver to be controlled with AppleScript\n# by creating a special Accessibility DB file (SIP must be disabled) and\n# updating the user defaults system to reflect this change.\nif csrutil status | grep -Eq  \"System Integrity Protection status: (disabled|unknown)\"; then\n    sudo bash -c 'echo -n \"a\" > /private/var/db/Accessibility/.VoiceOverAppleScriptEnabled'\nfi\ndefaults write com.apple.VoiceOver4/default SCREnableAppleScript -bool YES\n\n# https://developer.apple.com/support/expiration/\n# Enterprise iOS Distribution Certificates generated between February 7 and September 1st, 2020 will expire on February 7, 2023.\n# Rotate the certificate before expiration to ensure your apps are installed and signed with an active certificate.\n# Confirm that the correct intermediate certificate is installed by verifying the expiration date is set to 2030.\n# sudo security delete-certificate -Z FF6797793A3CD798DC5B2ABEF56F73EDC9F83A64 /Library/Keychains/System.keychain\n\nswiftc -suppress-warnings \"${HOME}/image-generation/add-certificate.swift\"\n\ncerts=(\n    AppleWWDRCAG3.cer\n    DeveloperIDG2CA.cer\n)\nfor cert in ${certs[@]}; do\n    echo \"Adding ${cert} certificate\"\n    cert_path=\"${HOME}/${cert}\"\n    curl -fsSL \"https://www.apple.com/certificateauthority/${cert}\" --output ${cert_path}\n    sudo ./add-certificate ${cert_path}\n    rm ${cert_path}\ndone\n\nrm -f ./add-certificate\n\n# enable-automationmode-without-authentication\nbrew install expect\nretry=10\nwhile [[ $retry -gt 0 ]]; do\n{\n    /usr/bin/expect <<EOF\n        spawn automationmodetool enable-automationmode-without-authentication\n        expect \"password\"\n        send \"${PASSWORD}\\r\"\n        expect {\n            \"succeeded.\" { puts \"Automation mode enabled successfully\"; exit 0 }\n            eof\n        }\nEOF\n} && break\n\n    retry=$((retry-1))\n    if [[ $retry -eq 0 ]]; then\n        echo \"No retry attempts left\"\n        exit 1\n    fi\n    sleep 10\ndone\n\necho \"Getting terminal windows\"\nlaunchctl_output=$(launchctl list | grep -i terminal || true)\n\nif [ -n \"$launchctl_output\" ]; then\n    term_service=$(echo \"$launchctl_output\" | cut -f3)\n    echo \"Close terminal windows: gui/501/${term_service}\"\n    launchctl bootout gui/501/${term_service} && sleep 5\nelse\n    echo \"No open terminal windows found.\"\nfi\n\n# test enable-automationmode-without-authentication\nif [[ ! \"$(automationmodetool)\" =~ \"DOES NOT REQUIRE\" ]]; then\n    echo \"Failed to enable enable-automationmode-without-authentication option\"\n    exit 1\nfi\n\n# Fix sudoers file permissions\nsudo chmod 440 /etc/sudoers.d/*\n\n# Add NOPASSWD for the current user to sudoers\nsudo sed -i '' 's/%admin\t\tALL = (ALL) ALL/%admin\t\tALL = (ALL) NOPASSWD: ALL/g' /etc/sudoers\n\n# Create symlink for tests running\nif [[ ! -d \"/usr/local/bin\" ]];then\n    sudo mkdir -p -m 775 /usr/local/bin\n    sudo chown $USER:admin /usr/local/bin\nfi\nchmod +x $HOME/utils/invoke-tests.sh\nsudo ln -s $HOME/utils/invoke-tests.sh /usr/local/bin/invoke_tests\n\n# Fix share dir permissions\nsudo chown \"$USER\":admin /usr/local/share\nsudo chmod 775 /usr/local/share\n"
  },
  {
    "path": "images/macos/scripts/build/configure-ntpconf.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  configure-ntpconf.sh\n##  Desc:  Configure NTP servers and set the timezone to UTC\n################################################################################\n\necho Additional NTP servers adding into /etc/ntp.conf file...\ncat > /etc/ntp.conf << EOF\nserver 0.pool.ntp.org\nserver 1.pool.ntp.org\nserver 2.pool.ntp.org\nserver 3.pool.ntp.org\nserver time.apple.com\nserver time.windows.com\nEOF\n\n# Set the timezone to UTC.\necho \"The Timezone setting to UTC...\"\nln -sf /usr/share/zoneinfo/UTC /etc/localtime\n"
  },
  {
    "path": "images/macos/scripts/build/configure-preimagedata.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  configure-preimagedata.sh\n##  Desc:  Configure data used in the image\n################################################################################\n\nsource ~/utils/utils.sh\n\narch=$(get_arch)\nimagedata_file=\"$HOME/imagedata.json\"\nimage_version=$(echo $IMAGE_VERSION | cut -d _ -f 2)\nimage_version_major=${image_version/.*/}\nimage_version_minor=$(echo $image_version | cut -d \".\" -f 2)\nos_name=$(sw_vers -productName)\nos_version=$(sw_vers -productVersion)\nos_build=$(sw_vers -buildVersion)\nlabel_version=$(echo $os_version | cut -d. -f1)\n\nif [[ $arch == \"arm64\" ]]; then\n  image_label=\"macos-${label_version}-arm64\"\nelse\n  image_label=\"macos-${label_version}\"\nfi\n\nsoftware_url=\"https://github.com/actions/runner-images/blob/${image_label}/${image_version_major}.${image_version_minor}/images/macos/${image_label}-Readme.md\"\nreleaseUrl=\"https://github.com/actions/runner-images/releases/tag/${image_label}%2F${image_version_major}.${image_version_minor}\"\n\ncat <<EOF > $imagedata_file\n    [\n      {\n        \"group\": \"Operating System\",\n        \"detail\": \"${os_name}\\n${os_version}\\n${os_build}\"\n      },\n      {\n        \"group\": \"Runner Image\",\n        \"detail\": \"Image: ${image_label}\\nVersion: ${image_version}\\nIncluded Software: ${software_url}\\nImage Release: ${releaseUrl}\"\n      }\n    ]\nEOF\n\necho \"export ImageVersion=$image_version\" >> $HOME/.bashrc\necho \"export ImageOS=$IMAGE_OS\" >> $HOME/.bashrc\n"
  },
  {
    "path": "images/macos/scripts/build/configure-shell.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  configure-shell.sh\n##  Desc:  Configure shell to use bash\n################################################################################\n\nsource ~/utils/utils.sh\narch=$(get_arch)\n\necho \"Changing shell to bash\"\nsudo chsh -s /bin/bash $USERNAME\nsudo chsh -s /bin/bash root\n\n# Check MacOS architecture and add HOMEBREW PATH to bashrc\nif [[ $arch == \"arm64\" ]]; then\n  echo \"Adding Homebrew environment to bash\"\n  # Discussed here: https://github.com/Homebrew/brew/pull/18366\n  echo 'eval \"$(/opt/homebrew/bin/brew shellenv)\"' >> ~/.bashrc\nfi\n"
  },
  {
    "path": "images/macos/scripts/build/configure-ssh.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  configure-ssh.sh\n##  Desc:  Configure ssh\n################################################################################\n\n[[ ! -d ~/.ssh ]] && mkdir ~/.ssh 2>/dev/null\nchmod 777 ~/.ssh\n\nssh-keyscan -t rsa,ecdsa,ed25519 github.com >> ~/.ssh/known_hosts\nssh-keyscan -t rsa ssh.dev.azure.com >> ~/.ssh/known_hosts\n"
  },
  {
    "path": "images/macos/scripts/build/configure-system.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  configure-system.sh\n##  Desc:  Post deployment system configuration actions\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Set solid color wallpaper\"\nosascript -e 'tell application \"Finder\" to set desktop picture to POSIX file \"/System/Library/Desktop Pictures/Solid Colors/Black.png\"'\n\necho \"Close all finder windows because they can interfere with UI tests\"\nclose_finder_window\n\necho \"Disable Handoff and Continuity\"\ndefaults write com.apple.coreservices.useractivityd ActivityReceivingEnabled -bool false\ndefaults write com.apple.coreservices.useractivityd ActivityAdvertisingAllowed -bool false\n\necho \"Disable graphic effects in System\"\ndefaults write com.apple.universalaccess reduceMotion -bool true\ndefaults write com.apple.universalaccess reduceTransparency -bool true\n\necho \"Disable analytics daemon (requires SIP to be disabled)\"\nsudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.SubmitDiagInfo.plist\n\necho \"Disable notification center agent\"\nlaunchctl unload -w /System/Library/LaunchAgents/com.apple.notificationcenterui.plist\n\necho \"Disable Time Machine and it's daemon\"\nsudo tmutil disable\nsudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.backupd.plist\n\necho \"Disable Apple Push Notification Service daemon\"\nsudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.apsd.plist\n\necho \"Set SMC monitoring cadence to 0 to reduce CPU usage\"\nsudo defaults -currentHost write /Library/Preferences/com.apple.powerlogd SMCMonitorCadence 0\n\necho \"Disable Performance and Power Management daemon if possible\"\nsudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.PerfPowerServices.plist\n\n# Remove Parallels Desktop\n# https://github.com/actions/runner-images/issues/6105\n# https://github.com/actions/runner-images/issues/10143\nif is_SonomaX64 || is_SequoiaX64; then\n    brew uninstall parallels\nfi\n\n# Simple warmup of the default Xcode\necho \"Warm up the default Xcode\"\nxcodebuild -version > /dev/null\nxcrun simctl list > /dev/null\nxcrun simctl list devices > /dev/null\n\necho \"Put documentation to $HOME root\"\ncp $HOME/image-generation/output/software-report.* $HOME/\n\necho \"Remove fastlane cached cookie\"\nrm -rf ~/.fastlane\n\n# Clean up npm cache which collected during image-generation\n# we have to do that here because `npm install` is run in a few different places during image-generation\necho \"Clean up npm cache\"\nnpm cache clean --force\n\n# Clean yarn cache\nyarn cache clean\n\necho \"Clean up temporary directories\"\nsudo rm -rf ~/utils /tmp/*\n\n# Erase all indexes and wait until the rebuilding process ends,\n# for now there is no clear way to get status of indexing process on macOS, it takes around 3-6 minutes to accomplish\necho \"Erase all MDS indexes and wait until the rebuilding process ends\"\nsudo mdutil -E / > /dev/null\n\necho \"Wait for 6 minutes or until the indexing process end signal is found in logs\"\nfor _ in {1..12}; do\n    sleep 30\n    result=$(sudo log show --last 1m | grep -E 'mds.*Released.*BackgroundTask' || true)\n    if [[ -n \"$result\" ]]; then\n        echo \"Sign of indexing completion found:\"\n        echo \"$result\"\n        break\n    fi\ndone\n\necho \"Check if the indexing process or other CPU intensive process (5% and more) is still running\"\n\ncool=0\nwhile true; do\n  usage=$(top -l 2 | grep \"CPU usage\" | awk '{print int($3)}' | tr -d '%' | tail -n 1)\n  echo \"Current CPU usage: ${usage}%\"\n  if [ $usage -lt 5 ]; then\n    cool=$((cool + 1))\n  else\n    ps -arcwwwxo ppid,pid,%cpu,%mem,time,command | head -n 2 || true\n    cool=0\n  fi\n \n  echo \"Feeling cool for $cool intervals\"\n\n  if [ $cool -gt 5 ]; then\n    echo \"Cooled down, exiting...\"\n    break\n  fi\n\n  sleep 1\ndone\n\necho \"Delete symlink for tests running\"\nsudo rm -f /usr/local/bin/invoke_tests\n\necho \"Clean Homebrew downloads\"\nsudo rm -rf /Users/$USER/Library/Caches/Homebrew/downloads/*\n\n# Uninstall expect used in configure-machine.sh\nbrew uninstall expect\n"
  },
  {
    "path": "images/macos/scripts/build/configure-tccdb-macos.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  configure-tccdb-macos.sh\n##  Desc:  Configure permissions to the TCC.db\n################################################################################\n\nsource ~/utils/utils.sh\n\n# /Library/Application\\ Support/com.apple.TCC/TCC.db\nsystemValuesArray=(\n    \"'kTCCServiceAccessibility','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993\"\n    \"'kTCCServiceAccessibility','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,NULL,1592919552\"\n    \"'kTCCServiceAccessibility','/opt/hca/start_hca.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1566321319\"\n    \"'kTCCServiceAccessibility','/usr/bin/osascript',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1566321319\"\n    \"'kTCCServiceAccessibility','/usr/libexec/sshd-keygen-wrapper',1,2,4,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,0,'UNUSED',NULL,0,1644564233\"\n    \"'kTCCServiceAccessibility','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,NULL,1592919552\"\n    \"'kTCCServiceAccessibility','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1566321319\"\n    \"'kTCCServiceAccessibility','com.apple.Terminal',0,2,0,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,NULL,'UNUSED',NULL,0,1591180502\"\n    \"'kTCCServiceAccessibility','com.apple.dt.Xcode-Helper',0,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1551941368\"\n    \"'kTCCServiceAppleEvents','/bin/bash',1,2,0,1,NULL,NULL,0,'com.apple.systemevents',NULL,NULL,1591532620\"\n    \"'kTCCServiceAppleEvents','/opt/hca/hosted-compute-agent',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1592919552\"\n    \"'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1592919552\"\n    \"'kTCCServiceAppleEvents','com.apple.Terminal',0,2,0,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,NULL,'UNUSED',NULL,0,1591180502\"\n    \"'kTCCServiceAppleEvents','/usr/bin/osascript',1,2,0,1,NULL,NULL,0,'com.apple.systemevents',NULL,NULL,1591532620\"\n    \"'kTCCServiceAppleEvents','/usr/bin/osascript',1,2,0,1,NULL,NULL,0,'com.apple.Safari',NULL,NULL,1755087312\"\n    \"'kTCCServiceAppleEvents','/bin/bash',1,2,0,1,NULL,NULL,0,'com.apple.Safari',NULL,NULL,1755087312\"\n    \"'kTCCServiceAppleEvents','/opt/hca/hosted-compute-agent',1,2,0,1,NULL,NULL,0,'com.apple.Safari',NULL,NULL,1755087312\"\n    \"'kTCCServiceBluetoothAlways','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1736467200\"\n    \"'kTCCServiceBluetoothAlways','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1736467200\"\n    \"'kTCCServiceMicrophone','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1736467200\"\n    \"'kTCCServiceMicrophone','/opt/hca/start_hca.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1576661342\"\n    \"'kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1736467200\"\n    \"'kTCCServiceMicrophone','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1576661342\"\n    \"'kTCCServicePostEvent','/Library/Application Support/Veertu/Anka/addons/ankarund',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1644565949\"\n    \"'kTCCServicePostEvent','/opt/hca/start_hca.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1566321326\"\n    \"'kTCCServicePostEvent','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1566321326\"\n    \"'kTCCServiceScreenCapture','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1599831148\"\n    \"'kTCCServiceScreenCapture','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159\"\n    \"'kTCCServiceScreenCapture','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159\"\n    \"'kTCCServiceSystemPolicyAllFiles','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993\"\n    \"'kTCCServiceSystemPolicyAllFiles','/opt/hca/start_hca.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993\"\n    \"'kTCCServiceSystemPolicyAllFiles','/usr/libexec/sshd-keygen-wrapper',1,0,4,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,0,'UNUSED',NULL,0,1639660695\"\n    \"'kTCCServiceSystemPolicyAllFiles','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993\"\n    \"'kTCCServiceSystemPolicyAllFiles','com.apple.Terminal',0,2,4,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,0,'UNUSED',NULL,0,1678990068\"\n    \"'kTCCServiceSystemPolicyAllFiles','com.microsoft.wdav',0,2,4,1,NULL,NULL,NULL,'UNUSED',NULL,0,1643970979\"\n    \"'kTCCServiceSystemPolicyAllFiles','com.microsoft.wdav.epsext',0,2,4,1,NULL,NULL,NULL,'UNUSED',NULL,0,1643970979\"\n    \"'kTCCServiceSystemPolicyNetworkVolumes','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993\"\n    \"'kTCCServiceSystemPolicyNetworkVolumes','com.apple.Terminal',0,2,4,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,0,'UNUSED',NULL,0,1678990068\"\n)\nfor values in \"${systemValuesArray[@]}\"; do\n    configure_system_tccdb \"$values,NULL,NULL,'UNUSED',${values##*,}\"\ndone\n\n# $HOME/Library/Application\\ Support/com.apple.TCC/TCC.db\nuserValuesArray=(\n    \"'kTCCServiceAccessibility','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993\"\n    \"'kTCCServiceAccessibility','/usr/bin/osascript',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1566321319\"\n    \"'kTCCServiceAccessibility','com.apple.Terminal',0,2,0,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,NULL,'UNUSED',NULL,0,1591180502\"\n    \"'kTCCServiceAppleEvents','/Library/Application Support/Veertu/Anka/addons/ankarund',1,2,3,1,NULL,NULL,0,'com.apple.Terminal',X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,1655808179\"\n    \"'kTCCServiceAppleEvents','/Library/Application Support/Veertu/Anka/addons/ankarund',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1629294900\"\n    \"'kTCCServiceAppleEvents','/Library/Application Support/Veertu/Anka/addons/ankarund',1,2,3,1,NULL,NULL,0,'com.apple.systemevents',X'fade0c000000003400000001000000060000000200000016636f6d2e6170706c652e73797374656d6576656e7473000000000003',NULL,164456761\"\n    \"'kTCCServiceAppleEvents','/bin/bash',1,2,0,1,NULL,NULL,0,'com.apple.finder',NULL,NULL,1592919552\"\n    \"'kTCCServiceAppleEvents','/bin/bash',1,2,0,1,NULL,NULL,0,'com.apple.systemevents',NULL,NULL,1591532620\"\n    \"'kTCCServiceAppleEvents','/usr/bin/osascript',1,2,0,1,NULL,NULL,0,'com.apple.finder',NULL,NULL,1592919552\"\n    \"'kTCCServiceAppleEvents','/usr/bin/osascript',1,2,0,1,NULL,NULL,0,'com.apple.systemevents',NULL,NULL,1591532620\"\n    \"'kTCCServiceAppleEvents','/opt/hca/hosted-compute-agent',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1592919552\"\n    \"'kTCCServiceAppleEvents','/opt/hca/hosted-compute-agent',1,2,3,1,NULL,NULL,0,'com.apple.systemevents',X'fade0c000000003400000001000000060000000200000016636f6d2e6170706c652e73797374656d6576656e7473000000000003',NULL,1592919552\"\n    \"'kTCCServiceAppleEvents','/opt/hca/start_hca.sh',1,2,0,1,NULL,NULL,0,'com.apple.systemevents',NULL,NULL,1574241374\"\n    \"'kTCCServiceAppleEvents','/usr/libexec/sshd-keygen-wrapper',1,2,0,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1591357685\"\n    \"'kTCCServiceAppleEvents','/usr/libexec/sshd-keygen-wrapper',1,2,3,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,0,'com.apple.systemevents',X'fade0c000000003400000001000000060000000200000016636f6d2e6170706c652e73797374656d6576656e7473000000000003',NULL,1644564201\"\n    \"'kTCCServiceAppleEvents','/usr/libexec/sshd-keygen-wrapper',1,2,3,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,0,'com.apple.Terminal',X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,1650386089\"\n    \"'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1592919552\"\n    \"'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,3,1,NULL,NULL,0,'com.apple.systemevents',X'fade0c000000003400000001000000060000000200000016636f6d2e6170706c652e73797374656d6576656e7473000000000003',NULL,1592919552\"\n    \"'kTCCServiceAppleEvents','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,0,'com.apple.systemevents',NULL,NULL,1574241374\"\n    \"'kTCCServiceAppleEvents','com.apple.Terminal',0,2,0,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,0,'com.apple.systemevents',X'fade0c000000003400000001000000060000000200000016636f6d2e6170706c652e73797374656d6576656e7473000000000003',NULL,1591180478\"\n    \"'kTCCServiceBluetoothAlways','/opt/hca/hosted-compute-agent',1,2,3,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1736467200\"\n    \"'kTCCServiceBluetoothAlways','/usr/local/opt/runner/provisioner/provisioner',1,2,3,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1736467200\"\n    \"'kTCCServiceMicrophone','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1736467200\"\n    \"'kTCCServiceMicrophone','/opt/hca/start_hca.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1576661342\"\n    \"'kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1736467200\"\n    \"'kTCCServiceMicrophone','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1576661342\"\n    \"'kTCCServiceMicrophone','com.apple.CoreSimulator.SimulatorTrampoline',0,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1576347152\"\n    \"'kTCCServicePostEvent','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993\"\n    \"'kTCCServiceScreenCapture','/opt/hca/hosted-compute-agent',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159\"\n    \"'kTCCServiceScreenCapture','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159\"\n    \"'kTCCServiceScreenCapture','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993\"\n    \"'kTCCServiceScreenCapture','/usr/bin/osascript',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1566321319\"\n    \"'kTCCServiceScreenCapture','com.apple.Terminal',0,2,4,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,0,'UNUSED',NULL,0,1678990068\"\n    \"'kTCCServiceSystemPolicyAllFiles','/opt/hca/start_hca.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993\"\n    \"'kTCCServiceSystemPolicyAllFiles','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993\"\n    \"'kTCCServiceUbiquity','/System/Library/PrivateFrameworks/PhotoLibraryServices.framework/Versions/A/Support/photolibraryd',1,2,5,1,NULL,NULL,NULL,'UNUSED',NULL,0,1619461750\"\n    \"'kTCCServiceUbiquity','com.apple.CloudDocs.MobileDocumentsFileProvider',0,2,0,1,X'fade0c000000004c0000000100000006000000020000002f636f6d2e6170706c652e436c6f7564446f63732e4d6f62696c65446f63756d656e747346696c6550726f76696465720000000003',NULL,NULL,'UNUSED',NULL,0,1570793290\"\n    \"'kTCCServiceUbiquity','com.apple.PassKitCore',0,2,5,1,NULL,NULL,NULL,'UNUSED',NULL,0,1619516250\"\n    \"'kTCCServiceUbiquity','com.apple.TextEdit',0,2,0,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465787445646974000000000003',NULL,NULL,'UNUSED',NULL,0,1566368356\"\n    \"'kTCCServiceUbiquity','com.apple.mail',0,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1551941469\"\n)\nfor values in \"${userValuesArray[@]}\"; do\n    configure_user_tccdb \"$values,NULL,NULL,'UNUSED',${values##*,}\"\ndone\n"
  },
  {
    "path": "images/macos/scripts/build/configure-windows.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  configure-windows.sh\n##  Desc:  Close open windows\n################################################################################\n\nsource ~/utils/utils.sh\n\n# Close System Preferences window because since Ventura arm64 it is opened by default on Apperance tab\nif is_Arm64; then\n    echo \"Close System Preferences window\"\n    osascript -e 'tell application \"System Preferences\" to quit'\nfi\n\nretry=10\nwhile [[ $retry -gt 0 ]]; do\n    openwindows=$(osascript -e 'tell application \"System Events\" to get every window of (every process whose class of windows contains window)') && break\n    retry=$((retry-1))\n    if [[ $retry -eq 0 ]]; then\n        echo \"No retry attempts left\"\n        exit 1\n    fi\n    sleep 30\ndone\nIFS=',' read -r -a windowslist <<< \"$openwindows\"\n\nif [[ -n ${openwindows} ]]; then\n    echo \"Found opened window:\"\nfi\n\nfor key in ${!windowslist[@]}; do\n    if [[ ${windowslist[$key]} =~ \"NotificationCenter\" ]]; then\n        echo \"[Warning] ${windowslist[$key]}\"\n    else\n        echo \" - ${windowslist[$key]}\" | xargs\n        scripterror=true\n    fi\ndone\n\nif [[ ${scripterror} = true ]]; then\n    exit 1\nfi\n"
  },
  {
    "path": "images/macos/scripts/build/configure-xcode.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  configure-xcode.sh\n##  Desc:  Configure Xcode after installation\n################################################################################\n\nsource ~/utils/utils.sh\n\nXCODE_LIST=($(get_toolset_value '.xcode.versions | reverse | .[].link'))\nDEFAULT_XCODE_VERSION=$(get_toolset_value '.xcode.default')\n\n# https://github.com/microsoft/appcenter/issues/847\n# Assets.xcassets : error : CoreData: error: (6922) I/O error for database\n# at $HOME/Library/Developer/Xcode/UserData/IB Support/Simulator Devices/{GUID}\necho \"Erase a device's contents and settings:\"\nfor XCODE_VERSION in ${XCODE_LIST[@]}; do\n    echo \"    Xcode Version: ${XCODE_VERSION}\"\n    launchctl remove com.apple.CoreSimulator.CoreSimulatorService || true\n    #add sleep to let CoreSimulatorService to exit\n    sleep 3\n\n    # Select xcode version by default\n    sudo xcode-select -s /Applications/Xcode_${XCODE_VERSION}.app/Contents/Developer\n\n    # Erase a device's contents and settings\n    xcrun simctl erase all\n\n    #add sleep due to sometimes \"xcrun simctl list\" takes more than a few moments and script fails when trying to remove CoreSimulatorSerivce\n    sleep 10\ndone\n\n# Select xcode version by default\necho \"Setting Xcode ${DEFAULT_XCODE_VERSION} as default\"\nsudo xcode-select -s /Applications/Xcode_${DEFAULT_XCODE_VERSION}.app/Contents/Developer\n"
  },
  {
    "path": "images/macos/scripts/build/install-actions-cache.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:       install-actions-cache.sh\n##  Desc:       Download latest release from https://github.com/actions/action-versions\n##  Maintainer: #actions-runtime and @TingluoHuang\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Check if ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE folder exist...\"\nif [[ ! -d $ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE ]]; then\n    mkdir -p $ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE\nfi\n\ndownload_url=$(resolve_github_release_asset_url \"actions/action-versions\" \"contains(\\\"action-versions.tar.gz\\\")\" \"latest\" \"$API_PAT\")\necho \"Downloading action-versions $download_url\"\narchive_path=$(download_with_retry $download_url)\ntar -xzf $archive_path -C $ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE\n\ninvoke_tests \"ActionArchiveCache\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-android-sdk.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-android-sdk.sh\n##  Desc:  Install Android SDK, NDK and tools\n################################################################################\n\nsource ~/utils/utils.sh\n\nadd_filtered_installation_components() {\n    local minimum_version=$1\n    shift\n    local tools_array=(\"$@\")\n\n    for item in ${tools_array[@]}; do\n        # Take the last version number that appears after the last '-' or ';'\n        item_version=$(echo \"$item\" | grep -oE '[-;][0-9.]+' | grep -oE '[0-9.]+')\n        if [[ \"$(printf \"${minimum_version}\\n${item_version}\\n\" | sort -V | head -n1)\" == \"$minimum_version\" ]]; then\n            components+=($item)\n        fi\n    done\n}\n\nget_full_ndk_version() {\n    local majorVersion=$1\n\n    ndkVersion=$(${SDKMANAGER} --list | grep \"ndk;${majorVersion}.*\" | awk '{gsub(\"ndk;\", \"\"); print $1}' | sort -V | tail -n1)\n    echo \"$ndkVersion\"\n}\n\ncomponents=()\n\nandroid_platform=$(get_toolset_value '.android.platform_min_version')\nandroid_build_tool=$(get_toolset_value '.android.build_tools_min_version')\nandroid_extra_list=($(get_toolset_value '.android.\"extras\"[]'))\nandroid_addon_list=($(get_toolset_value '.android.\"addons\"[]'))\nandroid_additional_tools=($(get_toolset_value '.android.\"additional_tools\"[]'))\nandroid_ndk_major_versions=($(get_toolset_value '.android.ndk.\"versions\"[]'))\nandroid_ndk_major_default=$(get_toolset_value '.android.ndk.default')\nandroid_ndk_major_latest=$(get_toolset_value '.android.ndk.\"versions\"[-1]')\n# Get the latest command line tools from https://developer.android.com/studio#cmdline-tools\n# Newer version(s) require Java 11 by default\n# See https://github.com/actions/runner-images/issues/6960\nANDROID_HOME=$HOME/Library/Android/sdk\n\n# Download the latest command line tools so that we can accept all of the licenses.\n# See https://developer.android.com/studio/#command-tools\ncmdlineToolsVersion=$(get_toolset_value '.android.\"cmdline-tools\"')\n\nif [[ $cmdlineToolsVersion == \"latest\" ]]; then\n    repository_xml_url=\"https://dl.google.com/android/repository/repository2-1.xml\"\n    repository_xml_path=$(download_with_retry $repository_xml_url)\n    cmdlineToolsVersion=$(\n    yq -p=xml \\\n    '.sdk-repository.remotePackage[] | select(.\"+@path\" == \"cmdline-tools;latest\" and .channelRef.\"+@ref\" == \"channel-0\").archives.archive[].complete.url | select(contains(\"commandlinetools-mac\"))' \\\n    \"$repository_xml_path\"\n    )\n\n    if [[ -z $cmdlineToolsVersion ]]; then\n        echo \"Failed to parse latest command-line tools version\"\n        exit 1\n    fi\nfi\n\necho \"Downloading android command line tools...\"\narchive_path=$(download_with_retry \"https://dl.google.com/android/repository/${cmdlineToolsVersion}\")\n\necho \"Uncompressing android command line tools...\"\nmkdir -p \"$ANDROID_HOME\"\nunzip -q \"$archive_path\" -d \"$ANDROID_HOME/cmdline-tools\"\n# Command line tools need to be placed in $ANDROID_HOME/cmdline-tools/latest to function properly\nmv \"$ANDROID_HOME/cmdline-tools/cmdline-tools\" \"$ANDROID_HOME/cmdline-tools/latest\"\n\necho ANDROID_HOME is \"$ANDROID_HOME\"\nexport PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest:$ANDROID_HOME/cmdline-tools/latest/bin\n\nSDKMANAGER=$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager\n\necho \"Installing latest tools & platform tools...\"\necho y | $SDKMANAGER \"tools\" \"platform-tools\"\n\necho \"Installing latest ndk...\"\nfor ndk_version in \"${android_ndk_major_versions[@]}\"\ndo\n    ndk_full_version=$(get_full_ndk_version $ndk_version)\n    echo y | $SDKMANAGER \"ndk;$ndk_full_version\"\ndone\n\nndkDefault=$(get_full_ndk_version $android_ndk_major_default)\nANDROID_NDK_HOME=$ANDROID_HOME/ndk/$ndkDefault\nndkLatest=$(get_full_ndk_version $android_ndk_major_latest)\nANDROID_NDK_LATEST_HOME=$ANDROID_HOME/ndk/$ndkLatest\n# 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\necho \"export ANDROID_NDK=$ANDROID_NDK_HOME\" >> \"${HOME}/.bashrc\"\necho \"export ANDROID_NDK_HOME=$ANDROID_NDK_HOME\" >> \"${HOME}/.bashrc\"\necho \"export ANDROID_NDK_ROOT=$ANDROID_NDK_HOME\" >> \"${HOME}/.bashrc\"\necho \"export ANDROID_NDK_LATEST_HOME=$ANDROID_NDK_LATEST_HOME\" >> \"${HOME}/.bashrc\"\n\navailablePlatforms=($($SDKMANAGER --list | grep \"platforms;android-[0-9]\" | cut -d\"|\" -f 1 | sort -u))\nadd_filtered_installation_components $android_platform \"${availablePlatforms[@]}\"\n\nallBuildTools=($($SDKMANAGER --list --include_obsolete | grep \"build-tools;\" | cut -d\"|\" -f 1 | sort -u))\navailableBuildTools=$(echo ${allBuildTools[@]//*rc[0-9]/})\nadd_filtered_installation_components $android_build_tool \"${availableBuildTools[@]}\"\n\necho \"y\" | $SDKMANAGER ${components[@]}\n\nfor extra_name in \"${android_extra_list[@]}\"\ndo\n    echo \"Installing extra $extra_name ...\"\n    echo y | $SDKMANAGER \"extras;$extra_name\"\ndone\n\nfor addon_name in \"${android_addon_list[@]}\"\ndo\n    echo \"Installing add-on $addon_name ...\"\n    echo y | $SDKMANAGER \"add-ons;$addon_name\"\ndone\n\nfor tool_name in \"${android_additional_tools[@]}\"\ndo\n    echo \"Installing additional tool $tool_name ...\"\n    echo y | $SDKMANAGER \"$tool_name\"\ndone\n\n# Download SDK tools to preserve backward compatibility\nsdk_tools_version=$(get_toolset_value '.android.\"sdk-tools\"')\nif [ \"$sdk_tools_version\" != \"null\" ]; then\n    sdk_tools_archive_path=$(download_with_retry \"https://dl.google.com/android/repository/${sdk_tools_version}\")\n    unzip -o -qq \"$sdk_tools_archive_path\" -d \"${ANDROID_SDK_ROOT}\"\nfi\n\ninvoke_tests \"Android\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-audiodevice.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-audiodevice.sh\n##  Desc:  Install audio device\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"install switchaudio-osx\"\nbrew_smart_install \"switchaudio-osx\"\n\necho \"install sox\"\nbrew_smart_install \"sox\"\n\ninvoke_tests \"System\" \"Audio Device\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-aws-tools.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-aws-tools.sh\n##  Desc:  Install the AWS CLI, Session Manager plugin for the AWS CLI, and AWS SAM CLI\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Installing aws...\"\nawscliv2_pkg_path=$(download_with_retry \"https://awscli.amazonaws.com/AWSCLIV2.pkg\")\nsudo installer -pkg \"$awscliv2_pkg_path\" -target /\n\necho \"Installing aws sam cli...\"\nbrew tap aws/tap\nbrew_smart_install aws-sam-cli\n\necho \"Install aws cli session manager\"\nbrew install --cask session-manager-plugin\n\ninvoke_tests \"Common\" \"AWS\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-azcopy.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-azcopy.sh\n##  Desc:  Install AzCopy\n################################################################################\n\nsource ~/utils/utils.sh\n\nif is_Arm64; then\n    url=\"https://aka.ms/downloadazcopy-v10-mac-arm64\"\nelse\n    url=\"https://aka.ms/downloadazcopy-v10-mac\"\nfi\n\n# Install AzCopy\narchive_path=$(download_with_retry ${url})\nunzip -qq $archive_path -d /tmp/azcopy\nextract_path=$(echo /tmp/azcopy/azcopy*)\ncp $extract_path/azcopy /usr/local/bin/azcopy\nchmod +x /usr/local/bin/azcopy\n\n\ninvoke_tests \"Common\" \"AzCopy\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-bicep.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-bicep.sh\n##  Desc:  Install bicep cli\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Installing bicep cli...\"\nbrew tap azure/bicep\nbrew_smart_install bicep\n\ninvoke_tests \"Common\" \"Bicep\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-chrome.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-chrome.sh\n##  Desc:  Install chrome and chrome for testing browsers\n################################################################################\n\nsource ~/utils/utils.sh\narch=$(get_arch)\n\necho \"Installing Google Chrome...\"\nbrew install --cask google-chrome\n\n# Parse Google Chrome version\nfull_chrome_version=$(\"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\" --version)\nfull_chrome_version=${full_chrome_version#Google Chrome }\nchrome_version=${full_chrome_version%.*}\necho \"Google Chrome version is $full_chrome_version\"\n\n# Get Google Chrome versions information\nchrome_platform=\"mac-$arch\"\nCHROME_VERSIONS_URL=\"https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build-with-downloads.json\"\nchrome_versions_json=$(download_with_retry \"$CHROME_VERSIONS_URL\")\n\n# Download and unpack the latest release of Chrome Driver\nchromedriver_version=$(cat $chrome_versions_json | jq -r '.builds[\"'\"$chrome_version\"'\"].version')\necho \"Installing Chrome Driver version $chromedriver_version\"\n\nchromedriver_url=$(cat $chrome_versions_json | jq -r '.builds[\"'\"$chrome_version\"'\"].downloads.chromedriver[] | select(.platform==\"'\"${chrome_platform}\"'\").url')\nchromedriver_dir=\"/usr/local/share/chromedriver-$chrome_platform\"\nchromedriver_bin=\"$chromedriver_dir/chromedriver\"\n\nchromedriver_archive_path=$(download_with_retry $chromedriver_url)\nunzip -qq $chromedriver_archive_path -d /tmp/\nsudo mv /tmp/chromedriver-$chrome_platform $chromedriver_dir\nln -s $chromedriver_bin /usr/local/bin/chromedriver\necho \"export CHROMEWEBDRIVER=$chromedriver_dir\" >> ${HOME}/.bashrc\n\n# Download and unpack the latest release of Google Chrome for Testing\nchrome_for_testing_version=$(cat $chrome_versions_json | jq -r '.builds[\"'\"$chrome_version\"'\"].version')\necho \"Installing Google Chrome for Testing version $chrome_for_testing_version\"\n\nchrome_for_testing_url=$(cat $chrome_versions_json | jq -r '.builds[\"'\"$chrome_version\"'\"].downloads.chrome[] | select(.platform==\"'\"${chrome_platform}\"'\").url')\nchrome_for_testing_app=\"Google Chrome for Testing.app\"\n\nchrome_for_testing_archive_path=$(download_with_retry $chrome_for_testing_url)\nunzip -qq $chrome_for_testing_archive_path -d /tmp/\nmv \"/tmp/chrome-$chrome_platform/$chrome_for_testing_app\" \"/Applications/$chrome_for_testing_app\"\n\necho \"Installing Selenium\"\nbrew_smart_install \"selenium-server\"\n\ninvoke_tests \"Browsers\" \"Chrome\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-cocoapods.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-cocoapods.sh\n##  Desc:  Install Cocoapods\n################################################################################\n\n# Setup the Cocoapods\necho \"Installing Cocoapods...\"\npod setup\n\n# Create a symlink to /usr/local/bin since it was removed due to Homebrew change.\nln -sf $(which pod) /usr/local/bin/pod\n\ninvoke_tests \"Common\" \"CocoaPods\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-codeql-bundle.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-codeql-bundle.sh\n##  Desc:  Install CodeQL bundle\n################################################################################\n\nsource ~/utils/utils.sh\n\n# Retrieve the latest major version of the CodeQL Action to use in the base URL for downloading the bundle.\n[ -n \"$API_PAT\" ] && authString=(-H \"Authorization: token ${API_PAT}\")\nreleases=$(curl \"${authString[@]}\" -s \"https://api.github.com/repos/github/codeql-action/releases\")\n\n# Get the release tags starting with v[0-9] and sort them in descending order, then parse the first one to get the major version.\ncodeql_action_latest_major_version=$(echo \"$releases\" |\n    jq -r '.[].tag_name' |\n    grep -E '^v[0-9]' |\n    sort -nr |\n    head -n 1 |\n    sed -E 's/^v([0-9]+).*/\\1/')\nif [ -z \"$codeql_action_latest_major_version\" ]; then\n  echo \"Error: Unable to find the latest major version of the CodeQL Action.\"\n  exit 1\nfi\n\n# Retrieve the CLI version of the latest CodeQL bundle.\ndefaults_json_path=$(download_with_retry \"https://raw.githubusercontent.com/github/codeql-action/v$codeql_action_latest_major_version/src/defaults.json\")\nbundle_version=$(jq -r '.cliVersion' \"$defaults_json_path\")\nbundle_tag_name=\"codeql-bundle-v$bundle_version\"\n\necho \"Downloading CodeQL bundle $bundle_version...\"\n# Note that this is the all-platforms CodeQL bundle, to support scenarios where customers run\n# different operating systems within containers.\narchive_path=$(download_with_retry \"https://github.com/github/codeql-action/releases/download/$bundle_tag_name/codeql-bundle-osx64.tar.gz\")\n\ncodeql_toolcache_path=$AGENT_TOOLSDIRECTORY/CodeQL/$bundle_version/x64\nmkdir -p \"$codeql_toolcache_path\" \n\necho \"Unpacking the downloaded CodeQL bundle archive...\"\ntar -xzf \"$archive_path\" -C \"$codeql_toolcache_path\"\n\n# Touch a file to indicate to the CodeQL Action that this bundle shipped with the toolcache. This is\n# to support overriding the CodeQL version specified in defaults.json on GitHub Enterprise.\ntouch \"$codeql_toolcache_path/pinned-version\"\n\n# Touch a file to indicate to the toolcache that setting up CodeQL is complete.\ntouch \"$codeql_toolcache_path.complete\"\n\ninvoke_tests \"Common\" \"CodeQL Bundle\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-common-utils.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-common-utils.sh\n##  Desc:  Install utils listed in toolset file\n################################################################################\n\nsource ~/utils/utils.sh\n\ncommon_packages=$(get_toolset_value '.brew.common_packages[]')\nfor package in $common_packages; do\n    echo \"Installing $package...\"\n    case \"$package\" in\n        packer)\n            # Packer has been deprecated in Homebrew. Use tap to install Packer.\n            brew install hashicorp/tap/packer\n            ;;\n\n        tcl-tk@8)\n            brew_smart_install \"$package\"\n            if is_SonomaX64 || is_SequoiaX64 || is_TahoeX64; then\n                # Fix for https://github.com/actions/runner-images/issues/11074\n                ln -sf \"$(brew --prefix tcl-tk@8)/lib/libtcl8.6.dylib\" /usr/local/lib/libtcl8.6.dylib\n                ln -sf \"$(brew --prefix tcl-tk@8)/lib/libtk8.6.dylib\" /usr/local/lib/libtk8.6.dylib\n            fi\n            ;;\n\n        # Default behaviour for all other packages\n        *)\n            brew_smart_install \"$package\"\n            ;;\n    esac\ndone\n\ncask_packages=$(get_toolset_value '.brew.cask_packages[]')\nfor package in $cask_packages; do\n    echo \"Installing $package...\"\n    if is_Arm64 && [[ $package == \"parallels\" ]]; then\n        echo \"Parallels installation is skipped for arm64 architecture\"\n    else\n        brew install --cask $package\n    fi\ndone\n\n# Load \"Parallels International GmbH\"\nif is_SonomaX64 || is_SequoiaX64; then\n    sudo kextload /Applications/Parallels\\ Desktop.app/Contents/Library/Extensions/10.9/prl_hypervisor.kext || true\nfi\n\n# Execute AppleScript to change security preferences for macOS12, macOS13, macOS14 and macOS15\n# System Preferences -> Security & Privacy -> General -> Unlock -> Allow -> Not now\nif is_SonomaX64 || is_SequoiaX64; then\n    for retry in {4..0}; do\n        echo \"Executing AppleScript to change security preferences. Retries left: $retry\"\n        {\n            set -e\n            osascript -e 'tell application \"System Events\" to get application processes where visible is true' \n\n            if is_SonomaX64; then\n                osascript $HOME/utils/confirm-identified-developers-macos14.scpt $USER_PASSWORD\n            fi\n\n            if is_SequoiaX64; then\n                osascript $HOME/utils/confirm-identified-developers-macos15.scpt $USER_PASSWORD\n            fi\n\n        } && break\n\n        if [[ $retry -eq 0 ]]; then\n            echo \"Executing AppleScript failed. No retries left\"\n            exit 1\n        fi\n\n        echo \"Executing AppleScript failed. Sleeping for 10 seconds and retrying\"\n        sleep 10\n    done\nfi\n\n# Validate \"Parallels International GmbH\" kext\nif is_SonomaX64 || is_SequoiaX64; then\n\n    echo \"Closing System Settings window if it is still opened\"\n    killall \"System Settings\" || true\n\n    echo \"Checking parallels kexts\"\n    dbName=\"/var/db/SystemPolicyConfiguration/KextPolicy\"\n    dbQuery=\"SELECT * FROM kext_policy WHERE bundle_id LIKE 'com.parallels.kext.%';\"\n    kext=$(sudo sqlite3 $dbName \"$dbQuery\")\n\n    if [[ -z $kext ]]; then\n        echo \"Parallels International GmbH not found\"\n        exit 1\n    fi\n\n    # Create env variable\n    url=$(brew info --json=v2 --installed | jq -r '.casks[] | select(.name[] == \"Parallels Desktop\").url')\n    if [[ -z $url ]]; then\n        echo \"Unable to parse url for Parallels Desktop cask\"\n        exit 1\n    fi\n    echo \"export PARALLELS_DMG_URL=$url\" >> ${HOME}/.bashrc\nfi\n\n# Install Azure DevOps extension for Azure Command Line Interface\naz extension add -n azure-devops\n\n# Invoke tests for all basic tools\ninvoke_tests \"BasicTools\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-dotnet.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-dotnet.sh\n##  Desc:  Install dotnet\n################################################################################\n\nsource ~/utils/utils.sh\n\nexport DOTNET_CLI_TELEMETRY_OPTOUT=1\n\narch=$(get_arch)\n\n# Download installer from dot.net and keep it locally\nDOTNET_INSTALL_SCRIPT=\"https://dot.net/v1/dotnet-install.sh\"\ninstall_script_path=$(download_with_retry $DOTNET_INSTALL_SCRIPT)\nchmod +x $install_script_path\n\nargs_list=()\necho \"Parsing dotnet SDK (except rc and preview versions) from .json...\"\n\ndotnet_versions=($(get_toolset_value \".dotnet.arch[\\\"$arch\\\"].versions | .[]\"))\n\nfor dotnet_version in ${dotnet_versions[@]}; do\n    release_url=\"https://raw.githubusercontent.com/dotnet/core/main/release-notes/${dotnet_version}/releases.json\"\n    releases_json_file=$(download_with_retry \"$release_url\")\n    args_list+=(\n        $(cat $releases_json_file | \\\n        jq -r '.releases[].sdk.\"version\"' | \\\n        grep -v -E '\\-(preview|rc)\\d*' | \\\n        sort -r | rev | uniq -s 2 | rev)\n    )\ndone\n\nfor ARGS in ${args_list[@]}; do\n    $install_script_path --version $ARGS -NoPath --arch $arch\ndone\n\n# dotnet installer doesn't create symlink to executable in /user/local/bin\n# Moreover at that moment /user/local/bin doesn't exist (though already added to $PATH)\nln -s ~/.dotnet/dotnet /usr/local/bin/dotnet\n\n# Validate installation\nif [[ $(dotnet --list-sdks | wc -l) -lt \"1\" ]]; then\n    echo \"The .NET Core SDK is not installed\"\n    exit 1\nfi\n\necho 'export PATH=\"$PATH:$HOME/.dotnet/tools\"' >> $HOME/.bashrc\necho \"Dotnet operations have been completed successfully...\"\n\ninvoke_tests \"Common\" \".NET\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-edge.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-edge.sh\n##  Desc:  Install edge browser\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Installing Microsoft Edge...\"\nbrew install --cask microsoft-edge\n\nEDGE_INSTALLATION_PATH=\"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge\"\nedge_version=$(\"$EDGE_INSTALLATION_PATH\" --version | cut -d' ' -f 3)\nedge_version_major=$(echo $edge_version | cut -d'.' -f 1)\n\necho \"Version of Microsoft Edge: ${edge_version}\"\n\necho \"Installing Microsoft Edge WebDriver...\"\n\nedge_driver_version_file_path=$(download_with_retry \"https://msedgedriver.microsoft.com/LATEST_RELEASE_${edge_version_major}_MACOS\")\nedge_driver_latest_version=$(iconv -f utf-16 -t utf-8 \"$edge_driver_version_file_path\" | tr -d '\\r')\n\nif is_Arm64; then\n    edge_driver_url=\"https://msedgedriver.microsoft.com/${edge_driver_latest_version}/edgedriver_mac64_m1.zip\"\nelse\n    edge_driver_url=\"https://msedgedriver.microsoft.com/${edge_driver_latest_version}/edgedriver_mac64.zip\"\nfi\n\necho \"Compatible version of WebDriver: ${edge_driver_latest_version}\"\n\nedge_driver_archive_path=$(download_with_retry \"$edge_driver_url\")\n\n# Move webdriver to the separate directory to be consistent with the docs\n# https://docs.microsoft.com/en-us/azure/devops/pipelines/test/continuous-test-selenium?view=azure-devops#decide-how-you-will-deploy-and-test-your-app\n\nEDGE_DRIVER_DIR=\"/usr/local/share/edge_driver\"\nmkdir -p $EDGE_DRIVER_DIR\nunzip -qq $edge_driver_archive_path -d $EDGE_DRIVER_DIR\nln -s $EDGE_DRIVER_DIR/msedgedriver /usr/local/bin/msedgedriver\n\necho \"export EDGEWEBDRIVER=${EDGE_DRIVER_DIR}\" >> ${HOME}/.bashrc\n\n# Configure Edge Updater to prevent auto update\n# https://learn.microsoft.com/en-us/deployedge/edge-learnmore-edgeupdater-for-macos\n\nsudo mkdir \"/Library/Managed Preferences\"\n\ncat <<EOF | sudo tee \"/Library/Managed Preferences/com.microsoft.EdgeUpdater.plist\" > /dev/null\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"https://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n    <key>updatePolicies</key>\n    <dict>\n        <key>global</key>\n        <dict>\n            <key>UpdateDefault</key>\n            <integer>3</integer>\n        </dict>\n    </dict>\n</dict>\n</plist>\nEOF\n\nsudo chown root:wheel \"/Library/Managed Preferences/com.microsoft.EdgeUpdater.plist\"\n\ninvoke_tests \"Browsers\" \"Edge\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-firefox.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-firefox.sh\n##  Desc:  Install firefox browser\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Installing Firefox...\"\nbrew install --cask firefox\n\necho \"Installing Geckodriver...\"\nbrew_smart_install \"geckodriver\"\ngeckoPath=\"$(brew --prefix geckodriver)/bin\"\n\necho \"Add GECKOWEBDRIVER to bashrc...\"\necho \"export GECKOWEBDRIVER=${geckoPath}\" >> ${HOME}/.bashrc\n\ninvoke_tests \"Browsers\" \"Firefox\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-gcc.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-gcc.sh\n##  Desc:  Install GCC\n################################################################################\n\nsource ~/utils/utils.sh\n\ngccVersions=$(get_toolset_value '.gcc.versions | .[]')\n\nfor gccVersion in $gccVersions; do\n    brew_smart_install \"gcc@${gccVersion}\"\ndone\n\n# Delete default gfortran link if it exists https://github.com/actions/runner-images/issues/1280\ngfortranPath=$(which gfortran) || true\nif [[ $gfortranPath ]]; then\n    rm $gfortranPath\nfi\n\ninvoke_tests \"Common\" \"GCC\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-git.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-git.sh\n##  Desc:  Install Git and Git LFS\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Installing Git...\"\nbrew_smart_install \"git\"\n\ngit config --global --add safe.directory \"*\"\n\necho \"Installing Git LFS\"\nbrew_smart_install \"git-lfs\"\n\n# Update global git config\ngit lfs install\n# Update system git config\nsudo git lfs install --system\n\necho \"Disable all the Git help messages...\"\ngit config --global advice.pushUpdateRejected false\ngit config --global advice.pushNonFFCurrent false\ngit config --global advice.pushNonFFMatching false\ngit config --global advice.pushAlreadyExists false\ngit config --global advice.pushFetchFirst false\ngit config --global advice.pushNeedsForce false\ngit config --global advice.statusHints false\ngit config --global advice.statusUoption false\ngit config --global advice.commitBeforeMerge false\ngit config --global advice.resolveConflict false\ngit config --global advice.implicitIdentity false\ngit config --global advice.detachedHead false\ngit config --global advice.amWorkDir false\ngit config --global advice.rmHints false\n\ninvoke_tests \"Git\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-homebrew.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-homebrew.sh\n##  Desc:  Install Homebrew\n################################################################################\n\nsource ~/utils/utils.sh\n\narch=$(get_arch)\n\necho \"Installing Homebrew...\"\nhomebrew_installer_path=$(download_with_retry \"https://raw.githubusercontent.com/Homebrew/install/master/install.sh\")\n/bin/bash $homebrew_installer_path\n\nif [[ $arch == \"arm64\" ]]; then\n  /opt/homebrew/bin/brew update\n  /opt/homebrew/bin/brew upgrade\n  /opt/homebrew/bin/brew upgrade --cask\n  /opt/homebrew/bin/brew cleanup\n  eval \"$(/opt/homebrew/bin/brew shellenv)\"\nfi\n\ngit clone https://github.com/Homebrew/homebrew-cask $(brew --repository)/Library/Taps/homebrew/homebrew-cask --origin=origin --template= --config core.fsmonitor=false --depth 1\ngit clone https://github.com/Homebrew/homebrew-core $(brew --repository)/Library/Taps/homebrew/homebrew-core --origin=origin --template= --config core.fsmonitor=false --depth 1\n\nbrew tap homebrew/cask\nbrew tap homebrew/core\n\necho \"Disabling Homebrew analytics...\"\nbrew analytics off\n\n# jq is required for further installation scripts\necho \"Installing jq...\"\nbrew_smart_install jq\n\necho \"Installing curl...\"\nbrew_smart_install curl\n\necho \"Installing wget...\"\nbrew_smart_install \"wget\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-llvm.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-llvm.sh\n##  Desc:  Install LLVM\n################################################################################\n\nsource ~/utils/utils.sh\n\nllvmVersion=$(get_toolset_value '.llvm.version')\n\nbrew_smart_install \"llvm@${llvmVersion}\"\n\ninvoke_tests \"LLVM\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-mono.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-mono.sh\n##  Desc:  Install Mono Framework\n################################################################################\n\nsource ~/utils/utils.sh\n\n# Install Mono Framework\nmono_version_full=$(get_toolset_value '.mono.framework.version')\nmono_pkg_sha256=$(get_toolset_value '.mono.framework.sha256')\nmono_version=$(echo $mono_version_full | cut -d. -f 1,2,3)\nmono_version_short=$(echo $mono_version_full | cut -d. -f 1,2)\nmono_pkg_url=\"https://download.mono-project.com/archive/${mono_version}/macos-10-universal/MonoFramework-MDK-${mono_version_full}.macos10.xamarin.universal.pkg\"\nMONO_VERSIONS_PATH='/Library/Frameworks/Mono.framework/Versions'\n\nmono_pkg_path=$(download_with_retry $mono_pkg_url)\nuse_checksum_comparison $mono_pkg_path $mono_pkg_sha256\necho \"Installing Mono Framework ${mono_version_full}...\"\nsudo installer -pkg $mono_pkg_path -target /\n\n# Download and install NUnit console\nnunit_version=$(get_toolset_value '.mono.nunit.version')\nnunit_archive_url=\"https://github.com/nunit/nunit-console/releases/download/${nunit_version}/NUnit.Console-${nunit_version}.zip\"\nnunit_archive_sha256=$(get_toolset_value '.mono.nunit.sha256')\nNUNIT_PATH=\"/Library/Developer/nunit\"\nnunit_version_path=$NUNIT_PATH/$nunit_version\n\nnunit_archive_path=$(download_with_retry $nunit_archive_url)\nuse_checksum_comparison $nunit_archive_path $nunit_archive_sha256\necho \"Installing NUnit ${nunit_version}...\"\nsudo mkdir -p $nunit_version_path\nsudo unzip -q $nunit_archive_path -d $nunit_version_path\n\n# Create a wrapper script for nunit3-console\necho \"Creating nunit3-console wrapper...\"\nnunit3_console_wrapper=$(mktemp)\ncat <<EOF > \"$nunit3_console_wrapper\"\n#!/bin/bash -e -o pipefail\nexec ${MONO_VERSIONS_PATH}/${mono_version}/bin/mono --debug \\$MONO_OPTIONS $nunit_version_path/nunit3-console.exe \"\\$@\"\nEOF\ncat $nunit3_console_wrapper\nsudo chmod +x $nunit3_console_wrapper\nsudo mv $nunit3_console_wrapper \"${MONO_VERSIONS_PATH}/${mono_version}/Commands/nunit3-console\"\n\n# Create a symlink for the short version of Mono (e.g., 6.12)\necho \"Creating short symlink '${mono_version_short}'...\"\nsudo ln -s ${MONO_VERSIONS_PATH}/${mono_version} ${MONO_VERSIONS_PATH}/${mono_version_short}\n\n# Invoke tests and Mono\ninvoke_tests \"Mono\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-nginx.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-nginx.sh\n##  Desc:  Install Nginx\n################################################################################\n\nsource ~/utils/utils.sh\n\nbrew_smart_install nginx\nsudo sed -Ei '' 's/listen.*/listen 80;/' $(brew --prefix)/etc/nginx/nginx.conf\n\ninvoke_tests \"WebServers\" \"Nginx\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-node.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-node.sh\n##  Desc:  Install Node.js\n################################################################################\n\nsource ~/utils/utils.sh\n\ndefaultVersion=$(get_toolset_value '.node.default')\n\necho \"Installing Node.js $defaultVersion\"\nbrew_smart_install \"node@$defaultVersion\"\nbrew link node@$defaultVersion --force --overwrite\n\necho Installing yarn...\nyarn_installer_path=$(download_with_retry \"https://yarnpkg.com/install.sh\")\nbash $yarn_installer_path\n\ninvoke_tests \"Node\" \"Node.js\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-openjdk.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-openjdk.sh\n##  Desc:  Install openjdk\n################################################################################\n\nsource ~/utils/utils.sh\n\ncreateEnvironmentVariable() {\n    local JAVA_VERSION=$1\n    local DEFAULT=$2\n\n    if [[ $arch == \"arm64\" ]]; then\n        INSTALL_PATH_PATTERN=$(echo ${AGENT_TOOLSDIRECTORY}/Java_Temurin-Hotspot_jdk/${JAVA_VERSION}*/arm64/Contents/Home/)\n    else\n        INSTALL_PATH_PATTERN=$(echo ${AGENT_TOOLSDIRECTORY}/Java_Temurin-Hotspot_jdk/${JAVA_VERSION}*/x64/Contents/Home/)\n    fi\n\n    if [[ ${DEFAULT} == \"True\" ]]; then\n        echo \"Setting up JAVA_HOME variable to ${INSTALL_PATH_PATTERN}\"\n        echo \"export JAVA_HOME=${INSTALL_PATH_PATTERN}\" >> ${HOME}/.bashrc\n    fi\n\n    if [[ $arch == \"arm64\" ]]; then\n        echo \"Setting up JAVA_HOME_${JAVA_VERSION}_arm64 variable to ${INSTALL_PATH_PATTERN}\"\n        echo \"export JAVA_HOME_${JAVA_VERSION}_arm64=${INSTALL_PATH_PATTERN}\" >> ${HOME}/.bashrc\n    else\n        echo \"Setting up JAVA_HOME_${JAVA_VERSION}_X64 variable to ${INSTALL_PATH_PATTERN}\"\n        echo \"export JAVA_HOME_${JAVA_VERSION}_X64=${INSTALL_PATH_PATTERN}\" >> ${HOME}/.bashrc\n    fi\n}\n\ninstallOpenJDK() {\n    local JAVA_VERSION=$1\n\n    # Get link for Java binaries and Java version\n    hotspot_json_path=$(download_with_retry \"https://api.adoptium.net/v3/assets/latest/${JAVA_VERSION}/hotspot\")\n\n    if [[ $arch == \"arm64\" ]]; then\n        asset=$(jq -r '.[] | select(.binary.os==\"mac\" and .binary.image_type==\"jdk\" and .binary.architecture==\"aarch64\")' \"$hotspot_json_path\")\n    else\n        asset=$(jq -r '.[] | select(.binary.os==\"mac\" and .binary.image_type==\"jdk\" and .binary.architecture==\"x64\")' \"$hotspot_json_path\")\n    fi\n\n    archive_url=$(echo \"$asset\" | jq -r '.binary.package.link')\n    fullVersion=$(echo \"$asset\" | jq -r '.version.semver' | tr '+' '-')\n\n    # Remove 'LTS' suffix from the version if present\n    fullVersion=\"${fullVersion//.LTS/}\"\n\n    JAVA_TOOLCACHE_PATH=${AGENT_TOOLSDIRECTORY}/Java_Temurin-Hotspot_jdk\n    javaToolcacheVersionPath=$JAVA_TOOLCACHE_PATH/${fullVersion}\n\n    if [[ $arch == \"arm64\" ]]; then\n        javaToolcacheVersionArchPath=${javaToolcacheVersionPath}/arm64\n    else\n        javaToolcacheVersionArchPath=${javaToolcacheVersionPath}/x64\n    fi\n\n    # Download and extract Java binaries\n    archive_path=$(download_with_retry $archive_url)\n\n    echo \"Creating ${javaToolcacheVersionArchPath} directory\"\n    mkdir -p ${javaToolcacheVersionArchPath}\n\n    tar -xf $archive_path -C ${javaToolcacheVersionArchPath} --strip-components=1\n\n    # Create complete file\n    if [[ $arch == \"arm64\" ]]; then\n        touch ${javaToolcacheVersionPath}/arm64.complete\n    else\n        touch ${javaToolcacheVersionPath}/x64.complete\n    fi\n\n    # Create a symlink to '/Library/Java/JavaVirtualMachines'\n    # so '/usr/libexec/java_home' will be able to find Java\n    sudo ln -sf ${javaToolcacheVersionArchPath} /Library/Java/JavaVirtualMachines/Temurin-Hotspot-${JAVA_VERSION}.jdk\n}\n\narch=$(get_arch)\ndefaultVersion=$(get_toolset_value '.java.'$arch'.default')\njdkVersionsToInstall=($(get_toolset_value \".java.${arch}.versions[]\"))\n\nfor jdkVersionToInstall in ${jdkVersionsToInstall[@]}; do\n    installOpenJDK ${jdkVersionToInstall}\n\n    if [[ ${jdkVersionToInstall} == ${defaultVersion} ]]\n    then\n        createEnvironmentVariable ${jdkVersionToInstall} True\n    else\n        createEnvironmentVariable ${jdkVersionToInstall} False\n    fi\ndone\n\necho Installing Maven...\nbrew_smart_install \"maven\"\n\necho Installing Gradle ...\nbrew_smart_install \"gradle\"\n\ninvoke_tests \"Java\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-openssl.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-openssl.sh\n##  Desc:  Install openssl\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Install openssl@1.1\"\n\nCOMMIT=d91dabd087cb0b906c92a825df9e5e5e1a4f59f8\nFORMULA_URL=\"https://raw.githubusercontent.com/Homebrew/homebrew-core/$COMMIT/Formula/o/openssl@1.1.rb\"\nFORMULA_PATH=\"$(brew --repository)/Library/Taps/homebrew/homebrew-core/Formula/o/openssl@1.1.rb\"\nmkdir -p \"$(dirname $FORMULA_PATH)\"\ncurl -fsSL $FORMULA_URL -o $FORMULA_PATH\nHOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_FROM_API=1 brew install openssl@1.1\n\nif ! is_Arm64; then\n  # Symlink brew openssl@1.1 to `/usr/local/bin` as Homebrew refuses\n  ln -sf $(brew --prefix openssl@1.1)/bin/openssl /usr/local/bin/openssl\nelse\n  # arm64 has a different installation prefix for brew\n  ln -sf $(brew --prefix openssl@1.1)/bin/openssl /opt/homebrew/bin/openssl\nfi\n\nif ! is_Arm64; then\n  # Most of build systems and scripts look up ssl here\n  ln -sf $(brew --cellar openssl@1.1)/1.1* /usr/local/opt/openssl\nfi\n\ninvoke_tests \"OpenSSL\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-php.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-php.sh\n##  Desc:  Install PHP\n################################################################################\n\nsource ~/utils/utils.sh\n\necho Installing PHP\nphpVersionToolset=$(get_toolset_value '.php.version')\nbrew_smart_install \"php@${phpVersionToolset}\"\n\necho Installing composer\nbrew_smart_install \"composer\"\n\ninvoke_tests \"PHP\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-postgresql.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-postgresql.sh\n##  Desc:  Install PostgreSQL\n################################################################################\n\nsource ~/utils/utils.sh\n\n# Fetch PostgreSQL version to install from the toolset\ntoolsetVersion=$(get_toolset_value '.postgresql.version')\n\n# Install latest version of PostgreSQL\nbrew_smart_install postgresql@$toolsetVersion\n\n# Service PostgreSQL should be started before use\npostgreService=$(brew services list | grep -oe \"postgresql\\S*\")\nbrew services start $postgreService\n\n# Verify PostgreSQL is ready for accept incoming connections\necho \"Check PostgreSQL service is running\"\ni=10\nCOMMAND='pg_isready'\nwhile [[ $i -gt 0 ]]; do\n    echo \"Check PostgreSQL service status\"\n    eval $COMMAND && break\n    ((i--))\n    if [[ $i == 0 ]]; then\n        echo \"PostgreSQL service not ready, all attempts exhausted\"\n        exit 1\n    fi\n    echo \"PostgreSQL service not ready, wait 10 more sec, attempts left: $i\"\n    sleep 10\ndone\n\n# Stop PostgreSQL\nbrew services stop $postgreService\n\ninvoke_tests \"Databases\" \"PostgreSQL\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-powershell.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-powershell.sh\n##  Desc:  Install PowerShell\n################################################################################\n\nsource ~/utils/utils.sh\n\necho Installing PowerShell...\narch=$(get_arch)\n\nmetadata_json_path=$(download_with_retry \"https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/metadata.json\")\npwshVersionToolset=$(get_toolset_value '.pwsh.version')\npwshVersions=$(jq -r '.LTSReleaseTag[]' $metadata_json_path)\n\nfor version in ${pwshVersions[@]}; do\n    if [[ \"$version\" =~ \"$pwshVersionToolset\" ]]; then\n        download_url=$(resolve_github_release_asset_url \"PowerShell/PowerShell\" \"contains(\\\"osx-$arch.pkg\\\")\" \"$version\" \"$API_PAT\")\n        break\n    fi\ndone\n\npkg_path=$(download_with_retry $download_url)\n\n# Work around the issue on macOS Big Sur 11.5 or higher for possible error message (\"can't be opened because Apple cannot check it for malicious software\") when installing the package\nsudo xattr -rd com.apple.quarantine $pkg_path\n\nsudo installer -pkg $pkg_path -target /\n\n# Install PowerShell modules\npsModules=$(get_toolset_value '.powershellModules[].name')\nfor module in ${psModules[@]}; do\n    echo \"Installing $module module\"\n    moduleVersions=\"$(get_toolset_value \".powershellModules[] | select(.name==\\\"$module\\\") | .versions[]?\")\"\n    if [[ -z $moduleVersions ]];then\n        # Check MacOS architecture and sudo on Arm64\n        if [[ $arch == \"arm64\" ]]; then\n            sudo pwsh -command \"& {Install-Module $module -Force -Scope AllUsers}\"\n        else\n            pwsh -command \"& {Install-Module $module -Force -Scope AllUsers}\"\n        fi\n    else\n        for version in ${moduleVersions[@]}; do\n            # Check MacOS architecture and sudo on Arm64\n            if [[ $arch == \"arm64\" ]]; then\n                echo \" - $version\"\n                sudo pwsh -command \"& {Install-Module $module -RequiredVersion $version -Force -Scope AllUsers}\"\n            else\n                echo \" - $version\"\n                pwsh -command \"& {Install-Module $module -RequiredVersion $version -Force -Scope AllUsers}\"\n            fi\n        done\n    fi\ndone\n\n# Fix permission root => runner after installing powershell for arm64 arch\nif [[ $arch == \"arm64\" ]]; then\n    sudo chown -R $USER ~/.local ~/.cache ~/.config\nfi\n\n# A dummy call to initialize .IdentityService directory\npwsh -command \"& {Import-Module Az}\"\n\n# powershell link was removed in powershell-6.0.0-beta9\nsudo ln -s /usr/local/bin/pwsh /usr/local/bin/powershell\n\ninvoke_tests \"Powershell\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-python.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-python.sh\n##  Desc:  Install Python\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Installing Python Tooling\"\n\n# Close Finder window\nclose_finder_window\n\n# Installing latest Homebrew Python 3 to handle python3 and pip3 symlinks\necho \"Brew Installing default Python 3\"\nbrew_smart_install \"python3\"\n\n# Pipx has its own Python dependency\necho \"Installing pipx\"\n\nif is_Arm64; then\n    export PIPX_BIN_DIR=\"$HOME/.local/bin\"\n    export PIPX_HOME=\"$HOME/.local/pipx\"\nelse\n    export PIPX_BIN_DIR=/usr/local/opt/pipx_bin\n    export PIPX_HOME=/usr/local/opt/pipx\nfi\n\nbrew_smart_install \"pipx\"\n\necho \"export PIPX_BIN_DIR=${PIPX_BIN_DIR}\" >> ${HOME}/.bashrc\necho \"export PIPX_HOME=${PIPX_HOME}\" >> ${HOME}/.bashrc\necho 'export PATH=\"$PIPX_BIN_DIR:$PATH\"' >> ${HOME}/.bashrc\n\ninvoke_tests \"Python\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-rosetta.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-rosetta.sh\n##  Desc:  Install Rosetta\n################################################################################\n\necho \"Installing Rosetta\"\n/usr/sbin/softwareupdate --install-rosetta --agree-to-license\n"
  },
  {
    "path": "images/macos/scripts/build/install-ruby.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-ruby.sh\n##  Desc:  Install Ruby\n################################################################################\n\nsource ~/utils/utils.sh\n\narch=$(get_arch)\nDEFAULT_RUBY_VERSION=$(get_toolset_value '.ruby.default')\nRUBY_PATH=$AGENT_TOOLSDIRECTORY/Ruby\nTOOLSET_VERSIONS=$(get_toolset_value '.toolcache[] | select(.name | contains(\"Ruby\")) | .arch.'$arch'.versions[]')\n\necho \"Installing Ruby...\"\nbrew_smart_install \"ruby@${DEFAULT_RUBY_VERSION}\"\n\necho \"Set Ruby ${DEFAULT_RUBY_VERSION} as default\"\nif [[ $arch == \"arm64\" ]]; then\n    export PATH=/opt/homebrew/opt/ruby@${DEFAULT_RUBY_VERSION}/bin:$PATH\nelse\n    export PATH=/usr/local/opt/ruby@${DEFAULT_RUBY_VERSION}/bin:$PATH\nfi\n\necho \"Setting up gem environment\"\nGEM_PATH=$(gem env|awk '/EXECUTABLE DIRECTORY/ {print $4}')\necho \"GEM_PATH=$GEM_PATH\" >> $HOME/.bashrc\n\nif [[ $arch == \"arm64\" ]]; then\n    echo 'export PATH=\"$GEM_PATH:/opt/homebrew/opt/ruby@'${DEFAULT_RUBY_VERSION}'/bin:$PATH\"'  >> $HOME/.bashrc\nelse\n    echo 'export PATH=\"$GEM_PATH:/usr/local/opt/ruby@'${DEFAULT_RUBY_VERSION}'/bin:$PATH\"'  >> $HOME/.bashrc\nfi\n\necho \"Check if Ruby hostedtoolcache folder exists\"\nif [[ ! -d $RUBY_PATH ]]; then\n    mkdir -p \"$RUBY_PATH\"\nfi\n\nfor toolset_version in ${TOOLSET_VERSIONS[@]}; do\n    echo \"Installing Ruby version: $toolset_version...\"\n    download_url=$(resolve_github_release_asset_url \"ruby/ruby-builder\" \"contains(\\\"darwin-$arch.tar.gz\\\")\" \"ruby-$toolset_version\" \"$API_PAT\")\n    package_tar_name=\"${download_url##*/}\"\n    ruby_version=$(echo \"$package_tar_name\" | cut -d'-' -f 2)\n    ruby_version_path=\"$RUBY_PATH/$ruby_version\"\n\n    echo \"Create Ruby $ruby_version directory\"\n    mkdir -p \"$ruby_version_path\"\n\n    echo \"Downloading tar archive $package_tar_name\"\n    archive_path=$(download_with_retry \"$download_url\")\n\n    echo \"Expand $package_tar_name to the $ruby_version_path folder\"\n    tar xf \"$archive_path\" -C \"$ruby_version_path\"\n    complete_file_path=$ruby_version_path/$arch.complete\n    if [[ ! -f $complete_file_path ]]; then\n        echo \"Create complete file\"\n        touch $complete_file_path\n    fi\ndone\n\ninvoke_tests \"Ruby\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-rubygems.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-rubygems.sh\n##  Desc:  Install RubyGems\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Updating RubyGems...\"\ngem update --system\n\n# Temporarily install activesupport 7.2.2.1 due to compatibility issues with cocoapods https://github.com/CocoaPods/CocoaPods/issues/12081\ngem install activesupport -v 7.2.2.1\n\ngemsToInstall=$(get_toolset_value '.ruby.rubygems | .[]')\nif [[ -n $gemsToInstall ]]; then\n    for gem in $gemsToInstall; do\n        echo \"Installing gem $gem\"\n        gem install $gem\n    done\nfi\n\ninvoke_tests \"RubyGem\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-rust.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-rust.sh\n##  Desc:  Install Rust\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Installing Rustup...\"\nbrew_smart_install \"rustup-init\"\n\necho \"Installing Rust language...\"\nrustup-init -y --no-modify-path --default-toolchain=stable --profile=minimal\n\necho \"Initialize environment variables...\"\nCARGO_HOME=$HOME/.cargo\n\necho \"Install common tools...\"\nrustup component add rustfmt clippy\n\necho \"Cleanup Cargo registry cached data...\"\nrm -rf $CARGO_HOME/registry/*\n\ninvoke_tests \"Rust\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-safari.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-safari.sh\n##  Desc:  Install Safari browser\n################################################################################\n\necho \"Enabling safari driver...\"\n# https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari\n# Safari’s executable is located at /usr/bin/safaridriver\n# Configure Safari to Enable WebDriver Support\nsudo safaridriver --enable\n\necho \"Enabling the 'Allow Remote Automation' option in Safari's Develop menu\"\nmkdir -p $HOME/Library/WebDriver\nsafari_plist=\"$HOME/Library/WebDriver/com.apple.Safari.plist\"\n# \"|| true\" is needed to suppress exit code 1 in case if property or file doesn't exist\n/usr/libexec/PlistBuddy -c 'delete AllowRemoteAutomation' $safari_plist || true\n/usr/libexec/PlistBuddy -c 'add AllowRemoteAutomation bool true' $safari_plist\n\ninvoke_tests \"Browsers\" \"Safari\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-swiftlint.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-swiftlint.sh\n##  Desc:  Install SwiftLint\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Installing Swiftlint...\"\n\nbrew_smart_install \"swiftlint\"\n\ninvoke_tests \"Linters\" \"SwiftLint\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-unxip.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-unxip.sh\n##  Desc:  Install unxip\n################################################################################\n\nsource ~/utils/utils.sh\n\necho \"Installing unxip...\"\nunxip_pkg=$(download_with_retry \"https://github.com/saagarjha/unxip/releases/download/v3.1/unxip\")\nunxip_pkg_sha256=\"926ecd7bffa201c7b2b8a729fc70fbf228cf624a0e6856c13f935a97fa4fc71a\"\nuse_checksum_comparison $unxip_pkg $unxip_pkg_sha256\ninstall \"$unxip_pkg\" /usr/local/bin/unxip\n\ninvoke_tests \"Common\" \"Unxip\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-vcpkg.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-vcpkg.sh\n##  Desc:  Install vcpkg\n################################################################################\n\nsource ~/utils/utils.sh\n\n# Set env variable for vcpkg\nVCPKG_INSTALLATION_ROOT=/usr/local/share/vcpkg\necho \"export VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}\" | tee -a ~/.bashrc\n\n# Install vcpkg\nsudo git clone https://github.com/Microsoft/vcpkg $VCPKG_INSTALLATION_ROOT\nsudo $VCPKG_INSTALLATION_ROOT/bootstrap-vcpkg.sh\n$VCPKG_INSTALLATION_ROOT/vcpkg integrate install\nsudo chmod -R 0777 $VCPKG_INSTALLATION_ROOT\nln -sf $VCPKG_INSTALLATION_ROOT/vcpkg /usr/local/bin\n\ninvoke_tests \"Common\" \"vcpkg\"\n"
  },
  {
    "path": "images/macos/scripts/build/install-xcode-clt.sh",
    "content": "#!/bin/bash -e -o pipefail\n################################################################################\n##  File:  install-xcode-clt.sh\n##  Desc:  Install Xcode Command Line Tools\n################################################################################\n\nsource ~/utils/utils.sh\n\nis_clt_installed() {\n    clt_path=$(xcode-select -p 2>&1)\n    [[ -d $clt_path ]]\n}\n\ninstall_clt() {\n    echo \"Searching online for the Command Line Tools\"\n    # This temporary file prompts the 'softwareupdate' utility to list the Command Line Tools\n    clt_placeholder=\"/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress\"\n    sudo touch $clt_placeholder\n    cltPattern=\"Command Line Tools\"\n\n    clt_label_command=\"/usr/sbin/softwareupdate -l |\n                        grep -B 1 -E '${cltPattern}' |\n                        awk -F'*' '/^ *\\\\*/ {print \\$2}' |\n                        sed -e 's/^ *Label: //' -e 's/^ *//' |\n                        sort -V |\n                        tail -n1\"\n    clt_label=$(eval $clt_label_command) || true\n    if [[ -n \"$clt_label\" ]]; then\n        echo \"Installing $clt_label\"\n        sudo \"/usr/sbin/softwareupdate\" \"-i\" \"$clt_label\"\n    fi\n    sudo \"/bin/rm\" \"-f\" \"$clt_placeholder\"\n}\n\necho \"Installing Command Line Tools...\"\ninstall_clt\n\n# Retry the installation if tools are not installed from the first attempt\nretries=30\nsleepInterval=60\nwhile ! is_clt_installed; do\n    if [[ $retries -eq 0 ]]; then\n        echo \"Unable to find the Command Line Tools, all the attempts exhausted\"\n        exit 1\n    fi\n    echo \"Command Line Tools not found, trying to install them via software updates, $retries attempts left\"\n    install_clt\n    ((retries--))\n    echo \"Wait $sleepInterval seconds before the next check for installed Command Line Tools\"\n    sleep $sleepInterval\ndone\n"
  },
  {
    "path": "images/macos/scripts/docs-gen/Generate-SoftwareReport.ps1",
    "content": "using module ./software-report-base/SoftwareReport.psm1\nusing module ./software-report-base/SoftwareReport.Nodes.psm1\n\nparam (\n    [Parameter(Mandatory)][string]\n    $OutputDirectory,\n    $ImageName\n)\n\n$ErrorActionPreference = \"Stop\"\n\nImport-Module \"$PSScriptRoot/SoftwareReport.Common.psm1\" -DisableNameChecking\nImport-Module \"$PSScriptRoot/SoftwareReport.Xcode.psm1\" -DisableNameChecking\nImport-Module \"$PSScriptRoot/SoftwareReport.Android.psm1\" -DisableNameChecking\nImport-Module \"$PSScriptRoot/SoftwareReport.Java.psm1\" -DisableNameChecking\nImport-Module \"$PSScriptRoot/SoftwareReport.Toolcache.psm1\" -DisableNameChecking\nImport-Module \"$PSScriptRoot/SoftwareReport.Browsers.psm1\" -DisableNameChecking\nImport-Module \"$PSScriptRoot/SoftwareReport.Helpers.psm1\" -DisableNameChecking\nImport-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/../helpers/Xcode.Helpers.psm1\"\n\n# Operating System info\n$os = Get-OSVersion\n\n# OS info\n$osInfo = Build-OSInfoSection $ImageName\n\n# Software report\n$softwareReport = [SoftwareReport]::new($osInfo)\n$installedSoftware = $softwareReport.Root.AddHeader(\"Installed Software\")\n\n# Language and Runtime\n$languageAndRuntime = $installedSoftware.AddHeader(\"Language and Runtime\")\n$languageAndRuntime.AddToolVersionsListInline(\".NET Core SDK\", $(Get-DotnetVersionList), '^\\d+\\.\\d+\\.\\d')\n$languageAndRuntime.AddToolVersion(\"Bash\", $(Get-BashVersion))\n$languageAndRuntime.AddNodes($(Get-ClangLLVMVersions))\n$languageAndRuntime.AddNodes($(Get-GccVersions))\n$languageAndRuntime.AddNodes($(Get-FortranVersions))\n$languageAndRuntime.AddToolVersion(\"Kotlin\", $(Get-KotlinVersion))\nif (($os.IsSonoma)) {\n    $languageAndRuntime.AddToolVersion(\"Mono\", $(Get-MonoVersion))\n}\n$languageAndRuntime.AddToolVersion(\"Node.js\", $(Get-NodeVersion))\n$languageAndRuntime.AddToolVersion(\"Perl\", $(Get-PerlVersion))\nif ((-not $os.IsArm64)) {\n    $languageAndRuntime.AddToolVersion(\"PHP\", $(Get-PHPVersion))\n}\n$languageAndRuntime.AddToolVersion(\"Python3\", $(Get-Python3Version))\n$languageAndRuntime.AddToolVersion(\"Ruby\", $(Get-RubyVersion))\n\n# Package Management\n$packageManagement = $installedSoftware.AddHeader(\"Package Management\")\n$packageManagement.AddToolVersion(\"Bundler\", $(Get-BundlerVersion))\n$packageManagement.AddToolVersion(\"Carthage\", $(Get-CarthageVersion))\n$packageManagement.AddToolVersion(\"CocoaPods\", $(Get-CocoaPodsVersion))\nif ((-not $os.IsArm64)) {\n    $packageManagement.AddToolVersion(\"Composer\", $(Get-ComposerVersion))\n}\n$packageManagement.AddToolVersion(\"Homebrew\", $(Get-HomebrewVersion))\n$packageManagement.AddToolVersion(\"NPM\", $(Get-NPMVersion))\nif (($os.IsSonoma)) {\n    $packageManagement.AddToolVersion(\"NuGet\", $(Get-NuGetVersion))\n}\n$packageManagement.AddToolVersion(\"Pip3\", $(Get-Pip3Version))\n$packageManagement.AddToolVersion(\"Pipx\", $(Get-PipxVersion))\n$packageManagement.AddToolVersion(\"RubyGems\", $(Get-RubyGemsVersion))\n$packageManagement.AddToolVersion(\"Vcpkg\", $(Get-VcpkgVersion))\n$packageManagement.AddToolVersion(\"Yarn\", $(Get-YarnVersion))\n\n# Project Management\n$projectManagement = $installedSoftware.AddHeader(\"Project Management\")\n$projectManagement.AddToolVersion(\"Apache Ant\", $(Get-ApacheAntVersion))\n$projectManagement.AddToolVersion(\"Apache Maven\", $(Get-MavenVersion))\n$projectManagement.AddToolVersion(\"Gradle\", $(Get-GradleVersion))\n\n# Utilities\n$utilities = $installedSoftware.AddHeader(\"Utilities\")\n$utilities.AddToolVersion(\"7-Zip\", $(Get-7zipVersion))\n$utilities.AddToolVersion(\"aria2\", $(Get-Aria2Version))\n$utilities.AddToolVersion(\"azcopy\", $(Get-AzcopyVersion))\n$utilities.AddToolVersion(\"bazel\", $(Get-BazelVersion))\n$utilities.AddToolVersion(\"bazelisk\", $(Get-BazeliskVersion))\n$utilities.AddToolVersion(\"bsdtar\", $(Get-BsdtarVersion))\n$utilities.AddToolVersion(\"Curl\", $(Get-CurlVersion))\n$utilities.AddToolVersion(\"Git\", $(Get-GitVersion))\n$utilities.AddToolVersion(\"Git LFS\", $(Get-GitLFSVersion))\n$utilities.AddToolVersion(\"GitHub CLI\", $(Get-GitHubCLIVersion))\n$utilities.AddToolVersion(\"GNU Tar\", $(Get-GnuTarVersion))\n$utilities.AddToolVersion(\"GNU Wget\", $(Get-WgetVersion))\n$utilities.AddToolVersion(\"gpg (GnuPG)\", $(Get-GPGVersion))\n$utilities.AddToolVersion(\"jq\", $(Get-JqVersion))\n$utilities.AddToolVersion(\"OpenSSL\", $(Get-OpenSSLVersion))\n$utilities.AddToolVersion(\"Packer\", $(Get-PackerVersion))\n$utilities.AddToolVersion(\"pkgconf\", $(Get-PKGConfVersion))\n$utilities.AddToolVersion(\"Unxip\", $(Get-UnxipVersion))\n$utilities.AddToolVersion(\"yq\", $(Get-YqVersion))\n$utilities.AddToolVersion(\"zstd\", $(Get-ZstdVersion))\n$utilities.AddToolVersion(\"Ninja\", $(Get-NinjaVersion))\n\n# Tools\n$tools = $installedSoftware.AddHeader(\"Tools\")\n$tools.AddToolVersion(\"AWS CLI\", $(Get-AWSCLIVersion))\n$tools.AddToolVersion(\"AWS SAM CLI\", $(Get-AWSSAMCLIVersion))\n$tools.AddToolVersion(\"AWS Session Manager CLI\", $(Get-AWSSessionManagerCLIVersion))\n$tools.AddToolVersion(\"Azure CLI\", $(Get-AzureCLIVersion))\n$tools.AddToolVersion(\"Azure CLI (azure-devops)\", $(Get-AzureDevopsVersion))\n$tools.AddToolVersion(\"Bicep CLI\", $(Get-BicepVersion))\n$tools.AddToolVersion(\"Cmake\", $(Get-CmakeVersion))\n$tools.AddToolVersion(\"CodeQL Action Bundle\", $(Get-CodeQLBundleVersion))\n$tools.AddToolVersion(\"Fastlane\", $(Get-FastlaneVersion))\n$tools.AddToolVersion(\"SwiftFormat\", $(Get-SwiftFormatVersion))\n$tools.AddToolVersion(\"Xcbeautify\", $(Get-XcbeautifyVersion))\n$tools.AddToolVersion(\"Xcode Command Line Tools\", $(Get-XcodeCommandLineToolsVersion))\n$tools.AddToolVersion(\"Xcodes\", $(Get-XcodesVersion))\n\n# Linters\nif ((-not $os.IsArm64)) {\n    $linters = $installedSoftware.AddHeader(\"Linters\")\n    $linters.AddToolVersion(\"SwiftLint\", $(Get-SwiftLintVersion))\n}\n\n# Browsers\n$browsers = $installedSoftware.AddHeader(\"Browsers\")\n$browsers.AddNodes($(Build-BrowserSection))\n$browsers.AddNode($(Build-BrowserWebdriversEnvironmentTable))\n\n# Java\n$java = $installedSoftware.AddHeader(\"Java\")\n$java.AddTable($(Get-JavaVersions))\n\n# Toolcache\n$toolcache = $installedSoftware.AddHeader(\"Cached Tools\")\n$toolcache.AddNodes($(Build-ToolcacheSection))\n\n# Rust\n$rust = $installedSoftware.AddHeader(\"Rust Tools\")\n$rust.AddToolVersion(\"Cargo\", $(Get-RustCargoVersion))\n$rust.AddToolVersion(\"Rust\", $(Get-RustVersion))\n$rust.AddToolVersion(\"Rustdoc\", $(Get-RustdocVersion))\n$rust.AddToolVersion(\"Rustup\", $(Get-RustupVersion))\n\n$rustPackages = $rust.AddHeader(\"Packages\")\n$rustPackages.AddToolVersion(\"Clippy\", $(Get-RustClippyVersion))\n$rustPackages.AddToolVersion(\"Rustfmt\", $(Get-RustfmtVersion))\n\n# PowerShell\n$powerShell = $installedSoftware.AddHeader(\"PowerShell Tools\")\n$powerShell.AddToolVersion(\"PowerShell\", $(Get-PowershellVersion))\n\n$powerShellModules = $powerShell.AddHeader(\"PowerShell Modules\")\n$powerShellModules.AddNodes($(Get-PowerShellModules))\n\n# Xcode section\n$xcode = $installedSoftware.AddHeader(\"Xcode\")\n# First run doesn't provide full data about devices and runtimes\nGet-XcodeInfoList | Out-Null\n\n$xcodeInfo = Get-XcodeInfoList\n$xcode.AddTable($(Build-XcodeTable $xcodeInfo))\n\n$installedSdks = $xcode.AddHeader(\"Installed SDKs\")\n$installedSdks.AddTable($(Build-XcodeSDKTable $xcodeInfo))\n\n$installedSimulators = $xcode.AddHeader(\"Installed Simulators\")\n$installedSimulators.AddTable($(Build-XcodeSimulatorsTable $xcodeInfo))\n\n# Android section\n$android = $installedSoftware.AddHeader(\"Android\")\n$androidTable = Build-AndroidTable\n$android.AddTable($androidTable)\n\n$androidEnv = $android.AddHeader(\"Environment variables\")\n$androidEnv.AddTable($(Build-AndroidEnvironmentTable))\n\nif (($os.IsSonoma -or $os.IsSequoia -or $os.IsTahoe)) {\n    $miscellaneous = $installedSoftware.AddHeader(\"Miscellaneous\")\n    $miscellaneous.AddToolVersion(\"Tcl/Tk\", $(Get-TclTkVersion))\n}\n\nif (($os.IsSonomaX64 -or $os.IsSequoiaX64)) {\n\n    Write-Host \"Adding environment variables for parallels\"\n\n    $miscellaneousEnv = $miscellaneous.AddHeader(\"Environment variables\")\n    $miscellaneousEnv.AddTable($(Build-MiscellaneousEnvironmentTable))\n\n    $notes = @'\nIf you want to use Parallels Desktop you should download a package from URL stored in\nPARALLELS_DMG_URL environment variable. A system extension is allowed for this version.\n'@\n    $miscellaneousEnvNotes = $miscellaneousEnv.AddHeader(\"Notes\")\n    $miscellaneousEnvNotes.AddNote($notes)\n}\n\nif (-not (Test-Path $OutputDirectory)) { New-Item -Path $OutputDirectory -ItemType Directory | Out-Null }\n\n#\n# Write final reports\n#\nWrite-Host $markdownExtended\n$softwareReport.ToJson() | Out-File -FilePath \"${OutputDirectory}/software-report.json\" -Encoding UTF8NoBOM\n$softwareReport.ToMarkdown() | Out-File -FilePath \"${OutputDirectory}/software-report.md\" -Encoding UTF8NoBOM\n"
  },
  {
    "path": "images/macos/scripts/docs-gen/SoftwareReport.Android.psm1",
    "content": "Import-Module \"$PSScriptRoot/SoftwareReport.Helpers.psm1\" -DisableNameChecking\nImport-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\nfunction Split-TableRowByColumns {\n    param (\n        [string] $Row\n    )\n\n    return $Row.Split(\"|\") | ForEach-Object { $_.trim() }\n}\n\nfunction Get-AndroidSDKRoot {\n    return Join-Path $env:HOME \"Library\" \"Android\" \"sdk\"\n}\n\nfunction Get-AndroidSDKManagerPath {\n    $androidSDKDir = Get-AndroidSDKRoot\n    return Join-Path $androidSDKDir \"cmdline-tools\" \"latest\" \"bin\" \"sdkmanager\"\n}\n\nfunction Get-AndroidInstalledPackages {\n    $androidSDKManagerPath = Get-AndroidSDKManagerPath\n    $androidSDKManagerList = Invoke-Expression \"$androidSDKManagerPath --list_installed\"\n    return $androidSDKManagerList\n}\n\nfunction Get-AndroidPackages {\n    $androidSDKDir = Get-AndroidSDKRoot\n    $androidSDKManagerPath = Get-AndroidSDKManagerPath\n\n    $packagesListFile = Join-Path $androidSDKDir \"packages-list.txt\"\n\n    if (-Not (Test-Path -Path $packagesListFile -PathType Leaf)) {\n        (& $androidSDKManagerPath --list --verbose) |\n        Where-Object { $_ -Match \"^[^\\s]\" } |\n        Where-Object { $_ -NotMatch \"^(Loading |Info: Parsing |---|\\[=+|Installed |Available )\" } |\n        Where-Object { $_ -NotMatch \"^[^;]*$\" } |\n        Out-File -FilePath $packagesListFile\n\n        Write-Host Android packages list:\n        Get-Content $packagesListFile\n    }\n\n    return Get-Content $packagesListFile\n}\n\nfunction Build-AndroidTable {\n    Write-Host \"Build-AndroidTable\"\n    $packageInfo = Get-AndroidInstalledPackages\n\n    return @(\n        @{\n            \"Package\" = \"Android Command Line Tools\"\n            \"Version\" = Get-AndroidCommandLineToolsVersion\n        },\n        @{\n            \"Package\" = \"Android Emulator\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Android Emulator\"\n        },\n        @{\n            \"Package\" = \"Android SDK Build-tools\"\n            \"Version\" = Get-AndroidBuildToolVersions -PackageInfo $packageInfo\n        },\n        @{\n            \"Package\" = \"Android SDK Platforms\"\n            \"Version\" = Get-AndroidPlatformVersions -PackageInfo $packageInfo\n        },\n        @{\n            \"Package\" = \"Android SDK Platform-Tools\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Android SDK Platform-Tools\"\n        },\n        @{\n            \"Package\" = \"Android SDK Tools\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Android SDK Tools\"\n        },\n        @{\n            \"Package\" = \"Android Support Repository\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Android Support Repository\"\n        },\n        @{\n            \"Package\" = \"CMake\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"cmake\"\n        },\n        @{\n            \"Package\" = \"Google APIs\"\n            \"Version\" = Get-AndroidGoogleAPIsVersions -PackageInfo $packageInfo\n        },\n        @{\n            \"Package\" = \"Google Play services\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Google Play services\"\n        },\n        @{\n            \"Package\" = \"Google Repository\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Google Repository\"\n        },\n        @{\n            \"Package\" = \"NDK\"\n            \"Version\" = Get-AndroidNDKVersions\n        },\n        @{\n            \"Package\" = \"SDK Patch Applier v4\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"SDK Patch Applier v4\"\n        }\n    ) | Where-Object { $_.Version } | ForEach-Object {\n        [PSCustomObject] @{\n            \"Package Name\" = $_.Package\n            \"Version\" = $_.Version\n        }\n    }\n}\n\nfunction Build-AndroidEnvironmentTable {\n    $androidVersions = Get-Item env:ANDROID_*\n\n    $shoulddResolveLink = 'ANDROID_NDK', 'ANDROID_NDK_HOME', 'ANDROID_NDK_ROOT', 'ANDROID_NDK_LATEST_HOME'\n\n    return $androidVersions | Sort-Object -Property Name | ForEach-Object {\n        [PSCustomObject] @{\n            \"Name\" = $_.Name\n            \"Value\" = if ($shoulddResolveLink.Contains($_.Name )) { Get-PathWithLink($_.Value) } else { $_.Value }\n        }\n    }\n}\n\nfunction Get-AndroidPackageVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo,\n        [Parameter(Mandatory)]\n        [object] $MatchedString\n    )\n\n    $versions = $packageInfo | Where-Object { $_ -Match $MatchedString } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        return $packageInfoParts[1]\n    }\n    return ($versions -Join \"<br>\")\n}\n\nfunction Get-AndroidPlatformVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo\n    )\n\n    $versions = $packageInfo | Where-Object { $_ -Match \"Android SDK Platform \" } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        $revision = $packageInfoParts[1]\n        $version = $packageInfoParts[0].split(\";\")[1]\n        return \"$version (rev $revision)\"\n    }\n    [array]::Reverse($versions)\n    return ($versions -Join \"<br>\")\n}\n\nfunction Get-AndroidCommandLineToolsVersion {\n    $commandLineTools = Get-AndroidSDKManagerPath\n    (& $commandLineTools --version | Out-String).Trim() -match \"(?<version>^(\\d+\\.){1,}\\d+$)\" | Out-Null\n    $commandLineToolsVersion = $Matches.Version\n    return $commandLineToolsVersion\n}\n\nfunction Get-AndroidBuildToolVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo\n    )\n\n    $versions = $packageInfo | Where-Object { $_ -Match \"Android SDK Build-Tools\" } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        return $packageInfoParts[1]\n    }\n    $groupVersions = @()\n    $versions | ForEach-Object {\n        $majorVersion = $_.Split(\".\")[0]\n        $groupVersions += $versions | Where-Object { $_.StartsWith($majorVersion) } | Join-String -Separator \" \"\n    }\n    return ($groupVersions | Sort-Object -Descending -Unique | Join-String -Separator \"<br>\")\n}\n\nfunction Get-AndroidGoogleAPIsVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo\n    )\n\n    $versions = $packageInfo | Where-Object { $_ -Match \"Google APIs\" } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        return $packageInfoParts[0].split(\";\")[1]\n    }\n    return ($versions -Join \"<br>\")\n}\n\nfunction Get-AndroidNDKVersions {\n    $ndkFolderPath = Join-Path (Get-AndroidSDKRoot) \"ndk\"\n    $versions += Get-ChildItem -Path $ndkFolderPath -Name\n    $ndkDefaultVersion = (Get-ToolsetContent).android.ndk.default\n    $ndkDefaultFullVersion = Get-ChildItem \"$env:ANDROID_HOME/ndk/$ndkDefaultVersion.*\" -Name | Select-Object -Last 1\n\n    return ($versions | ForEach-Object {\n        $defaultPostfix = ( $_ -eq $ndkDefaultFullVersion ) ? \" (default)\" : \"\"\n        $_ + $defaultPostfix\n    } | Join-String -Separator \"<br>\")\n}\n"
  },
  {
    "path": "images/macos/scripts/docs-gen/SoftwareReport.Browsers.psm1",
    "content": "function Build-BrowserSection {\n\n    $nodes = @()\n    $os = Get-OSVersion\n\n    $nodes += @(\n        [ToolVersionNode]::new(\"Safari\", $(Get-SafariVersion))\n        [ToolVersionNode]::new(\"SafariDriver\", $(Get-SafariDriverVersion))\n        [ToolVersionNode]::new(\"Google Chrome\", $(Get-ChromeVersion))\n        [ToolVersionNode]::new(\"Google Chrome for Testing\", $(Get-ChromeForTestingVersion))\n        [ToolVersionNode]::new(\"ChromeDriver\", $(Get-ChromeDriverVersion))\n        [ToolVersionNode]::new(\"Microsoft Edge\", $(Get-EdgeVersion))\n        [ToolVersionNode]::new(\"Microsoft Edge WebDriver\", $(Get-EdgeDriverVersion))\n    )\n\n    $nodes += @(\n        [ToolVersionNode]::new(\"Mozilla Firefox\", $(Get-FirefoxVersion))\n        [ToolVersionNode]::new(\"geckodriver\", $(Get-GeckodriverVersion))\n        [ToolVersionNode]::new(\"Selenium server\", $(Get-SeleniumVersion))\n    )\n\n    return $nodes\n}\n\nfunction Get-SafariVersion {\n    $version = Run-Command \"defaults read /Applications/Safari.app/Contents/Info CFBundleShortVersionString\"\n    $build = Run-Command \"defaults read /Applications/Safari.app/Contents/Info CFBundleVersion\"\n    return \"$version ($build)\"\n}\n\nfunction Get-SafariDriverVersion {\n    $version = Run-Command \"safaridriver --version\" | Take-Part -Part 3, 4\n    return $version\n}\n\nfunction Get-ChromeVersion {\n    $chromePath = \"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\"\n    $version = Run-Command \"'${chromePath}' --version\"\n    return ($version -replace (\"^Google Chrome\")).Trim()\n}\n\nfunction Get-ChromeForTestingVersion {\n    $chromePath = \"/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing\"\n    $version = Run-Command \"'${chromePath}' --version\"\n    return ($version -replace (\"^Google Chrome for Testing\")).Trim()\n}\n\nfunction Get-ChromeDriverVersion {\n    $rawOutput = Run-Command \"chromedriver --version\"\n    $version = $rawOutput | Take-Part -Part 1\n    return $version\n}\n\nfunction Get-EdgeVersion {\n    $edgePath = \"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge\"\n    $version = Run-Command \"'${edgePath}' --version\"\n    return ($version -replace (\"^Microsoft Edge\")).Trim()\n}\n\nfunction Get-EdgeDriverVersion {\n    return Run-Command \"msedgedriver --version\" | Take-Part -Part 3\n}\n\nfunction Get-FirefoxVersion {\n    $firefoxPath = \"/Applications/Firefox.app/Contents/MacOS/firefox\"\n    $version = Run-Command \"'${firefoxPath}' --version\"\n    return ($version -replace \"^Mozilla Firefox\").Trim()\n}\n\nfunction Get-GeckodriverVersion {\n    $version = Run-Command \"geckodriver --version\" | Select-Object -First 1\n    return ($version -replace \"^geckodriver\").Trim()\n}\n\nfunction Get-SeleniumVersion {\n    $os = Get-OSVersion\n    if ($os.IsArm64) {\n        $cellarPath = \"/opt/homebrew/Cellar\"\n    } else {\n        $cellarPath = \"/usr/local/Cellar\"\n    }\n    $seleniumVersion = (Get-ChildItem -Path \"$cellarPath/selenium-server*/*\").Name\n    return $seleniumVersion\n}\n\nfunction Build-BrowserWebdriversEnvironmentTable {\n    $node = [HeaderNode]::new(\"Environment variables\")\n\n    $table = @(\n        @{\n            \"Name\" = \"CHROMEWEBDRIVER\"\n            \"Value\" = $env:CHROMEWEBDRIVER\n        },\n        @{\n            \"Name\" = \"EDGEWEBDRIVER\"\n            \"Value\" = $env:EDGEWEBDRIVER\n        },\n        @{\n            \"Name\" = \"GECKOWEBDRIVER\"\n            \"Value\" = $env:GECKOWEBDRIVER\n        }\n    ) | ForEach-Object {\n        [PSCustomObject] @{\n            \"Name\" = $_.Name\n            \"Value\" = $_.Value\n        }\n    }\n\n    $node.AddTable($table)\n\n    return $node\n}\n"
  },
  {
    "path": "images/macos/scripts/docs-gen/SoftwareReport.Common.psm1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\nfunction Get-BashVersion {\n    $version = bash -c 'echo ${BASH_VERSION}'\n    return $version\n}\n\nfunction Get-DotnetVersionList {\n    $sdkRawList = Run-Command \"dotnet --list-sdks\"\n    return $sdkRawList | ForEach-Object { Take-Part $_ -Part 0 }\n}\n\nfunction Get-RustVersion {\n    $rustVersion = Run-Command \"rustc --version\" | Take-Part -Part 1\n    return $rustVersion\n}\n\nfunction Get-RustfmtVersion {\n    $version = Run-Command \"rustfmt --version\" | Take-Part -Part 1\n    return $version\n}\n\nfunction Get-RustdocVersion {\n    $version = Run-Command \"rustdoc --version\" | Take-Part -Part 1\n    return $version\n}\n\nfunction Get-RustCargoVersion {\n    $version = Run-Command \"cargo --version\" | Take-Part -Part 1\n    return $version\n}\n\nfunction Get-RustClippyVersion {\n    $version = Run-Command \"cargo clippy --version\" | Take-Part -Part 1\n    return $version\n}\n\nfunction Get-RustupVersion {\n    $rustupVersion = Run-Command \"rustup --version\" | Select-Object -First 1 | Take-Part -Part 1\n    return $rustupVersion\n}\n\nfunction Get-VcpkgVersion {\n    $vcpkgVersion = Run-Command \"vcpkg version\" | Select-Object -First 1 | Take-Part -Part 5 | Take-Part -Part 0 -Delimiter \"-\"\n    $commitId = git -C \"/usr/local/share/vcpkg\" rev-parse --short HEAD\n    return \"$vcpkgVersion (build from commit $commitId)\"\n}\n\nfunction Get-GccVersions {\n    $versionList = (Get-ToolsetContent).gcc.versions\n    $versionList | Foreach-Object {\n        $nameVersion = Run-Command \"gcc-${_} --version\" | Select-Object -First 1\n        $version = ($nameVersion -replace \"^gcc-${_}\").Trim() -replace '\\).*$', ')'\n        return [ToolVersionNode]::new(\"GCC ${_}\", \"$version - available by ``gcc-${_}`` alias\")\n    }\n}\n\nfunction Get-FortranVersions {\n    $versionList = (Get-ToolsetContent).gcc.versions\n    $versionList | Foreach-Object {\n        $nameVersion = Run-Command \"gfortran-${_} --version\" | Select-Object -First 1\n        $version = ($nameVersion -replace \"^GNU Fortran\").Trim() -replace '\\).*$', ')'\n        return [ToolVersionNode]::new(\"GNU Fortran ${_}\", \"$version - available by ``gfortran-${_}`` alias\")\n    }\n}\n\nfunction Get-ClangLLVMVersions {\n    $clangVersionRegex = [Regex]::new(\"(?<version>\\d+\\.\\d+\\.\\d+)\")\n\n    $defaultClangOutput = Run-Command \"clang --version\" | Out-String\n    $defaultClangVersion = $clangVersionRegex.Match($defaultClangOutput).Groups['version'].Value\n\n    $homebrewClangPath = '$(brew --prefix llvm@{0})/bin/clang' -f ((Get-ToolsetContent).llvm.version)\n    $homebrewClangOutput = Run-Command \"$homebrewClangPath --version\" | Out-String\n    $homebrewClangVersion = $clangVersionRegex.Match($homebrewClangOutput).Groups['version'].Value\n\n    return @(\n        [ToolVersionNode]::new(\"Clang/LLVM\", $defaultClangVersion)\n        [ToolVersionNode]::new(\"Clang/LLVM (Homebrew)\", \"$homebrewClangVersion - available on ``$homebrewClangPath``\")\n    )\n}\n\nfunction Get-Pip3Version {\n    $command = \"pip3 --version\"\n    $commandOutput = Run-Command $command\n    $versionPart1 = $commandOutput | Take-Part -Part 1\n    $versionPart2 = $commandOutput | Take-Part -Part 4\n    $versionPart3 = $commandOutput | Take-Part -Part 5\n    return \"${versionPart1} ${versionPart2} ${versionPart3}\"\n}\n\nfunction Get-PipxVersion {\n    $pipxVersion = Run-Command \"pipx --version\" -SuppressStderr\n    return $pipxVersion\n}\n\nfunction Build-OSInfoSection {\n    param (\n        [string] $ImageName\n    )\n\n    $fieldsToInclude = @(\"System Version:\", \"Kernel Version:\")\n    $rawSystemInfo = Run-Command \"system_profiler SPSoftwareDataType\"\n    $parsedSystemInfo = $rawSystemInfo | Where-Object { -not ($_ | Select-String -NotMatch $fieldsToInclude) } | ForEach-Object { $_.Trim() }\n    $parsedSystemInfo[0] -match \"System Version: macOS (?<version>\\d+)\" | Out-Null\n    $version = $Matches.Version\n    $systemVersion = $parsedSystemInfo[0].Replace($fieldsToInclude[0],\"\").Trim()\n    $kernelVersion = $parsedSystemInfo[1].Replace($fieldsToInclude[1],\"\").Trim()\n\n    $osInfoNode = [HeaderNode]::new(\"macOS $version\")\n    $osInfoNode.AddToolVersion(\"OS Version:\", $systemVersion)\n    $osInfoNode.AddToolVersion(\"Kernel Version:\", $kernelVersion)\n    $osInfoNode.AddToolVersion(\"Image Version:\", $ImageName.Split('_')[1])\n    return $osInfoNode\n}\n\nfunction Get-MonoVersion {\n    $monoVersion = Run-Command \"mono --version\" | Out-String | Take-Part -Part 4\n    return $monoVersion\n}\n\nfunction Get-NodeVersion {\n    $nodeVersion = Run-Command \"node --version\"\n    return $nodeVersion.TrimStart(\"v\")\n}\n\nfunction Get-PerlVersion {\n    $version = Run-Command \"perl -e 'print substr(`$^V,1)'\"\n    return $version\n}\n\nfunction Get-Python3Version {\n    $python3Version = Run-Command \"python3 --version\"\n    return ($python3Version -replace \"^Python\").Trim()\n}\n\nfunction Get-RubyVersion {\n    $rubyVersion = Run-Command \"ruby --version\" | Take-Part -Part 1\n    return $rubyVersion\n}\n\nfunction Get-PHPVersion {\n    $PHPVersion = Run-Command \"php --version\" | Select-Object -First 1 | Take-Part -Part 0,1\n    return ($PHPVersion -replace \"^PHP\").Trim()\n}\n\nfunction Get-BundlerVersion {\n    $bundlerVersion = Run-Command \"bundle --version\"\n    return ($bundlerVersion -replace \"^Bundler version\").Trim()\n}\n\nfunction Get-CarthageVersion {\n    $carthageVersion = Run-Command \"carthage version\" -SuppressStderr\n    return $carthageVersion\n}\n\nfunction Get-CocoaPodsVersion {\n    $cocoaPodsVersion = Run-Command \"pod --version\"\n    return $cocoaPodsVersion\n}\n\nfunction Get-HomebrewVersion {\n    $homebrewVersion = Run-Command \"brew --version\" | Select-Object -First 1\n    return ($homebrewVersion -replace \"^Homebrew\").Trim()\n}\n\nfunction Get-NPMVersion {\n    $NPMVersion = Run-Command \"npm --version\"\n    return $NPMVersion\n}\n\nfunction Get-YarnVersion {\n    $yarmVersion = Run-Command \"yarn --version\"\n    return $yarmVersion\n}\n\nfunction Get-NuGetVersion {\n    $nugetVersion = Run-Command \"nuget help\" | Select-Object -First 1 | Take-Part -Part 2\n    return $nugetVersion\n}\n\nfunction Get-RubyGemsVersion {\n    $rubyGemsVersion = Run-Command \"gem --version\"\n    return $rubyGemsVersion\n}\n\nfunction Get-ComposerVersion {\n    $composerVersion = Run-Command \"composer --version\" | Select-Object -First 1 | Take-Part -Part 2\n    return $composerVersion\n}\n\nfunction Get-MavenVersion {\n    $mavenVersion = Run-Command \"mvn -version\" | Select-Object -First 1 | Take-Part -Part 2\n    return $mavenVersion\n}\n\n#gradle output differs on the first launch – a welcome message, that we don't need is rendered. The solution is to take the last \"Gradle\" occurrence from the output\nfunction Get-GradleVersion {\n    $gradleVersion = (Run-Command \"gradle --version\" | Select-String \"Gradle\")[-1]\n    return ($gradleVersion.Line -replace \"^Gradle\").Trim()\n}\n\nfunction Get-ApacheAntVersion {\n    $apacheAntVersion = Run-Command \"ant -version\"  | Take-Part -Part 0,1,3\n    return ($apacheAntVersion -replace \"^Apache Ant\\(TM\\)\").Trim()\n}\n\nfunction Get-CurlVersion {\n    $curlVersion = Run-Command \"curl --version\" | Select-Object -First 1 | Take-Part -Part 1\n    return $curlVersion\n}\n\nfunction Get-GitVersion {\n    $gitVersion = Run-Command \"git --version\" | Take-Part -Part -1\n    return $gitVersion\n}\n\nfunction Get-GitLFSVersion {\n    $gitLFSVersion = Run-Command \"git-lfs version\" | Take-Part -Part 0 | Take-Part -Part 1 -Delimiter \"/\"\n    return $gitLFSVersion\n}\n\nfunction Get-GitHubCLIVersion {\n    $ghVersion = Run-Command \"gh --version\" | Select-String \"gh version\" | Select-Object -First 1 | Take-Part -Part 2\n    return $ghVersion\n}\n\nfunction Get-WgetVersion {\n    $wgetVersion = Run-Command \"wget --version\" | Select-String \"GNU Wget\" | Take-Part -Part 2\n    return $wgetVersion\n}\n\nfunction Get-PackerVersion {\n    $packerVersion = Run-Command \"packer --version\" | Select-String \"Packer\" | Select-Object -First 1 | Take-Part -Part 1\n    return ($packerVersion.Trim(\"v\"))\n}\n\nfunction Get-OpenSSLVersion {\n    $opensslVersion = Run-Command \"openssl version\"\n    return ($opensslVersion -replace \"^OpenSSL\").Trim()\n}\n\nfunction Get-JqVersion {\n    $jqVersion = Run-Command \"jq --version\" | Take-Part -Part 1 -Delimiter \"-\"\n    return $jqVersion\n}\n\nfunction Get-GPGVersion {\n    $gpgVersion = Run-Command \"gpg --version\" | Select-String 'gpg (GnuPG)' -SimpleMatch\n    return ($gpgVersion.Line -replace \"^gpg \\(GnuPG\\)\").Trim()\n}\n\nfunction Get-Aria2Version {\n    $aria2Version = Run-Command \"aria2c --version\" | Select-Object -First 1 | Take-Part -Part 2\n    return $aria2Version\n}\n\nfunction Get-AzcopyVersion {\n    $azcopyVersion = [string]$(Run-Command \"azcopy --version\") | Take-Part -Part 2\n    return $azcopyVersion\n}\n\nfunction Get-ZstdVersion {\n    $zstdVersion = Run-Command \"zstd --version\" | Take-Part -Part 1 -Delimiter \"v\" | Take-Part -Part 0 -Delimiter \",\"\n    return $zstdVersion\n}\n\nfunction Get-BazelVersion {\n    $bazelVersion = Run-Command \"bazel --version\" | Take-Part -Part 0 -Delimiter \"-\"\n    return ($bazelVersion -replace \"^bazel\").Trim()\n}\n\nfunction Get-BazeliskVersion {\n    $bazeliskVersion = Run-Command \"brew list bazelisk --versions\"\n    return ($bazeliskVersion -replace \"^bazelisk\").Trim()\n}\n\nfunction Get-7zipVersion {\n    $7zip = Run-Command \"7z i\" | Select-String \"7-Zip\" | Take-Part -Part 0,2\n    return ($7zip -replace \"^7-Zip\").Trim()\n}\n\nfunction Get-GnuTarVersion {\n    $gnuTar = Run-Command \"gtar --version\" | Select-String \"tar\" | Take-Part -Part 3\n    return \"$gnuTar - available by 'gtar' alias\"\n}\n\nfunction Get-BsdtarVersion {\n    $bsdtar = Run-Command \"tar --version\" | Take-Part -Part 1\n    return \"$bsdtar - available by 'tar' alias\"\n}\n\nfunction Get-ParallelVersion {\n    $parallelVersion = Run-Command \"parallel --version\" | Select-String \"GNU parallel\" | Select-Object -First 1\n    return ($parallelVersion -replace \"^GNU parallel\").Trim()\n}\n\nfunction Get-FastlaneVersion {\n    $fastlaneVersion = Run-Command \"fastlane --version\" | Select-String \"^fastlane [0-9]\" | Take-Part -Part 1\n    return $fastlaneVersion\n}\n\nfunction Get-CmakeVersion {\n    $cmakeVersion = Run-Command \"cmake --version\" | Select-Object -First 1 | Take-Part -Part 2\n    return $cmakeVersion\n}\n\nfunction Get-AzureCLIVersion {\n    $azureCLIVersion = (az version | ConvertFrom-Json).'azure-cli'\n    return $azureCLIVersion\n}\n\nfunction Get-AzureDevopsVersion {\n    $azdevopsVersion = (az version | ConvertFrom-Json).extensions.'azure-devops'\n    return $azdevopsVersion\n}\n\nfunction Get-AWSCLIVersion {\n    $awsVersion = Run-Command \"aws --version\" | Take-Part -Part 0 | Take-Part -Delimiter \"/\" -Part 1\n    return $awsVersion\n}\n\nfunction Get-AWSSAMCLIVersion {\n    $awsSamVersion = Run-Command \"sam --version\" | Take-Part -Part 3\n    return $awsSamVersion\n}\n\nfunction Get-AWSSessionManagerCLIVersion {\n    $awsSessionManagerVersion = Run-Command \"session-manager-plugin --version\"\n    return $awsSessionManagerVersion\n}\n\nfunction Get-SwiftFormatVersion {\n    $swiftFormatVersion = Run-Command \"swiftformat --version\"\n    return $swiftFormatVersion\n}\n\nfunction Get-SwiftLintVersion {\n    $swiftlintVersion = Run-Command \"swiftlint version\"\n    return $swiftlintVersion\n}\n\nfunction Get-PowershellVersion {\n    $powershellVersion = Run-Command \"powershell --version\"\n    return ($powershellVersion -replace \"^PowerShell\").Trim()\n}\n\nfunction Get-BicepVersion {\n    $bicepVersion = Run-Command \"bicep --version\" | Take-Part -Part 3\n    return $bicepVersion\n}\n\nfunction Get-KotlinVersion {\n    $kotlinVersion = Run-Command \"kotlin -version\" | Take-Part -Part 2\n    return $kotlinVersion\n}\n\nfunction Get-TclTkVersion {\n    $tcltkVersion = (Run-Command \"brew info --json tcl-tk@8\" | ConvertFrom-Json).installed.version\n    return $tcltkVersion\n}\n\nfunction Get-YqVersion {\n    $yqVersion = Run-Command \"yq --version\"\n    $yqVersion -match \"\\d{1,2}\\.\\d{1,2}\\.\\d{1,2}\" | Out-Null\n    return ($Matches[0])\n}\n\nfunction Build-MiscellaneousEnvironmentTable {\n    return @(\n        @{\n            \"Name\" = \"PARALLELS_DMG_URL\"\n            \"Value\" = $env:PARALLELS_DMG_URL\n        }\n    ) | ForEach-Object {\n        [PSCustomObject] @{\n            \"Name\" = $_.Name\n            \"Value\" = $_.Value\n        }\n    }\n}\n\nfunction Get-CodeQLBundleVersion {\n    $CodeQLVersionWildcard = Join-Path $Env:AGENT_TOOLSDIRECTORY -ChildPath \"CodeQL\" | Join-Path -ChildPath \"*\"\n    $CodeQLVersionPath = Get-ChildItem $CodeQLVersionWildcard | Select-Object -First 1 -Expand FullName\n    $CodeQLPath = Join-Path $CodeQLVersionPath -ChildPath \"x64\" | Join-Path -ChildPath \"codeql\" | Join-Path -ChildPath \"codeql\"\n    $CodeQLVersion = & $CodeQLPath version --quiet\n    return $CodeQLVersion\n}\n\nfunction Get-PKGConfVersion {\n    $pkgconfVersion = Run-Command \"pkgconf --version\"\n    return $pkgconfVersion\n}\n\nfunction Get-XcbeautifyVersion {\n    $XcbeautifyVersion = Run-Command \"xcbeautify --version\"\n    return $XcbeautifyVersion\n}\n\nfunction Get-XcodesVersion {\n    $XcodesVersion = Run-Command \"xcodes version\"\n    return $XcodesVersion\n}\n\nfunction Get-UnxipVersion {\n    $unxipVersion = Run-Command \"unxip --version\" | Take-Part -Part 1\n    return $unxipVersion\n}\n\nfunction Get-NinjaVersion {\n    return $(ninja --version)\n}\n"
  },
  {
    "path": "images/macos/scripts/docs-gen/SoftwareReport.Helpers.psm1",
    "content": "function Run-Command {\n    param (\n        [Parameter(Mandatory=$true)]\n        [string] $Command,\n        [switch] $SuppressStderr\n    )\n    # Bash trick to suppress and show error output because some commands write to stderr (for example, \"python --version\")\n    $redirectOutputArguments = If ($SuppressStderr) { \"2> /dev/null\" } Else { \"2>&1\" }\n    $stdout = & bash -c \"${Command} ${redirectOutputArguments}\"\n\n    return $stdout\n}\n\nfunction Take-Part {\n    param (\n        [Parameter(ValueFromPipeline)]\n        [string] $toolOutput,\n        [string] $Delimiter = \" \",\n        [int[]] $Part\n    )\n    $parts = $toolOutput.Split($Delimiter, [System.StringSplitOptions]::RemoveEmptyEntries)\n    $selectedParts = $parts[$Part]\n    return [string]::Join($Delimiter, $selectedParts)\n}\n\nfunction Get-LinkTarget {\n    param (\n        [string] $inputPath\n    )\n    $link = Get-Item $inputPath | Select-Object -ExpandProperty Target\n    if ($link) {\n      return \" -> $link\"\n    }\n    return \"\"\n}\n\nfunction Get-PathWithLink {\n    param (\n        [string] $inputPath\n    )\n    $link = Get-LinkTarget($inputPath)\n    return \"${inputPath}${link}\"\n}\n\nfunction Get-BrewPackageVersion {\n    param (\n        [string] $CommandName\n    )\n\n    (Get-LinkTarget (Get-Command $CommandName).Source | Out-String) -match \"(?<version>(\\d+.){2}\\d+)\" | Out-Null\n    $packageVersion = $Matches.Version\n\n    return $packageVersion\n}\n"
  },
  {
    "path": "images/macos/scripts/docs-gen/SoftwareReport.Java.psm1",
    "content": "function Get-JavaVersions {\n    $defaultJavaPath = (Get-Item env:JAVA_HOME).value\n\n    $os = Get-OSVersion\n    if ($os.IsArm64) {\n        $javaVersions = Get-Item env:JAVA_HOME_*_arm64\n    } else {\n        $javaVersions = Get-Item env:JAVA_HOME_*_X64\n    }\n\n    $sortRules = @{\n        Expression = { [Int32]$_.Name.Split(\"_\")[2] }\n        Descending = $false\n    }\n\n    return $javaVersions | Sort-Object $sortRules | ForEach-Object {\n        $javaPath = $_.Value\n        # Take semver from the java path\n        $version = $javaPath.split('/')[5]\n        $fullVersion = $version.Replace('-', '+')\n        $defaultPostfix = ($javaPath -eq $defaultJavaPath) ? \" (default)\" : \"\"\n\n        [PSCustomObject] @{\n            \"Version\"              = $fullVersion + $defaultPostfix\n            \"Environment Variable\" = $_.Name\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/docs-gen/SoftwareReport.Toolcache.psm1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\nfunction Get-ToolcacheRubyVersions {\n    $toolcachePath = Join-Path $env:HOME \"hostedtoolcache\" \"Ruby\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ }\n}\n\nfunction Get-ToolcachePythonVersions {\n    $toolcachePath = Join-Path $env:HOME \"hostedtoolcache\" \"Python\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ }\n}\n\nfunction Get-ToolcacheNodeVersions {\n    $toolcachePath = Join-Path $env:HOME \"hostedtoolcache\" \"Node\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ }\n}\n\nfunction Get-ToolcacheGoVersions {\n    $toolcachePath = Join-Path $env:HOME \"hostedtoolcache\" \"Go\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ }\n}\n\nfunction Build-ToolcacheSection {\n\n    $nodes = @()\n    $nodes += @(\n        [ToolVersionsListNode]::new(\"Ruby\", $(Get-ToolcacheRubyVersions), '^\\d+\\.\\d+', \"List\")\n        [ToolVersionsListNode]::new(\"Python\", $(Get-ToolcachePythonVersions), '^\\d+\\.\\d+', \"List\"),\n        [ToolVersionsListNode]::new(\"Node.js\", $(Get-ToolcacheNodeVersions), '^\\d+', \"List\"),\n        [ToolVersionsListNode]::new(\"Go\", $(Get-ToolcacheGoVersions), '^\\d+\\.\\d+', \"List\")\n    )\n\n    return $nodes\n}\n\nfunction Get-PowerShellModules {\n    $modules = ((Get-ToolsetContent).powershellModules).name\n    $modules | ForEach-Object {\n        $moduleName = $_\n        $moduleVersions = Get-Module -Name $moduleName -ListAvailable | Select-Object -ExpandProperty Version | Sort-Object -Unique\n        return [ToolVersionsListNode]::new($moduleName, $moduleVersions, '^\\d+', \"Inline\")\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/docs-gen/SoftwareReport.Xcode.psm1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/../helpers/Xcode.Helpers.psm1\"\n\nfunction Get-XcodePaths {\n    $xcodePaths = Get-ChildItem -Path \"/Applications\" -Filter \"Xcode_*.app\" | Where-Object { !$_.LinkType }\n    return $xcodePaths | Select-Object -ExpandProperty Fullname\n}\n\nfunction Get-XcodeSDKList {\n    param(\n        [Parameter(Mandatory)]\n        [string]$XcodeRootPath\n    )\n\n    $versionInfo = Get-XcodeVersionInfo -XcodeRootPath $XcodeRootPath\n    $xcodebuildPath = Get-XcodeToolPath -XcodeRootPath $XcodeRootPath -ToolName \"xcodebuild\"\n    if ($versionInfo.Version -le [System.Version]::Parse(\"9.4.1\")) {\n        $output = Invoke-Expression \"$xcodebuildPath -showsdks\"\n        $sdkList = $output | Where-Object { $_ -Match \"-sdk\" }\n\n        return $sdkList | ForEach-Object {\n            $displayName, $canonicalName = $_.Split(\"-sdk\")\n            return @{\n                canonicalName = $canonicalName.Trim()\n                displayName = $displayName.Trim()\n            }\n        }\n    }\n\n    [string]$output = Invoke-Expression \"$xcodebuildPath -showsdks -json\"\n    return $output | ConvertFrom-Json\n}\n\nfunction Get-XcodeInfoList {\n    $defaultXcodeRootPath = Get-DefaultXcodeRootPath\n\n    $xcodeInfo = @{}\n    Get-XcodePaths | ForEach-Object {\n        $xcodeRootPath = $_\n        Switch-Xcode -XcodeRootPath $xcodeRootPath\n\n        $versionInfo = Get-XcodeVersionInfo -XcodeRootPath $xcodeRootPath\n        $versionInfo.Path = $xcodeRootPath\n        $versionInfo.IsDefault = ($xcodeRootPath -eq $defaultXcodeRootPath)\n        $versionInfo.IsStable = Test-XcodeStableRelease -XcodeRootPath $xcodeRootPath\n\n        $xcodeInfo.Add($xcodeRootPath, [PSCustomObject] @{\n            VersionInfo = $versionInfo\n            SDKInfo = Get-XcodeSDKList -XcodeRootPath $xcodeRootPath\n            SimulatorsInfo = Get-XcodeSimulatorsInfo\n        })\n    }\n\n    Switch-Xcode -XcodeRootPath $defaultXcodeRootPath\n\n    return $xcodeInfo\n}\n\nfunction Get-XcodePlatformOrder {\n    param (\n        [Parameter(Mandatory)]\n        [string] $PlatformName\n    )\n\n    Switch ($PlatformName) {\n        \"macOS\" { 1 }\n        \"iOS\" { 2 }\n        \"Simulator - iOS\" { 3 }\n        \"tvOS\" { 4 }\n        \"Simulator - tvOS\" { 5 }\n        \"watchOS\" { 6 }\n        \"Simulator - watchOS\" { 7 }\n        \"visionOS\" { 8 }\n        \"Simulator - visionOS\" { 9 }\n        Default { 100 }\n    }\n}\n\nfunction Get-XcodeCommandLineToolsVersion {\n    $xcodeCommandLineToolsVersion = Run-Command \"pkgutil --pkg-info com.apple.pkg.CLTools_Executables\" | Select -Index 1 | Take-Part -Part 1\n    return $xcodeCommandLineToolsVersion\n}\n\nfunction Build-XcodeTable {\n    param (\n        [Parameter(Mandatory)]\n        [hashtable] $xcodeInfo\n    )\n\n    $sortRules = @{\n        Expression = { $_.Version }\n        Descending = $true\n    }\n\n    $xcodeList = $xcodeInfo.Values | ForEach-Object { $_.VersionInfo } | Sort-Object $sortRules\n    return $xcodeList | ForEach-Object {\n        $defaultPostfix = if ($_.IsDefault) { \" (default)\" } else { \"\" }\n        $betaPostfix = if ($_.IsStable) { \"\" } else { \" (beta)\" }\n        $targetPath = $_.Path\n        $symlinks = @()\n        Get-ChildItem -Path \"/Applications\" | ForEach-Object {\n            if ($_.LinkType -eq 'SymbolicLink') {\n                $linkTarget = & readlink $_.FullName\n                if ($linkTarget -eq $targetPath) {\n                    $symlinks += $_.FullName\n                }\n            }\n        }\n        if ($null -eq $symlinks) {\n            $symlinks = @(\"N/A\")\n        }\n        return [PSCustomObject] @{\n            \"Version\" = $_.Version.ToString() + $betaPostfix + $defaultPostfix\n            \"Build\" = $_.Build\n            \"Path\" = $_.Path\n            \"Symlinks\" = [String]::Join(\"<br>\", $symlinks)\n        }\n    }\n}\n\nfunction Build-XcodeDevicesList {\n    param (\n        [Parameter(Mandatory)][object] $XcodeInfo,\n        [Parameter(Mandatory)][object] $Runtime\n    )\n\n    $runtimeId = $Runtime.identifier\n    $runtimeName = $Runtime.name\n    $output = $XcodeInfo.SimulatorsInfo.devices.$runtimeId\n    if ($null -eq $output) {\n        $output = $XcodeInfo.SimulatorsInfo.devices.$runtimeName\n    }\n\n    return $output\n}\n\nfunction Build-XcodeSDKTable {\n    param (\n        [Parameter(Mandatory)]\n        [hashtable] $xcodeInfo\n    )\n\n    $sdkNames = @()\n    $xcodeInfo.Values | ForEach-Object {\n        $_.SDKInfo | ForEach-Object {\n            $sdkNames += $_.canonicalName\n        }\n    }\n\n    $sdkNames = $sdkNames | Select-Object -Unique\n    return $sdkNames | ForEach-Object {\n        $sdkName = $_\n        $sdkDisplayName = \"\"\n        $xcodeList = @()\n        $xcodeInfo.Values | ForEach-Object {\n            $sdk = $_.SDKInfo | Where-Object { $_.canonicalName -eq $sdkName } | Select-Object -First 1\n            if ($sdk) {\n                $sdkDisplayName = $sdk.displayName\n                $xcodeList += $_.VersionInfo.Version\n            }\n        }\n\n        $xcodeList = $xcodeList | Sort-Object\n\n        return [PSCustomObject] @{\n            \"SDK\" = $sdkDisplayName\n            \"SDK Name\" = $sdkName\n            \"Xcode Version\" = [String]::Join(\", \", $xcodeList)\n        }\n    } | Sort-Object {\n            # Sort rule 1\n            $sdkNameParts = $_.\"SDK\".Split(\" \")\n            $platformName = [String]::Join(\" \", $sdkNameParts[0..($sdkNameParts.Length - 2)])\n            return Get-XcodePlatformOrder $platformName\n        }, {\n            # Sort rule 2\n            $sdkNameParts = $_.\"SDK\".Split(\" \")\n            return [System.Version]::Parse($sdkNameParts[-1])\n        }\n}\n\nfunction Format-XcodeSimulatorName {\n    param(\n        [Parameter(Mandatory)][string] $Device\n    )\n\n    $formattedDeviceName = $Device.Replace(\"ʀ\", \"R\")\n    return $formattedDeviceName\n}\n\nfunction Build-XcodeSimulatorsTable {\n    param (\n        [Parameter(Mandatory)]\n        [hashtable] $xcodeInfo\n    )\n\n    $runtimes = @()\n    $xcodeInfo.Values | ForEach-Object {\n        $_.SimulatorsInfo.runtimes | ForEach-Object {\n            $runtimes += $_\n        }\n    }\n    $runtimes = $runtimes | Sort-Object @{ Expression = { $_.identifier } } -Unique\n    return $runtimes | ForEach-Object {\n        $runtime = $_\n        $runtimeDevices = @()\n        $xcodeInfo.Values | ForEach-Object {\n            $runtimeFound = $_.SimulatorsInfo.runtimes | Where-Object { $_.identifier -eq $runtime.identifier } | Select-Object -First 1\n            if ($runtimeFound) {\n                $devicesToAdd = Build-XcodeDevicesList -XcodeInfo $_ -Runtime $runtimeFound\n                $runtimeDevices += $devicesToAdd | Select-Object -ExpandProperty name\n            }\n        }\n        $runtimeDevices = $runtimeDevices | ForEach-Object { Format-XcodeSimulatorName $_ } | Select-Object -Unique\n        If (($runtimeDevices | Where-Object { -not ([string]::IsNullOrWhitespace($_)) }).Count -eq 0) {\n            $sortedRuntimeDevices = @(\"N/A\")\n        } else {\n            $sortedRuntimeDevices = $runtimeDevices | Sort-Object @{\n                Expression = { $_.Split(\" \")[0] };\n                Descending = $true;\n            }, {\n                $_.Split(\" \") | Select-Object -Skip 1 | Join-String -Separator \" \"\n            }\n        }\n        return [PSCustomObject] @{\n            \"Name\"       = $runtime.name\n            \"OS\"         = $runtime.version\n            \"Simulators\" = [String]::Join(\"<br>\", $sortedRuntimeDevices)\n        }\n    } | Sort-Object {\n        # Sort rule 1\n        $sdkNameParts = $_.\"Name\".Split(\" \")\n        $platformName = [String]::Join(\" \", $sdkNameParts[0..($sdkNameParts.Length - 2)])\n        return Get-XcodePlatformOrder $platformName\n    }, {\n        # Sort rule 2\n        $sdkNameParts = $_.\"Name\".Split(\" \")\n        return [System.Version]::Parse($sdkNameParts[-1])\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/helpers/Common.Helpers.psm1",
    "content": "function Get-CommandResult {\n    param (\n        [Parameter(Mandatory=$true)]\n        [string] $Command,\n        [switch] $Multiline\n    )\n    # Bash trick to suppress and show error output because some commands write to stderr (for example, \"python --version\")\n    $stdout = & bash -c \"$Command 2>&1\"\n    $exitCode = $LASTEXITCODE\n\n    return @{\n        Output = If ($Multiline -eq $true) { $stdout } else { [string]$stdout }\n        ExitCode = $exitCode\n    }\n}\n\n# Gets path to the tool, analogue of 'which tool'\nfunction Get-ToolPath($tool) {\n    return (Get-Command $tool).Path\n}\n\n# Returns the object with information about current OS\n# It can be used for OS-specific tests\nfunction Get-OSVersion {\n    $osVersion = [Environment]::OSVersion\n    $processorArchitecture = arch\n\n    return [PSCustomObject]@{\n        Version        = $osVersion.Version\n        Platform       = $osVersion.Platform\n        IsArm64        = $processorArchitecture -eq \"arm64\"\n        IsSonoma       = $($osVersion.Version.Major -eq \"14\")\n        IsSonomaArm64  = $($osVersion.Version.Major -eq \"14\" -and $processorArchitecture -eq \"arm64\")\n        IsSonomaX64    = $($osVersion.Version.Major -eq \"14\" -and $processorArchitecture -ne \"arm64\")\n        IsSequoia      = $($osVersion.Version.Major -eq \"15\")\n        IsSequoiaArm64 = $($osVersion.Version.Major -eq \"15\" -and $processorArchitecture -eq \"arm64\")\n        IsSequoiaX64   = $($osVersion.Version.Major -eq \"15\" -and $processorArchitecture -ne \"arm64\")\n        IsTahoe        = $($osVersion.Version.Major -eq \"26\")\n        IsTahoeArm64   = $($osVersion.Version.Major -eq \"26\" -and $processorArchitecture -eq \"arm64\")\n        IsTahoeX64     = $($osVersion.Version.Major -eq \"26\" -and $processorArchitecture -ne \"arm64\")\n    }\n}\n\nfunction Get-ToolsetContent {\n    <#\n    .SYNOPSIS\n        Retrieves the content of the toolset.json file.\n\n    .DESCRIPTION\n        This function reads the toolset.json in path provided by IMAGE_FOLDER\n        environment variable and returns the content as a PowerShell object.\n    #>\n\n    $toolsetPath = Join-Path $env:IMAGE_FOLDER \"toolset.json\"\n    $toolsetJson = Get-Content -Path $toolsetPath -Raw\n    ConvertFrom-Json -InputObject $toolsetJson\n}\n\nfunction Invoke-DownloadWithRetry {\n    Param\n    (\n        [Parameter(Mandatory)]\n        [string] $Url,\n        [Alias(\"Destination\")]\n        [string] $Path\n    )\n\n    if (-not $Path) {\n        $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''\n        $re = \"[{0}]\" -f [RegEx]::Escape($invalidChars)\n        $fileName = [IO.Path]::GetFileName($Url) -replace $re\n\n        if ([String]::IsNullOrEmpty($fileName)) {\n            $fileName = [System.IO.Path]::GetRandomFileName()\n        }\n        $Path = Join-Path -Path \"/tmp\" -ChildPath $fileName\n    }\n\n    Write-Host \"Downloading package from $Url to $Path...\"\n\n    $interval = 30\n    $downloadStartTime = Get-Date\n    for ($retries = 20; $retries -gt 0; $retries--) {\n        try {\n            $attemptStartTime = Get-Date\n            (New-Object System.Net.WebClient).DownloadFile($Url, $Path)\n            $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)\n            Write-Host \"Package downloaded in $attemptSeconds seconds\"\n            break\n        } catch {\n            $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)\n            Write-Warning \"Package download failed in $attemptSeconds seconds\"\n            Write-Warning $_.Exception.Message\n        }\n\n        if ($retries -eq 0) {\n            $totalSeconds = [math]::Round(($(Get-Date) - $downloadStartTime).TotalSeconds, 2)\n            throw \"Package download failed after $totalSeconds seconds\"\n        }\n\n        Write-Warning \"Waiting $interval seconds before retrying (retries left: $retries)...\"\n        Start-Sleep -Seconds $interval\n    }\n\n    return $Path\n}\n\nfunction Get-Architecture {\n    $arch = arch\n    if ($arch -ne \"arm64\") {\n        $arch = \"x64\"\n    }\n\n    return $arch\n}\n"
  },
  {
    "path": "images/macos/scripts/helpers/Xcode.Helpers.psm1",
    "content": "function Get-XcodeRootPath {\n    Param (\n        [Parameter(Mandatory)]\n        [string] $Version\n    )\n\n    return \"/Applications/Xcode_$Version.app\"\n}\n\nfunction Get-DefaultXcodeRootPath {\n    return (Get-Item -Path \"/Applications/Xcode.app\").Target\n}\n\nfunction Get-XcodeToolPath {\n    param (\n        [Parameter(ParameterSetName = 'Version')]\n        [string] $Version,\n        [Parameter(ParameterSetName = 'Path')]\n        [string] $XcodeRootPath,\n        [string] $ToolName\n    )\n\n    if ($PSCmdlet.ParameterSetName -eq \"Version\") {\n        $XcodeRootPath = Get-XcodeRootPath $Version\n    }\n\n    return Join-Path $XcodeRootPath \"Contents/Developer/usr/bin\" $ToolName\n}\n\nfunction Get-XcodeVersionInfo {\n    param (\n        [Parameter(Mandatory)]\n        [string] $XcodeRootPath\n    )\n\n    $xcodebuildPath = Get-XcodeToolPath -XcodeRootPath $XcodeRootPath -ToolName \"xcodebuild\"\n    [string]$output = Invoke-Expression \"$xcodebuildPath -version\"\n    $versionOutputParts = $output.Split(\" \")\n\n    return @{\n        Version = [System.Version]::Parse($versionOutputParts[1])\n        Build = $versionOutputParts[4]\n    }\n}\n\nfunction Switch-Xcode {\n    param (\n        [Parameter(ParameterSetName = 'Version')]\n        [string] $Version,\n        [Parameter(ParameterSetName = 'Path')]\n        [string] $XcodeRootPath\n    )\n\n    if ($PSCmdlet.ParameterSetName -eq \"Version\") {\n        $XcodeRootPath = Get-XcodeRootPath $Version\n    }\n\n    Write-Verbose \"Switching Xcode to '${XcodeRootPath}'\"\n    Invoke-Expression \"sudo xcode-select --switch ${XcodeRootPath}\"\n}\n\nfunction Test-XcodeStableRelease {\n    param (\n        [Parameter(ParameterSetName = 'Version')]\n        [string] $Version,\n        [Parameter(ParameterSetName = 'Path')]\n        [string] $XcodeRootPath\n    )\n\n    if ($PSCmdlet.ParameterSetName -eq \"Version\") {\n        $XcodeRootPath = Get-XcodeRootPath $Version\n    }\n\n    $licenseInfoPlistPath = Join-Path $XcodeRootPath \"Contents\" \"Resources\" \"LicenseInfo.plist\"\n    $releaseType = & defaults read $licenseInfoPlistPath \"licenseType\"\n\n    return -not ($releaseType -match \"beta\")\n}\n\nfunction Get-XcodeSimulatorsInfo {\n    param (\n        [string] $Filter\n    )\n\n    [string]$rawSimulatorsInfo = Invoke-Expression \"xcrun simctl list --json\"\n    $jsonSimulatorsInfo = $rawSimulatorsInfo | ConvertFrom-Json\n\n    if ($Filter) {\n        return $jsonSimulatorsInfo | Select-Object -ExpandProperty $Filter\n    }\n\n    return $jsonSimulatorsInfo\n}\n\nfunction Get-XcodeDevicesList {\n    $result = @()\n\n    $runtimes = Get-XcodeSimulatorsInfo -Filter \"devices\"\n    $runtimes.PSObject.Properties | ForEach-Object {\n        $runtimeName = $_.Name\n        $devices = $_.Value\n        $devices | Where-Object {\n            $availability = $_.availability\n            $isAvailable = $_.isAvailable\n            return (($availability -eq \"(available)\") -or ($isAvailable -eq \"YES\") -or ($isAvailable -eq $true))\n        } | ForEach-Object {\n            $deviceName = $_.name\n            $result += \"$runtimeName $deviceName\"\n        }\n    }\n\n    return $result\n}\n\nfunction Get-XcodePairsList {\n    $result = @()\n\n    $runtimes = Get-XcodeSimulatorsInfo -Filter \"pairs\"\n    $runtimes.PSObject.Properties | Where-Object {\n        return $_.Value.state -match \"active\"\n    } | ForEach-Object {\n        $watchName = $_.Value.watch.name\n        $phoneName = $_.Value.phone.name\n        $result += \"$watchName $phoneName\"\n    }\n\n    return $result\n}\n\n#Helper function for execution of xcversion due to: https://github.com/fastlane/fastlane/issues/18161\nfunction Invoke-XCVersion {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Arguments,\n        [Parameter()]\n        [int] $RetryAttempts = 7,\n        [Parameter()]\n        [int] $PauseDurationSecs = 1\n    )\n\n    $Command = \"xcversion $Arguments\"\n    Write-Host \"Execute [$Command]\"\n    for ($Attempt=1; $Attempt -le $RetryAttempts; $Attempt++) {\n        Write-Host \"Current attempt: [$Attempt]\"\n        $result = Get-CommandResult -Command $Command -Multiline\n        Write-Host \"Exit code: [$($result.ExitCode)]\"\n        Write-Host \"Output message: \"\n        $result.Output | ForEach-Object { Write-Host $_ }\n        if ($result.ExitCode -ne 0) {\n            Start-Sleep -Seconds $PauseDurationSecs\n        } else {\n            return $result.Output\n        }\n    }\n    if ($result.ExitCode -ne 0) {\n        throw \"Command [$Command] has finished with non-zero exit code.\"\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/helpers/Xcode.Installer.psm1",
    "content": "Import-Module \"$PSScriptRoot/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/Xcode.Helpers.psm1\"\n\nfunction Install-XcodeVersion {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Version,\n        [Parameter(Mandatory)]\n        [string] $LinkTo,\n        [Parameter(Mandatory)]\n        [string] $Sha256Sum\n    )\n\n    $xcodeDownloadDirectory = \"$env:HOME/Library/Caches/XcodeInstall\"\n    $xcodeTargetPath = Get-XcodeRootPath -Version $LinkTo\n    $xcodeXipDirectory = Invoke-DownloadXcodeArchive -DownloadDirectory $xcodeDownloadDirectory -Version $Version\n    Expand-XcodeXipArchive -DownloadDirectory $xcodeXipDirectory.FullName -TargetPath $xcodeTargetPath\n    Remove-Item -Path $xcodeXipDirectory -Force -Recurse\n}\n\nfunction Invoke-DownloadXcodeArchive {\n    param (\n        [Parameter(Mandatory)]\n        [string] $DownloadDirectory,\n        [Parameter(Mandatory)]\n        [string] $Version\n    )\n\n    Write-Host \"Downloading Xcode $Version\"\n    $tempXipDirectory = New-Item -Path $DownloadDirectory -Name \"Xcode$Version\" -ItemType \"Directory\"\n    $xcodeFileName = 'Xcode-{0}.xip' -f $Version\n    $xcodeUri = '{0}{1}?{2}'-f ${env:XCODE_INSTALL_STORAGE_URL}, $xcodeFileName, ${env:XCODE_INSTALL_SAS}\n    $xcodeFullPath = Join-Path $tempXipDirectory.FullName $xcodeFileName\n    Invoke-DownloadWithRetry -Url $xcodeUri -Path $xcodeFullPath | Out-Null\n\n    # Validating checksum\n    $xcodeSha256 = Get-FileHash -Path $xcodeFullPath -Algorithm SHA256 | Select-Object -ExpandProperty Hash\n    if ($xcodeSha256 -ne $Sha256Sum) {\n        throw \"Xcode $Version checksum mismatch. Expected: $Sha256Sum, Actual: $xcodeSha256\"\n    }\n\n    return $tempXipDirectory\n}\n\nfunction Expand-XcodeXipArchive {\n    param (\n        [Parameter(Mandatory)]\n        [string] $DownloadDirectory,\n        [Parameter(Mandatory)]\n        [string] $TargetPath\n    )\n\n    $xcodeXipPath = Get-ChildItem -Path $DownloadDirectory -Filter \"Xcode-*.xip\" | Select-Object -First 1\n\n    Write-Host \"Extracting Xcode from '$xcodeXipPath'\"\n    Push-Location $DownloadDirectory\n    if ([boolean] (Get-Command 'unxip' -ErrorAction 'SilentlyContinue')) {\n        Invoke-ValidateCommand \"unxip $xcodeXipPath\"\n    } else {\n        Invoke-ValidateCommand \"xip -x $xcodeXipPath\"\n    }\n    Pop-Location\n\n    if (Test-Path \"$DownloadDirectory/Xcode-beta.app\") {\n        Write-Host \"Renaming Xcode-beta.app to Xcode.app\"\n        Rename-Item -Path \"$DownloadDirectory/Xcode-beta.app\" -NewName \"Xcode.app\"\n    }\n\n    if (-not (Test-Path \"$DownloadDirectory/Xcode.app\")) {\n        throw \"XIP archive '$xcodeXipPath' doesn't contain 'Xcode.app'\"\n    }\n\n    Write-Host \"Moving '$DownloadDirectory/Xcode.app' to '$TargetPath'\"\n    Move-Item -Path \"$DownloadDirectory/Xcode.app\" -Destination $TargetPath\n}\n\nfunction Confirm-XcodeIntegrity {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Version\n    )\n\n    $XcodeRootPath = Get-XcodeRootPath -Version $Version\n    if (Test-XcodeStableRelease -XcodeRootPath $XcodeRootPath) {\n        Write-Host \"Validating Xcode integrity for '$XcodeRootPath'...\"\n        Invoke-ValidateCommand \"spctl --assess --raw $XcodeRootPath\"\n    }\n}\n\nfunction Approve-XcodeLicense {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Version\n    )\n\n    $os = Get-OSVersion\n    $XcodeRootPath = Get-XcodeRootPath -Version $Version\n    Write-Host \"Approving Xcode license for '$XcodeRootPath'...\"\n    $xcodeBuildPath = Get-XcodeToolPath -XcodeRootPath $XcodeRootPath -ToolName \"xcodebuild\"\n\n    if ($os.IsSonoma) {\n        Invoke-ValidateCommand -Command \"sudo $xcodeBuildPath -license accept\" -Timeout 15\n    } else {\n        Invoke-ValidateCommand -Command \"sudo $xcodeBuildPath -license accept\"\n    }\n}\n\nfunction Install-XcodeAdditionalComponents {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Version\n    )\n\n    Write-Host \"Installing additional MetalToolchain component for Xcode $Version...\"\n    $xcodeRootPath = Get-XcodeRootPath -Version $Version\n    $xcodeBuildPath = Get-XcodeToolPath -XcodeRootPath $xcodeRootPath -ToolName \"xcodebuild\"\n    Invoke-ValidateCommand \"$xcodeBuildPath -downloadComponent MetalToolchain\" | Out-Null\n}\n\nfunction Invoke-XcodeRunFirstLaunch {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Version\n    )\n\n    Write-Host \"Running 'runFirstLaunch' for Xcode $Version...\"\n    $xcodeRootPath = Get-XcodeToolPath -Version $Version -ToolName \"xcodebuild\"\n    Invoke-ValidateCommand \"sudo $xcodeRootPath -runFirstLaunch\"\n}\n\nfunction Install-XcodeAdditionalSimulatorRuntimes {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Version,\n        [Parameter(Mandatory)]\n        [string] $Arch,\n        [Parameter(Mandatory)]\n        [object] $Runtimes\n    )\n\n    Write-Host \"Installing Simulator Runtimes for Xcode $Version ...\"\n    $xcodebuildPath = Get-XcodeToolPath -Version $Version -ToolName 'xcodebuild'\n    $validRuntimes = @(\"iOS\", \"watchOS\", \"tvOS\")\n\n    # Determine architecture variant suffix for Xcode 26+\n    $archSuffix = \"\"\n    if ($Version -match '^(\\d+)\\.' -and [int]$matches[1] -ge 26) {\n        $archSuffix = \"-architectureVariant universal\"\n    }\n\n    # visionOS is only available on arm64\n    if ($Arch -eq \"arm64\") {\n        $validRuntimes += \"visionOS\"\n    }\n\n    # Install all runtimes / skip runtimes\n    if ($Runtimes -eq \"default\") {\n        Write-Host \"Installing all runtimes for Xcode $Version ...\"\n        Invoke-ValidateCommand \"$xcodebuildPath -downloadAllPlatforms $archSuffix\" | Out-Null\n        return\n    } elseif ($Runtimes -eq \"none\") {\n        Write-Host \"Skipping runtimes installation for Xcode $Version ...\"\n        return\n    }\n\n    # Convert $Runtimes to hashtable\n    if ($Runtimes -is [System.Object[]]) {\n        $convertedRuntimes = @{}\n\n        foreach ($entry in $Runtimes) {\n            if ($entry -is [PSCustomObject]) {\n                $entry = $entry | ConvertTo-Json -Compress | ConvertFrom-Json -AsHashtable\n            }\n            \n            # Copy all keys and values from the entry to the converted runtimes\n            foreach ($key in $entry.Keys) {\n                if ($convertedRuntimes.ContainsKey($key)) {\n                    $convertedRuntimes[$key] += $entry[$key]\n                } else {\n                    $convertedRuntimes[$key] = $entry[$key]\n                }\n            }\n        }\n        $Runtimes = $convertedRuntimes\n    }\n\n    # Validate runtimes format\n    if ($Runtimes -isnot [System.Collections.Hashtable]) {\n        throw \"Invalid runtime format for Xcode $(Version): Expected hashtable, got [$($Runtimes.GetType())]\"\n    }\n\n    # Install runtimes for specified platforms\n    foreach ($platform in $validRuntimes) {        \n        if (-not $Runtimes.ContainsKey($platform)) {\n            Write-Host \"No runtimes specified for $platform in the toolset for Xcode $Version, please check the toolset.\"\n            return\n        }\n        foreach ($platformVersion in $Runtimes[$platform]) {\n            switch ($platformVersion) {\n                \"skip\" {\n                    Write-Host \"Skipping $platform runtimes installation for Xcode $Version ...\"\n                    continue\n                }\n                \"default\" {\n                    Write-Host \"Installing default $platform runtime for Xcode $Version ...\"\n                    Invoke-ValidateCommand \"$xcodebuildPath -downloadPlatform $platform $archSuffix\" | Out-Null\n                    continue\n                }\n                default {\n                    # Version might be a semver or a build number\n                    if (($platformVersion -match \"^\\d{1,2}\\.\\d(\\.\\d)?$\") -or ($platformVersion -match \"^[a-zA-Z0-9]{6,8}$\")) {\n                        Write-Host \"Installing $platform $platformVersion runtime for Xcode $Version ...\"\n                        Invoke-ValidateCommand \"$xcodebuildPath -downloadPlatform $platform -buildVersion $platformVersion $archSuffix\" | Out-Null\n                        continue\n                    }\n                    throw \"$platformVersion is not a valid value for $platform version. Valid values are 'default', or 'skip', or a semver from 0.0 to 99.9.(9), or a build number.\"\n                }\n            }\n        }\n    }\n}\n\nfunction Build-XcodeSymlinks {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Version,\n        [string[]] $Symlinks\n    )\n\n    $sourcePath = Get-XcodeRootPath -Version $Version\n    $Symlinks | Where-Object { $_ } | ForEach-Object {\n        $targetPath = Get-XcodeRootPath -Version $_\n        Write-Host \"Creating symlink: '$targetPath' -> '$sourcePath'\"\n        New-Item -Path $targetPath -ItemType SymbolicLink -Value $sourcePath | Out-Null\n    }\n}\n\nfunction Initialize-XcodeLaunchServicesDb {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Version\n    )\n\n    $xcodePath = Get-XcodeRootPath -Version $Version\n    $lsregister = '/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister'\n    Get-ChildItem -Recurse -Filter \"*.app\" $xcodePath | Foreach-Object { & $lsregister -f $_.FullName}\n}\n\nfunction Build-ProvisionatorSymlink {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Version\n    )\n\n    $sourcePath = Get-XcodeRootPath -Version $Version\n    $versionInfo = Get-XcodeVersionInfo -XcodeRootPath $sourcePath\n\n    $targetVersion = [SemVer]::Parse($versionInfo.Version).ToString()\n    $targetPath = Get-XcodeRootPath -Version $targetVersion\n    if ($sourcePath -ne $targetPath) {\n        Write-Host \"Creating symlink: '$targetPath' -> '$sourcePath'\"\n        New-Item -Path $targetPath -ItemType SymbolicLink -Value $sourcePath | Out-Null\n    }\n}\n\nfunction Set-XcodeDeveloperDirEnvironmentVariables {\n    param (\n        [Parameter(Mandatory)]\n        [string[]] $XcodeList\n    )\n\n    $exactVersionsList = $XcodeList | Where-Object { Test-XcodeStableRelease -Version $_ } | ForEach-Object {\n        $xcodeRootPath = Get-XcodeRootPath -Version $_\n        $xcodeVersionInfo = Get-XcodeVersionInfo -XcodeRootPath $xcodeRootPath\n        return @{\n            RootPath = $xcodeRootPath\n            Version = [SemVer]::Parse($xcodeVersionInfo.Version)\n        }\n    } | Sort-Object -Property Version -Descending\n\n    $majorVersions = $exactVersionsList.Version.Major | Select-Object -Unique\n    $majorVersions | ForEach-Object {\n        $majorVersion = $_\n        $latestXcodeVersion = $exactVersionsList | Where-Object { $_.Version.Major -eq $majorVersion } | Select-Object -First 1\n        $variableName = \"XCODE_${_}_DEVELOPER_DIR\"\n        $variableValue = \"$($latestXcodeVersion.RootPath)/Contents/Developer\"\n        Write-Host \"Set ${variableName}=${variableValue}\"\n        \"export ${variableName}=${variableValue}\" | Out-File \"$env:HOME/.bashrc\" -Append\n    }\n}\n\nfunction Invoke-ValidateCommand {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Command,\n        [Uint] $Timeout = 0\n    )\n\n    if ($Timeout -eq 0) {\n        $output = Invoke-Expression -Command $Command\n        if ($LASTEXITCODE -ne 0) {\n            throw \"Command '$Command' has finished with exit code $LASTEXITCODE\"\n        }\n        return $output\n    } else {\n        $job = $command | Start-Job -ScriptBlock {\n            $output = Invoke-Expression -Command $input\n            if ($LASTEXITCODE -ne 0) {\n                  throw 'Command failed'\n            }\n            return $output\n        }\n        $waitObject = $job | Wait-Job -Timeout $Timeout\n\n        if (-not $waitObject) {\n             throw \"Command '$Command' has timed out\"\n        }\n\n        if ($waitObject.State -eq 'Failed') {\n             throw \"Command '$Command' has failed\"\n        }\n        Receive-Job -Job $job\n    }\n}\n\nfunction Update-DyldCache {\n    param (\n        [Parameter(Mandatory)]\n        [string] $Version\n    )\n\n    Write-Host \"Updating dyld shared cache for Xcode $Version ...\"\n    Switch-Xcode -Version $Version\n    Invoke-ValidateCommand \"xcrun simctl runtime dyld_shared_cache update --all\"\n}\n"
  },
  {
    "path": "images/macos/scripts/helpers/confirm-identified-developers-macos14.scpt",
    "content": "# This AppleScript clicks \"Allow\" for \"System Software from developer \"Parallels International GmbH\"\n# Steps:\n# - Open System Settings -> Privacy & Security\n# - Click 'Allow' for 'System Software from developer \"Parallels International GmbH'\n# - Enter password for runner\n\non run argv\n  set userpassword to item 1 of argv\n\n  tell application \"System Settings\"\n    activate\n    delay 5\n  end tell\n\n  tell application \"System Events\"\n    tell process \"System Settings\"\n      set frontmost to true\n      repeat until exists window 1\n        delay 2\n      end repeat\n\n      tell splitter group 1 of group 1 of window 1\n          select row 18 of outline 1 of scroll area 1 of group 1\n          delay 5\n          click UI Element 2 of group 5 of scroll area 1 of group 1 of group 2\n          delay 5\n          keystroke userpassword\n          delay 5\n          keystroke return\n          delay 5\n      end tell\n    end tell\n  end tell\nend run\n"
  },
  {
    "path": "images/macos/scripts/helpers/confirm-identified-developers-macos15.scpt",
    "content": "# This AppleScript clicks \"Allow\" for \"System Software from developer \"Parallels International GmbH\"\n# Steps:\n# - Open System Settings -> Privacy & Security\n# - Click 'Allow' for 'System Software from developer \"Parallels International GmbH'\n# - Enter password for runner\n\non run argv\n\tset userpassword to item 1 of argv\n\t\n\ttell application \"System Settings\"\n\t\tactivate\n\t\tdelay 5\n\tend tell\n\t\n\ttell application \"System Events\"\n\t\ttell process \"System Settings\"\n\t\t\tset frontmost to true\n\t\t\trepeat until exists window 1\n\t\t\t\tdelay 2\n\t\t\tend repeat\n\t\t\t\n\t\t\ttell splitter group 1 of group 1 of window 1\n\t\t\t\tselect row 27 of outline 1 of scroll area 1 of group 1\n\t\t\t\tdelay 5\n\t\t\t\tclick UI element 1 of row 27 of outline 1 of scroll area 1 of group 1\n\t\t\t\tdelay 5\n\t\t\t\tkeystroke userpassword\n\t\t\t\tdelay 5\n\t\t\t\tkeystroke return\n\t\t\t\tdelay 5\n\t\t\tend tell\n\t\tend tell\n\tend tell\nend run\n"
  },
  {
    "path": "images/macos/scripts/helpers/invoke-tests.sh",
    "content": "#!/bin/bash -e -o pipefail\n\nsource $HOME/.bashrc\npwsh -Command \"Import-Module '$HOME/image-generation/tests/Helpers.psm1' -DisableNameChecking\n        Invoke-PesterTests -TestFile \\\"$1\\\" -TestName \\\"$2\\\"\"\n"
  },
  {
    "path": "images/macos/scripts/helpers/utils.sh",
    "content": "#!/bin/bash -e -o pipefail\n\ndownload_with_retry() {\n    url=$1\n    download_path=$2\n\n    if [ -z \"$download_path\" ]; then\n        download_path=\"/tmp/$(basename \"$url\")\"\n    fi\n\n    echo \"Downloading package from $url to $download_path...\" >&2\n\n    interval=30\n    download_start_time=$(date +%s)\n\n    for ((retries=20; retries>0; retries--)); do\n        attempt_start_time=$(date +%s)\n        if http_code=$(curl -4sSLo \"$download_path\" \"$url\" -w '%{http_code}'); then\n            attempt_seconds=$(($(date +%s) - attempt_start_time))\n            if [ \"$http_code\" -eq 200 ]; then\n                echo \"Package downloaded in $attempt_seconds seconds\" >&2\n                break\n            else\n                echo \"Received HTTP status code $http_code after $attempt_seconds seconds\" >&2\n            fi\n        else\n            attempt_seconds=$(($(date +%s) - attempt_start_time))\n            echo \"Package download failed in $attempt_seconds seconds\" >&2\n        fi\n\n        if [ $retries -eq 0 ]; then\n            total_seconds=$(($(date +%s) - download_start_time))\n            echo \"Package download failed after $total_seconds seconds\" >&2\n            exit 1\n        fi\n\n        echo \"Waiting $interval seconds before retrying (retries left: $retries)...\" >&2\n        sleep $interval\n    done\n\n    echo \"$download_path\"\n}\n\nis_Arm64() {\n    [ \"$(arch)\" = \"arm64\" ]\n}\n\nis_Tahoe() {\n    [ \"$OSTYPE\" = \"darwin25\" ]\n}\n\nis_TahoeArm64() {\n    is_Tahoe && is_Arm64\n}\n\nis_TahoeX64() {\n    is_Tahoe && ! is_Arm64\n}\n\nis_Sequoia() {\n    [ \"$OSTYPE\" = \"darwin24\" ]\n}\n\nis_SequoiaArm64() {\n    is_Sequoia && is_Arm64\n}\n\nis_SequoiaX64() {\n    is_Sequoia && ! is_Arm64\n}\n\nis_Sonoma() {\n    [ \"$OSTYPE\" = \"darwin23\" ]\n}\n\nis_SonomaArm64() {\n    is_Sonoma && is_Arm64\n}\n\nis_SonomaX64() {\n    is_Sonoma && ! is_Arm64\n}\n\nget_toolset_value() {\n    local toolset_path=$(echo \"$IMAGE_FOLDER/toolset.json\")\n    local query=$1\n    echo \"$(jq -r \"$query\" $toolset_path)\"\n}\n\n# brew provides package bottles for different macOS versions\n# The 'brew install' command will fail if a package bottle does not exist\n# Use the '--build-from-source' option to build from source in this case\nbrew_smart_install() {\n    local tool_name=$1\n\n    echo \"Downloading $tool_name...\"\n\n    # get deps & cache em\n\n    failed=true\n    for i in {1..10}; do\n        brew deps $tool_name > /tmp/$tool_name && failed=false || sleep 60\n        [ \"$failed\" = false ] && break\n    done\n\n    if [ \"$failed\" = true ]; then\n       echo \"Failed: brew deps $tool_name\"\n       exit 1;\n    fi\n\n    for dep in $(cat /tmp/$tool_name) $tool_name; do\n\n    failed=true\n    for i in {1..10}; do\n        brew --cache $dep >/dev/null && failed=false || sleep 60\n        [ \"$failed\" = false ] && break\n    done\n\n    if [ \"$failed\" = true ]; then\n       echo \"Failed: brew --cache $dep\"\n       exit 1;\n    fi\n    done\n\n    failed=true\n    for i in {1..10}; do\n        brew install $tool_name && failed=false || sleep 60\n        [ \"$failed\" = false ] && break\n    done\n\n    if [ \"$failed\" = true ]; then\n       echo \"Failed: brew install $tool_name\"\n       exit 1;\n    fi\n}\n\nconfigure_system_tccdb () {\n    local values=$1\n    local dbPath=\"/Library/Application Support/com.apple.TCC/TCC.db\"\n    local sqlQuery=\"INSERT OR IGNORE INTO access VALUES($values);\"\n    sudo sqlite3 \"$dbPath\" \"$sqlQuery\"\n}\n\nconfigure_user_tccdb () {\n    local values=$1\n    local dbPath=\"$HOME/Library/Application Support/com.apple.TCC/TCC.db\"\n    local sqlQuery=\"INSERT OR IGNORE INTO access VALUES($values);\"\n    sqlite3 \"$dbPath\" \"$sqlQuery\"\n}\n\nresolve_github_release_asset_url() {\n    local repo=$1\n    local filter=$2\n    local version=${3:-\"*\"}\n    local api_pat=$4\n\n    page_size=\"100\"\n\n    [ -n \"$api_pat\" ] && authString=(-H \"Authorization: token ${api_pat}\")\n\n    json=$(curl \"${authString[@]}\" -fsSL \"https://api.github.com/repos/${repo}/releases?per_page=${page_size}\")\n\n    if [[ $version == \"latest\" ]]; then\n        tag_name=$(echo $json | jq -r '.[].tag_name' | sort --unique --version-sort | egrep -v \".*-[a-z]|beta\" | tail -n 1)\n    elif [[ $version == *\"*\"* ]]; then\n        tag_name=$(echo $json | jq -r '.[].tag_name' | sort --unique --version-sort | egrep -v \".*-[a-z]|beta\" | egrep \"${version}\" | tail -n 1)\n    else\n        tag_name=$(echo $json | jq -r '.[].tag_name' | sort --unique --version-sort | egrep -v \".*-[a-z]|beta\" | egrep \"${version}\")\n    fi\n\n    download_url=$(echo $json | jq -r \".[] | select(.tag_name==\\\"${tag_name}\\\").assets[].browser_download_url | select(${filter})\" | head -n 1)\n    if [ -z \"$download_url\" ]; then\n        echo \"Failed to parse a download url for the '${tag_name}' tag using '${filter}' filter\"\n        exit 1\n    fi\n\n    echo $download_url\n}\n\n# Close all finder windows because they can interfere with UI tests\nclose_finder_window() {\n    osascript -e 'tell application \"Finder\" to close windows'\n}\n\nget_arch() {\n    arch=$(arch)\n    if [[ $arch == \"arm64\" ]]; then\n        echo \"arm64\"\n    else\n        echo \"x64\"\n    fi\n}\n\nuse_checksum_comparison() {\n    local file_path=$1\n    local checksum=$2\n    local sha_type=${3:-\"256\"}\n\n    echo \"Performing checksum verification\"\n\n    if [[ ! -f \"$file_path\" ]]; then\n        echo \"File not found: $file_path\"\n        exit 1\n    fi\n\n    local_file_hash=$(shasum --algorithm \"$sha_type\" \"$file_path\" | awk '{print $1}')\n\n    if [[ \"$local_file_hash\" != \"$checksum\" ]]; then\n        echo \"Checksum verification failed. Expected hash: $checksum; Actual hash: $local_file_hash.\"\n        exit 1\n    else\n        echo \"Checksum verification passed\"\n    fi\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/ActionArchiveCache.Tests.ps1",
    "content": "Describe \"ActionArchiveCache\" {\n    Context \"Action archive cache directory not empty\" {\n        It \"$HOME/actionarchivecache not empty\" {\n            (Get-ChildItem -Path \"$env:HOME/actionarchivecache/*.tar.gz\" -Recurse).Count | Should -BeGreaterThan 0\n        }\n    }\n\n    Context \"Action tarball not empty\" {\n        $testCases = Get-ChildItem -Path \"$env:HOME/actionarchivecache/*.tar.gz\" -Recurse | ForEach-Object { @{ ActionTarball = $_.FullName } }\n        It \"<ActionTarball>\" -TestCases $testCases {\n            param ([string] $ActionTarball)\n            (Get-Item \"$ActionTarball\").Length | Should -BeGreaterThan 0\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Android.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\nImport-Module \"$PSScriptRoot/../software-report/SoftwareReport.Android.psm1\" -DisableNameChecking\n\n$os = Get-OSVersion\n\nDescribe \"Android\" {\n    $androidSdkManagerPackages = Get-AndroidPackages\n    [int]$platformMinVersion = (Get-ToolsetContent).android.platform_min_version\n    [version]$buildToolsMinVersion = (Get-ToolsetContent).android.build_tools_min_version\n    [array]$ndkVersions = (Get-ToolsetContent).android.ndk.versions\n    $ndkFullVersions = $ndkVersions | ForEach-Object { Get-ChildItem \"$env:ANDROID_HOME/ndk/${_}.*\" -Name | Select-Object -Last 1 } | ForEach-Object { \"ndk/${_}\" }\n    # Platforms starting with a letter are the preview versions, which is not installed on the image\n    $platformVersionsList = ($androidSdkManagerPackages | Where-Object { \"$_\".StartsWith(\"platforms;\") }) -replace 'platforms;android-', '' | Where-Object { $_ -match \"^\\d\" } | Sort-Object -Unique\n    $platformsInstalled = $platformVersionsList | Where-Object { [int]($_.Split(\"-\")[0]) -ge $platformMinVersion } | ForEach-Object { \"platforms/android-${_}\" }\n\n    $buildToolsList = ($androidSdkManagerPackages | Where-Object { \"$_\".StartsWith(\"build-tools;\") }) -replace 'build-tools;', ''\n    $buildTools = $buildToolsList | Where-Object { $_ -match \"\\d+(\\.\\d+){2,}$\" } | Where-Object { [version]$_ -ge $buildToolsMinVersion } | Sort-Object -Unique |\n        ForEach-Object { \"build-tools/${_}\" }\n\n    $androidPackages = @(\n        \"tools\",\n        \"platform-tools\",\n        \"cmake\",\n        $platformsInstalled,\n        $buildTools,\n        $ndkFullVersions,\n        ((Get-ToolsetContent).android.extras | ForEach-Object { \"extras/${_}\" }),\n        ((Get-ToolsetContent).android.addons | ForEach-Object { \"add-ons/${_}\" }),\n        ((Get-ToolsetContent).android.additional_tools)\n    ) | ForEach-Object { $_ }\n\n    # Remove empty strings from array to avoid possible issues\n    $androidPackages = $androidPackages | Where-Object { $_ }\n\n    BeforeAll {\n        $ANDROID_SDK_DIR = Join-Path $env:HOME \"Library\" \"Android\" \"sdk\"\n\n        function Confirm-AndroidPackage {\n            param (\n                [Parameter(Mandatory = $true)]\n                [string] $PackageName\n            )\n\n            # Convert 'm2repository;com;android;support;constraint;constraint-layout-solver;1.0.0-beta1' ->\n            #         'm2repository/com/android/support/constraint/constraint-layout-solver/1.0.0-beta1'\n            $PackageName = $PackageName.Replace(\";\", \"/\")\n            $targetPath = Join-Path $ANDROID_SDK_DIR $PackageName\n            $targetPath | Should -Exist\n        }\n    }\n\n    Context \"SDKManagers\" {\n        $testCases = @(\n            @{\n                PackageName = \"Command-line tools\"\n                Sdkmanager  = \"$env:ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager\"\n            }\n        )\n\n        It \"Sdkmanager from <PackageName> is available\" -TestCases $testCases {\n            \"$Sdkmanager --version\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    Context \"Packages\" {\n        $testCases = $androidPackages | ForEach-Object { @{ PackageName = $_ } }\n\n        It \"<PackageName>\" -TestCases $testCases {\n            param ([string] $PackageName)\n            Confirm-AndroidPackage $PackageName\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/BasicTools.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\n$os = Get-OSVersion\n\nDescribe \"Azure CLI\" {\n    It \"Azure CLI\" {\n        \"az -v\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Azure DevOps CLI\" {\n    It \"az devops\" {\n        \"az devops -h\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Carthage\" {\n    It \"Carthage\" {\n        \"carthage version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"cmake\" {\n    It \"cmake\" {\n        \"cmake --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"SwiftFormat\" {\n    It \"SwiftFormat\" {\n        \"swiftformat --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"GnuPG\" {\n    It \"GnuPG\" {\n        \"gpg --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"zstd\" {\n    It \"zstd\" {\n        \"zstd --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Packer\" {\n    It \"Packer\" {\n        \"packer --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Perl\" {\n    It \"Perl\" {\n        \"perl -e 'print substr($^V,1)'\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Tcl/Tk\" -Skip:($os.IsArm64) {\n    It \"libtcl\" {\n        Test-Path \"/usr/local/lib/libtcl8.6.dylib\" | Should -BeTrue\n        Test-Path \"/usr/local/lib/libtk8.6.dylib\" | Should -BeTrue\n    }\n}\n\nDescribe \"bazelisk\" {\n    It \"bazelisk\" {\n        \"bazelisk version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"GitHub CLI\" {\n    It \"GitHub CLI\" {\n        \"gh --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"7-Zip\" {\n    It \"7-Zip\" {\n        \"7z i\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Apache Ant\" {\n    It \"Apache Ant\" {\n        \"ant -version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Aria2\" {\n    It \"Aria2\" {\n        \"aria2c --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"GNU Tar\" {\n    It \"GNU Tar\" {\n        \"gtar --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"bazel\" {\n    It \"bazel\" {\n        \"bazel --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"jq\" {\n    It \"jq\" {\n        \"jq --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"curl\" {\n    It \"curl\" {\n        \"curl --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"wget\" {\n    It \"wget\" {\n        \"wget --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Homebrew\" {\n    It \"Homebrew\" {\n        \"brew --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Kotlin\" {\n    $kotlinPackages = @(\"kapt\", \"kotlin\", \"kotlinc\", \"kotlinc-jvm\", \"kotlinc-js\")\n\n    It \"<toolName> is available\" -TestCases ($kotlinPackages | ForEach-Object { @{ toolName = $_ } }) {\n        \"$toolName -help\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"yq\" {\n    It \"yq\" {\n        \"yq --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"pkgconf\" {\n    It \"pkgconf\" {\n        \"pkgconf --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Ninja\" {\n    New-item -Path \"/tmp/ninjaproject\" -ItemType Directory -Force\n    Set-Location '/tmp/ninjaproject'\n@'\ncmake_minimum_required(VERSION 3.10)\nproject(NinjaTest NONE)\n'@ | Out-File -FilePath \"./CMakeLists.txt\"\n\n    It \"Make a simple ninja project\" {\n    \"cmake -GNinja /tmp/ninjaproject\" | Should -ReturnZeroExitCode\n    }\n\n    It \"build.ninja file should exist\" {\n        $buildFilePath = Join-Path \"/tmp/ninjaproject\" \"build.ninja\"\n        $buildFilePath | Should -Exist\n    }\n\n    It \"Ninja\" {\n        \"ninja --version\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Browsers.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\n$os = Get-OSVersion\n\nDescribe \"Chrome\" {\n    BeforeAll {\n        $chromeLocation = \"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\"\n        $chromeForTestingLocation = \"/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing\"\n    }\n\n    It \"Chrome\" {\n        $chromeLocation | Should -Exist\n        \"'$chromeLocation' --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Chrome for Testing\" {\n        $chromeForTestingLocation | Should -Exist\n        \"'$chromeForTestingLocation' --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Chrome Driver\" {\n        \"chromedriver --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Chrome for Testing and Chrome Driver major versions are the same\" {\n        $chromeMajor = (& $chromeForTestingLocation --version).Trim(\"Google Chrome for Testing \").Split(\".\")[0]\n        $chromeDriverMajor = (chromedriver --version).Trim(\"ChromeDriver \").Split(\".\")[0]\n        $chromeMajor | Should -BeExactly $chromeDriverMajor\n    }\n}\n\nDescribe \"Selenium server\" {\n    It \"Selenium server\" {\n        $os = Get-OSVersion\n        if ($os.IsArm64) {\n            $cellarPath = \"/opt/homebrew/Cellar\"\n        } else {\n            $cellarPath = \"/usr/local/Cellar\"\n        }\n        (Get-ChildItem -Path \"$cellarPath/selenium-server*/*\").Name | Should -BeLike \"4.*\"\n    }\n}\n\nDescribe \"Edge\" {\n    It \"Microsoft Edge\" {\n        $edgeLocation = \"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge\"\n        $edgeLocation | Should -Exist\n        \"'$edgeLocation' --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Microsoft Edge Driver\" {\n        \"msedgedriver --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Firefox\" {\n    It \"Firefox\" {\n        $firefoxLocation = \"/Applications/Firefox.app/Contents/MacOS/firefox\"\n        $firefoxLocation | Should -Exist\n        \"'$firefoxLocation' --version\" | Should -ReturnZeroExitCode\n    }\n    It \"Geckodriver\" {\n        \"geckodriver --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Safari\" {\n    It \"'Allow Remote Automation' option is activated\" {\n        $plistPath = \"$env:HOME/Library/WebDriver/com.apple.Safari.plist\"\n        $command = \"/usr/libexec/PlistBuddy -c 'print AllowRemoteAutomation' $plistPath\"\n        $plistPath | Should -Exist\n        $commandResult = Get-CommandResult $command\n        $commandResult.ExitCode | Should -Be 0\n        $commandResult.Output | Should -Be \"true\"\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Common.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\r\nImport-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\r\n\r\n$os = Get-OSVersion\r\n\r\nDescribe \".NET\" {\r\n    It \".NET\" {\r\n        \"dotnet --version\" | Should -ReturnZeroExitCode\r\n    }\r\n}\r\n\r\nDescribe \"GCC\" {\r\n    $testCases = (Get-ToolsetContent).gcc.versions | ForEach-Object { @{Version = $_ } }\r\n\r\n    It \"GCC <Version>\" -TestCases $testCases {\r\n        param (\r\n            [string] $Version\r\n        )\r\n\r\n        \"gcc-$Version --version\" | Should -ReturnZeroExitCode\r\n    }\r\n\r\n    It \"Gfortran <Version>\" -TestCases $testCases {\r\n        param (\r\n            [string] $Version\r\n        )\r\n\r\n        \"gfortran-$Version --version\" | Should -ReturnZeroExitCode\r\n    }\r\n\r\n    It \"Gfortran is not found in the default path\" {\r\n        \"$(which gfortran)\" | Should -BeNullOrEmpty\r\n    }\r\n}\r\n\r\nDescribe \"vcpkg\" {\r\n    It \"vcpkg\" {\r\n        \"vcpkg version\" | Should -ReturnZeroExitCode\r\n    }\r\n}\r\n\r\nDescribe \"AWS\" {\r\n    It \"AWS CLI\" {\r\n        \"aws --version\" | Should -ReturnZeroExitCode\r\n    }\r\n    It \"AWS SAM CLI\" {\r\n        \"sam --version\" | Should -ReturnZeroExitCode\r\n    }\r\n\r\n    It \"AWS Session Manager CLI\" {\r\n        \"session-manager-plugin --version\" | Should -ReturnZeroExitCode\r\n    }\r\n}\r\n\r\nDescribe \"AzCopy\" {\r\n    It \"AzCopy\" {\r\n        \"azcopy --version\" | Should -ReturnZeroExitCode\r\n    }\r\n}\r\n\r\nDescribe \"CocoaPods\" {\r\n    It \"CocoaPods\" {\r\n        \"pod --version\" | Should -ReturnZeroExitCode\r\n    }\r\n}\r\n\r\nDescribe \"Bicep\" {\r\n    It \"Bicep\" {\r\n        \"bicep --version\" | Should -ReturnZeroExitCode\r\n    }\r\n}\r\n\r\nDescribe \"CodeQL Bundle\" {\r\n    It \"Is installed\" {\r\n        $CodeQLVersionWildcard = Join-Path $Env:AGENT_TOOLSDIRECTORY -ChildPath \"CodeQL\" | Join-Path -ChildPath \"*\"\r\n        $CodeQLVersionPath = Get-ChildItem $CodeQLVersionWildcard | Select-Object -First 1 -Expand FullName\r\n        $CodeQLPath = Join-Path $CodeQLVersionPath -ChildPath \"x64\" | Join-Path -ChildPath \"codeql\" | Join-Path -ChildPath \"codeql\"\r\n        \"$CodeQLPath version --quiet\" | Should -ReturnZeroExitCode\r\n\r\n        $CodeQLPacksPath = Join-Path $CodeQLVersionPath -ChildPath \"x64\" | Join-Path -ChildPath \"codeql\" | Join-Path -ChildPath \"qlpacks\"\r\n        $CodeQLPacksPath | Should -Exist\r\n    }\r\n}\r\n\r\nDescribe \"Unxip\" {\r\n    It \"Unxip\" {\r\n        \"unxip --version\" | Should -ReturnZeroExitCode\r\n    }\r\n}\r\n\r\nDescribe \"Sudoers\" {\r\n    It \"Sudo Cache\" {\r\n        \"sudo -v\" | Should -ReturnZeroExitCode\r\n    }\r\n    It \"Sudoers files\" {\r\n        \"sudo visudo -c\" | Should -ReturnZeroExitCode\r\n    }\r\n}\r\n"
  },
  {
    "path": "images/macos/scripts/tests/Git.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\n$os = Get-OSVersion\n\nDescribe \"Git\" {\n    It \"git is installed\" {\n        \"git --version\" | Should -ReturnZeroExitCode\n    }\n    It \"git lfs is installed\" {\n        \"git lfs version\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Helpers.psm1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\nfunction Confirm-ArrayWithoutDuplicates {\n    param (\n        [AllowEmptyCollection()]\n        [Parameter(Mandatory = $true)]\n        [array] $Items,\n        [string] $Because\n    )\n    $uniqueList = @()\n    $Items | ForEach-Object {\n        $uniqueList | Should -Not -Contain $_ -Because $Because\n        $uniqueList += $_\n    }\n}\n\nfunction Confirm-UrlAvailability {\n    param (\n        [Parameter(Mandatory)]\n        [ValidateNotNullOrEmpty()]\n        [string] $Url\n    )\n\n    $result = $true\n    try {\n        $requestResult = Invoke-WebRequest $Url -DisableKeepAlive -UseBasicParsing -Method Head\n        $result = ($requestResult.StatusCode -eq 200)\n    } catch {\n        $result = $false\n    }\n\n    $result | Should -BeTrue -Because \"'$Url' should be available, but it is not\"\n}\n\nfunction Confirm-IdenticalFileContent {\n    param (\n        [Parameter(Mandatory)]\n        [string] $File1,\n        [Parameter(Mandatory)]\n        [string] $File2\n    )\n\n    $File1 | Should -Exist -Because \"The content of '$File1' should be identical with content of '$File2' but '$File1' doesn't exist\"\n    $File2 | Should -Exist -Because \"The content of '$File1' should be identical with content of '$File2' but '$File2' doesn't exist\"\n\n    $content1 = Get-Content -Raw $File1\n    $content2 = Get-Content -Raw $File2\n    $content1 | Should -Be $content2 -Because \"The content of '$File1' should be identical with content of '$File2' but they are different\"\n}\n\nfunction ShouldReturnZeroExitCode {\n    Param (\n        [String] $ActualValue,\n        [switch] $Negate,\n        [string] $Because # This parameter is unused by we need it to match Pester asserts signature\n    )\n\n    $result = Get-CommandResult $ActualValue\n\n    [bool]$succeeded = $result.ExitCode -eq 0\n    if ($Negate) { $succeeded = -not $succeeded }\n\n    if (-not $succeeded) {\n        $commandOutputIndent = \" \" * 4\n        $commandOutput = ($result.Output | ForEach-Object { \"${commandOutputIndent}${_}\" }) -join \"`n\"\n        $failureMessage = \"Command '${ActualValue}' has finished with exit code ${actualExitCode}`n${commandOutput}\"\n    }\n\n    return [PSCustomObject] @{\n        Succeeded      = $succeeded\n        FailureMessage = $failureMessage\n    }\n}\n\nfunction ShouldMatchCommandOutput {\n    Param (\n        [String] $ActualValue,\n        [String] $RegularExpression,\n        [switch] $Negate\n    )\n\n    $output = (Get-CommandResult $ActualValue).Output | Out-String\n    [bool] $succeeded = $output -cmatch $RegularExpression\n\n    if ($Negate) {\n        $succeeded = -not $succeeded\n    }\n\n    $failureMessage = ''\n\n    if (-not $succeeded) {\n        if ($Negate) {\n            $failureMessage = \"Expected regular expression '$RegularExpression' for '$ActualValue' command to not match '$output', but it did match.\"\n        }\n        else {\n            $failureMessage = \"Expected regular expression '$RegularExpression' for '$ActualValue' command to match '$output', but it did not match.\"\n        }\n    }\n\n    return [PSCustomObject] @{\n        Succeeded      = $succeeded\n        FailureMessage = $failureMessage\n    }\n}\n\nIf (Get-Command -Name Add-ShouldOperator -ErrorAction SilentlyContinue) {\n    Add-ShouldOperator -Name ReturnZeroExitCode -InternalName ShouldReturnZeroExitCode -Test ${function:ShouldReturnZeroExitCode}\n    Add-ShouldOperator -Name MatchCommandOutput -InternalName ShouldMatchCommandOutput -Test ${function:ShouldMatchCommandOutput}\n}\n\nfunction Invoke-PesterTests {\n    Param (\n        [Parameter(Mandatory)][string] $TestFile,\n        [string] $TestName\n    )\n\n    $testPath = \"$HOME/image-generation/tests/${TestFile}.Tests.ps1\"\n    if (-not (Test-Path $testPath)) {\n        throw \"Unable to find test file '$TestFile' on '$testPath'.\"\n    }\n\n    # Check that Pester module is imported\n    if (!(Get-Module \"Pester\")) {\n        Import-Module Pester\n    }\n\n    $configuration = [PesterConfiguration] @{\n        Run = @{ Path = $testPath; PassThru = $true }\n        Output = @{ Verbosity = \"Detailed\"; RenderMode = \"Plaintext\" }\n    }\n    if ($TestName) {\n        $configuration.Filter.FullName = $TestName\n    }\n\n    # Switch ErrorActionPreference to make sure that tests will fail on silent errors too\n    $backupErrorActionPreference = $ErrorActionPreference\n    $ErrorActionPreference = \"Stop\"\n    $results = Invoke-Pester -Configuration $configuration\n    $ErrorActionPreference = $backupErrorActionPreference\n\n    # Fail in case if no tests are run\n    if (-not ($results -and ($results.FailedCount -eq 0) -and ($results.PassedCount -gt 0))) {\n        $results\n        throw \"Test run has failed\"\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Java.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\n\n$os = Get-OSVersion\n$arch = Get-Architecture\n\nfunction Get-NativeVersionFormat {\n    param($Version)\n    if ($Version -in \"8\") {\n        return \"1.${Version}\"\n    }\n    return $Version\n}\n\nDescribe \"Java\" {\n    BeforeAll {\n        function Confirm-JavaVersion {\n            param($JavaCommand, $ExpectedVersion)\n\n            $commandResult = Get-CommandResult $JavaCommand\n            $commandResult.ExitCode | Should -Be 0\n            $matchResult = $commandResult.Output | Select-String '^openjdk version \\\"([\\d\\._]+)\\\"'\n            $matchResult.Matches.Success | Should -BeTrue\n            $version = $matchResult.Matches.Groups[1].Value\n            $version | Should -BeLike \"${ExpectedVersion}*\"\n        }\n    }\n\n    $toolsetJava = (Get-ToolsetContent).java\n    $defaultVersion = $toolsetJava.$arch.default\n    $jdkVersions = $toolsetJava.$arch.versions\n\n    if ($os.IsArm64) {\n        $testCases = $jdkVersions | ForEach-Object { @{ Title = $_; Version = (Get-NativeVersionFormat $_); EnvVariable = \"JAVA_HOME_${_}_arm64\" } }\n    } else {\n        $testCases = $jdkVersions | ForEach-Object { @{ Title = $_; Version = (Get-NativeVersionFormat $_); EnvVariable = \"JAVA_HOME_${_}_X64\" } }\n    }\n    $testCases += @{ Title = \"Default\"; Version = (Get-NativeVersionFormat $defaultVersion); EnvVariable = \"JAVA_HOME\" }\n\n    $testCases | ForEach-Object {\n        Context $_.Title {\n            It \"Version is found by 'java_home'\" -TestCases $_ {\n                \"/usr/libexec/java_home -v${Version}\" | Should -ReturnZeroExitCode\n            }\n\n            It \"Java <Version>\" -TestCases $_ {\n                $envVariablePath = [System.Environment]::GetEnvironmentVariable($EnvVariable)\n                $javaBinPath = Join-Path $envVariablePath \"/bin/java\"\n                Confirm-JavaVersion -JavaCommand \"$javaBinPath -version\" -ExpectedVersion $Version\n            }\n\n            if ($_.Title -eq \"Default\") {\n                It \"Version is default\" -TestCases $_ {\n                    Confirm-JavaVersion -JavaCommand \"java -version\" -ExpectedVersion $Version\n                }\n            }\n        }\n    }\n\n    Context \"Maven\" {\n        Describe \"Maven\" {\n            It \"Maven\" {\n                \"mvn --version\" | Should -ReturnZeroExitCode\n            }\n        }\n    }\n\n    Context \"Gradle\" {\n        Describe \"Gradle\" {\n            It \"Gradle is installed\" {\n                \"gradle --version\" | Should -ReturnZeroExitCode\n            }\n\n            It \"Gradle is installed to /usr/local/bin\" -Skip:($os.IsArm64) {\n                (Get-Command \"gradle\").Path | Should -BeExactly \"/usr/local/bin/gradle\"\n            }\n\n            It \"Gradle is installed to /opt/homebrew/bin/gradle\" -Skip:(-not $os.IsArm64) {\n                (Get-Command \"gradle\").Path | Should -BeExactly \"/opt/homebrew/bin/gradle\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/LLVM.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\nDescribe \"Clang/LLVM\" {\n    BeforeAll {\n        $toolsetVersion = (Get-ToolsetContent).llvm.version\n    }\n\n    It \"Clang/LLVM <toolsetVersion> is installed and version is correct\" {\n        $clangVersion = & \"$(brew --prefix llvm@$toolsetVersion)/bin/clang\" --version\n        $clangVersion[0] | Should -BeLike \"*${toolsetVersion}*\"\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Linters.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\n\n$os = Get-OSVersion\n\nDescribe \"SwiftLint\" -Skip:($os.IsArm64) {\n    It \"SwiftLint\" {\n        \"swiftlint version\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Mono.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\n\n$os = Get-OSVersion\n\nBeforeAll {\n    function Get-ShortSymlink {\n        param (\n            [string] $Version\n        )\n\n        $versionParts = $Version.Split(\".\")\n        return [String]::Join(\".\", $versionParts[0..1])\n    }\n}\n\nif ($os.IsSonoma) {\n    Describe \"Mono\" {\n        $MONO_VERSIONS_PATH = \"/Library/Frameworks/Mono.framework/Versions\"\n        $monoToolsetVersion = @((Get-ToolsetContent).mono.framework.version)\n        $versionFolderPath = Join-Path $MONO_VERSIONS_PATH ([System.Version]::Parse($monoToolsetVersion).ToString(3))\n        $testCase = @{ MonoVersion = $monoToolsetVersion; VersionFolderPath = $versionFolderPath; MonoVersionsPath = $MONO_VERSIONS_PATH }\n\n        It \"is installed\" -TestCases $testCase {\n            param ( [string] $VersionFolderPath )\n\n            $monoBinPath = Join-Path $VersionFolderPath \"bin\" \"mono\"\n            $VersionFolderPath | Should -Exist\n            $monoBinPath | Should -Exist\n        }\n\n        It \"is available via short link\" -TestCases $testCase {\n            param (\n                [string] $MonoVersion,\n                [string] $MonoVersionsPath,\n                [string] $VersionFolderPath\n            )\n\n            $shortSymlink = Get-ShortSymlink $MonoVersion # only 'major.minor'\n            $shortSymlinkFolderPath = Join-Path $MonoVersionsPath $shortSymlink\n            if ($shortSymlink -eq \"4.8\") { return } # Skip this test for Mono 4.8 because it doesn't contain VERSION file\n            $shortVersionPath = Join-Path $shortSymlinkFolderPath \"VERSION\"\n            $fullVersionPath = Join-Path $VersionFolderPath \"VERSION\"\n            Confirm-IdenticalFileContent -File1 $shortVersionPath -File2 $fullVersionPath\n        }\n\n        It \"NUnit console is installed\" -TestCases $testCase {\n            param ( [string] $VersionFolderPath )\n\n            $nunitPath = Join-Path $VersionFolderPath \"Commands\" \"nunit3-console\"\n            $nunitPath | Should -Exist\n        }\n\n        It \"Nuget is installed\" -TestCases $testCase {\n            param ( [string] $VersionFolderPath )\n\n            $nugetBinaryPath = Join-Path $VersionFolderPath \"lib\" \"mono\" \"nuget\" \"nuget.exe\"\n            $nugetBinaryWrapperPath = Join-Path $VersionFolderPath \"bin\" \"nuget\"\n            $nugetCommandPath = Join-Path $VersionFolderPath \"Commands\" \"nuget\"\n            $nugetBinaryPath | Should -Exist\n            $nugetCommandPath | Should -Exist\n            $nugetBinaryWrapperPath | Should -Exist\n        }\n\n        It \"Nuget is valid\" -TestCases $testCase {\n            param ( [string] $VersionFolderPath )\n\n            $nugetBinaryWrapperPath = Join-Path $VersionFolderPath \"bin\" \"nuget\"\n            \"$nugetBinaryWrapperPath\" | Should -ReturnZeroExitCode\n        }\n\n        It \"MSBuild is available\" {\n            \"msbuild -version\" | Should -ReturnZeroExitCode\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Node.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\n\n$os = Get-OSVersion\n\nDescribe \"Node.js\" {\n    It \"Node.js is installed\" {\n        \"node --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Node.js version should correspond to the version in the toolset\" {\n        node --version | Should -BeLike \"v$((Get-ToolsetContent).node.default)*\"\n    }\n\n    It \"NPM is installed\" {\n        \"npm --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Yarn is installed\" {\n        \"yarn --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Global NPM Packages\" {\n    $globalNpmPackages = (Get-ToolsetContent).npm.global_packages\n    $globalNpmPackagesWithTests = $globalNpmPackages | Where-Object { $_.test } | ForEach-Object { @{ Name = $_.name; Test = $_.test } }\n\n    It \"<Name>\" -TestCases $globalNpmPackagesWithTests {\n        $Test | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/OpenSSL.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\n$os = Get-OSVersion\n\nDescribe \"OpenSSL\" {\n    Context \"OpenSSL Version\" {\n        It \"OpenSSL is available\" {\n            \"openssl version\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    Context \"OpenSSL 1.1 Path Check\" -Skip:($os.IsTahoe) {\n        It \"OpenSSL 1.1 path exists\" {\n            $openSSLpath = brew --prefix openssl@1.1\n            $openSSLpath | Should -Exist\n        }\n    }\n\n    Context \"OpenSSL 1.1 is default\" -Skip:($os.IsTahoe) {\n        It \"Default OpenSSL version is 1.1\" {\n            $commandResult = Get-CommandResult \"openssl version\"\n            $commandResult.Output | Should -Match \"OpenSSL 1.1\"\n        }\n    }\n\n    Context \"OpenSSL 3 Path Check\" -Skip:(-not $os.IsTahoe) {\n        It \"OpenSSL 3 path exists\" {\n            $openSSLpath = brew --prefix openssl@3\n            $openSSLpath | Should -Exist\n        }\n    }\n\n    Context \"OpenSSL 3 is default\" -Skip:(-not $os.IsTahoe) {\n        It \"Default OpenSSL version is 3\" {\n            $commandResult = Get-CommandResult \"openssl version\"\n            $commandResult.Output | Should -Match \"OpenSSL 3\"\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/PHP.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\n$os = Get-OSVersion\n\nDescribe \"PHP\" {\n    Context \"PHP\" -Skip:($os.IsArm64) {\n        It \"PHP Path\" {\n            Get-ToolPath \"php\" | Should -Not -BeLike \"/usr/bin/php*\"\n        }\n        It \"PHP version\" {\n            $phpVersionToolset = (Get-ToolsetContent).php.version\n            $phpInstalledVersion = php --version | Out-String | Select-String \"${phpVersionToolset}\"\n            $phpInstalledVersion | Should -BeLike \"PHP ${phpVersionToolset}*\"\n        }\n    }\n\n    Context \"Composer\" -Skip:($os.IsArm64) {\n        It \"Composer\" {\n            \"composer --version\" | Should -ReturnZeroExitCode\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Powershell.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\n\nDescribe \"Powershell\" {\n    Context \"Powershell is installed\" {\n        It \"Powershell is installed\" {\n            \"pwsh -v\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    Context \"Powershell Modules\" {\n        $modules = (Get-ToolsetContent).powershellModules\n        $withoutVersionsModules = $modules | Where-Object {-not $_.versions} | ForEach-Object {\n            @{moduleName = $_.name}\n        }\n\n        $withVersionsModules = $modules | Where-Object {$_.versions} | ForEach-Object {\n            $moduleName = $_.name\n            $_.versions | ForEach-Object {\n                @{moduleName = $moduleName; expectedVersion = $_}\n            }\n        }\n\n        It \"<moduleName> is installed\" -TestCases $withoutVersionsModules {\n            param (\n                [string] $moduleName\n            )\n\n            Get-Module -Name $moduleName -ListAvailable | Should -BeTrue\n        }\n\n        if ($withVersionsModules) {\n            It \"<moduleName> with <expectedVersion> is installed\" -TestCases $withVersionsModules {\n                param (\n                    [string] $moduleName,\n                    [string] $expectedVersion\n                )\n\n                (Get-Module -Name $moduleName -ListAvailable).Version -contains $expectedVersion | Should -BeTrue\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Python.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\n\n$os = Get-OSVersion\n\nDescribe \"Python3\" {\n    It \"Python 3 is available\" {\n        \"python3 --version\" | Should -ReturnZeroExitCode\n    }\n    \n    if ($os.IsArm64) {\n        It \"Python 3 is installed under /opt/homebrew/bin/\" {\n            Get-ToolPath \"python3\" | Should -BeLike \"/opt/homebrew/bin/*\"\n        }\n    } else {\n        It \"Python 3 is installed under /usr/local/bin\" {\n            Get-ToolPath \"python3\" | Should -BeLike \"/usr/local/bin/*\"\n        }\n    }\n\n    It \"Pip 3 is available\" {\n        \"pip3 --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Pipx is available\" {\n        \"pipx --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Pip 3 and Python 3 came from the same path prefix\" {\n        $pip3Path = Split-Path (readlink (which pip3))\n        $python3Path = Split-Path (readlink (which python3))\n        $pip3Path | Should -BeExactly $python3Path\n    }\n\n    It \"Pip 3 and Python 3 came from brew formula\" {\n        Split-Path (readlink (which pip3))    | Should -BeLike \"*/Cellar/*\"\n        Split-Path (readlink (which python3)) | Should -BeLike \"*/Cellar/*\"\n    }\n\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Rosetta.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\n$os = Get-OSVersion\n\nDescribe \"Rosetta\" -Skip:(-not $os.IsArm64) {\n    It \"Rosetta is available\" {\n        $commandResult = Get-CommandResult \"/usr/bin/pgrep oahd\"\n        $commandResult.Output | Should -Match \"\\d+\"\n        $commandResult.ExitCode | Should -Be 0\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Ruby.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\n\nDescribe \"Ruby\" {\n    It \"Ruby is available\" {\n        \"ruby --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Ruby is installed via HomeBrew\" {\n        Get-ToolPath \"ruby\" | Should -Not -BeLike \"/usr/bin/ruby*\"\n    }\n\n    It \"Ruby tools are consistent\" {\n        $os = Get-OSVersion\n        $expectedPrefix = if ($os.IsArm64) { \"/opt/homebrew\" } else { \"/usr/local\" }\n        Get-ToolPath \"ruby\"    | Should -Match \"^$expectedPrefix.*\"\n        Get-ToolPath \"gem\"     | Should -Match \"^$expectedPrefix.*\"\n        Get-ToolPath \"bundler\" | Should -Match \"^$expectedPrefix.*\"\n    }\n\n    It \"Ruby gems permissions are valid\" {\n        \"gem install bundle\" | Should -ReturnZeroExitCode\n        \"gem uninstall bundle\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/RubyGem.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\n$os = Get-OSVersion\n\nDescribe \"RubyGems\" {\n    $gemTestCases = (Get-ToolsetContent).ruby.rubygems | ForEach-Object {\n        @{gemName = $_}\n    }\n\n    if ($gemTestCases) {\n        It \"Gem <gemName> is installed\" -TestCases $gemTestCases {\n            \"gem list -i '^$gemName$'\" | Should -MatchCommandOutput \"true\"\n        }\n    }\n}\n\nDescribe \"Bundler\" {\n    It \"Bundler\" {\n        \"bundler --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Fastlane\" {\n    It \"Fastlane\" {\n        \"fastlane --version\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/RunAll-Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\n\nInvoke-PesterTests \"*\"\n"
  },
  {
    "path": "images/macos/scripts/tests/Rust.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\n$os = Get-OSVersion\n\nDescribe \"Rust\" {\n    Context \"Rust\" {\n        It \"Rustup is installed\" {\n            \"rustup --version\" | Should -ReturnZeroExitCode\n        }\n\n        It \"Rustc is installed\" {\n            \"rustc --version\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    Context \"Cargo\" {\n        It \"Cargo is installed\" {\n            \"cargo --version\" | Should -ReturnZeroExitCode\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/System.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\n$os = Get-OSVersion\n\nDescribe \"Disk free space\" {\n    It \"Image has more than 30GB free space\" {\n        # we should have at least 30 GB of free space on macOS images\n        # 10GB here: https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops#capabilities-and-limitations\n        # 14GB here: https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories\n        # 30GB due to: https://github.com/actions/runner-images/issues/10511\n        $diskInfo = Get-PSDrive \"/\"\n        $totalSpaceGB = [math]::Floor(($diskInfo.Used + $diskInfo.Free) / 1GB)\n        $freeSpaceGB = [math]::Floor($diskInfo.Free / 1GB)\n        Write-Host \"  [i] Disk size: ${totalSpaceGB} GB; Free space: ${freeSpaceGB} GB\"\n        $freeSpaceGB | Should -BeGreaterOrEqual 30\n    }\n}\n\nDescribe \"Certificate\" {\n    It \"Apple Worldwide Developer Relations Certification Authority[expired: 2030-02] is installed\" {\n        $sha1Hash = \"06EC06599F4ED0027CC58956B4D3AC1255114F35\"\n        $certs = security find-certificate -a -c Worldwide -p -Z | Out-String\n        $certs | Should -Match $sha1Hash\n    }\n\n    It \"Developer ID Certification Authority[expired: 2031-09] is installed\" {\n        $sha1Hash = \"5B45F61068B29FCC8FFFF1A7E99B78DA9E9C4635\"\n        $certs = security find-certificate -a -c \"Developer ID\" -p -Z | Out-String\n        $certs | Should -Match $sha1Hash\n    }\n}\n\nDescribe \"AutomationModeTool\" {\n    It \"Does not require user authentication\" {\n        automationmodetool | Out-String | Should -Match \"DOES NOT REQUIRE\"\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Toolcache.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\n\n$arch = Get-Architecture\n$os = Get-OSVersion\n\nDescribe \"Toolcache\" {\n    $toolcacheDirectory = Join-Path $env:HOME \"hostedtoolcache\"\n    [array]$packages += (Get-ToolsetContent).toolcache | ForEach-Object {\n        return [PSCustomObject] @{\n            ToolName = ($_.name).ToLower()\n            Arch = $arch\n            Versions = $_.arch.$arch | Where-Object{ $_ } | ForEach-Object { $_.versions.Replace(\".*\", \"\") }\n        }\n    }\n\n    Context \"Python\" {\n        $pythonDirectory = Join-Path $toolcacheDirectory \"Python\"\n        $pythonPackage = $packages | Where-Object { $_.ToolName -eq \"python\" } | Select-Object -First 1\n        $testCase = @{ PythonDirectory = $pythonDirectory }\n\n        It \"Toolcache directory exists\" -TestCases $testCase {\n            param ( [string] $PythonDirectory )\n\n            $PythonDirectory | Should -Exist\n        }\n\n        It \"Toolcache directory contains at least one version of Python\" -TestCases $testCase {\n            param ( [string] $PythonDirectory )\n\n            (Get-ChildItem -Path $PythonDirectory -Directory).Count | Should -BeGreaterThan 0\n        }\n\n        $pythonPackage.Versions | Where-Object { $_ } | ForEach-Object {\n            Context \"$_\" {\n                $versionDirectory = Get-ChildItem -Path $pythonDirectory -Directory -Filter \"$_*\" | Select-Object -First 1\n                $pythonBinPath = Join-Path $versionDirectory.FullName $pythonPackage.Arch \"python\"\n                $testCase = @{ PythonVersion = $_; PythonBinPath = $pythonBinPath }\n\n                It \"Version\" -TestCases $testCase {\n                    param (\n                        [string] $PythonVersion,\n                        [string] $PythonBinPath\n                    )\n\n                    $result = Get-CommandResult \"$PythonBinPath --version\"\n                    $result.Output | Should -BeLike \"*$PythonVersion*\"\n                    $result.ExitCode | Should -Be 0\n                }\n\n                It \"Run test script\" -TestCases $testCase {\n                    param ( [string] $PythonBinPath )\n\n                    \"$PythonBinPath -c 'import sys;print(sys.version)'\" | Should -ReturnZeroExitCode\n                }\n            }\n        }\n    }\n\n    Context \"Ruby\" {\n        $rubyDirectory = Join-Path $toolcacheDirectory \"Ruby\"\n        $rubyPackage = $packages | Where-Object { $_.ToolName -eq \"Ruby\" } | Select-Object -First 1\n        $testCase = @{ RubyDirectory = $rubyDirectory }\n\n        It \"Toolcache directory exists\" -TestCases $testCase {\n            param ( [string] $RubyDirectory )\n\n            $RubyDirectory | Should -Exist\n        }\n\n        It \"Toolcache directory contains at least one version of Ruby\" -TestCases $testCase {\n            param ( [string] $RubyDirectory )\n\n            (Get-ChildItem -Path $RubyDirectory -Directory).Count | Should -BeGreaterThan 0\n        }\n\n        $rubyPackage.Versions | Where-Object { $_ } | ForEach-Object {\n            Context \"$_\" {\n                $versionDirectory = Get-ChildItem -Path $rubyDirectory -Directory -Filter \"$_*\" | Select-Object -First 1\n                $rubyBinPath = Join-Path $versionDirectory.FullName $rubyPackage.Arch \"bin\" \"ruby\"\n                $testCase = @{ RubyVersion = $_; RubyBinPath = $rubyBinPath }\n\n                It \"Version\" -TestCases $testCase {\n                    param (\n                        [string] $RubyVersion,\n                        [string] $RubyBinPath\n                    )\n\n                    $result = Get-CommandResult \"$RubyBinPath --version\"\n                    $result.Output | Should -BeLike \"*$RubyVersion*\"\n                    $result.ExitCode | Should -Be 0\n                }\n\n                It \"Run test script\" -TestCases $testCase {\n                    param ( [string] $RubyBinPath )\n\n                    \"$RubyBinPath -e 'puts RUBY_VERSION'\" | Should -ReturnZeroExitCode\n                }\n            }\n        }\n    }\n\n    Context \"Node\" {\n        $nodeDirectory = Join-Path $toolcacheDirectory \"node\"\n        $nodePackage = $packages | Where-Object { $_.ToolName -eq \"node\" } | Select-Object -First 1\n        $testCase = @{ NodeDirectory = $nodeDirectory }\n\n        It \"Toolcache directory exists\" -TestCases $testCase {\n            param ( [string] $NodeDirectory )\n\n            $NodeDirectory | Should -Exist\n        }\n\n        It \"Toolcache directory contains at least one version of Node\" -TestCases $testCase {\n            param ( [string] $NodeDirectory )\n\n            (Get-ChildItem -Path $NodeDirectory -Directory).Count | Should -BeGreaterThan 0\n        }\n\n        $nodePackage.Versions | Where-Object { $_ } | ForEach-Object {\n            Context \"$_\" {\n                $versionDirectory = Get-ChildItem -Path $nodeDirectory -Directory -Filter \"$_*\" | Select-Object -First 1\n                $nodeBinPath = Join-Path $versionDirectory.FullName $nodePackage.Arch \"bin\" \"node\"\n                $npmBinPath = Join-Path $versionDirectory.FullName $nodePackage.Arch \"bin\" \"npm\"\n                $testCase = @{ NodeVersion = $_; NodeBinPath = $nodeBinPath; NpmBinPath = $npmBinPath }\n\n                It \"Version Node\" -TestCases $testCase {\n                    param (\n                        [string] $NodeVersion,\n                        [string] $NodeBinPath\n                    )\n\n                    $result = Get-CommandResult \"$NodeBinPath --version\"\n                    $result.Output | Should -BeLike \"*$NodeVersion*\"\n                    $result.ExitCode | Should -Be 0\n                }\n\n                It \"Version Npm\" -TestCases $testCase {\n                    param ( [string] $NpmBinPath )\n\n                    \"$NpmBinPath --version\" | Should -ReturnZeroExitCode\n                }\n\n                It \"Run test script\" -TestCases $testCase {\n                    param ( [string] $NodeBinPath )\n\n                    \"$NodeBinPath -e 'console.log(process.version)'\" | Should -ReturnZeroExitCode\n                }\n            }\n        }\n    }\n\n    Context \"Go\" {\n        $goDirectory = Join-Path $toolcacheDirectory \"go\"\n        $goPackage = $packages | Where-Object { $_.ToolName -eq \"go\" } | Select-Object -First 1\n        $testCase = @{ GoDirectory = $goDirectory }\n\n        It \"Toolcache directory exists\" -TestCases $testCase {\n            param ( [string] $GoDirectory )\n\n            $GoDirectory | Should -Exist\n        }\n\n        It \"Toolcache directory contains at least one version of Go\" -TestCases $testCase {\n            param ( [string] $GoDirectory )\n\n            (Get-ChildItem -Path $GoDirectory -Directory).Count | Should -BeGreaterThan 0\n        }\n\n        $goPackage.Versions | Where-Object { $_ } | ForEach-Object {\n            Context \"$_\" {\n                $versionDirectory = Get-ChildItem -Path $goDirectory -Directory -Filter \"$_*\" | Select-Object -First 1\n                $goBinPath = Join-Path $versionDirectory.FullName $goPackage.Arch \"bin\" \"go\"\n                $testCase = @{ GoVersion = $_; GoBinPath = $goBinPath }\n\n                It \"Version Go\" -TestCases $testCase {\n                    param (\n                        [string] $GoVersion,\n                        [string] $GoBinPath\n                    )\n\n                    $result = Get-CommandResult \"$GoBinPath version\"\n                    $result.Output | Should -BeLike \"*$GoVersion*\"\n                    $result.ExitCode | Should -Be 0\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Toolset.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/Helpers.psm1\"\n\n$toolsets = Get-ChildItem -Path $PSScriptRoot -Filter \"toolset-*.json\"\n\nfunction Get-ShortVersion([System.Version] $Version) {\n    return [System.Version]::Parse($Version).ToString(2)\n}\n\nDescribe \"Toolset JSON validation\" {\n    $toolsets | ForEach-Object {\n        It \"$($_.Name) is valid\" {\n            $jsonContent = Get-Content -Raw $_.Fullname\n            $jsonContent | Test-Json | Should -BeTrue\n        }\n    }\n}\n\n$toolsets | ForEach-Object {\n    Describe \"$($_.Name)\" {\n        $toolset = Get-Content -Raw $_.Fullname | ConvertFrom-Json\n\n        Context \"Xcode\" {\n            It \"Default Xcode should be defined\" {\n                $toolset.xcode.default | Should -BeTrue\n            }\n\n            It \"Default Xcode is listed in Xcode list\" {\n                $toolset.xcode.versions | Should -Contain $toolset.xcode.default\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/scripts/tests/Xcode.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/../helpers/Xcode.Helpers.psm1\"\nImport-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\n\n$arch = Get-Architecture\n$xcodeVersions = (Get-ToolsetContent).xcode.${arch}.versions\n$defaultXcode = (Get-ToolsetContent).xcode.default\n$latestXcodeVersion = $xcodeVersions | Select-Object -First 1\n$os = Get-OSVersion\n\nDescribe \"Xcode\" {\n    $testCases = $xcodeVersions | ForEach-Object {\n        @{\n            XcodeVersion = $_.link;\n            LatestXcodeVersion = $xcodeVersions[0].link;\n            Symlinks = $_.symlinks\n        }\n    }\n\n    Context \"Versions\" {\n        It \"<XcodeVersion>\" -TestCases $testCases {\n            $xcodebuildPath = Get-XcodeToolPath -Version $XcodeVersion -ToolName \"xcodebuild\"\n            \"$xcodebuildPath -version\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    Context \"Default\" {\n        $defaultXcodeTestCase = @{ DefaultXcode = $defaultXcode }\n        It \"Default Xcode is <DefaultXcode>\" -TestCases $defaultXcodeTestCase {\n            \"xcodebuild -version\" | Should -ReturnZeroExitCode\n            If ($DefaultXcode -ilike \"*_*\") {\n                Write-Host \"Composite version detected (beta/RC/preview)\"\n                $DefaultXcode = $DefaultXcode.split(\"_\")[0]\n                If ($DefaultXcode -notlike \"*.*\") {\n                    $DefaultXcode = \"${DefaultXcode}.0\"\n                }\n            }\n            (Get-CommandResult \"xcodebuild -version\").Output | Should -BeLike \"Xcode ${DefaultXcode}*\"\n        }\n\n        It \"Xcode.app points to default Xcode\" -TestCases $defaultXcodeTestCase {\n            $xcodeApp = \"/Applications/Xcode.app\"\n            $expectedTarget = Get-XcodeRootPath -Version $DefaultXcode\n            $xcodeApp | Should -Exist\n            $expectedTarget | Should -Exist\n            (Get-Item $xcodeApp).Target | Should -Be $expectedTarget\n        }\n    }\n\n    Context \"Additional tools\" {\n        It \"Xcode <XcodeVersion> tools are installed\" -TestCases $testCases {\n            $TOOLS_NOT_INSTALLED_EXIT_CODE = 69\n            $xcodebuildPath = Get-XcodeToolPath -Version $XcodeVersion -ToolName \"xcodebuild\"\n            $result = Get-CommandResult \"$xcodebuildPath -checkFirstLaunchStatus\"\n\n            if ($XcodeVersion -ne $LatestXcodeVersion) {\n                $result.ExitCode | Should -Not -Be $TOOLS_NOT_INSTALLED_EXIT_CODE\n            } else {\n                $result.ExitCode | Should -BeIn (0, $TOOLS_NOT_INSTALLED_EXIT_CODE)\n            }\n        }\n    }\n\n    Context \"Symlinks\" {\n        It \"Xcode <XcodeVersion> has correct symlinks\" -TestCases $testCases {\n            $sourcePath = Get-XcodeRootPath -Version $XcodeVersion\n            $Symlinks | Where-Object { $_ } | ForEach-Object {\n                $targetPath = Get-XcodeRootPath -Version $_\n                $targetPath | Should -Exist\n                (Get-Item $targetPath).Target | Should -Be $sourcePath\n            }\n        }\n    }\n\n    It \"/Applications/Xcode* symlinks are valid\" {\n        $symlinks = Get-ChildItem \"/Applications\" -Filter \"Xcode*\" | Where-Object { $_.LinkType }\n\n        $symlinks.Target | ForEach-Object {\n            $_ | Should -Exist\n        }\n    }\n}\n\nDescribe \"XCODE_DEVELOPER_DIR variables\" {\n    $exactVersionsList = $xcodeVersions.link | Where-Object { Test-XcodeStableRelease -Version $_ } | ForEach-Object {\n        $xcodeRootPath = Get-XcodeRootPath -Version $_\n        $xcodeVersionInfo = Get-XcodeVersionInfo -XcodeRootPath $xcodeRootPath\n        return @{\n            RootPath = $xcodeRootPath\n            Version = [SemVer]::Parse($xcodeVersionInfo.Version)\n        }\n    } | Sort-Object -Property Version -Descending\n    $majorVersions = $exactVersionsList.Version.Major | Select-Object -Unique\n    $testCases = $majorVersions | ForEach-Object { @{ MajorVersion = $_; VersionsList = $exactVersionsList } }\n\n    It \"XCODE_<MajorVersion>_DEVELOPER_DIR\" -TestCases $testCases {\n        $variableName = \"XCODE_${MajorVersion}_DEVELOPER_DIR\"\n        $actualPath = [System.Environment]::GetEnvironmentVariable($variableName)\n        $expectedVersion = $VersionsList | Where-Object { $_.Version.Major -eq $MajorVersion } | Select-Object -First 1\n        $expectedPath = \"$($expectedVersion.RootPath)/Contents/Developer\"\n        $actualPath | Should -Exist\n        $actualPath | Should -Be $expectedPath\n    }\n}\n\nDescribe \"Xcode simulators\" {\n    $xcodeVersions.link | Where-Object { Test-XcodeStableRelease -Version $_ } | ForEach-Object {\n        Context \"$_\" {\n            $testCase = @{ XcodeVersion = $_ }\n            It \"No duplicates in devices\" -TestCases $testCase {\n                Switch-Xcode -Version $XcodeVersion\n                [array]$devicesList = @(Get-XcodeDevicesList | Where-Object { $_ })\n                Write-Host \"Devices for $XcodeVersion\"\n                Write-Host ($devicesList -join \"`n\")\n                Confirm-ArrayWithoutDuplicates $devicesList -Because \"Found duplicate device simulators\"\n            }\n        }\n    }\n\n    AfterEach {\n        $defaultXcode = (Get-ToolsetContent).xcode.default\n        Switch-Xcode -Version $defaultXcode\n    }\n}\n"
  },
  {
    "path": "images/macos/templates/macOS-14.anka.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    veertu-anka = {\n      version = \">= v3.2.0\"\n      source  = \"github.com/veertuinc/veertu-anka\"\n    }\n  }\n}\n\nlocals {\n  image_folder = \"/Users/${var.vm_username}/image-generation\"\n}\n\nvariable \"builder_type\" {\n  type = string\n  default = \"veertu-anka-vm-clone\"\n  validation {\n    condition = contains([\"veertu-anka-vm-clone\", \"null\"], var.builder_type)\n    error_message = \"The builder_type value must be one of [veertu-anka-vm-clone, null].\"\n  }\n}\n\nvariable \"source_vm_name\" {\n  type = string\n}\n\nvariable \"source_vm_port\" {\n  type = number\n  default = 22\n}\n\nvariable \"source_vm_tag\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"socks_proxy\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"build_id\" {\n  type = string\n}\n\nvariable \"vm_username\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vm_password\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"github_api_pat\" {\n  type      = string\n  sensitive = true\n  default   = \"\"\n}\n\nvariable \"xcode_install_storage_url\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"xcode_install_sas\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vcpu_count\" {\n  type    = string\n  default = \"6\"\n}\n\nvariable \"ram_size\" {\n  type    = string\n  default = \"24G\"\n}\n\nvariable \"image_os\" {\n  type    = string\n  default = \"macos14\"\n}\n\nsource \"veertu-anka-vm-clone\" \"template\" {\n  vm_name        = \"${var.build_id}\"\n  source_vm_name = \"${var.source_vm_name}\"\n  source_vm_tag  = \"${var.source_vm_tag}\"\n  vcpu_count     = \"${var.vcpu_count}\"\n  ram_size       = \"${var.ram_size}\"\n  stop_vm        = \"true\"\n}\n\nsource \"null\" \"template\" {\n  ssh_host = \"${var.source_vm_name}\"\n  ssh_port = \"${var.source_vm_port}\"\n  ssh_username = \"${var.vm_username}\"\n  ssh_password = \"${var.vm_password}\"\n  ssh_proxy_host = \"${var.socks_proxy}\"\n}\n\nbuild {\n  sources = [\"source.${var.builder_type}.template\"]\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ${local.image_folder}\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/\"\n    sources     = [\n      \"${path.root}/../scripts/tests\",\n      \"${path.root}/../scripts/docs-gen\",\n      \"${path.root}/../scripts/helpers\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/docs-gen/\"\n    source      = \"${path.root}/../../../helpers/software-report-base\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/add-certificate.swift\"\n    source      = \"${path.root}/../assets/add-certificate.swift\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bashrc\"\n    source      = \"${path.root}/../assets/bashrc\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bash_profile\"\n    source      = \"${path.root}/../assets/bashprofile\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ~/bootstrap\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"bootstrap\"\n    source      = \"${path.root}/../assets/bootstrap-provisioner/\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/toolset.json\"\n    source      = \"${path.root}/../toolsets/toolset-14.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\n      \"mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report\",\n      \"mkdir ~/utils\",\n      \"mv ${local.image_folder}/helpers/confirm-identified-developers-macos14.scpt ~/utils\",\n      \"mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils\",\n      \"mv ${local.image_folder}/helpers/utils.sh ~/utils\",\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n      \"${path.root}/../scripts/build/install-xcode-clt.sh\",\n      \"${path.root}/../scripts/build/install-homebrew.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"PASSWORD=${var.vm_password}\", \"USERNAME=${var.vm_username}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-tccdb-macos.sh\",\n      \"${path.root}/../scripts/build/configure-autologin.sh\",\n      \"${path.root}/../scripts/build/configure-auto-updates.sh\",\n      \"${path.root}/../scripts/build/configure-ntpconf.sh\",\n      \"${path.root}/../scripts/build/configure-shell.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.build_id}\", \"IMAGE_OS=${var.image_os}\", \"PASSWORD=${var.vm_password}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-preimagedata.sh\",\n      \"${path.root}/../scripts/build/configure-ssh.sh\",\n      \"${path.root}/../scripts/build/configure-machine.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"USER_PASSWORD=${var.vm_password}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    pause_before     = \"30s\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-windows.sh\",\n      \"${path.root}/../scripts/build/install-powershell.sh\",\n      \"${path.root}/../scripts/build/install-mono.sh\",\n      \"${path.root}/../scripts/build/install-dotnet.sh\",\n      \"${path.root}/../scripts/build/install-python.sh\",\n      \"${path.root}/../scripts/build/install-azcopy.sh\",\n      \"${path.root}/../scripts/build/install-openssl.sh\",\n      \"${path.root}/../scripts/build/install-ruby.sh\",\n      \"${path.root}/../scripts/build/install-rubygems.sh\",\n      \"${path.root}/../scripts/build/install-git.sh\",\n      \"${path.root}/../scripts/build/install-node.sh\",\n      \"${path.root}/../scripts/build/install-common-utils.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}\", \"XCODE_INSTALL_SAS=${var.xcode_install_sas}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script           = \"${path.root}/../scripts/build/Install-Xcode.ps1\"\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/install-actions-cache.sh\",\n      \"${path.root}/../scripts/build/install-llvm.sh\",\n      \"${path.root}/../scripts/build/install-swiftlint.sh\",\n      \"${path.root}/../scripts/build/install-openjdk.sh\",\n      \"${path.root}/../scripts/build/install-php.sh\",\n      \"${path.root}/../scripts/build/install-aws-tools.sh\",\n      \"${path.root}/../scripts/build/install-rust.sh\",\n      \"${path.root}/../scripts/build/install-gcc.sh\",\n      \"${path.root}/../scripts/build/install-cocoapods.sh\",\n      \"${path.root}/../scripts/build/install-android-sdk.sh\",\n      \"${path.root}/../scripts/build/install-vcpkg.sh\",\n      \"${path.root}/../scripts/build/install-safari.sh\",\n      \"${path.root}/../scripts/build/install-chrome.sh\",\n      \"${path.root}/../scripts/build/install-edge.sh\",\n      \"${path.root}/../scripts/build/install-firefox.sh\",\n      \"${path.root}/../scripts/build/install-bicep.sh\",\n      \"${path.root}/../scripts/build/install-codeql-bundle.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Configure-Toolset.ps1\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script          = \"${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    inline           = [\n      \"pwsh -File \\\"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\\\" -OutputDirectory \\\"${local.image_folder}/output\\\" -ImageName ${var.build_id}\",\n      \"pwsh -File \\\"${local.image_folder}/tests/RunAll-Tests.ps1\\\"\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/macos-14-Readme.md\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.md\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/software-report.json\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.json\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\"rm -rf \\\"$(brew --cache)\\\"\"]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n      \"${path.root}/../scripts/build/configure-hostname.sh\",\n      \"${path.root}/../scripts/build/configure-system.sh\"\n    ]\n  }\n}\n"
  },
  {
    "path": "images/macos/templates/macOS-14.arm64.anka.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    veertu-anka = {\n      version = \">= v3.2.0\"\n      source  = \"github.com/veertuinc/veertu-anka\"\n    }\n  }\n}\n\nlocals {\n  image_folder = \"/Users/${var.vm_username}/image-generation\"\n}\n\nvariable \"builder_type\" {\n  type = string\n  default = \"veertu-anka-vm-clone\"\n  validation {\n    condition = contains([\"veertu-anka-vm-clone\", \"null\"], var.builder_type)\n    error_message = \"The builder_type value must be one of [veertu-anka-vm-clone, null].\"\n  }\n}\n\nvariable \"source_vm_name\" {\n  type = string\n}\n\nvariable \"source_vm_port\" {\n  type = number\n  default = 22\n}\n\nvariable \"source_vm_tag\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"socks_proxy\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"build_id\" {\n  type = string\n}\n\nvariable \"vm_username\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vm_password\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"github_api_pat\" {\n  type      = string\n  sensitive = true\n  default   = \"\"\n}\n\nvariable \"xcode_install_storage_url\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"xcode_install_sas\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vcpu_count\" {\n  type    = string\n  default = \"6\"\n}\n\nvariable \"ram_size\" {\n  type    = string\n  default = \"8G\"\n}\n\nvariable \"image_os\" {\n  type    = string\n  default = \"macos14\"\n}\n\nsource \"veertu-anka-vm-clone\" \"template\" {\n  vm_name        = \"${var.build_id}\"\n  source_vm_name = \"${var.source_vm_name}\"\n  source_vm_tag  = \"${var.source_vm_tag}\"\n  vcpu_count     = \"${var.vcpu_count}\"\n  ram_size       = \"${var.ram_size}\"\n  stop_vm        = \"true\"\n  log_level      = \"debug\"\n}\n\nsource \"null\" \"template\" {\n  ssh_host = \"${var.source_vm_name}\"\n  ssh_port = \"${var.source_vm_port}\"\n  ssh_username = \"${var.vm_username}\"\n  ssh_password = \"${var.vm_password}\"\n  ssh_proxy_host = \"${var.socks_proxy}\"\n}\n\nbuild {\n  sources = [\"source.${var.builder_type}.template\"]\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ${local.image_folder}\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/\"\n    sources     = [\n      \"${path.root}/../scripts/tests\",\n      \"${path.root}/../scripts/docs-gen\",\n      \"${path.root}/../scripts/helpers\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/docs-gen/\"\n    source      = \"${path.root}/../../../helpers/software-report-base\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/add-certificate.swift\"\n    source      = \"${path.root}/../assets/add-certificate.swift\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bashrc\"\n    source      = \"${path.root}/../assets/bashrc\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bash_profile\"\n    source      = \"${path.root}/../assets/bashprofile\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ~/bootstrap\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"bootstrap\"\n    source      = \"${path.root}/../assets/bootstrap-provisioner/\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/toolset.json\"\n    source      = \"${path.root}/../toolsets/toolset-14.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\n      \"mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report\",\n      \"mkdir ~/utils\",\n      \"mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils\",\n      \"mv ${local.image_folder}/helpers/utils.sh ~/utils\",\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n      \"${path.root}/../scripts/build/install-xcode-clt.sh\",\n      \"${path.root}/../scripts/build/install-homebrew.sh\",\n      \"${path.root}/../scripts/build/install-rosetta.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"PASSWORD=${var.vm_password}\", \"USERNAME=${var.vm_username}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-tccdb-macos.sh\",\n      \"${path.root}/../scripts/build/configure-autologin.sh\",\n      \"${path.root}/../scripts/build/configure-auto-updates.sh\",\n      \"${path.root}/../scripts/build/configure-ntpconf.sh\",\n      \"${path.root}/../scripts/build/configure-shell.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.build_id}\", \"IMAGE_OS=${var.image_os}\", \"PASSWORD=${var.vm_password}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-preimagedata.sh\",\n      \"${path.root}/../scripts/build/configure-ssh.sh\",\n      \"${path.root}/../scripts/build/configure-machine.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"USER_PASSWORD=${var.vm_password}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    pause_before     = \"30s\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-windows.sh\",\n      \"${path.root}/../scripts/build/install-powershell.sh\",\n      \"${path.root}/../scripts/build/install-mono.sh\",\n      \"${path.root}/../scripts/build/install-dotnet.sh\",\n      \"${path.root}/../scripts/build/install-python.sh\",\n      \"${path.root}/../scripts/build/install-azcopy.sh\",\n      \"${path.root}/../scripts/build/install-openssl.sh\",\n      \"${path.root}/../scripts/build/install-ruby.sh\",\n      \"${path.root}/../scripts/build/install-rubygems.sh\",\n      \"${path.root}/../scripts/build/install-git.sh\",\n      \"${path.root}/../scripts/build/install-node.sh\",\n      \"${path.root}/../scripts/build/install-common-utils.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}\", \"XCODE_INSTALL_SAS=${var.xcode_install_sas}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script           = \"${path.root}/../scripts/build/Install-Xcode.ps1\"\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/install-actions-cache.sh\",\n      \"${path.root}/../scripts/build/install-llvm.sh\",\n      \"${path.root}/../scripts/build/install-openjdk.sh\",\n      \"${path.root}/../scripts/build/install-aws-tools.sh\",\n      \"${path.root}/../scripts/build/install-rust.sh\",\n      \"${path.root}/../scripts/build/install-gcc.sh\",\n      \"${path.root}/../scripts/build/install-cocoapods.sh\",\n      \"${path.root}/../scripts/build/install-android-sdk.sh\",\n      \"${path.root}/../scripts/build/install-vcpkg.sh\",\n      \"${path.root}/../scripts/build/install-safari.sh\",\n      \"${path.root}/../scripts/build/install-chrome.sh\",\n      \"${path.root}/../scripts/build/install-firefox.sh\",\n      \"${path.root}/../scripts/build/install-bicep.sh\",\n      \"${path.root}/../scripts/build/install-codeql-bundle.sh\",\n      \"${path.root}/../scripts/build/install-edge.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Configure-Toolset.ps1\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script          = \"${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    inline           = [\n      \"pwsh -File \\\"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\\\" -OutputDirectory \\\"${local.image_folder}/output\\\" -ImageName ${var.build_id}\",\n      \"pwsh -File \\\"${local.image_folder}/tests/RunAll-Tests.ps1\\\"\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/macos-14-arm64-Readme.md\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.md\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/software-report.json\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n      \"${path.root}/../scripts/build/configure-hostname.sh\",\n      \"${path.root}/../scripts/build/configure-system.sh\"\n    ]\n  }\n}\n"
  },
  {
    "path": "images/macos/templates/macOS-15.anka.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    veertu-anka = {\n      version = \">= v3.2.0\"\n      source  = \"github.com/veertuinc/veertu-anka\"\n    }\n  }\n}\n\nlocals {\n  image_folder = \"/Users/${var.vm_username}/image-generation\"\n}\n\nvariable \"builder_type\" {\n  type = string\n  default = \"veertu-anka-vm-clone\"\n  validation {\n    condition = contains([\"veertu-anka-vm-clone\", \"null\"], var.builder_type)\n    error_message = \"The builder_type value must be one of [veertu-anka-vm-clone, null].\"\n  }\n}\n\nvariable \"source_vm_name\" {\n  type = string\n}\n\nvariable \"source_vm_port\" {\n  type = number\n  default = 22\n}\n\nvariable \"source_vm_tag\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"socks_proxy\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"build_id\" {\n  type = string\n}\n\nvariable \"vm_username\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vm_password\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"github_api_pat\" {\n  type      = string\n  sensitive = true\n  default   = \"\"\n}\n\nvariable \"xcode_install_storage_url\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"xcode_install_sas\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vcpu_count\" {\n  type    = string\n  default = \"6\"\n}\n\nvariable \"ram_size\" {\n  type    = string\n  default = \"8G\"\n}\n\nvariable \"image_os\" {\n  type    = string\n  default = \"macos15\"\n}\n\nsource \"veertu-anka-vm-clone\" \"template\" {\n  vm_name        = \"${var.build_id}\"\n  source_vm_name = \"${var.source_vm_name}\"\n  source_vm_tag  = \"${var.source_vm_tag}\"\n  vcpu_count     = \"${var.vcpu_count}\"\n  ram_size       = \"${var.ram_size}\"\n  stop_vm        = \"true\"\n}\n\nsource \"null\" \"template\" {\n  ssh_host = \"${var.source_vm_name}\"\n  ssh_port = \"${var.source_vm_port}\"\n  ssh_username = \"${var.vm_username}\"\n  ssh_password = \"${var.vm_password}\"\n  ssh_proxy_host = \"${var.socks_proxy}\"\n}\n\nbuild {\n  sources = [\"source.${var.builder_type}.template\"]\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ${local.image_folder}\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/\"\n    sources     = [\n      \"${path.root}/../scripts/tests\",\n      \"${path.root}/../scripts/docs-gen\",\n      \"${path.root}/../scripts/helpers\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/docs-gen/\"\n    source      = \"${path.root}/../../../helpers/software-report-base\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/add-certificate.swift\"\n    source      = \"${path.root}/../assets/add-certificate.swift\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bashrc\"\n    source      = \"${path.root}/../assets/bashrc\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bash_profile\"\n    source      = \"${path.root}/../assets/bashprofile\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ~/bootstrap\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"bootstrap\"\n    source      = \"${path.root}/../assets/bootstrap-provisioner/\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/toolset.json\"\n    source      = \"${path.root}/../toolsets/toolset-15.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\n      \"mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report\",\n      \"mkdir ~/utils\",\n      \"mv ${local.image_folder}/helpers/confirm-identified-developers-macos15.scpt ~/utils\",\n      \"mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils\",\n      \"mv ${local.image_folder}/helpers/utils.sh ~/utils\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n      \"${path.root}/../scripts/build/install-xcode-clt.sh\",\n      \"${path.root}/../scripts/build/install-homebrew.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"PASSWORD=${var.vm_password}\", \"USERNAME=${var.vm_username}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-tccdb-macos.sh\",\n      \"${path.root}/../scripts/build/configure-autologin.sh\",\n      \"${path.root}/../scripts/build/configure-auto-updates.sh\",\n      \"${path.root}/../scripts/build/configure-ntpconf.sh\",\n      \"${path.root}/../scripts/build/configure-shell.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.build_id}\", \"IMAGE_OS=${var.image_os}\", \"PASSWORD=${var.vm_password}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-preimagedata.sh\",\n      \"${path.root}/../scripts/build/configure-ssh.sh\",\n      \"${path.root}/../scripts/build/configure-machine.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"USER_PASSWORD=${var.vm_password}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    pause_before     = \"30s\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-windows.sh\",\n      \"${path.root}/../scripts/build/install-powershell.sh\",\n      \"${path.root}/../scripts/build/install-dotnet.sh\",\n      \"${path.root}/../scripts/build/install-python.sh\",\n      \"${path.root}/../scripts/build/install-azcopy.sh\",\n      \"${path.root}/../scripts/build/install-openssl.sh\",\n      \"${path.root}/../scripts/build/install-ruby.sh\",\n      \"${path.root}/../scripts/build/install-rubygems.sh\",\n      \"${path.root}/../scripts/build/install-git.sh\",\n      \"${path.root}/../scripts/build/install-node.sh\",\n      \"${path.root}/../scripts/build/install-common-utils.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}\", \"XCODE_INSTALL_SAS=${var.xcode_install_sas}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script           = \"${path.root}/../scripts/build/Install-Xcode.ps1\"\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/install-actions-cache.sh\",\n      \"${path.root}/../scripts/build/install-llvm.sh\",\n      \"${path.root}/../scripts/build/install-swiftlint.sh\",\n      \"${path.root}/../scripts/build/install-openjdk.sh\",\n      \"${path.root}/../scripts/build/install-php.sh\",\n      \"${path.root}/../scripts/build/install-aws-tools.sh\",\n      \"${path.root}/../scripts/build/install-rust.sh\",\n      \"${path.root}/../scripts/build/install-gcc.sh\",\n      \"${path.root}/../scripts/build/install-cocoapods.sh\",\n      \"${path.root}/../scripts/build/install-android-sdk.sh\",\n      \"${path.root}/../scripts/build/install-vcpkg.sh\",\n      \"${path.root}/../scripts/build/install-safari.sh\",\n      \"${path.root}/../scripts/build/install-chrome.sh\",\n      \"${path.root}/../scripts/build/install-edge.sh\",\n      \"${path.root}/../scripts/build/install-firefox.sh\",\n      \"${path.root}/../scripts/build/install-bicep.sh\",\n      \"${path.root}/../scripts/build/install-codeql-bundle.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Configure-Toolset.ps1\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script          = \"${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    inline           = [\n      \"pwsh -File \\\"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\\\" -OutputDirectory \\\"${local.image_folder}/output\\\" -ImageName ${var.build_id}\",\n      \"pwsh -File \\\"${local.image_folder}/tests/RunAll-Tests.ps1\\\"\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/macos-15-Readme.md\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.md\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/software-report.json\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.json\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\"rm -rf \\\"$(brew --cache)\\\"\"]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n    \"${path.root}/../scripts/build/configure-hostname.sh\",\n    \"${path.root}/../scripts/build/configure-system.sh\"\n    ]\n  }\n}\n"
  },
  {
    "path": "images/macos/templates/macOS-15.arm64.anka.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    veertu-anka = {\n      version = \">= v3.2.0\"\n      source  = \"github.com/veertuinc/veertu-anka\"\n    }\n  }\n}\n\nlocals {\n  image_folder = \"/Users/${var.vm_username}/image-generation\"\n}\n\nvariable \"builder_type\" {\n  type = string\n  default = \"veertu-anka-vm-clone\"\n  validation {\n    condition = contains([\"veertu-anka-vm-clone\", \"null\"], var.builder_type)\n    error_message = \"The builder_type value must be one of [veertu-anka-vm-clone, null].\"\n  }\n}\n\nvariable \"source_vm_name\" {\n  type = string\n}\n\nvariable \"source_vm_port\" {\n  type = number\n  default = 22\n}\n\nvariable \"source_vm_tag\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"socks_proxy\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"build_id\" {\n  type = string\n}\n\nvariable \"vm_username\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vm_password\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"github_api_pat\" {\n  type      = string\n  sensitive = true\n  default   = \"\"\n}\n\nvariable \"xcode_install_storage_url\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"xcode_install_sas\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vcpu_count\" {\n  type    = string\n  default = \"6\"\n}\n\nvariable \"ram_size\" {\n  type    = string\n  default = \"8G\"\n}\n\nvariable \"image_os\" {\n  type    = string\n  default = \"macos15\"\n}\n\nsource \"veertu-anka-vm-clone\" \"template\" {\n  vm_name        = \"${var.build_id}\"\n  source_vm_name = \"${var.source_vm_name}\"\n  source_vm_tag  = \"${var.source_vm_tag}\"\n  vcpu_count     = \"${var.vcpu_count}\"\n  ram_size       = \"${var.ram_size}\"\n  stop_vm        = \"true\"\n  log_level      = \"debug\"\n}\n\nsource \"null\" \"template\" {\n  ssh_host = \"${var.source_vm_name}\"\n  ssh_port = \"${var.source_vm_port}\"\n  ssh_username = \"${var.vm_username}\"\n  ssh_password = \"${var.vm_password}\"\n  ssh_proxy_host = \"${var.socks_proxy}\"\n}\n\nbuild {\n  sources = [\"source.${var.builder_type}.template\"]\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ${local.image_folder}\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/\"\n    sources     = [\n      \"${path.root}/../scripts/tests\",\n      \"${path.root}/../scripts/docs-gen\",\n      \"${path.root}/../scripts/helpers\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/docs-gen/\"\n    source      = \"${path.root}/../../../helpers/software-report-base\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/add-certificate.swift\"\n    source      = \"${path.root}/../assets/add-certificate.swift\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bashrc\"\n    source      = \"${path.root}/../assets/bashrc\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bash_profile\"\n    source      = \"${path.root}/../assets/bashprofile\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ~/bootstrap\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"bootstrap\"\n    source      = \"${path.root}/../assets/bootstrap-provisioner/\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/toolset.json\"\n    source      = \"${path.root}/../toolsets/toolset-15.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\n      \"mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report\",\n      \"mkdir ~/utils\",\n      \"mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils\",\n      \"mv ${local.image_folder}/helpers/utils.sh ~/utils\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n      \"${path.root}/../scripts/build/install-xcode-clt.sh\",\n      \"${path.root}/../scripts/build/install-homebrew.sh\",\n      \"${path.root}/../scripts/build/install-rosetta.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"PASSWORD=${var.vm_password}\", \"USERNAME=${var.vm_username}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-tccdb-macos.sh\",\n      \"${path.root}/../scripts/build/configure-autologin.sh\",\n      \"${path.root}/../scripts/build/configure-auto-updates.sh\",\n      \"${path.root}/../scripts/build/configure-ntpconf.sh\",\n      \"${path.root}/../scripts/build/configure-shell.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.build_id}\", \"IMAGE_OS=${var.image_os}\", \"PASSWORD=${var.vm_password}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-preimagedata.sh\",\n      \"${path.root}/../scripts/build/configure-ssh.sh\",\n      \"${path.root}/../scripts/build/configure-machine.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"USER_PASSWORD=${var.vm_password}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    pause_before     = \"30s\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-windows.sh\",\n      \"${path.root}/../scripts/build/install-powershell.sh\",\n      \"${path.root}/../scripts/build/install-dotnet.sh\",\n      \"${path.root}/../scripts/build/install-python.sh\",\n      \"${path.root}/../scripts/build/install-azcopy.sh\",\n      \"${path.root}/../scripts/build/install-openssl.sh\",\n      \"${path.root}/../scripts/build/install-ruby.sh\",\n      \"${path.root}/../scripts/build/install-rubygems.sh\",\n      \"${path.root}/../scripts/build/install-git.sh\",\n      \"${path.root}/../scripts/build/install-node.sh\",\n      \"${path.root}/../scripts/build/install-common-utils.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}\", \"XCODE_INSTALL_SAS=${var.xcode_install_sas}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script           = \"${path.root}/../scripts/build/Install-Xcode.ps1\"\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/install-actions-cache.sh\",\n      \"${path.root}/../scripts/build/install-llvm.sh\",\n      \"${path.root}/../scripts/build/install-openjdk.sh\",\n      \"${path.root}/../scripts/build/install-aws-tools.sh\",\n      \"${path.root}/../scripts/build/install-rust.sh\",\n      \"${path.root}/../scripts/build/install-gcc.sh\",\n      \"${path.root}/../scripts/build/install-cocoapods.sh\",\n      \"${path.root}/../scripts/build/install-android-sdk.sh\",\n      \"${path.root}/../scripts/build/install-vcpkg.sh\",\n      \"${path.root}/../scripts/build/install-safari.sh\",\n      \"${path.root}/../scripts/build/install-chrome.sh\",\n      \"${path.root}/../scripts/build/install-firefox.sh\",\n      \"${path.root}/../scripts/build/install-bicep.sh\",\n      \"${path.root}/../scripts/build/install-codeql-bundle.sh\",\n      \"${path.root}/../scripts/build/install-edge.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Configure-Toolset.ps1\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script          = \"${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    inline           = [\n      \"pwsh -File \\\"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\\\" -OutputDirectory \\\"${local.image_folder}/output\\\" -ImageName ${var.build_id}\",\n      \"pwsh -File \\\"${local.image_folder}/tests/RunAll-Tests.ps1\\\"\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/macos-15-arm64-Readme.md\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.md\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/software-report.json\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n      \"${path.root}/../scripts/build/configure-hostname.sh\",\n      \"${path.root}/../scripts/build/configure-system.sh\"\n    ]\n  }\n}\n"
  },
  {
    "path": "images/macos/templates/macOS-26.anka.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    veertu-anka = {\n      version = \">= v3.2.0\"\n      source  = \"github.com/veertuinc/veertu-anka\"\n    }\n  }\n}\n\nlocals {\n  image_folder = \"/Users/${var.vm_username}/image-generation\"\n}\n\nvariable \"builder_type\" {\n  type = string\n  default = \"veertu-anka-vm-clone\"\n  validation {\n    condition = contains([\"veertu-anka-vm-clone\", \"null\"], var.builder_type)\n    error_message = \"The builder_type value must be one of [veertu-anka-vm-clone, null].\"\n  }\n}\n\nvariable \"source_vm_name\" {\n  type = string\n}\n\nvariable \"source_vm_port\" {\n  type = number\n  default = 22\n}\n\nvariable \"source_vm_tag\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"socks_proxy\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"build_id\" {\n  type = string\n}\n\nvariable \"vm_username\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vm_password\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"github_api_pat\" {\n  type      = string\n  sensitive = true\n  default   = \"\"\n}\n\nvariable \"xcode_install_storage_url\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"xcode_install_sas\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vcpu_count\" {\n  type    = string\n  default = \"6\"\n}\n\nvariable \"ram_size\" {\n  type    = string\n  default = \"8G\"\n}\n\nvariable \"image_os\" {\n  type    = string\n  default = \"macos26\"\n}\n\nsource \"veertu-anka-vm-clone\" \"template\" {\n  vm_name        = \"${var.build_id}\"\n  source_vm_name = \"${var.source_vm_name}\"\n  source_vm_tag  = \"${var.source_vm_tag}\"\n  vcpu_count     = \"${var.vcpu_count}\"\n  ram_size       = \"${var.ram_size}\"\n  stop_vm        = \"true\"\n  log_level      = \"debug\"\n}\n\nsource \"null\" \"template\" {\n  ssh_host = \"${var.source_vm_name}\"\n  ssh_port = \"${var.source_vm_port}\"\n  ssh_username = \"${var.vm_username}\"\n  ssh_password = \"${var.vm_password}\"\n  ssh_proxy_host = \"${var.socks_proxy}\"\n}\n\nbuild {\n  sources = [\"source.${var.builder_type}.template\"]\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ${local.image_folder}\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/\"\n    sources     = [\n      \"${path.root}/../scripts/tests\",\n      \"${path.root}/../scripts/docs-gen\",\n      \"${path.root}/../scripts/helpers\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/docs-gen/\"\n    source      = \"${path.root}/../../../helpers/software-report-base\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/add-certificate.swift\"\n    source      = \"${path.root}/../assets/add-certificate.swift\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bashrc\"\n    source      = \"${path.root}/../assets/bashrc\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bash_profile\"\n    source      = \"${path.root}/../assets/bashprofile\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ~/bootstrap\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"bootstrap\"\n    source      = \"${path.root}/../assets/bootstrap-provisioner/\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/toolset.json\"\n    source      = \"${path.root}/../toolsets/toolset-26.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\n      \"mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report\",\n      \"mkdir ~/utils\",\n      \"mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils\",\n      \"mv ${local.image_folder}/helpers/utils.sh ~/utils\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n      \"${path.root}/../scripts/build/install-xcode-clt.sh\",\n      \"${path.root}/../scripts/build/install-homebrew.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"PASSWORD=${var.vm_password}\", \"USERNAME=${var.vm_username}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-tccdb-macos.sh\",\n      \"${path.root}/../scripts/build/configure-autologin.sh\",\n      \"${path.root}/../scripts/build/configure-auto-updates.sh\",\n      \"${path.root}/../scripts/build/configure-ntpconf.sh\",\n      \"${path.root}/../scripts/build/configure-shell.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.build_id}\", \"IMAGE_OS=${var.image_os}\", \"PASSWORD=${var.vm_password}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-preimagedata.sh\",\n      \"${path.root}/../scripts/build/configure-ssh.sh\",\n      \"${path.root}/../scripts/build/configure-machine.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"USER_PASSWORD=${var.vm_password}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    pause_before     = \"30s\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-windows.sh\",\n      \"${path.root}/../scripts/build/install-powershell.sh\",\n      \"${path.root}/../scripts/build/install-dotnet.sh\",\n      \"${path.root}/../scripts/build/install-python.sh\",\n      \"${path.root}/../scripts/build/install-azcopy.sh\",\n      \"${path.root}/../scripts/build/install-ruby.sh\",\n      \"${path.root}/../scripts/build/install-rubygems.sh\",\n      \"${path.root}/../scripts/build/install-git.sh\",\n      \"${path.root}/../scripts/build/install-node.sh\",\n      \"${path.root}/../scripts/build/install-common-utils.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}\", \"XCODE_INSTALL_SAS=${var.xcode_install_sas}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script           = \"${path.root}/../scripts/build/Install-Xcode.ps1\"\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/install-actions-cache.sh\",\n      \"${path.root}/../scripts/build/install-llvm.sh\",\n      \"${path.root}/../scripts/build/install-swiftlint.sh\",\n      \"${path.root}/../scripts/build/install-openjdk.sh\",\n      \"${path.root}/../scripts/build/install-php.sh\",\n      \"${path.root}/../scripts/build/install-aws-tools.sh\",\n      \"${path.root}/../scripts/build/install-rust.sh\",\n      \"${path.root}/../scripts/build/install-gcc.sh\",\n      \"${path.root}/../scripts/build/install-cocoapods.sh\",\n      \"${path.root}/../scripts/build/install-android-sdk.sh\",\n      \"${path.root}/../scripts/build/install-vcpkg.sh\",\n      \"${path.root}/../scripts/build/install-safari.sh\",\n      \"${path.root}/../scripts/build/install-chrome.sh\",\n      \"${path.root}/../scripts/build/install-firefox.sh\",\n      \"${path.root}/../scripts/build/install-bicep.sh\",\n      \"${path.root}/../scripts/build/install-codeql-bundle.sh\",\n      \"${path.root}/../scripts/build/install-edge.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Configure-Toolset.ps1\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script          = \"${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    inline           = [\n      \"pwsh -File \\\"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\\\" -OutputDirectory \\\"${local.image_folder}/output\\\" -ImageName ${var.build_id}\",\n      \"pwsh -File \\\"${local.image_folder}/tests/RunAll-Tests.ps1\\\"\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/macos-26-Readme.md\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.md\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/software-report.json\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n      \"${path.root}/../scripts/build/configure-hostname.sh\",\n      \"${path.root}/../scripts/build/configure-system.sh\"\n    ]\n  }\n}\n"
  },
  {
    "path": "images/macos/templates/macOS-26.arm64.anka.pkr.hcl",
    "content": "packer {\n  required_plugins {\n    veertu-anka = {\n      version = \">= v3.2.0\"\n      source  = \"github.com/veertuinc/veertu-anka\"\n    }\n  }\n}\n\nlocals {\n  image_folder = \"/Users/${var.vm_username}/image-generation\"\n}\n\nvariable \"builder_type\" {\n  type = string\n  default = \"veertu-anka-vm-clone\"\n  validation {\n    condition = contains([\"veertu-anka-vm-clone\", \"null\"], var.builder_type)\n    error_message = \"The builder_type value must be one of [veertu-anka-vm-clone, null].\"\n  }\n}\n\nvariable \"source_vm_name\" {\n  type = string\n}\n\nvariable \"source_vm_port\" {\n  type = number\n  default = 22\n}\n\nvariable \"source_vm_tag\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"socks_proxy\" {\n  type = string\n  default = \"\"\n}\n\nvariable \"build_id\" {\n  type = string\n}\n\nvariable \"vm_username\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vm_password\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"github_api_pat\" {\n  type      = string\n  sensitive = true\n  default   = \"\"\n}\n\nvariable \"xcode_install_storage_url\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"xcode_install_sas\" {\n  type      = string\n  sensitive = true\n}\n\nvariable \"vcpu_count\" {\n  type    = string\n  default = \"6\"\n}\n\nvariable \"ram_size\" {\n  type    = string\n  default = \"8G\"\n}\n\nvariable \"image_os\" {\n  type    = string\n  default = \"macos26\"\n}\n\nsource \"veertu-anka-vm-clone\" \"template\" {\n  vm_name        = \"${var.build_id}\"\n  source_vm_name = \"${var.source_vm_name}\"\n  source_vm_tag  = \"${var.source_vm_tag}\"\n  vcpu_count     = \"${var.vcpu_count}\"\n  ram_size       = \"${var.ram_size}\"\n  stop_vm        = \"true\"\n  log_level      = \"debug\"\n}\n\nsource \"null\" \"template\" {\n  ssh_host = \"${var.source_vm_name}\"\n  ssh_port = \"${var.source_vm_port}\"\n  ssh_username = \"${var.vm_username}\"\n  ssh_password = \"${var.vm_password}\"\n  ssh_proxy_host = \"${var.socks_proxy}\"\n}\n\nbuild {\n  sources = [\"source.${var.builder_type}.template\"]\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ${local.image_folder}\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/\"\n    sources     = [\n      \"${path.root}/../scripts/tests\",\n      \"${path.root}/../scripts/docs-gen\",\n      \"${path.root}/../scripts/helpers\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/docs-gen/\"\n    source      = \"${path.root}/../../../helpers/software-report-base\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/add-certificate.swift\"\n    source      = \"${path.root}/../assets/add-certificate.swift\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bashrc\"\n    source      = \"${path.root}/../assets/bashrc\"\n  }\n\n  provisioner \"file\" {\n    destination = \".bash_profile\"\n    source      = \"${path.root}/../assets/bashprofile\"\n  }\n\n  provisioner \"shell\" {\n    inline = [\"mkdir ~/bootstrap\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"bootstrap\"\n    source      = \"${path.root}/../assets/bootstrap-provisioner/\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${local.image_folder}/toolset.json\"\n    source      = \"${path.root}/../toolsets/toolset-26.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\n      \"mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report\",\n      \"mkdir ~/utils\",\n      \"mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils\",\n      \"mv ${local.image_folder}/helpers/utils.sh ~/utils\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n      \"${path.root}/../scripts/build/install-xcode-clt.sh\",\n      \"${path.root}/../scripts/build/install-homebrew.sh\",\n      \"${path.root}/../scripts/build/install-rosetta.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"PASSWORD=${var.vm_password}\", \"USERNAME=${var.vm_username}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-tccdb-macos.sh\",\n      \"${path.root}/../scripts/build/configure-autologin.sh\",\n      \"${path.root}/../scripts/build/configure-auto-updates.sh\",\n      \"${path.root}/../scripts/build/configure-ntpconf.sh\",\n      \"${path.root}/../scripts/build/configure-shell.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.build_id}\", \"IMAGE_OS=${var.image_os}\", \"PASSWORD=${var.vm_password}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-preimagedata.sh\",\n      \"${path.root}/../scripts/build/configure-ssh.sh\",\n      \"${path.root}/../scripts/build/configure-machine.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"USER_PASSWORD=${var.vm_password}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    pause_before     = \"30s\"\n    scripts          = [\n      \"${path.root}/../scripts/build/configure-windows.sh\",\n      \"${path.root}/../scripts/build/install-powershell.sh\",\n      \"${path.root}/../scripts/build/install-dotnet.sh\",\n      \"${path.root}/../scripts/build/install-python.sh\",\n      \"${path.root}/../scripts/build/install-azcopy.sh\",\n      \"${path.root}/../scripts/build/install-ruby.sh\",\n      \"${path.root}/../scripts/build/install-rubygems.sh\",\n      \"${path.root}/../scripts/build/install-git.sh\",\n      \"${path.root}/../scripts/build/install-node.sh\",\n      \"${path.root}/../scripts/build/install-common-utils.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}\", \"XCODE_INSTALL_SAS=${var.xcode_install_sas}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script           = \"${path.root}/../scripts/build/Install-Xcode.ps1\"\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"shutdown -r now\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"API_PAT=${var.github_api_pat}\", \"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/install-actions-cache.sh\",\n      \"${path.root}/../scripts/build/install-llvm.sh\",\n      \"${path.root}/../scripts/build/install-openjdk.sh\",\n      \"${path.root}/../scripts/build/install-aws-tools.sh\",\n      \"${path.root}/../scripts/build/install-rust.sh\",\n      \"${path.root}/../scripts/build/install-gcc.sh\",\n      \"${path.root}/../scripts/build/install-cocoapods.sh\",\n      \"${path.root}/../scripts/build/install-android-sdk.sh\",\n      \"${path.root}/../scripts/build/install-vcpkg.sh\",\n      \"${path.root}/../scripts/build/install-safari.sh\",\n      \"${path.root}/../scripts/build/install-chrome.sh\",\n      \"${path.root}/../scripts/build/install-firefox.sh\",\n      \"${path.root}/../scripts/build/install-bicep.sh\",\n      \"${path.root}/../scripts/build/install-codeql-bundle.sh\",\n      \"${path.root}/../scripts/build/install-edge.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Configure-Toolset.ps1\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}\"\n    script          = \"${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_FOLDER=${local.image_folder}\"]\n    execute_command  = \"source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    inline           = [\n      \"pwsh -File \\\"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\\\" -OutputDirectory \\\"${local.image_folder}/output\\\" -ImageName ${var.build_id}\",\n      \"pwsh -File \\\"${local.image_folder}/tests/RunAll-Tests.ps1\\\"\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/macos-26-arm64-Readme.md\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.md\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../../image-output/software-report.json\"\n    direction   = \"download\"\n    source      = \"${local.image_folder}/output/software-report.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}\"\n    scripts         = [\n      \"${path.root}/../scripts/build/configure-hostname.sh\",\n      \"${path.root}/../scripts/build/configure-system.sh\"\n    ]\n  }\n}\n"
  },
  {
    "path": "images/macos/toolsets/Readme.md",
    "content": "# Toolset JSON structure\n\n## Xcode\n\n- `versions` - the array of objects that will present installed Xcode versions  \n  - `link` property points to the place where Xcode will be located on image. `/Applications/Xcode_<link>.app`  \n  - `version` points to Xcode version that will be downloaded and installed\n  - `symlinks` describes the list of aliases where symlinks will be created to\n  - `sha256` used to check integrity of the Xcode installer file\n  - `install_runtimes` – controls the installation of simulator runtimes:\n    - `default` – installs all default runtimes.\n    - `none` – skips runtime installation.\n    - **Hashtable** – allows manual selection:\n      - Mandatory keys: `[ \"iOS\", \"watchOS\", \"tvOS\" ]`, plus `visionOS` for arm64 images.\n      - Values [array of string]:  \n        - `\"default\"` – installs the default runtime.  \n        - `\"skip\"` – skips installation.  \n        - Specific version numbers, e.g., `\"18.2\"`, `\"2.2\"`, `\"18.3.1\"`.\n        - Apple release version, e.g., `\"22E5216h\"`, `\"17A577\"`.\n\n- `default` - version of Xcode to set as default (should be matched with any `link` in `versions` property)\n    **Example:** `\"11.2\"`  \n\n**Note:**\n\n- `version` is specified with `+` or `-`, exact Xcode name should be like `16.2.0-Beta.3+16C5023f` or `16.2_Release_Candidate+16C5031c` and will be matching `.xip` file name.\n- `link` is specified with `_` and doesn't contain spaces, example: `16.2_Release_Candidate` or `16.1`; pattern description:\n  - DOWNLOAD_URL=\"https://download.developer.apple.com/Developer_Tools/$SOURCE_FILE_LOCATION/$SOURCE_FILE_NAME.$FILE_EXTENSION\"\n  - SOURCE_FILE_NAME: \"Xcode_$link\"\n  - SOURCE_FILE_LOCATION: \"Xcode_$link\"\n  - FILE_EXTENSION: xip\n\n**Example:**\n\nString format:\n\n```json\n\"versions\": [\n    { \"link\": \"16_beta_4\", \"version\": \"16.0.0-Beta.4+16A5211f\", \"symlinks\": [\"16.0\"], \"install_runtimes\": \"none\", \"sha256\": \"4270cd8021b2f7f512ce91bfc4423b25bccab36cdab21834709d798c8daade72\"},\n    { \"link\": \"15.4\", \"version\": \"15.4.0+15F31d\", \"install_runtimes\": \"default\", \"sha256\": \"82d3d61804ff3f4c7c82085e91dc701037ddaa770e542848b2477e22f4e8aa7a\"}\n]\n```\n\nBlock format:\n\n```json\n\"versions\": [\n    {\n        \"link\": \"16.2\",\n        \"version\": \"16.2+16C5032a\",\n        \"sha256\": \"0e367d06eb7c334ea143bada5e4422f56688aabff571bedf0d2ad9434b7290de\",\n        \"install_runtimes\": [\n          { \"iOS\": [\"18.0\", \"18.1\", \"18.2\"] },\n          { \"watchOS\": \"default\" },\n          { \"tvOS\": \"default\" },\n          { \"visionOS\": \"2.2\" }\n        ]\n    },\n    {\n        \"link\": \"16.1\",\n        \"version\": \"16.1+16B40\",\n        \"sha256\": \"8ca961d55981f983d21b99a95a6b0ac04905b837f6e11346ee86d28f12afe720\",\n        \"install_runtimes\": \"default\"\n    }\n]\n```\n\n## Android\n\n- `platform-list` - the array of android platforms to install.  \n    **Example:** `[ \"android-29\", \"android-28\", \"android-27\" ]`  \n\n- `build-tools` - the array of android build tools to install.  \n    **Example:** `[ \"29.0.2\", \"29.0.1\", \"29.0.0\", \"28.0.3\" ]`  \n\n- `extras` - the array of android extra items to install.  \n    **Example:** `[ \"google;google_play_services\", \"intel;Hardware_Accelerated_Execution_Manager\" ]`  \n\n- `addons` - the array of android add-ons to install.  \n    **Example:** `[ \"addon-google_apis-google-24\", \"addon-google_apis-google-23\" ]`  \n\n## Toolset JSON validation\n\nFile `Toolset.Tests.ps1` contains PowerShell [Pester](https://github.com/Pester/Pester) tests that validate JSON toolset files.\nType `Invoke-Pester` in the current folder in PowerShell to run tests.\n"
  },
  {
    "path": "images/macos/toolsets/toolset-14.json",
    "content": "{\n    \"xcode\": {\n        \"default\": \"15.4\",\n        \"x64\": {\n            \"versions\": [\n                {\n                    \"link\": \"16.2\",\n                    \"filename\": \"Xcode_16.2\",\n                    \"version\": \"16.2+16C5032a\",\n                    \"sha256\": \"0e367d06eb7c334ea143bada5e4422f56688aabff571bedf0d2ad9434b7290de\",\n                    \"install_runtimes\": [\n                        { \"iOS\": \"18.2\" },\n                        { \"watchOS\": \"default\" },\n                        { \"tvOS\": \"default\" }\n                    ]\n                },\n                {\n                    \"link\": \"16.1\",\n                    \"filename\": \"Xcode_16.1\",\n                    \"version\": \"16.1+16B40\",\n                    \"sha256\": \"8ca961d55981f983d21b99a95a6b0ac04905b837f6e11346ee86d28f12afe720\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"15.4\",\n                    \"filename\": \"Xcode_15.4\",\n                    \"version\": \"15.4.0+15F31d\",\n                    \"sha256\": \"82d3d61804ff3f4c7c82085e91dc701037ddaa770e542848b2477e22f4e8aa7a\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"15.3\",\n                    \"filename\": \"Xcode_15.3\",\n                    \"version\": \"15.3.0+15E204a\",\n                    \"sha256\": \"f13f6a2e2df432c3008e394640b8549a18c285acd7fd148d6c4bac8c3a5af234\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"15.2\",\n                    \"filename\": \"Xcode_15.2\",\n                    \"version\": \"15.2.0+15C500b\",\n                    \"sha256\": \"04E93680C6DDBEC84666531BE412DE778AFC8EAC6AB2037F4C2BE7290818B59B\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"15.1\",\n                    \"filename\": \"Xcode_15.1\",\n                    \"version\": \"15.1.0+15C65\",\n                    \"sha256\": \"857D8DB537BAC82BF99DE0E1D3895D214D4D02101C1340CEF3DAF6E821BA1D05\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"15.0.1\",\n                    \"filename\": \"Xcode_15.0.1\",\n                    \"version\": \"15.0.1+15A507\",\n                    \"sha256\": \"5AC17AE6060CAFC3C7112C6DA0B153450BE21F1DE6632777FBA9FBC9D999C9E8\",\n                    \"symlinks\": [\"15.0\"],\n                    \"install_runtimes\": \"default\"\n                }\n            ]\n        },\n        \"arm64\":{\n            \"versions\": [\n                {\n                    \"link\": \"16.2\",\n                    \"filename\": \"Xcode_16.2\",\n                    \"version\": \"16.2+16C5032a\",\n                    \"sha256\": \"0e367d06eb7c334ea143bada5e4422f56688aabff571bedf0d2ad9434b7290de\",\n                    \"install_runtimes\": [\n                        { \"iOS\": \"18.2\" },\n                        { \"watchOS\": \"default\" },\n                        { \"tvOS\": \"default\" },\n                        { \"visionOS\": \"2.2\" }\n                    ]\n                },\n                {\n                    \"link\": \"16.1\",\n                    \"filename\": \"Xcode_16.1\",\n                    \"version\": \"16.1+16B40\",\n                    \"sha256\": \"8ca961d55981f983d21b99a95a6b0ac04905b837f6e11346ee86d28f12afe720\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"15.4\",\n                    \"filename\": \"Xcode_15.4\",\n                    \"version\": \"15.4.0+15F31d\",\n                    \"sha256\": \"82d3d61804ff3f4c7c82085e91dc701037ddaa770e542848b2477e22f4e8aa7a\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"15.3\",\n                    \"filename\": \"Xcode_15.3\",\n                    \"version\": \"15.3.0+15E204a\",\n                    \"sha256\": \"f13f6a2e2df432c3008e394640b8549a18c285acd7fd148d6c4bac8c3a5af234\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"15.2\",\n                    \"filename\": \"Xcode_15.2\",\n                    \"version\": \"15.2.0+15C500b\",\n                    \"sha256\": \"04E93680C6DDBEC84666531BE412DE778AFC8EAC6AB2037F4C2BE7290818B59B\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"15.1\",\n                    \"filename\": \"Xcode_15.1\",\n                    \"version\": \"15.1.0+15C65\",\n                    \"sha256\": \"857D8DB537BAC82BF99DE0E1D3895D214D4D02101C1340CEF3DAF6E821BA1D05\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"15.0.1\",\n                    \"filename\": \"Xcode_15.0.1\",\n                    \"version\": \"15.0.1+15A507\",\n                    \"sha256\": \"5AC17AE6060CAFC3C7112C6DA0B153450BE21F1DE6632777FBA9FBC9D999C9E8\",\n                    \"symlinks\": [\"15.0\"],\n                    \"install_runtimes\": \"default\"\n                }\n            ]\n        }\n    },\n    \"java\": {\n        \"x64\": {\n            \"default\": \"21\",\n            \"versions\": [ \"8\", \"11\", \"17\", \"21\", \"25\" ]\n        },\n        \"arm64\": {\n            \"default\": \"21\",\n            \"versions\": [ \"11\", \"17\", \"21\", \"25\" ]\n        }\n    },\n    \"android\": {\n        \"cmdline-tools\": \"commandlinetools-mac-10406996_latest.zip\",\n        \"sdk-tools\": \"sdk-tools-darwin-4333796.zip\",\n        \"platform_min_version\": \"34\",\n        \"build_tools_min_version\": \"34.0.0\",\n        \"extras\": [\n            \"android;m2repository\", \"google;m2repository\", \"google;google_play_services\"\n        ],\n        \"addons\": [],\n        \"additional_tools\": [\n            \"cmake;3.31.5\",\n            \"cmake;4.1.2\"\n        ],\n        \"ndk\": {\n            \"default\": \"27\",\n            \"versions\": [\n                \"27\", \"28\", \"29\"\n            ]\n        }\n    },\n    \"powershellModules\": [\n        {\n            \"name\": \"Az\",\n            \"versions\": [\n                \"14.6.0\"\n            ]\n        },\n        { \"name\": \"Pester\" },\n        { \"name\": \"PSScriptAnalyzer\" }\n    ],\n    \"brew\": {\n        \"common_packages\": [\n            \"ant\",\n            \"aria2\",\n            \"azure-cli\",\n            \"bazelisk\",\n            \"carthage\",\n            \"cmake\",\n            \"gh\",\n            \"gnupg\",\n            \"gnu-tar\",\n            \"kotlin\",\n            \"libpq\",\n            \"libsodium\",\n            \"p7zip\",\n            \"packer\",\n            \"perl\",\n            \"pkgconf\",\n            \"swiftformat\",\n            \"tcl-tk@8\",\n            \"zstd\",\n            \"ninja\",\n            \"gmp\",\n            \"yq\",\n            \"unxip\",\n            \"xcbeautify\",\n            \"xcodes\"\n        ],\n        \"cask_packages\": [\n            \"parallels\"\n        ]\n    },\n    \"gcc\": {\n        \"versions\": [\n            \"13\",\n            \"14\",\n            \"15\"\n        ]\n    },\n    \"dotnet\": {\n        \"arch\":{\n            \"x64\": {\n                \"versions\": [\n                    \"8.0\",\n                    \"9.0\",\n                    \"10.0\"\n                ]\n            },\n            \"arm64\": {\n                \"versions\": [\n                    \"8.0\",\n                    \"9.0\",\n                    \"10.0\"\n                ]\n            }\n        }\n    },\n    \"ruby\": {\n        \"default\": \"3.3\",\n        \"rubygems\": [\n            \"cocoapods\",\n            \"bundler\",\n            \"fastlane\"\n        ]\n    },\n    \"toolcache\": [\n        {\n            \"name\": \"Python\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json\",\n            \"platform\" : \"darwin\",\n            \"arch\": {\n                \"x64\": {\n                    \"versions\": [\n                        \"3.10.*\",\n                        \"3.11.*\",\n                        \"3.12.*\",\n                        \"3.13.*\",\n                        \"3.14.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"versions\": [\n                        \"3.11.*\",\n                        \"3.12.*\",\n                        \"3.13.*\",\n                        \"3.14.*\"\n                    ]\n                }\n            }\n        },\n        {\n            \"name\": \"Node\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json\",\n            \"platform\" : \"darwin\",\n            \"arch\": {\n                \"x64\": {\n                    \"versions\": [\n                        \"20.*\",\n                        \"22.*\",\n                        \"24.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"versions\": [\n                        \"20.*\",\n                        \"22.*\",\n                        \"24.*\"\n                    ]\n                }\n            }\n        },\n        {\n            \"name\": \"Go\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json\",\n            \"platform\" : \"darwin\",\n            \"arch\": {\n                \"x64\": {\n                    \"variable_template\" : \"GOROOT_{0}_{1}_X64\",\n                    \"versions\": [\n                        \"1.22.*\",\n                        \"1.23.*\",\n                        \"1.24.*\",\n                        \"1.25.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"variable_template\" : \"GOROOT_{0}_{1}_ARM64\",\n                    \"versions\": [\n                        \"1.22.*\",\n                        \"1.23.*\",\n                        \"1.24.*\",\n                        \"1.25.*\"\n                    ]\n                }\n            }\n        },\n        {\n            \"name\": \"Ruby\",\n            \"arch\": {\n                \"x64\": {\n                    \"versions\": [\n                        \"3.2.*\",\n                        \"3.3.*\",\n                        \"3.4.*\",\n                        \"4.0.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"versions\": [\n                        \"3.2.*\",\n                        \"3.3.*\",\n                        \"3.4.*\",\n                        \"4.0.*\"\n                    ]\n                }\n            }\n        }\n    ],\n    \"node\": {\n        \"default\": \"20\"\n    },\n    \"llvm\": {\n        \"version\": \"15\"\n    },\n    \"php\": {\n        \"version\": \"8.5\"\n    },\n    \"pwsh\": {\n        \"version\": \"7.4\"\n    },\n    \"mono\": {\n        \"framework\":{\n            \"version\": \"6.12.0.188\",\n            \"sha256\": \"07cdd4e5e72b562892960b7fc73af470db7a4ffc2f68bb834eb3d0a874bbd12c\"\n        },\n        \"nunit\": {\n            \"version\": \"3.15.4\",\n            \"sha256\": \"356dab61433b5be76b76fd0e2e979bda56d164f6d85a900e55c3a4a5fffa28de\"\n        }\n    }\n}\n"
  },
  {
    "path": "images/macos/toolsets/toolset-15.json",
    "content": "{\n    \"xcode\": {\n        \"default\": \"16.4\",\n        \"x64\": {\n            \"versions\": [\n                {\n                    \"link\": \"26.3\",\n                    \"filename\": \"Xcode_26.3_Universal\",\n                    \"version\": \"26.3+17C529\",\n                    \"sha256\": \"cf87232e0419785170edcfa070b750f28808ec00b489ab540c08b7d197c79ae4\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"26.2\",\n                    \"filename\": \"Xcode_26.2_Universal\",\n                    \"version\": \"26.2+17C52\",\n                    \"sha256\": \"8f29ab6a9ac6670d3cf53545ffdb1c317d11607fa8db38fc56d3391df7783fbd\",\n                    \"install_runtimes\": [\n                        { \"iOS\": [\"26.2\"] },\n                        { \"watchOS\": [\"default\"] },\n                        { \"tvOS\": [\"default\"] }\n                    ]\n                },\n                {\n                    \"link\": \"26.1.1\",\n                    \"filename\": \"Xcode_26.1.1_Universal\",\n                    \"version\": \"26.1.1+17B100\",\n                    \"symlinks\": [\"26.1\"],\n                    \"sha256\": \"ed55d55fa28455c11a65e0809ba8fdf7d83fdeb268aabf9af7fcc1ee911543eb\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"26.0.1\",\n                    \"filename\": \"Xcode_26.0.1_Universal\",\n                    \"version\": \"26.0.1+17A400\",\n                    \"symlinks\": [\"26.0\"],\n                    \"sha256\": \"9881c457068c86ac91e94cca2d7116dfd01cb7179c22b0863b63c7f3bb7e7695\",\n                    \"install_runtimes\": [\n                        { \"iOS\": [\"default\"] },\n                        { \"watchOS\": [\"skip\"] },\n                        { \"tvOS\": [\"skip\"] }\n                    ]\n                },\n                {\n                    \"link\": \"16.4\",\n                    \"filename\": \"Xcode_16.4\",\n                    \"version\": \"16.4.0+16F6\",\n                    \"sha256\": \"2dbf65ba28fb85b34e72c14c529a42d5c3189ab0f11fb29fdebd5f4ee6c87900\",\n                    \"install_runtimes\": [\n                        { \"iOS\": [\"18.5\", \"18.6\"] },\n                        { \"watchOS\": [\"11.5\"] },\n                        { \"tvOS\": [\"18.5\"] }\n                    ]\n                },\n                {\n                    \"link\": \"16.3\",\n                    \"filename\": \"Xcode_16.3\",\n                    \"version\": \"16.3+16E140\",\n                    \"sha256\": \"c593177b73e45f31e1cf7ced131760d8aa8e1532f5bbf8ba11a4ded01da14fbb\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"16.2\",\n                    \"filename\": \"Xcode_16.2\",\n                    \"version\": \"16.2+16C5032a\",\n                    \"sha256\": \"0e367d06eb7c334ea143bada5e4422f56688aabff571bedf0d2ad9434b7290de\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"16.1\",\n                    \"filename\": \"Xcode_16.1\",\n                    \"version\": \"16.1+16B40\",\n                    \"sha256\": \"8ca961d55981f983d21b99a95a6b0ac04905b837f6e11346ee86d28f12afe720\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"16\",\n                    \"filename\": \"Xcode_16\",\n                    \"version\": \"16.0.0+16A242d\",\n                    \"sha256\": \"4a26c3d102a55c7222fb145e0ee1503249c9c26c6e02dc64d783c8810b37b1e3\",\n                    \"symlinks\": [\"16.0\"],\n                    \"install_runtimes\": \"none\"\n                }\n            ]\n        },\n        \"arm64\":{\n            \"versions\": [\n                {\n                    \"link\": \"26.3\",\n                    \"filename\": \"Xcode_26.3_Universal\",\n                    \"version\": \"26.3+17C529\",\n                    \"sha256\": \"cf87232e0419785170edcfa070b750f28808ec00b489ab540c08b7d197c79ae4\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"26.2\",\n                    \"filename\": \"Xcode_26.2_Universal\",\n                    \"version\": \"26.2+17C52\",\n                    \"sha256\": \"8f29ab6a9ac6670d3cf53545ffdb1c317d11607fa8db38fc56d3391df7783fbd\",\n                    \"install_runtimes\": [\n                        { \"iOS\": [\"26.2\"] },\n                        { \"watchOS\": [\"default\"] },\n                        { \"tvOS\": [\"default\"] },\n                        { \"visionOS\": [\"default\"] }\n                    ]\n                },\n                {\n                    \"link\": \"26.1.1\",\n                    \"filename\": \"Xcode_26.1.1_Universal\",\n                    \"version\": \"26.1.1+17B100\",\n                    \"symlinks\": [\"26.1\"],\n                    \"sha256\": \"ed55d55fa28455c11a65e0809ba8fdf7d83fdeb268aabf9af7fcc1ee911543eb\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"26.0.1\",\n                    \"filename\": \"Xcode_26.0.1_Universal\",\n                    \"version\": \"26.0.1+17A400\",\n                    \"symlinks\": [\"26.0\"],\n                    \"sha256\": \"9881c457068c86ac91e94cca2d7116dfd01cb7179c22b0863b63c7f3bb7e7695\",\n                    \"install_runtimes\": [\n                        { \"iOS\": [\"default\"] },\n                        { \"watchOS\": [\"skip\"] },\n                        { \"tvOS\": [\"skip\"] },\n                        { \"visionOS\": [\"skip\"] }\n                    ]\n                },\n                {\n                    \"link\": \"16.4\",\n                    \"filename\": \"Xcode_16.4\",\n                    \"version\": \"16.4.0+16F6\",\n                    \"sha256\": \"2dbf65ba28fb85b34e72c14c529a42d5c3189ab0f11fb29fdebd5f4ee6c87900\",\n                    \"install_runtimes\": [\n                        { \"iOS\": [\"18.5\", \"18.6\"] },\n                        { \"watchOS\": [\"11.5\"] },\n                        { \"tvOS\": [\"18.5\"] },\n                        { \"visionOS\": [\"2.3\", \"2.4\", \"2.5\"] }\n                    ]\n                },\n                {\n                    \"link\": \"16.3\",\n                    \"filename\": \"Xcode_16.3\",\n                    \"version\": \"16.3+16E140\",\n                    \"sha256\": \"c593177b73e45f31e1cf7ced131760d8aa8e1532f5bbf8ba11a4ded01da14fbb\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"16.2\",\n                    \"filename\": \"Xcode_16.2\",\n                    \"version\": \"16.2+16C5032a\",\n                    \"sha256\": \"0e367d06eb7c334ea143bada5e4422f56688aabff571bedf0d2ad9434b7290de\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"16.1\",\n                    \"filename\": \"Xcode_16.1\",\n                    \"version\": \"16.1+16B40\",\n                    \"sha256\": \"8ca961d55981f983d21b99a95a6b0ac04905b837f6e11346ee86d28f12afe720\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"16\",\n                    \"filename\": \"Xcode_16\",\n                    \"version\": \"16.0.0+16A242d\",\n                    \"sha256\": \"4a26c3d102a55c7222fb145e0ee1503249c9c26c6e02dc64d783c8810b37b1e3\",\n                    \"symlinks\": [\"16.0\"],\n                    \"install_runtimes\": \"none\"\n                }\n            ]\n        }\n    },    \n    \"java\": {\n        \"x64\": {\n            \"default\": \"21\",\n            \"versions\": [ \"11\", \"17\", \"21\", \"25\" ]\n        },\n        \"arm64\": {\n            \"default\": \"21\",\n            \"versions\": [ \"11\", \"17\", \"21\", \"25\" ]\n        }\n    },\n    \"android\": {\n        \"cmdline-tools\": \"commandlinetools-mac-12266719_latest.zip\",\n        \"sdk-tools\": \"sdk-tools-darwin-4333796.zip\",\n        \"platform_min_version\": \"34\",\n        \"build_tools_min_version\": \"35.0.0\",\n        \"extras\": [\n            \"android;m2repository\", \"google;m2repository\", \"google;google_play_services\"\n        ],\n        \"addons\": [],\n        \"additional_tools\": [\n            \"cmake;3.31.5\",\n            \"cmake;4.1.2\"\n        ],\n        \"ndk\": {\n            \"default\": \"27\",\n            \"versions\": [\n                \"27\", \"28\", \"29\"\n            ]\n        }\n    },\n    \"powershellModules\": [\n        {\n            \"name\": \"Az\",\n            \"versions\": [\n                \"14.6.0\"\n            ]\n        },\n        { \"name\": \"Pester\" },\n        { \"name\": \"PSScriptAnalyzer\" }\n    ],\n    \"brew\": {\n        \"common_packages\": [\n            \"ant\",\n            \"aria2\",\n            \"azure-cli\",\n            \"bazelisk\",\n            \"carthage\",\n            \"cmake\",\n            \"gh\",\n            \"gnupg\",\n            \"gnu-tar\",\n            \"kotlin\",\n            \"libpq\",\n            \"libsodium\",\n            \"p7zip\",\n            \"packer\",\n            \"perl\",\n            \"pkgconf\",\n            \"swiftformat\",\n            \"tcl-tk@8\",\n            \"zstd\",\n            \"ninja\",\n            \"gmp\",\n            \"yq\",\n            \"unxip\",\n            \"xcbeautify\",\n            \"xcodes\"\n        ],\n        \"cask_packages\": [\n            \"parallels\"\n        ]\n    },\n    \"gcc\": {\n        \"versions\": [\n            \"13\",\n            \"14\",\n            \"15\"\n        ]\n    },\n    \"dotnet\": {\n        \"arch\":{\n            \"x64\": {\n                \"versions\": [\n                    \"8.0\",\n                    \"9.0\",\n                    \"10.0\"\n                ]\n            },\n            \"arm64\": {\n                \"versions\": [\n                    \"8.0\",\n                    \"9.0\",\n                    \"10.0\"\n                ]\n            }\n        }\n    },\n    \"ruby\": {\n        \"default\": \"3.3\",\n        \"rubygems\": [\n            \"cocoapods\",\n            \"bundler\",\n            \"fastlane\"\n        ]\n    },\n    \"toolcache\": [\n        {\n            \"name\": \"Python\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json\",\n            \"platform\" : \"darwin\",\n            \"arch\": {\n                \"x64\": {\n                    \"versions\": [\n                        \"3.10.*\",\n                        \"3.11.*\",\n                        \"3.12.*\",\n                        \"3.13.*\",\n                        \"3.14.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"versions\": [\n                        \"3.11.*\",\n                        \"3.12.*\",\n                        \"3.13.*\",\n                        \"3.14.*\"\n                    ]\n                }\n            }\n        },\n        {\n            \"name\": \"Node\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json\",\n            \"platform\" : \"darwin\",\n            \"arch\": {\n                \"x64\": {\n                    \"versions\": [\n                        \"20.*\",\n                        \"22.*\",\n                        \"24.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"versions\": [\n                        \"20.*\",\n                        \"22.*\",\n                        \"24.*\"\n                    ]\n                }\n            }\n        },\n        {\n            \"name\": \"Go\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json\",\n            \"platform\" : \"darwin\",\n            \"arch\": {\n                \"x64\": {\n                    \"variable_template\" : \"GOROOT_{0}_{1}_X64\",\n                    \"versions\": [\n                        \"1.22.*\",\n                        \"1.23.*\",\n                        \"1.24.*\",\n                        \"1.25.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"variable_template\" : \"GOROOT_{0}_{1}_ARM64\",\n                    \"versions\": [\n                        \"1.22.*\",\n                        \"1.23.*\",\n                        \"1.24.*\",\n                        \"1.25.*\"\n                    ]\n                }\n            }\n        },\n        {\n            \"name\": \"Ruby\",\n            \"arch\": {\n                \"x64\": {\n                    \"versions\": [\n                        \"3.2.*\",\n                        \"3.3.*\",\n                        \"3.4.*\",\n                        \"4.0.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"versions\": [\n                        \"3.2.*\",\n                        \"3.3.*\",\n                        \"3.4.*\",\n                        \"4.0.*\"\n                    ]\n                }\n            }\n        }\n    ],\n    \"node\": {\n        \"default\": \"22\"\n    },\n    \"llvm\": {\n        \"version\": \"18\"\n    },\n    \"php\": {\n        \"version\": \"8.5\"\n    },\n    \"pwsh\": {\n        \"version\": \"7.4\"\n    }\n}\n"
  },
  {
    "path": "images/macos/toolsets/toolset-26.json",
    "content": "{\n    \"xcode\": {\n        \"default\": \"26.2\",\n        \"x64\": {\n            \"versions\": [\n                {\n                    \"link\": \"26.4_beta_3\",\n                    \"filename\": \"Xcode_26.4_beta_3_Universal\",\n                    \"version\": \"26.4+17E5179g\",\n                    \"symlinks\": [\"26.4\"],\n                    \"sha256\": \"dc55afeb7cdbfed3547996ce273058b46a8922c78005ab371da7f4bcdddfa53a\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"26.3\",\n                    \"filename\": \"Xcode_26.3_Universal\",\n                    \"version\": \"26.3+17C529\",\n                    \"sha256\": \"cf87232e0419785170edcfa070b750f28808ec00b489ab540c08b7d197c79ae4\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"26.2\",\n                    \"filename\": \"Xcode_26.2_Universal\",\n                    \"version\": \"26.2+17C52\",\n                    \"sha256\": \"8f29ab6a9ac6670d3cf53545ffdb1c317d11607fa8db38fc56d3391df7783fbd\",\n                    \"install_runtimes\": [\n                        { \"iOS\": [\"26.2\"] },\n                        { \"watchOS\": [\"default\"] },\n                        { \"tvOS\": [\"default\"] }\n                    ]\n                },\n                {\n                    \"link\": \"26.1.1\",\n                    \"filename\": \"Xcode_26.1.1_Universal\",\n                    \"version\": \"26.1.1+17B100\",\n                    \"symlinks\": [\"26.1\"],\n                    \"sha256\": \"ed55d55fa28455c11a65e0809ba8fdf7d83fdeb268aabf9af7fcc1ee911543eb\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"26.0.1\",\n                    \"filename\": \"Xcode_26.0.1_Universal\",\n                    \"version\": \"26.0.1+17A400\",\n                    \"symlinks\": [\"26.0\"],\n                    \"sha256\": \"9881c457068c86ac91e94cca2d7116dfd01cb7179c22b0863b63c7f3bb7e7695\",\n                    \"install_runtimes\": \"default\"\n                }\n            ]\n        },\n        \"arm64\": {\n            \"versions\": [\n                {\n                    \"link\": \"26.4_beta_3\",\n                    \"filename\": \"Xcode_26.4_beta_3_Universal\",\n                    \"version\": \"26.4+17E5179g\",\n                    \"symlinks\": [\"26.4\"],\n                    \"sha256\": \"dc55afeb7cdbfed3547996ce273058b46a8922c78005ab371da7f4bcdddfa53a\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"26.3\",\n                    \"filename\": \"Xcode_26.3_Universal\",\n                    \"version\": \"26.3+17C529\",\n                    \"sha256\": \"cf87232e0419785170edcfa070b750f28808ec00b489ab540c08b7d197c79ae4\",\n                    \"install_runtimes\": \"none\"\n                },\n                {\n                    \"link\": \"26.2\",\n                    \"filename\": \"Xcode_26.2_Universal\",\n                    \"version\": \"26.2+17C52\",\n                    \"sha256\": \"8f29ab6a9ac6670d3cf53545ffdb1c317d11607fa8db38fc56d3391df7783fbd\",\n                    \"install_runtimes\": [\n                        { \"iOS\": [\"26.2\"] },\n                        { \"watchOS\": [\"default\"] },\n                        { \"tvOS\": [\"default\"] },\n                        { \"visionOS\": [\"default\"] }\n                    ]\n                },\n                {\n                    \"link\": \"26.1.1\",\n                    \"filename\": \"Xcode_26.1.1_Universal\",\n                    \"version\": \"26.1.1+17B100\",\n                    \"symlinks\": [\"26.1\"],\n                    \"sha256\": \"ed55d55fa28455c11a65e0809ba8fdf7d83fdeb268aabf9af7fcc1ee911543eb\",\n                    \"install_runtimes\": \"default\"\n                },\n                {\n                    \"link\": \"26.0.1\",\n                    \"filename\": \"Xcode_26.0.1_Universal\",\n                    \"version\": \"26.0.1+17A400\",\n                    \"symlinks\": [\"26.0\"],\n                    \"sha256\": \"9881c457068c86ac91e94cca2d7116dfd01cb7179c22b0863b63c7f3bb7e7695\",\n                    \"install_runtimes\": \"default\"\n                }\n            ]\n        }\n    },    \n    \"java\": {\n        \"x64\": {\n            \"default\": \"21\",\n            \"versions\": [ \"11\", \"17\", \"21\", \"25\" ]\n        },\n        \"arm64\": {\n            \"default\": \"21\",\n            \"versions\": [ \"11\", \"17\", \"21\", \"25\" ]\n        }\n    },\n    \"android\": {\n        \"cmdline-tools\": \"commandlinetools-mac-12266719_latest.zip\",\n        \"sdk-tools\": \"sdk-tools-darwin-4333796.zip\",\n        \"platform_min_version\": \"35\",\n        \"build_tools_min_version\": \"35.0.0\",\n        \"extras\": [\n            \"android;m2repository\", \"google;m2repository\", \"google;google_play_services\"\n        ],\n        \"addons\": [],\n        \"additional_tools\": [\n            \"cmake;3.31.5\",\n            \"cmake;4.1.2\"\n        ],\n        \"ndk\": {\n            \"default\": \"27\",\n            \"versions\": [\n                \"27\",\"28\", \"29\"\n            ]\n        }\n    },\n    \"powershellModules\": [\n        {\n            \"name\": \"Az\",\n            \"versions\": [\n                \"14.6.0\"\n            ]\n        },\n        { \"name\": \"Pester\" },\n        { \"name\": \"PSScriptAnalyzer\" }\n    ],\n    \"brew\": {\n        \"common_packages\": [\n            \"ant\",\n            \"aria2\",\n            \"azure-cli\",\n            \"bazelisk\",\n            \"carthage\",\n            \"cmake\",\n            \"gh\",\n            \"gnupg\",\n            \"gnu-tar\",\n            \"kotlin\",\n            \"libpq\",\n            \"libsodium\",\n            \"openssl\",\n            \"p7zip\",\n            \"packer\",\n            \"perl\",\n            \"pkgconf\",\n            \"swiftformat\",\n            \"tcl-tk@8\",\n            \"zstd\",\n            \"ninja\",\n            \"gmp\",\n            \"yq\",\n            \"unxip\",\n            \"xcbeautify\",\n            \"xcodes\"\n        ],\n        \"cask_packages\": []\n    },\n    \"gcc\": {\n        \"versions\": [\n            \"13\",\n            \"14\",\n            \"15\"\n        ]\n    },\n    \"dotnet\": {\n        \"arch\":{\n            \"x64\": {\n                \"versions\": [\n                    \"8.0\",\n                    \"9.0\",\n                    \"10.0\"\n                ]\n            },\n            \"arm64\": {\n                \"versions\": [\n                    \"8.0\",\n                    \"9.0\",\n                    \"10.0\"\n                ]\n            }\n        }\n    },\n    \"ruby\": {\n        \"default\": \"3.4\",\n        \"rubygems\": [\n            \"cocoapods\",\n            \"bundler\",\n            \"fastlane\"\n        ]\n    },\n    \"toolcache\": [\n        {\n            \"name\": \"Python\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json\",\n            \"platform\" : \"darwin\",\n            \"arch\": {\n                \"x64\": {\n                    \"versions\": [\n                        \"3.11.*\",\n                        \"3.12.*\",\n                        \"3.13.*\",\n                        \"3.14.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"versions\": [\n                        \"3.11.*\",\n                        \"3.12.*\",\n                        \"3.13.*\",\n                        \"3.14.*\"\n                    ]\n                }\n            }\n        },\n        {\n            \"name\": \"Node\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json\",\n            \"platform\" : \"darwin\",\n            \"arch\": {\n                \"x64\": {\n                    \"versions\": [\n                        \"20.*\",\n                        \"22.*\",\n                        \"24.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"versions\": [\n                        \"20.*\",\n                        \"22.*\",\n                        \"24.*\"\n                    ]\n                }\n            }\n        },\n        {\n            \"name\": \"Go\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json\",\n            \"platform\" : \"darwin\",\n            \"arch\": {\n                \"x64\": {\n                    \"variable_template\" : \"GOROOT_{0}_{1}_X64\",\n                    \"versions\": [\n                        \"1.23.*\",\n                        \"1.24.*\",\n                        \"1.25.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"variable_template\" : \"GOROOT_{0}_{1}_ARM64\",\n                    \"versions\": [\n                        \"1.23.*\",\n                        \"1.24.*\",\n                        \"1.25.*\"\n                    ]\n                }\n            }\n        },\n        {\n            \"name\": \"Ruby\",\n            \"arch\": {\n                \"x64\": {\n                    \"versions\": [\n                        \"3.2.*\",\n                        \"3.3.*\",\n                        \"3.4.*\",\n                        \"4.0.*\"\n                    ]\n                },\n                \"arm64\": {\n                    \"versions\": [\n                        \"3.2.*\",\n                        \"3.3.*\",\n                        \"3.4.*\",\n                        \"4.0.*\"\n                    ]\n                }\n            }\n        }\n    ],\n    \"node\": {\n        \"default\": \"24\"\n    },\n    \"llvm\": {\n        \"version\": \"20\"\n    },\n    \"php\": {\n        \"version\": \"8.5\"\n    },\n    \"pwsh\": {\n        \"version\": \"7.4\"\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/Ubuntu2204-Readme.md",
    "content": "| Announcements |\n|-|\n| [[Windows/Ubuntu] Docker Server and Client will be updated to version 29.1.*, Docker Compose will be updated to version 2.40.3 on February 9th, 2026](https://github.com/actions/runner-images/issues/13474) |\n***\n# Ubuntu 22.04\n- OS Version: 22.04.5 LTS\n- Kernel Version: 6.8.0-1044-azure\n- Image Version: 20260309.57.1\n- Systemd version: 249.11-0ubuntu3.17\n\n## Installed Software\n\n### Language and Runtime\n- Bash 5.1.16(1)-release\n- Clang: 13.0.1, 14.0.0, 15.0.7\n- Clang-format: 13.0.1, 14.0.0, 15.0.7\n- Clang-tidy: 13.0.1, 14.0.0, 15.0.7\n- Dash 0.5.11+git20210903+057cd650a4ed-3build1\n- GNU C++: 10.5.0, 11.4.0, 12.3.0\n- GNU Fortran: 10.5.0, 11.4.0, 12.3.0\n- Julia 1.12.5\n- Kotlin 2.3.10-release-465\n- Mono 6.12.0.200\n- MSBuild 16.10.1.31701 (Mono 6.12.0.200)\n- Node.js 20.20.1\n- Perl 5.34.0\n- Python 3.10.12\n- Ruby 3.0.2p107\n- Swift 6.2.4\n\n### Package Management\n- cpan 1.64\n- Helm 3.20.0\n- Homebrew 5.0.16\n- Miniconda 26.1.1\n- Npm 10.8.2\n- NuGet 6.6.1.2\n- Pip 22.0.2\n- Pip3 22.0.2\n- Pipx 1.8.0\n- RubyGems 3.3.5\n- Vcpkg (build from commit 751fdf7bbc)\n- Yarn 1.22.22\n\n#### Environment variables\n| Name                    | Value                  |\n| ----------------------- | ---------------------- |\n| CONDA                   | /usr/share/miniconda   |\n| VCPKG_INSTALLATION_ROOT | /usr/local/share/vcpkg |\n\n#### Homebrew note\n```\nLocation: /home/linuxbrew\nNote: Homebrew is pre-installed on image but not added to PATH.\nrun the eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\" command\nto accomplish this.\n```\n\n### Project Management\n- Ant 1.10.12\n- Gradle 9.4.0\n- Lerna 9.0.5\n- Maven 3.9.13\n- Sbt 1.12.5\n\n### Tools\n- Ansible 2.17.14\n- apt-fast 1.10.0\n- AzCopy 10.32.1 - available by `azcopy` and `azcopy10` aliases\n- Bazel 9.0.0\n- Bazelisk 1.28.1\n- Bicep 0.41.2\n- Buildah 1.23.1\n- CMake 3.31.6\n- CodeQL Action Bundle 2.24.3\n- Docker Amazon ECR Credential Helper 0.12.0\n- Docker Compose v2 2.38.2\n- Docker-Buildx 0.32.1\n- Docker Client 28.0.4\n- Docker Server 28.0.4\n- Fastlane 2.232.2\n- Git 2.53.0\n- Git LFS 3.7.1\n- Git-ftp 1.6.0\n- Haveged 1.9.14\n- Heroku 10.17.0\n- jq 1.6\n- Kind 0.31.0\n- Kubectl 1.35.2\n- Kustomize 5.8.1\n- Leiningen 2.12.0\n- MediaInfo 21.09\n- Mercurial 6.1.1\n- Minikube 1.38.1\n- n 10.2.0\n- Newman 6.2.2\n- nvm 0.40.4\n- OpenSSL 3.0.2-0ubuntu1.21\n- Packer 1.15.0\n- Parcel 2.16.4\n- Podman 3.4.4\n- Pulumi 3.225.1\n- R 4.5.2\n- Skopeo 1.4.1\n- Sphinx Open Source Search Server 2.2.11\n- SVN 1.14.1\n- Terraform 1.14.6\n- yamllint 1.38.0\n- yq 4.52.4\n- zstd 1.5.7\n- Ninja 1.13.2\n\n### CLI Tools\n- Alibaba Cloud CLI 3.2.12\n- AWS CLI 2.34.5\n- AWS CLI Session Manager Plugin 1.2.779.0\n- AWS SAM CLI 1.155.2\n- Azure CLI 2.84.0\n- Azure CLI (azure-devops) 1.0.2\n- GitHub CLI 2.87.3\n- Google Cloud CLI 559.0.0\n- Netlify CLI 24.0.1\n- OpenShift CLI 4.21.4\n- ORAS CLI 1.3.0\n- Vercel CLI 50.29.0\n\n### Java\n| Version             | Environment Variable |\n| ------------------- | -------------------- |\n| 8.0.482+8           | JAVA_HOME_8_X64      |\n| 11.0.30+7 (default) | JAVA_HOME_11_X64     |\n| 17.0.18+8           | JAVA_HOME_17_X64     |\n| 21.0.10+7           | JAVA_HOME_21_X64     |\n| 25.0.2+10           | JAVA_HOME_25_X64     |\n\n### PHP Tools\n- PHP: 8.1.2\n- Composer 2.9.5\n- PHPUnit 8.5.52\n```\nBoth Xdebug and PCOV extensions are installed, but only Xdebug is enabled.\n```\n\n### Haskell Tools\n- Cabal 3.16.1.0\n- GHC 9.14.1\n- GHCup 0.1.50.2\n- Stack 3.9.3\n\n### Rust Tools\n- Cargo 1.94.0\n- Rust 1.94.0\n- Rustdoc 1.94.0\n- Rustup 1.28.2\n\n#### Packages\n- Bindgen 0.72.1\n- Cargo audit 0.22.1\n- Cargo clippy 0.1.94\n- Cargo outdated 0.17.0\n- Cbindgen 0.29.2\n- Rustfmt 1.8.0\n\n### Browsers and Drivers\n- Google Chrome 145.0.7632.159\n- ChromeDriver 145.0.7632.117\n- Chromium 145.0.7632.0\n- Microsoft Edge 145.0.3800.97\n- Microsoft Edge WebDriver 145.0.3800.97\n- Selenium server 4.41.0\n- Mozilla Firefox 148.0\n- Geckodriver 0.36.0\n\n#### Environment variables\n| Name              | Value                                 |\n| ----------------- | ------------------------------------- |\n| CHROMEWEBDRIVER   | /usr/local/share/chromedriver-linux64 |\n| EDGEWEBDRIVER     | /usr/local/share/edge_driver          |\n| GECKOWEBDRIVER    | /usr/local/share/gecko_driver         |\n| SELENIUM_JAR_PATH | /usr/share/java/selenium-server.jar   |\n\n### .NET Tools\n- .NET Core SDK: 8.0.124, 8.0.206, 8.0.319, 8.0.418, 9.0.114, 9.0.205, 9.0.311, 10.0.102\n- nbgv 3.9.50+6feeb89450\n\n### Databases\n- sqlite3 3.37.2\n\n#### PostgreSQL\n- PostgreSQL 14.22\n```\nUser: postgres\nPostgreSQL service is disabled by default.\nUse the following command as a part of your job to start the service: 'sudo systemctl start postgresql.service'\n```\n\n#### MySQL\n- MySQL 8.0.45-0ubuntu0.22.04.1\n```\nUser: root\nPassword: root\nMySQL service is disabled by default.\nUse the following command as a part of your job to start the service: 'sudo systemctl start mysql.service'\n```\n\n#### MS SQL\n- sqlcmd 17.10.0001.1\n- SqlPackage 170.3.93.6\n\n### Cached Tools\n\n#### Go\n- 1.22.12\n- 1.23.12\n- 1.24.13\n- 1.25.8\n\n#### Node.js\n- 20.20.1\n- 22.22.1\n- 24.14.0\n\n#### Python\n- 3.10.20\n- 3.11.15\n- 3.12.13\n- 3.13.12\n- 3.14.3\n\n#### PyPy\n- 3.7.13 [PyPy 7.3.9]\n- 3.8.16 [PyPy 7.3.11]\n- 3.9.19 [PyPy 7.3.16]\n- 3.10.16 [PyPy 7.3.19]\n- 3.11.13 [PyPy 7.3.20]\n\n#### Ruby\n- 3.2.10\n- 3.3.10\n- 3.4.8\n- 4.0.1\n\n### PowerShell Tools\n- PowerShell 7.4.13\n\n#### PowerShell Modules\n- Az: 14.6.0\n- MarkdownPS: 1.10\n- Microsoft.Graph: 2.35.1\n- Pester: 5.7.1\n- PSScriptAnalyzer: 1.24.0\n\n### Web Servers\n| Name    | Version | ConfigFile                | ServiceStatus | ListenPort |\n| ------- | ------- | ------------------------- | ------------- | ---------- |\n| apache2 | 2.4.52  | /etc/apache2/apache2.conf | inactive      | 80         |\n| nginx   | 1.18.0  | /etc/nginx/nginx.conf     | inactive      | 80         |\n\n### Android\n| Package Name               | Version                                                                                                                                                                                                                                                                                                               |\n| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Android Command Line Tools | 9.0                                                                                                                                                                                                                                                                                                                   |\n| Android SDK Build-tools    | 36.0.0 36.1.0<br>35.0.0 35.0.1<br>34.0.0                                                                                                                                                                                                                                                                              |\n| Android SDK Platform-Tools | 37.0.0                                                                                                                                                                                                                                                                                                                |\n| Android SDK Platforms      | android-36.1 (rev 1)<br>android-36-ext19 (rev 1)<br>android-36-ext18 (rev 1)<br>android-36 (rev 2)<br>android-35-ext15 (rev 1)<br>android-35-ext14 (rev 1)<br>android-35 (rev 2)<br>android-34-ext8 (rev 1)<br>android-34-ext12 (rev 1)<br>android-34-ext11 (rev 1)<br>android-34-ext10 (rev 1)<br>android-34 (rev 3) |\n| Android Support Repository | 47.0.0                                                                                                                                                                                                                                                                                                                |\n| CMake                      | 3.18.1<br>3.22.1<br>3.31.5                                                                                                                                                                                                                                                                                            |\n| Google Play services       | 49                                                                                                                                                                                                                                                                                                                    |\n| Google Repository          | 58                                                                                                                                                                                                                                                                                                                    |\n| NDK                        | 27.3.13750724 (default)<br>28.2.13676358<br>29.0.14206865                                                                                                                                                                                                                                                             |\n\n#### Environment variables\n| Name                    | Value                                        |\n| ----------------------- | -------------------------------------------- |\n| ANDROID_HOME            | /usr/local/lib/android/sdk                   |\n| ANDROID_NDK             | /usr/local/lib/android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_HOME        | /usr/local/lib/android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_LATEST_HOME | /usr/local/lib/android/sdk/ndk/29.0.14206865 |\n| ANDROID_NDK_ROOT        | /usr/local/lib/android/sdk/ndk/27.3.13750724 |\n| ANDROID_SDK_ROOT        | /usr/local/lib/android/sdk                   |\n\n### Installed apt packages\n| Name                   | Version                             |\n| ---------------------- | ----------------------------------- |\n| acl                    | 2.3.1-1                             |\n| aria2                  | 1.36.0-1                            |\n| autoconf               | 2.71-2                              |\n| automake               | 1:1.16.5-1.3                        |\n| binutils               | 2.38-4ubuntu2.12                    |\n| bison                  | 2:3.8.2+dfsg-1build1                |\n| brotli                 | 1.0.9-2build6                       |\n| bzip2                  | 1.0.8-5build1                       |\n| coreutils              | 8.32-4.1ubuntu1.2                   |\n| curl                   | 7.81.0-1ubuntu1.22                  |\n| dbus                   | 1.12.20-2ubuntu4.1                  |\n| dnsutils               | 1:9.18.39-0ubuntu0.22.04.2          |\n| dpkg                   | 1.21.1ubuntu2.6                     |\n| dpkg-dev               | 1.21.1ubuntu2.6                     |\n| fakeroot               | 1.28-1ubuntu1                       |\n| file                   | 1:5.41-3ubuntu0.1                   |\n| findutils              | 4.8.0-1ubuntu3                      |\n| flex                   | 2.6.4-8build2                       |\n| fonts-noto-color-emoji | 2.047-0ubuntu0.22.04.1              |\n| ftp                    | 20210827-4build1                    |\n| g++                    | 4:11.2.0-1ubuntu1                   |\n| gcc                    | 4:11.2.0-1ubuntu1                   |\n| gnupg2                 | 2.2.27-3ubuntu2.5                   |\n| haveged                | 1.9.14-1ubuntu1                     |\n| imagemagick            | 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.5 |\n| iproute2               | 5.15.0-1ubuntu2                     |\n| iputils-ping           | 3:20211215-1ubuntu0.1               |\n| jq                     | 1.6-2.1ubuntu3.1                    |\n| lib32z1                | 1:1.2.11.dfsg-2ubuntu9.2            |\n| libc++-dev             | 1:14.0-55\\~exp2                     |\n| libc++abi-dev          | 1:14.0-55\\~exp2                     |\n| libc6-dev              | 2.35-0ubuntu3.13                    |\n| libcurl4               | 7.81.0-1ubuntu1.22                  |\n| libgbm-dev             | 23.2.1-1ubuntu3.1\\~22.04.3          |\n| libgconf-2-4           | 3.2.6-7ubuntu2                      |\n| libgsl-dev             | 2.7.1+dfsg-3                        |\n| libgtk-3-0             | 3.24.33-1ubuntu2.2                  |\n| libmagic-dev           | 1:5.41-3ubuntu0.1                   |\n| libmagickcore-dev      | 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.5 |\n| libmagickwand-dev      | 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.5 |\n| libnss3-tools          | 2:3.98-0ubuntu0.22.04.3             |\n| libsecret-1-dev        | 0.20.5-2                            |\n| libsqlite3-dev         | 3.37.2-2ubuntu0.5                   |\n| libssl-dev             | 3.0.2-0ubuntu1.21                   |\n| libtool                | 2.4.6-15build2                      |\n| libunwind8             | 1.3.2-2build2.1                     |\n| libxkbfile-dev         | 1:1.1.0-1build3                     |\n| libxss1                | 1:1.2.3-1build2                     |\n| libyaml-dev            | 0.2.2-1build2                       |\n| locales                | 2.35-0ubuntu3.13                    |\n| lz4                    | 1.9.3-2build2                       |\n| m4                     | 1.4.18-5ubuntu2                     |\n| make                   | 4.3-4.1build1                       |\n| mediainfo              | 22.03-1                             |\n| mercurial              | 6.1.1-1ubuntu1                      |\n| net-tools              | 1.60+git20181103.0eebece-1ubuntu5.4 |\n| netcat                 | 1.218-4ubuntu1                      |\n| openssh-client         | 1:8.9p1-3ubuntu0.13                 |\n| p7zip-full             | 16.02+dfsg-8                        |\n| p7zip-rar              | 16.02-3build1                       |\n| parallel               | 20210822+ds-2                       |\n| pass                   | 1.7.4-5                             |\n| patchelf               | 0.14.3-1                            |\n| pigz                   | 2.6-1                               |\n| pkg-config             | 0.29.2-1ubuntu3                     |\n| pollinate              | 4.33-3ubuntu2.1                     |\n| python-is-python3      | 3.9.2-2                             |\n| rpm                    | 4.17.0+dfsg1-4build1                |\n| rsync                  | 3.2.7-0ubuntu0.22.04.4              |\n| shellcheck             | 0.8.0-2                             |\n| sphinxsearch           | 2.2.11-8                            |\n| sqlite3                | 3.37.2-2ubuntu0.5                   |\n| ssh                    | 1:8.9p1-3ubuntu0.13                 |\n| sshpass                | 1.09-1                              |\n| subversion             | 1.14.1-3ubuntu0.22.04.1             |\n| sudo                   | 1.9.9-1ubuntu2.5                    |\n| swig                   | 4.0.2-1ubuntu1                      |\n| systemd-coredump       | 249.11-0ubuntu3.17                  |\n| tar                    | 1.34+dfsg-1ubuntu0.1.22.04.2        |\n| telnet                 | 0.17-44build1                       |\n| texinfo                | 6.8-4build1                         |\n| time                   | 1.9-0.1build2                       |\n| tk                     | 8.6.11+1build2                      |\n| tzdata                 | 2025b-0ubuntu0.22.04.1              |\n| unzip                  | 6.0-26ubuntu3.2                     |\n| upx                    | 3.96-3                              |\n| wget                   | 1.21.2-2ubuntu1.1                   |\n| xorriso                | 1.5.4-2                             |\n| xvfb                   | 2:21.1.4-2ubuntu1.7\\~22.04.16       |\n| xz-utils               | 5.2.5-2ubuntu1                      |\n| zip                    | 3.0-12build2                        |\n| zsync                  | 0.6.2-3ubuntu1                      |\n\n"
  },
  {
    "path": "images/ubuntu/Ubuntu2404-Readme.md",
    "content": "| Announcements |\n|-|\n| [[Windows/Ubuntu] Docker Server and Client will be updated to version 29.1.*, Docker Compose will be updated to version 2.40.3 on February 9th, 2026](https://github.com/actions/runner-images/issues/13474) |\n***\n# Ubuntu 24.04\n- OS Version: 24.04.3 LTS\n- Kernel Version: 6.14.0-1017-azure\n- Image Version: 20260309.50.1\n- Systemd version: 255.4-1ubuntu8.12\n\n## Installed Software\n\n### Language and Runtime\n- Bash 5.2.21(1)-release\n- Clang: 16.0.6, 17.0.6, 18.1.3\n- Clang-format: 16.0.6, 17.0.6, 18.1.3\n- Clang-tidy: 16.0.6, 17.0.6, 18.1.3\n- Dash 0.5.12-6ubuntu5\n- GNU C++: 12.4.0, 13.3.0, 14.2.0\n- GNU Fortran: 12.4.0, 13.3.0, 14.2.0\n- Julia 1.12.5\n- Kotlin 2.3.10-release-465\n- Node.js 20.20.1\n- Perl 5.38.2\n- Python 3.12.3\n- Ruby 3.2.3\n- Swift 6.2.4\n\n### Package Management\n- cpan 1.64\n- Helm 3.20.0\n- Homebrew 5.0.16\n- Miniconda 26.1.1\n- Npm 10.8.2\n- Pip 24.0\n- Pip3 24.0\n- Pipx 1.8.0\n- RubyGems 3.4.20\n- Vcpkg (build from commit 751fdf7bbc)\n- Yarn 1.22.22\n\n#### Environment variables\n| Name                    | Value                  |\n| ----------------------- | ---------------------- |\n| CONDA                   | /usr/share/miniconda   |\n| VCPKG_INSTALLATION_ROOT | /usr/local/share/vcpkg |\n\n#### Homebrew note\n```\nLocation: /home/linuxbrew\nNote: Homebrew is pre-installed on image but not added to PATH.\nrun the eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\" command\nto accomplish this.\n```\n\n### Project Management\n- Ant 1.10.14\n- Gradle 9.4.0\n- Lerna 9.0.5\n- Maven 3.9.13\n\n### Tools\n- Ansible 2.20.3\n- AzCopy 10.32.1 - available by `azcopy` and `azcopy10` aliases\n- Bazel 9.0.0\n- Bazelisk 1.28.1\n- Bicep 0.41.2\n- Buildah 1.33.7\n- CMake 3.31.6\n- CodeQL Action Bundle 2.24.3\n- Docker Amazon ECR Credential Helper 0.12.0\n- Docker Compose v2 2.38.2\n- Docker-Buildx 0.32.1\n- Docker Client 28.0.4\n- Docker Server 28.0.4\n- Fastlane 2.232.2\n- Git 2.53.0\n- Git LFS 3.7.1\n- Git-ftp 1.6.0\n- Haveged 1.9.14\n- jq 1.7\n- Kind 0.31.0\n- Kubectl 1.35.2\n- Kustomize 5.8.1\n- MediaInfo 24.01\n- Mercurial 6.7.2\n- Minikube 1.38.1\n- n 10.2.0\n- Newman 6.2.2\n- nvm 0.40.4\n- OpenSSL 3.0.13-0ubuntu3.7\n- Packer 1.15.0\n- Parcel 2.16.4\n- Podman 4.9.3\n- Pulumi 3.225.1\n- Skopeo 1.13.3\n- Sphinx Open Source Search Server 2.2.11\n- yamllint 1.38.0\n- yq 4.52.4\n- zstd 1.5.7\n- Ninja 1.13.2\n\n### CLI Tools\n- AWS CLI 2.34.5\n- AWS CLI Session Manager Plugin 1.2.779.0\n- AWS SAM CLI 1.155.2\n- Azure CLI 2.84.0\n- Azure CLI (azure-devops) 1.0.2\n- GitHub CLI 2.87.3\n- Google Cloud CLI 559.0.0\n\n### Java\n| Version             | Environment Variable |\n| ------------------- | -------------------- |\n| 8.0.482+8           | JAVA_HOME_8_X64      |\n| 11.0.30+7           | JAVA_HOME_11_X64     |\n| 17.0.18+8 (default) | JAVA_HOME_17_X64     |\n| 21.0.10+7           | JAVA_HOME_21_X64     |\n| 25.0.2+10           | JAVA_HOME_25_X64     |\n\n### PHP Tools\n- PHP: 8.3.6\n- Composer 2.9.5\n- PHPUnit 8.5.52\n```\nBoth Xdebug and PCOV extensions are installed, but only Xdebug is enabled.\n```\n\n### Haskell Tools\n- Cabal 3.16.1.0\n- GHC 9.14.1\n- GHCup 0.1.50.2\n- Stack 3.9.3\n\n### Rust Tools\n- Cargo 1.94.0\n- Rust 1.94.0\n- Rustdoc 1.94.0\n- Rustup 1.28.2\n\n#### Packages\n- Rustfmt 1.8.0\n\n### Browsers and Drivers\n- Google Chrome 145.0.7632.159\n- ChromeDriver 145.0.7632.117\n- Chromium 145.0.7632.0\n- Microsoft Edge 145.0.3800.97\n- Microsoft Edge WebDriver 145.0.3800.97\n- Selenium server 4.41.0\n- Mozilla Firefox 148.0\n- Geckodriver 0.36.0\n\n#### Environment variables\n| Name              | Value                                 |\n| ----------------- | ------------------------------------- |\n| CHROMEWEBDRIVER   | /usr/local/share/chromedriver-linux64 |\n| EDGEWEBDRIVER     | /usr/local/share/edge_driver          |\n| GECKOWEBDRIVER    | /usr/local/share/gecko_driver         |\n| SELENIUM_JAR_PATH | /usr/share/java/selenium-server.jar   |\n\n### .NET Tools\n- .NET Core SDK: 8.0.124, 8.0.206, 8.0.319, 8.0.418, 9.0.114, 9.0.205, 9.0.311, 10.0.102\n- nbgv 3.9.50+6feeb89450\n\n### Databases\n- sqlite3 3.45.1\n\n#### PostgreSQL\n- PostgreSQL 16.13\n```\nUser: postgres\nPostgreSQL service is disabled by default.\nUse the following command as a part of your job to start the service: 'sudo systemctl start postgresql.service'\n```\n\n#### MySQL\n- MySQL 8.0.45-0ubuntu0.24.04.1\n```\nUser: root\nPassword: root\nMySQL service is disabled by default.\nUse the following command as a part of your job to start the service: 'sudo systemctl start mysql.service'\n```\n\n### Cached Tools\n\n#### Go\n- 1.22.12\n- 1.23.12\n- 1.24.13\n- 1.25.8\n\n#### Node.js\n- 20.20.1\n- 22.22.1\n- 24.14.0\n\n#### Python\n- 3.10.20\n- 3.11.15\n- 3.12.13\n- 3.13.12\n- 3.14.3\n\n#### PyPy\n- 3.9.19 [PyPy 7.3.16]\n- 3.10.16 [PyPy 7.3.19]\n- 3.11.13 [PyPy 7.3.20]\n\n#### Ruby\n- 3.2.10\n- 3.3.10\n- 3.4.8\n- 4.0.1\n\n### PowerShell Tools\n- PowerShell 7.4.13\n\n#### PowerShell Modules\n- Az: 14.6.0\n- Microsoft.Graph: 2.35.1\n- Pester: 5.7.1\n- PSScriptAnalyzer: 1.24.0\n\n### Web Servers\n| Name    | Version | ConfigFile                | ServiceStatus | ListenPort |\n| ------- | ------- | ------------------------- | ------------- | ---------- |\n| apache2 | 2.4.58  | /etc/apache2/apache2.conf | inactive      | 80         |\n| nginx   | 1.24.0  | /etc/nginx/nginx.conf     | inactive      | 80         |\n\n### Android\n| Package Name               | Version                                                                                                                                                                                                                                                                                                               |\n| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Android Command Line Tools | 12.0                                                                                                                                                                                                                                                                                                                  |\n| Android SDK Build-tools    | 36.0.0 36.1.0<br>35.0.0 35.0.1<br>34.0.0                                                                                                                                                                                                                                                                              |\n| Android SDK Platform-Tools | 37.0.0                                                                                                                                                                                                                                                                                                                |\n| Android SDK Platforms      | android-36.1 (rev 1)<br>android-36-ext19 (rev 1)<br>android-36-ext18 (rev 1)<br>android-36 (rev 2)<br>android-35-ext15 (rev 1)<br>android-35-ext14 (rev 1)<br>android-35 (rev 2)<br>android-34-ext8 (rev 1)<br>android-34-ext12 (rev 1)<br>android-34-ext11 (rev 1)<br>android-34-ext10 (rev 1)<br>android-34 (rev 3) |\n| Android Support Repository | 47.0.0                                                                                                                                                                                                                                                                                                                |\n| CMake                      | 3.31.5<br>4.1.2                                                                                                                                                                                                                                                                                                       |\n| Google Play services       | 49                                                                                                                                                                                                                                                                                                                    |\n| Google Repository          | 58                                                                                                                                                                                                                                                                                                                    |\n| NDK                        | 27.3.13750724 (default)<br>28.2.13676358<br>29.0.14206865                                                                                                                                                                                                                                                             |\n\n#### Environment variables\n| Name                    | Value                                        |\n| ----------------------- | -------------------------------------------- |\n| ANDROID_HOME            | /usr/local/lib/android/sdk                   |\n| ANDROID_NDK             | /usr/local/lib/android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_HOME        | /usr/local/lib/android/sdk/ndk/27.3.13750724 |\n| ANDROID_NDK_LATEST_HOME | /usr/local/lib/android/sdk/ndk/29.0.14206865 |\n| ANDROID_NDK_ROOT        | /usr/local/lib/android/sdk/ndk/27.3.13750724 |\n| ANDROID_SDK_ROOT        | /usr/local/lib/android/sdk                   |\n\n### Installed apt packages\n| Name                   | Version                      |\n| ---------------------- | ---------------------------- |\n| acl                    | 2.3.2-1build1.1              |\n| aria2                  | 1.37.0+debian-1build3        |\n| autoconf               | 2.71-3                       |\n| automake               | 1:1.16.5-1.3ubuntu1          |\n| binutils               | 2.42-4ubuntu2.8              |\n| bison                  | 2:3.8.2+dfsg-1build2         |\n| brotli                 | 1.1.0-2build2                |\n| bzip2                  | 1.0.8-5.1build0.1            |\n| coreutils              | 9.4-3ubuntu6.1               |\n| curl                   | 8.5.0-2ubuntu10.7            |\n| dbus                   | 1.14.10-4ubuntu4.1           |\n| dnsutils               | 1:9.18.39-0ubuntu0.24.04.2   |\n| dpkg                   | 1.22.6ubuntu6.5              |\n| dpkg-dev               | 1.22.6ubuntu6.5              |\n| fakeroot               | 1.33-1                       |\n| file                   | 1:5.45-3build1               |\n| findutils              | 4.9.0-5build1                |\n| flex                   | 2.6.4-8.2build1              |\n| fonts-noto-color-emoji | 2.047-0ubuntu0.24.04.1       |\n| ftp                    | 20230507-2build3             |\n| g++                    | 4:13.2.0-7ubuntu1            |\n| gcc                    | 4:13.2.0-7ubuntu1            |\n| gnupg2                 | 2.4.4-2ubuntu17.4            |\n| haveged                | 1.9.14-1ubuntu2              |\n| iproute2               | 6.1.0-1ubuntu6.2             |\n| iputils-ping           | 3:20240117-1ubuntu0.1        |\n| jq                     | 1.7.1-3ubuntu0.24.04.1       |\n| libnss3-tools          | 2:3.98-1ubuntu0.1            |\n| libsqlite3-dev         | 3.45.1-1ubuntu2.5            |\n| libssl-dev             | 3.0.13-0ubuntu3.7            |\n| libtool                | 2.4.7-7build1                |\n| libyaml-dev            | 0.2.5-1build1                |\n| locales                | 2.39-0ubuntu8.7              |\n| lz4                    | 1.9.4-1build1.1              |\n| m4                     | 1.4.19-4build1               |\n| make                   | 4.3-4.1build2                |\n| mediainfo              | 24.01.1-1build2              |\n| mercurial              | 6.7.2-1ubuntu2.2             |\n| net-tools              | 2.10-0.1ubuntu4.4            |\n| netcat                 | 1.226-1ubuntu2               |\n| openssh-client         | 1:9.6p1-3ubuntu13.14         |\n| p7zip-full             | 16.02+transitional.1         |\n| p7zip-rar              | 16.02+transitional.1         |\n| parallel               | 20231122+ds-1                |\n| patchelf               | 0.18.0-1.1build1             |\n| pigz                   | 2.8-1                        |\n| pkg-config             | 1.8.1-2build1                |\n| pollinate              | 4.33-3.1ubuntu1.1            |\n| python-is-python3      | 3.11.4-1                     |\n| rpm                    | 4.18.2+dfsg-2.1build2        |\n| rsync                  | 3.2.7-1ubuntu1.2             |\n| shellcheck             | 0.9.0-1                      |\n| sphinxsearch           | 2.2.11-8build1               |\n| sqlite3                | 3.45.1-1ubuntu2.5            |\n| ssh                    | 1:9.6p1-3ubuntu13.14         |\n| sshpass                | 1.09-1                       |\n| sudo                   | 1.9.15p5-3ubuntu5.24.04.1    |\n| swig                   | 4.2.0-2ubuntu1               |\n| systemd-coredump       | 255.4-1ubuntu8.12            |\n| tar                    | 1.35+dfsg-3build1            |\n| telnet                 | 0.17+2.5-3ubuntu4.1          |\n| texinfo                | 7.1-3build2                  |\n| time                   | 1.9-0.2build1                |\n| tk                     | 8.6.14build1                 |\n| tree                   | 2.1.1-2ubuntu3.24.04.2       |\n| tzdata                 | 2025b-0ubuntu0.24.04.1       |\n| unzip                  | 6.0-28ubuntu4.1              |\n| upx                    | 4.2.2-3                      |\n| wget                   | 1.21.4-1ubuntu4.1            |\n| xvfb                   | 2:21.1.12-1ubuntu1.5         |\n| xz-utils               | 5.6.1+really5.4.5-1ubuntu0.2 |\n| zip                    | 3.0-13ubuntu0.2              |\n| zsync                  | 0.6.2-5build1                |\n\n"
  },
  {
    "path": "images/ubuntu/assets/post-gen/cleanup-logs.sh",
    "content": "#!/bin/bash\n\n# journalctl\nif command -v journalctl; then\n    journalctl --rotate\n    journalctl --vacuum-time=1s\nfi\n\n# delete all .gz and rotated file\nfind /var/log -type f -regex \".*\\.gz$\" -delete\nfind /var/log -type f -regex \".*\\.[0-9]$\" -delete\n\n# wipe log files\nfind /var/log/ -type f -exec cp /dev/null {} \\;"
  },
  {
    "path": "images/ubuntu/assets/post-gen/environment-variables.sh",
    "content": "#!/bin/bash\n\n# Replace $HOME with the default user's home directory for environmental variables related to the default user home directory\n\nhomeDir=$(cut -d: -f6 /etc/passwd | tail -1)\nsed -i \"s|\\$HOME|$homeDir|g\" /etc/environment"
  },
  {
    "path": "images/ubuntu/assets/post-gen/systemd-linger.sh",
    "content": "#!/bin/bash\n\n# Enable user session on boot, not on login\nUserId=$(cut -d: -f3 /etc/passwd | tail -1)\nloginctl enable-linger $UserId\n"
  },
  {
    "path": "images/ubuntu/assets/ubuntu2204.conf",
    "content": "# Name of pool supported by this image\nPOOL_NAME=\"Ubuntu 2204\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/Configure-Toolset.ps1",
    "content": "################################################################################\n##  File:  Configure-Toolset.ps1\n##  Team:  CI-Build\n##  Desc:  Configure toolset\n################################################################################\n\nImport-Module \"$env:HELPER_SCRIPTS/../tests/Helpers.psm1\"\n\nfunction Get-TCToolVersionPath {\n    param(\n        [Parameter(Mandatory)]\n        [string] $ToolName,\n        [Parameter(Mandatory)]\n        [string] $ToolVersion,\n        [Parameter(Mandatory)]\n        [string] $ToolArchitecture\n    )\n\n    $toolPath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath $ToolName\n    $toolPathVersion = Join-Path -Path $toolPath -ChildPath $ToolVersion\n    $foundVersion = Get-Item $toolPathVersion | Sort-Object -Property { [version] $_.name } -Descending | Select-Object -First 1\n    $installationDir = Join-Path -Path $foundVersion -ChildPath $ToolArchitecture\n\n    return $installationDir\n}\n\nfunction Add-GlobalEnvironmentVariable {\n    param(\n        [Parameter(Mandatory)]\n        [string] $Name,\n        [Parameter(Mandatory)]\n        [string] $Value,\n        [string] $FilePath = \"/etc/environment\"\n    )\n\n    $envVar = \"{0}={1}\" -f $Name, $Value\n    Tee-Object -InputObject $envVar -FilePath $FilePath -Append\n}\n\n$ErrorActionPreference = \"Stop\"\n\nWrite-Host \"Configure toolcache tools environment...\"\n$toolEnvConfigs = @{\n    go = @{\n        command          = \"ln -s {0}/bin/* /usr/bin/\"\n        variableTemplate = \"GOROOT_{0}_{1}_X64\"\n    }\n}\n\n# Get toolcache content from toolset\n$tools = (Get-ToolsetContent).toolcache | Where-Object { $toolEnvConfigs.Keys -contains $_.name }\n\nforeach ($tool in $tools) {\n    $toolEnvConfig = $toolEnvConfigs[$tool.name]\n\n    if (-not ([string]::IsNullOrEmpty($toolEnvConfig.variableTemplate))) {\n        foreach ($toolVersion in $tool.versions) {\n            Write-Host \"Set $($tool.name) $toolVersion environment variable...\"\n            $toolPath = Get-TCToolVersionPath -ToolName $tool.name -ToolVersion $toolVersion -ToolArchitecture $tool.arch\n            $envVariableName = $toolEnvConfig.variableTemplate -f $toolVersion.split(\".\")\n\n            Add-GlobalEnvironmentVariable -Name $envVariableName -Value $toolPath\n        }\n    }\n\n    # Invoke command and add env variable for the default tool version\n    if (-not ([string]::IsNullOrEmpty($tool.default))) {\n        $toolDefaultPath = Get-TCToolVersionPath -ToolName $tool.name -ToolVersion $tool.default -ToolArchitecture $tool.arch\n\n        if (-not ([string]::IsNullOrEmpty($toolEnvConfig.defaultVariable))) {\n            Write-Host \"Set default $($toolEnvConfig.defaultVariable) for $($tool.name) $($tool.default) environment variable...\"\n            Add-GlobalEnvironmentVariable -Name $toolEnvConfig.defaultVariable -Value $toolDefaultPath\n        }\n\n        if (-not ([string]::IsNullOrEmpty($toolEnvConfig.command))) {\n            $command = $toolEnvConfig.command -f $toolDefaultPath\n            Write-Host \"Invoke $command command for default $($tool.name) $($tool.default) ...\"\n            Invoke-Expression -Command $command\n        }\n    }\n}\n\nInvoke-PesterTests -TestFile \"Toolset\" -TestName \"Toolset\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/Install-PowerShellAzModules.ps1",
    "content": "################################################################################\n##  File:  Install-PowerShellAzModules.ps1\n##  Desc:  Install Az modules for PowerShell\n################################################################################\n\n$ErrorActionPreference = \"Stop\"\n$ProgressPreference = \"SilentlyContinue\"\n\nImport-Module \"$env:HELPER_SCRIPTS/../tests/Helpers.psm1\"\n\n# Get modules content from toolset\n$modules = (Get-ToolsetContent).azureModules\n$installPSModulePath = \"/usr/share\"\n$psModuleMachinePath = $env:PSModulePath + \":\"\n\nforeach ($module in $modules) {\n    $moduleName = $module.name\n\n    Write-Host \"Installing ${moduleName} to the ${installPSModulePath} path...\"\n    foreach ($version in $module.versions) {\n        $modulePath = Join-Path -Path $installPSModulePath -ChildPath \"${moduleName}_${version}\"\n        Write-Host \" - $version [$modulePath]\"\n        $psModuleMachinePath += ($modulePath + \":\")\n        Save-Module -Path $modulePath -Name $moduleName -RequiredVersion $version -Force\n    }\n}\n\n$finalLine = \"PSModulePath=$($psModuleMachinePath.TrimEnd(':').Replace(\"\\root\", '$HOME'))\"\nAdd-Content -Path \"/etc/environment\" -Value $finalLine\n\nInvoke-PesterTests -TestFile \"PowerShellModules\" -TestName \"AzureModules\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/Install-PowerShellModules.ps1",
    "content": "################################################################################\n##  File:  Install-PowerShellModules.ps1\n##  Desc:  Install modules for PowerShell\n################################################################################\n\n$ErrorActionPreference = \"Stop\"\n$ProgressPreference = \"SilentlyContinue\"\n\nImport-Module \"$env:HELPER_SCRIPTS/../tests/Helpers.psm1\"\n\n# Specifies the installation policy\nSet-PSRepository -InstallationPolicy Trusted -Name PSGallery\n\n# Try to update PowerShellGet before the actual installation\nInstall-Module -Name PowerShellGet -Force\nUpdate-Module -Name PowerShellGet -Force\n\n# Install PowerShell modules\n$modules = (Get-ToolsetContent).powershellModules\n\nforeach($module in $modules) {\n    $moduleName = $module.name\n\n    Write-Host \"Installing ${moduleName} module\"\n    if ($module.versions) {\n        foreach ($version in $module.versions) {\n            Write-Host \" - $version\"\n            Install-Module -Name $moduleName -RequiredVersion $version -Scope AllUsers -SkipPublisherCheck -Force\n        }\n    } else {\n        Install-Module -Name $moduleName -Scope AllUsers -SkipPublisherCheck -Force\n    }\n}\n\nInvoke-PesterTests -TestFile \"PowerShellModules\" -TestName \"PowerShellModules\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/Install-Toolset.ps1",
    "content": "################################################################################\n##  File:  Install-Toolset.ps1\n##  Team:  CI-Build\n##  Desc:  Install toolset\n################################################################################\n\nImport-Module \"$env:HELPER_SCRIPTS/../tests/Helpers.psm1\"\n\nfunction Install-Asset {\n    param(\n        [Parameter(Mandatory = $true)]\n        [object] $ReleaseAsset\n    )\n\n    Write-Host \"Download $($ReleaseAsset.filename)\"\n    $assetArchivePath = Invoke-DownloadWithRetry $ReleaseAsset.download_url\n\n    Write-Host \"Extract $($ReleaseAsset.filename) content...\"\n    $assetFolderPath = Join-Path \"/tmp\" \"$($ReleaseAsset.filename)-temp-dir\"\n    New-Item -ItemType Directory -Path $assetFolderPath | Out-Null\n    tar -xzf $assetArchivePath -C $assetFolderPath\n\n    Write-Host \"Invoke installation script...\"\n    Push-Location -Path $assetFolderPath\n    Invoke-Expression \"bash ./setup.sh\"\n    Pop-Location\n}\n\n$ErrorActionPreference = \"Stop\"\n\n# Get toolcache content from toolset\n$tools = (Get-ToolsetContent).toolcache | Where-Object { $_.url -ne $null }\n\nforeach ($tool in $tools) {\n    # Get versions manifest for current tool\n    $assets = Invoke-RestMethod $tool.url\n\n    # Get github release asset for each version\n    foreach ($toolVersion in $tool.versions) {\n        $asset = $assets | Where-Object version -like $toolVersion `\n            | Select-Object -ExpandProperty files `\n            | Where-Object { ($_.platform -eq $tool.platform) -and ($_.arch -eq $tool.arch) -and ($_.platform_version -eq $tool.platform_version)} `\n            | Select-Object -First 1\n\n        if (-not $asset) {\n            Write-Host \"Asset for $($tool.name) $toolVersion $($tool.arch) not found in versions manifest\"\n            exit 1\n        }\n\n        Write-Host \"Installing $($tool.name) $toolVersion $($tool.arch)...\"\n        Install-Asset -ReleaseAsset $asset\n    }\n\n    chown -R \"$($env:SUDO_USER):$($env:SUDO_USER)\" \"/opt/hostedtoolcache/$($tool.name)\"\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/build/cleanup.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  cleanup.sh\n##  Desc:  Perform cleanup\n################################################################################\n\n# before cleanup\nbefore=$(df / -Pm | awk 'NR==2{print $4}')\n\n# clears out the local repository of retrieved package files\n# It removes everything but the lock file from /var/cache/apt/archives/ and /var/cache/apt/archives/partial\napt-get clean\nrm -rf /tmp/*\nrm -rf /root/.cache\n\n# journalctl\nif command -v journalctl; then\n    journalctl --rotate\n    journalctl --vacuum-time=1s\nfi\n\n# delete all .gz and rotated file\nfind /var/log -type f -regex \".*\\.gz$\" -delete\nfind /var/log -type f -regex \".*\\.[0-9]$\" -delete\n\n# wipe log files\nfind /var/log/ -type f -exec cp /dev/null {} \\;\n\n# delete symlink for tests running\nrm -f /usr/local/bin/invoke_tests\n\n# remove apt mock\nprefix=/usr/local/bin\nfor tool in apt apt-get apt-key;do\n    sudo rm -f $prefix/$tool\ndone\n\n# after cleanup\nafter=$(df / -Pm | awk 'NR==2{print $4}')\n\n# display size\necho \"Before: $before MB\"\necho \"After : $after MB\"\necho \"Delta : $(($after-$before)) MB\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/configure-apt-mock.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-apt-mock.sh\n##  Desc:  A temporary workaround for https://github.com/Azure/azure-linux-extensions/issues/1238.\n##         Cleaned up during cleanup.sh.\n################################################################################\n\nprefix=/usr/local/bin\n\nfor real_tool in /usr/bin/apt /usr/bin/apt-get /usr/bin/apt-key; do\n    tool=$(basename $real_tool)\n    cat >$prefix/$tool <<EOT\n#!/bin/sh\n\ni=1\nwhile [ \\$i -le 30 ];do\n  err=\\$(mktemp)\n  $real_tool \"\\$@\" 2>\\$err\n\n  # no errors, break the loop and continue normal flow\n  test -f \\$err || break\n  cat \\$err >&2\n\n  retry=false\n\n  if grep -q 'Could not get lock' \\$err;then\n    # apt db locked needs retry\n    retry=true\n  elif grep -q 'Could not get lock /var/lib/apt/lists/lock' \\$err;then\n    # apt update did not complete (race condition), needs retry\n    retry=true\n  elif grep -q 'Problem renaming the file /var/cache/apt/pkgcache.bin.* to /var/cache/apt/pkgcache.bin' \\$err;then\n    # apt-get did not complete (race condition), needs retry\n    retry=true\n  elif grep -q 'Problem renaming the file /var/cache/apt/srcpkgcache.bin.* to /var/cache/apt/srcpkgcache.bin' \\$err;then\n    # apt update did not complete (race condition), needs retry\n    retry=true\n  elif grep -q 'Could not open file /var/lib/apt/lists' \\$err;then\n    # apt update is not completed, needs retry\n    retry=true\n  elif grep -q 'IPC connect call failed' \\$err;then\n    # the delay should help with gpg-agent not ready\n    retry=true\n  elif grep -q 'Temporary failure in name resolution' \\$err;then\n    # It looks like DNS is not updated with random generated hostname yet\n    retry=true\n  elif grep -q 'dpkg frontend is locked by another process' \\$err;then\n    # dpkg process is busy by another process\n    retry=true\n  fi\n\n  rm \\$err\n  if [ \\$retry = false ]; then\n    break\n  fi\n\n  sleep 5\n  echo \"...retry \\$i\"\n  i=\\$((i + 1))\ndone\nEOT\n    chmod +x $prefix/$tool\ndone\n"
  },
  {
    "path": "images/ubuntu/scripts/build/configure-apt-sources.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-apt-sources.sh\n##  Desc:  Configure apt sources with failover from Azure to Ubuntu archives.\n################################################################################\n\nsource $HELPER_SCRIPTS/os.sh\n\ntouch /etc/apt/apt-mirrors.txt\n\nprintf \"http://azure.archive.ubuntu.com/ubuntu/\\tpriority:1\\n\" | tee -a /etc/apt/apt-mirrors.txt\nprintf \"https://archive.ubuntu.com/ubuntu/\\tpriority:2\\n\" | tee -a /etc/apt/apt-mirrors.txt\nprintf \"https://security.ubuntu.com/ubuntu/\\tpriority:3\\n\" | tee -a /etc/apt/apt-mirrors.txt\n\nif is_ubuntu24; then\n    sed -i 's|http://azure\\.archive\\.ubuntu\\.com/ubuntu/|mirror+file:/etc/apt/apt-mirrors.txt|' /etc/apt/sources.list.d/ubuntu.sources\n\n    # Apt changes to survive Cloud Init\n    cp -f /etc/apt/sources.list.d/ubuntu.sources  /etc/cloud/templates/sources.list.ubuntu.deb822.tmpl\nelse\n    sed -i 's|http://azure\\.archive\\.ubuntu\\.com/ubuntu/|mirror+file:/etc/apt/apt-mirrors.txt|' /etc/apt/sources.list\n\n    # Apt changes to survive Cloud Init\n    cp -f /etc/apt/sources.list /etc/cloud/templates/sources.list.ubuntu.tmpl\nfi\n"
  },
  {
    "path": "images/ubuntu/scripts/build/configure-apt.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-apt.sh\n##  Desc:  Configure apt, install jq and apt-fast packages.\n################################################################################\n\nsource $HELPER_SCRIPTS/os.sh\n\n# Stop and disable apt-daily upgrade services;\nsystemctl stop apt-daily.timer\nsystemctl disable apt-daily.timer\nsystemctl disable apt-daily.service\nsystemctl stop apt-daily-upgrade.timer\nsystemctl disable apt-daily-upgrade.timer\nsystemctl disable apt-daily-upgrade.service\n\n# Enable retry logic for apt up to 10 times\necho \"APT::Acquire::Retries \\\"10\\\";\" > /etc/apt/apt.conf.d/80-retries\n\n# Configure apt to always assume Y\necho \"APT::Get::Assume-Yes \\\"true\\\";\" > /etc/apt/apt.conf.d/90assumeyes\n\n# 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.\n# In case you have multiple systems that you want to receive the same set of updates, \n# you can set APT::Machine-ID to a UUID such that they all phase the same, \n# 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.\n# apt-cache policy pkgname\necho 'APT::Get::Always-Include-Phased-Updates \"true\";' > /etc/apt/apt.conf.d/99-phased-updates\n\n# Fix bad proxy and http headers settings\ncat <<EOF >> /etc/apt/apt.conf.d/99bad_proxy\nAcquire::http::Pipeline-Depth 0;\nAcquire::http::No-Cache true;\nAcquire::https::Pipeline-Depth 0;\nAcquire::https::No-Cache true;\nAcquire::BrokenProxy    true;\nEOF\n\n# Uninstall unattended-upgrades\napt-get purge unattended-upgrades\n\necho 'APT sources'\nif ! is_ubuntu24; then\n    cat /etc/apt/sources.list\nelse\n    cat /etc/apt/sources.list.d/ubuntu.sources\nfi\n\napt-get update\n# Install jq\napt-get install jq\n\nif ! is_ubuntu24; then\n    # Install apt-fast using quick-install.sh\n    # https://github.com/ilikenwf/apt-fast\n    bash -c \"$(curl -fsSL https://raw.githubusercontent.com/ilikenwf/apt-fast/master/quick-install.sh)\"\nfi\n"
  },
  {
    "path": "images/ubuntu/scripts/build/configure-dpkg.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-dpkg.sh\n##  Desc:  Configure dpkg\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\nsource $HELPER_SCRIPTS/os.sh\n# This is the anti-frontend. It never interacts with you  at  all,\n# and  makes  the  default  answers  be used for all questions. It\n# might mail error messages to root, but that's it;  otherwise  it\n# is  completely  silent  and  unobtrusive, a perfect frontend for\n# automatic installs. If you are using this front-end, and require\n# non-default  answers  to questions, you will need to pre-seed the\n# debconf database\nset_etc_environment_variable \"DEBIAN_FRONTEND\" \"noninteractive\"\n\n# dpkg can be instructed not to ask for confirmation\n# when replacing a configuration file (with the --force-confdef --force-confold options)\ncat <<EOF >> /etc/apt/apt.conf.d/10dpkg-options\nDpkg::Options {\n  \"--force-confdef\";\n  \"--force-confold\";\n}\nEOF\n\n# hide information about packages that are no longer required\ncat <<EOF >> /etc/apt/apt.conf.d/10apt-autoremove\nAPT::Get::AutomaticRemove \"0\";\nAPT::Get::HideAutoRemove \"1\";\nEOF\n\n# Install libicu70 package for Ubuntu 24\nif  is_ubuntu24 ; then\n  wget https://archive.ubuntu.com/ubuntu/pool/main/i/icu/libicu70_70.1-2_amd64.deb\n\n  EXPECTED_LIBICU_SHA512=\"a6315482d93606e375c272718d2458870b95e4ed4b672ea8640cf7bc2d2c2f41aea13b798b1e417e1ffc472a90c6aad150d3d293aa9bddec48e39106e4042807\"\n  ACTUAL_LIBICU_SHA512=\"$(sha512sum \"./libicu70_70.1-2_amd64.deb\" | awk '{print $1}')\"\n  [ \"$EXPECTED_LIBICU_SHA512\" = \"$ACTUAL_LIBICU_SHA512\" ] || { echo \"libicu checksum mismatch in configure-dpkg.sh\"; exit 1;}\n  sudo apt-get install -y ./libicu70_70.1-2_amd64.deb\nfi\n"
  },
  {
    "path": "images/ubuntu/scripts/build/configure-environment.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-environment.sh\n##  Desc:  Configure system and environment\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/os.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# Set ImageVersion and ImageOS env variables\nset_etc_environment_variable \"ImageVersion\" \"${IMAGE_VERSION}\"\nset_etc_environment_variable \"ImageOS\" \"${IMAGE_OS}\"\n\n# Set the ACCEPT_EULA variable to Y value to confirm your acceptance of the End-User Licensing Agreement\nset_etc_environment_variable \"ACCEPT_EULA\" \"Y\"\n\n# This directory is supposed to be created in $HOME and owned by user(https://github.com/actions/runner-images/issues/491)\nmkdir -p /etc/skel/.config/configstore\nset_etc_environment_variable \"XDG_CONFIG_HOME\" '$HOME/.config'\n\n# Change waagent entries to use /mnt for swap file\nsed -i 's/ResourceDisk.Format=n/ResourceDisk.Format=y/g' /etc/waagent.conf\nsed -i 's/ResourceDisk.EnableSwap=n/ResourceDisk.EnableSwap=y/g' /etc/waagent.conf\nsed -i 's/ResourceDisk.SwapSizeMB=0/ResourceDisk.SwapSizeMB=4096/g' /etc/waagent.conf\n\n# Add localhost alias to ::1 IPv6\nsed -i 's/::1 ip6-localhost ip6-loopback/::1     localhost ip6-localhost ip6-loopback/g' /etc/hosts\n\n# Prepare directory and env variable for toolcache\nAGENT_TOOLSDIRECTORY=/opt/hostedtoolcache\nmkdir $AGENT_TOOLSDIRECTORY\nset_etc_environment_variable \"AGENT_TOOLSDIRECTORY\" \"${AGENT_TOOLSDIRECTORY}\"\nset_etc_environment_variable \"RUNNER_TOOL_CACHE\" \"${AGENT_TOOLSDIRECTORY}\"\nchmod -R 777 $AGENT_TOOLSDIRECTORY\n\n# https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html\n# https://www.suse.com/support/kb/doc/?id=000016692\necho 'vm.max_map_count=262144' | tee -a /etc/sysctl.conf\n\n# https://kind.sigs.k8s.io/docs/user/known-issues/#pod-errors-due-to-too-many-open-files\necho 'fs.inotify.max_user_watches=655360' | tee -a /etc/sysctl.conf\necho 'fs.inotify.max_user_instances=1280' | tee -a /etc/sysctl.conf\n\n# https://github.com/actions/runner-images/issues/9491\necho 'vm.mmap_rnd_bits=28' | tee -a /etc/sysctl.conf\n\n# https://github.com/actions/runner-images/pull/7860\nnetfilter_rule='/etc/udev/rules.d/50-netfilter.rules'\nrules_directory=\"$(dirname \"${netfilter_rule}\")\"\nmkdir -p $rules_directory\ntouch $netfilter_rule\necho 'ACTION==\"add\", SUBSYSTEM==\"module\", KERNEL==\"nf_conntrack\", RUN+=\"/usr/sbin/sysctl net.netfilter.nf_conntrack_tcp_be_liberal=1\"' | tee -a $netfilter_rule\n\n# Create symlink for tests running\nchmod +x $HELPER_SCRIPTS/invoke-tests.sh\nln -s $HELPER_SCRIPTS/invoke-tests.sh /usr/local/bin/invoke_tests\n\n# Disable motd updates metadata\nsed -i 's/ENABLED=1/ENABLED=0/g' /etc/default/motd-news\n\n# Remove fwupd if installed. We're running on VMs in Azure and the fwupd package is not needed.\n# Leaving it enable means periodic refreshes show in network traffic and firewall logs\n# Check if fwupd-refresh.timer exists in systemd\nif systemctl list-unit-files fwupd-refresh.timer &>/dev/null; then\n    echo \"Masking fwupd-refresh.timer...\"\n    systemctl mask fwupd-refresh.timer\nfi\n\n# This is a legacy check, leaving for earlier versions of Ubuntu\n# If fwupd config still exists, disable the motd updates\nif [[ -f \"/etc/fwupd/daemon.conf\" ]]; then\n    sed -i 's/UpdateMotd=true/UpdateMotd=false/g' /etc/fwupd/daemon.conf\nfi\n\n# Disable to load providers\n# https://github.com/microsoft/azure-pipelines-agent/issues/3834\nif is_ubuntu22; then\n    sed -i 's/openssl_conf = openssl_init/#openssl_conf = openssl_init/g' /etc/ssl/openssl.cnf\nfi\n\n# Disable man-db auto update\necho \"set man-db/auto-update false\" | debconf-communicate\ndpkg-reconfigure man-db\n"
  },
  {
    "path": "images/ubuntu/scripts/build/configure-image-data.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-image-data.sh\n##  Desc:  Create a file with image data and documentation links\n################################################################################\n\nimagedata_file=$IMAGEDATA_FILE\nimage_version=$IMAGE_VERSION\nimage_version_major=${image_version/.*/}\nimage_version_minor=$(echo $image_version | cut -d \".\" -f 2)\nos_name=$(lsb_release -ds | sed \"s/ /\\\\\\n/g\")\nos_version=$(lsb_release -rs)\nimage_label=\"ubuntu-${os_version}\"\nversion_major=${os_version/.*/}\nversion_wo_dot=${os_version/./}\ngithub_url=\"https://github.com/actions/runner-images/blob\"\n\nsoftware_url=\"${github_url}/ubuntu${version_major}/${image_version_major}.${image_version_minor}/images/ubuntu/Ubuntu${version_wo_dot}-Readme.md\"\nreleaseUrl=\"https://github.com/actions/runner-images/releases/tag/ubuntu${version_major}%2F${image_version_major}.${image_version_minor}\"\n\ncat <<EOF > $imagedata_file\n[\n  {\n    \"group\": \"Operating System\",\n    \"detail\": \"${os_name}\"\n  },\n  {\n    \"group\": \"Runner Image\",\n    \"detail\": \"Image: ${image_label}\\nVersion: ${image_version}\\nIncluded Software: ${software_url}\\nImage Release: ${releaseUrl}\"\n  }\n]\nEOF\n"
  },
  {
    "path": "images/ubuntu/scripts/build/configure-limits.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-limits.sh\n##  Desc:  Configure limits\n################################################################################\n\necho 'session required pam_limits.so' >> /etc/pam.d/common-session\necho 'session required pam_limits.so' >> /etc/pam.d/common-session-noninteractive\necho 'DefaultLimitNOFILE=65536' >> /etc/systemd/system.conf\necho 'DefaultLimitSTACK=16M:infinity' >> /etc/systemd/system.conf\n\n# Raise Number of File Descriptors\necho '* soft nofile 65536' >> /etc/security/limits.conf\necho '* hard nofile 65536' >> /etc/security/limits.conf\n\n# Double stack size from default 8192KB\necho '* soft stack 16384' >> /etc/security/limits.conf\necho '* hard stack 16384' >> /etc/security/limits.conf\n"
  },
  {
    "path": "images/ubuntu/scripts/build/configure-snap.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-snap.sh\n##  Desc:  Configure snap\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# Update /etc/environment to include /snap/bin in PATH\n# because /etc/profile.d is ignored by `--norc` shell launch option\n\nprepend_etc_environment_path \"/snap/bin\"\n\n# Put snapd auto refresh on hold\n# as it may generate too much traffic on Canonical's snap server\n# when they are rolling a new major update out.\n# Hold is calculated as today's date + 60 days\n\n# snapd is started automatically, but during image generation\n# a unix socket may die, restart snapd.service (and therefore snapd.socket)\n# to make sure the socket is alive.\n\nsystemctl restart snapd.socket\nsystemctl restart snapd\nsnap set system refresh.hold=\"$(date --date='today+60 days' +%Y-%m-%dT%H:%M:%S%:z)\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/configure-system.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File: configure-system.sh\n##  Desc: Post deployment system configuration actions\n################################################################################\n\nsource $HELPER_SCRIPT_FOLDER/etc-environment.sh\nsource $HELPER_SCRIPT_FOLDER/os.sh\n\nmv -f /imagegeneration/post-generation /opt\n\necho \"chmod -R 777 /usr/share\"\nchmod -R 777 /usr/share\necho \"chmod -R 777 /opt\"\nchmod -R 777 /opt\necho \"Setting sticky bit on hostedtoolcache Ruby directories due to the changes in Ruby 4.0; see issue: https://github.com/actions/runner-images/issues/13647\"\nfind /opt/hostedtoolcache/Ruby -type d -exec chmod +t {} +\n\nchmod 755 $IMAGE_FOLDER\n\n# Remove quotes around PATH\nENVPATH=$(grep 'PATH=' /etc/environment | head -n 1 | sed -z 's/^PATH=*//')\nENVPATH=${ENVPATH#\"\\\"\"}\nENVPATH=${ENVPATH%\"\\\"\"}\nreplace_etc_environment_variable \"PATH\" \"${ENVPATH}\"\necho \"Updated /etc/environment: $(cat /etc/environment)\"\n\n# Clean yarn and npm cache\nif yarn --version > /dev/null; then\n    yarn cache clean\nfi\n\nif npm --version; then\n    npm cache clean --force\nfi\n\nif is_ubuntu24; then\n# Prevent needrestart from restarting the provisioner service.\n# Currently only happens on Ubuntu 24.04, so make it conditional for the time being\n# as configuration is too different between Ubuntu versions.\n    sed -i '/^\\s*};/i \\    qr(^runner-provisioner) => 0,' /etc/needrestart/needrestart.conf\nfi\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-actions-cache.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:       install-actions-cache.sh\n##  Desc:       Download latest release from https://github.com/actions/action-versions\n##  Maintainer: #actions-runtime and @TingluoHuang\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# Prepare directory and env variable for ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE\nACTION_ARCHIVE_CACHE_DIR=/opt/actionarchivecache\nmkdir -p $ACTION_ARCHIVE_CACHE_DIR\nchmod -R 777 $ACTION_ARCHIVE_CACHE_DIR\necho \"Setting up ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE variable to ${ACTION_ARCHIVE_CACHE_DIR}\"\nset_etc_environment_variable \"ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE\" \"${ACTION_ARCHIVE_CACHE_DIR}\"\n\n# Download latest release from github.com/actions/action-versions and untar to /opt/actionarchivecache\ndownload_url=$(resolve_github_release_asset_url \"actions/action-versions\" \"endswith(\\\"action-versions.tar.gz\\\")\" \"latest\")\narchive_path=$(download_with_retry \"$download_url\")\ntar -xzf \"$archive_path\" -C $ACTION_ARCHIVE_CACHE_DIR\n\ninvoke_tests \"ActionArchiveCache\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-aliyun-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-aliyun-cli.sh\n##  Desc:  Install Alibaba Cloud CLI\n##  Supply chain security: Alibaba Cloud CLI - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/os.sh\nsource $HELPER_SCRIPTS/install.sh\n\n# Install Alibaba Cloud CLI\n\ndownload_url=$(resolve_github_release_asset_url \"aliyun/aliyun-cli\" \"contains(\\\"aliyun-cli-linux\\\") and endswith(\\\"amd64.tgz\\\")\" \"latest\")\nhash_url=\"https://github.com/aliyun/aliyun-cli/releases/latest/download/SHASUMS256.txt\"\n\narchive_path=$(download_with_retry \"$download_url\")\n\n# Supply chain security - Alibaba Cloud CLI\nexternal_hash=$(get_checksum_from_url \"$hash_url\" \"aliyun-cli-linux.*amd64.tgz\" \"SHA256\")\n\nuse_checksum_comparison \"$archive_path\" \"$external_hash\"\n\ntar xzf \"$archive_path\"\nmv aliyun /usr/local/bin\n\ninvoke_tests \"CLI.Tools\" \"Aliyun CLI\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-android-sdk.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-android-sdk.sh\n##  Desc:  Install Android SDK and tools\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/os.sh\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\nadd_filtered_installation_components() {\n    local minimum_version=$1\n    shift\n    local tools_array=(\"$@\")\n\n    for item in ${tools_array[@]}; do\n        # Take the last version number that appears after the last '-' or ';'\n        item_version=$(echo \"$item\" | grep -oE '[-;][0-9.]+' | grep -oE '[0-9.]+')\n\n        # Semver 'comparison'. Add item to components array, if item's version is greater than or equal to minimum version\n        if [[ \"$(printf \"${minimum_version}\\n${item_version}\\n\" | sort -V | head -n1)\" == \"$minimum_version\" ]]; then\n            components+=($item)\n        fi\n    done\n}\n\nget_full_ndk_version() {\n    local major_version=$1\n\n    ndk_version=$($SDKMANAGER --list | grep \"ndk;${major_version}\\.\" | awk '{gsub(\"ndk;\", \"\"); print $1}' | sort -V | tail -n1)\n    echo \"$ndk_version\"\n}\n\n# Set env variable for SDK Root (https://developer.android.com/studio/command-line/variables)\nANDROID_ROOT=/usr/local/lib/android\nANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk\nSDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager\nset_etc_environment_variable \"ANDROID_SDK_ROOT\" \"${ANDROID_SDK_ROOT}\"\n\n# ANDROID_HOME is deprecated, but older versions of Gradle rely on it\nset_etc_environment_variable \"ANDROID_HOME\" \"${ANDROID_SDK_ROOT}\"\n\n# Create android sdk directory\nmkdir -p ${ANDROID_SDK_ROOT}\n\n# Download the latest command line tools so that we can accept all of the licenses.\n# See https://developer.android.com/studio/#command-tools\ncmdline_tools_package=$(get_toolset_value '.android.\"cmdline-tools\"')\nif [[ $cmdline_tools_version == \"latest\" ]]; then\n    REPOSITORY_XML_URL=\"https://dl.google.com/android/repository/repository2-1.xml\"\n    repository_xml_file=$(download_with_retry \"$REPOSITORY_XML_URL\")\n    cmdline_tools_package=$(\n        yq -p=xml \\\n        '.sdk-repository.remotePackage[] | select(.\"+@path\" == \"cmdline-tools;latest\" and .channelRef.\"+@ref\" == \"channel-0\").archives.archive[].complete.url | select(contains(\"commandlinetools-linux\"))' \\\n        \"${repository_xml_file}\"\n    )\n\n    if [[ -z $cmdline_tools_package ]]; then\n        echo \"Failed to parse latest command-line tools version\"\n        exit 1\n    fi\nfi\n\n# Install command line tools\narchive_path=$(download_with_retry \"https://dl.google.com/android/repository/${cmdline_tools_package}\")\nunzip -qq \"$archive_path\" -d ${ANDROID_SDK_ROOT}/cmdline-tools\n# Command line tools need to be placed in ${ANDROID_SDK_ROOT}/sdk/cmdline-tools/latest to determine SDK root\nmv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/latest\n\n# Check sdk manager installation\nif ${SDKMANAGER} --list 1>/dev/null; then\n    echo \"Android SDK manager was installed\"\nelse\n    echo \"Android SDK manager was not installed\"\n    exit 1\nfi\n\n# Get toolset values and prepare environment variables\nminimum_build_tool_version=$(get_toolset_value '.android.build_tools_min_version')\nminimum_platform_version=$(get_toolset_value '.android.platform_min_version')\nandroid_ndk_major_default=$(get_toolset_value '.android.ndk.default')\nandroid_ndk_major_versions=($(get_toolset_value '.android.ndk.versions[]'))\nandroid_ndk_major_latest=(${android_ndk_major_versions[-1]})\n\nndk_default_full_version=$(get_full_ndk_version $android_ndk_major_default)\nndk_latest_full_version=$(get_full_ndk_version $android_ndk_major_latest)\nANDROID_NDK=${ANDROID_SDK_ROOT}/ndk/${ndk_default_full_version}\n# 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\nset_etc_environment_variable \"ANDROID_NDK\" \"${ANDROID_NDK}\"\nset_etc_environment_variable \"ANDROID_NDK_HOME\" \"${ANDROID_NDK}\"\nset_etc_environment_variable \"ANDROID_NDK_ROOT\" \"${ANDROID_NDK}\"\nset_etc_environment_variable \"ANDROID_NDK_LATEST_HOME\" \"${ANDROID_SDK_ROOT}/ndk/${ndk_latest_full_version}\"\n\n# Prepare components for installation\nextras=$(get_toolset_value '.android.extra_list[] | \"extras;\" + .')\naddons=$(get_toolset_value '.android.addon_list[] | \"add-ons;\" + .')\nadditional=$(get_toolset_value '.android.additional_tools[]')\ncomponents=(\"${extras[@]}\" \"${addons[@]}\" \"${additional[@]}\")\n\nfor ndk_major_version in \"${android_ndk_major_versions[@]}\"; do\n    ndk_full_version=$(get_full_ndk_version $ndk_major_version)\n    components+=(\"ndk;$ndk_full_version\")\ndone\n\navailable_platforms=($($SDKMANAGER --list | sed -n '/Available Packages:/,/^$/p' | grep \"platforms;android-[0-9]\" | cut -d\"|\" -f 1))\nall_build_tools=($($SDKMANAGER --list | grep \"build-tools;\" | cut -d\"|\" -f 1 | sort -u))\navailable_build_tools=$(echo ${all_build_tools[@]//*rc[0-9]/})\n\nadd_filtered_installation_components $minimum_platform_version \"${available_platforms[@]}\"\nadd_filtered_installation_components $minimum_build_tool_version \"${available_build_tools[@]}\"\n\n# Add platform tools to the list of components to install\ncomponents+=(\"platform-tools\")\n\n# Install components\necho \"y\" | $SDKMANAGER ${components[@]}\n\n# Add required permissions\nchmod -R a+rwx ${ANDROID_SDK_ROOT}\n\nreload_etc_environment\n\ninvoke_tests \"Android\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-apache.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-apache.sh\n##  Desc:  Install Apache HTTP Server\n################################################################################\n\n# Install Apache\napt-get install apache2\n\n# Disable apache2.service\nsystemctl is-active --quiet apache2.service && systemctl stop apache2.service\nsystemctl disable apache2.service\n\ninvoke_tests \"WebServers\" \"Apache\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-apt-common.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-apt-common.sh\n##  Desc:  Install basic command line utilities and dev packages\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\ncommon_packages=$(get_toolset_value .apt.common_packages[])\ncmd_packages=$(get_toolset_value .apt.cmd_packages[])\n\nfor package in $common_packages $cmd_packages; do\n    echo \"Install $package\"\n    apt-get install --no-install-recommends $package\ndone\n\ninvoke_tests \"Apt\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-apt-vital.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-apt-vital.sh\n##  Desc:  Install vital command line utilities\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nvital_packages=$(get_toolset_value .apt.vital_packages[])\napt-get install --no-install-recommends $vital_packages\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-aws-tools.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-aws-tools.sh\n##  Desc:  Install the AWS CLI, Session Manager plugin for the AWS CLI, and AWS SAM CLI\n##  Supply chain security: AWS SAM CLI - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/os.sh\nsource $HELPER_SCRIPTS/install.sh\n\nawscliv2_archive_path=$(download_with_retry \"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip\")\nunzip -qq \"$awscliv2_archive_path\" -d /tmp\n/tmp/aws/install -i /usr/local/aws-cli -b /usr/local/bin\n\nsmplugin_deb_path=$(download_with_retry \"https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb\")\napt-get install \"$smplugin_deb_path\"\n\n# Download the latest aws sam cli release\naws_sam_cli_archive_name=\"aws-sam-cli-linux-x86_64.zip\"\nsam_cli_download_url=$(resolve_github_release_asset_url \"aws/aws-sam-cli\" \"endswith(\\\"$aws_sam_cli_archive_name\\\")\" \"latest\")\naws_sam_cli_archive_path=$(download_with_retry \"$sam_cli_download_url\")\n\n# Supply chain security - AWS SAM CLI\naws_sam_cli_hash=$(get_checksum_from_github_release \"aws/aws-sam-cli\" \"${aws_sam_cli_archive_name}.. \" \"latest\" \"SHA256\")\nuse_checksum_comparison \"$aws_sam_cli_archive_path\" \"$aws_sam_cli_hash\"\n\n# Install the latest aws sam cli release\nunzip \"$aws_sam_cli_archive_path\" -d /tmp\n/tmp/install\n\ninvoke_tests \"CLI.Tools\" \"AWS\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-azcopy.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-azcopy.sh\n##  Desc:  Install AzCopy\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Install AzCopy10\narchive_path=$(download_with_retry \"https://aka.ms/downloadazcopy-v10-linux\")\ntar xzf \"$archive_path\" --strip-components=1 -C /tmp\ninstall /tmp/azcopy /usr/local/bin/azcopy\n\n# Create azcopy 10 alias for backward compatibility\nln -sf /usr/local/bin/azcopy /usr/local/bin/azcopy10\n\ninvoke_tests \"Tools\" \"azcopy\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-azure-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-azure-cli.sh\n##  Desc:  Install Azure CLI (az)\n################################################################################\n\n# Install Azure CLI (instructions taken from https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)\ncurl -fsSL https://aka.ms/InstallAzureCLIDeb | sudo bash\n\necho \"azure-cli https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-linux?pivots=apt\" >> $HELPER_SCRIPTS/apt-sources.txt\n\nrm -f /etc/apt/sources.list.d/azure-cli.list\nrm -f /etc/apt/sources.list.d/azure-cli.list.save\n\ninvoke_tests \"CLI.Tools\" \"Azure CLI\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-azure-devops-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-azure-devops-cli.sh\n##  Desc:  Install Azure DevOps CLI (az devops)\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# AZURE_EXTENSION_DIR shell variable defines where modules are installed\n# https://docs.microsoft.com/en-us/cli/azure/azure-cli-extensions-overview\nexport AZURE_EXTENSION_DIR=/opt/az/azcliextensions\nset_etc_environment_variable \"AZURE_EXTENSION_DIR\" \"${AZURE_EXTENSION_DIR}\"\n\n# install azure devops Cli extension\naz extension add -n azure-devops\n\ninvoke_tests \"CLI.Tools\" \"Azure DevOps CLI\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-bazel.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-bazel.sh\n##  Desc:  Install Bazel and Bazelisk (A user-friendly launcher for Bazel)\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# Install bazelisk\nnpm install -g @bazel/bazelisk\n\n# Run bazelisk once in order to install /usr/local/bin/bazel binary\nbazel version\n\n# Get the installed Bazel version from bazelisk\nBAZEL_VERSION=$(bazel --version | grep \"Build label:\" | awk '{print $3}')\n\n# Set USE_BAZEL_FALLBACK_VERSION so that users without .bazelversion\n# get the preinstalled version instead of downloading latest\nset_etc_environment_variable \"USE_BAZEL_FALLBACK_VERSION\" \"silent:${BAZEL_VERSION}\"\n\ninvoke_tests \"Tools\" \"Bazel\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-bicep.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-bicep.sh\n##  Desc:  Install bicep cli\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Install Bicep CLI\ndownload_url=$(resolve_github_release_asset_url \"Azure/bicep\" \"endswith(\\\"bicep-linux-x64\\\")\" \"latest\")\nbicep_binary_path=$(download_with_retry \"${download_url}\")\n\n# Mark it as executable\ninstall \"$bicep_binary_path\" /usr/local/bin/bicep\n\ninvoke_tests \"Tools\" \"Bicep\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-clang.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-clang.sh\n##  Desc:  Install Clang compiler\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\ninstall_clang() {\n    local version=$1\n\n    echo \"Installing clang-$version...\"\n    apt-get install \"clang-$version\" \"lldb-$version\" \"lld-$version\" \"clang-format-$version\" \"clang-tidy-$version\"\n}\n\nset_default_clang() {\n    local version=$1\n\n    echo \"Make Clang ${version} default\"\n    update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${version} 100\n    update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${version} 100\n    update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-${version} 100\n    update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-${version} 100\n    update-alternatives --install /usr/bin/run-clang-tidy run-clang-tidy /usr/bin/run-clang-tidy-${version} 100\n}\n\nversions=$(get_toolset_value '.clang.versions[]')\ndefault_clang_version=$(get_toolset_value '.clang.default_version')\n\nfor version in ${versions[*]}; do\n    if [[ $version != $default_clang_version ]]; then\n        install_clang $version\n    fi\ndone\n\ninstall_clang $default_clang_version\nset_default_clang $default_clang_version\n\ninvoke_tests \"Tools\" \"clang\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-cmake.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-cmake.sh\n##  Desc:  Install CMake\n##  Supply chain security: CMake - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Test to see if the software in question is already installed, if not install it\necho \"Checking to see if the installer script has already been run\"\nif command -v cmake; then\n    echo \"cmake is already installed\"\nelse\n\t# Download script to install CMake\n\tdownload_url=$(resolve_github_release_asset_url \"Kitware/CMake\" \"endswith(\\\"inux-x86_64.sh\\\")\" \"3.31.6\")\n\tcurl -fsSL \"${download_url}\" -o cmakeinstall.sh\n\n\t# Supply chain security - CMake\n\thash_url=$(resolve_github_release_asset_url \"Kitware/CMake\" \"endswith(\\\"SHA-256.txt\\\")\" \"3.31.6\")\n\texternal_hash=$(get_checksum_from_url \"$hash_url\" \"linux-x86_64.sh\" \"SHA256\")\n\tuse_checksum_comparison \"cmakeinstall.sh\" \"$external_hash\"\n\n\t# Install CMake and remove the install script\n\tchmod +x cmakeinstall.sh \\\n\t&& ./cmakeinstall.sh --prefix=/usr/local --exclude-subdir \\\n\t&& rm cmakeinstall.sh\nfi\n\ninvoke_tests \"Tools\" \"Cmake\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-codeql-bundle.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-codeql-bundle.sh\n##  Desc:  Install CodeQL CLI Bundle to the toolcache.\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Retrieve the latest major version of the CodeQL Action to use in the base URL for downloading the bundle.\nreleases=$(curl -s \"https://api.github.com/repos/github/codeql-action/releases\")\n\n# Get the release tags starting with v[0-9] and sort them in descending order, then parse the first one to get the major version.\ncodeql_action_latest_major_version=$(echo \"$releases\" |\n    jq -r '.[].tag_name' |\n    grep -E '^v[0-9]' |\n    sort -nr |\n    head -n 1 |\n    sed -E 's/^v([0-9]+).*/\\1/')\nif [ -z \"$codeql_action_latest_major_version\" ]; then\n  echo \"Error: Unable to find the latest major version of the CodeQL Action.\"\n  exit 1\nfi\n\n# Retrieve the CLI version of the latest CodeQL bundle.\nbase_url=\"$(curl -fsSL https://raw.githubusercontent.com/github/codeql-action/v\"$codeql_action_latest_major_version\"/src/defaults.json)\"\nbundle_version=\"$(echo \"$base_url\" | jq -r '.cliVersion')\"\nbundle_tag_name=\"codeql-bundle-v$bundle_version\"\n\necho \"Downloading CodeQL bundle $bundle_version...\"\n# Note that this is the all-platforms CodeQL bundle, to support scenarios where customers run\n# different operating systems within containers.\ncodeql_archive=$(download_with_retry \"https://github.com/github/codeql-action/releases/download/$bundle_tag_name/codeql-bundle-linux64.tar.gz\")\n\ncodeql_toolcache_path=\"$AGENT_TOOLSDIRECTORY/CodeQL/$bundle_version/x64\"\nmkdir -p \"$codeql_toolcache_path\"\n\necho \"Unpacking the downloaded CodeQL bundle archive...\"\ntar -xzf \"$codeql_archive\" -C \"$codeql_toolcache_path\"\n\n# Touch a file to indicate to the CodeQL Action that this bundle shipped with the toolcache. This is\n# to support overriding the CodeQL version specified in defaults.json on GitHub Enterprise.\ntouch \"$codeql_toolcache_path/pinned-version\"\n\n# Touch a file to indicate to the toolcache that setting up CodeQL is complete.\ntouch \"$codeql_toolcache_path.complete\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-container-tools.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-container-tools.sh\n##  Desc:  Install container tools: podman, buildah and skopeo onto the image\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/os.sh\n\n#\n# pin podman due to https://github.com/actions/runner-images/issues/7753\n#                   https://bugs.launchpad.net/ubuntu/+source/libpod/+bug/2024394\n#\nif ! is_ubuntu22; then\n    install_packages=(podman buildah skopeo)\nelse\n    install_packages=(podman=3.4.4+ds1-1ubuntu1 buildah skopeo)\nfi\n\n\nif is_ubuntu22; then\n    # Install containernetworking-plugins for Ubuntu 22\n    curl -O http://archive.ubuntu.com/ubuntu/pool/universe/g/golang-github-containernetworking-plugins/containernetworking-plugins_1.1.1+ds1-3build1_amd64.deb\n    dpkg -i containernetworking-plugins_1.1.1+ds1-3build1_amd64.deb\nfi\n\n# Install podman, buildah, skopeo container's tools\napt-get update\napt-get install ${install_packages[@]}\nmkdir -p /etc/containers\nprintf \"[registries.search]\\nregistries = ['docker.io', 'quay.io']\\n\" | tee /etc/containers/registries.conf\n\ninvoke_tests \"Tools\" \"Containers\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-docker.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-docker.sh\n##  Desc:  Install docker onto the image\n##  Supply chain security: amazon-ecr-credential-helper - dynamic checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/os.sh\n\nREPO_URL=\"https://download.docker.com/linux/ubuntu\"\nGPG_KEY=\"/usr/share/keyrings/docker.gpg\"\nREPO_PATH=\"/etc/apt/sources.list.d/docker.list\"\nos_codename=$(lsb_release -cs)\n\ncurl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o $GPG_KEY\necho \"deb [arch=amd64 signed-by=$GPG_KEY] $REPO_URL ${os_codename} stable\" > $REPO_PATH\napt-get update\n\n# Install docker components which available via apt-get\n# Using toolsets keep installation order to install dependencies before the package in order to control versions\n\ncomponents=$(get_toolset_value '.docker.components[] .package')\nfor package in $components; do\n    version=$(get_toolset_value \".docker.components[] | select(.package == \\\"$package\\\") | .version\")\n    if [[ $version == \"latest\" ]]; then\n        apt-get install --no-install-recommends \"$package\"\n    else\n        version_string=$(apt-cache madison \"$package\" | awk '{ print $3 }' | grep \"$version\" | grep \"$os_codename\" | head -1)\n        apt-get install --no-install-recommends \"${package}=${version_string}\"\n    fi\ndone\n\n# Install plugins that are best installed from the GitHub repository\n# Be aware that `url` built from github repo name and plugin name because of current repo naming for those plugins\n\nplugins=$(get_toolset_value '.docker.plugins[] .plugin')\nfor plugin in $plugins; do\n    version=$(get_toolset_value \".docker.plugins[] | select(.plugin == \\\"$plugin\\\") | .version\")\n    filter=$(get_toolset_value \".docker.plugins[] | select(.plugin == \\\"$plugin\\\") | .asset\")\n    url=$(resolve_github_release_asset_url \"docker/$plugin\" \"endswith(\\\"$filter\\\")\" \"$version\")\n    binary_path=$(download_with_retry \"$url\" \"/tmp/docker-$plugin\")\n    mkdir -pv \"/usr/libexec/docker/cli-plugins\"\n    install \"$binary_path\" \"/usr/libexec/docker/cli-plugins/docker-$plugin\"\ndone\n\n# docker from official repo introduced different GID generation: https://github.com/actions/runner-images/issues/8157\ngid=$(cut -d \":\" -f 3 /etc/group | grep \"^1..$\" | sort -n | tail -n 1 | awk '{ print $1+1 }')\ngroupmod -g \"$gid\" docker\n\n# Create systemd-tmpfiles configuration for Docker\ncat <<EOF | sudo tee /etc/tmpfiles.d/docker.conf\nL /run/docker.sock - - - - root docker 0770\nEOF\n\n# Reload systemd-tmpfiles to apply the new configuration\nsystemd-tmpfiles --create /etc/tmpfiles.d/docker.conf\n\n# Enable docker.service\nsystemctl is-active --quiet docker.service || systemctl start docker.service\nsystemctl is-enabled --quiet docker.service || systemctl enable docker.service\n\n# Docker daemon takes time to come up after installing\nsleep 10\ndocker info\n\nif ! is_ubuntu22; then\n    # Pull Dependabot docker image\n    docker pull ghcr.io/dependabot/dependabot-updater-core:latest\n\n    # Pull AW docker images\n    docker pull ghcr.io/github/gh-aw-mcpg:latest\n    docker pull ghcr.io/github/gh-aw-firewall/agent:latest\n    docker pull ghcr.io/github/gh-aw-firewall/api-proxy:latest\n    docker pull ghcr.io/github/gh-aw-firewall/squid:latest\n    docker pull ghcr.io/github/github-mcp-server:latest\nfi\n\n# Download amazon-ecr-credential-helper\naws_latest_release_url=\"https://api.github.com/repos/awslabs/amazon-ecr-credential-helper/releases/latest\"\naws_helper_url=$(curl -fsSL \"${aws_latest_release_url}\" | jq -r '.body' | awk -F'[()]' '/linux-amd64/ {print $2}')\naws_helper_binary_path=$(download_with_retry \"$aws_helper_url\")\n\n# Supply chain security - amazon-ecr-credential-helper\naws_helper_external_hash=$(get_checksum_from_url \"${aws_helper_url}.sha256\" \"docker-credential-ecr-login\" \"SHA256\")\nuse_checksum_comparison \"$aws_helper_binary_path\" \"$aws_helper_external_hash\"\n\n# Install amazon-ecr-credential-helper\ninstall \"$aws_helper_binary_path\" \"/usr/bin/docker-credential-ecr-login\"\n\n# Cleanup custom repositories\nrm $GPG_KEY\nrm $REPO_PATH\n\ninvoke_tests \"Tools\" \"Docker\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-dotnetcore-sdk.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-dotnetcore-sdk.sh\n##  Desc:  Install .NET Core SDK\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/os.sh\n\ndotnet_versions=$(get_toolset_value '.dotnet.versions[]')\ndotnet_tools=$(get_toolset_value '.dotnet.tools[].name')\n\n# Disable telemetry\nexport DOTNET_CLI_TELEMETRY_OPTOUT=1\n\n# Install dotnet dependencies\n# https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu-decision#dependencies\napt-get update\napt-get install --no-install-recommends \\\n    ca-certificates \\\n    libc6 \\\n    libgcc-s1 \\\n    libgssapi-krb5-2 \\\n    liblttng-ust1 \\\n    libssl3 \\\n    libstdc++6 \\\n    zlib1g\n\nif is_ubuntu22; then\n    apt-get install --no-install-recommends libicu70\nfi\n\nif is_ubuntu24; then\n    apt-get install --no-install-recommends libicu74\nfi\n\n# Install .NET SDKs and Runtimes\nmkdir -p /usr/share/dotnet\n\nsdks=()\nfor version in ${dotnet_versions[@]}; do\n    release_url=\"https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/${version}/releases.json\"\n    releases=$(cat \"$(download_with_retry \"$release_url\")\")\n    sdks=(\"${sdks[@]}\" $(echo \"${releases}\" | jq -r '.releases[].sdk.version | select(contains(\"preview\") or contains(\"rc\") | not)'))\n    sdks=(\"${sdks[@]}\" $(echo \"${releases}\" | jq -r '.releases[].sdks[]?.version | select(contains(\"preview\") or contains(\"rc\") | not)'))\ndone\n\nsorted_sdks=$(echo ${sdks[@]} | tr ' ' '\\n' | sort -rV | awk -F'.' '!seen[$1\".\"$2\".\"int($3/100)]++' )\n\n# Issue https://github.com/actions/runner-images/issues/13705\n# Workaround for broken .NET SDK 10.0.103 - replace it with .NET SDK 10.0.102\nsorted_sdks=$(echo \"${sorted_sdks}\" | sed 's/^10\\.0\\.103$/10.0.102/')\n\n## Download installer from dot.net\nDOTNET_INSTALL_SCRIPT=\"https://dot.net/v1/dotnet-install.sh\"\ninstall_script_path=$(download_with_retry $DOTNET_INSTALL_SCRIPT)\nchmod +x $install_script_path\n\nfor sdk in ${sorted_sdks[@]}; do\n    echo \"Installing .NET SDK $sdk\"\n    $install_script_path --version $sdk --install-dir /usr/share/dotnet --no-path\ndone\n\n## Dotnet installer doesn't create symlinks to executable or modify PATH\nln -s /usr/share/dotnet/dotnet /usr/bin/dotnet\n\nset_etc_environment_variable DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1\nset_etc_environment_variable DOTNET_NOLOGO 1\nset_etc_environment_variable DOTNET_MULTILEVEL_LOOKUP 0\nprepend_etc_environment_path '$HOME/.dotnet/tools'\n\n# Install .Net tools\nfor dotnet_tool in ${dotnet_tools[@]}; do\n    echo \"Installing dotnet tool $dotnet_tool\"\n    dotnet tool install $dotnet_tool --tool-path '/etc/skel/.dotnet/tools'\ndone\n\ninvoke_tests \"DotnetSDK\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-firefox.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-firefox.sh\n##  Desc:  Install Firefox\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# Mozillateam PPA is added manually because sometimes\n# launchpad portal sends empty answer when trying to add it automatically\n\nREPO_URL=\"https://ppa.launchpadcontent.net/mozillateam/ppa/ubuntu/\"\nGPG_FINGERPRINT=\"0ab215679c571d1c8325275b9bdb3d89ce49ec21\"\nGPG_KEY=\"/etc/apt/trusted.gpg.d/mozillateam_ubuntu_ppa.gpg\"\nREPO_PATH=\"/etc/apt/sources.list.d/mozillateam-ubuntu-ppa-focal.list\"\n\n# Install Firefox\ncurl -fsSL \"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${GPG_FINGERPRINT}\" | sudo gpg --dearmor -o $GPG_KEY\necho \"deb $REPO_URL $(lsb_release -cs) main\" > $REPO_PATH\n\napt-get update\napt-get install --target-release 'o=LP-PPA-mozillateam' firefox\nrm $REPO_PATH\n\n# Document apt source repo's\necho \"mozillateam $REPO_URL\" >> $HELPER_SCRIPTS/apt-sources.txt\n\n# add to global system preferences for firefox locale en_US, because other browsers have en_US local.\n# Default firefox local is en_GB\necho 'pref(\"intl.locale.requested\",\"en_US\");' >> \"/usr/lib/firefox/browser/defaults/preferences/syspref.js\"\n\n# Download and unpack latest release of geckodriver\ndownload_url=$(resolve_github_release_asset_url \"mozilla/geckodriver\" \"test(\\\"linux64.tar.gz$\\\")\" \"latest\")\ndriver_archive_path=$(download_with_retry \"$download_url\")\n\nGECKODRIVER_DIR=\"/usr/local/share/gecko_driver\"\nGECKODRIVER_BIN=\"$GECKODRIVER_DIR/geckodriver\"\n\nmkdir -p $GECKODRIVER_DIR\ntar -xzf \"$driver_archive_path\" -C $GECKODRIVER_DIR\n\nchmod +x $GECKODRIVER_BIN\nln -s \"$GECKODRIVER_BIN\" /usr/bin/\nset_etc_environment_variable \"GECKOWEBDRIVER\" \"${GECKODRIVER_DIR}\"\n\ninvoke_tests \"Browsers\" \"Firefox\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-gcc-compilers.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-gcc-compilers.sh\n##  Desc:  Install GNU C++ compilers\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nversions=$(get_toolset_value '.gcc.versions[]')\n\nfor version in ${versions[*]}; do\n    echo \"Installing $version...\"\n    apt-get install $version\ndone\n\ninvoke_tests \"Tools\" \"gcc\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-gfortran.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-gfortran.sh\n##  Desc:  Install GNU Fortran\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nversions=$(get_toolset_value '.gfortran.versions[]')\n\nfor version in ${versions[*]}; do\n    echo \"Installing $version...\"\n    apt-get install $version\ndone\n\necho \"Install versionless gfortran (latest)\"\napt-get install gfortran\n\ninvoke_tests \"Tools\" \"gfortran\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-git-lfs.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-git-lfs.sh\n##  Desc:  Install Git-lfs\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nGIT_LFS_REPO=\"https://packagecloud.io/install/repositories/github/git-lfs\"\n\n# Install git-lfs\ncurl -fsSL $GIT_LFS_REPO/script.deb.sh | bash\napt-get install git-lfs\n\n# Remove source repo's\nrm /etc/apt/sources.list.d/github_git-lfs.list\n\n# Document apt source repo's\necho \"git-lfs $GIT_LFS_REPO\" >> $HELPER_SCRIPTS/apt-sources.txt\n\ninvoke_tests \"Tools\" \"Git-lfs\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-git.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-git.sh\n##  Desc:  Install Git and Git-FTP\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nGIT_REPO=\"ppa:git-core/ppa\"\n\n## Install git\nadd-apt-repository $GIT_REPO -y\napt-get update\napt-get install git\n\n# Git version 2.35.2 introduces security fix that breaks action\\checkout https://github.com/actions/checkout/issues/760\ncat <<EOF >> /etc/gitconfig\n[safe]\n        directory = *\nEOF\n\n# Install git-ftp\napt-get install git-ftp\n\n# Remove source repo's\nadd-apt-repository --remove $GIT_REPO\n\n# Document apt source repo's\necho \"git-core $GIT_REPO\" >> $HELPER_SCRIPTS/apt-sources.txt\n\n# Add well-known SSH host keys to known_hosts\nssh-keyscan -t rsa,ecdsa,ed25519 github.com >> /etc/ssh/ssh_known_hosts\nssh-keyscan -t rsa ssh.dev.azure.com >> /etc/ssh/ssh_known_hosts\n\ninvoke_tests \"Tools\" \"Git\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-github-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-github-cli.sh\n##  Desc:  Install GitHub CLI\n##         Must be run as non-root user after homebrew\n##  Supply chain security: GitHub CLI - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Download GitHub CLI\ngh_cli_url=$(resolve_github_release_asset_url \"cli/cli\" \"contains(\\\"linux\\\") and contains(\\\"amd64\\\") and endswith(\\\".deb\\\")\" \"latest\")\ngh_cli_deb_path=$(download_with_retry \"$gh_cli_url\")\n\n# Supply chain security - GitHub CLI\nhash_url=$(resolve_github_release_asset_url \"cli/cli\" \"endswith(\\\"checksums.txt\\\")\" \"latest\")\nexternal_hash=$(get_checksum_from_url \"$hash_url\" \"linux_amd64.deb\" \"SHA256\")\nuse_checksum_comparison \"$gh_cli_deb_path\" \"$external_hash\"\n\n# Install GitHub CLI\napt-get install \"$gh_cli_deb_path\"\n\ninvoke_tests \"CLI.Tools\" \"GitHub CLI\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-google-chrome.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-google-chrome.sh\n##  Desc:  Install google-chrome, chromedriver and chromium\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\nget_chromium_revision() {\n    local chrome_revision=$1\n\n    # Take the first part of the revision variable to search not only for a specific version,\n    # but also for similar ones, so that we can get a previous one if the required revision is not found\n    chrome_revision_prefix=${chrome_revision:0:${#chrome_revision}/2+1}\n    SEARCH_URL=\"https://www.googleapis.com/storage/v1/b/chromium-browser-snapshots/o?delimiter=/&prefix=Linux_x64\"\n    # Revision can include a hash instead of a number. Need to filter it out https://github.com/actions/runner-images/issues/5256\n    revisions_available=$(curl -s $SEARCH_URL/${chrome_revision_prefix} | jq -r '.prefixes[]' | grep -E \"Linux_x64\\/[0-9]+\\/\"| cut -d \"/\" -f 2 | sort --version-sort)\n\n    # If required Chromium revision is not found in the list\n    # we should have to decrement the revision number until we find one.\n    # This is mentioned in the documentation we use for this installation:\n    # https://www.chromium.org/getting-involved/download-chromium\n    latest_valid_revision=$(echo $revisions_available | cut -f 1 -d \" \")\n    for revision in $revisions_available; do\n        if [ \"$chrome_revision\" -lt \"$revision\" ]; then\n          break\n        fi\n\n        latest_valid_revision=$revision\n    done\n\n    echo $latest_valid_revision\n}\n\n# Download and install Google Chrome\nCHROME_DEB_URL=\"https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb\"\nchrome_deb_path=$(download_with_retry \"$CHROME_DEB_URL\")\napt-get install \"$chrome_deb_path\" -f\nset_etc_environment_variable \"CHROME_BIN\" \"/usr/bin/google-chrome\"\n\n# Remove Google Chrome repo\nrm -f /etc/cron.daily/google-chrome /etc/apt/sources.list.d/google-chrome.list /etc/apt/sources.list.d/google-chrome.list.save\n\n# Parse Google Chrome version\nfull_chrome_version=$(google-chrome --product-version)\nchrome_version=${full_chrome_version%.*}\necho \"Chrome version is $full_chrome_version\"\n\n# Get chrome versions information\nCHROME_PLATFORM=\"linux64\"\nCHROME_VERSIONS_URL=\"https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build-with-downloads.json\"\nchrome_versions_json=$(curl -fsSL \"${CHROME_VERSIONS_URL}\")\n\n# Download and unpack the latest release of chromedriver\nchromedriver_version=$(echo \"${chrome_versions_json}\" | jq -r '.builds[\"'\"$chrome_version\"'\"].version')\nchromedriver_url=$(echo \"${chrome_versions_json}\" | jq -r '.builds[\"'\"$chrome_version\"'\"].downloads.chromedriver[] | select(.platform==\"'\"${CHROME_PLATFORM}\"'\").url')\nCHROMEDRIVER_DIR=\"/usr/local/share/chromedriver-linux64\"\nchromedriver_bin=\"$CHROMEDRIVER_DIR/chromedriver\"\n\necho \"Installing chromedriver version $chromedriver_version\"\ndriver_archive_path=$(download_with_retry \"$chromedriver_url\")\nunzip -qq \"$driver_archive_path\" -d /usr/local/share\n\nchmod +x $chromedriver_bin\nln -s \"$chromedriver_bin\" /usr/bin/\nset_etc_environment_variable \"CHROMEWEBDRIVER\" \"${CHROMEDRIVER_DIR}\"\n\n# Download and unpack Chromium\nchrome_revision=$(echo \"${chrome_versions_json}\" | jq -r '.builds[\"'\"$chrome_version\"'\"].revision')\nchromium_revision=$(get_chromium_revision $chrome_revision)\nchromium_url=\"https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${chromium_revision}%2Fchrome-linux.zip?alt=media\"\nCHROMIUM_DIR=\"/usr/local/share/chromium\"\nchromium_bin=\"${CHROMIUM_DIR}/chrome-linux/chrome\"\n\necho \"Installing chromium revision $chromium_revision\"\nchromium_archive_path=$(download_with_retry \"$chromium_url\")\nmkdir $CHROMIUM_DIR\nunzip -qq \"$chromium_archive_path\" -d $CHROMIUM_DIR\n\nln -s $chromium_bin /usr/bin/chromium\nln -s $chromium_bin /usr/bin/chromium-browser\n\ninvoke_tests \"Browsers\" \"Chrome\"\ninvoke_tests \"Browsers\" \"Chromium\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-google-cloud-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-google-cloud-cli.sh\n##  Desc:  Install the Google Cloud CLI\n################################################################################\n\nREPO_URL=\"https://packages.cloud.google.com/apt\"\n\n# Install the Google Cloud CLI\necho \"deb [signed-by=/usr/share/keyrings/cloud.google.gpg] $REPO_URL cloud-sdk main\" > /etc/apt/sources.list.d/google-cloud-sdk.list\nwget -qO- https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor > /usr/share/keyrings/cloud.google.gpg\napt-get update\napt-get install google-cloud-cli\n\n# remove apt\nrm /etc/apt/sources.list.d/google-cloud-sdk.list\nrm /usr/share/keyrings/cloud.google.gpg\n\n# add repo to the apt-sources.txt\necho \"google-cloud-sdk $REPO_URL\" >> $HELPER_SCRIPTS/apt-sources.txt\n\ninvoke_tests \"CLI.Tools\" \"Google Cloud CLI\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-haskell.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-haskell.sh\n##  Desc:  Install Haskell, GHCup, Cabal and Stack\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# Any nonzero value for non-interactive installation\nexport BOOTSTRAP_HASKELL_NONINTERACTIVE=1\nexport BOOTSTRAP_HASKELL_INSTALL_NO_STACK_HOOK=1\nexport GHCUP_INSTALL_BASE_PREFIX=/usr/local\nexport BOOTSTRAP_HASKELL_GHC_VERSION=0\nghcup_bin=$GHCUP_INSTALL_BASE_PREFIX/.ghcup/bin\nset_etc_environment_variable \"BOOTSTRAP_HASKELL_NONINTERACTIVE\" $BOOTSTRAP_HASKELL_NONINTERACTIVE\nset_etc_environment_variable \"GHCUP_INSTALL_BASE_PREFIX\" $GHCUP_INSTALL_BASE_PREFIX\n\n# Install GHCup\ncurl --proto '=https' --tlsv1.2 -fsSL https://get-ghcup.haskell.org | sh > /dev/null 2>&1 || true\nexport PATH=\"$ghcup_bin:$PATH\"\nprepend_etc_environment_path $ghcup_bin\n\navailable_versions=$(ghcup list -t ghc -r | grep -v \"prerelease\" | awk '{print $2}')\n\n# Install latest Haskell Major.Minor version\nmajor_minor_versions=$(echo \"$available_versions\" | cut -d\".\" -f 1,2 | uniq | tail -n1)\nfor major_minor_version in $major_minor_versions; do\n    full_version=$(echo \"$available_versions\" | grep \"$major_minor_version.\" | tail -n1)\n    echo \"install ghc version $full_version...\"\n    ghcup install ghc $full_version\n    ghcup set ghc $full_version\ndone\n\necho \"install cabal...\"\nghcup install cabal latest\n\nchmod -R 777 $GHCUP_INSTALL_BASE_PREFIX/.ghcup\nln -s $GHCUP_INSTALL_BASE_PREFIX/.ghcup /etc/skel/.ghcup\n\n# Install the latest stable release of haskell stack\ncurl -fsSL https://get.haskellstack.org/ | bash\n\ninvoke_tests \"Haskell\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-heroku.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-heroku.sh\n##  Desc:  Install Heroku CLI. Based on instructions found here: https://devcenter.heroku.com/articles/heroku-cli\n################################################################################\n\nREPO_URL=\"https://cli-assets.heroku.com/channels/stable/apt\"\nGPG_KEY=\"/usr/share/keyrings/heroku.gpg\"\nREPO_PATH=\"/etc/apt/sources.list.d/heroku.list\"\n\n# add heroku repository to apt\ncurl -fsSL \"${REPO_URL}/release.key\" | gpg --dearmor -o $GPG_KEY\necho \"deb [trusted=yes] $REPO_URL ./\" > $REPO_PATH\n\n# install heroku\napt-get update\napt-get install heroku\n\n# remove heroku's apt repository\nrm $REPO_PATH\nrm $GPG_KEY\n\ninvoke_tests \"Tools\" \"Heroku\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-homebrew.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-homebrew.sh\n##  Desc:  Install Homebrew on Linux\n##  Caveat: Brew MUST NOT be used to install any tool during the image build to avoid dependencies, which may come along with the tool\n################################################################################\n\n# Source the helpers\nsource $HELPER_SCRIPTS/etc-environment.sh\nsource $HELPER_SCRIPTS/install.sh\n\n# Install the Homebrew on Linux\n/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)\"\n\n# Invoke shellenv to make brew available during running session\neval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n\nset_etc_environment_variable HOMEBREW_NO_AUTO_UPDATE 1\nset_etc_environment_variable HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS 3650\n\n# Validate the installation ad hoc\necho \"Validate the installation reloading /etc/environment\"\nreload_etc_environment\n\ngfortran=$(brew --prefix)/bin/gfortran\n# Remove gfortran symlink, not to conflict with system gfortran\nif [[ -e $gfortran ]]; then\n    rm $gfortran\nfi\n\ninvoke_tests \"Tools\" \"Homebrew\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-java-tools.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-java-tools.sh\n##  Desc:  Install Java and related tooling (Ant, Gradle, Maven)\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\ncreate_java_environment_variable() {\n    local java_version=$1\n    local default=$2\n\n    local install_path_pattern=\"/usr/lib/jvm/temurin-${java_version}-jdk-amd64\"\n\n    if [[ ${default} == \"True\" ]]; then\n        echo \"Setting up JAVA_HOME variable to ${install_path_pattern}\"\n        set_etc_environment_variable \"JAVA_HOME\" \"${install_path_pattern}\"\n        echo \"Setting up default symlink\"\n        update-java-alternatives -s ${install_path_pattern}\n    fi\n\n    echo \"Setting up JAVA_HOME_${java_version}_X64 variable to ${install_path_pattern}\"\n    set_etc_environment_variable \"JAVA_HOME_${java_version}_X64\" \"${install_path_pattern}\"\n}\n\ninstall_open_jdk() {\n    local java_version=$1\n\n    # Install Java from PPA repositories.\n    apt-get -y install temurin-${java_version}-jdk=\\*\n    java_version_path=\"/usr/lib/jvm/temurin-${java_version}-jdk-amd64\"\n\n    java_toolcache_path=\"${AGENT_TOOLSDIRECTORY}/Java_Temurin-Hotspot_jdk\"\n\n    full_java_version=$(cat \"${java_version_path}/release\" | grep \"^SEMANTIC\" | cut -d \"=\" -f 2 | tr -d \"\\\"\" | tr \"+\" \"-\")\n\n    # If there is no semver in java release, then extract java version from -fullversion\n    [[ -z ${full_java_version} ]] && full_java_version=$(${java_version_path}/bin/java -fullversion 2>&1 | tr -d \"\\\"\" | tr \"+\" \"-\" | awk '{print $4}')\n\n    # Convert non valid semver like 11.0.14.1-9 -> 11.0.14-9\n    # https://github.com/adoptium/temurin-build/issues/2248\n    [[ ${full_java_version} =~ ^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+ ]] && full_java_version=$(echo $full_java_version | sed -E 's/\\.[0-9]+-/-/')\n\n    # When version string is too short, add extra \".0\" to make it valid semver\n    [[ ${full_java_version} =~ ^[0-9]+- ]] && full_java_version=$(echo $full_java_version | sed -E 's/-/.0-/')\n    [[ ${full_java_version} =~ ^[0-9]+\\.[0-9]+- ]] && full_java_version=$(echo $full_java_version | sed -E 's/-/.0-/')\n\n    java_toolcache_version_path=\"${java_toolcache_path}/${full_java_version}\"\n    echo \"Java ${java_version} Toolcache Version Path: ${java_toolcache_version_path}\"\n    mkdir -p \"${java_toolcache_version_path}\"\n\n    # Create a complete file\n    touch \"${java_toolcache_version_path}/x64.complete\"\n\n    # Create symlink for Java\n    ln -s ${java_version_path} \"${java_toolcache_version_path}/x64\"\n\n    # add extra permissions to be able execute command without sudo\n    chmod -R 777 /usr/lib/jvm\n}\n\n# Add Adoptium PPA\n# apt-key is deprecated, dearmor and add manually\nwget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor > /usr/share/keyrings/adoptium.gpg\necho \"deb [signed-by=/usr/share/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb/ $(lsb_release -cs) main\" > /etc/apt/sources.list.d/adoptium.list\n\n# Get all the updates from enabled repositories.\napt-get update\n\n# While Ubuntu 24.04 binaries are not released in the Adoptium repo, we will not install Java\ndefaultVersion=$(get_toolset_value '.java.default')\njdkVersionsToInstall=($(get_toolset_value \".java.versions[]\"))\n\nfor jdkVersionToInstall in ${jdkVersionsToInstall[@]}; do\n    install_open_jdk ${jdkVersionToInstall}\n\n    if [[ ${jdkVersionToInstall} == ${defaultVersion} ]]\n    then\n        create_java_environment_variable ${jdkVersionToInstall} True\n    else\n        create_java_environment_variable ${jdkVersionToInstall} False\n    fi\ndone\n\n# Install Ant\napt-get install --no-install-recommends ant ant-optional\nset_etc_environment_variable \"ANT_HOME\" \"/usr/share/ant\"\n\n# Install Maven\nmavenVersion=$(get_toolset_value '.java.maven')\nmavenDownloadUrl=\"https://dlcdn.apache.org/maven/maven-3/${mavenVersion}/binaries/apache-maven-${mavenVersion}-bin.zip\"\nmaven_archive_path=$(download_with_retry \"$mavenDownloadUrl\")\nunzip -qq -d /usr/share \"$maven_archive_path\"\nln -s /usr/share/apache-maven-${mavenVersion}/bin/mvn /usr/bin/mvn\n\n# Install Gradle\n# This script founds the latest gradle release from https://services.gradle.org/versions/all\n# The release is downloaded, extracted, a symlink is created that points to it, and GRADLE_HOME is set.\ngradleJson=$(curl -fsSL https://services.gradle.org/versions/all)\ngradleLatestVersion=$(echo ${gradleJson} | jq -r '.[] | select(.version | contains(\"-\") | not).version' | sort -V | tail -n1)\ngradleDownloadUrl=$(echo ${gradleJson} | jq -r \".[] | select(.version==\\\"$gradleLatestVersion\\\") | .downloadUrl\")\necho \"gradleUrl=${gradleDownloadUrl}\"\necho \"gradleVersion=${gradleLatestVersion}\"\ngradle_archive_path=$(download_with_retry \"$gradleDownloadUrl\")\nunzip -qq -d /usr/share \"$gradle_archive_path\"\nln -s /usr/share/gradle-\"${gradleLatestVersion}\"/bin/gradle /usr/bin/gradle\ngradle_home_dir=$(find /usr/share -depth -maxdepth 1 -name \"gradle*\")\nset_etc_environment_variable \"GRADLE_HOME\" \"${gradle_home_dir}\"\n\n# Delete java repositories and keys\nrm -f /etc/apt/sources.list.d/adoptium.list\nrm -f /etc/apt/sources.list.d/zulu.list\nrm -f /usr/share/keyrings/adoptium.gpg\nrm -f /usr/share/keyrings/zulu.gpg\n\nreload_etc_environment\ninvoke_tests \"Java\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-julia.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-julia.sh\n##  Desc:  Install Julia and add to the path\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# get the latest julia version\njson=$(curl -fsSL \"https://julialang-s3.julialang.org/bin/versions.json\")\njulia_version=$(echo $json | jq -r '.[].files[] | select(.triplet==\"x86_64-linux-gnu\" and (.version | contains(\"-\") | not)).version' | sort -V | tail -n1)\n\n# download julia archive\njulia_tar_url=$(echo $json | jq -r \".[].files[].url | select(endswith(\\\"julia-${julia_version}-linux-x86_64.tar.gz\\\"))\")\njulia_archive_path=$(download_with_retry \"$julia_tar_url\")\n\n# extract files and make symlink\njulia_installation_path=\"/usr/local/julia${julia_version}\"\nmkdir -p \"${julia_installation_path}\"\ntar -C \"${julia_installation_path}\" -xzf \"$julia_archive_path\" --strip-components=1\nln -s \"${julia_installation_path}/bin/julia\" /usr/bin/julia\n\ninvoke_tests \"Tools\" \"Julia\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-kotlin.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-kotlin.sh\n##  Desc:  Install Kotlin\n##  Supply chain security: Kotlin - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nKOTLIN_ROOT=\"/usr/share\"\ndownload_url=$(resolve_github_release_asset_url \"JetBrains/kotlin\" \"contains(\\\"kotlin-compiler\\\") and endswith(\\\".zip\\\")\" \"latest\")\narchive_path=$(download_with_retry \"$download_url\")\n\n# Supply chain security - Kotlin\nkotlin_hash_file=$(download_with_retry \"${download_url}.sha256\")\nkotlin_hash=$(cat \"$kotlin_hash_file\")\nuse_checksum_comparison \"$archive_path\" \"$kotlin_hash\"\n\nunzip -qq \"$archive_path\" -d $KOTLIN_ROOT\nrm $KOTLIN_ROOT/kotlinc/bin/*.bat\nln -sf $KOTLIN_ROOT/kotlinc/bin/* /usr/bin\n\ninvoke_tests \"Tools\" \"Kotlin\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-kubernetes-tools.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-kubernetes-tools.sh\n##  Desc:  Installs kubectl, helm, kustomize\n##  Supply chain security: KIND, minikube - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Download KIND\nkind_url=$(resolve_github_release_asset_url \"kubernetes-sigs/kind\" \"endswith(\\\"kind-linux-amd64\\\")\" \"latest\")\nkind_binary_path=$(download_with_retry \"${kind_url}\")\n\n# Supply chain security - KIND\nkind_external_hash=$(get_checksum_from_url \"${kind_url}.sha256sum\" \"kind-linux-amd64\" \"SHA256\")\nuse_checksum_comparison \"${kind_binary_path}\" \"${kind_external_hash}\"\n\n# Install KIND\ninstall \"${kind_binary_path}\" /usr/local/bin/kind\n\n## Install kubectl\n\n# Ensure keyrings directory exists only if it doesn't already\n[ -d /etc/apt/keyrings ] || sudo mkdir -p -m 755 /etc/apt/keyrings\n\nkubectl_minor_version=$(curl -fsSL --retry 5 --retry-delay 10 \"https://dl.k8s.io/release/stable.txt\" | cut -d'.' -f1,2 )\n\n# Download and validate GPG key\nkey_url=\"https://pkgs.k8s.io/core:/stable:/$kubectl_minor_version/deb/Release.key\"\nif curl -fsSL --retry 5 --retry-delay 10 -A \"Mozilla/5.0\" \"$key_url\" | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg; then\n    echo \"Key downloaded and stored successfully.\"\nelse\n    echo \"Failed to download valid GPG key from: $key_url\"\n    exit 1\nfi\n\necho 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/'$kubectl_minor_version'/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list\napt-get update\napt-get install kubectl\nrm -f /etc/apt/sources.list.d/kubernetes.list\n\n# Install Helm\ncurl -fsSL https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash\n\n\n# Download and install minikube\nminikube_version=\"latest\"\nminikube_binary_path=$(download_with_retry \"https://storage.googleapis.com/minikube/releases/${minikube_version}/minikube-linux-amd64\")\n\n# Supply chain security - Minikube\nminikube_hash=$(get_checksum_from_github_release \"kubernetes/minikube\" \"linux-amd64\" \"${minikube_version}\" \"SHA256\")\nuse_checksum_comparison \"${minikube_binary_path}\" \"${minikube_hash}\"\n\ninstall \"${minikube_binary_path}\" /usr/local/bin/minikube\n\n# Install kustomize\ndownload_url=\"https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh\"\ncurl -fsSL \"$download_url\" | bash\nmv kustomize /usr/local/bin\n\ninvoke_tests \"Tools\" \"Kubernetes tools\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-leiningen.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-leiningen.sh\n##  Desc:  Install Leiningen\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\n\nLEIN_BIN=/usr/local/bin/lein\ncurl -fsSL https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein > $LEIN_BIN\nchmod 0755 $LEIN_BIN\n\n# Run lein to trigger self-install\nexport LEIN_HOME=/usr/local/lib/lein\nlein\n\nLEIN_JAR=$(find $LEIN_HOME -name \"leiningen-*-standalone.jar\")\nset_etc_environment_variable \"LEIN_JAR\" \"${LEIN_JAR}\"\nset_etc_environment_variable \"LEIN_HOME\" \"${LEIN_HOME}\"\n\ninvoke_tests \"Tools\" \"Leiningen\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-microsoft-edge.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-microsoft-edge.sh\n##  Desc:  Install Microsoft Edge and WebDriver\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\nREPO_URL=\"https://packages.microsoft.com/repos/edge\"\nGPG_KEY=\"/usr/share/keyrings/microsoft-edge.gpg\"\nREPO_PATH=\"/etc/apt/sources.list.d/microsoft-edge.list\"\n\nwget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > $GPG_KEY\n# Specify an arch as Microsoft repository supports armhf and arm64 as well\necho \"deb [arch=amd64 signed-by=$GPG_KEY] $REPO_URL stable main\" > $REPO_PATH\n\napt-get update\napt-get install --no-install-recommends microsoft-edge-stable\n\nrm $GPG_KEY\nrm $REPO_PATH\n\necho \"microsoft-edge $REPO_URL\" >> $HELPER_SCRIPTS/apt-sources.txt\n\n# Install Microsoft Edge Webdriver\n\nEDGEDRIVER_DIR=\"/usr/local/share/edge_driver\"\nedgedriver_bin=\"$EDGEDRIVER_DIR/msedgedriver\"\n\nmkdir -p $EDGEDRIVER_DIR\n\nedge_version=$(microsoft-edge --version | cut -d' ' -f 3)\nedge_version_major=$(echo $edge_version | cut -d'.' -f 1)\n\nedgedriver_version_url=\"https://msedgedriver.microsoft.com/LATEST_RELEASE_${edge_version_major}_LINUX\"\n# Convert a resulting file to normal UTF-8\nedgedriver_latest_version=$(curl -fsSL \"$edgedriver_version_url\" | iconv -f utf-16 -t utf-8 | tr -d '\\r')\n\nedgedriver_url=\"https://msedgedriver.microsoft.com/${edgedriver_latest_version}/edgedriver_linux64.zip\"\nedgedriver_archive_path=$(download_with_retry \"$edgedriver_url\")\n\nunzip -qq \"$edgedriver_archive_path\" -d \"$EDGEDRIVER_DIR\"\nchmod +x $edgedriver_bin\nln -s $edgedriver_bin /usr/bin\n\nset_etc_environment_variable \"EDGEWEBDRIVER\" \"${EDGEDRIVER_DIR}\"\n\ninvoke_tests \"Browsers\" \"Edge\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-miniconda.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-miniconda.sh\n##  Desc:  Install miniconda\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# Install Miniconda\ncurl -fsSL https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -o miniconda.sh \\\n    && chmod +x miniconda.sh \\\n    && ./miniconda.sh -b -p /usr/share/miniconda \\\n    && rm miniconda.sh\n\nCONDA=/usr/share/miniconda\nset_etc_environment_variable \"CONDA\" \"${CONDA}\"\n\nln -s $CONDA/bin/conda /usr/bin/conda\n\ninvoke_tests \"Tools\" \"Conda\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-mono.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-mono.sh\n##  Desc:  Install Mono\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/os.sh\n\nos_label=$(lsb_release -cs)\nREPO_URL=\"https://download.mono-project.com/repo/ubuntu\"\nGPG_KEY=\"/usr/share/keyrings/mono-official-stable.gpg\"\nREPO_PATH=\"/etc/apt/sources.list.d/mono-official-stable.list\"\n\n# There are no packages for Ubuntu 22 in the repo, but developers confirmed that packages from Ubuntu 20 should work\nif is_ubuntu22; then\n    os_label=\"focal\"\nfi\n\n# Install Mono repo\ncurl -fsSL https://download.mono-project.com/repo/xamarin.gpg | gpg --dearmor -o $GPG_KEY\necho \"deb [signed-by=$GPG_KEY] $REPO_URL stable-$os_label main\" > $REPO_PATH\n\n# Install Mono\napt-get update\napt-get install --no-install-recommends apt-transport-https mono-complete nuget\n\n# Remove Mono's apt repo\nrm $REPO_PATH\nrm -f \"${REPO_PATH}.save\"\nrm $GPG_KEY\n\n# Document source repo\necho \"mono https://download.mono-project.com/repo/ubuntu stable-$os_label main\" >> $HELPER_SCRIPTS/apt-sources.txt\n\ninvoke_tests \"Tools\" \"Mono\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-ms-repos.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-ms-repos.sh\n##  Desc:  Install official Microsoft package repos for the distribution\n################################################################################\n\nos_label=$(lsb_release -rs)\n\n# Install Microsoft repository\nwget https://packages.microsoft.com/config/ubuntu/$os_label/packages-microsoft-prod.deb\ndpkg -i packages-microsoft-prod.deb\n\n# update\napt-get install apt-transport-https ca-certificates curl software-properties-common\napt-get update\napt-get dist-upgrade\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-mssql-tools.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-mssql-tools.sh\n##  Desc:  Install MS SQL Server client tools (https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-tools?view=sql-server-2017)\n################################################################################\n\nexport ACCEPT_EULA=Y\n\napt-get update\napt-get install mssql-tools unixodbc-dev\napt-get -f install\nln -s /opt/mssql-tools/bin/* /usr/local/bin/\n\ninvoke_tests \"Tools\" \"MSSQLCommandLineTools\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-mysql.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-mysql.sh\n##  Desc:  Install MySQL Client\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/os.sh\n\n# Mysql setting up root password\nMYSQL_ROOT_PASSWORD=root\necho \"mysql-server mysql-server/root_password password $MYSQL_ROOT_PASSWORD\" | debconf-set-selections\necho \"mysql-server mysql-server/root_password_again password $MYSQL_ROOT_PASSWORD\" | debconf-set-selections\n\nexport ACCEPT_EULA=Y\n\n# Install MySQL Client\napt-get install mysql-client\n\n# Install MySQL Server\napt-get install mysql-server\n\n# Install MySQL Dev tools\napt-get install libmysqlclient-dev\n\n# Disable mysql.service\nsystemctl is-active --quiet mysql.service && systemctl stop mysql.service\nsystemctl disable mysql.service\n\ninvoke_tests \"Databases\" \"MySQL\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-nginx.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-nginx.sh\n##  Desc:  Install Nginx\n################################################################################\n\n# Install Nginx\napt-get install nginx\n\n# Disable nginx.service\nsystemctl is-active --quiet nginx.service && systemctl stop nginx.service\nsystemctl disable nginx.service\n\ninvoke_tests \"WebServers\" \"Nginx\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-ninja.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-ninja.sh\n##  Desc:  Install ninja-build\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Install ninja\ndownload_url=$(resolve_github_release_asset_url \"ninja-build/ninja\" \"endswith(\\\"ninja-linux.zip\\\")\" \"latest\")\nninja_binary_path=$(download_with_retry \"${download_url}\")\n\n# Unzip the ninja binary\nunzip -qq \"$ninja_binary_path\" -d /usr/local/bin\n\ninvoke_tests \"Tools\" \"Ninja\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-nodejs.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-nodejs.sh\n##  Desc:  Install Node.js LTS and related tooling (Gulp, Grunt)\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Install default Node.js\ndefault_version=$(get_toolset_value '.node.default')\ncurl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n -o ~/n\nbash ~/n $default_version\n\n# Install node modules\nnode_modules=$(get_toolset_value '.node_modules[].name')\nnpm install -g $node_modules\n\necho \"Creating the symlink for [now] command to vercel CLI\"\nln -s /usr/local/bin/vercel /usr/local/bin/now\n\n# fix global modules installation as regular user\n# related issue https://github.com/actions/runner-images/issues/3727\nsudo chmod -R 777 /usr/local/lib/node_modules \nsudo chmod -R 777 /usr/local/bin\n\nrm -rf ~/n\n\ninvoke_tests \"Node\" \"Node.js\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-nvm.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-nvm.sh\n##  Desc:  Install Nvm\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\n\nexport NVM_DIR=\"/etc/skel/.nvm\"\nmkdir $NVM_DIR\nnvm_version=$(curl -fsSL https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')\ncurl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/$nvm_version/install.sh | bash\nset_etc_environment_variable \"NVM_DIR\" '$HOME/.nvm'\n\necho '[ -s \"$NVM_DIR/nvm.sh\" ] && \\. \"$NVM_DIR/nvm.sh\"  # This loads nvm' | tee -a /etc/skel/.bash_profile\n[ -s \"$NVM_DIR/nvm.sh\" ] && \\. \"$NVM_DIR/nvm.sh\"\n\ninvoke_tests \"Tools\" \"nvm\"\n\n# set system node.js as default one\nnvm alias default system\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-oc-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-oc-cli.sh\n##  Desc:  Install the OC CLI\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/os.sh\nsource $HELPER_SCRIPTS/install.sh\n\n\n# Install the oc CLI\ndownload_url=\"https://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest/openshift-client-linux.tar.gz\"\n\narchive_path=$(download_with_retry \"$download_url\")\n\ntar xzf \"$archive_path\" -C \"/usr/local/bin\" oc\n\ninvoke_tests \"CLI.Tools\" \"OC CLI\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-oras-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-oras-cli.sh\n##  Desc:  Install ORAS CLI\n##  Supply chain security: ORAS CLI - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Determine latest ORAS CLI version\ndownload_url=$(resolve_github_release_asset_url \"oras-project/oras\" \"endswith(\\\"linux_amd64.tar.gz\\\")\" \"latest\")\n\n# Download ORAS CLI\narchive_path=$(download_with_retry \"$download_url\")\n\n# Supply chain security - ORAS CLI\nhash_url=$(resolve_github_release_asset_url \"oras-project/oras\" \"endswith(\\\"checksums.txt\\\")\" \"latest\")\nexternal_hash=$(get_checksum_from_url \"${hash_url}\" \"linux_amd64.tar.gz\" \"SHA256\")\nuse_checksum_comparison \"$archive_path\" \"${external_hash}\"\n\n# Unzip ORAS CLI\ntar xzf \"$archive_path\" -C /usr/local/bin oras\n\ninvoke_tests \"CLI.Tools\" \"Oras CLI\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-packer.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-packer.sh\n##  Desc:  Install packer\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Install Packer\ndownload_url=$(curl -fsSL https://api.releases.hashicorp.com/v1/releases/packer/latest | jq -r '.builds[] | select((.arch==\"amd64\") and (.os==\"linux\")).url')\narchive_path=$(download_with_retry \"$download_url\")\nunzip -o -qq \"$archive_path\" -d /usr/local/bin\n\ninvoke_tests \"Tools\" \"Packer\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-php.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-php.sh\n##  Desc:  Install php\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\nsource $HELPER_SCRIPTS/os.sh\nsource $HELPER_SCRIPTS/install.sh\n\n# Install PHP\nphp_versions=$(get_toolset_value '.php.versions[]')\n\nfor version in $php_versions; do\n    echo \"Installing PHP $version\"\n    apt-get install --no-install-recommends \\\n        php$version \\\n        php$version-amqp \\\n        php$version-apcu \\\n        php$version-bcmath \\\n        php$version-bz2 \\\n        php$version-cgi \\\n        php$version-cli \\\n        php$version-common \\\n        php$version-curl \\\n        php$version-dba \\\n        php$version-dev \\\n        php$version-enchant \\\n        php$version-fpm \\\n        php$version-gd \\\n        php$version-gmp \\\n        php$version-igbinary \\\n        php$version-imagick \\\n        php$version-imap \\\n        php$version-interbase \\\n        php$version-intl \\\n        php$version-ldap \\\n        php$version-mbstring \\\n        php$version-memcache \\\n        php$version-memcached \\\n        php$version-mongodb \\\n        php$version-mysql \\\n        php$version-odbc \\\n        php$version-opcache \\\n        php$version-pgsql \\\n        php$version-phpdbg \\\n        php$version-pspell \\\n        php$version-readline \\\n        php$version-redis \\\n        php$version-snmp \\\n        php$version-soap \\\n        php$version-sqlite3 \\\n        php$version-sybase \\\n        php$version-tidy \\\n        php$version-xdebug \\\n        php$version-xml \\\n        php$version-xsl \\\n        php$version-yaml \\\n        php$version-zip \\\n        php$version-zmq\n\n        apt-get install --no-install-recommends php$version-pcov\n\n        # Disable PCOV, as Xdebug is enabled by default\n        # https://github.com/krakjoe/pcov#interoperability\n        phpdismod -v $version pcov\n\n    if [[ $version == \"7.2\" || $version == \"7.3\" || $version == \"7.4\" ]]; then\n        apt-get install --no-install-recommends php$version-recode\n    fi\n\n    if [[ $version != \"8.0\" && $version != \"8.1\" && $version != \"8.2\" && $version != \"8.3\" ]]; then\n        apt-get install --no-install-recommends php$version-xmlrpc php$version-json\n    fi\ndone\n\napt-get install --no-install-recommends php-pear\n\napt-get install --no-install-recommends snmp\n\n# Install composer\nphp -r \"copy('https://getcomposer.org/installer', 'composer-setup.php');\"\nphp -r \"if (hash_file('sha384', 'composer-setup.php') === file_get_contents('https://composer.github.io/installer.sig')) { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;\"\nphp composer-setup.php\nsudo mv composer.phar /usr/bin/composer\nphp -r \"unlink('composer-setup.php');\"\n\n# Add composer bin folder to path\nprepend_etc_environment_path '$HOME/.config/composer/vendor/bin'\n\n#Create composer folder for user to preserve folder permissions\nmkdir -p /etc/skel/.composer\n\n# Install phpunit (for PHP)\nwget -q -O phpunit https://phar.phpunit.de/phpunit-8.phar\ninstall phpunit /usr/local/bin/phpunit\n\ninvoke_tests \"Common\" \"PHP\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-pipx-packages.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-pipx-packages.sh\n##  Desc:  Install tools via pipx\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nexport PATH=\"$PATH:/opt/pipx_bin\"\n\npipx_packages=$(get_toolset_value \".pipx[] .package\")\n\nfor package in $pipx_packages; do\n    echo \"Install $package into default python\"\n    pipx install $package\n\n    # https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html\n    # Install ansible into an existing ansible-core Virtual Environment\n    if [[ $package == \"ansible-core\" ]]; then\n        pipx inject $package ansible\n    fi\ndone\n\ninvoke_tests \"Common\" \"PipxPackages\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-postgresql.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-postgresql.sh\n##  Desc:  Install PostgreSQL\n################################################################################\n\n# Source the helpers\nsource $HELPER_SCRIPTS/os.sh\nsource $HELPER_SCRIPTS/install.sh\n\nREPO_URL=\"https://apt.postgresql.org/pub/repos/apt/\"\n\n# Preparing repo for PostgreSQL\nwget -qO - https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor > /usr/share/keyrings/postgresql.gpg\necho \"deb [signed-by=/usr/share/keyrings/postgresql.gpg] $REPO_URL $(lsb_release -cs)-pgdg main\" > /etc/apt/sources.list.d/pgdg.list\n\n# Fetch PostgreSQL version to install from the toolset\ntoolset_version=$(get_toolset_value '.postgresql.version')\n\n# Install PostgreSQL\necho \"Install PostgreSQL\"\napt update\napt-get install postgresql-$toolset_version\n\necho \"Install libpq-dev\"\napt-get install libpq-dev\n\n# Disable postgresql.service\nsystemctl is-active --quiet postgresql.service && systemctl stop postgresql.service\nsystemctl disable postgresql.service\n\nrm /etc/apt/sources.list.d/pgdg.list\nrm /usr/share/keyrings/postgresql.gpg\n\necho \"postgresql $REPO_URL\" >> $HELPER_SCRIPTS/apt-sources.txt\n\ninvoke_tests \"Databases\" \"PostgreSQL\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-powershell.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-powershell.sh\n##  Desc:  Install PowerShell Core\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/os.sh\n\npwsh_version=$(get_toolset_value .pwsh.version)\n\n# Install Powershell\n\n    apt-get install powershell=$pwsh_version*\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-pulumi.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-pulumi.sh\n##  Desc:  Install Pulumi\n##  Supply chain security: Pulumi - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Dowload Pulumi\nversion=$(curl -fsSL \"https://www.pulumi.com/latest-version\")\ndownload_url=\"https://get.pulumi.com/releases/sdk/pulumi-v${version}-linux-x64.tar.gz\"\narchive_path=$(download_with_retry \"$download_url\")\n\n# Supply chain security - Pulumi\nexternal_hash=$(get_checksum_from_url \"https://github.com/pulumi/pulumi/releases/download/v${version}/SHA512SUMS\" \"linux-x64.tar.gz\" \"SHA512\")\nuse_checksum_comparison \"$archive_path\" \"$external_hash\" \"512\"\n\n# Unzipping Pulumi\ntar --strip=1 -xf \"$archive_path\" -C /usr/local/bin\n\ninvoke_tests \"Tools\" \"Pulumi\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-pypy.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-pypy.sh\n##  Desc:  Install PyPy\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# This function installs PyPy using the specified arguments:\n#   $1=package_url\ninstall_pypy() {\n    local package_url=$1\n\n    package_tar_name=$(echo \"$package_url\" | awk -F/ '{print $NF}')\n    package_name=${package_tar_name/.tar.bz2/}\n\n    echo \"Downloading tar archive '$package_name'\"\n    package_tar_temp_path=$(download_with_retry $package_url)\n\n    echo \"Expand '$package_name' to the /tmp folder\"\n    tar xf \"$package_tar_temp_path\" -C /tmp\n\n    # Get Python version\n    major_version=$(echo ${package_name/pypy/} | cut -d. -f1)\n    python_major=\"python$major_version\"\n\n    if [ $major_version != 2 ]; then\n        pypy_major=\"pypy$major_version\"\n    else\n        pypy_major=\"pypy\"\n    fi\n\n    package_temp_folder=\"/tmp/$package_name\"\n    python_full_version=$(\"$package_temp_folder/bin/$pypy_major\" -c \"import sys;print('{}.{}.{}'.format(sys.version_info[0],sys.version_info[1],sys.version_info[2]))\")\n    pypy_full_version=$(\"$package_temp_folder/bin/$pypy_major\" -c \"import sys;print('{}.{}.{}'.format(*sys.pypy_version_info[0:3]))\")\n    echo \"Put '$pypy_full_version' to PYPY_VERSION file\"\n    echo $pypy_full_version > \"$package_temp_folder/PYPY_VERSION\"\n\n    # PyPy folder structure\n    pypy_toolcache_path=$AGENT_TOOLSDIRECTORY/PyPy\n    pypy_toolcache_version_path=$pypy_toolcache_path/$python_full_version\n    pypy_toolcache_version_arch_path=$pypy_toolcache_version_path/x64\n\n    echo \"Check if PyPy hostedtoolcache folder exist...\"\n    if [ ! -d $pypy_toolcache_path ]; then\n        mkdir -p $pypy_toolcache_path\n    fi\n\n    echo \"Create PyPy '$pypy_toolcache_version_path' folder\"\n    mkdir $pypy_toolcache_version_path\n\n    echo \"Move PyPy '$package_temp_folder' binaries to '$pypy_toolcache_version_arch_path' folder\"\n    mv $package_temp_folder $pypy_toolcache_version_arch_path\n\n    echo \"Create additional symlinks (Required for UsePythonVersion Azure DevOps task)\"\n    cd $pypy_toolcache_version_arch_path/bin\n\n    # Starting from PyPy 7.3.4 these links are already included in the package\n    [ -f ./$python_major ] || ln -s $pypy_major $python_major\n    [ -f ./python ] || ln -s $python_major python\n\n    chmod +x ./python ./$python_major\n\n    echo \"Install latest Pip\"\n    ./python -m ensurepip\n    ./python -m pip install --ignore-installed pip\n\n    echo \"Create complete file\"\n    touch $pypy_toolcache_version_path/x64.complete\n\n    echo \"Remove '$package_tar_temp_path'\"\n    rm -f $package_tar_temp_path\n}\n\n# Installation PyPy\npypy_versions_json=$(curl -fsSL https://downloads.python.org/pypy/versions.json)\ntoolset_versions=$(get_toolset_value '.toolcache[] | select(.name | contains(\"PyPy\")) | .versions[]')\n\nfor toolset_version in $toolset_versions; do\n    latest_major_pypy_version=$(echo $pypy_versions_json |\n        jq -r --arg toolset_version $toolset_version '.[]\n        | select((.python_version | startswith($toolset_version)) and .stable == true).files[]\n        | select(.arch == \"x64\" and .platform == \"linux\").download_url' | head -1)\n    if [[ -z \"$latest_major_pypy_version\" ]]; then\n        echo \"Failed to get PyPy version '$toolset_version'\"\n        exit 1\n    fi\n\n    install_pypy $latest_major_pypy_version\ndone\n\nchown -R \"$SUDO_USER:$SUDO_USER\" \"$AGENT_TOOLSDIRECTORY/PyPy\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-python.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-python.sh\n##  Desc:  Install Python 3\n################################################################################\n\nset -e\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\nsource $HELPER_SCRIPTS/os.sh\n\n# Install Python, Python 3, pip, pip3\napt-get install --no-install-recommends python3 python3-dev python3-pip python3-venv\n\nif is_ubuntu24; then\n# Create temporary workaround to allow user to continue using pip\n    sudo cat <<EOF > /etc/pip.conf\n[global]\nbreak-system-packages = true\nEOF\nfi\n\n# Install pipx\n# Set pipx custom directory\nexport PIPX_BIN_DIR=/opt/pipx_bin\nexport PIPX_HOME=/opt/pipx\n\npython3 -m pip install pipx\npython3 -m pipx ensurepath\n\n# Update /etc/environment\nset_etc_environment_variable \"PIPX_BIN_DIR\" $PIPX_BIN_DIR\nset_etc_environment_variable \"PIPX_HOME\" $PIPX_HOME\nprepend_etc_environment_path $PIPX_BIN_DIR\n\n# Test pipx\nif ! command -v pipx; then\n    echo \"pipx was not installed or not found on PATH\"\n    exit 1\nfi\n\n# Adding this dir to PATH will make installed pip commands are immediately available.\nprepend_etc_environment_path '$HOME/.local/bin'\n\ninvoke_tests \"Tools\" \"Python\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-rlang.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-rlang.sh\n##  Desc:  Install R\n################################################################################\n\n# install R\nos_label=$(lsb_release -cs)\n\nwget -qO- https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc | gpg --dearmor > /usr/share/keyrings/rlang.gpg\necho \"deb [signed-by=/usr/share/keyrings/rlang.gpg] https://cloud.r-project.org/bin/linux/ubuntu $os_label-cran40/\" > /etc/apt/sources.list.d/rlang.list\n\napt-get update\napt-get install r-base\n\nrm /etc/apt/sources.list.d/rlang.list\nrm /usr/share/keyrings/rlang.gpg\n\ninvoke_tests \"Tools\" \"R\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-ruby.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-ruby.sh\n##  Desc:  Install Ruby requirements and ruby gems\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/os.sh\nsource $HELPER_SCRIPTS/install.sh\n\napt-get install ruby-full\n\n# Install ruby gems from toolset\ngems_to_install=$(get_toolset_value \".rubygems[] .name\")\nif [[ -n \"$gems_to_install\" ]]; then\n    for gem in $gems_to_install; do\n        echo \"Installing gem $gem\"\n        gem install --no-document $gem\n    done\nfi\n\n# Install Ruby requirements\napt-get install libz-dev openssl libssl-dev\n\necho \"Install Ruby from toolset...\"\ntoolset_versions=$(get_toolset_value '.toolcache[] | select(.name | contains(\"Ruby\")) | .versions[]')\nplatform_version=$(get_toolset_value '.toolcache[] | select(.name | contains(\"Ruby\")) | .platform_version')\narch=$(get_toolset_value '.toolcache[] | select(.name | contains(\"Ruby\")) | .arch')\nruby_path=\"$AGENT_TOOLSDIRECTORY/Ruby\"\n\necho \"Check if Ruby hostedtoolcache folder exist...\"\nif [[ ! -d $ruby_path ]]; then\n    mkdir -p $ruby_path\nfi\n\nfor toolset_version in ${toolset_versions[@]}; do\n    download_url=$(resolve_github_release_asset_url \"ruby/ruby-builder\" \"test(\\\"ruby-${toolset_version}-ubuntu-${platform_version}-${arch}.tar.gz\\\")\" \"${toolset_version}\" \"false\" \"true\")\n    package_tar_name=\"${download_url##*/}\"\n    ruby_version=$(echo \"$package_tar_name\" | cut -d'-' -f 2)\n    ruby_version_path=\"$ruby_path/$ruby_version\"\n\n    echo \"Create Ruby $ruby_version directory...\"\n    mkdir -p $ruby_version_path\n\n    echo \"Downloading tar archive $package_tar_name\"\n    package_archive_path=$(download_with_retry \"$download_url\")\n\n    echo \"Expand '$package_tar_name' to the '$ruby_version_path' folder\"\n    tar xf \"$package_archive_path\" -C $ruby_version_path\n\n    complete_file_path=\"$ruby_version_path/x64.complete\"\n    if [[ ! -f $complete_file_path ]]; then\n        echo \"Create complete file\"\n        touch $complete_file_path\n    fi\ndone\n\ninvoke_tests \"Tools\" \"Ruby\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-rust.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-rust.sh\n##  Desc:  Install Rust\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\nsource $HELPER_SCRIPTS/os.sh\n\nexport RUSTUP_HOME=/etc/skel/.rustup\nexport CARGO_HOME=/etc/skel/.cargo\n\ncurl -fsSL https://sh.rustup.rs | sh -s -- -y --default-toolchain=stable --profile=minimal\n\n# Initialize environment variables\nsource $CARGO_HOME/env\n\n# Install common tools\nrustup component add rustfmt clippy\n\nif is_ubuntu22; then\n    cargo install --locked bindgen-cli cbindgen cargo-audit cargo-outdated\nfi\n\n# Cleanup Cargo cache\nrm -rf ${CARGO_HOME}/registry/*\n\n# Update /etc/environment\nprepend_etc_environment_path '$HOME/.cargo/bin'\n\ninvoke_tests \"Tools\" \"Rust\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-sbt.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-sbt.sh\n##  Desc:  Install sbt\n################################################################################\n\nsource $HELPER_SCRIPTS/install.sh\n\n# Install latest sbt release\ndownload_url=$(resolve_github_release_asset_url \"sbt/sbt\" \"endswith(\\\".tgz\\\")\" \"latest\")\narchive_path=$(download_with_retry \"$download_url\")\ntar zxf \"$archive_path\" -C /usr/share\nln -s /usr/share/sbt/bin/sbt /usr/bin/sbt\n\ninvoke_tests \"Tools\" \"Sbt\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-selenium.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-selenium.sh\n##  Desc:  Install selenium server\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\nselenium_major_version=$(get_toolset_value '.selenium.version')\n\n# Download Selenium server\nselenium_download_url=$(resolve_github_release_asset_url \"SeleniumHQ/selenium\" \"contains(\\\"selenium-server-\\\") and endswith(\\\".jar\\\")\" \"$selenium_major_version\\.+\" \"\" \"true\")\nselenium_jar_path=$(download_with_retry \"$selenium_download_url\" \"/usr/share/java/selenium-server.jar\")\n\n# Create an empty file to retrieve selenium version\nselenium_full_version=$(echo $selenium_download_url | awk -F\"selenium-server-|.jar\" '{print $2}')\ntouch \"/usr/share/java/selenium-server-$selenium_full_version\"\n\n# Add SELENIUM_JAR_PATH environment variable\nset_etc_environment_variable \"SELENIUM_JAR_PATH\" \"$selenium_jar_path\"\n\ninvoke_tests \"Tools\" \"Selenium\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-sqlpackage.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-sqlpackage.sh\n##  Desc:  Install SqlPackage CLI to DacFx (https://docs.microsoft.com/sql/tools/sqlpackage/sqlpackage-download#get-sqlpackage-net-core-for-linux)\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/os.sh\n\n# Install libssl1.1 dependency\nif is_ubuntu22; then\n    focal_list=/etc/apt/sources.list.d/focal-security.list\n    echo \"deb https://archive.ubuntu.com/ubuntu/ focal-security main\" | tee \"${focal_list}\"\n    apt-get update --quiet\n\n    apt-get install --no-install-recommends libssl1.1\n\n    rm \"${focal_list}\"\n    apt-get update --quiet\nfi\n\n# Install SqlPackage\narchive_path=$(download_with_retry \"https://aka.ms/sqlpackage-linux\")\nunzip -qq \"$archive_path\" -d /usr/local/sqlpackage\nchmod +x /usr/local/sqlpackage/sqlpackage\nln -sf /usr/local/sqlpackage/sqlpackage /usr/local/bin\n\ninvoke_tests \"Tools\" \"SqlPackage\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-swift.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-swift.sh\n##  Desc:  Install Swift\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# Install\nimage_label=\"ubuntu$(lsb_release -rs)\"\nswift_version=$(curl -fsSL \"https://api.github.com/repos/apple/swift/releases/latest\" | jq -r '.tag_name | match(\"[0-9.]+\").string')\nswift_release_name=\"swift-${swift_version}-RELEASE-${image_label}\"\n\narchive_url=\"https://swift.org/builds/swift-${swift_version}-release/${image_label//./}/swift-${swift_version}-RELEASE/${swift_release_name}.tar.gz\"\narchive_path=$(download_with_retry \"$archive_url\")\n\n# Verifying PGP signature using official Swift PGP key. Referring to https://www.swift.org/install/linux/#Installation-via-Tarball\n# Download and import Swift PGP keys\ngpg --keyserver hkps://keyserver.ubuntu.com:443 \\\n      --recv-keys \\\n      '7463 A81A 4B2E EA1B 551F  FBCF D441 C977 412B 37AD' \\\n      '1BE1 E29A 084C B305 F397  D62A 9F59 7F4D 21A5 6D5F' \\\n      'A3BA FD35 56A5 9079 C068  94BD 63BC 1CFE 91D3 06C6' \\\n      '5E4D F843 FB06 5D7F 7E24  FBA2 EF54 30F0 71E1 B235' \\\n      '8513 444E 2DA3 6B7C 1659  AF4D 7638 F1FB 2B2B 08C4' \\\n      'A62A E125 BBBF BB96 A6E0  42EC 925C C1CC ED3D 1561' \\\n      '8A74 9566 2C3C D4AE 18D9  5637 FAF6 989E 1BC1 6FEA' \\\n      'E813 C892 820A 6FA1 3755  B268 F167 DF1A CF9C E069' \\\n      '52BB 7E3D E28A 71BE 22EC  05FF EF80 A866 B47A 981F'\n\ngpg --keyserver hkps://keyserver.ubuntu.com:443 --refresh-keys Swift\n\n# Download and verify signature\nsignature_path=$(download_with_retry \"${archive_url}.sig\")\ngpg --verify \"$signature_path\" \"$archive_path\"\n\n# Remove Swift PGP public key with temporary keyring\nrm -rf ~/.gnupg\n\n# Extract and install swift\ntar xzf \"$archive_path\" -C /tmp\n\nSWIFT_INSTALL_ROOT=\"/usr/share/swift\"\nswift_bin_root=\"$SWIFT_INSTALL_ROOT/usr/bin\"\nswift_lib_root=\"$SWIFT_INSTALL_ROOT/usr/lib\"\n\nmv \"/tmp/${swift_release_name}\" $SWIFT_INSTALL_ROOT\nmkdir -p /usr/local/lib\n\nln -s \"$swift_bin_root/swift\" /usr/local/bin/swift\nln -s \"$swift_bin_root/swiftc\" /usr/local/bin/swiftc\nln -s \"$swift_lib_root/libsourcekitdInProc.so\" /usr/local/lib/libsourcekitdInProc.so\n\nset_etc_environment_variable \"SWIFT_PATH\" \"${swift_bin_root}\"\n\ninvoke_tests \"Common\" \"Swift\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-terraform.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-terraform.sh\n##  Desc:  Install terraform\n################################################################################\n\nsource $HELPER_SCRIPTS/install.sh\n\n# Install Terraform\ndownload_url=$(curl -fsSL https://api.releases.hashicorp.com/v1/releases/terraform/latest | jq -r '.builds[] | select((.arch==\"amd64\") and (.os==\"linux\")).url')\narchive_path=$(download_with_retry \"${download_url}\")\nunzip -qq \"$archive_path\" -d /usr/local/bin\n\ninvoke_tests \"Tools\" \"Terraform\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-vcpkg.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-vcpkg.sh\n##  Desc:  Install vcpkg\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# Set env variable for vcpkg\nVCPKG_INSTALLATION_ROOT=/usr/local/share/vcpkg\nset_etc_environment_variable \"VCPKG_INSTALLATION_ROOT\" \"${VCPKG_INSTALLATION_ROOT}\"\n\n# Install vcpkg\ngit clone https://github.com/Microsoft/vcpkg $VCPKG_INSTALLATION_ROOT\n\n$VCPKG_INSTALLATION_ROOT/bootstrap-vcpkg.sh\n\n# workaround https://github.com/microsoft/vcpkg/issues/27786\n\nmkdir -p /root/.vcpkg/ $HOME/.vcpkg\ntouch /root/.vcpkg/vcpkg.path.txt $HOME/.vcpkg/vcpkg.path.txt\n\n$VCPKG_INSTALLATION_ROOT/vcpkg integrate install\nchmod 0777 -R $VCPKG_INSTALLATION_ROOT\nln -sf $VCPKG_INSTALLATION_ROOT/vcpkg /usr/local/bin\n\nrm -rf /root/.vcpkg $HOME/.vcpkg\n\ninvoke_tests \"Tools\" \"Vcpkg\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-yq.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-yq.sh\n##  Desc:  Install yq - a command-line YAML, JSON and XML processor\n##  Supply chain security: yq - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Download yq\nyq_url=$(resolve_github_release_asset_url \"mikefarah/yq\" \"endswith(\\\"yq_linux_amd64\\\")\" \"latest\")\nbinary_path=$(download_with_retry \"${yq_url}\")\n\n# Supply chain security - yq\nhash_url=$(resolve_github_release_asset_url \"mikefarah/yq\" \"endswith(\\\"checksums\\\")\" \"latest\")\nexternal_hash=$(get_checksum_from_url \"${hash_url}\" \"yq_linux_amd64 \" \"SHA256\" \"true\" \" \" \"19\")\nuse_checksum_comparison \"$binary_path\" \"$external_hash\"\n\n# Install yq\ninstall \"$binary_path\" /usr/bin/yq\n\ninvoke_tests \"Tools\" \"yq\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/install-zstd.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-zstd.sh\n##  Desc:  Install zstd\n##  Supply chain security: zstd - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Download zstd\nrelease_tag=$(curl -fsSL https://api.github.com/repos/facebook/zstd/releases/latest | jq -r '.tag_name')\nrelease_name=\"zstd-${release_tag//v}\"\ndownload_url=\"https://github.com/facebook/zstd/releases/download/${release_tag}/${release_name}.tar.gz\"\narchive_path=$(download_with_retry \"${download_url}\")\n\n# Supply chain security - zstd\nexternal_hash=$(get_checksum_from_url \"${download_url}.sha256\" \"${release_name}.tar.gz\" \"SHA256\")\nuse_checksum_comparison \"$archive_path\" \"$external_hash\"\n\n# Install zstd\napt-get install liblz4-dev\ntar xzf \"$archive_path\" -C /tmp\n\nmake -C \"/tmp/${release_name}/contrib/pzstd\" -j $(nproc) all\nmake -C \"/tmp/${release_name}\" -j $(nproc) zstd-release\n\nfor copyprocess in zstd zstdless zstdgrep; do\n    cp \"/tmp/${release_name}/programs/${copyprocess}\" /usr/local/bin/\ndone\n\ncp \"/tmp/${release_name}/contrib/pzstd/pzstd\" /usr/local/bin/\n\nfor symlink in zstdcat zstdmt unzstd; do\n    ln -sf /usr/local/bin/zstd /usr/local/bin/${symlink}\ndone\n\ninvoke_tests \"Tools\" \"Zstd\"\n"
  },
  {
    "path": "images/ubuntu/scripts/build/list-dpkg.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  list-dpkg.sh\n##  Desc:  List all installed dpkg packages\n################################################################################\n\necho \"Listing all installed dpkg packages...\"\ndpkg-query -W -f='${Package} ${Version}\\n' | sort\n"
  },
  {
    "path": "images/ubuntu/scripts/build/post-build-validation.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  post-build-validation.sh\n##  Desc:  Validate different aspects of the image after build\n################################################################################\n\necho \"Test microsoft defender not installed using '-d /opt/microsoft/mdatp'\"\n# Validate Defender not installed test 1\nif [ -d /opt/microsoft/mdatp ]; then\n    echo \"Microsoft Defender for Endpoint is installed.\"\n    exit 1\nfi\n\necho \"Test microsoft defender not installed using 'systemctl list-units --type=service --all | grep mdatp'\"\n# Validate Defender not installed test 2\nif systemctl list-units --type=service --all | grep -w mdatp &>/dev/null; then\n    echo \"Microsoft Defender for Endpoint is installed.\"\n    exit 1\nfi\n"
  },
  {
    "path": "images/ubuntu/scripts/docs-gen/Generate-SoftwareReport.ps1",
    "content": "using module ./software-report-base/SoftwareReport.psm1\nusing module ./software-report-base/SoftwareReport.Nodes.psm1\n\nparam (\n    [Parameter(Mandatory)]\n    [string] $OutputDirectory\n)\n\n$global:ErrorActionPreference = \"Stop\"\n$global:ErrorView = \"NormalView\"\nSet-StrictMode -Version Latest\n\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Android.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Browsers.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.CachedTools.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Common.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Databases.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Helpers.psm1\") -DisableNameChecking\nImport-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\" -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Java.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Rust.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Tools.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.WebServers.psm1\") -DisableNameChecking\n\n# Restore file owner in user profile\nsudo chown -R ${env:USER}: $env:HOME\n\n# Software report\n$softwareReport = [SoftwareReport]::new(\"Ubuntu $(Get-OSVersionShort)\")\n$softwareReport.Root.AddToolVersion(\"OS Version:\", $(Get-OSVersionFull))\n$softwareReport.Root.AddToolVersion(\"Kernel Version:\", $(Get-KernelVersion))\n$softwareReport.Root.AddToolVersion(\"Image Version:\", $env:IMAGE_VERSION)\n$softwareReport.Root.AddToolVersion(\"Systemd version:\", $(Get-SystemdVersion))\n\n$installedSoftware = $softwareReport.Root.AddHeader(\"Installed Software\")\n\n# Language and Runtime\n$languageAndRuntime = $installedSoftware.AddHeader(\"Language and Runtime\")\n$languageAndRuntime.AddToolVersion(\"Bash\", $(Get-BashVersion))\n$languageAndRuntime.AddToolVersionsListInline(\"Clang\", $(Get-ClangToolVersions -ToolName \"clang\"), \"^\\d+\")\n$languageAndRuntime.AddToolVersionsListInline(\"Clang-format\", $(Get-ClangToolVersions -ToolName \"clang-format\"), \"^\\d+\")\n$languageAndRuntime.AddToolVersionsListInline(\"Clang-tidy\", $(Get-ClangTidyVersions), \"^\\d+\")\n$languageAndRuntime.AddToolVersion(\"Dash\", $(Get-DashVersion))\n$languageAndRuntime.AddToolVersionsListInline(\"GNU C++\", $(Get-CPPVersions), \"^\\d+\")\n$languageAndRuntime.AddToolVersionsListInline(\"GNU Fortran\", $(Get-FortranVersions), \"^\\d+\")\n$languageAndRuntime.AddToolVersion(\"Julia\", $(Get-JuliaVersion))\n$languageAndRuntime.AddToolVersion(\"Kotlin\", $(Get-KotlinVersion))\nif (-not $(Test-IsUbuntu24)) {\n    $languageAndRuntime.AddToolVersion(\"Mono\", $(Get-MonoVersion))\n    $languageAndRuntime.AddToolVersion(\"MSBuild\", $(Get-MsbuildVersion))\n}\n$languageAndRuntime.AddToolVersion(\"Node.js\", $(Get-NodeVersion))\n$languageAndRuntime.AddToolVersion(\"Perl\", $(Get-PerlVersion))\n$languageAndRuntime.AddToolVersion(\"Python\", $(Get-PythonVersion))\n$languageAndRuntime.AddToolVersion(\"Ruby\", $(Get-RubyVersion))\n$languageAndRuntime.AddToolVersion(\"Swift\", $(Get-SwiftVersion))\n\n\n# Package Management\n$packageManagement = $installedSoftware.AddHeader(\"Package Management\")\n$packageManagement.AddToolVersion(\"cpan\", $(Get-CpanVersion))\n$packageManagement.AddToolVersion(\"Helm\", $(Get-HelmVersion))\n$packageManagement.AddToolVersion(\"Homebrew\", $(Get-HomebrewVersion))\n$packageManagement.AddToolVersion(\"Miniconda\", $(Get-MinicondaVersion))\n$packageManagement.AddToolVersion(\"Npm\", $(Get-NpmVersion))\nif (-not $(Test-IsUbuntu24)) {\n    $packageManagement.AddToolVersion(\"NuGet\", $(Get-NuGetVersion))\n}\n$packageManagement.AddToolVersion(\"Pip\", $(Get-PipVersion))\n$packageManagement.AddToolVersion(\"Pip3\", $(Get-Pip3Version))\n$packageManagement.AddToolVersion(\"Pipx\", $(Get-PipxVersion))\n$packageManagement.AddToolVersion(\"RubyGems\", $(Get-GemVersion))\n$packageManagement.AddToolVersion(\"Vcpkg\", $(Get-VcpkgVersion))\n$packageManagement.AddToolVersion(\"Yarn\", $(Get-YarnVersion))\n$packageManagement.AddHeader(\"Environment variables\").AddTable($(Build-PackageManagementEnvironmentTable))\n$packageManagement.AddHeader(\"Homebrew note\").AddNote(@'\nLocation: /home/linuxbrew\nNote: Homebrew is pre-installed on image but not added to PATH.\nrun the eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\" command\nto accomplish this.\n'@)\n\n# Project Management\n$projectManagement = $installedSoftware.AddHeader(\"Project Management\")\n$projectManagement.AddToolVersion(\"Ant\", $(Get-AntVersion))\n$projectManagement.AddToolVersion(\"Gradle\", $(Get-GradleVersion))\n$projectManagement.AddToolVersion(\"Lerna\", $(Get-LernaVersion))\n$projectManagement.AddToolVersion(\"Maven\", $(Get-MavenVersion))\nif (Test-IsUbuntu22) {\n    $projectManagement.AddToolVersion(\"Sbt\", $(Get-SbtVersion))\n}\n\n# Tools\n$tools = $installedSoftware.AddHeader(\"Tools\")\n$tools.AddToolVersion(\"Ansible\", $(Get-AnsibleVersion))\nif (Test-IsUbuntu22) {\n    $tools.AddToolVersion(\"apt-fast\", $(Get-AptFastVersion))\n}\n$tools.AddToolVersion(\"AzCopy\", $(Get-AzCopyVersion))\n$tools.AddToolVersion(\"Bazel\", $(Get-BazelVersion))\n$tools.AddToolVersion(\"Bazelisk\", $(Get-BazeliskVersion))\n$tools.AddToolVersion(\"Bicep\", $(Get-BicepVersion))\n$tools.AddToolVersion(\"Buildah\", $(Get-BuildahVersion))\n$tools.AddToolVersion(\"CMake\", $(Get-CMakeVersion))\n$tools.AddToolVersion(\"CodeQL Action Bundle\", $(Get-CodeQLBundleVersion))\n$tools.AddToolVersion(\"Docker Amazon ECR Credential Helper\", $(Get-DockerAmazonECRCredHelperVersion))\n$tools.AddToolVersion(\"Docker Compose v2\", $(Get-DockerComposeV2Version))\n$tools.AddToolVersion(\"Docker-Buildx\", $(Get-DockerBuildxVersion))\n$tools.AddToolVersion(\"Docker Client\", $(Get-DockerClientVersion))\n$tools.AddToolVersion(\"Docker Server\", $(Get-DockerServerVersion))\n$tools.AddToolVersion(\"Fastlane\", $(Get-FastlaneVersion))\n$tools.AddToolVersion(\"Git\", $(Get-GitVersion))\n$tools.AddToolVersion(\"Git LFS\", $(Get-GitLFSVersion))\n$tools.AddToolVersion(\"Git-ftp\", $(Get-GitFTPVersion))\n$tools.AddToolVersion(\"Haveged\", $(Get-HavegedVersion))\nif (Test-IsUbuntu22) {\n    $tools.AddToolVersion(\"Heroku\", $(Get-HerokuVersion))\n}\n$tools.AddToolVersion(\"jq\", $(Get-JqVersion))\n$tools.AddToolVersion(\"Kind\", $(Get-KindVersion))\n$tools.AddToolVersion(\"Kubectl\", $(Get-KubectlVersion))\n$tools.AddToolVersion(\"Kustomize\", $(Get-KustomizeVersion))\nif (Test-IsUbuntu22) {\n    $tools.AddToolVersion(\"Leiningen\", $(Get-LeiningenVersion))\n}\n$tools.AddToolVersion(\"MediaInfo\", $(Get-MediainfoVersion))\n$tools.AddToolVersion(\"Mercurial\", $(Get-HGVersion))\n$tools.AddToolVersion(\"Minikube\", $(Get-MinikubeVersion))\n$tools.AddToolVersion(\"n\", $(Get-NVersion))\n$tools.AddToolVersion(\"Newman\", $(Get-NewmanVersion))\n$tools.AddToolVersion(\"nvm\", $(Get-NvmVersion))\n$tools.AddToolVersion(\"OpenSSL\", $(Get-OpensslVersion))\n$tools.AddToolVersion(\"Packer\", $(Get-PackerVersion))\n$tools.AddToolVersion(\"Parcel\", $(Get-ParcelVersion))\n$tools.AddToolVersion(\"Podman\", $(Get-PodManVersion))\n$tools.AddToolVersion(\"Pulumi\", $(Get-PulumiVersion))\nif (Test-IsUbuntu22) {\n    $tools.AddToolVersion(\"R\", $(Get-RVersion))\n}\n$tools.AddToolVersion(\"Skopeo\", $(Get-SkopeoVersion))\n$tools.AddToolVersion(\"Sphinx Open Source Search Server\", $(Get-SphinxVersion))\nif (Test-IsUbuntu22) {\n    $tools.AddToolVersion(\"SVN\", $(Get-SVNVersion))\n    $tools.AddToolVersion(\"Terraform\", $(Get-TerraformVersion))\n}\n$tools.AddToolVersion(\"yamllint\", $(Get-YamllintVersion))\n$tools.AddToolVersion(\"yq\", $(Get-YqVersion))\n$tools.AddToolVersion(\"zstd\", $(Get-ZstdVersion))\n$tools.AddToolVersion(\"Ninja\", $(Get-NinjaVersion))\n\n# CLI Tools\n$cliTools = $installedSoftware.AddHeader(\"CLI Tools\")\nif (Test-IsUbuntu22) {\n    $cliTools.AddToolVersion(\"Alibaba Cloud CLI\", $(Get-AlibabaCloudCliVersion))\n}\n$cliTools.AddToolVersion(\"AWS CLI\", $(Get-AWSCliVersion))\n$cliTools.AddToolVersion(\"AWS CLI Session Manager Plugin\", $(Get-AWSCliSessionManagerPluginVersion))\n$cliTools.AddToolVersion(\"AWS SAM CLI\", $(Get-AWSSAMVersion))\n$cliTools.AddToolVersion(\"Azure CLI\", $(Get-AzureCliVersion))\n$cliTools.AddToolVersion(\"Azure CLI (azure-devops)\", $(Get-AzureDevopsVersion))\n$cliTools.AddToolVersion(\"GitHub CLI\", $(Get-GitHubCliVersion))\n$cliTools.AddToolVersion(\"Google Cloud CLI\", $(Get-GoogleCloudCLIVersion))\nif (Test-IsUbuntu22) {\n    $cliTools.AddToolVersion(\"Netlify CLI\", $(Get-NetlifyCliVersion))\n    $cliTools.AddToolVersion(\"OpenShift CLI\", $(Get-OCCliVersion))\n    $cliTools.AddToolVersion(\"ORAS CLI\", $(Get-ORASCliVersion))\n    $cliTools.AddToolVersion(\"Vercel CLI\", $(Get-VerselCliversion))\n}\n\n# Java\n$installedSoftware.AddHeader(\"Java\").AddTable($(Get-JavaVersionsTable))\n\n# PHP Tools\n$phpTools = $installedSoftware.AddHeader(\"PHP Tools\")\n$phpTools.AddToolVersionsListInline(\"PHP\", $(Get-PHPVersions), \"^\\d+\\.\\d+\")\n$phpTools.AddToolVersion(\"Composer\", $(Get-ComposerVersion))\n$phpTools.AddToolVersion(\"PHPUnit\", $(Get-PHPUnitVersion))\n$phpTools.AddNote(\"Both Xdebug and PCOV extensions are installed, but only Xdebug is enabled.\")\n\n# Haskell Tools\n$haskellTools = $installedSoftware.AddHeader(\"Haskell Tools\")\n$haskellTools.AddToolVersion(\"Cabal\", $(Get-CabalVersion))\n$haskellTools.AddToolVersion(\"GHC\", $(Get-GHCVersion))\n$haskellTools.AddToolVersion(\"GHCup\", $(Get-GHCupVersion))\n$haskellTools.AddToolVersion(\"Stack\", $(Get-StackVersion))\n\n# Rust Tools\nInitialize-RustEnvironment\n$rustTools = $installedSoftware.AddHeader(\"Rust Tools\")\n$rustTools.AddToolVersion(\"Cargo\", $(Get-CargoVersion))\n$rustTools.AddToolVersion(\"Rust\", $(Get-RustVersion))\n$rustTools.AddToolVersion(\"Rustdoc\", $(Get-RustdocVersion))\n$rustTools.AddToolVersion(\"Rustup\", $(Get-RustupVersion))\n\n# Packages\n$rustToolsPackages = $rustTools.AddHeader(\"Packages\")\nif (Test-IsUbuntu22) {\n    $rustToolsPackages.AddToolVersion(\"Bindgen\", $(Get-BindgenVersion))\n    $rustToolsPackages.AddToolVersion(\"Cargo audit\", $(Get-CargoAuditVersion))\n    $rustToolsPackages.AddToolVersion(\"Cargo clippy\", $(Get-CargoClippyVersion))\n    $rustToolsPackages.AddToolVersion(\"Cargo outdated\", $(Get-CargoOutdatedVersion))\n    $rustToolsPackages.AddToolVersion(\"Cbindgen\", $(Get-CbindgenVersion))\n}\n$rustToolsPackages.AddToolVersion(\"Rustfmt\", $(Get-RustfmtVersion))\n\n# Browsers and Drivers\n$browsersTools = $installedSoftware.AddHeader(\"Browsers and Drivers\")\n$browsersTools.AddToolVersion(\"Google Chrome\", $(Get-ChromeVersion))\n$browsersTools.AddToolVersion(\"ChromeDriver\", $(Get-ChromeDriverVersion))\n$browsersTools.AddToolVersion(\"Chromium\", $(Get-ChromiumVersion))\n$browsersTools.AddToolVersion(\"Microsoft Edge\", $(Get-EdgeVersion))\n$browsersTools.AddToolVersion(\"Microsoft Edge WebDriver\", $(Get-EdgeDriverVersion))\n\n$browsersTools.AddToolVersion(\"Selenium server\", $(Get-SeleniumVersion))\n$browsersTools.AddToolVersion(\"Mozilla Firefox\", $(Get-FirefoxVersion))\n$browsersTools.AddToolVersion(\"Geckodriver\", $(Get-GeckodriverVersion))\n\n\n# Environment variables\n$browsersTools.AddHeader(\"Environment variables\").AddTable($(Build-BrowserWebdriversEnvironmentTable))\n\n# .NET Tools\n$netCoreTools = $installedSoftware.AddHeader(\".NET Tools\")\n$netCoreTools.AddToolVersionsListInline(\".NET Core SDK\", $(Get-DotNetCoreSdkVersions), \"^\\d+\\.\\d+\\.\\d\")\n$netCoreTools.AddNodes($(Get-DotnetTools))\n\n# Databases\n$databasesTools = $installedSoftware.AddHeader(\"Databases\")\n$databasesTools.AddToolVersion(\"sqlite3\", $(Get-SqliteVersion))\n$databasesTools.AddNode($(Build-PostgreSqlSection))\n$databasesTools.AddNode($(Build-MySQLSection))\nif (-not $(Test-IsUbuntu24)) {\n    $databasesTools.AddNode($(Build-MSSQLToolsSection))\n}\n\n# Cached Tools\n$cachedTools = $installedSoftware.AddHeader(\"Cached Tools\")\n$cachedTools.AddToolVersionsList(\"Go\", $(Get-ToolcacheGoVersions), \"^\\d+\\.\\d+\")\n$cachedTools.AddToolVersionsList(\"Node.js\", $(Get-ToolcacheNodeVersions), \"^\\d+\")\n$cachedTools.AddToolVersionsList(\"Python\", $(Get-ToolcachePythonVersions), \"^\\d+\\.\\d+\")\n$cachedTools.AddToolVersionsList(\"PyPy\", $(Get-ToolcachePyPyVersions), \"^\\d+\\.\\d+\")\n$cachedTools.AddToolVersionsList(\"Ruby\", $(Get-ToolcacheRubyVersions), \"^\\d+\\.\\d+\")\n\n\n# PowerShell Tools\n$powerShellTools = $installedSoftware.AddHeader(\"PowerShell Tools\")\n$powerShellTools.AddToolVersion(\"PowerShell\", $(Get-PowershellVersion))\n$powerShellTools.AddHeader(\"PowerShell Modules\").AddNodes($(Get-PowerShellModules))\n\n$installedSoftware.AddHeader(\"Web Servers\").AddTable($(Build-WebServersTable))\n\n$androidTools = $installedSoftware.AddHeader(\"Android\")\n$androidTools.AddTable($(Build-AndroidTable))\n\n$androidTools.AddHeader(\"Environment variables\").AddTable($(Build-AndroidEnvironmentTable))\n\n$installedSoftware.AddHeader(\"Installed apt packages\").AddTable($(Get-AptPackages))\n\n$softwareReport.ToJson() | Out-File -FilePath \"${OutputDirectory}/software-report.json\" -Encoding UTF8NoBOM\n$softwareReport.ToMarkdown() | Out-File -FilePath \"${OutputDirectory}/software-report.md\" -Encoding UTF8NoBOM\n"
  },
  {
    "path": "images/ubuntu/scripts/docs-gen/SoftwareReport.Android.psm1",
    "content": "function Split-TableRowByColumns {\n    param(\n        [string] $Row\n    )\n    return $Row.Split(\"|\") | ForEach-Object { $_.trim() }\n}\n\nfunction Get-AndroidSDKRoot {\n    return \"/usr/local/lib/android/sdk\"\n}\n\nfunction Get-AndroidSDKManagerPath {\n    $androidSDKDir = Get-AndroidSDKRoot\n    return Join-Path $androidSDKDir \"cmdline-tools\" \"latest\" \"bin\" \"sdkmanager\"\n}\n\nfunction Get-AndroidInstalledPackages {\n    $androidSDKManagerPath = Get-AndroidSDKManagerPath\n    $androidSDKManagerList = Invoke-Expression \"$androidSDKManagerPath --list_installed --include_obsolete\"\n    return $androidSDKManagerList\n}\n\nfunction Build-AndroidTable {\n    $packageInfo = Get-AndroidInstalledPackages\n    return @(\n        @{\n            \"Package\" = \"Android Command Line Tools\"\n            \"Version\" = Get-AndroidCommandLineToolsVersion\n        },\n        @{\n            \"Package\" = \"Android Emulator\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Android Emulator\"\n        },\n        @{\n            \"Package\" = \"Android SDK Build-tools\"\n            \"Version\" = Get-AndroidBuildToolVersions -PackageInfo $packageInfo\n        },\n        @{\n            \"Package\" = \"Android SDK Platform-Tools\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Android SDK Platform-Tools\"\n        },\n        @{\n            \"Package\" = \"Android SDK Platforms\"\n            \"Version\" = Get-AndroidPlatformVersions -PackageInfo $packageInfo\n        },\n        @{\n            \"Package\" = \"Android Support Repository\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Android Support Repository\"\n        },\n        @{\n            \"Package\" = \"CMake\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"cmake\"\n        },\n        @{\n            \"Package\" = \"Google APIs\"\n            \"Version\" = Get-AndroidGoogleAPIsVersions -PackageInfo $packageInfo\n        },\n        @{\n            \"Package\" = \"Google Play services\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Google Play services\"\n        },\n        @{\n            \"Package\" = \"Google Repository\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Google Repository\"\n        },\n        @{\n            \"Package\" = \"NDK\"\n            \"Version\" = Get-AndroidNDKVersions\n        },\n        @{\n            \"Package\" = \"SDK Patch Applier v4\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"SDK Patch Applier v4\"\n        }\n    ) | Where-Object { $_.Version } | ForEach-Object {\n        [PSCustomObject] @{\n            \"Package Name\" = $_.Package\n            \"Version\"      = $_.Version\n        }\n    }\n}\n\nfunction Get-AndroidPackageVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo,\n        [Parameter(Mandatory)]\n        [object] $MatchedString\n    )\n\n    $versions = $PackageInfo | Where-Object { $_ -Match $MatchedString } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        return $packageInfoParts[1]\n    }\n    return ($versions -Join \"<br>\")\n}\n\nfunction Get-AndroidPlatformVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo\n    )\n\n    $versions = $PackageInfo | Where-Object { $_ -Match \"Android SDK Platform \" } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        $revision = $packageInfoParts[1]\n        $version = $packageInfoParts[0].split(\";\")[1]\n        return \"$version (rev $revision)\"\n    }\n    [array]::Reverse($versions)\n    return ($versions -Join \"<br>\")\n}\n\nfunction Get-AndroidCommandLineToolsVersion {\n    $commandLineTools = Get-AndroidSDKManagerPath\n    (& $commandLineTools --version | Out-String).Trim() -match \"(?<version>^(\\d+\\.){1,}\\d+$)\" | Out-Null\n    $commandLineToolsVersion = $Matches.Version\n    return $commandLineToolsVersion\n}\n\nfunction Get-AndroidBuildToolVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo\n    )\n\n    $versions = $PackageInfo | Where-Object { $_ -Match \"Android SDK Build-Tools\" } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        return $packageInfoParts[1]\n    }\n    $groupVersions = @()\n    $versions | ForEach-Object {\n        $majorVersion = $_.Split(\".\")[0]\n        $groupVersions += $versions | Where-Object { $_.StartsWith($majorVersion) } | Join-String -Separator \" \"\n    }\n    return ($groupVersions | Sort-Object -Descending -Unique | Join-String -Separator \"<br>\")\n}\n\nfunction Get-AndroidGoogleAPIsVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo\n    )\n\n    $versions = $PackageInfo | Where-Object { $_ -Match \"Google APIs\" } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        return $packageInfoParts[0].split(\";\")[1]\n    }\n    return ($versions -Join \"<br>\")\n}\n\nfunction Get-AndroidNDKVersions {\n    $ndkFolderPath = Join-Path (Get-AndroidSDKRoot) \"ndk\"\n    $versions = Get-ChildItem -Path $ndkFolderPath -Name\n    $ndkDefaultVersion = (Get-ToolsetContent).android.ndk.default\n    $ndkDefaultFullVersion = Get-ChildItem \"$env:ANDROID_HOME/ndk/$ndkDefaultVersion.*\" -Name | Select-Object -Last 1\n\n    return ($versions | ForEach-Object {\n        $defaultPostfix = ( $_ -eq $ndkDefaultFullVersion ) ? \" (default)\" : \"\"\n        $_ + $defaultPostfix\n    } | Join-String -Separator \"<br>\")\n}\n\nfunction Build-AndroidEnvironmentTable {\n    $androidVersions = Get-Item env:ANDROID_*\n\n    $shouldResolveLink = 'ANDROID_NDK', 'ANDROID_NDK_HOME', 'ANDROID_NDK_ROOT', 'ANDROID_NDK_LATEST_HOME'\n    return $androidVersions | Sort-Object -Property Name | ForEach-Object {\n        [PSCustomObject] @{\n            \"Name\"  = $_.Name\n            \"Value\" = if ($shouldResolveLink.Contains($_.Name )) { Get-PathWithLink($_.Value) } else {$_.Value}\n        }\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/docs-gen/SoftwareReport.Browsers.psm1",
    "content": "function Get-ChromeVersion {\n    $googleChromeVersion = google-chrome --version | Get-StringPart -Part 2\n    return $googleChromeVersion\n}\n\nfunction Get-ChromeDriverVersion {\n    $chromeDriverVersion = chromedriver --version | Get-StringPart -Part 1\n    return $chromeDriverVersion\n}\n\nfunction Get-FirefoxVersion {\n    $firefoxVersion = $(firefox --version) | Get-StringPart -Part 2\n    return $firefoxVersion\n}\n\nfunction Get-GeckodriverVersion {\n    $geckodriverVersion = geckodriver --version | Select-Object -First 1 | Get-StringPart -Part 1\n    return $geckodriverVersion\n}\n\nfunction Get-ChromiumVersion {\n    $chromiumVersion = chromium-browser --version | Get-StringPart -Part 1\n    return $chromiumVersion\n}\n\nfunction Get-EdgeVersion {\n    $edgeVersion = (microsoft-edge --version).Trim() | Get-StringPart -Part 2\n    return $edgeVersion\n}\n\nfunction Get-EdgeDriverVersion {\n    $edgeDriverVersion = msedgedriver --version | Get-StringPart -Part 3\n    return $edgeDriverVersion\n}\n\nfunction Get-SeleniumVersion {\n    $fullSeleniumVersion = (Get-ChildItem \"/usr/share/java/selenium-server-*\").Name -replace \"selenium-server-\"\n    return $fullSeleniumVersion\n}\n\nfunction Build-BrowserWebdriversEnvironmentTable {\n    return @(\n        [PSCustomObject] @{\n            \"Name\"  = \"CHROMEWEBDRIVER\"\n            \"Value\" = $env:CHROMEWEBDRIVER\n        },\n        [PSCustomObject] @{\n            \"Name\"  = \"EDGEWEBDRIVER\"\n            \"Value\" = $env:EDGEWEBDRIVER\n        },\n        [PSCustomObject] @{\n            \"Name\"  = \"GECKOWEBDRIVER\"\n            \"Value\" = $env:GECKOWEBDRIVER\n        },\n        [PSCustomObject] @{\n            \"Name\"  = \"SELENIUM_JAR_PATH\"\n            \"Value\" = $env:SELENIUM_JAR_PATH\n        }\n    )\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/docs-gen/SoftwareReport.CachedTools.psm1",
    "content": "function Get-ToolcacheRubyVersions {\n    $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY \"Ruby\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ }\n}\n\nfunction Get-ToolcachePythonVersions {\n    $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY \"Python\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ }\n}\n\nfunction Get-ToolcachePyPyVersions {\n    $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY \"PyPy\"\n    Get-ChildItem -Path $toolcachePath -Name | Sort-Object { [Version] $_ } | ForEach-Object {\n        $pypyRootPath = Join-Path $toolcachePath $_ \"x64\"\n        [string] $pypyVersionOutput = & \"$pypyRootPath/bin/python\" -c \"import sys;print(sys.version)\"\n        $pypyVersionOutput -match \"^([\\d\\.]+) \\(.+\\) \\[PyPy ([\\d\\.]+\\S*) .+]$\" | Out-Null\n        return \"{0} [PyPy {1}]\" -f $Matches[1], $Matches[2]\n    }\n}\n\nfunction Get-ToolcacheNodeVersions {\n    $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY \"node\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ }\n}\n\nfunction Get-ToolcacheGoVersions {\n    $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY \"go\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/docs-gen/SoftwareReport.Common.psm1",
    "content": "function Get-BashVersion {\n    $version = bash -c 'echo ${BASH_VERSION}'\n    return $version\n}\n\nfunction Get-DashVersion {\n    $version = dpkg-query -W -f '${Version}' dash\n    return $version\n}\n\nfunction Get-CPPVersions {\n    $result = Get-CommandResult \"apt list --installed\" -Multiline\n    $cppVersions = $result.Output | Where-Object { $_ -match \"g\\+\\+-\\d\\d\\/\" } | ForEach-Object {\n        & $_.Split(\"/\")[0] --version | Select-Object -First 1 | Get-StringPart -Part 3\n    } | Sort-Object {[Version] $_}\n    return $cppVersions\n}\n\nfunction Get-FortranVersions {\n    $result = Get-CommandResult \"apt list --installed\" -Multiline\n    $fortranVersions = $result.Output | Where-Object { $_ -match \"^gfortran-\\d\\d\\/\" } | ForEach-Object {\n        & $_.Split(\"/\")[0] --version | Select-Object -First 1 | Get-StringPart -Part 4\n    } | Sort-Object {[Version] $_}\n    return $fortranVersions\n}\n\nfunction Get-ClangToolVersions {\n    param (\n        [Parameter(Mandatory = $true)]\n        [string] $ToolName,\n        [string] $VersionLineMatcher = \"${ToolName} version\",\n        [string] $VersionPattern = \"\\d+\\.\\d+\\.\\d+)\"\n    )\n\n    $result = Get-CommandResult \"apt list --installed\" -Multiline\n    $toolVersions = $result.Output | Where-Object { $_ -match \"^${ToolName}-\\d+\" } | ForEach-Object {\n        $clangCommand = ($_ -Split \"/\")[0]\n        Invoke-Expression \"$clangCommand --version\" | Where-Object { $_ -match \"${VersionLineMatcher}\" } | ForEach-Object {\n            $_ -match \"${VersionLineMatcher} (?<version>${VersionPattern}\" | Out-Null\n            $Matches.version\n            }\n        } | Sort-Object {[Version] $_}\n\n    return $toolVersions\n}\n\n\nfunction Get-ClangTidyVersions {\n    $clangVersions = Get-ClangToolVersions -ToolName \"clang-tidy\" -VersionLineMatcher \"LLVM version\" -VersionPattern \"\\d+\\.\\d+\\.\\d+)\"\n    return $clangVersions\n}\n\nfunction Get-MonoVersion {\n    $monoVersion = mono --version | Out-String | Get-StringPart -Part 4\n    return $monoVersion\n}\n\nfunction Get-MsbuildVersion {\n    $msbuildVersion = msbuild -version | Select-Object -Last 1\n    $monoVersion = Get-MonoVersion\n    return \"$msbuildVersion (Mono $monoVersion)\"\n}\n\nfunction Get-NuGetVersion {\n    $nugetVersion = nuget help | Select-Object -First 1 | Get-StringPart -Part 2\n    return $nugetVersion\n}\n\nfunction Get-NodeVersion {\n    $nodeVersion = $(node --version).Substring(1)\n    return $nodeVersion\n}\n\nfunction Get-OpensslVersion {\n    $opensslVersion = $(dpkg-query -W -f '${Version}' openssl)\n    return $opensslVersion\n}\n\nfunction Get-PerlVersion {\n    $version = $(perl -e 'print substr($^V,1)')\n    return $version\n}\n\nfunction Get-PythonVersion {\n    $result = Get-CommandResult \"python --version\"\n    $version = $result.Output | Get-StringPart -Part 1\n    return $version\n}\n\nfunction Get-PowershellVersion {\n    $pwshVersion = $(pwsh --version) | Get-StringPart -Part 1\n    return $pwshVersion\n}\n\nfunction Get-RubyVersion {\n    $rubyVersion = ruby --version | Out-String | Get-StringPart -Part 1\n    return $rubyVersion\n}\n\nfunction Get-SwiftVersion {\n    $swiftVersion = swift --version | Out-String | Get-StringPart -Part 2\n    return $swiftVersion\n}\n\nfunction Get-KotlinVersion {\n    $kotlinVersion = kotlin -version | Out-String | Get-StringPart -Part 2\n    return $kotlinVersion\n}\n\nfunction Get-JuliaVersion {\n    $juliaVersion = julia --version | Get-StringPart -Part 2\n    return $juliaVersion\n}\n\nfunction Get-LernaVersion {\n    $version = lerna -v\n    return $version\n}\n\nfunction Get-HomebrewVersion {\n    $result = Get-CommandResult \"/home/linuxbrew/.linuxbrew/bin/brew --version\"\n    $result.Output -match \"Homebrew (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.version\n}\n\nfunction Get-CpanVersion {\n    $result = Get-CommandResult \"cpan --version\" -ExpectedExitCode @(25, 255)\n    $result.Output -match \"version (?<version>\\d+\\.\\d+) \" | Out-Null\n    return $Matches.version\n}\n\nfunction Get-GemVersion {\n    $result = Get-CommandResult \"gem --version\"\n    $result.Output -match \"(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.version\n}\n\nfunction Get-MinicondaVersion {\n    $condaVersion = conda --version | Get-StringPart -Part 1\n    return $condaVersion\n}\n\nfunction Get-HelmVersion {\n    $(helm version) -match 'Version:\"v(?<version>\\d+\\.\\d+\\.\\d+)\"' | Out-Null\n    return $Matches.version\n}\n\nfunction Get-NpmVersion {\n    $npmVersion = npm --version\n    return $npmVersion\n}\n\nfunction Get-YarnVersion {\n    $yarnVersion = yarn --version\n    return $yarnVersion\n}\n\nfunction Get-ParcelVersion {\n    $parcelVersion = parcel --version\n    return $parcelVersion\n}\n\nfunction Get-PipVersion {\n    $pipVersion = pip --version | Get-StringPart -Part 1\n    return $pipVersion\n}\n\nfunction Get-Pip3Version {\n    $pip3Version = pip3 --version | Get-StringPart -Part 1\n    return $pip3Version\n}\n\nfunction Get-VcpkgVersion {\n    $commitId = git -C \"/usr/local/share/vcpkg\" rev-parse --short HEAD\n    return \"(build from commit $commitId)\"\n}\n\nfunction Get-AntVersion {\n    $result = ant -version | Out-String\n    $result -match \"version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.version\n}\n\nfunction Get-GradleVersion {\n    $gradleVersion = (gradle -v) -match \"^Gradle \\d\" | Get-StringPart -Part 1\n    return $gradleVersion\n}\n\nfunction Get-MavenVersion {\n    $result = mvn -version | Out-String\n    $result -match \"Apache Maven (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.version\n}\n\nfunction Get-SbtVersion {\n    $result = Get-CommandResult \"sbt -version\"\n    $result.Output -match \"sbt runner version: (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.version\n}\n\nfunction Get-PHPVersions {\n    $result = Get-CommandResult \"apt list --installed\" -Multiline\n    return $result.Output | Where-Object { $_ -match \"^php\\d+\\.\\d+/\" } | ForEach-Object {\n        $_ -match \"now (\\d+:)?(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n        $Matches.version\n    }\n}\n\nfunction Get-ComposerVersion {\n    $composerVersion = (composer --version) -replace \" version\" | Get-StringPart -Part 1\n    return $composerVersion\n}\n\nfunction Get-PHPUnitVersion {\n    $(phpunit --version | Out-String) -match \"PHPUnit (?<version>\\d+\\.\\d+\\.\\d+)\\s\" | Out-Null\n    return $Matches.version\n}\n\nfunction Get-GHCVersion {\n    $(ghc --version) -match \"version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.version\n}\n\nfunction Get-GHCupVersion {\n    $(ghcup --version) -match \"version (?<version>\\d+(\\.\\d+){2,})\" | Out-Null\n    return $Matches.version\n}\n\nfunction Get-CabalVersion {\n    $(cabal --version | Out-String) -match \"cabal-install version (?<version>\\d+\\.\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.version\n}\n\nfunction Get-StackVersion {\n    $(stack --version | Out-String) -match \"Version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.version\n}\n\nfunction Get-PowerShellModules {\n    [Array] $result = @()\n\n    [Array] $azureInstalledModules = Get-ChildItem -Path \"/usr/share/az_*\" -Directory | ForEach-Object { $_.Name.Split(\"_\")[1] }\n    if ($azureInstalledModules.Count -gt 0) {\n        $result += [ToolVersionsListNode]::new(\"Az\", $azureInstalledModules, \"^\\d+\\.\\d+\", \"Inline\")\n    }\n\n    (Get-ToolsetContent).powershellModules.name | ForEach-Object {\n        $moduleName = $_\n        $moduleVersions = Get-Module -Name $moduleName -ListAvailable | Select-Object -ExpandProperty Version | Sort-Object -Unique\n        $result += [ToolVersionsListNode]::new($moduleName, $moduleVersions, \"^\\d+\", \"Inline\")\n    }\n\n    return $result\n}\n\nfunction Get-DotNetCoreSdkVersions {\n    $dotNetCoreSdkVersion = dotnet --list-sdks list | ForEach-Object { $_ | Get-StringPart -Part 0 }\n    return $dotNetCoreSdkVersion\n}\n\nfunction Get-DotnetTools {\n    $env:PATH = \"/etc/skel/.dotnet/tools:$($env:PATH)\"\n    $dotnetTools = (Get-ToolsetContent).dotnet.tools\n\n    return $dotnetTools | ForEach-Object {\n        $version = Invoke-Expression $_.getversion\n        return [ToolVersionNode]::new($_.name, $version)\n    }\n}\n\nfunction Get-CachedDockerImages {\n    $toolsetJson = Get-ToolsetContent\n    $images = $toolsetJson.docker.images\n    return $images\n}\n\nfunction Get-CachedDockerImagesTableData {\n    $allImages = sudo docker images --digests --format \"*{{.Repository}}:{{.Tag}}|{{.Digest}} |{{.CreatedAt}}\"\n    $allImages.Split(\"*\") | Where-Object { $_ } | ForEach-Object {\n        $parts = $_.Split(\"|\")\n        [PSCustomObject] @{\n            \"Repository:Tag\" = $parts[0]\n            \"Digest\"         = $parts[1]\n            \"Created\"        = $parts[2].split(' ')[0]\n        }\n    } | Sort-Object -Property \"Repository:Tag\"\n}\n\nfunction Get-AptPackages {\n    $apt = (Get-ToolsetContent).Apt\n    $output = @()\n    ForEach ($pkg in ($apt.vital_packages + $apt.common_packages + $apt.cmd_packages)) {\n        $version = $(dpkg-query -W -f '${Version}' $pkg)\n        if ($null -eq $version) {\n            $version = $(dpkg-query -W -f '${Version}' \"$pkg*\")\n        }\n\n        $version = $version -replace '~','\\~'\n\n        $output += [PSCustomObject] @{\n            Name    = $pkg\n            Version = $version\n        }\n    }\n    return ($output | Sort-Object Name)\n}\n\nfunction Get-PipxVersion {\n    $result = (Get-CommandResult \"pipx --version\").Output\n    $result -match \"(?<version>\\d+\\.\\d+\\.\\d+\\.?\\d*)\" | Out-Null\n    return $Matches.Version\n}\n\nfunction Build-PackageManagementEnvironmentTable {\n    return @(\n        [PSCustomObject] @{\n            \"Name\"  = \"CONDA\"\n            \"Value\" = $env:CONDA\n        },\n        [PSCustomObject] @{\n            \"Name\"  = \"VCPKG_INSTALLATION_ROOT\"\n            \"Value\" = $env:VCPKG_INSTALLATION_ROOT\n        }\n    )\n}\n\nfunction Get-SystemdVersion {\n    $matchCollection = [regex]::Matches((systemctl --version | head -n 1), \"\\((.*?)\\)\")\n    $result = foreach ($match in $matchCollection) {$match.Groups[1].Value}\n    return $result\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/docs-gen/SoftwareReport.Databases.psm1",
    "content": "function Get-PostgreSqlVersion {\n    $postgreSQLVersion = psql --version | Get-StringPart -Part 2\n    return $postgreSQLVersion\n}\n\nfunction Get-SqliteVersion {\n    $sqliteVersion = sqlite3 --version | Get-StringPart -Part 0\n    return $sqliteVersion\n}\n\nfunction Get-MySQLVersion {\n    $mySQLVersion = mysqld --version | Get-StringPart -Part 2\n    return $mySQLVersion\n}\n\nfunction Get-SQLCmdVersion {\n    $sqlcmdVersion = sqlcmd -? | Select-String -Pattern \"Version\" | Get-StringPart -Part 1\n    return $sqlcmdVersion\n}\n\nfunction Get-SqlPackageVersion {\n    $sqlPackageVersion = sqlpackage /version\n    return $sqlPackageVersion\n}\n\nfunction Build-PostgreSqlSection {\n    $node = [HeaderNode]::new(\"PostgreSQL\")\n    $node.AddToolVersion(\"PostgreSQL\", $(Get-PostgreSqlVersion))\n    $node.AddNote(@(\n        \"User: postgres\",\n        \"PostgreSQL service is disabled by default.\",\n        \"Use the following command as a part of your job to start the service: 'sudo systemctl start postgresql.service'\"\n    ) -join \"`n\")\n\n    return $node\n}\n\nfunction Build-MySQLSection {\n    $node = [HeaderNode]::new(\"MySQL\")\n    $node.AddToolVersion(\"MySQL\", $(Get-MySQLVersion))\n    $node.AddNote(@(\n        \"User: root\",\n        \"Password: root\",\n        \"MySQL service is disabled by default.\",\n        \"Use the following command as a part of your job to start the service: 'sudo systemctl start mysql.service'\"\n    ) -join \"`n\")\n\n    return $node\n}\n\nfunction Build-MSSQLToolsSection {\n    $node = [HeaderNode]::new(\"MS SQL\")\n    $node.AddToolVersion(\"sqlcmd\", $(Get-SQLCmdVersion))\n    $node.AddToolVersion(\"SqlPackage\", $(Get-SqlPackageVersion))\n    return $node\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/docs-gen/SoftwareReport.Helpers.psm1",
    "content": "function Get-StringPart {\n    param (\n        [Parameter(ValueFromPipeline)]\n        [string] $ToolOutput,\n        [string] $Delimiter = \" \",\n        [int[]] $Part\n    )\n\n    $parts = $ToolOutput.Split($Delimiter, [System.StringSplitOptions]::RemoveEmptyEntries)\n    $selectedParts = $parts[$Part]\n    return [string]::Join($Delimiter, $selectedParts)\n}\n\nfunction Get-PathWithLink {\n    param (\n        [string] $InputPath\n    )\n\n    $link = Get-Item $InputPath | Select-Object -ExpandProperty Target\n    if (-not [string]::IsNullOrEmpty($link)) {\n      return \"${InputPath} -> ${link}\"\n    }\n    return \"${InputPath}\"\n}\n\nfunction Get-OSVersionShort {\n    $(Get-OSVersionFull) | Get-StringPart -Delimiter '.' -Part 0,1\n}\n\nfunction Get-OSVersionFull {\n    lsb_release -ds | Get-StringPart -Part 1, 2\n}\n\nfunction Get-KernelVersion {\n    $kernelVersion = uname -r\n    return $kernelVersion\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/docs-gen/SoftwareReport.Java.psm1",
    "content": "function Get-JavaVersionsTable {\n    $javaToolcacheVersions = Get-ChildItem $env:AGENT_TOOLSDIRECTORY/Java*/* -Directory | Sort-Object { [int] $_.Name.Split(\".\")[0] }\n\n    return $javaToolcacheVersions | ForEach-Object {\n        $majorVersion = $_.Name.split(\".\")[0]\n        $fullVersion = $_.Name.Replace(\"-\", \"+\")\n        $defaultJavaPath = $env:JAVA_HOME\n        $javaPath = Get-Item env:JAVA_HOME_${majorVersion}_X64\n\n        $defaultPostfix = ($javaPath.Value -eq $defaultJavaPath) ? \" (default)\" : \"\"\n\n        [PSCustomObject] @{\n            \"Version\"              = $fullVersion + $defaultPostfix\n            \"Environment Variable\" = $javaPath.Name\n        }\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/docs-gen/SoftwareReport.Rust.psm1",
    "content": "function Initialize-RustEnvironment {\n    $env:PATH = \"/etc/skel/.cargo/bin:/etc/skel/.rustup/bin:$($env:PATH)\"\n    $env:RUSTUP_HOME = \"/etc/skel/.rustup\"\n    $env:CARGO_HOME = \"/etc/skel/.cargo\"\n}\n\nfunction Get-RustVersion {\n    $rustVersion = $(rustc --version) | Get-StringPart -Part 1\n    return $rustVersion\n}\n\nfunction Get-BindgenVersion {\n    $bindgenVersion = $(bindgen --version) | Get-StringPart -Part 1\n    return $bindgenVersion\n}\n\nfunction Get-CargoVersion {\n    $cargoVersion = $(cargo --version) | Get-StringPart -Part 1\n    return $cargoVersion\n}\n\nfunction Get-CargoAuditVersion {\n    $cargoAuditVersion = $(cargo-audit --version) | Get-StringPart -Part 1\n    return $cargoAuditVersion\n}\n\nfunction Get-CargoOutdatedVersion {\n    $cargoOutdatedVersion = cargo outdated --version | Get-StringPart -Part 1\n    return $cargoOutdatedVersion\n}\n\nfunction Get-CargoClippyVersion {\n    $cargoClippyVersion = $(cargo-clippy --version) | Get-StringPart -Part 1\n    return $cargoClippyVersion\n}\n\nfunction Get-CbindgenVersion {\n    $cbindgenVersion = $(cbindgen --version) | Get-StringPart -Part 1\n    return $cbindgenVersion\n}\n\nfunction Get-RustupVersion {\n    $rustupVersion = $(rustup --version) | Get-StringPart -Part 1\n    return $rustupVersion\n}\n\nfunction Get-RustdocVersion {\n    $rustdocVersion = $(rustdoc --version) | Get-StringPart -Part 1\n    return $rustdocVersion\n}\n\nfunction Get-RustfmtVersion {\n    $rustfmtVersion = $(rustfmt --version) | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter \"-\"\n    return $rustfmtVersion\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/docs-gen/SoftwareReport.Tools.psm1",
    "content": "function Get-AnsibleVersion {\n    $ansibleVersion = (ansible --version)[0] -replace \"[^\\d.]\"\n    return $ansibleVersion\n}\n\nfunction Get-AptFastVersion {\n    $versionFileContent = Get-Content (which apt-fast) -Raw\n    $match = [Regex]::Match($versionFileContent, '# apt-fast v(.+)\\n')\n    return $match.Groups[1].Value\n}\n\nfunction Get-AzCopyVersion {\n    $azcopyVersion = [string]$(azcopy --version) | Get-StringPart -Part 2\n    return \"$azcopyVersion - available by ``azcopy`` and ``azcopy10`` aliases\"\n}\n\nfunction Get-BazelVersion {\n    $bazelVersion = bazel --version | Select-String \"bazel\" | Get-StringPart -Part 1\n    return $bazelVersion\n}\n\nfunction Get-BazeliskVersion {\n    $result = Get-CommandResult \"bazelisk version\" -Multiline\n    $bazeliskVersion = $result.Output | Select-String \"Bazelisk version:\" | Get-StringPart -Part 2 | Get-StringPart -Part 0 -Delimiter \"v\"\n    return $bazeliskVersion\n}\n\nfunction Get-BicepVersion {\n    (bicep --version | Out-String) -match  \"bicep cli version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.Version\n}\n\nfunction Get-CodeQLBundleVersion {\n    $CodeQLVersionsWildcard = Join-Path $Env:AGENT_TOOLSDIRECTORY -ChildPath \"CodeQL\" | Join-Path -ChildPath \"*\"\n    $CodeQLVersionPath = Get-ChildItem $CodeQLVersionsWildcard | Select-Object -First 1 -Expand FullName\n    $CodeQLPath = Join-Path $CodeQLVersionPath -ChildPath \"x64\" | Join-Path -ChildPath \"codeql\" | Join-Path -ChildPath \"codeql\"\n    $CodeQLVersion = & $CodeQLPath version --quiet\n    return $CodeQLVersion\n}\n\nfunction Get-PodManVersion {\n    $podmanVersion = podman --version | Get-StringPart -Part 2\n    return $podmanVersion\n}\n\nfunction Get-BuildahVersion {\n    $buildahVersion = buildah --version | Get-StringPart -Part 2\n    return $buildahVersion\n}\n\nfunction Get-SkopeoVersion {\n    $skopeoVersion = skopeo --version | Get-StringPart -Part 2\n    return $skopeoVersion\n}\n\nfunction Get-CMakeVersion {\n    $cmakeVersion = cmake --version | Select-Object -First 1 | Get-StringPart -Part 2\n    return $cmakeVersion\n}\n\nfunction Get-DockerComposeV2Version {\n    $composeVersion = docker compose version | Get-StringPart -Part 3 | Get-StringPart -Part 0 -Delimiter \"v\"\n    return $composeVersion\n}\n\nfunction Get-DockerClientVersion {\n    $dockerClientVersion = sudo docker version --format '{{.Client.Version}}'\n    return $dockerClientVersion\n}\n\nfunction Get-DockerServerVersion {\n    $dockerServerVersion = sudo docker version --format '{{.Server.Version}}'\n    return $dockerServerVersion\n}\n\nfunction Get-DockerBuildxVersion {\n    $buildxVersion = docker buildx version  | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter \"v\"\n    return $buildxVersion\n}\n\nfunction Get-DockerAmazonECRCredHelperVersion {\n    $ecrVersion = docker-credential-ecr-login -v | Select-String \"Version:\" | Get-StringPart -Part 1\n    return $ecrVersion\n}\n\nfunction Get-GitVersion {\n    $gitVersion = git --version | Get-StringPart -Part -1\n    return $gitVersion\n}\n\nfunction Get-GitLFSVersion {\n    $result = Get-CommandResult \"git-lfs --version\"\n    $gitlfsversion = $result.Output | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter \"/\"\n    return $gitlfsversion\n}\n\nfunction Get-GitFTPVersion {\n    $gitftpVersion = git-ftp --version | Get-StringPart -Part 2\n    return $gitftpVersion\n}\n\nfunction Get-GoogleCloudCLIVersion {\n    return (gcloud --version | Select-Object -First 1) | Get-StringPart -Part 3\n}\n\nfunction Get-HavegedVersion {\n    $havegedVersion = dpkg-query --showformat='${Version}' --show haveged | Get-StringPart -Part 0 -Delimiter \"-\"\n    return $havegedVersion\n}\n\nfunction Get-HerokuVersion {\n    $herokuVersion = heroku version | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter \"/\"\n    return $herokuVersion\n}\n\nfunction Get-SVNVersion {\n    $svnVersion = svn --version | Select-Object -First 1 | Get-StringPart -Part 2\n    return $svnVersion\n}\n\nfunction Get-KustomizeVersion {\n    $kustomizeVersion = kustomize version --short | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter \"v\"\n    return $kustomizeVersion\n}\n\nfunction Get-KindVersion {\n    $kindVersion = kind version | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter \"v\"\n    return $kindVersion\n}\n\nfunction Get-KubectlVersion {\n    $kubectlVersion = (kubectl version --client --output=json | ConvertFrom-Json).clientVersion.gitVersion.Replace('v','')\n    return $kubectlVersion\n}\n\nfunction Get-MinikubeVersion {\n    $minikubeVersion = minikube version --short | Get-StringPart -Part 0 -Delimiter \"v\"\n    return $minikubeVersion\n}\n\nfunction Get-HGVersion {\n    $hgVersion = hg --version | Select-Object -First 1 | Get-StringPart -Part -1 | Get-StringPart -Part 0 -Delimiter \")\"\n    return $hgVersion\n}\n\nfunction Get-LeiningenVersion {\n    return \"$(lein -v | Get-StringPart -Part 1)\"\n}\n\nfunction Get-MediainfoVersion {\n    $mediainfoVersion = (mediainfo --version | Select-Object -Index 1 | Get-StringPart -Part 2).Replace('v', '')\n    return $mediainfoVersion\n}\n\nfunction Get-NewmanVersion {\n    return $(newman --version)\n}\n\nfunction Get-NVersion {\n    $nVersion = (n --version).Replace('v', '')\n    return $nVersion\n}\n\nfunction Get-NvmVersion {\n    $nvmVersion = bash -c \"source /etc/skel/.nvm/nvm.sh && nvm --version\"\n    return $nvmVersion\n}\n\nfunction Get-PackerVersion {\n    $packerVersion = (packer --version | Select-String \"^Packer\").Line.Replace('v','') | Get-StringPart -Part 1\n    return $packerVersion\n}\n\nfunction Get-TerraformVersion {\n    return (terraform version | Select-String \"^Terraform\").Line.Replace('v','') | Get-StringPart -Part 1\n}\n\nfunction Get-JqVersion {\n    $jqVersion = jq --version | Get-StringPart -Part 1 -Delimiter \"-\"\n    return $jqVersion\n}\n\nfunction Get-AzureCliVersion {\n    $azcliVersion = (az version | ConvertFrom-Json).'azure-cli'\n    return $azcliVersion\n}\n\nfunction Get-AzureDevopsVersion {\n    $azdevopsVersion = (az version | ConvertFrom-Json).extensions.'azure-devops'\n    return $azdevopsVersion\n}\n\nfunction Get-AlibabaCloudCliVersion {\n    return $(aliyun version)\n}\n\nfunction Get-AWSCliVersion {\n    $result = Get-CommandResult \"aws --version\"\n    $awsVersion = $result.Output | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter \"/\"\n    return $awsVersion\n}\n\nfunction Get-AWSCliSessionManagerPluginVersion {\n    $result = (Get-CommandResult \"session-manager-plugin --version\").Output\n    return $result\n}\n\nfunction Get-AWSSAMVersion {\n    return $(sam --version | Get-StringPart -Part -1)\n}\n\nfunction Get-FastlaneVersion {\n    $fastlaneVersion = fastlane --version | Select-String \"^fastlane [0-9]\" | Get-StringPart -Part 1\n    return $fastlaneVersion\n}\n\nfunction Get-GitHubCliVersion {\n    $ghVersion = gh --version | Select-String \"gh version\" | Get-StringPart -Part 2\n    return $ghVersion\n}\n\nfunction Get-NetlifyCliVersion {\n    $netlifyVersion = netlify --version | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter \"/\"\n    return $netlifyVersion\n}\n\nfunction Get-OCCliVersion {\n    $ocVersion = oc version  -o=json | jq -r '.releaseClientVersion'\n    return $ocVersion\n}\n\nfunction Get-ORASCliVersion {\n    $orasVersion = oras version | Select-String \"^Version:\" | Get-StringPart -Part 1\n    return $orasVersion\n}\n\nfunction Get-VerselCliversion {\n    $result = Get-CommandResult \"vercel --version\" -Multiline\n    return $result.Output | Select-Object -Skip 1 -First 1\n}\n\nfunction Get-PulumiVersion {\n    $pulumiVersion = pulumi version | Get-StringPart -Part 0 -Delimiter \"v\"\n    return $pulumiVersion\n}\n\nfunction Get-RVersion {\n    $rVersion = (Get-CommandResult \"R --version | grep 'R version'\").Output |  Get-StringPart -Part 2\n    return $rVersion\n}\n\nfunction Get-SphinxVersion {\n    $sphinxVersion = searchd -h | Select-Object -First 1 | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter \"-\"\n    return $sphinxVersion\n}\n\nfunction Get-YamllintVersion {\n    return $(yamllint --version) | Get-StringPart -Part 1\n}\n\nfunction Get-ZstdVersion {\n    $zstdVersion = zstd --version | Get-StringPart -Part 1 -Delimiter \"v\" | Get-StringPart -Part 0 -Delimiter \",\"\n    return \"$zstdVersion\"\n}\n\nfunction Get-NinjaVersion {\n    return $(ninja --version)\n}\n\nfunction Get-YqVersion {\n    $yqVersion = $(yq -V) | Get-StringPart -Part 3\n    return $yqVersion.TrimStart(\"v\").Trim()\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/docs-gen/SoftwareReport.WebServers.psm1",
    "content": "function Get-ApacheVersion {\n    $name = \"apache2\"\n    $port = 80\n    $version = bash -c \"apache2 -v | grep -Po 'Apache/(\\d+.){2}\\d+'\" | Get-StringPart -Part 1 -Delimiter \"/\"\n    $serviceStatus = systemctl status apache2 | grep \"Active:\" | Get-StringPart -Part 1\n    $configFile = \"/etc/apache2/apache2.conf\"\n    return [PsCustomObject]@{\n        \"Name\"          = $name\n        \"Version\"       = $version\n        \"ConfigFile\"    = $configFile\n        \"ServiceStatus\" = $serviceStatus\n        \"ListenPort\"    = $port\n    }\n}\n\nfunction Get-NginxVersion {\n    $name = \"nginx\"\n    $port = 80\n    $version = (dpkg-query --showformat='${Version}' --show nginx).Split('-')[0]\n    $serviceStatus = systemctl status nginx | grep \"Active:\" | Get-StringPart -Part 1\n    $configFile = \"/etc/nginx/nginx.conf\"\n    return [PsCustomObject]@{\n        \"Name\"          = $name\n        \"Version\"       = $version\n        \"ConfigFile\"    = $configFile\n        \"ServiceStatus\" = $serviceStatus\n        \"ListenPort\"    = $port\n    }\n}\n\nfunction Build-WebServersTable {\n    $servers = @()\n    $servers += (Get-ApacheVersion)\n    $servers += (Get-NginxVersion)\n\n    return $servers\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/helpers/Common.Helpers.psm1",
    "content": "function Get-CommandResult {\n    <#\n    .SYNOPSIS\n        Runs a command in bash and returns the output and exit code.\n\n    .DESCRIPTION\n        Function runs a provided command in bash and returns the output and exit code as hashtable.\n\n    .PARAMETER Command\n        The command to run.\n\n    .PARAMETER ExpectedExitCode\n        The expected exit code. If the actual exit code does not match, an exception is thrown.\n\n    .PARAMETER Multiline\n        If true, the output is returned as an array of strings. Otherwise, the output is returned as a single string.\n\n    .PARAMETER ValidateExitCode\n        If true, the actual exit code is compared to the expected exit code.\n\n    .EXAMPLE\n        $result = Get-CommandResult \"ls -la\"\n\n        This command runs \"ls -la\" in bash and returns the output and exit code as hashtable.\n\n    #>\n    param(\n        [Parameter(Mandatory=$true)]\n        [string] $Command,\n        [int[]] $ExpectedExitCode = 0,\n        [switch] $Multiline,\n        [bool] $ValidateExitCode = $true\n    )\n\n    # Bash trick to suppress and show error output because some commands write to stderr (for example, \"python --version\")\n    $stdout = & bash -c \"$Command 2>&1\"\n    $exitCode = $LASTEXITCODE\n\n    if ($ValidateExitCode) {\n        if ($ExpectedExitCode -notcontains $exitCode) {\n            try {\n                throw \"StdOut: '$stdout' ExitCode: '$exitCode'\"\n            } catch {\n                Write-Host $_.Exception.Message\n                Write-Host $_.ScriptStackTrace\n                exit $LASTEXITCODE\n            }\n        }\n    }\n\n    return @{\n        Output = If ($Multiline -eq $true) { $stdout } else { [string] $stdout }\n        ExitCode = $exitCode\n    }\n}\n\nfunction Test-IsUbuntu22 {\n    return (lsb_release -rs) -eq \"22.04\"\n}\n\nfunction Test-IsUbuntu24 {\n    return (lsb_release -rs) -eq \"24.04\"\n}\n\nfunction Get-ToolsetContent {\n    <#\n    .SYNOPSIS\n        Retrieves the content of the toolset.json file.\n\n    .DESCRIPTION\n        This function reads the toolset.json in path provided by INSTALLER_SCRIPT_FOLDER\n        environment variable and returns the content as a PowerShell object.\n    #>\n\n    $toolsetPath = Join-Path $env:INSTALLER_SCRIPT_FOLDER \"toolset.json\"\n    $toolsetJson = Get-Content -Path $toolsetPath -Raw\n    ConvertFrom-Json -InputObject $toolsetJson\n}\n\nfunction Invoke-DownloadWithRetry {\n    <#\n    .SYNOPSIS\n        Downloads a file from a given URL with retry functionality.\n\n    .DESCRIPTION\n        The Invoke-DownloadWithRetry function downloads a file from the specified URL\n        to the specified path. It includes retry functionality in case the download fails.\n\n    .PARAMETER Url\n        The URL of the file to download.\n\n    .PARAMETER Path\n        The path where the downloaded file will be saved. If not provided, a temporary path\n        will be used.\n\n    .EXAMPLE\n        Invoke-DownloadWithRetry -Url \"https://example.com/file.zip\" -Path \"/usr/local/bin\"\n        Downloads the file from the specified URL and saves it to the specified path.\n\n    .EXAMPLE\n        Invoke-DownloadWithRetry -Url \"https://example.com/file.zip\"\n        Downloads the file from the specified URL and saves it to a temporary path.\n\n    .OUTPUTS\n        The path where the downloaded file is saved.\n    #>\n    param(\n        [Parameter(Mandatory)]\n        [string] $Url,\n        [Alias(\"Destination\")]\n        [string] $DestinationPath\n    )\n\n    if (-not $DestinationPath) {\n        $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''\n        $re = \"[{0}]\" -f [RegEx]::Escape($invalidChars)\n        $fileName = [IO.Path]::GetFileName($Url) -replace $re\n\n        if ([String]::IsNullOrEmpty($fileName)) {\n            $fileName = [System.IO.Path]::GetRandomFileName()\n        }\n        $DestinationPath = Join-Path -Path \"/tmp\" -ChildPath $fileName\n    }\n\n    Write-Host \"Downloading package from $Url to $DestinationPath...\"\n\n    $interval = 30\n    $downloadStartTime = Get-Date\n    for ($retries = 20; $retries -gt 0; $retries--) {\n        try {\n            $attemptStartTime = Get-Date\n            Invoke-WebRequest -Uri $Url -Outfile $DestinationPath\n            $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)\n            Write-Host \"Package downloaded in $attemptSeconds seconds\"\n            break\n        } catch {\n            $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)\n            Write-Warning \"Package download failed in $attemptSeconds seconds\"\n            Write-Warning $_.Exception.Message\n        }\n\n        if ($retries -eq 0) {\n            $totalSeconds = [math]::Round(($(Get-Date) - $downloadStartTime).TotalSeconds, 2)\n            throw \"Package download failed after $totalSeconds seconds\"\n        }\n\n        Write-Warning \"Waiting $interval seconds before retrying (retries left: $retries)...\"\n        Start-Sleep -Seconds $interval\n    }\n\n    return $DestinationPath\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/helpers/etc-environment.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  etc-environment.sh\n##  Desc:  Helper functions for source and modify /etc/environment\n################################################################################\n\n# NB: sed expression use '%' as a delimiter in order to simplify handling\n#     values containing slashes (i.e. directory path)\n#     The values containing '%' will break the functions\n\nget_etc_environment_variable() {\n    local variable_name=$1\n\n    # remove `variable_name=` and possible quotes from the line\n    grep \"^${variable_name}=\" /etc/environment | sed -E \"s%^${variable_name}=\\\"?([^\\\"]+)\\\"?.*$%\\1%\"\n}\n\nadd_etc_environment_variable() {\n    local variable_name=$1\n    local variable_value=$2\n\n    echo \"${variable_name}=${variable_value}\" | sudo tee -a /etc/environment\n}\n\nreplace_etc_environment_variable() {\n    local variable_name=$1\n    local variable_value=$2\n\n    # modify /etc/environment in place by replacing a string that begins with variable_name\n    sudo sed -i -e \"s%^${variable_name}=.*$%${variable_name}=${variable_value}%\" /etc/environment\n}\n\nset_etc_environment_variable() {\n    local variable_name=$1\n    local variable_value=$2\n\n    if grep \"^${variable_name}=\" /etc/environment > /dev/null; then\n        replace_etc_environment_variable $variable_name $variable_value\n    else\n        add_etc_environment_variable $variable_name $variable_value\n    fi\n}\n\nprepend_etc_environment_variable() {\n    local variable_name=$1\n    local element=$2\n\n    # TODO: handle the case if the variable does not exist\n    existing_value=$(get_etc_environment_variable \"${variable_name}\")\n    set_etc_environment_variable \"${variable_name}\" \"${element}:${existing_value}\"\n}\n\nappend_etc_environment_variable() {\n    local variable_name=$1\n    local element=$2\n\n    # TODO: handle the case if the variable does not exist\n    existing_value=$(get_etc_environment_variable \"${variable_name}\")\n    set_etc_environment_variable \"${variable_name}\" \"${existing_value}:${element}\"\n}\n\nprepend_etc_environment_path() {\n    local element=$1\n\n    prepend_etc_environment_variable PATH \"${element}\"\n}\n\nappend_etc_environment_path() {\n    local element=$1\n\n    append_etc_environment_variable PATH \"${element}\"\n}\n\n# Process /etc/environment as if it were shell script with `export VAR=...` expressions\n#    The PATH variable is handled specially in order to do not override the existing PATH\n#    variable. The value of PATH variable read from /etc/environment is added to the end\n#    of value of the exiting PATH variable exactly as it would happen with real PAM app read\n#    /etc/environment\n#\n# TODO: there might be the others variables to be processed in the same way as \"PATH\" variable\n#       ie MANPATH, INFOPATH, LD_*, etc. In the current implementation the values from /etc/environment\n#       replace the values of the current environment\nreload_etc_environment() {\n    # add `export ` to every variable of /etc/environment except PATH and eval the result shell script\n    eval $(grep -v '^PATH=' /etc/environment | sed -e 's%^%export %')\n    # handle PATH specially\n    etc_path=$(get_etc_environment_variable PATH)\n    export PATH=\"$PATH:$etc_path\"\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/helpers/install.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install.sh\n##  Desc:  Helper functions for installing tools\n################################################################################\n\ndownload_with_retry() {\n    local url=$1\n    local download_path=$2\n\n    if [ -z \"$download_path\" ]; then\n        download_path=\"/tmp/$(basename \"$url\")\"\n    fi\n\n    echo \"Downloading package from $url to $download_path...\" >&2\n\n    interval=30\n    download_start_time=$(date +%s)\n\n    for ((retries=20; retries>0; retries--)); do\n        attempt_start_time=$(date +%s)\n        if http_code=$(curl -4sSLo \"$download_path\" \"$url\" -w '%{http_code}'); then\n            attempt_seconds=$(($(date +%s) - attempt_start_time))\n            if [ \"$http_code\" -eq 200 ]; then\n                echo \"Package downloaded in $attempt_seconds seconds\" >&2\n                break\n            else\n                echo \"Received HTTP status code $http_code after $attempt_seconds seconds\" >&2\n            fi\n        else\n            attempt_seconds=$(($(date +%s) - attempt_start_time))\n            echo \"Package download failed in $attempt_seconds seconds\" >&2\n        fi\n\n        if [ \"$retries\" -le 1 ]; then\n            total_seconds=$(($(date +%s) - download_start_time))\n            echo \"Package download failed after $total_seconds seconds\" >&2\n            exit 1\n        fi\n\n        echo \"Waiting $interval seconds before retrying (retries left: $retries)...\" >&2\n        sleep $interval\n    done\n\n    echo \"$download_path\"\n}\n\nget_toolset_value() {\n    local toolset_path=\"${INSTALLER_SCRIPT_FOLDER}/toolset.json\"\n    local query=$1\n\n    echo \"$(jq -r \"$query\" $toolset_path)\"\n}\n\nget_github_releases_by_version() {\n    local repo=$1\n    local version=${2:-\".+\"}\n    local allow_pre_release=${3:-false}\n    local with_assets_only=${4:-false}\n\n    page_size=\"100\"\n\n    json=$(curl -fsSL \"https://api.github.com/repos/${repo}/releases?per_page=${page_size}\")\n\n    if [[ -z \"$json\" ]]; then\n        echo \"Failed to get releases\" >&2\n        exit 1\n    fi\n\n    if [[ $with_assets_only == \"true\" ]]; then\n        json=$(echo $json | jq -r '.[] | select(.assets | length > 0)')\n    else\n        json=$(echo $json | jq -r '.[]')\n    fi\n\n    if [[ $allow_pre_release == \"true\" ]]; then\n        json=$(echo $json | jq -r '.')\n    else\n        json=$(echo $json | jq -r '. | select(.prerelease==false)')\n    fi\n\n    # Filter out rc/beta/etc releases, convert to numeric version and sort\n    json=$(echo $json | jq '. | select(.tag_name | test(\".*-[a-z]|beta\") | not)' | jq '.tag_name |= gsub(\"[^\\\\d.]\"; \"\")' | jq -s 'sort_by(.tag_name | split(\".\") | map(tonumber))')\n\n    # Select releases matching version\n    if [[ $version == \"latest\" ]]; then\n        json_filtered=$(echo $json | jq .[-1])\n    elif [[ $version == *\"+\"* ]] || [[ $version == *\"*\"* ]]; then\n        json_filtered=$(echo $json | jq --arg version $version '.[] | select(.tag_name | test($version))')\n    else\n        json_filtered=$(echo $json | jq --arg version $version '.[] | select(.tag_name | contains($version))')\n    fi\n\n    if [[ -z \"$json_filtered\" ]]; then\n        echo \"Failed to get releases from ${repo} matching version ${version}\" >&2\n        echo \"Available versions: $(echo \"$json\" | jq -r '.tag_name')\" >&2\n        exit 1\n    fi\n\n    echo $json_filtered\n}\n\nresolve_github_release_asset_url() {\n    local repo=$1\n    local url_filter=$2\n    local version=${3:-\".+\"}\n    local allow_pre_release=${4:-false}\n    local allow_multiple_matches=${5:-false}\n\n    matching_releases=$(get_github_releases_by_version \"${repo}\" \"${version}\" \"${allow_pre_release}\" \"true\")\n    matched_url=$(echo $matching_releases | jq -r \".assets[].browser_download_url | select(${url_filter})\")\n\n    if [[ -z \"$matched_url\" ]]; then\n        echo \"Found no download urls matching pattern: ${url_filter}\" >&2\n        echo \"Available download urls: $(echo \"$matching_releases\" | jq -r '.assets[].browser_download_url')\" >&2\n        exit 1\n    fi\n\n    if [[ \"$(echo \"$matched_url\" | wc -l)\" -gt 1 ]]; then\n        if [[ $allow_multiple_matches == \"true\" ]]; then\n            matched_url=$(echo \"$matched_url\" | tail -n 1)\n        else\n            echo \"Multiple matches found for ${version} version and ${url_filter} URL filter. Please make filters more specific\" >&2\n            exit 1\n        fi\n    fi\n\n    echo $matched_url\n}\n\nget_checksum_from_github_release() {\n    local repo=$1\n    local file_name=$2\n    local version=${3:-\".+\"}\n    local hash_type=$4\n    local allow_pre_release=${5:-false}\n\n    if [[ -z \"$file_name\" ]]; then\n        echo \"File name is not specified.\" >&2\n        exit 1\n    fi\n\n    if [[ \"$hash_type\" == \"SHA256\" ]]; then\n        hash_pattern=\"[A-Fa-f0-9]{64}\"\n    elif [[ \"$hash_type\" == \"SHA512\" ]]; then\n        hash_pattern=\"[A-Fa-f0-9]{128}\"\n    else\n        echo \"Unknown hash type: ${hash_type}\" >&2\n        exit 1\n    fi\n\n    matching_releases=$(get_github_releases_by_version \"${repo}\" \"${version}\" \"${allow_pre_release}\" \"true\")\n    matched_line=$(printf \"$(echo $matching_releases | jq '.body')\\n\" | grep \"$file_name\")\n\n    if [[ -z \"$matched_line\" ]]; then\n        echo \"File name ${file_name} not found in release body\" >&2\n        exit 1\n    fi\n\n    if [[ \"$(echo \"$matched_line\" | wc -l)\" -gt 1 ]]; then\n        echo \"Multiple matches found for ${file_name} in release body: ${matched_line}\" >&2\n        exit 1\n    fi\n\n    hash=$(echo $matched_line | grep -oP \"$hash_pattern\")\n\n    if [[ -z \"$hash\" ]]; then\n        echo \"Found ${file_name} in body of release, but failed to get hash from it: ${matched_line}\" >&2\n        exit 1\n    fi\n\n    echo \"$hash\"\n}\n\nget_checksum_from_url() {\n    local url=$1\n    local file_name=$2\n    local hash_type=$3\n    local use_custom_search_pattern=${4:-false}\n    local delimiter=${5:-' '}\n    local word_number=${6:-1}\n\n    if [[ \"$hash_type\" == \"SHA256\" ]]; then\n        hash_pattern=\"[A-Fa-f0-9]{64}\"\n    elif [[ \"$hash_type\" == \"SHA512\" ]]; then\n        hash_pattern=\"[A-Fa-f0-9]{128}\"\n    else\n        echo \"Unknown hash type: ${hash_type}\" >&2\n        exit 1\n    fi\n\n    checksums_file_path=$(download_with_retry \"$url\")\n    checksums=$(cat \"$checksums_file_path\")\n    rm \"$checksums_file_path\"\n\n    matched_line=$(printf \"$checksums\\n\" | grep \"$file_name\")\n\n    if [[ \"$(echo \"$matched_line\" | wc -l)\" -gt 1 ]]; then\n        echo \"Found multiple lines matching file name ${file_name} in checksum file.\" >&2\n        exit 1\n    fi\n\n    if [[ -z \"$matched_line\" ]]; then\n        echo \"File name ${file_name} not found in checksum file.\" >&2\n        exit 1\n    fi\n\n    if [[ $use_custom_search_pattern == \"true\" ]]; then\n        hash=$(echo \"$matched_line\" | sed 's/  */ /g' | cut -d \"$delimiter\" -f \"$word_number\" | tr -d -c '[:alnum:]')\n    else\n        hash=$(echo $matched_line | grep -oP \"$hash_pattern\")\n    fi\n\n    if [[ -z \"$hash\" ]]; then\n        echo \"Found ${file_name} in checksum file, but failed to get hash from it: ${matched_line}\" >&2\n        exit 1\n    fi\n\n    echo \"$hash\"\n}\n\nuse_checksum_comparison() {\n    local file_path=$1\n    local checksum=$2\n    local sha_type=${3:-\"256\"}\n\n    echo \"Performing checksum verification\"\n\n    if [[ ! -f \"$file_path\" ]]; then\n        echo \"File not found: $file_path\"\n        exit 1\n    fi\n\n    local_file_hash=$(shasum --algorithm \"$sha_type\" \"$file_path\" | awk '{print $1}')\n\n    if [[ \"$local_file_hash\" != \"$checksum\" ]]; then\n        echo \"Checksum verification failed. Expected hash: $checksum; Actual hash: $local_file_hash.\"\n        exit 1\n    else\n        echo \"Checksum verification passed\"\n    fi\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/helpers/invoke-tests.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  invoke-tests.sh\n##  Desc:  Helper function for invoking tests\n################################################################################\n\npwsh -Command \"Import-Module '$HELPER_SCRIPTS/../tests/Helpers.psm1' -DisableNameChecking\n    Invoke-PesterTests -TestFile \\\"$1\\\" -TestName \\\"$2\\\"\"\n"
  },
  {
    "path": "images/ubuntu/scripts/helpers/os.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  os.sh\n##  Desc:  Helper functions for OS releases\n################################################################################\n\nis_ubuntu22() {\n    lsb_release -rs | grep -q '22.04'\n}\n\nis_ubuntu24() {\n    lsb_release -rs | grep -q '24.04'\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/ActionArchiveCache.Tests.ps1",
    "content": "Describe \"ActionArchiveCache\" {\n    BeforeDiscovery {\n        $actionArchiveCachePath = \"/opt/actionarchivecache\"\n        $tarballTestCases = Get-ChildItem -Path \"$actionArchiveCachePath/*.tar.gz\" -Recurse | ForEach-Object { @{ ActionTarball = $_.FullName } }\n    }\n\n    Context \"Action archive cache directory not empty\" {\n        It \"<ActionArchiveCachepath> not empty\" -TestCases @{ ActionArchiveCachepath = $actionArchiveCachePath } {\n            (Get-ChildItem -Path \"$ActionArchiveCachepath/*.tar.gz\" -Recurse).Count | Should -BeGreaterThan 0\n        }\n    }\n\n    Context \"Action tarball not empty\" {\n        It \"<ActionTarball>\" -TestCases $tarballTestCases {\n            (Get-Item \"$ActionTarball\").Length | Should -BeGreaterThan 0\n        }\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/Android.Tests.ps1",
    "content": "Describe \"Android\" {\n    function Get-AndroidPackages {\n        <#\n        .SYNOPSIS\n            This function returns a list of available Android packages.\n\n        .DESCRIPTION\n            The Get-AndroidPackages function checks if a list of packages is already available in a file.\n            If not, it uses the sdkmanager to generate a list of available packages and saves it to a file.\n            It then returns the content of this file.\n\n        .PARAMETER SDKRootPath\n            The root path of the Android SDK installation.\n            If not specified, the function uses the ANDROID_HOME environment variable.\n\n        .EXAMPLE\n            Get-AndroidPackages -SDKRootPath \"/usr/local/lib/android/sdk\"\n\n            This command returns a list of available Android packages for the specified SDK root path.\n\n        .NOTES\n            This function requires the Android SDK to be installed.\n        #>\n        param (\n            [Parameter(Mandatory=$false)]\n            [string] $SDKRootPath\n        )\n\n        if (-not $SDKRootPath) {\n            $SDKRootPath = $env:ANDROID_HOME\n        }\n\n        $packagesListFile = \"$SDKRootPath/packages-list.txt\"\n\n        if (-not (Test-Path -Path $packagesListFile -PathType Leaf)) {\n            (/usr/local/lib/android/sdk/cmdline-tools/latest/bin/sdkmanager --list --verbose 2>&1) |\n                Where-Object { $_ -Match \"^[^\\s]\" } |\n                Where-Object { $_ -NotMatch \"^(Loading |Info: Parsing |---|\\[=+|Installed |Available )\" } |\n                Where-Object { $_ -NotMatch \"^[^;]*$\" } |\n                Out-File -FilePath $packagesListFile\n\n            Write-Host \"Android packages list:\"\n            Get-Content $packagesListFile\n        }\n\n        return Get-Content $packagesListFile\n    }\n\n    $androidSdkManagerPackages = Get-AndroidPackages\n    [int]$platformMinVersion = (Get-ToolsetContent).android.platform_min_version\n    [version]$buildToolsMinVersion = (Get-ToolsetContent).android.build_tools_min_version\n    [array]$ndkVersions = (Get-ToolsetContent).android.ndk.versions\n    $ndkFullVersions = $ndkVersions |\n        ForEach-Object { (Get-ChildItem \"/usr/local/lib/android/sdk/ndk/${_}.*\" |\n        Select-Object -Last 1).Name } | ForEach-Object { \"ndk/${_}\" }\n    # Platforms starting with a letter are the preview versions, which is not installed on the image\n    $platformVersionsList = ($androidSdkManagerPackages |\n        Where-Object { \"$_\".StartsWith(\"platforms;\") }) -replace 'platforms;android-', '' |\n        Where-Object { $_ -match \"^\\d\" } | Sort-Object -Unique\n    $platformsInstalled = $platformVersionsList |\n        Where-Object { [int]($_.Split(\"-\")[0]) -ge $platformMinVersion } |\n        ForEach-Object { \"platforms/android-${_}\" }\n\n    $buildToolsList = ($androidSdkManagerPackages | Where-Object { \"$_\".StartsWith(\"build-tools;\") }) -replace 'build-tools;', ''\n    $buildTools = $buildToolsList |\n        Where-Object { $_ -match \"\\d+(\\.\\d+){2,}$\"} |\n        Where-Object { [version]$_ -ge $buildToolsMinVersion } |\n        Sort-Object -Unique |\n        ForEach-Object { \"build-tools/${_}\" }\n\n    $androidPackages = @(\n        $platformsInstalled,\n        $buildTools,\n        $ndkFullVersions,\n        \"platform-tools\",\n        ((Get-ToolsetContent).android.extra_list | ForEach-Object { \"extras/${_}\" }),\n        ((Get-ToolsetContent).android.addon_list | ForEach-Object { \"add-ons/${_}\" }),\n        ((Get-ToolsetContent).android.additional_tools | ForEach-Object { \"${_}\" })\n    )\n\n    $androidPackages = $androidPackages | ForEach-Object { $_ }\n\n    Context \"SDKManagers\" {\n        $testCases = @(\n            @{\n                PackageName = \"Command-line tools\"\n                Sdkmanager = \"$env:ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager\"\n            }\n        )\n\n        It \"Sdkmanager from <PackageName> is available\" -TestCases $testCases {\n            \"$Sdkmanager --version\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    Context \"Packages\" {\n        $testCases = $androidPackages | ForEach-Object { @{ PackageName = $_ } }\n\n        It \"<PackageName>\" -TestCases $testCases {\n            # Convert 'cmake;3.6.4111459' -> 'cmake/3.6.4111459'\n            $PackageName = $PackageName.Replace(\";\", \"/\")\n            $targetPath = Join-Path $env:ANDROID_HOME $PackageName\n            $targetPath | Should -Exist\n        }\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/Apt.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\nDescribe \"Apt\" {\n    $packages = (Get-ToolsetContent).apt.cmd_packages + (Get-ToolsetContent).apt.vital_packages\n    $testCases = $packages | ForEach-Object { @{ toolName = $_ } }\n\n    It \"<toolName> is available\" -TestCases $testCases {\n        switch ($toolName) {\n            \"acl\"               { $toolName = \"getfacl\"; break }\n            \"aria2\"             { $toolName = \"aria2c\"; break }\n            \"libnss3-tools\"     { $toolName = \"certutil\"; break }\n            \"p7zip-full\"        { $toolName = \"p7zip\"; break }\n            \"subversion\"        { $toolName = \"svn\"; break }\n            \"sphinxsearch\"      { $toolName = \"searchd\"; break }\n            \"binutils\"          { $toolName = \"strings\"; break }\n            \"coreutils\"         { $toolName = \"tr\"; break }\n            \"net-tools\"         { $toolName = \"netstat\"; break }\n            \"mercurial\"         { $toolName = \"hg\"; break }\n            \"findutils\"         { $toolName = \"find\"; break }\n            \"systemd-coredump\"  { $toolName = \"coredumpctl\"; break }\n        }\n\n        (Get-Command -Name $toolName).CommandType | Should -BeExactly \"Application\"\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/Browsers.Tests.ps1",
    "content": "Describe \"Firefox\" {\n    It \"Firefox\" {\n        \"firefox --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Geckodriver\" {\n        \"geckodriver --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Chrome\" {\n    It \"Chrome\" {\n        \"google-chrome --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Chrome Driver\" {\n        \"chromedriver --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Chrome and Chrome Driver major versions are the same\" {\n        $chromeMajor = (google-chrome --version).Trim(\"Google Chrome \").Split(\".\")[0]\n        $chromeDriverMajor = (chromedriver --version).Trim(\"ChromeDriver \").Split(\".\")[0]\n        $chromeMajor | Should -BeExactly $chromeDriverMajor\n    }\n}\n\nDescribe \"Edge\" {\n    It \"Edge\" {\n        \"microsoft-edge --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Edge Driver\" {\n        \"msedgedriver --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Chromium\" {\n    It \"Chromium\" {\n        \"chromium-browser --version\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/CLI.Tools.Tests.ps1",
    "content": "Describe \"Azure CLI\" {\n    It \"Azure CLI\" {\n        \"az --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Azure DevOps CLI\" {\n    It \"az devops\" {\n        \"az devops -h\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Aliyun CLI\" -Skip:((-not (Test-IsUbuntu22))) {\n    It \"Aliyun CLI\" {\n        \"aliyun version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"AWS\" {\n    It \"AWS CLI\" {\n        \"aws --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Session Manager Plugin for the AWS CLI\" {\n        session-manager-plugin 2>&1 | Out-String | Should -Match \"plugin was installed successfully\"\n    }\n\n    It \"AWS SAM CLI\" {\n        \"sam --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"GitHub CLI\" {\n    It \"gh cli\" {\n        \"gh --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Google Cloud CLI\" {\n    It \"Google Cloud CLI\" {\n        \"gcloud --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"OC CLI\" -Skip:((-not (Test-IsUbuntu22))) {\n    It \"OC CLI\" {\n        \"oc version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Oras CLI\" -Skip:((-not (Test-IsUbuntu22))) {\n    It \"Oras CLI\" {\n        \"oras version\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/Common.Tests.ps1",
    "content": "Describe \"PHP\" {\n    $testCases = (Get-ToolsetContent).php.versions | ForEach-Object { @{PhpVersion = $_} }\n\n    It \"php <phpVersion>\" -TestCases $testCases {\n        \"php${PhpVersion} --version\" | Should -ReturnZeroExitCode\n        \"php-config${PhpVersion} --version\" | Should -ReturnZeroExitCode\n        \"phpize${PhpVersion} --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"PHPUnit\" {\n        \"phpunit --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Composer\" {\n        \"composer --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Pear\" {\n        \"pear\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Pecl\" {\n        \"pecl\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Swift\" {\n    It \"swift\" {\n        \"swift --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"swiftc\" {\n        \"swiftc --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"libsourcekitd\" {\n        \"/usr/local/lib/libsourcekitdInProc.so\" | Should -Exist\n    }\n}\n\nDescribe \"PipxPackages\" {\n    $testCases = (Get-ToolsetContent).pipx | ForEach-Object { @{package=$_.package; cmd = $_.cmd} }\n\n    It \"<package>\" -TestCases $testCases {\n        \"$cmd --version\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/Databases.Tests.ps1",
    "content": "Describe \"PostgreSQL\" {\n    It \"PostgreSQL Service\" {\n        \"sudo systemctl start postgresql\" | Should -ReturnZeroExitCode\n        \"pg_isready\" | Should -OutputTextMatchingRegex \"/var/run/postgresql:5432 - accepting connections\"\n        \"sudo systemctl stop postgresql\" | Should -ReturnZeroExitCode\n    }\n\n    It \"PostgreSQL version should correspond to the version in the toolset\" {\n        $toolsetVersion = (Get-ToolsetContent).postgresql.version\n        # Client version\n        (psql --version).split()[-1] | Should -BeLike \"$toolsetVersion*\"\n        # Server version\n        (pg_config --version).split()[-1] | Should -BeLike \"$toolsetVersion*\"\n    }\n}\n\nDescribe \"MySQL\" {\n    It \"MySQL CLI\" {\n        \"mysql -V\" | Should -ReturnZeroExitCode\n    }\n\n    It \"MySQL Service\" {\n        \"sudo systemctl start mysql\" | Should -ReturnZeroExitCode\n        mysql -s -N -h localhost -uroot -proot -e \"select count(*) from mysql.user where user='root' and authentication_string is null;\" | Should -BeExactly 0\n        \"sudo mysql -vvv -e 'CREATE DATABASE smoke_test' -uroot -proot\" | Should -ReturnZeroExitCode\n        \"sudo mysql -vvv -e 'DROP DATABASE smoke_test' -uroot -proot\" | Should -ReturnZeroExitCode\n        \"sudo systemctl stop mysql\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/DotnetSDK.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\n\nDescribe \"Dotnet and tools\" {\n    BeforeAll {\n        $env:PATH = \"/etc/skel/.dotnet/tools:$($env:PATH)\"\n        $dotnetSDKs = dotnet --list-sdks | ConvertTo-Json\n        $dotnetRuntimes = dotnet --list-runtimes | ConvertTo-Json\n    }\n\n    $dotnetVersions = (Get-ToolsetContent).dotnet.versions\n\n    Context \"Default\" {\n        It \"Default Dotnet SDK is available\" {\n            \"dotnet --version\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    foreach ($version in $dotnetVersions) {\n        Context \"Dotnet $version\" {\n            $dotnet = @{ dotnetVersion = $version }\n\n            It \"SDK <dotnetVersion> is available\" -TestCases $dotnet {\n                $dotnetSDKs | Should -Match \"$dotnetVersion.[1-9]*\"\n            }\n\n            It \"Runtime <dotnetVersion> is available\" -TestCases $dotnet {\n                $dotnetRuntimes | Should -Match \"$dotnetVersion.[1-9]*\"\n            }\n        }\n    }\n\n    Context \"Dotnet tools\" {\n        $dotnetTools = (Get-ToolsetContent).dotnet.tools\n        $testCases = $dotnetTools | ForEach-Object { @{ ToolName = $_.name; TestInstance = $_.test }}\n\n        It \"<ToolName> is available\" -TestCases $testCases {\n            \"$TestInstance\" | Should -ReturnZeroExitCode\n        }\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/Haskell.Tests.ps1",
    "content": "Describe \"Haskell\" {\n    $GHCCommonPath = \"/usr/local/.ghcup/ghc\"\n    $GHCVersions = Get-ChildItem -Path $GHCCommonPath | Where-Object { $_.Name -match \"\\d+\\.\\d+\" }\n\n    $testCases = $GHCVersions | ForEach-Object { @{ GHCPath = \"${_}/bin/ghc\"} }\n\n    It \"GHC version <GHCPath>\" -TestCases $testCases {\n        \"$GHCPath --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"GHCup\" {\n        \"ghcup --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Default GHC\" {\n        \"ghc --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Cabal\" {\n        \"cabal --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Stack\" {\n        \"stack --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Stack hook is not installed\" {\n        \"$HOME/.stack/hooks/ghc-install.sh\" | Should -Not -Exist\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/Helpers.psm1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\" -DisableNameChecking\n\nfunction Invoke-PesterTests {\n    <#\n    .SYNOPSIS\n        Runs Pester tests based on the provided test file and test name.\n\n    .DESCRIPTION\n        The Invoke-PesterTests function runs Pester tests based on the provided test file and test name.\n\n    .PARAMETER TestFile\n        The name of the test file to run. This should be the base name of the test file without the extension.\n        Using \"*\" will run all tests from all test files.\n\n    .PARAMETER TestName\n        The name of the specific test to run. If provided, only the test with the matching name will be executed.\n\n    .EXAMPLE\n        Invoke-PesterTests -TestFile \"MyTests\" -TestName \"Test1\"\n        Runs the test named \"Test1\" from the test file \"MyTests.Tests.ps1\".\n\n    .EXAMPLE\n        Invoke-PesterTests -TestFile \"*\"\n        Runs all tests from all test files\n\n    #>\n    Param(\n        [Parameter(Mandatory = $true)]\n        [string] $TestFile,\n        [string] $TestName\n    )\n\n    $testPath = \"/imagegeneration/tests/${TestFile}.Tests.ps1\"\n    if (-not (Test-Path $testPath)) {\n        throw \"Unable to find test file '$TestFile' on '$testPath'.\"\n    }\n\n    # Check that Pester module is imported\n    if (-not (Get-Module \"Pester\")) {\n        Import-Module Pester\n    }\n\n    $configuration = [PesterConfiguration] @{\n        Run    = @{ Path = $testPath; PassThru = $true }\n        Output = @{ Verbosity = \"Detailed\"; RenderMode = \"Plaintext\" }\n    }\n    if ($TestName) {\n        $configuration.Filter.FullName = $TestName\n    }\n\n    # Switch ErrorActionPreference to Stop temporary to make sure that tests will fail on silent errors too\n    $backupErrorActionPreference = $ErrorActionPreference\n    $ErrorActionPreference = \"Stop\"\n    $results = Invoke-Pester -Configuration $configuration\n    $ErrorActionPreference = $backupErrorActionPreference\n\n    # Fail in case if no tests are run\n    if (-not ($results -and ($results.FailedCount -eq 0) -and (($results.PassedCount + $results.SkippedCount) -gt 0))) {\n        $results\n        throw \"Test run has failed\"\n    }\n}\n\nfunction ShouldReturnZeroExitCode {\n    <#\n    .SYNOPSIS\n        Implements a custom Should-operator for the Pester framework.\n\n    .DESCRIPTION\n        This function is used to check if a command has returned a zero exit code.\n        It can be used by registering it using the Add-ShouldOperator function in Pester.\n\n    .PARAMETER ActualValue\n        The actual value to be checked.\n\n    .PARAMETER Negate\n        A switch parameter that, when specified, negates the result of the check.\n\n    .PARAMETER Because\n        An optional string that provides additional context or explanation for the check.\n\n    .NOTES\n        This function is designed to be used with the Pester framework.\n\n    .LINK\n        https://pester.dev/docs/assertions/custom-assertions\n    #>\n    Param(\n        [string] $ActualValue,\n        [switch] $Negate,\n        [string] $Because # This parameter is unused but we need it to match Pester asserts signature\n    )\n\n    $result = Get-CommandResult $ActualValue -ValidateExitCode $false\n\n    [bool] $succeeded = $result.ExitCode -eq 0\n    if ($Negate) { $succeeded = -not $succeeded }\n\n    if (-not $succeeded) {\n        $commandOutputIndent = \" \" * 4\n        $commandOutput = ($result.Output | ForEach-Object { \"${commandOutputIndent}${_}\" }) -join \"`n\"\n        $failureMessage = \"Command '${ActualValue}' has finished with exit code`n${commandOutput}\"\n    }\n\n    return [PSCustomObject] @{\n        Succeeded      = $succeeded\n        FailureMessage = $failureMessage\n    }\n}\n\nfunction ShouldOutputTextMatchingRegex {\n    <#\n    .SYNOPSIS\n        Implements a custom Should-operator for the Pester framework.\n\n    .DESCRIPTION\n        This function is used to check if a command outputs text that matches a regular expression.\n        It can be used by registering it using the Add-ShouldOperator function in Pester.\n\n    .PARAMETER ActualValue\n        The actual value to be checked.\n\n    .PARAMETER RegularExpression\n        The regular expression to be used for the check.\n\n    .PARAMETER Negate\n        A switch parameter that, when specified, negates the result of the check.\n\n    .NOTES\n        This function is designed to be used with the Pester framework.\n\n    .LINK\n        https://pester.dev/docs/assertions/custom-assertions\n    #>\n    Param(\n        [string] $ActualValue,\n        [string] $RegularExpression,\n        [switch] $Negate\n    )\n\n    $output = (Get-CommandResult $ActualValue -ValidateExitCode $false).Output | Out-String\n    [bool] $succeeded = $output -cmatch $RegularExpression\n\n    if ($Negate) { $succeeded = -not $succeeded }\n\n    $failureMessage = ''\n\n    if (-not $succeeded) {\n        if ($Negate) {\n            $failureMessage = \"Expected regular expression '$RegularExpression' for '$ActualValue' command to not match '$output', but it did match.\"\n        } else {\n            $failureMessage = \"Expected regular expression '$RegularExpression' for '$ActualValue' command to match '$output', but it did not match.\"\n        }\n    }\n\n    return [PSCustomObject] @{\n        Succeeded      = $succeeded\n        FailureMessage = $failureMessage\n    }\n}\n\nIf (Get-Command -Name Add-ShouldOperator -ErrorAction SilentlyContinue) {\n    Add-ShouldOperator -Name ReturnZeroExitCode -InternalName ShouldReturnZeroExitCode -Test ${function:ShouldReturnZeroExitCode}\n    Add-ShouldOperator -Name OutputTextMatchingRegex -InternalName ShouldOutputTextMatchingRegex -Test ${function:ShouldOutputTextMatchingRegex}\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/Java.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\" -DisableNameChecking\n\nDescribe \"Java\" {\n    $toolsetJava = (Get-ToolsetContent).java\n    $defaultVersion = $toolsetJava.default\n    $jdkVersions = $toolsetJava.versions\n\n    It \"Java <DefaultJavaVersion> is default\" -TestCases @{ DefaultJavaVersion = $defaultVersion } {\n        $actualJavaPath = [System.Environment]::GetEnvironmentVariable(\"JAVA_HOME\")\n        $expectedJavaPath = [System.Environment]::GetEnvironmentVariable(\"JAVA_HOME_${DefaultJavaVersion}_X64\")\n\n        $actualJavaPath | Should -Not -BeNullOrEmpty\n        $expectedJavaPath | Should -Not -BeNullOrEmpty\n        $actualJavaPath | Should -Be $expectedJavaPath\n    }\n\n    It \"<ToolName>\" -TestCases @(\n        @{ ToolName = \"java\" }\n        @{ ToolName = \"javac\" }\n    ) {\n        \"$ToolName -version\" | Should -ReturnZeroExitCode\n    }\n\n    $testCases = $jdkVersions | ForEach-Object { @{Version = $_ } }\n\n    It \"Java <Version>\" -TestCases $testCases {\n        $javaVariableValue = [System.Environment]::GetEnvironmentVariable(\"JAVA_HOME_${Version}_X64\")\n        $javaVariableValue | Should -Not -BeNullOrEmpty\n        $javaPath = Join-Path $javaVariableValue \"bin/java\"\n\n        \"`\"$javaPath`\" -version\" | Should -ReturnZeroExitCode\n\n        if ($Version -eq 8) {\n            $Version = \"1.${Version}\"\n        }\n        \"`\"$javaPath`\" -version\" | Should -OutputTextMatchingRegex \"openjdk\\ version\\ `\"${Version}(\\.[0-9_\\.]+)?`\"\"\n    }\n}\n\nDescribe \"Java-Tools\" {\n    It \"Gradle\" {\n        \"gradle -version\" | Should -ReturnZeroExitCode\n\n        $gradleVariableValue = [System.Environment]::GetEnvironmentVariable(\"GRADLE_HOME\")\n        $gradleVariableValue | Should -BeLike \"/usr/share/gradle-*\"\n\n        $gradlePath = Join-Path $env:GRADLE_HOME \"bin/gradle\"\n        \"`\"$GradlePath`\" -version\" | Should -ReturnZeroExitCode\n    }\n    It \"<ToolName>\" -TestCases @(\n        @{ ToolName = \"mvn\" }\n        @{ ToolName = \"ant\" }\n    ) {\n        \"$ToolName -version\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/Node.Tests.ps1",
    "content": "Describe \"Node.js\" {\n    $binaries = @(\"node\")\n    $module_commands = (Get-ToolsetContent).node_modules | ForEach-Object { $_.command }\n    $testCases = $binaries + $module_commands | ForEach-Object { @{NodeCommand = $_} }\n\n    It \"<NodeCommand>\" -TestCases $testCases {\n        \"$NodeCommand --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Node.js version should correspond to the version in the toolset\" {\n        node --version | Should -BeLike \"v$((Get-ToolsetContent).node.default).*\"\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/PowerShellModules.Tests.ps1",
    "content": "Describe \"PowerShellModules\" {\n    $modules = (Get-ToolsetContent).powershellModules\n    $modulesWithoutVersions = $modules | Where-Object { -not $_.versions } | ForEach-Object {\n        @{moduleName = $_.name}\n    }\n\n    $modulesWithVersions = $modules | Where-Object { $_.versions } | ForEach-Object {\n        $moduleName = $_.name\n        $_.versions | ForEach-Object {\n            @{moduleName = $moduleName; expectedVersion = $_}\n        }\n    }\n\n    It \"<moduleName> is installed\" -TestCases $modulesWithoutVersions {\n        Get-Module -Name $moduleName -ListAvailable | Should -BeTrue\n    }\n\n    if ($modulesWithVersions) {\n        It \"<moduleName> with <expectedVersion> is installed\" -TestCases $modulesWithVersions {\n            (Get-Module -Name $moduleName -ListAvailable).Version -contains $expectedVersion | Should -BeTrue\n        }\n    }\n}\n\nDescribe \"AzureModules\" {\n    $modules = (Get-ToolsetContent).azureModules\n    $modulesRootPath = \"/usr/share\"\n\n    foreach ($module in $modules) {\n        $moduleName = $module.name\n\n        Context \"$moduleName\" {\n            foreach ($version in $module.versions) {\n                $modulePath = Join-Path -Path $modulesRootPath -ChildPath \"${moduleName}_${version}\"\n                $moduleInfo = @{ moduleName = $moduleName; modulePath = $modulePath; expectedVersion = $version }\n\n                It \"<expectedVersion> exists in modules directory\" -TestCases $moduleInfo {\n                    $testJob = Start-Job -ScriptBlock {\n                        param (\n                            $modulePath,\n                            $moduleName\n                        )\n\n                        $env:PSModulePath = \"${modulePath}:${env:PSModulePath}\"\n                        (Get-Module -ListAvailable -Name $moduleName).Version.ToString()\n                    } -ArgumentList $modulePath, $moduleName\n\n                    $moduleVersion = $testJob | Wait-Job | Receive-Job\n                    Remove-Job $testJob\n                    $moduleVersion | Should -Match $expectedVersion\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/RunAll-Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/Helpers.psm1\" -DisableNameChecking\n\nInvoke-PesterTests \"*\"\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/System.Tests.ps1",
    "content": "# The $env:AGENT_NAME and $env:RUNNER_NAME are predefined variables for the ADO pipelines and for the GitHub actions respectively.\n# If the test is running on the ADO pipeline or on the GitHub actions, the test will be skipped\nDescribe \"Disk free space\" -Skip:(-not [String]::IsNullOrEmpty($env:AGENT_NAME) -or -not [String]::IsNullOrEmpty($env:RUNNER_NAME)) {\n    It \"Image has enough free space\" {\n        $diskInfo = Get-PSDrive \"/\"\n        $totalSpaceGB = [math]::Floor(($diskInfo.Used + $diskInfo.Free) / 1GB)\n        $freeSpaceGB = [math]::Floor($diskInfo.Free / 1GB)\n        Write-Host \"  [i] Disk size: ${totalSpaceGB} GB; Free space: ${freeSpaceGB} GB\"\n        $freeSpaceGB | Should -BeGreaterOrEqual 17\n    }\n}\n\nDescribe \"fwupd removed\" {\n    It \"Is not present on box\" {\n        $systemctlOutput = & systemctl list-units fwupd-refresh.timer --no-legend\n        # When disabled the output looks like this:\n        #❯ systemctl list-units fwupd-refresh.timer --no-legend\n        #● fwupd-refresh.timer masked failed failed fwupd-refresh.timer\n        # When enabled the output looks like this:\n        #❯ systemctl list-units fwupd-refresh.timer --no-legend\n        #fwupd-refresh.timer loaded active waiting Refresh fwupd metadata regularly\n        $systemctlOutput | Should -Not -Match \"active\"\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/Tools.Tests.ps1",
    "content": "Import-Module \"$PSScriptRoot/../helpers/Common.Helpers.psm1\"\nDescribe \"azcopy\" {\n    It \"azcopy\" {\n        \"azcopy --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"azcopy10 link exists\" {\n        \"azcopy10 --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Bicep\" {\n    It \"Bicep\" {\n        \"bicep --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Rust\" {\n    BeforeAll {\n        $env:PATH = \"/etc/skel/.cargo/bin:/etc/skel/.rustup/bin:$($env:PATH)\"\n        $env:RUSTUP_HOME = \"/etc/skel/.rustup\"\n        $env:CARGO_HOME = \"/etc/skel/.cargo\"\n    }\n\n    It \"Rustup is installed\" {\n        \"rustup --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Rustc is installed\" {\n        \"rustc --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Rustdoc is installed\" {\n        \"rustdoc --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Rustfmt is installed\" {\n        \"rustfmt --version\" | Should -ReturnZeroExitCode\n    }\n    \n    It \"cargo\" {\n        \"cargo --version\" | Should -ReturnZeroExitCode\n    }\n\n    Context \"Cargo dependencies\" -Skip:((-not (Test-IsUbuntu22))) {\n        It \"bindgen\" {\n            \"bindgen --version\" | Should -ReturnZeroExitCode\n        }\n\n        It \"cbindgen\" {\n            \"cbindgen --version\" | Should -ReturnZeroExitCode\n        }\n\n        It \"cargo-clippy\" {\n            \"cargo-clippy --version\" | Should -ReturnZeroExitCode\n        }\n\n        It \"Cargo audit\" {\n            \"cargo audit --version\" | Should -ReturnZeroExitCode\n        }\n\n        It \"Cargo outdated\" {\n            \"cargo outdated --version\" | Should -ReturnZeroExitCode\n        }\n    }\n}\n\nDescribe \"Docker\" {\n    It \"docker client\" {\n        $version=(Get-ToolsetContent).docker.components | Where-Object { $_.package -eq 'docker-ce-cli' } | Select-Object -ExpandProperty version\n        If ($version -ne \"latest\") {\n            $(sudo docker version --format '{{.Client.Version}}') | Should -BeLike \"*$version*\"\n        }else{\n            \"sudo docker version --format '{{.Client.Version}}'\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    It \"docker server\" {\n        $version=(Get-ToolsetContent).docker.components | Where-Object { $_.package -eq 'docker-ce' } | Select-Object -ExpandProperty version\n        If ($version -ne \"latest\") {\n            $(sudo docker version --format '{{.Server.Version}}') | Should -BeLike \"*$version*\"\n        }else{\n            \"sudo docker version --format '{{.Server.Version}}'\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    It \"docker client/server versions match\" {\n        $clientVersion = $(sudo docker version --format '{{.Client.Version}}')\n        $serverVersion = $(sudo docker version --format '{{.Server.Version}}')\n        $clientVersion | Should -Be $serverVersion\n    }\n\n    It \"docker buildx\" {\n        $version=(Get-ToolsetContent).docker.plugins | Where-Object { $_.plugin -eq 'buildx' } | Select-Object -ExpandProperty version\n        If ($version -ne \"latest\") {\n            $(docker buildx version) | Should -BeLike \"*$version*\"\n        }else{\n            \"docker buildx\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    It \"docker compose v2\" {\n        $version=(Get-ToolsetContent).docker.plugins | Where-Object { $_.plugin -eq 'compose' } | Select-Object -ExpandProperty version\n        If ($version -ne \"latest\") {\n            $(docker compose version --short) | Should -BeLike \"*$version*\"\n        }else{\n            \"docker compose version --short\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    It \"docker-credential-ecr-login\" {\n        \"docker-credential-ecr-login -v\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Ansible\" {\n    It \"Ansible\" {\n        \"ansible --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Bazel\" {\n    It \"<ToolName>\" -TestCases @(\n        @{ ToolName = \"bazel\" }\n        @{ ToolName = \"bazelisk\" }\n    ) {\n        \"$ToolName --version\"| Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"clang\" {\n    $testCases = (Get-ToolsetContent).clang.Versions | ForEach-Object { @{ClangVersion = $_} }\n\n    It \"clang <ClangVersion>\" -TestCases $testCases {\n        \"clang-$ClangVersion --version\" | Should -ReturnZeroExitCode\n        \"clang++-$ClangVersion --version\" | Should -ReturnZeroExitCode\n        \"clang-format-$ClangVersion --version\" | Should -ReturnZeroExitCode\n        \"clang-tidy-$ClangVersion --version\" | Should -ReturnZeroExitCode\n        \"run-clang-tidy-$ClangVersion --help\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Cmake\" {\n    It \"cmake\" {\n        \"cmake --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"gcc\" {\n    $testCases = (Get-ToolsetContent).gcc.Versions | ForEach-Object { @{GccVersion = $_} }\n\n    It \"gcc <GccVersion>\" -TestCases $testCases {\n        \"$GccVersion --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"gfortran\" {\n    $testCases = (Get-ToolsetContent).gfortran.Versions | ForEach-Object { @{GfortranVersion = $_} }\n\n    It \"gfortran <GfortranVersion>\" -TestCases $testCases {\n        \"$GfortranVersion --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Mono\" -Skip:(Test-IsUbuntu24) {\n    It \"mono\" {\n        \"mono --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"msbuild\" {\n        \"msbuild -version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"nuget\" {\n        \"nuget\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"MSSQLCommandLineTools\" -Skip:((-not (Test-IsUbuntu22))) {\n    It \"sqlcmd\" {\n        \"sqlcmd -?\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"SqlPackage\" -Skip:((-not (Test-IsUbuntu22))) {\n    It \"sqlpackage\" {\n        \"sqlpackage /version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"R\" -Skip:((-not (Test-IsUbuntu22))) {\n    It \"r\" {\n        \"R --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Sbt\" -Skip:((-not (Test-IsUbuntu22))) {\n    It \"sbt\" {\n        \"sbt --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Selenium\" {\n    It \"Selenium is installed\" {\n        $seleniumPath = Join-Path \"/usr/share/java\" \"selenium-server.jar\"\n        $seleniumPath | Should -Exist\n    }\n}\n\nDescribe \"Terraform\" -Skip:((-not (Test-IsUbuntu22))) {\n    It \"terraform\" {\n        \"terraform --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Zstd\" {\n    It \"zstd\" {\n        \"zstd --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"pzstd\" {\n        \"pzstd --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Vcpkg\" {\n    It \"vcpkg\" {\n        \"vcpkg version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Git\" {\n    It \"git\" {\n        \"git --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"git-ftp\" {\n        \"git-ftp --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Git-lfs\" {\n    It \"git-lfs\" {\n        \"git-lfs --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Heroku\" -Skip:((-not (Test-IsUbuntu22))) {\n    It \"heroku\" {\n        \"heroku --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Homebrew\" {\n    It \"homebrew\" {\n        \"/home/linuxbrew/.linuxbrew/bin/brew --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Julia\" {\n    It \"julia\" {\n        \"julia --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Kubernetes tools\" {\n    It \"kind\" {\n        \"kind version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"kubectl\" {\n        \"kubectl version --client=true\" | Should -OutputTextMatchingRegex \"Client Version: v\"\n    }\n\n    It \"helm\" {\n        \"helm version --short\" | Should -ReturnZeroExitCode\n    }\n\n    It \"minikube\" {\n        \"minikube version --short\" | Should -ReturnZeroExitCode\n    }\n\n    It \"kustomize\" {\n        \"kustomize version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Leiningen\" -Skip:((-not (Test-IsUbuntu22))) {\n    It \"leiningen\" {\n        \"lein --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Conda\" {\n    It \"conda\" {\n        \"conda --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Packer\" {\n    It \"packer\" {\n        \"packer --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Pulumi\" {\n    It \"pulumi\" {\n        \"pulumi version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Containers\" {\n    $testCases = @(\"podman\", \"buildah\", \"skopeo\") | ForEach-Object { @{ContainerCommand = $_} }\n\n    It \"<ContainerCommand>\" -TestCases $testCases {\n        \"$ContainerCommand -v\" | Should -ReturnZeroExitCode\n    }\n\n    # https://github.com/actions/runner-images/issues/7753\n    It \"podman networking\" -TestCases \"podman CNI plugins\" {\n        \"podman network create -d bridge test-net\" | Should -ReturnZeroExitCode\n        \"podman network ls\" | Should -Not -OutputTextMatchingRegex \"Error\"\n        \"podman network rm test-net\" | Should -ReturnZeroExitCode\n    }\n\n}\n\nDescribe \"nvm\" {\n    It \"nvm\" {\n        \"source /etc/skel/.nvm/nvm.sh && nvm --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Python\" {\n    $testCases = @(\"python\", \"pip\", \"python3\", \"pip3\") | ForEach-Object { @{PythonCommand = $_} }\n\n    It \"<PythonCommand>\" -TestCases $testCases {\n        \"$PythonCommand --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Ruby\" {\n    $testCases = @(\"ruby\", \"gem\") | ForEach-Object { @{RubyCommand = $_} }\n\n    It \"<RubyCommand>\" -TestCases $testCases {\n        \"$RubyCommand --version\" | Should -ReturnZeroExitCode\n    }\n\n    $gemTestCases = (Get-ToolsetContent).rubygems | ForEach-Object { @{gemName = $_.name} }\n\n    if ($gemTestCases) {\n        It \"Gem <gemName> is installed\" -TestCases $gemTestCases {\n            \"gem list -i '^$gemName$'\" | Should -OutputTextMatchingRegex \"true\"\n        }\n    }\n}\n\nDescribe \"yq\" {\n    It \"yq\" {\n        \"yq -V\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Kotlin\" {\n    It \"kapt\" {\n        \"kapt -version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"kotlin\" {\n        \"kotlin -version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"kotlinc\" {\n        \"kotlinc -version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"kotlinc-jvm\" {\n        \"kotlinc-jvm -version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"kotlinc-js\" {\n        \"kotlinc-js -help\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Ninja\" {\n    BeforeAll {\n        New-item -Path \"/tmp/ninjaproject\" -ItemType Directory -Force\n@'\ncmake_minimum_required(VERSION 3.10)\nproject(NinjaTest NONE)\n'@ | Out-File -FilePath \"/tmp/ninjaproject/CMakeLists.txt\"\n    }\n\n    It \"Make a simple ninja project\" {\n        \"cmake -GNinja -S /tmp/ninjaproject -B /tmp/ninjaproject\" | Should -ReturnZeroExitCode\n    }\n\n    It \"build.ninja file should exist\" {\n        $buildFilePath = Join-Path \"/tmp/ninjaproject\" \"build.ninja\"\n        $buildFilePath | Should -Exist\n    }\n\n    It \"Ninja\" {\n        \"ninja --version\" | Should -ReturnZeroExitCode\n    }\n\n    AfterAll {\n        Remove-Item -Path \"/tmp/ninjaproject\" -Recurse -Force\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/Toolset.Tests.ps1",
    "content": "Describe \"Toolset\" {\n    $tools = (Get-ToolsetContent).toolcache\n\n    $toolsExecutables = @{\n        Python = @{\n            tools = @(\"python\", \"bin/pip\")\n            command = \"--version\"\n        }\n        node = @{\n            tools = @(\"bin/node\", \"bin/npm\")\n            command = \"--version\"\n        }\n        PyPy = @{\n            tools = @(\"bin/python\", \"bin/pip\")\n            command = \"--version\"\n        }\n        go = @{\n            tools = @(\"bin/go\")\n            command = \"version\"\n        }\n        Ruby = @{\n            tools = @(\"bin/ruby\")\n            command = \"--version\"\n        }\n        CodeQL = @{\n            tools = @(\"codeql/codeql\")\n            command = \"version\"\n        }\n    }\n\n    foreach ($tool in $tools) {\n        $toolName = $tool.Name\n        Context \"$toolName\" {\n            $toolExecs = $toolsExecutables[$toolName]\n\n            foreach ($version in $tool.versions) {\n                # Add wildcard if missing\n                if ($version.Split(\".\").Length -lt 3) {\n                    $version += \".*\"\n                }\n\n                $expectedVersionPath = Join-Path $env:AGENT_TOOLSDIRECTORY $toolName $version\n\n                It \"$version version folder exists\" -TestCases @{ ExpectedVersionPath = $expectedVersionPath} {\n                    $ExpectedVersionPath | Should -Exist\n                }\n\n                $foundVersion = Get-Item $expectedVersionPath `\n                    | Sort-Object -Property {[SemVer]$_.name} -Descending `\n                    | Select-Object -First 1\n                $foundVersionPath = Join-Path $foundVersion $tool.arch\n\n                if ($toolExecs) {\n                    foreach ($executable in $toolExecs[\"tools\"]) {\n                        $executablePath = Join-Path $foundVersionPath $executable\n\n                        It \"Validate $executable\" -TestCases @{ExecutablePath = $executablePath} {\n                            $ExecutablePath | Should -Exist\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/scripts/tests/WebServers.Tests.ps1",
    "content": "Describe \"Apache\" {\n    It \"Apache CLI\" {\n        \"apache2 -v\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Apache Service\" {\n        \"sudo systemctl start apache2\" | Should -ReturnZeroExitCode\n        \"apachectl configtest\" | Should -ReturnZeroExitCode\n        \"sudo systemctl stop apache2\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Nginx\" {\n    It \"Nginx CLI\" {\n        \"nginx -v\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Nginx Service\" {\n        \"sudo systemctl start nginx\" | Should -ReturnZeroExitCode\n        \"sudo nginx -t\" | Should -ReturnZeroExitCode\n        \"sudo systemctl stop nginx\" | Should -ReturnZeroExitCode\n    }\n}"
  },
  {
    "path": "images/ubuntu/templates/build.ubuntu-22_04.pkr.hcl",
    "content": "build {\n  sources = [\"source.azure-arm.image\"]\n  name = \"ubuntu-22_04\"\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\"mkdir ${var.image_folder}\", \"chmod 777 ${var.image_folder}\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.helper_script_folder}\"\n    source      = \"${path.root}/../scripts/helpers\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    script          = \"${path.root}/../scripts/build/configure-apt-mock.sh\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\",\"DEBIAN_FRONTEND=noninteractive\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\n      \"${path.root}/../scripts/build/install-ms-repos.sh\",\n      \"${path.root}/../scripts/build/configure-apt-sources.sh\",\n      \"${path.root}/../scripts/build/configure-apt.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    script          = \"${path.root}/../scripts/build/configure-limits.sh\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.installer_script_folder}\"\n    source      = \"${path.root}/../scripts/build\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.image_folder}\"\n    sources     = [\n      \"${path.root}/../assets/post-gen\",\n      \"${path.root}/../scripts/tests\",\n      \"${path.root}/../scripts/docs-gen\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.image_folder}/docs-gen/\"\n    source      = \"${path.root}/../../../helpers/software-report-base\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.installer_script_folder}/toolset.json\"\n    source      = \"${path.root}/../toolsets/toolset-2204.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\n      \"mv ${var.image_folder}/docs-gen ${var.image_folder}/SoftwareReport\",\n      \"mv ${var.image_folder}/post-gen ${var.image_folder}/post-generation\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"IMAGEDATA_FILE=${var.imagedata_file}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/configure-image-data.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"IMAGE_OS=${var.image_os}\", \"HELPER_SCRIPTS=${var.helper_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/configure-environment.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"DEBIAN_FRONTEND=noninteractive\", \"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/install-apt-vital.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/install-powershell.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} pwsh -f {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/Install-PowerShellModules.ps1\", \"${path.root}/../scripts/build/Install-PowerShellAzModules.ps1\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\", \"DEBIAN_FRONTEND=noninteractive\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\n      \"${path.root}/../scripts/build/install-actions-cache.sh\",\n      \"${path.root}/../scripts/build/install-apt-common.sh\",\n      \"${path.root}/../scripts/build/install-azcopy.sh\",\n      \"${path.root}/../scripts/build/install-azure-cli.sh\",\n      \"${path.root}/../scripts/build/install-azure-devops-cli.sh\",\n      \"${path.root}/../scripts/build/install-bicep.sh\",\n      \"${path.root}/../scripts/build/install-aliyun-cli.sh\",\n      \"${path.root}/../scripts/build/install-apache.sh\",\n      \"${path.root}/../scripts/build/install-aws-tools.sh\",\n      \"${path.root}/../scripts/build/install-clang.sh\",\n      \"${path.root}/../scripts/build/install-swift.sh\",\n      \"${path.root}/../scripts/build/install-cmake.sh\",\n      \"${path.root}/../scripts/build/install-codeql-bundle.sh\",\n      \"${path.root}/../scripts/build/install-container-tools.sh\",\n      \"${path.root}/../scripts/build/install-dotnetcore-sdk.sh\",\n      \"${path.root}/../scripts/build/install-firefox.sh\",\n      \"${path.root}/../scripts/build/install-microsoft-edge.sh\",\n      \"${path.root}/../scripts/build/install-gcc-compilers.sh\",\n      \"${path.root}/../scripts/build/install-gfortran.sh\",\n      \"${path.root}/../scripts/build/install-git.sh\",\n      \"${path.root}/../scripts/build/install-git-lfs.sh\",\n      \"${path.root}/../scripts/build/install-github-cli.sh\",\n      \"${path.root}/../scripts/build/install-google-chrome.sh\",\n      \"${path.root}/../scripts/build/install-google-cloud-cli.sh\",\n      \"${path.root}/../scripts/build/install-haskell.sh\",\n      \"${path.root}/../scripts/build/install-heroku.sh\",\n      \"${path.root}/../scripts/build/install-java-tools.sh\",\n      \"${path.root}/../scripts/build/install-kubernetes-tools.sh\",\n      \"${path.root}/../scripts/build/install-oc-cli.sh\",\n      \"${path.root}/../scripts/build/install-leiningen.sh\",\n      \"${path.root}/../scripts/build/install-miniconda.sh\",\n      \"${path.root}/../scripts/build/install-mono.sh\",\n      \"${path.root}/../scripts/build/install-kotlin.sh\",\n      \"${path.root}/../scripts/build/install-mysql.sh\",\n      \"${path.root}/../scripts/build/install-mssql-tools.sh\",\n      \"${path.root}/../scripts/build/install-sqlpackage.sh\",\n      \"${path.root}/../scripts/build/install-nginx.sh\",\n      \"${path.root}/../scripts/build/install-nvm.sh\",\n      \"${path.root}/../scripts/build/install-nodejs.sh\",\n      \"${path.root}/../scripts/build/install-bazel.sh\",\n      \"${path.root}/../scripts/build/install-oras-cli.sh\",\n      \"${path.root}/../scripts/build/install-php.sh\",\n      \"${path.root}/../scripts/build/install-postgresql.sh\",\n      \"${path.root}/../scripts/build/install-pulumi.sh\",\n      \"${path.root}/../scripts/build/install-ruby.sh\",\n      \"${path.root}/../scripts/build/install-rlang.sh\",\n      \"${path.root}/../scripts/build/install-rust.sh\",\n      \"${path.root}/../scripts/build/install-julia.sh\",\n      \"${path.root}/../scripts/build/install-sbt.sh\",\n      \"${path.root}/../scripts/build/install-selenium.sh\",\n      \"${path.root}/../scripts/build/install-terraform.sh\",\n      \"${path.root}/../scripts/build/install-packer.sh\",\n      \"${path.root}/../scripts/build/install-vcpkg.sh\",\n      \"${path.root}/../scripts/build/configure-dpkg.sh\",\n      \"${path.root}/../scripts/build/install-yq.sh\",\n      \"${path.root}/../scripts/build/install-android-sdk.sh\",\n      \"${path.root}/../scripts/build/install-pypy.sh\",\n      \"${path.root}/../scripts/build/install-python.sh\",\n      \"${path.root}/../scripts/build/install-zstd.sh\",\n      \"${path.root}/../scripts/build/install-ninja.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/install-docker.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} pwsh -f {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/Install-Toolset.ps1\", \"${path.root}/../scripts/build/Configure-Toolset.ps1\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/install-pipx-packages.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"DEBIAN_FRONTEND=noninteractive\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"/bin/sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/install-homebrew.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/configure-snap.sh\"]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    script          = \"${path.root}/../scripts/build/list-dpkg.sh\"\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"sudo reboot\"]\n  }\n\n  provisioner \"shell\" {\n    execute_command     = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    pause_before        = \"5m0s\"\n    scripts             = [\"${path.root}/../scripts/build/cleanup.sh\"]\n    start_retry_timeout = \"10m\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    inline           = [\"pwsh -File ${var.image_folder}/SoftwareReport/Generate-SoftwareReport.ps1 -OutputDirectory ${var.image_folder}\", \"pwsh -File ${var.image_folder}/tests/RunAll-Tests.ps1 -OutputDirectory ${var.image_folder}\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../Ubuntu2204-Readme.md\"\n    direction   = \"download\"\n    source      = \"${var.image_folder}/software-report.md\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../software-report.json\"\n    direction   = \"download\"\n    source      = \"${var.image_folder}/software-report.json\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPT_FOLDER=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\", \"IMAGE_FOLDER=${var.image_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/configure-system.sh\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"/tmp/\"\n    source      = \"${path.root}/../assets/ubuntu2204.conf\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\"mkdir -p /etc/vsts\", \"cp /tmp/ubuntu2204.conf /etc/vsts/machine_instance.conf\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/post-build-validation.sh\"]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\"sleep 30\", \"/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync\"]\n  }\n\n}\n"
  },
  {
    "path": "images/ubuntu/templates/build.ubuntu-24_04.pkr.hcl",
    "content": "build {\n  sources = [\"source.azure-arm.image\"]\n  name = \"ubuntu-24_04\"\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\"mkdir ${var.image_folder}\", \"chmod 777 ${var.image_folder}\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.helper_script_folder}\"\n    source      = \"${path.root}/../scripts/helpers\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    script          = \"${path.root}/../scripts/build/configure-apt-mock.sh\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\",\"DEBIAN_FRONTEND=noninteractive\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\n      \"${path.root}/../scripts/build/install-ms-repos.sh\",\n      \"${path.root}/../scripts/build/configure-apt-sources.sh\",\n      \"${path.root}/../scripts/build/configure-apt.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    script          = \"${path.root}/../scripts/build/configure-limits.sh\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.installer_script_folder}\"\n    source      = \"${path.root}/../scripts/build\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.image_folder}\"\n    sources     = [\n      \"${path.root}/../assets/post-gen\",\n      \"${path.root}/../scripts/tests\",\n      \"${path.root}/../scripts/docs-gen\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.image_folder}/docs-gen/\"\n    source      = \"${path.root}/../../../helpers/software-report-base\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.installer_script_folder}/toolset.json\"\n    source      = \"${path.root}/../toolsets/toolset-2404.json\"\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\n      \"mv ${var.image_folder}/docs-gen ${var.image_folder}/SoftwareReport\",\n      \"mv ${var.image_folder}/post-gen ${var.image_folder}/post-generation\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"IMAGEDATA_FILE=${var.imagedata_file}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/configure-image-data.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"IMAGE_OS=${var.image_os}\", \"HELPER_SCRIPTS=${var.helper_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/configure-environment.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"DEBIAN_FRONTEND=noninteractive\", \"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/install-apt-vital.sh\"]\n  }\n\nprovisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/install-powershell.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} pwsh -f {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/Install-PowerShellModules.ps1\", \"${path.root}/../scripts/build/Install-PowerShellAzModules.ps1\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\", \"DEBIAN_FRONTEND=noninteractive\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\n      \"${path.root}/../scripts/build/install-actions-cache.sh\",\n      \"${path.root}/../scripts/build/install-apt-common.sh\",\n      \"${path.root}/../scripts/build/install-azcopy.sh\",\n      \"${path.root}/../scripts/build/install-azure-cli.sh\",\n      \"${path.root}/../scripts/build/install-azure-devops-cli.sh\",\n      \"${path.root}/../scripts/build/install-bicep.sh\",\n      \"${path.root}/../scripts/build/install-apache.sh\",\n      \"${path.root}/../scripts/build/install-aws-tools.sh\",\n      \"${path.root}/../scripts/build/install-clang.sh\",\n      \"${path.root}/../scripts/build/install-swift.sh\",\n      \"${path.root}/../scripts/build/install-cmake.sh\",\n      \"${path.root}/../scripts/build/install-codeql-bundle.sh\",\n      \"${path.root}/../scripts/build/install-container-tools.sh\",\n      \"${path.root}/../scripts/build/install-dotnetcore-sdk.sh\",\n      \"${path.root}/../scripts/build/install-microsoft-edge.sh\",\n      \"${path.root}/../scripts/build/install-gcc-compilers.sh\",\n      \"${path.root}/../scripts/build/install-firefox.sh\",\n      \"${path.root}/../scripts/build/install-gfortran.sh\",\n      \"${path.root}/../scripts/build/install-git.sh\",\n      \"${path.root}/../scripts/build/install-git-lfs.sh\",\n      \"${path.root}/../scripts/build/install-github-cli.sh\",\n      \"${path.root}/../scripts/build/install-google-chrome.sh\",\n      \"${path.root}/../scripts/build/install-google-cloud-cli.sh\",\n      \"${path.root}/../scripts/build/install-haskell.sh\",\n      \"${path.root}/../scripts/build/install-java-tools.sh\",\n      \"${path.root}/../scripts/build/install-kubernetes-tools.sh\",\n      \"${path.root}/../scripts/build/install-miniconda.sh\",\n      \"${path.root}/../scripts/build/install-kotlin.sh\",\n      \"${path.root}/../scripts/build/install-mysql.sh\",\n      \"${path.root}/../scripts/build/install-nginx.sh\",\n      \"${path.root}/../scripts/build/install-nvm.sh\",\n      \"${path.root}/../scripts/build/install-nodejs.sh\",\n      \"${path.root}/../scripts/build/install-bazel.sh\",\n      \"${path.root}/../scripts/build/install-php.sh\",\n      \"${path.root}/../scripts/build/install-postgresql.sh\",\n      \"${path.root}/../scripts/build/install-pulumi.sh\",\n      \"${path.root}/../scripts/build/install-ruby.sh\",\n      \"${path.root}/../scripts/build/install-rust.sh\",\n      \"${path.root}/../scripts/build/install-julia.sh\",\n      \"${path.root}/../scripts/build/install-selenium.sh\",\n      \"${path.root}/../scripts/build/install-packer.sh\",\n      \"${path.root}/../scripts/build/install-vcpkg.sh\",\n      \"${path.root}/../scripts/build/configure-dpkg.sh\",\n      \"${path.root}/../scripts/build/install-yq.sh\",\n      \"${path.root}/../scripts/build/install-android-sdk.sh\",\n      \"${path.root}/../scripts/build/install-pypy.sh\",\n      \"${path.root}/../scripts/build/install-python.sh\",\n      \"${path.root}/../scripts/build/install-zstd.sh\",\n      \"${path.root}/../scripts/build/install-ninja.sh\"\n    ]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/install-docker.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} pwsh -f {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/Install-Toolset.ps1\", \"${path.root}/../scripts/build/Configure-Toolset.ps1\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/install-pipx-packages.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\", \"DEBIAN_FRONTEND=noninteractive\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    execute_command  = \"/bin/sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/install-homebrew.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/configure-snap.sh\"]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    script          = \"${path.root}/../scripts/build/list-dpkg.sh\"\n  }\n\n  provisioner \"shell\" {\n    execute_command   = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    expect_disconnect = true\n    inline            = [\"echo 'Reboot VM'\", \"sudo reboot\"]\n  }\n\n  provisioner \"shell\" {\n    execute_command     = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    pause_before        = \"5m0s\"\n    scripts             = [\"${path.root}/../scripts/build/cleanup.sh\"]\n    start_retry_timeout = \"10m\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\"]\n    inline           = [\"pwsh -File ${var.image_folder}/SoftwareReport/Generate-SoftwareReport.ps1 -OutputDirectory ${var.image_folder}\", \"pwsh -File ${var.image_folder}/tests/RunAll-Tests.ps1 -OutputDirectory ${var.image_folder}\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../Ubuntu2404-Readme.md\"\n    direction   = \"download\"\n    source      = \"${var.image_folder}/software-report.md\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../software-report.json\"\n    direction   = \"download\"\n    source      = \"${var.image_folder}/software-report.json\"\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPT_FOLDER=${var.helper_script_folder}\", \"INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}\", \"IMAGE_FOLDER=${var.image_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/configure-system.sh\"]\n  }\n\n  provisioner \"shell\" {\n    environment_vars = [\"HELPER_SCRIPTS=${var.helper_script_folder}\"]\n    execute_command  = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    scripts          = [\"${path.root}/../scripts/build/post-build-validation.sh\"]\n  }\n\n  provisioner \"shell\" {\n    execute_command = \"sudo sh -c '{{ .Vars }} {{ .Path }}'\"\n    inline          = [\"sleep 30\", \"/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync\"]\n  }\n\n}\n"
  },
  {
    "path": "images/ubuntu/templates/locals.ubuntu.pkr.hcl",
    "content": "locals {\n  image_properties_map = {\n      \"ubuntu22\" = {\n            source_image_marketplace_sku = \"canonical:0001-com-ubuntu-server-jammy:22_04-lts-gen2\"\n            os_disk_size_gb = 75\n      },\n      \"ubuntu24\" = {\n            source_image_marketplace_sku = \"canonical:ubuntu-24_04-lts:server\"\n            os_disk_size_gb = 75\n      }\n  }\n\n  source_image_marketplace_sku = local.image_properties_map[var.image_os].source_image_marketplace_sku\n  os_disk_size_gb = coalesce(var.os_disk_size_gb, local.image_properties_map[var.image_os].os_disk_size_gb)\n}\n"
  },
  {
    "path": "images/ubuntu/templates/source.ubuntu.pkr.hcl",
    "content": "source \"azure-arm\" \"image\" {\n  client_cert_path                       = var.client_cert_path\n  client_id                              = var.client_id\n  client_secret                          = var.client_secret\n  object_id                              = var.object_id\n  oidc_request_token                     = var.oidc_request_token\n  oidc_request_url                       = var.oidc_request_url\n  subscription_id                        = var.subscription_id\n  tenant_id                              = var.tenant_id\n  use_azure_cli_auth                     = var.use_azure_cli_auth\n\n  allowed_inbound_ip_addresses           = var.allowed_inbound_ip_addresses\n  build_resource_group_name              = var.build_resource_group_name\n  image_publisher                        = split(\":\", local.source_image_marketplace_sku)[0]\n  image_offer                            = split(\":\", local.source_image_marketplace_sku)[1]\n  image_sku                              = split(\":\", local.source_image_marketplace_sku)[2]\n  image_version                          = var.source_image_version\n  location                               = var.location\n  managed_image_name                     = var.managed_image_name\n  managed_image_resource_group_name      = var.managed_image_resource_group_name\n  managed_image_storage_account_type     = var.managed_image_storage_account_type\n  os_disk_size_gb                        = local.os_disk_size_gb\n  os_type                                = var.image_os_type\n  private_virtual_network_with_public_ip = var.private_virtual_network_with_public_ip\n  ssh_clear_authorized_keys              = var.ssh_clear_authorized_keys\n  temp_resource_group_name               = var.temp_resource_group_name\n  virtual_network_name                   = var.virtual_network_name\n  virtual_network_resource_group_name    = var.virtual_network_resource_group_name\n  virtual_network_subnet_name            = var.virtual_network_subnet_name\n  vm_size                                = var.vm_size\n  winrm_username                         = var.winrm_username\n\n  shared_image_gallery_destination {\n    subscription                         = var.subscription_id\n    gallery_name                         = var.gallery_name\n    resource_group                       = var.gallery_resource_group_name\n    image_name                           = var.gallery_image_name\n    image_version                        = var.gallery_image_version\n    storage_account_type                 = var.gallery_storage_account_type\n  }\n\n  dynamic \"azure_tag\" {\n    for_each = var.azure_tags\n    content {\n      name  = azure_tag.key\n      value = azure_tag.value\n    }\n  }\n}\n"
  },
  {
    "path": "images/ubuntu/templates/variable.ubuntu.pkr.hcl",
    "content": "// Authentication related variables\nvariable \"client_cert_path\" {\n  type    = string\n  default = \"${env(\"ARM_CLIENT_CERT_PATH\")}\"\n}\nvariable \"client_id\" {\n  type    = string\n  default = \"${env(\"ARM_CLIENT_ID\")}\"\n}\nvariable \"client_secret\" {\n  type      = string\n  default   = \"${env(\"ARM_CLIENT_SECRET\")}\"\n  sensitive = true\n}\nvariable \"object_id\" {\n  type    = string\n  default = \"${env(\"ARM_OBJECT_ID\")}\"\n}\nvariable \"oidc_request_token\" {\n  type    = string\n  default = \"\"\n}\nvariable \"oidc_request_url\" {\n  type    = string\n  default = \"\"\n}\nvariable \"subscription_id\" {\n  type    = string\n  default = \"${env(\"ARM_SUBSCRIPTION_ID\")}\"\n}\nvariable \"tenant_id\" {\n  type    = string\n  default = \"${env(\"ARM_TENANT_ID\")}\"\n}\nvariable \"use_azure_cli_auth\" {\n  type    = bool\n  default = false\n}\n\n// Azure environment related variables\nvariable \"allowed_inbound_ip_addresses\" {\n  type    = list(string)\n  default = []\n}\nvariable \"azure_tags\" {\n  type    = map(string)\n  default = {}\n}\nvariable \"build_resource_group_name\" {\n  type    = string\n  default = \"${env(\"BUILD_RG_NAME\")}\"\n}\nvariable \"gallery_image_name\" {\n  type    = string\n  default = \"${env(\"GALLERY_IMAGE_NAME\")}\"\n}\nvariable \"gallery_image_version\" {\n  type    = string\n  default = \"${env(\"GALLERY_IMAGE_VERSION\")}\"\n}\nvariable \"gallery_name\" {\n  type    = string\n  default = \"${env(\"GALLERY_NAME\")}\"\n}\nvariable \"gallery_resource_group_name\" {\n  type    = string\n  default = \"${env(\"GALLERY_RG_NAME\")}\"\n}\nvariable \"gallery_storage_account_type\" {\n  type    = string\n  default = \"${env(\"GALLERY_STORAGE_ACCOUNT_TYPE\")}\"\n}\nvariable \"image_os_type\" {\n  type    = string\n  default = \"Linux\"\n}\nvariable \"location\" {\n  type    = string\n  default = \"\"\n}\nvariable \"managed_image_name\" {\n  type    = string\n  default = \"\"\n}\nvariable \"managed_image_resource_group_name\" {\n  type    = string\n  default = \"${env(\"ARM_RESOURCE_GROUP\")}\"\n}\nvariable \"managed_image_storage_account_type\" {\n  type    = string\n  default = \"Premium_LRS\"\n}\nvariable \"private_virtual_network_with_public_ip\" {\n  type    = bool\n  default = false\n}\nvariable \"os_disk_size_gb\" {\n  type    = number\n  default = null\n}\nvariable \"source_image_version\" {\n  type    = string\n  default = \"latest\"\n}\nvariable \"ssh_clear_authorized_keys\" {\n  type    = bool\n  default = true\n}\nvariable \"temp_resource_group_name\" {\n  type    = string\n  default = \"${env(\"TEMP_RESOURCE_GROUP_NAME\")}\"\n}\nvariable \"virtual_network_name\" {\n  type    = string\n  default = \"${env(\"VNET_NAME\")}\"\n}\nvariable \"virtual_network_resource_group_name\" {\n  type    = string\n  default = \"${env(\"VNET_RESOURCE_GROUP\")}\"\n}\nvariable \"virtual_network_subnet_name\" {\n  type    = string\n  default = \"${env(\"VNET_SUBNET\")}\"\n}\nvariable \"vm_size\" {\n  type    = string\n  default = \"Standard_D4s_v4\"\n}\nvariable \"winrm_username\" {         // The username used to connect to the VM via WinRM\n    type    = string                // Also applies to the username used to create the VM\n    default = \"packer\"\n}\n\n// Image related variables\nvariable \"helper_script_folder\" {\n  type    = string\n  default = \"/imagegeneration/helpers\"\n}\nvariable \"image_folder\" {\n  type    = string\n  default = \"/imagegeneration\"\n}\nvariable \"image_os\" {\n  type    = string\n  default = \"\"\n}\nvariable \"image_version\" {\n  type    = string\n  default = \"dev\"\n}\nvariable \"imagedata_file\" {\n  type    = string\n  default = \"/imagegeneration/imagedata.json\"\n}\nvariable \"installer_script_folder\" {\n  type    = string\n  default = \"/imagegeneration/installers\"\n}\nvariable \"install_password\" {\n  type      = string\n  default   = \"\"\n  sensitive = true\n}\nvariable \"install_user\" {\n  type    = string\n  default = \"installer\"\n}\n"
  },
  {
    "path": "images/ubuntu/toolsets/toolset-2204.json",
    "content": "{\n    \"toolcache\": [\n        {\n            \"name\": \"Python\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json\",\n            \"platform\" : \"linux\",\n            \"platform_version\": \"22.04\",\n            \"arch\": \"x64\",\n            \"versions\": [\n                \"3.10.*\",\n                \"3.11.*\",\n                \"3.12.*\",\n                \"3.13.*\",\n                \"3.14.*\"\n            ]\n        },\n        {\n            \"name\": \"PyPy\",\n            \"arch\": \"x64\",\n            \"platform\" : \"linux\",\n            \"versions\": [\n                \"3.7\",\n                \"3.8\",\n                \"3.9\",\n                \"3.10\",\n                \"3.11\"\n            ]\n        },\n        {\n            \"name\": \"node\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json\",\n            \"platform\" : \"linux\",\n            \"arch\": \"x64\",\n            \"versions\": [\n                \"20.*\",\n                \"22.*\",\n                \"24.*\"\n            ]\n        },\n        {\n            \"name\": \"go\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json\",\n            \"arch\": \"x64\",\n            \"platform\" : \"linux\",\n            \"versions\": [\n                \"1.22.*\",\n                \"1.23.*\",\n                \"1.24.*\",\n                \"1.25.*\"\n            ],\n            \"default\": \"1.24.*\"\n        },\n        {\n            \"name\": \"Ruby\",\n            \"platform_version\": \"22.04\",\n            \"arch\": \"x64\",\n            \"versions\": [\n                \"3.2.*\",\n                \"3.3.*\",\n                \"3.4.*\",\n                \"4.0.*\"\n            ]\n        },\n        {\n            \"name\": \"CodeQL\",\n            \"platform\" : \"linux\",\n            \"arch\": \"x64\",\n            \"versions\": [\n                \"*\"\n            ]\n        }\n    ],\n    \"java\": {\n        \"default\": \"11\",\n        \"versions\": [ \"8\", \"11\", \"17\", \"21\", \"25\"],\n        \"maven\": \"3.9.14\"\n    },\n    \"android\": {\n        \"cmdline-tools\": \"commandlinetools-linux-9477386_latest.zip\",\n        \"platform_min_version\": \"34\",\n        \"build_tools_min_version\": \"34.0.0\",\n        \"extra_list\": [\n            \"android;m2repository\",\n            \"google;m2repository\",\n            \"google;google_play_services\"\n        ],\n        \"addon_list\": [\n        ],\n        \"additional_tools\": [\n            \"cmake;3.18.1\",\n            \"cmake;3.22.1\",\n            \"cmake;3.31.5\"\n        ],\n        \"ndk\": {\n            \"default\": \"27\",\n            \"versions\": [\n                \"27\", \"28\", \"29\"\n            ]\n        }\n    },\n    \"powershellModules\": [\n        {\"name\": \"MarkdownPS\"},\n        {\"name\": \"Microsoft.Graph\"},\n        {\"name\": \"Pester\"},\n        {\"name\": \"PSScriptAnalyzer\"}\n    ],\n    \"azureModules\": [\n        {\n            \"name\": \"az\",\n            \"versions\": [\n                \"14.6.0\"\n            ]\n        }\n    ],\n    \"apt\": {\n        \"vital_packages\": [\n            \"bzip2\",\n            \"curl\",\n            \"g++\",\n            \"gcc\",\n            \"make\",\n            \"jq\",\n            \"tar\",\n            \"unzip\",\n            \"wget\"\n        ],\n        \"common_packages\": [\n            \"autoconf\",\n            \"automake\",\n            \"dbus\",\n            \"dnsutils\",\n            \"dpkg\",\n            \"dpkg-dev\",\n            \"fakeroot\",\n            \"fonts-noto-color-emoji\",\n            \"gnupg2\",\n            \"imagemagick\",\n            \"iproute2\",\n            \"iputils-ping\",\n            \"lib32z1\",\n            \"libc++abi-dev\",\n            \"libc++-dev\",\n            \"libc6-dev\",\n            \"libcurl4\",\n            \"libgbm-dev\",\n            \"libgconf-2-4\",\n            \"libgsl-dev\",\n            \"libgtk-3-0\",\n            \"libmagic-dev\",\n            \"libmagickcore-dev\",\n            \"libmagickwand-dev\",\n            \"libsecret-1-dev\",\n            \"libsqlite3-dev\",\n            \"libyaml-dev\",\n            \"libtool\",\n            \"libunwind8\",\n            \"libxkbfile-dev\",\n            \"libxss1\",\n            \"libssl-dev\",\n            \"locales\",\n            \"mercurial\",\n            \"openssh-client\",\n            \"p7zip-rar\",\n            \"pkg-config\",\n            \"python-is-python3\",\n            \"rpm\",\n            \"texinfo\",\n            \"tk\",\n            \"tzdata\",\n            \"upx\",\n            \"xorriso\",\n            \"xvfb\",\n            \"xz-utils\",\n            \"zsync\"\n        ],\n        \"cmd_packages\": [\n            \"acl\",\n            \"aria2\",\n            \"binutils\",\n            \"bison\",\n            \"brotli\",\n            \"libnss3-tools\",\n            \"coreutils\",\n            \"file\",\n            \"findutils\",\n            \"flex\",\n            \"ftp\",\n            \"haveged\",\n            \"lz4\",\n            \"m4\",\n            \"mediainfo\",\n            \"netcat\",\n            \"net-tools\",\n            \"p7zip-full\",\n            \"parallel\",\n            \"pass\",\n            \"patchelf\",\n            \"pigz\",\n            \"pollinate\",\n            \"rsync\",\n            \"shellcheck\",\n            \"sphinxsearch\",\n            \"sqlite3\",\n            \"ssh\",\n            \"sshpass\",\n            \"subversion\",\n            \"sudo\",\n            \"systemd-coredump\",\n            \"swig\",\n            \"telnet\",\n            \"time\",\n            \"zip\"\n        ]\n    },\n    \"brew\": [\n    ],\n    \"docker\": {\n        \"components\": [\n            {\n                \"package\": \"containerd.io\",\n                \"version\": \"latest\"\n            },\n            {\n                \"package\": \"docker-ce-cli\",\n                \"version\": \"28.0.4\"\n            },\n            {\n                \"package\": \"docker-ce\",\n                \"version\": \"28.0.4\"\n            }\n        ],\n        \"plugins\": [\n            {\n                \"plugin\": \"buildx\",\n                \"version\": \"latest\",\n                \"asset\": \"linux-amd64\"\n            },\n            {\n                \"plugin\": \"compose\",\n                \"version\": \"2.38.2\",\n                \"asset\": \"linux-x86_64\"\n            }\n        ]\n    },\n    \"pipx\": [\n        {\n            \"package\": \"yamllint\",\n            \"cmd\": \"yamllint\"\n        },\n        {\n            \"package\": \"ansible-core\",\n            \"cmd\": \"ansible\"\n        }\n    ],\n    \"dotnet\": {\n        \"versions\": [\n            \"8.0\",\n            \"9.0\",\n            \"10.0\"\n        ],\n        \"tools\": [\n            { \"name\": \"nbgv\", \"test\": \"nbgv --version\", \"getversion\" : \"nbgv --version\" }\n        ]\n    },\n    \"clang\": {\n        \"versions\": [\n            \"13\",\n            \"14\",\n            \"15\"\n        ],\n        \"default_version\": \"14\"\n    },\n    \"gcc\": {\n        \"versions\": [\n            \"g++-10\",\n            \"g++-12\"\n        ]\n    },\n    \"gfortran\": {\n        \"versions\": [\n            \"gfortran-9\",\n            \"gfortran-10\",\n            \"gfortran-12\"\n        ]\n    },\n    \"php\": {\n        \"versions\": [\n            \"8.1\"\n        ]\n    },\n    \"rubygems\": [\n        {\"name\": \"fastlane\"}\n    ],\n    \"selenium\": {\n        \"version\": \"4\"\n    },\n    \"node\": {\n        \"default\": \"20\"\n    },\n    \"node_modules\": [\n        {\n            \"name\": \"grunt\",\n            \"command\": \"grunt\"\n        },\n        {\n            \"name\": \"gulp\",\n            \"command\": \"gulp\"\n        },\n        {\n            \"name\": \"n\",\n            \"command\": \"n\"\n        },\n        {\n            \"name\": \"parcel\",\n            \"command\": \"parcel\"\n        },\n        {\n            \"name\": \"typescript\",\n            \"command\": \"tsc\"\n        },\n        {\n            \"name\": \"newman\",\n            \"command\": \"newman\"\n        },\n        {\n            \"name\": \"vercel\",\n            \"command\": \"vercel\"\n        },\n        {\n            \"name\": \"webpack\",\n            \"command\": \"webpack\"\n        },\n        {\n            \"name\": \"webpack-cli\",\n            \"command\": \"webpack-cli\"\n        },\n        {\n            \"name\": \"netlify-cli\",\n            \"command\": \"netlify\"\n        },\n        {\n            \"name\": \"lerna\",\n            \"command\": \"lerna\"\n        },\n        {\n            \"name\": \"yarn\",\n            \"command\": \"yarn\"\n        }\n    ],\n     \"postgresql\": {\n        \"version\": \"14\"\n    },\n    \"pwsh\": {\n        \"version\": \"7.4\"\n    }\n}\n"
  },
  {
    "path": "images/ubuntu/toolsets/toolset-2404.json",
    "content": "{\n    \"toolcache\": [\n        {\n            \"name\": \"Python\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json\",\n            \"platform\" : \"linux\",\n            \"platform_version\": \"24.04\",\n            \"arch\": \"x64\",\n            \"versions\": [\n                \"3.10.*\",\n                \"3.11.*\",\n                \"3.12.*\",\n                \"3.13.*\",\n                \"3.14.*\"\n            ]\n        },\n        {\n            \"name\": \"PyPy\",\n            \"arch\": \"x64\",\n            \"platform\" : \"linux\",\n            \"versions\": [\n                \"3.9\",\n                \"3.10\",\n                \"3.11\"\n            ]\n        },\n        {\n            \"name\": \"node\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json\",\n            \"platform\" : \"linux\",\n            \"arch\": \"x64\",\n            \"versions\": [\n                \"20.*\",\n                \"22.*\",\n                \"24.*\"\n            ]\n        },\n        {\n            \"name\": \"go\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json\",\n            \"arch\": \"x64\",\n            \"platform\" : \"linux\",\n            \"versions\": [\n                \"1.22.*\",\n                \"1.23.*\",\n                \"1.24.*\",\n                \"1.25.*\"\n            ],\n            \"default\": \"1.24.*\"\n        },\n        {\n            \"name\": \"Ruby\",\n            \"platform_version\": \"24.04\",\n            \"arch\": \"x64\",\n            \"versions\": [\n                \"3.2.*\",\n                \"3.3.*\",\n                \"3.4.*\",\n                \"4.0.*\"\n            ]\n        },\n        {\n            \"name\": \"CodeQL\",\n            \"platform\" : \"linux\",\n            \"arch\": \"x64\",\n            \"versions\": [\n                \"*\"\n            ]\n        }\n    ],\n    \"java\": {\n        \"default\": \"17\",\n        \"versions\": [ \"8\", \"11\", \"17\", \"21\", \"25\"],\n        \"maven\": \"3.9.14\"\n    },\n    \"android\": {\n        \"cmdline-tools\": \"commandlinetools-linux-11076708_latest.zip\",\n        \"platform_min_version\": \"34\",\n        \"build_tools_min_version\": \"34.0.0\",\n        \"extra_list\": [\n            \"android;m2repository\",\n            \"google;m2repository\",\n            \"google;google_play_services\"\n        ],\n        \"addon_list\": [\n        ],\n        \"additional_tools\": [\n            \"cmake;3.31.5\",\n            \"cmake;4.1.2\"\n        ],\n        \"ndk\": {\n            \"default\": \"27\",\n            \"versions\": [\n                \"27\", \"28\", \"29\"\n            ]\n        }\n    },\n    \"powershellModules\": [\n        {\"name\": \"Microsoft.Graph\"},\n        {\"name\": \"Pester\"},\n        {\"name\": \"PSScriptAnalyzer\"}\n    ],\n    \"azureModules\": [\n        {\n            \"name\": \"az\",\n            \"versions\": [\n                \"14.6.0\"\n            ]\n        }\n    ],\n    \"apt\": {\n        \"vital_packages\": [\n            \"bzip2\",\n            \"curl\",\n            \"g++\",\n            \"gcc\",\n            \"make\",\n            \"jq\",\n            \"tar\",\n            \"unzip\",\n            \"wget\"\n        ],\n        \"common_packages\": [\n            \"autoconf\",\n            \"automake\",\n            \"dbus\",\n            \"dnsutils\",\n            \"dpkg\",\n            \"dpkg-dev\",\n            \"fakeroot\",\n            \"fonts-noto-color-emoji\",\n            \"gnupg2\",\n            \"iproute2\",\n            \"iputils-ping\",\n            \"libyaml-dev\",\n            \"libtool\",\n            \"libssl-dev\",\n            \"libsqlite3-dev\",\n            \"locales\",\n            \"mercurial\",\n            \"openssh-client\",\n            \"p7zip-rar\",\n            \"pkg-config\",\n            \"python-is-python3\",\n            \"rpm\",\n            \"texinfo\",\n            \"tk\",\n            \"tree\",\n            \"tzdata\",\n            \"upx\",\n            \"xvfb\",\n            \"xz-utils\",\n            \"zsync\"\n        ],\n        \"cmd_packages\": [\n            \"acl\",\n            \"aria2\",\n            \"binutils\",\n            \"bison\",\n            \"brotli\",\n            \"libnss3-tools\",\n            \"coreutils\",\n            \"file\",\n            \"findutils\",\n            \"flex\",\n            \"ftp\",\n            \"haveged\",\n            \"lz4\",\n            \"m4\",\n            \"mediainfo\",\n            \"netcat\",\n            \"net-tools\",\n            \"p7zip-full\",\n            \"parallel\",\n            \"patchelf\",\n            \"pigz\",\n            \"pollinate\",\n            \"rsync\",\n            \"shellcheck\",\n            \"sphinxsearch\",\n            \"sqlite3\",\n            \"ssh\",\n            \"sshpass\",\n            \"sudo\",\n            \"systemd-coredump\",\n            \"swig\",\n            \"telnet\",\n            \"time\",\n            \"zip\"\n        ]\n    },\n    \"brew\": [\n    ],\n    \"docker\": {\n        \"components\": [\n            {\n                \"package\": \"containerd.io\",\n                \"version\": \"latest\"\n            },\n            {\n                \"package\": \"docker-ce-cli\",\n                \"version\": \"28.0.4\"\n            },\n            {\n                \"package\": \"docker-ce\",\n                \"version\": \"28.0.4\"\n            }\n        ],\n        \"plugins\": [\n            {\n                \"plugin\": \"buildx\",\n                \"version\": \"latest\",\n                \"asset\": \"linux-amd64\"\n            },\n            {\n                \"plugin\": \"compose\",\n                \"version\": \"2.38.2\",\n                \"asset\": \"linux-x86_64\"\n            }\n        ]\n    },\n    \"pipx\": [\n        {\n            \"package\": \"yamllint\",\n            \"cmd\": \"yamllint\"\n        },\n        {\n            \"package\": \"ansible-core\",\n            \"cmd\": \"ansible\"\n        }\n    ],\n    \"dotnet\": {\n        \"versions\": [\n            \"8.0\",\n            \"9.0\",\n            \"10.0\"\n        ],\n        \"tools\": [\n            { \"name\": \"nbgv\", \"test\": \"nbgv --version\", \"getversion\" : \"nbgv --version\" }\n        ]\n    },\n    \"clang\": {\n        \"versions\": [\n            \"16\",\n            \"17\",\n            \"18\"\n        ],\n        \"default_version\": \"18\"\n    },\n    \"gcc\": {\n        \"versions\": [\n            \"g++-12\",\n            \"g++-13\",\n            \"g++-14\"\n        ]\n    },\n    \"gfortran\": {\n        \"versions\": [\n            \"gfortran-12\",\n            \"gfortran-13\",\n            \"gfortran-14\"\n        ]\n    },\n    \"php\": {\n        \"versions\": [\n            \"8.3\"\n        ]\n    },\n    \"rubygems\": [\n        {\"name\": \"fastlane\"}\n    ],\n    \"selenium\": {\n        \"version\": \"4\"\n    },\n    \"node\": {\n        \"default\": \"20\"\n    },\n    \"node_modules\": [\n        {\n            \"name\": \"grunt\",\n            \"command\": \"grunt\"\n        },\n        {\n            \"name\": \"gulp\",\n            \"command\": \"gulp\"\n        },\n        {\n            \"name\": \"n\",\n            \"command\": \"n\"\n        },\n        {\n            \"name\": \"parcel\",\n            \"command\": \"parcel\"\n        },\n        {\n            \"name\": \"typescript\",\n            \"command\": \"tsc\"\n        },\n        {\n            \"name\": \"newman\",\n            \"command\": \"newman\"\n        },\n        {\n            \"name\": \"webpack\",\n            \"command\": \"webpack\"\n        },\n        {\n            \"name\": \"webpack-cli\",\n            \"command\": \"webpack-cli\"\n        },\n        {\n            \"name\": \"lerna\",\n            \"command\": \"lerna\"\n        },\n        {\n            \"name\": \"yarn\",\n            \"command\": \"yarn\"\n        }\n    ],\n    \"postgresql\": {\n        \"version\": \"16\"\n    },\n    \"pwsh\": {\n        \"version\": \"7.4\"\n    }\n}\n"
  },
  {
    "path": "images/ubuntu-slim/Dockerfile",
    "content": "FROM ubuntu:24.04 AS base\n\nARG IMAGE_VERSION=1.0.0\nARG IMAGE_OWNER=\"GitHub\"\n\nENV IMAGE_OWNER=$IMAGE_OWNER\nENV ImageVersion=$IMAGE_VERSION\nENV IMAGE_VERSION=$IMAGE_VERSION\nENV ImageOS=\"Linux\"\nENV IMAGE_TARGET_PLATFORM=\"GitHub\"\nENV POWERSHELL_DISTRIBUTION_CHANNEL=\"GitHub-Actions-$ImageOS\"\nENV IMAGEDATA_NAME=\"ubuntu:24.04\"\nENV NVM_DIR=\"/etc/skel/.nvm\"\nENV HELPER_SCRIPTS=\"/tmp/scripts/helpers\"\nENV INSTALLER_SCRIPT_FOLDER=\"/tmp/toolsets\"\n\n# Avoid interactive prompts\nENV DEBIAN_FRONTEND=noninteractive\n\nCOPY scripts/build /tmp/scripts/build\nCOPY scripts/helpers /tmp/scripts/helpers\nCOPY toolsets/ /tmp/toolsets/\nRUN find /tmp/scripts -name \"*.sh\" -type f -exec chmod +x {} \\;\n\nCOPY scripts/entrypoint.sh /opt/entrypoint.sh\nRUN chmod +x /opt/entrypoint.sh\n\nRUN echo 'set -eo pipefail' >> /etc/bash.bashrc\n\nRUN apt-get update && apt-get upgrade -y && apt-get install -y sudo lsb-release jq dpkg && \\\n    touch /run/.containerenv && \\\n    /tmp/scripts/build/configure-apt-sources.sh && \\\n    /tmp/scripts/build/configure-apt.sh && \\\n    /tmp/scripts/build/install-apt-vital.sh && \\\n    /tmp/scripts/build/install-ms-repos.sh && \\\n    /tmp/scripts/build/configure-image-data-file.sh && \\\n    /tmp/scripts/build/configure-environment.sh && \\\n    /tmp/scripts/build/install-actions-cache.sh && \\\n    /tmp/scripts/build/install-apt-common.sh && \\\n    /tmp/scripts/build/install-azcopy.sh && \\\n    /tmp/scripts/build/install-azure-cli.sh && \\\n    /tmp/scripts/build/install-azure-devops-cli.sh && \\\n    /tmp/scripts/build/install-bicep.sh && \\\n    /tmp/scripts/build/install-aws-tools.sh && \\\n    /tmp/scripts/build/install-git.sh && \\\n    /tmp/scripts/build/install-git-lfs.sh && \\\n    /tmp/scripts/build/install-github-cli.sh && \\\n    /tmp/scripts/build/install-google-cloud-cli.sh && \\\n    /tmp/scripts/build/install-nvm.sh && \\\n    /tmp/scripts/build/install-nodejs.sh && \\\n    /tmp/scripts/build/install-powershell.sh && \\\n    /tmp/scripts/build/configure-dpkg.sh && \\\n    /tmp/scripts/build/install-yq.sh && \\\n    /tmp/scripts/build/install-python.sh && \\\n    /tmp/scripts/build/install-zstd.sh && \\\n    /tmp/scripts/build/install-pipx-packages.sh && \\\n    /tmp/scripts/build/install-docker-cli.sh && \\\n    /tmp/scripts/build/configure-system.sh && \\\n    /tmp/scripts/helpers/cleanup.sh\n\nRUN sed -i '/set -eo pipefail/d' /etc/bash.bashrc\n\nENTRYPOINT [\"/opt/entrypoint.sh\"]\n\nCMD [ \"bash\" ]\n"
  },
  {
    "path": "images/ubuntu-slim/generate-software-report.sh",
    "content": "#!/bin/bash -e\n\nshow_help() {\n    echo \"Usage: $0 [IMAGE_NAME]\"\n    echo \"\"\n    echo \"Generate a software report for a Docker image.\"\n    echo \"\"\n    echo \"Arguments:\"\n    echo \"  IMAGE_NAME    Docker image name to generate report for (default: ubuntu-slim:test)\"\n    echo \"\"\n    echo \"Examples:\"\n    echo \"  $0                           # Generate report for ubuntu-slim:test (builds image first)\"\n    echo \"  $0 my-registry/ubuntu:latest # Generate report for existing image\"\n    echo \"  $0 ubuntu-slim:v1.2.3        # Generate report for tagged image\"\n    echo \"\"\n    echo \"Options:\"\n    echo \"  -h, --help    Show this help message\"\n}\n\n# Handle help flags\nif [[ \"$1\" == \"-h\" || \"$1\" == \"--help\" ]]; then\n    show_help\n    exit 0\nfi\n\n# Set the image name from parameter or use default\nIMAGE_NAME=\"${1:-ubuntu-slim:test}\"\n\n# Build the image only if using the default name (for backward compatibility)\nif [[ \"$IMAGE_NAME\" == \"ubuntu-slim:test\" ]]; then\n    echo \"Building image: $IMAGE_NAME\"\n    docker build --debug --progress plain -t \"$IMAGE_NAME\" .\nelse\n    # Check if the image exists\n    if ! docker image inspect \"$IMAGE_NAME\" >/dev/null 2>&1; then\n        echo \"Error: Image '$IMAGE_NAME' does not exist. Please build it first or provide a valid image name.\"\n        echo \"Run '$0 --help' for usage information.\"\n        exit 1\n    fi\nfi\n\necho \"Generating software report for image: $IMAGE_NAME\"\n\n# Get the script directory\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nBASE_DIR=\"$(cd ../../helpers/software-report-base && pwd)\"\n\necho $BASE_DIR\n\n# Create a temporary directory for output\nOUTPUT_DIR=$(mktemp -d)\necho \"Using temporary directory: $OUTPUT_DIR\"\n\n# Run the container and execute the PowerShell script inside it\necho \"Running Generate-SoftwareReport.ps1 inside the container...\"\ndocker run --rm \\\n    -v \"$OUTPUT_DIR:/output\" \\\n    -v \"$SCRIPT_DIR/scripts/docs-gen:/scripts/docs-gen:ro\" \\\n    -v \"$BASE_DIR:/scripts/software-report-base:ro\" \\\n    \"$IMAGE_NAME\" \\\n    pwsh /scripts/docs-gen/Generate-SoftwareReport.ps1 -OutputDirectory /output\n\nif [ -f \"$OUTPUT_DIR/software-report.md\" ]; then\n    cp \"$OUTPUT_DIR/software-report.md\" ubuntu-slim-Readme.md\n    echo \"✓ Copied software-report.md to current directory\"\nelse\n    echo \"✗ Error: software-report.md was not generated\"\n    rm -rf \"$OUTPUT_DIR\"\n    exit 1\nfi\n\nif [ -f \"$OUTPUT_DIR/software-report.json\" ]; then\n    cp \"$OUTPUT_DIR/software-report.json\" ubuntu-slim-Report.json\n    echo \"✓ Copied software-report.json to current directory\"\nelse\n    echo \"✗ Error: software-report.json was not generated\"\n    rm -rf \"$OUTPUT_DIR\"\n    exit 1\nfi\n\n# Clean up temporary directory\nrm -rf \"$OUTPUT_DIR\"\necho \"✓ Software report generation complete\"\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/configure-apt-sources.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-apt-sources.sh\n##  Desc:  Configure apt sources with failover from Azure to Ubuntu archives.\n################################################################################\n\nsource $HELPER_SCRIPTS/os.sh\n\ntouch /etc/apt/apt-mirrors.txt\n\nprintf \"http://azure.archive.ubuntu.com/ubuntu/\\tpriority:1\\n\" | tee -a /etc/apt/apt-mirrors.txt\nprintf \"https://archive.ubuntu.com/ubuntu/\\tpriority:2\\n\" | tee -a /etc/apt/apt-mirrors.txt\nprintf \"https://security.ubuntu.com/ubuntu/\\tpriority:3\\n\" | tee -a /etc/apt/apt-mirrors.txt\n\nif is_ubuntu24; then\n    sed -i 's|http://archive\\.ubuntu\\.com/ubuntu/|mirror+file:/etc/apt/apt-mirrors.txt|' /etc/apt/sources.list.d/ubuntu.sources\nelse\n    sed -i 's|http://archive\\.ubuntu\\.com/ubuntu/|mirror+file:/etc/apt/apt-mirrors.txt|' /etc/apt/sources.list\nfi\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/configure-apt.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-apt.sh\n##  Desc:  Configure apt, install jq and apt-fast packages.\n################################################################################\n\nsource $HELPER_SCRIPTS/os.sh\n\n# Stop and disable apt-daily upgrade services;\n# systemctl stop apt-daily.timer\n# systemctl disable apt-daily.timer\n# systemctl disable apt-daily.service\n# systemctl stop apt-daily-upgrade.timer\n# systemctl disable apt-daily-upgrade.timer\n# systemctl disable apt-daily-upgrade.service\n\n# Enable retry logic for apt up to 10 times\necho \"APT::Acquire::Retries \\\"10\\\";\" > /etc/apt/apt.conf.d/80-retries\n\n# Configure apt to always assume Y\necho \"APT::Get::Assume-Yes \\\"true\\\";\" > /etc/apt/apt.conf.d/90assumeyes\n\n# 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.\n# In case you have multiple systems that you want to receive the same set of updates, \n# you can set APT::Machine-ID to a UUID such that they all phase the same, \n# 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.\n# apt-cache policy pkgname\necho 'APT::Get::Always-Include-Phased-Updates \"true\";' > /etc/apt/apt.conf.d/99-phased-updates\n\n# Fix bad proxy and http headers settings\ncat <<EOF >> /etc/apt/apt.conf.d/99bad_proxy\nAcquire::http::Pipeline-Depth 0;\nAcquire::http::No-Cache true;\nAcquire::https::Pipeline-Depth 0;\nAcquire::https::No-Cache true;\nAcquire::BrokenProxy    true;\nEOF\n\necho 'APT sources'\nif ! is_ubuntu24; then\n    cat /etc/apt/sources.list\nelse\n    cat /etc/apt/sources.list.d/ubuntu.sources\nfi\n\napt-get update\n\necho \"ubuntu ALL=(ALL) NOPASSWD:ALL\" >> /etc/sudoers\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/configure-dpkg.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-dpkg.sh\n##  Desc:  Configure dpkg\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\nsource $HELPER_SCRIPTS/os.sh\n# This is the anti-frontend. It never interacts with you  at  all,\n# and  makes  the  default  answers  be used for all questions. It\n# might mail error messages to root, but that's it;  otherwise  it\n# is  completely  silent  and  unobtrusive, a perfect frontend for\n# automatic installs. If you are using this front-end, and require\n# non-default  answers  to questions, you will need to pre-seed the\n# debconf database\nset_etc_environment_variable \"DEBIAN_FRONTEND\" \"noninteractive\"\n\n# dpkg can be instructed not to ask for confirmation\n# when replacing a configuration file (with the --force-confdef --force-confold options)\ncat <<EOF >> /etc/apt/apt.conf.d/10dpkg-options\nDpkg::Options {\n  \"--force-confdef\";\n  \"--force-confold\";\n}\nEOF\n\n# hide information about packages that are no longer required\ncat <<EOF >> /etc/apt/apt.conf.d/10apt-autoremove\nAPT::Get::AutomaticRemove \"0\";\nAPT::Get::HideAutoRemove \"1\";\nEOF\n\n# Install libicu70 package for Ubuntu 24\nif  is_ubuntu24 ; then\n  wget https://archive.ubuntu.com/ubuntu/pool/main/i/icu/libicu70_70.1-2_amd64.deb\n\n  EXPECTED_LIBICU_SHA512=\"a6315482d93606e375c272718d2458870b95e4ed4b672ea8640cf7bc2d2c2f41aea13b798b1e417e1ffc472a90c6aad150d3d293aa9bddec48e39106e4042807\"\n  ACTUAL_LIBICU_SHA512=\"$(sha512sum \"./libicu70_70.1-2_amd64.deb\" | awk '{print $1}')\"\n  [ \"$EXPECTED_LIBICU_SHA512\" = \"$ACTUAL_LIBICU_SHA512\" ] || { echo \"libicu checksum mismatch in configure-dpkg.sh\"; exit 1;}\n  sudo apt-get install -y ./libicu70_70.1-2_amd64.deb\nfi\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/configure-environment.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  configure-environment.sh\n##  Desc:  Configure system and environment\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/os.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\nwhoami\n\n# Set ImageVersion and ImageOS env variables\nset_etc_environment_variable \"ImageVersion\" \"${IMAGE_VERSION}\"\nset_etc_environment_variable \"ImageOS\" \"${IMAGE_OS}\"\n\n# Set the ACCEPT_EULA variable to Y value to confirm your acceptance of the End-User Licensing Agreement\nset_etc_environment_variable \"ACCEPT_EULA\" \"Y\"\n\n# This directory is supposed to be created in $HOME and owned by user(https://github.com/actions/runner-images/issues/491)\nmkdir -p /etc/skel/.config/configstore\nset_etc_environment_variable \"XDG_CONFIG_HOME\" '$HOME/.config'\n\n# Prepare directory and env variable for toolcache\necho \"Setting up AGENT_TOOLSDIRECTORY and RUNNER_TOOL_CACHE variable to /opt/hostedtoolcache\"\nAGENT_TOOLSDIRECTORY=/opt/hostedtoolcache\nmkdir $AGENT_TOOLSDIRECTORY\nset_etc_environment_variable \"AGENT_TOOLSDIRECTORY\" \"${AGENT_TOOLSDIRECTORY}\"\nset_etc_environment_variable \"RUNNER_TOOL_CACHE\" \"${AGENT_TOOLSDIRECTORY}\"\nchmod -R 777 $AGENT_TOOLSDIRECTORY\n\n# https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html\n# https://www.suse.com/support/kb/doc/?id=000016692\necho 'vm.max_map_count=262144' | tee -a /etc/sysctl.conf\n\n# https://kind.sigs.k8s.io/docs/user/known-issues/#pod-errors-due-to-too-many-open-files\necho 'fs.inotify.max_user_watches=655360' | tee -a /etc/sysctl.conf\necho 'fs.inotify.max_user_instances=1280' | tee -a /etc/sysctl.conf\n\n# https://github.com/actions/runner-images/issues/9491\necho 'vm.mmap_rnd_bits=28' | tee -a /etc/sysctl.conf\n\n# https://github.com/actions/runner-images/pull/7860\nnetfilter_rule='/etc/udev/rules.d/50-netfilter.rules'\nrules_directory=\"$(dirname \"${netfilter_rule}\")\"\nmkdir -p $rules_directory\ntouch $netfilter_rule\necho 'ACTION==\"add\", SUBSYSTEM==\"module\", KERNEL==\"nf_conntrack\", RUN+=\"/usr/sbin/sysctl net.netfilter.nf_conntrack_tcp_be_liberal=1\"' | tee -a $netfilter_rule\n\n# Remove fwupd if installed. We're running on VMs in Azure and the fwupd package is not needed.\n# Leaving it enable means periodic refreshes show in network traffic and firewall logs\n# Check if fwupd-refresh.timer exists in systemd\nif systemctl list-unit-files fwupd-refresh.timer &>/dev/null; then\n    echo \"Masking fwupd-refresh.timer...\"\n    systemctl mask fwupd-refresh.timer\nfi\n\n# This is a legacy check, leaving for earlier versions of Ubuntu\n# If fwupd config still exists, disable the motd updates\nif [[ -f \"/etc/fwupd/daemon.conf\" ]]; then\n    sed -i 's/UpdateMotd=true/UpdateMotd=false/g' /etc/fwupd/daemon.conf\nfi\n\n# Disable to load providers\n# https://github.com/microsoft/azure-pipelines-agent/issues/3834\nif is_ubuntu22; then\n    sed -i 's/openssl_conf = openssl_init/#openssl_conf = openssl_init/g' /etc/ssl/openssl.cnf\nfi\n\n# # Disable man-db auto update\n# echo \"set man-db/auto-update false\" | debconf-communicate\n# dpkg-reconfigure man-db\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/configure-image-data-file.sh",
    "content": "#!/bin/bash -e\n\nfunction create_imagedata_json() {\n\n  arch=$(uname -m)\n  if [[ $arch == \"x86_64\" ]]; then\n    arch=\"x64\"\n  elif [[ $arch == \"aarch64\" ]]; then\n    arch=\"arm64\"\n  else\n    echo \"Unsupported architecture: $arch\"\n    exit 1\n  fi\n\n  if [[ -n \"$IMAGEDATA_INCLUDED_SOFTWARE\" ]]; then\n    included_software=\"- Included Software: ${IMAGEDATA_INCLUDED_SOFTWARE}\"\n  fi\n\n  imagedata_file=\"/imagegeneration/imagedata.json\"\n\n  cat <<EOF > $imagedata_file\n[\n  {\n    \"group\": \"VM Image\",\n    \"detail\": \"- OS: Linux (${arch})\\n- Source: Docker\\n- Name: ${IMAGEDATA_NAME}\\n- Version: ${IMAGE_VERSION}\\n${included_software}\"\n  }\n]\nEOF\n\n}\n\nmkdir -p /imagegeneration\n\n# Generate the imagedata JSON file displayed on workflow initialization\nif [[ -n \"$IMAGEDATA_NAME\" ]]; then\n  echo \"Generating imagedata JSON file\"\n  create_imagedata_json\nelse\n  echo \"IMAGEDATA_NAME is null or empty. Skipping imagedata JSON generation.\"\nfi"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/configure-system.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File: configure-system.sh\n##  Desc: Post deployment system configuration actions\n################################################################################\n\nsource $HELPER_SCRIPTS/etc-environment.sh\nsource $HELPER_SCRIPTS/os.sh\n\necho \"chmod -R 777 /opt\"\nchmod -R 777 /opt\necho \"chmod -R 777 /usr/share\"\nchmod -R 777 /usr/share\n\n# Remove quotes around PATH\nENVPATH=$(grep 'PATH=' /etc/environment | head -n 1 | sed -z 's/^PATH=*//')\nENVPATH=${ENVPATH#\"\\\"\"}\nENVPATH=${ENVPATH%\"\\\"\"}\nreplace_etc_environment_variable \"PATH\" \"${ENVPATH}\"\necho \"Updated /etc/environment: $(cat /etc/environment)\"\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-actions-cache.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:       install-actions-cache.sh\n##  Desc:       Download latest release from https://github.com/actions/action-versions\n##  Maintainer: #actions-runtime and @TingluoHuang\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# Prepare directory and env variable for ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE\nACTION_ARCHIVE_CACHE_DIR=/opt/actionarchivecache\nmkdir -p $ACTION_ARCHIVE_CACHE_DIR\nchmod -R 777 $ACTION_ARCHIVE_CACHE_DIR\necho \"Setting up ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE variable to ${ACTION_ARCHIVE_CACHE_DIR}\"\nset_etc_environment_variable \"ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE\" \"${ACTION_ARCHIVE_CACHE_DIR}\"\n\n# Download latest release from github.com/actions/action-versions and untar to /opt/actionarchivecache\ndownload_url=$(resolve_github_release_asset_url \"actions/action-versions\" \"endswith(\\\"action-versions.tar.gz\\\")\" \"latest\")\narchive_path=$(download_with_retry \"$download_url\")\ntar -xzf \"$archive_path\" -C $ACTION_ARCHIVE_CACHE_DIR\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-apt-common.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-apt-common.sh\n##  Desc:  Install basic command line utilities and dev packages\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\ncommon_packages=$(get_toolset_value .apt.common_packages[])\ncmd_packages=$(get_toolset_value .apt.cmd_packages[])\n\napt-get install --no-install-recommends $common_packages $cmd_packages\n\n# for package in $common_packages $cmd_packages; do\n#     echo \"Install $package\"\n#     apt-get install --no-install-recommends $package\n# done\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-apt-vital.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-apt-vital.sh\n##  Desc:  Install vital command line utilities\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nvital_packages=$(get_toolset_value .apt.vital_packages[])\napt-get install --no-install-recommends $vital_packages\n\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-aws-tools.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-aws-tools.sh\n##  Desc:  Install the AWS CLI, Session Manager plugin for the AWS CLI, and AWS SAM CLI\n##  Supply chain security: AWS SAM CLI - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nawscliv2_archive_path=$(download_with_retry \"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip\")\nunzip -qq \"$awscliv2_archive_path\" -d /tmp/installers/\n/tmp/installers/aws/install -i /usr/local/aws-cli -b /usr/local/bin\n\nsmplugin_deb_path=$(download_with_retry \"https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb\")\napt-get install \"$smplugin_deb_path\"\n\n# Download the latest aws sam cli release\naws_sam_cli_archive_name=\"aws-sam-cli-linux-x86_64.zip\"\nsam_cli_download_url=$(resolve_github_release_asset_url \"aws/aws-sam-cli\" \"endswith(\\\"$aws_sam_cli_archive_name\\\")\" \"latest\")\naws_sam_cli_archive_path=$(download_with_retry \"$sam_cli_download_url\")\n\n# Supply chain security - AWS SAM CLI\naws_sam_cli_hash=$(get_checksum_from_github_release \"aws/aws-sam-cli\" \"${aws_sam_cli_archive_name}.. \" \"latest\" \"SHA256\")\nuse_checksum_comparison \"$aws_sam_cli_archive_path\" \"$aws_sam_cli_hash\"\n\n# Install the latest aws sam cli release\nmkdir -p /tmp/installers/aws-sam-cli\nunzip \"$aws_sam_cli_archive_path\" -d /tmp/installers/aws-sam-cli\n/tmp/installers/aws-sam-cli/install -i /usr/local/aws-sam-cli -b /usr/local/bin\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-azcopy.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-azcopy.sh\n##  Desc:  Install AzCopy\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Install AzCopy10\narchive_path=$(download_with_retry \"https://aka.ms/downloadazcopy-v10-linux\")\ntar xzf \"$archive_path\" --strip-components=1 -C /tmp\ninstall /tmp/azcopy /usr/local/bin/azcopy\n\n# Create azcopy 10 alias for backward compatibility\nln -sf /usr/local/bin/azcopy /usr/local/bin/azcopy10\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-azure-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-azure-cli.sh\n##  Desc:  Install Azure CLI (az)\n################################################################################\n\n# Install Azure CLI (instructions taken from https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)\ncurl -fsSL https://aka.ms/InstallAzureCLIDeb | sudo bash\n\necho \"azure-cli https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-linux?pivots=apt\" >> $HELPER_SCRIPTS/apt-sources.txt\n\nrm -f /etc/apt/sources.list.d/azure-cli.list\nrm -f /etc/apt/sources.list.d/azure-cli.list.save\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-azure-devops-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-azure-devops-cli.sh\n##  Desc:  Install Azure DevOps CLI (az devops)\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\n\n# AZURE_EXTENSION_DIR shell variable defines where modules are installed\n# https://docs.microsoft.com/en-us/cli/azure/azure-cli-extensions-overview\nexport AZURE_EXTENSION_DIR=/opt/az/azcliextensions\nset_etc_environment_variable \"AZURE_EXTENSION_DIR\" \"${AZURE_EXTENSION_DIR}\"\n\n# install azure devops Cli extension\naz extension add -n azure-devops\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-bicep.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-bicep.sh\n##  Desc:  Install bicep cli\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Install Bicep CLI\ndownload_url=$(resolve_github_release_asset_url \"Azure/bicep\" \"endswith(\\\"bicep-linux-x64\\\")\" \"latest\")\nbicep_binary_path=$(download_with_retry \"${download_url}\")\n\n# Mark it as executable\ninstall \"$bicep_binary_path\" /usr/local/bin/bicep\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-docker-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-docker-cli.sh\n##  Desc:  Install Docker CLI and plugins (Compose, Buildx) but not the engine.\n##         The Docker daemon is not included since ubuntu-slim runs as a container.\n##         ubuntu-slim does not run in Privileged mode, so functionality from these tools is limited.\n##         It cannot build or run containers locally.\n################################################################################\n\ncurl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg\n\necho \\\n  \"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \\\n  $(lsb_release -cs) stable\" | tee /etc/apt/sources.list.d/docker.list > /dev/null\n\napt-get update\n\n# Install Docker CLI components (not the daemon)\n# docker-ce-cli: Docker command line interface\n# docker-buildx-plugin: Build with BuildKit\n# docker-compose-plugin: Docker Compose V2\napt-get install --no-install-recommends -y \\\n    docker-ce-cli \\\n    docker-buildx-plugin \\\n    docker-compose-plugin\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-git-lfs.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-git-lfs.sh\n##  Desc:  Install Git-lfs\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nGIT_LFS_REPO=\"https://packagecloud.io/install/repositories/github/git-lfs\"\n\n# Install git-lfs\ncurl -fsSL $GIT_LFS_REPO/script.deb.sh | bash\napt-get install git-lfs\n\n# Remove source repo's\nrm /etc/apt/sources.list.d/github_git-lfs.list\n\n# Document apt source repo's\necho \"git-lfs $GIT_LFS_REPO\" >> $HELPER_SCRIPTS/apt-sources.txt\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-git.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-git.sh\n##  Desc:  Install Git and Git-FTP\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nGIT_REPO=\"ppa:git-core/ppa\"\n\n## Install git\nadd-apt-repository $GIT_REPO -y\napt-get update\napt-get install git\n\n# Git version 2.35.2 introduces security fix that breaks action\\checkout https://github.com/actions/checkout/issues/760\ncat <<EOF >> /etc/gitconfig\n[safe]\n        directory = *\nEOF\n\n# Install git-ftp\napt-get install git-ftp\n\n# Remove source repo's\nadd-apt-repository --remove $GIT_REPO\n\n# Document apt source repo's\necho \"git-core $GIT_REPO\" >> $HELPER_SCRIPTS/apt-sources.txt\n\n# Add well-known SSH host keys to known_hosts\nssh-keyscan -t rsa,ecdsa,ed25519 github.com >> /etc/ssh/ssh_known_hosts\nssh-keyscan -t rsa ssh.dev.azure.com >> /etc/ssh/ssh_known_hosts\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-github-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-github-cli.sh\n##  Desc:  Install GitHub CLI\n##         Must be run as non-root user after homebrew\n##  Supply chain security: GitHub CLI - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Download GitHub CLI\ngh_cli_url=$(resolve_github_release_asset_url \"cli/cli\" \"contains(\\\"linux\\\") and contains(\\\"amd64\\\") and endswith(\\\".deb\\\")\" \"latest\")\ngh_cli_deb_path=$(download_with_retry \"$gh_cli_url\")\n\n# Supply chain security - GitHub CLI\nhash_url=$(resolve_github_release_asset_url \"cli/cli\" \"endswith(\\\"checksums.txt\\\")\" \"latest\")\nexternal_hash=$(get_checksum_from_url \"$hash_url\" \"linux_amd64.deb\" \"SHA256\")\nuse_checksum_comparison \"$gh_cli_deb_path\" \"$external_hash\"\n\n# Install GitHub CLI\napt-get install \"$gh_cli_deb_path\"\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-google-cloud-cli.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-google-cloud-cli.sh\n##  Desc:  Install the Google Cloud CLI\n################################################################################\n\nREPO_URL=\"https://packages.cloud.google.com/apt\"\n\n# Install the Google Cloud CLI\necho \"deb [signed-by=/usr/share/keyrings/cloud.google.gpg] $REPO_URL cloud-sdk main\" > /etc/apt/sources.list.d/google-cloud-sdk.list\nwget -qO- https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor > /usr/share/keyrings/cloud.google.gpg\napt-get update\napt-get install google-cloud-cli\n\n# remove apt\nrm /etc/apt/sources.list.d/google-cloud-sdk.list\nrm /usr/share/keyrings/cloud.google.gpg\n\n# add repo to the apt-sources.txt\necho \"google-cloud-sdk $REPO_URL\" >> $HELPER_SCRIPTS/apt-sources.txt\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-ms-repos.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-ms-repos.sh\n##  Desc:  Install official Microsoft package repos for the distribution\n################################################################################\n\nos_label=$(lsb_release -rs)\n\n# Install Microsoft repository\nwget https://packages.microsoft.com/config/ubuntu/$os_label/packages-microsoft-prod.deb\ndpkg -i packages-microsoft-prod.deb\n\n# update\napt-get install apt-transport-https ca-certificates curl software-properties-common\napt-get update\napt-get dist-upgrade\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-nodejs.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-nodejs.sh\n##  Desc:  Install Node.js LTS and related tooling (Gulp, Grunt)\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Install default Node.js\ndefault_version=$(get_toolset_value '.node.default')\n\ncurl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n -o ~/n\nsudo bash ~/n $default_version\n\n# Install node modules\nnode_modules=$(get_toolset_value '.node_modules[].name')\nif [ -n \"$node_modules\" ]; then\n    npm install -g $node_modules\nelse\n    echo \"No node modules to install\"\nfi\n\n# fix global modules installation as regular user\n# related issue https://github.com/actions/runner-images/issues/3727\nsudo chmod -R 777 /usr/local/lib/node_modules \nsudo chmod -R 777 /usr/local/bin\n\nrm -rf ~/n\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-nvm.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-nvm.sh\n##  Desc:  Install Nvm\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\n\nexport NVM_DIR=\"/etc/skel/.nvm\"\nmkdir ${NVM_DIR}\nnvm_version=$(curl -fsSL https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name')\ncurl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/$nvm_version/install.sh | bash\nset_etc_environment_variable \"NVM_DIR\" '$HOME/.nvm'\n\necho '[ -s \"$NVM_DIR/nvm.sh\" ] && \\. \"$NVM_DIR/nvm.sh\"  # This loads nvm' | tee -a /etc/skel/.bash_profile\n[ -s \"${NVM_DIR}/nvm.sh\" ] && \\. \"${NVM_DIR}/nvm.sh\"\n\necho \"source ${NVM_DIR}/nvm.sh\" | tee -a /etc/skel/.bashrc\n\n# set system node.js as default one\nnvm alias default system"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-pipx-packages.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-pipx-packages.sh\n##  Desc:  Install tools via pipx\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\nexport PATH=\"$PATH:/opt/pipx_bin\"\n\npipx_packages=$(get_toolset_value \".pipx[] .package\")\n\nif [ -z \"$pipx_packages\" ]; then  \n    echo \"No pipx packages defined in toolset. Skipping pipx installation.\"  \n    exit 0  \nfi \n\nfor package in $pipx_packages; do\n    echo \"Install $package into default python\"\n    pipx install $package\n\n    # https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html\n    # Install ansible into an existing ansible-core Virtual Environment\n    if [[ $package == \"ansible-core\" ]]; then\n        pipx inject $package ansible\n    fi\ndone\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-powershell.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-powershell.sh\n##  Desc:  Install PowerShell Core\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\nsource $HELPER_SCRIPTS/os.sh\n\npwsh_version=$(get_toolset_value .pwsh.version)\n\n# Install Powershell\n\n    apt-get install powershell=$pwsh_version*"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-python.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-python.sh\n##  Desc:  Install Python 3\n################################################################################\n\nset -e\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/etc-environment.sh\nsource $HELPER_SCRIPTS/os.sh\n\n# Install Python, Python 3, pip, pip3\napt-get install -y --no-install-recommends python3 python3-dev python3-pip python3-venv\n\nif is_ubuntu24; then\n# Create temporary workaround to allow user to continue using pip\n    sudo cat <<EOF > /etc/pip.conf\n[global]\nbreak-system-packages = true\nEOF\nfi\n\n# Install pipx\n# Set pipx custom directory\nexport PIPX_BIN_DIR=/opt/pipx_bin\nexport PIPX_HOME=/opt/pipx\n\npython3 -m pip install pipx\npython3 -m pipx ensurepath\n\n# Update /etc/environment\nset_etc_environment_variable \"PIPX_BIN_DIR\" $PIPX_BIN_DIR\nset_etc_environment_variable \"PIPX_HOME\" $PIPX_HOME\nprepend_etc_environment_path $PIPX_BIN_DIR\n\n# Adding this dir to PATH will make installed pip commands are immediately available.\nprepend_etc_environment_path '$HOME/.local/bin'"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-yq.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-yq.sh\n##  Desc:  Install yq - a command-line YAML, JSON and XML processor\n##  Supply chain security: yq - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Download yq\nyq_url=$(resolve_github_release_asset_url \"mikefarah/yq\" \"endswith(\\\"yq_linux_amd64\\\")\" \"latest\")\nbinary_path=$(download_with_retry \"${yq_url}\")\n\n# Supply chain security - yq\nhash_url=$(resolve_github_release_asset_url \"mikefarah/yq\" \"endswith(\\\"checksums\\\")\" \"latest\")\nexternal_hash=$(get_checksum_from_url \"${hash_url}\" \"yq_linux_amd64 \" \"SHA256\" \"true\" \" \" \"19\")\nuse_checksum_comparison \"$binary_path\" \"$external_hash\"\n\n# Install yq\ninstall \"$binary_path\" /usr/bin/yq\n\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/build/install-zstd.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install-zstd.sh\n##  Desc:  Install zstd\n##  Supply chain security: zstd - checksum validation\n################################################################################\n\n# Source the helpers for use with the script\nsource $HELPER_SCRIPTS/install.sh\n\n# Download zstd\nrelease_tag=$(curl -fsSL https://api.github.com/repos/facebook/zstd/releases/latest | jq -r '.tag_name')\nrelease_name=\"zstd-${release_tag//v}\"\ndownload_url=\"https://github.com/facebook/zstd/releases/download/${release_tag}/${release_name}.tar.gz\"\narchive_path=$(download_with_retry \"${download_url}\")\n\n# Supply chain security - zstd\nexternal_hash=$(get_checksum_from_url \"${download_url}.sha256\" \"${release_name}.tar.gz\" \"SHA256\")\nuse_checksum_comparison \"$archive_path\" \"$external_hash\"\n\n# Install zstd\napt-get install liblz4-dev\ntar xzf \"$archive_path\" -C /tmp\n\nmake -C \"/tmp/${release_name}/contrib/pzstd\" -j $(nproc) all\nmake -C \"/tmp/${release_name}\" -j $(nproc) zstd-release\n\nfor copyprocess in zstd zstdless zstdgrep; do\n    cp \"/tmp/${release_name}/programs/${copyprocess}\" /usr/local/bin/\ndone\n\ncp \"/tmp/${release_name}/contrib/pzstd/pzstd\" /usr/local/bin/\n\nfor symlink in zstdcat zstdmt unzstd; do\n    ln -sf /usr/local/bin/zstd /usr/local/bin/${symlink}\ndone\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/docs-gen/Common.Helpers.psm1",
    "content": "function Get-CommandResult {\n    <#\n    .SYNOPSIS\n        Runs a command in bash and returns the output and exit code.\n\n    .DESCRIPTION\n        Function runs a provided command in bash and returns the output and exit code as hashtable.\n\n    .PARAMETER Command\n        The command to run.\n\n    .PARAMETER ExpectedExitCode\n        The expected exit code. If the actual exit code does not match, an exception is thrown.\n\n    .PARAMETER Multiline\n        If true, the output is returned as an array of strings. Otherwise, the output is returned as a single string.\n\n    .PARAMETER ValidateExitCode\n        If true, the actual exit code is compared to the expected exit code.\n\n    .EXAMPLE\n        $result = Get-CommandResult \"ls -la\"\n\n        This command runs \"ls -la\" in bash and returns the output and exit code as hashtable.\n\n    #>\n    param(\n        [Parameter(Mandatory=$true)]\n        [string] $Command,\n        [int[]] $ExpectedExitCode = 0,\n        [switch] $Multiline,\n        [bool] $ValidateExitCode = $true\n    )\n\n    # Bash trick to suppress and show error output because some commands write to stderr (for example, \"python --version\")\n    $stdout = & bash -c \"$Command 2>&1\"\n    $exitCode = $LASTEXITCODE\n\n    if ($ValidateExitCode) {\n        if ($ExpectedExitCode -notcontains $exitCode) {\n            try {\n                throw \"StdOut: '$stdout' ExitCode: '$exitCode'\"\n            } catch {\n                Write-Host $_.Exception.Message\n                Write-Host $_.ScriptStackTrace\n                exit $LASTEXITCODE\n            }\n        }\n    }\n\n    return @{\n        Output = If ($Multiline -eq $true) { $stdout } else { [string] $stdout }\n        ExitCode = $exitCode\n    }\n}\n\nfunction Test-IsUbuntu22 {\n    return (lsb_release -rs) -eq \"22.04\"\n}\n\nfunction Test-IsUbuntu24 {\n    return (lsb_release -rs) -eq \"24.04\"\n}\n\nfunction Get-ToolsetContent {\n    <#\n    .SYNOPSIS\n        Retrieves the content of the toolset.json file.\n\n    .DESCRIPTION\n        This function reads the toolset.json in path provided by INSTALLER_SCRIPT_FOLDER\n        environment variable and returns the content as a PowerShell object.\n    #>\n\n    $toolsetPath = Join-Path $env:INSTALLER_SCRIPT_FOLDER \"toolset.json\"\n    $toolsetJson = Get-Content -Path $toolsetPath -Raw\n    ConvertFrom-Json -InputObject $toolsetJson\n}\n\nfunction Invoke-DownloadWithRetry {\n    <#\n    .SYNOPSIS\n        Downloads a file from a given URL with retry functionality.\n\n    .DESCRIPTION\n        The Invoke-DownloadWithRetry function downloads a file from the specified URL\n        to the specified path. It includes retry functionality in case the download fails.\n\n    .PARAMETER Url\n        The URL of the file to download.\n\n    .PARAMETER Path\n        The path where the downloaded file will be saved. If not provided, a temporary path\n        will be used.\n\n    .EXAMPLE\n        Invoke-DownloadWithRetry -Url \"https://example.com/file.zip\" -Path \"/usr/local/bin\"\n        Downloads the file from the specified URL and saves it to the specified path.\n\n    .EXAMPLE\n        Invoke-DownloadWithRetry -Url \"https://example.com/file.zip\"\n        Downloads the file from the specified URL and saves it to a temporary path.\n\n    .OUTPUTS\n        The path where the downloaded file is saved.\n    #>\n    param(\n        [Parameter(Mandatory)]\n        [string] $Url,\n        [Alias(\"Destination\")]\n        [string] $DestinationPath\n    )\n\n    if (-not $DestinationPath) {\n        $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''\n        $re = \"[{0}]\" -f [RegEx]::Escape($invalidChars)\n        $fileName = [IO.Path]::GetFileName($Url) -replace $re\n\n        if ([String]::IsNullOrEmpty($fileName)) {\n            $fileName = [System.IO.Path]::GetRandomFileName()\n        }\n        $DestinationPath = Join-Path -Path \"/tmp\" -ChildPath $fileName\n    }\n\n    Write-Host \"Downloading package from $Url to $DestinationPath...\"\n\n    $interval = 30\n    $downloadStartTime = Get-Date\n    for ($retries = 20; $retries -gt 0; $retries--) {\n        try {\n            $attemptStartTime = Get-Date\n            Invoke-WebRequest -Uri $Url -Outfile $DestinationPath\n            $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)\n            Write-Host \"Package downloaded in $attemptSeconds seconds\"\n            break\n        } catch {\n            $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)\n            Write-Warning \"Package download failed in $attemptSeconds seconds\"\n            Write-Warning $_.Exception.Message\n        }\n\n        if ($retries -eq 0) {\n            $totalSeconds = [math]::Round(($(Get-Date) - $downloadStartTime).TotalSeconds, 2)\n            throw \"Package download failed after $totalSeconds seconds\"\n        }\n\n        Write-Warning \"Waiting $interval seconds before retrying (retries left: $retries)...\"\n        Start-Sleep -Seconds $interval\n    }\n\n    return $DestinationPath\n}\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/docs-gen/Generate-SoftwareReport.ps1",
    "content": "using module ../software-report-base/SoftwareReport.psm1\nusing module ../software-report-base/SoftwareReport.Nodes.psm1\n\nparam (\n    [Parameter(Mandatory)]\n    [string] $OutputDirectory\n)\n\n$global:ErrorActionPreference = \"Stop\"\n$global:ErrorView = \"NormalView\"\nSet-StrictMode -Version Latest\n\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Common.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Helpers.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"Common.Helpers.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Tools.psm1\") -DisableNameChecking\n\n# Restore file owner in user profile\nsudo chown -R ${env:USER}: $env:HOME\n\n# Software report\n$softwareReport = [SoftwareReport]::new(\"Ubuntu-Slim\")\n$softwareReport.Root.AddToolVersion(\"OS Version:\", $(Get-OSVersionFull))\n$softwareReport.Root.AddToolVersion(\"Image Version:\", $env:IMAGE_VERSION)\n$softwareReport.Root.AddToolVersion(\"Systemd version:\", $(Get-SystemdVersion))\n\n$installedSoftware = $softwareReport.Root.AddHeader(\"Installed Software\")\n\n# Language and Runtime\n$languageAndRuntime = $installedSoftware.AddHeader(\"Language and Runtime\")\n$languageAndRuntime.AddToolVersion(\"Bash\", $(Get-BashVersion))\n$languageAndRuntime.AddToolVersion(\"Dash\", $(Get-DashVersion))\n$languageAndRuntime.AddToolVersion(\"Node.js\", $(Get-NodeVersion))\n$languageAndRuntime.AddToolVersion(\"Perl\", $(Get-PerlVersion))\n$languageAndRuntime.AddToolVersion(\"Python\", $(Get-PythonVersion))\n\n# Package Management\n$packageManagement = $installedSoftware.AddHeader(\"Package Management\")\n$packageManagement.AddToolVersion(\"Npm\", $(Get-NpmVersion))\n$packageManagement.AddToolVersion(\"Pip\", $(Get-PipVersion))\n$packageManagement.AddToolVersion(\"Pip3\", $(Get-Pip3Version))\n$packageManagement.AddToolVersion(\"Pipx\", $(Get-PipxVersion))\n\n# Tools\n$tools = $installedSoftware.AddHeader(\"Tools\")\n$tools.AddToolVersion(\"AzCopy\", $(Get-AzCopyVersion))\n$tools.AddToolVersion(\"Bicep\", $(Get-BicepVersion))\n$tools.AddToolVersion(\"Docker Compose v2\", $(Get-DockerComposeV2Version))\n$tools.AddToolVersion(\"Docker-Buildx\", $(Get-DockerBuildxVersion))\n$tools.AddToolVersion(\"Docker Client\", $(Get-DockerClientVersion))\n$tools.AddToolVersion(\"Git\", $(Get-GitVersion))\n$tools.AddToolVersion(\"Git LFS\", $(Get-GitLFSVersion))\n$tools.AddToolVersion(\"Git-ftp\", $(Get-GitFTPVersion))\n$tools.AddToolVersion(\"jq\", $(Get-JqVersion))\n$tools.AddToolVersion(\"nvm\", $(Get-NvmVersion))\n$tools.AddToolVersion(\"OpenSSL\", $(Get-OpensslVersion))\n$tools.AddToolVersion(\"yq\", $(Get-YqVersion))\n$tools.AddToolVersion(\"zstd\", $(Get-ZstdVersion))\n\n# CLI Tools\n$cliTools = $installedSoftware.AddHeader(\"CLI Tools\")\n$cliTools.AddToolVersion(\"AWS CLI\", $(Get-AWSCliVersion))\n$cliTools.AddToolVersion(\"AWS CLI Session Manager Plugin\", $(Get-AWSCliSessionManagerPluginVersion))\n$cliTools.AddToolVersion(\"AWS SAM CLI\", $(Get-AWSSAMVersion))\n$cliTools.AddToolVersion(\"Azure CLI\", $(Get-AzureCliVersion))\n$cliTools.AddToolVersion(\"Azure CLI (azure-devops)\", $(Get-AzureDevopsVersion))\n$cliTools.AddToolVersion(\"GitHub CLI\", $(Get-GitHubCliVersion))\n$cliTools.AddToolVersion(\"Google Cloud CLI\", $(Get-GoogleCloudCLIVersion))\n\n# PowerShell Tools\n$powerShellTools = $installedSoftware.AddHeader(\"PowerShell Tools\")\n$powerShellTools.AddToolVersion(\"PowerShell\", $(Get-PowershellVersion))\n\n$installedSoftware.AddHeader(\"Installed apt packages\").AddTable($(Get-AptPackages))\n\n$softwareReport.ToJson() | Out-File -FilePath \"${OutputDirectory}/software-report.json\" -Encoding UTF8NoBOM\n$softwareReport.ToMarkdown() | Out-File -FilePath \"${OutputDirectory}/software-report.md\" -Encoding UTF8NoBOM\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/docs-gen/SoftwareReport.Common.psm1",
    "content": "function Get-BashVersion {\n    $version = bash -c 'echo ${BASH_VERSION}'\n    return $version\n}\n\nfunction Get-DashVersion {\n    $version = dpkg-query -W -f '${Version}' dash\n    return $version\n}\n\nfunction Get-NodeVersion {\n    $nodeVersion = $(node --version).Substring(1)\n    return $nodeVersion\n}\n\nfunction Get-OpensslVersion {\n    $opensslVersion = $(dpkg-query -W -f '${Version}' openssl)\n    return $opensslVersion\n}\n\nfunction Get-PerlVersion {\n    $version = $(perl -e 'print substr($^V,1)')\n    return $version\n}\n\nfunction Get-PythonVersion {\n    $result = Get-CommandResult \"python --version\"\n    $version = $result.Output | Get-StringPart -Part 1\n    return $version\n}\n\nfunction Get-PowershellVersion {\n    $pwshVersion = $(pwsh --version) | Get-StringPart -Part 1\n    return $pwshVersion\n}\n\nfunction Get-NpmVersion {\n    $npmVersion = npm --version\n    return $npmVersion\n}\n\nfunction Get-PipVersion {\n    $pipVersion = pip --version | Get-StringPart -Part 1\n    return $pipVersion\n}\n\nfunction Get-Pip3Version {\n    $pip3Version = pip3 --version | Get-StringPart -Part 1\n    return $pip3Version\n}\n\nfunction Get-AptPackages {\n    $apt = (Get-ToolsetContent).Apt\n    $output = @()\n    ForEach ($pkg in ($apt.vital_packages + $apt.common_packages + $apt.cmd_packages)) {\n        $version = $(dpkg-query -W -f '${Version}' $pkg)\n        if ($null -eq $version) {\n            $version = $(dpkg-query -W -f '${Version}' \"$pkg*\")\n        }\n\n        $version = $version -replace '~','\\~'\n\n        $output += [PSCustomObject] @{\n            Name    = $pkg\n            Version = $version\n        }\n    }\n    return ($output | Sort-Object Name)\n}\n\nfunction Get-PipxVersion {\n    $result = (Get-CommandResult \"pipx --version\").Output\n    $result -match \"(?<version>\\d+\\.\\d+\\.\\d+\\.?\\d*)\" | Out-Null\n    return $Matches.Version\n}\n\nfunction Get-SystemdVersion {\n    $matchCollection = [regex]::Matches((systemctl --version | head -n 1), \"\\((.*?)\\)\")\n    $result = foreach ($match in $matchCollection) {$match.Groups[1].Value}\n    return $result\n}\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/docs-gen/SoftwareReport.Helpers.psm1",
    "content": "function Get-StringPart {\n    param (\n        [Parameter(ValueFromPipeline)]\n        [string] $ToolOutput,\n        [string] $Delimiter = \" \",\n        [int[]] $Part\n    )\n\n    $parts = $ToolOutput.Split($Delimiter, [System.StringSplitOptions]::RemoveEmptyEntries)\n    $selectedParts = $parts[$Part]\n    return [string]::Join($Delimiter, $selectedParts)\n}\n\nfunction Get-PathWithLink {\n    param (\n        [string] $InputPath\n    )\n\n    $link = Get-Item $InputPath | Select-Object -ExpandProperty Target\n    if (-not [string]::IsNullOrEmpty($link)) {\n      return \"${InputPath} -> ${link}\"\n    }\n    return \"${InputPath}\"\n}\n\nfunction Get-OSVersionShort {\n    $(Get-OSVersionFull) | Get-StringPart -Delimiter '.' -Part 0,1\n}\n\nfunction Get-OSVersionFull {\n    lsb_release -ds | Get-StringPart -Part 1, 2\n}\n\nfunction Get-KernelVersion {\n    $kernelVersion = uname -r\n    return $kernelVersion\n}\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/docs-gen/SoftwareReport.Tools.psm1",
    "content": "function Get-AzCopyVersion {\n    $azcopyVersion = [string]$(azcopy --version) | Get-StringPart -Part 2\n    return \"$azcopyVersion - available by ``azcopy`` and ``azcopy10`` aliases\"\n}\n\nfunction Get-BicepVersion {\n    (bicep --version | Out-String) -match  \"bicep cli version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.Version\n}\n\nfunction Get-GitVersion {\n    $gitVersion = git --version | Get-StringPart -Part -1\n    return $gitVersion\n}\n\nfunction Get-GitLFSVersion {\n    $result = Get-CommandResult \"git-lfs --version\"\n    $gitlfsversion = $result.Output | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter \"/\"\n    return $gitlfsversion\n}\n\nfunction Get-GitFTPVersion {\n    $gitftpVersion = git-ftp --version | Get-StringPart -Part 2\n    return $gitftpVersion\n}\n\nfunction Get-GoogleCloudCLIVersion {\n    return (gcloud --version | Select-Object -First 1) | Get-StringPart -Part 3\n}\n\nfunction Get-NvmVersion {\n    $nvmVersion = bash -c \"source /etc/skel/.nvm/nvm.sh && nvm --version\"\n    return $nvmVersion\n}\n\nfunction Get-JqVersion {\n    $jqVersion = jq --version | Get-StringPart -Part 1 -Delimiter \"-\"\n    return $jqVersion\n}\n\nfunction Get-AzureCliVersion {\n    $azcliVersion = (az version | ConvertFrom-Json).'azure-cli'\n    return $azcliVersion\n}\n\nfunction Get-AzureDevopsVersion {\n    $azdevopsVersion = (az version | ConvertFrom-Json).extensions.'azure-devops'\n    return $azdevopsVersion\n}\n\nfunction Get-AWSCliVersion {\n    $result = Get-CommandResult \"aws --version\"\n    $awsVersion = $result.Output | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter \"/\"\n    return $awsVersion\n}\n\nfunction Get-AWSCliSessionManagerPluginVersion {\n    $result = (Get-CommandResult \"session-manager-plugin --version\").Output\n    return $result\n}\n\nfunction Get-AWSSAMVersion {\n    return $(sam --version | Get-StringPart -Part -1)\n}\n\nfunction Get-GitHubCliVersion {\n    $ghVersion = gh --version | Select-String \"gh version\" | Get-StringPart -Part 2\n    return $ghVersion\n}\n\nfunction Get-ZstdVersion {\n    $zstdVersion = zstd --version | Get-StringPart -Part 1 -Delimiter \"v\" | Get-StringPart -Part 0 -Delimiter \",\"\n    return \"$zstdVersion\"\n}\n\nfunction Get-YqVersion {\n    $yqVersion = $(yq -V) | Get-StringPart -Part 3\n    return $yqVersion.TrimStart(\"v\").Trim()\n}\n\nfunction Get-DockerComposeV2Version {\n    $composeVersion = docker compose version | Get-StringPart -Part 3 | Get-StringPart -Part 0 -Delimiter \"v\"\n    return $composeVersion\n}\n\nfunction Get-DockerClientVersion {\n    $dockerClientVersion = sudo docker version --format '{{.Client.Version}}'\n    return $dockerClientVersion\n}\n\nfunction Get-DockerBuildxVersion {\n    $buildxVersion = docker buildx version  | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter \"v\"\n    return $buildxVersion\n}\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/entrypoint.sh",
    "content": "#!/bin/bash\n# /opt/entrypoint.sh\n\n# Load environment variables from file\nset -a\nsource /etc/environment\nset +a\n\n# Execute the actual command\nexec \"$@\""
  },
  {
    "path": "images/ubuntu-slim/scripts/helpers/cleanup.sh",
    "content": "#!/bin/bash -e\n\n# delete all .gz and rotated file\nfind /var/log -type f -regex \".*\\.gz$\" -delete\nfind /var/log -type f -regex \".*\\.[0-9]$\" -delete\n\n# wipe log files\nfind /var/log/ -type f -exec cp /dev/null {} \\;\n\nrm -rf /tmp/downloads /tmp/installers\n\napt-get clean\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/helpers/etc-environment.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  etc-environment.sh\n##  Desc:  Helper functions for source and modify /etc/environment\n################################################################################\n\n# NB: sed expression use '%' as a delimiter in order to simplify handling\n#     values containing slashes (i.e. directory path)\n#     The values containing '%' will break the functions\n\nget_etc_environment_variable() {\n    local variable_name=$1\n\n    # remove `variable_name=` and possible quotes from the line\n    grep \"^${variable_name}=\" /etc/environment | sed -E \"s%^${variable_name}=\\\"?([^\\\"]+)\\\"?.*$%\\1%\"\n}\n\nadd_etc_environment_variable() {\n    local variable_name=$1\n    local variable_value=$2\n\n    echo \"${variable_name}=${variable_value}\" | sudo tee -a /etc/environment\n}\n\nreplace_etc_environment_variable() {\n    local variable_name=$1\n    local variable_value=$2\n\n    # modify /etc/environment in place by replacing a string that begins with variable_name\n    sudo sed -i -e \"s%^${variable_name}=.*$%${variable_name}=${variable_value}%\" /etc/environment\n}\n\nset_etc_environment_variable() {\n    local variable_name=$1\n    local variable_value=$2\n\n    if grep \"^${variable_name}=\" /etc/environment > /dev/null; then\n        replace_etc_environment_variable $variable_name $variable_value\n    else\n        add_etc_environment_variable $variable_name $variable_value\n    fi\n}\n\nprepend_etc_environment_variable() {\n    local variable_name=$1\n    local element=$2\n\n    # TODO: handle the case if the variable does not exist\n    existing_value=$(get_etc_environment_variable \"${variable_name}\")\n    set_etc_environment_variable \"${variable_name}\" \"${element}:${existing_value}\"\n}\n\nappend_etc_environment_variable() {\n    local variable_name=$1\n    local element=$2\n\n    # TODO: handle the case if the variable does not exist\n    existing_value=$(get_etc_environment_variable \"${variable_name}\")\n    set_etc_environment_variable \"${variable_name}\" \"${existing_value}:${element}\"\n}\n\nprepend_etc_environment_path() {\n    local element=$1\n\n    prepend_etc_environment_variable PATH \"${element}\"\n}\n\nappend_etc_environment_path() {\n    local element=$1\n\n    append_etc_environment_variable PATH \"${element}\"\n}\n\n# Process /etc/environment as if it were shell script with `export VAR=...` expressions\n#    The PATH variable is handled specially in order to do not override the existing PATH\n#    variable. The value of PATH variable read from /etc/environment is added to the end\n#    of value of the exiting PATH variable exactly as it would happen with real PAM app read\n#    /etc/environment\n#\n# TODO: there might be the others variables to be processed in the same way as \"PATH\" variable\n#       ie MANPATH, INFOPATH, LD_*, etc. In the current implementation the values from /etc/environment\n#       replace the values of the current environment\nreload_etc_environment() {\n    # add `export ` to every variable of /etc/environment except PATH and eval the result shell script\n    eval $(grep -v '^PATH=' /etc/environment | sed -e 's%^%export %')\n    # handle PATH specially\n    etc_path=$(get_etc_environment_variable PATH)\n    export PATH=\"$PATH:$etc_path\"\n}\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/helpers/install.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  install.sh\n##  Desc:  Helper functions for installing tools\n################################################################################\n\ndownload_with_retry() {\n    local url=$1\n    local download_path=$2\n\n    if [ -z \"$download_path\" ]; then\n        mkdir -p /tmp/downloads\n        download_path=\"/tmp/downloads/$(basename \"$url\")\"\n    fi\n\n    echo \"Downloading package from $url to $download_path...\" >&2\n\n    interval=30\n    download_start_time=$(date +%s)\n\n    for ((retries=20; retries>0; retries--)); do\n        attempt_start_time=$(date +%s)\n        if http_code=$(curl -4sSLo \"$download_path\" \"$url\" -w '%{http_code}'); then\n            attempt_seconds=$(($(date +%s) - attempt_start_time))\n            if [ \"$http_code\" -eq 200 ]; then\n                echo \"Package downloaded in $attempt_seconds seconds\" >&2\n                break\n            else\n                echo \"Received HTTP status code $http_code after $attempt_seconds seconds\" >&2\n            fi\n        else\n            attempt_seconds=$(($(date +%s) - attempt_start_time))\n            echo \"Package download failed in $attempt_seconds seconds\" >&2\n        fi\n\n        if [ \"$retries\" -le 1 ]; then\n            total_seconds=$(($(date +%s) - download_start_time))\n            echo \"Package download failed after $total_seconds seconds\" >&2\n            exit 1\n        fi\n\n        echo \"Waiting $interval seconds before retrying (retries left: $retries)...\" >&2\n        sleep $interval\n    done\n\n    echo \"$download_path\"\n}\n\nget_github_releases_by_version() {\n    local repo=$1\n    local version=${2:-\".+\"}\n    local allow_pre_release=${3:-false}\n    local with_assets_only=${4:-false}\n\n    page_size=\"100\"\n\n    json=$(curl -fsSL \"https://api.github.com/repos/${repo}/releases?per_page=${page_size}\")\n\n    if [[ -z \"$json\" ]]; then\n        echo \"Failed to get releases\" >&2\n        exit 1\n    fi\n\n    if [[ $with_assets_only == \"true\" ]]; then\n        json=$(echo $json | jq -r '.[] | select(.assets | length > 0)')\n    else\n        json=$(echo $json | jq -r '.[]')\n    fi\n\n    if [[ $allow_pre_release == \"true\" ]]; then\n        json=$(echo $json | jq -r '.')\n    else\n        json=$(echo $json | jq -r '. | select(.prerelease==false)')\n    fi\n\n    # Filter out rc/beta/etc releases, convert to numeric version and sort\n    json=$(echo $json | jq '. | select(.tag_name | test(\".*-[a-z]|beta\") | not)' | jq '.tag_name |= gsub(\"[^\\\\d.]\"; \"\")' | jq -s 'sort_by(.tag_name | split(\".\") | map(tonumber))')\n\n    # Select releases matching version\n    if [[ $version == \"latest\" ]]; then\n        json_filtered=$(echo $json | jq .[-1])\n    elif [[ $version == *\"+\"* ]] || [[ $version == *\"*\"* ]]; then\n        json_filtered=$(echo $json | jq --arg version $version '.[] | select(.tag_name | test($version))')\n    else\n        json_filtered=$(echo $json | jq --arg version $version '.[] | select(.tag_name | contains($version))')\n    fi\n\n    if [[ -z \"$json_filtered\" ]]; then\n        echo \"Failed to get releases from ${repo} matching version ${version}\" >&2\n        echo \"Available versions: $(echo \"$json\" | jq -r '.tag_name')\" >&2\n        exit 1\n    fi\n\n    echo $json_filtered\n}\n\nresolve_github_release_asset_url() {\n    local repo=$1\n    local url_filter=$2\n    local version=${3:-\".+\"}\n    local allow_pre_release=${4:-false}\n    local allow_multiple_matches=${5:-false}\n\n    matching_releases=$(get_github_releases_by_version \"${repo}\" \"${version}\" \"${allow_pre_release}\" \"true\")\n    matched_url=$(echo $matching_releases | jq -r \".assets[].browser_download_url | select(${url_filter})\")\n\n    if [[ -z \"$matched_url\" ]]; then\n        echo \"Found no download urls matching pattern: ${url_filter}\" >&2\n        echo \"Available download urls: $(echo \"$matching_releases\" | jq -r '.assets[].browser_download_url')\" >&2\n        exit 1\n    fi\n\n    if [[ \"$(echo \"$matched_url\" | wc -l)\" -gt 1 ]]; then\n        if [[ $allow_multiple_matches == \"true\" ]]; then\n            matched_url=$(echo \"$matched_url\" | tail -n 1)\n        else\n            echo \"Multiple matches found for ${version} version and ${url_filter} URL filter. Please make filters more specific\" >&2\n            exit 1\n        fi\n    fi\n\n    echo $matched_url\n}\n\nget_checksum_from_github_release() {\n    local repo=$1\n    local file_name=$2\n    local version=${3:-\".+\"}\n    local hash_type=$4\n    local allow_pre_release=${5:-false}\n\n    if [[ -z \"$file_name\" ]]; then\n        echo \"File name is not specified.\" >&2\n        exit 1\n    fi\n\n    if [[ \"$hash_type\" == \"SHA256\" ]]; then\n        hash_pattern=\"[A-Fa-f0-9]{64}\"\n    elif [[ \"$hash_type\" == \"SHA512\" ]]; then\n        hash_pattern=\"[A-Fa-f0-9]{128}\"\n    else\n        echo \"Unknown hash type: ${hash_type}\" >&2\n        exit 1\n    fi\n\n    matching_releases=$(get_github_releases_by_version \"${repo}\" \"${version}\" \"${allow_pre_release}\" \"true\")\n    matched_line=$(printf \"$(echo $matching_releases | jq '.body')\\n\" | grep \"$file_name\")\n\n    if [[ -z \"$matched_line\" ]]; then\n        echo \"File name ${file_name} not found in release body\" >&2\n        exit 1\n    fi\n\n    if [[ \"$(echo \"$matched_line\" | wc -l)\" -gt 1 ]]; then\n        echo \"Multiple matches found for ${file_name} in release body: ${matched_line}\" >&2\n        exit 1\n    fi\n\n    hash=$(echo $matched_line | grep -oP \"$hash_pattern\")\n\n    if [[ -z \"$hash\" ]]; then\n        echo \"Found ${file_name} in body of release, but failed to get hash from it: ${matched_line}\" >&2\n        exit 1\n    fi\n\n    echo \"$hash\"\n}\n\nget_checksum_from_url() {\n    local url=$1\n    local file_name=$2\n    local hash_type=$3\n    local use_custom_search_pattern=${4:-false}\n    local delimiter=${5:-' '}\n    local word_number=${6:-1}\n\n    if [[ \"$hash_type\" == \"SHA256\" ]]; then\n        hash_pattern=\"[A-Fa-f0-9]{64}\"\n    elif [[ \"$hash_type\" == \"SHA512\" ]]; then\n        hash_pattern=\"[A-Fa-f0-9]{128}\"\n    else\n        echo \"Unknown hash type: ${hash_type}\" >&2\n        exit 1\n    fi\n\n    checksums_file_path=$(download_with_retry \"$url\")\n    checksums=$(cat \"$checksums_file_path\")\n    rm \"$checksums_file_path\"\n\n    matched_line=$(printf \"$checksums\\n\" | grep \"$file_name\")\n\n    if [[ \"$(echo \"$matched_line\" | wc -l)\" -gt 1 ]]; then\n        echo \"Found multiple lines matching file name ${file_name} in checksum file.\" >&2\n        exit 1\n    fi\n\n    if [[ -z \"$matched_line\" ]]; then\n        echo \"File name ${file_name} not found in checksum file.\" >&2\n        exit 1\n    fi\n\n    if [[ $use_custom_search_pattern == \"true\" ]]; then\n        hash=$(echo \"$matched_line\" | sed 's/  */ /g' | cut -d \"$delimiter\" -f \"$word_number\" | tr -d -c '[:alnum:]')\n    else\n        hash=$(echo $matched_line | grep -oP \"$hash_pattern\")\n    fi\n\n    if [[ -z \"$hash\" ]]; then\n        echo \"Found ${file_name} in checksum file, but failed to get hash from it: ${matched_line}\" >&2\n        exit 1\n    fi\n\n    echo \"$hash\"\n}\n\nuse_checksum_comparison() {\n    local file_path=$1\n    local checksum=$2\n    local sha_type=${3:-\"256\"}\n\n    echo \"Performing checksum verification\"\n\n    if [[ ! -f \"$file_path\" ]]; then\n        echo \"File not found: $file_path\"\n        exit 1\n    fi\n\n    local_file_hash=$(shasum --algorithm \"$sha_type\" \"$file_path\" | awk '{print $1}')\n\n    if [[ \"$local_file_hash\" != \"$checksum\" ]]; then\n        echo \"Checksum verification failed. Expected hash: $checksum; Actual hash: $local_file_hash.\"\n        exit 1\n    else\n        echo \"Checksum verification passed. Expected hash: $checksum; Actual hash: $local_file_hash.\"\n    fi\n}\n\nget_toolset_value() {\n    local toolset_path=\"${INSTALLER_SCRIPT_FOLDER}/toolset.json\"\n    local query=$1\n\n    echo \"$(jq -r \"$query\" $toolset_path)\"\n}\n"
  },
  {
    "path": "images/ubuntu-slim/scripts/helpers/os.sh",
    "content": "#!/bin/bash -e\n################################################################################\n##  File:  os.sh\n##  Desc:  Helper functions for OS releases\n################################################################################\n\nis_ubuntu22() {\n    lsb_release -rs | grep -q '22.04'\n}\n\nis_ubuntu24() {\n    lsb_release -rs | grep -q '24.04'\n}\n"
  },
  {
    "path": "images/ubuntu-slim/test.sh",
    "content": "#!/bin/bash -e\n\n# This script builds and runs various tests on the ubuntu-slim Docker image\n# to ensure it contains the expected software and configurations.\n# The build and test workflows for docker images expect this script to be present.\n#\n# Usage: test.sh [IMAGE_NAME]\n# If IMAGE_NAME is not provided, defaults to ubuntu-slim:test\n\nset -eo pipefail\n\nshow_help() {\n    echo \"Usage: $0 [IMAGE_NAME]\"\n    echo \"\"\n    echo \"Test a Docker image to ensure it contains the expected software and configurations.\"\n    echo \"\"\n    echo \"Arguments:\"\n    echo \"  IMAGE_NAME    Docker image name to test (default: ubuntu-slim:test)\"\n    echo \"\"\n    echo \"Examples:\"\n    echo \"  $0                           # Test ubuntu-slim:test (builds image first)\"\n    echo \"  $0 my-registry/ubuntu:latest # Test existing image\"\n    echo \"  $0 ubuntu-slim:v1.2.3        # Test tagged image\"\n    echo \"\"\n    echo \"Options:\"\n    echo \"  -h, --help    Show this help message\"\n}\n\n# Handle help flags\nif [[ \"$1\" == \"-h\" || \"$1\" == \"--help\" ]]; then\n    show_help\n    exit 0\nfi\n\n# Set the image name from parameter or use default\nIMAGE_NAME=\"${1:-ubuntu-slim:test}\"\n\necho \"Testing image: $IMAGE_NAME\"\n\nrun_test() {\n    local desc=\"$1\"\n    shift\n    if output=$(docker run --rm \"$IMAGE_NAME\" \"$@\" 2>&1); then\n        echo \"PASS: $desc\"\n        echo \"$output\" | sed 's/^/    /'\n    else\n        echo \"FAIL: $desc\"\n        echo \"$output\" | sed 's/^/    /'\n        exit 1\n    fi\n}\n\n# Build the image only if using the default name (for backward compatibility)\nif [[ \"$IMAGE_NAME\" == \"ubuntu-slim:test\" ]]; then\n    echo \"Building image: $IMAGE_NAME\"\n    if ! docker build --no-cache --debug --progress plain -t \"$IMAGE_NAME\" .; then\n        echo \"Error: Docker build failed\"\n        exit 1\n    fi\nelse\n    # Check if the image exists\n    if ! docker image inspect \"$IMAGE_NAME\" >/dev/null 2>&1; then\n        echo \"Error: Image '$IMAGE_NAME' does not exist. Please build it first or provide a valid image name.\"\n        echo \"Run '$0 --help' for usage information.\"\n        exit 1\n    fi\nfi\n\necho \"Running tests on image: $IMAGE_NAME\"\n\ndocker history --no-trunc \"$IMAGE_NAME\"\ndocker inspect -f \"{{ .Size }}\" \"$IMAGE_NAME\" | numfmt --to=iec | sed 's/^/Image size: /'\n\n# Ensure key software is installed and runnable\nrun_test \"GitHub CLI is installed\" gh --version\nrun_test \"Azure CLI is installed\" az version\nrun_test \"AWS CLI is installed\" aws --version\nrun_test \"Session Manager plugin is installed\" session-manager-plugin --version\nrun_test \"AWS SAM CLI is installed\" sam --version\nrun_test \"jq is installed\" jq --version\nrun_test \"git is installed\" git --version\nrun_test \"node is installed\" node --version\nrun_test \"npm is installed\" npm --version\nrun_test \"python3 is installed\" python3 --version\nrun_test \"python is aliased\" python --version\nrun_test \"pipx is installed\" pipx --version\nrun_test \"curl is installed\" curl --version\nrun_test \"wget is installed\" wget --version\nrun_test \"yq is installed\" yq --version\nrun_test \"parallel is installed\" parallel --version\nrun_test \"bc is installed\" bc --version\nrun_test \"zstd is installed\" zstd --version\nrun_test \"google cloud SDK is installed\" gcloud --version\nrun_test \"git lfs is installed\" git lfs version\nrun_test \"powershell is installed\" pwsh --version\nrun_test \"docker-cli is installed\" docker --version\nrun_test \"docker compose is installed\" docker compose version\nrun_test \"docker buildx is installed\" docker buildx version\n\n# Quick check: ensure the imagedata JSON file was created during image build\nrun_test \"imagedata JSON file exists\" test -f /imagegeneration/imagedata.json\n"
  },
  {
    "path": "images/ubuntu-slim/toolsets/toolset.json",
    "content": "{\n    \"toolcache\": [\n         {\n            \"name\": \"node\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json\",\n            \"platform\" : \"linux\",\n            \"arch\": \"x64\",\n            \"versions\": [\n                \"22.*\",\n                \"24.*\"\n            ]\n        },\n        {\n            \"name\": \"CodeQL\",\n            \"platform\" : \"linux\",\n            \"arch\": \"x64\",\n            \"versions\": [\n                \"*\"\n            ]\n        }\n    ],\n    \"apt\": {\n        \"vital_packages\": [\n            \"apt-utils\",\n            \"bzip2\",\n            \"ca-certificates\",\n            \"curl\",\n            \"g++\",\n            \"gcc\",\n            \"make\",\n            \"jq\",\n            \"tar\",\n            \"unzip\",\n            \"wget\"\n        ],\n        \"common_packages\": [\n            \"autoconf\",\n            \"automake\",\n            \"bc\",\n            \"dbus\",\n            \"dnsutils\",\n            \"dpkg\",\n            \"dpkg-dev\",\n            \"fakeroot\",\n            \"fonts-noto-color-emoji\",\n            \"gnupg2\",\n            \"iproute2\",\n            \"iputils-ping\",\n            \"libyaml-dev\",\n            \"libtool\",\n            \"libssl-dev\",\n            \"libsqlite3-dev\",\n            \"locales\",\n            \"lzma\",\n            \"mercurial\",\n            \"openssh-client\",\n            \"p7zip-rar\",\n            \"pkg-config\",\n            \"python-is-python3\",\n            \"rpm\",\n            \"texinfo\",\n            \"tk\",\n            \"tree\",\n            \"tzdata\",\n            \"upx\",\n            \"xvfb\",\n            \"xz-utils\",\n            \"zsync\"\n        ],\n        \"cmd_packages\": [\n            \"acl\",\n            \"binutils\",\n            \"libnss3-tools\",\n            \"coreutils\",\n            \"file\",\n            \"findutils\",\n            \"flex\",\n            \"ftp\",\n            \"haveged\",\n            \"lz4\",\n            \"netcat-openbsd\",\n            \"net-tools\",\n            \"p7zip-full\",\n            \"parallel\",\n            \"patchelf\",\n            \"pigz\",\n            \"pollinate\",\n            \"rsync\",\n            \"shellcheck\",\n            \"sqlite3\",\n            \"ssh\",\n            \"sshpass\",\n            \"sudo\",\n            \"systemd-coredump\",\n            \"telnet\",\n            \"time\",\n            \"zip\"\n        ]\n    },\n    \"brew\": [\n    ],\n    \"node\": {\n        \"default\": \"24\"\n    },\n    \"node_modules\": [ ],\n    \"pwsh\": {\n        \"version\": \"7.5\"\n    }\n}\n"
  },
  {
    "path": "images/ubuntu-slim/ubuntu-slim-Readme.md",
    "content": "# Ubuntu-Slim\n- OS Version: 24.04.3 LTS\n- Image Version: 20260120.46.1\n- Systemd version: 255.4-1ubuntu8.12\n\n## Installed Software\n\n### Language and Runtime\n- Bash 5.2.21(1)-release\n- Dash 0.5.12-6ubuntu5\n- Node.js 24.13.0\n- Perl 5.38.2\n- Python 3.12.3\n\n### Package Management\n- Npm 11.6.2\n- Pip 24.0\n- Pip3 24.0\n- Pipx 1.8.0\n\n### Tools\n- AzCopy 10.31.1 - available by `azcopy` and `azcopy10` aliases\n- Bicep 0.39.26\n- Docker Compose v2 5.0.1\n- Docker-Buildx 0.30.1\n- Docker Client 29.1.5\n- Git 2.52.0\n- Git LFS 3.7.1\n- Git-ftp 1.6.0\n- jq 1.7\n- nvm 0.40.3\n- OpenSSL 3.0.13-0ubuntu3.6\n- yq 4.50.1\n- zstd 1.5.7\n\n### CLI Tools\n- AWS CLI 2.33.2\n- AWS CLI Session Manager Plugin 1.2.764.0\n- AWS SAM CLI 1.151.0\n- Azure CLI 2.82.0\n- Azure CLI (azure-devops) 1.0.2\n- GitHub CLI 2.85.0\n- Google Cloud CLI 552.0.0\n\n### PowerShell Tools\n- PowerShell 7.5.4\n\n### Installed apt packages\n| Name                   | Version                      |\n| ---------------------- | ---------------------------- |\n| acl                    | 2.3.2-1build1.1              |\n| apt-utils              | 2.8.3                        |\n| autoconf               | 2.71-3                       |\n| automake               | 1:1.16.5-1.3ubuntu1          |\n| bc                     | 1.07.1-3ubuntu4              |\n| binutils               | 2.42-4ubuntu2.8              |\n| bzip2                  | 1.0.8-5.1build0.1            |\n| ca-certificates        | 20240203                     |\n| coreutils              | 9.4-3ubuntu6.1               |\n| curl                   | 8.5.0-2ubuntu10.6            |\n| dbus                   | 1.14.10-4ubuntu4.1           |\n| dnsutils               | 1:9.18.39-0ubuntu0.24.04.2   |\n| dpkg                   | 1.22.6ubuntu6.5              |\n| dpkg-dev               | 1.22.6ubuntu6.5              |\n| fakeroot               | 1.33-1                       |\n| file                   | 1:5.45-3build1               |\n| findutils              | 4.9.0-5build1                |\n| flex                   | 2.6.4-8.2build1              |\n| fonts-noto-color-emoji | 2.047-0ubuntu0.24.04.1       |\n| ftp                    | 20230507-2build3             |\n| g++                    | 4:13.2.0-7ubuntu1            |\n| gcc                    | 4:13.2.0-7ubuntu1            |\n| gnupg2                 | 2.4.4-2ubuntu17.4            |\n| haveged                | 1.9.14-1ubuntu2              |\n| iproute2               | 6.1.0-1ubuntu6.2             |\n| iputils-ping           | 3:20240117-1ubuntu0.1        |\n| jq                     | 1.7.1-3ubuntu0.24.04.1       |\n| libnss3-tools          | 2:3.98-1build1               |\n| libsqlite3-dev         | 3.45.1-1ubuntu2.5            |\n| libssl-dev             | 3.0.13-0ubuntu3.6            |\n| libtool                | 2.4.7-7build1                |\n| libyaml-dev            | 0.2.5-1build1                |\n| locales                | 2.39-0ubuntu8.6              |\n| lz4                    | 1.9.4-1build1.1              |\n| lzma                   | 9.22-2.2                     |\n| make                   | 4.3-4.1build2                |\n| mercurial              | 6.7.2-1ubuntu2.2             |\n| net-tools              | 2.10-0.1ubuntu4.4            |\n| netcat-openbsd         | 1.226-1ubuntu2               |\n| openssh-client         | 1:9.6p1-3ubuntu13.14         |\n| p7zip-full             | 16.02+transitional.1         |\n| p7zip-rar              | 16.02+transitional.1         |\n| parallel               | 20231122+ds-1                |\n| patchelf               | 0.18.0-1.1build1             |\n| pigz                   | 2.8-1                        |\n| pkg-config             | 1.8.1-2build1                |\n| pollinate              | 4.33-3.1ubuntu1.1            |\n| python-is-python3      | 3.11.4-1                     |\n| rpm                    | 4.18.2+dfsg-2.1build2        |\n| rsync                  | 3.2.7-1ubuntu1.2             |\n| shellcheck             | 0.9.0-1                      |\n| sqlite3                | 3.45.1-1ubuntu2.5            |\n| ssh                    | 1:9.6p1-3ubuntu13.14         |\n| sshpass                | 1.09-1                       |\n| sudo                   | 1.9.15p5-3ubuntu5.24.04.1    |\n| systemd-coredump       | 255.4-1ubuntu8.12            |\n| tar                    | 1.35+dfsg-3build1            |\n| telnet                 | 0.17+2.5-3ubuntu4            |\n| texinfo                | 7.1-3build2                  |\n| time                   | 1.9-0.2build1                |\n| tk                     | 8.6.14build1                 |\n| tree                   | 2.1.1-2ubuntu3.24.04.2       |\n| tzdata                 | 2025b-0ubuntu0.24.04.1       |\n| unzip                  | 6.0-28ubuntu4.1              |\n| upx                    | 4.2.2-3                      |\n| wget                   | 1.21.4-1ubuntu4.1            |\n| xvfb                   | 2:21.1.12-1ubuntu1.5         |\n| xz-utils               | 5.6.1+really5.4.5-1ubuntu0.2 |\n| zip                    | 3.0-13ubuntu0.2              |\n| zsync                  | 0.6.2-5build1                |\n"
  },
  {
    "path": "images/ubuntu-slim/ubuntu-slim-Report.json",
    "content": "{\n  \"NodeType\": \"HeaderNode\",\n  \"Title\": \"Ubuntu-Slim\",\n  \"Children\": [\n    {\n      \"NodeType\": \"ToolVersionNode\",\n      \"ToolName\": \"OS Version:\",\n      \"Version\": \"24.04.3 LTS\"\n    },\n    {\n      \"NodeType\": \"ToolVersionNode\",\n      \"ToolName\": \"Image Version:\",\n      \"Version\": \"20260120.46.1\"\n    },\n    {\n      \"NodeType\": \"ToolVersionNode\",\n      \"ToolName\": \"Systemd version:\",\n      \"Version\": \"255.4-1ubuntu8.12\"\n    },\n    {\n      \"NodeType\": \"HeaderNode\",\n      \"Title\": \"Installed Software\",\n      \"Children\": [\n        {\n          \"NodeType\": \"HeaderNode\",\n          \"Title\": \"Language and Runtime\",\n          \"Children\": [\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Bash\",\n              \"Version\": \"5.2.21(1)-release\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Dash\",\n              \"Version\": \"0.5.12-6ubuntu5\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Node.js\",\n              \"Version\": \"24.13.0\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Perl\",\n              \"Version\": \"5.38.2\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Python\",\n              \"Version\": \"3.12.3\"\n            }\n          ]\n        },\n        {\n          \"NodeType\": \"HeaderNode\",\n          \"Title\": \"Package Management\",\n          \"Children\": [\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Npm\",\n              \"Version\": \"11.6.2\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Pip\",\n              \"Version\": \"24.0\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Pip3\",\n              \"Version\": \"24.0\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Pipx\",\n              \"Version\": \"1.8.0\"\n            }\n          ]\n        },\n        {\n          \"NodeType\": \"HeaderNode\",\n          \"Title\": \"Tools\",\n          \"Children\": [\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"AzCopy\",\n              \"Version\": \"10.31.1 - available by `azcopy` and `azcopy10` aliases\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Bicep\",\n              \"Version\": \"0.39.26\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Docker Compose v2\",\n              \"Version\": \"5.0.1\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Docker-Buildx\",\n              \"Version\": \"0.30.1\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Docker Client\",\n              \"Version\": \"29.1.5\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Git\",\n              \"Version\": \"2.52.0\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Git LFS\",\n              \"Version\": \"3.7.1\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Git-ftp\",\n              \"Version\": \"1.6.0\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"jq\",\n              \"Version\": \"1.7\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"nvm\",\n              \"Version\": \"0.40.3\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"OpenSSL\",\n              \"Version\": \"3.0.13-0ubuntu3.6\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"yq\",\n              \"Version\": \"4.50.1\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"zstd\",\n              \"Version\": \"1.5.7\"\n            }\n          ]\n        },\n        {\n          \"NodeType\": \"HeaderNode\",\n          \"Title\": \"CLI Tools\",\n          \"Children\": [\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"AWS CLI\",\n              \"Version\": \"2.33.2\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"AWS CLI Session Manager Plugin\",\n              \"Version\": \"1.2.764.0\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"AWS SAM CLI\",\n              \"Version\": \"1.151.0\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Azure CLI\",\n              \"Version\": \"2.82.0\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Azure CLI (azure-devops)\",\n              \"Version\": \"1.0.2\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"GitHub CLI\",\n              \"Version\": \"2.85.0\"\n            },\n            {\n              \"NodeType\": \"ToolVersionNode\",\n              \"ToolName\": \"Google Cloud CLI\",\n              \"Version\": \"552.0.0\"\n            }\n          ]\n        },\n        {\n          \"NodeType\": \"HeaderNode\",\n          \"Title\": \"PowerShell Tools\",\n          \"Children\": {\n            \"NodeType\": \"ToolVersionNode\",\n            \"ToolName\": \"PowerShell\",\n            \"Version\": \"7.5.4\"\n          }\n        },\n        {\n          \"NodeType\": \"HeaderNode\",\n          \"Title\": \"Installed apt packages\",\n          \"Children\": {\n            \"NodeType\": \"TableNode\",\n            \"Headers\": \"Name|Version\",\n            \"Rows\": [\n              \"acl|2.3.2-1build1.1\",\n              \"apt-utils|2.8.3\",\n              \"autoconf|2.71-3\",\n              \"automake|1:1.16.5-1.3ubuntu1\",\n              \"bc|1.07.1-3ubuntu4\",\n              \"binutils|2.42-4ubuntu2.8\",\n              \"bzip2|1.0.8-5.1build0.1\",\n              \"ca-certificates|20240203\",\n              \"coreutils|9.4-3ubuntu6.1\",\n              \"curl|8.5.0-2ubuntu10.6\",\n              \"dbus|1.14.10-4ubuntu4.1\",\n              \"dnsutils|1:9.18.39-0ubuntu0.24.04.2\",\n              \"dpkg|1.22.6ubuntu6.5\",\n              \"dpkg-dev|1.22.6ubuntu6.5\",\n              \"fakeroot|1.33-1\",\n              \"file|1:5.45-3build1\",\n              \"findutils|4.9.0-5build1\",\n              \"flex|2.6.4-8.2build1\",\n              \"fonts-noto-color-emoji|2.047-0ubuntu0.24.04.1\",\n              \"ftp|20230507-2build3\",\n              \"g++|4:13.2.0-7ubuntu1\",\n              \"gcc|4:13.2.0-7ubuntu1\",\n              \"gnupg2|2.4.4-2ubuntu17.4\",\n              \"haveged|1.9.14-1ubuntu2\",\n              \"iproute2|6.1.0-1ubuntu6.2\",\n              \"iputils-ping|3:20240117-1ubuntu0.1\",\n              \"jq|1.7.1-3ubuntu0.24.04.1\",\n              \"libnss3-tools|2:3.98-1build1\",\n              \"libsqlite3-dev|3.45.1-1ubuntu2.5\",\n              \"libssl-dev|3.0.13-0ubuntu3.6\",\n              \"libtool|2.4.7-7build1\",\n              \"libyaml-dev|0.2.5-1build1\",\n              \"locales|2.39-0ubuntu8.6\",\n              \"lz4|1.9.4-1build1.1\",\n              \"lzma|9.22-2.2\",\n              \"make|4.3-4.1build2\",\n              \"mercurial|6.7.2-1ubuntu2.2\",\n              \"net-tools|2.10-0.1ubuntu4.4\",\n              \"netcat-openbsd|1.226-1ubuntu2\",\n              \"openssh-client|1:9.6p1-3ubuntu13.14\",\n              \"p7zip-full|16.02+transitional.1\",\n              \"p7zip-rar|16.02+transitional.1\",\n              \"parallel|20231122+ds-1\",\n              \"patchelf|0.18.0-1.1build1\",\n              \"pigz|2.8-1\",\n              \"pkg-config|1.8.1-2build1\",\n              \"pollinate|4.33-3.1ubuntu1.1\",\n              \"python-is-python3|3.11.4-1\",\n              \"rpm|4.18.2+dfsg-2.1build2\",\n              \"rsync|3.2.7-1ubuntu1.2\",\n              \"shellcheck|0.9.0-1\",\n              \"sqlite3|3.45.1-1ubuntu2.5\",\n              \"ssh|1:9.6p1-3ubuntu13.14\",\n              \"sshpass|1.09-1\",\n              \"sudo|1.9.15p5-3ubuntu5.24.04.1\",\n              \"systemd-coredump|255.4-1ubuntu8.12\",\n              \"tar|1.35+dfsg-3build1\",\n              \"telnet|0.17+2.5-3ubuntu4\",\n              \"texinfo|7.1-3build2\",\n              \"time|1.9-0.2build1\",\n              \"tk|8.6.14build1\",\n              \"tree|2.1.1-2ubuntu3.24.04.2\",\n              \"tzdata|2025b-0ubuntu0.24.04.1\",\n              \"unzip|6.0-28ubuntu4.1\",\n              \"upx|4.2.2-3\",\n              \"wget|1.21.4-1ubuntu4.1\",\n              \"xvfb|2:21.1.12-1ubuntu1.5\",\n              \"xz-utils|5.6.1+really5.4.5-1ubuntu0.2\",\n              \"zip|3.0-13ubuntu0.2\",\n              \"zsync|0.6.2-5build1\"\n            ]\n          }\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "images/windows/Windows2022-Readme.md",
    "content": "| Announcements |\n|-|\n| [Windows Server 2025 with Visual Studio 2026 is now available as a public beta](https://github.com/actions/runner-images/issues/13638) |\n| [[Windows/Ubuntu] Docker Server and Client will be updated to version 29.1.*, Docker Compose will be updated to version 2.40.3 on February 9th, 2026](https://github.com/actions/runner-images/issues/13474) |\n***\n# Windows Server 2022\n- OS Version: 10.0.20348 Build 4773\n- Image Version: 20260301.50.1\n\n## Windows features\n- Windows Subsystem for Linux (WSLv1): Enabled\n\n## Installed Software\n\n### Language and Runtime\n- Bash 5.2.37(1)-release\n- Go 1.24.13\n- Julia 1.12.0\n- Kotlin 2.3.10\n- LLVM 20.1.8\n- Node 20.20.0\n- Perl 5.32.1\n- PHP 8.5.3\n- Python 3.12.10\n- Ruby 3.3.10\n\n### Package Management\n- Chocolatey 2.6.0\n- Composer 2.9.5\n- Helm 4.1.0\n- Miniconda 25.11.1 (pre-installed on the image but not added to PATH)\n- NPM 10.8.2\n- NuGet 7.3.0.70\n- pip 26.0.1 (python 3.12)\n- Pipx 1.8.0\n- RubyGems 3.5.22\n- Vcpkg (build from commit 62159a45e1)\n- Yarn 1.22.22\n\n#### Environment variables\n| Name                    | Value        |\n| ----------------------- | ------------ |\n| VCPKG_INSTALLATION_ROOT | C:\\vcpkg     |\n| CONDA                   | C:\\Miniconda |\n\n### Project Management\n- Ant 1.10.15\n- Gradle 9.3\n- Maven 3.9.12\n- sbt 1.12.4\n\n### Tools\n- 7zip 26.00\n- aria2 1.37.0\n- azcopy 10.32.1\n- Bazel 9.0.0\n- Bazelisk 1.28.1\n- Bicep 0.41.2\n- Cabal 3.16.1.0\n- CMake 3.31.6\n- CodeQL Action Bundle 2.24.2\n- Docker 29.1.5\n- Docker Compose v2 2.40.3\n- Docker-wincred 0.9.5\n- ghc 9.14.1\n- Git 2.53.0.windows.1\n- Git LFS 3.7.1\n- ImageMagick 7.1.2-15\n- InnoSetup 6.7.1\n- jq 1.8.1\n- Kind 0.31.0\n- Kubectl 1.35.2\n- Mercurial 6.3.1\n- gcc 14.2.0\n- gdb 16.2\n- GNU Binutils 2.44\n- Newman 6.2.2\n- NSIS 3.10\n- OpenSSL 3.6.1\n- Packer 1.15.0\n- Pulumi 3.224.0\n- R 4.5.2\n- Service Fabric SDK 10.1.2493.9590\n- Stack 3.9.3\n- Subversion (SVN) 1.14.5\n- Swig 4.3.1\n- VSWhere 3.1.7\n- WinAppDriver 1.2.2009.02003\n- WiX Toolset 3.14.1.8722\n- yamllint 1.38.0\n- zstd 1.5.7\n- Ninja 1.13.2\n\n### CLI Tools\n- Alibaba Cloud CLI 3.2.10\n- AWS CLI 2.34.0\n- AWS SAM CLI 1.154.0\n- AWS Session Manager CLI 1.2.779.0\n- Azure CLI 2.83.0\n- Azure DevOps CLI extension 1.0.2\n- GitHub CLI 2.87.3\n\n### Rust Tools\n- Cargo 1.93.1\n- Rust 1.93.1\n- Rustdoc 1.93.1\n- Rustup 1.28.2\n\n#### Packages\n- bindgen 0.72.1\n- cargo-audit 0.22.1\n- cargo-outdated 0.17.0\n- cbindgen 0.29.2\n- Clippy 0.1.93\n- Rustfmt 1.8.0\n\n### Browsers and Drivers\n- Google Chrome 145.0.7632.117\n- Chrome Driver 145.0.7632.117\n- Microsoft Edge 145.0.3800.82\n- Microsoft Edge Driver 145.0.3800.82\n- Mozilla Firefox 148.0\n- Gecko Driver 0.36.0\n- IE Driver 4.14.0.0\n- Selenium server 4.41.0\n\n#### Environment variables\n| Name              | Value                              |\n| ----------------- | ---------------------------------- |\n| CHROMEWEBDRIVER   | C:\\SeleniumWebDrivers\\ChromeDriver |\n| EDGEWEBDRIVER     | C:\\SeleniumWebDrivers\\EdgeDriver   |\n| GECKOWEBDRIVER    | C:\\SeleniumWebDrivers\\GeckoDriver  |\n| SELENIUM_JAR_PATH | C:\\selenium\\selenium-server.jar    |\n\n### Java\n| Version             | Environment Variable |\n| ------------------- | -------------------- |\n| 8.0.482+8 (default) | JAVA_HOME_8_X64      |\n| 11.0.30+7           | JAVA_HOME_11_X64     |\n| 17.0.18+8           | JAVA_HOME_17_X64     |\n| 21.0.10+7.0         | JAVA_HOME_21_X64     |\n| 25.0.2+10.0         | JAVA_HOME_25_X64     |\n\n### Shells\n| Name          | Target                            |\n| ------------- | --------------------------------- |\n| gitbash.exe   | C:\\Program Files\\Git\\bin\\bash.exe |\n| msys2bash.cmd | C:\\msys64\\usr\\bin\\bash.exe        |\n| wslbash.exe   | C:\\Windows\\System32\\bash.exe      |\n\n### MSYS2\n- Pacman 6.1.0\n\n#### Notes\n```\nLocation: C:\\msys64\n\nNote: MSYS2 is pre-installed on image but not added to PATH.\n```\n\n### Cached Tools\n\n#### Go\n- 1.22.12\n- 1.23.12\n- 1.24.13\n- 1.25.7\n\n#### Node.js\n- 20.20.0\n- 22.22.0\n- 24.14.0\n\n#### Python\n- 3.10.11\n- 3.11.9\n- 3.12.10\n- 3.13.12\n- 3.14.3\n\n#### PyPy\n- 2.7.18 [PyPy 7.3.20]\n- 3.7.13 [PyPy 7.3.9]\n- 3.8.16 [PyPy 7.3.11]\n- 3.9.19 [PyPy 7.3.16]\n- 3.10.16 [PyPy 7.3.19]\n\n#### Ruby\n- 3.2.10\n- 3.3.10\n- 3.4.8\n- 4.0.1\n\n### Databases\n\n#### PostgreSQL\n| Property             | Value                                                                                                                  |\n| -------------------- | ---------------------------------------------------------------------------------------------------------------------- |\n| ServiceName          | postgresql-x64-14                                                                                                      |\n| Version              | 14.22                                                                                                                  |\n| ServiceStatus        | Stopped                                                                                                                |\n| ServiceStartType     | Disabled                                                                                                               |\n| EnvironmentVariables | PGBIN=C:\\Program Files\\PostgreSQL\\14\\bin <br> PGDATA=C:\\PostgreSQL\\14\\data <br> PGROOT=C:\\Program Files\\PostgreSQL\\14  |\n| Path                 | C:\\Program Files\\PostgreSQL\\14                                                                                         |\n| UserName             | postgres                                                                                                               |\n| Password             | root                                                                                                                   |\n\n#### MongoDB\n| Version  | ServiceName | ServiceStatus | ServiceStartType |\n| -------- | ----------- | ------------- | ---------------- |\n| 7.0.30.0 | MongoDB     | Stopped       | Disabled         |\n\n### Database tools\n- Azure CosmosDb Emulator 2.14.27.0\n- DacFx 170.3.93.6\n- MySQL 8.0.45.0\n- SQL OLEDB Driver 18 18.7.5.0\n- SQL OLEDB Driver 19 19.4.1.0\n- SQLPS 1.0\n- MongoDB Shell (mongosh) 2.7.0\n\n### Web Servers\n| Name   | Version | ConfigFile                            | ServiceName | ServiceStatus | ListenPort |\n| ------ | ------- | ------------------------------------- | ----------- | ------------- | ---------- |\n| Apache | 2.4.55  | C:\\tools\\Apache24\\conf\\httpd.conf     | Apache      | Stopped       | 80         |\n| Nginx  | 1.29.5  | C:\\tools\\nginx-1.29.5\\conf\\nginx.conf | nginx       | Stopped       | 80         |\n\n### Visual Studio Enterprise 2022\n| Name                          | Version       | Path                                                     |\n| ----------------------------- | ------------- | -------------------------------------------------------- |\n| Visual Studio Enterprise 2022 | 17.14.37012.4 | C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise |\n\n#### Workloads, components and extensions\n| Package                                                                   | Version         |\n| ------------------------------------------------------------------------- | --------------- |\n| android                                                                   | 35.0.78.0       |\n| Component.Android.NDK.R23C                                                | 17.14.36510.44  |\n| Component.Android.SDK.MAUI                                                | 17.14.36510.44  |\n| Component.Dotfuscator                                                     | 17.14.36510.44  |\n| Component.Linux.CMake                                                     | 17.14.36510.44  |\n| Component.Linux.RemoteFileExplorer                                        | 17.14.36510.44  |\n| Component.MDD.Android                                                     | 17.14.36804.6   |\n| Component.MDD.Linux                                                       | 17.14.36510.44  |\n| Component.Microsoft.VisualStudio.RazorExtension                           | 17.14.36510.44  |\n| Component.Microsoft.VisualStudio.Tools.Applications.amd64                 | 17.0.36522.0    |\n| Component.Microsoft.VisualStudio.Web.AzureFunctions                       | 17.14.36510.44  |\n| Component.Microsoft.Web.LibraryManager                                    | 17.14.36510.44  |\n| Component.Microsoft.WebTools.BrowserLink.WebLivePreview                   | 17.14.2.50506   |\n| Component.Microsoft.Windows.DriverKit                                     | 10.0.26100.16   |\n| Component.OpenJDK                                                         | 17.14.36510.44  |\n| Component.UnityEngine.x64                                                 | 17.14.36510.44  |\n| Component.Unreal                                                          | 17.14.36510.44  |\n| Component.Unreal.Android                                                  | 17.14.36510.44  |\n| Component.Unreal.Debugger                                                 | 17.14.36907.17  |\n| Component.Unreal.Ide                                                      | 17.14.36510.44  |\n| Component.VisualStudio.GitHub.Copilot                                     | 17.14.37011.9   |\n| Component.VSInstallerProjects2022                                         | 3.0.0           |\n| Component.WixToolset.VisualStudioExtension.Dev17                          | 1.0.0.22        |\n| Component.WixToolset.VisualStudioExtension.Schemas3                       | 1.0.0.22        |\n| Component.Xamarin                                                         | 17.14.36510.44  |\n| ComponentGroup.Microsoft.NET.AppModernization                             | 17.14.37011.9   |\n| ios                                                                       | 26.0.9752.0     |\n| maccatalyst                                                               | 26.0.9752.0     |\n| maui.blazor                                                               | 9.0.111.6930    |\n| maui.core                                                                 | 9.0.111.6930    |\n| maui.windows                                                              | 9.0.111.6930    |\n| Microsoft.Component.Azure.DataLake.Tools                                  | 17.14.36510.44  |\n| Microsoft.Component.ClickOnce                                             | 17.14.36510.44  |\n| Microsoft.Component.CodeAnalysis.SDK                                      | 17.14.36510.44  |\n| Microsoft.Component.MSBuild                                               | 17.14.36510.44  |\n| Microsoft.Component.NetFX.Native                                          | 17.14.36510.44  |\n| Microsoft.Component.PythonTools                                           | 17.14.36510.44  |\n| Microsoft.Component.PythonTools.Web                                       | 17.14.36510.44  |\n| Microsoft.Component.VC.Runtime.UCRTSDK                                    | 17.14.36510.44  |\n| Microsoft.ComponentGroup.Blend                                            | 17.14.36510.44  |\n| Microsoft.ComponentGroup.ClickOnce.Publish                                | 17.14.36510.44  |\n| Microsoft.Net.Component.4.5.2.TargetingPack                               | 17.14.36510.44  |\n| Microsoft.Net.Component.4.6.2.TargetingPack                               | 17.14.36510.44  |\n| Microsoft.Net.Component.4.6.TargetingPack                                 | 17.14.36510.44  |\n| Microsoft.Net.Component.4.7.1.TargetingPack                               | 17.14.36510.44  |\n| Microsoft.Net.Component.4.7.2.SDK                                         | 17.14.36510.44  |\n| Microsoft.Net.Component.4.7.2.TargetingPack                               | 17.14.36510.44  |\n| Microsoft.Net.Component.4.7.TargetingPack                                 | 17.14.36510.44  |\n| Microsoft.Net.Component.4.8.1.SDK                                         | 17.14.36510.44  |\n| Microsoft.Net.Component.4.8.1.TargetingPack                               | 17.14.36510.44  |\n| Microsoft.Net.Component.4.8.SDK                                           | 17.14.36510.44  |\n| Microsoft.Net.Component.4.8.TargetingPack                                 | 17.14.36510.44  |\n| Microsoft.Net.ComponentGroup.4.8.DeveloperTools                           | 17.14.36510.44  |\n| Microsoft.Net.ComponentGroup.DevelopmentPrerequisites                     | 17.14.36510.44  |\n| Microsoft.Net.ComponentGroup.TargetingPacks.Common                        | 17.14.36510.44  |\n| microsoft.net.runtime.android                                             | 9.0.1326.6317   |\n| microsoft.net.runtime.android.aot                                         | 9.0.1326.6317   |\n| microsoft.net.runtime.android.aot.net8                                    | 9.0.1326.6317   |\n| microsoft.net.runtime.android.net8                                        | 9.0.1326.6317   |\n| microsoft.net.runtime.ios                                                 | 9.0.1326.6317   |\n| microsoft.net.runtime.maccatalyst                                         | 9.0.1326.6317   |\n| microsoft.net.runtime.mono.tooling                                        | 9.0.1326.6317   |\n| microsoft.net.runtime.mono.tooling.net8                                   | 9.0.1326.6317   |\n| microsoft.net.sdk.emscripten                                              | 9.0.14.5604     |\n| Microsoft.NetCore.Component.DevelopmentTools                              | 17.14.36510.44  |\n| Microsoft.NetCore.Component.Runtime.8.0                                   | 17.14.36928.8   |\n| Microsoft.NetCore.Component.Runtime.9.0                                   | 17.14.36928.8   |\n| Microsoft.NetCore.Component.SDK                                           | 17.14.36928.8   |\n| Microsoft.NetCore.Component.Web                                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.AppInsights.Tools                        | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.AspNet                                   | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.AspNet45                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Azure.AuthoringTools                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Azure.ClientLibs                         | 17.14.36907.17  |\n| Microsoft.VisualStudio.Component.Azure.Compute.Emulator                   | 17.14.36517.7   |\n| Microsoft.VisualStudio.Component.Azure.ResourceManager.Tools              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Azure.Waverton                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Azure.Waverton.BuildTools                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.ClassDesigner                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.CodeMap                                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Common.Azure.Tools                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.CoreEditor                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.CppBuildInsights                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Debugger.JustInTime                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.DiagnosticTools                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.DockerTools                              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.DotNetModelBuilder                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.DslTools                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.EntityFramework                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.FSharp                                   | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.FSharp.Desktop                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.FSharp.WebTemplates                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.GraphDocument                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Graphics                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Graphics.Tools                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.HLSL                                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.IISExpress                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.IntelliCode                              | 17.14.36621.7   |\n| Microsoft.VisualStudio.Component.IntelliTrace.FrontEnd                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.JavaScript.Diagnostics                   | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.JavaScript.TypeScript                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.LinqToSql                                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.LiveUnitTesting                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.ManagedDesktop.Core                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.MSODBC.SQL                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.MSSQL.CMDLnUtils                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Node.Tools                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.NuGet                                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.NuGet.BuildTools                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.PortableLibrary                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Roslyn.Compiler                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Roslyn.LanguageServices                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Sharepoint.Tools                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.SQL.CLR                                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.SQL.DataSources                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.SQL.SSDT                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.TeamOffice                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.TestTools.CodedUITest                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.TestTools.WebLoadTest                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.TextTemplating                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.TypeScript.TSServer                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Unity                                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.UWP.VC.ARM64                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.UWP.VC.ARM64EC                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.14.29.16.11.ARM                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.14.29.16.11.ARM64                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ASAN                                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL                                   | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL.ARM                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL.ARM.Spectre                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL.ARM64                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL.Spectre                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATLMFC                                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre                        | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.CLI.Support                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.CMake.Project                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.CoreIde                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.DiagnosticTools                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Llvm.Clang                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.MFC.ARM                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.MFC.ARM.Spectre                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.MFC.ARM64                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Modules.x86.x64                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Redist.14.Latest                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Redist.MSM                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Runtimes.ARM.Spectre                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Runtimes.ARM64EC.Spectre              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Tools.ARM                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Tools.ARM64                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Tools.ARM64EC                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Tools.x86.x64                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Vcpkg                                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VSSDK                                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Wcf.Tooling                              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Web                                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.WebDeploy                                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Windows10SDK                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Windows10SDK.19041                       | 17.14.36809.9   |\n| Microsoft.VisualStudio.Component.Windows11SDK.22621                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Windows11SDK.26100                       | 17.14.37011.9   |\n| Microsoft.VisualStudio.Component.Windows11Sdk.WindowsPerformanceToolkit   | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.WindowsAppSdkSupport.CSharp              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Workflow                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.WslDebugging                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.ArchitectureTools.Native            | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Azure.Prerequisites                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools         | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.AzureFunctions                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.All                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Android                        | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Blazor                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.iOS                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.MacCatalyst                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Shared                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Windows                        | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang            | 17.14.36802.14  |\n| Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard              | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.VC.Tools.142.x86.x64                | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.VisualStudioExtension.Prerequisites | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Web                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Web.CloudTools                      | 17.14.36614.30  |\n| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.CMake            | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.TemplateEngine   | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.WindowsAppDevelopment.Prerequisites | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.WindowsAppSDK.Cs                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Workload.Azure                                     | 17.14.36904.0   |\n| Microsoft.VisualStudio.Workload.CoreEditor                                | 17.14.36015.10  |\n| Microsoft.VisualStudio.Workload.Data                                      | 17.14.36015.10  |\n| Microsoft.VisualStudio.Workload.DataScience                               | 17.14.36015.10  |\n| Microsoft.VisualStudio.Workload.ManagedDesktop                            | 17.14.36518.2   |\n| Microsoft.VisualStudio.Workload.ManagedGame                               | 17.14.36301.6   |\n| Microsoft.VisualStudio.Workload.NativeCrossPlat                           | 17.14.36716.0   |\n| Microsoft.VisualStudio.Workload.NativeDesktop                             | 17.14.36517.7   |\n| Microsoft.VisualStudio.Workload.NativeGame                                | 17.14.36331.10  |\n| Microsoft.VisualStudio.Workload.NativeMobile                              | 17.14.36802.14  |\n| Microsoft.VisualStudio.Workload.NetCrossPlat                              | 17.14.36518.2   |\n| Microsoft.VisualStudio.Workload.NetWeb                                    | 17.14.36518.2   |\n| Microsoft.VisualStudio.Workload.Node                                      | 17.14.36517.7   |\n| Microsoft.VisualStudio.Workload.Office                                    | 17.14.36015.10  |\n| Microsoft.VisualStudio.Workload.Python                                    | 17.14.36015.10  |\n| Microsoft.VisualStudio.Workload.Universal                                 | 17.14.36331.10  |\n| Microsoft.VisualStudio.Workload.VisualStudioExtension                     | 17.14.36015.10  |\n| runtimes.ios                                                              | 9.0.1326.6317   |\n| runtimes.maccatalyst                                                      | 9.0.1326.6317   |\n| wasm.tools                                                                | 9.0.1326.6317   |\n| ProBITools.MicrosoftAnalysisServicesModelingProjects2022                  | 4.0.0           |\n| ProBITools.MicrosoftReportProjectsforVisualStudio2022                     | 4.0.0           |\n| SSIS.MicrosoftDataToolsIntegrationServices                                | 2.1.2           |\n| VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects             | 3.0.0           |\n| Windows Driver Kit                                                        | 10.1.26100.4202 |\n| Windows Driver Kit Visual Studio Extension                                | 10.0.26100.16   |\n| Windows Software Development Kit                                          | 10.1.26100.7705 |\n| WixToolset.WixToolsetVisualStudio2022Extension                            | 1.0.0.22        |\n\n#### Microsoft Visual C++\n| Name                                         | Architecture | Version     |\n| -------------------------------------------- | ------------ | ----------- |\n| Microsoft Visual C++ 2013 Additional Runtime | x64          | 12.0.40660  |\n| Microsoft Visual C++ 2013 Minimum Runtime    | x64          | 12.0.40660  |\n| Microsoft Visual C++ 2022 Additional Runtime | x64          | 14.50.35719 |\n| Microsoft Visual C++ 2022 Debug Runtime      | x64          | 14.44.35211 |\n| Microsoft Visual C++ 2022 Minimum Runtime    | x64          | 14.50.35719 |\n| Microsoft Visual C++ 2022 Additional Runtime | x86          | 14.50.35719 |\n| Microsoft Visual C++ 2022 Debug Runtime      | x86          | 14.44.35211 |\n| Microsoft Visual C++ 2022 Minimum Runtime    | x86          | 14.50.35719 |\n\n#### Installed Windows SDKs\n- 10.0.17763.0\n- 10.0.19041.0\n- 10.0.22621.0\n- 10.0.26100.0\n\n### .NET Core Tools\n- .NET Core SDK: 8.0.124, 8.0.206, 8.0.319, 8.0.418, 9.0.114, 9.0.205, 9.0.311, 10.0.102\n- .NET Framework: 4.7.2, 4.8, 4.8.1\n- Microsoft.AspNetCore.App: 6.0.40, 8.0.6, 8.0.22, 8.0.24, 9.0.6, 9.0.13, 10.0.2\n- Microsoft.NETCore.App: 6.0.40, 8.0.6, 8.0.22, 8.0.24, 9.0.6, 9.0.13, 10.0.2\n- Microsoft.WindowsDesktop.App: 8.0.6, 8.0.22, 8.0.24, 9.0.6, 9.0.13, 10.0.2\n- nbgv 3.9.50+6feeb89450\n\n### PowerShell Tools\n- PowerShell 7.4.13\n\n#### Powershell Modules\n- Az: 14.6.0\n- AWSPowershell: 5.0.165\n- DockerMsftProvider: 1.0.0.8\n- MarkdownPS: 1.10\n- Microsoft.Graph: 2.35.1\n- Pester: 3.4.0, 5.7.1\n- PowerShellGet: 1.0.0.1, 2.2.5\n- PSScriptAnalyzer: 1.24.0\n- PSWindowsUpdate: 2.2.1.5\n- SqlServer: 22.3.0\n- VSSetup: 2.2.16\n\n### Android\n| Package Name               | Version                                                                                                                                                                                                                                                                                                                                     |\n| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Android Command Line Tools | 8.0                                                                                                                                                                                                                                                                                                                                         |\n| Android Emulator           | 36.4.9                                                                                                                                                                                                                                                                                                                                      |\n| Android SDK Build-tools    | 36.0.0 36.1.0<br>35.0.0 35.0.1<br>34.0.0<br>32.0.0                                                                                                                                                                                                                                                                                          |\n| Android SDK Platforms      | android-36.1 (rev 1)<br>android-36-ext19 (rev 1)<br>android-36-ext18 (rev 1)<br>android-36 (rev 2)<br>android-35-ext15 (rev 1)<br>android-35-ext14 (rev 1)<br>android-35 (rev 2)<br>android-34-ext8 (rev 1)<br>android-34-ext12 (rev 1)<br>android-34-ext11 (rev 1)<br>android-34-ext10 (rev 1)<br>android-34 (rev 3)<br>android-33 (rev 3) |\n| Android SDK Platform-Tools | 36.0.2                                                                                                                                                                                                                                                                                                                                      |\n| Android Support Repository | 47.0.0                                                                                                                                                                                                                                                                                                                                      |\n| CMake                      | 3.22.1<br>3.31.5<br>4.1.2                                                                                                                                                                                                                                                                                                                   |\n| Google Play services       | 49                                                                                                                                                                                                                                                                                                                                          |\n| Google Repository          | 58                                                                                                                                                                                                                                                                                                                                          |\n| NDK                        | 27.3.13750724<br>28.2.13676358<br>29.0.14206865                                                                                                                                                                                                                                                                                             |\n\n#### Environment variables\n| Name                    | Value                                    |\n| ----------------------- | ---------------------------------------- |\n| ANDROID_HOME            | C:\\Android\\android-sdk                   |\n| ANDROID_NDK             | C:\\Android\\android-sdk\\ndk\\27.3.13750724 |\n| ANDROID_NDK_HOME        | C:\\Android\\android-sdk\\ndk\\27.3.13750724 |\n| ANDROID_NDK_LATEST_HOME | C:\\Android\\android-sdk\\ndk\\29.0.14206865 |\n| ANDROID_NDK_ROOT        | C:\\Android\\android-sdk\\ndk\\27.3.13750724 |\n| ANDROID_SDK_ROOT        | C:\\Android\\android-sdk                   |\n\n### Cached Docker images\n| Repository:Tag                                                            | Digest                                                                   | Created    |\n| ------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ---------- |\n| mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022  | sha256:ec04e733695f49a0dc9132184f6b06704866b34f422004093c1972512c86259e  | 2025-09-09 |\n| mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2022 | sha256:3983348680840ca6e53ad641e314c3c9184ca2fd19f88bc467600f7d9f6e9d73  | 2025-09-09 |\n| mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2022     | sha256:460dedaed73224f73ff10dc3ad754d0ed250aa57bcdf6c5052a811b4b7e29345  | 2025-09-09 |\n| mcr.microsoft.com/windows/nanoserver:ltsc2022                             | sha256:60612a30303eb5a15ce7f53fa2eecf70bca41d72657de0482fbde601ae5f3403  | 2026-02-06 |\n| mcr.microsoft.com/windows/servercore:ltsc2022                             | sha256:a264df8cd8c329eed3fd1e0cafcd4f3dc453e2c72a277f9bb140fd6f10a2eefc  | 2026-02-06 |\n\n"
  },
  {
    "path": "images/windows/Windows2025-Readme.md",
    "content": "| Announcements |\n|-|\n| [Windows Server 2025 with Visual Studio 2026 is now available as a public beta](https://github.com/actions/runner-images/issues/13638) |\n| [[Windows/Ubuntu] Docker Server and Client will be updated to version 29.1.*, Docker Compose will be updated to version 2.40.3 on February 9th, 2026](https://github.com/actions/runner-images/issues/13474) |\n***\n# Windows Server 2025\n- OS Version: 10.0.26100 Build 32370\n- Image Version: 20260302.43.1\n\n## Windows features\n- Windows Subsystem for Linux (WSLv1): Enabled\n- Windows Subsystem for Linux (Default, WSLv2): 2.6.3.0\n\n## Installed Software\n\n### Language and Runtime\n- Bash 5.2.37(1)-release\n- Go 1.24.13\n- Julia 1.12.0\n- Kotlin 2.3.10\n- LLVM 20.1.8\n- Node 22.22.0\n- Perl 5.42.0\n- PHP 8.5.3\n- Python 3.12.10\n- Ruby 3.3.10\n\n### Package Management\n- Chocolatey 2.6.0\n- Composer 2.9.5\n- Helm 4.1.0\n- Miniconda 25.11.1 (pre-installed on the image but not added to PATH)\n- NPM 10.9.4\n- NuGet 7.3.0.70\n- pip 26.0.1 (python 3.12)\n- Pipx 1.8.0\n- RubyGems 3.5.22\n- Vcpkg (build from commit 39a6cc0e44)\n- Yarn 1.22.22\n\n#### Environment variables\n| Name                    | Value        |\n| ----------------------- | ------------ |\n| VCPKG_INSTALLATION_ROOT | C:\\vcpkg     |\n| CONDA                   | C:\\Miniconda |\n\n### Project Management\n- Ant 1.10.15\n- Gradle 9.3\n- Maven 3.9.12\n- sbt 1.12.5\n\n### Tools\n- 7zip 26.00\n- aria2 1.37.0\n- azcopy 10.32.1\n- Bazel 9.0.0\n- Bazelisk 1.28.1\n- Bicep 0.41.2\n- Cabal 3.16.1.0\n- CMake 3.31.6\n- CodeQL Action Bundle 2.24.2\n- Docker 29.1.5\n- Docker Compose v2 2.40.3\n- Docker-wincred 0.9.5\n- ghc 9.14.1\n- Git 2.53.0.windows.1\n- Git LFS 3.7.1\n- ImageMagick 7.1.2-15\n- InnoSetup 6.7.1\n- jq 1.8.1\n- Kind 0.31.0\n- Kubectl 1.35.2\n- gcc 15.2.0\n- gdb 17.1\n- GNU Binutils 2.46\n- Newman 6.2.2\n- OpenSSL 3.6.1\n- Packer 1.15.0\n- Pulumi 3.224.0\n- R 4.5.2\n- Service Fabric SDK 10.1.2493.9590\n- Stack 3.9.3\n- Swig 4.3.1\n- VSWhere 3.1.7\n- WinAppDriver 1.2.2009.02003\n- WiX Toolset 3.14.1.8722\n- yamllint 1.38.0\n- zstd 1.5.7\n- Ninja 1.13.2\n\n### CLI Tools\n- AWS CLI 2.34.0\n- AWS SAM CLI 1.154.0\n- AWS Session Manager CLI 1.2.779.0\n- Azure CLI 2.83.0\n- Azure DevOps CLI extension 1.0.2\n- GitHub CLI 2.87.3\n\n### Rust Tools\n- Cargo 1.93.1\n- Rust 1.93.1\n- Rustdoc 1.93.1\n- Rustup 1.28.2\n\n#### Packages\n- Clippy 0.1.93\n- Rustfmt 1.8.0\n\n### Browsers and Drivers\n- Google Chrome 145.0.7632.117\n- Chrome Driver 145.0.7632.117\n- Microsoft Edge 145.0.3800.82\n- Microsoft Edge Driver 145.0.3800.82\n- Mozilla Firefox 148.0\n- Gecko Driver 0.36.0\n- IE Driver 4.14.0.0\n- Selenium server 4.41.0\n\n#### Environment variables\n| Name              | Value                              |\n| ----------------- | ---------------------------------- |\n| CHROMEWEBDRIVER   | C:\\SeleniumWebDrivers\\ChromeDriver |\n| EDGEWEBDRIVER     | C:\\SeleniumWebDrivers\\EdgeDriver   |\n| GECKOWEBDRIVER    | C:\\SeleniumWebDrivers\\GeckoDriver  |\n| SELENIUM_JAR_PATH | C:\\selenium\\selenium-server.jar    |\n\n### Java\n| Version             | Environment Variable |\n| ------------------- | -------------------- |\n| 8.0.482+8           | JAVA_HOME_8_X64      |\n| 11.0.30+7           | JAVA_HOME_11_X64     |\n| 17.0.18+8 (default) | JAVA_HOME_17_X64     |\n| 21.0.10+7.0         | JAVA_HOME_21_X64     |\n| 25.0.2+10.0         | JAVA_HOME_25_X64     |\n\n### Shells\n| Name          | Target                            |\n| ------------- | --------------------------------- |\n| gitbash.exe   | C:\\Program Files\\Git\\bin\\bash.exe |\n| msys2bash.cmd | C:\\msys64\\usr\\bin\\bash.exe        |\n| wslbash.exe   | C:\\Windows\\System32\\bash.exe      |\n\n### MSYS2\n- Pacman 6.1.0\n\n#### Notes\n```\nLocation: C:\\msys64\n\nNote: MSYS2 is pre-installed on image but not added to PATH.\n```\n\n### Cached Tools\n\n#### Go\n- 1.22.12\n- 1.23.12\n- 1.24.13\n- 1.25.7\n\n#### Node.js\n- 20.20.0\n- 22.22.0\n- 24.14.0\n\n#### Python\n- 3.10.11\n- 3.11.9\n- 3.12.10\n- 3.13.12\n- 3.14.3\n\n#### PyPy\n- 3.9.19 [PyPy 7.3.16]\n- 3.10.16 [PyPy 7.3.19]\n\n#### Ruby\n- 3.2.10\n- 3.3.10\n- 3.4.8\n- 4.0.1\n\n### Databases\n\n#### PostgreSQL\n| Property             | Value                                                                                                                  |\n| -------------------- | ---------------------------------------------------------------------------------------------------------------------- |\n| ServiceName          | postgresql-x64-17                                                                                                      |\n| Version              | 17.9                                                                                                                   |\n| ServiceStatus        | Stopped                                                                                                                |\n| ServiceStartType     | Disabled                                                                                                               |\n| EnvironmentVariables | PGBIN=C:\\Program Files\\PostgreSQL\\17\\bin <br> PGDATA=C:\\PostgreSQL\\17\\data <br> PGROOT=C:\\Program Files\\PostgreSQL\\17  |\n| Path                 | C:\\Program Files\\PostgreSQL\\17                                                                                         |\n| UserName             | postgres                                                                                                               |\n| Password             | root                                                                                                                   |\n\n#### MongoDB\n| Version  | ServiceName | ServiceStatus | ServiceStartType |\n| -------- | ----------- | ------------- | ---------------- |\n| 7.0.30.0 | MongoDB     | Stopped       | Disabled         |\n\n### Database tools\n- Azure CosmosDb Emulator 2.14.27.0\n- DacFx 170.3.93.6\n- MySQL 8.0.45.0\n- SQL OLEDB Driver 18 18.7.5.0\n- SQL OLEDB Driver 19 19.4.1.0\n- SQLPS 1.0\n- MongoDB Shell (mongosh) 2.7.0\n\n### Web Servers\n| Name   | Version | ConfigFile                            | ServiceName | ServiceStatus | ListenPort |\n| ------ | ------- | ------------------------------------- | ----------- | ------------- | ---------- |\n| Apache | 2.4.55  | C:\\tools\\Apache24\\conf\\httpd.conf     | Apache      | Stopped       | 80         |\n| Nginx  | 1.29.5  | C:\\tools\\nginx-1.29.5\\conf\\nginx.conf | nginx       | Stopped       | 80         |\n\n### Visual Studio Enterprise 2022\n| Name                          | Version       | Path                                                     |\n| ----------------------------- | ------------- | -------------------------------------------------------- |\n| Visual Studio Enterprise 2022 | 17.14.37012.4 | C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise |\n\n#### Workloads, components and extensions\n| Package                                                                   | Version         |\n| ------------------------------------------------------------------------- | --------------- |\n| android                                                                   | 35.0.78.0       |\n| Component.Android.NDK.R23C                                                | 17.14.36510.44  |\n| Component.Android.SDK.MAUI                                                | 17.14.36510.44  |\n| Component.Dotfuscator                                                     | 17.14.36510.44  |\n| Component.Linux.CMake                                                     | 17.14.36510.44  |\n| Component.Linux.RemoteFileExplorer                                        | 17.14.36510.44  |\n| Component.MDD.Android                                                     | 17.14.36804.6   |\n| Component.MDD.Linux                                                       | 17.14.36510.44  |\n| Component.Microsoft.VisualStudio.RazorExtension                           | 17.14.36510.44  |\n| Component.Microsoft.VisualStudio.Tools.Applications.amd64                 | 17.0.36522.0    |\n| Component.Microsoft.VisualStudio.Web.AzureFunctions                       | 17.14.36510.44  |\n| Component.Microsoft.Web.LibraryManager                                    | 17.14.36510.44  |\n| Component.Microsoft.WebTools.BrowserLink.WebLivePreview                   | 17.14.2.50506   |\n| Component.Microsoft.Windows.DriverKit                                     | 10.0.26100.16   |\n| Component.OpenJDK                                                         | 17.14.36510.44  |\n| Component.UnityEngine.x64                                                 | 17.14.36510.44  |\n| Component.Unreal.Debugger                                                 | 17.14.36907.17  |\n| Component.Unreal.Ide                                                      | 17.14.36510.44  |\n| Component.VisualStudio.GitHub.Copilot                                     | 17.14.37011.9   |\n| Component.VSInstallerProjects2022                                         | 3.0.0           |\n| Component.WixToolset.VisualStudioExtension.Dev17                          | 1.0.0.22        |\n| Component.WixToolset.VisualStudioExtension.Schemas3                       | 1.0.0.22        |\n| ComponentGroup.Microsoft.NET.AppModernization                             | 17.14.37011.9   |\n| ios                                                                       | 26.0.9752.0     |\n| maccatalyst                                                               | 26.0.9752.0     |\n| maui.blazor                                                               | 9.0.111.6930    |\n| maui.core                                                                 | 9.0.111.6930    |\n| maui.windows                                                              | 9.0.111.6930    |\n| Microsoft.Component.Azure.DataLake.Tools                                  | 17.14.36510.44  |\n| Microsoft.Component.ClickOnce                                             | 17.14.36510.44  |\n| Microsoft.Component.CodeAnalysis.SDK                                      | 17.14.36510.44  |\n| Microsoft.Component.MSBuild                                               | 17.14.36510.44  |\n| Microsoft.Component.NetFX.Native                                          | 17.14.36510.44  |\n| Microsoft.Component.PythonTools                                           | 17.14.36510.44  |\n| Microsoft.Component.PythonTools.Web                                       | 17.14.36510.44  |\n| Microsoft.Component.VC.Runtime.UCRTSDK                                    | 17.14.36510.44  |\n| Microsoft.ComponentGroup.Blend                                            | 17.14.36510.44  |\n| Microsoft.ComponentGroup.ClickOnce.Publish                                | 17.14.36510.44  |\n| Microsoft.Net.Component.4.5.2.TargetingPack                               | 17.14.36510.44  |\n| Microsoft.Net.Component.4.6.2.TargetingPack                               | 17.14.36510.44  |\n| Microsoft.Net.Component.4.6.TargetingPack                                 | 17.14.36510.44  |\n| Microsoft.Net.Component.4.7.1.TargetingPack                               | 17.14.36510.44  |\n| Microsoft.Net.Component.4.7.2.TargetingPack                               | 17.14.36510.44  |\n| Microsoft.Net.Component.4.7.TargetingPack                                 | 17.14.36510.44  |\n| Microsoft.Net.Component.4.8.1.SDK                                         | 17.14.36510.44  |\n| Microsoft.Net.Component.4.8.1.TargetingPack                               | 17.14.36510.44  |\n| Microsoft.Net.Component.4.8.SDK                                           | 17.14.36510.44  |\n| Microsoft.Net.Component.4.8.TargetingPack                                 | 17.14.36510.44  |\n| Microsoft.Net.ComponentGroup.4.8.DeveloperTools                           | 17.14.36510.44  |\n| Microsoft.Net.ComponentGroup.DevelopmentPrerequisites                     | 17.14.36510.44  |\n| Microsoft.Net.ComponentGroup.TargetingPacks.Common                        | 17.14.36510.44  |\n| microsoft.net.runtime.android                                             | 9.0.1326.6317   |\n| microsoft.net.runtime.android.aot                                         | 9.0.1326.6317   |\n| microsoft.net.runtime.android.aot.net8                                    | 9.0.1326.6317   |\n| microsoft.net.runtime.android.net8                                        | 9.0.1326.6317   |\n| microsoft.net.runtime.ios                                                 | 9.0.1326.6317   |\n| microsoft.net.runtime.maccatalyst                                         | 9.0.1326.6317   |\n| microsoft.net.runtime.mono.tooling                                        | 9.0.1326.6317   |\n| microsoft.net.runtime.mono.tooling.net8                                   | 9.0.1326.6317   |\n| microsoft.net.sdk.emscripten                                              | 9.0.14.5604     |\n| Microsoft.NetCore.Component.DevelopmentTools                              | 17.14.36510.44  |\n| Microsoft.NetCore.Component.Runtime.8.0                                   | 17.14.36928.8   |\n| Microsoft.NetCore.Component.Runtime.9.0                                   | 17.14.36928.8   |\n| Microsoft.NetCore.Component.SDK                                           | 17.14.36928.8   |\n| Microsoft.NetCore.Component.Web                                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.AppInsights.Tools                        | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.AspNet                                   | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.AspNet45                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Azure.AuthoringTools                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Azure.ClientLibs                         | 17.14.36907.17  |\n| Microsoft.VisualStudio.Component.Azure.Compute.Emulator                   | 17.14.36517.7   |\n| Microsoft.VisualStudio.Component.Azure.ResourceManager.Tools              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Azure.Waverton                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Azure.Waverton.BuildTools                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.ClassDesigner                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.CodeMap                                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Common.Azure.Tools                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.CoreEditor                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.CppBuildInsights                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Debugger.JustInTime                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.DiagnosticTools                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.DockerTools                              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.DotNetModelBuilder                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.DslTools                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.EntityFramework                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.FSharp                                   | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.FSharp.Desktop                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.FSharp.WebTemplates                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.GraphDocument                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Graphics                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Graphics.Tools                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.HLSL                                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.IISExpress                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.IntelliCode                              | 17.14.36621.7   |\n| Microsoft.VisualStudio.Component.IntelliTrace.FrontEnd                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.JavaScript.Diagnostics                   | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.JavaScript.TypeScript                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.LinqToSql                                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.LiveUnitTesting                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.ManagedDesktop.Core                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.MSODBC.SQL                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.MSSQL.CMDLnUtils                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Node.Tools                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.NuGet                                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.NuGet.BuildTools                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.PortableLibrary                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Roslyn.Compiler                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Roslyn.LanguageServices                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Sharepoint.Tools                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.SQL.CLR                                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.SQL.DataSources                          | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.SQL.SSDT                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.TeamOffice                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.TestTools.CodedUITest                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.TestTools.WebLoadTest                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.TextTemplating                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.TypeScript.TSServer                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Unity                                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.UWP.VC.ARM64                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.UWP.VC.ARM64EC                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.14.29.16.11.ARM                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.14.29.16.11.ARM64                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ASAN                                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL                                   | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL.ARM                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL.ARM.Spectre                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL.ARM64                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATL.Spectre                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATLMFC                                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre                        | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.CLI.Support                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.CMake.Project                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.CoreIde                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.DiagnosticTools                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Llvm.Clang                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.MFC.ARM                               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.MFC.ARM.Spectre                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.MFC.ARM64                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre                     | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Modules.x86.x64                       | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Redist.14.Latest                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Redist.MSM                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Runtimes.ARM.Spectre                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Runtimes.ARM64EC.Spectre              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest               | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Tools.ARM                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Tools.ARM64                           | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Tools.ARM64EC                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VC.Tools.x86.x64                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Vcpkg                                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.VSSDK                                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Wcf.Tooling                              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Web                                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.WebDeploy                                | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Windows10SDK                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Windows11SDK.26100                       | 17.14.37011.9   |\n| Microsoft.VisualStudio.Component.Windows11Sdk.WindowsPerformanceToolkit   | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.WindowsAppSdkSupport.CSharp              | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.Workflow                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.Component.WslDebugging                             | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.ArchitectureTools.Native            | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Azure.Prerequisites                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools         | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.AzureFunctions                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.All                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Android                        | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Blazor                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.iOS                            | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.MacCatalyst                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Shared                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Windows                        | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging                      | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang            | 17.14.36802.14  |\n| Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard              | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142                         | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.VC.Tools.142.x86.x64                | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.VisualStudioExtension.Prerequisites | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Web                                 | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.Web.CloudTools                      | 17.14.36614.30  |\n| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions                  | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.CMake            | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.TemplateEngine   | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.WindowsAppDevelopment.Prerequisites | 17.14.36510.44  |\n| Microsoft.VisualStudio.ComponentGroup.WindowsAppSDK.Cs                    | 17.14.36510.44  |\n| Microsoft.VisualStudio.Workload.Azure                                     | 17.14.36904.0   |\n| Microsoft.VisualStudio.Workload.CoreEditor                                | 17.14.36015.10  |\n| Microsoft.VisualStudio.Workload.Data                                      | 17.14.36015.10  |\n| Microsoft.VisualStudio.Workload.DataScience                               | 17.14.36015.10  |\n| Microsoft.VisualStudio.Workload.ManagedDesktop                            | 17.14.36518.2   |\n| Microsoft.VisualStudio.Workload.ManagedGame                               | 17.14.36301.6   |\n| Microsoft.VisualStudio.Workload.NativeCrossPlat                           | 17.14.36716.0   |\n| Microsoft.VisualStudio.Workload.NativeDesktop                             | 17.14.36517.7   |\n| Microsoft.VisualStudio.Workload.NativeGame                                | 17.14.36331.10  |\n| Microsoft.VisualStudio.Workload.NativeMobile                              | 17.14.36802.14  |\n| Microsoft.VisualStudio.Workload.NetCrossPlat                              | 17.14.36518.2   |\n| Microsoft.VisualStudio.Workload.NetWeb                                    | 17.14.36518.2   |\n| Microsoft.VisualStudio.Workload.Node                                      | 17.14.36517.7   |\n| Microsoft.VisualStudio.Workload.Office                                    | 17.14.36015.10  |\n| Microsoft.VisualStudio.Workload.Python                                    | 17.14.36015.10  |\n| Microsoft.VisualStudio.Workload.Universal                                 | 17.14.36331.10  |\n| Microsoft.VisualStudio.Workload.VisualStudioExtension                     | 17.14.36015.10  |\n| runtimes.ios                                                              | 9.0.1326.6317   |\n| runtimes.maccatalyst                                                      | 9.0.1326.6317   |\n| wasm.tools                                                                | 9.0.1326.6317   |\n| ProBITools.MicrosoftAnalysisServicesModelingProjects2022                  | 4.0.0           |\n| ProBITools.MicrosoftReportProjectsforVisualStudio2022                     | 4.0.0           |\n| SSIS.MicrosoftDataToolsIntegrationServices                                | 2.1.2           |\n| VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects             | 3.0.0           |\n| Windows Driver Kit Visual Studio Extension                                | 10.0.26100.16   |\n| Windows Software Development Kit                                          | 10.1.26100.7705 |\n| WixToolset.WixToolsetVisualStudio2022Extension                            | 1.0.0.22        |\n\n#### Microsoft Visual C++\n| Name                                         | Architecture | Version     |\n| -------------------------------------------- | ------------ | ----------- |\n| Microsoft Visual C++ 2013 Additional Runtime | x64          | 12.0.40660  |\n| Microsoft Visual C++ 2013 Minimum Runtime    | x64          | 12.0.40660  |\n| Microsoft Visual C++ 2022 Additional Runtime | x64          | 14.50.35719 |\n| Microsoft Visual C++ 2022 Debug Runtime      | x64          | 14.44.35211 |\n| Microsoft Visual C++ 2022 Minimum Runtime    | x64          | 14.50.35719 |\n| Microsoft Visual C++ 2022 Additional Runtime | x86          | 14.50.35719 |\n| Microsoft Visual C++ 2022 Debug Runtime      | x86          | 14.44.35211 |\n| Microsoft Visual C++ 2022 Minimum Runtime    | x86          | 14.50.35719 |\n\n#### Installed Windows SDKs\n- 10.0.26100.0\n\n### .NET Core Tools\n- .NET Core SDK: 8.0.124, 8.0.206, 8.0.319, 8.0.418, 9.0.114, 9.0.205, 9.0.311, 10.0.102\n- .NET Framework: 4.8, 4.8.1\n- Microsoft.AspNetCore.App: 8.0.6, 8.0.22, 8.0.24, 9.0.6, 9.0.13, 10.0.2\n- Microsoft.NETCore.App: 8.0.6, 8.0.22, 8.0.24, 9.0.6, 9.0.13, 10.0.2\n- Microsoft.WindowsDesktop.App: 8.0.6, 8.0.22, 8.0.24, 9.0.6, 9.0.13, 10.0.2\n- nbgv 3.9.50+6feeb89450\n\n### PowerShell Tools\n- PowerShell 7.4.13\n\n#### Powershell Modules\n- Az: 14.6.0\n- AWSPowershell: 5.0.165\n- DockerMsftProvider: 1.0.0.8\n- MarkdownPS: 1.10\n- Microsoft.Graph: 2.35.1\n- Pester: 3.4.0, 5.7.1\n- PowerShellGet: 1.0.0.1, 2.2.5\n- PSScriptAnalyzer: 1.24.0\n- PSWindowsUpdate: 2.2.1.5\n- SqlServer: 22.3.0\n- VSSetup: 2.2.16\n\n### Android\n| Package Name               | Version                                                                                                                                                                                                                                                                                                               |\n| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Android Command Line Tools | 16.0                                                                                                                                                                                                                                                                                                                  |\n| Android Emulator           | 36.4.9                                                                                                                                                                                                                                                                                                                |\n| Android SDK Build-tools    | 36.0.0 36.1.0<br>35.0.0 35.0.1<br>34.0.0                                                                                                                                                                                                                                                                              |\n| Android SDK Platforms      | android-36.1 (rev 1)<br>android-36-ext19 (rev 1)<br>android-36-ext18 (rev 1)<br>android-36 (rev 2)<br>android-35-ext15 (rev 1)<br>android-35-ext14 (rev 1)<br>android-35 (rev 2)<br>android-34-ext8 (rev 1)<br>android-34-ext12 (rev 1)<br>android-34-ext11 (rev 1)<br>android-34-ext10 (rev 1)<br>android-34 (rev 3) |\n| Android SDK Platform-Tools | 37.0.0                                                                                                                                                                                                                                                                                                                |\n| Android Support Repository | 47.0.0                                                                                                                                                                                                                                                                                                                |\n| CMake                      | 3.30.5<br>3.31.5<br>4.1.2                                                                                                                                                                                                                                                                                             |\n| Google Play services       | 49                                                                                                                                                                                                                                                                                                                    |\n| Google Repository          | 58                                                                                                                                                                                                                                                                                                                    |\n| NDK                        | 27.3.13750724<br>28.2.13676358<br>29.0.14206865                                                                                                                                                                                                                                                                       |\n\n#### Environment variables\n| Name                    | Value                                    |\n| ----------------------- | ---------------------------------------- |\n| ANDROID_HOME            | C:\\Android\\android-sdk                   |\n| ANDROID_NDK             | C:\\Android\\android-sdk\\ndk\\27.3.13750724 |\n| ANDROID_NDK_HOME        | C:\\Android\\android-sdk\\ndk\\27.3.13750724 |\n| ANDROID_NDK_LATEST_HOME | C:\\Android\\android-sdk\\ndk\\29.0.14206865 |\n| ANDROID_NDK_ROOT        | C:\\Android\\android-sdk\\ndk\\27.3.13750724 |\n| ANDROID_SDK_ROOT        | C:\\Android\\android-sdk                   |\n\n"
  },
  {
    "path": "images/windows/Windows2025-VS2026-Readme.md",
    "content": "| Announcements |\n|-|\n| [Windows Server 2025 with Visual Studio 2026 is now available as a public beta](https://github.com/actions/runner-images/issues/13638) |\n| [[Windows/Ubuntu] Docker Server and Client will be updated to version 29.1.*, Docker Compose will be updated to version 2.40.3 on February 9th, 2026](https://github.com/actions/runner-images/issues/13474) |\n***\n# Windows Server 2025\n- OS Version: 10.0.26100 Build 32370\n- Image Version: 20260301.27.1\n\n## Windows features\n- Windows Subsystem for Linux (WSLv1): Enabled\n- Windows Subsystem for Linux (Default, WSLv2): 2.6.3.0\n\n## Installed Software\n\n### Language and Runtime\n- Bash 5.2.37(1)-release\n- Go 1.24.13\n- Julia 1.12.0\n- Kotlin 2.3.10\n- LLVM 20.1.8\n- Node 22.22.0\n- Perl 5.42.0\n- PHP 8.5.3\n- Python 3.12.10\n- Ruby 3.3.10\n\n### Package Management\n- Chocolatey 2.6.0\n- Composer 2.9.5\n- Helm 4.1.0\n- Miniconda 25.11.1 (pre-installed on the image but not added to PATH)\n- NPM 10.9.4\n- NuGet 7.3.0.70\n- pip 26.0.1 (python 3.12)\n- Pipx 1.8.0\n- RubyGems 3.5.22\n- Vcpkg (build from commit 62159a45e1)\n- Yarn 1.22.22\n\n#### Environment variables\n| Name                    | Value        |\n| ----------------------- | ------------ |\n| VCPKG_INSTALLATION_ROOT | C:\\vcpkg     |\n| CONDA                   | C:\\Miniconda |\n\n### Project Management\n- Ant 1.10.15\n- Gradle 9.3\n- Maven 3.9.12\n- sbt 1.12.4\n\n### Tools\n- 7zip 26.00\n- aria2 1.37.0\n- azcopy 10.32.1\n- Bazel 9.0.0\n- Bazelisk 1.28.1\n- Bicep 0.41.2\n- Cabal 3.16.1.0\n- CMake 4.2.3\n- CodeQL Action Bundle 2.24.2\n- Docker 29.1.5\n- Docker Compose v2 2.40.3\n- Docker-wincred 0.9.5\n- ghc 9.14.1\n- Git 2.53.0.windows.1\n- Git LFS 3.7.1\n- ImageMagick 7.1.2-15\n- InnoSetup 6.7.1\n- jq 1.8.1\n- Kind 0.31.0\n- Kubectl 1.35.2\n- gcc 15.2.0\n- gdb 17.1\n- GNU Binutils 2.46\n- Newman 6.2.2\n- OpenSSL 3.6.1\n- Packer 1.15.0\n- Pulumi 3.224.0\n- R 4.5.2\n- Service Fabric SDK 10.1.2493.9590\n- Stack 3.9.3\n- Swig 4.3.1\n- VSWhere 3.1.7\n- WinAppDriver 1.2.2009.02003\n- WiX Toolset 3.14.1.8722\n- yamllint 1.38.0\n- zstd 1.5.7\n- Ninja 1.13.2\n\n### CLI Tools\n- AWS CLI 2.34.0\n- AWS SAM CLI 1.154.0\n- AWS Session Manager CLI 1.2.779.0\n- Azure CLI 2.83.0\n- Azure DevOps CLI extension 1.0.2\n- GitHub CLI 2.87.3\n\n### Rust Tools\n- Cargo 1.93.1\n- Rust 1.93.1\n- Rustdoc 1.93.1\n- Rustup 1.28.2\n\n#### Packages\n- Clippy 0.1.93\n- Rustfmt 1.8.0\n\n### Browsers and Drivers\n- Google Chrome 145.0.7632.117\n- Chrome Driver 145.0.7632.117\n- Microsoft Edge 145.0.3800.82\n- Microsoft Edge Driver 145.0.3800.82\n- Mozilla Firefox 148.0\n- Gecko Driver 0.36.0\n- IE Driver 4.14.0.0\n- Selenium server 4.41.0\n\n#### Environment variables\n| Name              | Value                              |\n| ----------------- | ---------------------------------- |\n| CHROMEWEBDRIVER   | C:\\SeleniumWebDrivers\\ChromeDriver |\n| EDGEWEBDRIVER     | C:\\SeleniumWebDrivers\\EdgeDriver   |\n| GECKOWEBDRIVER    | C:\\SeleniumWebDrivers\\GeckoDriver  |\n| SELENIUM_JAR_PATH | C:\\selenium\\selenium-server.jar    |\n\n### Java\n| Version             | Environment Variable |\n| ------------------- | -------------------- |\n| 8.0.482+8           | JAVA_HOME_8_X64      |\n| 11.0.30+7           | JAVA_HOME_11_X64     |\n| 17.0.18+8 (default) | JAVA_HOME_17_X64     |\n| 21.0.10+7.0         | JAVA_HOME_21_X64     |\n| 25.0.2+10.0         | JAVA_HOME_25_X64     |\n\n### Shells\n| Name          | Target                            |\n| ------------- | --------------------------------- |\n| gitbash.exe   | C:\\Program Files\\Git\\bin\\bash.exe |\n| msys2bash.cmd | C:\\msys64\\usr\\bin\\bash.exe        |\n| wslbash.exe   | C:\\Windows\\System32\\bash.exe      |\n\n### MSYS2\n- Pacman 6.1.0\n\n#### Notes\n```\nLocation: C:\\msys64\n\nNote: MSYS2 is pre-installed on image but not added to PATH.\n```\n\n### Cached Tools\n\n#### Go\n- 1.22.12\n- 1.23.12\n- 1.24.13\n- 1.25.7\n\n#### Node.js\n- 20.20.0\n- 22.22.0\n- 24.14.0\n\n#### Python\n- 3.10.11\n- 3.11.9\n- 3.12.10\n- 3.13.12\n- 3.14.3\n\n#### PyPy\n- 3.9.19 [PyPy 7.3.16]\n- 3.10.16 [PyPy 7.3.19]\n\n#### Ruby\n- 3.2.10\n- 3.3.10\n- 3.4.8\n- 4.0.1\n\n### Databases\n\n#### PostgreSQL\n| Property             | Value                                                                                                                  |\n| -------------------- | ---------------------------------------------------------------------------------------------------------------------- |\n| ServiceName          | postgresql-x64-17                                                                                                      |\n| Version              | 17.9                                                                                                                   |\n| ServiceStatus        | Stopped                                                                                                                |\n| ServiceStartType     | Disabled                                                                                                               |\n| EnvironmentVariables | PGBIN=C:\\Program Files\\PostgreSQL\\17\\bin <br> PGDATA=C:\\PostgreSQL\\17\\data <br> PGROOT=C:\\Program Files\\PostgreSQL\\17  |\n| Path                 | C:\\Program Files\\PostgreSQL\\17                                                                                         |\n| UserName             | postgres                                                                                                               |\n| Password             | root                                                                                                                   |\n\n#### MongoDB\n| Version  | ServiceName | ServiceStatus | ServiceStartType |\n| -------- | ----------- | ------------- | ---------------- |\n| 7.0.30.0 | MongoDB     | Stopped       | Disabled         |\n\n### Database tools\n- Azure CosmosDb Emulator 2.14.27.0\n- DacFx 170.3.93.6\n- MySQL 8.0.45.0\n- SQL OLEDB Driver 18 18.7.5.0\n- SQL OLEDB Driver 19 19.4.1.0\n- SQLPS 1.0\n- MongoDB Shell (mongosh) 2.7.0\n\n### Web Servers\n| Name   | Version | ConfigFile                            | ServiceName | ServiceStatus | ListenPort |\n| ------ | ------- | ------------------------------------- | ----------- | ------------- | ---------- |\n| Apache | 2.4.55  | C:\\tools\\Apache24\\conf\\httpd.conf     | Apache      | Stopped       | 80         |\n| Nginx  | 1.29.5  | C:\\tools\\nginx-1.29.5\\conf\\nginx.conf | nginx       | Stopped       | 80         |\n\n### Visual Studio Enterprise 2026\n| Name                          | Version       | Path                                                   |\n| ----------------------------- | ------------- | ------------------------------------------------------ |\n| Visual Studio Enterprise 2026 | 18.3.11520.95 | C:\\Program Files\\Microsoft Visual Studio\\18\\Enterprise |\n\n#### Workloads, components and extensions\n| Package                                                                   | Version         |\n| ------------------------------------------------------------------------- | --------------- |\n| android                                                                   | 36.1.2.0        |\n| Component.Android.NDK.R27C                                                | 18.3.11407.131  |\n| Component.Android.SDK.MAUI                                                | 18.3.11407.204  |\n| Component.Linux.CMake                                                     | 18.3.11407.204  |\n| Component.Linux.RemoteFileExplorer                                        | 18.3.11407.204  |\n| Component.MDD.Linux                                                       | 18.3.11407.204  |\n| Component.Microsoft.NET.AppModernization                                  | 18.3.11520.95   |\n| Component.Microsoft.VisualStudio.RazorExtension                           | 18.3.11407.204  |\n| Component.Microsoft.VisualStudio.Tools.Applications.amd64                 | 17.0.36522.0    |\n| Component.Microsoft.VisualStudio.Web.AzureFunctions                       | 18.3.11407.204  |\n| Component.Microsoft.Web.LibraryManager                                    | 18.3.11407.131  |\n| Component.Microsoft.Windows.DriverKit                                     | 10.0.26100.16   |\n| Component.OpenJDK                                                         | 18.3.11407.204  |\n| Component.UnityEngine.x64                                                 | 18.3.11407.204  |\n| Component.Unreal.Debugger                                                 | 18.3.11407.204  |\n| Component.Unreal.Ide                                                      | 18.3.11407.204  |\n| Component.VisualStudio.GitHub.Copilot                                     | 18.3.11407.204  |\n| Component.VSInstallerProjects2022                                         | 3.0.0           |\n| Component.WixToolset.VisualStudioExtension.Dev17                          | 1.0.0.22        |\n| Component.WixToolset.VisualStudioExtension.Schemas3                       | 1.0.0.22        |\n| ComponentGroup.Microsoft.NET.AppModernization                             | 18.3.11407.131  |\n| ios                                                                       | 26.2.10191      |\n| maccatalyst                                                               | 26.2.10191      |\n| maui.blazor                                                               | 10.0.20.7528    |\n| maui.core                                                                 | 10.0.20.7528    |\n| maui.windows                                                              | 10.0.20.7528    |\n| Microsoft.Component.Azure.DataLake.Tools                                  | 18.3.11407.204  |\n| Microsoft.Component.ClickOnce                                             | 18.3.11407.204  |\n| Microsoft.Component.CodeAnalysis.SDK                                      | 18.3.11407.204  |\n| Microsoft.Component.MSBuild                                               | 18.3.11407.204  |\n| Microsoft.Component.NetFX.Native                                          | 18.3.11407.204  |\n| Microsoft.Component.PythonTools                                           | 18.3.11407.204  |\n| Microsoft.Component.PythonTools.Web                                       | 18.3.11407.204  |\n| Microsoft.Component.VC.Runtime.UCRTSDK                                    | 18.3.11407.204  |\n| Microsoft.ComponentGroup.Blend                                            | 18.3.11407.131  |\n| Microsoft.ComponentGroup.ClickOnce.Publish                                | 18.3.11407.131  |\n| Microsoft.Net.Component.4.6.2.TargetingPack                               | 18.3.11407.204  |\n| Microsoft.Net.Component.4.6.TargetingPack                                 | 18.3.11407.204  |\n| Microsoft.Net.Component.4.7.1.TargetingPack                               | 18.3.11407.204  |\n| Microsoft.Net.Component.4.7.2.TargetingPack                               | 18.3.11407.204  |\n| Microsoft.Net.Component.4.7.TargetingPack                                 | 18.3.11407.204  |\n| Microsoft.Net.Component.4.8.1.SDK                                         | 18.3.11407.204  |\n| Microsoft.Net.Component.4.8.1.TargetingPack                               | 18.3.11407.204  |\n| Microsoft.Net.Component.4.8.SDK                                           | 18.3.11407.204  |\n| Microsoft.Net.Component.4.8.TargetingPack                                 | 18.3.11407.204  |\n| Microsoft.Net.ComponentGroup.4.8.DeveloperTools                           | 18.3.11407.131  |\n| Microsoft.Net.ComponentGroup.DevelopmentPrerequisites                     | 18.3.11407.131  |\n| Microsoft.Net.ComponentGroup.TargetingPacks.Common                        | 18.3.11407.131  |\n| microsoft.net.runtime.android                                             | 10.1.326.7603   |\n| microsoft.net.runtime.android.aot                                         | 10.1.326.7603   |\n| microsoft.net.runtime.android.aot.net9                                    | 10.1.326.7603   |\n| microsoft.net.runtime.android.net9                                        | 10.1.326.7603   |\n| microsoft.net.runtime.ios                                                 | 10.1.326.7603   |\n| microsoft.net.runtime.ios.net9                                            | 10.1.326.7603   |\n| microsoft.net.runtime.maccatalyst                                         | 10.1.326.7603   |\n| microsoft.net.runtime.maccatalyst.net9                                    | 10.1.326.7603   |\n| microsoft.net.runtime.mono.tooling                                        | 10.1.326.7603   |\n| microsoft.net.runtime.mono.tooling.net9                                   | 10.1.326.7603   |\n| microsoft.net.sdk.emscripten                                              | 10.1.326.7603   |\n| Microsoft.NetCore.Component.DevelopmentTools                              | 18.3.11407.204  |\n| Microsoft.NetCore.Component.Runtime.10.0                                  | 18.3.11505.172  |\n| Microsoft.NetCore.Component.Runtime.8.0                                   | 18.3.11505.172  |\n| Microsoft.NetCore.Component.SDK                                           | 18.3.11505.172  |\n| Microsoft.NetCore.Component.Web                                           | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.AppInsights.Tools                        | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.AspNet                                   | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.AspNet45                                 | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.ClassDesigner                            | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.CodeMap                                  | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.CoreEditor                               | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.CppBuildInsights                         | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Debugger.JustInTime                      | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.DiagnosticTools                          | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.DockerTools                              | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.DotNetModelBuilder                       | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.DslTools                                 | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.EntityFramework                          | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.FSharp                                   | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.FSharp.Desktop                           | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.FSharp.WebTemplates                      | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.GraphDocument                            | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Graphics                                 | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Graphics.Tools                           | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.HLSL                                     | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.IISExpress                               | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.IntelliCode                              | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.IntelliTrace.FrontEnd                    | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.JavaScript.Diagnostics                   | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.JavaScript.TypeScript                    | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.LinqToSql                                | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.LiveUnitTesting                          | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.ManagedDesktop.Core                      | 18.3.11407.131  |\n| Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites             | 18.3.11407.131  |\n| Microsoft.VisualStudio.Component.MSODBC.SQL                               | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.MSSQL.CMDLnUtils                         | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Node.Tools                               | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.NuGet                                    | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.NuGet.BuildTools                         | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.PortableLibrary                          | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Roslyn.Compiler                          | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Roslyn.LanguageServices                  | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Sharepoint.Tools                         | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.SQL.CLR                                  | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.SQL.DataSources                          | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime                      | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.SQL.SSDT                                 | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.TeamOffice                               | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.TextTemplating                           | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.TypeScript.TSServer                      | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Unity                                    | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.UWP.VC.ARM64                             | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.UWP.VC.ARM64EC                           | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.14.29.16.11.ARM                       | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.14.29.16.11.ARM64                     | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64                   | 18.3.11415.102  |\n| Microsoft.VisualStudio.Component.VC.ASAN                                  | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.ATL                                   | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.ATL.ARM64                             | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre                     | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.ATL.Spectre                           | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.ATLMFC                                | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre                        | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.CLI.Support                           | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.CMake.Project                         | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.CoreIde                               | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.DiagnosticTools                       | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.Llvm.Clang                            | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset                     | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.MFC.ARM64                             | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre                     | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.Redist.14.Latest                      | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.Redist.MSM                            | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre                | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.Runtimes.ARM64EC.Spectre              | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre              | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest               | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest              | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.Tools.ARM64                           | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.Tools.ARM64EC                         | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VC.Tools.x86.x64                         | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Vcpkg                                    | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.VSSDK                                    | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Wcf.Tooling                              | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Web                                      | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.WebDeploy                                | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Windows10SDK                             | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Windows11SDK.26100                       | 18.3.11505.172  |\n| Microsoft.VisualStudio.Component.Windows11Sdk.WindowsPerformanceToolkit   | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.WindowsAppSdkSupport.CSharp              | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.Workflow                                 | 18.3.11407.204  |\n| Microsoft.VisualStudio.Component.WslDebugging                             | 18.3.11407.204  |\n| Microsoft.VisualStudio.ComponentGroup.ArchitectureTools.Native            | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.Azure.Prerequisites                 | 18.3.11407.204  |\n| Microsoft.VisualStudio.ComponentGroup.AzureFunctions                      | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.All                            | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Android                        | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Blazor                         | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.iOS                            | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.MacCatalyst                    | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Shared                         | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.Maui.Windows                        | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging                      | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core                  | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang            | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard              | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142                         | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.VC.Tools.142.x86.x64                | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.VisualStudioExtension.Prerequisites | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.Web                                 | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.Web.CloudTools                      | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions                  | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.CMake            | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.TemplateEngine   | 18.3.11407.131  |\n| Microsoft.VisualStudio.ComponentGroup.WindowsAppDevelopment.Prerequisites | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.Azure                                     | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.CoreEditor                                | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.Data                                      | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.DataScience                               | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.ManagedDesktop                            | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.ManagedGame                               | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.NativeCrossPlat                           | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.NativeDesktop                             | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.NativeGame                                | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.NativeMobile                              | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.NetCrossPlat                              | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.NetWeb                                    | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.Node                                      | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.Office                                    | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.Python                                    | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.Universal                                 | 18.3.11407.131  |\n| Microsoft.VisualStudio.Workload.VisualStudioExtension                     | 18.3.11407.131  |\n| runtimes.ios                                                              | 10.1.326.7603   |\n| runtimes.ios.net9                                                         | 10.1.326.7603   |\n| runtimes.maccatalyst                                                      | 10.1.326.7603   |\n| runtimes.maccatalyst.net9                                                 | 10.1.326.7603   |\n| wasm.tools                                                                | 10.1.326.7603   |\n| ProBITools.MicrosoftAnalysisServicesModelingProjects2022                  | 4.0.0           |\n| ProBITools.MicrosoftReportProjectsforVisualStudio2022                     | 4.0.0           |\n| SSIS.MicrosoftDataToolsIntegrationServices                                | 2.1.2           |\n| VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects             | 3.0.0           |\n| Windows Driver Kit Visual Studio Extension                                | 10.0.26100.16   |\n| Windows Software Development Kit                                          | 10.1.26100.7705 |\n| WixToolset.WixToolsetVisualStudio2022Extension                            | 1.0.0.22        |\n\n#### Microsoft Visual C++\n| Name                                         | Architecture | Version     |\n| -------------------------------------------- | ------------ | ----------- |\n| Microsoft Visual C++ 2013 Additional Runtime | x64          | 12.0.40660  |\n| Microsoft Visual C++ 2013 Minimum Runtime    | x64          | 12.0.40660  |\n| Microsoft Visual C++ 2022 Additional Runtime | x64          | 14.50.35719 |\n| Microsoft Visual C++ 2022 Debug Runtime      | x64          | 14.50.35719 |\n| Microsoft Visual C++ 2022 Minimum Runtime    | x64          | 14.50.35719 |\n| Microsoft Visual C++ 2022 Additional Runtime | x86          | 14.50.35719 |\n| Microsoft Visual C++ 2022 Debug Runtime      | x86          | 14.50.35719 |\n| Microsoft Visual C++ 2022 Minimum Runtime    | x86          | 14.50.35719 |\n\n#### Installed Windows SDKs\n- 10.0.26100.0\n\n### .NET Core Tools\n- .NET Core SDK: 8.0.124, 8.0.206, 8.0.319, 8.0.418, 9.0.114, 9.0.205, 9.0.311, 10.0.102, 10.0.103\n- .NET Framework: 4.8, 4.8.1\n- Microsoft.AspNetCore.App: 8.0.6, 8.0.22, 8.0.24, 9.0.6, 9.0.13, 10.0.2, 10.0.3\n- Microsoft.NETCore.App: 8.0.6, 8.0.22, 8.0.24, 9.0.6, 9.0.13, 10.0.2, 10.0.3\n- Microsoft.WindowsDesktop.App: 8.0.6, 8.0.22, 8.0.24, 9.0.6, 9.0.13, 10.0.2, 10.0.3\n- nbgv 3.9.50+6feeb89450\n\n### PowerShell Tools\n- PowerShell 7.4.13\n\n#### Powershell Modules\n- Az: 14.6.0\n- AWSPowershell: 5.0.165\n- DockerMsftProvider: 1.0.0.8\n- MarkdownPS: 1.10\n- Microsoft.Graph: 2.35.1\n- Pester: 3.4.0, 5.7.1\n- PowerShellGet: 1.0.0.1, 2.2.5\n- PSScriptAnalyzer: 1.24.0\n- PSWindowsUpdate: 2.2.1.5\n- SqlServer: 22.3.0\n- VSSetup: 2.2.16\n\n### Android\n| Package Name               | Version                                                                                                                                                                                                                                                                                                               |\n| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Android Command Line Tools | 19.0                                                                                                                                                                                                                                                                                                                  |\n| Android Emulator           | 36.4.9                                                                                                                                                                                                                                                                                                                |\n| Android SDK Build-tools    | 36.0.0 36.1.0<br>35.0.0 35.0.1<br>34.0.0                                                                                                                                                                                                                                                                              |\n| Android SDK Platforms      | android-36.1 (rev 1)<br>android-36-ext19 (rev 1)<br>android-36-ext18 (rev 1)<br>android-36 (rev 2)<br>android-35-ext15 (rev 1)<br>android-35-ext14 (rev 1)<br>android-35 (rev 2)<br>android-34-ext8 (rev 1)<br>android-34-ext12 (rev 1)<br>android-34-ext11 (rev 1)<br>android-34-ext10 (rev 1)<br>android-34 (rev 3) |\n| Android SDK Platform-Tools | 36.0.2                                                                                                                                                                                                                                                                                                                |\n| Android Support Repository | 47.0.0                                                                                                                                                                                                                                                                                                                |\n| CMake                      | 3.30.5<br>3.31.5<br>4.1.2                                                                                                                                                                                                                                                                                             |\n| Google Play services       | 49                                                                                                                                                                                                                                                                                                                    |\n| Google Repository          | 58                                                                                                                                                                                                                                                                                                                    |\n| NDK                        | 27.3.13750724<br>28.2.13676358<br>29.0.14206865                                                                                                                                                                                                                                                                       |\n\n#### Environment variables\n| Name                    | Value                                    |\n| ----------------------- | ---------------------------------------- |\n| ANDROID_HOME            | C:\\Android\\android-sdk                   |\n| ANDROID_NDK             | C:\\Android\\android-sdk\\ndk\\27.3.13750724 |\n| ANDROID_NDK_HOME        | C:\\Android\\android-sdk\\ndk\\27.3.13750724 |\n| ANDROID_NDK_LATEST_HOME | C:\\Android\\android-sdk\\ndk\\29.0.14206865 |\n| ANDROID_NDK_ROOT        | C:\\Android\\android-sdk\\ndk\\27.3.13750724 |\n| ANDROID_SDK_ROOT        | C:\\Android\\android-sdk                   |\n\n"
  },
  {
    "path": "images/windows/assets/post-gen/GenerateIISExpressCertificate.ps1",
    "content": "$friendlyName = \"IIS Express Development Certificate\"\n$certStore = \"Cert:\\LocalMachine\\My\"\n$oldCert = Get-ChildItem $certStore | Where-Object FriendlyName -match $friendlyName\n\nif(-not $oldCert) {\n    Write-Host \"$friendlyName certificate not found\"\n    return\n}\n\nWrite-Host \"Removing $($oldCert.Thumbprint) certificate\"\nRemove-Item -Path $oldCert.PSPath -Confirm:$false\n\nWrite-Host \"Creating $friendlyName certificate\"\n$selfSignedCertParam = @{\n    Subject = \"localhost\"\n    DnsName = \"localhost\"\n    KeyAlgorithm = \"RSA\"\n    KeyLength = 2048\n    NotBefore = (Get-Date)\n    NotAfter = (Get-Date).AddYears(5)\n    CertStoreLocation = $certStore\n    FriendlyName = $friendlyName\n    HashAlgorithm = \"SHA256\"\n    KeyUsage = \"DigitalSignature\", \"KeyEncipherment\", \"DataEncipherment\"\n    TextExtension = @(\"2.5.29.37={text}1.3.6.1.5.5.7.3.1\")\n}\n$cert = New-SelfSignedCertificate @selfSignedCertParam\n\n# The app ID is the IIS Express app ID\n$certThumbprint = $cert.Thumbprint\n$appId = \"{214124cd-d05b-4309-9af9-9caa44b2b74a}\"\n$startPort = 44300\n$endPort = 44399\n\nWrite-Host \"Binding ${certThumbprint} certificate using netsh port=${startPort}:${endPort} and appID=${appId}\"\n$startPort..$endPort | ForEach-Object {\n    $port = $_\n    cmd /c \"netsh http delete sslcert ipport=0.0.0.0:$port > nul 2>&1\"\n    cmd /c \"netsh http add sslcert ipport=0.0.0.0:$port certhash=$certThumbprint appid=$appId certstorename=MY > nul 2>&1\"\n}\n"
  },
  {
    "path": "images/windows/assets/post-gen/InternetExplorerConfiguration.ps1",
    "content": "# https://docs.microsoft.com/en-us/troubleshoot/browsers/enhanced-security-configuration-faq#how-to-disable-internet-explorer-esc-by-using-a-script\n# turn off the Internet Explorer Enhanced Security feature\nRundll32 iesetup.dll, IEHardenLMSettings, 1, True\nRundll32 iesetup.dll, IEHardenUser, 1, True\nRundll32 iesetup.dll, IEHardenAdmin, 1, True\n$AdminKey = \"HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}\"\n$UserKey = \"HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}\"\nSet-ItemProperty -Path $AdminKey -Name \"IsInstalled\" -Value 0 -Force\nSet-ItemProperty -Path $UserKey -Name \"IsInstalled\" -Value 0 -Force\n# restart Explorer process\n$ieProcess = Get-Process -Name Explorer -ErrorAction Ignore\nif ($ieProcess) {\n    Stop-Process -Name Explorer -Force -ErrorAction Ignore\n}\nWrite-Host \"IE Enhanced Security Configuration (ESC) has been disabled.\"\n"
  },
  {
    "path": "images/windows/assets/post-gen/Msys2FirstLaunch.ps1",
    "content": "# create user profile at the first launch\n$null = cmd /c \"C:\\msys64\\usr\\bin\\bash.exe -leo pipefail -c 'echo $SHELL' 2>&1\""
  },
  {
    "path": "images/windows/assets/post-gen/VSConfiguration.ps1",
    "content": "$vsInstallRoot = (Get-VisualStudioInstance).InstallationPath\n$devEnvPath = \"$vsInstallRoot\\Common7\\IDE\\devenv.exe\"\n\n# Initialize Visual Studio Experimental Instance\n# The Out-Null cmdlet is required to ensure PowerShell waits until the '/ResetSettings' command fully completes.\n& \"$devEnvPath\" /RootSuffix Exp /ResetSettings General.vssettings /Command File.Exit | Out-Null\n\ncmd.exe /c \"`\"$devEnvPath`\" /updateconfiguration\"\n\n#\n# https://github.com/actions/runner-images/issues/5301\n#\n$warmup_vdproj = $(Join-Path $PSScriptRoot 'warmup.vdproj')\n& \"$devEnvPath\" $warmup_vdproj /build Release | Out-Null\n"
  },
  {
    "path": "images/windows/assets/post-gen/warmup.vdproj",
    "content": "﻿\"DeployProject\"\n{\n\"VSVersion\" = \"3:800\"\n\"ProjectType\" = \"8:{978C614F-708E-4E1A-B201-565925725DBA}\"\n\"IsWebType\" = \"8:FALSE\"\n\"ProjectName\" = \"8:Setup1\"\n\"LanguageId\" = \"3:1033\"\n\"CodePage\" = \"3:1252\"\n\"UILanguageId\" = \"3:1033\"\n\"SccProjectName\" = \"8:\"\n\"SccLocalPath\" = \"8:\"\n\"SccAuxPath\" = \"8:\"\n\"SccProvider\" = \"8:\"\n    \"Hierarchy\"\n    {\n    }\n    \"Configurations\"\n    {\n        \"Debug\"\n        {\n        \"DisplayName\" = \"8:Debug\"\n        \"IsDebugOnly\" = \"11:TRUE\"\n        \"IsReleaseOnly\" = \"11:FALSE\"\n        \"OutputFilename\" = \"8:Debug\\\\Setup1.msi\"\n        \"PackageFilesAs\" = \"3:2\"\n        \"PackageFileSize\" = \"3:-2147483648\"\n        \"CabType\" = \"3:1\"\n        \"Compression\" = \"3:2\"\n        \"SignOutput\" = \"11:FALSE\"\n        \"CertificateFile\" = \"8:\"\n        \"PrivateKeyFile\" = \"8:\"\n        \"TimeStampServer\" = \"8:\"\n        \"InstallerBootstrapper\" = \"3:2\"\n            \"BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}\"\n            {\n            \"Enabled\" = \"11:FALSE\"\n            \"PromptEnabled\" = \"11:TRUE\"\n            \"PrerequisitesLocation\" = \"2:1\"\n            \"Url\" = \"8:\"\n            \"ComponentsUrl\" = \"8:\"\n                \"Items\"\n                {\n                    \"{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2\"\n                    {\n                    \"Name\" = \"8:Microsoft .NET Framework 4.7.2 (x86 and x64)\"\n                    \"ProductCode\" = \"8:.NETFramework,Version=v4.7.2\"\n                    }\n                }\n            }\n        }\n        \"Release\"\n        {\n        \"DisplayName\" = \"8:Release\"\n        \"IsDebugOnly\" = \"11:FALSE\"\n        \"IsReleaseOnly\" = \"11:TRUE\"\n        \"OutputFilename\" = \"8:Release\\\\Setup1.msi\"\n        \"PackageFilesAs\" = \"3:2\"\n        \"PackageFileSize\" = \"3:-2147483648\"\n        \"CabType\" = \"3:1\"\n        \"Compression\" = \"3:2\"\n        \"SignOutput\" = \"11:FALSE\"\n        \"CertificateFile\" = \"8:\"\n        \"PrivateKeyFile\" = \"8:\"\n        \"TimeStampServer\" = \"8:\"\n        \"InstallerBootstrapper\" = \"3:2\"\n        }\n    }\n    \"Deployable\"\n    {\n        \"CustomAction\"\n        {\n        }\n        \"DefaultFeature\"\n        {\n        \"Name\" = \"8:DefaultFeature\"\n        \"Title\" = \"8:\"\n        \"Description\" = \"8:\"\n        }\n        \"ExternalPersistence\"\n        {\n            \"LaunchCondition\"\n            {\n            }\n        }\n        \"File\"\n        {\n        }\n        \"FileType\"\n        {\n        }\n        \"Folder\"\n        {\n            \"{1525181F-901A-416C-8A58-119130FE478E}:_2CFC2A48A3B644ED96FB521F59C388D4\"\n            {\n            \"Name\" = \"8:#1916\"\n            \"AlwaysCreate\" = \"11:FALSE\"\n            \"Condition\" = \"8:\"\n            \"Transitive\" = \"11:FALSE\"\n            \"Property\" = \"8:DesktopFolder\"\n                \"Folders\"\n                {\n                }\n            }\n            \"{3C67513D-01DD-4637-8A68-80971EB9504F}:_3389E1C43BBB433484C8A1DBE5968F24\"\n            {\n            \"DefaultLocation\" = \"8:[ProgramFilesFolder][Manufacturer]\\\\[ProductName]\"\n            \"Name\" = \"8:#1925\"\n            \"AlwaysCreate\" = \"11:FALSE\"\n            \"Condition\" = \"8:\"\n            \"Transitive\" = \"11:FALSE\"\n            \"Property\" = \"8:TARGETDIR\"\n                \"Folders\"\n                {\n                }\n            }\n            \"{1525181F-901A-416C-8A58-119130FE478E}:_FA2F90B46134404A830C21AAA6741088\"\n            {\n            \"Name\" = \"8:#1919\"\n            \"AlwaysCreate\" = \"11:FALSE\"\n            \"Condition\" = \"8:\"\n            \"Transitive\" = \"11:FALSE\"\n            \"Property\" = \"8:ProgramMenuFolder\"\n                \"Folders\"\n                {\n                }\n            }\n        }\n        \"LaunchCondition\"\n        {\n        }\n        \"Locator\"\n        {\n        }\n        \"MsiBootstrapper\"\n        {\n        \"LangId\" = \"3:1033\"\n        \"RequiresElevation\" = \"11:FALSE\"\n        }\n        \"Product\"\n        {\n        \"Name\" = \"8:Microsoft Visual Studio\"\n        \"ProductName\" = \"8:Setup1\"\n        \"ProductCode\" = \"8:{22F710BC-F7AA-4E2C-86A3-63647F82EF0D}\"\n        \"PackageCode\" = \"8:{3ADCCB4E-232A-40DF-A810-672AA9AD4B72}\"\n        \"UpgradeCode\" = \"8:{19174734-DD62-4C8E-9716-6DCFC3F06116}\"\n        \"AspNetVersion\" = \"8:4.0.30319.0\"\n        \"RestartWWWService\" = \"11:FALSE\"\n        \"RemovePreviousVersions\" = \"11:FALSE\"\n        \"DetectNewerInstalledVersion\" = \"11:TRUE\"\n        \"InstallAllUsers\" = \"11:FALSE\"\n        \"ProductVersion\" = \"8:1.0.0\"\n        \"Manufacturer\" = \"8:Default Company Name\"\n        \"ARPHELPTELEPHONE\" = \"8:\"\n        \"ARPHELPLINK\" = \"8:\"\n        \"Title\" = \"8:Setup1\"\n        \"Subject\" = \"8:\"\n        \"ARPCONTACT\" = \"8:Default Company Name\"\n        \"Keywords\" = \"8:\"\n        \"ARPCOMMENTS\" = \"8:\"\n        \"ARPURLINFOABOUT\" = \"8:\"\n        \"ARPPRODUCTICON\" = \"8:\"\n        \"ARPIconIndex\" = \"3:0\"\n        \"SearchPath\" = \"8:\"\n        \"UseSystemSearchPath\" = \"11:TRUE\"\n        \"TargetPlatform\" = \"3:0\"\n        \"PreBuildEvent\" = \"8:\"\n        \"PostBuildEvent\" = \"8:\"\n        \"RunPostBuildEvent\" = \"3:0\"\n        }\n        \"Registry\"\n        {\n            \"HKLM\"\n            {\n                \"Keys\"\n                {\n                    \"{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_AE076FF564A049948DF2258C70AE5799\"\n                    {\n                    \"Name\" = \"8:Software\"\n                    \"Condition\" = \"8:\"\n                    \"AlwaysCreate\" = \"11:FALSE\"\n                    \"DeleteAtUninstall\" = \"11:FALSE\"\n                    \"Transitive\" = \"11:FALSE\"\n                        \"Keys\"\n                        {\n                            \"{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_47FD8B4AFAAC41A48FAC411F8ABDEB59\"\n                            {\n                            \"Name\" = \"8:[Manufacturer]\"\n                            \"Condition\" = \"8:\"\n                            \"AlwaysCreate\" = \"11:FALSE\"\n                            \"DeleteAtUninstall\" = \"11:FALSE\"\n                            \"Transitive\" = \"11:FALSE\"\n                                \"Keys\"\n                                {\n                                }\n                                \"Values\"\n                                {\n                                }\n                            }\n                        }\n                        \"Values\"\n                        {\n                        }\n                    }\n                }\n            }\n            \"HKCU\"\n            {\n                \"Keys\"\n                {\n                    \"{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_98C374DEA4C841988657D6A8CC3F6CC3\"\n                    {\n                    \"Name\" = \"8:Software\"\n                    \"Condition\" = \"8:\"\n                    \"AlwaysCreate\" = \"11:FALSE\"\n                    \"DeleteAtUninstall\" = \"11:FALSE\"\n                    \"Transitive\" = \"11:FALSE\"\n                        \"Keys\"\n                        {\n                            \"{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_A31118797AC14549912315624636D7C5\"\n                            {\n                            \"Name\" = \"8:[Manufacturer]\"\n                            \"Condition\" = \"8:\"\n                            \"AlwaysCreate\" = \"11:FALSE\"\n                            \"DeleteAtUninstall\" = \"11:FALSE\"\n                            \"Transitive\" = \"11:FALSE\"\n                                \"Keys\"\n                                {\n                                }\n                                \"Values\"\n                                {\n                                }\n                            }\n                        }\n                        \"Values\"\n                        {\n                        }\n                    }\n                }\n            }\n            \"HKCR\"\n            {\n                \"Keys\"\n                {\n                }\n            }\n            \"HKU\"\n            {\n                \"Keys\"\n                {\n                }\n            }\n            \"HKPU\"\n            {\n                \"Keys\"\n                {\n                }\n            }\n        }\n        \"Sequences\"\n        {\n        }\n        \"Shortcut\"\n        {\n        }\n        \"UserInterface\"\n        {\n            \"{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_0F20B65AC09E4AA78D95E5A7A7B152ED\"\n            {\n            \"UseDynamicProperties\" = \"11:FALSE\"\n            \"IsDependency\" = \"11:FALSE\"\n            \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdUserInterface.wim\"\n            }\n            \"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_1450B51044A346A59AD67962D438B359\"\n            {\n            \"Name\" = \"8:#1901\"\n            \"Sequence\" = \"3:1\"\n            \"Attributes\" = \"3:2\"\n                \"Dialogs\"\n                {\n                    \"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_056EB6D1A4954D7D8D59FCF53B8AF02E\"\n                    {\n                    \"Sequence\" = \"3:100\"\n                    \"DisplayName\" = \"8:Progress\"\n                    \"UseDynamicProperties\" = \"11:TRUE\"\n                    \"IsDependency\" = \"11:FALSE\"\n                    \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdProgressDlg.wid\"\n                        \"Properties\"\n                        {\n                            \"BannerBitmap\"\n                            {\n                            \"Name\" = \"8:BannerBitmap\"\n                            \"DisplayName\" = \"8:#1001\"\n                            \"Description\" = \"8:#1101\"\n                            \"Type\" = \"3:8\"\n                            \"ContextData\" = \"8:Bitmap\"\n                            \"Attributes\" = \"3:4\"\n                            \"Setting\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                            \"ShowProgress\"\n                            {\n                            \"Name\" = \"8:ShowProgress\"\n                            \"DisplayName\" = \"8:#1009\"\n                            \"Description\" = \"8:#1109\"\n                            \"Type\" = \"3:5\"\n                            \"ContextData\" = \"8:1;True=1;False=0\"\n                            \"Attributes\" = \"3:0\"\n                            \"Setting\" = \"3:0\"\n                            \"Value\" = \"3:1\"\n                            \"DefaultValue\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                        }\n                    }\n                }\n            }\n            \"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_3F72D8AE479B4820864530F5C9607524\"\n            {\n            \"Name\" = \"8:#1900\"\n            \"Sequence\" = \"3:1\"\n            \"Attributes\" = \"3:1\"\n                \"Dialogs\"\n                {\n                    \"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_7DE3964E30704BA99E5B564A29CC3A74\"\n                    {\n                    \"Sequence\" = \"3:300\"\n                    \"DisplayName\" = \"8:Confirm Installation\"\n                    \"UseDynamicProperties\" = \"11:TRUE\"\n                    \"IsDependency\" = \"11:FALSE\"\n                    \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdConfirmDlg.wid\"\n                        \"Properties\"\n                        {\n                            \"BannerBitmap\"\n                            {\n                            \"Name\" = \"8:BannerBitmap\"\n                            \"DisplayName\" = \"8:#1001\"\n                            \"Description\" = \"8:#1101\"\n                            \"Type\" = \"3:8\"\n                            \"ContextData\" = \"8:Bitmap\"\n                            \"Attributes\" = \"3:4\"\n                            \"Setting\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                        }\n                    }\n                    \"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_B3DAD8BF301A400680846A2BB7451D0D\"\n                    {\n                    \"Sequence\" = \"3:200\"\n                    \"DisplayName\" = \"8:Installation Folder\"\n                    \"UseDynamicProperties\" = \"11:TRUE\"\n                    \"IsDependency\" = \"11:FALSE\"\n                    \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdFolderDlg.wid\"\n                        \"Properties\"\n                        {\n                            \"BannerBitmap\"\n                            {\n                            \"Name\" = \"8:BannerBitmap\"\n                            \"DisplayName\" = \"8:#1001\"\n                            \"Description\" = \"8:#1101\"\n                            \"Type\" = \"3:8\"\n                            \"ContextData\" = \"8:Bitmap\"\n                            \"Attributes\" = \"3:4\"\n                            \"Setting\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                            \"InstallAllUsersVisible\"\n                            {\n                            \"Name\" = \"8:InstallAllUsersVisible\"\n                            \"DisplayName\" = \"8:#1059\"\n                            \"Description\" = \"8:#1159\"\n                            \"Type\" = \"3:5\"\n                            \"ContextData\" = \"8:1;True=1;False=0\"\n                            \"Attributes\" = \"3:0\"\n                            \"Setting\" = \"3:0\"\n                            \"Value\" = \"3:1\"\n                            \"DefaultValue\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                        }\n                    }\n                    \"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_D2FF63E39B7649E6BB76320118D41624\"\n                    {\n                    \"Sequence\" = \"3:100\"\n                    \"DisplayName\" = \"8:Welcome\"\n                    \"UseDynamicProperties\" = \"11:TRUE\"\n                    \"IsDependency\" = \"11:FALSE\"\n                    \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdWelcomeDlg.wid\"\n                        \"Properties\"\n                        {\n                            \"BannerBitmap\"\n                            {\n                            \"Name\" = \"8:BannerBitmap\"\n                            \"DisplayName\" = \"8:#1001\"\n                            \"Description\" = \"8:#1101\"\n                            \"Type\" = \"3:8\"\n                            \"ContextData\" = \"8:Bitmap\"\n                            \"Attributes\" = \"3:4\"\n                            \"Setting\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                            \"CopyrightWarning\"\n                            {\n                            \"Name\" = \"8:CopyrightWarning\"\n                            \"DisplayName\" = \"8:#1002\"\n                            \"Description\" = \"8:#1102\"\n                            \"Type\" = \"3:3\"\n                            \"ContextData\" = \"8:\"\n                            \"Attributes\" = \"3:0\"\n                            \"Setting\" = \"3:1\"\n                            \"Value\" = \"8:#1202\"\n                            \"DefaultValue\" = \"8:#1202\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                            \"Welcome\"\n                            {\n                            \"Name\" = \"8:Welcome\"\n                            \"DisplayName\" = \"8:#1003\"\n                            \"Description\" = \"8:#1103\"\n                            \"Type\" = \"3:3\"\n                            \"ContextData\" = \"8:\"\n                            \"Attributes\" = \"3:0\"\n                            \"Setting\" = \"3:1\"\n                            \"Value\" = \"8:#1203\"\n                            \"DefaultValue\" = \"8:#1203\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                        }\n                    }\n                }\n            }\n            \"{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_6069164ECEB944A3AF499EF7E4AF562E\"\n            {\n            \"UseDynamicProperties\" = \"11:FALSE\"\n            \"IsDependency\" = \"11:FALSE\"\n            \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdBasicDialogs.wim\"\n            }\n            \"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_6E590DA0E3F44E7EB06E9B583A05C84F\"\n            {\n            \"Name\" = \"8:#1900\"\n            \"Sequence\" = \"3:2\"\n            \"Attributes\" = \"3:1\"\n                \"Dialogs\"\n                {\n                    \"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_38649E4FCD994315918CF9F68EEDFBD4\"\n                    {\n                    \"Sequence\" = \"3:200\"\n                    \"DisplayName\" = \"8:Installation Folder\"\n                    \"UseDynamicProperties\" = \"11:TRUE\"\n                    \"IsDependency\" = \"11:FALSE\"\n                    \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdAdminFolderDlg.wid\"\n                        \"Properties\"\n                        {\n                            \"BannerBitmap\"\n                            {\n                            \"Name\" = \"8:BannerBitmap\"\n                            \"DisplayName\" = \"8:#1001\"\n                            \"Description\" = \"8:#1101\"\n                            \"Type\" = \"3:8\"\n                            \"ContextData\" = \"8:Bitmap\"\n                            \"Attributes\" = \"3:4\"\n                            \"Setting\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                        }\n                    }\n                    \"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_883E4215F7404FD2B27299E3D09CA1EE\"\n                    {\n                    \"Sequence\" = \"3:300\"\n                    \"DisplayName\" = \"8:Confirm Installation\"\n                    \"UseDynamicProperties\" = \"11:TRUE\"\n                    \"IsDependency\" = \"11:FALSE\"\n                    \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdAdminConfirmDlg.wid\"\n                        \"Properties\"\n                        {\n                            \"BannerBitmap\"\n                            {\n                            \"Name\" = \"8:BannerBitmap\"\n                            \"DisplayName\" = \"8:#1001\"\n                            \"Description\" = \"8:#1101\"\n                            \"Type\" = \"3:8\"\n                            \"ContextData\" = \"8:Bitmap\"\n                            \"Attributes\" = \"3:4\"\n                            \"Setting\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                        }\n                    }\n                    \"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_99855627211D468E907FCF6B662C325C\"\n                    {\n                    \"Sequence\" = \"3:100\"\n                    \"DisplayName\" = \"8:Welcome\"\n                    \"UseDynamicProperties\" = \"11:TRUE\"\n                    \"IsDependency\" = \"11:FALSE\"\n                    \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdAdminWelcomeDlg.wid\"\n                        \"Properties\"\n                        {\n                            \"BannerBitmap\"\n                            {\n                            \"Name\" = \"8:BannerBitmap\"\n                            \"DisplayName\" = \"8:#1001\"\n                            \"Description\" = \"8:#1101\"\n                            \"Type\" = \"3:8\"\n                            \"ContextData\" = \"8:Bitmap\"\n                            \"Attributes\" = \"3:4\"\n                            \"Setting\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                            \"CopyrightWarning\"\n                            {\n                            \"Name\" = \"8:CopyrightWarning\"\n                            \"DisplayName\" = \"8:#1002\"\n                            \"Description\" = \"8:#1102\"\n                            \"Type\" = \"3:3\"\n                            \"ContextData\" = \"8:\"\n                            \"Attributes\" = \"3:0\"\n                            \"Setting\" = \"3:1\"\n                            \"Value\" = \"8:#1202\"\n                            \"DefaultValue\" = \"8:#1202\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                            \"Welcome\"\n                            {\n                            \"Name\" = \"8:Welcome\"\n                            \"DisplayName\" = \"8:#1003\"\n                            \"Description\" = \"8:#1103\"\n                            \"Type\" = \"3:3\"\n                            \"ContextData\" = \"8:\"\n                            \"Attributes\" = \"3:0\"\n                            \"Setting\" = \"3:1\"\n                            \"Value\" = \"8:#1203\"\n                            \"DefaultValue\" = \"8:#1203\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                        }\n                    }\n                }\n            }\n            \"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_90155C3C794546069868B80B82B57EE9\"\n            {\n            \"Name\" = \"8:#1902\"\n            \"Sequence\" = \"3:1\"\n            \"Attributes\" = \"3:3\"\n                \"Dialogs\"\n                {\n                    \"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_4490713900D94D3B9375F45EE3575B13\"\n                    {\n                    \"Sequence\" = \"3:100\"\n                    \"DisplayName\" = \"8:Finished\"\n                    \"UseDynamicProperties\" = \"11:TRUE\"\n                    \"IsDependency\" = \"11:FALSE\"\n                    \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdFinishedDlg.wid\"\n                        \"Properties\"\n                        {\n                            \"BannerBitmap\"\n                            {\n                            \"Name\" = \"8:BannerBitmap\"\n                            \"DisplayName\" = \"8:#1001\"\n                            \"Description\" = \"8:#1101\"\n                            \"Type\" = \"3:8\"\n                            \"ContextData\" = \"8:Bitmap\"\n                            \"Attributes\" = \"3:4\"\n                            \"Setting\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                            \"UpdateText\"\n                            {\n                            \"Name\" = \"8:UpdateText\"\n                            \"DisplayName\" = \"8:#1058\"\n                            \"Description\" = \"8:#1158\"\n                            \"Type\" = \"3:15\"\n                            \"ContextData\" = \"8:\"\n                            \"Attributes\" = \"3:0\"\n                            \"Setting\" = \"3:1\"\n                            \"Value\" = \"8:#1258\"\n                            \"DefaultValue\" = \"8:#1258\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                        }\n                    }\n                }\n            }\n            \"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_A7B6301BE8B0403C959609D5053EDF6E\"\n            {\n            \"Name\" = \"8:#1902\"\n            \"Sequence\" = \"3:2\"\n            \"Attributes\" = \"3:3\"\n                \"Dialogs\"\n                {\n                    \"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_D47DBC39DB33492AA6C089D6D13580FB\"\n                    {\n                    \"Sequence\" = \"3:100\"\n                    \"DisplayName\" = \"8:Finished\"\n                    \"UseDynamicProperties\" = \"11:TRUE\"\n                    \"IsDependency\" = \"11:FALSE\"\n                    \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdAdminFinishedDlg.wid\"\n                        \"Properties\"\n                        {\n                            \"BannerBitmap\"\n                            {\n                            \"Name\" = \"8:BannerBitmap\"\n                            \"DisplayName\" = \"8:#1001\"\n                            \"Description\" = \"8:#1101\"\n                            \"Type\" = \"3:8\"\n                            \"ContextData\" = \"8:Bitmap\"\n                            \"Attributes\" = \"3:4\"\n                            \"Setting\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                        }\n                    }\n                }\n            }\n            \"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_BA6A3FEC255C49059A6576E2F533202F\"\n            {\n            \"Name\" = \"8:#1901\"\n            \"Sequence\" = \"3:2\"\n            \"Attributes\" = \"3:2\"\n                \"Dialogs\"\n                {\n                    \"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_A46B26FE90274EBB937E90E163295D0B\"\n                    {\n                    \"Sequence\" = \"3:100\"\n                    \"DisplayName\" = \"8:Progress\"\n                    \"UseDynamicProperties\" = \"11:TRUE\"\n                    \"IsDependency\" = \"11:FALSE\"\n                    \"SourcePath\" = \"8:<VsdDialogDir>\\\\VsdAdminProgressDlg.wid\"\n                        \"Properties\"\n                        {\n                            \"BannerBitmap\"\n                            {\n                            \"Name\" = \"8:BannerBitmap\"\n                            \"DisplayName\" = \"8:#1001\"\n                            \"Description\" = \"8:#1101\"\n                            \"Type\" = \"3:8\"\n                            \"ContextData\" = \"8:Bitmap\"\n                            \"Attributes\" = \"3:4\"\n                            \"Setting\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                            \"ShowProgress\"\n                            {\n                            \"Name\" = \"8:ShowProgress\"\n                            \"DisplayName\" = \"8:#1009\"\n                            \"Description\" = \"8:#1109\"\n                            \"Type\" = \"3:5\"\n                            \"ContextData\" = \"8:1;True=1;False=0\"\n                            \"Attributes\" = \"3:0\"\n                            \"Setting\" = \"3:0\"\n                            \"Value\" = \"3:1\"\n                            \"DefaultValue\" = \"3:1\"\n                            \"UsePlugInResources\" = \"11:TRUE\"\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        \"MergeModule\"\n        {\n        }\n        \"ProjectOutput\"\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-BaseImage.ps1",
    "content": "################################################################################\n##  File:  Configure-BaseImage.ps1\n##  Desc:  Prepare the base image for software installation\n################################################################################\n\nfunction Disable-InternetExplorerESC {\n    $adminKey = \"HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}\"\n    $userKey = \"HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}\"\n    Set-ItemProperty -Path $adminKey -Name \"IsInstalled\" -Value 0 -Force\n    Set-ItemProperty -Path $userKey -Name \"IsInstalled\" -Value 0 -Force\n\n    $ieProcess = Get-Process -Name Explorer -ErrorAction SilentlyContinue\n\n    if ($ieProcess) {\n        Stop-Process -Name Explorer -Force -ErrorAction Continue\n    }\n\n    Write-Host \"IE Enhanced Security Configuration (ESC) has been disabled.\"\n}\n\nfunction Disable-InternetExplorerWelcomeScreen {\n    $adminKey = \"HKLM:\\Software\\Policies\\Microsoft\\Internet Explorer\\Main\"\n    New-Item -Path $adminKey -Value 1 -Force\n    Set-ItemProperty -Path $adminKey -Name \"DisableFirstRunCustomize\" -Value 1 -Force\n    Write-Host \"Disabled IE Welcome screen\"\n}\n\nfunction Disable-UserAccessControl {\n    Set-ItemProperty \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\" -Name \"ConsentPromptBehaviorAdmin\" -Value 00000000 -Force\n    Write-Host \"User Access Control (UAC) has been disabled.\"\n}\n\nfunction Disable-WindowsUpdate {\n    $autoUpdatePath = \"HKLM:SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU\"\n    if (Test-Path -Path $autoUpdatePath) {\n        Set-ItemProperty -Path $autoUpdatePath -Name NoAutoUpdate -Value 1\n        Write-Host \"Disabled Windows Update\"\n    } else {\n        Write-Host \"Windows Update key does not exist\"\n    }\n}\n\n# Enable $ErrorActionPreference='Stop' for AllUsersAllHosts\nAdd-Content -Path $profile.AllUsersAllHosts -Value '$ErrorActionPreference=\"Stop\"'\n\nWrite-Host \"Disable Server Manager on Logon\"\nGet-ScheduledTask -TaskName ServerManager | Disable-ScheduledTask\n\nWrite-Host \"Disable 'Allow your PC to be discoverable by other PCs' popup\"\nNew-Item -Path HKLM:\\System\\CurrentControlSet\\Control\\Network -Name NewNetworkWindowOff -Force\n\nWrite-Host 'Disable Windows Update Medic Service'\nSet-ItemProperty -Path HKLM:\\System\\CurrentControlSet\\Services\\WaaSMedicSvc -Name Start -Value 4 -Force\n\nWrite-Host \"Disable Windows Update\"\nDisable-WindowsUpdate\n\nWrite-Host \"Disable UAC\"\nDisable-UserAccessControl\n\nWrite-Host \"Disable IE Welcome Screen\"\nDisable-InternetExplorerWelcomeScreen\n\nWrite-Host \"Disable IE ESC\"\nDisable-InternetExplorerESC\n\nWrite-Host \"Setting local execution policy\"\nSet-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachine -ErrorAction Continue | Out-Null\nGet-ExecutionPolicy -List\n\nWrite-Host \"Enable long path behavior\"\n# See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file#maximum-path-length-limitation\nSet-ItemProperty -Path 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\FileSystem' -Name 'LongPathsEnabled' -Value 1\n\n# Expand disk size of OS drive\n$driveLetter = \"C\"\n$size = Get-PartitionSupportedSize -DriveLetter $driveLetter\nResize-Partition -DriveLetter $driveLetter -Size $size.SizeMax\nGet-Volume | Select-Object DriveLetter, SizeRemaining, Size | Sort-Object DriveLetter\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-DeveloperMode.ps1",
    "content": "################################################################################\n##  File:  Configure-DeveloperMode.ps1\n##  Desc:  Enables Developer Mode by toggling registry setting. Developer Mode is required to enable certain tools (e.g. WinAppDriver). \n################################################################################\n\n# Create AppModelUnlock if it doesn't exist, required for enabling Developer Mode\n$registryKeyPath = \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock\"\nif (-not(Test-Path -Path $registryKeyPath)) {\n    New-Item -Path $registryKeyPath -ItemType Directory -Force\n}\n\n# Add registry value to enable Developer Mode\nNew-ItemProperty -Path $registryKeyPath -Name AllowDevelopmentWithoutDevLicense -PropertyType DWORD -Value 1\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-Diagnostics.ps1",
    "content": "################################################################################\n##  File:  Configure-Diagnostics.ps1\n##  Desc:  Disables Just-In-Time Debugger and Windows Error Reporting\n################################################################################\n\nWrite-Host \"Disable Just-In-Time Debugger\"\n\n# Turn off Application Error Debugger\nNew-ItemProperty -Path \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug\" -Name Debugger -Value \"-\" -Type String -Force\nNew-ItemProperty -Path \"HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug\" -Name Debugger -Value \"-\" -Type String -Force\n\n# Turn off the Debug dialog\nNew-ItemProperty -Path \"HKLM:\\SOFTWARE\\Microsoft\\.NETFramework\" -Name DbgManagedDebugger -Value \"-\" -Type String -Force\nNew-ItemProperty -Path \"HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\.NETFramework\" -Name DbgManagedDebugger -Value \"-\" -Type String -Force\n\n# Disable the WER UI\nNew-ItemProperty -Path \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\" -Name DontShowUI -Value 1 -Type DWORD -Force\n# Send all reports to the user's queue\nNew-ItemProperty -Path \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\" -Name ForceQueue -Value 1 -Type DWORD -Force\n# Default consent choice 1 - Always ask (default)\nNew-ItemProperty -Path \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\Consent\" -Name DefaultConsent -Value 1 -Type DWORD -Force\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-DotnetSecureChannel.ps1",
    "content": "################################################################################\n##  File:  Configure-DotnetSecureChannel.ps1\n##  Desc:  Configure .NET to use TLS 1.2\n################################################################################\n\n$registryPath = \"HKLM:\\SOFTWARE\\Microsoft\\.NETFramework\\v4.0.30319\"\n$name = \"SchUseStrongCrypto\"\n$value = \"1\"\nif (Test-Path $registryPath) {\n    Set-ItemProperty -Path $registryPath -Name $name -Value $value -Type DWORD\n}\n\n$registryPath = \"HKLM:\\SOFTWARE\\Wow6432Node\\Microsoft\\.NETFramework\\v4.0.30319\"\nif (Test-Path $registryPath) {\n    Set-ItemProperty -Path $registryPath -Name $name -Value $value -Type DWORD\n}\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-DynamicPort.ps1",
    "content": "################################################################################\n##  File:  Configure-DynamicPort.ps1\n##  Desc:  Configure dynamic port range for TCP and UDP to start at port 49152\n##         and to end at the 65536 (16384 ports)\n################################################################################\n\n# https://support.microsoft.com/en-us/help/929851/the-default-dynamic-port-range-for-tcp-ip-has-changed-in-windows-vista\n# The new default start port is 49152, and the new default end port is 65535.\n# Default port configuration was changed during image generation by Visual Studio Enterprise Installer to:\n#   Protocol tcp Dynamic Port Range\n#   ---------------------------------\n#   Start Port      : 1024\n#   Number of Ports : 64511\n\nWrite-Host \"Set the dynamic port range to start at port 49152 and to end at the 65536 (16384 ports)\"\nforeach ($ipVersion in @(\"ipv4\", \"ipv6\")) {\n    foreach ($protocol in @(\"tcp\", \"udp\")) {\n        $command = \"netsh int $ipVersion set dynamicport $protocol start=49152 num=16384\"\n        Invoke-Expression $command | Out-Null\n        if ($LASTEXITCODE -ne 0) {\n            Write-Host \"Failed to set dynamic port range for $ipVersion $protocol\"\n            exit $LASTEXITCODE\n        }\n    }\n}\n\nInvoke-PesterTests -TestFile \"WindowsFeatures\" -TestName \"DynamicPorts\"\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-GDIProcessHandleQuota.ps1",
    "content": "################################################################################\n##  File:  Configure-GDIProcessHandleQuota.ps1\n##  Desc:  Set the GDIProcessHandleQuota value to 20000\n################################################################################\n\n# https://docs.microsoft.com/en-us/windows/win32/sysinfo/gdi-objects\n# This value can be set to a number between 256 and 65,536\n\n$defaultValue = 20000\nWrite-Host \"Set the GDIProcessHandleQuota value to $defaultValue\"\nSet-ItemProperty -Path \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows\" -Name GDIProcessHandleQuota -Value $defaultValue\nSet-ItemProperty -Path \"HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Windows\" -Name GDIProcessHandleQuota -Value $defaultValue\n\nInvoke-PesterTests -TestFile \"WindowsFeatures\" -TestName \"GDIProcessHandleQuota\"\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-ImageDataFile.ps1",
    "content": "################################################################################\n##  File:  Configure-ImageDataFile.ps1\n##  Desc:  Creates a JSON file with information about the image\n################################################################################\n\n$os = Get-CimInstance -ClassName Win32_OperatingSystem\n$caption = $os.Caption\n$osName = $caption.Substring(0, $caption.LastIndexOf(\" \"))\n$osEdition = $caption.Substring($caption.LastIndexOf(\" \") + 1)\n$osVersion = $os.Version\n$imageVersion = $env:IMAGE_VERSION\n$imageVersionComponents = $imageVersion.Split('.')\n$imageMajorVersion = $imageVersionComponents[0]\n$imageMinorVersion = $imageVersionComponents[1]\n$imageDataFile = $env:IMAGEDATA_FILE\n$githubUrl = \"https://github.com/actions/runner-images/blob\"\n\nif ((Test-IsWin25) -and $env:INSTALL_VS_2026) {\n    $imageLabel = \"windows-2025-vs2026\"\n    $softwareUrl = \"${githubUrl}/win25-vs2026/$imageMajorVersion.$imageMinorVersion/images/windows/Windows2025-VS2026-Readme.md\"\n    $releaseUrl = \"https://github.com/actions/runner-images/releases/tag/win25-vs2026%2F$imageMajorVersion.$imageMinorVersion\"\n} elseif (Test-IsWin25) {\n    $imageLabel = \"windows-2025\"\n    $softwareUrl = \"${githubUrl}/win25/$imageMajorVersion.$imageMinorVersion/images/windows/Windows2025-Readme.md\"\n    $releaseUrl = \"https://github.com/actions/runner-images/releases/tag/win25%2F$imageMajorVersion.$imageMinorVersion\"\n} elseif (Test-IsWin22) {\n    $imageLabel = \"windows-2022\"\n    $softwareUrl = \"${githubUrl}/win22/$imageMajorVersion.$imageMinorVersion/images/windows/Windows2022-Readme.md\"\n    $releaseUrl = \"https://github.com/actions/runner-images/releases/tag/win22%2F$imageMajorVersion.$imageMinorVersion\"\n} else {\n    throw \"Invalid platform version is found. Either Windows Server 2022 or 2025 are required\"\n}\n\n$json = @\"\n[\n  {\n    \"group\": \"Operating System\",\n    \"detail\": \"${osName}\\n${osVersion}\\n${osEdition}\"\n  },\n  {\n    \"group\": \"Runner Image\",\n    \"detail\": \"Image: ${imageLabel}\\nVersion: ${imageVersion}\\nIncluded Software: ${softwareUrl}\\nImage Release: ${releaseUrl}\"\n  }\n]\n\"@\n\n$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False\n[System.IO.File]::WriteAllText($imageDataFile, $json, $Utf8NoBomEncoding)\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-PowerShell.ps1",
    "content": "################################################################################\n##  File:  Configure-Powershell.ps1\n##  Desc:  Manage PowerShell configuration\n################################################################################\n\n#region System\nWrite-Host \"Setup PowerShellGet\"\nInstall-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force\n\n# Specifies the installation policy\nSet-PSRepository -InstallationPolicy Trusted -Name PSGallery\n\nWrite-Host 'Warmup PSModuleAnalysisCachePath (speedup first powershell invocation by 20s)'\n$PSModuleAnalysisCachePath = 'C:\\PSModuleAnalysisCachePath\\ModuleAnalysisCache'\n\n[Environment]::SetEnvironmentVariable('PSModuleAnalysisCachePath', $PSModuleAnalysisCachePath, \"Machine\")\n# make variable to be available in the current session\n${env:PSModuleAnalysisCachePath} = $PSModuleAnalysisCachePath\n\nNew-Item -Path $PSModuleAnalysisCachePath -ItemType 'File' -Force | Out-Null\n#endregion\n\n#region User (current user, image generation only)\nif (-not (Test-Path $profile)) {\n    New-Item $profile -ItemType File -Force\n}\n  \n@\" \n  if ( -not(Get-Module -ListAvailable -Name PowerHTML)) {\n      Install-Module PowerHTML -Scope CurrentUser \n  } \n  \n  if ( -not(Get-Module -Name PowerHTML)) {\n      Import-Module PowerHTML\n  } \n\"@ | Add-Content -Path $profile -Force\n\n#endregion\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-Shell.ps1",
    "content": "# Create shells folder\n$shellPath = \"C:\\shells\"\nNew-Item -Path $shellPath -ItemType Directory | Out-Null\n\n# add a wrapper for C:\\msys64\\usr\\bin\\bash.exe\n@'\n@echo off\nsetlocal\nIF NOT DEFINED MSYS2_PATH_TYPE set MSYS2_PATH_TYPE=strict\nIF NOT DEFINED MSYSTEM set MSYSTEM=mingw64\nset CHERE_INVOKING=1\nC:\\msys64\\usr\\bin\\bash.exe -leo pipefail %*\n'@ | Out-File -FilePath \"$shellPath\\msys2bash.cmd\" -Encoding ascii\n\n# gitbash <--> C:\\Program Files\\Git\\bin\\bash.exe\nNew-Item -ItemType SymbolicLink -Path \"$shellPath\\gitbash.exe\" -Target \"$env:ProgramFiles\\Git\\bin\\bash.exe\" | Out-Null\n\n# wslbash <--> C:\\Windows\\System32\\bash.exe\nNew-Item -ItemType SymbolicLink -Path \"$shellPath\\wslbash.exe\" -Target \"$env:SystemRoot\\System32\\bash.exe\" | Out-Null\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-System.ps1",
    "content": "################################################################################\n##  File:  Configure-System.ps1\n##  Desc:  Applies various configuration settings to the final image\n################################################################################\n\n# Set default version to 1 for WSL (aka LXSS - Linux Subsystem)\n# The value should be set in the default user registry hive\n# https://github.com/actions/runner-images/issues/5760\nif (Test-IsWin22) {\n    Write-Host \"Setting WSL default version to 1\"\n\n    Mount-RegistryHive `\n        -FileName \"C:\\Users\\Default\\NTUSER.DAT\" `\n        -SubKey \"HKLM\\DEFAULT\"\n\n    # Create the key if it doesn't exist\n    $keyPath = \"DEFAULT\\Software\\Microsoft\\Windows\\CurrentVersion\\Lxss\"\n    if (-not (Test-Path $keyPath)) {\n        Write-Host \"Creating $keyPath key\"\n        New-Item -Path (Join-Path \"HKLM:\\\" $keyPath) -Force | Out-Null\n    }\n\n    # Set the DefaultVersion value to 1\n    $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($keyPath, $true)\n    $key.SetValue(\"DefaultVersion\", \"1\", \"DWord\")\n    $key.Handle.Close()\n    [System.GC]::Collect()\n\n    Dismount-RegistryHive \"HKLM\\DEFAULT\"\n}\n\n# allow msi to write to temp folder\n# see https://github.com/actions/runner-images/issues/1704\ncmd /c \"icacls $env:SystemRoot\\Temp /grant Users:f /t /c /q 2>&1\" | Out-Null\nif ($LASTEXITCODE -ne 0) {\n    throw \"Failed to grant Users full control of $env:SystemRoot\\Temp\"\n}\n\n# Enable inheritance for the entire C:\\ drive\nif (Test-IsWin25) {\n    cmd /c \"icacls C:\\ /inheritance:e /c /q 2>&1\" | Out-Null\n    if ($LASTEXITCODE -ne 0) {\n        throw \"Failed to enable inheritance for C:\\ drive\"\n    }\n}\n\n# Registry settings\n$registrySettings = @(\n    @{Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU\"; Name = \"AUOptions\"; Value = 1; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU\"; Name = \"NoAutoUpdate\"; Value = 1; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate\"; Name = \"DoNotConnectToWindowsUpdateInternetLocations\"; Value = 1; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate\"; Name = \"DisableWindowsUpdateAccess\"; Value = 1; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Device Metadata\"; Name = \"PreventDeviceMetadataFromNetwork\"; Value = 1; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\DataCollection\"; Name = \"AllowTelemetry\"; Value = 0; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\SQMClient\\Windows\"; Name = \"CEIPEnable\"; Value = 0; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\AppCompat\"; Name = \"AITEnable\"; Value = 0; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\AppCompat\"; Name = \"DisableUAR\"; Value = 1; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\Software\\Policies\\Microsoft\\Windows\\DataCollection\"; Name = \"AllowTelemetry\"; Value = 0; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Wow6432Node\\Policies\\Microsoft\\Windows\\DataCollection\"; Name = \"AllowTelemetry\"; Value = 0; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Schedule\\Maintenance\"; Name = \"MaintenanceDisabled\"; Value = 1; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\MRT\"; Name = \"DontOfferThroughWUAU\"; Value = 1; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\MRT\"; Name = \"DontReportInfectionInformation\"; Value = 1; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Windows Search\"; Name = \"AllowCortana\"; Value = 0; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SYSTEM\\CurrentControlSet\\Control\"; Name = \"ServicesPipeTimeout\"; Value = 120000; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SYSTEM\\CurrentControlSet\\Control\\WMI\\AutoLogger\\AutoLogger-Diagtrack-Listener\"; Name = \"Start\"; Value = 0; PropertyType = \"DWORD\" }\n    @{Path = \"HKLM:\\SYSTEM\\CurrentControlSet\\Control\\WMI\\AutoLogger\\SQMLogger\"; Name = \"Start\"; Value = 0; PropertyType = \"DWORD\" }\n)\n\n$registrySettings | ForEach-Object {\n    $regPath = $_.Path\n    if (-not (Test-Path $regPath)) {\n        New-Item -Path $regPath -Force -ErrorAction Ignore | Out-Null\n    }\n    New-ItemProperty @_ -Force -ErrorAction Ignore\n} | Out-Null\n\n# Disable Template Services / User Services added by Desktop Experience\n$regUserServicesToDisables = @(\n    \"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\CDPUserSvc\"\n    \"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\OneSyncSvc\"\n    \"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\PimIndexMaintenanceSvc\"\n    \"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\UnistoreSvc\"\n    \"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\UserDataSvc\"\n)\n\n$regUserServicesToDisables | ForEach-Object {\n    $regPath = $_\n    if (-not (Test-Path $regPath)) {\n        New-Item -Path $regPath -Force -ErrorAction Ignore | Out-Null\n    }\n    New-ItemProperty -Path $regPath -Name \"Start\" -Value 4 -PropertyType DWORD -Force -ErrorAction Ignore\n    New-ItemProperty -Path $regPath -Name \"UserServiceFlags\" -Value 0 -PropertyType DWORD -Force -ErrorAction Ignore\n} | Out-Null\n\n\nWrite-Host 'Disable Windows Update Service'\nSet-ItemProperty -Path HKLM:\\System\\CurrentControlSet\\Services\\wuauserv -Name Start -Value 4 -Force\n\n# Disabled services\n$servicesToDisable = @(\n    'wuauserv'\n    'DiagTrack'\n    'dmwappushservice'\n    $(if(-not (Test-IsWin25)){'PcaSvc'})\n    'SysMain'\n    'gupdate'\n    'gupdatem'\n    $(if(-not (Test-IsWin25)){'StorSvc'})\n) | Get-Service -ErrorAction SilentlyContinue\nStop-Service $servicesToDisable\n$servicesToDisable.WaitForStatus('Stopped', \"00:01:00\")\n$servicesToDisable | Set-Service -StartupType Disabled\n\n# Disable scheduled tasks\n$allTasksInTaskPath = @(\n    \"\\\"\n    \"\\Microsoft\\Azure\\Security\\\"\n    \"\\Microsoft\\VisualStudio\\\"\n    \"\\Microsoft\\VisualStudio\\Updates\\\"\n    \"\\Microsoft\\Windows\\Application Experience\\\"\n    \"\\Microsoft\\Windows\\ApplicationData\\\"\n    \"\\Microsoft\\Windows\\Autochk\\\"\n    \"\\Microsoft\\Windows\\Chkdsk\\\"\n    \"\\Microsoft\\Windows\\Customer Experience Improvement Program\\\"\n    \"\\Microsoft\\Windows\\Data Integrity Scan\\\"\n    \"\\Microsoft\\Windows\\Defrag\\\"\n    \"\\Microsoft\\Windows\\Diagnosis\\\"\n    \"\\Microsoft\\Windows\\DiskCleanup\\\"\n    \"\\Microsoft\\Windows\\DiskDiagnostic\\\"\n    \"\\Microsoft\\Windows\\Maintenance\\\"\n    \"\\Microsoft\\Windows\\PI\\\"\n    \"\\Microsoft\\Windows\\Power Efficiency Diagnostics\\\"\n    \"\\Microsoft\\Windows\\Server Manager\\\"\n    \"\\Microsoft\\Windows\\Speech\\\"\n    \"\\Microsoft\\Windows\\UpdateOrchestrator\\\"\n    \"\\Microsoft\\Windows\\Windows Error Reporting\\\"\n    \"\\Microsoft\\Windows\\WindowsUpdate\\\"\n    \"\\Microsoft\\XblGameSave\\\"\n)\n\n$allTasksInTaskPath | ForEach-Object {\n    Get-ScheduledTask -TaskPath $_ -ErrorAction Ignore | Disable-ScheduledTask -ErrorAction Ignore\n} | Out-Null\n\n$disableTaskNames = @(\n    @{TaskPath = \"\\Microsoft\\Windows\\.NET Framework\\\"; TaskName = \".NET Framework NGEN v4.0.30319\" }\n    @{TaskPath = \"\\Microsoft\\Windows\\.NET Framework\\\"; TaskName = \".NET Framework NGEN v4.0.30319 64\" }\n    @{TaskPath = \"\\Microsoft\\Windows\\AppID\\\"; TaskName = \"SmartScreenSpecific\" }\n)\n\n$disableTaskNames | ForEach-Object {\n    Disable-ScheduledTask @PSItem -ErrorAction Ignore\n} | Out-Null\n\nWrite-Host \"Configure-System.ps1 - completed\"\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-SystemEnvironment.ps1",
    "content": "################################################################################\n##  File:  Configure-SystemEnvironment.ps1\n##  Desc:  Configures system environment variables\n################################################################################\n\n$variables = @{\n    \"ImageVersion\"                        = $env:IMAGE_VERSION\n    \"ImageOS\"                             = $env:IMAGE_OS\n    \"AGENT_TOOLSDIRECTORY\"                = $env:AGENT_TOOLSDIRECTORY\n    \"RUNNER_TOOL_CACHE\"                   = $env:AGENT_TOOLSDIRECTORY\n}\n\n$variables.GetEnumerator() | ForEach-Object {\n    [Environment]::SetEnvironmentVariable($_.Key, $_.Value, \"Machine\")\n}\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-Toolset.ps1",
    "content": "################################################################################\n##  File:  Configure-Toolset.ps1\n##  Team:  CI-Build\n##  Desc:  Configure Toolset\n################################################################################\n\n$toolEnvConfigs = @{\n    Python = @{\n        pathTemplates = @(\n            \"{0}\"\n            \"{0}\\Scripts\"\n        )\n    }\n    go     = @{\n        pathTemplates  = @(\n            \"{0}\\bin\"\n        )\n        envVarTemplate = \"GOROOT_{0}_{1}_X64\"\n    }\n}\n\n$tools = Get-ToolsetContent `\n| Select-Object -ExpandProperty toolcache `\n| Where-Object { $toolEnvConfigs.Keys -contains $_.name }\n\nWrite-Host \"Configure toolset tools environment...\"\nforeach ($tool in $tools) {\n    $toolEnvConfig = $toolEnvConfigs[$tool.name]\n    \n    if (-not ([string]::IsNullOrEmpty($toolEnvConfig.envVarTemplate))) {\n        foreach ($version in $tool.versions) {\n            Write-Host \"Set $($tool.name) $version environment variable...\"\n\n            $foundVersionArchPath = Get-TCToolVersionPath -Name $tool.name -Version $version -Arch $tool.arch\n            $envName = $toolEnvConfig.envVarTemplate -f $version.Split(\".\")\n\n            Write-Host \"Set $envName to $foundVersionArchPath\"\n            [Environment]::SetEnvironmentVariable($envName, $foundVersionArchPath, \"Machine\")\n        }\n    }\n\n    if (-not ([string]::IsNullOrEmpty($tool.default))) {\n        Write-Host \"Use $($tool.name) $($tool.default) as a system $($tool.name)...\"\n\n        $toolVersionPath = Get-TCToolVersionPath -Name $tool.name -Version $tool.default -Arch $tool.arch\n\n        foreach ($template in $toolEnvConfig.pathTemplates) {\n            $toolSystemPath = $template -f $toolVersionPath\n            Write-Host \"Add $toolSystemPath to system PATH...\"\n            Add-MachinePathItem -PathItem $toolSystemPath | Out-Null\n        }\n    \n        if (-not ([string]::IsNullOrEmpty($tool.defaultVariable))) {\n            Write-Host \"Set $($tool.name) $($tool.default) $($tool.defaultVariable) environment variable...\"\n            [Environment]::SetEnvironmentVariable($tool.defaultVariable, $toolVersionPath, \"Machine\")\n        }\n    }\n}\n\nInvoke-PesterTests -TestFile \"Toolset\"\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-User.ps1",
    "content": "################################################################################\n##  File:  Configure-User.ps1\n##  Desc:  Performs user part of warm up and moves data to C:\\Users\\Default\n################################################################################\n\n#\n# more: https://github.com/actions/runner-images-internal/issues/5320\n#       https://github.com/actions/runner-images/issues/5301#issuecomment-1648292990\n#\n\nWrite-Host \"Warmup 'devenv.exe /updateconfiguration'\"\n$vsInstallRoot = (Get-VisualStudioInstance).InstallationPath\ncmd.exe /c \"`\"$vsInstallRoot\\Common7\\IDE\\devenv.exe`\" /updateconfiguration\"\nif ($LASTEXITCODE -ne 0) {\n    throw \"Failed to warmup 'devenv.exe /updateconfiguration'\"\n}\n\n# we are fine if some file is locked and cannot be copied\nCopy-Item ${env:USERPROFILE}\\AppData\\Local\\Microsoft\\VisualStudio -Destination c:\\users\\default\\AppData\\Local\\Microsoft\\VisualStudio -Recurse -ErrorAction SilentlyContinue\n\nMount-RegistryHive `\n    -FileName \"C:\\Users\\Default\\NTUSER.DAT\" `\n    -SubKey \"HKLM\\DEFAULT\"\n\nreg.exe copy HKCU\\Software\\Microsoft\\VisualStudio HKLM\\DEFAULT\\Software\\Microsoft\\VisualStudio /s\nif ($LASTEXITCODE -ne 0) {\n    throw \"Failed to copy HKCU\\Software\\Microsoft\\VisualStudio to HKLM\\DEFAULT\\Software\\Microsoft\\VisualStudio\"\n}\n\n# TortoiseSVN not installed on Windows 2025 image due to Sysprep issues\nif (-not (Test-IsWin25)) {\n    # disable TSVNCache.exe\n    $registryKeyPath = 'HKCU:\\Software\\TortoiseSVN'\n    if (-not(Test-Path -Path $registryKeyPath)) {\n        New-Item -Path $registryKeyPath -ItemType Directory -Force\n    }\n\n    New-ItemProperty -Path $registryKeyPath -Name CacheType -PropertyType DWORD -Value 0\n    reg.exe copy HKCU\\Software\\TortoiseSVN HKLM\\DEFAULT\\Software\\TortoiseSVN /s\n    if ($LASTEXITCODE -ne 0) {\n        throw \"Failed to copy HKCU\\Software\\TortoiseSVN to HKLM\\DEFAULT\\Software\\TortoiseSVN\"\n    }\n}\n# Accept by default \"Send Diagnostic data to Microsoft\" consent.\nif (Test-IsWin25) {\n    $registryKeyPath = 'HKLM:\\DEFAULT\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Privacy'\n    New-ItemProperty -Path $registryKeyPath -Name PrivacyConsentPresentationVersion -PropertyType DWORD -Value 3 | Out-Null\n    New-ItemProperty -Path $registryKeyPath -Name PrivacyConsentSettingsValidMask -PropertyType DWORD -Value 4 | Out-Null\n    New-ItemProperty -Path $registryKeyPath -Name PrivacyConsentSettingsVersion -PropertyType DWORD -Value 5 | Out-Null\n}\n\nDismount-RegistryHive \"HKLM\\DEFAULT\"\n\n# Remove the \"installer\" (var.install_user) user profile for Windows 2025 image\nif (Test-IsWin25) {\n    Get-CimInstance -ClassName Win32_UserProfile | where-object {$_.LocalPath -match $env:INSTALL_USER} | Remove-CimInstance -Confirm:$false\n    & net user $env:INSTALL_USER /DELETE\n}\n\nWrite-Host \"Configure-User.ps1 - completed\"\n"
  },
  {
    "path": "images/windows/scripts/build/Configure-WindowsDefender.ps1",
    "content": "################################################################################\n##  File:  Configure-WindowsDefender.ps1\n##  Desc:  Disables Windows Defender\n################################################################################\n\nWrite-Host \"Disable Windows Defender...\"\n$avPreference = @(\n    @{DisableArchiveScanning = $true}\n    @{DisableAutoExclusions = $true}\n    @{DisableBehaviorMonitoring = $true}\n    @{DisableBlockAtFirstSeen = $true}\n    @{DisableCatchupFullScan = $true}\n    @{DisableCatchupQuickScan = $true}\n    @{DisableIntrusionPreventionSystem = $true}\n    @{DisableIOAVProtection = $true}\n    @{DisablePrivacyMode = $true}\n    @{DisableScanningNetworkFiles = $true}\n    @{DisableScriptScanning = $true}\n    @{MAPSReporting = 0}\n    @{PUAProtection = 0}\n    @{SignatureDisableUpdateOnStartupWithoutEngine = $true}\n    @{SubmitSamplesConsent = 2}\n    @{ScanAvgCPULoadFactor = 5; ExclusionPath = @(\"D:\\\", \"C:\\\")}\n    @{DisableRealtimeMonitoring = $true}\n    @{ScanScheduleDay = 8}\n)\n\n$avPreference += @(\n    @{EnableControlledFolderAccess = \"Disable\"}\n    @{EnableNetworkProtection = \"Disabled\"}\n)\n\n$avPreference | Foreach-Object {\n    $avParams = $_\n    Set-MpPreference @avParams\n}\n\n# https://github.com/actions/runner-images/issues/4277\n# https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/microsoft-defender-antivirus-compatibility?view=o365-worldwide\n$atpRegPath = 'HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows Advanced Threat Protection'\nif (Test-Path $atpRegPath) {\n    Write-Host \"Set Microsoft Defender Antivirus to passive mode\"\n    Set-ItemProperty -Path $atpRegPath -Name 'ForceDefenderPassiveMode' -Value '1' -Type 'DWORD'\n}\n"
  },
  {
    "path": "images/windows/scripts/build/Install-AWSTools.ps1",
    "content": "################################################################################\n##  File:  Install-AWSTools.ps1\n##  Desc:  Install AWS tools: CLI, Session Manager Plugin, AWS SAM CLI\n##  Supply chain security: AWS CLI - managed by package manager, Session Manager Plugin for the AWS CLI - missing, AWS SAM CLI - checksum validation\n################################################################################\n\n# Install AWS CLI\nInstall-ChocoPackage awscli\n\n# Install Session Manager Plugin for the AWS CLI\nInstall-Binary `\n    -Url \"https://s3.amazonaws.com/session-manager-downloads/plugin/latest/windows/SessionManagerPluginSetup.exe\" `\n    -InstallArgs (\"/silent\", \"/install\") `\n    -ExpectedSubject 'CN=\"Amazon Web Services, Inc.\", OU=AWS Systems Manager, O=\"Amazon Web Services, Inc.\", L=Seattle, S=Washington, C=US, SERIALNUMBER=4152954, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US'\n$env:Path = $env:Path + \";$env:ProgramFiles\\Amazon\\SessionManagerPlugin\\bin\"\n\n# Install AWS SAM CLI\n$downloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"awslabs/aws-sam-cli\" `\n    -Version \"latest\" `\n    -UrlMatchPattern \"AWS_SAM_CLI_64_PY3.msi\"\n$externalHash = Get-ChecksumFromGithubRelease `\n    -Repo \"awslabs/aws-sam-cli\" `\n    -Version \"latest\" `\n    -FileName (Split-Path $downloadUrl -Leaf) `\n    -HashType \"SHA256\"\n\nInstall-Binary `\n    -Url $downloadUrl `\n    -ExpectedSHA256Sum $externalHash\n\nInvoke-PesterTests -TestFile \"CLI.Tools\" -TestName \"AWS\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-ActionsCache.ps1",
    "content": "################################################################################\n##  File:       Install-ActionsCache.ps1\n##  Desc:       Downloads latest release from https://github.com/actions/action-versions\n##  Maintainer: #actions-runtime and @TingluoHuang\n################################################################################\n\n$actionArchiveCache = \"C:\\actionarchivecache\\\"\n\nif (-not (Test-Path $actionArchiveCache)) {\n    Write-Host \"Creating action archive cache folder\"\n    New-Item -ItemType Directory -Path $actionArchiveCache | Out-Null\n}\n\n$downloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"actions/action-versions\" `\n    -Version \"latest\" `\n    -Asset \"action-versions.zip\"\n\nWrite-Host \"Download Latest action-versions archive from $downloadUrl\"\n$actionVersionsArchivePath = Invoke-DownloadWithRetry $downloadUrl\n\nWrite-Host \"Expand action-versions archive\"\nExpand-7ZipArchive -Path $actionVersionsArchivePath -DestinationPath $actionArchiveCache\n\n[Environment]::SetEnvironmentVariable(\"ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE\", $actionArchiveCache, \"Machine\")\n\nInvoke-PesterTests -TestFile \"ActionArchiveCache\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-AliyunCli.ps1",
    "content": "################################################################################\n##  File:  Install-AliyunCli.ps1\n##  Desc:  Install Alibaba Cloud CLI\n##  Supply chain security: Alibaba Cloud CLI - checksum validation\n################################################################################\n\nWrite-Host \"Download Latest aliyun-cli archive\"\n$downloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"aliyun/aliyun-cli\" `\n    -Version \"latest\" `\n    -UrlMatchPattern \"aliyun-cli-windows-*-amd64.zip\"\n$packagePath = Invoke-DownloadWithRetry $downloadUrl\n\n#region Supply chain security - Alibaba Cloud CLI\n$packageName = Split-Path $downloadUrl -Leaf\n$externalHash = Get-ChecksumFromUrl -Type \"SHA256\" `\n    -Url ($downloadUrl -replace $packageName, \"SHASUMS256.txt\") `\n    -FileName $packageName\nTest-FileChecksum $packagePath -ExpectedSHA256Sum $externalHash\n#endregion\n\nWrite-Host \"Expand aliyun-cli archive\"\n$aliyunPath = \"C:\\aliyun-cli\"\nNew-Item -Path $aliyunPath -ItemType Directory -Force\nExpand-7ZipArchive -Path $packagePath -DestinationPath $aliyunPath\n\n# Add aliyun-cli to path\nAdd-MachinePathItem $aliyunPath\n\nInvoke-PesterTests -TestFile \"CLI.Tools\" -TestName \"Aliyun CLI\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-AndroidSDK.ps1",
    "content": "################################################################################\n##  File:  Install-AndroidSDK.ps1\n##  Desc:  Install and update Android SDK and tools\n##  Supply chain security: checksum validation\n################################################################################\n\n# Actual Android SDK installation directory\n$SDKInstallRoot = \"C:\\Program Files (x86)\\Android\\android-sdk\"\n\n# Hardlink to the Android SDK installation directory with no spaces in the path.\n# ANDROID_NDK* env vars should not contain spaces, otherwise ndk-build.cmd gives an error\n# https://github.com/actions/runner-images/issues/1122\n$SDKRootPath = \"C:\\Android\\android-sdk\"\n\n#region functions\nfunction Install-AndroidSDKPackages {\n    <#\n    .SYNOPSIS\n        This function installs the specified Android SDK packages.\n\n    .DESCRIPTION\n        The Install-AndroidSDKPackages function takes an array of package names as a parameter and installs each of them using the sdkmanager.bat script.\n\n    .PARAMETER Packages\n        An array of package names in the format of SDK-style paths to be installed.\n\n    .EXAMPLE\n        Install-AndroidSDKPackages -Packages \"platforms;android-29\", \"build-tools;29.0.2\"\n\n        This command installs the Android SDK Platform 29 and Build-Tools 29.0.2.\n\n    #>\n    Param\n    (\n        [Parameter(Mandatory = $true)]\n        [AllowEmptyCollection()]\n        [AllowNull()]\n        [string[]] $Packages\n    )\n    \n    # The sdkmanager.bat script is used to install Android SDK packages.\n    $SDKManager = \"$SDKRootPath\\cmdline-tools\\latest\\bin\\sdkmanager.bat\"\n\n    $errors = @()\n\n    foreach ($package in $Packages) {\n        & $SDKManager --install \"$package\" --sdk_root=$SDKRootPath\n        if ($LASTEXITCODE -ne 0) {\n            $errors += \"Failed to install package $package with exit code $LASTEXITCODE\"\n        }\n    }\n\n    if ($errors.Count -gt 0) {\n        throw $errors\n    }\n}\n#endregion\n\n# get packages to install from the toolset\n$androidToolset = (Get-ToolsetContent).android\n# Newer version(s) require Java 11 by default\n# See https://github.com/actions/runner-images/issues/6960\n$cmdlineToolsUrl = $androidToolset.commandline_tools_url\n$cmdlineToolsArchPath = Invoke-DownloadWithRetry $cmdlineToolsUrl\n\nTest-FileChecksum $cmdlineToolsArchPath -ExpectedSHA256Sum $androidToolset.hash\n\n$cmdlineToolsPath = Join-Path -Path $SDKInstallRoot -ChildPath \"cmdline-tools\"\nif (Test-Path \"$cmdlineToolsPath\\latest\") {\n    Write-Host \"Removing previous cmdline-tools installation from Visual Studio workload\"\n    Remove-Item \"$cmdlineToolsPath\\latest\" -Recurse -Force\n}\n\nExpand-7ZipArchive -Path $cmdlineToolsArchPath -DestinationPath $cmdlineToolsPath\n\n# cmdline tools should be installed in ${SDKInstallRoot}\\cmdline-tools\\latest\\bin, but archive contains ${SDKInstallRoot}\\cmdline-tools\\bin \n# we need to create the proper folder structure\nInvoke-ScriptBlockWithRetry -Command {\n    Rename-Item \"${SDKInstallRoot}\\cmdline-tools\\cmdline-tools\" \"latest\" -ErrorAction Stop\n}\n\n# Create hardlink at $SDKRootPath pointing to SDK installation directory in Program Files\nNew-Item -Path (Split-Path $SDKRootPath -Parent) -ItemType Directory -Force\nNew-Item -Path \"$SDKRootPath\" -ItemType SymbolicLink -Value \"$SDKInstallRoot\"\n\n# Install the standard Android SDK licenses. Currently, there isn't a better way to do this,\n# so we are base64-encoded a zip of the licenses directory from another installation.\n# To create this base64 string, create a zip file that contains nothing but a 'licenses' folder,\n# which folder contains the accepted license files found in 'C:\\Program Files (x86)\\Android\\android-sdk\\licenses'.\n# Then, run this in PowerShell:\n#     $LicensesZipFileName = 'C:\\Program Files (x86)\\Android\\android-sdk\\Licenses.zip'\n#     $base64Content = [Convert]::ToBase64String([IO.File]::ReadAllBytes($LicensesZipFileName))\n#     echo $base64Content\n# Another possible solution that works in powershell core:\n# Write-Ouptut \"y\" | $sdkmanager.bat <packagename>\n$licenseContentBase64 = \"UEsDBBQAAAAAAKNK11IAAAAAAAAAAAAAAAAJAAAAbGljZW5zZXMvUEsDBAoAAAAAAJ1K11K7n0IrKgAAACoAAAAhAAAAbGljZW5zZXMvYW5kcm9pZC1nb29nbGV0di1saWNlbnNlDQo2MDEwODViOTRjZDc3ZjBiNTRmZjg2NDA2OTU3MDk5ZWJlNzljNGQ2UEsDBAoAAAAAAKBK11LzQumJKgAAACoAAAAkAAAAbGljZW5zZXMvYW5kcm9pZC1zZGstYXJtLWRidC1saWNlbnNlDQo4NTlmMzE3Njk2ZjY3ZWYzZDdmMzBhNTBhNTU2MGU3ODM0YjQzOTAzUEsDBAoAAAAAAKFK11IKSOJFKgAAACoAAAAcAAAAbGljZW5zZXMvYW5kcm9pZC1zZGstbGljZW5zZQ0KMjQzMzNmOGE2M2I2ODI1ZWE5YzU1MTRmODNjMjgyOWIwMDRkMWZlZVBLAwQKAAAAAACiStdSec1a4SoAAAAqAAAAJAAAAGxpY2Vuc2VzL2FuZHJvaWQtc2RrLXByZXZpZXctbGljZW5zZQ0KODQ4MzFiOTQwOTY0NmE5MThlMzA1NzNiYWI0YzljOTEzNDZkOGFiZFBLAwQKAAAAAACiStdSk6vQKCoAAAAqAAAAGwAAAGxpY2Vuc2VzL2dvb2dsZS1nZGstbGljZW5zZQ0KMzNiNmEyYjY0NjA3ZjExYjc1OWYzMjBlZjlkZmY0YWU1YzQ3ZDk3YVBLAwQKAAAAAACiStdSrE3jESoAAAAqAAAAJAAAAGxpY2Vuc2VzL2ludGVsLWFuZHJvaWQtZXh0cmEtbGljZW5zZQ0KZDk3NWY3NTE2OThhNzdiNjYyZjEyNTRkZGJlZWQzOTAxZTk3NmY1YVBLAwQKAAAAAACjStdSkb1vWioAAAAqAAAAJgAAAGxpY2Vuc2VzL21pcHMtYW5kcm9pZC1zeXNpbWFnZS1saWNlbnNlDQplOWFjYWI1YjVmYmI1NjBhNzJjZmFlY2NlODk0Njg5NmZmNmFhYjlkUEsBAj8AFAAAAAAAo0rXUgAAAAAAAAAAAAAAAAkAJAAAAAAAAAAQAAAAAAAAAGxpY2Vuc2VzLwoAIAAAAAAAAQAYACIHOBcRaNcBIgc4FxFo1wHBTVQTEWjXAVBLAQI/AAoAAAAAAJ1K11K7n0IrKgAAACoAAAAhACQAAAAAAAAAIAAAACcAAABsaWNlbnNlcy9hbmRyb2lkLWdvb2dsZXR2LWxpY2Vuc2UKACAAAAAAAAEAGACUEFUTEWjXAZQQVRMRaNcB6XRUExFo1wFQSwECPwAKAAAAAACgStdS80LpiSoAAAAqAAAAJAAkAAAAAAAAACAAAACQAAAAbGljZW5zZXMvYW5kcm9pZC1zZGstYXJtLWRidC1saWNlbnNlCgAgAAAAAAABABgAsEM0FBFo1wGwQzQUEWjXAXb1MxQRaNcBUEsBAj8ACgAAAAAAoUrXUgpI4kUqAAAAKgAAABwAJAAAAAAAAAAgAAAA/AAAAGxpY2Vuc2VzL2FuZHJvaWQtc2RrLWxpY2Vuc2UKACAAAAAAAAEAGAAsMGUVEWjXASwwZRURaNcB5whlFRFo1wFQSwECPwAKAAAAAACiStdSec1a4SoAAAAqAAAAJAAkAAAAAAAAACAAAABgAQAAbGljZW5zZXMvYW5kcm9pZC1zZGstcHJldmlldy1saWNlbnNlCgAgAAAAAAABABgA7s3WFRFo1wHuzdYVEWjXAfGm1hURaNcBUEsBAj8ACgAAAAAAokrXUpOr0CgqAAAAKgAAABsAJAAAAAAAAAAgAAAAzAEAAGxpY2Vuc2VzL2dvb2dsZS1nZGstbGljZW5zZQoAIAAAAAAAAQAYAGRDRxYRaNcBZENHFhFo1wFfHEcWEWjXAVBLAQI/AAoAAAAAAKJK11KsTeMRKgAAACoAAAAkACQAAAAAAAAAIAAAAC8CAABsaWNlbnNlcy9pbnRlbC1hbmRyb2lkLWV4dHJhLWxpY2Vuc2UKACAAAAAAAAEAGADGsq0WEWjXAcayrRYRaNcBxrKtFhFo1wFQSwECPwAKAAAAAACjStdSkb1vWioAAAAqAAAAJgAkAAAAAAAAACAAAACbAgAAbGljZW5zZXMvbWlwcy1hbmRyb2lkLXN5c2ltYWdlLWxpY2Vuc2UKACAAAAAAAAEAGAA4LjgXEWjXATguOBcRaNcBIgc4FxFo1wFQSwUGAAAAAAgACACDAwAACQMAAAAA\"\n$licenseContent = [System.Convert]::FromBase64String($licenseContentBase64)\nSet-Content -Path \"$SDKInstallRoot\\android-sdk-licenses.zip\" -Value $licenseContent -Encoding Byte\nExpand-7ZipArchive -Path \"$SDKInstallRoot\\android-sdk-licenses.zip\" -DestinationPath $SDKInstallRoot\n\n# Install platform-tools\n$platformToolsPath = Join-Path -Path $SDKInstallRoot -ChildPath \"platform-tools\"\nif (Test-Path $platformToolsPath) {\n    Write-Host \"Removing previous platform-tools installation from Visual Studio component\"\n    Remove-Item $platformToolsPath -Recurse -Force\n}\nInstall-AndroidSDKPackages \"platform-tools\"\n\n# Get Android SDK packages list\n$androidPackages = Get-AndroidPackages -SDKRootPath $SDKRootPath\n\n# Install Android platform versions\n# that are greater than or equal to the minimum version\nWrite-Host \"Installing Android SDK packages for platforms...\"\n$platformList = Get-AndroidPlatformPackages `\n    -SDKRootPath $SDKRootPath `\n    -minVersion $androidToolset.platform_min_version\nInstall-AndroidSDKPackages $platformList\n\n# Install Android build-tools versions\n# that are greater than or equal to the minimum version\nWrite-Host \"Installing Android SDK packages for build tools...\"\n$buildToolsList = Get-AndroidBuildToolPackages `\n    -SDKRootPath $SDKRootPath `\n    -minVersion $androidToolset.build_tools_min_version\nInstall-AndroidSDKPackages $buildToolsList\n\n# Install Android Emulator\nInstall-AndroidSDKPackages \"emulator\"\n\n# Install extras, add-ons and additional tools\nWrite-Host \"Installing Android SDK extras, add-ons and additional tools...\"\nInstall-AndroidSDKPackages ($androidToolset.extras | ForEach-Object { \"extras;$_\" })\nInstall-AndroidSDKPackages ($androidToolset.addons | ForEach-Object { \"add-ons;$_\" })\nInstall-AndroidSDKPackages ($androidToolset.additional_tools)\n\n# Install NDKs\n$ndkMajorVersions = $androidToolset.ndk.versions\n$ndkDefaultMajorVersion = $androidToolset.ndk.default\n$ndkLatestMajorVersion = $ndkMajorVersions | Select-Object -Last 1\n\n$androidNDKs = @()\nforeach ($version in $ndkMajorVersions) {\n    $packageNamePrefix = \"ndk;$version\"\n    $package = $androidPackages | Where-Object { $_.StartsWith($packageNamePrefix) } | Sort-Object -Unique | Select-Object -Last 1\n    $androidNDKs += $package\n}\n\nWrite-Host \"Installing Android SDK packages for NDKs...\"\nInstall-AndroidSDKPackages $androidNDKs\n\n$ndkLatestVersion = ($androidNDKs | Where-Object { $_ -match \"ndk;$ndkLatestMajorVersion\" }).Split(';')[1]\n$ndkDefaultVersion = ($androidNDKs | Where-Object { $_ -match \"ndk;$ndkDefaultMajorVersion\" }).Split(';')[1]\n$ndkRoot = \"$SDKRootPath\\ndk\\$ndkDefaultVersion\"\n\n# Create env variables\n[Environment]::SetEnvironmentVariable(\"ANDROID_HOME\", $SDKRootPath, \"Machine\")\n[Environment]::SetEnvironmentVariable(\"ANDROID_SDK_ROOT\", $SDKRootPath, \"Machine\")\n# 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\n[Environment]::SetEnvironmentVariable(\"ANDROID_NDK\", $ndkRoot, \"Machine\")\n[Environment]::SetEnvironmentVariable(\"ANDROID_NDK_HOME\", $ndkRoot, \"Machine\")\n[Environment]::SetEnvironmentVariable(\"ANDROID_NDK_ROOT\", $ndkRoot, \"Machine\")\n\n$ndkLatestPath = \"$SDKRootPath\\ndk\\$ndkLatestVersion\"\nif (Test-Path $ndkLatestPath) {\n    [Environment]::SetEnvironmentVariable(\"ANDROID_NDK_LATEST_HOME\", $ndkLatestPath, \"Machine\")\n} else {\n    Write-Host \"Latest NDK $ndkLatestVersion is not installed at path $ndkLatestPath\"\n    exit 1\n}\n\nInvoke-PesterTests -TestFile \"Android\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Apache.ps1",
    "content": "################################################################################\n##  File:  Install-Apache.ps1\n##  Desc:  Install Apache HTTP Server\n################################################################################\n\n# Stop w3svc service\nStop-Service -Name w3svc\n\n# Install latest apache in chocolatey\n$installDir = \"C:\\tools\"\nInstall-ChocoPackage apache-httpd -ArgumentList \"--force\", \"--params\", \"/installLocation:$installDir /port:80\"\n\n# Stop and disable Apache service\nStop-Service -Name Apache\nSet-Service -Name Apache -StartupType Disabled\n\n# Start w3svc service\nStart-Service -Name w3svc\n\n# Invoke Pester Tests\nInvoke-PesterTests -TestFile \"Apache\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-AzureCli.ps1",
    "content": "################################################################################\n##  File:  Install-AzureCli.ps1\n##  Desc:  Install and warm-up Azure CLI\n################################################################################\n\nWrite-Host 'Install the latest Azure CLI release'\n\n$azureCliConfigPath = 'C:\\azureCli'\n# Store azure-cli cache outside of the provisioning user's profile\n[Environment]::SetEnvironmentVariable('AZURE_CONFIG_DIR', $azureCliConfigPath, \"Machine\")\n\n$azureCliExtensionPath = Join-Path $env:CommonProgramFiles 'AzureCliExtensionDirectory'\nNew-Item -ItemType 'Directory' -Path $azureCliExtensionPath | Out-Null\n[Environment]::SetEnvironmentVariable('AZURE_EXTENSION_DIR', $azureCliExtensionPath, \"Machine\")\n\nInstall-Binary -Type MSI `\n    -Url 'https://aka.ms/installazurecliwindowsx64' `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n\nUpdate-Environment\n\n# Warm-up Azure CLI\nWrite-Host \"Warmup 'az'\"\naz --help | Out-Null\nif ($LASTEXITCODE -ne 0) {\n    throw \"Command 'az --help' failed\"\n}\n\nInvoke-PesterTests -TestFile 'CLI.Tools' -TestName 'Azure CLI'\n"
  },
  {
    "path": "images/windows/scripts/build/Install-AzureCosmosDbEmulator.ps1",
    "content": "####################################################################################\n##  File:  Install-AzureCosmosDbEmulator.ps1\n##  Desc:  Install Azure CosmosDb Emulator\n####################################################################################\n\nInstall-Binary -Type MSI `\n    -Url \"https://aka.ms/cosmosdb-emulator\"\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"Azure Cosmos DB Emulator\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-AzureDevOpsCli.ps1",
    "content": "################################################################################\n##  File:  Install-AzureDevOpsCli.ps1\n##  Desc:  Install Azure DevOps CLI\n################################################################################\n\n$azureDevOpsCliConfigPath = 'C:\\azureDevOpsCli'\n# Store azure-devops-cli cache outside of the provisioning user's profile\n[Environment]::SetEnvironmentVariable('AZ_DEVOPS_GLOBAL_CONFIG_DIR', $azureDevOpsCliConfigPath, \"Machine\")\n\n$azureDevOpsCliCachePath = Join-Path $azureDevOpsCliConfigPath 'cache'\nNew-Item -ItemType 'Directory' -Path $azureDevOpsCliCachePath | Out-Null\n[Environment]::SetEnvironmentVariable('AZURE_DEVOPS_CACHE_DIR', $azureDevOpsCliCachePath, \"Machine\")\n\nUpdate-Environment\n\naz extension add -n azure-devops\nif ($LASTEXITCODE -ne 0) {\n    throw \"Command 'az extension add -n azure-devops' failed\"\n}\n\n# Warm-up Azure DevOps CLI\nWrite-Host \"Warmup 'az-devops'\"\n@('devops', 'pipelines', 'boards', 'repos', 'artifacts') | ForEach-Object {\n    az $_ --help\n    if ($LASTEXITCODE -ne 0) {\n        throw \"Command 'az $_ --help' failed\"\n    }\n}\n\n# calling az devops login to force it to install `keyring`. Login will actually fail, redirecting error to null\nWrite-Output 'fake token' | az devops login | Out-Null\n# calling az devops logout to be sure no credentials remain.\naz devops logout | out-null\n\nInvoke-PesterTests -TestFile 'CLI.Tools' -TestName 'Azure DevOps CLI'\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Bazel.ps1",
    "content": "################################################################################\n##  File:  Install-Bazel.ps1\n##  Desc:  Install Bazel and Bazelisk (A user-friendly launcher for Bazel)\n################################################################################\n\nInstall-ChocoPackage bazel\n\nnpm install -g @bazel/bazelisk\nif ($LASTEXITCODE -ne 0) {\n    throw \"Command 'npm install -g @bazel/bazelisk' failed\"\n}\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"Bazel\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Chocolatey.ps1",
    "content": "################################################################################\n##  File:  Install-Chocolatey.ps1\n##  Desc:  Install Chocolatey package manager\n################################################################################\n\nWrite-Host \"Set TLS1.2\"\n[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor \"Tls12\"\n\nWrite-Host \"Install chocolatey\"\n\n# Add to system PATH\nAdd-MachinePathItem 'C:\\ProgramData\\Chocolatey\\bin'\nUpdate-Environment\n\n# Verify and run choco installer\n$installScriptPath = Invoke-DownloadWithRetry 'https://chocolatey.org/install.ps1'\nTest-FileSignature -Path $installScriptPath -ExpectedSubject 'CN=\"Chocolatey Software, Inc\", O=\"Chocolatey Software, Inc\", L=Topeka, S=Kansas, C=US'\nInvoke-Expression $installScriptPath\n\n# Turn off confirmation\nchoco feature enable -n allowGlobalConfirmation\n\n# Initialize environmental variable ChocolateyToolsLocation by invoking choco Get-ToolsLocation function\nImport-Module \"$env:ChocolateyInstall\\helpers\\chocolateyInstaller.psm1\" -Force\nGet-ToolsLocation\n"
  },
  {
    "path": "images/windows/scripts/build/Install-ChocolateyPackages.ps1",
    "content": "################################################################################\n##  File:  Install-ChocolateyPackages.ps1\n##  Desc:  Install common Chocolatey packages\n################################################################################\n\n$commonPackages = (Get-ToolsetContent).choco.common_packages\n\nforeach ($package in $commonPackages) {\n    Install-ChocoPackage $package.name -Version $package.version -ArgumentList $package.args\n}\n\nInvoke-PesterTests -TestFile \"ChocoPackages\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Chrome.ps1",
    "content": "################################################################################\n##  File:  Install-Chrome.ps1\n##  Desc:  Install Google Chrome browser and Chrome WebDriver\n################################################################################\n\n# Download and install latest Chrome browser\nInstall-Binary `\n    -Url 'https://dl.google.com/tag/s/dl/chrome/install/googlechromestandaloneenterprise64.msi' `\n    -ExpectedSubject 'CN=Google LLC, O=Google LLC, L=Mountain View, S=California, C=US, SERIALNUMBER=3582691, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US'\n\n# Prepare firewall rules\nWrite-Host \"Adding the firewall rule for Google update blocking...\"\nNew-NetFirewallRule -DisplayName \"BlockGoogleUpdate\" -Direction Outbound -Action Block -Program \"C:\\Program Files (x86)\\Google\\Update\\GoogleUpdate.exe\"\n\n$googleServices = Get-Service -Name \"GoogleUpdater*\"\nStop-Service $googleServices\n$googleServices.WaitForStatus('Stopped', \"00:01:00\")\n$googleServices | Set-Service -StartupType Disabled\n\n$regGoogleUpdatePath = \"HKLM:\\SOFTWARE\\Policies\\Google\\Update\"\n$regGoogleUpdateChrome = \"HKLM:\\SOFTWARE\\Policies\\Google\\Chrome\"\n($regGoogleUpdatePath, $regGoogleUpdateChrome) | ForEach-Object {\n    New-Item -Path $_ -Force\n}\n\n$regGoogleParameters = @(\n    @{ Name = \"AutoUpdateCheckPeriodMinutes\"; Value = 00000000},\n    @{ Name = \"UpdateDefault\"; Value = 00000000 },\n    @{ Name = \"DisableAutoUpdateChecksCheckboxValue\"; Value = 00000001 },\n    @{ Name = \"Update{8A69D345-D564-463C-AFF1-A69D9E530F96}\"; Value = 00000000 },\n    @{ Path = $regGoogleUpdateChrome; Name = \"DefaultBrowserSettingEnabled\"; Value = 00000000 }\n)\n\n$regGoogleParameters | ForEach-Object {\n    $arguments = $_\n    if (-not ($arguments.Path)) {\n        $arguments.Add(\"Path\", $regGoogleUpdatePath)\n    }\n    $arguments.Add(\"Force\", $true)\n    New-ItemProperty @arguments\n}\n# Install Chrome WebDriver\nWrite-Host \"Install Chrome WebDriver...\"\n$chromeDriverPath = \"$($env:SystemDrive)\\SeleniumWebDrivers\\ChromeDriver\"\nif (-not (Test-Path -Path $chromeDriverPath)) {\n    New-Item -Path $chromeDriverPath -ItemType Directory -Force\n}\n\nWrite-Host \"Get the Chrome WebDriver download URL...\"\n$registryPath = \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\"\n$chromePath = (Get-ItemProperty \"$registryPath\\chrome.exe\").'(default)'\n[version] $chromeVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($chromePath).ProductVersion\n$chromeBuild = \"$($chromeVersion.Major).$($chromeVersion.Minor).$($chromeVersion.Build)\"\n$chromeDriverVersionsUrl = \"https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build-with-downloads.json\"\n\nWrite-Host \"Chrome version is $chromeVersion\"\n$chromeDriverVersions = Invoke-RestMethod -Uri $chromeDriverVersionsUrl\n$chromeDriverVersion = $chromeDriverVersions.builds.$chromeBuild\n\nif (-not ($chromeDriverVersion)) {\n    $availableVersions = $chromeDriverVersions.builds | Get-Member | Select-Object -ExpandProperty Name\n    Write-Host \"Available chromedriver builds are $availableVersions\"\n    throw \"Can't determine chromedriver version that matches chrome build $chromeBuild\"\n}\n\n$chromeDriverVersion.version | Out-File -FilePath \"$chromeDriverPath\\versioninfo.txt\" -Force;\n\nWrite-Host \"Chrome WebDriver version to install is $($chromeDriverVersion.version)\"\n$chromeDriverZipDownloadUrl = ($chromeDriverVersion.downloads.chromedriver | Where-Object platform -eq \"win64\").url\n\nWrite-Host \"Download Chrome WebDriver from $chromeDriverZipDownloadUrl...\"\n$chromeDriverArchPath = Invoke-DownloadWithRetry $chromeDriverZipDownloadUrl\n\nWrite-Host \"Expand Chrome WebDriver archive (without using directory names)...\"\nExpand-7ZipArchive -Path $chromeDriverArchPath -DestinationPath $chromeDriverPath -ExtractMethod \"e\"\n\nWrite-Host \"Setting the environment variables...\"\n[Environment]::SetEnvironmentVariable(\"ChromeWebDriver\", $chromeDriverPath, \"Machine\")\nAdd-MachinePathItem $chromeDriverPath\nUpdate-Environment\n\nInvoke-PesterTests -TestFile \"Browsers\" -TestName \"Chrome\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-CodeQLBundle.ps1",
    "content": "################################################################################\n##  File:  Install-CodeQLBundle.ps1\n##  Desc:  Install the CodeQL CLI Bundle to the toolcache.\n################################################################################\n\n# Retrieve the latest major version of the CodeQL Action to use in the base URL for downloading the bundle.\n$releases = Invoke-RestMethod -Uri \"https://api.github.com/repos/github/codeql-action/releases\"\n\n# Get the release tags starting with v[0-9] and sort them in descending order, then parse the first one to get the major version.\n$latestTag = $releases.tag_name |\n    Where-Object { $_ -match '^v[0-9]' } |\n    Sort-Object { [version]($_ -replace '^v','') } -Descending |\n    Select-Object -First 1\n\nif ([string]::IsNullOrEmpty($latestTag)) {\n    Write-Error \"Error: Unable to find the latest major version of the CodeQL Action.\"\n    exit 1\n}\nif ($latestTag -match '^v([0-9]+)') {\n    $codeqlActionLatestMajorVersion = $matches[1]\n} else {\n    Write-Error \"Error: Unable to parse the major version from the latest tag.\"\n    exit 1\n}\n\n# Retrieve the CLI version of the latest CodeQL bundle.\n$defaults = (Invoke-RestMethod \"https://raw.githubusercontent.com/github/codeql-action/v$($codeqlActionLatestMajorVersion)/src/defaults.json\")\n$cliVersion = $defaults.cliVersion\n$tagName = \"codeql-bundle-v\" + $cliVersion\n\nWrite-Host \"Downloading CodeQL bundle $($cliVersion)...\"\n# Note that this is the all-platforms CodeQL bundle, to support scenarios where customers run\n# different operating systems within containers.\n$codeQLBundlePath = Invoke-DownloadWithRetry \"https://github.com/github/codeql-action/releases/download/$($tagName)/codeql-bundle-win64.tar.gz\"\n$downloadDirectoryPath = (Get-Item $codeQLBundlePath).Directory.FullName\n\n$codeQLToolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY -ChildPath \"CodeQL\" | Join-Path -ChildPath $cliVersion | Join-Path -ChildPath \"x64\"\nNew-Item -Path $codeQLToolcachePath -ItemType Directory -Force | Out-Null\n\nWrite-Host \"Unpacking the downloaded CodeQL bundle archive...\"\nExpand-7ZipArchive -Path $codeQLBundlePath -DestinationPath $downloadDirectoryPath\n$unGzipedCodeQLBundlePath = Join-Path $downloadDirectoryPath \"codeql-bundle-win64.tar\"\nExpand-7ZipArchive -Path $unGzipedCodeQLBundlePath -DestinationPath $codeQLToolcachePath\n\nWrite-Host \"CodeQL bundle at $($codeQLToolcachePath) contains the following directories:\"\nGet-ChildItem -Path $codeQLToolcachePath -Depth 2\n\n# Touch a file to indicate to the CodeQL Action that this bundle shipped with the toolcache. This is\n# to support overriding the CodeQL version specified in defaults.json on GitHub Enterprise.\nNew-Item -ItemType file (Join-Path $codeQLToolcachePath -ChildPath \"pinned-version\")\n\n# Touch a file to indicate to the toolcache that setting up CodeQL is complete.\nNew-Item -ItemType file \"$codeQLToolcachePath.complete\"\n\n# Test that the tools have been extracted successfully.\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"CodeQL Bundle\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-DACFx.ps1",
    "content": "####################################################################################\n##  File:  Install-DACFx.ps1\n##  Desc:  Install SQL Server® Data-Tier Application Framework (DacFx) for Windows\n####################################################################################\n\nInstall-Binary -Type MSI `\n    -Url 'https://aka.ms/dacfx-msi' `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"DACFx\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Docker.ps1",
    "content": "################################################################################\n##  File:  Install-Docker.ps1\n##  Desc:  Install Docker.\n##         Must be an independent step because it requires a restart before we\n##         can continue.\n################################################################################\n\nWrite-Host \"Get latest Moby release\"\n$toolsetVersion = (Get-ToolsetContent).docker.components.docker\n$mobyVersion = (Get-GithubReleasesByVersion -Repo \"moby/moby\" -Version \"${toolsetVersion}\").version\n$dockerceUrl = \"https://download.docker.com/win/static/stable/x86_64/\"\n$dockerceBinaries = Invoke-WebRequest -Uri $dockerceUrl -UseBasicParsing\n\nWrite-Host \"Check Moby version $mobyVersion\"\n$mobyRelease = $dockerceBinaries.Links.href -match \"${mobyVersion}\\.zip\" | Select-Object -Last 1\nif (-not $mobyRelease) {\n    Write-Host \"Release not found for $mobyLatestRelease version\"\n    $versions = [regex]::Matches($dockerceBinaries.Links.href, \"docker-(\\d+\\.\\d+\\.\\d+)\\.zip\") | Sort-Object { [version] $_.Groups[1].Value }\n    $mobyRelease = $versions | Select-Object -ExpandProperty Value -Last 1\n    Write-Host \"Found $mobyRelease\"\n}\n$mobyReleaseUrl = $dockerceUrl + $mobyRelease\n\nWrite-Host \"Install Moby $mobyRelease...\"\n$mobyArchivePath = Invoke-DownloadWithRetry $mobyReleaseUrl\nExpand-Archive -Path $mobyArchivePath -DestinationPath $env:TEMP_DIR\n$dockerPath = \"$env:TEMP_DIR\\docker\\docker.exe\"\n$dockerdPath = \"$env:TEMP_DIR\\docker\\dockerd.exe\"\n\nWrite-Host \"Install Docker CE\"\n$instScriptUrl = \"https://raw.githubusercontent.com/microsoft/Windows-Containers/Main/helpful_tools/Install-DockerCE/install-docker-ce.ps1\"\n$instScriptPath = Invoke-DownloadWithRetry $instScriptUrl\n& $instScriptPath -DockerPath $dockerPath -DockerDPath $dockerdPath\nif ($LastExitCode -ne 0) {\n    Write-Host \"Docker installation failed with exit code $LastExitCode\"\n    exit $exitCode\n}\n\n# Fix AZ CLI DOCKER_COMMAND_ERROR\n# cli.azure.cli.command_modules.acr.custom: Could not run 'docker.exe' command.\n# https://github.com/Azure/azure-cli/issues/18766\nNew-Item -ItemType SymbolicLink -Path \"C:\\Windows\\SysWOW64\\docker.exe\" -Target \"C:\\Windows\\System32\\docker.exe\"\n\nif (-not (Test-IsWin25)) {\n    Write-Host \"Download docker images\"\n    $dockerImages = (Get-ToolsetContent).docker.images\n    foreach ($dockerImage in $dockerImages) {\n        Write-Host \"Pulling docker image $dockerImage ...\"\n        docker pull $dockerImage\n\n        if (!$?) {\n            throw \"Docker pull failed with a non-zero exit code ($LastExitCode)\"\n        }\n    }\n    Invoke-PesterTests -TestFile \"Docker\" -TestName \"DockerImages\"\n}\n\nInvoke-PesterTests -TestFile \"Docker\" -TestName \"Docker\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-DockerCompose.ps1",
    "content": "################################################################################\n##  File:  Install-Docker-Compose.ps1\n##  Desc:  Install Docker Compose.\n################################################################################\nWrite-Host \"Install-Package Docker-Compose v2\"\n$toolsetVersion = (Get-ToolsetContent).docker.components.compose\n$composeVersion = (Get-GithubReleasesByVersion -Repo \"docker/compose\" -Version \"${toolsetVersion}\").version\n$dockerComposev2Url = \"https://github.com/docker/compose/releases/download/v${composeVersion}/docker-compose-windows-x86_64.exe\"\n$cliPluginsDir = \"C:\\ProgramData\\docker\\cli-plugins\"\nNew-Item -Path $cliPluginsDir -ItemType Directory\nInvoke-DownloadWithRetry -Url $dockerComposev2Url -Path \"$cliPluginsDir\\docker-compose.exe\"\n\nInvoke-PesterTests -TestFile \"Docker\" -TestName \"DockerCompose\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-DockerWinCred.ps1",
    "content": "################################################################################\n##  File:  Install-Docker-WinCred.ps1\n##  Desc:  Install Docker credential helper.\n##  Supply chain security: checksum validation\n################################################################################\n\nWrite-Host \"Install docker-wincred\"\n$downloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"docker/docker-credential-helpers\" `\n    -Version \"latest\" `\n    -UrlMatchPattern \"docker-credential-wincred-*amd64.exe\"\n$binaryPath = Invoke-DownloadWithRetry -Url $downloadUrl -Path \"C:\\Windows\\System32\\docker-credential-wincred.exe\"\n\n#region Supply chain security\n$binaryName = Split-Path $downloadUrl -Leaf\n$externalHash = Get-ChecksumFromUrl -Type \"SHA256\" `\n    -Url ($downloadUrl -replace $binaryName, \"checksums.txt\") `\n    -FileName $binaryName\nTest-FileChecksum -Path $binaryPath -ExpectedSHA256Sum $externalHash\n#endregion\n\nInvoke-PesterTests -TestFile \"Docker\" -TestName \"DockerWinCred\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-DotnetSDK.ps1",
    "content": "################################################################################\n##  File:  Install-DotnetSDK.ps1\n##  Desc:  Install all released versions of the dotnet sdk and populate package\n##         cache. Should run after VS and Node\n##  Supply chain security: checksum validation\n################################################################################\n\n# Set environment variables\n[Environment]::SetEnvironmentVariable(\"DOTNET_MULTILEVEL_LOOKUP\", \"0\", \"Machine\")\n[Environment]::SetEnvironmentVariable(\"DOTNET_NOLOGO\", \"1\", \"Machine\")\n[Environment]::SetEnvironmentVariable(\"DOTNET_SKIP_FIRST_TIME_EXPERIENCE\", \"1\", \"Machine\")\n\n[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor \"Tls12\"\n\n#region \"Functions\"\nfunction Get-SDKVersionsToInstall {\n    param (\n        [Parameter(Mandatory)]\n        [string] $DotnetVersion\n    )\n    $releasesJsonUri = \"https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/${DotnetVersion}/releases.json\"\n    $releasesData = (Invoke-DownloadWithRetry $releasesJsonUri) | Get-Item | Get-Content | ConvertFrom-Json\n    # filtering out the preview/rc releases\n    $releases = $releasesData.'releases' | Where-Object { !$_.'release-version'.Contains('-') }\n\n    $sdks = @()\n    foreach ($release in $releases) {\n        $sdks += $release.'sdk'\n        $sdks += $release.'sdks'\n    }\n\n    return $sdks.version `\n    | Sort-Object { [Version] $_ } -Unique `\n    | Group-Object { $_.Substring(0, $_.LastIndexOf('.') + 2) } `\n    | ForEach-Object { $_.Group[-1] }\n}\n\nfunction Invoke-DotnetWarmup {\n    param (\n        [Parameter(Mandatory)]\n        [string] $SDKVersion\n    )\n    # warm up dotnet for first time experience\n    $projectTypes = @('console', 'mstest', 'web', 'mvc', 'webapi')\n    foreach ($template in $projectTypes) {\n        $projectPath = Join-Path -Path \"C:\\temp\" -ChildPath $template\n        New-Item -Path $projectPath -Force -ItemType Directory\n        Push-Location -Path $projectPath\n        & \"$env:ProgramFiles\\dotnet\\dotnet.exe\" new globaljson --sdk-version \"$SDKVersion\"\n        if ($LastExitCode -ne 0) {\n            throw \"Dotnet new globaljson failed with exit code $LastExitCode\"\n        }\n        & \"$env:ProgramFiles\\dotnet\\dotnet.exe\" new $template\n        if ($LastExitCode -ne 0) {\n            throw \"Dotnet new $template failed with exit code $LastExitCode\"\n        }\n        Pop-Location\n        Remove-Item $projectPath -Force -Recurse\n    }\n}\n\nfunction Install-DotnetSDK {\n    param (\n        [Parameter(Mandatory)]\n        [string] $InstallScriptPath,\n        [Parameter(Mandatory)]\n        [Alias('Version')]\n        [string] $SDKVersion,\n        [Parameter(Mandatory)]\n        [string] $DotnetVersion\n    )\n\n    if (Test-Path -Path \"C:\\Program Files\\dotnet\\sdk\\$SDKVersion\") {\n        Write-Host \"Sdk version $SDKVersion already installed\"\n        return\n    }\n\n    Write-Host \"Installing dotnet $SDKVersion\"\n    $zipPath = Join-Path ([IO.Path]::GetTempPath()) ([IO.Path]::GetRandomFileName())\n    & $InstallScriptPath -Version $SDKVersion -InstallDir $(Join-Path -Path $env:ProgramFiles -ChildPath 'dotnet') -ZipPath $zipPath -KeepZip\n    # Installer is PowerShell script that doesn't set exit code on failure\n    # If installation failed, tests will fail anyway\n\n    #region Supply chain security\n    $releasesJsonUri = \"https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/${DotnetVersion}/releases.json\"\n    $releasesData = (Invoke-DownloadWithRetry $releasesJsonUri) | Get-Item | Get-Content | ConvertFrom-Json\n    $distributorFileHash = $releasesData.releases.sdks.Where({ $_.version -eq $SDKVersion }).files.Where({ $_.name -eq 'dotnet-sdk-win-x64.zip' }).hash\n    Test-FileChecksum $zipPath -ExpectedSHA512Sum $distributorFileHash\n    #endregion\n}\n#endregion\n\n$dotnetToolset = (Get-ToolsetContent).dotnet\n\n# Download installation script.\n$installScriptPath = Invoke-DownloadWithRetry -Url \"https://dot.net/v1/dotnet-install.ps1\"\n\n# Visual Studio 2022 pre-creates sdk-manifests/8.0.100 folder, causing dotnet-install to skip manifests creation\n# https://github.com/actions/runner-images/issues/11402\nif ((Test-IsWin22) -or (Test-IsWin25)) {\n    $sdkManifestPath = \"C:\\Program Files\\dotnet\\sdk-manifests\\8.0.100\"\n    if (Test-Path $sdkManifestPath) {\n        Move-Item -Path $sdkManifestPath -Destination $env:TEMP_DIR -ErrorAction Stop\n    }\n}\n\n# Install and warm up dotnet\nforeach ($dotnetVersion in $dotnetToolset.versions) {\n    $sdkVersionsToInstall = Get-SDKVersionsToInstall -DotnetVersion $dotnetVersion\n\n    # Issue https://github.com/actions/runner-images/issues/13705\n    # Workaround for broken .NET SDK 10.0.103 - replace it with .NET SDK 10.0.102\n    $sdkVersionsToInstall = $sdkVersionsToInstall | ForEach-Object { if ($_ -eq \"10.0.103\") { Write-Host \".NET 10.0.103 detected, replacing with 10.0.102\"; \"10.0.102\" } else { $_ } }\n\n    foreach ($sdkVersion in $sdkVersionsToInstall) {\n        Install-DotnetSDK -InstallScriptPath $installScriptPath -SDKVersion $sdkVersion -DotnetVersion $dotnetVersion\n        if ($dotnetToolset.warmup) {\n            Invoke-DotnetWarmup -SDKVersion $sdkVersion\n        }\n    }\n}\n\n# Replace manifests inside sdk-manifests/8.0.100 folder with ones from Visual Studio\n# https://github.com/actions/runner-images/issues/11402\nif ((Test-IsWin22) -or (Test-IsWin25)) {\n    if (Test-Path \"${env:TEMP_DIR}\\8.0.100\") {\n        Get-ChildItem -Path \"${env:TEMP_DIR}\\8.0.100\" | ForEach-Object {\n            Remove-Item -Path \"$sdkManifestPath\\$($_.BaseName)\" -Recurse -Force | Out-Null\n            Move-Item -Path $_.FullName -Destination $sdkManifestPath -Force -ErrorAction Stop\n        }\n    }\n}\n\n# Add dotnet to PATH\nAdd-MachinePathItem \"C:\\Program Files\\dotnet\"\n\n# Remove NuGet Folder\n$nugetPath = \"$env:APPDATA\\NuGet\"\nif (Test-Path $nugetPath) {\n    Remove-Item -Path $nugetPath -Force -Recurse\n}\n\n# Generate and copy new NuGet.Config config\ndotnet nuget list source | Out-Null\nif ($LastExitCode -ne 0) {\n    throw \"Dotnet nuget list source failed with exit code $LastExitCode\"\n}\nCopy-Item -Path $nugetPath -Destination \"C:\\Users\\Default\\AppData\\Roaming\" -Force -Recurse\n\n# Install dotnet tools\nWrite-Host \"Installing dotnet tools\"\nAdd-DefaultPathItem \"%USERPROFILE%\\.dotnet\\tools\"\nforeach ($dotnetTool in $dotnetToolset.tools) {\n    dotnet tool install $($dotnetTool.name) --tool-path \"C:\\Users\\Default\\.dotnet\\tools\" --add-source \"https://api.nuget.org/v3/index.json\" | Out-Null\n    if ($LastExitCode -ne 0) {\n        throw \"Dotnet tool install failed with exit code $LastExitCode\"\n    }\n}\n\nInvoke-PesterTests -TestFile \"DotnetSDK\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-EdgeDriver.ps1",
    "content": "################################################################################\n##  File:  Install-EdgeDriver.ps1\n##  Desc:  Install Edge WebDriver and configure Microsoft Edge\n################################################################################\n\n# Disable Edge auto-updates\nRename-Item -Path \"C:\\Program Files (x86)\\Microsoft\\EdgeUpdate\\MicrosoftEdgeUpdate.exe\" -NewName \"Disabled_MicrosoftEdgeUpdate.exe\" -ErrorAction Stop\n\nWrite-Host \"Get the Microsoft Edge WebDriver version...\"\n$edgeBinaryPath = (Get-ItemProperty \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\msedge.exe\").'(default)'\n[version] $edgeVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($edgeBinaryPath).ProductVersion\n\n$edgeDriverPath = \"$($env:SystemDrive)\\SeleniumWebDrivers\\EdgeDriver\"\nif (-not (Test-Path -Path $edgeDriverPath)) {\n    New-Item -Path $edgeDriverPath -ItemType Directory -Force\n}\n\n$versionInfoUrl = \"https://msedgedriver.microsoft.com/LATEST_RELEASE_$($edgeVersion.Major)_WINDOWS\"\n$versionInfoFile = Invoke-DownloadWithRetry -Url $versionInfoUrl -Path \"$edgeDriverPath\\versioninfo.txt\"\n$latestVersion = Get-Content -Path $versionInfoFile\n\nWrite-Host \"Download Microsoft Edge WebDriver...\"\n$downloadUrl = \"https://msedgedriver.microsoft.com/$latestVersion/edgedriver_win64.zip\"\n$archivePath = Invoke-DownloadWithRetry $downloadUrl\n\nWrite-Host \"Expand Microsoft Edge WebDriver archive...\"\nExpand-7ZipArchive -Path $archivePath -DestinationPath $edgeDriverPath\n\n#Validate the EdgeDriver signature\nTest-FileSignature -Path \"$edgeDriverPath\\msedgedriver.exe\" -ExpectedSubject $(Get-MicrosoftPublisher)\n\nWrite-Host \"Setting the environment variables...\"\n[Environment]::SetEnvironmentVariable(\"EdgeWebDriver\", $EdgeDriverPath, \"Machine\")\nAdd-MachinePathItem \"$edgeDriverPath\\\"\n\nInvoke-PesterTests -TestFile \"Browsers\" -TestName \"Edge\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Firefox.ps1",
    "content": "################################################################################\n##  File:  Install-Firefox.ps1\n##  Desc:  Install Mozilla Firefox browser and Gecko WebDriver\n##  Supply chain security: Firefox browser - checksum validation\n################################################################################\n\n# Install and configure Firefox browser\nWrite-Host \"Get the latest Firefox version...\"\n$versionsManifest = Invoke-RestMethod \"https://product-details.mozilla.org/1.0/firefox_versions.json\"\n\nWrite-Host \"Install Firefox browser...\"\n$installerUrl = \"https://download.mozilla.org/?product=firefox-$($versionsManifest.LATEST_FIREFOX_VERSION)&os=win64&lang=en-US\"\n$hashUrl = \"https://archive.mozilla.org/pub/firefox/releases/$($versionsManifest.LATEST_FIREFOX_VERSION)/SHA256SUMS\"\n\n$externalHash = Get-ChecksumFromUrl -Type \"SHA256\" `\n    -Url $hashUrl `\n    -FileName \"win64/en-US/Firefox Setup*exe\"\n\nInstall-Binary -Type EXE `\n    -Url $installerUrl `\n    -InstallArgs @(\"/silent\", \"/install\") `\n    -ExpectedSHA256Sum $externalHash\n\nWrite-Host \"Disable autoupdate...\"\n$firefoxDirectoryPath = Join-Path $env:ProgramFiles \"Mozilla Firefox\"\nNew-Item -path $firefoxDirectoryPath -Name 'mozilla.cfg' -Value '//\npref(\"browser.shell.checkDefaultBrowser\", false);\npref(\"app.update.enabled\", false);' -ItemType file -force\n\n$firefoxPreferencesFolder = Join-Path $firefoxDirectoryPath \"defaults\\pref\"\nNew-Item -path $firefoxPreferencesFolder -Name 'local-settings.js' -Value 'pref(\"general.config.obscure_value\", 0);\npref(\"general.config.filename\", \"mozilla.cfg\");' -ItemType file -force\n\n# Download and install Gecko WebDriver\nWrite-Host \"Install Gecko WebDriver...\"\n$geckoDriverPath = \"$($env:SystemDrive)\\SeleniumWebDrivers\\GeckoDriver\"\nif (-not (Test-Path -Path $geckoDriverPath)) {\n    New-Item -Path $geckoDriverPath -ItemType Directory -Force\n}\n\nWrite-Host \"Get the Gecko WebDriver version...\"\n$geckoDriverVersion = (Get-GithubReleasesByVersion -Repo \"mozilla/geckodriver\" -Version \"latest\").version\n$geckoDriverVersion | Out-File -FilePath \"$geckoDriverPath\\versioninfo.txt\" -Force\n\nWrite-Host \"Download Gecko WebDriver WebDriver...\"\n$geckoDriverDownloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"mozilla/geckodriver\" `\n    -Version $geckoDriverVersion `\n    -UrlMatchPattern \"geckodriver-*-win64.zip\"\n$geckoDriverArchPath = Invoke-DownloadWithRetry $geckoDriverDownloadUrl\n\nWrite-Host \"Expand Gecko WebDriver archive...\"\nExpand-7ZipArchive -Path $geckoDriverArchPath -DestinationPath $geckoDriverPath\n\n# Validate Gecko WebDriver signature\nTest-FileSignature -Path \"$geckoDriverPath/geckodriver.exe\" -ExpectedSubject 'CN=Mozilla Corporation, OU=Firefox Engineering Operations, O=Mozilla Corporation, L=San Francisco, S=California, C=US'\n\nWrite-Host \"Setting the environment variables...\"\nAdd-MachinePathItem -PathItem $geckoDriverPath\n[Environment]::SetEnvironmentVariable(\"GeckoWebDriver\", $geckoDriverPath, \"Machine\")\n\nInvoke-PesterTests -TestFile \"Browsers\" -TestName \"Firefox\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Git.ps1",
    "content": "################################################################################\n##  File:  Install-Git.ps1\n##  Desc:  Install Git for Windows\n##  Supply chain security: Git - checksum validation, Hub CLI - managed by package manager\n################################################################################\n\n# Install the latest version of Git for Windows\n\n$downloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"git-for-windows/git\" `\n    -Version \"latest\" `\n    -UrlMatchPattern \"Git-*-64-bit.exe\"\n\n$externalHash = Get-ChecksumFromGithubRelease `\n    -Repo \"git-for-windows/git\" `\n    -Version \"latest\" `\n    -FileName (Split-Path $downloadUrl -Leaf) `\n    -HashType \"SHA256\"\n\nInstall-Binary `\n    -Url $downloadUrl `\n    -InstallArgs @(`\n        \"/VERYSILENT\", `\n        \"/NORESTART\", `\n        \"/NOCANCEL\", `\n        \"/SP-\", `\n        \"/CLOSEAPPLICATIONS\", `\n        \"/RESTARTAPPLICATIONS\", `\n        \"/o:PathOption=CmdTools\", `\n        \"/o:BashTerminalOption=ConHost\", `\n        \"/o:EnableSymlinks=Enabled\", `\n        \"/COMPONENTS=gitlfs\") `\n    -ExpectedSHA256Sum $externalHash\n\nUpdate-Environment\n\ngit config --system --add safe.directory \"*\"\nif ($LASTEXITCODE -ne 0) {\n    Write-Error \"Failed to configure safe.directory for Git with exit code $LASTEXITCODE\"\n}\n\n# Disable GCM machine-wide\n[Environment]::SetEnvironmentVariable(\"GCM_INTERACTIVE\", \"Never\", \"Machine\")\n\n# Add to PATH\nAdd-MachinePathItem \"C:\\Program Files\\Git\\bin\"\n\n# Add well-known SSH host keys to ssh_known_hosts\n# Write content to the file used by OpenSSH and the version includes with Git\n# for Windows\n$windowsSSHKnownHosts = \"C:\\ProgramData\\ssh\\ssh_known_hosts\"\n$gitSSHKnownHosts = \"C:\\Program Files\\Git\\etc\\ssh\\ssh_known_hosts\"\n\n$sshKeyScan = \"C:\\Program Files\\Git\\usr\\bin\\ssh-keyscan\"\n$githubHostKeys = &$sshKeyScan -t rsa,ecdsa,ed25519 github.com\n$azureHostKeys = &$sshKeyscan -t rsa ssh.dev.azure.com\n\nSet-Content -Encoding ASCII -Path $windowsSSHKnownHosts, $gitSSHKnownHosts -Value $githubHostKeys\nAdd-Content -Encoding ASCII -Path $windowsSSHKnownHosts, $gitSSHKnownHosts -Value $azureHostKeys\n\nInvoke-PesterTests -TestFile \"Git\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-GitHub-CLI.ps1",
    "content": "################################################################################\n##  File:  Install-GitHub-CLI.ps1\n##  Desc:  Install GitHub CLI\n##  Supply chain security: GitHub CLI - checksum validation\n################################################################################\n\nWrite-Host \"Get the latest gh version...\"\n\n$downloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"cli/cli\" `\n    -Version \"latest\" `\n    -UrlMatchPattern \"gh_*_windows_amd64.msi\"\n\n$checksumsUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"cli/cli\" `\n    -Version \"latest\" `\n    -UrlMatchPattern \"gh_*_checksums.txt\"\n$externalHash = Get-ChecksumFromUrl -Type \"SHA256\" `\n    -Url $checksumsUrl `\n    -FileName (Split-Path $downloadUrl -Leaf)\n\nInstall-Binary `\n    -Url $downloadUrl `\n    -ExpectedSHA256Sum $externalHash\n\nAdd-MachinePathItem \"C:\\Program Files (x86)\\GitHub CLI\"\n\nInvoke-PesterTests -TestFile \"CLI.Tools\" -TestName \"GitHub CLI\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Haskell.ps1",
    "content": "################################################################################\n##  File:  Install-Haskell.ps1\n##  Desc:  Install Haskell for Windows\n################################################################################\n\n# install minimal ghcup, utilizing pre-installed msys2 at C:\\msys64\nWrite-Host 'Installing ghcup...'\n$msysPath = \"C:\\msys64\"\n$ghcupPrefix = \"C:\\\"\n$cabalDir = \"C:\\cabal\"\n\n$ghcupDownloadURL = \"https://downloads.haskell.org/~ghcup/x86_64-mingw64-ghcup.exe\"\n\n# If you want to install a specific version of ghcup, uncomment the following lines\n# $ghver = \"0.1.19.4\"\n# $ghcupDownloadURL = \"https://downloads.haskell.org/~ghcup/${ghver}/x86_64-mingw64-ghcup-${ghver}.exe\"\n\n# Other option is to download ghcup from GitHub releases:\n# https://github.com/haskell/ghcup-hs/releases/latest\n\nNew-Item -Path \"$ghcupPrefix\\ghcup\" -ItemType 'directory' -ErrorAction SilentlyContinue | Out-Null\nNew-Item -Path \"$ghcupPrefix\\ghcup\\bin\" -ItemType 'directory' -ErrorAction SilentlyContinue | Out-Null\nInvoke-DownloadWithRetry -Url $ghcupDownloadURL -Path \"$ghcupPrefix\\ghcup\\bin\\ghcup.exe\"\n\n[Environment]::SetEnvironmentVariable(\"GHCUP_INSTALL_BASE_PREFIX\", $ghcupPrefix, \"Machine\")\n[Environment]::SetEnvironmentVariable(\"GHCUP_MSYS2\", $msysPath, \"Machine\")\n[Environment]::SetEnvironmentVariable(\"CABAL_DIR\", $cabalDir, \"Machine\")\nAdd-MachinePathItem \"$ghcupPrefix\\ghcup\\bin\"\nAdd-MachinePathItem \"$cabalDir\\bin\"\nUpdate-Environment\n\n# Get 1 or 3 latest versions of GHC depending on the OS version\nIf (Test-IsWin25) {\n    $numberOfVersions = 1\n} else {\n    $numberOfVersions = 3\n}\n$versions = ghcup list -t ghc -r | Where-Object { $_ -notlike \"prerelease\" }\n$versionsOutput = [version[]]($versions | ForEach-Object { $_.Split(' ')[1]; })\n$latestMajorMinor = $versionsOutput | Group-Object { $_.ToString(2) } | Sort-Object { [Version] $_.Name } | Select-Object -last $numberOfVersions\n$versionsList = $latestMajorMinor | ForEach-Object { $_.Group | Select-Object -Last 1 } | Sort-Object\n\n# The latest version will be installed as a default\nforeach ($version in $versionsList) {\n    Write-Host \"Installing ghc $version...\"\n    ghcup install ghc $version\n    if ($LastExitCode -ne 0) {\n        throw \"GHC installation failed with exit code $LastExitCode\"\n    }\n    ghcup set ghc $version\n    if ($LastExitCode -ne 0) {\n        throw \"Setting GHC version failed with exit code $LastExitCode\"\n    }\n}\n\n# Add default version of GHC to path\n$defaultGhcVersion = $versionsList | Select-Object -Last 1\nghcup set ghc $defaultGhcVersion\nif ($LastExitCode -ne 0) {\n    throw \"Setting default GHC version failed with exit code $LastExitCode\"\n}\n\nWrite-Host 'Installing cabal...'\nghcup install cabal latest\nif ($LastExitCode -ne 0) {\n    throw \"Cabal installation failed with exit code $LastExitCode\"\n}\n\nInvoke-PesterTests -TestFile 'Haskell'\n"
  },
  {
    "path": "images/windows/scripts/build/Install-IEWebDriver.ps1",
    "content": "################################################################################\n##  File:  Install-IEWebDriver.ps1\n##  Desc:  Install IE Web Driver\n################################################################################\n\n$seleniumMajorVersion = (Get-ToolsetContent).selenium.version\n$ieDriverUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"SeleniumHQ/selenium\" `\n    -Version \"$seleniumMajorVersion.*\" `\n    -Asset \"IEDriverServer_x64_*.zip\"\n\n# Download IE selenium driver\nWrite-Host \"Selenium IEDriverServer download and install...\"\n$driverZipFile = Invoke-DownloadWithRetry $ieDriverUrl\n\n$ieDriverPath = \"C:\\SeleniumWebDrivers\\IEDriver\"\nif (-not (Test-Path -Path $ieDriverPath)) {\n    New-Item -Path $ieDriverPath -ItemType Directory -Force | Out-Null\n}\n\nExpand-7ZipArchive -Path $driverZipFile -DestinationPath $ieDriverPath\nRemove-Item $driverZipFile\n\nWrite-Host \"Get the IEDriver version...\"\n(Get-Item \"$ieDriverPath\\IEDriverServer.exe\").VersionInfo.FileVersion | Out-File -FilePath \"$ieDriverPath\\versioninfo.txt\"\n\nWrite-Host \"Setting the IEWebDriver environment variables\"\n[Environment]::SetEnvironmentVariable(\"IEWebDriver\", $ieDriverPath, \"Machine\")\n\nInvoke-PesterTests -TestFile \"Browsers\" -TestName \"Internet Explorer\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-JavaTools.ps1",
    "content": "################################################################################\n##  File:  Install-JavaTools.ps1\n##  Desc:  Install various JDKs and java tools\n##  Supply chain security: JDK - checksum validation\n################################################################################\n\nfunction Set-JavaPath {\n    param (\n        [string] $Version,\n        [string] $Architecture = \"x64\",\n        [switch] $Default\n    )\n\n    $javaPathPattern = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath \"Java_Temurin-Hotspot_jdk/${Version}*/${Architecture}\"\n    $javaPath = (Get-Item -Path $javaPathPattern).FullName\n\n    if ([string]::IsNullOrEmpty($javaPath)) {\n        Write-Host \"Not found path to Java '${Version}'\"\n        exit 1\n    }\n\n    Write-Host \"Set 'JAVA_HOME_${Version}_X64' environmental variable as $javaPath\"\n    [Environment]::SetEnvironmentVariable(\"JAVA_HOME_${Version}_X64\", $javaPath, \"Machine\")\n\n    if ($Default) {\n        # Clean up any other Java folders from PATH to make sure that they won't conflict with each other\n        $currentPath = [System.Environment]::GetEnvironmentVariable(\"PATH\", \"Machine\")\n\n        $pathSegments = $currentPath.Split(';')\n        $newPathSegments = @()\n\n        foreach ($pathSegment in $pathSegments) {\n            if ($pathSegment -notlike '*java*') {\n                $newPathSegments += $pathSegment\n            }\n        }\n\n        $newPath = [string]::Join(';', $newPathSegments)\n        $newPath = $javaPath + '\\bin;' + $newPath\n\n        Write-Host \"Add $javaPath\\bin to PATH\"\n        [Environment]::SetEnvironmentVariable(\"PATH\", $newPath, \"Machine\")\n\n        Write-Host \"Set JAVA_HOME environmental variable as $javaPath\"\n        [Environment]::SetEnvironmentVariable(\"JAVA_HOME\", $javaPath, \"Machine\")\n    }\n}\n\nfunction Install-JavaJDK {\n    param(\n        [string] $JDKVersion,\n        [string] $Architecture = \"x64\"\n    )\n\n    # Get Java version from api\n    $assetUrl = Invoke-RestMethod -Uri \"https://api.adoptium.net/v3/assets/latest/${JDKVersion}/hotspot?architecture=${Architecture}&os=windows\" -Headers @{\"Accept\" = \"application/json\"}\n\n    $asset = $assetUrl | Where-Object {\n        $_.binary.os -eq \"windows\" `\n            -and $_.binary.architecture -eq $Architecture `\n            -and $_.binary.image_type -eq \"jdk\"\n    }\n\n    # Download and extract java binaries to temporary folder\n    $downloadUrl = $asset.binary.package.link\n    $archivePath = Invoke-DownloadWithRetry $downloadUrl\n    Test-FileChecksum $archivePath -ExpectedSHA256Sum $asset.binary.package.checksum\n\n    # We have to replace '+' sign in the version to '-' due to the issue with incorrect path in Android builds https://github.com/actions/runner-images/issues/3014\n    $fullJavaVersion = $asset.version.semver -replace '\\+', '-'\n    # Remove 'LTS' suffix from the version if present\n    $fullJavaVersion = $fullJavaVersion -replace '\\.LTS$', ''\n    # Create directories in toolcache path\n    $javaToolcachePath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath \"Java_Temurin-Hotspot_jdk\"\n    $javaVersionPath = Join-Path -Path $javaToolcachePath -ChildPath $fullJavaVersion\n    $javaArchPath = Join-Path -Path $javaVersionPath -ChildPath $Architecture\n\n    if (-not (Test-Path $javaToolcachePath)) {\n        Write-Host \"Creating Temurin-Hotspot toolcache folder\"\n        New-Item -ItemType Directory -Path $javaToolcachePath | Out-Null\n    }\n\n    Write-Host \"Creating Java '${fullJavaVersion}' folder in '${javaVersionPath}'\"\n    New-Item -ItemType Directory -Path $javaVersionPath -Force | Out-Null\n\n    # Complete the installation by extracting Java binaries to toolcache and creating the complete file\n    Expand-7ZipArchive -Path $archivePath -DestinationPath $javaVersionPath\n    Invoke-ScriptBlockWithRetry -Command {\n        Get-ChildItem -Path $javaVersionPath | Rename-Item -NewName $javaArchPath -ErrorAction Stop\n    }\n    New-Item -ItemType File -Path $javaVersionPath -Name \"$Architecture.complete\" | Out-Null\n}\n\n$toolsetJava = (Get-ToolsetContent).java\n$defaultVersion = $toolsetJava.default\n$jdkVersionsToInstall = $toolsetJava.versions\n\nforeach ($jdkVersionToInstall in $jdkVersionsToInstall) {\n    $isDefaultVersion = $jdkVersionToInstall -eq $defaultVersion\n\n    Install-JavaJDK -JDKVersion $jdkVersionToInstall\n\n    if ($isDefaultVersion) {\n        Set-JavaPath -Version $jdkVersionToInstall -Default\n    } else {\n        Set-JavaPath -Version $jdkVersionToInstall\n    }\n}\n\n# Install Java tools\n# Force chocolatey to ignore dependencies on Ant and Maven or else they will download the Oracle JDK\nInstall-ChocoPackage ant -ArgumentList \"--ignore-dependencies\"\n$toolsetMavenVersion = (Get-ToolsetContent).maven.version\n$versionToInstall = Resolve-ChocoPackageVersion -PackageName \"maven\" -TargetVersion $toolsetMavenVersion\n\nInstall-ChocoPackage maven -ArgumentList \"--version=$versionToInstall\"\nInstall-ChocoPackage gradle\n\n# Add maven env variables to Machine\n[string] $m2Path = ([Environment]::GetEnvironmentVariable(\"PATH\", \"Machine\")).Split(\";\") -match \"maven\"\n\n$m2RepoPath = 'C:\\ProgramData\\m2'\nNew-Item -Path $m2RepoPath -ItemType Directory -Force | Out-Null\n\n[Environment]::SetEnvironmentVariable(\"M2\", $m2Path, \"Machine\")\n[Environment]::SetEnvironmentVariable(\"M2_REPO\", $m2RepoPath, \"Machine\")\n[Environment]::SetEnvironmentVariable(\"MAVEN_OPTS\", \"-Xms256m\", \"Machine\")\n\n# Download cobertura jars\n$uri = 'https://repo1.maven.org/maven2/net/sourceforge/cobertura/cobertura/2.1.1/cobertura-2.1.1-bin.zip'\n$sha256sum = '79479DDE416B082F38ECD1F2F7C6DEBD4D0C2249AF80FD046D1CE05D628F2EC6'\n$coberturaPath = \"C:\\cobertura-2.1.1\"\n\n$archivePath = Invoke-DownloadWithRetry $uri\nTest-FileChecksum $archivePath -ExpectedSHA256Sum $sha256sum\nExpand-7ZipArchive -Path $archivePath -DestinationPath \"C:\\\"\n\n[Environment]::SetEnvironmentVariable(\"COBERTURA_HOME\", $coberturaPath, \"Machine\")\n\nInvoke-PesterTests -TestFile \"Java\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Kotlin.ps1",
    "content": "################################################################################\n##  File:  Install-Kotlin.ps1\n##  Desc:  Install Kotlin\n##  Supply chain security: Kotlin - checksum validation\n################################################################################\n\n# Install Kotlin\n$kotlinVersion = (Get-ToolsetContent).kotlin.version\n\n$kotlinDownloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"JetBrains/kotlin\" `\n    -Version \"$kotlinVersion\" `\n    -Asset \"kotlin-compiler-*.zip\"\n$kotlinArchivePath = Invoke-DownloadWithRetry $kotlinDownloadUrl\n\n#region Supply chain security\n$externalHash = Get-Content $(Invoke-DownloadWithRetry \"$kotlinDownloadUrl.sha256\")\nTest-FileChecksum $kotlinArchivePath -ExpectedSHA256Sum $externalHash\n#endregion\n\nWrite-Host \"Expand Kotlin archive\"\n$kotlinPath = \"C:\\tools\"\nExpand-7ZipArchive -Path $kotlinArchivePath -DestinationPath $kotlinPath\n\n# Add to PATH\nAdd-MachinePathItem \"$kotlinPath\\kotlinc\\bin\"\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"Kotlin\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-KubernetesTools.ps1",
    "content": "################################################################################\n##  File:  Install-KubernetesTools.ps1\n##  Desc:  Install tools for K8s.\n##  Supply chain security: GitHub Kind - checksum validation, Kubectl, Helm, Minikube - by package manager\n################################################################################\n\nWrite-Host \"Install Kind\"\n# Choco installation can't be used because it depends on docker-desktop\n\n$targetDir = \"C:\\ProgramData\\kind\"\nNew-Item -Path $targetDir -ItemType Directory -Force | Out-Null\n\n$downloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"kubernetes-sigs/kind\" `\n    -Version \"latest\" `\n    -UrlMatchPattern \"kind-windows-amd64\"\n$packagePath = Invoke-DownloadWithRetry -Url $downloadUrl -Path \"$targetDir\\kind.exe\"\n\n#region Supply chain security - Kind\n$externalHash = Get-ChecksumFromUrl -Type \"SHA256\" `\n    -Url \"$downloadUrl.sha256sum\" `\n    -FileName (Split-Path $downloadUrl -Leaf)\nTest-FileChecksum $packagePath -ExpectedSHA256Sum $externalHash\n#endregion\n\nAdd-MachinePathItem $targetDir\n\nWrite-Host \"Install Kubectl\"\nInstall-ChocoPackage kubernetes-cli\n\nWrite-Host \"Install Helm\"\nInstall-ChocoPackage kubernetes-helm\n\nWrite-Host \"Install Minikube\"\nInstall-ChocoPackage minikube\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"KubernetesTools\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-LLVM.ps1",
    "content": "################################################################################\n##  File:  Install-LLVM.ps1\n##  Desc:  Install the latest stable version of llvm and clang compilers\n################################################################################\n\n$llvmVersion = (Get-ToolsetContent).llvm.version\n$latestChocoVersion = Resolve-ChocoPackageVersion -PackageName \"llvm\" -TargetVersion $llvmVersion\nInstall-ChocoPackage llvm -ArgumentList '--version', $latestChocoVersion\n\nInvoke-PesterTests -TestFile \"LLVM\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Mercurial.ps1",
    "content": "################################################################################\n##  File:  Install-Mercurial.ps1\n##  Desc:  Install Mercurial\n################################################################################\n\nInstall-ChocoPackage hg -ArgumentList \"--version\", \"6.3.1\"\n\nAdd-MachinePathItem \"${env:ProgramFiles}\\Mercurial\\\"\nUpdate-Environment\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"Mercurial\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Mingw64.ps1",
    "content": "################################################################################\n##  File:  Install-Mingw64.ps1\n##  Desc:  Install GNU tools for Windows\n################################################################################\n\n# Install version specified in the toolset\n$version = (Get-ToolsetContent).mingw.version\n$runtime = (Get-ToolsetContent).mingw.runtime\n\n$(\"mingw32\", \"mingw64\") | ForEach-Object {\n    if ($_ -eq \"mingw32\") {\n        $arch = \"i686\"\n        $threads = \"posix\"\n        $exceptions = \"dwarf\"\n    } elseif ($_ -eq \"mingw64\") {\n        $arch = \"x86_64\"\n        $threads = \"posix\"\n        $exceptions = \"seh\"\n    } else {\n        throw \"Unknown architecture $_\"\n    }\n\n    $url = Resolve-GithubReleaseAssetUrl `\n        -Repo \"niXman/mingw-builds-binaries\" `\n        -Version \"$version\" `\n        -Asset \"$arch-*-release-$threads-$exceptions-$runtime-*.7z\"\n\n    $packagePath = Invoke-DownloadWithRetry $url\n    Expand-7ZipArchive -Path $packagePath -DestinationPath \"C:\\\"\n\n    # Make a copy of mingw-make.exe to make.exe, which is a more discoverable name\n    # and so the same command line can be used on Windows as on macOS and Linux\n    $path = \"C:\\$_\\bin\\mingw32-make.exe\" | Get-Item\n    Copy-Item -Path $path -Destination (Join-Path $path.Directory 'make.exe')\n}\n\nAdd-MachinePathItem \"C:\\mingw64\\bin\"\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"Mingw64\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Miniconda.ps1",
    "content": "################################################################################\n##  File:  Install-Miniconda.ps1\n##  Desc:  Install the latest version of Miniconda and set $env:CONDA\n##  Supply chain security: checksum validation\n################################################################################\n\n$condaDestination = \"C:\\Miniconda\"\n$installerName = \"Miniconda3-latest-Windows-x86_64.exe\"\n\n#region Supply chain security\n$distributorFileHash = $null\n$checksums = (ConvertFrom-HTML -Uri 'https://repo.anaconda.com/miniconda/').SelectNodes('//html/body/table/tr')\n\nforeach ($node in $checksums) {\n    if ($node.ChildNodes[1].InnerText -eq $installerName) {\n        $distributorFileHash = $node.ChildNodes[7].InnerText\n    }\n}\n\nif ($null -eq $distributorFileHash) {\n    throw \"Unable to find checksum for $installerName in https://repo.anaconda.com/miniconda/\"\n}\n#endregion\n\nInstall-Binary `\n    -Url \"https://repo.anaconda.com/miniconda/${installerName}\" `\n    -InstallArgs @(\"/S\", \"/AddToPath=0\", \"/RegisterPython=0\", \"/D=$condaDestination\") `\n    -ExpectedSHA256Sum $distributorFileHash\n\n[Environment]::SetEnvironmentVariable(\"CONDA\", $condaDestination, \"Machine\")\n\nInvoke-PesterTests -TestFile \"Miniconda\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-MongoDB.ps1",
    "content": "####################################################################################\n##  File:  Install-MongoDB.ps1\n##  Desc:  Install MongoDB\n####################################################################################\n\n# Install mongodb package\n$toolsetContent = Get-ToolsetContent\n$toolsetVersion = $toolsetContent.mongodb.version\n\n$getMongoReleases = Invoke-WebRequest -Uri \"mongodb.com/docs/v$toolsetVersion/release-notes/$toolsetVersion/\" -UseBasicParsing\n$targetReleases = $getMongoReleases.Links.href | Where-Object { $_ -like \"#$toolsetVersion*---*\" }\n\n$minorVersions = @()\nforeach ($release in $targetReleases) {\n    if ($release -notlike \"*upcoming*\") {\n        $pattern = '\\d+\\.\\d+\\.\\d+'\n        $version = $release | Select-String -Pattern $pattern -AllMatches | ForEach-Object { $_.Matches } | ForEach-Object { $_.Value }\n        $minorVersions += $version\n    }\n}\n\n$latestVersion = $minorVersions[0]\n\nInstall-Binary `\n    -Url \"https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-$latestVersion-signed.msi\" `\n    -ExtraInstallArgs @('TARGETDIR=C:\\PROGRA~1\\MongoDB ADDLOCAL=ALL') `\n    -ExpectedSubject 'CN=\"MONGODB, INC.\", O=\"MONGODB, INC.\", L=New York, S=New York, C=US'\n\n# Add mongodb to the PATH\n$mongoPath = (Get-CimInstance Win32_Service -Filter \"Name LIKE 'mongodb'\").PathName\n$mongoBin = Split-Path -Path $mongoPath.split('\"')[1]\nAdd-MachinePathItem \"$mongoBin\"\n\n# Wait for mongodb service running\n$mongodbService = Get-Service \"mongodb\"\n$mongodbService.WaitForStatus('Running', '00:01:00')\n\n# Stop and disable mongodb service\nStop-Service $mongodbService\n$mongodbService | Set-Service -StartupType Disabled\n\n# Install mongodb shell for mongodb\n$mongoshVersion = (Get-GithubReleasesByVersion -Repo \"mongodb-js/mongosh\" -Version \"latest\").version\n\n$mongoshDownloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"mongodb-js/mongosh\" `\n    -Version $mongoshVersion `\n    -UrlMatchPattern \"mongosh-*-x64.msi\"\n\nInstall-Binary -Type MSI `\n    -Url $mongoshDownloadUrl `\n    -ExtraInstallArgs @('ALLUSERS=1') `\n    -ExpectedSubject 'CN=\"MongoDB, Inc.\", O=\"MongoDB, Inc.\", L=New York, S=New York, C=US'\n\n\nInvoke-PesterTests -TestFile \"Databases\" -TestName \"MongoDB\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Msys2.ps1",
    "content": "################################################################################\n##  File:  Install-Msys2.ps1\n##  Desc:  Install Msys2 and 64 & 32 bit gcc, cmake, & llvm\n################################################################################\n\n# References\n# https://github.com/msys2/MINGW-packages/blob/master/azure-pipelines.yml\n# https://packages.msys2.org/group/\n\n$logPrefix = \"`n\" + (\"-\" * 40) + \"`n---\"\n$origPath = $env:PATH\n\nfunction Install-Msys2 {\n    # We can't use Resolve-GithubReleaseAssetUrl function here\n    # because msys2-installer releases don't have a consistent versioning scheme\n\n    $assets = (Invoke-RestMethod -Uri \"https://api.github.com/repos/msys2/msys2-installer/releases/latest\").assets\n    $downloadUri = ($assets | Where-Object { $_.name -match \"^msys2-x86_64\" -and $_.name.EndsWith(\".exe\") }).browser_download_url\n    $installerName = Split-Path $downloadUri -Leaf\n  \n    # Download the latest msys2 x86_64, filename includes release date\n    Write-Host \"Download msys2 installer $installerName\"\n    $installerPath = Invoke-DownloadWithRetry $downloadUri\n\n    Write-Host \"Starting msys2 installation\"\n    & $installerPath in --confirm-command --accept-messages --root C:/msys64\n    if ($LastExitCode -ne 0) {\n        throw \"MSYS2 installation failed with exit code $LastExitCode\"\n    }\n    Remove-Item $installerPath\n}\n\nfunction Install-Msys2Packages {\n    param (\n        [Parameter(Mandatory = $true)]\n        [AllowEmptyCollection()]\n        [string[]]$Packages\n    )\n\n    if (-not $Packages) {\n        return\n    }\n\n    Write-Host \"$logPrefix Install msys2 packages\"\n    pacman.exe -S --noconfirm --needed --noprogressbar $Packages\n    if ($LastExitCode -ne 0) {\n        throw \"MSYS2 packages installation failed with exit code $LastExitCode\"\n    }\n    taskkill /f /fi \"MODULES eq msys-2.0.dll\"\n\n    Write-Host \"$logPrefix Remove p7zip/7z package due to conflicts\"\n    pacman.exe -R --noconfirm --noprogressbar p7zip\n    if ($LastExitCode -ne 0) {\n        throw \"Removal of p7zip/7z package failed with exit code $LastExitCode\"\n    }\n}\n\nfunction Install-MingwPackages {\n    param (\n        [Parameter(Mandatory = $true)]\n        [AllowEmptyCollection()]\n        [object[]] $Packages\n    )\n\n    if (-not $Packages) {\n        return\n    }\n\n    Write-Host \"$logPrefix Install mingw packages\"\n    $archs = $Packages.arch\n\n    foreach ($arch in $archs) {\n        Write-Host \"Installing $arch packages\"\n        $archPackages = $toolsetContent.mingw | Where-Object { $_.arch -eq $arch }\n        $runtimePackages = $archPackages.runtime_packages.name | ForEach-Object { \"${arch}-$_\" }\n        $additionalPackages = $archPackages.additional_packages | ForEach-Object { \"${arch}-$_\" }\n        $packagesToInstall = $runtimePackages + $additionalPackages\n        Write-Host \"The following packages will be installed: $packagesToInstall\"\n        pacman.exe -S --noconfirm --needed --noprogressbar $packagesToInstall\n        if ($LastExitCode -ne 0) {\n            throw \"Installation of $arch packages failed with exit code $LastExitCode\"\n        }\n    }\n\n    # clean all packages to decrease image size\n    Write-Host \"$logPrefix Clean packages\"\n    pacman.exe -Scc --noconfirm\n    if ($LastExitCode -ne 0) {\n        throw \"Cleaning of packages failed with exit code $LastExitCode\"\n    }\n\n    $pkgs = pacman.exe -Q\n    if ($LastExitCode -ne 0) {\n        throw \"Listing of packages failed with exit code $LastExitCode\"\n    }\n\n    foreach ($arch in $archs) {\n        Write-Host \"$logPrefix Installed $arch packages\"\n        $pkgs | Select-String -Pattern \"^${arch}-\"\n    }\n}\n\nInstall-Msys2\n\n# Add msys2 bin tools folders to PATH temporary\n$env:PATH = \"C:\\msys64\\mingw64\\bin;C:\\msys64\\usr\\bin;$origPath\"\n\nWrite-Host \"$logPrefix pacman --noconfirm -Syyuu\"\npacman.exe -Syyuu --noconfirm\nif ($LastExitCode -ne 0) {\n    throw \"Updating of packages failed with exit code $LastExitCode\"\n}\ntaskkill /f /fi \"MODULES eq msys-2.0.dll\"\n\nWrite-Host \"$logPrefix pacman --noconfirm -Syuu (2nd pass)\"\npacman.exe -Syuu --noconfirm\nif ($LastExitCode -ne 0) {\n    throw \"Second pass updating of packages failed with exit code $LastExitCode\"\n}\ntaskkill /f /fi \"MODULES eq msys-2.0.dll\"\n\n$toolsetContent = (Get-ToolsetContent).MsysPackages\nInstall-Msys2Packages -Packages $toolsetContent.msys2\nInstall-MingwPackages -Packages $toolsetContent.mingw\n\n$env:PATH = $origPath\nWrite-Host \"`nMSYS2 installation completed\"\n\nInvoke-PesterTests -TestFile \"MSYS2\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-MysqlCli.ps1",
    "content": "################################################################################\n##  File:  Install-MysqlCli.ps1\n##  Desc:  Install Mysql CLI\n##  Supply chain security: checksum validation (visual c++ redistributable package)\n################################################################################\n\n# Installing visual c++ redistributable package.\nInstall-Binary `\n    -Url 'https://download.microsoft.com/download/0/5/6/056dcda9-d667-4e27-8001-8a0c6971d6b1/vcredist_x64.exe' `\n    -InstallArgs @(\"/install\", \"/quiet\", \"/norestart\") `\n    -ExpectedSHA256Sum '20E2645B7CD5873B1FA3462B99A665AC8D6E14AAE83DED9D875FEA35FFDD7D7E'\n\n# Downloading mysql\n[version] $mysqlVersion = (Get-ToolsetContent).mysql.version\n$mysqlVersionMajorMinor = $mysqlVersion.ToString(2)\n\nif ($mysqlVersion.Build -lt 0) {\n    if ($mysqlVersionMajorMinor -eq \"5.7\") {\n        $downloadsPageUrl = \"https://downloads.mysql.com/archives/community/\"\n    } else {\n        $downloadsPageUrl = \"https://dev.mysql.com/downloads/mysql/${mysqlVersionMajorMinor}.html\"\n    }\n    $mysqlVersion = Invoke-RestMethod -Uri $downloadsPageUrl -Headers @{ 'User-Agent' = 'curl/8.4.0' } `\n    | Select-String -Pattern \"${mysqlVersionMajorMinor}\\.\\d+\" `\n    | ForEach-Object { $_.Matches.Value }\n}\n\n$mysqlVersionFull = $mysqlVersion.ToString()\n$mysqlVersionUrl = \"https://cdn.mysql.com/Downloads/MySQL-${mysqlVersionMajorMinor}/mysql-${mysqlVersionFull}-winx64.msi\"\n\nInstall-Binary `\n    -Url $mysqlVersionUrl `\n    -ExpectedSubject 'CN=\"Oracle America, Inc.\", O=\"Oracle America, Inc.\", L=Redwood City, S=California, C=US, SERIALNUMBER=2101822, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US'\n\n# Adding mysql in system environment path\n$mysqlPath = $(Get-ChildItem -Path \"C:\\PROGRA~1\\MySQL\" -Directory)[0].FullName\n\nAdd-MachinePathItem \"${mysqlPath}\\bin\"\n\nInvoke-PesterTests -TestFile \"Databases\" -TestName \"MySQL\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-NSIS.ps1",
    "content": "################################################################################\n##  File:  Install-NSIS.ps1\n##  Desc:  Install NSIS\n##  Supply chain security: NSIS - managed by package manager\n################################################################################\n\n$nsisVersion = (Get-ToolsetContent).nsis.version\n\nInstall-ChocoPackage nsis -ArgumentList \"--version\", \"$nsisVersion\"\n\nAdd-MachinePathItem \"${env:ProgramFiles(x86)}\\NSIS\\\"\nUpdate-Environment\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"NSIS\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-NativeImages.ps1",
    "content": "################################################################################\n##  File: Install-NativeImages.ps1\n##  Desc: Generate and install native images for .NET assemblies\n################################################################################\n\nWrite-Host \"NGen: install Microsoft.PowerShell.Utility.Activities...\"\n& $env:SystemRoot\\Microsoft.NET\\Framework64\\v4.0.30319\\ngen.exe install \"Microsoft.PowerShell.Utility.Activities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" | Out-Null\nif ($LASTEXITCODE -ne 0) {\n    throw \"Installation of Microsoft.PowerShell.Utility.Activities failed with exit code $LASTEXITCODE\"\n}\n\nWrite-Host \"NGen: update x64 native images...\"\n& $env:SystemRoot\\Microsoft.NET\\Framework64\\v4.0.30319\\ngen.exe update | Out-Null\nif ($LASTEXITCODE -ne 0) {\n    throw \"Update of x64 native images failed with exit code $LASTEXITCODE\"\n}\n\nWrite-Host \"NGen: update x86 native images...\"\n& $env:SystemRoot\\Microsoft.NET\\Framework\\v4.0.30319\\ngen.exe update | Out-Null\nif ($LASTEXITCODE -ne 0) {\n    throw \"Update of x86 native images failed with exit code $LASTEXITCODE\"\n}\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Nginx.ps1",
    "content": "################################################################################\n##  File:  Install-Nginx.ps1\n##  Desc:  Install Nginx\n################################################################################\n\n# Stop w3svc service\nStop-Service -Name w3svc\n\n# Install latest nginx in chocolatey\n$installDir = \"C:\\tools\"\nInstall-ChocoPackage nginx -ArgumentList \"--force\", \"--params\", \"/installLocation:$installDir /port:80\"\n\n# Stop and disable Nginx service\nStop-Service -Name nginx\nSet-Service -Name nginx -StartupType Disabled\n\n# Start w3svc service\nStart-Service -Name w3svc\n\n# Invoke Pester Tests\nInvoke-PesterTests -TestFile \"Nginx\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-NodeJS.ps1",
    "content": "################################################################################\n##  File:  Install-NodeJS.ps1\n##  Desc:  Install nodejs-lts and other common node tools.\n##         Must run after python is configured\n################################################################################\n\n$prefixPath = 'C:\\npm\\prefix'\n$cachePath = 'C:\\npm\\cache'\n\nNew-Item -Path $prefixPath -Force -ItemType Directory\nNew-Item -Path $cachePath -Force -ItemType Directory\n\n$defaultVersion = (Get-ToolsetContent).node.default\n$nodeVersion = (Get-GithubReleasesByVersion -Repo \"nodejs/node\" -Version \"${defaultVersion}\").version | Select-Object -First 1\n$downloadUrl = \"https://nodejs.org/dist/v${nodeVersion}/node-v${nodeVersion}-x64.msi\"\n\n$packageName = Split-Path $downloadUrl -Leaf\n$externalHash = Get-ChecksumFromUrl -Type \"SHA256\" `\n    -Url ($downloadUrl -replace $packageName, \"SHASUMS256.txt\") `\n    -FileName $packageName\n\nInstall-Binary -Type MSI `\n    -Url $downloadUrl `\n    -ExtraInstallArgs @('ADDLOCAL=ALL') `\n    -ExpectedSHA256Sum $externalHash\n\nAdd-MachinePathItem $prefixPath\nUpdate-Environment\n\n[Environment]::SetEnvironmentVariable(\"npm_config_prefix\", $prefixPath, \"Machine\")\n$env:npm_config_prefix = $prefixPath\n\nnpm config set cache $cachePath --global\nnpm config set registry https://registry.npmjs.org/\n\n$globalNpmPackages = (Get-ToolsetContent).npm.global_packages\n$globalNpmPackages | ForEach-Object {\n    npm install -g $_.name\n}\n\nInvoke-PesterTests -TestFile \"Node\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-OpenSSL.ps1",
    "content": "################################################################################\n##  File:  Install-OpenSSL.ps1\n##  Desc:  Install win64-openssl.\n##  Supply chain security: checksum validation\n################################################################################\n\n$arch = 'INTEL'\n$bits = '64'\n$light = $false\n$installerType = \"exe\"\n$version = (Get-ToolsetContent).openssl.version\n$installDir = \"$env:ProgramFiles\\OpenSSL\"\n\n# Fetch available installers list\n$jsonUrl = 'https://raw.githubusercontent.com/slproweb/opensslhashes/master/win32_openssl_hashes.json'\n\n$installersAvailable = (Invoke-RestMethod $jsonUrl).files\n$installerNames = $installersAvailable | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name\n\n$installerUrl = $null\n$installerHash = $null\n\nforeach ($key in $installerNames) {\n    $installer = $installersAvailable.$key\n    if (($installer.light -eq $light) -and ($installer.arch -eq $arch) -and ($installer.bits -eq $bits) -and ($installer.installer -eq $installerType) -and ($installer.basever -like $version)) {\n        $installerUrl = $installer.url\n        $installerHash = $installer.sha512\n    }\n}\n\nif ($null -eq $installerUrl) {\n    throw \"Installer not found for version $version\"\n}\n\nInstall-Binary `\n    -Url $installerUrl `\n    -InstallArgs @('/silent', '/sp-', '/suppressmsgboxes','/tasks=\"copytobin\"', \"/DIR=`\"$installDir`\"\") `\n    -ExpectedSHA512Sum $installerHash\n\n# Update PATH\nAdd-MachinePathItem \"$installDir\\bin\"\nUpdate-Environment\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"OpenSSL\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-PHP.ps1",
    "content": "################################################################################\n##  File:  Install-PHP.ps1\n##  Desc:  Install PHP\n################################################################################\n\n# Install latest PHP in chocolatey\n$installDir = \"c:\\tools\\php\"\n$phpMajorMinor = (Get-ToolsetContent).php.version\n$phpVersionToInstall = Resolve-ChocoPackageVersion -PackageName \"php\" -TargetVersion $phpMajorMinor\nInstall-ChocoPackage php -ArgumentList \"--params\", \"/InstallDir:$installDir\", \"--version=$phpVersionToInstall\"\n\n# Install latest Composer in chocolatey\nInstall-ChocoPackage composer -ArgumentList \"--install-args\", \"/DEV=$installDir /PHP=$installDir\"\n\n# update path to extensions and enable curl and mbstring extensions, and enable php openssl extensions.\n((Get-Content -path $installDir\\php.ini -Raw) -replace ';extension=curl','extension=curl' -replace ';extension=mbstring','extension=mbstring' -replace ';extension_dir = \"ext\"','extension_dir = \"ext\"' -replace ';extension=openssl','extension=openssl') | Set-Content -Path $installDir\\php.ini\n\n# Set the PHPROOT environment variable.\n[Environment]::SetEnvironmentVariable(\"PHPROOT\", $installDir, \"Machine\")\n\n# Invoke Pester Tests\nInvoke-PesterTests -TestFile \"PHP\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Pipx.ps1",
    "content": "################################################################################\n##  File:  Install-Pipx.ps1\n##  Desc:  Install pipx and pipx packages\n################################################################################\n\nWrite-Host \"Installing pipx...\"\n$env:PIPX_BIN_DIR = \"${env:ProgramFiles(x86)}\\pipx_bin\"\n$env:PIPX_HOME = \"${env:ProgramFiles(x86)}\\pipx\"\n\npip install pipx\nif ($LASTEXITCODE -ne 0) {\n    throw \"pipx installation failed with exit code $LASTEXITCODE\"\n}\n\nAdd-MachinePathItem \"${env:PIPX_BIN_DIR}\"\n[Environment]::SetEnvironmentVariable(\"PIPX_BIN_DIR\", $env:PIPX_BIN_DIR, \"Machine\")\n[Environment]::SetEnvironmentVariable(\"PIPX_HOME\", $env:PIPX_HOME, \"Machine\")\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"Pipx\"\n\nWrite-Host \"Installing pipx packages...\"\n\n$pipxToolset = (Get-ToolsetContent).pipx\nforeach ($tool in $pipxToolset) {\n    if ($tool.python) {\n        $pythonPath = (Get-Item -Path \"${env:AGENT_TOOLSDIRECTORY}\\Python\\${tool.python}.*\\x64\\python-${tool.python}*\").FullName\n        Write-Host \"Install ${tool.package} into python ${tool.python}\"\n        pipx install $tool.package --python $pythonPath\n    } else {\n        Write-Host \"Install ${tool.package} into default python\"\n        pipx install $tool.package\n    }\n\n    if ($LASTEXITCODE -ne 0) {\n        throw \"Package ${tool.package} installation failed with exit code $LASTEXITCODE\"\n    }\n}\n\nInvoke-PesterTests -TestFile \"PipxPackages\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-PostgreSQL.ps1",
    "content": "################################################################################\n##  File:  Install-PostgreSQL.ps1\n##  Desc:  Install PostgreSQL\n################################################################################\n\n# Define user and password for PostgreSQL database\n$pgUser = \"postgres\"\n$pgPwd  = \"root\"\n\n# Save current value of ErrorActionPreference and set it to Stop\n$errorActionOldValue = $ErrorActionPreference\n\n# Prepare environment variable for validation\n[Environment]::SetEnvironmentVariable(\"PGUSER\", $pgUser, \"Machine\")\n[Environment]::SetEnvironmentVariable(\"PGPASSWORD\", $pgPwd, \"Machine\")\n\n$toolsetVersion = (Get-ToolsetContent).postgresql.version\nif ($null -ne ($toolsetVersion | Select-String -Pattern '\\d+\\.\\d+\\.\\d+')) {\n    $majorVersion = ([version]$toolsetVersion).Major\n    $minorVersion = ([version]$toolsetVersion).Minor\n    $patchVersion = ([version]$toolsetVersion).Build\n    $installerUrl = \"https://get.enterprisedb.com/postgresql/postgresql-$majorVersion.$minorVersion-$patchVersion-windows-x64.exe\"\n} else {\n    # Define latest available version to install based on version specified in the toolset\n    $getPostgreReleases = Invoke-WebRequest -Uri \"https://git.postgresql.org/gitweb/?p=postgresql.git;a=tags\" -UseBasicParsing\n    # Getting all links matched to the pattern (e.g.a=log;h=refs/tags/REL_14)\n    $targetReleases = $getPostgreReleases.Links.href | Where-Object { $_ -match \"a=log;h=refs/tags/REL_$toolsetVersion\" }\n    [Int32] $outNumber = $null\n    $minorVersions = @()\n    foreach ($release in $targetReleases) {\n        $version = $release.split('/')[-1]\n        # Checking if the latest symbol of the release version is actually a number. If yes, add to $minorVersions array\n        if ([Int32]::TryParse($($version.Split('_')[-1]), [ref] $outNumber)) {\n            $minorVersions += $outNumber\n        }\n    }\n    # Sorting and getting the last one\n    $targetMinorVersions = ($minorVersions | Sort-Object)[-1]\n\n    # In order to get rid of error messages (we know we will have them), force ErrorAction to SilentlyContinue\n    $ErrorActionPreference = 'SilentlyContinue'\n\n    # Install latest PostgreSQL\n    # Starting from number 9 and going down, check if the installer is available. If yes, break the loop.\n    # If an installer with $targetMinorVersions is not to be found, the $targetMinorVersions will be decreased by 1\n    $increment = 9\n    do {\n        $url = \"https://get.enterprisedb.com/postgresql/postgresql-$toolsetVersion.$targetMinorVersions-$increment-windows-x64.exe\"\n        $checkAccess = [System.Net.WebRequest]::Create($url)\n        $response = $null\n        $response = $checkAccess.GetResponse()\n        if ($response) {\n            $installerUrl = $response.ResponseUri.OriginalString\n        } elseif (!$response -and ($increment -eq 0)) {\n            $increment = 9\n            $targetMinorVersions--\n        } else {\n            $increment--\n        }\n    } while (!$response)\n}\n\n# Return the previous value of ErrorAction and invoke Install-Binary function\n$ErrorActionPreference = $errorActionOldValue\n\n# Define new data directory for PostgreSQL and create it\nif ($installerUrl -match 'postgresql-(\\d+)') {\n    $pgMajorVersion = $matches[1]\n}\n$pgData = \"C:\\PostgreSQL\\$pgMajorVersion\\data\"\nif (-Not (Test-Path -Path $pgData)) {\n    New-Item -ItemType Directory -Path $pgData | Out-Null\n}\n\n# Define silent install arguments for PostgreSQL\n$installerArgs = @(\n    \"--install_runtimes 0\",\n    \"--superpassword root\",\n    \"--enable_acledit 1\",\n    \"--unattendedmodeui none\",\n    \"--mode unattended\",\n    \"--datadir `\"$pgData`\"\"\n)\n\nInstall-Binary `\n    -Url $installerUrl `\n    -InstallArgs $installerArgs `\n    -ExpectedSubject 'CN=EnterpriseDB Corporation, O=EnterpriseDB Corporation, L=Wilmington, S=Delaware, C=US' `\n    -InstallerLogPath \"$env:TEMP\\**\\install-postgresql.log\"\n\n# Get Path to pg_ctl.exe\n$pgPath = (Get-CimInstance Win32_Service -Filter \"Name LIKE 'postgresql-%'\").PathName\n\n# Parse output of command above to obtain pure path\n$pgBin = Split-Path -Path $pgPath.split('\"')[1]\n$pgRoot = Split-Path $pgBin -Parent\n\n# Validate PostgreSQL installation\n$pgReadyPath = Join-Path $pgBin \"pg_isready.exe\"\n$pgReady = Start-Process -FilePath $pgReadyPath -Wait -PassThru\n$exitCode = $pgReady.ExitCode\n\nif ($exitCode -ne 0) {\n    Write-Host -Object \"PostgreSQL is not ready. Exitcode: $exitCode\"\n    exit $exitCode\n}\n\n# Added PostgreSQL environment variable\n[Environment]::SetEnvironmentVariable(\"PGBIN\", $pgBin, \"Machine\")\n[Environment]::SetEnvironmentVariable(\"PGROOT\", $pgRoot, \"Machine\")\n[Environment]::SetEnvironmentVariable(\"PGDATA\", $pgData, \"Machine\")\n\n# Stop and disable PostgreSQL service\n$pgService = Get-Service -Name postgresql*\nStop-Service $pgService\n$pgService | Set-Service -StartupType Disabled\n\nInvoke-PesterTests -TestFile \"Databases\" -TestName \"PostgreSQL\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-PowerShellModules.ps1",
    "content": "################################################################################\n##  File:  Install-PowershellModules.ps1\n##  Desc:  Install common PowerShell modules\n################################################################################\n\n# Set TLS1.2\n[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor \"Tls12\"\n\n# Install PowerShell modules\n$modules = (Get-ToolsetContent).powershellModules\n\nforeach ($module in $modules) {\n    $moduleName = $module.name\n    Write-Host \"Installing ${moduleName} module\"\n\n    if ($module.versions) {\n        foreach ($version in $module.versions) {\n            Write-Host \" - $version\"\n            Install-Module -Name $moduleName -RequiredVersion $version -Scope AllUsers -SkipPublisherCheck -Force\n        }\n    } else {\n        Install-Module -Name $moduleName -Scope AllUsers -SkipPublisherCheck -Force\n    }\n}\n\nImport-Module Pester\nInvoke-PesterTests -TestFile \"PowerShellModules\" -TestName \"PowerShellModules\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-PowershellAzModules.ps1",
    "content": "################################################################################\n##  File:  Install-PowershellAzModules.ps1\n##  Desc:  Install PowerShell modules used by AzureFileCopy@4, AzureFileCopy@5, AzurePowerShell@4, AzurePowerShell@5 tasks\n##  Supply chain security: package manager\n################################################################################\n\n# The correct Modules need to be saved in C:\\Modules\n$installPSModulePath = \"C:\\\\Modules\"\nif (-not (Test-Path -LiteralPath $installPSModulePath)) {\n    Write-Host \"Creating ${installPSModulePath} folder to store PowerShell Azure modules...\"\n    New-Item -Path $installPSModulePath -ItemType Directory | Out-Null\n}\n\n# Get modules content from toolset\n$modules = (Get-ToolsetContent).azureModules\n\n$psModuleMachinePath = \"\"\n\nforeach ($module in $modules) {\n    $moduleName = $module.name\n\n    Write-Host \"Installing ${moduleName} to the ${installPSModulePath} path...\"\n    foreach ($version in $module.versions) {\n        $modulePath = Join-Path -Path $installPSModulePath -ChildPath \"${moduleName}_${version}\"\n        Write-Host \" - $version [$modulePath]\"\n        $psModuleMachinePath += \"$modulePath;\"\n        Save-Module -Path $modulePath -Name $moduleName -RequiredVersion $version -Force -ErrorAction Stop\n    }\n}\n\n# Add modules to the PSModulePath\n$psModuleMachinePath += $env:PSModulePath\n[Environment]::SetEnvironmentVariable(\"PSModulePath\", $psModuleMachinePath, \"Machine\")\n\nInvoke-PesterTests -TestFile \"PowerShellAzModules\" -TestName \"AzureModules\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-PowershellCore.ps1",
    "content": "################################################################################\n##  File:  Install-PowershellCore.ps1\n##  Desc:  Install PowerShell Core\n##  Supply chain security: checksum validation\n################################################################################\n\n$ErrorActionPreference = \"Stop\"\n\n$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())\nNew-Item -ItemType Directory -Path $tempDir -Force -ErrorAction SilentlyContinue | Out-Null\ntry {\n    $originalValue = [Net.ServicePointManager]::SecurityProtocol\n    [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12\n\n    $metadata = Invoke-RestMethod https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/metadata.json\n    $pwshMajorMinor = (Get-ToolsetContent).pwsh.version\n\n    $releases = $metadata.LTSReleaseTag -replace '^v'\n    foreach ($release in $releases) {\n        if ($release -like \"${pwshMajorMinor}*\") {\n            $downloadUrl = \"https://github.com/PowerShell/PowerShell/releases/download/v${release}/PowerShell-${release}-win-x64.msi\"\n            break\n        }\n    }\n\n    $installerName = Split-Path $downloadUrl -Leaf\n    $externalHash = Get-ChecksumFromUrl -Type \"SHA256\" `\n        -Url ($downloadUrl -replace $installerName, \"hashes.sha256\") `\n        -FileName $installerName\n    Install-Binary -Url $downloadUrl -ExpectedSHA256Sum $externalHash\n} finally {\n    # Restore original value\n    [Net.ServicePointManager]::SecurityProtocol = $originalValue\n    Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue\n}\n\n# about_update_notifications\n# While the update check happens during the first session in a given 24-hour period, for performance reasons,\n# the notification will only be shown on the start of subsequent sessions.\n# Also for performance reasons, the check will not start until at least 3 seconds after the session begins.\n[Environment]::SetEnvironmentVariable(\"POWERSHELL_UPDATECHECK\", \"Off\", \"Machine\")\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"PowerShell Core\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-PyPy.ps1",
    "content": "################################################################################\n##  File:  Install-PyPy.ps1\n##  Desc:  Install PyPy\n##  Supply chain security: checksum validation\n################################################################################\n\nfunction Install-PyPy {\n    param(\n        [String] $PackagePath,\n        [String] $Architecture\n    )\n\n    # Create PyPy toolcache folder\n    $pypyToolcachePath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath \"PyPy\"\n    if (-not (Test-Path $pypyToolcachePath)) {\n        Write-Host \"Create PyPy toolcache folder\"\n        New-Item -ItemType Directory -Path $pypyToolcachePath | Out-Null\n    }\n\n    # Expand archive with binaries\n    $packageName = [IO.Path]::GetFileNameWithoutExtension((Split-Path -Path $packagePath -Leaf))\n    $tempFolder = Join-Path -Path $pypyToolcachePath -ChildPath $packageName\n    Expand-7ZipArchive -Path $packagePath -DestinationPath $pypyToolcachePath\n\n    # Get Python version from binaries\n    $pypyApp = Get-ChildItem -Path \"$tempFolder\\pypy*.exe\" | Where-Object Name -match \"pypy(\\d+)?.exe\" | Select-Object -First 1\n    $pythonVersion = & $pypyApp -c \"import sys;print('{}.{}.{}'.format(sys.version_info[0],sys.version_info[1],sys.version_info[2]))\"\n\n    $pypyFullVersion = & $pypyApp -c \"import sys;print('{}.{}.{}'.format(*sys.pypy_version_info[0:3]))\"\n    Write-Host \"Put '$pypyFullVersion' to PYPY_VERSION file\"\n    New-Item -Path \"$tempFolder\\PYPY_VERSION\" -Value $pypyFullVersion | Out-Null\n\n    if ($pythonVersion) {\n        Write-Host \"Installing PyPy $pythonVersion\"\n        $pypyVersionPath = Join-Path -Path $pypyToolcachePath -ChildPath $pythonVersion\n        $pypyArchPath = Join-Path -Path $pypyVersionPath -ChildPath $architecture\n\n        Write-Host \"Create PyPy '${pythonVersion}' folder in '${pypyVersionPath}'\"\n        New-Item -ItemType Directory -Path $pypyVersionPath -Force | Out-Null\n\n        Write-Host \"Move PyPy '${pythonVersion}' files to '${pypyArchPath}'\"\n        Invoke-ScriptBlockWithRetry -Command {\n            Move-Item -Path $tempFolder -Destination $pypyArchPath -ErrorAction Stop | Out-Null\n        }\n\n        Write-Host \"Install PyPy '${pythonVersion}' in '${pypyArchPath}'\"\n        if (Test-Path \"$pypyArchPath\\python.exe\") {\n            cmd.exe /c \"cd /d $pypyArchPath && python.exe -m ensurepip && python.exe -m pip install --upgrade pip\"\n        } else {\n            $pypyName = $pypyApp.Name\n            cmd.exe /c \"cd /d $pypyArchPath && mklink python.exe $pypyName && python.exe -m ensurepip && python.exe -m pip install --upgrade pip\"\n        }\n\n        # Create pip.exe if missing\n        $pipPath = Join-Path -Path $pypyArchPath -ChildPath \"Scripts/pip.exe\"\n        if (-not (Test-Path $pipPath)) {\n            $pip3Path = Join-Path -Path $pypyArchPath -ChildPath \"Scripts/pip3.exe\"\n            Copy-Item -Path $pip3Path -Destination $pipPath \n        }\n\n        if ($LASTEXITCODE -ne 0) {\n            throw \"PyPy installation failed with exit code $LASTEXITCODE\"\n        }\n\n        Write-Host \"Create complete file\"\n        New-Item -ItemType File -Path $pypyVersionPath -Name \"$architecture.complete\" | Out-Null\n    } else {\n        throw \"PyPy application is not found. Failed to expand '$packagePath' archive\"\n    }\n}\n\n# Get PyPy content from toolset\n$toolsetVersions = Get-ToolsetContent | Select-Object -ExpandProperty toolcache | Where-Object Name -eq \"PyPy\"\n\n# Get PyPy releases\n$pypyVersions = Invoke-RestMethod https://downloads.python.org/pypy/versions.json\n\n# required for html parsing\n$checksums = (Invoke-RestMethod -Uri 'https://www.pypy.org/checksums.html' | ConvertFrom-HTML).SelectNodes('//*[@id=\"content\"]/article/div/pre')\n\nWrite-Host \"Start PyPy installation\"\nforeach ($toolsetVersion in $toolsetVersions.versions) {\n    # Query latest PyPy version\n    $latestMajorPyPyVersion = $pypyVersions |\n        Where-Object { $_.python_version.StartsWith(\"$toolsetVersion\") -and $_.stable -eq $true } |\n        Select-Object -ExpandProperty files -First 1 |\n        Where-Object platform -like \"win*\"\n    \n    if (-not $latestMajorPyPyVersion) {\n        throw \"Failed to query PyPy version '$toolsetVersion'\"\n    }\n\n    $filename = $latestMajorPyPyVersion.filename\n    Write-Host \"Found PyPy '$filename' package\"\n    $tempPyPyPackagePath = Invoke-DownloadWithRetry $latestMajorPyPyVersion.download_url\n\n    #region Supply chain security\n    $distributorFileHash = $null\n    foreach ($node in $checksums) {\n        if ($node.InnerText -ilike \"*${filename}*\") {\n            $distributorFileHash = $node.InnerText.ToString().Split(\"`n\").Where({ $_ -ilike \"*${filename}*\" }).Split(' ')[0]\n        }\n    }\n    Test-FileChecksum $tempPyPyPackagePath -ExpectedSHA256Sum $distributorFileHash\n    #endregion\n\n    Install-PyPy -PackagePath $tempPyPyPackagePath -Architecture $toolsetVersions.arch\n}\n"
  },
  {
    "path": "images/windows/scripts/build/Install-R.ps1",
    "content": "################################################################################\n##  File:  Install-R.ps1\n##  Desc:  Install R for Windows\n################################################################################\n\nInstall-ChocoPackage R.Project\nInstall-ChocoPackage rtools\n\n$rscriptPath = Resolve-Path \"C:\\Program Files\\R\\*\\bin\\x64\"\nAdd-MachinePathItem $rscriptPath\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"R\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-RootCA.ps1",
    "content": "################################################################################\n##  File:  Install-RootCA.ps1\n##  Desc:  Install Root CA certificates\n################################################################################\n\n# https://www.sysadmins.lv/blog-en/how-to-retrieve-certificate-purposes-property-with-cryptoapi-and-powershell.aspx\n# https://www.sysadmins.lv/blog-en/dump-authroot-and-disallowed-certificates-with-powershell.aspx\n# https://www.sysadmins.lv/blog-en/constraining-extended-key-usages-in-microsoft-windows.aspx\n\nfunction Add-ExtendedCertType {\n    $signature = @\"\n        [DllImport(\"Crypt32.dll\", SetLastError = true, CharSet = CharSet.Auto)]\n        public static extern bool CertGetCertificateContextProperty(\n            IntPtr pCertContext,\n            uint dwPropId,\n            Byte[] pvData,\n            ref uint pcbData\n        );\n\n        [DllImport(\"Crypt32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern bool CertSetCertificateContextProperty(\n            IntPtr pCertContext,\n            int dwPropId,\n            uint dwFlags,\n            IntPtr pvData\n        );\n\"@\n\n    Add-Type -MemberDefinition $signature -Namespace PKI -Name Cert\n}\n\nfunction Get-CertificatesWithoutPropId {\n    # List installed certificates\n    $certs = Get-ChildItem -Path \"Cert:\\LocalMachine\\Root\"\n\n    Write-Host \"Certificates without CERT_NOT_BEFORE_FILETIME_PROP_ID property\"\n    $certsWithoutPropId = @{}\n    $certs | ForEach-Object -Process {\n        $certHandle = $_.Handle\n        $isPropertySet = [PKI.Cert]::CertGetCertificateContextProperty(\n            $certHandle, $CERT_NOT_BEFORE_FILETIME_PROP_ID, $null, [ref] $null\n        )\n        if (-not $isPropertySet) {\n            Write-Host \"Subject: $($_.Subject)\"\n            $certsWithoutPropId[$_.Thumbprint] = $null\n        }\n    }\n    $certsWithoutPropId\n}\n\nfunction Import-SSTFromWU {\n    # Serialized Certificate Store File\n    $sstFile = \"$env:TEMP\\roots.sst\"\n    # Generate SST from Windows Update\n    $result = Invoke-ScriptBlockWithRetry -RetryCount 5 -RetryIntervalSeconds 10 -Command {\n        $r = certutil.exe -generateSSTFromWU $sstFile\n        if ($LASTEXITCODE -ne 0) {\n            throw \"failed to generate $sstFile sst file`n$o\"\n        }\n        return $r\n    }\n    if ($LASTEXITCODE -ne 0) {\n        Write-Host \"[Error]: failed to generate $sstFile sst file`n$result\"\n        exit $LASTEXITCODE\n    }\n\n    $result = certutil.exe -dump $sstFile\n    if ($LASTEXITCODE -ne 0) {\n        Write-Host \"[Error]: failed to dump $sstFile sst file`n$result\"\n        exit $LASTEXITCODE\n    }\n\n    Import-Certificate -FilePath $sstFile -CertStoreLocation Cert:\\LocalMachine\\Root\n}\n\nfunction Clear-CertificatesPropId {\n    param(\n        [hashtable] $CertsWithoutPropId\n    )\n\n    # List installed certificates\n    $certs = Get-ChildItem -Path Cert:\\LocalMachine\\Root\n\n    # Clear property CERT_NOT_BEFORE_FILETIME_PROP_ID\n    $certs | ForEach-Object -Process {\n        $thumbprint = $_.Thumbprint\n        if ($certsWithoutPropId.ContainsKey($thumbprint)) {\n            $subject = $_.Subject\n            $certHandle = $_.Handle\n            $result = [PKI.Cert]::CertSetCertificateContextProperty(\n                $certHandle, $CERT_NOT_BEFORE_FILETIME_PROP_ID, 0, [System.IntPtr]::Zero\n            )\n            if ($result) {\n                Write-Host \"[Success] Clear CERT_NOT_BEFORE_FILETIME_PROP_ID property $subject\"\n            } else {\n                Write-Host \"[Fail] Clear CERT_NOT_BEFORE_FILETIME_PROP_ID property $subject\"\n            }\n        }\n    }\n}\n\nfunction Disable-RootAutoUpdate {\n    Write-Host \"Disable auto root update mechanism\"\n    $regPath = \"HKLM:\\Software\\Policies\\Microsoft\\SystemCertificates\\AuthRoot\"\n    $regKey = \"DisableRootAutoUpdate\"\n\n    # Create the registry key if it doesn't exist\n    if (-not (Test-Path $regPath)) {\n        Write-Verbose \"Creating $regPath\"\n        New-Item $regPath | Out-Null\n    }\n\n    Set-ItemProperty $regPath -Name $regKey -Type DWord -Value 1\n}\n\n# Property to remove\n$CERT_NOT_BEFORE_FILETIME_PROP_ID = 126\n\n# Add extended cert type\nAdd-ExtendedCertType\n\n# Get certificates without property CERT_NOT_BEFORE_FILETIME_PROP_ID\n$certsWithoutPropId = Get-CertificatesWithoutPropId\n\n# Download and install the latest version of root ca list\nImport-SSTFromWU\n\n# Clear property CERT_NOT_BEFORE_FILETIME_PROP_ID\nif ($certsWithoutPropId.Count -gt 0) {\n    Clear-CertificatesPropId -CertsWithoutPropId $certsWithoutPropId\n} else {\n    Write-Host \"Nothing to clear\"\n}\n\n# Disable auto root update mechanism\nDisable-RootAutoUpdate\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Ruby.ps1",
    "content": "################################################################################\n##  File:  Install-Ruby.ps1\n##  Desc:  Install Ruby using the RubyInstaller2 package and set the default Ruby version\n################################################################################\n\n# Most of this logic is from\n# https://github.com/ruby/setup-ruby/blob/master/windows.js\nfunction Install-Ruby {\n    param(\n        [String] $PackagePath,\n        [String] $Architecture = \"x64\"\n    )\n\n    # Create Ruby toolcache folder\n    $rubyToolcachePath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath \"Ruby\"\n    if (-not (Test-Path $rubyToolcachePath)) {\n        Write-Host \"Creating Ruby toolcache folder\"\n        New-Item -ItemType Directory -Path $rubyToolcachePath | Out-Null\n    }\n    \n    $packageName = [IO.Path]::GetFileNameWithoutExtension((Split-Path -Path $PackagePath -Leaf))\n    Write-Host \"Expanding Ruby archive $packageName\"\n    $tempFolder = Join-Path -Path $rubyToolcachePath -ChildPath $packageName\n    Expand-7ZipArchive -Path $PackagePath -DestinationPath $rubyToolcachePath\n\n    # Get Ruby version from binaries\n    $rubyVersion = & \"$tempFolder\\bin\\ruby.exe\" -e \"print RUBY_VERSION\"\n    if (($LASTEXITCODE -ne 0) -or (-not $rubyVersion)) {\n        throw \"Unable to determine Ruby version. Exit code: $LASTEXITCODE, output: '$rubyVersion'\"\n    }\n    Write-Host \"Ruby version is $rubyVersion\"\n\n    $rubyVersionPath = Join-Path -Path $rubyToolcachePath -ChildPath $rubyVersion\n    $rubyArchPath = Join-Path -Path $rubyVersionPath -ChildPath $Architecture\n\n    Write-Host \"Creating Ruby '${rubyVersion}' folder in '${rubyVersionPath}'\"\n    New-Item -ItemType Directory -Path $rubyVersionPath -Force | Out-Null\n\n    Write-Host \"Moving Ruby '${rubyVersion}' files to '${rubyArchPath}'\"\n    Invoke-ScriptBlockWithRetry -Command {\n        Move-Item -Path $tempFolder -Destination $rubyArchPath -ErrorAction Stop | Out-Null\n    }\n\n    Write-Host \"Removing Ruby '${rubyVersion}' documentation '${rubyArchPath}\\share\\doc' folder\"\n    Remove-Item -Path \"${rubyArchPath}\\share\\doc\" -Force -Recurse -ErrorAction Ignore\n\n    Write-Host \"Creating complete file for Ruby $rubyVersion $Architecture\"\n    New-Item -ItemType File -Path $rubyVersionPath -Name \"$Architecture.complete\" | Out-Null\n}\n\nfunction Set-DefaultRubyVersion {\n    param(\n        [Parameter(Mandatory = $true)]\n        [version] $Version,\n        [Alias(\"Arch\")]\n        [string] $Architecture = \"x64\"\n    )\n\n    $rubyPath = Join-Path $env:AGENT_TOOLSDIRECTORY \"/Ruby/${Version}*/${Architecture}/bin\"\n    $rubyDir = (Get-Item -Path $rubyPath).FullName\n\n    Write-Host \"Use Ruby ${Version} as a system Ruby\"\n    Add-MachinePathItem -PathItem $rubyDir | Out-Null\n}\n\n# Install Ruby\n$rubyTools = (Get-ToolsetContent).toolcache | Where-Object { $_.name -eq \"Ruby\" }\n$rubyToolVersions = $rubyTools.versions\n\nWrite-Host \"Starting installation Ruby...\"\nforeach ($rubyVersion in $rubyToolVersions) {\n    Write-Host \"Starting Ruby $rubyVersion installation\"\n    $downloadUrl = Resolve-GithubReleaseAssetUrl `\n        -Repo \"oneclick/rubyinstaller2\" `\n        -Version \"$rubyVersion*\" `\n        -UrlMatchPattern \"*-x64.7z\"\n    $packagePath = Invoke-DownloadWithRetry $downloadUrl\n    Install-Ruby -PackagePath $packagePath\n}\n\nSet-DefaultRubyVersion -Version $rubyTools.default -Arch $rubyTools.arch\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Rust.ps1",
    "content": "################################################################################\n##  File:  Install-Rust.ps1\n##  Desc:  Install Rust for Windows\n##  Supply chain security: checksum validation for bootstrap, managed by rustup for workloads\n################################################################################\n\n# Rust Env\n$env:RUSTUP_HOME = \"C:\\Users\\Default\\.rustup\"\n$env:CARGO_HOME = \"C:\\Users\\Default\\.cargo\"\n\n# Download the latest rustup-init.exe for Windows x64\n# See https://rustup.rs/#\n$rustupPath = Invoke-DownloadWithRetry \"https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe\"\n\n#region Supply chain security\n$distributorFileHash = (Invoke-RestMethod -Uri 'https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe.sha256').Trim()\nTest-FileChecksum $rustupPath -ExpectedSHA256Sum $distributorFileHash\n#endregion\n\n# Install Rust by running rustup-init.exe (disabling the confirmation prompt with -y)\n& $rustupPath -y --default-toolchain=stable --profile=minimal\nif ($LASTEXITCODE -ne 0) {\n    throw \"Rust installation failed with exit code $LASTEXITCODE\"\n}\n\n# Add %USERPROFILE%\\.cargo\\bin to USER PATH\nAdd-DefaultPathItem \"%USERPROFILE%\\.cargo\\bin\"\n# Add Rust binaries to the path\n$env:Path += \";$env:CARGO_HOME\\bin\"\n\n# Add i686 target for building 32-bit binaries\nrustup target add i686-pc-windows-msvc\n\n# Add target for building mingw-w64 binaries\nrustup target add x86_64-pc-windows-gnu\n\n# Install common tools\nrustup component add rustfmt clippy\nif ($LASTEXITCODE -ne 0) {\n    throw \"Rust component installation failed with exit code $LASTEXITCODE\"\n}\nif (-not (Test-IsWin25)) {\n    cargo install --locked bindgen-cli cbindgen cargo-audit cargo-outdated\n    if ($LASTEXITCODE -ne 0) {\n        throw \"Rust tools installation failed with exit code $LASTEXITCODE\"\n    }\n    # Cleanup Cargo crates cache\n    Remove-Item \"${env:CARGO_HOME}\\registry\\*\" -Recurse -Force\n}\n\nInvoke-PesterTests -TestFile \"Rust\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-SQLOLEDBDriver.ps1",
    "content": "################################################################################\n##  File:  Install-SQLOLEDBDriver.ps1\n##  Desc:  Install OLE DB Driver for SQL Server\n################################################################################\n\n# Install OLE DB Driver 18\nInstall-Binary -Type MSI `\n    -Url \"https://go.microsoft.com/fwlink/?linkid=2242656\" `\n    -ExtraInstallArgs @(\"ADDLOCAL=ALL\", \"IACCEPTMSOLEDBSQLLICENSETERMS=YES\") `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n\n# Install OLE DB Driver 19\nInstall-Binary -Type MSI `\n    -Url \"https://go.microsoft.com/fwlink/?linkid=2318101\" `\n    -ExtraInstallArgs @(\"ADDLOCAL=ALL\", \"IACCEPTMSOLEDBSQLLICENSETERMS=YES\") `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n"
  },
  {
    "path": "images/windows/scripts/build/Install-SQLPowerShellTools.ps1",
    "content": "################################################################################\n##  File:  Install-SQLPowerShellTools.ps1\n##  Desc:  Install SQL PowerShell tool\n################################################################################\n\n$baseUrl = \"https://download.microsoft.com/download/B/1/7/B1783FE9-717B-4F78-A39A-A2E27E3D679D/ENU/x64\"\n\n# install required MSIs\nInstall-Binary `\n    -Url \"${baseUrl}/SQLSysClrTypes.msi\" `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n\nInstall-Binary `\n    -Url \"${baseUrl}/SharedManagementObjects.msi\" `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n\nInstall-Binary `\n    -Url \"${baseUrl}/PowerShellTools.msi\" `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Sbt.ps1",
    "content": "################################################################################\n##  File:  Install-Sbt.ps1\n##  Desc:  Install sbt for Windows\n################################################################################\n\n# Install the latest version of sbt.\n# See https://chocolatey.org/packages/sbt\nInstall-ChocoPackage sbt\n\n$env:SBT_HOME=\"${env:ProgramFiles(x86)}\\sbt\"\n\n# Add sbt binaries to the path\nAdd-MachinePathItem \"$env:SBT_HOME\\bin\"\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"Sbt\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Selenium.ps1",
    "content": "################################################################################\n##  File:  Install-Selenium.ps1\n##  Desc:  Install Selenium Server standalone\n################################################################################\n\n# Create Selenium directory\n$seleniumDirectory = \"C:\\selenium\\\"\nNew-Item -ItemType directory -Path $seleniumDirectory\n\n# Download Selenium\n$seleniumMajorVersion = (Get-ToolsetContent).selenium.version\n\n$seleniumDownloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"SeleniumHQ/selenium\" `\n    -Version \"$seleniumMajorVersion.*\" `\n    -Asset \"selenium-server-*.jar\" `\n    -AllowMultipleMatches\n\n$seleniumBinPath = Join-Path $seleniumDirectory \"selenium-server.jar\"\nInvoke-DownloadWithRetry -Url $seleniumDownloadUrl -Path $seleniumBinPath\n\n# Create an empty file to retrieve Selenium version\n$seleniumFullVersion = $seleniumDownloadUrl.Split(\"-\")[1].Split(\"/\")[0]\nNew-Item -Path $seleniumDirectory -Name \"selenium-server-$seleniumFullVersion\"\n\n# Add SELENIUM_JAR_PATH environment variable\n[Environment]::SetEnvironmentVariable(\"SELENIUM_JAR_PATH\", $seleniumBinPath, \"Machine\")\n\nInvoke-PesterTests -TestFile \"Browsers\" -TestName \"Selenium\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-ServiceFabricSDK.ps1",
    "content": "################################################################################\n##  File:  Install-ServiceFabricSDK.ps1\n##  Desc:  Install webpicmd and then the service fabric sdk\n##         must be install after Visual Studio\n##  Supply chain security: checksum validation\n################################################################################\n\n# Creating 'Installer' cache folder if it doesn't exist\nNew-Item -Path 'C:\\Windows\\Installer' -ItemType Directory -Force\n\n# Get Service Fabric components versions\n$runtimeVersion = (Get-ToolsetContent).serviceFabric.runtime.version\n$sdkVersion = (Get-ToolsetContent).serviceFabric.sdk.version\n\n$urlBase = \"https://download.microsoft.com/download/b/8/a/b8a2fb98-0ec1-41e5-be98-9d8b5abf7856\"\n\n# Install Service Fabric Runtime for Windows\nInstall-Binary `\n    -Url \"${urlBase}/MicrosoftServiceFabric.${runtimeVersion}.exe\" `\n    -InstallArgs @(\"/accepteula \", \"/quiet\", \"/force\") `\n    -ExpectedSHA256Sum (Get-ToolsetContent).serviceFabric.runtime.checksum\n\n\n# Install Service Fabric SDK\nInstall-Binary `\n    -Url \"${urlBase}/MicrosoftServiceFabricSDK.${sdkVersion}.msi\" `\n    -ExpectedSHA256Sum (Get-ToolsetContent).serviceFabric.sdk.checksum\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"ServiceFabricSDK\" \n"
  },
  {
    "path": "images/windows/scripts/build/Install-Stack.ps1",
    "content": "################################################################################\n##  File:  Install-Stack.ps1\n##  Desc:  Install Stack for Windows\n##  Supply chain security: Stack - checksum validation\n################################################################################\n\nWrite-Host \"Get the latest Stack version...\"\n\n$version = (Get-GithubReleasesByVersion -Repo \"commercialhaskell/stack\" -Version \"latest\" -WithAssetsOnly).version\n\n$downloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"commercialhaskell/stack\" `\n    -Version $version `\n    -UrlMatchPattern \"stack-*-windows-x86_64.zip\"\n\nWrite-Host \"Download stack archive\"\n$stackToolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY \"stack\\$version\"\n$destinationPath = Join-Path $stackToolcachePath \"x64\"\n$stackArchivePath = Invoke-DownloadWithRetry $downloadUrl\n\n#region Supply chain security - Stack\n$externalHash = Get-ChecksumFromUrl -Type \"SHA256\" `\n    -Url \"$downloadUrl.sha256\" `\n    -FileName (Split-Path $downloadUrl -Leaf)\nTest-FileChecksum $stackArchivePath -ExpectedSHA256Sum $externalHash\n#endregion\n\nWrite-Host \"Expand stack archive\"\nExpand-7ZipArchive -Path $stackArchivePath -DestinationPath $destinationPath\n\nNew-Item -Name \"x64.complete\" -Path $stackToolcachePath\n\nAdd-MachinePathItem -PathItem $destinationPath\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"Stack\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Toolset.ps1",
    "content": "################################################################################\n##  File:  Install-Toolset.ps1\n##  Team:  CI-Build\n##  Desc:  Install toolset\n################################################################################\n\n[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13\n\nFunction Install-Asset {\n    param(\n        [Parameter(Mandatory=$true)]\n        [object] $ReleaseAsset\n    )\n\n    $releaseAssetName = [System.IO.Path]::GetFileNameWithoutExtension($ReleaseAsset.filename)\n    $assetFolderPath = Join-Path $env:TEMP_DIR $releaseAssetName\n    $assetArchivePath = Invoke-DownloadWithRetry $ReleaseAsset.download_url\n\n    Write-Host \"Extract $($ReleaseAsset.filename) content...\"\n    if ($assetArchivePath.EndsWith(\".tar.gz\")) {\n        $assetTarPath = $assetArchivePath.TrimEnd(\".tar.gz\")\n        Expand-7ZipArchive -Path $assetArchivePath -DestinationPath $assetTarPath\n        Expand-7ZipArchive -Path $assetTarPath -DestinationPath $assetFolderPath\n    } else {\n        Expand-7ZipArchive -Path $assetArchivePath -DestinationPath $assetFolderPath\n    }\n\n    Write-Host \"Invoke installation script...\"\n    Push-Location -Path $assetFolderPath\n    Invoke-Expression .\\setup.ps1\n    Pop-Location\n}\n\n# Get toolcache content from toolset\n$toolsToInstall = @(\"Python\", \"Node\", \"Go\")\n$tools = Get-ToolsetContent | Select-Object -ExpandProperty toolcache | Where-Object { $toolsToInstall -contains $_.Name }\n\nforeach ($tool in $tools) {\n    # Get versions manifest for current tool\n    # Invoke-RestMethod doesn't support retry in PowerShell 5.1\n    $assets = Invoke-ScriptBlockWithRetry -Command {\n        Invoke-RestMethod $tool.url\n    }\n\n    # Get github release asset for each version\n    foreach ($toolVersion in $tool.versions) {\n        $asset = $assets `\n        | Where-Object version -like $toolVersion `\n        | Select-Object -ExpandProperty files `\n        | Where-Object { ($_.platform -eq $tool.platform) -and ($_.arch -eq $tool.arch) -and ($_.toolset -eq $tool.toolset) } `\n        | Select-Object -First 1\n\n        if (-not $asset) {\n            throw \"Asset for $($tool.name) $toolVersion $($tool.arch) not found in versions manifest\"\n        }\n\n        Write-Host \"Installing $($tool.name) $toolVersion $($tool.arch)...\"\n        Install-Asset -ReleaseAsset $asset\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/build/Install-TortoiseSvn.ps1",
    "content": "################################################################################\n##  File:  Install-TortoiseSvn.ps1\n##  Desc:  Install TortoiseSvn\n################################################################################\n\nInstall-ChocoPackage tortoisesvn\n"
  },
  {
    "path": "images/windows/scripts/build/Install-VSExtensions.ps1",
    "content": "###################################################################################\n##  File:  Install-VSExtensions.ps1\n##  Desc:  Install the Visual Studio Extensions from toolset.json\n###################################################################################\n\n$toolset = Get-ToolsetContent\n$vsixPackagesList = $toolset.visualStudio.vsix\nif (-not $vsixPackagesList) {\n    Write-Host \"No extensions to install\"\n    exit 0\n}\n\n$vsixPackagesList | ForEach-Object {\n    # Retrieve cdn endpoint to avoid HTTP error 429\n    # https://github.com/actions/runner-images/issues/3074\n    $vsixPackage = Get-VsixInfoFromMarketplace $_\n    Write-Host \"Installing $vsixPackage\"\n    if ($vsixPackage.FileName.EndsWith(\".vsix\")) {\n        Install-VSIXFromUrl $vsixPackage.DownloadUri\n    } else {\n        Install-Binary `\n            -Url $vsixPackage.DownloadUri `\n            -InstallArgs @('/install', '/quiet', '/norestart')\n    }\n}\n\nInvoke-PesterTests -TestFile \"Vsix\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Vcpkg.ps1",
    "content": "################################################################################\n##  File:  Install-Vcpkg.ps1\n##  Desc:  Install vcpkg\n################################################################################\n\n$Uri = 'https://github.com/Microsoft/vcpkg.git'\n$InstallDir = 'C:\\vcpkg'\n$VcpkgExecPath = 'vcpkg.exe'\n\ngit clone $Uri $InstallDir -q\n\n# Build and integrate vcpkg\nInvoke-Expression \"$InstallDir\\bootstrap-vcpkg.bat\"\nif ($LASTEXITCODE -ne 0) {\n    throw \"vcpkg bootstrap failed with exit code $LASTEXITCODE\"\n}\nInvoke-Expression \"$InstallDir\\$VcpkgExecPath integrate install\"\nif ($LASTEXITCODE -ne 0) {\n    throw \"vcpkg integration failed with exit code $LASTEXITCODE\"\n}\n\n# Add vcpkg to system environment\nAdd-MachinePathItem $InstallDir\n[Environment]::SetEnvironmentVariable(\"VCPKG_INSTALLATION_ROOT\", $InstallDir, \"Machine\")\nUpdate-Environment\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"Vcpkg\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-VisualStudio.ps1",
    "content": "################################################################################\n##  File:  Install-VisualStudio.ps1\n##  Desc:  Install Visual Studio\n################################################################################\n$vsToolset = (Get-ToolsetContent).visualStudio\n\n# Install Visual Studio for Windows 22 and 25 with InstallChannel\nInstall-VisualStudio `\n    -Version $vsToolset.subversion `\n    -Edition $vsToolset.edition `\n    -Channel $vsToolset.channel `\n    -InstallChannelUri $vsToolset.installChannelUri `\n    -RequiredComponents $vsToolset.workloads `\n    -ExtraArgs \"--allWorkloads --includeRecommended --remove Component.CPython3.x64\"\n\n# Find the version of VS installed for this instance\n# Only supports a single instance\n$vsProgramData = Get-Item -Path \"C:\\ProgramData\\Microsoft\\VisualStudio\\Packages\\_Instances\"\n$instanceFolders = Get-ChildItem -Path $vsProgramData.FullName\n\nif ($instanceFolders -is [array]) {\n    throw \"More than one instance installed\"\n}\n\n# Updating content of MachineState.json file to disable autoupdate of VSIX extensions\n$vsInstallRoot = (Get-VisualStudioInstance).InstallationPath\n$newContent = '{\"Extensions\":[{\"Key\":\"1e906ff5-9da8-4091-a299-5c253c55fdc9\",\"Value\":{\"ShouldAutoUpdate\":false}},{\"Key\":\"Microsoft.VisualStudio.Web.AzureFunctions\",\"Value\":{\"ShouldAutoUpdate\":false}}],\"ShouldAutoUpdate\":false,\"ShouldCheckForUpdates\":false}'\nSet-Content -Path \"$vsInstallRoot\\Common7\\IDE\\Extensions\\MachineState.json\" -Value $newContent\n\nif (Test-IsWin22) {\n    # Install Windows 10 SDK version 10.0.17763\n    Install-Binary -Type EXE `\n    -Url 'https://go.microsoft.com/fwlink/p/?LinkID=2033908' `\n    -InstallArgs @(\"/q\", \"/norestart\", \"/ceip off\", \"/features OptionId.UWPManaged OptionId.UWPCPP OptionId.UWPLocalized OptionId.DesktopCPPx86 OptionId.DesktopCPPx64 OptionId.DesktopCPParm64\") `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n}\n\n# Install Windows 11 SDK version 10.0.26100\nInstall-Binary -Type EXE `\n    -Url 'https://go.microsoft.com/fwlink/?linkid=2349110' `\n    -InstallArgs @(\"/q\", \"/norestart\", \"/ceip off\", \"/features OptionId.UWPManaged OptionId.UWPCPP OptionId.UWPLocalized OptionId.DesktopCPPx86 OptionId.DesktopCPPx64 OptionId.DesktopCPParm64\") `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n\n# Enable Windows Desktop Debuggers (cdb.exe) on Windows Server 2025\nif (Test-IsWin25) {\n    $installerEntry = Get-ItemProperty HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\* `\n        | Where-Object { $_.DisplayName -match \"Windows Software Development Kit\" } `\n        | Sort-Object DisplayVersion -Descending | Select-Object -First 1\n    \n    if ($installerEntry -and $installerEntry.BundleCachePath) {\n        Install-Binary -Type EXE `\n            -LocalPath $installerEntry.BundleCachePath `\n            -InstallArgs @(\"/features\", \"OptionId.WindowsDesktopDebuggers\", \"/q\", \"/norestart\") `\n            -ExpectedSubject $(Get-MicrosoftPublisher)\n    }\n}\n\nInvoke-PesterTests -TestFile \"VisualStudio\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-WDK.ps1",
    "content": "################################################################################\n##  File:  Install-WDK.ps1\n##  Desc:  Install the Windows Driver Kit\n################################################################################\n\n# Requires Windows SDK with the same version number as the WDK\nif (Test-IsWin22) {\n    # SDK is available through Visual Studio\n    $wdkUrl = \"https://go.microsoft.com/fwlink/?linkid=2324617\"\n} else {\n    throw \"Invalid version of Visual Studio is found. Windows Server 2022 is required\"\n}\n\n# Install all features without showing the GUI using wdksetup.exe\nInstall-Binary -Type EXE `\n    -Url $wdkUrl `\n    -InstallArgs @(\"/features\", \"+\", \"/quiet\") `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n\nInvoke-PesterTests -TestFile \"WDK\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-WSL2.ps1",
    "content": "Write-Host \"Install WSL2\"\n\n$version = (Get-GithubReleasesByVersion -Repo \"microsoft/WSL\" -Version \"latest\").version\n$downloadUrl =  Resolve-GithubReleaseAssetUrl `\n    -Repo \"microsoft/WSL\" `\n    -Version $version `\n    -UrlMatchPattern \"wsl.*.x64.msi\"\n\nInstall-Binary -Type MSI `\n    -Url $downloadUrl\n\nWrite-Host \"Performing wsl --install --no-distribution\"\nwsl.exe --install --no-distribution\n\nInvoke-PesterTests -TestFile \"WindowsFeatures\" -TestName \"WSL2\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-WebPlatformInstaller.ps1",
    "content": "################################################################################\n##  File:  Install-WebPI.ps1\n##  Desc:  Install WebPlatformInstaller\n################################################################################\n\nInstall-Binary -Type MSI `\n    -Url 'https://go.microsoft.com/fwlink/?LinkId=287166' `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"WebPlatformInstaller\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-WinAppDriver.ps1",
    "content": "####################################################################################\n##  File:  Install-WinAppDriver.ps1\n##  Desc:  Install Windows Application Driver (WinAppDriver)\n####################################################################################\n\n[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n$downloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"microsoft/WinAppDriver\" `\n    -Version \"latest\" `\n    -UrlMatchPattern \"WindowsApplicationDriver_*.msi\"\n\nInstall-Binary `\n    -Url $downloadUrl `\n    -ExpectedSubject $(Get-MicrosoftPublisher)\n\nInvoke-PesterTests -TestFile \"WinAppDriver\" -TestName \"WinAppDriver\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-WindowsFeatures.ps1",
    "content": "####################################################################################\n##  File:  Install-WindowsFeatures.ps1\n##  Desc:  Install Windows Features\n####################################################################################\n\n$windowsFeatures = (Get-ToolsetContent).windowsFeatures\n\nforeach ($feature in $windowsFeatures) {\n    if ($feature.optionalFeature) {\n        Write-Host \"Activating Windows Optional Feature '$($feature.name)'...\"\n        Enable-WindowsOptionalFeature -Online -FeatureName $feature.name -NoRestart\n\n        $resultSuccess = $?\n    } else {\n        Write-Host \"Activating Windows Feature '$($feature.name)'...\"\n        $arguments = @{\n            Name                   = $feature.name\n            IncludeAllSubFeature   = [System.Convert]::ToBoolean($feature.includeAllSubFeatures)\n            IncludeManagementTools = [System.Convert]::ToBoolean($feature.includeManagementTools)\n        }\n        $result = Install-WindowsFeature @arguments\n\n        $resultSuccess = $result.Success\n    }\n\n    if ($resultSuccess) {\n        Write-Host \"Windows Feature '$($feature.name)' was activated successfully\"\n    } else {\n        throw \"Failed to activate Windows Feature '$($feature.name)'\"\n    }\n}\n\n# it improves Android emulator launch on Windows Server\n# https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/manage/manage-hyper-v-scheduler-types\nbcdedit /set hypervisorschedulertype root\nif ($LASTEXITCODE -ne 0) {\n    throw \"Failed to set hypervisorschedulertype to root\"\n}\n"
  },
  {
    "path": "images/windows/scripts/build/Install-WindowsUpdates.ps1",
    "content": "################################################################################\n##  File:  Install-WindowsUpdates.ps1\n##  Desc:  Install Windows Updates.\n##         Should be run at end, just before SoftwareReport and Finalize-VM.ps1.\n################################################################################\n\nfunction Install-WindowsUpdates {\n    Write-Host \"Starting wuauserv\"\n    Start-Service -Name wuauserv -PassThru | Out-Host\n\n    # Temporarily exclude Windows update KB5034439 since it throws an error.\n    # The known issue (https://support.microsoft.com/en-au/topic/kb5034439-windows-recovery-environment-update-for-azure-stack-hci-version-22h2-and-windows-server-2022-january-9-2024-6f9d26e6-784c-4503-a3c6-0beedda443ca)\n    Write-Host \"Getting list of available windows updates\"\n    Get-WindowsUpdate -MicrosoftUpdate -NotKBArticleID \"KB5034439\" -OutVariable updates | Out-Host\n\n    if ( -not $updates ) {\n        Write-Host \"There are no windows updates to install\"\n        return\n    }\n\n    Write-Host \"Installing windows updates\"\n    Get-WindowsUpdate -MicrosoftUpdate -NotKBArticleID \"KB5034439\" -AcceptAll -Install -IgnoreUserInput -IgnoreReboot | Out-Host\n\n    Write-Host \"Validating windows updates installation\"\n    # Get-WUHistory doesn't support Windows Server 2022\n    $notFailedUpdateNames = Get-WindowsUpdateStates | Where-Object { $_.State -in (\"Installed\", \"Running\") } | Select-Object -ExpandProperty Title\n    # We ignore Microsoft Defender Antivirus updates; Azure service updates AV automatically\n    $failedUpdates = $updates[0] | Where-Object Title -notmatch \"Microsoft Defender Antivirus\" | Where-Object { -not ($notFailedUpdateNames -match $_.KB) }\n\n    if ( $failedUpdates ) {\n        throw \"Windows updates failed to install: $($failedUpdates.KB)\"\n    }\n}\n\nInstall-WindowsUpdates\n\n# Create complete windows update file\nNew-Item -Path $env:windir -Name WindowsUpdateDone.txt -ItemType File | Out-Null\n"
  },
  {
    "path": "images/windows/scripts/build/Install-WindowsUpdatesAfterReboot.ps1",
    "content": "################################################################################\n##  File: Install-WindowsUpdatesAfterReboot.ps1\n##  Desc: Waits for Windows Updates to finish installing after reboot\n################################################################################\n\nInvoke-ScriptBlockWithRetry -RetryCount 10 -RetryIntervalSeconds 120 -Command {\n    $inProgress = Get-WindowsUpdateStates | Where-Object State -eq \"Running\" | Where-Object Title -notmatch \"Microsoft Defender Antivirus\"\n    if ( $inProgress ) {\n        $title = $inProgress.Title -join \"`n\"\n        throw \"Windows updates are still installing: $title\"\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Wix.ps1",
    "content": "################################################################################\n##  File:  Install-Wix.ps1\n##  Desc:  Install WIX.\n################################################################################\n\nInstall-ChocoPackage wixtoolset -ArgumentList \"--force\"\n\nUpdate-Environment\n$currentPath = [System.Environment]::GetEnvironmentVariable(\"PATH\", \"Machine\")\n$newPath = $currentPath + \";$(Join-Path -Path $env:WIX -ChildPath \"bin\")\"\n[Environment]::SetEnvironmentVariable(\"PATH\", $newPath, \"Machine\")\nUpdate-Environment\n\nInvoke-PesterTests -TestFile \"Wix\"\n"
  },
  {
    "path": "images/windows/scripts/build/Install-Zstd.ps1",
    "content": "################################################################################\n##  File:  Install-Zstd.ps1\n##  Desc:  Install zstd\n################################################################################\n\n$downloadUrl = Resolve-GithubReleaseAssetUrl `\n    -Repo \"facebook/zstd\" `\n    -Version \"latest\" `\n    -UrlMatchPattern \"zstd-*-win64.zip\"\n$zstdArchivePath = Invoke-DownloadWithRetry $downloadUrl\n$zstdName = [IO.Path]::GetFileNameWithoutExtension($zstdArchivePath)\n\n$toolPath = \"C:\\tools\"\n$zstdPath = Join-Path $toolPath zstd\n$filesInArchive = 7z l $zstdArchivePath | Out-String\n\nif ($filesInArchive.Contains($zstdName)) {\n    Expand-7ZipArchive -Path $zstdArchivePath -DestinationPath $toolPath\n    Invoke-ScriptBlockWithRetry -Command {\n        Move-Item -Path \"${zstdPath}*\" -Destination $zstdPath -ErrorAction Stop\n    }\n} else {\n    Expand-7ZipArchive -Path $zstdArchivePath -DestinationPath $zstdPath\n}\n\n# Add zstd-win64 to PATH\nAdd-MachinePathItem $zstdPath\n\nInvoke-PesterTests -TestFile \"Tools\" -TestName \"Zstd\"\n"
  },
  {
    "path": "images/windows/scripts/build/Invoke-Cleanup.ps1",
    "content": "################################################################################\n##  File:  Invoke-Cleanup.ps1\n##  Desc:  Cleanup WinSxS, temp, cache and compress some directories\n################################################################################\n\nWrite-Host \"Cleanup WinSxS\"\ndism.exe /online /Cleanup-Image /StartComponentCleanup /ResetBase\nif ($LASTEXITCODE -ne 0) {\n    throw \"Failed to cleanup WinSxS\"\n}\n\nWrite-Host \"Clean up various directories\"\n@(\n    \"$env:SystemDrive\\Recovery\",\n    \"$env:SystemRoot\\logs\",\n    \"$env:SystemRoot\\winsxs\\manifestcache\",\n    \"$env:SystemRoot\\Temp\",\n    \"$env:SystemDrive\\Users\\$env:INSTALL_USER\\AppData\\Local\\Temp\",\n    \"$env:TEMP\",\n    \"$env:AZURE_CONFIG_DIR\\logs\",\n    \"$env:AZURE_CONFIG_DIR\\commands\",\n    \"$env:AZURE_CONFIG_DIR\\telemetry\"\n) | ForEach-Object {\n    if (Test-Path $_) {\n        Write-Host \"Removing $_\"\n        cmd /c \"takeown /d Y /R /f $_ 2>&1\" | Out-Null\n        if ($LASTEXITCODE -ne 0) {\n            throw \"Failed to take ownership of $_\"\n        }\n        cmd /c \"icacls $_ /grant:r administrators:f /t /c /q 2>&1\" | Out-Null\n        if ($LASTEXITCODE -ne 0) {\n            throw \"Failed to grant administrators full control of $_\"\n        }\n        Remove-Item $_ -Recurse -Force -ErrorAction SilentlyContinue | Out-Null\n    }\n}\n\n# Remove AllUsersAllHosts profile\nRemove-Item $profile.AllUsersAllHosts -Force -ErrorAction SilentlyContinue | Out-Null\n\n# Clean yarn and npm cache\ncmd /c \"yarn cache clean 2>&1\" | Out-Null\nif ($LASTEXITCODE -ne 0) {\n    throw \"Failed to clean yarn cache\"\n}\n\ncmd /c \"npm cache clean --force 2>&1\" | Out-Null\nif ($LASTEXITCODE -ne 0) {\n    throw \"Failed to clean npm cache\"\n}\n\nif (Test-IsWin25) {\n    $directoriesToCompact = @(\n        \"C:\\Program Files (x86)\\Android\",\n        \"C:\\Program Files\\dotnet\",\n        \"$env:SystemRoot\\assembly\",\n        \"$env:SystemRoot\\WinSxS\"\n    )\n    Write-Host \"Starting Image slimming process\"\n    $start = get-date\n    $ErrorActionPreviousValue = $ErrorActionPreference\n    $ErrorActionPreference = 'SilentlyContinue'\n    foreach ($directory in $directoriesToCompact) {\n        Write-Host \"Compressing '$directory' directory\"\n        $compressionResult =  & compact /s:\"$directory\" /c /a /i /EXE:LZX *\n        $compressionResult | Select-Object -Last 3\n    }\n    $ErrorActionPreference = $ErrorActionPreviousValue\n    $finish = get-date\n    $time = \"$(($finish - $start).Minutes):$(($finish - $start).Seconds)\"\n    Write-Host \"The process took a total of $time (in minutes:seconds)\"\n}\n"
  },
  {
    "path": "images/windows/scripts/build/Post-Build-Validation.ps1",
    "content": "################################################################################\n##  File:  post-build-validation.sh\n##  Desc:  Validate different aspects of the image after build\n################################################################################\n\nWrite-Host \"Test Microsoft Defender not set up using 'sc query sense'\"\n$response =  sc query sense\nforeach ($item in $response) {\n    if ($item -match \"STATE\") {\n        $state = $item.Split(\":\")[1].Trim()\n        if ($state -notmatch \"RUNNING\") {\n            Write-Host \"MDE is not running\"\n        } else {\n            Write-Host \"MDE is running\"\n            exit 1\n        }\n    }\n}\n\nWrite-Host \"Test Microsoft Defender not set up by checking for the MDE extension\"\nif (Test-Path -Path \"C:\\Packages\\Plugins\\Microsoft.Azure.AzureDefenderForServers.MDE.Windows\") {\n    Write-Error \"MDE extension detected, MDE is more likely installed on the system\"\n    exit 1\n} else {\n    Write-Host \"MDE is not setup on the system\"\n}\n"
  },
  {
    "path": "images/windows/scripts/docs-gen/Generate-SoftwareReport.ps1",
    "content": "using module ./software-report-base/SoftwareReport.psm1\nusing module ./software-report-base/SoftwareReport.Nodes.psm1\n\n$global:ErrorActionPreference = \"Stop\"\n$global:ProgressPreference = \"SilentlyContinue\"\n$ErrorView = \"NormalView\"\nSet-StrictMode -Version Latest\n\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Android.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Browsers.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.CachedTools.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Common.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Databases.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Helpers.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Tools.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.Java.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.WebServers.psm1\") -DisableNameChecking\nImport-Module (Join-Path $PSScriptRoot \"SoftwareReport.VisualStudio.psm1\") -DisableNameChecking\n\n# Software report\n$softwareReport = [SoftwareReport]::new($(Build-OSInfoSection))\n$optionalFeatures = $softwareReport.Root.AddHeader(\"Windows features\")\n$optionalFeatures.AddToolVersion(\"Windows Subsystem for Linux (WSLv1):\", \"Enabled\")\nif (Test-IsWin25) {\n    $optionalFeatures.AddToolVersion(\"Windows Subsystem for Linux (Default, WSLv2):\", $(Get-WSL2Version))\n}\n$installedSoftware = $softwareReport.Root.AddHeader(\"Installed Software\")\n\n# Language and Runtime\n$languageAndRuntime = $installedSoftware.AddHeader(\"Language and Runtime\")\n$languageAndRuntime.AddToolVersion(\"Bash\", $(Get-BashVersion))\n$languageAndRuntime.AddToolVersion(\"Go\", $(Get-GoVersion))\n$languageAndRuntime.AddToolVersion(\"Julia\", $(Get-JuliaVersion))\n$languageAndRuntime.AddToolVersion(\"Kotlin\", $(Get-KotlinVersion))\n$languageAndRuntime.AddToolVersion(\"LLVM\", $(Get-LLVMVersion))\n$languageAndRuntime.AddToolVersion(\"Node\", $(Get-NodeVersion))\n$languageAndRuntime.AddToolVersion(\"Perl\", $(Get-PerlVersion))\n$languageAndRuntime.AddToolVersion(\"PHP\", $(Get-PHPVersion))\n$languageAndRuntime.AddToolVersion(\"Python\", $(Get-PythonVersion))\n$languageAndRuntime.AddToolVersion(\"Ruby\", $(Get-RubyVersion))\n\n# Package Management\n$packageManagement = $installedSoftware.AddHeader(\"Package Management\")\n$packageManagement.AddToolVersion(\"Chocolatey\", $(Get-ChocoVersion))\n$packageManagement.AddToolVersion(\"Composer\", $(Get-ComposerVersion))\n$packageManagement.AddToolVersion(\"Helm\", $(Get-HelmVersion))\n$packageManagement.AddToolVersion(\"Miniconda\", $(Get-CondaVersion))\n$packageManagement.AddToolVersion(\"NPM\", $(Get-NPMVersion))\n$packageManagement.AddToolVersion(\"NuGet\", $(Get-NugetVersion))\n$packageManagement.AddToolVersion(\"pip\", $(Get-PipVersion))\n$packageManagement.AddToolVersion(\"Pipx\", $(Get-PipxVersion))\n$packageManagement.AddToolVersion(\"RubyGems\", $(Get-RubyGemsVersion))\n$packageManagement.AddToolVersion(\"Vcpkg\", $(Get-VcpkgVersion))\n$packageManagement.AddToolVersion(\"Yarn\", $(Get-YarnVersion))\n\n$packageManagement.AddHeader(\"Environment variables\").AddTable($(Build-PackageManagementEnvironmentTable))\n\n# Project Management\n$projectManagement = $installedSoftware.AddHeader(\"Project Management\")\n$projectManagement.AddToolVersion(\"Ant\", $(Get-AntVersion))\n$projectManagement.AddToolVersion(\"Gradle\", $(Get-GradleVersion))\n$projectManagement.AddToolVersion(\"Maven\", $(Get-MavenVersion))\n$projectManagement.AddToolVersion(\"sbt\", $(Get-SbtVersion))\n\n# Tools\n$tools = $installedSoftware.AddHeader(\"Tools\")\n$tools.AddToolVersion(\"7zip\", $(Get-7zipVersion))\n$tools.AddToolVersion(\"aria2\", $(Get-Aria2Version))\n$tools.AddToolVersion(\"azcopy\", $(Get-AzCopyVersion))\n$tools.AddToolVersion(\"Bazel\", $(Get-BazelVersion))\n$tools.AddToolVersion(\"Bazelisk\", $(Get-BazeliskVersion))\n$tools.AddToolVersion(\"Bicep\", $(Get-BicepVersion))\n$tools.AddToolVersion(\"Cabal\", $(Get-CabalVersion))\n$tools.AddToolVersion(\"CMake\", $(Get-CMakeVersion))\n$tools.AddToolVersion(\"CodeQL Action Bundle\", $(Get-CodeQLBundleVersion))\n$tools.AddToolVersion(\"Docker\", $(Get-DockerVersion))\n$tools.AddToolVersion(\"Docker Compose v2\", $(Get-DockerComposeVersionV2))\n$tools.AddToolVersion(\"Docker-wincred\", $(Get-DockerWincredVersion))\n$tools.AddToolVersion(\"ghc\", $(Get-GHCVersion))\n$tools.AddToolVersion(\"Git\", $(Get-GitVersion))\n$tools.AddToolVersion(\"Git LFS\", $(Get-GitLFSVersion))\n$tools.AddToolVersion(\"ImageMagick\", $(Get-ImageMagickVersion))\n$tools.AddToolVersion(\"InnoSetup\", $(Get-InnoSetupVersion))\n$tools.AddToolVersion(\"jq\", $(Get-JQVersion))\n$tools.AddToolVersion(\"Kind\", $(Get-KindVersion))\n$tools.AddToolVersion(\"Kubectl\", $(Get-KubectlVersion))\nif (-not (Test-IsWin25)) {\n    $tools.AddToolVersion(\"Mercurial\", $(Get-MercurialVersion))\n}\n$tools.AddToolVersion(\"gcc\", $(Get-GCCVersion))\n$tools.AddToolVersion(\"gdb\", $(Get-GDBVersion))\n$tools.AddToolVersion(\"GNU Binutils\", $(Get-GNUBinutilsVersion))\n$tools.AddToolVersion(\"Newman\", $(Get-NewmanVersion))\nif (-not (Test-IsWin25)) {\n    $tools.AddToolVersion(\"NSIS\", $(Get-NSISVersion))\n}\n$tools.AddToolVersion(\"OpenSSL\", $(Get-OpenSSLVersion))\n$tools.AddToolVersion(\"Packer\", $(Get-PackerVersion))\n$tools.AddToolVersion(\"Pulumi\", $(Get-PulumiVersion))\n$tools.AddToolVersion(\"R\", $(Get-RVersion))\n$tools.AddToolVersion(\"Service Fabric SDK\", $(Get-ServiceFabricSDKVersion))\n$tools.AddToolVersion(\"Stack\", $(Get-StackVersion))\nif (-not (Test-IsWin25)) {\n    $tools.AddToolVersion(\"Subversion (SVN)\", $(Get-SVNVersion))\n}\n$tools.AddToolVersion(\"Swig\", $(Get-SwigVersion))\n$tools.AddToolVersion(\"VSWhere\", $(Get-VSWhereVersion))\n$tools.AddToolVersion(\"WinAppDriver\", $(Get-WinAppDriver))\n$tools.AddToolVersion(\"WiX Toolset\", $(Get-WixVersion))\n$tools.AddToolVersion(\"yamllint\", $(Get-YAMLLintVersion))\n$tools.AddToolVersion(\"zstd\", $(Get-ZstdVersion))\n$tools.AddToolVersion(\"Ninja\", $(Get-NinjaVersion))\n\n# CLI Tools\n$cliTools = $installedSoftware.AddHeader(\"CLI Tools\")\nif (-not (Test-IsWin25)) {\n    $cliTools.AddToolVersion(\"Alibaba Cloud CLI\", $(Get-AlibabaCLIVersion))\n}\n$cliTools.AddToolVersion(\"AWS CLI\", $(Get-AWSCLIVersion))\n$cliTools.AddToolVersion(\"AWS SAM CLI\", $(Get-AWSSAMVersion))\n$cliTools.AddToolVersion(\"AWS Session Manager CLI\", $(Get-AWSSessionManagerVersion))\n$cliTools.AddToolVersion(\"Azure CLI\", $(Get-AzureCLIVersion))\n$cliTools.AddToolVersion(\"Azure DevOps CLI extension\", $(Get-AzureDevopsExtVersion))\n$cliTools.AddToolVersion(\"GitHub CLI\", $(Get-GHVersion))\n\n# Rust Tools\nInitialize-RustEnvironment\n$rustTools = $installedSoftware.AddHeader(\"Rust Tools\")\n$rustTools.AddToolVersion(\"Cargo\", $(Get-RustCargoVersion))\n$rustTools.AddToolVersion(\"Rust\", $(Get-RustVersion))\n$rustTools.AddToolVersion(\"Rustdoc\", $(Get-RustdocVersion))\n$rustTools.AddToolVersion(\"Rustup\", $(Get-RustupVersion))\n\n$rustToolsPackages = $rustTools.AddHeader(\"Packages\")\nif (-not (Test-IsWin25)) {\n    $rustToolsPackages.AddToolVersion(\"bindgen\", $(Get-BindgenVersion))\n    $rustToolsPackages.AddToolVersion(\"cargo-audit\", $(Get-CargoAuditVersion))\n    $rustToolsPackages.AddToolVersion(\"cargo-outdated\", $(Get-CargoOutdatedVersion))\n    $rustToolsPackages.AddToolVersion(\"cbindgen\", $(Get-CbindgenVersion))\n}\n$rustToolsPackages.AddToolVersion(\"Clippy\", $(Get-RustClippyVersion))\n$rustToolsPackages.AddToolVersion(\"Rustfmt\", $(Get-RustfmtVersion))\n\n# Browsers and Drivers\n$browsersAndWebdrivers = $installedSoftware.AddHeader(\"Browsers and Drivers\")\n$browsersAndWebdrivers.AddNodes($(Build-BrowserSection))\n$browsersAndWebdrivers.AddHeader(\"Environment variables\").AddTable($(Build-BrowserWebdriversEnvironmentTable))\n\n# Java\n$installedSoftware.AddHeader(\"Java\").AddTable($(Get-JavaVersions))\n\n# Shells\n$installedSoftware.AddHeader(\"Shells\").AddTable($(Get-ShellTarget))\n\n# MSYS2\n$msys2 = $installedSoftware.AddHeader(\"MSYS2\")\n$msys2.AddToolVersion(\"Pacman\", $(Get-PacmanVersion))\n\n$notes = @'\nLocation: C:\\msys64\n\nNote: MSYS2 is pre-installed on image but not added to PATH.\n'@\n$msys2.AddHeader(\"Notes\").AddNote($notes)\n\n# Cached Tools\n$installedSoftware.AddHeader(\"Cached Tools\").AddNodes($(Build-CachedToolsSection))\n\n# Databases\n$databases = $installedSoftware.AddHeader(\"Databases\")\n$databases.AddHeader(\"PostgreSQL\").AddTable($(Get-PostgreSQLTable))\n$databases.AddHeader(\"MongoDB\").AddTable($(Get-MongoDBTable))\n\n# Database tools\n$databaseTools = $installedSoftware.AddHeader(\"Database tools\")\n$databaseTools.AddToolVersion(\"Azure CosmosDb Emulator\", $(Get-AzCosmosDBEmulatorVersion))\n$databaseTools.AddToolVersion(\"DacFx\", $(Get-DacFxVersion))\n$databaseTools.AddToolVersion(\"MySQL\", $(Get-MySQLVersion))\n$databaseTools.AddToolVersion(\"SQL OLEDB Driver 18\", $(Get-SQLOLEDBDriver18Version))\n$databaseTools.AddToolVersion(\"SQL OLEDB Driver 19\", $(Get-SQLOLEDBDriver19Version))\n$databaseTools.AddToolVersion(\"SQLPS\", $(Get-SQLPSVersion))\n$databaseTools.AddToolVersion(\"MongoDB Shell (mongosh)\", $(Get-MongoshVersion))\n\n\n# Web Servers\n$installedSoftware.AddHeader(\"Web Servers\").AddTable($(Build-WebServersSection))\n\n# Visual Studio\n$vsTable = Get-VisualStudioVersion\n$visualStudio = $installedSoftware.AddHeader($vsTable.Name)\n$visualStudio.AddTable($vsTable)\n\n$workloads = $visualStudio.AddHeader(\"Workloads, components and extensions\")\n$workloads.AddTable((Get-VisualStudioComponents) + (Get-VisualStudioExtensions))\n\n$msVisualCpp = $visualStudio.AddHeader(\"Microsoft Visual C++\")\n$msVisualCpp.AddTable($(Get-VisualCPPComponents))\n\n$visualStudio.AddToolVersionsList(\"Installed Windows SDKs\", $(Get-WindowsSDKs).Versions, '^.+')\n\n# .NET Core Tools\n$netCoreTools = $installedSoftware.AddHeader(\".NET Core Tools\")\n$netCoreTools.AddToolVersionsListInline(\".NET Core SDK\", $(Get-DotnetSdks).Versions, '^\\d+\\.\\d+\\.\\d{3}')\n$netCoreTools.AddToolVersionsListInline(\".NET Framework\", $(Get-DotnetFrameworkVersions), '^.+')\nGet-DotnetRuntimes | ForEach-Object {\n    $netCoreTools.AddToolVersionsListInline($_.Runtime, $_.Versions, '^.+')\n}\n$netCoreTools.AddNodes($(Get-DotnetTools))\n\n# PowerShell Tools\n$psTools = $installedSoftware.AddHeader(\"PowerShell Tools\")\n$psTools.AddToolVersion(\"PowerShell\", $(Get-PowershellCoreVersion))\n\n$psModules = $psTools.AddHeader(\"Powershell Modules\")\n$psModules.AddNodes($(Get-PowerShellModules))\n\n\n# Android\n$android = $installedSoftware.AddHeader(\"Android\")\n$android.AddTable($(Build-AndroidTable))\n\n$android.AddHeader(\"Environment variables\").AddTable($(Build-AndroidEnvironmentTable))\n\n# Cached Docker images\nif (-not (Test-IsWin25)) {\n    $installedSoftware.AddHeader(\"Cached Docker images\").AddTable($(Get-CachedDockerImagesTableData))\n}\n\n# Generate reports\n$softwareReport.ToJson() | Out-File -FilePath \"C:\\software-report.json\" -Encoding UTF8NoBOM\n$softwareReport.ToMarkdown() | Out-File -FilePath \"C:\\software-report.md\" -Encoding UTF8NoBOM\n"
  },
  {
    "path": "images/windows/scripts/docs-gen/SoftwareReport.Android.psm1",
    "content": "Import-Module (Join-Path $PSScriptRoot \"SoftwareReport.Helpers.psm1\") -DisableNameChecking\n\nfunction Split-TableRowByColumns {\n    param(\n        [string] $Row\n    )\n    return $Row.Split(\"|\") | ForEach-Object { $_.trim() }\n}\n\nfunction Build-AndroidTable {\n    $packageInfo = Get-AndroidInstalledPackages\n    return @(\n        @{\n            \"Package\" = \"Android Command Line Tools\"\n            \"Version\" = Get-AndroidCommandLineToolsVersion\n        },\n        @{\n            \"Package\" = \"Android Emulator\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Android Emulator\"\n        },\n        @{\n            \"Package\" = \"Android SDK Build-tools\"\n            \"Version\" = Get-AndroidBuildToolVersions -PackageInfo $packageInfo\n        },\n        @{\n            \"Package\" = \"Android SDK Platforms\"\n            \"Version\" = Get-AndroidPlatformVersions -PackageInfo $packageInfo\n        },\n        @{\n            \"Package\" = \"Android SDK Platform-Tools\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Android SDK Platform-Tools\"\n        },\n        @{\n            \"Package\" = \"Android Support Repository\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Android Support Repository\"\n        },\n        @{\n            \"Package\" = \"CMake\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"cmake\"\n        },\n        @{\n            \"Package\" = \"Google APIs\"\n            \"Version\" = Get-AndroidGoogleAPIsVersions -PackageInfo $packageInfo\n        },\n        @{\n            \"Package\" = \"Google Play services\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Google Play services\"\n        },\n        @{\n            \"Package\" = \"Google Repository\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"Google Repository\"\n        },\n        @{\n            \"Package\" = \"NDK\"\n            \"Version\" = Get-AndroidNdkVersions -PackageInfo $packageInfo\n        },\n        @{\n            \"Package\" = \"SDK Patch Applier v4\"\n            \"Version\" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString \"SDK Patch Applier v4\"\n        }\n    ) | Where-Object { $_.Version } | ForEach-Object {\n        [PSCustomObject] @{\n            \"Package Name\" = $_.Package\n            \"Version\"      = $_.Version\n        }\n    }\n}\n\nfunction Get-AndroidPackageVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo,\n        [Parameter(Mandatory)]\n        [object] $MatchedString\n    )\n\n    $versions = $packageInfo | Where-Object { $_ -Match $MatchedString } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        return $packageInfoParts[1]\n    }\n    return ($versions -Join \"<br>\")\n}\n\nfunction Get-AndroidPlatformVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo\n    )\n\n    $versions = $packageInfo | Where-Object { $_ -Match \"Android SDK Platform \" } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        $revision = $packageInfoParts[1]\n        $version = $packageInfoParts[0].split(\";\")[1]\n        return \"$version (rev $revision)\"\n    }\n    [array]::Reverse($versions)\n    return ($versions -Join \"<br>\")\n}\n\nfunction Get-AndroidCommandLineToolsVersion {\n    $commandLineTools = (Join-Path $env:ANDROID_HOME \"cmdline-tools\\latest\\bin\\sdkmanager.bat\")\n    (cmd /c \"$commandLineTools --version 2>NUL\" | Out-String).Trim() -match \"(?<version>^(\\d+\\.){1,}\\d+$)\" | Out-Null\n    $commandLineToolsVersion = $Matches.Version\n    return $commandLineToolsVersion\n}\n\nfunction Get-AndroidBuildToolVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo\n    )\n\n    $versions = $packageInfo | Where-Object { $_ -Match \"Android SDK Build-Tools\" } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        return $packageInfoParts[1]\n    }\n    $groupVersions = @()\n    $versions | ForEach-Object {\n        $majorVersion = $_.Split(\".\")[0]\n        $groupVersions += $versions | Where-Object { $_.StartsWith($majorVersion) } | Join-String -Separator \" \"\n    }\n    return ($groupVersions | Sort-Object -Descending -Unique | Join-String -Separator \"<br>\")\n}\n\nfunction Get-AndroidGoogleAPIsVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo\n    )\n\n    $versions = $packageInfo | Where-Object { $_ -Match \"addon-google_apis\" } | ForEach-Object {\n        $packageInfoParts = Split-TableRowByColumns $_\n        return $packageInfoParts[0].split(\";\")[1]\n    }\n    return ($versions -Join \"<br>\")\n}\n\nfunction Get-AndroidNdkVersions {\n    param (\n        [Parameter(Mandatory)]\n        [object] $PackageInfo\n    )\n\n    $ndkDefaultFullVersion = Get-ChildItem $env:ANDROID_NDK_HOME -Name\n\n    $versions = $packageInfo | Where-Object { $_ -Match \"ndk;\" } | ForEach-Object {\n        $version = (Split-TableRowByColumns $_)[1]\n        if ($version -eq $ndkDefaultFullVersion) {\n            $version += \" (default)\"\n        }\n        $version\n    }\n    return ($versions -Join \"<br>\")\n}\n\nfunction Build-AndroidEnvironmentTable {\n    $androidVersions = Get-Item env:ANDROID_*\n\n    $shoulddResolveLink = 'ANDROID_NDK', 'ANDROID_NDK_HOME', 'ANDROID_NDK_ROOT', 'ANDROID_NDK_LATEST_HOME'\n    return $androidVersions | Sort-Object -Property Name | ForEach-Object {\n        [PSCustomObject] @{\n            \"Name\" = $_.Name\n            \"Value\" = if ($shoulddResolveLink.Contains($_.Name )) { Get-PathWithLink($_.Value) } else {$_.Value}\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/docs-gen/SoftwareReport.Browsers.psm1",
    "content": "$browsers = @{\n    chrome = @{\n        Name=\"Google Chrome\";\n        File=\"chrome.exe\"\n    };\n    edge = @{\n        Name=\"Microsoft Edge\";\n        File=\"msedge.exe\"\n    };\n    firefox = @{\n        Name=\"Mozilla Firefox\";\n        File=\"firefox.exe\"\n    }\n}\n\n$webDrivers = @{\n    chrome = @{\n        Name=\"Chrome Driver\";\n        Path=\"C:\\SeleniumWebDrivers\\ChromeDriver\"\n    };\n    edge = @{\n        Name=\"Microsoft Edge Driver\";\n        Path=\"C:\\SeleniumWebDrivers\\EdgeDriver\"\n    };\n    firefox = @{\n        Name=\"Gecko Driver\";\n        Path=\"C:\\SeleniumWebDrivers\\GeckoDriver\"\n    };\n    iexplorer = @{\n        Name=\"IE Driver\";\n        Path=\"C:\\SeleniumWebDrivers\\IEDriver\"\n    }\n}\n\nfunction Build-BrowserSection {\n    return @(\n        $(Get-BrowserVersion -Browser \"chrome\"),\n        $(Get-SeleniumWebDriverVersion -Driver \"chrome\"),\n        $(Get-BrowserVersion -Browser \"edge\"),\n        $(Get-SeleniumWebDriverVersion -Driver \"edge\"),\n        $(Get-BrowserVersion -Browser \"firefox\"),\n        $(Get-SeleniumWebDriverVersion -Driver \"firefox\"),\n        $(Get-SeleniumWebDriverVersion -Driver \"iexplorer\"),\n        $(Get-SeleniumVersion)\n    )\n}\n\nfunction Get-BrowserVersion {\n    param(\n        [string] $Browser\n    )\n    $browserName = $browsers.$Browser.Name\n    $browserFile = $browsers.$Browser.File\n    $registryKey = \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\$browserFile\"\n    $browserVersion = (Get-Item (Get-ItemProperty $registryKey).\"(Default)\").VersionInfo.FileVersion\n    return [ToolVersionNode]::new($browserName, $browserVersion)\n}\n\nfunction Get-SeleniumWebDriverVersion {\n    param(\n        [string] $Driver\n    )\n    $driverName = $webDrivers.$Driver.Name\n    $driverPath = $webDrivers.$Driver.Path\n    $versionFileName = \"versioninfo.txt\";\n    $webDriverVersion = Get-Content -Path \"$driverPath\\$versionFileName\"\n    return [ToolVersionNode]::new($driverName, $webDriverVersion)\n}\n\nfunction Get-SeleniumVersion {\n    $seleniumBinaryName = \"selenium-server\"\n    $fullSeleniumVersion = (Get-ChildItem \"C:\\selenium\\${seleniumBinaryName}-*\").Name -replace \"${seleniumBinaryName}-\"\n    return [ToolVersionNode]::new(\"Selenium server\", $fullSeleniumVersion)\n}\n\nfunction Build-BrowserWebdriversEnvironmentTable {\n    return @(\n        @{\n            \"Name\" = \"CHROMEWEBDRIVER\"\n            \"Value\" = $env:CHROMEWEBDRIVER\n        },\n        @{\n            \"Name\" = \"EDGEWEBDRIVER\"\n            \"Value\" = $env:EDGEWEBDRIVER\n        },\n        @{\n            \"Name\" = \"GECKOWEBDRIVER\"\n            \"Value\" = $env:GECKOWEBDRIVER\n        },\n        @{\n            \"Name\" = \"SELENIUM_JAR_PATH\"\n            \"Value\" = $env:SELENIUM_JAR_PATH\n        }\n    ) | ForEach-Object {\n        [PSCustomObject] @{\n            \"Name\" = $_.Name\n            \"Value\" = $_.Value\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/docs-gen/SoftwareReport.CachedTools.psm1",
    "content": "function Get-ToolcacheGoVersions {\n    $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY \"Go\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ }\n}\n\nfunction Get-ToolcacheNodeVersions {\n    $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY \"Node\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ }\n}\n\nfunction Get-ToolcachePythonVersions {\n    $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY \"Python\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ }\n}\n\nfunction Get-ToolcacheRubyVersions {\n    $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY \"Ruby\"\n    return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ }\n}\n\nfunction Get-ToolcachePyPyVersions {\n    $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY \"PyPy\"\n    Get-ChildItem -Path $toolcachePath -Name | Sort-Object { [Version] $_ } | ForEach-Object {\n        $pypyRootPath = Join-Path $toolcachePath $_ \"x86\"\n        [string] $pypyVersionOutput = & \"$pypyRootPath\\python.exe\" -c \"import sys;print(sys.version)\"\n        $pypyVersionOutput -match \"^([\\d\\.]+) \\(.+\\) \\[PyPy ([\\d\\.]+\\S*) .+]$\" | Out-Null\n        return \"{0} [PyPy {1}]\" -f $Matches[1], $Matches[2]\n    }\n}\n\nfunction Build-CachedToolsSection\n{\n    return @(\n        [ToolVersionsListNode]::new(\"Go\", $(Get-ToolcacheGoVersions), '^\\d+\\.\\d+', 'List'),\n        [ToolVersionsListNode]::new(\"Node.js\", $(Get-ToolcacheNodeVersions), '^\\d+', 'List'),\n        [ToolVersionsListNode]::new(\"Python\", $(Get-ToolcachePythonVersions), '^\\d+\\.\\d+', 'List'), \n        [ToolVersionsListNode]::new(\"PyPy\", $(Get-ToolcachePyPyVersions), '^\\d+\\.\\d+', 'List'),\n        [ToolVersionsListNode]::new(\"Ruby\", $(Get-ToolcacheRubyVersions), '^\\d+\\.\\d+', 'List')\n    )\n}\n"
  },
  {
    "path": "images/windows/scripts/docs-gen/SoftwareReport.Common.psm1",
    "content": "function Initialize-RustEnvironment {\n    $env:RUSTUP_HOME = \"C:\\Users\\Default\\.rustup\"\n    $env:CARGO_HOME = \"C:\\Users\\Default\\.cargo\"\n    $env:Path += \";$env:CARGO_HOME\\bin\"\n}\n\nfunction Get-OSName {\n    return (Get-CimInstance -ClassName Win32_OperatingSystem).Caption | Get-StringPart -Part 1,2,3\n}\n\nfunction Get-OSVersion {\n    $OSVersion = (Get-CimInstance -ClassName Win32_OperatingSystem).Version\n    $OSBuild = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion' UBR).UBR\n    return \"$OSVersion Build $OSBuild\"\n}\n\nfunction Build-OSInfoSection {\n    $osInfoNode = [HeaderNode]::new($(Get-OSName))\n    $osInfoNode.AddToolVersion(\"OS Version:\", $(Get-OSVersion))\n    $osInfoNode.AddToolVersion(\"Image Version:\", $env:IMAGE_VERSION)\n    return $osInfoNode\n}\n\nfunction Get-BashVersion {\n    bash --% -c 'echo ${BASH_VERSION}'\n}\n\nfunction Get-RustVersion {\n    rustc --version | Get-StringPart -Part 1\n}\n\nfunction Get-RustupVersion {\n    cmd /c \"rustup --version 2>NUL\" | Get-StringPart -Part 1\n}\n\nfunction Get-RustCargoVersion {\n    cargo --version | Get-StringPart -Part 1\n}\n\nfunction Get-RustdocVersion {\n    rustdoc --version | Get-StringPart -Part 1\n}\n\nfunction Get-RustfmtVersion {\n    rustfmt --version | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter ('-')\n}\n\nfunction Get-RustClippyVersion {\n    cargo clippy --version | Get-StringPart -Part 1\n}\n\nfunction Get-BindgenVersion {\n    bindgen --version | Get-StringPart -Part 1\n}\n\nfunction Get-CbindgenVersion {\n    cbindgen --version | Get-StringPart -Part 1\n}\n\nfunction Get-CargoAuditVersion {\n    cargo-audit --version | Get-StringPart -Part 1\n}\n\nfunction Get-CargoOutdatedVersion {\n    cargo outdated --version | Get-StringPart -Part 1\n}\n\nfunction Get-PythonVersion {\n    python --version | Get-StringPart -Part 1\n}\n\nfunction Get-PowershellCoreVersion {\n    pwsh --version | Get-StringPart -Part 1\n}\n\nfunction Get-RubyVersion {\n    ruby --version | Get-StringPart -Part 1\n}\n\nfunction Get-GoVersion {\n    go version | Get-StringPart -Part 2 | Get-StringPart -Part 1 -Delimiter ('o')\n}\n\nfunction Get-KotlinVersion {\n    cmd /c \"kotlinc -version 2>&1\" | Get-StringPart -Part 2\n}\n\nfunction Get-PHPVersion {\n    php --version | Out-String | Get-StringPart -Part 1\n}\n\nfunction Get-JuliaVersion {\n    julia --version | Get-StringPart -Part 2\n}\n\nfunction Get-LLVMVersion {\n    (clang --version) -match \"clang\" | Get-StringPart -Part 2\n}\n\nfunction Get-PerlVersion {\n    ($(perl --version) | Out-String) -match \"\\(v(?<version>\\d+\\.\\d+\\.\\d+)\\)\" | Out-Null\n    $perlVersion = $Matches.Version\n    return $perlVersion\n}\n\nfunction Get-NodeVersion {\n    node --version | Get-StringPart -Part 0 -Delimiter ('v')\n}\n\nfunction Get-ChocoVersion {\n    choco --version\n}\n\nfunction Get-VcpkgVersion {\n    $commitId = git -C \"C:\\vcpkg\" rev-parse --short HEAD\n    return \"(build from commit $commitId)\"\n}\n\nfunction Get-NPMVersion {\n    npm -version\n}\n\nfunction Get-YarnVersion {\n    yarn -version\n}\n\nfunction Get-RubyGemsVersion {\n    gem --version\n}\n\nfunction Get-HelmVersion {\n    ($(helm version --short) | Out-String) -match \"v(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $helmVersion = $Matches.Version\n    return $helmVersion\n}\n\nfunction Get-PipVersion {\n    (pip --version) -match \"pip\" | Get-StringPart -Part 1, 4, 5\n}\n\nfunction Get-CondaVersion {\n    $condaVersion = ((& \"$env:CONDA\\Scripts\\conda.exe\" --version) -replace \"^conda\").Trim()\n    return \"$condaVersion (pre-installed on the image but not added to PATH)\"\n}\n\nfunction Get-ComposerVersion {\n    composer --version | Get-StringPart -Part 2\n}\n\nfunction Get-NugetVersion {\n    (nuget help) -match \"Nuget Version\" | Get-StringPart -Part 2\n}\n\nfunction Get-AntVersion {\n    ant -version | Get-StringPart -Part 3\n}\n\nfunction Get-MavenVersion {\n    (mvn -version) -match \"Apache Maven\" | Get-StringPart -Part 2\n}\n\nfunction Get-GradleVersion {\n    ($(gradle -version) | Out-String) -match \"Gradle (?<version>\\d+\\.\\d+)\" | Out-Null\n    $gradleVersion = $Matches.Version\n    return $gradleVersion\n}\n\nfunction Get-SbtVersion {\n    sbt --script-version\n}\n\nfunction Get-DotnetSdks {\n    $sdksRawList = dotnet --list-sdks\n    $sdkVersions = $sdksRawList | Foreach-Object { $_.Split()[0] }\n    $sdkPath = $sdksRawList[0].Split(' ', 2)[1] -replace '\\[|]'\n    [PSCustomObject]@{\n        Versions = $sdkVersions\n        Path     = $sdkPath\n    }\n}\n\nfunction Get-DotnetTools {\n    $env:Path += \";C:\\Users\\Default\\.dotnet\\tools\"\n    $dotnetTools = (Get-ToolsetContent).dotnet.tools\n\n    $toolsList = @()\n\n    foreach ($dotnetTool in $dotnetTools) {\n        $version = Invoke-Expression $dotnetTool.getversion\n        $toolsList += [ToolVersionNode]::new($dotnetTool.name, $version)\n    }\n    return $toolsList\n}\n\nfunction Get-DotnetRuntimes {\n    $runtimesRawList = dotnet --list-runtimes\n    $runtimesRawList | Group-Object { $_.Split()[0] } | ForEach-Object {\n        $runtimeName = $_.Name\n        $runtimeVersions = $_.Group | Foreach-Object { $_.split()[1] }\n        $runtimePath = $_.Group[0].Split(' ', 3)[2] -replace '\\[|]'\n        [PSCustomObject]@{\n            \"Runtime\"  = $runtimeName\n            \"Versions\" = $runtimeVersions\n            \"Path\"     = $runtimePath\n        }\n    }\n}\n\nfunction Get-DotnetFrameworkVersions {\n    $path = \"${env:ProgramFiles(x86)}\\Microsoft SDKs\\Windows\\*\\*\\NETFX * Tools\"\n    return Get-ChildItem -Path $path -Directory | ForEach-Object { $_.Name | Get-StringPart -Part 1 }\n}\n\nfunction Get-PowerShellAzureModules {\n    [Array] $result = @()\n    $defaultAzureModuleVersion = \"12.5.0\"\n\n    [Array] $azInstalledModules = Get-ChildItem -Path \"C:\\Modules\\az_*\" -Directory | ForEach-Object { $_.Name.Split(\"_\")[1] }\n    if ($azInstalledModules.Count -gt 0) {\n        $result += [ToolVersionsListNode]::new(\"Az\", $($azInstalledModules), '^\\d+\\.\\d+', \"Inline\")\n    }\n\n    [Array] $azureInstalledModules = Get-ChildItem -Path \"C:\\Modules\\azure_*\" -Directory | ForEach-Object { $_.Name.Split(\"_\")[1] } | ForEach-Object { if ($_ -eq $defaultAzureModuleVersion) { \"$($_) (Default)\" } else { $_ } }\n    if ($azureInstalledModules.Count -gt 0) {\n        $result += [ToolVersionsListNode]::new(\"Azure\", $($azureInstalledModules), '^\\d+\\.\\d+', \"Inline\")\n    }\n\n    [Array] $azurermInstalledModules = Get-ChildItem -Path \"C:\\Modules\\azurerm_*\" -Directory | ForEach-Object { $_.Name.Split(\"_\")[1] } | ForEach-Object { if ($_ -eq $defaultAzureModuleVersion) { \"$($_) (Default)\" } else { $_ } }\n    if ($azurermInstalledModules.Count -gt 0) {\n        $result += [ToolVersionsListNode]::new(\"AzureRM\", $($azurermInstalledModules), '^\\d+\\.\\d+', \"Inline\")\n    }\n\n    return $result\n}\n\nfunction Get-PowerShellModules {\n    [Array] $result = @()\n\n    $result += Get-PowerShellAzureModules\n\n    $result += (Get-ToolsetContent).powershellModules.name | Sort-Object | ForEach-Object {\n        $moduleName = $_\n        $moduleVersions = Get-Module -Name $moduleName -ListAvailable | Select-Object -ExpandProperty Version | Sort-Object -Unique\n        return [ToolVersionsListNode]::new($moduleName, $moduleVersions, '^\\d+', \"Inline\")\n    }\n\n    return $result\n}\n\nfunction Get-CachedDockerImagesTableData {\n    $allImages = docker images --digests --format \"*{{.Repository}}:{{.Tag}}|{{.Digest}} |{{.CreatedAt}}\"\n    if (-not $allImages) {\n        return $null\n    }\n\n    $allImages.Split(\"*\") | Where-Object { $_ } | ForEach-Object {\n        $parts = $_.Split(\"|\")\n        [PSCustomObject] @{\n            \"Repository:Tag\" = $parts[0]\n            \"Digest\"         = $parts[1]\n            \"Created\"        = $parts[2].split(' ')[0]\n        }\n    } | Sort-Object -Property \"Repository:Tag\"\n}\n\nfunction Get-ShellTarget {\n    return Get-ChildItem C:\\shells -File | Select-Object Name, @{n = \"Target\"; e = {\n            if ($_.Name -eq \"msys2bash.cmd\") {\n                \"C:\\msys64\\usr\\bin\\bash.exe\"\n            } else {\n                @($_.Target)[0]\n            }\n        }\n    } | Sort-Object Name\n}\n\nfunction Get-PacmanVersion {\n    $msys2BinDir = \"C:\\msys64\\usr\\bin\"\n    $pacmanPath = Join-Path $msys2BinDir \"pacman.exe\"\n    $rawVersion = & $pacmanPath --version\n    $rawVersion.Split([System.Environment]::NewLine)[1] -match \"\\d+\\.\\d+(\\.\\d+)?\" | Out-Null\n    $pacmanVersion = $matches[0]\n    return $pacmanVersion\n}\n\nfunction Get-YAMLLintVersion {\n    yamllint --version | Get-StringPart -Part 1\n}\n\nfunction Get-PipxVersion {\n    pipx --version\n}\n\nfunction Build-PackageManagementEnvironmentTable {\n    return @(\n        [PSCustomObject] @{\n            \"Name\" = \"VCPKG_INSTALLATION_ROOT\"\n            \"Value\" = $env:VCPKG_INSTALLATION_ROOT\n        },\n        [PSCustomObject] @{\n            \"Name\" = \"CONDA\"\n            \"Value\" = $env:CONDA\n        }\n    )\n}\n"
  },
  {
    "path": "images/windows/scripts/docs-gen/SoftwareReport.Databases.psm1",
    "content": "function Get-PostgreSQLTable\n{\n    $pgService = Get-CimInstance Win32_Service -Filter \"Name LIKE 'postgresql-%'\"\n    $pgPath = $pgService.PathName\n    $pgRoot = $pgPath.split('\"')[1].replace(\"\\bin\\pg_ctl.exe\", \"\")\n    $env:Path += \";${env:PGBIN}\"\n    $pgVersion = (postgres --version).split()[2].Trim()\n\n    return @(\n        [PSCustomObject]@{ Property = \"ServiceName\"; Value = $pgService.Name },\n        [PSCustomObject]@{ Property = \"Version\"; Value = $pgVersion },\n        [PSCustomObject]@{ Property = \"ServiceStatus\"; Value = $pgService.State },\n        [PSCustomObject]@{ Property = \"ServiceStartType\"; Value = $pgService.StartMode },\n        [PSCustomObject]@{ Property = \"EnvironmentVariables\"; Value = \"`PGBIN=$env:PGBIN` <br> `PGDATA=$env:PGDATA` <br> `PGROOT=$env:PGROOT` \" },\n        [PSCustomObject]@{ Property = \"Path\"; Value = $pgRoot },\n        [PSCustomObject]@{ Property = \"UserName\"; Value = $env:PGUSER },\n        [PSCustomObject]@{ Property = \"Password\"; Value = $env:PGPASSWORD }\n    )\n}\n\nfunction Get-MongoDBTable\n{\n    $name = \"MongoDB\"\n    $command = \"mongod\"\n    $mongoService = Get-Service -Name $name\n    $mongoVersion = (Get-Command -Name $command).Version.ToString()\n    return [PSCustomObject]@{\n        Version = $mongoVersion\n        ServiceName = $name\n        ServiceStatus = $mongoService.Status\n        ServiceStartType = $mongoService.StartType\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/docs-gen/SoftwareReport.Helpers.psm1",
    "content": "function Get-LinkTarget {\n    param (\n        [string] $inputPath\n    )\n    $link = Get-Item $inputPath | Select-Object -ExpandProperty Target\n    if ($link) {\n      return \" -> $link\"\n    }\n    return \"\"\n}\n\nfunction Get-PathWithLink {\n    param (\n        [string] $inputPath\n    )\n    $link = Get-LinkTarget($inputPath)\n    return \"${inputPath}${link}\"\n}\n\nfunction Get-StringPart {\n    param (\n        [Parameter(ValueFromPipeline)]\n        [string] $toolOutput,\n        [string] $Delimiter = \" \",\n        [int[]] $Part\n    )\n    $parts = $toolOutput.Split($Delimiter, [System.StringSplitOptions]::RemoveEmptyEntries)\n    $selectedParts = $parts[$Part]\n    return [string]::Join($Delimiter, $selectedParts)\n}\n"
  },
  {
    "path": "images/windows/scripts/docs-gen/SoftwareReport.Java.psm1",
    "content": "function Get-JavaVersions {\n    $defaultJavaPath = $env:JAVA_HOME\n    $javaVersions = Get-Item env:JAVA_HOME_*_X64\n    $sortRules = @{\n        Expression = { [Int32] $_.Name.Split(\"_\")[2] }  \n        Descending = $false\n    }\n\n    return $javaVersions | Sort-Object $sortRules | ForEach-Object {\n        $javaPath = $_.Value\n        # Take semver from the java path\n        # The path contains '-' sign in the version number instead of '+' due to the following issue, need to substitute it back https://github.com/actions/runner-images/issues/3014\n        $versionInPath = (Split-Path $javaPath) -replace \"\\w:\\\\.*\\\\\"\n        $version = $versionInPath -replace '-', '+'\n        $defaultPostfix = ($javaPath -eq $defaultJavaPath) ? \" (default)\" : \"\"\n\n        [PSCustomObject] @{\n            \"Version\"              = $version + $defaultPostfix\n            \"Environment Variable\" = $_.Name\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/docs-gen/SoftwareReport.Tools.psm1",
    "content": "function Get-Aria2Version {\n    (aria2c -v | Out-String) -match \"(?<version>(\\d+\\.){1,}\\d+)\" | Out-Null\n    $aria2Version = $Matches.Version\n    return $aria2Version\n}\n\nfunction Get-AzCosmosDBEmulatorVersion {\n    $regKey = gci HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\* | gp | ? { $_.DisplayName -eq 'Azure Cosmos DB Emulator' }\n    $installDir = $regKey.InstallLocation\n    $exeFilePath = Join-Path $installDir 'CosmosDB.Emulator.exe'\n    $version = (Get-Item $exeFilePath).VersionInfo.FileVersion\n    return $version\n}\n\nfunction Get-BazelVersion {\n    ((cmd /c \"bazel --version 2>&1\") | Out-String) -match \"bazel (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $bazelVersion = $Matches.Version\n    return $bazelVersion\n}\n\nfunction Get-BazeliskVersion {\n    ((cmd /c \"bazelisk version 2>&1\") | Out-String) -match \"Bazelisk version: v(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $bazeliskVersion = $Matches.Version\n    return $bazeliskVersion\n}\n\nfunction Get-BicepVersion {\n    (bicep --version | Out-String) -match \"bicep cli version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $bicepVersion = $Matches.Version\n    return $bicepVersion\n}\n\nfunction Get-RVersion {\n    ($(cmd /c \"Rscript --version 2>&1\") | Out-String) -match \"Rscript .* version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $rVersion = $Matches.Version\n    return $rVersion\n}\n\nfunction Get-CMakeVersion {\n    ($(cmake -version) | Out-String) -match \"cmake version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $cmakeVersion = $Matches.Version\n    return $cmakeVersion\n}\n\nfunction Get-CodeQLBundleVersion {\n    $CodeQLVersionsWildcard = Join-Path $env:AGENT_TOOLSDIRECTORY -ChildPath \"CodeQL\" | Join-Path -ChildPath \"*\"\n    $CodeQLVersionPath = Get-ChildItem $CodeQLVersionsWildcard | Select-Object -First 1 -Expand FullName\n    $CodeQLPath = Join-Path $CodeQLVersionPath -ChildPath \"x64\" | Join-Path -ChildPath \"codeql\" | Join-Path -ChildPath \"codeql.exe\"\n    $CodeQLVersion = & $CodeQLPath version --quiet\n    return $CodeQLVersion\n}\n\nfunction Get-DockerVersion {\n    $dockerVersion = $(docker version --format \"{{.Server.Version}}\")\n    return $dockerVersion\n}\n\nfunction Get-DockerComposeVersionV2 {\n    $dockerComposeVersion = docker compose version --short\n    return $dockerComposeVersion\n}\n\nfunction Get-DockerWincredVersion {\n    $dockerCredVersion = docker-credential-wincred version | Get-StringPart -Part 2 | Get-StringPart -Part 0 -Delimiter \"v\"\n    return $dockerCredVersion\n}\n\nfunction Get-GitVersion {\n    $gitVersion = git --version | Get-StringPart -Part -1\n    return $gitVersion\n}\n\nfunction Get-GitLFSVersion {\n    $(git-lfs version) -match \"git-lfs\\/(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $gitLfsVersion = $Matches.Version\n    return $gitLfsVersion\n}\n\nfunction Get-InnoSetupVersion {\n    $innoSetupVersion = $(choco list innosetup) | Select-String -Pattern \"InnoSetup\"\n    return ($innoSetupVersion -replace \"^InnoSetup\").Trim()\n}\n\nfunction Get-JQVersion {\n    $jqVersion = ($(jq --version) -Split \"jq-\")[1]\n    return $jqVersion\n}\n\nfunction Get-KubectlVersion {\n    $kubectlVersion = (kubectl version --client --output=json | ConvertFrom-Json).clientVersion.gitVersion.Replace('v', '')\n    return $kubectlVersion\n}\n\nfunction Get-KindVersion {\n    $(kind version) -match \"kind v(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $kindVersion = $Matches.Version\n    return $kindVersion\n}\n\nfunction Get-GCCVersion {\n    (gcc --version | Select-String -Pattern \"gcc.exe\") -match \"(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $mingwVersion = $Matches.Version\n    return $mingwVersion\n}\n\nfunction Get-GDBVersion {\n    (gdb --version | Select-String -Pattern \"GNU gdb\") -match \"(?<version>\\d+\\.\\d+)\" | Out-Null\n    $mingwVersion = $Matches.Version\n    return $mingwVersion\n}\n\nfunction Get-GNUBinutilsVersion {\n    (ld --version | Select-String -Pattern \"GNU Binutils\") -match \"(?<version>\\d+\\.\\d+)\" | Out-Null\n    $mingwVersion = $Matches.Version\n    return $mingwVersion\n}\n\nfunction Get-MySQLVersion {\n    $mysqlCommand = Get-Command -Name \"mysql\"\n    $mysqlVersion = $mysqlCommand.Version.ToString()\n    return $mysqlVersion\n}\n\nfunction Get-SQLOLEDBDriver18Version {\n    $SQLOLEDBDriverVersion = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSOLEDBSQL' InstalledVersion).InstalledVersion\n    return $SQLOLEDBDriverVersion\n}\n\nfunction Get-SQLOLEDBDriver19Version {\n    $SQLOLEDBDriverVersion = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSOLEDBSQL19' InstalledVersion).InstalledVersion\n    return $SQLOLEDBDriverVersion\n}\n\nfunction Get-MercurialVersion {\n    ($(hg --version) | Out-String) -match \"version (?<version>\\d+\\.\\d+\\.?\\d*)\" | Out-Null\n    $mercurialVersion = $Matches.Version\n    return $mercurialVersion\n}\n\nfunction Get-NSISVersion {\n    $nsisVersion = & \"c:\\Program Files (x86)\\NSIS\\makensis.exe\" \"/Version\"\n    return $nsisVersion.TrimStart(\"v\")\n}\n\nfunction Get-OpenSSLVersion {\n    $(openssl version) -match \"OpenSSL (?<version>\\d+\\.\\d+\\.\\d+\\w?) \" | Out-Null\n    $opensslVersion = $Matches.Version\n    return $opensslVersion\n}\n\nfunction Get-PackerVersion {\n    $packerVersion = (packer --version | Select-String \"^Packer\").Line.Replace('v','') | Get-StringPart -Part 1\n    return $packerVersion\n}\n\nfunction Get-PulumiVersion {\n    return (pulumi version).TrimStart(\"v\")\n}\n\nfunction Get-SQLPSVersion {\n    $module = Get-Module -Name SQLPS -ListAvailable\n    $version = $module.Version\n    return $version\n}\n\nfunction Get-SVNVersion {\n    $svnVersion = $(svn --version --quiet)\n    return $svnVersion\n}\n\nfunction Get-VSWhereVersion {\n    ($(Get-Command -Name vswhere).FileVersionInfo.ProductVersion) -match \"(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $vswhereVersion = $Matches.Version\n    return $vswhereVersion\n}\n\nfunction Get-WinAppDriver {\n    $winAppDriverVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo(\"C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe\").FileVersion\n    return $winAppDriverVersion\n}\n\nfunction Get-WixVersion {\n    $regKey = \"HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\"\n    $installedApplications = Get-ItemProperty -Path $regKey\n    $wixToolsetVersion = ($installedApplications | Where-Object { $_.BundleCachePath -imatch \".*\\\\WiX\\d*\\.exe$\" } | Select-Object -First 1).DisplayName\n    return ($wixToolsetVersion -replace \"^WiX Toolset v\").Trim()\n}\n\nfunction Get-ZstdVersion {\n    $(zstd --version) -match \"v(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $zstdVersion = $Matches.Version\n    return $zstdVersion\n}\n\nfunction Get-AzureCLIVersion {\n    $azureCLIVersion = $(az version) | ConvertFrom-Json | Foreach{ $_.\"azure-cli\" }\n    return $azureCLIVersion\n}\n\nfunction Get-AzCopyVersion {\n    return ($(azcopy --version) -replace \"^azcopy version\").Trim()\n}\n\nfunction Get-AzureDevopsExtVersion {\n    $azureDevExtVersion = (az version | ConvertFrom-Json | ForEach-Object { $_.\"extensions\" }).\"azure-devops\"\n    return $azureDevExtVersion\n}\n\nfunction Get-AWSCLIVersion {\n    $(aws --version) -match \"aws-cli\\/(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $awscliVersion = $Matches.Version\n    return $awscliVersion\n}\n\nfunction Get-AWSSAMVersion {\n    $(sam --version) -match \"version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $awssamVersion = $Matches.Version\n    return $awssamVersion\n}\n\nfunction Get-AWSSessionManagerVersion {\n    $awsSessionManagerVersion = $(session-manager-plugin --version)\n    return $awsSessionManagerVersion\n}\n\nfunction Get-AlibabaCLIVersion {\n    $alicliVersion = $(aliyun version)\n    return $alicliVersion\n}\n\nfunction Get-7zipVersion {\n    (7z | Out-String) -match \"7-Zip (?<version>\\d+\\.\\d+\\.?\\d*)\" | Out-Null\n    $version = $Matches.Version\n    return $version\n}\n\nfunction Get-GHCVersion {\n    ((ghc --version) | Out-String) -match \"version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $ghcVersion = $Matches.Version\n    return $ghcVersion\n}\n\nfunction Get-CabalVersion {\n    ((cabal --version) | Out-String) -match \"version (?<version>\\d+\\.\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $cabalVersion = $Matches.Version\n    return $cabalVersion\n}\n\nfunction Get-StackVersion {\n    ((stack --version --quiet) | Out-String) -match \"Version (?<version>\\d+\\.\\d+\\.\\d+),\" | Out-Null\n    $stackVersion = $Matches.Version\n    return $stackVersion\n}\n\nfunction Get-ServiceFabricSDKVersion {\n    $serviceFabricSDKVersion = Get-ItemPropertyValue 'HKLM:\\SOFTWARE\\Microsoft\\Service Fabric\\' -Name FabricVersion\n    return $serviceFabricSDKVersion\n}\n\nfunction Get-NewmanVersion {\n    return $(newman --version)\n}\n\nfunction Get-GHVersion {\n    ($(gh --version) | Select-String -Pattern \"gh version\") -match \"gh version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $ghVersion = $Matches.Version\n    return $ghVersion\n}\n\nfunction Get-VisualCPPComponents {\n    $regKeys = @(\n        \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\"\n        \"HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\"\n    )\n    $vcpp = Get-ItemProperty -Path $regKeys | Where-Object DisplayName -like \"Microsoft Visual C++*\"\n    $vcpp | Sort-Object DisplayName, DisplayVersion | ForEach-Object {\n        $isMatch = $_.DisplayName -match \"^(?<Name>Microsoft Visual C\\+\\+ \\d{4})\\s+(?<Arch>\\w{3})\\s+(?<Ext>.+)\\s+-\"\n        if ($isMatch) {\n            $name = '{0} {1}' -f $matches[\"Name\"], $matches[\"Ext\"]\n            $arch = $matches[\"Arch\"].ToLower()\n            $version = $_.DisplayVersion\n            [PSCustomObject]@{\n                Name = $name\n                Architecture = $arch\n                Version = $version\n            }\n        }\n    }\n}\n\nfunction Get-DacFxVersion {\n    $dacfxversion = & \"$env:ProgramFiles\\Microsoft SQL Server\\170\\DAC\\bin\\sqlpackage.exe\" /version\n    return $dacfxversion\n}\n\nfunction Get-SwigVersion {\n    (swig -version | Out-String) -match \"version (?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    $swigVersion = $Matches.Version\n    return $swigVersion\n}\n\nfunction Get-ImageMagickVersion {\n    (magick -version | Select-String -Pattern \"Version\") -match \"(?<version>\\d+\\.\\d+\\.\\d+-\\d+)\" | Out-Null\n    $magickVersion = $Matches.Version\n    return $magickVersion\n}\n\nfunction Get-MongoshVersion {\n    return $(mongosh --version)\n}\n\nfunction Get-WSL2Version {\n    return $((Get-AppxPackage -Name \"MicrosoftCorporationII.WindowsSubsystemForLinux\").version)\n}\n\nfunction Get-NinjaVersion {\n    return $(ninja --version)\n}\n"
  },
  {
    "path": "images/windows/scripts/docs-gen/SoftwareReport.VisualStudio.psm1",
    "content": "function Get-VisualStudioVersion {\n    $vsInstance = Get-VisualStudioInstance\n    [PSCustomObject]@{\n        Name = $vsInstance.DisplayName\n        Version = $vsInstance.InstallationVersion\n        Path = $vsInstance.InstallationPath\n    }\n}\n\nfunction Get-SDKVersion {\n    $regKey = \"HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\"\n    $installedApplications = Get-ItemProperty -Path $regKey\n    ($installedApplications | Where-Object { $_.DisplayName -eq 'Windows SDK' } | Select-Object -First 1).DisplayVersion\n}\n\nfunction Get-WDKVersion {\n    $regKey = \"HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\"\n    $installedApplications = Get-ItemProperty -Path $regKey\n    ($installedApplications | Where-Object { $_.DisplayName -eq 'Windows Driver Kit' } | Select-Object -First 1).DisplayVersion\n}\n\nfunction Get-VisualStudioExtensions {\n    $vsPackages = (Get-VisualStudioInstance).Packages\n\n    # Additional vsixs\n    $toolset = Get-ToolsetContent\n    $vsixPackagesList = $toolset.visualStudio.vsix\n    if ($vsixPackagesList) {\n        $vsixs = $vsixPackagesList | ForEach-Object {\n            $vsixPackage = Get-VsixInfoFromMarketplace $_\n            $vsixVersion = ($vsPackages | Where-Object { $_.Id -match $vsixPackage.VsixId -and $_.type -eq 'vsix' }).Version\n            @{\n                Package = $vsixPackage.ExtensionName\n                Version = $vsixVersion\n            }\n        }\n    }\n\n    # SDK\n    $sdkVersion = Get-SDKVersion\n    $sdkPackages = @(\n        @{Package = 'Windows Software Development Kit'; Version = $sdkVersion }\n    )\n\n    # WDK\n    if (-not (Test-IsWin25)) {\n        $wdkVersion = Get-WDKVersion\n        $wdkPackages = @(\n            @{Package = 'Windows Driver Kit'; Version = $wdkVersion }\n        )\n    }\n\n    # WDK extension\n    $wdkExtensionVersion = Get-VSExtensionVersion -packageName 'Microsoft.Windows.DriverKit'\n    $wdkExtensions = @(\n        @{Package = 'Windows Driver Kit Visual Studio Extension'; Version = $wdkExtensionVersion }\n    )\n\n    $extensions = @(\n        $vsixs\n        $sdkPackages\n        $wdkPackages\n        $wdkExtensions\n    )\n\n    $extensions | Foreach-Object {\n        [PSCustomObject] $_\n    } | Select-Object Package, Version | Sort-Object Package\n}\n\nfunction Get-WindowsSDKs {\n    $path = \"${env:ProgramFiles(x86)}\\Windows Kits\\10\\Extension SDKs\\WindowsDesktop\"\n    return [PSCustomObject]@{\n        Path     = $path\n        Versions = $(Get-ChildItem $path).Name\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/docs-gen/SoftwareReport.WebServers.psm1",
    "content": "function Get-ApachePath {\n    return Join-Path \"C:\\tools\\\" (Get-Item C:\\tools\\apache*).Name\n}\n\nfunction Get-NginxPath {\n    return Join-Path \"C:\\tools\\\" (Get-Item C:\\tools\\nginx*).Name\n}\n\nfunction Get-ApacheVersion {\n    $apacheBinPath = Join-Path (Get-ApachePath) \"\\bin\\httpd\"\n    (. $apacheBinPath -V | Select-String -Pattern \"Apache/\") -match \"Apache/(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.Version\n}\n\nfunction Get-NginxVersion {\n    $nginxBinPath = Join-Path (Get-NginxPath) \"nginx\"\n    (cmd /c \"$nginxBinPath -v 2>&1\") -match \"nginx/(?<version>\\d+\\.\\d+\\.\\d+)\" | Out-Null\n    return $Matches.Version\n}\n\nfunction Get-ApacheSection\n{\n    $name = \"Apache\"\n    $apachePort = \"80\"\n    $apacheService = Get-Service -Name $name\n    $apacheVersion = Get-ApacheVersion\n    $apacheConfigFile = Join-Path (Get-ApachePath) \"\\conf\\httpd.conf\"\n    return [PSCustomObject]@{\n        Name = $name\n        Version = $apacheVersion\n        ConfigFile = $apacheConfigFile\n        ServiceName = $apacheService.Name\n        ServiceStatus = $apacheService.Status\n        ListenPort = $apachePort\n    }\n}\n\nfunction Get-NginxSection\n{\n    $name = \"Nginx\"\n    $nginxPort = \"80\"\n    $nginxService = Get-Service -Name $name\n    $nginxVersion = Get-NginxVersion\n    $nginxConfigFile = Join-Path (Get-NginxPath) \"\\conf\\nginx.conf\"\n    return [PSCustomObject]@{\n        Name = $name\n        Version = $nginxVersion\n        ConfigFile = $nginxConfigFile\n        ServiceName = $nginxService.Name\n        ServiceStatus = $nginxService.Status\n        ListenPort = $nginxPort\n    }\n}\n\nfunction Build-WebServersSection {\n    return @(\n        (Get-ApacheSection),\n        (Get-NginxSection)\n    )\n}"
  },
  {
    "path": "images/windows/scripts/helpers/AndroidHelpers.ps1",
    "content": "function Get-AndroidPackages {\n    <#\n    .SYNOPSIS\n        This function returns a list of available Android packages.\n\n    .DESCRIPTION\n        The Get-AndroidPackages function checks if a list of packages is already available in a file.\n        If not, it uses the sdkmanager.bat script to generate a list of available packages and saves it to a file. \n        It then returns the content of this file.\n\n    .PARAMETER SDKRootPath\n        The root path of the Android SDK installation.\n        If not specified, the function uses the ANDROID_HOME environment variable.\n\n    .EXAMPLE\n        Get-AndroidPackages -SDKRootPath \"C:\\Android\\SDK\"\n\n        This command returns a list of available Android packages for the specified SDK root path.\n\n    .NOTES\n        This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible.\n\n    #>\n    Param\n    (\n        [string] $SDKRootPath\n    )\n    \n    if (-not $SDKRootPath) {\n        $SDKRootPath = $env:ANDROID_HOME\n    }\n\n    $packagesListFile = \"$SDKRootPath\\packages-list.txt\"\n    $sdkManager = \"$SDKRootPath\\cmdline-tools\\latest\\bin\\sdkmanager.bat\"\n\n    if (-Not (Test-Path -Path $packagesListFile -PathType Leaf)) {\n        (cmd /c \"$sdkManager --list --verbose 2>&1\") |\n            Where-Object { $_ -Match \"^[^\\s]\" } |\n            Where-Object { $_ -NotMatch \"^(Loading |Info: Parsing |---|\\[=+|Installed |Available )\" } |\n            Where-Object { $_ -NotMatch \"^[^;]*$\" } |\n            Out-File -FilePath $packagesListFile\n    }\n\n    return Get-Content $packagesListFile\n}\n\nfunction Get-AndroidPlatformPackages {\n    <#\n    .SYNOPSIS\n        This function returns a list of available Android platform packages.\n\n    .DESCRIPTION\n        The Get-AndroidPlatformPackages function uses the Get-AndroidPackages function to get a list of available packages\n        and filters it to return only platform packages.\n\n    .PARAMETER SDKRootPath\n        The root path of the Android SDK installation.\n        If not specified, the function uses the ANDROID_HOME environment variable.\n\n    .PARAMETER minimumVersion\n        The minimum version of the platform packages to include in the result. Default is 0.\n\n    .EXAMPLE\n        Get-AndroidPlatformPackages -SDKRootPath \"C:\\Android\\SDK\" -minimumVersion 29\n\n        This command returns a list of available Android platform packages for the specified SDK root path with a minimum version of 29.\n\n    .NOTES\n        This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible.\n\n    #>\n    Param\n    (\n        [string] $SDKRootPath,\n        [Alias(\"minVersion\")]\n        [int] $minimumVersion = 0\n    )\n    \n    if (-not $SDKRootPath) {\n        $SDKRootPath = $env:ANDROID_HOME\n    }\n\n    return (Get-AndroidPackages -SDKRootPath $SDKRootPath) `\n    | Where-Object { \"$_\".StartsWith(\"platforms;\") } `\n    | Where-Object { ($_.Split(\"-\")[1] -as [int]) -ge $minimumVersion } `\n    | Sort-Object -Unique\n}\n\nfunction Get-AndroidBuildToolPackages {\n    <#\n    .SYNOPSIS\n        This function returns a list of available Android build tool packages.\n\n    .DESCRIPTION\n        The Get-AndroidBuildToolPackages function uses the Get-AndroidPackages function to get a list of available packages\n        and filters it to return only build tool packages.\n\n    .PARAMETER SDKRootPath\n        The root path of the Android SDK installation.\n        If not specified, the function uses the ANDROID_HOME environment variable.\n\n    .PARAMETER minimumVersion\n        The minimum version of the build tool packages to include in the result. Default is 0.0.0.\n\n    .EXAMPLE\n        Get-AndroidBuildToolPackages -SDKRootPath \"C:\\Android\\SDK\" -minimumVersion \"30.0.2\"\n\n        This command returns a list of available Android build tool packages for the specified SDK root path with a minimum version of 30.0.2.\n\n    .NOTES\n        This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible.\n\n    #>\n    Param\n    (\n        [string] $SDKRootPath,\n        [Alias(\"minVersion\")]\n        [version] $minimumVersion = \"0.0.0\"\n    )\n    \n    if (-not $SDKRootPath) {\n        $SDKRootPath = $env:ANDROID_HOME\n    }\n\n    return (Get-AndroidPackages -SDKRootPath $SDKRootPath) `\n    | Where-Object { \"$_\".StartsWith(\"build-tools;\") } `\n    | Where-Object { ($_.Split(\";\")[1] -as [version]) -ge $minimumVersion } `\n    | Sort-Object -Unique\n}\n\nfunction Get-AndroidInstalledPackages {\n    <#\n    .SYNOPSIS\n        Retrieves a list of installed Android packages.\n\n    .DESCRIPTION\n        This function retrieves a list of installed Android packages using the specified SDK root path.\n\n    .PARAMETER SDKRootPath\n        The root path of the Android SDK.\n        If not specified, the function uses the ANDROID_HOME environment variable.\n\n    .EXAMPLE\n        Get-AndroidInstalledPackages -SDKRootPath \"C:\\Android\\SDK\"\n        Retrieves a list of installed Android packages using the specified SDK root path.\n\n    .NOTES\n        This function requires the Android SDK to be installed and the SDK root path to be provided.\n    #>\n\n    Param\n    (\n        [string] $SDKRootPath\n    )\n    \n    if (-not $SDKRootPath) {\n        $SDKRootPath = $env:ANDROID_HOME\n    }\n    \n    $sdkManager = \"$SDKRootPath\\cmdline-tools\\latest\\bin\\sdkmanager.bat\"\n\n    return (cmd /c \"$sdkManager --list_installed 2>&1\") -notmatch \"Warning\"\n}\n"
  },
  {
    "path": "images/windows/scripts/helpers/ChocoHelpers.ps1",
    "content": "function Install-ChocoPackage {\n    <#\n    .SYNOPSIS\n        A function to install a Chocolatey package with retries.\n\n    .DESCRIPTION\n        This function attempts to install a specified Chocolatey package. If the \n        installation fails, it retries a specified number of times.\n\n    .PARAMETER PackageName\n        The name of the Chocolatey package to install.\n\n    .PARAMETER ArgumentList\n        An array of arguments to pass to the choco install command.\n\n    .PARAMETER RetryCount\n        The number of times to retry the installation if it fails. Default is 5.\n\n    .PARAMETER Version\n        The version of the package to install.\n\n    .EXAMPLE\n        Install-ChocoPackage -PackageName \"git\" -Version \"2.39.2\" -RetryCount 3\n    #>\n\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory)]\n        [string] $PackageName,\n        [string[]] $ArgumentList,\n        [string] $Version,\n        [int] $RetryCount = 5\n    )\n\n    process {\n        $count = 1\n        while ($true) {\n            Write-Host \"Running [#$count]: choco install $packageName -y $argumentList\"\n            if ($Version) {\n                choco install $packageName --version $Version -y @ArgumentList --no-progress --require-checksums\n            } else {\n                choco install $packageName -y @ArgumentList --no-progress --require-checksums\n            }\n            $pkg = choco list --localonly $packageName --exact --all --limitoutput\n            if ($pkg) {\n                Write-Host \"Package installed: $pkg\"\n                break\n            } else {\n                $count++\n                if ($count -ge $retryCount) {\n                    Write-Host \"Could not install $packageName after $count attempts\"\n                    exit 1\n                }\n                Start-Sleep -Seconds 30\n            }\n        }\n    }\n}\n\nfunction Resolve-ChocoPackageVersion {\n    <#\n    .SYNOPSIS\n        Resolves the latest version of a Chocolatey package.\n\n    .DESCRIPTION\n        This function takes a package name and a target version as input and returns the latest\n        version of the package that is greater than or equal to the target version.\n\n    .PARAMETER PackageName\n        The name of the Chocolatey package.\n\n    .PARAMETER TargetVersion\n        The target version of the package.\n\n    .EXAMPLE\n        Resolve-ChocoPackageVersion -PackageName \"example-package\" -TargetVersion \"1.0.0\"\n        Returns the latest version of the \"example-package\" that is greater than or equal to \"1.0.0\".\n    #>\n\n    param(\n        [Parameter(Mandatory)]\n        [string] $PackageName,\n        [Parameter(Mandatory)]\n        [string] $TargetVersion\n    )\n\n    $searchResult = choco search $PackageName --exact --all-versions --approved-only --limit-output | \n        ConvertFrom-CSV -Delimiter '|' -Header 'Name', 'Version'\n\n    $latestVersion = $searchResult.Version | \n        Where-Object { $_ -Like \"$TargetVersion.*\" -or $_ -eq $TargetVersion } | \n        Sort-Object { [version] $_ } | \n        Select-Object -Last 1\n\n    return $latestVersion\n}\n"
  },
  {
    "path": "images/windows/scripts/helpers/ImageHelpers.psd1",
    "content": "@{\n\n# Script module or binary module file associated with this manifest.\nRootModule = 'ImageHelpers.psm1'\n\n# Version number of this module.\nModuleVersion = '0.0.1'\n\n# Supported PSEditions\n# CompatiblePSEditions = @()\n\n# ID used to uniquely identify this module\nGUID = 'c9334909-16a1-48f1-a94a-c7baf1b961d9'\n\n# Description of the functionality provided by this module\nDescription = 'Helper functions for creating vsts images'\n\n# Minimum version of the Windows PowerShell engine required by this module\n# PowerShellVersion = ''\n\n# Name of the Windows PowerShell host required by this module\n# PowerShellHostName = ''\n\n# Minimum version of the Windows PowerShell host required by this module\n# PowerShellHostVersion = ''\n\n# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.\n# DotNetFrameworkVersion = ''\n\n# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.\n# CLRVersion = ''\n\n# Processor architecture (None, X86, Amd64) required by this module\n# ProcessorArchitecture = ''\n\n# Modules that must be imported into the global environment prior to importing this module\n# RequiredModules = @()\n\n# Assemblies that must be loaded prior to importing this module\n# RequiredAssemblies = @()\n\n# Script files (.ps1) that are run in the caller's environment prior to importing this module.\n# ScriptsToProcess = @()\n\n# Type files (.ps1xml) to be loaded when importing this module\n# TypesToProcess = @()\n\n# Format files (.ps1xml) to be loaded when importing this module\n# FormatsToProcess = @()\n\n# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess\n# NestedModules = @()\n\n# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.\nFunctionsToExport = '*'\n\n# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.\nCmdletsToExport = '*'\n\n# Variables to export from this module\nVariablesToExport = '*'\n\n# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.\nAliasesToExport = '*'\n\n# DSC resources to export from this module\n# DscResourcesToExport = @()\n\n# List of all modules packaged with this module\n# ModuleList = @()\n\n# List of all files packaged with this module\n# FileList = @()\n\n# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.\nPrivateData = @{\n\n    PSData = @{\n\n        # Tags applied to this module. These help with module discovery in online galleries.\n        # Tags = @()\n\n        # A URL to the license for this module.\n        # LicenseUri = ''\n\n        # A URL to the main website for this project.\n        # ProjectUri = ''\n\n        # A URL to an icon representing this module.\n        # IconUri = ''\n\n        # ReleaseNotes of this module\n        # ReleaseNotes = ''\n\n    } # End of PSData hashtable\n\n} # End of PrivateData hashtable\n\n# HelpInfo URI of this module\n# HelpInfoURI = ''\n\n# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.\n# DefaultCommandPrefix = ''\n\n}\n\n\n"
  },
  {
    "path": "images/windows/scripts/helpers/ImageHelpers.psm1",
    "content": "[CmdletBinding()]\nparam()\n\n. $PSScriptRoot\\AndroidHelpers.ps1\nExport-ModuleMember -Function @(\n    'Get-AndroidPackages'\n    'Get-AndroidPlatformPackages'\n    'Get-AndroidBuildToolPackages'\n    'Get-AndroidInstalledPackages'\n)\n\n. $PSScriptRoot\\ChocoHelpers.ps1\nExport-ModuleMember -Function @(\n    'Install-ChocoPackage'\n    'Resolve-ChocoPackageVersion'\n)\n\n. $PSScriptRoot\\InstallHelpers.ps1\nExport-ModuleMember -Function @(\n    'Install-Binary'\n    'Invoke-DownloadWithRetry'\n    'Get-MicrosoftPublisher'\n    'Get-ToolsetContent'\n    'Get-TCToolPath'\n    'Get-TCToolVersionPath'\n    'Test-IsWin25'\n    'Test-IsWin22'\n    'Expand-7ZipArchive'\n    'Get-WindowsUpdateStates'\n    'Invoke-ScriptBlockWithRetry'\n    'Get-GithubReleasesByVersion'\n    'Resolve-GithubReleaseAssetUrl'\n    'Get-ChecksumFromGithubRelease'\n    'Get-ChecksumFromUrl'\n    'Test-FileChecksum'\n    'Test-FileSignature'\n    'Update-Environment'\n)\n\n. $PSScriptRoot\\PathHelpers.ps1\nExport-ModuleMember -Function @(\n    'Mount-RegistryHive'\n    'Dismount-RegistryHive'\n    'Add-MachinePathItem'\n    'Add-DefaultPathItem'\n)\n\n. $PSScriptRoot\\VisualStudioHelpers.ps1\nExport-ModuleMember -Function @(\n    'Install-VisualStudio'\n    'Get-VisualStudioInstance'\n    'Get-VisualStudioComponents'\n    'Get-VsixInfoFromMarketplace'\n    'Install-VSIXFromFile'\n    'Install-VSIXFromUrl'\n    'Get-VSExtensionVersion'\n)\n"
  },
  {
    "path": "images/windows/scripts/helpers/InstallHelpers.ps1",
    "content": "function Install-Binary {\n    <#\n    .SYNOPSIS\n        A function to install binaries from either a URL or a local path.\n\n    .DESCRIPTION\n        This function downloads and installs .exe or .msi binaries from a specified URL or a local path. It also supports checking the binary's signature and SHA256/SHA512 sum before installation.\n\n    .PARAMETER Url\n        The URL from which the binary will be downloaded. This parameter is required if LocalPath is not specified.\n\n    .PARAMETER LocalPath\n        The local path of the binary to be installed. This parameter is required if Url is not specified.\n\n    .PARAMETER Type\n        The type of the binary to be installed. Valid values are \"MSI\" and \"EXE\". If not specified, the type is inferred from the file extension.\n\n    .PARAMETER InstallArgs\n        The list of arguments that will be passed to the installer. Cannot be used together with ExtraInstallArgs.\n\n    .PARAMETER ExtraInstallArgs\n        Additional arguments that will be passed to the installer. Cannot be used together with InstallArgs.\n\n    .PARAMETER ExpectedSubject\n        The expected signature subject of the binary. If specified, the binary's signature is checked before installation.\n\n    .PARAMETER ExpectedSHA256Sum\n        The expected SHA256 sum of the binary. If specified, the binary's SHA256 sum is checked before installation.\n\n    .PARAMETER ExpectedSHA512Sum\n        The expected SHA512 sum of the binary. If specified, the binary's SHA512 sum is checked before installation.\n\n    .PARAMETER InstallerLogPath\n        The path to the log file which is produced when the installation fails. This can be used for debugging purposes.\n        This is only displayed when the installation fails.\n\n    .EXAMPLE\n        Install-Binary -Url \"https://go.microsoft.com/fwlink/p/?linkid=2083338\" -Type EXE -InstallArgs (\"/features\", \"+\", \"/quiet\") -ExpectedSubject \"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US\"\n    #>\n\n    Param\n    (\n        [Parameter(Mandatory, ParameterSetName = \"Url\")]\n        [String] $Url,\n        [Parameter(Mandatory, ParameterSetName = \"LocalPath\")]\n        [String] $LocalPath,\n        [ValidateSet(\"MSI\", \"EXE\")]\n        [String] $Type,\n        [String[]] $InstallArgs,\n        [String[]] $ExtraInstallArgs,\n        [String] $ExpectedSubject,\n        [String] $ExpectedSHA256Sum,\n        [String] $ExpectedSHA512Sum,\n        [String] $InstallerLogPath\n    )\n\n    if ($PSCmdlet.ParameterSetName -eq \"LocalPath\") {\n        if (-not (Test-Path -Path $LocalPath)) {\n            throw \"LocalPath parameter is specified, but the file does not exist.\"\n        }\n        if (-not $Type) {\n            $Type = ([System.IO.Path]::GetExtension($LocalPath)).Replace(\".\", \"\").ToUpper()\n            if ($Type -ne \"MSI\" -and $Type -ne \"EXE\") {\n                throw \"LocalPath parameter is specified, but the file extension is not .msi or .exe. Please specify the Type parameter.\"\n            }\n        }\n        $filePath = $LocalPath\n    } else {\n        if (-not $Type) {\n            $Type = ([System.IO.Path]::GetExtension($Url)).Replace(\".\", \"\").ToUpper()\n            if ($Type -ne \"MSI\" -and $Type -ne \"EXE\") {\n                throw \"Cannot determine the file type from the URL. Please specify the Type parameter.\"\n            }\n            $fileName = [System.IO.Path]::GetFileName($Url)\n        } else {\n            $fileName = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetRandomFileName()) + \".$Type\".ToLower()\n        }\n        $filePath = Invoke-DownloadWithRetry -Url $Url -Path \"${env:TEMP_DIR}\\$fileName\"\n    }\n\n    if ($PSBoundParameters.ContainsKey('ExpectedSubject')) {\n        if ($ExpectedSubject) {\n            Test-FileSignature -Path $filePath -ExpectedSubject $ExpectedSubject\n        } else {\n            throw \"ExpectedSubject parameter is specified, but no value is provided.\"\n        }\n    }\n\n    if ($ExpectedSHA256Sum) {\n        Test-FileChecksum $filePath -ExpectedSHA256Sum $ExpectedSHA256Sum\n    }\n\n    if ($ExpectedSHA512Sum) {\n        Test-FileChecksum $filePath -ExpectedSHA512Sum $ExpectedSHA512Sum\n    }\n\n    if ($ExtraInstallArgs -and $InstallArgs) {\n        throw \"InstallArgs and ExtraInstallArgs parameters cannot be used together.\"\n    }\n\n    if ($Type -eq \"MSI\") {\n        # MSI binaries should be installed via msiexec.exe\n        if ($ExtraInstallArgs) {\n            $InstallArgs = @('/i', $filePath, '/qn', '/norestart') + $ExtraInstallArgs\n        } elseif (-not $InstallArgs) {\n            Write-Host \"No arguments provided for MSI binary. Using default arguments: /i, /qn, /norestart\"\n            $InstallArgs = @('/i', $filePath, '/qn', '/norestart')\n        }\n        $filePath = \"msiexec.exe\"\n    } else {\n        # EXE binaries should be started directly\n        if ($ExtraInstallArgs) {\n            $InstallArgs = $ExtraInstallArgs\n        }\n    }\n\n    $installStartTime = Get-Date\n    Write-Host \"Starting Install $Name...\"\n    try {\n        $process = Start-Process -FilePath $filePath -ArgumentList $InstallArgs -Wait -PassThru\n        $exitCode = $process.ExitCode\n        $installCompleteTime = [math]::Round(($(Get-Date) - $installStartTime).TotalSeconds, 2)\n        if ($exitCode -eq 0) {\n            Write-Host \"Installation successful in $installCompleteTime seconds\"\n        } elseif ($exitCode -eq 3010) {\n            Write-Host \"Installation successful in $installCompleteTime seconds. Reboot is required.\"\n        } else {\n            Write-Host \"Installation process returned unexpected exit code: $exitCode\"\n            Write-Host \"Time elapsed: $installCompleteTime seconds\"\n\n            if ($InstallerLogPath) {\n                Write-Host \"Searching for logs maching $InstallerLogPath pattern\"\n                Get-ChildItem -Recurse -Path $InstallerLogPath | ForEach-Object {\n                    Write-Output \"Found Installer Log: $InstallerLogPath\"\n                    Write-Output \"File content:\"\n                    Get-Content -Path $_.FullName\n                }\n            }\n            exit $exitCode\n        }\n    } catch {\n        $installCompleteTime = [math]::Round(($(Get-Date) - $installStartTime).TotalSeconds, 2)\n        Write-Host \"Installation failed in $installCompleteTime seconds\"\n    }\n}\n\nfunction Invoke-DownloadWithRetry {\n    <#\n    .SYNOPSIS\n        Downloads a file from a given URL with retry functionality.\n\n    .DESCRIPTION\n        The Invoke-DownloadWithRetry function downloads a file from the specified URL\n        to the specified path. It includes retry functionality in case the download fails.\n\n    .PARAMETER Url\n        The URL of the file to download.\n\n    .PARAMETER Path\n        The path where the downloaded file will be saved. If not provided, a temporary path\n        will be used.\n\n    .EXAMPLE\n        Invoke-DownloadWithRetry -Url \"https://example.com/file.zip\" -Path \"C:\\Downloads\\file.zip\"\n        Downloads the file from the specified URL and saves it to the specified path.\n\n    .EXAMPLE\n        Invoke-DownloadWithRetry -Url \"https://example.com/file.zip\"\n        Downloads the file from the specified URL and saves it to a temporary path.\n\n    .OUTPUTS\n        The path where the downloaded file is saved.\n    #>\n\n    Param\n    (\n        [Parameter(Mandatory)]\n        [string] $Url,\n        [Alias(\"Destination\")]\n        [string] $Path\n    )\n\n    if (-not $Path) {\n        $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''\n        $re = \"[{0}]\" -f [RegEx]::Escape($invalidChars)\n        $fileName = [IO.Path]::GetFileName($Url) -replace $re\n\n        if ([String]::IsNullOrEmpty($fileName)) {\n            $fileName = [System.IO.Path]::GetRandomFileName()\n        }\n        $Path = Join-Path -Path \"${env:TEMP_DIR}\" -ChildPath $fileName\n    }\n\n    Write-Host \"Downloading package from $Url to $Path...\"\n\n    $interval = 30\n    $downloadStartTime = Get-Date\n    for ($retries = 20; $retries -gt 0; $retries--) {\n        try {\n            $attemptStartTime = Get-Date\n            $ProgressPreference = 'SilentlyContinue'\n            Invoke-WebRequest -Uri $Url -OutFile $Path -UseBasicParsing\n            $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)\n            Write-Host \"Package downloaded in $attemptSeconds seconds\"\n            break\n        } catch {\n            $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2)\n            Write-Warning \"Package download failed in $attemptSeconds seconds\"\n            Write-Warning $_.Exception.Message\n\n            if ($_.Exception.InnerException.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {\n                Write-Warning \"Request returned 404 Not Found. Aborting download.\"\n                $retries = 0\n            }\n        }\n\n        if ($retries -eq 0) {\n            $totalSeconds = [math]::Round(($(Get-Date) - $downloadStartTime).TotalSeconds, 2)\n            throw \"Package download failed after $totalSeconds seconds\"\n        }\n\n        Write-Warning \"Waiting $interval seconds before retrying (retries left: $retries)...\"\n        Start-Sleep -Seconds $interval\n    }\n\n    return $Path\n}\n\nfunction Get-ToolsetContent {\n    <#\n    .SYNOPSIS\n        Retrieves the content of the toolset.json file.\n\n    .DESCRIPTION\n        This function reads the toolset.json file in path provided by IMAGE_FOLDER\n        environment variable and returns the content as a PowerShell object.\n    #>\n\n    $toolsetPath = Join-Path $env:IMAGE_FOLDER \"toolset.json\"\n    $toolsetJson = Get-Content -Path $toolsetPath -Raw\n    ConvertFrom-Json -InputObject $toolsetJson\n}\n\nfunction Get-TCToolPath {\n    <#\n    .SYNOPSIS\n        This function returns the full path of a tool in the tool cache.\n\n    .DESCRIPTION\n        The Get-TCToolPath function takes a tool name as a parameter and returns the full path of the tool in the tool cache. \n        It uses the AGENT_TOOLSDIRECTORY environment variable to determine the root path of the tool cache.\n\n    .PARAMETER ToolName\n        The name of the tool for which the path is to be returned.\n\n    .EXAMPLE\n        Get-TCToolPath -ToolName \"Tool1\"\n\n        This command returns the full path of \"Tool1\" in the tool cache.\n\n    #>\n    Param\n    (\n        [string] $ToolName\n    )\n\n    $toolcacheRootPath = Resolve-Path $env:AGENT_TOOLSDIRECTORY\n    return Join-Path $toolcacheRootPath $ToolName\n}\n\nfunction Get-TCToolVersionPath {\n    <#\n    .SYNOPSIS\n        This function returns the full path of a specific version of a tool in the tool cache.\n\n    .DESCRIPTION\n        The Get-TCToolVersionPath function takes a tool name, version, and architecture as parameters and returns the full path of the specified version of the tool in the tool cache. \n        It uses the Get-TCToolPath function to get the root path of the tool.\n\n    .PARAMETER Name\n        The name of the tool for which the path is to be returned.\n\n    .PARAMETER Version\n        The version of the tool for which the path is to be returned. If the version number is less than 3 parts, a wildcard is added.\n\n    .PARAMETER Arch\n        The architecture of the tool for which the path is to be returned. Defaults to \"x64\".\n\n    .EXAMPLE\n        Get-TCToolVersionPath -Name \"Tool1\" -Version \"1.0\" -Arch \"x86\"\n\n        This command returns the full path of version \"1.0\" of \"Tool1\" for \"x86\" architecture in the tool cache.\n\n    #>\n    Param\n    (\n        [Parameter(Mandatory = $true)]\n        [string] $Name,\n        [Parameter(Mandatory = $true)]\n        [string] $Version,\n        [string] $Arch = \"x64\"\n    )\n\n    $toolPath = Get-TCToolPath -ToolName $Name\n\n    # Add wildcard if missing\n    if ($Version.Split(\".\").Length -lt 3) {\n        $Version += \".*\"\n    }\n\n    $versionPath = Join-Path $toolPath $Version\n\n    # Take latest installed version in case if toolset version contains wildcards\n    $foundVersion = Get-Item $versionPath `\n    | Sort-Object -Property { [version] $_.name } -Descending `\n    | Select-Object -First 1\n\n    if (-not $foundVersion) {\n        return $null\n    }\n\n    return Join-Path $foundVersion $Arch\n}\n\nfunction Test-IsWin25 {\n    <#\n    .SYNOPSIS\n        Checks if the current Windows operating system is Windows Server 2025.\n    .DESCRIPTION\n        This function uses the Get-CimInstance cmdlet to retrieve information\n        about the current Windows operating system. It then checks if the Caption\n        property of the Win32_OperatingSystem class contains the string \"2025\",\n        indicating that the operating system is Windows Server 2025.\n    .OUTPUTS\n        Returns $true if the current Windows operating system is Windows Server 2025.\n        Otherwise, returns $false.\n    #>\n    (Get-CimInstance -ClassName Win32_OperatingSystem).Caption -match \"2025\"\n}\n\nfunction Test-IsWin22 {\n    <#\n    .SYNOPSIS\n        Checks if the current Windows operating system is Windows Server 2022.\n\n    .DESCRIPTION\n        This function uses the Get-CimInstance cmdlet to retrieve information\n        about the current Windows operating system. It then checks if the Caption\n        property of the Win32_OperatingSystem class contains the string \"2022\",\n        indicating that the operating system is Windows Server 2022.\n\n    .OUTPUTS\n        Returns $true if the current Windows operating system is Windows Server 2022.\n        Otherwise, returns $false.\n    #>\n    (Get-CimInstance -ClassName Win32_OperatingSystem).Caption -match \"2022\"\n}\n\nfunction Expand-7ZipArchive {\n    <#\n    .SYNOPSIS\n        Extracts files from a 7-Zip archive.\n\n    .DESCRIPTION\n        This function uses the 7z.exe command-line tool to extract files from an archive.\n        The archive path, destination path, and extract method are specified as parameters.\n\n    .PARAMETER Path\n        The path to the archive.\n\n    .PARAMETER DestinationPath\n        The path to the directory where the files will be extracted.\n\n    .PARAMETER ExtractMethod\n        The method used to extract the files.\n        Valid values are \"x\" (extract with full paths) and \"e\" (extract without paths).\n\n    .EXAMPLE\n        Expand-7ZipArchive -Path \"C:\\archive.7z\" -DestinationPath \"C:\\extracted\" -ExtractMethod \"x\"\n\n        Extracts files from the \"C:\\archive.7z\" archive to the \"C:\\extracted\" directory keeping the full paths.\n    #>\n    Param\n    (\n        [Parameter(Mandatory = $true)]\n        [string] $Path,\n        [Parameter(Mandatory = $true)]\n        [string] $DestinationPath,\n        [ValidateSet(\"x\", \"e\")]\n        [char] $ExtractMethod = \"x\"\n    )\n\n    Write-Host \"Expand archive '$PATH' to '$DestinationPath' directory\"\n    7z.exe $ExtractMethod \"$Path\" -o\"$DestinationPath\" -y | Out-Null\n\n    if ($LASTEXITCODE -ne 0) {\n        Write-Host \"There is an error during expanding '$Path' to '$DestinationPath' directory\"\n        exit 1\n    }\n}\n\nfunction Get-WindowsUpdateStates {\n    <#\n    .SYNOPSIS\n        Retrieves the status of Windows updates.\n\n    .DESCRIPTION\n        The Get-WindowsUpdateStates function checks the Windows Event Log for specific event IDs related to Windows updates and returns a custom PowerShell object with the state and title of each update.\n\n    .PARAMETER None\n        This function does not take any parameters.\n\n    .OUTPUTS\n        PSCustomObject. This function returns a collection of custom PowerShell objects. Each object has two properties:\n        - State: A string that represents the state of the update. Possible values are \"Installed\", \"Failed\", and \"Running\".\n        - Title: A string that represents the title of the update.\n\n    .NOTES\n        Event IDs used:\n        - 19: Installation Successful: Windows successfully installed the following update\n        - 20: Installation Failure: Windows failed to install the following update with error\n        - 43: Installation Started: Windows has started installing the following update\n    #>\n\n    $completedUpdates = @{}\n    $filter = @{\n        LogName      = \"System\"\n        Id           = 19, 20, 43\n        ProviderName = \"Microsoft-Windows-WindowsUpdateClient\"\n    }\n    $events = Get-WinEvent -FilterHashtable $filter -ErrorAction SilentlyContinue | Sort-Object Id\n\n    foreach ( $event in $events ) {\n        switch ( $event.Id ) {\n            19 {\n                $state = \"Installed\"\n                $title = $event.Properties[0].Value\n                $completedUpdates[$title] = $state\n                break\n            }\n            20 {\n                $state = \"Failed\"\n                $title = $event.Properties[1].Value\n                if (-not $completedUpdates.ContainsKey($title)) {\n                    $completedUpdates[$title] = $state\n                }\n                break\n            }\n            43 {\n                $state = \"Running\"\n                $title = $event.Properties[0].Value\n                break\n            }\n        }\n\n        # Skip Running update event if it was already completed\n        if ( ($state -eq \"Running\") -and $completedUpdates.ContainsKey($title) ) {\n            continue\n        }\n\n        # Skip Failed update event if it was already successfully installed\n        if ( ($state -eq \"Failed\") -and $completedUpdates[$title] -eq \"Installed\" ) {\n            continue\n        }\n\n        [PSCustomObject]@{\n            State = $state\n            Title = $title\n        }\n    }\n}\n\nfunction Invoke-ScriptBlockWithRetry {\n    <#\n    .SYNOPSIS\n        Executes a script block with retry logic.\n\n    .DESCRIPTION\n        The Invoke-ScriptBlockWithRetry function executes a specified script block with retry logic. It allows you to specify the number of retries and the interval between retries.\n\n    .PARAMETER Command\n        The script block to be executed.\n\n    .PARAMETER RetryCount\n        The number of times to retry executing the script block. The default value is 10.\n\n    .PARAMETER RetryIntervalSeconds\n        The interval in seconds between each retry. The default value is 5.\n\n    .EXAMPLE\n        Invoke-ScriptBlockWithRetry -Command { Get-Process } -RetryCount 3 -RetryIntervalSeconds 10\n        This example executes the script block { Get-Process } with 3 retries and a 10-second interval between each retry.\n\n    #>\n\n    param (\n        [scriptblock] $Command,\n        [int] $RetryCount = 10,\n        [int] $RetryIntervalSeconds = 5\n    )\n\n    while ($RetryCount -gt 0) {\n        try {\n            & $Command\n            return\n        } catch {\n            Write-Host \"There is an error encountered:`n $_\"\n            $RetryCount--\n\n            if ($RetryCount -eq 0) {\n                exit 1\n            }\n\n            Write-Host \"Waiting $RetryIntervalSeconds seconds before retrying. Retries left: $RetryCount\"\n            Start-Sleep -Seconds $RetryIntervalSeconds\n        }\n    }\n}\n\nfunction Get-GithubReleasesByVersion {\n    <#\n    .SYNOPSIS\n        Retrieves GitHub releases for a specified repository based on version.\n\n    .DESCRIPTION\n        The function retrieves GitHub releases for a specified repository based on the\n        version provided. It supports filtering by version, allowing for the retrieval\n        of specific releases or the latest release. The function utilizes the GitHub REST API\n        to fetch the releases and caches the results to improve performance and reduce\n        the number of API calls.\n\n    .PARAMETER Repository\n        The name of the GitHub repository in the format \"owner/repo\".\n\n    .PARAMETER Version\n        The version of the release to retrieve. It can be a specific version number,\n        \"latest\" to retrieve the latest release, or a wildcard pattern to match multiple versions.\n\n    .PARAMETER AllowPrerelease\n        Specifies whether to include prerelease versions in the results. By default,\n        prerelease versions are excluded.\n\n    .PARAMETER WithAssetsOnly\n        Specifies whether to exclude releases without assets. By default, releases without\n        assets are included.\n\n    .EXAMPLE\n        Get-GithubReleasesByVersion -Repository \"Microsoft/PowerShell\" -Version \"7.2.0\"\n\n        Retrieves the GitHub releases for the \"Microsoft/PowerShell\" repository with the version \"7.2.0\".\n\n    .EXAMPLE\n        Get-GithubReleasesByVersion -Repository \"Microsoft/PowerShell\" -Version \"latest\"\n\n        Retrieves the latest GitHub release for the \"Microsoft/PowerShell\" repository.\n\n    .EXAMPLE\n        Get-GithubReleasesByVersion -Repository \"Microsoft/PowerShell\" -Version \"7.*\"\n\n        Retrieves all GitHub releases for the \"Microsoft/PowerShell\" repository with versions starting with \"7.\".\n    #>\n\n    param (\n        [Parameter(Mandatory = $true)]\n        [Alias(\"Repo\")]\n        [string] $Repository,\n        [string] $Version = \"*\",\n        [switch] $AllowPrerelease,\n        [switch] $WithAssetsOnly\n    )\n\n    $localCacheFile = Join-Path ${env:TEMP_DIR} \"github-releases_$($Repository -replace \"/\", \"_\").json\"\n\n    if (Test-Path $localCacheFile) {\n        $releases = Get-Content $localCacheFile | ConvertFrom-Json\n        Write-Debug \"Found cached releases for ${Repository} in local file\"\n        Write-Debug \"Release count: $($releases.Count)\"\n    } else {\n        $releases = @()\n        $page = 1\n        $pageSize = 100\n        do {\n            $releasesPage = Invoke-RestMethod -Uri \"https://api.github.com/repos/${Repository}/releases?per_page=${pageSize}&page=${page}\"\n            $releases += $releasesPage\n            $page++\n        } while ($releasesPage.Count -eq $pageSize)\n\n        Write-Debug \"Found $($releases.Count) releases for ${Repository}\"\n        Write-Debug \"Caching releases for ${Repository} in local file\"\n        $releases | ConvertTo-Json -Depth 10 | Set-Content $localCacheFile\n    }\n\n    if (-not $releases) {\n        throw \"Failed to get releases from ${Repository}\"\n    }\n\n    if ($WithAssetsOnly) {\n        $releases = $releases.Where{ $_.assets }\n    }\n    if (-not $AllowPrerelease) {\n        $releases = $releases.Where{ $_.prerelease -eq $false }\n    }\n    Write-Debug \"Found $($releases.Count) releases with assets for ${Repository}\"\n\n    # Parse version from tag name and put it to parameter Version\n    foreach ($release in $releases) {\n        $release | Add-Member -MemberType NoteProperty -Name version -Value (\n            $release.tag_name | Select-String -Pattern \"\\d+.\\d+.\\d+\" | ForEach-Object { $_.Matches.Value }\n        )\n    }\n\n    # Sort releases by version, then by tag name parts if version is the same\n    $releases = $releases | Sort-Object -Descending {\n        [version] $_.version\n    }, {\n        $cleanTagName = $_.tag_name -replace '^v', ''\n        $parts = $cleanTagName -split '[.\\-]'\n        $parsedParts = $parts | ForEach-Object {\n            if ($_ -match '^\\d+$') { [int]$_ } else { $_ }\n        }\n        $parsedParts\n    }\n\n    # Select releases matching version\n    if ($Version -eq \"latest\") {\n        $matchingReleases = $releases | Select-Object -First 1\n    } elseif ($Version.Contains(\"*\")) {\n        $matchingReleases = $releases | Where-Object { $_.version -like \"$Version\" }\n    } else {\n        $matchingReleases = $releases | Where-Object { $_.version -eq \"$Version\" }\n    }\n\n    if (-not $matchingReleases) {\n        throw \"Failed to get releases from ${Repository} matching version `\"${Version}`\".`nAvailable versions: $($availableVersions -join \", \")\"\n    }\n    Write-Debug \"Found $($matchingReleases.Count) releases matching version ${Version} for ${Repository}\"\n\n    return $matchingReleases\n}\n\nfunction Resolve-GithubReleaseAssetUrl {\n    <#\n    .SYNOPSIS\n        Resolves the download URL for a specific asset in a GitHub release.\n\n    .DESCRIPTION\n        This function retrieves the download URL for a specific asset in a GitHub release.\n        It takes the repository name, version, and a URL match pattern as input parameters.\n        It searches for releases that match the specified version and then looks\n        for a download URL that matches the provided pattern. If a matching URL is found,\n        it returns the URL. If no matching URL is found, an exception is thrown.\n\n    .PARAMETER Repository\n        The name of the GitHub repository in the format \"owner/repo\".\n\n    .PARAMETER Version\n        The version of the release to retrieve. It can be a specific version number,\n        \"latest\" to retrieve the latest release, or a wildcard pattern to match multiple versions.\n\n    .PARAMETER AllowPrerelease\n        Specifies whether to include prerelease versions in the results. By default,\n        prerelease versions are excluded.\n\n    .PARAMETER UrlMatchPattern\n        The pattern to match against the download URLs of the release assets.\n        Wildcards (*) can be used to match any characters.\n\n    .PARAMETER AllowMultipleMatches\n        Specifies whether to choose one of multiple assets matching the pattern or consider this behavior to be erroneous.\n        By default, multiple matches are not considered normal behavior and result in an error.\n\n    .EXAMPLE\n        Resolve-GithubReleaseAssetUrl -Repository \"myrepo\" -Version \"1.0\" -UrlMatchPattern \"*.zip\"\n        Retrieves the download URL for the asset in the \"myrepo\" repository with version \"1.0\" and a file extension of \".zip\".\n\n    #>\n\n    param (\n        [Parameter(Mandatory = $true)]\n        [Alias(\"Repo\")]\n        [string] $Repository,\n        [string] $Version = \"*\",\n        [switch] $AllowPrerelease,\n        [Parameter(Mandatory = $true)]\n        [Alias(\"Pattern\", \"File\", \"Asset\")]\n        [string] $UrlMatchPattern,\n        [switch] $AllowMultipleMatches = $false\n    )\n\n    $matchingReleases = Get-GithubReleasesByVersion `\n        -Repository $Repository `\n        -AllowPrerelease:$AllowPrerelease `\n        -Version $Version `\n        -WithAssetsOnly\n\n    # Add wildcard to the beginning of the pattern if it's not there\n    if ($UrlMatchPattern.Substring(0, 2) -ne \"*/\") {\n        $UrlMatchPattern = \"*/$UrlMatchPattern\"\n    }\n\n    # Loop over releases until we find a download url matching the pattern\n    foreach ($release in $matchingReleases) {\n        $matchedVersion = $release.version\n        $matchedUrl = ([string[]] $release.assets.browser_download_url) -like $UrlMatchPattern\n        if ($matchedUrl) {\n            break\n        }\n    }\n\n    if (-not $matchedUrl) {\n        Write-Debug \"Found no download urls matching pattern ${UrlMatchPattern}\"\n        Write-Debug \"Available download urls:`n$($matchingReleases.assets.browser_download_url -join \"`n\")\"\n        throw \"No assets found in ${Repository} matching version `\"${Version}`\" and pattern `\"${UrlMatchPattern}`\"\"\n    }\n    # If multiple urls match the pattern, sort them and take the last one\n    # Will only work with simple number series of no more than nine in a row.\n    if ($matchedUrl.Count -gt 1) {\n        if ($AllowMultipleMatches) {\n            Write-Debug \"Found multiple download urls matching pattern ${UrlMatchPattern}:`n$($matchedUrl -join \"`n\")\"\n            Write-Host \"Performing sorting of urls to find the most recent version matching the pattern\"\n            $matchedUrl = $matchedUrl | Sort-Object -Descending\n            $matchedUrl = $matchedUrl[0]\n        } else {\n            throw \"Found multiple assets in ${Repository} matching version `\"${Version}`\" and pattern `\"${UrlMatchPattern}`\".`nAvailable assets:`n$($matchedUrl -join \"`n\")\"\n        }\n    }\n\n    Write-Host \"Found download url for ${Repository} version ${matchedVersion}: ${matchedUrl}\"\n\n    return ($matchedUrl -as [string])\n}\n\nfunction Get-ChecksumFromGithubRelease {\n    <#\n    .SYNOPSIS\n        Retrieves the hash value of a specific file from a GitHub release body.\n\n    .DESCRIPTION\n        The Get-ChecksumFromGithubRelease function retrieves the hash value (SHA256 or SHA512)\n        of a specific file from a GitHub release. It searches for the file in the release body\n        and returns the hash value if found.\n\n    .PARAMETER Repository\n        The name of the GitHub repository in the format \"owner/repo\".\n\n    .PARAMETER Version\n        The version of the release to inspect. It can be a specific version number,\n        \"latest\" to retrieve the latest release, or a wildcard pattern to match multiple versions.\n\n    .PARAMETER AllowPrerelease\n        Specifies whether to include prerelease versions in the results. By default,\n        prerelease versions are excluded.\n\n    .PARAMETER FileName\n        The name of the file to retrieve the hash value for.\n\n    .PARAMETER HashType\n        The type of hash value to retrieve. Valid values are \"SHA256\" and \"SHA512\".\n\n    .EXAMPLE\n        Get-ChecksumFromGithubRelease -Repository \"MyRepo\" -FileName \"myfile.txt\" -HashType \"SHA256\"\n\n        Retrieves the SHA256 hash value of \"myfile.txt\" from the latest release of the \"MyRepo\" repository.\n\n    .EXAMPLE\n        Get-ChecksumFromGithubRelease -Repository \"MyRepo\" -Version \"1.0\" -FileName \"myfile.txt\" -HashType \"SHA512\"\n\n        Retrieves the SHA512 hash value of \"myfile.txt\" from the release version \"1.0\" of the \"MyRepo\" repository.\n    #>\n\n    param (\n        [Parameter(Mandatory = $true)]\n        [Alias(\"Repo\")]\n        [string] $Repository,\n        [string] $Version = \"*\",\n        [switch] $AllowPrerelease,\n        [Parameter(Mandatory = $true)]\n        [Alias(\"File\", \"Asset\")]\n        [string] $FileName,\n        [Parameter(Mandatory = $false)]\n        [ValidateSet(\"SHA256\", \"SHA512\")]\n        [string] $HashType\n    )\n\n    $matchingReleases = Get-GithubReleasesByVersion `\n        -Repository $Repository `\n        -AllowPrerelease:$AllowPrerelease `\n        -Version $Version `\n        -WithAssetsOnly\n\n    foreach ($release in $matchingReleases) {\n        $matchedVersion = $release.version\n        $matchedBody = $release.body\n        $matchedLine = $matchedBody.Split(\"`n\") | Where-Object { $_ -like \"*$FileName*\" }\n        if ($matchedLine.Count -gt 1) {\n            throw \"Found multiple lines matching file name '${FileName}' in body of release ${matchedVersion}.\"\n        } elseif ($matchedLine.Count -ne 0) {\n            break\n        }\n    }\n    if (-not $matchedLine) {\n        throw \"File name '${FileName}' not found in release body.\"\n    }\n    Write-Debug \"Found line matching file name '${FileName}' in body of release ${matchedVersion}:`n${matchedLine}\"\n\n    if ($HashType -eq \"SHA256\") {\n        $pattern = \"[A-Fa-f0-9]{64}\"\n    } elseif ($HashType -eq \"SHA512\") {\n        $pattern = \"[A-Fa-f0-9]{128}\"\n    } else {\n        throw \"Unknown hash type: ${HashType}\"\n    }\n\n    $hash = $matchedLine | Select-String -Pattern $pattern | ForEach-Object { $_.Matches.Value }\n\n    if ([string]::IsNullOrEmpty($hash)) {\n        throw \"Found '${FileName}' in body of release ${matchedVersion}, but failed to get hash from it.`nLine: ${matchedLine}\"\n    }\n    Write-Host \"Found hash for ${FileName} in release ${matchedVersion}: $hash\"\n\n    return $hash\n}\n\nfunction Get-ChecksumFromUrl {\n    <#\n    .SYNOPSIS\n        Retrieves the checksum hash for a file from a given URL.\n\n    .DESCRIPTION\n        The Get-ChecksumFromUrl function retrieves the checksum hash for a specified file\n        from a given URL. It supports SHA256 and SHA512 hash types.\n\n    .PARAMETER Url\n        The URL of the checksum file.\n\n    .PARAMETER FileName\n        The name of the file to retrieve the checksum hash for.\n\n    .PARAMETER HashType\n        The type of hash to retrieve. Valid values are \"SHA256\" and \"SHA512\".\n\n    .EXAMPLE\n        Get-ChecksumFromUrl -Url \"https://example.com/checksums.txt\" -FileName \"file.txt\" -HashType \"SHA256\"\n        Retrieves the SHA256 checksum hash for the file \"file.txt\" from the URL \"https://example.com/checksums.txt\".\n    #>\n\n    param (\n        [Parameter(Mandatory = $true)]\n        [string] $Url,\n        [Parameter(Mandatory = $true)]\n        [Alias(\"File\", \"Asset\")]\n        [string] $FileName,\n        [Parameter(Mandatory = $false)]\n        [ValidateSet(\"SHA256\", \"SHA512\")]\n        [Alias(\"Type\")]\n        [string] $HashType\n    )\n\n    $tempFile = Join-Path -Path $env:TEMP_DIR -ChildPath ([System.IO.Path]::GetRandomFileName())\n    $checksums = (Invoke-DownloadWithRetry -Url $Url -Path $tempFile | Get-Item | Get-Content) -as [string[]]\n    Remove-Item -Path $tempFile\n\n    $matchedLine = $checksums | Where-Object { $_ -like \"*$FileName*\" }\n    if ($matchedLine.Count -gt 1) {\n        throw \"Found multiple lines matching file name '${FileName}' in checksum file.\"\n    } elseif ($matchedLine.Count -eq 0) {\n        throw \"File name '${FileName}' not found in checksum file.\"\n    }\n\n    if ($HashType -eq \"SHA256\") {\n        $pattern = \"[A-Fa-f0-9]{64}\"\n    } elseif ($HashType -eq \"SHA512\") {\n        $pattern = \"[A-Fa-f0-9]{128}\"\n    } else {\n        throw \"Unknown hash type: ${HashType}\"\n    }\n    Write-Debug \"Found line matching file name '${FileName}' in checksum file:`n${matchedLine}\"\n\n    $hash = $matchedLine | Select-String -Pattern $pattern | ForEach-Object { $_.Matches.Value }\n    if ([string]::IsNullOrEmpty($hash)) {\n        throw \"Found '${FileName}' in checksum file, but failed to get hash from it.`nLine: ${matchedLine}\"\n    }\n    Write-Host \"Found hash for ${FileName} in checksum file: $hash\"\n\n    return $hash\n}\n\nfunction Test-FileChecksum {\n    <#\n    .SYNOPSIS\n        Verifies the checksum of a file.\n\n    .DESCRIPTION\n        The Test-FileChecksum function verifies the SHA256 or SHA512 checksum of a file against an expected value. \n        If the checksum does not match the expected value, the function throws an error.\n\n    .PARAMETER Path\n        The path to the file for which to verify the checksum.\n\n    .PARAMETER ExpectedSHA256Sum\n        The expected SHA256 checksum. If this parameter is provided, the function will calculate the SHA256 checksum of the file and compare it to this value.\n\n    .PARAMETER ExpectedSHA512Sum\n        The expected SHA512 checksum. If this parameter is provided, the function will calculate the SHA512 checksum of the file and compare it to this value.\n\n    .EXAMPLE\n        Test-FileChecksum -Path \"C:\\temp\\file.txt\" -ExpectedSHA256Sum \"ABC123\"\n\n        Verifies that the SHA256 checksum of the file at C:\\temp\\file.txt is ABC123.\n\n    .EXAMPLE\n        Test-FileChecksum -Path \"C:\\temp\\file.txt\" -ExpectedSHA512Sum \"DEF456\"\n\n        Verifies that the SHA512 checksum of the file at C:\\temp\\file.txt is DEF456.\n\n    #>\n\n    param (\n        [Parameter(Mandatory = $true, Position = 0)]\n        [string] $Path,\n        [Parameter(Mandatory = $false)]\n        [String] $ExpectedSHA256Sum,\n        [Parameter(Mandatory = $false)]\n        [String] $ExpectedSHA512Sum\n    )\n\n    Write-Verbose \"Performing checksum verification\"\n\n    if ($ExpectedSHA256Sum -and $ExpectedSHA512Sum) {\n        throw \"Only one of the ExpectedSHA256Sum and ExpectedSHA512Sum parameters can be provided\"\n    }\n\n    if (-not (Test-Path $Path)) {\n        throw \"File not found: $Path\"\n    }\n\n    if ($ExpectedSHA256Sum) {\n        $fileHash = (Get-FileHash -Path $Path -Algorithm SHA256).Hash\n        $expectedHash = $ExpectedSHA256Sum\n    }\n\n    if ($ExpectedSHA512Sum) {\n        $fileHash = (Get-FileHash -Path $Path -Algorithm SHA512).Hash\n        $expectedHash = $ExpectedSHA512Sum\n    }\n\n    if ($fileHash -ne $expectedHash) {\n        throw \"Checksum verification failed: expected $expectedHash, got $fileHash\"\n    } else {\n        Write-Verbose \"Checksum verification passed\"\n    }\n}\n\nfunction Test-FileSignature {\n    <#\n    .SYNOPSIS\n        Tests the file signature of a given file.\n\n    .DESCRIPTION\n        The Test-FileSignature function checks the signature of a file against the expected subject. \n        It uses the Get-AuthenticodeSignature cmdlet to retrieve the signature information of the file.\n        If the signature status is not valid or the subject of the signing certificate does not match the expected subject, an exception is thrown.\n\n    .PARAMETER Path\n        Specifies the path of the file to test.\n\n    .PARAMETER ExpectedSubject\n        Specifies the expected subject to match against the file's signature.\n\n    .EXAMPLE\n        Test-FileSignature -Path \"C:\\Path\\To\\File.exe\" -ExpectedSubject \"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US\"\n\n        This example tests the signature of the file \"C:\\Path\\To\\File.exe\" against the expected subject \"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US\".\n\n    #>\n\n    param(\n        [Parameter(Mandatory = $true, Position = 0)]\n        [string] $Path,\n        [Parameter(Mandatory = $true, Position = 1)]\n        [string] $ExpectedSubject\n    )\n\n    $signature = Get-AuthenticodeSignature $Path\n\n    if ($signature.Status -ne \"Valid\") {\n        throw \"Signature status is not valid. Status: $($signature.Status)\"\n    }\n\n    if ($signature.SignerCertificate.EnhancedKeyUsageList.FriendlyName -notcontains \"Code Signing\") {\n        throw \"Certificate is not for code signing. Key usage: $($signature.SignerCertificate.EnhancedKeyUsageList)\"\n    }\n\n    if ($signature.SignerCertificate.Subject -ne $ExpectedSubject) {\n        throw \"Certificate subject does not match. Subject: $($signature.SignerCertificate.Subject)\"\n    }\n\n    Write-Output \"Signature for $Path is valid\"\n}\n\nfunction Update-Environment {\n    <#\n    .SYNOPSIS\n        Updates the environment variables by reading values from the registry.\n\n    .DESCRIPTION\n        This function updates current environment by reading values from the registry.\n        It is useful when you need to update the environment variables without restarting the current session.\n\n    .NOTES\n        The function requires administrative privileges to modify the system registry.\n    #>\n\n    $locations = @(\n        'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment',\n        'HKCU:\\Environment'\n    )\n\n    # Update PATH variable\n    $pathItems = $locations | ForEach-Object {\n        (Get-Item $_).GetValue('PATH').Split(';')\n    } | Select-Object -Unique\n    $env:PATH = $pathItems -join ';'\n\n    # Update other variables\n    $locations | ForEach-Object {\n        $key = Get-Item $_\n        foreach ($name in $key.GetValueNames()) {\n            $value = $key.GetValue($name)\n            if (-not ($name -ieq 'PATH')) {\n                Set-Item -Path Env:$name -Value $value\n            }\n        }\n    }\n}\n\nfunction Get-MicrosoftPublisher {\n    <#\n    .SYNOPSIS\n        Returns well-known subject for the Microsoft signing certificate\n    #>\n\n    return \"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US\"\n}\n"
  },
  {
    "path": "images/windows/scripts/helpers/PathHelpers.ps1",
    "content": "\nfunction Mount-RegistryHive {\n    <#\n    .SYNOPSIS\n        Mounts a registry hive from a file.\n\n    .DESCRIPTION\n        The Mount-RegistryHive function loads a registry hive from a specified file into a specified subkey.\n\n    .PARAMETER FileName\n        The path to the file from which to load the registry hive.\n\n    .PARAMETER SubKey\n        The registry subkey into which to load the hive.\n\n    .EXAMPLE\n        Mount-RegistryHive -FileName \"C:\\Path\\To\\HiveFile.hiv\" -SubKey \"HKLM\\SubKey\"\n    #>\n    param(\n        [Parameter(Mandatory = $true)]\n        [string] $FileName,\n        [Parameter(Mandatory = $true)]\n        [string] $SubKey\n    )\n\n    Write-Host \"Loading the file $FileName to the Key $SubKey\"\n    if (Test-Path $SubKey.Replace(\"\\\", \":\")) {\n        Write-Warning \"The key $SubKey is already loaded\"\n        return\n    }\n\n    $result = reg load $SubKey $FileName *>&1\n    if ($LASTEXITCODE -ne 0) {\n        throw \"Failed to load file $FileName to the key ${SubKey}: $result\"\n    }\n}\n\nfunction Dismount-RegistryHive {\n    <#\n    .SYNOPSIS\n        Dismounts a registry hive.\n\n    .DESCRIPTION\n        The Dismount-RegistryHive function unloads a registry hive from a specified subkey.\n\n    .PARAMETER SubKey\n        The registry subkey from which to unload the hive.\n\n    .EXAMPLE\n        Dismount-RegistryHive -SubKey \"HKLM\\SubKey\"\n    #>\n    param(\n        [Parameter(Mandatory = $true)]\n        [string] $SubKey\n    )\n\n    Write-Host \"Unloading the hive $SubKey\"\n    if (-not (Test-Path $SubKey.Replace(\"\\\", \":\"))) {\n        return\n    }\n\n    $result = reg unload $SubKey *>&1\n    if ($LASTEXITCODE -ne 0) {\n        Write-Host \"Failed to unload hive: $result\"\n        exit 1\n    }\n}\n\nfunction Add-MachinePathItem {\n    <#\n    .SYNOPSIS\n        Adds a new item to the machine-level PATH environment variable.\n\n    .DESCRIPTION\n        The Add-MachinePathItem function adds a new item to the machine-level PATH environment variable.\n        It takes a string parameter, $PathItem, which represents the new item to be added to the PATH.\n\n    .PARAMETER PathItem\n        Specifies the new item to be added to the machine-level PATH environment variable.\n\n    .EXAMPLE\n        Add-MachinePathItem -PathItem \"C:\\Program Files\\MyApp\"\n\n        This example adds \"C:\\Program Files\\MyApp\" to the machine-level PATH environment variable.\n    #>\n\n    param(\n        [Parameter(Mandatory = $true)]\n        [string] $PathItem\n    )\n\n    $currentPath = [System.Environment]::GetEnvironmentVariable(\"PATH\", \"Machine\")\n    $newPath = $PathItem + ';' + $currentPath\n    [Environment]::SetEnvironmentVariable(\"PATH\", $newPath, \"Machine\")\n}\n\nfunction Add-DefaultPathItem {\n    <#\n    .SYNOPSIS\n        Adds a path item to the default user profile path.\n\n    .DESCRIPTION\n        This function adds a specified path item to the default user profile path.\n        It mounts the NTUSER.DAT file of the default user to the registry,\n        retrieves the current value of the \"Path\" environment variable,\n        appends the new path item to it, and updates the registry with the modified value.\n\n    .PARAMETER PathItem\n        The path item to be added to the default user profile path.\n\n    .EXAMPLE\n        Add-DefaultPathItem -PathItem \"C:\\Program Files\\MyApp\"\n\n        This example adds \"C:\\Program Files\\MyApp\" to the default user profile path.\n\n    .NOTES\n        This function requires administrative privileges to modify the Windows registry.\n    #>\n\n    param(\n        [Parameter(Mandatory = $true)]\n        [string] $PathItem\n    )\n\n    Mount-RegistryHive `\n        -FileName \"C:\\Users\\Default\\NTUSER.DAT\" `\n        -SubKey \"HKLM\\DEFAULT\"\n\n    $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey(\"DEFAULT\\Environment\", $true)\n    $currentValue = $key.GetValue(\"Path\", \"\", \"DoNotExpandEnvironmentNames\")\n    $updatedValue = $PathItem + ';' + $currentValue\n    $key.SetValue(\"Path\", $updatedValue, \"ExpandString\")\n    $key.Handle.Close()\n    [System.GC]::Collect()\n\n    Dismount-RegistryHive \"HKLM\\DEFAULT\"\n}\n"
  },
  {
    "path": "images/windows/scripts/helpers/VisualStudioHelpers.ps1",
    "content": "Function Install-VisualStudio {\n    <#\n    .SYNOPSIS\n        A helper function to install Visual Studio.\n\n    .DESCRIPTION\n        Prepare system environment, and install Visual Studio bootstrapper with selected workloads.\n\n    .PARAMETER Version\n        The version of Visual Studio that will be installed. Required parameter.\n\n    .PARAMETER Edition\n        The edition of Visual Studio that will be installed. Required parameter.\n\n    .PARAMETER Channel\n        The channel of Visual Studio that will be installed. Required parameter.\n\n    .PARAMETER InstallChannelUri\n        The InstallChannelUri of Visual Studio that will be installed. Optional parameter.\n\n    .PARAMETER RequiredComponents\n        The list of required components. Required parameter.\n\n    .PARAMETER ExtraArgs\n        The extra arguments to pass to the bootstrapper. Optional parameter.\n    #>\n\n    Param\n    (\n        [Parameter(Mandatory)] [String] $Version,\n        [Parameter(Mandatory)] [String] $Edition,\n        [Parameter(Mandatory)] [String] $Channel,\n        [String] $InstallChannelUri = \"\",\n        [Parameter(Mandatory)] [String[]] $RequiredComponents,\n        [String] $ExtraArgs = \"\"\n    )\n\n    if ($env:INSTALL_VS_2026) {\n        $bootstrapperUrl = \"https://aka.ms/vs/postGRO-${Channel}/vs_${Edition}.exe\"\n    } else {\n        $bootstrapperUrl = \"https://aka.ms/vs/${Version}/postGRO-${Channel}/vs_${Edition}.exe\"\n    }\n    $channelUri = \"https://aka.ms/vs/${Version}/${Channel}/channel\"\n    $channelId = \"VisualStudio.${Version}.Release\"\n    $productId = \"Microsoft.VisualStudio.Product.${Edition}\"\n    if (-not [string]::IsNullOrEmpty($InstallChannelUri)) {\n        $installChannelUri = $InstallChannelUri\n    } else {\n        $installChannelUri = $channelUri\n    }\n\n    Write-Host \"Downloading Bootstrapper ...\"\n    $bootstrapperFilePath = Invoke-DownloadWithRetry $BootstrapperUrl\n\n    # Verify that the bootstrapper is signed by Microsoft\n    Test-FileSignature -Path $bootstrapperFilePath -ExpectedSubject $(Get-MicrosoftPublisher)\n\n    try {\n        $responseData = @{\n            \"installChannelUri\" = $installChannelUri\n            \"channelUri\"        = $channelUri\n            \"channelId\"         = $channelId\n            \"productId\"         = $productId\n            \"arch\"              = \"x64\"\n            \"add\"               = $RequiredComponents | ForEach-Object { \"$_;includeRecommended\" }\n        }\n\n        # Create json file with response data\n        $responseDataPath = \"$env:TEMP\\vs_install_response.json\"\n        $responseData | ConvertTo-Json | Out-File -FilePath $responseDataPath\n\n        $installStartTime = Get-Date\n        Write-Host \"Starting Install ...\"\n        $bootstrapperArgumentList = ('/c', $bootstrapperFilePath, '--in', $responseDataPath, $ExtraArgs, '--quiet', '--norestart', '--wait', '--nocache' )\n        Write-Host \"Bootstrapper arguments: $bootstrapperArgumentList\"\n        $process = Start-Process -FilePath cmd.exe -ArgumentList $bootstrapperArgumentList -Wait -PassThru\n\n        $exitCode = $process.ExitCode\n        $installCompleteTime = [math]::Round(($(Get-Date) - $installStartTime).TotalSeconds, 2)\n        if ($exitCode -eq 0) {\n            Write-Host \"Installation successful in $installCompleteTime seconds\"\n            return $exitCode\n        } elseif ($exitCode -eq 3010) {\n            Write-Host \"Installation successful in $installCompleteTime seconds. Reboot is required.\"\n            return $exitCode\n        } else {\n            Write-Host \"Non zero exit code returned by the installation process : $exitCode\"\n\n            # Try to download tool to collect logs\n            $collectExeUrl = \"https://aka.ms/vscollect.exe\"\n            $collectExePath = Invoke-DownloadWithRetry -Url $collectExeUrl\n\n            # Collect installation logs using the collect.exe tool and check if it is successful\n            & \"$collectExePath\"\n            if ($LastExitCode -ne 0) {\n                Write-Host \"Failed to collect logs using collect.exe tool. Exit code : $LastExitCode\"\n                exit $exitCode\n            }\n\n            # Expand the zip file\n            Expand-Archive -Path \"$env:TEMP_DIR\\vslogs.zip\" -DestinationPath \"$env:TEMP_DIR\\vslogs\"\n\n            # Print logs\n            $vsLogsPath = \"$env:TEMP_DIR\\vslogs\"\n            $vsLogs = Get-ChildItem -Path $vsLogsPath -Recurse | Where-Object { -not $_.PSIsContainer } | Select-Object -ExpandProperty FullName\n            foreach ($log in $vsLogs) {\n                Write-Host \"============================\"\n                Write-Host \"== Log file : $log \"\n                Write-Host \"============================\"\n                Get-Content -Path $log -ErrorAction Continue\n            }\n\n            exit $exitCode\n        }\n    }\n    catch\n    {\n        Write-Host \"Failed to install Visual Studio; $($_.Exception.Message)\"\n        exit -1\n    }\n}\n\nfunction Get-VisualStudioInstance {\n    <#\n    .SYNOPSIS\n        Retrieves the Visual Studio instance.\n\n    .DESCRIPTION\n        This function retrieves the Visual Studio instance\n        using the Get-VSSetupInstance cmdlet.\n        It searches for both regular and preview versions\n        of Visual Studio and returns the first instance found.\n    #>\n\n    # Use -Prerelease and -All flags to make sure that Preview versions of VS are found correctly\n    $vsInstance = Get-VSSetupInstance -Prerelease -All | Where-Object { $_.DisplayName -match \"Visual Studio\" } | Select-Object -First 1\n    $vsInstance | Select-VSSetupInstance -Product *\n}\n\nfunction Get-VisualStudioComponents {\n    <#\n    .SYNOPSIS\n        Retrieves the Visual Studio components.\n\n    .DESCRIPTION\n        This function retrieves the Visual Studio components\n        by filtering the packages returned by Get-VisualStudioInstance cmdlet.\n        It filters the packages based on their type, sorts them by Id and Version,\n        and excludes packages with GUID-like Ids.\n    #>\n\n    (Get-VisualStudioInstance).Packages `\n    | Where-Object type -in 'Component', 'Workload' `\n    | Sort-Object Id, Version `\n    | Select-Object @{n = 'Package'; e = { $_.Id } }, Version `\n    | Where-Object { $_.Package -notmatch \"[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}\" }\n}\n\nfunction Get-VsixInfoFromMarketplace {\n    <#\n    .SYNOPSIS\n        Retrieves information about a Visual Studio extension from the Visual Studio Marketplace.\n\n    .DESCRIPTION\n        The Get-VsixInfoFromMarketplace function retrieves information\n        about a Visual Studio extension from the Visual Studio Marketplace.\n        It takes the name of the extension as input and returns\n        the extension's name, VsixId, filename, and download URI.\n\n    .PARAMETER Name\n        The name of the Visual Studio extension.\n\n    .PARAMETER MarketplaceUri\n        The URI of the Visual Studio Marketplace.\n        Default value is \"https://marketplace.visualstudio.com/items?itemName=\".\n\n    .EXAMPLE\n        Get-VsixInfoFromMarketplace -Name \"ProBITools.MicrosoftReportProjectsforVisualStudio2022\"\n        Retrieves information about the \"ProBITools.MicrosoftReportProjectsforVisualStudio2022\" extension\n        from the Visual Studio Marketplace.\n    #>\n\n    Param\n    (\n        [Parameter(Mandatory)]\n        [Alias(\"ExtensionMarketPlaceName\")]\n        [string] $Name,\n        [string] $MarketplaceUri = \"https://marketplace.visualstudio.com/items?itemName=\"\n    )\n\n    # Invoke-WebRequest doesn't support retry in PowerShell 5.1\n    $webResponse = Invoke-ScriptBlockWithRetry -RetryCount 20 -RetryIntervalSeconds 30 -Command {\n        Invoke-WebRequest -Uri \"${MarketplaceUri}${Name}\" -UseBasicParsing\n    }\n\n    $webResponse -match 'UniqueIdentifierValue\":\"(?<extensionname>[^\"]*)' | Out-Null\n    $extensionName = $Matches.extensionname\n\n    $webResponse -match 'VsixId\":\"(?<vsixid>[^\"]*)' | Out-Null\n    $vsixId = $Matches.vsixid\n\n    $webResponse -match 'AssetUri\":\"(?<uri>[^\"]*)' | Out-Null\n    $assetUri = $Matches.uri\n\n    $webResponse -match 'Microsoft\\.VisualStudio\\.Services\\.Payload\\.FileName\":\"(?<filename>[^\"]*)' | Out-Null\n    $fileName = $Matches.filename\n\n    switch ($Name) {\n        # ProBITools.MicrosoftReportProjectsforVisualStudio2022 has different URL\n        # https://github.com/actions/runner-images/issues/5340\n        \"ProBITools.MicrosoftReportProjectsforVisualStudio2022\" {\n            $assetUri = \"https://download.microsoft.com/download/1fd275d8-5163-476b-910b-e2f678b3fdbc\"\n            $fileName = \"Microsoft.DataTools.ReportingServices.vsix\"\n        }\n        \"ProBITools.MicrosoftAnalysisServicesModelingProjects2022\" {\n            $assetUri = \"https://download.microsoft.com/download/7c91cb5c-1e9c-4df7-a053-d2852e22c658\"\n            $fileName = \"Microsoft.DataTools.AnalysisServices.vsix\"\n        }\n\n        # Starting from version 4.1 SqlServerIntegrationServicesProjects extension is distributed as exe file\n        \"SSIS.SqlServerIntegrationServicesProjects\" {\n            $fileName = \"Microsoft.DataTools.IntegrationServices.exe\"\n        }\n    }\n\n    $downloadUri = $assetUri + \"/\" + $fileName\n\n    return [PSCustomObject] @{\n        \"ExtensionName\" = $extensionName\n        \"VsixId\"        = $vsixId\n        \"FileName\"      = $fileName\n        \"DownloadUri\"   = $downloadUri\n    }\n}\n\nfunction Install-VSIXFromFile {\n    <#\n    .SYNOPSIS\n        Installs a Visual Studio Extension (VSIX) from a file.\n\n    .DESCRIPTION\n        This function installs a Visual Studio Extension (VSIX)\n        from the specified file path. It uses the VSIXInstaller.exe\n        tool provided by Microsoft Visual Studio.\n\n    .PARAMETER FilePath\n        The path to the VSIX file that needs to be installed.\n\n    .PARAMETER Retries\n        The number of retries to attempt if the installation fails. Default is 20.\n\n    .EXAMPLE\n        Install-VSIXFromFile -FilePath \"C:\\Extensions\\MyExtension.vsix\" -Retries 10\n        Installs the VSIX file located at \"C:\\Extensions\\MyExtension.vsix\" with 10 retries in case of failure.\n    #>\n    Param\n    (\n        [Parameter(Mandatory = $true)]\n        [string] $FilePath,\n        [int] $Retries = 20\n    )\n\n    Write-Host \"Installing VSIX from $FilePath...\"\n    while ($True) {\n        $installerPath = \"${env:ProgramFiles(x86)}\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service\\VSIXInstaller.exe\"\n        try {\n            $process = Start-Process `\n                -FilePath $installerPath `\n                -ArgumentList @('/quiet', \"`\"$FilePath`\"\") `\n                -Wait -PassThru\n        } catch {\n            Write-Host \"Failed to start VSIXInstaller.exe with error:\"\n            $_\n            exit 1\n        }\n\n        $exitCode = $process.ExitCode\n\n        if ($exitCode -eq 0) {\n            Write-Host \"VSIX installed successfully.\"\n            break\n        } elseif ($exitCode -eq 1001) {\n            Write-Host \"VSIX is already installed.\"\n            break\n        }\n\n        Write-Host \"VSIX installation failed with exit code $exitCode.\"\n\n        $Retries--\n        if ($Retries -eq 0) {\n            Write-Host \"VSIX installation failed after $Retries retries.\"\n            exit 1\n        }\n\n        Write-Host \"Waiting 10 seconds before retrying. Retries left: $Retries\"\n        Start-Sleep -Seconds 10\n    }\n}\n\nfunction Install-VSIXFromUrl {\n    <#\n    .SYNOPSIS\n        Installs a Visual Studio extension (VSIX) from a given URL.\n\n    .DESCRIPTION\n        This function downloads a Visual Studio extension (VSIX)\n        from the specified URL and installs it.\n\n    .PARAMETER Url\n        The URL of the VSIX file to download and install.\n\n    .PARAMETER Retries\n        The number of retries to attempt if the download fails. Default is 20.\n\n    .EXAMPLE\n        Install-VSIXFromUrl -Url \"https://example.com/extension.vsix\" -Retries 10\n        Downloads and installs the VSIX file from the specified URL with 10 retries.\n    #>\n\n    Param\n    (\n        [Parameter(Mandatory = $true)]\n        [string] $Url,\n        [int] $Retries = 20\n    )\n\n    $filePath = Invoke-DownloadWithRetry $Url\n    Install-VSIXFromFile -FilePath $filePath -Retries $Retries\n    Remove-Item -Force -Confirm:$false $filePath\n}\n\nfunction Get-VSExtensionVersion {\n    <#\n    .SYNOPSIS\n        Retrieves the version of a Visual Studio extension package.\n\n    .DESCRIPTION\n        This function retrieves the version of a specified Visual Studio extension package.\n        It searches for the package in the installed instances of Visual Studio and\n        returns the version number.\n\n    .PARAMETER packageName\n        The name of the extension package.\n\n    .EXAMPLE\n        Get-VSExtensionVersion -packageName \"MyExtensionPackage\"\n        Retrieves the version of the extension package named \"MyExtensionPackage\" for Visual Studio.\n    #>\n    Param\n    (\n        [Parameter(Mandatory = $true)]\n        [string] $packageName\n    )\n\n    $instanceFolders = Get-ChildItem -Path \"C:\\ProgramData\\Microsoft\\VisualStudio\\Packages\\_Instances\"\n    if ($instanceFolders -is [array]) {\n        Write-Host ($instanceFolders | Out-String)\n        Write-Host ($instanceFolders | Get-ChildItem | Out-String)\n        Write-Host \"More than one instance installed\"\n        exit 1\n    }\n\n    $stateContent = Get-Content -Path (Join-Path $instanceFolders.FullName '\\state.packages.json')\n    $state = $stateContent | ConvertFrom-Json\n    $packageVersion = ($state.packages | Where-Object { $_.id -eq $packageName }).version\n\n    if (-not $packageVersion) {\n        Write-Host \"Installed package $packageName for Visual Studio was not found\"\n        exit 1\n    }\n\n    return $packageVersion\n}\n"
  },
  {
    "path": "images/windows/scripts/helpers/test/ImageHelpers.Tests.ps1",
    "content": "$ModuleManifestName = 'ImageHelpers.psd1'\n$ModuleManifestPath = \"$PSScriptRoot\\..\\$ModuleManifestName\"\n\n\n\nDescribe 'Module Manifest Tests' {\n    It 'Passes Test-ModuleManifest' {\n        Test-ModuleManifest -Path $ModuleManifestPath | Should Not BeNullOrEmpty\n        $? | Should Be $true\n    }\n}\n\n\n"
  },
  {
    "path": "images/windows/scripts/tests/ActionArchiveCache.Tests.ps1",
    "content": "Describe \"ActionArchiveCache\" {\n    Context \"Action archive cache directory not empty\" {\n        It \"C:\\actionarchivecache not empty\" {\n            (Get-ChildItem -Path \"C:\\actionarchivecache\\*.zip\" -Recurse).Count | Should -BeGreaterThan 0\n        }\n    }\n\n    Context \"Action zipball not empty\" {\n        $testCases = Get-ChildItem -Path \"C:\\actionarchivecache\\*.zip\" -Recurse | ForEach-Object { @{ ActionZipball = $_.FullName } }\n        It \"<ActionZipball>\" -TestCases $testCases {\n            param ([string] $ActionZipball)\n            (Get-Item \"$ActionZipball\").Length | Should -BeGreaterThan 0\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Android.Tests.ps1",
    "content": "Describe \"Android SDK\" {\n    $androidToolset = (Get-ToolsetContent).android\n    $androidInstalledPackages = Get-AndroidInstalledPackages\n\n    $platformList = Get-AndroidPlatformPackages -minVersion $androidToolset.platform_min_version\n    $platformTestCases = $platformList | ForEach-Object {\n        @{ platformVersion = $_; installedPackages = $androidInstalledPackages }\n    }\n\n    $buildToolsList = Get-AndroidBuildToolPackages -minVersion $androidToolset.build_tools_min_version\n    $buildToolsTestCases = $buildToolsList | ForEach-Object {\n        @{ buildToolsVersion = $_; installedPackages = $androidInstalledPackages }\n    }\n\n    $extraPackagesTestCases = $androidToolset.extra_list | ForEach-Object {\n        @{ extraPackage = $_; installedPackages = $androidInstalledPackages }\n    }\n\n    $addonsTestCases = $androidToolset.addon_list | ForEach-Object {\n        @{ addonPackage = $_; installedPackages = $androidInstalledPackages }\n    }\n\n    $additionalToolsTestCases = $androidToolset.additional_tools | ForEach-Object {\n        @{ additionalToolVersion = $_; installedPackages = $androidInstalledPackages }\n    }\n\n    $ndkPackagesTestCases = $androidToolset.ndk.versions | ForEach-Object {\n        @{ ndkPackage = $_; installedPackages = $androidInstalledPackages }\n    }\n\n    Context \"SDKManagers\" {\n        $testCases = @(\n            @{\n                PackageName = \"Command-line tools\"\n                Sdkmanager = \"$env:ANDROID_HOME\\cmdline-tools\\latest\\bin\\sdkmanager.bat\"\n            }\n        )\n\n        It \"Sdkmanager from <PackageName> is available\" -TestCases $testCases {\n            \"$Sdkmanager --list\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    Context \"Packages\" {\n        It \"Platform version <platformVersion> is installed\" -TestCases $platformTestCases {\n            \"$installedPackages\" | Should -Match \"$platformVersion\"\n        }\n\n        It \"Platform build tools <buildToolsVersion> is installed\" -TestCases $buildToolsTestCases {\n            \"$installedPackages\" | Should -Match \"$buildToolsVersion\"\n        }\n\n        It \"Additional tool <additionalToolVersion> is installed\" -TestCases $additionalToolsTestCases {\n            \"$installedPackages\" | Should -Match $additionalToolVersion\n        }\n\n        It \"NDK <ndkPackage> is installed\" -TestCases $ndkPackagesTestCases {\n            \"$installedPackages\" | Should -Match \"ndk;$ndkPackage\"\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Apache.Tests.ps1",
    "content": "Describe \"Apache\" {\n    Context \"Path\" {\n        It \"Apache\" {\n            $apachePath = Join-Path (Join-Path \"C:\\tools\\\" (Get-Item C:\\tools\\apache*).Name) \"\\bin\\httpd\"\n            \"$apachePath -V\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    Context \"Service\" {\n        $apacheService = Get-Service -Name Apache\n        $apacheServiceTests = @{\n            Name = $apacheService.Name\n            Status = $apacheService.Status\n            StartType = $apacheService.StartType\n        }\n\n        It \"<Name> service is stopped\" -TestCases $apacheServiceTests {\n            $Status | Should -Be \"Stopped\"\n        }\n\n        It \"<Name> service is disabled\" -TestCases $apacheServiceTests {\n            $StartType | Should -Be \"Disabled\"\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Browsers.Tests.ps1",
    "content": "Describe \"Chrome\" {\n    Context \"WebDriver\" {\n        It \"ChromeWebDriver environment variable and path exists\" {\n            $env:ChromeWebDriver | Should -Not -BeNullOrEmpty\n            $env:ChromeWebDriver | Should -BeExactly \"C:\\SeleniumWebDrivers\\ChromeDriver\"\n            $env:ChromeWebDriver | Should -Exist\n        }\n\n        It \"chromedriver.exe is installed\" {\n            \"$env:ChromeWebDriver\\chromedriver.exe --version\" | Should -ReturnZeroExitCode\n        }\n\n        It \"versioninfo.txt exists\" {\n            \"$env:ChromeWebDriver\\versioninfo.txt\" | Should -Exist\n        }\n    }\n\n    Context \"Browser\" {\n        $chromeRegPath = \"HKLM:SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe\"\n        $chromePath = (Get-ItemProperty $chromeRegPath).'(default)'\n\n        It \"Chrome '<chromeRegPath>' registry path exists\" -TestCases @{chromeRegPath = $chromeRegPath} {\n            $chromeRegPath | Should -Exist\n        }\n\n        It \"Chrome VersionInfo registry value exists\" -TestCases @{chromePath = $chromePath} {\n            $versionInfo = (Get-Item $chromePath).VersionInfo\n            $versionInfo | Should -Not -BeNullOrEmpty\n        }\n\n        It \"GoogleUpdater services is stopped\" {\n            $services = Get-Service -Name \"GoogleUpdater*\"\n            foreach ($svc in $services) {\n                $svc.Status | Should -BeExactly 'Stopped'\n            }\n        }\n\n        It \"BlockGoogleUpdate firewall rule exists\" {\n            Get-NetFirewallRule -DisplayName BlockGoogleUpdate | Should -Not -BeNullOrEmpty\n        }\n\n        It \"<chromePath> is installed\" -TestCases @{chromePath = $chromePath} {\n            $chromeName = (Get-Item $chromePath).Name\n            $chromePath | Should -Exist\n            $chromeName | Should -BeExactly \"chrome.exe\"\n        }\n\n        It \"Chrome and Chrome Driver major versions are the same\" -TestCases @{chromePath = $chromePath} {\n            $chromeMajor = (Get-Item $chromePath).VersionInfo.ProductMajorPart\n            $chromeDriverMajor = (Get-Content $env:ChromeWebDriver\\versioninfo.txt).Split(\".\")[0]\n            $chromeMajor | Should -BeExactly $chromeDriverMajor\n        }\n    }\n}\n\nDescribe \"Edge\" {\n    Context \"WebDriver\" {\n        It \"EdgeWebDriver environment variable and path exists\" {\n            $env:EdgeWebDriver | Should -Not -BeNullOrEmpty\n            $env:EdgeWebDriver | Should -BeExactly \"C:\\SeleniumWebDrivers\\EdgeDriver\"\n            $env:EdgeWebDriver | Should -Exist\n        }\n\n        It \"msedgedriver.exe is installed\" {\n            \"$env:EdgeWebDriver\\msedgedriver.exe --version\" | Should -ReturnZeroExitCode\n        }\n\n        It \"versioninfo.txt exists\" {\n            \"$env:EdgeWebDriver\\versioninfo.txt\" | Should -Exist\n        }\n    }\n\n    Context \"Browser\" {\n        $edgeRegPath = \"HKLM:SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\msedge.exe\"\n\n        It \"Edge '<edgeRegPath>' registry path exists\" -TestCases @{edgeRegPath = $edgeRegPath} {\n            $edgeRegPath | Should -Exist\n        }\n\n        It \"Edge VersionInfo registry value exists\" -TestCases @{edgeRegPath = $edgeRegPath} {\n            $versionInfo = (Get-Item (Get-ItemProperty $edgeRegPath).'(Default)').VersionInfo\n            $versionInfo | Should -Not -BeNullOrEmpty\n        }\n\n        It \"msedge.exe is installed\" {\n            \"${env:ProgramFiles(x86)}\\Microsoft\\Edge\\Application\\msedge.exe\" | Should -Exist\n        }\n    }\n}\n\nDescribe \"Firefox\" {\n    Context \"WebDriver\" {\n        It \"GeckoWebDriver environment variable and path exists\" {\n            $env:GeckoWebDriver | Should -Not -BeNullOrEmpty\n            $env:GeckoWebDriver | Should -BeExactly \"C:\\SeleniumWebDrivers\\GeckoDriver\"\n            $env:GeckoWebDriver | Should -Exist\n        }\n\n        It \"geckodriver.exe is installed\" {\n            \"$env:GeckoWebDriver\\geckodriver.exe --version\" | Should -ReturnZeroExitCode\n        }\n\n        It \"versioninfo.txt exists\" {\n            \"$env:GeckoWebDriver\\versioninfo.txt\" | Should -Exist\n        }\n    }\n\n    Context \"Browser\" {\n        $firefoxRegPath = \"HKLM:SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\firefox.exe\"\n\n        It \"Firefox '<firefoxRegPath>' registry path exists\" -TestCases @{firefoxRegPath = $firefoxRegPath} {\n            $firefoxRegPath | Should -Exist\n        }\n\n        It \"Firefox VersionInfo registry value exists\" -TestCases @{firefoxRegPath = $firefoxRegPath} {\n            $versionInfo = (Get-Item (Get-ItemProperty $firefoxRegPath).'(Default)').VersionInfo\n            $versionInfo | Should -Not -BeNullOrEmpty\n        }\n\n        It \"firefox.exe is installed\" {\n            \"$env:ProgramFiles\\Mozilla Firefox\\firefox.exe\" | Should -Exist\n        }\n    }\n}\n\nDescribe \"Internet Explorer\" {\n    Context \"WebDriver\" {\n        It \"IEWebDriver environment variable and path exists\" {\n            $env:IEWebDriver | Should -Not -BeNullOrEmpty\n            $env:IEWebDriver | Should -BeExactly \"C:\\SeleniumWebDrivers\\IEDriver\"\n            $env:IEWebDriver | Should -Exist\n        }\n\n        It \"iedriverserver.exe is installed\" {\n            \"$env:IEWebDriver\\IEDriverServer.exe --version\" | Should -ReturnZeroExitCode\n        }\n\n        It \"versioninfo.txt exists\" {\n            \"$env:IEWebDriver\\versioninfo.txt\" | Should -Exist\n        }\n    }\n}\n\nDescribe \"Selenium\" {\n    BeforeAll {\n        $seleniumBinPath = \"C:\\selenium\\selenium-server.jar\"\n    }\n\n    It \"Selenium server is installed\" {\n        $seleniumBinPath | Should -Exist\n    }\n\n    It \"SELENIUM_JAR_PATH environment variable exists\" {\n        Get-EnvironmentVariable \"SELENIUM_JAR_PATH\" | Should -BeExactly \"$seleniumBinPath\"\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/CLI.Tools.Tests.ps1",
    "content": "\nDescribe \"Azure CLI\" {\n    It \"Azure CLI\" {\n        \"az --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Azure DevOps CLI\" {\n    It \"az devops\" {\n        \"az devops -h\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Aliyun CLI\" -Skip:(Test-IsWin25) {\n    It \"Aliyun CLI\" {\n        \"aliyun version\" | Should -ReturnZeroExitCode\n    }\n}\n\n\nDescribe \"AWS\" {\n    It \"AWS CLI\" {\n        \"aws --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Session Manager Plugin for the AWS CLI\" {\n        @(session-manager-plugin) -Match '\\S' | Out-String | Should -Match \"plugin was installed successfully\"\n    }\n\n    It \"AWS SAM CLI\" {\n        \"sam --version\" | Should -ReturnZeroExitCode\n    }\n}\n\n\nDescribe \"GitHub CLI\" {\n    It \"gh\" {\n        \"gh --version\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/ChocoPackages.Tests.ps1",
    "content": "Describe \"7-Zip\" {\n    It \"7z\" {\n        \"7z\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Aria2\" {\n    It \"Aria2\" {\n        \"aria2c --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"AzCopy\" {\n    It \"AzCopy\" {\n        \"azcopy --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Bicep\" {\n    It \"Bicep\" {\n        \"bicep --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"InnoSetup\" {\n    It \"InnoSetup\" {\n        (Get-Command -Name iscc).CommandType | Should -BeExactly \"Application\"\n    }\n}\n\nDescribe \"Jq\" {\n    It \"Jq\" {\n        \"jq -n .\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Nuget\" {\n    It \"Nuget\" {\n       \"nuget\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Packer\" {\n    It \"Packer\" {\n       \"packer --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Perl\" {\n    It \"Perl\" {\n       \"perl --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Pulumi\" {\n    It \"pulumi\" {\n       \"pulumi version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Svn\" -Skip:(Test-IsWin25) {\n    It \"svn\" {\n        \"svn --version --quiet\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Swig\" {\n    It \"Swig\" {\n        \"swig -version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"VSWhere\" {\n    It \"vswhere\" {\n        \"vswhere\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Julia\" {\n    It \"Julia path exists\" {\n        \"C:\\Julia\" | Should -Exist\n    }\n\n    It \"Julia\" {\n        \"julia --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"CMake\" {\n    It \"cmake\" {\n        \"cmake --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"ImageMagick\" {\n    It \"ImageMagick\" {\n        \"magick -version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Ninja\" {\n    BeforeAll {\n        $ninjaProjectPath = $(Join-Path $env:TEMP_DIR \"ninjaproject\")\n        New-item -Path $ninjaProjectPath -ItemType Directory -Force\n@'\ncmake_minimum_required(VERSION 3.10)\nproject(NinjaTest NONE)\n'@ | Out-File -FilePath \"$ninjaProjectPath/CMakeLists.txt\" -Encoding utf8\n\n        $ninjaProjectBuildPath = $(Join-Path $ninjaProjectPath \"build\")\n        New-item -Path $ninjaProjectBuildPath -ItemType Directory -Force\n        Set-Location $ninjaProjectBuildPath\n    }\n\n    It \"Make a simple ninja project\" {\n    \"cmake -GNinja $ninjaProjectPath\" | Should -ReturnZeroExitCode\n    }\n\n    It \"build.ninja file should exist\" {\n        $buildFilePath = $(Join-Path $ninjaProjectBuildPath \"build.ninja\")\n        $buildFilePath | Should -Exist\n    }\n\n    It \"Ninja\" {\n        \"ninja --version\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Databases.Tests.ps1",
    "content": "Describe \"MongoDB\" {\n    Context \"Version\" {\n        It \"<ToolName>\" -TestCases @(\n            @{ ToolName = \"mongos\" }\n            @{ ToolName = \"mongod\" }\n        ) {\n            $toolsetVersion = (Get-ToolsetContent).mongodb.version\n            (& $ToolName --version)[2].Split('\"')[-2] | Should -BeLike \"$toolsetVersion*\"\n        }\n    }\n\n    Context \"Service\" {\n        $mongoService = Get-Service -Name mongodb -ErrorAction Ignore\n        $mongoServiceTests = @{\n            Name      = $mongoService.Name\n            Status    = $mongoService.Status\n            StartType = $mongoService.StartType\n        }\n\n        It \"<Name> service is stopped\" -TestCases $mongoServiceTests {\n            $Status | Should -Be \"Stopped\"\n        }\n\n        It \"<Name> service is disabled\" -TestCases $mongoServiceTests {\n            $StartType | Should -Be \"Disabled\"\n        }\n    }\n\n    Context \"Shell\" {\n        It \"mongosh\" {\n            \"mongosh --version\" | Should -ReturnZeroExitCode\n        }\n    }\n}\n\nDescribe \"PostgreSQL\" {\n    $psqlTests = @(\n        @{envVar = \"PGROOT\"; pgPath = Get-EnvironmentVariable \"PGROOT\" }\n        @{envVar = \"PGBIN\"; pgPath = Get-EnvironmentVariable \"PGBIN\" }\n        @{envVar = \"PGDATA\"; pgPath = Get-EnvironmentVariable \"PGDATA\" }\n    )\n\n    Context \"Environment variable\" {\n        It \"PGUSER contains postgres\" {\n            Get-EnvironmentVariable \"PGUSER\" | Should -Be \"postgres\"\n        }\n\n        It \"PGPASSWORD contains root\" {\n            Get-EnvironmentVariable \"PGPASSWORD\" | Should -Be \"root\"\n        }\n\n        It \"<envVar> environment variable exists\" -TestCases $psqlTests {\n            Get-EnvironmentVariable $envVar | Should -Not -BeNullOrEmpty\n        }\n    }\n\n    Context \"Path\" {\n        It \"<pgPath> path exists\" -TestCases $psqlTests {\n            $pgPath | Should -Exist\n        }\n    }\n\n    Context \"Service\" {\n        $psqlService = Get-Service -Name postgresql*\n        $psqlServiceTests = @{\n            Name      = $psqlService.Name\n            Status    = $psqlService.Status\n            StartType = $psqlService.StartType\n        }\n\n        It \"<Name> service is stopped\" -TestCases $psqlServiceTests {\n            $Status | Should -Be \"Stopped\"\n        }\n\n        It \"<Name> service is disabled\" -TestCases $psqlServiceTests {\n            $StartType | Should -Be \"Disabled\"\n        }\n    }\n\n    Context \"PostgreSQL version\" {\n        It \"PostgreSQL version should correspond to the Major version in the toolset\" {\n            $toolsetVersion = (Get-ToolsetContent).postgresql.version.Split(\".\")[0]\n            # Client version\n            (& $env:PGBIN\\psql --version).split()[-1] | Should -BeLike \"$toolsetVersion*\"\n            # Server version\n            (& $env:PGBIN\\pg_config --version).split()[-1] | Should -BeLike \"$toolsetVersion*\"\n        }\n    }\n}\n\nDescribe \"MySQL\" {\n    It \"MySQL CLI\" {\n        $MysqlVersion = (Get-ToolsetContent).mysql.version\n        mysql -V | Should -BeLike \"*${MysqlVersion}*\"\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Docker.Tests.ps1",
    "content": "Describe \"Docker\" {\n    It \"docker is installed\" {\n        \"docker --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"docker service is up\" {\n        \"docker images\" | Should -ReturnZeroExitCode\n    }\n\n    It \"docker symlink\" {\n        \"C:\\Windows\\SysWOW64\\docker.exe ps\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"DockerCompose\" {\n    It \"docker compose v2\" {\n        \"docker compose version\" | Should -ReturnZeroExitCode\n    }\n\n}\n\nDescribe \"DockerWinCred\" {\n    It \"docker-wincred\" {\n        \"docker-credential-wincred version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"DockerImages\" -Skip:(Test-IsWin25) {\n    Context \"docker images\" {\n        $testCases = (Get-ToolsetContent).docker.images | ForEach-Object { @{ ImageName = $_ } }\n\n        It \"<ImageName>\" -TestCases $testCases {\n            docker images \"$ImageName\" --format \"{{.Repository}}\" | Should -Not -BeNullOrEmpty\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/DotnetSDK.Tests.ps1",
    "content": "$dotnetVersions = (Get-ToolsetContent).dotnet.versions\n$dotnetTools = (Get-ToolsetContent).dotnet.tools\n\nDescribe \"Dotnet SDK and tools\" {\n\n    Context \"Default\" {\n        It \"Default Dotnet SDK is available\" {\n            \"dotnet --version\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    foreach ($version in $dotnetVersions) {\n        Context \"Dotnet $version\" {\n            $dotnet = @{ dotnetVersion = $version }\n\n            It \"SDK $version is available\" -TestCases $dotnet {\n                (dotnet --list-sdks | Where-Object { $_ -match \"${dotnetVersion}\\.[0-9]*\" }).Count | Should -BeGreaterThan 0\n            }\n\n            It \"Runtime $version is available\" -TestCases $dotnet {\n                (dotnet --list-runtimes | Where-Object { $_ -match \"${dotnetVersion}\\.[0-9]*\" }).Count | Should -BeGreaterThan 0\n            }\n        }\n    }\n\n    Context \"Dotnet tools\" {\n        $env:Path += \";C:\\Users\\Default\\.dotnet\\tools\"\n        $testCases = $dotnetTools | ForEach-Object { @{ ToolName = $_.name; TestInstance = $_.test }}\n\n        It \"<ToolName> is available\" -TestCases $testCases {\n            \"$TestInstance\" | Should -ReturnZeroExitCode\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Git.Tests.ps1",
    "content": "Describe \"Git\" {\n    $gitTools = 'bash', 'awk', 'git', 'git-lfs'\n    $gitTestCases = $gitTools | ForEach-Object {\n        @{\n            toolName = $_\n            source = [regex]::Escape(\"$env:ProgramFiles\\Git\")\n        }\n    }\n\n    It \"<toolName> is installed\" -TestCases $gitTestCases {\n        \"$toolName --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"<toolName> is located in '<source>'\" -TestCases $gitTestCases {\n        (Get-Command -Name $toolName).Source | Should -Match $source\n    }\n\n    It \"Git core.symlinks=true option is enabled\" {\n        git config core.symlinks | Should -BeExactly true\n    }\n\n    It \"GCM_INTERACTIVE environment variable should be equal Never\" {\n        $env:GCM_INTERACTIVE | Should -BeExactly Never\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Haskell.Tests.ps1",
    "content": "Describe \"Haskell\" {\n    $ghcPackagesPath = \"c:\\ghcup\\ghc\"\n    [array] $ghcVersionList = Get-ChildItem -Path $ghcPackagesPath -Filter \"*\" | ForEach-Object { $_.Name.Trim() }\n    $ghcCount = $ghcVersionList.Count\n    $defaultGhcVersion = $ghcVersionList | Sort-Object {[Version] $_} | Select-Object -Last 1\n    $ghcDefaultCases = @{\n        defaultGhcVersion = $defaultGhcVersion\n        defaultGhcShortVersion = ([version] $defaultGhcVersion).ToString(3)\n    }\n\n    $ghcTestCases = $ghcVersionList | ForEach-Object {\n        $ghcVersion = $_\n        $ghcShortVersion = ([version] $ghcVersion).ToString(3)\n        $binGhcPath = Join-Path $ghcPackagesPath \"$ghcShortVersion\\bin\\ghc.exe\"\n        @{\n            ghcVersion = $ghcVersion\n            ghcShortVersion = $ghcShortVersion\n            binGhcPath = $binGhcPath\n        }\n    }\n\n    $ghcupEnvExists = @(\n        @{envVar = \"GHCUP_INSTALL_BASE_PREFIX\"}\n        @{envVar = \"GHCUP_MSYS2\"}\n    )\n\n    If (Test-IsWin25) {\n        $numberOfVersions = 1\n    } else {\n        $numberOfVersions = 3\n    }\n\n    It \"<envVar> environment variable exists\" -TestCases $ghcupEnvExists {\n        Test-Path env:\\$envVar\n    }\n\n    It \"Accurate $numberOfVersions versions of GHC are installed\" -TestCases @{ghcCount = $ghcCount; numberOfVersions = $numberOfVersions} {\n        $ghcCount | Should -BeExactly $numberOfVersions\n    }\n\n    It \"GHC <ghcVersion> is installed\" -TestCases $ghcTestCases {\n        \"$binGhcPath --version\" | Should -OutputTextMatchingRegex $ghcShortVersion\n    }\n\n    It \"GHC <defaultGhcVersion> is the default version and should be the latest installed\" -TestCases $ghcDefaultCases {\n        \"ghc --version\" | Should -OutputTextMatchingRegex $defaultGhcShortVersion\n    }\n\n    It \"Cabal is installed\" {\n        \"cabal --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"cabal folder does not exist\" {\n        $env:CABAL_DIR | Should -Not -Exist\n    }\n\n    It \"CABAL_DIR environment variable exists\" {\n        Get-EnvironmentVariable CABAL_DIR | Should -BeExactly \"C:\\cabal\"\n    }\n\n    It \"ghcup is installed\" {\n        \"ghcup --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"ghcup can access msys2\" {\n        \"ghcup run --mingw-path -- pacman --version\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Helpers.psm1",
    "content": "[CmdletBinding()]\nparam()\n\n# Gets value of environment variable by the name\nfunction Get-EnvironmentVariable($variable) {\n    return [System.Environment]::GetEnvironmentVariable($variable, \"Machine\")\n}\n\nfunction Invoke-PesterTests {\n    <#\n    .SYNOPSIS\n        Runs Pester tests based on the provided test file and test name.\n\n    .DESCRIPTION\n        The Invoke-PesterTests function runs Pester tests based on the provided test file and test name.\n        It supports filtering tests by name and generating test result output.\n\n    .PARAMETER TestFile\n        The name of the test file to run. This should be the base name of the test file without the extension.\n\n    .PARAMETER TestName\n        The name of the specific test to run. If provided, only the test with the matching name will be executed.\n\n    .EXAMPLE\n        Invoke-PesterTests -TestFile \"MyTests\" -TestName \"Test1\"\n        Runs the test named \"Test1\" from the test file \"MyTests.Tests.ps1\".\n\n    .EXAMPLE\n        Invoke-PesterTests -TestFile \"*\" -TestName \"Test2\"\n        Runs all tests from all test files and generates the test result output.\n\n    .NOTES\n        This function requires the Pester module to be installed.\n\n    #>\n\n    Param(\n        [Parameter(Mandatory)][string] $TestFile,\n        [string] $TestName\n    )\n\n    $testPath = \"C:\\image\\tests\\${TestFile}.Tests.ps1\"\n    if (-not (Test-Path $testPath)) {\n        throw \"Unable to find test file '$TestFile' on '$testPath'.\"\n    }\n\n    $configuration = [PesterConfiguration] @{\n        Run    = @{ Path = $testPath; PassThru = $true }\n        Output = @{ Verbosity = \"Detailed\"; RenderMode = \"Plaintext\" }\n    }\n    if ($TestName) {\n        $configuration.Filter.FullName = $TestName\n    }\n    if ($TestFile -eq \"*\") {\n        $configuration.TestResult.Enabled = $true\n        $configuration.TestResult.OutputPath = \"C:\\image\\tests\\testResults.xml\"\n    }\n\n    # Update environment variables without reboot\n    Update-Environment\n\n    # Switch ErrorActionPreference to Stop temporary to make sure that tests will on silent errors too\n    $backupErrorActionPreference = $ErrorActionPreference\n    $ErrorActionPreference = \"Stop\"\n    $results = Invoke-Pester -Configuration $configuration\n    $ErrorActionPreference = $backupErrorActionPreference\n\n    # Fail in case if no tests are run\n    if (-not ($results -and ($results.FailedCount -eq 0) -and ($results.PassedCount -gt 0))) {\n        $results\n        throw \"Test run has failed\"\n    }\n}\n\nfunction ShouldReturnZeroExitCode {\n    <#\n    .SYNOPSIS\n        Implements a custom Should-operator for the Pester framework.\n\n    .DESCRIPTION\n        This function is used to check if a command has returned a zero exit code.\n        It can be used by registering it using the Add-ShouldOperator function in Pester.\n\n    .PARAMETER ActualValue\n        The actual value to be checked.\n\n    .PARAMETER Negate\n        A switch parameter that, when specified, negates the result of the check.\n\n    .PARAMETER Because\n        An optional string that provides additional context or explanation for the check.\n\n    .NOTES\n        This function is designed to be used with the Pester framework.\n\n    .LINK\n        https://pester.dev/docs/assertions/custom-assertions\n    #>\n\n    Param(\n        [string] $ActualValue,\n        [switch] $Negate,\n        [string] $Because\n    )\n\n    $outputLines = (& $env:comspec /c \"$ActualValue 2>&1\") -as [string[]]\n    $exitCode = $LASTEXITCODE\n\n    [bool] $succeeded = $exitCode -eq 0\n    if ($Negate) { $succeeded = -not $succeeded }\n\n    if (-not $succeeded) {\n        $commandOutputIndent = \" \" * 4\n        $commandOutput = ($outputLines | ForEach-Object { \"${commandOutputIndent}${_}\" }) -join \"`n\"\n        $failureMessage = \"Command '${ActualValue}' has finished with exit code ${exitCode} and output:`n${commandOutput}\"\n    }\n\n    return [PSCustomObject] @{\n        Succeeded      = $succeeded\n        FailureMessage = $failureMessage\n    }\n}\n\nfunction ShouldOutputTextMatchingRegex {\n    <#\n    .SYNOPSIS\n        Implements a custom Should-operator for the Pester framework.\n\n    .DESCRIPTION\n        This function is used to check if a command outputs text that matches a regular expression.\n        It can be used by registering it using the Add-ShouldOperator function in Pester.\n\n    .PARAMETER ActualValue\n        The actual value to be checked.\n\n    .PARAMETER Negate\n        A switch parameter that, when specified, negates the result of the check.\n\n    .PARAMETER Because\n        An optional string that provides additional context or explanation for the check.\n\n    .NOTES\n        This function is designed to be used with the Pester framework.\n\n    .LINK\n        https://pester.dev/docs/assertions/custom-assertions\n    #>\n\n    Param(\n        [String] $ActualValue,\n        [String] $RegularExpression,\n        [switch] $Negate\n    )\n\n    [string] $output = (& $env:comspec /c \"$ActualValue 2>&1\")\n    [bool] $succeeded = $output -cmatch $RegularExpression\n\n    if ($Negate) { $succeeded = -not $succeeded }\n\n    $failureMessage = ''\n\n    if (-not $succeeded) {\n        if ($Negate) {\n            $failureMessage = \"Expected regular expression '$RegularExpression' for '$ActualValue' command to not match '$output', but it did match.\"\n        } else {\n            $failureMessage = \"Expected regular expression '$RegularExpression' for '$ActualValue' command to match '$output', but it did not match.\"\n        }\n    }\n\n    return [PSCustomObject] @{\n        Succeeded      = $succeeded\n        FailureMessage = $failureMessage\n    }\n}\n\nIf (Get-Command -Name Add-ShouldOperator -ErrorAction SilentlyContinue) {\n    Add-ShouldOperator -Name ReturnZeroExitCode -InternalName ShouldReturnZeroExitCode -Test ${function:ShouldReturnZeroExitCode}\n    Add-ShouldOperator -Name OutputTextMatchingRegex -InternalName ShouldOutputTextMatchingRegex -Test ${function:ShouldOutputTextMatchingRegex}\n}\n\nfunction Get-ModuleVersionAsJob {\n    Param (\n        [Parameter(Mandatory)]\n        [String] $modulePath,\n        [Parameter(Mandatory)]\n        [String] $moduleName\n    )\n    # Script block to run commands in separate PowerShell environment\n    $testJob = Start-Job -ScriptBlock {\n        param (\n            $modulePath,\n            $moduleName\n        )\n        # No need to suppress AzureRM warnings now, only Az module is considered\n        $env:PsModulePath = \"$modulePath;$env:PsModulePath\"\n        Import-Module -Name $moduleName\n        (Get-Module -Name $moduleName).Version.ToString()\n\n    } -ArgumentList $modulePath, $moduleName\n\n    $testJob | Wait-Job | Receive-Job | Out-File -FilePath \"${env:TEMP}\\module-version.txt\"\n    Remove-Job $testJob\n}\n\nExport-ModuleMember -Function @(\n    'Get-EnvironmentVariable'\n    'Invoke-PesterTests'\n    'Get-ModuleVersionAsJob'\n)\n"
  },
  {
    "path": "images/windows/scripts/tests/Java.Tests.ps1",
    "content": "Describe \"Java\" {\n    $toolsetJava = (Get-ToolsetContent).java\n    $defaultVersion = $toolsetJava.default\n    $jdkVersions = $toolsetJava.versions\n\n    [array] $testCases = $jdkVersions | ForEach-Object { @{Version = $_ } }\n\n    It \"Java <DefaultJavaVersion> is default\" -TestCases @(@{ DefaultJavaVersion = $defaultVersion }) {\n        $actualJavaPath = Get-EnvironmentVariable \"JAVA_HOME\"\n        $expectedJavaPath = Get-EnvironmentVariable \"JAVA_HOME_${DefaultJavaVersion}_X64\"\n\n        $actualJavaPath | Should -Not -BeNullOrEmpty\n        $expectedJavaPath | Should -Not -BeNullOrEmpty\n        $actualJavaPath | Should -Be $expectedJavaPath\n    }\n\n    It \"<ToolName>\" -TestCases @(\n        @{ ToolName = \"java\" }\n        @{ ToolName = \"mvn\" }\n        @{ ToolName = \"ant\" }\n        @{ ToolName = \"gradle\" }\n    ) {\n        \"$ToolName -version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Java <Version>\" -TestCases $testCases {\n        $javaVariableValue = Get-EnvironmentVariable \"JAVA_HOME_${Version}_X64\"\n        $javaVariableValue | Should -Not -BeNullOrEmpty\n        $javaPath = Join-Path $javaVariableValue \"bin\\java\"\n\n        if ($Version -eq 8) {\n            $Version = \"1.${Version}\"\n        }\n        $outputPattern = \"openjdk version `\"${Version}\"\n\n        $outputLines = (& $env:comspec /c \"`\"$javaPath`\" -version 2>&1\") -as [string[]]\n        $LASTEXITCODE | Should -Be 0\n        $outputLines[0] | Should -Match $outputPattern\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/LLVM.Tests.ps1",
    "content": "Describe \"Clang/LLVM\" {\n    BeforeAll {\n        $toolsetVersion = (Get-ToolsetContent).llvm.version\n    }\n\n    It \"Clang/LLVM <toolsetVersion> installed and version is correct\" {\n        $clangVersion = clang --version\n        $clangVersion[0] | Should -BeLike \"*${toolsetVersion}*\"\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/MSYS2.Tests.ps1",
    "content": "BeforeAll {\n    $msys2Dir = \"C:\\msys64\\usr\\bin\"\n    $originalPath = $env:PATH\n}\n\nDescribe \"MSYS2 packages\" {\n    BeforeEach {\n        $env:PATH = \"$msys2Dir;$env:PATH\"\n    }\n\n    It \"msys2Dir exists\" {\n        $msys2Dir | Should -Exist\n    }\n\n    $TestCases = @(\n        @{ ToolName = \"bash.exe\" }\n    )\n\n    It \"<ToolName> is installed in <msys2Dir>\" -TestCases $TestCases {\n        (Get-Command \"$ToolName\").Source | Should -BeLike \"$msys2Dir*\"\n    }\n\n    It \"<ToolName> is avaialable\" -TestCases $TestCases {\n        \"$ToolName --version\" | Should -ReturnZeroExitCode\n    }\n\n    AfterEach {\n        $env:PATH = $originalPath\n    }\n}\n\n$mingwTypes = (Get-ToolsetContent).MsysPackages.mingw\nforeach ($mingwType in $mingwTypes) {\n    Describe \"$($mingwType.arch) packages\" {\n        $tools = $mingwType.runtime_packages\n        $execDir = Join-Path \"C:\\msys64\" $mingwType.exec_dir | Join-Path -ChildPath \"bin\"\n        \n        foreach ($tool in $tools) {\n            Context \"$($tool.name) package\" {\n                $executables = $tool.executables | ForEach-Object {\n                    @{\n                        ExecName = $_\n                        ExecDir  = $execDir\n                    }\n                }\n\n                BeforeEach {\n                    $env:PATH = \"$execDir;$env:PATH\"\n                }\n\n                It \"<ExecName> is installed in <ExecDir>\" -TestCases $executables {\n                    (Get-Command \"$ExecName\").Source | Should -BeLike \"$ExecDir*\"\n                }\n\n                It \"<ExecName> is available\" -TestCases $executables {\n                    \"$ExecName --version\" | Should -ReturnZeroExitCode\n                }\n\n                AfterEach {\n                    $env:PATH = $originalPath\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Miniconda.Tests.ps1",
    "content": "Describe \"Miniconda\" {\n    It \"Miniconda Environment variables is set. \" {\n        ${env:CONDA} | Should -Not -BeNullOrEmpty\n    }\n\n    It \"Miniconda $env:CONDA\\<PathTest> \" -TestCases @(\n        @{ PathTest = \"python.exe\" }\n        @{ PathTest = \"Scripts\\conda.exe\" }\n    ) {\n        $condaPath = Join-Path ${env:CONDA} $PathTest\n        $condaPath | Should -Exist\n        \"$condaPath --version\" | Should -ReturnZeroExitCode\n    }\n}"
  },
  {
    "path": "images/windows/scripts/tests/Nginx.Tests.ps1",
    "content": "Describe \"Nginx\" {\n    Context \"Path\" {\n        It \"Nginx\" {\n            $nginxPath = Join-Path (Join-Path \"C:\\tools\\\" (Get-Item C:\\tools\\nginx*).Name) \"nginx\"\n            \"$nginxPath -v\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    Context \"Service\" {\n        $nginxService = Get-Service -Name nginx\n        $nginxServiceTests = @{\n            Name = $nginxService.Name\n            Status = $nginxService.Status\n            StartType = $nginxService.StartType\n        }\n\n        It \"<Name> service is stopped\" -TestCases $nginxServiceTests {\n            $Status | Should -Be \"Stopped\"\n        }\n\n        It \"<Name> service is disabled\" -TestCases $nginxServiceTests {\n            $StartType | Should -Be \"Disabled\"\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Node.Tests.ps1",
    "content": "Describe \"Node.JS\" {\n    Context \"Basic modules\"{\n        It \"<ToolName> \" -TestCases @(\n            @{ ToolName = \"node\" }\n            @{ ToolName = \"npm\" }\n        ) {\n            \"$ToolName --version\" | Should -ReturnZeroExitCode\n        }\n    }\n\n    $globalNpmPackages = (Get-ToolsetContent).npm.global_packages\n    $globalNpmPackagesWithTests = $globalNpmPackages | Where-Object { $_.test } | ForEach-Object { @{ Name = $_.name; Test = $_.test } }\n\n    Context \"Global NPM Packages\" {\n        It \"<Name>\" -TestCases $globalNpmPackagesWithTests {\n            $Test | Should -ReturnZeroExitCode\n        }\n    }\n\n    Context \"Node.js version\" {\n        It \"Node.js version should correspond to the version in the toolset\" {\n            node --version | Should -BeLike \"v$((Get-ToolsetContent).node.default)*\"\n        }\n    }\n}"
  },
  {
    "path": "images/windows/scripts/tests/PHP.Tests.ps1",
    "content": "Describe \"PHP\" {\n    It \"Check PHP version\" {\n        $phpMajorMinor = (Get-ToolsetContent).php.version\n        $phpInstalledVersion = php --version | Select-String -Pattern \"PHP $phpMajorMinor\"\n        $phpInstalledVersion | Should -BeLike \"*${phpMajorMinor}*\"\n    }\n\n    It \"Check Composer in the PATH\" {\n        \"composer --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"PHP Environment variables is set.\" {\n        ${env:PHPROOT} | Should -Not -BeNullOrEmpty\n        ${env:PHPROOT} | Should -Exist\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/PipxPackages.Tests.ps1",
    "content": "Describe \"PipxPackages\" {\n    $pipxToolset = (Get-ToolsetContent).pipx\n    $testCases = $pipxToolset | ForEach-Object { @{package = $_.package; cmd = $_.cmd} }\n    It \"<package>\" -TestCases $testCases {\n        \"$cmd\" | Should -ReturnZeroExitCode\n    }\n}"
  },
  {
    "path": "images/windows/scripts/tests/PowerShellAzModules.Tests.ps1",
    "content": "Describe \"AzureModules\" {\n\n    $modules = (Get-ToolsetContent).azureModules\n    $modulesRootPath = \"C:\\\\Modules\"\n\n    foreach ($module in $modules) {\n        $moduleName = $module.name\n\n        Context \"$moduleName\" {\n\n            foreach ($version in $module.versions) {\n                $modulePath = Join-Path -Path $modulesRootPath -ChildPath \"${moduleName}_${version}\"\n                $moduleInfo = @{ moduleName = $moduleName; modulePath = $modulePath; expectedVersion = $version }\n                It \"<expectedVersion> exists in modules directory\" -TestCases $moduleInfo {\n                    $ScriptBlock = {\n                        param ($modulePath, $moduleName)\n                        Import-Module ImageHelpers\n                        Get-ModuleVersionAsJob -modulePath $modulePath -moduleName $moduleName\n                    }\n                    if ($moduleName -eq \"Az\"){\n                        Start-Process -FilePath pwsh.exe -ArgumentList \"-Command (Invoke-Command -ScriptBlock {$ScriptBlock} -ArgumentList $modulePath, $moduleName)\" -Wait -PassThru\n                    } else {\n                        Start-Process -FilePath powershell.exe -ArgumentList \"-Command (Invoke-Command -ScriptBlock {$ScriptBlock} -ArgumentList $modulePath, $moduleName)\" -Wait -PassThru\n                    }\n                    $moduleVersion = Get-Content -Path \"$env:TEMP\\module-version.txt\"\n                    Remove-Item -Path \"${env:TEMP}\\module-version.txt\" -Force\n                    $moduleVersion | Should -Match $expectedVersion\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/PowerShellModules.Tests.ps1",
    "content": "Describe \"PowerShellModules\" {\n    $modules = (Get-ToolsetContent).powershellModules\n    $withoutVersionsModules = $modules | Where-Object {-not $_.versions} | ForEach-Object {\n        @{moduleName = $_.name}\n    }\n\n    $withVersionsModules = $modules | Where-Object {$_.versions} | ForEach-Object {\n        $moduleName = $_.name\n        $_.versions | ForEach-Object {\n            @{moduleName = $moduleName; expectedVersion = $_}\n        }\n    }\n\n    It \"<moduleName> is installed\" -TestCases $withoutVersionsModules {\n        Get-Module -Name $moduleName -ListAvailable | Should -BeTrue\n    }\n\n    if ($withVersionsModules) {\n        It \"<moduleName> with <expectedVersion> is installed\" -TestCases $withVersionsModules {\n            (Get-Module -Name $moduleName -ListAvailable).Version -contains $expectedVersion | Should -BeTrue\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/RunAll-Tests.ps1",
    "content": "Invoke-PesterTests \"*\""
  },
  {
    "path": "images/windows/scripts/tests/Rust.Tests.ps1",
    "content": "Describe \"Rust\" {\n    BeforeAll {\n        $env:RUSTUP_HOME = \"C:\\Users\\Default\\.rustup\"\n        $env:CARGO_HOME = \"C:\\Users\\Default\\.cargo\"\n        $env:Path += \";$env:CARGO_HOME\\bin\"\n    }\n\n    if (Test-IsWin25) {\n        $rustTools = @(\n            @{ToolName = \"rustup\"; binPath = \"C:\\Users\\Default\\.cargo\\bin\\rustup.exe\"}\n            @{ToolName = \"rustc\"; binPath = \"C:\\Users\\Default\\.cargo\\bin\\rustc.exe\"}\n            @{ToolName = \"cargo\"; binPath = \"C:\\Users\\Default\\.cargo\\bin\\cargo.exe\"}\n        )\n    } else {\n        $rustTools = @(\n            @{ToolName = \"rustup\"; binPath = \"C:\\Users\\Default\\.cargo\\bin\\rustup.exe\"}\n            @{ToolName = \"rustc\"; binPath = \"C:\\Users\\Default\\.cargo\\bin\\rustc.exe\"}\n            @{ToolName = \"bindgen.exe\"; binPath = \"C:\\Users\\Default\\.cargo\\bin\\bindgen.exe\"}\n            @{ToolName = \"cbindgen.exe\"; binPath = \"C:\\Users\\Default\\.cargo\\bin\\cbindgen.exe\"}\n            @{ToolName = \"cargo\"; binPath = \"C:\\Users\\Default\\.cargo\\bin\\cargo.exe\"}\n            @{ToolName = \"cargo audit\"; binPath = \"C:\\Users\\Default\\.cargo\\bin\\cargo-audit.exe\"}\n            @{ToolName = \"cargo outdated\"; binPath = \"C:\\Users\\Default\\.cargo\\bin\\cargo-outdated.exe\"}\n        )\n    }\n\n    $rustEnvNotExists = @(\n        @{envVar = \"RUSTUP_HOME\"}\n        @{envVar = \"CARGO_HOME\"}\n    )\n\n    It \"C:\\Users\\Default\\.rustup and C:\\Users\\Default\\.cargo folders exist\" {\n        \"C:\\Users\\Default\\.rustup\", \"C:\\Users\\Default\\.cargo\" | Should -Exist\n    }\n\n    It \"<envVar> environment variable does not exist\" -TestCases $rustEnvNotExists {\n        [Environment]::GetEnvironmentVariables(\"Machine\").ContainsKey($envVar) | Should -BeFalse\n    }\n\n    It \"<ToolName> is installed to the '<binPath>' folder\" -TestCases $rustTools {\n        \"$ToolName --version\" | Should -ReturnZeroExitCode\n        $binPath | Should -Exist\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/SSDTExtensions.Tests.ps1",
    "content": "Describe \"SSDTExtensions\" {\n#These extensions don't have any proper name in the state.packages.json file, only id is available, which can be found on extension marketplace download page\n    It \"Extension SSDT\" {\n        $version = Get-VSExtensionVersion -packageName \"SSDT\"\n        $version | Should -Not -BeNullOrEmpty\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Shell.Tests.ps1",
    "content": "Describe \"Shell\" {\n    $shellTestCases = @(\n        @{Name = \"C:\\shells\\gitbash.exe\"; Target = \"$env:ProgramFiles\\Git\\bin\\bash.exe\"},\n        @{Name = \"C:\\shells\\msys2bash.cmd\"; Target = $null}\n        @{Name = \"C:\\shells\\wslbash.exe\"; Target = \"$env:SystemRoot\\System32\\bash.exe\"}\n    )\n\n    It \"<Name> target to <Target>\" -TestCases $shellTestCases {\n        (Get-Item $Name).Target | Should -BeExactly $Target\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Tools.Tests.ps1",
    "content": "Describe \"Azure Cosmos DB Emulator\" {\n    $cosmosDbEmulatorRegKey = Get-ChildItem \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\" | Get-ItemProperty | Where-Object { $_.DisplayName -eq 'Azure Cosmos DB Emulator' }\n    $installDir = $cosmosDbEmulatorRegKey.InstallLocation\n\n    It \"Azure Cosmos DB Emulator install location registry key exists\" -TestCases @{installDir = $installDir} {\n        $installDir | Should -Not -BeNullOrEmpty\n    }\n\n    It \"Azure Cosmos DB Emulator exe file exists\" -TestCases @{installDir = $installDir} {\n        $exeFilePath = Join-Path $installDir 'CosmosDB.Emulator.exe'\n        $exeFilePath | Should -Exist\n    }\n}\n\nDescribe \"Bazel\" {\n    It \"<ToolName>\" -TestCases @(\n        @{ ToolName = \"bazel\" }\n        @{ ToolName = \"bazelisk\" }\n    ) {\n        \"$ToolName --version\"| Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"CodeQL Bundle\" {\n    It \"Single distribution installed\" {\n        $CodeQLVersionsWildcard = Join-Path $env:AGENT_TOOLSDIRECTORY -ChildPath \"CodeQL\" | Join-Path -ChildPath \"*\"\n        $CodeQLVersionPath = Get-ChildItem $CodeQLVersionsWildcard | Should -HaveCount 1\n    }\n\n    It \"Contains CodeQL executable\" {\n        $CodeQLVersionsWildcard = Join-Path $env:AGENT_TOOLSDIRECTORY -ChildPath \"CodeQL\" | Join-Path -ChildPath \"*\"\n        $CodeQLVersionPath = Get-ChildItem $CodeQLVersionsWildcard | Sort-Object -Descending | Select-Object -First 1 -Expand FullName\n        $CodeQLPath = Join-Path $CodeQLVersionPath -ChildPath \"x64\" | Join-Path -ChildPath \"codeql\" | Join-Path -ChildPath \"codeql.exe\"\n        \"$CodeQLPath version --quiet\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Contains CodeQL packs\" {\n        $CodeQLVersionsWildcard = Join-Path $env:AGENT_TOOLSDIRECTORY -ChildPath \"CodeQL\" | Join-Path -ChildPath \"*\"\n        $CodeQLVersionPath = Get-ChildItem $CodeQLVersionsWildcard | Sort-Object -Descending | Select-Object -First 1 -Expand FullName\n        $CodeQLPacksPath = Join-Path $CodeQLVersionPath -ChildPath \"x64\" | Join-Path -ChildPath \"codeql\" | Join-Path -ChildPath \"qlpacks\"\n        $CodeQLPacksPath | Should -Exist\n    }\n}\n\nDescribe \"R\" {\n    It \"Rscript\" {\n        \"Rscript --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"DACFx\" {\n    It \"DACFx\" {\n        (Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*).DisplayName -Contains \"Microsoft SQL Server Data-Tier Application Framework\" | Should -BeTrue\n        $sqlPackagePath = 'C:\\Program Files\\Microsoft SQL Server\\170\\DAC\\bin\\SqlPackage.exe'\n        \"${sqlPackagePath}\" | Should -Exist\n    }\n}\n\nDescribe \"Mercurial\" -Skip:(Test-IsWin25) {\n    It \"Mercurial\" {\n        \"hg --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"KubernetesTools\" {\n    It \"Kind\" {\n        \"kind version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"kubectl\" {\n        \"kubectl version --client=true\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Helm\" {\n        \"helm version --short\" | Should -ReturnZeroExitCode\n    }\n\n    It \"minikube\" {\n        \"minikube version --short\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Mingw64\" {\n    It \"<ToolName>\" -TestCases @(\n        @{ ToolName = \"gcc\" }\n        @{ ToolName = \"g++\" }\n        @{ ToolName = \"make\" }\n    ) {\n        \"$ToolName --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"NET48\" {\n    It \"NET48\" {\n        Get-ChildItem -Path \"${env:ProgramFiles(x86)}\\Microsoft SDKs\\Windows\\*\\*\\NETFX 4.8 Tools\" -Directory | Should -HaveCount 1\n    }\n}\n\nDescribe \"NSIS\" -Skip:(Test-IsWin25) {\n    It \"NSIS\" {\n        \"makensis /VERSION\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"PowerShell Core\" {\n    It \"pwsh\" {\n        \"pwsh --version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"Execute 2+2 command\" {\n        pwsh -Command \"2+2\" | Should -BeExactly 4\n    }\n}\n\nDescribe \"Sbt\" {\n    It \"sbt\" {\n        \"sbt --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"ServiceFabricSDK\" {\n    It \"PowerShell Module\" {\n        # Ignore PowerShell version check if running in PowerShell Core\n        # https://github.com/microsoft/service-fabric/issues/1343\n        if ($PSVersionTable.PSEdition -eq 'Core') {\n            Get-Module -Name ServiceFabric -SkipEditionCheck -ListAvailable | Should -Not -BeNullOrEmpty\n        } else {\n            Get-Module -Name ServiceFabric -ListAvailable | Should -Not -BeNullOrEmpty\n        }\n    }\n\n    It \"ServiceFabricSDK version\" {\n        Get-ItemPropertyValue 'HKLM:\\SOFTWARE\\Microsoft\\Service Fabric\\' -Name FabricVersion | Should -Not -BeNullOrEmpty\n    }\n}\n\nDescribe \"Stack\" {\n    It \"Stack\" {\n        \"stack --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Vcpkg\" {\n    It \"vcpkg\" {\n        \"vcpkg version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"env variable VCPKG_INSTALLATION_ROOT is set\" {\n        $env:VCPKG_INSTALLATION_ROOT | Should -Not -BeNullOrEmpty\n    }\n\n    It \"VCPKG_INSTALLATION_ROOT directory\" {\n        $env:VCPKG_INSTALLATION_ROOT | Should -Exist\n    }\n}\n\nDescribe \"WebPlatformInstaller\" {\n    It \"WebPlatformInstaller\" {\n        \"WebPICMD\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Zstd\" {\n    It \"zstd\" {\n        \"zstd -V\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Pipx\" {\n    It \"Pipx\" {\n        \"pipx --version\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"Kotlin\" {\n    $kotlinPackages = @(\"kapt\", \"kotlin\", \"kotlinc\", \"kotlinc-jvm\")\n\n    It \"<toolName> is available\" -TestCases ($kotlinPackages | ForEach-Object { @{ toolName = $_ } }) {\n        \"$toolName -version\" | Should -ReturnZeroExitCode\n    }\n\n    It \"kotlinc-js is available\" {\n        \"kotlinc-js -help\" | Should -ReturnZeroExitCode\n    }\n}\n\nDescribe \"SQL OLEDB Driver\" {\n    It \"SQL OLEDB Driver 18\" {\n        \"HKLM:\\SOFTWARE\\Microsoft\\MSOLEDBSQL\" | Should -Exist\n    }\n\n    It \"SQL OLEDB Driver 19\" {\n        \"HKLM:\\SOFTWARE\\Microsoft\\MSOLEDBSQL19\" | Should -Exist\n    }\n}\n\nDescribe \"OpenSSL\" {\n    It \"OpenSSL Version\" {\n        $OpenSSLVersion = (Get-ToolsetContent).openssl.version\n        openssl version | Should -BeLike \"* ${OpenSSLVersion}*\"\n    }\n\n    It \"OpenSSL Path\" {\n        (Get-Command openssl).Source -eq (Join-Path ${env:ProgramFiles} 'OpenSSL\\bin\\openssl.exe') | Should -Be $true\n    }\n\n    It \"OpenSSL Full package\" {\n        Join-Path ${env:ProgramFiles} 'OpenSSL\\include' | Should -Exist\n    }\n\n    It \"OpenSSL DLLs not in System32\" {\n        Get-ChildItem -Path \"$env:SystemRoot\\System32\" -Filter \"libcrypto-*.dll\" -File -ErrorAction SilentlyContinue | Should -BeNullOrEmpty\n\t    Get-ChildItem -Path \"$env:SystemRoot\\System32\" -Filter \"libssl-*.dll\" -File -ErrorAction SilentlyContinue | Should -BeNullOrEmpty\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Toolset.Tests.ps1",
    "content": "$toolsExecutables = @{\n    Python = @(\n        @{ Binary = \"python.exe\"; Arguments = \"--version\" },\n        @{ Binary = \"Scripts\\pip.exe\"; Arguments = \"--version\" }\n    )\n    PyPy = @(\n        @{ Binary = \"python.exe\"; Arguments = \"--version\" },\n        @{ Binary = \"Scripts\\pip.exe\"; Arguments = \"--version\" }\n    )\n    Node = @(\n        @{ Binary = \"node.exe\"; Arguments = \"--version\" },\n        @{ Binary = \"npm\"; Arguments = \"--version\" }\n    )\n    Go = @(\n        @{ Binary = \"bin\\go.exe\"; Arguments = \"version\" }\n    )\n    Ruby = @(\n        @{ Binary = \"bin\\ruby.exe\"; Arguments = \"--version\" }\n    )\n}\n\nfunction Get-ToolExecutables {\n    Param ([String] $Name)\n    if ($toolsExecutables.ContainsKey($Name)) { $toolsExecutables[$Name] } else { @() }\n}\n\nfunction Test-Binaries {\n    Param (\n        [String] $Name,\n        [String] $Version,\n        [String] $Arch,\n        [Array] $ToolExecs\n    )\n    $testCases = $ToolExecs | ForEach-Object {\n        @{ Name = $Name; Version = $Version; Arch = $Arch; Binary = $_.Binary; Arguments = $_.Arguments }\n    }\n    It \"<Binary> <Arguments>\" -TestCases $testCases {\n        $binaryFullPath = Join-Path (Get-TCToolVersionPath -Name $Name -Version $Version -Arch $Arch) $Binary\n        \"$binaryFullPath $Arguments\" | Should -ReturnZeroExitCode\n    }\n}\n\nfunction Test-DefaultVersion {\n    Param (\n        [String] $Name,\n        [String] $ExpectedVersion,\n        [Array] $ToolExecs\n    )\n    $binaryName = [IO.Path]::GetFileNameWithoutExtension($ToolExecs[0].Binary)\n    $testCase = @{ Binary = $binaryName; Arguments = $ToolExecs[0].Arguments; ExpectedVersion = $ExpectedVersion }\n    It \"<ExpectedVersion> is default version\" -TestCases $testCase {\n        $outputLines = (& $env:comspec /c \"$Binary $Arguments 2>&1\") -as [string[]]\n        $LASTEXITCODE | Should -Be 0\n        $outputLines | Should -Match $ExpectedVersion\n    }\n\n    It \"default version is located in tool-cache\" -TestCases $testCase {\n        $binaryFullPath = (Get-Command $Binary).Path\n        $toolcacheDirectory = Get-TCToolPath -ToolName $Name\n        $binaryFullPath | Should -Match ([Regex]::Escape($toolcacheDirectory))\n    }\n}\n\n$tools = Get-ToolsetContent | Select-Object -ExpandProperty toolcache\nforeach ($tool in $tools) {\n    Describe \"$($tool.name) [$($tool.arch)]\" {\n        $toolExecs = Get-ToolExecutables -Name $tool.name\n\n        foreach ($version in $tool.versions) {\n            Context \"$version\" {\n                $toolInfo = @{ Name = $tool.name; Version = $version; Arch = $tool.arch }\n                It \"tool-cache directory exists\" -TestCases $toolInfo {\n                    $toolFullPath = Get-TCToolVersionPath -Name $Name -Version $Version -Arch $Arch\n                    $toolFullPath | Should -Exist\n                }\n\n                if ($toolExecs) {\n                    Test-Binaries -Name $tool.name -Version $version -Arch $tool.arch -ToolExecs $toolExecs\n                }\n            }\n        }\n\n        if ($tool.default -and $toolExecs) {\n            Context \"Default\" {\n                Test-DefaultVersion -Name $tool.name -ExpectedVersion $tool.default -ToolExecs $toolExecs\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/VisualStudio.Tests.ps1",
    "content": "Describe \"Visual Studio\" {\n    Context \"Basic\" {\n        It \"Catalog.json\" {\n            $instanceFolder = Get-Item \"C:\\ProgramData\\Microsoft\\VisualStudio\\Packages\\_Instances\\*\" | Select-Object -First 1\n            Join-Path $instanceFolder.FullName \"catalog.json\" | Should -Exist\n        }\n\n        It \"Devenv.exe\" {\n            $vsInstallRoot = (Get-VisualStudioInstance).InstallationPath\n            $devenvexePath = \"${vsInstallRoot}\\Common7\\IDE\\devenv.exe\"\n            $devenvexePath | Should -Exist\n        }\n    }\n\n    Context \"Visual Studio components\" {\n        $expectedComponents = Get-ToolsetContent | Select-Object -ExpandProperty visualStudio | Select-Object -ExpandProperty workloads\n        $testCases = $expectedComponents | ForEach-Object { @{ComponentName = $_} }\n        BeforeAll {\n            $installedComponents = Get-VisualStudioComponents | Select-Object -ExpandProperty Package\n        }\n\n        It \"<ComponentName>\" -TestCases $testCases {\n            $installedComponents | Should -Contain $ComponentName\n        }\n    }\n}\n\nDescribe \"Windows 10 SDK\" -Skip:(Test-IsWin25) {\n    It \"Verifies 17763 SDK is installed\" {\n        \"${env:ProgramFiles(x86)}\\Windows Kits\\10\\DesignTime\\CommonConfiguration\\Neutral\\UAP\\10.0.17763.0\\UAP.props\" | Should -Exist\n    }\n}\n\nDescribe \"Windows 11 SDK\" {\n    It \"Verifies 26100 SDK is installed\" {\n        \"${env:ProgramFiles(x86)}\\Windows Kits\\10\\DesignTime\\CommonConfiguration\\Neutral\\UAP\\10.0.26100.0\\UAP.props\" | Should -Exist\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Vsix.Tests.ps1",
    "content": "Describe \"Vsix\" {\n    $toolset = Get-ToolsetContent\n    $requiredVsixPackages = $toolset.visualStudio.vsix\n\n    $allPackages = (Get-VisualStudioInstance).Packages\n    $testCases = $requiredVsixPackages | ForEach-Object {\n        $vsixPackage = Get-VsixInfoFromMarketplace $_\n        @{\n            VsixName    = $vsixPackage.ExtensionName\n            VsixId      = $vsixPackage.VsixId\n            AllPackages = $allPackages\n        }\n    }\n    if ($testCases.Count -gt 0) {\n        It \"Extension <VsixName> is installed\" -TestCases $testCases {\n            $objVsix = $AllPackages | Where-Object { $_.id -eq $VsixId }\n            $objVsix | Should -Not -BeNullOrEmpty\n        }\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/WDK.Tests.ps1",
    "content": "Describe \"WDK\" -Skip:(Test-IsWin25) {\n  It \"WDK exists\" {\n    $regKey = \"HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\"\n    $installedApplications = Get-ItemProperty -Path $regKey\n    $WDKVersion = $installedApplications | Where-Object DisplayName -eq 'Windows Driver Kit' | Select-Object -First 1 -ExpandProperty DisplayVersion\n    $WDKVersion | Should -Not -BeNullOrEmpty\n  }\n\n  It \"Windows Driver Kit VSIX extension\" {\n    $version = Get-VSExtensionVersion -packageName \"Microsoft.Windows.DriverKit\"\n    $version | Should -Not -BeNullOrEmpty\n  }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/WinAppDriver.Tests.ps1",
    "content": "Describe \"WinAppDriver\" {\n  It \"WinAppDriver directory exists\" {\n    Test-Path -Path \"${env:ProgramFiles(x86)}\\Windows Application Driver\" | Should -BeTrue\n  }\n}\n\nDescribe \"Developer Mode\" {\n  It \"Developer Mode is enabled\" {\n    $path = \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock\";\n    Get-ItemProperty -Path $path | Select-Object -ExpandProperty \"AllowDevelopmentWithoutDevLicense\" | Should -Be 1\n  }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/WindowsFeatures.Tests.ps1",
    "content": "Describe \"WindowsFeatures\" {\n    $windowsFeatures = (Get-ToolsetContent).windowsFeatures\n    $testCases = $windowsFeatures | ForEach-Object { @{ Name = $_.name; OptionalFeature = $_.optionalFeature } }\n\n    It \"Windows Feature <Name> is installed\" -TestCases $testCases {\n        if ($OptionalFeature) {\n            (Get-WindowsOptionalFeature -Online -FeatureName $Name).State | Should -Be \"Enabled\"\n        } else {\n            (Get-WindowsFeature -Name $Name).InstallState | Should -Be \"Installed\"\n        }\n    }\n\n    it \"Check WSL is on path\" {\n        (Get-Command -Name 'wsl') | Should -BeTrue\n    }\n\n    it \"Check WLAN service is stopped\" {\n        (Get-Service -Name wlansvc).Status | Should -Be \"Stopped\"\n    }\n}\n\nDescribe \"DiskSpace\" {\n    It \"The image has enough disk space\" {\n        $diskInfo = Get-PSDrive -Name C\n        $totalSpaceGB = [math]::Floor(($diskInfo.Used + $diskInfo.Free) / 1GB)\n        $freeSpaceGB = [math]::Floor($diskInfo.Free / 1GB)\n        Write-Host \"  [i] Disk size: ${totalSpaceGB} GB; Free space: ${freeSpaceGB} GB\"\n        $freeSpaceGB | Should -BeGreaterOrEqual 18\n    }\n}\n\nDescribe \"DynamicPorts\" {\n    It \"Test TCP dynamicport start=49152 num=16384\" {\n        $tcpPorts = Get-NetTCPSetting | Where-Object { $_.SettingName -ne \"Automatic\" } | Where-Object {\n            $_.DynamicPortRangeStartPort -ne 49152 -or $_.DynamicPortRangeNumberOfPorts -ne 16384\n        }\n\n        $tcpPorts | Should -BeNullOrEmpty\n    }\n\n    It \"Test UDP dynamicport start=49152 num=16384\" {\n        $udpPorts = Get-NetUDPSetting | Where-Object {\n            $_.DynamicPortRangeStartPort -ne 49152 -or $_.DynamicPortRangeNumberOfPorts -ne 16384\n        }\n\n        $udpPorts | Should -BeNullOrEmpty\n    }\n}\n\nDescribe \"GDIProcessHandleQuota\" {\n    It \"The GDIProcessHandleQuota value is 20000\" {\n        $regPath = \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows\"\n        $regPath32 = \"HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows NT\\CurrentVersion\\Windows\"\n        (Get-ItemProperty $regPath).GDIProcessHandleQuota | Should -BeExactly 20000\n        (Get-ItemProperty $regPath32).GDIProcessHandleQuota | Should -BeExactly 20000\n    }\n}\n\nDescribe \"Test Signed Drivers\" {\n    It \"bcdedit testsigning should be Yes\" {\n        \"$(bcdedit)\" | Should -Match \"testsigning\\s+Yes\"\n    }\n}\n\nDescribe \"Windows Updates\" {\n    It \"WindowsUpdateDone.txt should exist\" {\n        \"$env:windir\\WindowsUpdateDone.txt\" | Should -Exist\n    }\n\n    $testCases = Get-WindowsUpdateStates | Sort-Object Title | ForEach-Object {\n        @{\n            Title = $_.Title\n            State = $_.State\n        }\n    }\n\n    It \"<Title>\" -TestCases $testCases {\n        $expect = \"Installed\"\n        if ( $Title -match \"Microsoft Defender Antivirus\" ) {\n            $expect = \"Installed\", \"Failed\", \"Running\"\n        }\n\n        $State | Should -BeIn $expect\n    }\n}\n\nDescribe \"WSL2\" -Skip:(Test-IsWin22) {\n    It \"WSL status should return zero exit code\" {\n        \"wsl --status\" | Should -ReturnZeroExitCode\n    }\n}\n"
  },
  {
    "path": "images/windows/scripts/tests/Wix.Tests.ps1",
    "content": "Describe \"Wix\" {\n    BeforeAll {\n      $regKey = \"HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\"\n      $installedApplications = Get-ItemProperty -Path $regKey\n      $version = ($installedApplications | Where-Object { $_.BundleCachePath -imatch \".*\\\\WiX\\d*\\.exe$\" } | Select-Object -First 1).DisplayName\n    }\n\n    It \"Wix Toolset version from registry\" {\n      $version | Should -Not -BeNullOrEmpty\n    }\n\n    It \"Wix variable exists\" {\n      $env:WIX | Should -Not -BeNullOrEmpty\n    }\n\n    It \"Wix binaries folder exists\" {\n      Test-Path -Path $(Join-Path -Path $env:WIX -ChildPath \"bin\") | Should -Be $true\n    }\n\n    It \"Wix binaries folder is in PATH\" {\n      $testPath = Join-Path -Path $env:WIX -ChildPath \"bin\"\n      $env:PATH -split \";\" | Should -Contain \"$testPath\"\n    }\n}\n"
  },
  {
    "path": "images/windows/templates/build.windows-2022.pkr.hcl",
    "content": "build {\n  sources = [\"source.azure-arm.image\"]\n  name = \"windows-2022\"\n\n  provisioner \"powershell\" {\n    inline = [\n      \"New-Item -Path ${var.image_folder} -ItemType Directory -Force\",\n      \"New-Item -Path ${var.temp_dir} -ItemType Directory -Force\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.image_folder}\\\\\"\n    sources     = [\n      \"${path.root}/../assets\",\n      \"${path.root}/../scripts\",\n      \"${path.root}/../toolsets\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.image_folder}\\\\scripts\\\\docs-gen\\\\\"\n    source      = \"${path.root}/../../../helpers/software-report-base\"\n  }\n\n  provisioner \"powershell\" {\n    inline = [\n      \"Move-Item '${var.image_folder}\\\\assets\\\\post-gen' 'C:\\\\post-generation'\",\n      \"Remove-Item -Recurse '${var.image_folder}\\\\assets'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\docs-gen' '${var.image_folder}\\\\SoftwareReport'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\helpers' '${var.helper_script_folder}\\\\ImageHelpers'\",\n      \"New-Item -Type Directory -Path '${var.helper_script_folder}\\\\TestsHelpers\\\\'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\tests\\\\Helpers.psm1' '${var.helper_script_folder}\\\\TestsHelpers\\\\TestsHelpers.psm1'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\tests' '${var.image_folder}\\\\tests'\",\n      \"Remove-Item -Recurse '${var.image_folder}\\\\scripts'\",\n      \"Move-Item '${var.image_folder}\\\\toolsets\\\\toolset-2022.json' '${var.image_folder}\\\\toolset.json'\",\n      \"Remove-Item -Recurse '${var.image_folder}\\\\toolsets'\"\n    ]\n  }\n\n  provisioner \"windows-shell\" {\n    inline = [\n      \"net user ${var.install_user} ${var.install_password} /add /passwordchg:no /passwordreq:yes /active:yes /Y\",\n      \"net localgroup Administrators ${var.install_user} /add\",\n      \"winrm set winrm/config/service/auth @{Basic=\\\"true\\\"}\",\n      \"winrm get winrm/config/service/auth\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"if (-not ((net localgroup Administrators) -contains '${var.install_user}')) { exit 1 }\"]\n  }\n\n  provisioner \"powershell\" {\n    elevated_password = \"${var.install_password}\"\n    elevated_user     = \"${var.install_user}\"\n    inline            = [\"bcdedit.exe /set TESTSIGNING ON\"]\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"IMAGE_OS=${var.image_os}\", \"AGENT_TOOLSDIRECTORY=${var.agent_tools_directory}\", \"IMAGEDATA_FILE=${var.imagedata_file}\", \"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    execution_policy = \"unrestricted\"\n    scripts          = [\n      \"${path.root}/../scripts/build/Configure-WindowsDefender.ps1\",\n      \"${path.root}/../scripts/build/Configure-PowerShell.ps1\",\n      \"${path.root}/../scripts/build/Install-PowerShellModules.ps1\",\n      \"${path.root}/../scripts/build/Install-WindowsFeatures.ps1\",\n      \"${path.root}/../scripts/build/Install-Chocolatey.ps1\",\n      \"${path.root}/../scripts/build/Configure-BaseImage.ps1\",\n      \"${path.root}/../scripts/build/Configure-ImageDataFile.ps1\",\n      \"${path.root}/../scripts/build/Configure-SystemEnvironment.ps1\",\n      \"${path.root}/../scripts/build/Configure-DotnetSecureChannel.ps1\"\n    ]\n  }\n\n  provisioner \"windows-restart\" {\n    check_registry        = true\n    restart_check_command = \"powershell -command \\\"& {while ( (Get-WindowsOptionalFeature -Online -FeatureName Containers -ErrorAction SilentlyContinue).State -ne 'Enabled' ) { Start-Sleep 30; Write-Output 'InProgress' }}\\\"\"\n    restart_timeout       = \"10m\"\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"Set-Service -Name wlansvc -StartupType Manual\", \"if ($(Get-Service -Name wlansvc).Status -eq 'Running') { Stop-Service -Name wlansvc}\"]\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Docker.ps1\",\n      \"${path.root}/../scripts/build/Install-DockerWinCred.ps1\",\n      \"${path.root}/../scripts/build/Install-DockerCompose.ps1\",\n      \"${path.root}/../scripts/build/Install-PowershellCore.ps1\",\n      \"${path.root}/../scripts/build/Install-WebPlatformInstaller.ps1\",\n      \"${path.root}/../scripts/build/Install-TortoiseSvn.ps1\"\n    ]\n  }\n\n  provisioner \"windows-restart\" {\n    restart_timeout = \"30m\"\n  }\n\n  provisioner \"powershell\" {\n    elevated_password = \"${var.install_password}\"\n    elevated_user     = \"${var.install_user}\"\n    environment_vars  = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts           = [\n      \"${path.root}/../scripts/build/Install-VisualStudio.ps1\",\n      \"${path.root}/../scripts/build/Install-KubernetesTools.ps1\"\n    ]\n    valid_exit_codes  = [0, 3010]\n  }\n\n  provisioner \"windows-restart\" {\n    check_registry  = true\n    restart_timeout = \"10m\"\n  }\n\n  provisioner \"powershell\" {\n    pause_before     = \"2m0s\"\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Wix.ps1\",\n      \"${path.root}/../scripts/build/Install-WDK.ps1\",\n      \"${path.root}/../scripts/build/Install-VSExtensions.ps1\",\n      \"${path.root}/../scripts/build/Install-AzureCli.ps1\",\n      \"${path.root}/../scripts/build/Install-AzureDevOpsCli.ps1\",\n      \"${path.root}/../scripts/build/Install-ChocolateyPackages.ps1\",\n      \"${path.root}/../scripts/build/Install-JavaTools.ps1\",\n      \"${path.root}/../scripts/build/Install-Kotlin.ps1\",\n      \"${path.root}/../scripts/build/Install-OpenSSL.ps1\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    execution_policy = \"remotesigned\"\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\"${path.root}/../scripts/build/Install-ServiceFabricSDK.ps1\"]\n  }\n\n  provisioner \"windows-restart\" {\n    restart_timeout = \"10m\"\n  }\n\n  provisioner \"windows-shell\" {\n    inline = [\"wmic product where \\\"name like '%%microsoft azure powershell%%'\\\" call uninstall /nointeractive\"]\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-ActionsCache.ps1\",\n      \"${path.root}/../scripts/build/Install-Ruby.ps1\",\n      \"${path.root}/../scripts/build/Install-PyPy.ps1\",\n      \"${path.root}/../scripts/build/Install-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Configure-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Install-NodeJS.ps1\",\n      \"${path.root}/../scripts/build/Install-AndroidSDK.ps1\",\n      \"${path.root}/../scripts/build/Install-PowershellAzModules.ps1\",\n      \"${path.root}/../scripts/build/Install-Pipx.ps1\",\n      \"${path.root}/../scripts/build/Install-Git.ps1\",\n      \"${path.root}/../scripts/build/Install-GitHub-CLI.ps1\",\n      \"${path.root}/../scripts/build/Install-PHP.ps1\",\n      \"${path.root}/../scripts/build/Install-Rust.ps1\",\n      \"${path.root}/../scripts/build/Install-Sbt.ps1\",\n      \"${path.root}/../scripts/build/Install-Chrome.ps1\",\n      \"${path.root}/../scripts/build/Install-EdgeDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-Firefox.ps1\",\n      \"${path.root}/../scripts/build/Install-Selenium.ps1\",\n      \"${path.root}/../scripts/build/Install-IEWebDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-Apache.ps1\",\n      \"${path.root}/../scripts/build/Install-Nginx.ps1\",\n      \"${path.root}/../scripts/build/Install-Msys2.ps1\",\n      \"${path.root}/../scripts/build/Install-WinAppDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-R.ps1\",\n      \"${path.root}/../scripts/build/Install-AWSTools.ps1\",\n      \"${path.root}/../scripts/build/Install-DACFx.ps1\",\n      \"${path.root}/../scripts/build/Install-MysqlCli.ps1\",\n      \"${path.root}/../scripts/build/Install-SQLPowerShellTools.ps1\",\n      \"${path.root}/../scripts/build/Install-SQLOLEDBDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-DotnetSDK.ps1\",\n      \"${path.root}/../scripts/build/Install-Mingw64.ps1\",\n      \"${path.root}/../scripts/build/Install-Haskell.ps1\",\n      \"${path.root}/../scripts/build/Install-Stack.ps1\",\n      \"${path.root}/../scripts/build/Install-Miniconda.ps1\",\n      \"${path.root}/../scripts/build/Install-AzureCosmosDbEmulator.ps1\",\n      \"${path.root}/../scripts/build/Install-Mercurial.ps1\",\n      \"${path.root}/../scripts/build/Install-Zstd.ps1\",\n      \"${path.root}/../scripts/build/Install-NSIS.ps1\",\n      \"${path.root}/../scripts/build/Install-Vcpkg.ps1\",\n      \"${path.root}/../scripts/build/Install-PostgreSQL.ps1\",\n      \"${path.root}/../scripts/build/Install-Bazel.ps1\",\n      \"${path.root}/../scripts/build/Install-AliyunCli.ps1\",\n      \"${path.root}/../scripts/build/Install-RootCA.ps1\",\n      \"${path.root}/../scripts/build/Install-MongoDB.ps1\",\n      \"${path.root}/../scripts/build/Install-CodeQLBundle.ps1\",\n      \"${path.root}/../scripts/build/Configure-Diagnostics.ps1\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    elevated_password = \"${var.install_password}\"\n    elevated_user     = \"${var.install_user}\"\n    environment_vars  = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts           = [\n      \"${path.root}/../scripts/build/Install-WindowsUpdates.ps1\",\n      \"${path.root}/../scripts/build/Configure-DynamicPort.ps1\",\n      \"${path.root}/../scripts/build/Configure-GDIProcessHandleQuota.ps1\",\n      \"${path.root}/../scripts/build/Configure-Shell.ps1\",\n      \"${path.root}/../scripts/build/Configure-DeveloperMode.ps1\",\n      \"${path.root}/../scripts/build/Install-LLVM.ps1\"\n    ]\n  }\n\n  provisioner \"windows-restart\" {\n    check_registry        = true\n    restart_check_command = \"powershell -command \\\"& {if ((-not (Get-Process TiWorker.exe -ErrorAction SilentlyContinue)) -and (-not [System.Environment]::HasShutdownStarted) ) { Write-Output 'Restart complete' }}\\\"\"\n    restart_timeout       = \"30m\"\n  }\n\n  provisioner \"powershell\" {\n    pause_before     = \"2m0s\"\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-WindowsUpdatesAfterReboot.ps1\",\n      \"${path.root}/../scripts/build/Invoke-Cleanup.ps1\",\n      \"${path.root}/../scripts/tests/RunAll-Tests.ps1\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"if (-not (Test-Path ${var.image_folder}\\\\tests\\\\testResults.xml)) { throw '${var.image_folder}\\\\tests\\\\testResults.xml not found' }\"]\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"IMAGE_FOLDER=${var.image_folder}\"]\n    inline           = [\"pwsh -File '${var.image_folder}\\\\SoftwareReport\\\\Generate-SoftwareReport.ps1'\"]\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"if (-not (Test-Path C:\\\\software-report.md)) { throw 'C:\\\\software-report.md not found' }\", \"if (-not (Test-Path C:\\\\software-report.json)) { throw 'C:\\\\software-report.json not found' }\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../Windows2022-Readme.md\"\n    direction   = \"download\"\n    source      = \"C:\\\\software-report.md\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../software-report.json\"\n    direction   = \"download\"\n    source      = \"C:\\\\software-report.json\"\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"INSTALL_USER=${var.install_user}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-NativeImages.ps1\",\n      \"${path.root}/../scripts/build/Configure-System.ps1\",\n      \"${path.root}/../scripts/build/Configure-User.ps1\",\n      \"${path.root}/../scripts/build/Post-Build-Validation.ps1\"\n    ]\n    skip_clean       = true\n  }\n\n  provisioner \"windows-restart\" {\n    restart_timeout = \"10m\"\n  }\n\n  provisioner \"powershell\" {\n    inline = [\n      \"if( Test-Path $env:SystemRoot\\\\System32\\\\Sysprep\\\\unattend.xml ){ rm $env:SystemRoot\\\\System32\\\\Sysprep\\\\unattend.xml -Force}\",\n      \"& $env:SystemRoot\\\\System32\\\\Sysprep\\\\Sysprep.exe /oobe /generalize /mode:vm /quiet /quit\",\n      \"while($true) { $imageState = Get-ItemProperty HKLM:\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Setup\\\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }\"\n    ]\n  }\n\n}\n"
  },
  {
    "path": "images/windows/templates/build.windows-2025-vs2026.pkr.hcl",
    "content": "build {\n  sources = [\"source.azure-arm.image\"]\n  name = \"windows-2025-vs2026\"\n\n  provisioner \"powershell\" {\n    inline = [\n      \"New-Item -Path ${var.image_folder} -ItemType Directory -Force\",\n      \"New-Item -Path ${var.temp_dir} -ItemType Directory -Force\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.image_folder}\\\\\"\n    sources     = [\n      \"${path.root}/../assets\",\n      \"${path.root}/../scripts\",\n      \"${path.root}/../toolsets\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.image_folder}\\\\scripts\\\\docs-gen\\\\\"\n    source      = \"${path.root}/../../../helpers/software-report-base\"\n  }\n\n  provisioner \"powershell\" {\n    inline = [\n      \"Move-Item '${var.image_folder}\\\\assets\\\\post-gen' 'C:\\\\post-generation'\",\n      \"Remove-Item -Recurse '${var.image_folder}\\\\assets'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\docs-gen' '${var.image_folder}\\\\SoftwareReport'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\helpers' '${var.helper_script_folder}\\\\ImageHelpers'\",\n      \"New-Item -Type Directory -Path '${var.helper_script_folder}\\\\TestsHelpers\\\\'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\tests\\\\Helpers.psm1' '${var.helper_script_folder}\\\\TestsHelpers\\\\TestsHelpers.psm1'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\tests' '${var.image_folder}\\\\tests'\",\n      \"Remove-Item -Recurse '${var.image_folder}\\\\scripts'\",\n      \"Move-Item '${var.image_folder}\\\\toolsets\\\\toolset-2025-vs2026.json' '${var.image_folder}\\\\toolset.json'\",\n      \"Remove-Item -Recurse '${var.image_folder}\\\\toolsets'\"\n    ]\n  }\n\n  provisioner \"windows-shell\" {\n    inline = [\n      \"net user ${var.install_user} ${var.install_password} /add /passwordchg:no /passwordreq:yes /active:yes /Y\",\n      \"net localgroup Administrators ${var.install_user} /add\",\n      \"winrm set winrm/config/service/auth @{Basic=\\\"true\\\"}\",\n      \"winrm get winrm/config/service/auth\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"if (-not ((net localgroup Administrators) -contains '${var.install_user}')) { exit 1 }\"]\n  }\n\n  provisioner \"powershell\" {\n    elevated_password = \"${var.install_password}\"\n    elevated_user     = \"${var.install_user}\"\n    inline            = [\"bcdedit.exe /set TESTSIGNING ON\"]\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"IMAGE_OS=${var.image_os}\", \"AGENT_TOOLSDIRECTORY=${var.agent_tools_directory}\", \"IMAGEDATA_FILE=${var.imagedata_file}\", \"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\", \"INSTALL_VS_2026=true\"]\n    execution_policy = \"unrestricted\"\n    scripts          = [\n      \"${path.root}/../scripts/build/Configure-WindowsDefender.ps1\",\n      \"${path.root}/../scripts/build/Configure-PowerShell.ps1\",\n      \"${path.root}/../scripts/build/Install-PowerShellModules.ps1\",\n      \"${path.root}/../scripts/build/Install-WSL2.ps1\",\n      \"${path.root}/../scripts/build/Install-WindowsFeatures.ps1\",\n      \"${path.root}/../scripts/build/Install-Chocolatey.ps1\",\n      \"${path.root}/../scripts/build/Configure-BaseImage.ps1\",\n      \"${path.root}/../scripts/build/Configure-ImageDataFile.ps1\",\n      \"${path.root}/../scripts/build/Configure-SystemEnvironment.ps1\",\n      \"${path.root}/../scripts/build/Configure-DotnetSecureChannel.ps1\"\n    ]\n  }\n\n  provisioner \"windows-restart\" {\n    check_registry        = true\n    restart_check_command = \"powershell -command \\\"& {while ( (Get-WindowsOptionalFeature -Online -FeatureName Containers -ErrorAction SilentlyContinue).State -ne 'Enabled' ) { Start-Sleep 30; Write-Output 'InProgress' }}\\\"\"\n    restart_timeout       = \"10m\"\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"Set-Service -Name wlansvc -StartupType Manual\", \"if ($(Get-Service -Name wlansvc).Status -eq 'Running') { Stop-Service -Name wlansvc}\"]\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Docker.ps1\",\n      \"${path.root}/../scripts/build/Install-DockerWinCred.ps1\",\n      \"${path.root}/../scripts/build/Install-DockerCompose.ps1\",\n      \"${path.root}/../scripts/build/Install-PowershellCore.ps1\",\n      \"${path.root}/../scripts/build/Install-WebPlatformInstaller.ps1\"\n    ]\n  }\n\n  provisioner \"windows-restart\" {\n    restart_timeout = \"30m\"\n  }\n\n  provisioner \"powershell\" {\n    elevated_password = \"${var.install_password}\"\n    elevated_user     = \"${var.install_user}\"\n    environment_vars  = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\", \"INSTALL_VS_2026=true\"]\n    scripts           = [\n      \"${path.root}/../scripts/build/Install-VisualStudio.ps1\",\n      \"${path.root}/../scripts/build/Install-KubernetesTools.ps1\"\n    ]\n    valid_exit_codes  = [0, 3010]\n  }\n\n  provisioner \"windows-restart\" {\n    check_registry  = true\n    restart_timeout = \"10m\"\n  }\n\n  provisioner \"powershell\" {\n    pause_before     = \"2m0s\"\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Wix.ps1\",\n      \"${path.root}/../scripts/build/Install-VSExtensions.ps1\",\n      \"${path.root}/../scripts/build/Install-AzureCli.ps1\",\n      \"${path.root}/../scripts/build/Install-AzureDevOpsCli.ps1\",\n      \"${path.root}/../scripts/build/Install-ChocolateyPackages.ps1\",\n      \"${path.root}/../scripts/build/Install-JavaTools.ps1\",\n      \"${path.root}/../scripts/build/Install-Kotlin.ps1\",\n      \"${path.root}/../scripts/build/Install-OpenSSL.ps1\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    execution_policy = \"remotesigned\"\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\"${path.root}/../scripts/build/Install-ServiceFabricSDK.ps1\"]\n  }\n\n  provisioner \"windows-restart\" {\n    restart_timeout = \"10m\"\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-ActionsCache.ps1\",\n      \"${path.root}/../scripts/build/Install-Ruby.ps1\",\n      \"${path.root}/../scripts/build/Install-PyPy.ps1\",\n      \"${path.root}/../scripts/build/Install-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Configure-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Install-NodeJS.ps1\",\n      \"${path.root}/../scripts/build/Install-AndroidSDK.ps1\",\n      \"${path.root}/../scripts/build/Install-PowershellAzModules.ps1\",\n      \"${path.root}/../scripts/build/Install-Pipx.ps1\",\n      \"${path.root}/../scripts/build/Install-Git.ps1\",\n      \"${path.root}/../scripts/build/Install-GitHub-CLI.ps1\",\n      \"${path.root}/../scripts/build/Install-PHP.ps1\",\n      \"${path.root}/../scripts/build/Install-Rust.ps1\",\n      \"${path.root}/../scripts/build/Install-Sbt.ps1\",\n      \"${path.root}/../scripts/build/Install-Chrome.ps1\",\n      \"${path.root}/../scripts/build/Install-EdgeDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-Firefox.ps1\",\n      \"${path.root}/../scripts/build/Install-Selenium.ps1\",\n      \"${path.root}/../scripts/build/Install-IEWebDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-Apache.ps1\",\n      \"${path.root}/../scripts/build/Install-Nginx.ps1\",\n      \"${path.root}/../scripts/build/Install-Msys2.ps1\",\n      \"${path.root}/../scripts/build/Install-WinAppDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-R.ps1\",\n      \"${path.root}/../scripts/build/Install-AWSTools.ps1\",\n      \"${path.root}/../scripts/build/Install-DACFx.ps1\",\n      \"${path.root}/../scripts/build/Install-MysqlCli.ps1\",\n      \"${path.root}/../scripts/build/Install-SQLPowerShellTools.ps1\",\n      \"${path.root}/../scripts/build/Install-SQLOLEDBDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-DotnetSDK.ps1\",\n      \"${path.root}/../scripts/build/Install-Mingw64.ps1\",\n      \"${path.root}/../scripts/build/Install-Haskell.ps1\",\n      \"${path.root}/../scripts/build/Install-Stack.ps1\",\n      \"${path.root}/../scripts/build/Install-Miniconda.ps1\",\n      \"${path.root}/../scripts/build/Install-AzureCosmosDbEmulator.ps1\",\n      \"${path.root}/../scripts/build/Install-Zstd.ps1\",\n      \"${path.root}/../scripts/build/Install-Vcpkg.ps1\",\n      \"${path.root}/../scripts/build/Install-Bazel.ps1\",\n      \"${path.root}/../scripts/build/Install-RootCA.ps1\",\n      \"${path.root}/../scripts/build/Install-MongoDB.ps1\",\n      \"${path.root}/../scripts/build/Install-CodeQLBundle.ps1\",\n      \"${path.root}/../scripts/build/Configure-Diagnostics.ps1\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    elevated_password = \"${var.install_password}\"\n    elevated_user     = \"${var.install_user}\"\n    environment_vars  = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts           = [\n      \"${path.root}/../scripts/build/Install-PostgreSQL.ps1\",\n      \"${path.root}/../scripts/build/Install-WindowsUpdates.ps1\",\n      \"${path.root}/../scripts/build/Configure-DynamicPort.ps1\",\n      \"${path.root}/../scripts/build/Configure-GDIProcessHandleQuota.ps1\",\n      \"${path.root}/../scripts/build/Configure-Shell.ps1\",\n      \"${path.root}/../scripts/build/Configure-DeveloperMode.ps1\",\n      \"${path.root}/../scripts/build/Install-LLVM.ps1\"\n    ]\n  }\n\n  provisioner \"windows-restart\" {\n    check_registry        = true\n    restart_check_command = \"powershell -command \\\"& {if ((-not (Get-Process TiWorker.exe -ErrorAction SilentlyContinue)) -and (-not [System.Environment]::HasShutdownStarted) ) { Write-Output 'Restart complete' }}\\\"\"\n    restart_timeout       = \"30m\"\n  }\n\n  provisioner \"powershell\" {\n    pause_before     = \"2m0s\"\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-WindowsUpdatesAfterReboot.ps1\",\n      \"${path.root}/../scripts/build/Invoke-Cleanup.ps1\",\n      \"${path.root}/../scripts/tests/RunAll-Tests.ps1\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"if (-not (Test-Path ${var.image_folder}\\\\tests\\\\testResults.xml)) { throw '${var.image_folder}\\\\tests\\\\testResults.xml not found' }\"]\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"IMAGE_FOLDER=${var.image_folder}\"]\n    inline           = [\"pwsh -File '${var.image_folder}\\\\SoftwareReport\\\\Generate-SoftwareReport.ps1'\"]\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"if (-not (Test-Path C:\\\\software-report.md)) { throw 'C:\\\\software-report.md not found' }\", \"if (-not (Test-Path C:\\\\software-report.json)) { throw 'C:\\\\software-report.json not found' }\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../Windows2025-VS2026-Readme.md\"\n    direction   = \"download\"\n    source      = \"C:\\\\software-report.md\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../software-report.json\"\n    direction   = \"download\"\n    source      = \"C:\\\\software-report.json\"\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"INSTALL_USER=${var.install_user}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-NativeImages.ps1\",\n      \"${path.root}/../scripts/build/Configure-System.ps1\",\n      \"${path.root}/../scripts/build/Configure-User.ps1\",\n      \"${path.root}/../scripts/build/Post-Build-Validation.ps1\"\n    ]\n    skip_clean       = true\n  }\n\n  provisioner \"windows-restart\" {\n    restart_timeout = \"10m\"\n  }\n\n  provisioner \"powershell\" {\n    inline = [\n      \"if( Test-Path $env:SystemRoot\\\\System32\\\\Sysprep\\\\unattend.xml ){ rm $env:SystemRoot\\\\System32\\\\Sysprep\\\\unattend.xml -Force}\",\n      \"& $env:SystemRoot\\\\System32\\\\Sysprep\\\\Sysprep.exe /oobe /generalize /mode:vm /quiet /quit\",\n      \"while($true) { $imageState = Get-ItemProperty HKLM:\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Setup\\\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }\"\n    ]\n  }\n\n}\n"
  },
  {
    "path": "images/windows/templates/build.windows-2025.pkr.hcl",
    "content": "build {\n  sources = [\"source.azure-arm.image\"]\n  name = \"windows-2025\"\n\n  provisioner \"powershell\" {\n    inline = [\n      \"New-Item -Path ${var.image_folder} -ItemType Directory -Force\",\n      \"New-Item -Path ${var.temp_dir} -ItemType Directory -Force\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.image_folder}\\\\\"\n    sources     = [\n      \"${path.root}/../assets\",\n      \"${path.root}/../scripts\",\n      \"${path.root}/../toolsets\"\n    ]\n  }\n\n  provisioner \"file\" {\n    destination = \"${var.image_folder}\\\\scripts\\\\docs-gen\\\\\"\n    source      = \"${path.root}/../../../helpers/software-report-base\"\n  }\n\n  provisioner \"powershell\" {\n    inline = [\n      \"Move-Item '${var.image_folder}\\\\assets\\\\post-gen' 'C:\\\\post-generation'\",\n      \"Remove-Item -Recurse '${var.image_folder}\\\\assets'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\docs-gen' '${var.image_folder}\\\\SoftwareReport'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\helpers' '${var.helper_script_folder}\\\\ImageHelpers'\",\n      \"New-Item -Type Directory -Path '${var.helper_script_folder}\\\\TestsHelpers\\\\'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\tests\\\\Helpers.psm1' '${var.helper_script_folder}\\\\TestsHelpers\\\\TestsHelpers.psm1'\",\n      \"Move-Item '${var.image_folder}\\\\scripts\\\\tests' '${var.image_folder}\\\\tests'\",\n      \"Remove-Item -Recurse '${var.image_folder}\\\\scripts'\",\n      \"Move-Item '${var.image_folder}\\\\toolsets\\\\toolset-2025.json' '${var.image_folder}\\\\toolset.json'\",\n      \"Remove-Item -Recurse '${var.image_folder}\\\\toolsets'\"\n    ]\n  }\n\n  provisioner \"windows-shell\" {\n    inline = [\n      \"net user ${var.install_user} ${var.install_password} /add /passwordchg:no /passwordreq:yes /active:yes /Y\",\n      \"net localgroup Administrators ${var.install_user} /add\",\n      \"winrm set winrm/config/service/auth @{Basic=\\\"true\\\"}\",\n      \"winrm get winrm/config/service/auth\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"if (-not ((net localgroup Administrators) -contains '${var.install_user}')) { exit 1 }\"]\n  }\n\n  provisioner \"powershell\" {\n    elevated_password = \"${var.install_password}\"\n    elevated_user     = \"${var.install_user}\"\n    inline            = [\"bcdedit.exe /set TESTSIGNING ON\"]\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"IMAGE_OS=${var.image_os}\", \"AGENT_TOOLSDIRECTORY=${var.agent_tools_directory}\", \"IMAGEDATA_FILE=${var.imagedata_file}\", \"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    execution_policy = \"unrestricted\"\n    scripts          = [\n      \"${path.root}/../scripts/build/Configure-WindowsDefender.ps1\",\n      \"${path.root}/../scripts/build/Configure-PowerShell.ps1\",\n      \"${path.root}/../scripts/build/Install-PowerShellModules.ps1\",\n      \"${path.root}/../scripts/build/Install-WSL2.ps1\",\n      \"${path.root}/../scripts/build/Install-WindowsFeatures.ps1\",\n      \"${path.root}/../scripts/build/Install-Chocolatey.ps1\",\n      \"${path.root}/../scripts/build/Configure-BaseImage.ps1\",\n      \"${path.root}/../scripts/build/Configure-ImageDataFile.ps1\",\n      \"${path.root}/../scripts/build/Configure-SystemEnvironment.ps1\",\n      \"${path.root}/../scripts/build/Configure-DotnetSecureChannel.ps1\"\n    ]\n  }\n\n  provisioner \"windows-restart\" {\n    check_registry        = true\n    restart_check_command = \"powershell -command \\\"& {while ( (Get-WindowsOptionalFeature -Online -FeatureName Containers -ErrorAction SilentlyContinue).State -ne 'Enabled' ) { Start-Sleep 30; Write-Output 'InProgress' }}\\\"\"\n    restart_timeout       = \"10m\"\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"Set-Service -Name wlansvc -StartupType Manual\", \"if ($(Get-Service -Name wlansvc).Status -eq 'Running') { Stop-Service -Name wlansvc}\"]\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Docker.ps1\",\n      \"${path.root}/../scripts/build/Install-DockerWinCred.ps1\",\n      \"${path.root}/../scripts/build/Install-DockerCompose.ps1\",\n      \"${path.root}/../scripts/build/Install-PowershellCore.ps1\",\n      \"${path.root}/../scripts/build/Install-WebPlatformInstaller.ps1\"\n    ]\n  }\n\n  provisioner \"windows-restart\" {\n    restart_timeout = \"30m\"\n  }\n\n  provisioner \"powershell\" {\n    elevated_password = \"${var.install_password}\"\n    elevated_user     = \"${var.install_user}\"\n    environment_vars  = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts           = [\n      \"${path.root}/../scripts/build/Install-VisualStudio.ps1\",\n      \"${path.root}/../scripts/build/Install-KubernetesTools.ps1\"\n    ]\n    valid_exit_codes  = [0, 3010]\n  }\n\n  provisioner \"windows-restart\" {\n    check_registry  = true\n    restart_timeout = \"10m\"\n  }\n\n  provisioner \"powershell\" {\n    pause_before     = \"2m0s\"\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-Wix.ps1\",\n      \"${path.root}/../scripts/build/Install-VSExtensions.ps1\",\n      \"${path.root}/../scripts/build/Install-AzureCli.ps1\",\n      \"${path.root}/../scripts/build/Install-AzureDevOpsCli.ps1\",\n      \"${path.root}/../scripts/build/Install-ChocolateyPackages.ps1\",\n      \"${path.root}/../scripts/build/Install-JavaTools.ps1\",\n      \"${path.root}/../scripts/build/Install-Kotlin.ps1\",\n      \"${path.root}/../scripts/build/Install-OpenSSL.ps1\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    execution_policy = \"remotesigned\"\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\"${path.root}/../scripts/build/Install-ServiceFabricSDK.ps1\"]\n  }\n\n  provisioner \"windows-restart\" {\n    restart_timeout = \"10m\"\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-ActionsCache.ps1\",\n      \"${path.root}/../scripts/build/Install-Ruby.ps1\",\n      \"${path.root}/../scripts/build/Install-PyPy.ps1\",\n      \"${path.root}/../scripts/build/Install-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Configure-Toolset.ps1\",\n      \"${path.root}/../scripts/build/Install-NodeJS.ps1\",\n      \"${path.root}/../scripts/build/Install-AndroidSDK.ps1\",\n      \"${path.root}/../scripts/build/Install-PowershellAzModules.ps1\",\n      \"${path.root}/../scripts/build/Install-Pipx.ps1\",\n      \"${path.root}/../scripts/build/Install-Git.ps1\",\n      \"${path.root}/../scripts/build/Install-GitHub-CLI.ps1\",\n      \"${path.root}/../scripts/build/Install-PHP.ps1\",\n      \"${path.root}/../scripts/build/Install-Rust.ps1\",\n      \"${path.root}/../scripts/build/Install-Sbt.ps1\",\n      \"${path.root}/../scripts/build/Install-Chrome.ps1\",\n      \"${path.root}/../scripts/build/Install-EdgeDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-Firefox.ps1\",\n      \"${path.root}/../scripts/build/Install-Selenium.ps1\",\n      \"${path.root}/../scripts/build/Install-IEWebDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-Apache.ps1\",\n      \"${path.root}/../scripts/build/Install-Nginx.ps1\",\n      \"${path.root}/../scripts/build/Install-Msys2.ps1\",\n      \"${path.root}/../scripts/build/Install-WinAppDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-R.ps1\",\n      \"${path.root}/../scripts/build/Install-AWSTools.ps1\",\n      \"${path.root}/../scripts/build/Install-DACFx.ps1\",\n      \"${path.root}/../scripts/build/Install-MysqlCli.ps1\",\n      \"${path.root}/../scripts/build/Install-SQLPowerShellTools.ps1\",\n      \"${path.root}/../scripts/build/Install-SQLOLEDBDriver.ps1\",\n      \"${path.root}/../scripts/build/Install-DotnetSDK.ps1\",\n      \"${path.root}/../scripts/build/Install-Mingw64.ps1\",\n      \"${path.root}/../scripts/build/Install-Haskell.ps1\",\n      \"${path.root}/../scripts/build/Install-Stack.ps1\",\n      \"${path.root}/../scripts/build/Install-Miniconda.ps1\",\n      \"${path.root}/../scripts/build/Install-AzureCosmosDbEmulator.ps1\",\n      \"${path.root}/../scripts/build/Install-Zstd.ps1\",\n      \"${path.root}/../scripts/build/Install-Vcpkg.ps1\",\n      \"${path.root}/../scripts/build/Install-Bazel.ps1\",\n      \"${path.root}/../scripts/build/Install-RootCA.ps1\",\n      \"${path.root}/../scripts/build/Install-MongoDB.ps1\",\n      \"${path.root}/../scripts/build/Install-CodeQLBundle.ps1\",\n      \"${path.root}/../scripts/build/Configure-Diagnostics.ps1\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    elevated_password = \"${var.install_password}\"\n    elevated_user     = \"${var.install_user}\"\n    environment_vars  = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts           = [\n      \"${path.root}/../scripts/build/Install-PostgreSQL.ps1\",\n      \"${path.root}/../scripts/build/Install-WindowsUpdates.ps1\",\n      \"${path.root}/../scripts/build/Configure-DynamicPort.ps1\",\n      \"${path.root}/../scripts/build/Configure-GDIProcessHandleQuota.ps1\",\n      \"${path.root}/../scripts/build/Configure-Shell.ps1\",\n      \"${path.root}/../scripts/build/Configure-DeveloperMode.ps1\",\n      \"${path.root}/../scripts/build/Install-LLVM.ps1\"\n    ]\n  }\n\n  provisioner \"windows-restart\" {\n    check_registry        = true\n    restart_check_command = \"powershell -command \\\"& {if ((-not (Get-Process TiWorker.exe -ErrorAction SilentlyContinue)) -and (-not [System.Environment]::HasShutdownStarted) ) { Write-Output 'Restart complete' }}\\\"\"\n    restart_timeout       = \"30m\"\n  }\n\n  provisioner \"powershell\" {\n    pause_before     = \"2m0s\"\n    environment_vars = [\"IMAGE_FOLDER=${var.image_folder}\", \"TEMP_DIR=${var.temp_dir}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-WindowsUpdatesAfterReboot.ps1\",\n      \"${path.root}/../scripts/build/Invoke-Cleanup.ps1\",\n      \"${path.root}/../scripts/tests/RunAll-Tests.ps1\"\n    ]\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"if (-not (Test-Path ${var.image_folder}\\\\tests\\\\testResults.xml)) { throw '${var.image_folder}\\\\tests\\\\testResults.xml not found' }\"]\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"IMAGE_VERSION=${var.image_version}\", \"IMAGE_FOLDER=${var.image_folder}\"]\n    inline           = [\"pwsh -File '${var.image_folder}\\\\SoftwareReport\\\\Generate-SoftwareReport.ps1'\"]\n  }\n\n  provisioner \"powershell\" {\n    inline = [\"if (-not (Test-Path C:\\\\software-report.md)) { throw 'C:\\\\software-report.md not found' }\", \"if (-not (Test-Path C:\\\\software-report.json)) { throw 'C:\\\\software-report.json not found' }\"]\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../Windows2025-Readme.md\"\n    direction   = \"download\"\n    source      = \"C:\\\\software-report.md\"\n  }\n\n  provisioner \"file\" {\n    destination = \"${path.root}/../software-report.json\"\n    direction   = \"download\"\n    source      = \"C:\\\\software-report.json\"\n  }\n\n  provisioner \"powershell\" {\n    environment_vars = [\"INSTALL_USER=${var.install_user}\"]\n    scripts          = [\n      \"${path.root}/../scripts/build/Install-NativeImages.ps1\",\n      \"${path.root}/../scripts/build/Configure-System.ps1\",\n      \"${path.root}/../scripts/build/Configure-User.ps1\",\n      \"${path.root}/../scripts/build/Post-Build-Validation.ps1\"\n    ]\n    skip_clean       = true\n  }\n\n  provisioner \"windows-restart\" {\n    restart_timeout = \"10m\"\n  }\n\n  provisioner \"powershell\" {\n    inline = [\n      \"if( Test-Path $env:SystemRoot\\\\System32\\\\Sysprep\\\\unattend.xml ){ rm $env:SystemRoot\\\\System32\\\\Sysprep\\\\unattend.xml -Force}\",\n      \"& $env:SystemRoot\\\\System32\\\\Sysprep\\\\Sysprep.exe /oobe /generalize /mode:vm /quiet /quit\",\n      \"while($true) { $imageState = Get-ItemProperty HKLM:\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Setup\\\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }\"\n    ]\n  }\n\n}\n"
  },
  {
    "path": "images/windows/templates/locals.windows.pkr.hcl",
    "content": "locals {\n    image_properties_map = {\n      \"win22\" = {\n            source_image_marketplace_sku = \"MicrosoftWindowsServer:WindowsServer:2022-Datacenter-g2\"\n            os_disk_size_gb = 256\n      },\n      \"win25\" = {\n            source_image_marketplace_sku = \"MicrosoftWindowsServer:WindowsServer:2025-Datacenter-g2\"\n            os_disk_size_gb = 150\n      },\n      \"win25-vs2026\" = {\n            source_image_marketplace_sku = \"MicrosoftWindowsServer:WindowsServer:2025-Datacenter-g2\"\n            os_disk_size_gb = 150\n      }\n  }\n\n  source_image_marketplace_sku = local.image_properties_map[var.image_os].source_image_marketplace_sku\n  os_disk_size_gb = coalesce(var.os_disk_size_gb, local.image_properties_map[var.image_os].os_disk_size_gb)\n}\n"
  },
  {
    "path": "images/windows/templates/source.windows.pkr.hcl",
    "content": "source \"azure-arm\" \"image\" {\n  client_cert_path                       = var.client_cert_path\n  client_id                              = var.client_id\n  client_secret                          = var.client_secret\n  object_id                              = var.object_id\n  oidc_request_token                     = var.oidc_request_token\n  oidc_request_url                       = var.oidc_request_url\n  subscription_id                        = var.subscription_id\n  tenant_id                              = var.tenant_id\n  use_azure_cli_auth                     = var.use_azure_cli_auth\n\n  allowed_inbound_ip_addresses           = var.allowed_inbound_ip_addresses\n  build_key_vault_name                   = var.build_key_vault_name\n  build_key_vault_secret_name            = var.build_key_vault_secret_name\n  build_resource_group_name              = var.build_resource_group_name\n  communicator                           = \"winrm\"\n  image_publisher                        = split(\":\", local.source_image_marketplace_sku)[0]\n  image_offer                            = split(\":\", local.source_image_marketplace_sku)[1]\n  image_sku                              = split(\":\", local.source_image_marketplace_sku)[2]\n  image_version                          = var.source_image_version\n  location                               = var.location\n  managed_image_name                     = var.managed_image_name\n  managed_image_resource_group_name      = var.managed_image_resource_group_name\n  managed_image_storage_account_type     = var.managed_image_storage_account_type\n  os_disk_size_gb                        = local.os_disk_size_gb\n  os_type                                = var.image_os_type\n  private_virtual_network_with_public_ip = var.private_virtual_network_with_public_ip\n  temp_resource_group_name               = var.temp_resource_group_name\n  virtual_network_name                   = var.virtual_network_name\n  virtual_network_resource_group_name    = var.virtual_network_resource_group_name\n  virtual_network_subnet_name            = var.virtual_network_subnet_name\n  vm_size                                = var.vm_size\n  winrm_expiration_time                  = var.winrm_expiration_time\n  winrm_insecure                         = \"true\"\n  winrm_use_ssl                          = \"true\"\n  winrm_username                         = var.winrm_username\n\n  shared_image_gallery_destination {\n    subscription                         = var.subscription_id\n    gallery_name                         = var.gallery_name\n    resource_group                       = var.gallery_resource_group_name\n    image_name                           = var.gallery_image_name\n    image_version                        = var.gallery_image_version\n    storage_account_type                 = var.gallery_storage_account_type\n  }\n\n  dynamic \"azure_tag\" {\n    for_each = var.azure_tags\n    content {\n      name  = azure_tag.key\n      value = azure_tag.value\n    }\n  }\n}\n"
  },
  {
    "path": "images/windows/templates/variable.windows.pkr.hcl",
    "content": "// Authentication related variables\nvariable \"client_cert_path\" {\n  type    = string\n  default = \"${env(\"ARM_CLIENT_CERT_PATH\")}\"\n}\nvariable \"client_id\" {\n  type    = string\n  default = \"${env(\"ARM_CLIENT_ID\")}\"\n}\nvariable \"client_secret\" {\n  type      = string\n  default   = \"${env(\"ARM_CLIENT_SECRET\")}\"\n  sensitive = true\n}\nvariable \"object_id\" {\n  type    = string\n  default = \"${env(\"ARM_OBJECT_ID\")}\"\n}\nvariable \"oidc_request_token\" {\n  type    = string\n  default = \"\"\n}\nvariable \"oidc_request_url\" {\n  type    = string\n  default = \"\"\n}\nvariable \"subscription_id\" {\n  type    = string\n  default = \"${env(\"ARM_SUBSCRIPTION_ID\")}\"\n}\nvariable \"tenant_id\" {\n  type    = string\n  default = \"${env(\"ARM_TENANT_ID\")}\"\n}\nvariable \"use_azure_cli_auth\" {\n  type    = bool\n  default = false\n}\n\n// Azure environment related variables\nvariable \"allowed_inbound_ip_addresses\" {\n  type    = list(string)\n  default = []\n}\nvariable \"azure_tags\" {\n  type    = map(string)\n  default = {}\n}\nvariable \"build_key_vault_name\" {\n  type    = string\n  default = \"${env(\"BUILD_KEY_VAULT_NAME\")}\"\n}\nvariable \"build_key_vault_secret_name\" {\n  type    = string\n  default = \"${env(\"BUILD_KEY_VAULT_SECRET_NAME\")}\"\n}\nvariable \"build_resource_group_name\" {\n  type    = string\n  default = \"${env(\"BUILD_RG_NAME\")}\"\n}\nvariable \"gallery_image_name\" {\n  type    = string\n  default = \"${env(\"GALLERY_IMAGE_NAME\")}\"\n}\nvariable \"gallery_image_version\" {\n  type    = string\n  default = \"${env(\"GALLERY_IMAGE_VERSION\")}\"\n}\nvariable \"gallery_name\" {\n  type    = string\n  default = \"${env(\"GALLERY_NAME\")}\"\n}\nvariable \"gallery_resource_group_name\" {\n  type    = string\n  default = \"${env(\"GALLERY_RG_NAME\")}\"\n}\nvariable \"gallery_storage_account_type\" {\n  type    = string\n  default = \"${env(\"GALLERY_STORAGE_ACCOUNT_TYPE\")}\"\n}\nvariable \"image_os_type\" {\n  type    = string\n  default = \"Windows\"\n}\nvariable \"location\" {\n  type    = string\n  default = \"\"\n}\nvariable \"managed_image_name\" {\n  type    = string\n  default = \"\"\n}\nvariable \"managed_image_resource_group_name\" {\n  type    = string\n  default = \"${env(\"ARM_RESOURCE_GROUP\")}\"\n}\nvariable \"managed_image_storage_account_type\" {\n  type    = string\n  default = \"Premium_LRS\"\n}\nvariable \"private_virtual_network_with_public_ip\" {\n  type    = bool\n  default = false\n}\nvariable \"os_disk_size_gb\" {\n  type    = number\n  default = null\n}\nvariable \"source_image_version\" {\n  type    = string\n  default = \"latest\"\n}\nvariable \"temp_resource_group_name\" {\n  type    = string\n  default = \"${env(\"TEMP_RESOURCE_GROUP_NAME\")}\"\n}\nvariable \"virtual_network_name\" {\n  type    = string\n  default = \"${env(\"VNET_NAME\")}\"\n}\nvariable \"virtual_network_resource_group_name\" {\n  type    = string\n  default = \"${env(\"VNET_RESOURCE_GROUP\")}\"\n}\nvariable \"virtual_network_subnet_name\" {\n  type    = string\n  default = \"${env(\"VNET_SUBNET\")}\"\n}\nvariable \"vm_size\" {\n  type    = string\n  default = \"Standard_F8s_v2\"\n}\nvariable \"winrm_expiration_time\" {  // A time duration with which to set the WinRM certificate to expire\n  type    = string                  // Also applies to key vault secret expiration time\n  default = \"1440h\"\n}\nvariable \"winrm_username\" {         // The username used to connect to the VM via WinRM\n    type    = string                // Also applies to the username used to create the VM\n    default = \"packer\"\n}\n\n// Image related variables\nvariable \"agent_tools_directory\" {\n  type    = string\n  default = \"C:\\\\hostedtoolcache\\\\windows\"\n}\nvariable \"helper_script_folder\" {\n  type    = string\n  default = \"C:\\\\Program Files\\\\WindowsPowerShell\\\\Modules\\\\\"\n}\nvariable \"image_folder\" {\n  type    = string\n  default = \"C:\\\\image\"\n}\nvariable \"image_os\" {\n  type    = string\n  default = \"\"\n}\nvariable \"image_version\" {\n  type    = string\n  default = \"dev\"\n}\nvariable \"imagedata_file\" {\n  type    = string\n  default = \"C:\\\\imagedata.json\"\n}\nvariable \"install_password\" {\n  type      = string\n  default   = \"\"\n  sensitive = true\n}\nvariable \"install_user\" {\n  type    = string\n  default = \"installer\"\n}\nvariable \"temp_dir\" {\n  type    = string\n  default = \"D:\\\\temp\"\n}\n"
  },
  {
    "path": "images/windows/toolsets/toolset-2022.json",
    "content": "{\n    \"toolcache\": [\n        {\n            \"name\": \"Ruby\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"3.2\",\n                \"3.3\",\n                \"3.4\",\n                \"4.0\"\n            ],\n            \"default\": \"3.3\"\n        },\n        {\n            \"name\": \"Python\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"3.10.*\",\n                \"3.11.*\",\n                \"3.12.*\",\n                \"3.13.*\",\n                \"3.14.*\"\n            ],\n            \"default\": \"3.12.*\"\n        },\n        {\n            \"name\": \"Python\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json\",\n            \"arch\": \"x86\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"3.10.*\",\n                \"3.11.*\",\n                \"3.12.*\",\n                \"3.13.*\"\n            ]\n        },\n        {\n            \"name\": \"PyPy\",\n            \"arch\": \"x86\",\n            \"platform\" : \"win64\",\n            \"versions\": [\n                \"2.7\",\n                \"3.7\",\n                \"3.8\",\n                \"3.9\",\n                \"3.10\"\n            ]\n        },\n        {\n            \"name\": \"node\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"20.*\",\n                \"22.*\",\n                \"24.*\"\n            ]\n        },\n        {\n            \"name\": \"go\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"1.22.*\",\n                \"1.23.*\",\n                \"1.24.*\",\n                \"1.25.*\"\n            ],\n            \"default\": \"1.24.*\"\n        }\n    ],\n    \"powershellModules\": [\n        { \"name\": \"DockerMsftProvider\" },\n        { \"name\": \"MarkdownPS\" },\n        { \"name\": \"Pester\" },\n        { \"name\": \"PowerShellGet\" },\n        { \"name\": \"PSScriptAnalyzer\" },\n        { \"name\": \"PSWindowsUpdate\" },\n        { \"name\": \"SqlServer\", \"versions\": [ \"22.3.0\" ] },\n        { \"name\": \"VSSetup\" },\n        { \"name\": \"Microsoft.Graph\" },\n        {\"name\": \"AWSPowershell\"}\n    ],\n    \"azureModules\": [\n        {\n            \"name\": \"az\",\n            \"versions\": [\n                \"14.6.0\"\n            ]\n        }\n    ],\n    \"java\": {\n        \"default\": \"8\",\n        \"versions\": [ \"8\", \"11\", \"17\", \"21\", \"25\"]\n    },\n    \"android\": {\n        \"commandline_tools_url\": \"https://dl.google.com/android/repository/commandlinetools-win-9123335_latest.zip\",\n        \"hash\": \"8A90E6A3DEB2FA13229B2E335EFD07687DCC8A55A3C544DA9F40B41404993E7D\",\n        \"platform_min_version\": \"34\",\n        \"build_tools_min_version\": \"34.0.0\",\n        \"extras\": [\n            \"android;m2repository\",\n            \"google;m2repository\",\n            \"google;google_play_services\"\n        ],\n        \"addons\": [],\n        \"additional_tools\": [\n            \"cmake;3.22.1\",\n            \"cmake;3.31.5\",\n            \"cmake;4.1.2\"\n        ],\n        \"ndk\": {\n            \"default\": \"27\",\n            \"versions\": [\n                \"27\", \"28\", \"29\"\n            ]\n        }\n    },\n    \"mingw\": {\n        \"version\": \"14.*\",\n        \"runtime\": \"ucrt\"\n    },\n    \"MsysPackages\": {\n        \"msys2\": [],\n        \"mingw\": []\n    },\n    \"windowsFeatures\": [\n        { \"name\": \"Containers\" },\n        { \"name\": \"Microsoft-Windows-Subsystem-Linux\", \"optionalFeature\": true },\n        { \"name\": \"VirtualMachinePlatform\", \"optionalFeature\": true },\n        { \"name\": \"NET-Framework-45-Features\", \"includeAllSubFeatures\": true },\n        { \"name\": \"Client-ProjFS\", \"optionalFeature\": true },\n        { \"name\": \"NET-Framework-Features\", \"includeAllSubFeatures\": true },\n        { \"name\": \"Hyper-V\", \"includeAllSubFeatures\": true },\n        { \"name\": \"HypervisorPlatform\", \"optionalFeature\": true },\n        { \"name\": \"Hyper-V-PowerShell\" },\n        { \"name\": \"Wireless-Networking\" }\n    ],\n    \"visualStudio\": {\n        \"version\" : \"2022\",\n        \"subversion\" : \"17\",\n        \"edition\" : \"Enterprise\",\n        \"channel\": \"release\",\n        \"installChannelUri\": \"\",\n        \"workloads\": [\n            \"Component.Dotfuscator\",\n            \"Component.Linux.CMake\",\n            \"Component.UnityEngine.x64\",\n            \"Component.Unreal.Android\",\n            \"Component.Xamarin\",\n            \"Microsoft.Component.VC.Runtime.UCRTSDK\",\n            \"Microsoft.Net.Component.4.7.2.SDK\",\n            \"Microsoft.Net.Component.4.7.TargetingPack\",\n            \"Microsoft.Net.Component.4.7.2.TargetingPack\",\n            \"Microsoft.Net.Component.4.8.1.SDK\",\n            \"Microsoft.Net.Component.4.8.1.TargetingPack\",\n            \"Microsoft.VisualStudio.Component.AspNet45\",\n            \"Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools\",\n            \"Microsoft.VisualStudio.Component.Debugger.JustInTime\",\n            \"Microsoft.VisualStudio.Component.EntityFramework\",\n            \"Microsoft.VisualStudio.Component.DslTools\",\n            \"Microsoft.VisualStudio.Component.LinqToSql\",\n            \"Microsoft.VisualStudio.Component.SQL.SSDT\",\n            \"Microsoft.VisualStudio.Component.Sharepoint.Tools\",\n            \"Microsoft.VisualStudio.Component.PortableLibrary\",\n            \"Microsoft.VisualStudio.Component.TeamOffice\",\n            \"Microsoft.VisualStudio.Component.TestTools.CodedUITest\",\n            \"Microsoft.VisualStudio.Component.TestTools.WebLoadTest\",\n            \"Microsoft.VisualStudio.Component.UWP.VC.ARM64\",\n            \"Microsoft.VisualStudio.Component.UWP.VC.ARM64EC\",\n            \"Microsoft.VisualStudio.Component.VC.CLI.Support\",\n            \"Microsoft.VisualStudio.Component.VC.CMake.Project\",\n            \"Microsoft.VisualStudio.Component.VC.DiagnosticTools\",\n            \"Microsoft.VisualStudio.Component.VC.Llvm.Clang\",\n            \"Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset\",\n            \"Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest\",\n            \"Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest\",\n            \"Microsoft.VisualStudio.Component.VC.Tools.ARM\",\n            \"Microsoft.VisualStudio.Component.VC.Tools.ARM64\",\n            \"Microsoft.VisualStudio.Component.VC.Tools.ARM64EC\",\n            \"Microsoft.VisualStudio.Component.VC.Tools.x86.x64\",\n            \"Microsoft.VisualStudio.Component.VC.Modules.x86.x64\",\n            \"Microsoft.VisualStudio.Component.VC.Redist.MSM\",\n            \"Microsoft.VisualStudio.Component.VC.Runtimes.ARM.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.Runtimes.ARM64EC.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.MFC.ARM\",\n            \"Microsoft.VisualStudio.Component.VC.MFC.ARM.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.MFC.ARM64\",\n            \"Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ATLMFC\",\n            \"Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ATL\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.ARM\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.ARM.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.ARM64\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ASAN\",\n            \"Microsoft.VisualStudio.Component.Windows10SDK.19041\",\n            \"Microsoft.VisualStudio.Component.Windows11SDK.22621\",\n            \"Microsoft.VisualStudio.Component.Windows11SDK.26100\",\n            \"Microsoft.VisualStudio.Component.Workflow\",\n            \"Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices\",\n            \"Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools\",\n            \"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang\",\n            \"Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142\",\n            \"Microsoft.VisualStudio.ComponentGroup.Web.CloudTools\",\n            \"Microsoft.VisualStudio.Workload.Azure\",\n            \"Microsoft.VisualStudio.Workload.Data\",\n            \"Microsoft.VisualStudio.Workload.ManagedDesktop\",\n            \"Microsoft.VisualStudio.Workload.ManagedGame\",\n            \"Microsoft.VisualStudio.Workload.NativeCrossPlat\",\n            \"Microsoft.VisualStudio.Workload.NativeDesktop\",\n            \"Microsoft.VisualStudio.Workload.NativeGame\",\n            \"Microsoft.VisualStudio.Workload.NativeMobile\",\n            \"Microsoft.VisualStudio.Workload.NetCrossPlat\",\n            \"Microsoft.VisualStudio.Workload.NetWeb\",\n            \"Microsoft.VisualStudio.Workload.Node\",\n            \"Microsoft.VisualStudio.Workload.Office\",\n            \"Microsoft.VisualStudio.Workload.Python\",\n            \"Microsoft.VisualStudio.Workload.Universal\",\n            \"Microsoft.VisualStudio.Workload.VisualStudioExtension\",\n            \"Component.MDD.Linux\",\n            \"Component.Microsoft.Windows.DriverKit\",\n            \"wasm.tools\",\n            \"Microsoft.Component.MSBuild\"\n            \n        ],\n        \"vsix\": [\n            \"SSIS.MicrosoftDataToolsIntegrationServices\",\n            \"VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects\",\n            \"WixToolset.WixToolsetVisualStudio2022Extension\",\n            \"ProBITools.MicrosoftReportProjectsforVisualStudio2022\",\n            \"ProBITools.MicrosoftAnalysisServicesModelingProjects2022\"\n        ]\n    },\n    \"docker\": {\n        \"images\": [\n            \"mcr.microsoft.com/windows/servercore:ltsc2022\",\n            \"mcr.microsoft.com/windows/nanoserver:ltsc2022\",\n            \"mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022\",\n            \"mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2022\",\n            \"mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2022\"\n        ],\n        \"components\": {\n            \"docker\": \"29.1.5\",\n            \"compose\": \"2.40.3\"\n        }\n    },\n    \"pipx\": [\n        {\n            \"package\": \"yamllint\",\n            \"cmd\": \"yamllint --version\"\n        }\n    ],\n    \"selenium\": {\n        \"version\": \"4\"\n    },\n    \"npm\": {\n        \"global_packages\": [\n            { \"name\": \"yarn\", \"test\": \"yarn --version\" },\n            { \"name\": \"newman\", \"test\": \"newman --version\" },\n            { \"name\": \"lerna\", \"test\": \"lerna --version\" },\n            { \"name\": \"gulp-cli\", \"test\": \"gulp --version\" },\n            { \"name\": \"grunt-cli\", \"test\": \"grunt --version\" }\n        ]\n    },\n    \"serviceFabric\": {\n        \"runtime\": {\n            \"version\": \"10.1.2493.9590\",\n            \"checksum\": \"09C63A971BACDE338282C73B3C9174BED9AAD53E1D3A1B73D44515852C9C00CF\"\n        },\n        \"sdk\": {\n            \"version\": \"7.1.2493\",\n            \"checksum\": \"0CB1084156C75CF5075EA91ABA330CF10B58648B8E036C9C2F286805263C497F\"\n        }\n    },\n    \"dotnet\": {\n        \"versions\": [\n            \"8.0\",\n            \"9.0\",\n            \"10.0\"\n        ],\n        \"tools\": [\n            { \"name\": \"nbgv\", \"test\": \"nbgv --version\", \"getversion\": \"nbgv --version\" }\n        ],\n        \"warmup\": false\n    },\n    \"choco\": {\n        \"common_packages\": [\n            { \"name\": \"7zip.install\" },\n            { \"name\": \"aria2\" },\n            { \"name\": \"azcopy10\" },\n            { \"name\": \"Bicep\" },\n            { \"name\": \"innosetup\" },\n            { \"name\": \"jq\" },\n            { \"name\": \"NuGet.CommandLine\" },\n            { \"name\": \"packer\" },\n            {\n                \"name\": \"strawberryperl\" ,\n                \"args\": [ \"--version\", \"5.32.1.1\" ]\n            },\n            { \"name\": \"pulumi\" },\n            { \"name\": \"swig\" },\n            { \"name\": \"vswhere\" },\n            {\n                \"name\": \"julia\",\n                \"args\": [ \"--ia\", \"/DIR=C:\\\\Julia\" ]\n            },\n            {\n                \"name\": \"cmake.install\",\n                \"version\": \"3.31.6\",\n                \"args\": [ \"--installargs\", \"ADD_CMAKE_TO_PATH=\\\"System\\\"\" ]\n            },\n            { \"name\": \"imagemagick\" },\n            { \"name\": \"ninja\" }\n        ]\n    },\n    \"node\": {\n        \"default\": \"20.*\"\n    },\n    \"maven\": {\n        \"version\": \"3.9\"\n    },\n    \"mysql\": {\n        \"version\": \"8.0\"\n    },\n    \"mongodb\": {\n        \"version\": \"7.0\"\n    },\n    \"nsis\": {\n        \"version\": \"3.10\"\n    },\n    \"llvm\": {\n        \"version\": \"20\"\n    },\n    \"php\": {\n        \"version\": \"8.5\"\n    },\n    \"postgresql\": {\n        \"version\": \"14\"\n    },\n    \"kotlin\": {\n        \"version\": \"latest\"\n    },\n    \"openssl\": {\n        \"version\": \"3.*\"\n    },\n    \"pwsh\": {\n        \"version\": \"7.4\"\n    }\n}\n"
  },
  {
    "path": "images/windows/toolsets/toolset-2025-vs2026.json",
    "content": "{\n    \"toolcache\": [\n        {\n            \"name\": \"Ruby\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"3.2\",\n                \"3.3\",\n                \"3.4\",\n                \"4.0\"\n            ],\n            \"default\": \"3.3\"\n        },\n        {\n            \"name\": \"Python\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"3.10.*\",\n                \"3.11.*\",\n                \"3.12.*\",\n                \"3.13.*\",\n                \"3.14.*\"\n            ],\n            \"default\": \"3.12.*\"\n        },\n        {\n            \"name\": \"PyPy\",\n            \"arch\": \"x86\",\n            \"platform\" : \"win64\",\n            \"versions\": [\n                \"3.9\",\n                \"3.10\"\n            ]\n        },\n        {\n            \"name\": \"node\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"20.*\",\n                \"22.*\",\n                \"24.*\"\n            ]\n        },\n        {\n            \"name\": \"go\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"1.22.*\",\n                \"1.23.*\",\n                \"1.24.*\",\n                \"1.25.*\"\n            ],\n            \"default\": \"1.24.*\"\n        }\n    ],\n    \"powershellModules\": [\n        { \"name\": \"DockerMsftProvider\" },\n        { \"name\": \"MarkdownPS\" },\n        { \"name\": \"Pester\" },\n        { \"name\": \"PowerShellGet\" },\n        { \"name\": \"PSScriptAnalyzer\" },\n        { \"name\": \"PSWindowsUpdate\" },\n        { \"name\": \"SqlServer\", \"versions\": [ \"22.3.0\" ] },\n        { \"name\": \"VSSetup\" },\n        { \"name\": \"Microsoft.Graph\" },\n        {\"name\": \"AWSPowershell\"}\n    ],\n    \"azureModules\": [\n        {\n            \"name\": \"az\",\n            \"versions\": [\n                \"14.6.0\"\n            ]\n        }\n    ],\n    \"java\": {\n        \"default\": \"17\",\n        \"versions\": [ \"8\", \"11\", \"17\", \"21\", \"25\"]\n    },\n    \"android\": {\n        \"commandline_tools_url\": \"https://dl.google.com/android/repository/commandlinetools-win-13114758_latest.zip\",\n        \"hash\": \"98B565CB657B012DAE6794CEFC0F66AE1EFB4690C699B78A614B4A6A3505B003\",\n        \"platform_min_version\": \"34\",\n        \"build_tools_min_version\": \"34.0.0\",\n        \"extras\": [\n            \"android;m2repository\",\n            \"google;m2repository\",\n            \"google;google_play_services\"\n        ],\n        \"addons\": [],\n        \"additional_tools\": [\n            \"cmake;3.30.5\",\n            \"cmake;3.31.5\",\n            \"cmake;4.1.2\"\n        ],\n        \"ndk\": {\n            \"default\": \"27\",\n            \"versions\": [\n                \"27\", \"28\", \"29\"\n            ]\n        }\n    },\n    \"mingw\": {\n        \"version\": \"15.*\",\n        \"runtime\": \"ucrt\"\n    },\n    \"MsysPackages\": {\n        \"msys2\": [],\n        \"mingw\": []\n    },\n    \"windowsFeatures\": [\n        { \"name\": \"Containers\" },\n        { \"name\": \"Microsoft-Windows-Subsystem-Linux\", \"optionalFeature\": true },\n        { \"name\": \"VirtualMachinePlatform\", \"optionalFeature\": true },\n        { \"name\": \"NET-Framework-45-Features\", \"includeAllSubFeatures\": true },\n        { \"name\": \"Client-ProjFS\", \"optionalFeature\": true },\n        { \"name\": \"NET-Framework-Features\", \"includeAllSubFeatures\": true },\n        { \"name\": \"Hyper-V\", \"includeAllSubFeatures\": true },\n        { \"name\": \"HypervisorPlatform\", \"optionalFeature\": true },\n        { \"name\": \"Hyper-V-PowerShell\" },\n        { \"name\": \"Wireless-Networking\" }\n    ],\n    \"visualStudio\": {\n        \"version\" : \"2026\",\n        \"subversion\" : \"18\",\n        \"edition\" : \"Enterprise\",\n        \"channel\": \"stable\",\n        \"installChannelUri\": \"\",\n        \"workloads\": [\n            \"Component.Linux.CMake\",\n            \"Component.UnityEngine.x64\",\n            \"Microsoft.Component.VC.Runtime.UCRTSDK\",\n            \"Microsoft.Net.Component.4.8.1.SDK\",\n            \"Microsoft.Net.Component.4.8.1.TargetingPack\",\n            \"Microsoft.VisualStudio.Component.AspNet45\",\n            \"Microsoft.VisualStudio.Component.Debugger.JustInTime\",\n            \"Microsoft.VisualStudio.Component.EntityFramework\",\n            \"Microsoft.VisualStudio.Component.DslTools\",\n            \"Microsoft.VisualStudio.Component.LinqToSql\",\n            \"Microsoft.VisualStudio.Component.SQL.SSDT\",\n            \"Microsoft.VisualStudio.Component.Sharepoint.Tools\",\n            \"Microsoft.VisualStudio.Component.PortableLibrary\",\n            \"Microsoft.VisualStudio.Component.TeamOffice\",\n            \"Microsoft.VisualStudio.Component.UWP.VC.ARM64\",\n            \"Microsoft.VisualStudio.Component.UWP.VC.ARM64EC\",\n            \"Microsoft.VisualStudio.Component.VC.CLI.Support\",\n            \"Microsoft.VisualStudio.Component.VC.CMake.Project\",\n            \"Microsoft.VisualStudio.Component.VC.DiagnosticTools\",\n            \"Microsoft.VisualStudio.Component.VC.Llvm.Clang\",\n            \"Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset\",\n            \"Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest\",\n            \"Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest\",\n            \"Microsoft.VisualStudio.Component.VC.Tools.ARM64\",\n            \"Microsoft.VisualStudio.Component.VC.Tools.ARM64EC\",\n            \"Microsoft.VisualStudio.Component.VC.Tools.x86.x64\",\n            \"Microsoft.VisualStudio.Component.VC.Redist.MSM\",\n            \"Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.Runtimes.ARM64EC.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.MFC.ARM64\",\n            \"Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ATLMFC\",\n            \"Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ATL\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.ARM64\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ASAN\",\n            \"Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64\",\n            \"Microsoft.VisualStudio.Component.Windows11SDK.26100\",\n            \"Microsoft.VisualStudio.Component.Workflow\",\n            \"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang\",\n            \"Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142\",\n            \"Microsoft.VisualStudio.ComponentGroup.Web.CloudTools\",\n            \"Microsoft.VisualStudio.Workload.Azure\",\n            \"Microsoft.VisualStudio.Workload.Data\",\n            \"Microsoft.VisualStudio.Workload.ManagedDesktop\",\n            \"Microsoft.VisualStudio.Workload.ManagedGame\",\n            \"Microsoft.VisualStudio.Workload.NativeCrossPlat\",\n            \"Microsoft.VisualStudio.Workload.NativeDesktop\",\n            \"Microsoft.VisualStudio.Workload.NativeGame\",\n            \"Microsoft.VisualStudio.Workload.NativeMobile\",\n            \"Microsoft.VisualStudio.Workload.NetCrossPlat\",\n            \"Microsoft.VisualStudio.Workload.NetWeb\",\n            \"Microsoft.VisualStudio.Workload.Node\",\n            \"Microsoft.VisualStudio.Workload.Office\",\n            \"Microsoft.VisualStudio.Workload.Python\",\n            \"Microsoft.VisualStudio.Workload.Universal\",\n            \"Microsoft.VisualStudio.Workload.VisualStudioExtension\",\n            \"Component.MDD.Linux\",\n            \"Component.Microsoft.Windows.DriverKit\",\n            \"wasm.tools\",\n            \"Microsoft.Component.MSBuild\"\n        ],\n        \"vsix\": [\n            \"SSIS.MicrosoftDataToolsIntegrationServices\",\n            \"VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects\",\n            \"WixToolset.WixToolsetVisualStudio2022Extension\",\n            \"ProBITools.MicrosoftReportProjectsforVisualStudio2022\",\n            \"ProBITools.MicrosoftAnalysisServicesModelingProjects2022\"\n        ]\n    },\n    \"docker\": {\n        \"images\": [],\n        \"components\": {\n            \"docker\": \"29.1.5\",\n            \"compose\": \"2.40.3\"\n        }\n    },\n    \"pipx\": [\n        {\n            \"package\": \"yamllint\",\n            \"cmd\": \"yamllint --version\"\n        }\n    ],\n    \"selenium\": {\n        \"version\": \"4\"\n    },\n    \"npm\": {\n        \"global_packages\": [\n            { \"name\": \"yarn\", \"test\": \"yarn --version\" },\n            { \"name\": \"newman\", \"test\": \"newman --version\" },\n            { \"name\": \"lerna\", \"test\": \"lerna --version\" },\n            { \"name\": \"gulp-cli\", \"test\": \"gulp --version\" },\n            { \"name\": \"grunt-cli\", \"test\": \"grunt --version\" }\n        ]\n    },\n    \"serviceFabric\": {\n        \"runtime\": {\n            \"version\": \"10.1.2493.9590\",\n            \"checksum\": \"09C63A971BACDE338282C73B3C9174BED9AAD53E1D3A1B73D44515852C9C00CF\"\n        },\n        \"sdk\": {\n            \"version\": \"7.1.2493\",\n            \"checksum\": \"0CB1084156C75CF5075EA91ABA330CF10B58648B8E036C9C2F286805263C497F\"\n        }\n    },\n    \"dotnet\": {\n        \"versions\": [\n            \"8.0\",\n            \"9.0\",\n            \"10.0\"\n        ],\n        \"tools\": [\n            { \"name\": \"nbgv\", \"test\": \"nbgv --version\", \"getversion\": \"nbgv --version\" }\n        ],\n        \"warmup\": false\n    },\n    \"choco\": {\n        \"common_packages\": [\n            { \"name\": \"7zip.install\" },\n            { \"name\": \"aria2\" },\n            { \"name\": \"azcopy10\" },\n            { \"name\": \"Bicep\" },\n            { \"name\": \"innosetup\" },\n            { \"name\": \"jq\" },\n            { \"name\": \"NuGet.CommandLine\" },\n            { \"name\": \"packer\" },\n            { \"name\": \"pulumi\" },\n            { \"name\": \"swig\" },\n            { \"name\": \"vswhere\" },\n            {\n                \"name\": \"julia\",\n                \"args\": [ \"--ia\", \"/DIR=C:\\\\Julia\" ]\n            },\n            {\n                \"name\": \"cmake.install\",\n                \"args\": [ \"--installargs\", \"ADD_CMAKE_TO_PATH=\\\"System\\\"\" ]\n            },\n            {\n                \"name\": \"strawberryperl\" ,\n                \"args\": [ \"--version\", \"5.42.0.1\" ]\n            },\n            { \"name\": \"imagemagick\" },\n            { \"name\": \"ninja\" }\n        ]\n    },\n    \"node\": {\n        \"default\": \"22.*\"\n    },\n    \"maven\": {\n        \"version\": \"3.9\"\n    },\n    \"mysql\": {\n        \"version\": \"8.0\"\n    },\n    \"mongodb\": {\n        \"version\": \"7.0\"\n    },\n    \"llvm\": {\n        \"version\": \"20\"\n    },\n    \"php\": {\n        \"version\": \"8.5\"\n    },\n    \"postgresql\": {\n        \"version\": \"17\"\n    },\n    \"kotlin\": {\n        \"version\": \"latest\"\n    },\n    \"openssl\": {\n        \"version\": \"3.*\"\n    },\n    \"pwsh\": {\n        \"version\": \"7.4\"\n    }\n}\n"
  },
  {
    "path": "images/windows/toolsets/toolset-2025.json",
    "content": "{\n    \"toolcache\": [\n        {\n            \"name\": \"Ruby\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"3.2\",\n                \"3.3\",\n                \"3.4\",\n                \"4.0\"\n            ],\n            \"default\": \"3.3\"\n        },\n        {\n            \"name\": \"Python\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"3.10.*\",\n                \"3.11.*\",\n                \"3.12.*\",\n                \"3.13.*\",\n                \"3.14.*\"\n            ],\n            \"default\": \"3.12.*\"\n        },\n        {\n            \"name\": \"PyPy\",\n            \"arch\": \"x86\",\n            \"platform\" : \"win64\",\n            \"versions\": [\n                \"3.9\",\n                \"3.10\"\n            ]\n        },\n        {\n            \"name\": \"node\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"20.*\",\n                \"22.*\",\n                \"24.*\"\n            ]\n        },\n        {\n            \"name\": \"go\",\n            \"url\" : \"https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json\",\n            \"arch\": \"x64\",\n            \"platform\" : \"win32\",\n            \"versions\": [\n                \"1.22.*\",\n                \"1.23.*\",\n                \"1.24.*\",\n                \"1.25.*\"\n            ],\n            \"default\": \"1.24.*\"\n        }\n    ],\n    \"powershellModules\": [\n        { \"name\": \"DockerMsftProvider\" },\n        { \"name\": \"MarkdownPS\" },\n        { \"name\": \"Pester\" },\n        { \"name\": \"PowerShellGet\" },\n        { \"name\": \"PSScriptAnalyzer\" },\n        { \"name\": \"PSWindowsUpdate\" },\n        { \"name\": \"SqlServer\", \"versions\": [ \"22.3.0\" ] },\n        { \"name\": \"VSSetup\" },\n        { \"name\": \"Microsoft.Graph\" },\n        {\"name\": \"AWSPowershell\"}\n    ],\n    \"azureModules\": [\n        {\n            \"name\": \"az\",\n            \"versions\": [\n                \"14.6.0\"\n            ]\n        }\n    ],\n    \"java\": {\n        \"default\": \"17\",\n        \"versions\": [ \"8\", \"11\", \"17\", \"21\", \"25\"]\n    },\n    \"android\": {\n        \"commandline_tools_url\": \"https://dl.google.com/android/repository/commandlinetools-win-12266719_latest.zip\",\n        \"hash\": \"F9088C04A44F1F37A8A3A228A7663E11AE9445FA07529C96CEF38ACB985A88F3\",\n        \"platform_min_version\": \"34\",\n        \"build_tools_min_version\": \"34.0.0\",\n        \"extras\": [\n            \"android;m2repository\",\n            \"google;m2repository\",\n            \"google;google_play_services\"\n        ],\n        \"addons\": [],\n        \"additional_tools\": [\n            \"cmake;3.30.5\",\n            \"cmake;3.31.5\",\n            \"cmake;4.1.2\"\n        ],\n        \"ndk\": {\n            \"default\": \"27\",\n            \"versions\": [\n                \"27\", \"28\", \"29\"\n            ]\n        }\n    },\n    \"mingw\": {\n        \"version\": \"15.*\",\n        \"runtime\": \"ucrt\"\n    },\n    \"MsysPackages\": {\n        \"msys2\": [],\n        \"mingw\": []\n    },\n    \"windowsFeatures\": [\n        { \"name\": \"Containers\" },\n        { \"name\": \"Microsoft-Windows-Subsystem-Linux\", \"optionalFeature\": true },\n        { \"name\": \"VirtualMachinePlatform\", \"optionalFeature\": true },\n        { \"name\": \"NET-Framework-45-Features\", \"includeAllSubFeatures\": true },\n        { \"name\": \"Client-ProjFS\", \"optionalFeature\": true },\n        { \"name\": \"NET-Framework-Features\", \"includeAllSubFeatures\": true },\n        { \"name\": \"Hyper-V\", \"includeAllSubFeatures\": true },\n        { \"name\": \"HypervisorPlatform\", \"optionalFeature\": true },\n        { \"name\": \"Hyper-V-PowerShell\" },\n        { \"name\": \"Wireless-Networking\" }\n    ],\n    \"visualStudio\": {\n        \"version\" : \"2022\",\n        \"subversion\" : \"17\",\n        \"edition\" : \"Enterprise\",\n        \"channel\": \"release\",\n        \"installChannelUri\": \"\",\n        \"workloads\": [\n            \"Component.Dotfuscator\",\n            \"Component.Linux.CMake\",\n            \"Component.UnityEngine.x64\",\n            \"Microsoft.Component.VC.Runtime.UCRTSDK\",\n            \"Microsoft.Net.Component.4.8.1.SDK\",\n            \"Microsoft.Net.Component.4.8.1.TargetingPack\",\n            \"Microsoft.VisualStudio.Component.AspNet45\",\n            \"Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools\",\n            \"Microsoft.VisualStudio.Component.Debugger.JustInTime\",\n            \"Microsoft.VisualStudio.Component.EntityFramework\",\n            \"Microsoft.VisualStudio.Component.DslTools\",\n            \"Microsoft.VisualStudio.Component.LinqToSql\",\n            \"Microsoft.VisualStudio.Component.SQL.SSDT\",\n            \"Microsoft.VisualStudio.Component.Sharepoint.Tools\",\n            \"Microsoft.VisualStudio.Component.PortableLibrary\",\n            \"Microsoft.VisualStudio.Component.TeamOffice\",\n            \"Microsoft.VisualStudio.Component.TestTools.CodedUITest\",\n            \"Microsoft.VisualStudio.Component.TestTools.WebLoadTest\",\n            \"Microsoft.VisualStudio.Component.UWP.VC.ARM64\",\n            \"Microsoft.VisualStudio.Component.UWP.VC.ARM64EC\",\n            \"Microsoft.VisualStudio.Component.VC.CLI.Support\",\n            \"Microsoft.VisualStudio.Component.VC.CMake.Project\",\n            \"Microsoft.VisualStudio.Component.VC.DiagnosticTools\",\n            \"Microsoft.VisualStudio.Component.VC.Llvm.Clang\",\n            \"Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset\",\n            \"Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest\",\n            \"Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest\",\n            \"Microsoft.VisualStudio.Component.VC.Tools.ARM\",\n            \"Microsoft.VisualStudio.Component.VC.Tools.ARM64\",\n            \"Microsoft.VisualStudio.Component.VC.Tools.ARM64EC\",\n            \"Microsoft.VisualStudio.Component.VC.Tools.x86.x64\",\n            \"Microsoft.VisualStudio.Component.VC.Modules.x86.x64\",\n            \"Microsoft.VisualStudio.Component.VC.Redist.MSM\",\n            \"Microsoft.VisualStudio.Component.VC.Runtimes.ARM.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.Runtimes.ARM64EC.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.MFC.ARM\",\n            \"Microsoft.VisualStudio.Component.VC.MFC.ARM.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.MFC.ARM64\",\n            \"Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ATLMFC\",\n            \"Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ATL\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.ARM\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.ARM.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.ARM64\",\n            \"Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre\",\n            \"Microsoft.VisualStudio.Component.VC.ASAN\",\n            \"Microsoft.VisualStudio.Component.Windows11SDK.26100\",\n            \"Microsoft.VisualStudio.Component.Workflow\",\n            \"Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices\",\n            \"Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools\",\n            \"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang\",\n            \"Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142\",\n            \"Microsoft.VisualStudio.ComponentGroup.Web.CloudTools\",\n            \"Microsoft.VisualStudio.Workload.Azure\",\n            \"Microsoft.VisualStudio.Workload.Data\",\n            \"Microsoft.VisualStudio.Workload.ManagedDesktop\",\n            \"Microsoft.VisualStudio.Workload.ManagedGame\",\n            \"Microsoft.VisualStudio.Workload.NativeCrossPlat\",\n            \"Microsoft.VisualStudio.Workload.NativeDesktop\",\n            \"Microsoft.VisualStudio.Workload.NativeGame\",\n            \"Microsoft.VisualStudio.Workload.NativeMobile\",\n            \"Microsoft.VisualStudio.Workload.NetCrossPlat\",\n            \"Microsoft.VisualStudio.Workload.NetWeb\",\n            \"Microsoft.VisualStudio.Workload.Node\",\n            \"Microsoft.VisualStudio.Workload.Office\",\n            \"Microsoft.VisualStudio.Workload.Python\",\n            \"Microsoft.VisualStudio.Workload.Universal\",\n            \"Microsoft.VisualStudio.Workload.VisualStudioExtension\",\n            \"Component.MDD.Linux\",\n            \"Component.Microsoft.Windows.DriverKit\",\n            \"wasm.tools\",\n            \"Microsoft.Component.MSBuild\"\n        ],\n        \"vsix\": [\n            \"SSIS.MicrosoftDataToolsIntegrationServices\",\n            \"VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects\",\n            \"WixToolset.WixToolsetVisualStudio2022Extension\",\n            \"ProBITools.MicrosoftReportProjectsforVisualStudio2022\",\n            \"ProBITools.MicrosoftAnalysisServicesModelingProjects2022\"\n        ]\n    },\n    \"docker\": {\n        \"images\": [],\n        \"components\": {\n            \"docker\": \"29.1.5\",\n            \"compose\": \"2.40.3\"\n        }\n    },\n    \"pipx\": [\n        {\n            \"package\": \"yamllint\",\n            \"cmd\": \"yamllint --version\"\n        }\n    ],\n    \"selenium\": {\n        \"version\": \"4\"\n    },\n    \"npm\": {\n        \"global_packages\": [\n            { \"name\": \"yarn\", \"test\": \"yarn --version\" },\n            { \"name\": \"newman\", \"test\": \"newman --version\" },\n            { \"name\": \"lerna\", \"test\": \"lerna --version\" },\n            { \"name\": \"gulp-cli\", \"test\": \"gulp --version\" },\n            { \"name\": \"grunt-cli\", \"test\": \"grunt --version\" }\n        ]\n    },\n    \"serviceFabric\": {\n        \"runtime\": {\n            \"version\": \"10.1.2493.9590\",\n            \"checksum\": \"09C63A971BACDE338282C73B3C9174BED9AAD53E1D3A1B73D44515852C9C00CF\"\n        },\n        \"sdk\": {\n            \"version\": \"7.1.2493\",\n            \"checksum\": \"0CB1084156C75CF5075EA91ABA330CF10B58648B8E036C9C2F286805263C497F\"\n        }\n    },\n    \"dotnet\": {\n        \"versions\": [\n            \"8.0\",\n            \"9.0\",\n            \"10.0\"\n        ],\n        \"tools\": [\n            { \"name\": \"nbgv\", \"test\": \"nbgv --version\", \"getversion\": \"nbgv --version\" }\n        ],\n        \"warmup\": false\n    },\n    \"choco\": {\n        \"common_packages\": [\n            { \"name\": \"7zip.install\" },\n            { \"name\": \"aria2\" },\n            { \"name\": \"azcopy10\" },\n            { \"name\": \"Bicep\" },\n            { \"name\": \"innosetup\" },\n            { \"name\": \"jq\" },\n            { \"name\": \"NuGet.CommandLine\" },\n            { \"name\": \"packer\" },\n            { \"name\": \"pulumi\" },\n            { \"name\": \"swig\" },\n            { \"name\": \"vswhere\" },\n            {\n                \"name\": \"julia\",\n                \"args\": [ \"--ia\", \"/DIR=C:\\\\Julia\" ]\n            },\n            {\n                \"name\": \"cmake.install\",\n                \"version\": \"3.31.6\",\n                \"args\": [ \"--installargs\", \"ADD_CMAKE_TO_PATH=\\\"System\\\"\" ]\n            },\n            {\n                \"name\": \"strawberryperl\" ,\n                \"args\": [ \"--version\", \"5.42.0.1\" ]\n            },\n            { \"name\": \"imagemagick\" },\n            { \"name\": \"ninja\" }\n        ]\n    },\n    \"node\": {\n        \"default\": \"22.*\"\n    },\n    \"maven\": {\n        \"version\": \"3.9\"\n    },\n    \"mysql\": {\n        \"version\": \"8.0\"\n    },\n    \"mongodb\": {\n        \"version\": \"7.0\"\n    },\n    \"llvm\": {\n        \"version\": \"20\"\n    },\n    \"php\": {\n        \"version\": \"8.5\"\n    },\n    \"postgresql\": {\n        \"version\": \"17\"\n    },\n    \"kotlin\": {\n        \"version\": \"latest\"\n    },\n    \"openssl\": {\n        \"version\": \"3.*\"\n    },\n    \"pwsh\": {\n        \"version\": \"7.4\"\n    }\n}\n"
  },
  {
    "path": "images.CI/credscan-exclusions.json",
    "content": "{\n    \"tool\": \"Credential Scanner\",\n    \"suppressions\": [\n    {\n        \"placeholder\": \"P@ssword!!\",\n        \"_justification\": \"Password used by SQL Express. It is required to interact with database.\"\n    }\n  ]\n}"
  },
  {
    "path": "images.CI/linux-and-win/build-image.ps1",
    "content": "param(\n    [String] [Parameter (Mandatory=$true)] $TemplatePath,\n    [String] [Parameter (Mandatory=$true)] $BuildTemplateName,\n    [String] [Parameter (Mandatory=$true)] $ClientId,\n    [String] [Parameter (Mandatory=$false)] $ClientSecret,\n    [String] [Parameter (Mandatory=$true)] $Location,\n    [String] [Parameter (Mandatory=$true)] $ImageName,\n    [String] [Parameter (Mandatory=$true)] $ImageResourceGroupName,\n    [String] [Parameter (Mandatory=$true)] $TempResourceGroupName,\n    [String] [Parameter (Mandatory=$true)] $SubscriptionId,\n    [String] [Parameter (Mandatory=$true)] $TenantId,\n    [String] [Parameter (Mandatory=$true)] $ImageOS, # e.g. \"ubuntu22\", \"ubuntu24\" or \"win22\", \"win25\"\n    [String] [Parameter (Mandatory=$false)] $UseAzureCliAuth = \"false\",\n    [String] [Parameter (Mandatory=$false)] $PluginVersion = \"2.3.3\",\n    [String] [Parameter (Mandatory=$false)] $VirtualNetworkName,\n    [String] [Parameter (Mandatory=$false)] $VirtualNetworkRG,\n    [String] [Parameter (Mandatory=$false)] $VirtualNetworkSubnet,\n    [String] [Parameter (Mandatory=$false)] $AllowedInboundIpAddresses = \"[]\",\n    [hashtable] [Parameter (Mandatory=$false)] $Tags = @{}\n)\n\nif (-not (Test-Path $TemplatePath))\n{\n    Write-Error \"'-TemplatePath' parameter is not valid. You have to specify correct Template Path\"\n    exit 1\n}\n\n$buildName = $($BuildTemplateName).Split(\".\")[1]\n$InstallPassword = [System.GUID]::NewGuid().ToString().ToUpper()\n\n$SensitiveData = @(\n    'OSType',\n    'StorageAccountLocation',\n    'OSDiskUri',\n    'OSDiskUriReadOnlySas',\n    'TemplateUri',\n    'TemplateUriReadOnlySas',\n    ':  ->'\n)\n\n$azure_tags = $Tags | ConvertTo-Json -Compress\n\nWrite-Host \"Show Packer Version\"\npacker --version\n\nWrite-Host \"Download packer plugins\"\npacker plugins install github.com/hashicorp/azure $pluginVersion\n\nWrite-Host \"Validate packer template\"\npacker validate -syntax-only -only \"$buildName*\" $TemplatePath\n\nWrite-Host \"Build $buildName VM\"\npacker build    -only \"$buildName*\" `\n                -var \"client_id=$ClientId\" `\n                -var \"client_secret=$ClientSecret\" `\n                -var \"install_password=$InstallPassword\" `\n                -var \"location=$Location\" `\n                -var \"image_os=$ImageOS\" `\n                -var \"managed_image_name=$ImageName\" `\n                -var \"managed_image_resource_group_name=$ImageResourceGroupName\" `\n                -var \"subscription_id=$SubscriptionId\" `\n                -var \"temp_resource_group_name=$TempResourceGroupName\" `\n                -var \"tenant_id=$TenantId\" `\n                -var \"virtual_network_name=$VirtualNetworkName\" `\n                -var \"virtual_network_resource_group_name=$VirtualNetworkRG\" `\n                -var \"virtual_network_subnet_name=$VirtualNetworkSubnet\" `\n                -var \"allowed_inbound_ip_addresses=$($AllowedInboundIpAddresses)\" `\n                -var \"use_azure_cli_auth=$UseAzureCliAuth\" `\n                -var \"azure_tags=$azure_tags\" `\n                -color=false `\n                $TemplatePath `\n        | Where-Object {\n            #Filter sensitive data from Packer logs\n            $currentString = $_\n            $sensitiveString = $SensitiveData | Where-Object { $currentString -match $_ }\n            $sensitiveString -eq $null\n        }\n"
  },
  {
    "path": "images.CI/linux-and-win/cleanup.ps1",
    "content": "param(\n    [Parameter (Mandatory=$true)] [string] $TempResourceGroupName\n)\n\n$groupExist = az group exists --name $TempResourceGroupName\nif ($groupExist -eq \"true\") {\n    Write-Host \"Found a match, deleting temporary files\"\n    az group delete --name $TempResourceGroupName --yes | Out-Null\n    Write-Host \"Temporary group was deleted successfully\"\n} else {\n    Write-Host \"No temporary groups found\"\n}\n"
  },
  {
    "path": "images.CI/linux-and-win/create-release.ps1",
    "content": "param(\n    [Parameter (Mandatory)] [UInt32] $BuildId,\n    [Parameter (Mandatory)] [string] $Organization,\n    [Parameter (Mandatory)] [string] $Project,\n    [Parameter (Mandatory)] [string] $ImageType,\n    [Parameter (Mandatory)] [string] $ManagedImageName,\n    [Parameter (Mandatory)] [string] $DefinitionId,\n    [Parameter (Mandatory)] [string] $AccessToken\n)\n\n$Body = @{\n    definitionId = $DefinitionId\n    variables = @{\n      ImageBuildId = @{\n        value = $BuildId\n      }\n      ImageType = @{\n        value = $ImageType\n      }\n      ManagedImageName = @{\n        value = $ManagedImageName\n      }\n    }\n    isDraft = \"false\"\n} | ConvertTo-Json -Depth 3\n\n$URL = \"https://vsrm.dev.azure.com/$Organization/$Project/_apis/release/releases?api-version=5.1\"\n$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(\"'':${AccessToken}\"))\n$headers = @{\n    Authorization = \"Basic ${base64AuthInfo}\"\n}\n\n[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13\n$NewRelease = Invoke-RestMethod $URL -Body $Body -Method \"POST\" -Headers $headers -ContentType \"application/json\"\n\nWrite-Host \"Created release: $($NewRelease._links.web.href)\"\n"
  },
  {
    "path": "images.CI/measure-provisioners-duration.ps1",
    "content": "param(\n    [Parameter(Mandatory=$true)]\n    [string]$PackerLogPath,\n    [string]$PrefixToPathTrim,\n    [int]$PrintTopNLongest = 25\n)\n\n$DateTimeRegex = \"(\\d+\\/\\d+\\/\\d+ \\d+:\\d+:\\d+)\"\n$TelemetryLineRegex = \"\\[INFO\\] \\(telemetry\\)\"\n$StartProvisionerRegex = \"^${DateTimeRegex} ${TelemetryLineRegex} Starting provisioner (.+)$\"\n$EndProvisionerRegex = \"^${DateTimeRegex} ${TelemetryLineRegex} ending (.+)$\"\n$ShellScriptSubItemRegex = \"${DateTimeRegex} ui: ==> .+: Provisioning with \\w+ script: (.+)\"\n$DownloadUploadSubItemRegex = \"${DateTimeRegex} ui: ==> .+: (Downloading .+|Uploading .+)\"\n\nfunction Start-ProvisionerItem {\n    param([string]$ProvisionerType, [string]$StartTime)\n\n    return @{\n        ProvisionerType = $ProvisionerType\n        StartTime = [DateTime]::Parse($StartTime)\n        SubItems = @()\n    }\n}\n\nfunction End-ProvisionerItem {\n    param([object]$Provisioner, [string]$EndTime)\n\n    $Provisioner.EndTime = [DateTime]::Parse($EndTime)\n    $Provisioner.Duration = New-TimeSpan -Start $Provisioner.StartTime -End $Provisioner.EndTime\n}\n\nfunction Add-ProvisionerSubItem {\n    param([object]$Provisioner, [string]$Command, [string]$DateTime)\n\n    $lastItem = $Provisioner.SubItems | Select-Object -Last 1\n    if ($lastItem) {\n        $lastItem.EndTime = [DateTime]::Parse($DateTime)\n        $lastItem.Duration = New-TimeSpan -Start $lastItem.StartTime -End $lastItem.EndTime\n    }\n\n    if ($Command) {\n        if ($PrefixToPathTrim) { $Command = $Command.Replace($PrefixToPathTrim, \".\") }\n        $Provisioner.SubItems += @{\n            Command = $Command\n            StartTime = [DateTime]::Parse($DateTime)\n        }\n    }\n}\n\nfunction Invoke-TryFindProvisionerSubItem {\n    param([object]$Provisioner, [string] $Line)\n\n    if ($Provisioner.ProvisionerType -in \"powershell\", \"shell\", \"windows-shell\") {\n        if ($Line -match $ShellScriptSubItemRegex) {\n            Add-ProvisionerSubItem -Provisioner $Provisioner -Command $Matches[2] -DateTime $Matches[1]\n        }\n    } elseif ($Provisioner.ProvisionerType -eq \"file\") {\n        if ($Line -match $DownloadUploadSubItemRegex) {\n            Add-ProvisionerSubItem -Provisioner $Provisioner -Command $Matches[2] -DateTime $Matches[1]\n        }\n    }\n}\n\nfunction Assert-StartProvisioner {\n    param([object]$Provisioner, [string]$ProvisionerType)\n    if ($null -ne $Provisioner) {\n        throw \"New provisioner '$ProvisionerType' has been started but previous '$($Provisioner.ProvisionerType)' was not finished yet\"\n    }\n}\n\nfunction Assert-EndProvisioner {\n    param([object]$Provisioner, [string]$ProvisionerType)\n    if (($null -ne $Provisioner) -and ($Provisioner.ProvisionerType -ne $ProvisionerType)) {\n        throw \"Expected end of '$($Provisioner.ProvisionerType)' provisioner but found end of '$ProvisionerType'\"\n    }\n}\n\n$provisionersList = @()\n$currentProvisioner = $null\n\nif ((Get-Content $PackerLogPath -Raw) -notmatch $TelemetryLineRegex) {\n    throw \"Packer log doesn't contain diagnostic information. Env PACKER_LOG must be set to 1\"\n}\n \nGet-Content $PackerLogPath | ForEach-Object {\n    if ($_ -match $StartProvisionerRegex) {\n        Assert-StartProvisioner -Provisioner $currentProvisioner -ProvisionerType $Matches[2]\n\n        $currentProvisioner = Start-ProvisionerItem -ProvisionerType $Matches[2] -StartTime $Matches[1]\n    } elseif (($_ -match $EndProvisionerRegex) -and $currentProvisioner) {\n        Assert-EndProvisioner -Provisioner $currentProvisioner -ProvisionerType $Matches[2]\n\n        End-ProvisionerItem -Provisioner $currentProvisioner -EndTime $Matches[1]\n        Add-ProvisionerSubItem -Provisioner $currentProvisioner -Command $null -DateTime $Matches[1]\n        $provisionersList += $currentProvisioner\n        $currentProvisioner = $null\n    } elseif ($currentProvisioner) {\n        Invoke-TryFindProvisionerSubItem -Provisioner $currentProvisioner -Line $_\n    }\n}\n$totalProvisionersTime = New-TimeSpan\n$provisionersList | ForEach-Object { $totalProvisionersTime = $totalProvisionersTime.Add($_.Duration) }\n\n# Print information about provisioners in order of execution\nWrite-Host \"Build timeline:\"\n$provisionersList | ForEach-Object {\n    Write-Host \"- $($_.Duration) | $($_.ProvisionerType)\"\n    $_.SubItems | ForEach-Object {\n        Write-Host \"      $($_.Duration) | $($_.Command)\"\n    }\n    Write-Host \"\"\n}\nWrite-Host \"Total provisioners time: $totalProvisionersTime\"\n\nif ($PrintTopNLongest -gt 0) {\n    Write-Host \"`n`nTop longest provisioners:\"\n    $provisionersList | ForEach-Object {\n        if ($_.SubItems.Length -gt 0) { $_.SubItems } else { @{ Command = $_.ProvisionerType; Duration = $_.Duration } }\n    } | Sort-Object { $_.Duration } | Select-Object -Last $PrintTopNLongest | ForEach-Object {\n        Write-Host \"- $($_.Duration) | $($_.Command)\"\n    }\n}\n"
  },
  {
    "path": "images.CI/shebang-linter.ps1",
    "content": "$ErrorActionPreference = \"Stop\"\n\nfunction Validate-Scripts {\n    Param (\n        [Parameter(Mandatory=$true)]\n        [string[]]$Path,\n        [Parameter(Mandatory=$true)]\n        [string]$ExpectedShebang\n    )\n    $ScriptWithoutShebangLine = @()\n    Get-ChildItem $path -Recurse -File -Filter \"*.sh\" | ForEach-Object {\n        $relativePath = Resolve-Path $_.FullName -Relative\n        $shebangLine = Get-Content -Path $_.FullName | Select-Object -First 1\n        if ($shebangLine -eq $ExpectedShebang) {\n            Write-Host \"[+] '$relativePath'\"\n        }\n        else {\n            Write-Host \"[-] '$relativePath'\"\n            $ScriptWithoutShebangLine += $relativePath\n        }\n    }\n    return $ScriptWithoutShebangLine\n}\n\n$PathUbuntu = \"./images/ubuntu/scripts\"\n$PathMacOS = \"./images/macos\"\n$PatternUbuntu = \"#!/bin/bash -e\"\n$PatternMacOS = \"#!/bin/bash -e -o pipefail\"\n$ScriptsWithBrokenShebang = @()\n$ScriptsWithBrokenShebang += Validate-Scripts -Path $PathUbuntu -ExpectedShebang $PatternUbuntu\n$ScriptsWithBrokenShebang += Validate-Scripts -Path $PathMacOS -ExpectedShebang $PatternMacOS\nif ($ScriptsWithBrokenShebang.Length -gt 0) {\n    Write-Host \"`n`n`n##[error] The following scripts have incorrect shebang:\"\n    $ScriptsWithBrokenShebang | ForEach-Object {\n        Write-Host \"##[error] '$_'\"\n    }\n    Write-Host \"`n`n##[error] Expected shebang for scripts in 'images/ubuntu' folder is '$PatternUbuntu'\"\n    Write-Host \"##[error] Expected shebang for scripts in 'images/macos' folder is '$PatternMacOS'\"\n    exit 1\n    else {\n        Write-Host \"All scripts have correct shebang.\"\n    }\n}\n"
  },
  {
    "path": "schemas/toolset-schema.json",
    "content": "{\n  \"$schema\": \"https://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"patternProperties\": {\n    \"^.*$\": {\n      \"if\": {\n        \"type\": \"object\",\n        \"required\": [\n          \"version\"\n        ],\n        \"properties\": {\n          \"version\": {\n            \"type\": \"string\",\n            \"pattern\": \"^[0-9]+\\\\.[0-9]+\\\\.[0-9]+.*$\"\n          }\n        }\n      },\n      \"then\": {\n        \"required\": [\n          \"pinnedDetails\"\n        ],\n        \"properties\": {\n          \"pinnedDetails\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"reason\": {\n                \"type\": \"string\"\n              },\n              \"link\": {\n                \"type\": \"string\"\n              },\n              \"review-at\": {\n                \"type\": \"string\",\n                \"format\": \"date\"\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  }
]