diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2adc202 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..70bcba5 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @actions/runner-images-team diff --git a/.github/ISSUE_TEMPLATE/announcement.yml b/.github/ISSUE_TEMPLATE/announcement.yml new file mode 100644 index 0000000..59e683b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/announcement.yml @@ -0,0 +1,56 @@ +name: Announcement +description: Submit an announcement +labels: [Announcement] +body: + - type: textarea + attributes: + label: Breaking changes + placeholder: Short description of the upcoming change + validations: + required: true + - type: textarea + attributes: + label: Target date + placeholder: Date of changes propagation start + validations: + required: true + - type: textarea + attributes: + label: The motivation for the changes + placeholder: Description of main reasons for this change + validations: + required: true + - type: textarea + attributes: + label: Possible impact + placeholder: Description of who might be impacted by this change + validations: + required: true + - type: checkboxes + attributes: + label: Platforms affected + options: + - label: Azure DevOps + - label: GitHub Actions + - type: checkboxes + attributes: + label: Runner images affected + options: + - label: Ubuntu 20.04 + - label: Ubuntu 22.04 + - label: Ubuntu 24.04 + - label: macOS 12 + - label: macOS 13 + - label: macOS 13 Arm64 + - label: macOS 14 + - label: macOS 14 Arm64 + - label: macOS 15 + - label: macOS 15 Arm64 + - label: Windows Server 2019 + - label: Windows Server 2022 + - type: textarea + attributes: + label: Mitigation ways + description: Steps or options for impact mitigation + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..695f5c8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,71 @@ +name: Bug Report +description: Submit a bug report. +labels: [bug report, needs triage] +body: + - type: textarea + attributes: + label: Description + description: A clear and concise description of what the bug is, and why you consider it to be a bug. + validations: + required: true + - type: checkboxes + attributes: + label: Platforms affected + options: + - label: Azure DevOps + - label: GitHub Actions - Standard Runners + - label: GitHub Actions - Larger Runners + - type: checkboxes + attributes: + label: Runner images affected + options: + - label: Ubuntu 20.04 + - label: Ubuntu 22.04 + - label: Ubuntu 24.04 + - label: macOS 12 + - label: macOS 13 + - label: macOS 13 Arm64 + - label: macOS 14 + - label: macOS 14 Arm64 + - label: macOS 15 + - label: macOS 15 Arm64 + - label: Windows Server 2019 + - label: Windows Server 2022 + - type: textarea + attributes: + label: Image version and build link + description: | + Image version where you are experiencing the issue. Where to find image version in build logs: + 1. For GitHub Actions, under "Set up job" -> "Runner Image" -> "Version". + 2. For Azure DevOps, under "Initialize job" -> "Runner Image" -> "Version". + + If you have a public example, please, provide a link to the failed build. + validations: + required: true + - type: input + attributes: + label: Is it regression? + description: If yes, please, provide the latest image version where the issue didn't persist, and a link to the latest successful build. + validations: + required: true + - type: textarea + attributes: + label: Expected behavior + description: A description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: Actual behavior + description: A description of what is actually happening. + validations: + required: true + - type: textarea + attributes: + label: Repro steps + placeholder: | + A description with steps to reproduce the issue. + 1. Step 1 + 2. Step 2 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..259e620 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +blank_issues_enabled: false + +contact_links: + - name: Get help in GitHub Discussions + url: https://github.com/actions/runner-images/discussions + about: Have a question? Feel free to ask in the runner-images GitHub Discussions! diff --git a/.github/ISSUE_TEMPLATE/tool-request.yml b/.github/ISSUE_TEMPLATE/tool-request.yml new file mode 100644 index 0000000..b0fb98a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tool-request.yml @@ -0,0 +1,84 @@ +name: Tool request +description: Request a new tool or update to a tool +title: Update/Add [tool name] +labels: [feature request, needs triage] +body: + - type: markdown + attributes: + value: "## Tool information" + - type: input + attributes: + label: Tool name + validations: + required: true + - type: input + attributes: + label: Tool license + description: Type of licensing for desired tool. + validations: + required: true + - type: checkboxes + attributes: + label: Add or update? + options: + - label: Add + - label: Update + - type: input + attributes: + label: Desired version + description: Let us know if you're requesting a specific version, dev/RC, whatever is latest, etc. + validations: + required: true + - type: input + attributes: + label: Approximate size + description: Leave blank if unknown. + - type: markdown + attributes: + value: "## If this is an add request" + - type: textarea + attributes: + label: Brief description of tool + - type: input + attributes: + label: URL for tool's homepage + - type: textarea + attributes: + label: Provide a basic test case to validate the tool's functionality. + description: This will be automatically formatted into code. + render: bash + - type: checkboxes + attributes: + label: Platforms where you need the tool + options: + - label: Azure DevOps + - label: GitHub Actions + - type: checkboxes + attributes: + label: Runner images where you need the tool + options: + - label: Ubuntu 20.04 + - label: Ubuntu 22.04 + - label: Ubuntu 24.04 + - label: macOS 12 + - label: macOS 13 + - label: macOS 13 Arm64 + - label: macOS 14 + - label: macOS 14 Arm64 + - label: macOS 15 + - label: macOS 15 Arm64 + - label: Windows Server 2019 + - label: Windows Server 2022 + - type: textarea + attributes: + label: Can this tool be installed during the build? + description: If so, please provide a description with required steps. This will be automatically formatted into code. + render: bash + - type: input + attributes: + label: Tool installation time in runtime + description: How long does it take to install the tool? + - type: input + attributes: + label: Are you willing to submit a PR? + description: We accept contributions! diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..ea57d45 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,14 @@ +# Description +New tool, Bug fixing, or Improvement? +Please include a summary of the change and which issue is fixed. Also include relevant motivation and context. +**For new tools, please provide total size and installation time.** + + + +#### Related issue: + +## Check list +- [ ] Related issue / work item is attached +- [ ] Tests are written (if applicable) +- [ ] Documentation is updated (if applicable) +- [ ] Changes are tested and related VM images are successfully generated diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..2c28c6e --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,72 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '32 4 * * 0' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + # only required for workflows in private repositories + actions: read + contents: read + # required for all workflows + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python', 'ruby' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/create_github_release.yml b/.github/workflows/create_github_release.yml new file mode 100644 index 0000000..d107ed2 --- /dev/null +++ b/.github/workflows/create_github_release.yml @@ -0,0 +1,22 @@ +name: Create GitHub release + +on: + repository_dispatch: + types: [create-github-release] + + +jobs: + Create_GitHub_release: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Create release for ${{ github.event.client_payload.ReleaseBranchName }} + uses: ncipollo/release-action@v1.14.0 + with: + tag: ${{ github.event.client_payload.ReleaseBranchName }} + name: ${{ github.event.client_payload.ReleaseTitle }} + body: ${{ github.event.client_payload.ReleaseBody }} + prerelease: ${{ github.event.client_payload.Prerelease }} + commit: ${{ github.event.client_payload.Commitish }} diff --git a/.github/workflows/create_pull_request.yml b/.github/workflows/create_pull_request.yml new file mode 100644 index 0000000..67c23a3 --- /dev/null +++ b/.github/workflows/create_pull_request.yml @@ -0,0 +1,49 @@ +name: Create Pull Request + +on: + repository_dispatch: + types: [create-pr] + + +jobs: + Create_pull_request: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Clone release branch to create pull request + run: | + git checkout ${{ github.event.client_payload.ReleaseBranchName }} + git branch ${{ github.event.client_payload.ReleaseBranchName }}-docs + git push origin ${{ github.event.client_payload.ReleaseBranchName }}-docs + + - name: Create pull request for ${{ github.event.client_payload.ReleaseBranchName }} + id: create-pr + uses: actions/github-script@v7 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + let response = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: "${{ github.event.client_payload.PullRequestTitle }}", + head: "${{ github.event.client_payload.ReleaseBranchName }}-docs", + base: "${{ github.event.client_payload.PullRequestBase }}", + body: `${{ github.event.client_payload.PullRequestBody }}` + }); + return response.data.number + + - name: Request reviewers + uses: actions/github-script@v7 + with: + github-token: ${{secrets.PRAPPROVAL_SECRET}} + script: | + github.rest.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: ${{ steps.create-pr.outputs.result }}, + team_reviewers: ['runner-images-team'] + }) diff --git a/.github/workflows/create_sbom_report.yml b/.github/workflows/create_sbom_report.yml new file mode 100644 index 0000000..a44c0b2 --- /dev/null +++ b/.github/workflows/create_sbom_report.yml @@ -0,0 +1,92 @@ +name: Create SBOM for the release +# Inherited variables: +# github.event.client_payload.agentSpec - Current YAML Label +# github.event.client_payload.ReleaseID - Current release ID +# github.event.client_payload.imageVersion - AzDO image version "major.minor" +# github.event.client_payload.ReleaseBranchName - Necessary to identify workflow run +# +# Current SYFT tool issues: +# macOS (major): prompt privilegies that blocking process indefinetely (https://github.com/anchore/syft/issues/1367) +on: + repository_dispatch: + types: [generate-sbom] +defaults: + run: + shell: pwsh +jobs: + #Checking current release for SBOM + sbom-check: + outputs: + check_status: ${{ steps.check.outputs.status }} + runs-on: ubuntu-latest + steps: + - name: Check release for ${{ github.event.client_payload.ReleaseBranchName }} + id: check + shell: pwsh + run: | + $apiUrl = "https://api.github.com/repos/actions/runner-images/releases/${{ github.event.client_payload.ReleaseID }}" + $response = Invoke-RestMethod -Uri $apiUrl -Method Get -SkipHttpErrorCheck + if ($response.message -ilike "Not Found") { + echo "status=release_not_found" >> $env:GITHUB_OUTPUT + Write-Error "Release ${{ github.event.client_payload.ReleaseID }} wasn't found" + exit 1 + } + foreach ($asset in $response.assets) { + if ($asset.name -like '*sbom*') { + echo "status=sbom_exists" >> $env:GITHUB_OUTPUT + return "Release ${{ github.event.client_payload.ReleaseID }} already contains a SBOM" + } + } + Write-Host "Release has been found, SBOM is not attached, starting generation." + echo "status=okay" >> $env:GITHUB_OUTPUT + #Generating SBOM + building-sbom: + needs: sbom-check + if: ${{ needs.sbom-check.outputs.check_status == 'okay' }} + runs-on: ${{ github.event.client_payload.agentSpec }} + steps: + - name: Available image version check for ${{ github.event.client_payload.ReleaseBranchName }} + run: | + $imageVersionComponents = $env:ImageVersion.Split('.') + $imageMajorVersion = $imageVersionComponents[0] + $imageMinorVersion = $imageVersionComponents[1] + if ("$imageMajorVersion.$imageMinorVersion" -ne '${{ github.event.client_payload.imageVersion }}') { + throw "Current runner $imageMajorVersion.$imageMinorVersion image version doesn't match ${{ github.event.client_payload.imageVersion }}." + } + - name: Install SYFT tool on Windows + if: ${{ runner.os == 'Windows' }} + run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b D:/syft + - name: Install SYFT tool on Ubuntu or macOS + if: ${{ runner.os != 'Windows' }} + run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin v0.100.0 + #Running section. + - name: Run SYFT on Windows + if: ${{ runner.os == 'Windows' }} + run: D:/syft/syft dir:C:/ -vv -o spdx-json=sbom.json + - name: Run SYFT on Ubuntu + if: ${{ runner.os == 'Linux' }} + run: syft dir:/ -vv -o spdx-json=sbom.json + - name: Run SYFT on macOS + if: ${{ runner.os == 'macOS' }} + run: sudo syft dir:/ -vv -o spdx-json=sbom.json --exclude ./Users --exclude ./System/Volumes --exclude ./private + shell: bash + #Preparing artifact (raw SBOM.json is too big) + - name: Compress SBOM file + run: Compress-Archive sbom.json sbom.json.zip + #Upload artifact action + - uses: actions/upload-artifact@v4 + with: + name: sbom-${{ github.event.client_payload.agentSpec }}-${{ github.event.client_payload.imageVersion }} + path: sbom.json.zip + if-no-files-found: warn + #Upload release asset action + #Might be changed to softprops/action-gh-release after additional check + - name: Upload release asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: "https://uploads.github.com/repos/actions/runner-images/releases/${{ github.event.client_payload.ReleaseID }}/assets{?name,label}" + asset_path: ./sbom.json.zip + asset_name: sbom.${{ github.event.client_payload.agentSpec }}.json.zip + asset_content_type: application/zip diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 0000000..5f2c2cb --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,36 @@ +# CI Validation + +name: Linter + +on: + pull_request: + branches: [ main ] + paths: + - '**.json' + - '**.md' + - '**.sh' + +jobs: + build: + name: Lint JSON & MD files + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Lint Code Base + uses: github/super-linter/slim@v4 + env: + VALIDATE_ALL_CODEBASE: false + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VALIDATE_JSON: true + VALIDATE_MARKDOWN: true + DEFAULT_BRANCH: ${{ github.base_ref }} + FILTER_REGEX_EXCLUDE: .*images/*/.*-Readme.md + + - name: Checking shebang lines in MacOS and Ubuntu releases. + run: ./images.CI/shebang-linter.ps1 + shell: pwsh diff --git a/.github/workflows/merge_pull_request.yml b/.github/workflows/merge_pull_request.yml new file mode 100644 index 0000000..916e7db --- /dev/null +++ b/.github/workflows/merge_pull_request.yml @@ -0,0 +1,48 @@ +name: Merge pull request + +on: + repository_dispatch: + types: [merge-pr] + + +jobs: + Merge_pull_request: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Resolve possible conflicts ${{ github.event.client_payload.ReleaseBranchName }} with main + run: | + git config --global user.email "no-reply@github.com" + git config --global user.name "Actions service account" + git checkout ${{ github.event.client_payload.ReleaseBranchName }}-docs + git merge --no-edit --strategy-option=ours main + git push origin ${{ github.event.client_payload.ReleaseBranchName }}-docs + sleep 30 + + - name: Approve pull request by GitHub-Actions bot + uses: actions/github-script@v7 + with: + github-token: ${{secrets.PRAPPROVAL_SECRET}} + script: | + github.rest.pulls.createReview({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: ${{ github.event.client_payload.PullRequestNumber }}, + event: "APPROVE" + }); + + - name: Merge pull request for ${{ github.event.client_payload.ReleaseBranchName }} + uses: actions/github-script@v7 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + github.rest.pulls.merge({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: ${{ github.event.client_payload.PullRequestNumber }}, + merge_method: "squash" + }) diff --git a/.github/workflows/powershell-tests.yml b/.github/workflows/powershell-tests.yml new file mode 100644 index 0000000..28afce8 --- /dev/null +++ b/.github/workflows/powershell-tests.yml @@ -0,0 +1,25 @@ +# CI Validation + +name: PowerShell Tests + +on: + pull_request: + branches: [ main ] + paths: + - 'helpers/software-report-base/**' + +jobs: + powershell-tests: + name: PowerShell tests + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Run Software Report module tests + shell: pwsh + run: | + $ErrorActionPreference = "Stop" + Invoke-Pester -Output Detailed "helpers/software-report-base/tests" + \ No newline at end of file diff --git a/.github/workflows/trigger-ubuntu-win-build.yml b/.github/workflows/trigger-ubuntu-win-build.yml new file mode 100644 index 0000000..973a325 --- /dev/null +++ b/.github/workflows/trigger-ubuntu-win-build.yml @@ -0,0 +1,47 @@ +name: Trigger Build workflow + +on: + workflow_call: + inputs: + image_type: + required: true + type: string + +defaults: + run: + shell: pwsh + +jobs: + trigger-workflow: + runs-on: ubuntu-latest + steps: + - name: Trigger Build workflow + env: + CI_PR_TOKEN: ${{ secrets.CI_PR_TOKEN }} + PR_TITLE: ${{ github.event.pull_request.title }} + CI_PR: ${{ secrets.CI_REPO }} + run: | + $headers = @{ + Authorization="Bearer $env:CI_PR_TOKEN" + } + + # Private repository for builds + $apiRepoUrl = "https://api.github.com/repos/$env:CI_PR" + + $eventType = "trigger-${{ inputs.image_type }}-build" + $body = @{ + event_type = $eventType; + client_payload = @{ + pr_title = "$env:PR_TITLE" + custom_repo = "${{ github.event.pull_request.head.repo.full_name }}" + custom_repo_commit_hash = "${{ github.event.pull_request.head.sha }}" + } + } + + $bodyString = $body | ConvertTo-Json + + try { + Invoke-WebRequest -Uri "$apiRepoUrl/dispatches" -Method Post -Headers $headers -Body $bodyString | Out-Null + } catch { + throw "$($_.exception[0].message)" + } diff --git a/.github/workflows/ubuntu2004.yml b/.github/workflows/ubuntu2004.yml new file mode 100644 index 0000000..a1773f1 --- /dev/null +++ b/.github/workflows/ubuntu2004.yml @@ -0,0 +1,20 @@ +name: Trigger Ubuntu20.04 CI +run-name: Ubuntu20.04 - ${{ github.event.pull_request.title }} + +on: + pull_request_target: + types: labeled + paths: + - 'images/ubuntu/**' + +defaults: + run: + shell: pwsh + +jobs: + Ubuntu_2004: + if: github.event.label.name == 'CI ubuntu-all' || github.event.label.name == 'CI ubuntu-2004' + uses: ./.github/workflows/trigger-ubuntu-win-build.yml + with: + image_type: 'ubuntu2004' + secrets: inherit diff --git a/.github/workflows/ubuntu2204.yml b/.github/workflows/ubuntu2204.yml new file mode 100644 index 0000000..c1f60c1 --- /dev/null +++ b/.github/workflows/ubuntu2204.yml @@ -0,0 +1,20 @@ +name: Trigger Ubuntu22.04 CI +run-name: Ubuntu22.04 - ${{ github.event.pull_request.title }} + +on: + pull_request_target: + types: labeled + paths: + - 'images/ubuntu/**' + +defaults: + run: + shell: pwsh + +jobs: + Ubuntu_2204: + if: github.event.label.name == 'CI ubuntu-all' || github.event.label.name == 'CI ubuntu-2204' + uses: ./.github/workflows/trigger-ubuntu-win-build.yml + with: + image_type: 'ubuntu2204' + secrets: inherit diff --git a/.github/workflows/ubuntu2404.yml b/.github/workflows/ubuntu2404.yml new file mode 100644 index 0000000..5f8db2b --- /dev/null +++ b/.github/workflows/ubuntu2404.yml @@ -0,0 +1,20 @@ +name: Trigger Ubuntu24.04 CI +run-name: Ubuntu24.04 - ${{ github.event.pull_request.title }} + +on: + pull_request_target: + types: labeled + paths: + - 'images/ubuntu/**' + +defaults: + run: + shell: pwsh + +jobs: + Ubuntu_2404: + if: github.event.label.name == 'CI ubuntu-all' || github.event.label.name == 'CI ubuntu-2404' + uses: ./.github/workflows/trigger-ubuntu-win-build.yml + with: + image_type: 'ubuntu2404' + secrets: inherit diff --git a/.github/workflows/update_github_release.yml b/.github/workflows/update_github_release.yml new file mode 100644 index 0000000..2121575 --- /dev/null +++ b/.github/workflows/update_github_release.yml @@ -0,0 +1,30 @@ +name: Update release + +on: + repository_dispatch: + types: [update-github-release] + + +jobs: + Update_GitHub_release: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Update release for ${{ github.event.client_payload.ReleaseBranchName }} + uses: actions/github-script@v7 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const response = await github.rest.repos.getReleaseByTag({ + owner: context.repo.owner, + repo: context.repo.repo, + tag: "${{ github.event.client_payload.ReleaseBranchName }}" + }); + github.rest.repos.updateRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: response.data.id, + prerelease: ${{ github.event.client_payload.Prerelease }} + }); diff --git a/.github/workflows/windows2019.yml b/.github/workflows/windows2019.yml new file mode 100644 index 0000000..3205a54 --- /dev/null +++ b/.github/workflows/windows2019.yml @@ -0,0 +1,20 @@ +name: Trigger Windows19 CI +run-name: Windows2019 - ${{ github.event.pull_request.title }} + +on: + pull_request_target: + types: labeled + paths: + - 'images/windows/**' + +defaults: + run: + shell: pwsh + +jobs: + Windows_2019: + if: github.event.label.name == 'CI windows-all' || github.event.label.name == 'CI windows-2019' + uses: ./.github/workflows/trigger-ubuntu-win-build.yml + with: + image_type: 'windows2019' + secrets: inherit diff --git a/.github/workflows/windows2022.yml b/.github/workflows/windows2022.yml new file mode 100644 index 0000000..74be2a6 --- /dev/null +++ b/.github/workflows/windows2022.yml @@ -0,0 +1,20 @@ +name: Trigger Windows22 CI +run-name: Windows2022 - ${{ github.event.pull_request.title }} + +on: + pull_request_target: + types: labeled + paths: + - 'images/windows/**' + +defaults: + run: + shell: pwsh + +jobs: + Windows_2022: + if: github.event.label.name == 'CI windows-all' || github.event.label.name == 'CI windows-2022' + uses: ./.github/workflows/trigger-ubuntu-win-build.yml + with: + image_type: 'windows2022' + secrets: inherit diff --git a/.github/workflows/windows2025.yml b/.github/workflows/windows2025.yml new file mode 100644 index 0000000..9340893 --- /dev/null +++ b/.github/workflows/windows2025.yml @@ -0,0 +1,20 @@ +name: Trigger Windows25 CI +run-name: Windows2025 - ${{ github.event.pull_request.title }} + +on: + pull_request_target: + types: labeled + paths: + - 'images/windows/**' + +defaults: + run: + shell: pwsh + +jobs: + Windows_2025: + if: github.event.label.name == 'CI windows-all' || github.event.label.name == 'CI windows-2025' + uses: ./.github/workflows/trigger-ubuntu-win-build.yml + with: + image_type: 'windows2025' + secrets: inherit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6018a37 --- /dev/null +++ b/.gitignore @@ -0,0 +1,399 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# VSCode settings +.vscode/** +!.vscode/extensions.json +!.vscode/settings.json +!.vscode/tasks.json + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# Ignore files generated by packer +InstalledSoftware.md + +# Desktop Service Store +.DS_Store + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# gatsby files +.cache/ +public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# visual studio code launch configuration +launch.json + +# Ignore dynamic template +images/*/*-temp.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..4336e1e --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "streetsidesoftware.code-spell-checker", + "hashicorp.hcl", + "davidanson.vscode-markdownlint", + "ms-vscode.powershell", + "timonwong.shellcheck" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f0ce6b6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,25 @@ +{ + "files.trimFinalNewlines": true, + "files.insertFinalNewline": true, + "powershell.codeFormatting.addWhitespaceAroundPipe": true, + "powershell.codeFormatting.alignPropertyValuePairs": true, + "powershell.codeFormatting.autoCorrectAliases": true, + "powershell.codeFormatting.newLineAfterCloseBrace": false, + "powershell.codeFormatting.newLineAfterOpenBrace": true, + "powershell.codeFormatting.openBraceOnSameLine": true, + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", + "powershell.codeFormatting.preset": "OTBS", + "powershell.codeFormatting.trimWhitespaceAroundPipe": true, + "powershell.codeFormatting.whitespaceAfterSeparator": true, + "powershell.codeFormatting.whitespaceAroundOperator": true, + "powershell.codeFormatting.whitespaceBeforeOpenBrace": true, + "powershell.codeFormatting.whitespaceBeforeOpenParen": true, + "powershell.codeFormatting.whitespaceBetweenParameters": true, + "powershell.codeFormatting.whitespaceInsideBrace": true, + "shellcheck.exclude": [ + "SC1090","SC2096" + ], + "shellcheck.customArgs": [ + "-x" + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..da6002d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,42 @@ +// Available variables which can be used inside of strings. +// ${workspaceRoot}: the root folder of the team +// ${file}: the current opened file +// ${relativeFile}: the current opened file relative to workspaceRoot +// ${fileBasename}: the current opened file's basename +// ${fileDirname}: the current opened file's dirname +// ${fileExtname}: the current opened file's extension +// ${cwd}: the current working directory of the spawned process +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + + // Start PowerShell + "windows": { + "command": "${env:windir}/System32/WindowsPowerShell/v1.0/powershell.exe", + //"command": "${env:ProgramFiles}/PowerShell/6.0.0/powershell.exe", + "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass" ] + }, + "linux": { + "command": "/usr/bin/powershell", + "args": [ "-NoProfile" ] + }, + "osx": { + "command": "/usr/local/bin/powershell", + "args": [ "-NoProfile" ] + }, + + // Associate with test task runner + "tasks": [ + { + "taskName": "Test", + "suppressTaskName": true, + "isTestCommand": true, + "args": [ + "Write-Host 'Invoking Pester...'; $ProgressPreference = 'SilentlyContinue'; Invoke-Pester -Script test -PesterOption @{IncludeVSCodeMarker=$true};", + "Invoke-Command { Write-Host 'Completed Test task in task runner.' }" + ], + "problemMatcher": "$pester" + } + ] +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..3a64696 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to make participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when +an individual is representing the project or its community in public spaces. +Examples of representing a project or community include using an official +project e-mail address, posting via an official social media account, or acting +as an appointed representative at an online or offline event. Representation of +a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at opensource@github.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..65ce664 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,67 @@ +# Contributing + +[fork]: https://github.com/actions/runner-images/fork +[pr]: https://github.com//actions/runner-images/compare +[code-of-conduct]: CODE_OF_CONDUCT.md + +Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. + +Contributions 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. + +Please 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. + +## Submitting a pull request + +1. [Fork][fork] and clone the repository. +1. Create a new branch: `git checkout -b my-branch-name`. +1. Make your changes, ensuring that they include steps to install, validate post-install, and update the software report (please see [How to add a new tool](CONTRIBUTING.md#how-to-add-a-new-tool) for details). +1. Test your changes by [creating an image and deploying a VM](docs/create-image-and-azure-resources.md). +1. Push to your fork and [submit a pull request][pr]. + +Here are a few things you can do that will increase the likelihood of your pull request being accepted: + +- 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:. +- Include complete details of why this is needed in the PR description. +- 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. +- Write [good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). +- For new tools: + - Make sure that the tool satisfies the [Software Guidelines](README.md#software-guidelines). + - Create an issue and get approval from us to add this tool to the image before creating the pull request. + +## How to add a new tool + +### General rules + +- For every new tool, add validation scripts and update the software report script to ensure that it is included in the documentation. +- If the tool is available on multiple platforms (macOS, Windows, Linux), make sure you include it on as many as possible. +- 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-2019.json](images/windows/toolsets/toolset-2019.json) as an example. +- Use consistent naming across all files. +- Validation scripts should be simple and shouldn't change the image content. + +### Windows + +- Add a script that will install the tool and put the script in the `scripts/build` folder. +There are a bunch of helper functions that could simplify your code: `Install-ChocoPackage`, `Install-Binary`, `Install-VSIXFromFile`, `Install-VSIXFromUrl`, `Invoke-DownloadWithRetry`, `Test-IsWin19`, `Test-IsWin22` (find the full list of helpers in [ImageHelpers.psm1](images/windows/scripts/helpers/ImageHelpers.psm1)). +- Add a script that will validate the tool installation and put the script in the `scripts/tests` folder. +We use [Pester v5](https://github.com/pester/pester) for validation scripts. If the tests for the tool are complex enough, create a separate `*.Tests.ps1`. Otherwise, use `Tools.Tests.ps1` for simple tests. +Add `Invoke-PesterTests -TestFile [-TestName ]` at the end of the installation script to ensure that your tests will be run. +- 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. [Windows2019-Readme.md](images/windows/Windows2019-Readme.md) and uses [MarkdownPS](https://github.com/Sarafian/MarkdownPS). + +### Ubuntu + +- Add a script that will install and validate the tool and put the script in the `scripts/build` folder. +Use existing scripts such as [github-cli.sh](images/ubuntu/scripts/build/github-cli.sh) as a starting point. + - Use [helpers](images/ubuntu/scripts/helpers/install.sh) to simplify the installation process. + - The validation part should `exit 1` if there is any issue with the installation. +- 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. [Ubuntu2004-Readme.md](images/ubuntu/Ubuntu2004-README.md) and it uses [MarkdownPS](https://github.com/Sarafian/MarkdownPS). + +### macOS + +The 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. +We 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. + +## Resources + +- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) +- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) +- [GitHub Help](https://help.github.com) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..163ff11 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE 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. diff --git a/README.md b/README.md index 16ed5c2..cdc3c3f 100644 --- a/README.md +++ b/README.md @@ -1 +1,216 @@ -# create-win-version \ No newline at end of file + +# GitHub Actions Runner Images + +**Table of Contents** + +- [About](#about) +- [Available Images](#available-images) +- [Announcements](#announcements) +- [Image Definitions](#image-definitions) +- [Image Releases](#image-releases) +- [Software and Image Support](#software-and-image-support) +- [How to Interact with the Repo](#how-to-interact-with-the-repo) +- [FAQs](#faqs) + +## About + +This repository contains the source code used to create the VM images for [GitHub-hosted runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners) used for Actions, as well as for [Microsoft-hosted agents](https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops#use-a-microsoft-hosted-agent) used for Azure Pipelines. +To build a VM machine from this repo's source, see the [instructions](docs/create-image-and-azure-resources.md). + +## Available Images + +| Image | YAML Label | Included Software | Rollout Status of Latest Image Release | +| --------------------|---------------------|--------------------|--------------------| +| Ubuntu 24.04 | `ubuntu-latest` or `ubuntu-24.04` | [ubuntu-24.04] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fubuntu24.json) | +| Ubuntu 22.04 | `ubuntu-22.04` | [ubuntu-22.04] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fubuntu22.json) | +| Ubuntu 20.04 | `ubuntu-20.04` | [ubuntu-20.04] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fubuntu20.json) | +| macOS 15 beta | `macos-15-large`| [macOS-15] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fmacos-15.json) | +| macOS 15 Arm64 beta | `macos-15` or `macos-15-xlarge` | [macOS-15-arm64] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fmacos-15-arm64.json) | +| macOS 14 | `macos-latest-large` or `macos-14-large`| [macOS-14] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fmacos-14.json) | +| macOS 14 Arm64 |`macos-latest`, `macos-14`, `macos-latest-xlarge` or `macos-14-xlarge`| [macOS-14-arm64] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fmacos-14-arm64.json) | +| macOS 13 | `macos-13` or `macos-13-large` | [macOS-13] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fmacos-13.json) | +| macOS 13 Arm64 | `macos-13-xlarge` | [macOS-13-arm64] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fmacos-13-arm64.json) | +| macOS 12 deprecated | `macos-12` or `macos-12-large`| [macOS-12] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fmacos-12.json) | +| Windows Server 2022 | `windows-latest` or `windows-2022` | [windows-2022] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fwin22.json) | +| Windows Server 2019 | `windows-2019` | [windows-2019] | ![Endpoint Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fijunaidm%2F0b5459bbe18604a639e1d2f7accd204f%2Fraw%2Fwin19.json) | + +### Label scheme + +- In general the `-latest` label is used for the latest OS image version that is GA +- Before moving the`-latest` label to a new OS version we will announce the change and give sufficient lead time for users to update their workflows + +[ubuntu-24.04]: https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md +[ubuntu-22.04]: https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md +[ubuntu-20.04]: https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2004-Readme.md +[windows-2022]: https://github.com/actions/runner-images/blob/main/images/windows/Windows2022-Readme.md +[windows-2019]: https://github.com/actions/runner-images/blob/main/images/windows/Windows2019-Readme.md +[macOS-12]: https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md +[macOS-13]: https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md +[macOS-13-arm64]: https://github.com/actions/runner-images/blob/main/images/macos/macos-13-arm64-Readme.md +[macOS-14]: https://github.com/actions/runner-images/blob/main/images/macos/macos-14-Readme.md +[macOS-14-arm64]: https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md +[macOS-15]: https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md +[macOS-15-arm64]: https://github.com/actions/runner-images/blob/main/images/macos/macos-15-arm64-Readme.md +[self-hosted runners]: https://help.github.com/en/actions/hosting-your-own-runners + +## Announcements + +See notable upcoming changes by viewing issues with the [Announcement](https://github.com/actions/runner-images/labels/Announcement) label. + +## Image Definitions + +### Beta + +The purpose of a Beta is to collect feedback on an image before it is released to GA. The goal of a Beta is to identify and fix any potential issues that exist on that +image. Images are updated on a weekly cadence. Any workflows that run on a beta image do not fall under the customer [SLA](https://github.com/customer-terms/github-online-services-sla) in place for Actions. +Customers choosing to use Beta images are encouraged to provide feedback in the runner-images repo by creating an issue. A Beta may take on different availability, i.e. public vs private. + +### GA + +A GA (General Availability) image has been through a Beta period and is deemed ready for general use. Images are updated on a weekly cadence. In order to be moved to +GA the image must meet the following criteria: + +1. Has been through a Beta period (public or private) +2. Most major software we install on the image has a compatible +version for the underlying OS and +3. All major bugs reported during the Beta period have been addressed. + +This image type falls under the customer [SLA](https://github.com/customer-terms/github-online-services-sla) for actions. GA images are eventually deprecated according to our guidelines as we only support the +latest 2 versions of an OS. + +#### Latest Migration Process + +GitHub Actions and Azure DevOps use the `-latest` YAML label (ex: `ubuntu-latest`, `windows-latest`, and `macos-latest`). These labels point towards the newest stable OS version available. + + +The `-latest` migration process is gradual and happens over 1-2 months in order to allow customers to adapt their workflows to the newest OS version. During this process, any workflow using the `-latest` label, may see changes in the OS version in their workflows or pipelines. To avoid unwanted migration, users can specify a specific OS version in the yaml file (ex: macos-14, windows-2022, ubuntu-22.04). + +## Image Releases + +*How to best follow along with changes* + +1. Find the latest releases for this repository [here.](https://github.com/actions/runner-images/releases) +2. Subscribe to the releases coming out of this repository, instructions [here.](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository) +3. Upcoming changes: A pre-release is created when the deployment of an image has started. As soon as the deployment is finished, the pre-release is converted to a release. If you have subscribed to releases, you will get notified of pre-releases as well. + + - You can also track upcoming changes using the [awaiting-deployment](https://github.com/actions/runner-images/labels/awaiting-deployment) label. +4. For high impact changes, we will post these in advance to the GitHub Changelog on our [blog](https://github.blog/changelog/) and on [twitter](https://twitter.com/GHchangelog). + - Ex: breaking changes, GA or deprecation of images + +*Cadence* + +- We typically deploy weekly updates to the software on the runner images. + +## Software and Image Support + +### Support Policy + +- Tools and versions will typically be removed 6 months after they are deprecated or have reached end-of-life +- We support (at maximum) 2 GA images and 1 beta image at a time. We begin the deprecation process of the oldest image label once the newest OS image label has been released to GA. +- The images generally contain the latest versions of packages installed except for Ubuntu LTS where we mostly rely on the Canonical-provided repositories. + +- Popular tools can have several versions installed side-by-side with the following strategy: + +| Tool name | Installation strategy | +|-----------|-----------------------| +| Docker images | not more than 3 latest LTS OS\tool versions. New images or new versions of current images are added using the standard tool request process | +| Java | all LTS versions | +| Node.js | 3 latest LTS versions | +| Go | 3 latest minor versions | +| Python
Ruby | 5 most popular `major.minor` versions | +| PyPy | 3 most popular `major.minor` versions | +| .NET Core | 2 latest LTS versions and 1 latest version. For each feature version only latest patch is installed | +| GCC
GNU Fortran
Clang
GNU C++ | 3 latest major versions | +| Android NDK | 1 latest non-LTS, 2 latest LTS versions | +| Xcode | - all OS compatible versions side-by-side
- for beta, GM versions - latest beta only
- old patch versions are deprecated in 3 months | + +### Package managers usage + +We use third-party package managers to install software during the image generation process. The table below lists the package managers and the software installed. +> **Note**: third-party repositories are re-evaluated every year to identify if they are still useful and secure. + +| Operating system | Package manager | Third-party repos and packages | +| :--- | :---: | ---: | +| Ubuntu | [APT](https://wiki.debian.org/Apt) | [containers](https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable) (Ubuntu 20 only)
[docker](https://download.docker.com/linux/ubuntu) (Ubuntu20 0nly)
[Eclipse-Temurin (Adoptium)](https://packages.adoptium.net/artifactory/deb/)
[Erlang](https://packages.erlang-solutions.com/ubuntu)
[Firefox](http://ppa.launchpad.net/mozillateam/ppa/ubuntu)
[git-lfs](https://packagecloud.io/install/repositories/github/git-lfs)
[git](https://launchpad.net/~git-core/+archive/ubuntu/ppa)
[Google Cloud CLI](https://packages.cloud.google.com/apt)
[Heroku](https://cli-assets.heroku.com/channels/stable/apt)
[HHvm](https://dl.hhvm.com/ubuntu)
[MongoDB](https://repo.mongodb.org/apt/ubuntu)
[Mono](https://download.mono-project.com/repo/ubuntu)
[MS Edge](https://packages.microsoft.com/repos/edge)
[PostgreSQL](https://apt.postgresql.org/pub/repos/apt/)
[R](https://cloud.r-project.org/bin/linux/ubuntu) | +| | [pipx](https://pypa.github.io/pipx) | ansible-core
yamllint | +| Windows | [Chocolatey](https://chocolatey.org) | No third-party repos installed | +| macOS | [Homebrew](https://brew.sh) | [aws-cli v2](https://github.com/aws/homebrew-tap)
[azure/bicep](https://github.com/Azure/homebrew-bicep)
[mongodb/brew](https://github.com/mongodb/homebrew-brew) | +| | [pipx](https://pypa.github.io/pipx/) | yamllint | + +### Image Deprecation Policy + +- Images begin the deprecation process of the oldest image label once a new GA OS version has been released. +- Deprecation process begins with an announcement that sets a date for deprecation +- As it gets closer to the date, GitHub begins doing scheduled brownouts of the image +- During this time there will be an Announcement pinned in the repo to remind users of the deprecation. +- Finally GitHub will deprecate the image and it will no longer be available + +### Preinstallation Policy + +In general, these are the guidelines we follow when deciding what to pre-install on our images: + +- Popularity: widely-used tools and ecosystems will be given priority. +- Latest Technology: recent versions of tools will be given priority. +- Deprecation: end-of-life tools and versions will not be added. +- Licensing: MIT, Apache, or GNU licenses are allowed. +- Time & Space on the Image: we will evaluate how much time is saved and how much space is used by having the tool pre-installed. +- Support: If a tool requires the support of more than one version, we will consider the cost of this maintenance. + +### Default Version Update Policy + +- In general, once a new version is installed on the image, we announce the default version update 2 weeks prior to deploying it. +- For potentially dangerous updates, we may extend the timeline up to 1 month between the announcement and deployment. + +## How to Interact with the Repo + +- **Issues**: To file a bug report, or request tools to be added/updated, please [open an issue using the appropriate template](https://github.com/actions/runner-images/issues/new/choose) +- **Discussions**: If you want to share your thoughts about image configuration, installed software, or bring a new idea, please create a new topic in a [discussion](https://github.com/actions/runner-images/discussions) for a corresponding category. Before making a new discussion please make sure no similar topics were created earlier. +- For general questions about using the runner images or writing your Actions workflow, please open requests in the [GitHub Actions Community Forum](https://github.community/c/github-actions/41). + +## FAQs + +
+ What images are available for GitHub Actions and Azure DevOps? + +The availability of images for GitHub Actions and Azure DevOps is the same. However, deprecation policies may differ. See documentation for more details: +- [GitHub Actions](https://docs.github.com/en/free-pro-team@latest/actions/reference/specifications-for-github-hosted-runners#supported-runners-and-hardware-resources) +- [Azure DevOps](https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml#software) +
+ +
+ What image version is used in my build? + +Usually, image deployment takes 2-3 days, and documentation in the `main` branch is only updated when deployment is finished. To find out which image version and what software versions are used in a specific build, see `Set up job` (GitHub Actions) or `Initialize job` (Azure DevOps) step log. +actions-runner-image +
+ +
+ Looking for other Linux distributions? + +We do not plan to offer other Linux distributions. We recommend using Docker if you'd like to build using other distributions with the hosted runner images. Alternatively, you can leverage [self-hosted runners] and fully customize your VM image to your needs. +
+ +
+ How do I contribute to the macOS source? + +macOS source lives in this repository and is available for everyone. However, macOS image-generation CI doesn't support external contributions yet so we are not able to accept pull-requests for now. + +We are in the process of preparing macOS CI to accept contributions. Until then, we appreciate your patience and ask you to continue to make tool requests by filing issues. +
+ +
+ How does GitHub determine what tools are installed on the images? + +For some tools, we always install the latest at the time of the deployment; for others, we pin the tool to specific version(s). For more details please see the [Preinstallation Policy](#preinstallation-policy) +
+ +
+ How do I request that a new tool be pre-installed on the image? +Please create an issue and get an approval from us to add this tool to the image before creating the pull request. +
+ +
+ What branch should I use to build custom image? +We strongly encourage customers to build their own images using the main branch. +This 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. +
diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..f0b196f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +If you discover a security issue in this repo, please submit it through the [GitHub Security Bug Bounty](https://hackerone.com/github) + +Thanks for helping make GitHub Actions safe for everyone. diff --git a/docs/create-image-and-azure-resources.md b/docs/create-image-and-azure-resources.md new file mode 100644 index 0000000..1433672 --- /dev/null +++ b/docs/create-image-and-azure-resources.md @@ -0,0 +1,311 @@ +# GitHub Actions Runner Images + +The runner-images project uses [Packer](https://www.packer.io/) to generate disk images for Windows 2019/2022 and Ubuntu 20.04/22.04. + +Each image is configured by a HCL2 Packer template that specifies where to build the image (Azure, in this case), +and what steps to run to install software and prepare the disk. + +The Packer process initializes a connection to the Azure subscription using Azure CLI and creates temporary resources +required for the build process: a resource group, network interfaces and a virtual machine from the "clean" image specified in the template. + +If the VM deployment succeeds, Packer connects to it using SSH or WinRM and begins executing installation steps from the template one-by-one. +If any step fails, image generation is aborted, and the temporary VM is terminated. +Packer also attempts to clean up all the temporary resources it created (unless otherwise configured). + +After successful completion of all installation steps, Packer creates a managed image from the temporary VM's disk and deletes the VM. + +- [Build Agent Preparation](#build-agent-preparation) +- [Manual image generation](#manual-image-generation) +- [Manual Image Generation Customization](#manual-image-generation-customization) + - [Network Security](#network-security) + - [Azure Subscription Authentication](#azure-subscription-authentication) +- [Generated Machine Deployment](#generated-machine-deployment) +- [Automated image generation](#automated-image-generation) + - [Required variables](#required-variables) + - [Optional variables](#optional-variables) +- [Builder variables](#builder-variables) +- [Toolset](#toolset) +- [Post-generation scripts](#post-generation-scripts) + - [Running scripts](#running-scripts) + - [Script Details: Ubuntu](#script-details-ubuntu) + - [Script Details: Windows](#script-details-windows) + +## Build Agent Preparation + +The build agent is a machine where the Packer process will be started. +You can use any physical or virtual machine running Windows or Linux OS. +Of course, you may also use an [Azure VM](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/quick-create-cli). +In any case, you will need these software installed: + +- Packer 1.8.2 or higher. + + Download and install it manually from [here](https://www.packer.io/downloads) or use [Chocolatey](https://chocolatey.org/): + + ```powershell + choco install packer + ``` + +- Git. + + For Linux - install the latest version from your distro's package repo. + + For Windows - download and install it from [here](https://gitforwindows.org/) of use [Chocolatey](https://chocolatey.org/): + + ```powershell + choco install git -params '"/GitAndUnixToolsOnPath"' + ``` + +- Powershell 5.0 or higher. + + In Windows you already have it. + + For Linux follow instructions [here](https://learn.microsoft.com/en-us/windows-server/administration/linux-package-repository-for-microsoft-software) + to add Microsoft's Linux Software Repository and then install the `powershell` package. + +- Azure CLI. + + Follow the instructions [here](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli). + Or if you use Windows, you may run this command in Powershell instead: + + ```powershell + Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi + Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet'; rm .\AzureCLI.msi + ``` + +## Manual image generation + +This repository includes a script that assists in generating images in Azure. +All you need is an Azure subscription and a build agent configured as described above. +We suggest starting with building the UbuntuMinimal image because it includes only basic software and builds in less than 30 minutes. + +All the commands below should be executed in PowerShell. + +First, clone the runner-images repository and set the current directory to it: + +```powershell +git clone https://github.com/actions/runner-images.git +Set-Location runner-images +``` + +Then, import the [GenerateResourcesAndImage](../helpers/GenerateResourcesAndImage.ps1) script from the `helpers` subdirectory: + +```powershell +Import-Module .\helpers\GenerateResourcesAndImage.ps1 +``` + +Finally, run the `GenerateResourcesAndImage` function, setting the mandatory arguments: image type and where to build and store the resulting managed image: + +- `SubscriptionId` - your Azure Subscription ID; +- `ResourceGroupName` - the name of the resource group that will be created within your subscription (e.g., "imagegen-test"); +- `AzureLocation` - the location where resources will be created (e.g., "East US"); +- `ImageType` - the type of image to build (we suggest choosing "UbuntuMinimal" here; other valid options are "Windows2019", "Windows2022", "Ubuntu2004", "Ubuntu2204"). + +This function automatically creates all required Azure resources and initiates the Packer image generation for the selected image type. + +When the image is ready, you may proceed to [deployment](#generated-machine-deployment). + +## Manual Image Generation Customization + +The `GenerateResourcesAndImage` function accepts a number of arguments that may assist you in generating an image in your specific environment. + +For example, you may want all the resources involved in the image generation process to be tagged. +In this case, pass a HashTable of tags as a value for the `Tags` parameter. + +If 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`. +You can find more details in the [corresponding section below](#azure-subscription-authentication). + +Use `get-help GenerateResourcesAndImage -Detailed` for the complete list of available parameters. + +### Network Security + +To connect to a temporary virtual machine, Packer uses WinRM or SSH. + +If your build agent is located outside of the Azure subscription where the temporary VM is created, a public network interface and public IP address are used. +Make 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. +Also, if you don't want the temporary VM to be accessible from everywhere, set the `RestrictToAgentIpAddress` parameter value to `$true` +to set up firewall rules allowing access only from your build agent's public IP address. + +If your build agent and temporary VM are in the same subscription, you can configure Packer to connect using a private virtual network. +To achieve this, set proper values for the environment variables `VNET_RESOURCE_GROUP`, `VNET_NAME` and `VNET_SUBNET`. + +### Azure Subscription Authentication + +Packer uses a Service Principal to authenticate in Azure infrastructure. +For more information about Service Principals, refer to the +[Azure documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal). + +The `GenerateResourcesAndImage` function is able to create a Service Principal to be used by Packer. +It uses the Connect-AzAccount cmdlet that invokes an interactive authentication process by default. +If 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 +and provide proper values for the parameters `AzureClientId`, `AzureClientSecret` and `AzureTenantId`. + +Here is an example of how to create a Service Principal using the Az PowerShell module: + +```powershell +$credentials = [Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPasswordCredential]@{ + StartDateTime = Get-Date + EndDateTime = (Get-Date).AddDays(7) +} + +$sp = New-AzADServicePrincipal -DisplayName "imagegen-app" +$appCred = New-AzADAppCredential -ApplicationId $sp.AppId -PasswordCredentials $credentials + +Start-Sleep -Seconds 30 +New-AzRoleAssignment -RoleDefinitionName "Contributor" -PrincipalId $sp.Id +Start-Sleep -Seconds 30 + +@{ + ClientId = $sp.AppId + ClientSecret = $appCred.SecretText + TenantId = (Get-AzSubscription -SubscriptionId $SubscriptionId).TenantId +} +``` + +## Generated Machine Deployment + +After successful image generation, a Virtual Machine can be created from the generated image using the [CreateAzureVMFromPackerTemplate](../helpers/CreateAzureVMFromPackerTemplate.ps1) script. + +```powershell +Import-Module .\helpers\CreateAzureVMFromPackerTemplate.ps1 + +CreateAzureVMFromPackerTemplate -SubscriptionId {YourSubscriptionId} -ResourceGroupName {ResourceGroupName} -ManagedImageName "Runner-Image-Ubuntu2204" -VirtualMachineName "testvm1" -AdminUsername "shady1" -AdminPassword "SomeSecurePassword1" -AzureLocation "eastus" +``` + +Where: + +- `SubscriptionId` - the Azure subscription ID where resources will be created; +- `ResourceGroupName` - the Azure resource group name where the Azure virtual machine will be created; +- `ManagedImageName` - the name of the managed image to be used for the virtual machine creation; +- `VirtualMachineName` - the name of the virtual machine to be generated; +- `AdminUserName` - the administrator username for the virtual machine to be created; +- `AdminPassword` - the administrator password for the virtual machine to be created; +- `AzureLocation` - the location where the Azure virtual machine will be provisioned (e.g., "eastus"). + +This function creates an Azure VM and generates network resources in Azure to make the VM accessible. + +## Automated image generation + +If you want to generate images automatically (e.g., as a part of a CI/CD pipeline), +you can use Packer directly. To do this, you will need: + +- a build agent configured as described in the + [Build agent preparation](#build-agent-preparation) section; +- an Azure subscription and Service Principal configured as described in the + [Azure subscription authentication](#azure-subscription-authentication) section; +- a resource group created in your Azure subscription where the managed image will be stored; +- a string to be used as a password for the user used to install software (Windows only). + +Then, you can invoke Packer in your CI/CD pipeline using the following command: + +```powershell +packer build -var "subscription_id=$SubscriptionId" ` + -var "client_id=$ClientId" ` + -var "client_secret=$ClientSecret" ` + -var "install_password=$InstallPassword" ` + -var "location=$Location" ` + -var "managed_image_name=$ImageName" ` + -var "managed_image_resource_group_name=$ImageResourceGroupName" ` + -var "tenant_id=$TenantId" ` + $TemplatePath +``` + +Where: + +- `SubscriptionId` - your Azure Subscription ID; +- `ClientId` and `ClientSecret` - Service Principal credentials; +- `TenantId` - Azure Tenant ID; +- `InstallPassword` - password for the user used to install software (Windows only); +- `Location` - location where resources will be created (e.g., "East US"); +- `ImageName` and `ImageResourceGroupName` - name of the resource group where the managed image will be stored; +- `TemplatePath` - path to the Packer template file (e.g., "images/windows/templates/windows-2022.pkr.hcl"). + +### Required variables + +The following variables are required to be passed to the Packer process: + +| Template var | Env var | Description +| ------------ | ------- | ----------- +| `subscription_id` | `ARM_SUBSCRIPTION_ID` | The subscription under which the build will be performed. +| `client_id` | `ARM_CLIENT_ID` | The Active Directory service principal associated with your builder. +| `client_secret` | `ARM_CLIENT_SECRET` | The password or secret for your service principal; may be omitted if `client_cert_path` is set. +| `client_cert_path` | `ARM_CLIENT_CERT_PATH` | The location of a PEM file containing a certificate and private key for the service principal; may be omitted if `client_secret` is set. +| `location` | `ARM_RESOURCE_LOCATION` | The Azure datacenter in which your VM will be built. +| `managed_image_resource_group_name` | `ARM_RESOURCE_GROUP` | The resource group under which the final artifact will be stored. + +### Optional variables + +The following variables are optional: + +- `managed_image_name` - the name of the managed image to create. If not specified, "Runner-Image-{{ImageType}}" will be used; +- `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; +- `object_id` - the object ID for the AAD SP; will be derived from the oAuth token if empty; +- `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`; +- `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; +- `private_virtual_network_with_public_ip` - this value allows you to set a `virtual_network_name` and obtain a public IP; if this value is not set and `virtual_network_name` is defined, Packer is only allowed to be executed from a host on the same subnet / virtual network; +- `virtual_network_name` - use a pre-existing virtual network for the VM; this option enables private communication with the VM, no public IP address is used or provisioned (unless you set `private_virtual_network_with_public_ip`); +- `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; +- `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. + +## Builder variables + +The `builders` section contains variables for the `azure-arm` builder used in the project. Most of the builder variables are inherited from the `user variables` section, however, the variables can be overwritten to adjust image-generation performance. + +- `vm_size` - the size of the VM used for building; this can be changed when you deploy a VM from your image; +- `image_os` - the type of OS that will be deployed as a temporary VM; +- `image_version` - specify the version of an OS to boot from. + +**Detailed Azure builders documentation can be found in the [packer documentation](https://www.packer.io/docs/builders/azure).** + +## Toolset + +The 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. + +Generated tool versions and details can be found in related projects: + +- [Python](https://github.com/actions/python-versions/) +- [Go](https://github.com/actions/go-versions) +- [Node](https://github.com/actions/node-versions) + +## Post-generation scripts + +> :warning: These scripts are intended to be run on a VM deployed in Azure + +The 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: + +- Windows: +- Linux: + +**Note:** The default user for Linux should have `sudo privileges`. + +The scripts are copied to the image during the generation process to the following paths: + +- Windows: `C:\post-generation` +- Linux: `/opt/post-generation` + +### Running scripts + +- Ubuntu + + ```bash + sudo su -c "find /opt/post-generation -mindepth 1 -maxdepth 1 -type f -name '*.sh' -exec bash {} \;" + ``` + +- Windows + + ```powershell + Get-ChildItem C:\post-generation -Filter *.ps1 | ForEach-Object { & $_.FullName } + ``` + +### Script Details: Ubuntu + +- **cleanup-logs.sh** - removes all build process logs from the machine; +- **environment-variables.sh** - replaces `$HOME` with the default user's home directory for environment variables related to the default user home directory; +- **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; +- **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). + +### Script Details: Windows + +- **GenerateIISExpressCertificate.ps1** - generates and imports a certificate to run applications with IIS Express through HTTPS; +- **InternetExplorerConfiguration.ps1** - turns off the Internet Explorer Enhanced Security feature; +- **Msys2FirstLaunch.ps1** - initializes the bash user profile in MSYS2; +- **VSConfiguration.ps1** - performs initial Visual Studio configuration. diff --git a/git_old/COMMIT_EDITMSG b/git_old/COMMIT_EDITMSG new file mode 100644 index 0000000..1b93394 --- /dev/null +++ b/git_old/COMMIT_EDITMSG @@ -0,0 +1 @@ +chore: Add test for mongo shell diff --git a/git_old/FETCH_HEAD b/git_old/FETCH_HEAD new file mode 100644 index 0000000..0a7b370 --- /dev/null +++ b/git_old/FETCH_HEAD @@ -0,0 +1,5 @@ +64d35b3c9f9647a1e1c983c789efb821cf19c09f branch 'windows-25' of https://github.com/subir0071/runner-images-kishore +812cdcdce8ac2ac45125eb02b40a6e8ae9739bbb not-for-merge branch 'az-cli' of https://github.com/subir0071/runner-images-kishore +4a0b4ab1fb88e3c87565bc645a2837593612d56f not-for-merge branch 'main' of https://github.com/subir0071/runner-images-kishore +b2bf917d661e3e1f48a3ca9414ebd3c8dcef2513 not-for-merge branch 'rust-cargo' of https://github.com/subir0071/runner-images-kishore +6c68f398472e6efc44975c55ff57e8d917d77e11 not-for-merge branch 'vs-22-sign' of https://github.com/subir0071/runner-images-kishore diff --git a/git_old/HEAD b/git_old/HEAD new file mode 100644 index 0000000..a24be5e --- /dev/null +++ b/git_old/HEAD @@ -0,0 +1 @@ +ref: refs/heads/windows-25 diff --git a/git_old/ORIG_HEAD b/git_old/ORIG_HEAD new file mode 100644 index 0000000..259ab96 --- /dev/null +++ b/git_old/ORIG_HEAD @@ -0,0 +1 @@ +64d35b3c9f9647a1e1c983c789efb821cf19c09f diff --git a/git_old/config b/git_old/config new file mode 100644 index 0000000..b115647 --- /dev/null +++ b/git_old/config @@ -0,0 +1,18 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true +[remote "origin"] + url = https://github.com/subir0071/runner-images-kishore.git + fetch = +refs/heads/*:refs/remotes/origin/* +[branch "main"] + remote = origin + merge = refs/heads/main + vscode-merge-base = origin/main +[branch "windows-25"] + remote = origin + merge = refs/heads/windows-25 + vscode-merge-base = origin/windows-25 diff --git a/git_old/description b/git_old/description new file mode 100644 index 0000000..498b267 --- /dev/null +++ b/git_old/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/git_old/hooks/applypatch-msg.sample b/git_old/hooks/applypatch-msg.sample new file mode 100755 index 0000000..a5d7b84 --- /dev/null +++ b/git_old/hooks/applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +commitmsg="$(git rev-parse --git-path hooks/commit-msg)" +test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} +: diff --git a/git_old/hooks/commit-msg.sample b/git_old/hooks/commit-msg.sample new file mode 100755 index 0000000..b58d118 --- /dev/null +++ b/git_old/hooks/commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/git_old/hooks/fsmonitor-watchman.sample b/git_old/hooks/fsmonitor-watchman.sample new file mode 100755 index 0000000..23e856f --- /dev/null +++ b/git_old/hooks/fsmonitor-watchman.sample @@ -0,0 +1,174 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + +# An example hook script to integrate Watchman +# (https://facebook.github.io/watchman/) with git to speed up detecting +# new and modified files. +# +# The hook is passed a version (currently 2) and last update token +# formatted as a string and outputs to stdout a new update token and +# all files that have been modified since the update token. Paths must +# be relative to the root of the working tree and separated by a single NUL. +# +# To enable this hook, rename this file to "query-watchman" and set +# 'git config core.fsmonitor .git/hooks/query-watchman' +# +my ($version, $last_update_token) = @ARGV; + +# Uncomment for debugging +# print STDERR "$0 $version $last_update_token\n"; + +# Check the hook interface version +if ($version ne 2) { + die "Unsupported query-fsmonitor hook version '$version'.\n" . + "Falling back to scanning...\n"; +} + +my $git_work_tree = get_working_dir(); + +my $retry = 1; + +my $json_pkg; +eval { + require JSON::XS; + $json_pkg = "JSON::XS"; + 1; +} or do { + require JSON::PP; + $json_pkg = "JSON::PP"; +}; + +launch_watchman(); + +sub launch_watchman { + my $o = watchman_query(); + if (is_work_tree_watched($o)) { + output_result($o->{clock}, @{$o->{files}}); + } +} + +sub output_result { + my ($clockid, @files) = @_; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # binmode $fh, ":utf8"; + # print $fh "$clockid\n@files\n"; + # close $fh; + + binmode STDOUT, ":utf8"; + print $clockid; + print "\0"; + local $, = "\0"; + print @files; +} + +sub watchman_clock { + my $response = qx/watchman clock "$git_work_tree"/; + die "Failed to get clock id on '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + + return $json_pkg->new->utf8->decode($response); +} + +sub watchman_query { + my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') + or die "open2() failed: $!\n" . + "Falling back to scanning...\n"; + + # In the query expression below we're asking for names of files that + # changed since $last_update_token but not from the .git folder. + # + # To accomplish this, we're using the "since" generator to use the + # recency index to select candidate nodes and "fields" to limit the + # output to file names only. Then we're using the "expression" term to + # further constrain the results. + my $last_update_line = ""; + if (substr($last_update_token, 0, 1) eq "c") { + $last_update_token = "\"$last_update_token\""; + $last_update_line = qq[\n"since": $last_update_token,]; + } + my $query = <<" END"; + ["query", "$git_work_tree", {$last_update_line + "fields": ["name"], + "expression": ["not", ["dirname", ".git"]] + }] + END + + # Uncomment for debugging the watchman query + # open (my $fh, ">", ".git/watchman-query.json"); + # print $fh $query; + # close $fh; + + print CHLD_IN $query; + close CHLD_IN; + my $response = do {local $/; }; + + # Uncomment for debugging the watch response + # open ($fh, ">", ".git/watchman-response.json"); + # print $fh $response; + # close $fh; + + die "Watchman: command returned no output.\n" . + "Falling back to scanning...\n" if $response eq ""; + die "Watchman: command returned invalid output: $response\n" . + "Falling back to scanning...\n" unless $response =~ /^\{/; + + return $json_pkg->new->utf8->decode($response); +} + +sub is_work_tree_watched { + my ($output) = @_; + my $error = $output->{error}; + if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) { + $retry--; + my $response = qx/watchman watch "$git_work_tree"/; + die "Failed to make watchman watch '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + $output = $json_pkg->new->utf8->decode($response); + $error = $output->{error}; + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # close $fh; + + # Watchman will always return all files on the first query so + # return the fast "everything is dirty" flag to git and do the + # Watchman query just to get it over with now so we won't pay + # the cost in git to look up each individual file. + my $o = watchman_clock(); + $error = $output->{error}; + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + output_result($o->{clock}, ("/")); + $last_update_token = $o->{clock}; + + eval { launch_watchman() }; + return 0; + } + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + return 1; +} + +sub get_working_dir { + my $working_dir; + if ($^O =~ 'msys' || $^O =~ 'cygwin') { + $working_dir = Win32::GetCwd(); + $working_dir =~ tr/\\/\//; + } else { + require Cwd; + $working_dir = Cwd::cwd(); + } + + return $working_dir; +} diff --git a/git_old/hooks/post-update.sample b/git_old/hooks/post-update.sample new file mode 100755 index 0000000..ec17ec1 --- /dev/null +++ b/git_old/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info diff --git a/git_old/hooks/pre-applypatch.sample b/git_old/hooks/pre-applypatch.sample new file mode 100755 index 0000000..4142082 --- /dev/null +++ b/git_old/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +precommit="$(git rev-parse --git-path hooks/pre-commit)" +test -x "$precommit" && exec "$precommit" ${1+"$@"} +: diff --git a/git_old/hooks/pre-commit.sample b/git_old/hooks/pre-commit.sample new file mode 100755 index 0000000..e144712 --- /dev/null +++ b/git_old/hooks/pre-commit.sample @@ -0,0 +1,49 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=$(git hash-object -t tree /dev/null) +fi + +# If you want to allow non-ASCII filenames set this variable to true. +allownonascii=$(git config --type=bool hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ASCII filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against -- diff --git a/git_old/hooks/pre-merge-commit.sample b/git_old/hooks/pre-merge-commit.sample new file mode 100755 index 0000000..399eab1 --- /dev/null +++ b/git_old/hooks/pre-merge-commit.sample @@ -0,0 +1,13 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git merge" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message to +# stderr if it wants to stop the merge commit. +# +# To enable this hook, rename this file to "pre-merge-commit". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" +: diff --git a/git_old/hooks/pre-push.sample b/git_old/hooks/pre-push.sample new file mode 100755 index 0000000..4ce688d --- /dev/null +++ b/git_old/hooks/pre-push.sample @@ -0,0 +1,53 @@ +#!/bin/sh + +# An example hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# +# +# This sample shows how to prevent push of commits where the log message starts +# with "WIP" (work in progress). + +remote="$1" +url="$2" + +zero=$(git hash-object --stdin &2 "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/git_old/hooks/pre-rebase.sample b/git_old/hooks/pre-rebase.sample new file mode 100755 index 0000000..6cbef5c --- /dev/null +++ b/git_old/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up to date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +<<\DOC_END + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". + +DOC_END diff --git a/git_old/hooks/pre-receive.sample b/git_old/hooks/pre-receive.sample new file mode 100755 index 0000000..a1fd29e --- /dev/null +++ b/git_old/hooks/pre-receive.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to make use of push options. +# The example simply echoes all push options that start with 'echoback=' +# and rejects all pushes when the "reject" push option is used. +# +# To enable this hook, rename this file to "pre-receive". + +if test -n "$GIT_PUSH_OPTION_COUNT" +then + i=0 + while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" + do + eval "value=\$GIT_PUSH_OPTION_$i" + case "$value" in + echoback=*) + echo "echo from the pre-receive-hook: ${value#*=}" >&2 + ;; + reject) + exit 1 + esac + i=$((i + 1)) + done +fi diff --git a/git_old/hooks/prepare-commit-msg.sample b/git_old/hooks/prepare-commit-msg.sample new file mode 100755 index 0000000..10fa14c --- /dev/null +++ b/git_old/hooks/prepare-commit-msg.sample @@ -0,0 +1,42 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first one removes the +# "# Please enter the commit message..." help message. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +COMMIT_MSG_FILE=$1 +COMMIT_SOURCE=$2 +SHA1=$3 + +/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" + +# case "$COMMIT_SOURCE,$SHA1" in +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; +# *) ;; +# esac + +# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" +# if test -z "$COMMIT_SOURCE" +# then +# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" +# fi diff --git a/git_old/hooks/push-to-checkout.sample b/git_old/hooks/push-to-checkout.sample new file mode 100755 index 0000000..af5a0c0 --- /dev/null +++ b/git_old/hooks/push-to-checkout.sample @@ -0,0 +1,78 @@ +#!/bin/sh + +# An example hook script to update a checked-out tree on a git push. +# +# This hook is invoked by git-receive-pack(1) when it reacts to git +# push and updates reference(s) in its repository, and when the push +# tries to update the branch that is currently checked out and the +# receive.denyCurrentBranch configuration variable is set to +# updateInstead. +# +# By default, such a push is refused if the working tree and the index +# of the remote repository has any difference from the currently +# checked out commit; when both the working tree and the index match +# the current commit, they are updated to match the newly pushed tip +# of the branch. This hook is to be used to override the default +# behaviour; however the code below reimplements the default behaviour +# as a starting point for convenient modification. +# +# The hook receives the commit with which the tip of the current +# branch is going to be updated: +commit=$1 + +# It can exit with a non-zero status to refuse the push (when it does +# so, it must not modify the index or the working tree). +die () { + echo >&2 "$*" + exit 1 +} + +# Or it can make any necessary changes to the working tree and to the +# index to bring them to the desired state when the tip of the current +# branch is updated to the new commit, and exit with a zero status. +# +# For example, the hook can simply run git read-tree -u -m HEAD "$1" +# in order to emulate git fetch that is run in the reverse direction +# with git push, as the two-tree form of git read-tree -u -m is +# essentially the same as git switch or git checkout that switches +# branches while keeping the local changes in the working tree that do +# not interfere with the difference between the branches. + +# The below is a more-or-less exact translation to shell of the C code +# for the default behaviour for git's push-to-checkout hook defined in +# the push_to_deploy() function in builtin/receive-pack.c. +# +# Note that the hook will be executed from the repository directory, +# not from the working tree, so if you want to perform operations on +# the working tree, you will have to adapt your code accordingly, e.g. +# by adding "cd .." or using relative paths. + +if ! git update-index -q --ignore-submodules --refresh +then + die "Up-to-date check failed" +fi + +if ! git diff-files --quiet --ignore-submodules -- +then + die "Working directory has unstaged changes" +fi + +# This is a rough translation of: +# +# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX +if git cat-file -e HEAD 2>/dev/null +then + head=HEAD +else + head=$(git hash-object -t tree --stdin &2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --type=bool hooks.allowunannotated) +allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch) +denycreatebranch=$(git config --type=bool hooks.denycreatebranch) +allowdeletetag=$(git config --type=bool hooks.allowdeletetag) +allowmodifytag=$(git config --type=bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero=$(git hash-object --stdin &2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/git_old/index b/git_old/index new file mode 100644 index 0000000..c8871cc Binary files /dev/null and b/git_old/index differ diff --git a/git_old/info/exclude b/git_old/info/exclude new file mode 100644 index 0000000..a5196d1 --- /dev/null +++ b/git_old/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/git_old/objects/00/0ac5f4d94bc83bc9f500d5f1e2436414a18354 b/git_old/objects/00/0ac5f4d94bc83bc9f500d5f1e2436414a18354 new file mode 100644 index 0000000..d3d74b9 Binary files /dev/null and b/git_old/objects/00/0ac5f4d94bc83bc9f500d5f1e2436414a18354 differ diff --git a/git_old/objects/00/849b3a6bf1e211739df2ce0d4879efdce7c02d b/git_old/objects/00/849b3a6bf1e211739df2ce0d4879efdce7c02d new file mode 100644 index 0000000..458a68e Binary files /dev/null and b/git_old/objects/00/849b3a6bf1e211739df2ce0d4879efdce7c02d differ diff --git a/git_old/objects/00/8de27762faeab37102cc404b737c36147b4e62 b/git_old/objects/00/8de27762faeab37102cc404b737c36147b4e62 new file mode 100644 index 0000000..bea99d9 Binary files /dev/null and b/git_old/objects/00/8de27762faeab37102cc404b737c36147b4e62 differ diff --git a/git_old/objects/00/a28240a739a1fcface998a16bebc4f80064e1e b/git_old/objects/00/a28240a739a1fcface998a16bebc4f80064e1e new file mode 100644 index 0000000..cf01cae Binary files /dev/null and b/git_old/objects/00/a28240a739a1fcface998a16bebc4f80064e1e differ diff --git a/git_old/objects/00/d1bf2a7e71c65b9a90bc79eb37df7cda7c722f b/git_old/objects/00/d1bf2a7e71c65b9a90bc79eb37df7cda7c722f new file mode 100644 index 0000000..9d6a46b Binary files /dev/null and b/git_old/objects/00/d1bf2a7e71c65b9a90bc79eb37df7cda7c722f differ diff --git a/git_old/objects/01/15b475b6fa540b4b3d6d28704e26c6ad4f32fe b/git_old/objects/01/15b475b6fa540b4b3d6d28704e26c6ad4f32fe new file mode 100644 index 0000000..0bec5d9 Binary files /dev/null and b/git_old/objects/01/15b475b6fa540b4b3d6d28704e26c6ad4f32fe differ diff --git a/git_old/objects/01/d26c99da2520875581ff4fc1c937bb8f91b9b8 b/git_old/objects/01/d26c99da2520875581ff4fc1c937bb8f91b9b8 new file mode 100644 index 0000000..a59daa2 Binary files /dev/null and b/git_old/objects/01/d26c99da2520875581ff4fc1c937bb8f91b9b8 differ diff --git a/git_old/objects/03/3b658ce587cdaf28654411a61b88289fe06650 b/git_old/objects/03/3b658ce587cdaf28654411a61b88289fe06650 new file mode 100644 index 0000000..ff2b0d2 Binary files /dev/null and b/git_old/objects/03/3b658ce587cdaf28654411a61b88289fe06650 differ diff --git a/git_old/objects/03/52771bcc056fb7cdf5b7eb21707add5fdc09a3 b/git_old/objects/03/52771bcc056fb7cdf5b7eb21707add5fdc09a3 new file mode 100644 index 0000000..8ee58ea --- /dev/null +++ b/git_old/objects/03/52771bcc056fb7cdf5b7eb21707add5fdc09a3 @@ -0,0 +1,3 @@ +xn0짘v(I%4j6I Ty]iO=unI47Ese|= m]Zf;{ +_%FՈ}Ļ|{E:d;t{~vѧCSqIS{ ${Zvf|$sI퐧iS1E!y8?mh. ?u 0 p \ No newline at end of file diff --git a/git_old/objects/05/4530702eb9bf9ea4c9d1f320c0478806527277 b/git_old/objects/05/4530702eb9bf9ea4c9d1f320c0478806527277 new file mode 100644 index 0000000..bb3ad27 Binary files /dev/null and b/git_old/objects/05/4530702eb9bf9ea4c9d1f320c0478806527277 differ diff --git a/git_old/objects/06/6af0a14eb1d7d1c170bd4abb25785844f2ba0f b/git_old/objects/06/6af0a14eb1d7d1c170bd4abb25785844f2ba0f new file mode 100644 index 0000000..545d442 Binary files /dev/null and b/git_old/objects/06/6af0a14eb1d7d1c170bd4abb25785844f2ba0f differ diff --git a/git_old/objects/06/9100fb22f5c1f78e05e4e4bb874da34fa955ce b/git_old/objects/06/9100fb22f5c1f78e05e4e4bb874da34fa955ce new file mode 100644 index 0000000..d805b6a --- /dev/null +++ b/git_old/objects/06/9100fb22f5c1f78e05e4e4bb874da34fa955ce @@ -0,0 +1,3 @@ +xMO@=xvY]0iRVko,R +_oē'6O2̈AVJ0$)cP]O 2J-`ijRD3b2sS#† 4NbR3p)d]-h.`]|. +TNcNԱKgM d;!X7G,~l<} WG=}6=7z2x߇9XxѨosյ~vx:.>Zb`;Q3,RnY܊UɶZFʳż;N!n~8_[8-/ Ul-ěZБ \ No newline at end of file diff --git a/git_old/objects/06/f35f6714c74556bfa4776f3d72ffd217e661bb b/git_old/objects/06/f35f6714c74556bfa4776f3d72ffd217e661bb new file mode 100644 index 0000000..752946c Binary files /dev/null and b/git_old/objects/06/f35f6714c74556bfa4776f3d72ffd217e661bb differ diff --git a/git_old/objects/07/9814107558cdf7490b3192746899af54f70d94 b/git_old/objects/07/9814107558cdf7490b3192746899af54f70d94 new file mode 100644 index 0000000..e283271 Binary files /dev/null and b/git_old/objects/07/9814107558cdf7490b3192746899af54f70d94 differ diff --git a/git_old/objects/07/afa0538215241be5b7590a304ec9cd89237b02 b/git_old/objects/07/afa0538215241be5b7590a304ec9cd89237b02 new file mode 100644 index 0000000..88aed9b Binary files /dev/null and b/git_old/objects/07/afa0538215241be5b7590a304ec9cd89237b02 differ diff --git a/git_old/objects/08/3fd9bbaa6c6b9b16648d1e9ad4336dfc776fef b/git_old/objects/08/3fd9bbaa6c6b9b16648d1e9ad4336dfc776fef new file mode 100644 index 0000000..641b5e5 --- /dev/null +++ b/git_old/objects/08/3fd9bbaa6c6b9b16648d1e9ad4336dfc776fef @@ -0,0 +1,2 @@ +xIo0{x II\Ȳdэ)"U>v +Sm>`ynPoP\⑔U@jKќ1JlAX+mb- !g*̨G,B2i{!kڡY`ɯfL S)8!B%f=euW%i6B0dx_7^qz'yR_֛vVQ,+:wy9~|x-?og_8eƲ(V#ڽ?4ESم'D,̮9N@7}b2 _O:jg"]LB,gttoeI |rޛ==VT}ge2}eƫqF ;[#_<&%&fa8(k~ޕU \ No newline at end of file diff --git a/git_old/objects/09/2b231ae81db39da85274d09b27c3372e2683e8 b/git_old/objects/09/2b231ae81db39da85274d09b27c3372e2683e8 new file mode 100644 index 0000000..14c668c Binary files /dev/null and b/git_old/objects/09/2b231ae81db39da85274d09b27c3372e2683e8 differ diff --git a/git_old/objects/09/a1085f0dc31e9c22458f6940af40200eeaec6c b/git_old/objects/09/a1085f0dc31e9c22458f6940af40200eeaec6c new file mode 100644 index 0000000..df8320e Binary files /dev/null and b/git_old/objects/09/a1085f0dc31e9c22458f6940af40200eeaec6c differ diff --git a/git_old/objects/0a/cc84e28890fcc25e433011e8c78ead5e328782 b/git_old/objects/0a/cc84e28890fcc25e433011e8c78ead5e328782 new file mode 100644 index 0000000..92ac7f3 Binary files /dev/null and b/git_old/objects/0a/cc84e28890fcc25e433011e8c78ead5e328782 differ diff --git a/git_old/objects/0a/d7cd969cd7df5237b4107569f7aee762dd260d b/git_old/objects/0a/d7cd969cd7df5237b4107569f7aee762dd260d new file mode 100644 index 0000000..82f8156 --- /dev/null +++ b/git_old/objects/0a/d7cd969cd7df5237b4107569f7aee762dd260d @@ -0,0 +1,4 @@ +xr0Ȯ[I*cc @08}rMR@$v- fsLH.I+)t5ZZy^z RTp*99ʵD|릃d(Laj>\ƜUgopmBE +;3={: +.x +p8ztsL~|v,KܕVbY4xO߼v{,VYvOi;; h1&I_ZvYtrcg=R)7FB]YǍxFFi韯\ ./z3/02D \ No newline at end of file diff --git a/git_old/objects/0d/37e5090641f883a305355b0933666156d458ab b/git_old/objects/0d/37e5090641f883a305355b0933666156d458ab new file mode 100644 index 0000000..5d424b5 Binary files /dev/null and b/git_old/objects/0d/37e5090641f883a305355b0933666156d458ab differ diff --git a/git_old/objects/0d/41a12758aedc2c2a5e0785f1d55af4190bc542 b/git_old/objects/0d/41a12758aedc2c2a5e0785f1d55af4190bc542 new file mode 100644 index 0000000..88b1a2d Binary files /dev/null and b/git_old/objects/0d/41a12758aedc2c2a5e0785f1d55af4190bc542 differ diff --git a/git_old/objects/0d/b832a3f634665e4f8f34c5727e3c6cbb21eacc b/git_old/objects/0d/b832a3f634665e4f8f34c5727e3c6cbb21eacc new file mode 100644 index 0000000..b8bc811 Binary files /dev/null and b/git_old/objects/0d/b832a3f634665e4f8f34c5727e3c6cbb21eacc differ diff --git a/git_old/objects/0f/a070c4a0b87648b75bd6f3b9398015b6bad343 b/git_old/objects/0f/a070c4a0b87648b75bd6f3b9398015b6bad343 new file mode 100644 index 0000000..a40bfd4 Binary files /dev/null and b/git_old/objects/0f/a070c4a0b87648b75bd6f3b9398015b6bad343 differ diff --git a/git_old/objects/0f/f3593af99309af8b9aebd6538727dd9930ce39 b/git_old/objects/0f/f3593af99309af8b9aebd6538727dd9930ce39 new file mode 100644 index 0000000..1f2937a Binary files /dev/null and b/git_old/objects/0f/f3593af99309af8b9aebd6538727dd9930ce39 differ diff --git a/git_old/objects/10/a46649a9365a94a58299f43562a514d8f1bd6b b/git_old/objects/10/a46649a9365a94a58299f43562a514d8f1bd6b new file mode 100644 index 0000000..f7cfef6 Binary files /dev/null and b/git_old/objects/10/a46649a9365a94a58299f43562a514d8f1bd6b differ diff --git a/git_old/objects/10/a8fd79d42c99d9ec690b1b198baece5f2ae069 b/git_old/objects/10/a8fd79d42c99d9ec690b1b198baece5f2ae069 new file mode 100644 index 0000000..d961c48 Binary files /dev/null and b/git_old/objects/10/a8fd79d42c99d9ec690b1b198baece5f2ae069 differ diff --git a/git_old/objects/12/093dbe1a6e0cb83359476aea062c0e75652896 b/git_old/objects/12/093dbe1a6e0cb83359476aea062c0e75652896 new file mode 100644 index 0000000..67c29c5 --- /dev/null +++ b/git_old/objects/12/093dbe1a6e0cb83359476aea062c0e75652896 @@ -0,0 +1,2 @@ +xKo0jZ<*(3̈́ fl2@ +f/J]uջt{Ωk ؜3k.(lf3J*ΉdL*!K)zF/BdY Dbiӊ:WJUiY#D05i3EŇz>a{cv1F ]~4EOea!ȷQ?Y#@n_o5~ܦy|2:['4onbyz\7/GL=v䔛os.S|[dt.~t}" L}_[cwc\w?n8\[jPU{l{ FN” \ No newline at end of file diff --git a/git_old/objects/12/6a3d1b4d0848191a51d0313d12761fe0f6f118 b/git_old/objects/12/6a3d1b4d0848191a51d0313d12761fe0f6f118 new file mode 100644 index 0000000..f6db67e Binary files /dev/null and b/git_old/objects/12/6a3d1b4d0848191a51d0313d12761fe0f6f118 differ diff --git a/git_old/objects/12/d107ac2b9156f188398d9e12fe777c5738af72 b/git_old/objects/12/d107ac2b9156f188398d9e12fe777c5738af72 new file mode 100644 index 0000000..9540d53 Binary files /dev/null and b/git_old/objects/12/d107ac2b9156f188398d9e12fe777c5738af72 differ diff --git a/git_old/objects/13/2f51b2bfaa09a4f884a8bd0b19917e814b70c2 b/git_old/objects/13/2f51b2bfaa09a4f884a8bd0b19917e814b70c2 new file mode 100644 index 0000000..039e5b9 Binary files /dev/null and b/git_old/objects/13/2f51b2bfaa09a4f884a8bd0b19917e814b70c2 differ diff --git a/git_old/objects/13/5eeb84ffd6bf80bac6139774d54f0277258738 b/git_old/objects/13/5eeb84ffd6bf80bac6139774d54f0277258738 new file mode 100644 index 0000000..72f2de3 Binary files /dev/null and b/git_old/objects/13/5eeb84ffd6bf80bac6139774d54f0277258738 differ diff --git a/git_old/objects/13/8eb26c9e897422fcb5d7e01240d0680d0d07b6 b/git_old/objects/13/8eb26c9e897422fcb5d7e01240d0680d0d07b6 new file mode 100644 index 0000000..b323f04 Binary files /dev/null and b/git_old/objects/13/8eb26c9e897422fcb5d7e01240d0680d0d07b6 differ diff --git a/git_old/objects/14/e9dbf69db3756df8e9cffd75fdb842b7896007 b/git_old/objects/14/e9dbf69db3756df8e9cffd75fdb842b7896007 new file mode 100644 index 0000000..13edbbf Binary files /dev/null and b/git_old/objects/14/e9dbf69db3756df8e9cffd75fdb842b7896007 differ diff --git a/git_old/objects/16/054996a9c570072492e85ccd6ba83464d0d90b b/git_old/objects/16/054996a9c570072492e85ccd6ba83464d0d90b new file mode 100644 index 0000000..af0965f Binary files /dev/null and b/git_old/objects/16/054996a9c570072492e85ccd6ba83464d0d90b differ diff --git a/git_old/objects/16/0b36c81da356db7ba7145e6b4ed58d28726791 b/git_old/objects/16/0b36c81da356db7ba7145e6b4ed58d28726791 new file mode 100644 index 0000000..6c06fb1 Binary files /dev/null and b/git_old/objects/16/0b36c81da356db7ba7145e6b4ed58d28726791 differ diff --git a/git_old/objects/16/b10b16ac41a643d96d45b16c2845f927d0973a b/git_old/objects/16/b10b16ac41a643d96d45b16c2845f927d0973a new file mode 100644 index 0000000..8ab0dd3 --- /dev/null +++ b/git_old/objects/16/b10b16ac41a643d96d45b16c2845f927d0973a @@ -0,0 +1 @@ +xKo@_qQP5U!ƵԱ 0 nݝO:9VT[pCx<b P%9BޫB0WP#}§2"|Ǯruc7p"lj{;}0@yRRrVۼ6p{8Y,s,6P.ymM~stDU(M53a(geyJ487jrr~Nk-K}6L f}J8hH^>ilvi 뚁ї \ No newline at end of file diff --git a/git_old/objects/17/71908c1840e2c53af1e2862c3fb91692866038 b/git_old/objects/17/71908c1840e2c53af1e2862c3fb91692866038 new file mode 100644 index 0000000..09ca5e4 Binary files /dev/null and b/git_old/objects/17/71908c1840e2c53af1e2862c3fb91692866038 differ diff --git a/git_old/objects/17/c2708dcd939f6abd99bdff2387a18089944cc2 b/git_old/objects/17/c2708dcd939f6abd99bdff2387a18089944cc2 new file mode 100644 index 0000000..61916f5 Binary files /dev/null and b/git_old/objects/17/c2708dcd939f6abd99bdff2387a18089944cc2 differ diff --git a/git_old/objects/18/f3d058470aaebe9f9de4e2f0e974e98e797376 b/git_old/objects/18/f3d058470aaebe9f9de4e2f0e974e98e797376 new file mode 100644 index 0000000..631c6ef Binary files /dev/null and b/git_old/objects/18/f3d058470aaebe9f9de4e2f0e974e98e797376 differ diff --git a/git_old/objects/19/c264df0b5786e0ee63cc8d5a0f39e5f99d887b b/git_old/objects/19/c264df0b5786e0ee63cc8d5a0f39e5f99d887b new file mode 100644 index 0000000..dfb4933 Binary files /dev/null and b/git_old/objects/19/c264df0b5786e0ee63cc8d5a0f39e5f99d887b differ diff --git a/git_old/objects/1a/148bd308186a37c5782d49b5833da6d9f1a487 b/git_old/objects/1a/148bd308186a37c5782d49b5833da6d9f1a487 new file mode 100644 index 0000000..bbd908a Binary files /dev/null and b/git_old/objects/1a/148bd308186a37c5782d49b5833da6d9f1a487 differ diff --git a/git_old/objects/1b/ee387381e3b8df3c556d340d83f757e925fd18 b/git_old/objects/1b/ee387381e3b8df3c556d340d83f757e925fd18 new file mode 100644 index 0000000..52b6301 --- /dev/null +++ b/git_old/objects/1b/ee387381e3b8df3c556d340d83f757e925fd18 @@ -0,0 +1,2 @@ +xR@E]WB2y͌%IAD*nLG PޠU\ٻ>Uݷd :oRЅ q-Nu#34%BH,Ёb_FRSpSipʍ01ט`1jz$XJ #yQBD//kO&#`)*UU($ji{c-7Jfйz ЛXo k٫e+p,7b͌4dډD Lg5r/G@dmZgr_4ru1]۹]~ۡ\9 ߍ/Ӱb +M.])] _0 cB?nI//(npCsHx- Ny \ No newline at end of file diff --git a/git_old/objects/1d/092ea8547011a861fef775d8aed83d9f53fd20 b/git_old/objects/1d/092ea8547011a861fef775d8aed83d9f53fd20 new file mode 100644 index 0000000..6f3cef7 Binary files /dev/null and b/git_old/objects/1d/092ea8547011a861fef775d8aed83d9f53fd20 differ diff --git a/git_old/objects/1d/4c8b5988fb342b44133fe3a1ffd16db5e70872 b/git_old/objects/1d/4c8b5988fb342b44133fe3a1ffd16db5e70872 new file mode 100644 index 0000000..63b8178 Binary files /dev/null and b/git_old/objects/1d/4c8b5988fb342b44133fe3a1ffd16db5e70872 differ diff --git a/git_old/objects/1d/6a0a5b0d3df86a96dd4c07d9d52d5867b32de8 b/git_old/objects/1d/6a0a5b0d3df86a96dd4c07d9d52d5867b32de8 new file mode 100644 index 0000000..688c6f7 Binary files /dev/null and b/git_old/objects/1d/6a0a5b0d3df86a96dd4c07d9d52d5867b32de8 differ diff --git a/git_old/objects/1d/8f5799eda6c3789884719af1c1b84ed0362044 b/git_old/objects/1d/8f5799eda6c3789884719af1c1b84ed0362044 new file mode 100644 index 0000000..42d30f3 Binary files /dev/null and b/git_old/objects/1d/8f5799eda6c3789884719af1c1b84ed0362044 differ diff --git a/git_old/objects/1d/fc7a77814b09f7a93f74f9670b60f01470ecde b/git_old/objects/1d/fc7a77814b09f7a93f74f9670b60f01470ecde new file mode 100644 index 0000000..a7ff819 --- /dev/null +++ b/git_old/objects/1d/fc7a77814b09f7a93f74f9670b60f01470ecde @@ -0,0 +1,2 @@ +x0{S}m0nUEfc&!MJ=Թ'T9Вת>-f$ f<أʳ>:`3!4J g +ja_՜3P9?C1 q>ۂ1'_MA} SJ7:is23p08"Ss8bp!$yW&gFxZtnf=ߍ2r9_9=qćͭkK&nb1#IҗyMI@4a4re.ɺ,;&JUW ǛۓcwAN~oIn \ No newline at end of file diff --git a/git_old/objects/1e/1209764d24def8a4511e7e05839d15eaedca04 b/git_old/objects/1e/1209764d24def8a4511e7e05839d15eaedca04 new file mode 100644 index 0000000..b8e2fcd Binary files /dev/null and b/git_old/objects/1e/1209764d24def8a4511e7e05839d15eaedca04 differ diff --git a/git_old/objects/1e/84ab43e2edfe8f6cdba9eac0f8b91470adbc54 b/git_old/objects/1e/84ab43e2edfe8f6cdba9eac0f8b91470adbc54 new file mode 100644 index 0000000..43f67fd Binary files /dev/null and b/git_old/objects/1e/84ab43e2edfe8f6cdba9eac0f8b91470adbc54 differ diff --git a/git_old/objects/1e/ccc918c3b1485d72df21bff18f367f971dba87 b/git_old/objects/1e/ccc918c3b1485d72df21bff18f367f971dba87 new file mode 100644 index 0000000..cb6ee43 Binary files /dev/null and b/git_old/objects/1e/ccc918c3b1485d72df21bff18f367f971dba87 differ diff --git a/git_old/objects/1e/e5125d80c2d46fa9fa4fd7e928e404e6379c95 b/git_old/objects/1e/e5125d80c2d46fa9fa4fd7e928e404e6379c95 new file mode 100644 index 0000000..30da7f0 Binary files /dev/null and b/git_old/objects/1e/e5125d80c2d46fa9fa4fd7e928e404e6379c95 differ diff --git a/git_old/objects/1f/073ff62fbadbe59bf9bf95ebd8f0816d4a4108 b/git_old/objects/1f/073ff62fbadbe59bf9bf95ebd8f0816d4a4108 new file mode 100644 index 0000000..c814d7e Binary files /dev/null and b/git_old/objects/1f/073ff62fbadbe59bf9bf95ebd8f0816d4a4108 differ diff --git a/git_old/objects/1f/ac7e094e9efd524a0e5a837b8169fa8727f0f3 b/git_old/objects/1f/ac7e094e9efd524a0e5a837b8169fa8727f0f3 new file mode 100644 index 0000000..9b5a98b Binary files /dev/null and b/git_old/objects/1f/ac7e094e9efd524a0e5a837b8169fa8727f0f3 differ diff --git a/git_old/objects/20/3de1553a43262cc1e3af5fbb58d8acad68c37d b/git_old/objects/20/3de1553a43262cc1e3af5fbb58d8acad68c37d new file mode 100644 index 0000000..71e5cc0 Binary files /dev/null and b/git_old/objects/20/3de1553a43262cc1e3af5fbb58d8acad68c37d differ diff --git a/git_old/objects/20/a556c9fbea8ef45381f84cbb389e8da082943b b/git_old/objects/20/a556c9fbea8ef45381f84cbb389e8da082943b new file mode 100644 index 0000000..c42b65b Binary files /dev/null and b/git_old/objects/20/a556c9fbea8ef45381f84cbb389e8da082943b differ diff --git a/git_old/objects/21/5dd419d5ea1e79db652ac29d0e98696d751ea7 b/git_old/objects/21/5dd419d5ea1e79db652ac29d0e98696d751ea7 new file mode 100644 index 0000000..882b80b Binary files /dev/null and b/git_old/objects/21/5dd419d5ea1e79db652ac29d0e98696d751ea7 differ diff --git a/git_old/objects/21/762d2a50d2b07d2f68bddfaea353266a9bae8a b/git_old/objects/21/762d2a50d2b07d2f68bddfaea353266a9bae8a new file mode 100644 index 0000000..c1d7973 Binary files /dev/null and b/git_old/objects/21/762d2a50d2b07d2f68bddfaea353266a9bae8a differ diff --git a/git_old/objects/21/ba6dddb1bf1d85aabfa09a0156cf449d2e8517 b/git_old/objects/21/ba6dddb1bf1d85aabfa09a0156cf449d2e8517 new file mode 100644 index 0000000..7fe30f3 Binary files /dev/null and b/git_old/objects/21/ba6dddb1bf1d85aabfa09a0156cf449d2e8517 differ diff --git a/git_old/objects/21/bc4394aa30a84aa91cbe95f2480e41437da0d2 b/git_old/objects/21/bc4394aa30a84aa91cbe95f2480e41437da0d2 new file mode 100644 index 0000000..fb621d0 Binary files /dev/null and b/git_old/objects/21/bc4394aa30a84aa91cbe95f2480e41437da0d2 differ diff --git a/git_old/objects/21/bdd61e4158f5cdcf2de42340076a74d7575fca b/git_old/objects/21/bdd61e4158f5cdcf2de42340076a74d7575fca new file mode 100644 index 0000000..0b9b8ea Binary files /dev/null and b/git_old/objects/21/bdd61e4158f5cdcf2de42340076a74d7575fca differ diff --git a/git_old/objects/21/fc394623f68d738c1e9f05ce948a0df8e9be3a b/git_old/objects/21/fc394623f68d738c1e9f05ce948a0df8e9be3a new file mode 100644 index 0000000..5123fa5 Binary files /dev/null and b/git_old/objects/21/fc394623f68d738c1e9f05ce948a0df8e9be3a differ diff --git a/git_old/objects/22/81276c735eaef4e8d4d61bcf0c14853a1f88b7 b/git_old/objects/22/81276c735eaef4e8d4d61bcf0c14853a1f88b7 new file mode 100644 index 0000000..0be94b0 Binary files /dev/null and b/git_old/objects/22/81276c735eaef4e8d4d61bcf0c14853a1f88b7 differ diff --git a/git_old/objects/23/4284b7427e7846032167c06a1fedb107bc2e61 b/git_old/objects/23/4284b7427e7846032167c06a1fedb107bc2e61 new file mode 100644 index 0000000..ff21513 Binary files /dev/null and b/git_old/objects/23/4284b7427e7846032167c06a1fedb107bc2e61 differ diff --git a/git_old/objects/23/a2814af6c7378f9bbbab57c7c0c2042223a77b b/git_old/objects/23/a2814af6c7378f9bbbab57c7c0c2042223a77b new file mode 100644 index 0000000..59177cd Binary files /dev/null and b/git_old/objects/23/a2814af6c7378f9bbbab57c7c0c2042223a77b differ diff --git a/git_old/objects/23/b0216403443bd8238d6d0b8ff31c4bad8fa411 b/git_old/objects/23/b0216403443bd8238d6d0b8ff31c4bad8fa411 new file mode 100644 index 0000000..bcb2d95 Binary files /dev/null and b/git_old/objects/23/b0216403443bd8238d6d0b8ff31c4bad8fa411 differ diff --git a/git_old/objects/23/ddbd1e768119f93ed2f94a5bfa1856deb19841 b/git_old/objects/23/ddbd1e768119f93ed2f94a5bfa1856deb19841 new file mode 100644 index 0000000..d255805 Binary files /dev/null and b/git_old/objects/23/ddbd1e768119f93ed2f94a5bfa1856deb19841 differ diff --git a/git_old/objects/24/9a7ee341292d67679a6c61a4c462a6732cb070 b/git_old/objects/24/9a7ee341292d67679a6c61a4c462a6732cb070 new file mode 100644 index 0000000..fc45231 Binary files /dev/null and b/git_old/objects/24/9a7ee341292d67679a6c61a4c462a6732cb070 differ diff --git a/git_old/objects/25/293b10019865ecaa9b8ceae6d6e27fb9ee5ecc b/git_old/objects/25/293b10019865ecaa9b8ceae6d6e27fb9ee5ecc new file mode 100644 index 0000000..ecb4ebe --- /dev/null +++ b/git_old/objects/25/293b10019865ecaa9b8ceae6d6e27fb9ee5ecc @@ -0,0 +1,2 @@ +x+)JMU041a040031Q(K//520+.Ha\Y)Wٱ?W( +C\*3ĎElJByLq7\1՛kFMLw/\6 \ No newline at end of file diff --git a/git_old/objects/26/e77079279524ad45910954e8975b26a314e86c b/git_old/objects/26/e77079279524ad45910954e8975b26a314e86c new file mode 100644 index 0000000..d25a13b Binary files /dev/null and b/git_old/objects/26/e77079279524ad45910954e8975b26a314e86c differ diff --git a/git_old/objects/28/672d31155876940b686965fede6ad8c7eb0abb b/git_old/objects/28/672d31155876940b686965fede6ad8c7eb0abb new file mode 100644 index 0000000..efe9eca Binary files /dev/null and b/git_old/objects/28/672d31155876940b686965fede6ad8c7eb0abb differ diff --git a/git_old/objects/29/1b0fea5ea72e589c0a4384ed9d18b60fd4ba88 b/git_old/objects/29/1b0fea5ea72e589c0a4384ed9d18b60fd4ba88 new file mode 100644 index 0000000..53b995c Binary files /dev/null and b/git_old/objects/29/1b0fea5ea72e589c0a4384ed9d18b60fd4ba88 differ diff --git a/git_old/objects/29/40a7c1ae7cacd09bacc7470c3cc5060b36d5ae b/git_old/objects/29/40a7c1ae7cacd09bacc7470c3cc5060b36d5ae new file mode 100644 index 0000000..9382962 Binary files /dev/null and b/git_old/objects/29/40a7c1ae7cacd09bacc7470c3cc5060b36d5ae differ diff --git a/git_old/objects/2a/2455815fbb62369cf9418fbd2f979e4319a874 b/git_old/objects/2a/2455815fbb62369cf9418fbd2f979e4319a874 new file mode 100644 index 0000000..30451f6 Binary files /dev/null and b/git_old/objects/2a/2455815fbb62369cf9418fbd2f979e4319a874 differ diff --git a/git_old/objects/2b/0cb2287126dd53ed63bb0616d4b89c65a7aa94 b/git_old/objects/2b/0cb2287126dd53ed63bb0616d4b89c65a7aa94 new file mode 100644 index 0000000..a5956a1 Binary files /dev/null and b/git_old/objects/2b/0cb2287126dd53ed63bb0616d4b89c65a7aa94 differ diff --git a/git_old/objects/2b/850a044a1043a9c760fe15708ba57a13d739c5 b/git_old/objects/2b/850a044a1043a9c760fe15708ba57a13d739c5 new file mode 100644 index 0000000..731ea7d Binary files /dev/null and b/git_old/objects/2b/850a044a1043a9c760fe15708ba57a13d739c5 differ diff --git a/git_old/objects/2c/1309d561d633db86542a1ba52c0dce8ad1ff00 b/git_old/objects/2c/1309d561d633db86542a1ba52c0dce8ad1ff00 new file mode 100644 index 0000000..680860e Binary files /dev/null and b/git_old/objects/2c/1309d561d633db86542a1ba52c0dce8ad1ff00 differ diff --git a/git_old/objects/2d/e0aeeac0f40f9d9efe6d771d57b12690abd6a6 b/git_old/objects/2d/e0aeeac0f40f9d9efe6d771d57b12690abd6a6 new file mode 100644 index 0000000..f80b79f Binary files /dev/null and b/git_old/objects/2d/e0aeeac0f40f9d9efe6d771d57b12690abd6a6 differ diff --git a/git_old/objects/2e/03fb59f7721e6a572232a79f102d021381d4a9 b/git_old/objects/2e/03fb59f7721e6a572232a79f102d021381d4a9 new file mode 100644 index 0000000..030d785 Binary files /dev/null and b/git_old/objects/2e/03fb59f7721e6a572232a79f102d021381d4a9 differ diff --git a/git_old/objects/2e/aeecc9d5ee985277cd34692ff4939951d3537c b/git_old/objects/2e/aeecc9d5ee985277cd34692ff4939951d3537c new file mode 100644 index 0000000..cdbc23d Binary files /dev/null and b/git_old/objects/2e/aeecc9d5ee985277cd34692ff4939951d3537c differ diff --git a/git_old/objects/2f/7755e5f4a8111c4e42c1125ba4b40950ccdec4 b/git_old/objects/2f/7755e5f4a8111c4e42c1125ba4b40950ccdec4 new file mode 100644 index 0000000..6ca474a Binary files /dev/null and b/git_old/objects/2f/7755e5f4a8111c4e42c1125ba4b40950ccdec4 differ diff --git a/git_old/objects/30/7f246d533c02cd5254d3dc402ab3679faede46 b/git_old/objects/30/7f246d533c02cd5254d3dc402ab3679faede46 new file mode 100644 index 0000000..9c53fa4 Binary files /dev/null and b/git_old/objects/30/7f246d533c02cd5254d3dc402ab3679faede46 differ diff --git a/git_old/objects/31/b002d7f7d6e0b241b80e0a0544c5a7bb6d06c7 b/git_old/objects/31/b002d7f7d6e0b241b80e0a0544c5a7bb6d06c7 new file mode 100644 index 0000000..b8b4a33 Binary files /dev/null and b/git_old/objects/31/b002d7f7d6e0b241b80e0a0544c5a7bb6d06c7 differ diff --git a/git_old/objects/31/e8509d33b53ba8fc1c60623c3058ce7e40e851 b/git_old/objects/31/e8509d33b53ba8fc1c60623c3058ce7e40e851 new file mode 100644 index 0000000..17e2e1f Binary files /dev/null and b/git_old/objects/31/e8509d33b53ba8fc1c60623c3058ce7e40e851 differ diff --git a/git_old/objects/32/361b3ff646130fbb126c0d2ac68ebf04e955e9 b/git_old/objects/32/361b3ff646130fbb126c0d2ac68ebf04e955e9 new file mode 100644 index 0000000..9f72f48 Binary files /dev/null and b/git_old/objects/32/361b3ff646130fbb126c0d2ac68ebf04e955e9 differ diff --git a/git_old/objects/32/ea8a1f2e5f04f1de11d5a9ecfd2b13e3766153 b/git_old/objects/32/ea8a1f2e5f04f1de11d5a9ecfd2b13e3766153 new file mode 100644 index 0000000..268183c Binary files /dev/null and b/git_old/objects/32/ea8a1f2e5f04f1de11d5a9ecfd2b13e3766153 differ diff --git a/git_old/objects/33/19c98f45427d968499570d229c789690bb4173 b/git_old/objects/33/19c98f45427d968499570d229c789690bb4173 new file mode 100644 index 0000000..f8aa4b2 Binary files /dev/null and b/git_old/objects/33/19c98f45427d968499570d229c789690bb4173 differ diff --git a/git_old/objects/33/9574a251d265624e53dc31e7be354598dcf066 b/git_old/objects/33/9574a251d265624e53dc31e7be354598dcf066 new file mode 100644 index 0000000..8ec7214 Binary files /dev/null and b/git_old/objects/33/9574a251d265624e53dc31e7be354598dcf066 differ diff --git a/git_old/objects/33/9f9b00ba672e2db0336e5b393eae629f2cc79a b/git_old/objects/33/9f9b00ba672e2db0336e5b393eae629f2cc79a new file mode 100644 index 0000000..ea9e527 Binary files /dev/null and b/git_old/objects/33/9f9b00ba672e2db0336e5b393eae629f2cc79a differ diff --git a/git_old/objects/33/d53466f4cae71b9e5609840edb37e35dd60894 b/git_old/objects/33/d53466f4cae71b9e5609840edb37e35dd60894 new file mode 100644 index 0000000..f1864d4 Binary files /dev/null and b/git_old/objects/33/d53466f4cae71b9e5609840edb37e35dd60894 differ diff --git a/git_old/objects/35/68f67642e98d2bbba5970132bf3f9cdd2da738 b/git_old/objects/35/68f67642e98d2bbba5970132bf3f9cdd2da738 new file mode 100644 index 0000000..59dfd43 Binary files /dev/null and b/git_old/objects/35/68f67642e98d2bbba5970132bf3f9cdd2da738 differ diff --git a/git_old/objects/35/c4a7296cabef7c9be48fbe94591f5eeeb3c92a b/git_old/objects/35/c4a7296cabef7c9be48fbe94591f5eeeb3c92a new file mode 100644 index 0000000..ab4a644 Binary files /dev/null and b/git_old/objects/35/c4a7296cabef7c9be48fbe94591f5eeeb3c92a differ diff --git a/git_old/objects/36/f2e086b41af08feadd6040815c27b96e8b899b b/git_old/objects/36/f2e086b41af08feadd6040815c27b96e8b899b new file mode 100644 index 0000000..6396a4e Binary files /dev/null and b/git_old/objects/36/f2e086b41af08feadd6040815c27b96e8b899b differ diff --git a/git_old/objects/37/b6a1713a9d544f84d1ab9e58aa0f4e14d3cf31 b/git_old/objects/37/b6a1713a9d544f84d1ab9e58aa0f4e14d3cf31 new file mode 100644 index 0000000..8d33f9f --- /dev/null +++ b/git_old/objects/37/b6a1713a9d544f84d1ab9e58aa0f4e14d3cf31 @@ -0,0 +1,4 @@ +xOs0{֧ػ'@FBSp0q1M3C@$~v7;勇i* ~0@ZHND(q2W9(msԉ^ 0B +-a.%9 +‚hX&͵!eCxm+|uU|F-wb&:h]]9T%ܽ0l ~sCifG+S8qxngU>|zՋ;/G tied3BJ$̿y3$ׇ4vlVհNob? +XC-ce{QП4A,HMEOߴcIJ]LZ, \ No newline at end of file diff --git a/git_old/objects/37/c52d403459d18b664f37c64f6f75f7d4f82dff b/git_old/objects/37/c52d403459d18b664f37c64f6f75f7d4f82dff new file mode 100644 index 0000000..ba8b3ed Binary files /dev/null and b/git_old/objects/37/c52d403459d18b664f37c64f6f75f7d4f82dff differ diff --git a/git_old/objects/37/ea7c11092b15b4c84809fd1f84de0a9ea806be b/git_old/objects/37/ea7c11092b15b4c84809fd1f84de0a9ea806be new file mode 100644 index 0000000..16f0bcb Binary files /dev/null and b/git_old/objects/37/ea7c11092b15b4c84809fd1f84de0a9ea806be differ diff --git a/git_old/objects/37/f6ea648fdda8a5c561a39e97998654be9f7541 b/git_old/objects/37/f6ea648fdda8a5c561a39e97998654be9f7541 new file mode 100644 index 0000000..52a9c03 Binary files /dev/null and b/git_old/objects/37/f6ea648fdda8a5c561a39e97998654be9f7541 differ diff --git a/git_old/objects/37/f724b57f072e7cf3d37121506565b6b7d82230 b/git_old/objects/37/f724b57f072e7cf3d37121506565b6b7d82230 new file mode 100644 index 0000000..9dc53d4 Binary files /dev/null and b/git_old/objects/37/f724b57f072e7cf3d37121506565b6b7d82230 differ diff --git a/git_old/objects/38/56f1c849592034d02fd5c40cf2127357727222 b/git_old/objects/38/56f1c849592034d02fd5c40cf2127357727222 new file mode 100644 index 0000000..52540b5 Binary files /dev/null and b/git_old/objects/38/56f1c849592034d02fd5c40cf2127357727222 differ diff --git a/git_old/objects/38/79a3c24e8c8ed97af1cbb1b4ec54872d93d07a b/git_old/objects/38/79a3c24e8c8ed97af1cbb1b4ec54872d93d07a new file mode 100644 index 0000000..c303319 Binary files /dev/null and b/git_old/objects/38/79a3c24e8c8ed97af1cbb1b4ec54872d93d07a differ diff --git a/git_old/objects/39/21c71a9f81eabd30740787b4d561ab238f3e7a b/git_old/objects/39/21c71a9f81eabd30740787b4d561ab238f3e7a new file mode 100644 index 0000000..c8b775a Binary files /dev/null and b/git_old/objects/39/21c71a9f81eabd30740787b4d561ab238f3e7a differ diff --git a/git_old/objects/39/45c449c1c8871d281ec02d55f1d4dfe0004d75 b/git_old/objects/39/45c449c1c8871d281ec02d55f1d4dfe0004d75 new file mode 100644 index 0000000..e60c9e8 Binary files /dev/null and b/git_old/objects/39/45c449c1c8871d281ec02d55f1d4dfe0004d75 differ diff --git a/git_old/objects/39/701aeec47154c33510b7f5e61984312641f3ea b/git_old/objects/39/701aeec47154c33510b7f5e61984312641f3ea new file mode 100644 index 0000000..b76e4f2 Binary files /dev/null and b/git_old/objects/39/701aeec47154c33510b7f5e61984312641f3ea differ diff --git a/git_old/objects/3a/535ac3f663a0d0ac41c11e67b1252662ec62a0 b/git_old/objects/3a/535ac3f663a0d0ac41c11e67b1252662ec62a0 new file mode 100644 index 0000000..0c45faf Binary files /dev/null and b/git_old/objects/3a/535ac3f663a0d0ac41c11e67b1252662ec62a0 differ diff --git a/git_old/objects/3a/b867ce84c4d5f33d3ba45a924c7af2d1dfe4f4 b/git_old/objects/3a/b867ce84c4d5f33d3ba45a924c7af2d1dfe4f4 new file mode 100644 index 0000000..0f39a20 Binary files /dev/null and b/git_old/objects/3a/b867ce84c4d5f33d3ba45a924c7af2d1dfe4f4 differ diff --git a/git_old/objects/3a/c4d2fe3a02190445c276cc764322465e4f4ab0 b/git_old/objects/3a/c4d2fe3a02190445c276cc764322465e4f4ab0 new file mode 100644 index 0000000..b9fa43f Binary files /dev/null and b/git_old/objects/3a/c4d2fe3a02190445c276cc764322465e4f4ab0 differ diff --git a/git_old/objects/3b/49aa976c04ac0d177dd05baea78226c1cb6803 b/git_old/objects/3b/49aa976c04ac0d177dd05baea78226c1cb6803 new file mode 100644 index 0000000..7f61635 Binary files /dev/null and b/git_old/objects/3b/49aa976c04ac0d177dd05baea78226c1cb6803 differ diff --git a/git_old/objects/3b/8aa48fd1307baf8736b55f2f9115ed8de5066d b/git_old/objects/3b/8aa48fd1307baf8736b55f2f9115ed8de5066d new file mode 100644 index 0000000..6bb7594 Binary files /dev/null and b/git_old/objects/3b/8aa48fd1307baf8736b55f2f9115ed8de5066d differ diff --git a/git_old/objects/3b/8ab02f7f1592f4838456a2092a817743f25374 b/git_old/objects/3b/8ab02f7f1592f4838456a2092a817743f25374 new file mode 100644 index 0000000..6e4f6db --- /dev/null +++ b/git_old/objects/3b/8ab02f7f1592f4838456a2092a817743f25374 @@ -0,0 +1,2 @@ +xOs0s֧xN)B:M`ciqBC?}I;SO}͛,ݘN)u͊\qF2L-[cڬSg6t +cGJY V<XBYom/ta\*zsԦ缩pJma-?-/ۏpu<\zOi( <{>7SV\]yC~e:9\}IxQfDVI_i~_\t!}Me4&dǢ,V.S.Z סy< xgS uA87"Xv;,Sq䉵s%I7nhcџ6a|.H6p} æݑ \ No newline at end of file diff --git a/git_old/objects/44/97eb2e3d35c67182071be74ddba0722626e791 b/git_old/objects/44/97eb2e3d35c67182071be74ddba0722626e791 new file mode 100644 index 0000000..1f807c5 Binary files /dev/null and b/git_old/objects/44/97eb2e3d35c67182071be74ddba0722626e791 differ diff --git a/git_old/objects/45/4276664b083ebddb1cd02beb3721bc90042ba4 b/git_old/objects/45/4276664b083ebddb1cd02beb3721bc90042ba4 new file mode 100644 index 0000000..48edaea Binary files /dev/null and b/git_old/objects/45/4276664b083ebddb1cd02beb3721bc90042ba4 differ diff --git a/git_old/objects/45/96b674951eef2a3f4562175837fc3f2fe41ed2 b/git_old/objects/45/96b674951eef2a3f4562175837fc3f2fe41ed2 new file mode 100644 index 0000000..239d0f8 Binary files /dev/null and b/git_old/objects/45/96b674951eef2a3f4562175837fc3f2fe41ed2 differ diff --git a/git_old/objects/46/5985bf9dd4979ad968f8236dd5b5f54e9b49ea b/git_old/objects/46/5985bf9dd4979ad968f8236dd5b5f54e9b49ea new file mode 100644 index 0000000..641bca8 Binary files /dev/null and b/git_old/objects/46/5985bf9dd4979ad968f8236dd5b5f54e9b49ea differ diff --git a/git_old/objects/46/7fa3b167f5b10c35b861e0e42a25ae2f9afe62 b/git_old/objects/46/7fa3b167f5b10c35b861e0e42a25ae2f9afe62 new file mode 100644 index 0000000..53fd300 Binary files /dev/null and b/git_old/objects/46/7fa3b167f5b10c35b861e0e42a25ae2f9afe62 differ diff --git a/git_old/objects/46/f52593a407d65e35161e1cafd69d102a5f6b93 b/git_old/objects/46/f52593a407d65e35161e1cafd69d102a5f6b93 new file mode 100644 index 0000000..bb6dcff --- /dev/null +++ b/git_old/objects/46/f52593a407d65e35161e1cafd69d102a5f6b93 @@ -0,0 +1,3 @@ +xSmO0gSd04mH>e2^L;ʶ/u{{[o!+ R!y S1)I0<⠰Aeq;~SX1E_HN3u+#'u׵sq{J K,uVS1;gno'/Fu:m8T2lꅳbi0z)J,,7؂M=V3UKaʹr_;!w[v+.?]^ЛdOw7? +܇ڙ/@(^9&>hHjIt{3rpܓkO>c+9Cu  "T `H!uNG??evbHU0oK#ϝ+l}g=ĭ\ 5+{xE + L1 Q7f4Jq7 0NI0qz$ҠՍvNqЈVً#ӓly].[䮕VK=b@Ȝ`S9#zAQ{B͍^AD>:)ܘ/299ȟ믒=~GNGm^[Qo~Y \ No newline at end of file diff --git a/git_old/objects/47/5de2bfdc2be19baa681237c4212754e2c9b554 b/git_old/objects/47/5de2bfdc2be19baa681237c4212754e2c9b554 new file mode 100644 index 0000000..c45ef68 Binary files /dev/null and b/git_old/objects/47/5de2bfdc2be19baa681237c4212754e2c9b554 differ diff --git a/git_old/objects/49/698c57f297d104e8d6f478536634efd0c726c4 b/git_old/objects/49/698c57f297d104e8d6f478536634efd0c726c4 new file mode 100644 index 0000000..3fe4a7b Binary files /dev/null and b/git_old/objects/49/698c57f297d104e8d6f478536634efd0c726c4 differ diff --git a/git_old/objects/49/d172705dc9f1b3c1224a6cf07f6631c6ad06e0 b/git_old/objects/49/d172705dc9f1b3c1224a6cf07f6631c6ad06e0 new file mode 100644 index 0000000..1ea926d Binary files /dev/null and b/git_old/objects/49/d172705dc9f1b3c1224a6cf07f6631c6ad06e0 differ diff --git a/git_old/objects/4a/38b99c93f22d32cf663bf18f414132a43e35e7 b/git_old/objects/4a/38b99c93f22d32cf663bf18f414132a43e35e7 new file mode 100644 index 0000000..1ad3f85 Binary files /dev/null and b/git_old/objects/4a/38b99c93f22d32cf663bf18f414132a43e35e7 differ diff --git a/git_old/objects/4a/dd9617911bcf3cbaff82a99bd610ec58f2b399 b/git_old/objects/4a/dd9617911bcf3cbaff82a99bd610ec58f2b399 new file mode 100644 index 0000000..de7843a Binary files /dev/null and b/git_old/objects/4a/dd9617911bcf3cbaff82a99bd610ec58f2b399 differ diff --git a/git_old/objects/4b/2a86a9211dcc6ed714ee8020ccb2f3af78eb42 b/git_old/objects/4b/2a86a9211dcc6ed714ee8020ccb2f3af78eb42 new file mode 100644 index 0000000..9745706 Binary files /dev/null and b/git_old/objects/4b/2a86a9211dcc6ed714ee8020ccb2f3af78eb42 differ diff --git a/git_old/objects/4b/e96f64a71df09712478b73f6fb07ec47c3d506 b/git_old/objects/4b/e96f64a71df09712478b73f6fb07ec47c3d506 new file mode 100644 index 0000000..b04b64b Binary files /dev/null and b/git_old/objects/4b/e96f64a71df09712478b73f6fb07ec47c3d506 differ diff --git a/git_old/objects/4c/113d8af3528ddeb6a11d9e190b2d8949c4bdf1 b/git_old/objects/4c/113d8af3528ddeb6a11d9e190b2d8949c4bdf1 new file mode 100644 index 0000000..0f1c0c4 Binary files /dev/null and b/git_old/objects/4c/113d8af3528ddeb6a11d9e190b2d8949c4bdf1 differ diff --git a/git_old/objects/4c/d7805333526603e6b0a24db42938d082804826 b/git_old/objects/4c/d7805333526603e6b0a24db42938d082804826 new file mode 100644 index 0000000..faf6ff5 Binary files /dev/null and b/git_old/objects/4c/d7805333526603e6b0a24db42938d082804826 differ diff --git a/git_old/objects/4c/ecd5c5fe0c9fd6a2a386f9f7f50c549383174c b/git_old/objects/4c/ecd5c5fe0c9fd6a2a386f9f7f50c549383174c new file mode 100644 index 0000000..15a2a88 Binary files /dev/null and b/git_old/objects/4c/ecd5c5fe0c9fd6a2a386f9f7f50c549383174c differ diff --git a/git_old/objects/4d/a20800f8063d837c0196511e08f4c63785a38b b/git_old/objects/4d/a20800f8063d837c0196511e08f4c63785a38b new file mode 100644 index 0000000..346fcc2 Binary files /dev/null and b/git_old/objects/4d/a20800f8063d837c0196511e08f4c63785a38b differ diff --git a/git_old/objects/4e/7ebc1baf00686d6338d6a0584c1ebca9f83884 b/git_old/objects/4e/7ebc1baf00686d6338d6a0584c1ebca9f83884 new file mode 100644 index 0000000..c269761 Binary files /dev/null and b/git_old/objects/4e/7ebc1baf00686d6338d6a0584c1ebca9f83884 differ diff --git a/git_old/objects/4e/948e1722ad03991e039e06fe80ae7c1eaba50e b/git_old/objects/4e/948e1722ad03991e039e06fe80ae7c1eaba50e new file mode 100644 index 0000000..4d68552 Binary files /dev/null and b/git_old/objects/4e/948e1722ad03991e039e06fe80ae7c1eaba50e differ diff --git a/git_old/objects/4e/e7024e7d5a9a3cf411b6b18f71160073b389db b/git_old/objects/4e/e7024e7d5a9a3cf411b6b18f71160073b389db new file mode 100644 index 0000000..451daca Binary files /dev/null and b/git_old/objects/4e/e7024e7d5a9a3cf411b6b18f71160073b389db differ diff --git a/git_old/objects/4f/3153a13e0cbf1d194f8581fc546f083a3eaddc b/git_old/objects/4f/3153a13e0cbf1d194f8581fc546f083a3eaddc new file mode 100644 index 0000000..0cfbfb8 Binary files /dev/null and b/git_old/objects/4f/3153a13e0cbf1d194f8581fc546f083a3eaddc differ diff --git a/git_old/objects/4f/c7ecc4b875ccea87b5b5f20d8a277a85f805fc b/git_old/objects/4f/c7ecc4b875ccea87b5b5f20d8a277a85f805fc new file mode 100644 index 0000000..dd23d10 Binary files /dev/null and b/git_old/objects/4f/c7ecc4b875ccea87b5b5f20d8a277a85f805fc differ diff --git a/git_old/objects/4f/ce7ed23669e9204dd3bf95e08d71065ae612de b/git_old/objects/4f/ce7ed23669e9204dd3bf95e08d71065ae612de new file mode 100644 index 0000000..8eaacc5 Binary files /dev/null and b/git_old/objects/4f/ce7ed23669e9204dd3bf95e08d71065ae612de differ diff --git a/git_old/objects/4f/f5484064608a4a29cf66d47f5e44eaa879d861 b/git_old/objects/4f/f5484064608a4a29cf66d47f5e44eaa879d861 new file mode 100644 index 0000000..3f05251 Binary files /dev/null and b/git_old/objects/4f/f5484064608a4a29cf66d47f5e44eaa879d861 differ diff --git a/git_old/objects/50/7986e920dd0aa29da4c7c2b40fa7ad2b515b80 b/git_old/objects/50/7986e920dd0aa29da4c7c2b40fa7ad2b515b80 new file mode 100644 index 0000000..cac6ab2 Binary files /dev/null and b/git_old/objects/50/7986e920dd0aa29da4c7c2b40fa7ad2b515b80 differ diff --git a/git_old/objects/50/b4250b496d3a1fc0abf26e8c7f9712e381b910 b/git_old/objects/50/b4250b496d3a1fc0abf26e8c7f9712e381b910 new file mode 100644 index 0000000..7d2906d Binary files /dev/null and b/git_old/objects/50/b4250b496d3a1fc0abf26e8c7f9712e381b910 differ diff --git a/git_old/objects/50/c7f426504a30d16cfe1cbd0c266cb20b3b0c5f b/git_old/objects/50/c7f426504a30d16cfe1cbd0c266cb20b3b0c5f new file mode 100644 index 0000000..eadef98 Binary files /dev/null and b/git_old/objects/50/c7f426504a30d16cfe1cbd0c266cb20b3b0c5f differ diff --git a/git_old/objects/50/e2dc831e7b8f91cce31aabfaebb2c5073fe9eb b/git_old/objects/50/e2dc831e7b8f91cce31aabfaebb2c5073fe9eb new file mode 100644 index 0000000..5e05db0 Binary files /dev/null and b/git_old/objects/50/e2dc831e7b8f91cce31aabfaebb2c5073fe9eb differ diff --git a/git_old/objects/51/78cbc77bc90e97c4f6e921067309e7d752e89b b/git_old/objects/51/78cbc77bc90e97c4f6e921067309e7d752e89b new file mode 100644 index 0000000..1a9856b Binary files /dev/null and b/git_old/objects/51/78cbc77bc90e97c4f6e921067309e7d752e89b differ diff --git a/git_old/objects/51/860eddcee878aa00d230ef9bf4823cf7109ac1 b/git_old/objects/51/860eddcee878aa00d230ef9bf4823cf7109ac1 new file mode 100644 index 0000000..cad29f8 Binary files /dev/null and b/git_old/objects/51/860eddcee878aa00d230ef9bf4823cf7109ac1 differ diff --git a/git_old/objects/51/a566b3040d79d173a3931eb4e66488751521ae b/git_old/objects/51/a566b3040d79d173a3931eb4e66488751521ae new file mode 100644 index 0000000..3cff222 Binary files /dev/null and b/git_old/objects/51/a566b3040d79d173a3931eb4e66488751521ae differ diff --git a/git_old/objects/51/b15a57e80db598cd5d4eb446bc3c734a2f04bb b/git_old/objects/51/b15a57e80db598cd5d4eb446bc3c734a2f04bb new file mode 100644 index 0000000..7d7660a Binary files /dev/null and b/git_old/objects/51/b15a57e80db598cd5d4eb446bc3c734a2f04bb differ diff --git a/git_old/objects/51/eaa4ef38bb4df1707977bcad34b5ab94363d60 b/git_old/objects/51/eaa4ef38bb4df1707977bcad34b5ab94363d60 new file mode 100644 index 0000000..9c55d72 Binary files /dev/null and b/git_old/objects/51/eaa4ef38bb4df1707977bcad34b5ab94363d60 differ diff --git a/git_old/objects/52/8a0c904b643eae60d8aa1a6e8eb91721f9216e b/git_old/objects/52/8a0c904b643eae60d8aa1a6e8eb91721f9216e new file mode 100644 index 0000000..53b893c --- /dev/null +++ b/git_old/objects/52/8a0c904b643eae60d8aa1a6e8eb91721f9216e @@ -0,0 +1,2 @@ +xO0{xh[cWݪ$E$?dٛưKp6hVꩧfxlPrgVZ1T)DqY 5$ϺsÅdI=ɨS44 + K$W3dCќ!m_&0wӸz(> 6KkfT_P$kҧzs<](Ow/EbiX1KdqXtS? "m; _:GS;K~F/6=]^d{<헮&oz@ī6ICtm /pֶ= ]". \ No newline at end of file diff --git a/git_old/objects/53/074ce0c0308773491b758b5e1462859df8b9a6 b/git_old/objects/53/074ce0c0308773491b758b5e1462859df8b9a6 new file mode 100644 index 0000000..f22763d Binary files /dev/null and b/git_old/objects/53/074ce0c0308773491b758b5e1462859df8b9a6 differ diff --git a/git_old/objects/53/99653266cd7037e9ef54afe60dbb2736310756 b/git_old/objects/53/99653266cd7037e9ef54afe60dbb2736310756 new file mode 100644 index 0000000..3121e73 Binary files /dev/null and b/git_old/objects/53/99653266cd7037e9ef54afe60dbb2736310756 differ diff --git a/git_old/objects/53/c743fd06d2228cf308de692bbcb0a45b9d4d97 b/git_old/objects/53/c743fd06d2228cf308de692bbcb0a45b9d4d97 new file mode 100644 index 0000000..e43f54e Binary files /dev/null and b/git_old/objects/53/c743fd06d2228cf308de692bbcb0a45b9d4d97 differ diff --git a/git_old/objects/54/53ced87c4612cfeee2719ebd20e0b6b1645f41 b/git_old/objects/54/53ced87c4612cfeee2719ebd20e0b6b1645f41 new file mode 100644 index 0000000..fcff2e5 Binary files /dev/null and b/git_old/objects/54/53ced87c4612cfeee2719ebd20e0b6b1645f41 differ diff --git a/git_old/objects/55/01c76f6ae9037a3c460e9cc1c4ccdae4fd0356 b/git_old/objects/55/01c76f6ae9037a3c460e9cc1c4ccdae4fd0356 new file mode 100644 index 0000000..cbbe5c1 Binary files /dev/null and b/git_old/objects/55/01c76f6ae9037a3c460e9cc1c4ccdae4fd0356 differ diff --git a/git_old/objects/55/22f5a5f6c77e03a887f3fb6af74d1cf4f7a721 b/git_old/objects/55/22f5a5f6c77e03a887f3fb6af74d1cf4f7a721 new file mode 100644 index 0000000..50e58a4 Binary files /dev/null and b/git_old/objects/55/22f5a5f6c77e03a887f3fb6af74d1cf4f7a721 differ diff --git a/git_old/objects/55/328f2d5a5be454d59ff7f11e7007d9b0de9275 b/git_old/objects/55/328f2d5a5be454d59ff7f11e7007d9b0de9275 new file mode 100644 index 0000000..baaf49a Binary files /dev/null and b/git_old/objects/55/328f2d5a5be454d59ff7f11e7007d9b0de9275 differ diff --git a/git_old/objects/55/876826a4abb77948566f61ae1605b696efe498 b/git_old/objects/55/876826a4abb77948566f61ae1605b696efe498 new file mode 100644 index 0000000..3251eb6 Binary files /dev/null and b/git_old/objects/55/876826a4abb77948566f61ae1605b696efe498 differ diff --git a/git_old/objects/55/cc8acda10b718528b37f946de92d2c16269f3d b/git_old/objects/55/cc8acda10b718528b37f946de92d2c16269f3d new file mode 100644 index 0000000..745b013 Binary files /dev/null and b/git_old/objects/55/cc8acda10b718528b37f946de92d2c16269f3d differ diff --git a/git_old/objects/56/29634cd774548a08c24bdb26feb6925427ecea b/git_old/objects/56/29634cd774548a08c24bdb26feb6925427ecea new file mode 100644 index 0000000..9b98829 --- /dev/null +++ b/git_old/objects/56/29634cd774548a08c24bdb26feb6925427ecea @@ -0,0 +1,2 @@ +xr0{S|/Z L)`$, b8O_)m3;k?_ 3Ṯ {X{C~:ʓ~ \Pӊb%JMTx)SExHʝ`իY +cN~غztw #$L04B/;i{mm]qg0gh/ӿzQ\LoƋvU=ߎ-y="EdpG߆]\_?,}4S:{(#,m4FPAWR XGsw77Swkп5lT^װԍ{p<'PWԼ \ No newline at end of file diff --git a/git_old/objects/56/2a5857ebce42a2e79d5161e4390576965e0320 b/git_old/objects/56/2a5857ebce42a2e79d5161e4390576965e0320 new file mode 100644 index 0000000..e336d17 Binary files /dev/null and b/git_old/objects/56/2a5857ebce42a2e79d5161e4390576965e0320 differ diff --git a/git_old/objects/56/3f9bf823265647bbe8f51240112d6aa9a25001 b/git_old/objects/56/3f9bf823265647bbe8f51240112d6aa9a25001 new file mode 100644 index 0000000..2590e7b Binary files /dev/null and b/git_old/objects/56/3f9bf823265647bbe8f51240112d6aa9a25001 differ diff --git a/git_old/objects/56/5e85e9a1ba9b83f92e6f1746f2006c719af132 b/git_old/objects/56/5e85e9a1ba9b83f92e6f1746f2006c719af132 new file mode 100644 index 0000000..9632a58 Binary files /dev/null and b/git_old/objects/56/5e85e9a1ba9b83f92e6f1746f2006c719af132 differ diff --git a/git_old/objects/56/680c63ba9bee6d1e06cc19cc97537b9c217e82 b/git_old/objects/56/680c63ba9bee6d1e06cc19cc97537b9c217e82 new file mode 100644 index 0000000..902f389 Binary files /dev/null and b/git_old/objects/56/680c63ba9bee6d1e06cc19cc97537b9c217e82 differ diff --git a/git_old/objects/57/259eb3a79e3293ec89dc7b94bc04bb71ea8fcc b/git_old/objects/57/259eb3a79e3293ec89dc7b94bc04bb71ea8fcc new file mode 100644 index 0000000..6a64d52 Binary files /dev/null and b/git_old/objects/57/259eb3a79e3293ec89dc7b94bc04bb71ea8fcc differ diff --git a/git_old/objects/57/36876159f8fde4494bc94ea828d55cd8aabe44 b/git_old/objects/57/36876159f8fde4494bc94ea828d55cd8aabe44 new file mode 100644 index 0000000..6fea38b Binary files /dev/null and b/git_old/objects/57/36876159f8fde4494bc94ea828d55cd8aabe44 differ diff --git a/git_old/objects/57/4d2a2c40ec09178882590e8529d2ccd893e327 b/git_old/objects/57/4d2a2c40ec09178882590e8529d2ccd893e327 new file mode 100644 index 0000000..d3f7da2 Binary files /dev/null and b/git_old/objects/57/4d2a2c40ec09178882590e8529d2ccd893e327 differ diff --git a/git_old/objects/57/b25b307c511f43bf8dce330fb7a95215a1f4e6 b/git_old/objects/57/b25b307c511f43bf8dce330fb7a95215a1f4e6 new file mode 100644 index 0000000..d7808ae Binary files /dev/null and b/git_old/objects/57/b25b307c511f43bf8dce330fb7a95215a1f4e6 differ diff --git a/git_old/objects/58/11c1a65d6a75f27f4775ea1ca2770df46da74f b/git_old/objects/58/11c1a65d6a75f27f4775ea1ca2770df46da74f new file mode 100644 index 0000000..5ca3c59 Binary files /dev/null and b/git_old/objects/58/11c1a65d6a75f27f4775ea1ca2770df46da74f differ diff --git a/git_old/objects/58/99b3bdc1fbb90b591145b056cf24304bd7752b b/git_old/objects/58/99b3bdc1fbb90b591145b056cf24304bd7752b new file mode 100644 index 0000000..b2b4a34 Binary files /dev/null and b/git_old/objects/58/99b3bdc1fbb90b591145b056cf24304bd7752b differ diff --git a/git_old/objects/5a/4d99fd22d0db95917f469fac6059f3c0d609d8 b/git_old/objects/5a/4d99fd22d0db95917f469fac6059f3c0d609d8 new file mode 100644 index 0000000..a168ae0 Binary files /dev/null and b/git_old/objects/5a/4d99fd22d0db95917f469fac6059f3c0d609d8 differ diff --git a/git_old/objects/5a/65d0e4bfb7e169a91baf04f20bfee12282e748 b/git_old/objects/5a/65d0e4bfb7e169a91baf04f20bfee12282e748 new file mode 100644 index 0000000..4946ba3 Binary files /dev/null and b/git_old/objects/5a/65d0e4bfb7e169a91baf04f20bfee12282e748 differ diff --git a/git_old/objects/5a/68f84a440e2f261e010c73a1f69587bbef41a7 b/git_old/objects/5a/68f84a440e2f261e010c73a1f69587bbef41a7 new file mode 100644 index 0000000..f3e4c1f Binary files /dev/null and b/git_old/objects/5a/68f84a440e2f261e010c73a1f69587bbef41a7 differ diff --git a/git_old/objects/5a/792965bdeb20b9ce1b867ca6fdf302b273b781 b/git_old/objects/5a/792965bdeb20b9ce1b867ca6fdf302b273b781 new file mode 100644 index 0000000..3a1d424 Binary files /dev/null and b/git_old/objects/5a/792965bdeb20b9ce1b867ca6fdf302b273b781 differ diff --git a/git_old/objects/5a/849cad42d2f9a5a456ec5ce188a7958e87c56c b/git_old/objects/5a/849cad42d2f9a5a456ec5ce188a7958e87c56c new file mode 100644 index 0000000..150a74d Binary files /dev/null and b/git_old/objects/5a/849cad42d2f9a5a456ec5ce188a7958e87c56c differ diff --git a/git_old/objects/5a/a9243067c30b2c6891543c82f568ab7fe2342d b/git_old/objects/5a/a9243067c30b2c6891543c82f568ab7fe2342d new file mode 100644 index 0000000..4ccf787 Binary files /dev/null and b/git_old/objects/5a/a9243067c30b2c6891543c82f568ab7fe2342d differ diff --git a/git_old/objects/5b/9d634fc578299644a87717afa5a1b24e6c6d9e b/git_old/objects/5b/9d634fc578299644a87717afa5a1b24e6c6d9e new file mode 100644 index 0000000..60cbbfc Binary files /dev/null and b/git_old/objects/5b/9d634fc578299644a87717afa5a1b24e6c6d9e differ diff --git a/git_old/objects/5c/dabfcf75575ff78cc7e70b7e03a705a6aee007 b/git_old/objects/5c/dabfcf75575ff78cc7e70b7e03a705a6aee007 new file mode 100644 index 0000000..4b0623d Binary files /dev/null and b/git_old/objects/5c/dabfcf75575ff78cc7e70b7e03a705a6aee007 differ diff --git a/git_old/objects/5d/6fae821ea59f171f66b2d21356d68485315a35 b/git_old/objects/5d/6fae821ea59f171f66b2d21356d68485315a35 new file mode 100644 index 0000000..e1d0068 Binary files /dev/null and b/git_old/objects/5d/6fae821ea59f171f66b2d21356d68485315a35 differ diff --git a/git_old/objects/5d/c9541d352de66f48d9cd3aaf6b36cec371d5c9 b/git_old/objects/5d/c9541d352de66f48d9cd3aaf6b36cec371d5c9 new file mode 100644 index 0000000..74b314e Binary files /dev/null and b/git_old/objects/5d/c9541d352de66f48d9cd3aaf6b36cec371d5c9 differ diff --git a/git_old/objects/5e/7730b4a71e0963bb3f4732e0f1111a7a364342 b/git_old/objects/5e/7730b4a71e0963bb3f4732e0f1111a7a364342 new file mode 100644 index 0000000..b728d9e Binary files /dev/null and b/git_old/objects/5e/7730b4a71e0963bb3f4732e0f1111a7a364342 differ diff --git a/git_old/objects/5f/140ebc786cf395c36137a01e5adf739e146098 b/git_old/objects/5f/140ebc786cf395c36137a01e5adf739e146098 new file mode 100644 index 0000000..20874a5 Binary files /dev/null and b/git_old/objects/5f/140ebc786cf395c36137a01e5adf739e146098 differ diff --git a/git_old/objects/5f/5302556988709b8bf1572e415c875e8504c2cc b/git_old/objects/5f/5302556988709b8bf1572e415c875e8504c2cc new file mode 100644 index 0000000..e7d1cc4 Binary files /dev/null and b/git_old/objects/5f/5302556988709b8bf1572e415c875e8504c2cc differ diff --git a/git_old/objects/60/184dac51e178a3a76c0f971331cbb916afe519 b/git_old/objects/60/184dac51e178a3a76c0f971331cbb916afe519 new file mode 100644 index 0000000..7ca22b1 Binary files /dev/null and b/git_old/objects/60/184dac51e178a3a76c0f971331cbb916afe519 differ diff --git a/git_old/objects/60/415fcfc0c9b237edca6abe57a1cc7878d9920c b/git_old/objects/60/415fcfc0c9b237edca6abe57a1cc7878d9920c new file mode 100644 index 0000000..f51105b --- /dev/null +++ b/git_old/objects/60/415fcfc0c9b237edca6abe57a1cc7878d9920c @@ -0,0 +1 @@ +xQN0쯘{cNE8mH 4]XzgQi~=)H81Ҍ4o4{*733 I\h<#Lc)U3.l-Bho5 +<-9;5AuW)Xy/VQu8obԢ;dA 0&0Fc9_Lobez/ty2qrY}߲^lIPc܊Z"N8=K6^G=1"G6j6p}J5A_= \ No newline at end of file diff --git a/git_old/objects/61/58b5e7c3016939a7d303b768b4f9ebc53c82f5 b/git_old/objects/61/58b5e7c3016939a7d303b768b4f9ebc53c82f5 new file mode 100644 index 0000000..cbe0f04 Binary files /dev/null and b/git_old/objects/61/58b5e7c3016939a7d303b768b4f9ebc53c82f5 differ diff --git a/git_old/objects/62/37af25893e4e827775548a88aa5d909ad6f872 b/git_old/objects/62/37af25893e4e827775548a88aa5d909ad6f872 new file mode 100644 index 0000000..22c20a7 Binary files /dev/null and b/git_old/objects/62/37af25893e4e827775548a88aa5d909ad6f872 differ diff --git a/git_old/objects/62/5264caca600545d1b812983603aeaa780c8d24 b/git_old/objects/62/5264caca600545d1b812983603aeaa780c8d24 new file mode 100644 index 0000000..a5643dc Binary files /dev/null and b/git_old/objects/62/5264caca600545d1b812983603aeaa780c8d24 differ diff --git a/git_old/objects/63/58aec8c6a4600ff15454dd68757b7ba16a4806 b/git_old/objects/63/58aec8c6a4600ff15454dd68757b7ba16a4806 new file mode 100644 index 0000000..8397702 --- /dev/null +++ b/git_old/objects/63/58aec8c6a4600ff15454dd68757b7ba16a4806 @@ -0,0 +1 @@ +xK@]{kG?,2H2Q0캡D\Wu97N'3#7c5(Uw(%.VS5q u-"Us,RRҜ*/ple-u4]La 5|ncF>Wf'.N0Kpfcׁ;L1FWz8sW`*0tFv6lxJ6U;a(ć^l{yod$MTŗ{#M6niRrK/|?uG}~وx圢>j/rdv#iDLmۓִ(0v]3_2 \ No newline at end of file diff --git a/git_old/objects/63/cb341db7fbef2374fa8bf840bd3790fcd22811 b/git_old/objects/63/cb341db7fbef2374fa8bf840bd3790fcd22811 new file mode 100644 index 0000000..cbbd99b Binary files /dev/null and b/git_old/objects/63/cb341db7fbef2374fa8bf840bd3790fcd22811 differ diff --git a/git_old/objects/63/dc559254702b4f91ac42ec7726404b94b7c050 b/git_old/objects/63/dc559254702b4f91ac42ec7726404b94b7c050 new file mode 100644 index 0000000..cd43858 Binary files /dev/null and b/git_old/objects/63/dc559254702b4f91ac42ec7726404b94b7c050 differ diff --git a/git_old/objects/63/f42ce5a6e97762984fc29b7cae32f81b6d7a90 b/git_old/objects/63/f42ce5a6e97762984fc29b7cae32f81b6d7a90 new file mode 100644 index 0000000..1300be0 Binary files /dev/null and b/git_old/objects/63/f42ce5a6e97762984fc29b7cae32f81b6d7a90 differ diff --git a/git_old/objects/64/2bdeeb1a0ed99162fbfc27b448c9911f357719 b/git_old/objects/64/2bdeeb1a0ed99162fbfc27b448c9911f357719 new file mode 100644 index 0000000..c2338fb Binary files /dev/null and b/git_old/objects/64/2bdeeb1a0ed99162fbfc27b448c9911f357719 differ diff --git a/git_old/objects/64/84bd4e1686725be3a1087417c53f778fd503ab b/git_old/objects/64/84bd4e1686725be3a1087417c53f778fd503ab new file mode 100644 index 0000000..f2897ca Binary files /dev/null and b/git_old/objects/64/84bd4e1686725be3a1087417c53f778fd503ab differ diff --git a/git_old/objects/64/b98309f071fb6279d145ed42901f2665717eef b/git_old/objects/64/b98309f071fb6279d145ed42901f2665717eef new file mode 100644 index 0000000..2fd3136 Binary files /dev/null and b/git_old/objects/64/b98309f071fb6279d145ed42901f2665717eef differ diff --git a/git_old/objects/64/bf4a4e85d165b2dc692eed5583face9739ac94 b/git_old/objects/64/bf4a4e85d165b2dc692eed5583face9739ac94 new file mode 100644 index 0000000..da51457 --- /dev/null +++ b/git_old/objects/64/bf4a4e85d165b2dc692eed5583face9739ac94 @@ -0,0 +1 @@ +xKS@=ﯘ;nBZbQ dD$ʓ'6_UOMwEg5&K!nJ$ 'LJ*5MP)g 锠]\Hlp$uyӨ4zLiny3nPoCojyij*7*-;ШΘIL̈́KS2=4?pOExly[6^Ԝ%)zcEU?4+k/G@XswtLc׉̩<@Ni B]sYuu '*VQ^N4T g#2z0ϔ#x@?nA8V$84꘢/HM \ No newline at end of file diff --git a/git_old/objects/64/d35b3c9f9647a1e1c983c789efb821cf19c09f b/git_old/objects/64/d35b3c9f9647a1e1c983c789efb821cf19c09f new file mode 100644 index 0000000..034ae6a --- /dev/null +++ b/git_old/objects/64/d35b3c9f9647a1e1c983c789efb821cf19c09f @@ -0,0 +1,3 @@ +xKo@{OGIVMcRs[QcRO=unFUWUo]5 OMiڐ +3K&fܤYTlqb2N '̔I5C2l&3컢n 쓲yQh? xˮ'UW?DX3A3ѝ;v)o?LBwh3hj;w7&].RxksϚd}-H^֟ 6㨥j3`jXFe?O~p diff --git a/git_old/objects/6b/cc89a3b3f87a2d02aa6d4dc94b6b345ac6befc b/git_old/objects/6b/cc89a3b3f87a2d02aa6d4dc94b6b345ac6befc new file mode 100644 index 0000000..6a768c2 Binary files /dev/null and b/git_old/objects/6b/cc89a3b3f87a2d02aa6d4dc94b6b345ac6befc differ diff --git a/git_old/objects/6c/6d68d360d4aa02a5b68818943d7de258743c7b b/git_old/objects/6c/6d68d360d4aa02a5b68818943d7de258743c7b new file mode 100644 index 0000000..4b6aaa1 Binary files /dev/null and b/git_old/objects/6c/6d68d360d4aa02a5b68818943d7de258743c7b differ diff --git a/git_old/objects/6e/f373128af47d42a9a42f0286024c1b2fd230f2 b/git_old/objects/6e/f373128af47d42a9a42f0286024c1b2fd230f2 new file mode 100644 index 0000000..6803788 Binary files /dev/null and b/git_old/objects/6e/f373128af47d42a9a42f0286024c1b2fd230f2 differ diff --git a/git_old/objects/6f/98d24f974fb47d69508b49570d2666b6fdaf2f b/git_old/objects/6f/98d24f974fb47d69508b49570d2666b6fdaf2f new file mode 100644 index 0000000..de9e509 --- /dev/null +++ b/git_old/objects/6f/98d24f974fb47d69508b49570d2666b6fdaf2f @@ -0,0 +1,2 @@ +x+)JMU026c040031QK//620 JMLMMa9`|\to>&*IC3 DZE<_|5 PH,.N-)fdU+޶8x! +2 *t.m{` *JRs rKRDL~gfBU.l)E}Z~:%={Ў.N[ \ No newline at end of file diff --git a/git_old/objects/6f/d92680fe5c3a35b42686b3fef7165332bd681f b/git_old/objects/6f/d92680fe5c3a35b42686b3fef7165332bd681f new file mode 100644 index 0000000..6c29c6b Binary files /dev/null and b/git_old/objects/6f/d92680fe5c3a35b42686b3fef7165332bd681f differ diff --git a/git_old/objects/6f/dfd4ec1437ba5b6fb669e0e0b3ece8336898bc b/git_old/objects/6f/dfd4ec1437ba5b6fb669e0e0b3ece8336898bc new file mode 100644 index 0000000..a3e30db --- /dev/null +++ b/git_old/objects/6f/dfd4ec1437ba5b6fb669e0e0b3ece8336898bc @@ -0,0 +1,3 @@ +xMo@{_ޣԻ]`٨ ŎkLi Wb:SOfڟ_W-zFIf6ŷnE|BD_[mn@U.m]³al   \ No newline at end of file diff --git a/git_old/objects/70/4cce21211feaaf467a0d25b265a67fa8a0aa98 b/git_old/objects/70/4cce21211feaaf467a0d25b265a67fa8a0aa98 new file mode 100644 index 0000000..5c3c2ab Binary files /dev/null and b/git_old/objects/70/4cce21211feaaf467a0d25b265a67fa8a0aa98 differ diff --git a/git_old/objects/70/919e2efd142eee0b01318b8b8897728ab8edcf b/git_old/objects/70/919e2efd142eee0b01318b8b8897728ab8edcf new file mode 100644 index 0000000..c3ef07f --- /dev/null +++ b/git_old/objects/70/919e2efd142eee0b01318b8b8897728ab8edcf @@ -0,0 +1,2 @@ +xr0{H+ tʟ ƌS @'}\*Wݻv9E ,̾LRҤ1,QUA1d7`ۂˈɅ‹c͉Ad)Ts~q|o0gzοn`l;&ghIVὼ0 bH7=f#@Yz]6IҴJ,ur;wJoG^eN}?ǕǺ=0[#u,gyč~b=s+)nͷD_X1 +5P%M., *C DVZ=N_( D \ No newline at end of file diff --git a/git_old/objects/70/abd6eae6b8f74abac9247d1c92249dab3b9ea9 b/git_old/objects/70/abd6eae6b8f74abac9247d1c92249dab3b9ea9 new file mode 100644 index 0000000..7f54ee8 Binary files /dev/null and b/git_old/objects/70/abd6eae6b8f74abac9247d1c92249dab3b9ea9 differ diff --git a/git_old/objects/71/342270b03edd87f973af2a5c18348c4646ccd1 b/git_old/objects/71/342270b03edd87f973af2a5c18348c4646ccd1 new file mode 100644 index 0000000..b5f5089 Binary files /dev/null and b/git_old/objects/71/342270b03edd87f973af2a5c18348c4646ccd1 differ diff --git a/git_old/objects/71/a87e0473af24114f7f524c4ed23159175a8435 b/git_old/objects/71/a87e0473af24114f7f524c4ed23159175a8435 new file mode 100644 index 0000000..559181f Binary files /dev/null and b/git_old/objects/71/a87e0473af24114f7f524c4ed23159175a8435 differ diff --git a/git_old/objects/72/cfb776e18bcc81a8d04f447db560f71e8b3b8a b/git_old/objects/72/cfb776e18bcc81a8d04f447db560f71e8b3b8a new file mode 100644 index 0000000..1d3e218 Binary files /dev/null and b/git_old/objects/72/cfb776e18bcc81a8d04f447db560f71e8b3b8a differ diff --git a/git_old/objects/73/1ae7e1509a543fb093657713df4f0446e69abc b/git_old/objects/73/1ae7e1509a543fb093657713df4f0446e69abc new file mode 100644 index 0000000..499b320 Binary files /dev/null and b/git_old/objects/73/1ae7e1509a543fb093657713df4f0446e69abc differ diff --git a/git_old/objects/73/2b91c4ccd6abb73839e85d6f72775abc8639d3 b/git_old/objects/73/2b91c4ccd6abb73839e85d6f72775abc8639d3 new file mode 100644 index 0000000..9bbc4c5 Binary files /dev/null and b/git_old/objects/73/2b91c4ccd6abb73839e85d6f72775abc8639d3 differ diff --git a/git_old/objects/73/3f85d115786309dd9a66acc4fc0a3e8f00e37b b/git_old/objects/73/3f85d115786309dd9a66acc4fc0a3e8f00e37b new file mode 100644 index 0000000..e34d4e9 Binary files /dev/null and b/git_old/objects/73/3f85d115786309dd9a66acc4fc0a3e8f00e37b differ diff --git a/git_old/objects/73/8750ea3b82957269faa4adb4b8e78a7c11771a b/git_old/objects/73/8750ea3b82957269faa4adb4b8e78a7c11771a new file mode 100644 index 0000000..6c8ea9a Binary files /dev/null and b/git_old/objects/73/8750ea3b82957269faa4adb4b8e78a7c11771a differ diff --git a/git_old/objects/73/f857ac510734e728172a220864ad1f4f00c25f b/git_old/objects/73/f857ac510734e728172a220864ad1f4f00c25f new file mode 100644 index 0000000..50fa0d1 Binary files /dev/null and b/git_old/objects/73/f857ac510734e728172a220864ad1f4f00c25f differ diff --git a/git_old/objects/74/36949b674364e88454c4714457774cb574a512 b/git_old/objects/74/36949b674364e88454c4714457774cb574a512 new file mode 100644 index 0000000..8431b21 --- /dev/null +++ b/git_old/objects/74/36949b674364e88454c4714457774cb574a512 @@ -0,0 +1,3 @@ +xIo0{x"-d /Z(ŋdHNzʻ 03OvCr/W +]ic,&!%mAù, cb1baWQN 2-FIiPd"Cea*S^p뮇tMA 5ncF~UGMv@-LSѕ^3j9wuy~ +"H`>g hz\a]Rv;96f^ ʦK}&?čpӰ9a˲1u*{[\/ۭ;6teE:6$ %ryJ~q >6j.H^V0nOҵsyXp_ \ No newline at end of file diff --git a/git_old/objects/74/398b76a80ef779afcdcb5751a034874533f005 b/git_old/objects/74/398b76a80ef779afcdcb5751a034874533f005 new file mode 100644 index 0000000..fc19626 Binary files /dev/null and b/git_old/objects/74/398b76a80ef779afcdcb5751a034874533f005 differ diff --git a/git_old/objects/74/858fe52f4e2320bd8c4149785952561d2ce0b6 b/git_old/objects/74/858fe52f4e2320bd8c4149785952561d2ce0b6 new file mode 100644 index 0000000..7c95a7d --- /dev/null +++ b/git_old/objects/74/858fe52f4e2320bd8c4149785952561d2ce0b6 @@ -0,0 +1,2 @@ +xr0{H+ A'l7ul|Ǐ&r$UWg=g նOlL6RdܲƜduՀ̱,L,J)q #9D e,lV9%AYojϛZ{1'ec>\q;qm7,u# +W¥sf FzwEz\Ł742JptP=o^D_"NaI,gʂ+$UFhEU9+ITToo)e \ No newline at end of file diff --git a/git_old/objects/74/bdffc5d6f7d2eccd8dd65ef4e0fa805e2ae4e0 b/git_old/objects/74/bdffc5d6f7d2eccd8dd65ef4e0fa805e2ae4e0 new file mode 100644 index 0000000..f7ee6a0 Binary files /dev/null and b/git_old/objects/74/bdffc5d6f7d2eccd8dd65ef4e0fa805e2ae4e0 differ diff --git a/git_old/objects/74/de3aadfb2dcf5dedc5841d0fc0adff1ce33b1c b/git_old/objects/74/de3aadfb2dcf5dedc5841d0fc0adff1ce33b1c new file mode 100644 index 0000000..1f62be3 Binary files /dev/null and b/git_old/objects/74/de3aadfb2dcf5dedc5841d0fc0adff1ce33b1c differ diff --git a/git_old/objects/75/2536e25bc0370a7b6acf450ad0e9848db7790c b/git_old/objects/75/2536e25bc0370a7b6acf450ad0e9848db7790c new file mode 100644 index 0000000..bcba26c Binary files /dev/null and b/git_old/objects/75/2536e25bc0370a7b6acf450ad0e9848db7790c differ diff --git a/git_old/objects/75/dd9446bbbd23f227f9b242bc76cb60c849e81f b/git_old/objects/75/dd9446bbbd23f227f9b242bc76cb60c849e81f new file mode 100644 index 0000000..9922bbd Binary files /dev/null and b/git_old/objects/75/dd9446bbbd23f227f9b242bc76cb60c849e81f differ diff --git a/git_old/objects/75/f08edff32b864384903cc3bf1d38e9d45d6e2a b/git_old/objects/75/f08edff32b864384903cc3bf1d38e9d45d6e2a new file mode 100644 index 0000000..66eb7a6 Binary files /dev/null and b/git_old/objects/75/f08edff32b864384903cc3bf1d38e9d45d6e2a differ diff --git a/git_old/objects/76/3af71d5be4cbbeecff96955e61ac7eaabed824 b/git_old/objects/76/3af71d5be4cbbeecff96955e61ac7eaabed824 new file mode 100644 index 0000000..dc5962c --- /dev/null +++ b/git_old/objects/76/3af71d5be4cbbeecff96955e61ac7eaabed824 @@ -0,0 +1,3 @@ +xn@8{+0\: +8`03߾v#uU#j6^JaR(t6m&DJa:^F6qe:q$ 65&8:6]ƶ!TcK{m^'_@64Sstx&FoG͹Twys?`! ?s.\am.mZi9BԺ `5&q⺋YCk'Xyw?63 $8t +U_RjRmjC3 |pZmfcӎܯsxR,>E0-%G_iۿ ~+Z~{m> .gFibXI \ No newline at end of file diff --git a/git_old/objects/76/a9df9a865ebdcacfa3ed947d88905d2cc8cc85 b/git_old/objects/76/a9df9a865ebdcacfa3ed947d88905d2cc8cc85 new file mode 100644 index 0000000..d8c8547 Binary files /dev/null and b/git_old/objects/76/a9df9a865ebdcacfa3ed947d88905d2cc8cc85 differ diff --git a/git_old/objects/77/41db960cf04b34566240b95611777b7325673a b/git_old/objects/77/41db960cf04b34566240b95611777b7325673a new file mode 100644 index 0000000..4debbf8 Binary files /dev/null and b/git_old/objects/77/41db960cf04b34566240b95611777b7325673a differ diff --git a/git_old/objects/77/4fa9c4bbce68d8585a2a71a370f3d244e95acc b/git_old/objects/77/4fa9c4bbce68d8585a2a71a370f3d244e95acc new file mode 100644 index 0000000..771e282 --- /dev/null +++ b/git_old/objects/77/4fa9c4bbce68d8585a2a71a370f3d244e95acc @@ -0,0 +1 @@ +xMo@{_1(7JĉCl17Xn[2#:~rdW(E|)vݾi60SȢ 8JUU9 +s:_}_מ}b^CJm#k`sYOb#`]sv =p͚{ϯ"E^܇br[:v::mwFjAtDZ5~i?9}@ E҄,TZ90qax \ No newline at end of file diff --git a/git_old/objects/83/3e8bd6a6c5ecd4255222187e6c808ac34c3c56 b/git_old/objects/83/3e8bd6a6c5ecd4255222187e6c808ac34c3c56 new file mode 100644 index 0000000..a3e4d85 Binary files /dev/null and b/git_old/objects/83/3e8bd6a6c5ecd4255222187e6c808ac34c3c56 differ diff --git a/git_old/objects/83/6522c6a122c3dec88a7b5b4df4bc1219213f66 b/git_old/objects/83/6522c6a122c3dec88a7b5b4df4bc1219213f66 new file mode 100644 index 0000000..30e997b Binary files /dev/null and b/git_old/objects/83/6522c6a122c3dec88a7b5b4df4bc1219213f66 differ diff --git a/git_old/objects/84/91235da0c8ac5d7f94be97ccf9be16c697bad0 b/git_old/objects/84/91235da0c8ac5d7f94be97ccf9be16c697bad0 new file mode 100644 index 0000000..7b2bb6e --- /dev/null +++ b/git_old/objects/84/91235da0c8ac5d7f94be97ccf9be16c697bad0 @@ -0,0 +1 @@ +xr0{S=T2P''cun1ngzʩ{o_6Mm/mKIRq,NtGTy#Xq]X'f*]Z#AiIp(q&ء j;(^wWm_mY0vɽM56߁ 0k`|/DtYA1grpdz?vZSxuիraܡb~{;%phἥWIj6~7~a`W$xl.D ?~L,mOȱYeУnܕ fg.HRECɌDI \ No newline at end of file diff --git a/git_old/objects/84/c791250d40f1405f2fc5ee4828d7b45944b305 b/git_old/objects/84/c791250d40f1405f2fc5ee4828d7b45944b305 new file mode 100644 index 0000000..1bc24e5 Binary files /dev/null and b/git_old/objects/84/c791250d40f1405f2fc5ee4828d7b45944b305 differ diff --git a/git_old/objects/85/4241a2fdd96148da3a119a299b57b1876c229e b/git_old/objects/85/4241a2fdd96148da3a119a299b57b1876c229e new file mode 100644 index 0000000..f45da48 Binary files /dev/null and b/git_old/objects/85/4241a2fdd96148da3a119a299b57b1876c229e differ diff --git a/git_old/objects/85/dbf982aa128cf571ded6e781b4818e595eb3b3 b/git_old/objects/85/dbf982aa128cf571ded6e781b4818e595eb3b3 new file mode 100644 index 0000000..9c32f70 --- /dev/null +++ b/git_old/objects/85/dbf982aa128cf571ded6e781b4818e595eb3b3 @@ -0,0 +1,3 @@ +xKo@_qUy񘪩 +"N`ŻafScaڭUVIG:\յΧ7d%ژZ(!uMU\WfD}7 5bThɕhq-=i1z(och/kLN dCs \/Y +YA1`̣{| B͋_]7s^ v򄦅OGYNׯiis͒ bLےmʼm_g.%?!M鉞ĢՋ[d2_ Ht*'C\/mR_>NԼnO}һ u|lƃ! \ No newline at end of file diff --git a/git_old/objects/86/c5d0dfeb6b7db23eaa3b1e878d0e52d11b2dce b/git_old/objects/86/c5d0dfeb6b7db23eaa3b1e878d0e52d11b2dce new file mode 100644 index 0000000..a9858e2 Binary files /dev/null and b/git_old/objects/86/c5d0dfeb6b7db23eaa3b1e878d0e52d11b2dce differ diff --git a/git_old/objects/86/dbd7dfd2397e4568ff39fea7c7c8829966c68a b/git_old/objects/86/dbd7dfd2397e4568ff39fea7c7c8829966c68a new file mode 100644 index 0000000..332f880 Binary files /dev/null and b/git_old/objects/86/dbd7dfd2397e4568ff39fea7c7c8829966c68a differ diff --git a/git_old/objects/88/14d244e1a2de1f6c66b0f64a02eac026c46f96 b/git_old/objects/88/14d244e1a2de1f6c66b0f64a02eac026c46f96 new file mode 100644 index 0000000..e8b8edb Binary files /dev/null and b/git_old/objects/88/14d244e1a2de1f6c66b0f64a02eac026c46f96 differ diff --git a/git_old/objects/88/83183ad5278e7585367f2fb5aa5a02ba367780 b/git_old/objects/88/83183ad5278e7585367f2fb5aa5a02ba367780 new file mode 100644 index 0000000..eb42fb6 Binary files /dev/null and b/git_old/objects/88/83183ad5278e7585367f2fb5aa5a02ba367780 differ diff --git a/git_old/objects/89/1b3d762d89c560238cb2fdb7851fd5281b39f5 b/git_old/objects/89/1b3d762d89c560238cb2fdb7851fd5281b39f5 new file mode 100644 index 0000000..6ac6483 --- /dev/null +++ b/git_old/objects/89/1b3d762d89c560238cb2fdb7851fd5281b39f5 @@ -0,0 +1 @@ +xr@ {OHae;I,:&>߱&PXOHgzիJF6u]):)H L\0- v.2#gIl,)Fmɋ̄5'X6&8TǘPJq* l:8 /~.4ߋJ6#԰ ffit7]E_p7U[ :h}r v{/v$e敲8Ts"rqh-~8)lGDdbzѕEG+|z[.̲mݼMys>j~D~>D/\K J'7.qT)V]f:^ \ No newline at end of file diff --git a/git_old/objects/89/83daa707d9a4fb039788a79e24adc8485b564f b/git_old/objects/89/83daa707d9a4fb039788a79e24adc8485b564f new file mode 100644 index 0000000..aecdb00 Binary files /dev/null and b/git_old/objects/89/83daa707d9a4fb039788a79e24adc8485b564f differ diff --git a/git_old/objects/89/a544d69ea45153109b414a68750452316b6559 b/git_old/objects/89/a544d69ea45153109b414a68750452316b6559 new file mode 100644 index 0000000..5c0a5aa Binary files /dev/null and b/git_old/objects/89/a544d69ea45153109b414a68750452316b6559 differ diff --git a/git_old/objects/89/c583301322219151b701e9be424255a365f8d1 b/git_old/objects/89/c583301322219151b701e9be424255a365f8d1 new file mode 100644 index 0000000..1e9c3f4 --- /dev/null +++ b/git_old/objects/89/c583301322219151b701e9be424255a365f8d1 @@ -0,0 +1 @@ +xAo0w߫.cu$(AihfcH@/]vڻ^ќϵNkZb|L9FKOkBt%FVtbJz0fW5H j:Yw0o;?VZ4p=ue[u yz,nԋ]Fe tx9f*3y-vCu<ߢIu1]nܟy&{匇|T._x>YfGVR1Eƫؾ5q:T܎0JX (N0ꮯK~: \ No newline at end of file diff --git a/git_old/objects/89/cc1b528646e3b64eb4cb15453285a02d68d625 b/git_old/objects/89/cc1b528646e3b64eb4cb15453285a02d68d625 new file mode 100644 index 0000000..15574d9 Binary files /dev/null and b/git_old/objects/89/cc1b528646e3b64eb4cb15453285a02d68d625 differ diff --git a/git_old/objects/8a/2bf492de2174d48daf9edc9fb583fae01c63c1 b/git_old/objects/8a/2bf492de2174d48daf9edc9fb583fae01c63c1 new file mode 100644 index 0000000..39ec8ad Binary files /dev/null and b/git_old/objects/8a/2bf492de2174d48daf9edc9fb583fae01c63c1 differ diff --git a/git_old/objects/8b/5e59cf28493549cb42afc3e16454c37449fd54 b/git_old/objects/8b/5e59cf28493549cb42afc3e16454c37449fd54 new file mode 100644 index 0000000..4c2d501 Binary files /dev/null and b/git_old/objects/8b/5e59cf28493549cb42afc3e16454c37449fd54 differ diff --git a/git_old/objects/8b/8ec8804b3c2099b1adc83b34df07b6ae8bff28 b/git_old/objects/8b/8ec8804b3c2099b1adc83b34df07b6ae8bff28 new file mode 100644 index 0000000..324181c Binary files /dev/null and b/git_old/objects/8b/8ec8804b3c2099b1adc83b34df07b6ae8bff28 differ diff --git a/git_old/objects/8c/892f2923ab2ce8becff0ccdae223e9823c3109 b/git_old/objects/8c/892f2923ab2ce8becff0ccdae223e9823c3109 new file mode 100644 index 0000000..3805ed5 Binary files /dev/null and b/git_old/objects/8c/892f2923ab2ce8becff0ccdae223e9823c3109 differ diff --git a/git_old/objects/8c/b0486cecae1f96a9afa2c0676a324013a5d387 b/git_old/objects/8c/b0486cecae1f96a9afa2c0676a324013a5d387 new file mode 100644 index 0000000..24324a9 Binary files /dev/null and b/git_old/objects/8c/b0486cecae1f96a9afa2c0676a324013a5d387 differ diff --git a/git_old/objects/8c/b13b983635ab58c179b0a99b84f0257f76d246 b/git_old/objects/8c/b13b983635ab58c179b0a99b84f0257f76d246 new file mode 100644 index 0000000..c8c6076 Binary files /dev/null and b/git_old/objects/8c/b13b983635ab58c179b0a99b84f0257f76d246 differ diff --git a/git_old/objects/8e/7cf66d812cd455f82f601e4323d0686fadc494 b/git_old/objects/8e/7cf66d812cd455f82f601e4323d0686fadc494 new file mode 100644 index 0000000..2bba98d Binary files /dev/null and b/git_old/objects/8e/7cf66d812cd455f82f601e4323d0686fadc494 differ diff --git a/git_old/objects/8f/0911d0a61fcad00748d56083a71bf40bfa16f9 b/git_old/objects/8f/0911d0a61fcad00748d56083a71bf40bfa16f9 new file mode 100644 index 0000000..addd23b Binary files /dev/null and b/git_old/objects/8f/0911d0a61fcad00748d56083a71bf40bfa16f9 differ diff --git a/git_old/objects/90/2885597bc5349f60e199ca24d0d1551e030d95 b/git_old/objects/90/2885597bc5349f60e199ca24d0d1551e030d95 new file mode 100644 index 0000000..e1412a9 --- /dev/null +++ b/git_old/objects/90/2885597bc5349f60e199ca24d0d1551e030d95 @@ -0,0 +1 @@ +xAo0_1+W&E$MBH$7cCK 2&/ٕm>i潧v@YKLIUb^HeXJMp ?('Ru0BǴpJIdC]py:O㙔:Ώw]EG .6Bّއj,O K|Eg#xKÆHuӆLGoܼ}?!hs ؆*vnoSpr \ No newline at end of file diff --git a/git_old/objects/a1/4ddd219d9ac229857f9d8c37826a8397347cbf b/git_old/objects/a1/4ddd219d9ac229857f9d8c37826a8397347cbf new file mode 100644 index 0000000..57a8568 Binary files /dev/null and b/git_old/objects/a1/4ddd219d9ac229857f9d8c37826a8397347cbf differ diff --git a/git_old/objects/a2/204bf553020052a720266b913148d927a6586c b/git_old/objects/a2/204bf553020052a720266b913148d927a6586c new file mode 100644 index 0000000..5f41c34 Binary files /dev/null and b/git_old/objects/a2/204bf553020052a720266b913148d927a6586c differ diff --git a/git_old/objects/a2/21d413025718a0f3712736ca3f7970f71c34bf b/git_old/objects/a2/21d413025718a0f3712736ca3f7970f71c34bf new file mode 100644 index 0000000..a59a0ab Binary files /dev/null and b/git_old/objects/a2/21d413025718a0f3712736ca3f7970f71c34bf differ diff --git a/git_old/objects/a2/c2f2a52247cb61045905f141de3f025588e197 b/git_old/objects/a2/c2f2a52247cb61045905f141de3f025588e197 new file mode 100644 index 0000000..795e2fe Binary files /dev/null and b/git_old/objects/a2/c2f2a52247cb61045905f141de3f025588e197 differ diff --git a/git_old/objects/a2/d9c4fd72beaa648521b016bf1b069d7914e4ae b/git_old/objects/a2/d9c4fd72beaa648521b016bf1b069d7914e4ae new file mode 100644 index 0000000..182548b Binary files /dev/null and b/git_old/objects/a2/d9c4fd72beaa648521b016bf1b069d7914e4ae differ diff --git a/git_old/objects/a3/4018edede7d23f3de43361239f37a2f7e01954 b/git_old/objects/a3/4018edede7d23f3de43361239f37a2f7e01954 new file mode 100644 index 0000000..465713e Binary files /dev/null and b/git_old/objects/a3/4018edede7d23f3de43361239f37a2f7e01954 differ diff --git a/git_old/objects/a3/61a59bb8e2ce662aeff0f5ef6a55e7da15544f b/git_old/objects/a3/61a59bb8e2ce662aeff0f5ef6a55e7da15544f new file mode 100644 index 0000000..31b7757 Binary files /dev/null and b/git_old/objects/a3/61a59bb8e2ce662aeff0f5ef6a55e7da15544f differ diff --git a/git_old/objects/a4/4e16034f83a69716aedf31b36a0e406275536b b/git_old/objects/a4/4e16034f83a69716aedf31b36a0e406275536b new file mode 100644 index 0000000..74b29d3 Binary files /dev/null and b/git_old/objects/a4/4e16034f83a69716aedf31b36a0e406275536b differ diff --git a/git_old/objects/a4/7a4d8afa9246c39222f2ba1fce69e114bec32b b/git_old/objects/a4/7a4d8afa9246c39222f2ba1fce69e114bec32b new file mode 100644 index 0000000..34006e2 Binary files /dev/null and b/git_old/objects/a4/7a4d8afa9246c39222f2ba1fce69e114bec32b differ diff --git a/git_old/objects/a6/6f2cba1941f070be6036240e3661f2797483db b/git_old/objects/a6/6f2cba1941f070be6036240e3661f2797483db new file mode 100644 index 0000000..708755f Binary files /dev/null and b/git_old/objects/a6/6f2cba1941f070be6036240e3661f2797483db differ diff --git a/git_old/objects/a6/a78a1b62cfc26c79baf0554da366d87131eb02 b/git_old/objects/a6/a78a1b62cfc26c79baf0554da366d87131eb02 new file mode 100644 index 0000000..9a69ad4 --- /dev/null +++ b/git_old/objects/a6/a78a1b62cfc26c79baf0554da366d87131eb02 @@ -0,0 +1 @@ +xK0YWhn?HJv:JMwn841~=X'{vsP 1@|.ъRʉT)EPR: 6FzTj!F Rg$f#5uA6^\q5|_Ɯ|n۲}½QL%c1q0϶w_/ux:NC~s*!>tmWGp۝Wۧ|yοLiYÿk.e$1(?;exWXAjH uE7^r$ݥ>&y~CQf45bM|WTΏ6Ai k>myk(і \ No newline at end of file diff --git a/git_old/objects/a6/d9c968780f3c2b7c3184894676595cb489d36a b/git_old/objects/a6/d9c968780f3c2b7c3184894676595cb489d36a new file mode 100644 index 0000000..070c90d Binary files /dev/null and b/git_old/objects/a6/d9c968780f3c2b7c3184894676595cb489d36a differ diff --git a/git_old/objects/a7/b3d7dc2c53b3f8ea0e94389f07875924d5cef6 b/git_old/objects/a7/b3d7dc2c53b3f8ea0e94389f07875924d5cef6 new file mode 100644 index 0000000..d761d0b Binary files /dev/null and b/git_old/objects/a7/b3d7dc2c53b3f8ea0e94389f07875924d5cef6 differ diff --git a/git_old/objects/a8/31c92be44eb3138ca4ecae629dd707c37229e4 b/git_old/objects/a8/31c92be44eb3138ca4ecae629dd707c37229e4 new file mode 100644 index 0000000..e28ecb5 Binary files /dev/null and b/git_old/objects/a8/31c92be44eb3138ca4ecae629dd707c37229e4 differ diff --git a/git_old/objects/a8/9bca0c837fe447cfd2cc8fd31b2bd6256488f0 b/git_old/objects/a8/9bca0c837fe447cfd2cc8fd31b2bd6256488f0 new file mode 100644 index 0000000..671e700 Binary files /dev/null and b/git_old/objects/a8/9bca0c837fe447cfd2cc8fd31b2bd6256488f0 differ diff --git a/git_old/objects/a9/975567a0de6054937971332f5aece44209a437 b/git_old/objects/a9/975567a0de6054937971332f5aece44209a437 new file mode 100644 index 0000000..eb20c86 Binary files /dev/null and b/git_old/objects/a9/975567a0de6054937971332f5aece44209a437 differ diff --git a/git_old/objects/a9/f3899d9c22eeb02fdc43db19c08e723c96c2a2 b/git_old/objects/a9/f3899d9c22eeb02fdc43db19c08e723c96c2a2 new file mode 100644 index 0000000..3f236bf Binary files /dev/null and b/git_old/objects/a9/f3899d9c22eeb02fdc43db19c08e723c96c2a2 differ diff --git a/git_old/objects/aa/4ee988c2423689c6a64810d749e24fdc86baf1 b/git_old/objects/aa/4ee988c2423689c6a64810d749e24fdc86baf1 new file mode 100644 index 0000000..428d41d Binary files /dev/null and b/git_old/objects/aa/4ee988c2423689c6a64810d749e24fdc86baf1 differ diff --git a/git_old/objects/aa/e9fc9313bd5af2e98dc0581bd454bccb42ea82 b/git_old/objects/aa/e9fc9313bd5af2e98dc0581bd454bccb42ea82 new file mode 100644 index 0000000..5e12f38 Binary files /dev/null and b/git_old/objects/aa/e9fc9313bd5af2e98dc0581bd454bccb42ea82 differ diff --git a/git_old/objects/ab/4a7a4634bb632808e3f1b58f6855358b5d6b36 b/git_old/objects/ab/4a7a4634bb632808e3f1b58f6855358b5d6b36 new file mode 100644 index 0000000..ef67bdc Binary files /dev/null and b/git_old/objects/ab/4a7a4634bb632808e3f1b58f6855358b5d6b36 differ diff --git a/git_old/objects/ab/8e7d42a5d1e5b0b2f6a92d857b22e2a7993eed b/git_old/objects/ab/8e7d42a5d1e5b0b2f6a92d857b22e2a7993eed new file mode 100644 index 0000000..97a7f71 Binary files /dev/null and b/git_old/objects/ab/8e7d42a5d1e5b0b2f6a92d857b22e2a7993eed differ diff --git a/git_old/objects/ab/a6feefc811c9adf292c36049db8c9f31ceed83 b/git_old/objects/ab/a6feefc811c9adf292c36049db8c9f31ceed83 new file mode 100644 index 0000000..5b98bfe Binary files /dev/null and b/git_old/objects/ab/a6feefc811c9adf292c36049db8c9f31ceed83 differ diff --git a/git_old/objects/ab/c71efcf035e194c75e0f6fb90ce2d8dbce368f b/git_old/objects/ab/c71efcf035e194c75e0f6fb90ce2d8dbce368f new file mode 100644 index 0000000..7df1705 Binary files /dev/null and b/git_old/objects/ab/c71efcf035e194c75e0f6fb90ce2d8dbce368f differ diff --git a/git_old/objects/ab/f40da063b5f831eba7e4ff0b14f1f8e8dcf572 b/git_old/objects/ab/f40da063b5f831eba7e4ff0b14f1f8e8dcf572 new file mode 100644 index 0000000..6c28522 Binary files /dev/null and b/git_old/objects/ab/f40da063b5f831eba7e4ff0b14f1f8e8dcf572 differ diff --git a/git_old/objects/ac/4524be7993ccc51d1c463bd1eaee525924ca86 b/git_old/objects/ac/4524be7993ccc51d1c463bd1eaee525924ca86 new file mode 100644 index 0000000..bac8d6c Binary files /dev/null and b/git_old/objects/ac/4524be7993ccc51d1c463bd1eaee525924ca86 differ diff --git a/git_old/objects/ac/c424cf1c46c818663dc4f7fefab5afbbeccc15 b/git_old/objects/ac/c424cf1c46c818663dc4f7fefab5afbbeccc15 new file mode 100644 index 0000000..f5fa9b9 --- /dev/null +++ b/git_old/objects/ac/c424cf1c46c818663dc4f7fefab5afbbeccc15 @@ -0,0 +1,2 @@ +xOo@{OVòlTkNn^``Xrʩ6?iFoF5糱HvZ/\.Jư";iiNB+ڼӿ,`W˂Ғ+R +Oc'cΜ2+Mۡ0u 2Ϫ9" 7X`F:hM7oD<,`;nrl~NOͦZr/U#ݚa.{Q‡ArE 91OLGu8U5U%9F$=R60*"ق? 2éIz?^0v^+Vmk%ӏҼ|6-)v[f{'ZR#p؛G?l|_-2BgD#N}dˇ u}Gh:8଱/~5 \ No newline at end of file diff --git a/git_old/objects/bc/9de773511554b129ca967861921c9da212f93f b/git_old/objects/bc/9de773511554b129ca967861921c9da212f93f new file mode 100644 index 0000000..f319d12 Binary files /dev/null and b/git_old/objects/bc/9de773511554b129ca967861921c9da212f93f differ diff --git a/git_old/objects/bd/4f0ea1d42a66ffa46554ae625c59f0fc201211 b/git_old/objects/bd/4f0ea1d42a66ffa46554ae625c59f0fc201211 new file mode 100644 index 0000000..c120462 Binary files /dev/null and b/git_old/objects/bd/4f0ea1d42a66ffa46554ae625c59f0fc201211 differ diff --git a/git_old/objects/bd/6b54df06868da790106ed17791e1df41f45269 b/git_old/objects/bd/6b54df06868da790106ed17791e1df41f45269 new file mode 100644 index 0000000..37ae072 Binary files /dev/null and b/git_old/objects/bd/6b54df06868da790106ed17791e1df41f45269 differ diff --git a/git_old/objects/bd/83432c0c895fd6f384a7deb9b8e436c4b209e7 b/git_old/objects/bd/83432c0c895fd6f384a7deb9b8e436c4b209e7 new file mode 100644 index 0000000..0ad2be9 Binary files /dev/null and b/git_old/objects/bd/83432c0c895fd6f384a7deb9b8e436c4b209e7 differ diff --git a/git_old/objects/bd/be181bdde4738b7fc72e9166a99ab787947872 b/git_old/objects/bd/be181bdde4738b7fc72e9166a99ab787947872 new file mode 100644 index 0000000..3b20332 Binary files /dev/null and b/git_old/objects/bd/be181bdde4738b7fc72e9166a99ab787947872 differ diff --git a/git_old/objects/bd/e6574e255347b0de2a9150bb74049eb1e714c2 b/git_old/objects/bd/e6574e255347b0de2a9150bb74049eb1e714c2 new file mode 100644 index 0000000..99ab781 Binary files /dev/null and b/git_old/objects/bd/e6574e255347b0de2a9150bb74049eb1e714c2 differ diff --git a/git_old/objects/be/780a717c36e29cbbbaf31eb0dde9faf186d83e b/git_old/objects/be/780a717c36e29cbbbaf31eb0dde9faf186d83e new file mode 100644 index 0000000..7caac3a Binary files /dev/null and b/git_old/objects/be/780a717c36e29cbbbaf31eb0dde9faf186d83e differ diff --git a/git_old/objects/bf/825434761f262f618cc917b6416fd3e3e1adac b/git_old/objects/bf/825434761f262f618cc917b6416fd3e3e1adac new file mode 100644 index 0000000..5b6ac16 --- /dev/null +++ b/git_old/objects/bf/825434761f262f618cc917b6416fd3e3e1adac @@ -0,0 +1,2 @@ +xKo@_qQaa( +6" F`TꪫOm( T/h,0.B<džm1ÐX4L.Xj ui//Y!IV9ɤagij %Jc1S=u;0|s[t=f_y2[?jvU#k[&;#@qBŇ vWE<yx,N?o&'\ܿW'ZR^ި)\(}@3,&cT'cʣg1?П6z.(2&u RKIؒ \ No newline at end of file diff --git a/git_old/objects/c0/6a6e95d7923427993e9d9520cc998387875ec7 b/git_old/objects/c0/6a6e95d7923427993e9d9520cc998387875ec7 new file mode 100644 index 0000000..da8a4dc --- /dev/null +++ b/git_old/objects/c0/6a6e95d7923427993e9d9520cc998387875ec7 @@ -0,0 +1,2 @@ +xN0EY+Fٴ]*Q UB8XvG_ljiI6!qfΝ;3 WjXB!ﭢ=OC }SGzZS!|A\HS@X%ީ[f"Һ!8-Є祾)]REy> +q+Q>ܱfw6p7ɞ;#2fX*x6g2m]_9sfؐz˸f8F17}l&%U,g'x2dfm߼K>H+R @+G}$M`и%$C3u{[\hw6\zI|z|zL' \ No newline at end of file diff --git a/git_old/objects/c0/9168e192d21f86f2e356f17e66ec5f7f454dd9 b/git_old/objects/c0/9168e192d21f86f2e356f17e66ec5f7f454dd9 new file mode 100644 index 0000000..a8728fd Binary files /dev/null and b/git_old/objects/c0/9168e192d21f86f2e356f17e66ec5f7f454dd9 differ diff --git a/git_old/objects/c0/aa38ac21c26a369b926b775e8f4a6f01196c16 b/git_old/objects/c0/aa38ac21c26a369b926b775e8f4a6f01196c16 new file mode 100644 index 0000000..fbf9d21 Binary files /dev/null and b/git_old/objects/c0/aa38ac21c26a369b926b775e8f4a6f01196c16 differ diff --git a/git_old/objects/c0/ae6bcf7908e689124d45769ff002d8c0578596 b/git_old/objects/c0/ae6bcf7908e689124d45769ff002d8c0578596 new file mode 100644 index 0000000..fc29ecd Binary files /dev/null and b/git_old/objects/c0/ae6bcf7908e689124d45769ff002d8c0578596 differ diff --git a/git_old/objects/c1/109820ad3ffd73668e1261eb146a538002a04c b/git_old/objects/c1/109820ad3ffd73668e1261eb146a538002a04c new file mode 100644 index 0000000..ac44e4d Binary files /dev/null and b/git_old/objects/c1/109820ad3ffd73668e1261eb146a538002a04c differ diff --git a/git_old/objects/c1/8f3c97ae26b2a3e43c672a4ea85232e71b4fbd b/git_old/objects/c1/8f3c97ae26b2a3e43c672a4ea85232e71b4fbd new file mode 100644 index 0000000..bc76f33 Binary files /dev/null and b/git_old/objects/c1/8f3c97ae26b2a3e43c672a4ea85232e71b4fbd differ diff --git a/git_old/objects/c1/f9a1cdfc4e9395236079bb9083bacdb6588c62 b/git_old/objects/c1/f9a1cdfc4e9395236079bb9083bacdb6588c62 new file mode 100644 index 0000000..6c5d1b1 Binary files /dev/null and b/git_old/objects/c1/f9a1cdfc4e9395236079bb9083bacdb6588c62 differ diff --git a/git_old/objects/c2/17e2cdd95c30db3212e81ae7bdd63770ad9816 b/git_old/objects/c2/17e2cdd95c30db3212e81ae7bdd63770ad9816 new file mode 100644 index 0000000..985eeee Binary files /dev/null and b/git_old/objects/c2/17e2cdd95c30db3212e81ae7bdd63770ad9816 differ diff --git a/git_old/objects/c4/674f9ec44cb03114a8b95a0f8e916ab3c66769 b/git_old/objects/c4/674f9ec44cb03114a8b95a0f8e916ab3c66769 new file mode 100644 index 0000000..d5424fb Binary files /dev/null and b/git_old/objects/c4/674f9ec44cb03114a8b95a0f8e916ab3c66769 differ diff --git a/git_old/objects/c5/4fbb6b417a9c704f732a46005309903142343c b/git_old/objects/c5/4fbb6b417a9c704f732a46005309903142343c new file mode 100644 index 0000000..745e9a9 Binary files /dev/null and b/git_old/objects/c5/4fbb6b417a9c704f732a46005309903142343c differ diff --git a/git_old/objects/c5/af862b51912ad56c895196e12cad78e37e66b9 b/git_old/objects/c5/af862b51912ad56c895196e12cad78e37e66b9 new file mode 100644 index 0000000..ff6a4ae Binary files /dev/null and b/git_old/objects/c5/af862b51912ad56c895196e12cad78e37e66b9 differ diff --git a/git_old/objects/c6/b856f372907aa632542f7d0f0f4e2395aadd06 b/git_old/objects/c6/b856f372907aa632542f7d0f0f4e2395aadd06 new file mode 100644 index 0000000..01bc020 Binary files /dev/null and b/git_old/objects/c6/b856f372907aa632542f7d0f0f4e2395aadd06 differ diff --git a/git_old/objects/c6/f629a4f643b04942387a8713047d053512bc17 b/git_old/objects/c6/f629a4f643b04942387a8713047d053512bc17 new file mode 100644 index 0000000..bcb0963 Binary files /dev/null and b/git_old/objects/c6/f629a4f643b04942387a8713047d053512bc17 differ diff --git a/git_old/objects/c7/9a71ea873e187a080250e34aa11b739f9671b5 b/git_old/objects/c7/9a71ea873e187a080250e34aa11b739f9671b5 new file mode 100644 index 0000000..32b0865 Binary files /dev/null and b/git_old/objects/c7/9a71ea873e187a080250e34aa11b739f9671b5 differ diff --git a/git_old/objects/c7/fce8fc4193c4cb2d8aaeb8526a6753b8a256e5 b/git_old/objects/c7/fce8fc4193c4cb2d8aaeb8526a6753b8a256e5 new file mode 100644 index 0000000..0731661 Binary files /dev/null and b/git_old/objects/c7/fce8fc4193c4cb2d8aaeb8526a6753b8a256e5 differ diff --git a/git_old/objects/c8/197047ecf831c0fa54e914b17e6c8a5340d0fd b/git_old/objects/c8/197047ecf831c0fa54e914b17e6c8a5340d0fd new file mode 100644 index 0000000..418e7c9 Binary files /dev/null and b/git_old/objects/c8/197047ecf831c0fa54e914b17e6c8a5340d0fd differ diff --git a/git_old/objects/c8/74e956bda221ef564c6f67ebe95de38944ccea b/git_old/objects/c8/74e956bda221ef564c6f67ebe95de38944ccea new file mode 100644 index 0000000..22c3571 Binary files /dev/null and b/git_old/objects/c8/74e956bda221ef564c6f67ebe95de38944ccea differ diff --git a/git_old/objects/c8/8cc123a859bb911af697705c6d43bc9cae6181 b/git_old/objects/c8/8cc123a859bb911af697705c6d43bc9cae6181 new file mode 100644 index 0000000..0e5b60b Binary files /dev/null and b/git_old/objects/c8/8cc123a859bb911af697705c6d43bc9cae6181 differ diff --git a/git_old/objects/c8/93ab7589878be4b38f79511b09f8a1fa98b2e1 b/git_old/objects/c8/93ab7589878be4b38f79511b09f8a1fa98b2e1 new file mode 100644 index 0000000..d4f0f3d Binary files /dev/null and b/git_old/objects/c8/93ab7589878be4b38f79511b09f8a1fa98b2e1 differ diff --git a/git_old/objects/c8/a6cf7c2f25384068c09a3d514839f8eed74546 b/git_old/objects/c8/a6cf7c2f25384068c09a3d514839f8eed74546 new file mode 100644 index 0000000..594ca4e Binary files /dev/null and b/git_old/objects/c8/a6cf7c2f25384068c09a3d514839f8eed74546 differ diff --git a/git_old/objects/c8/b44f49c3890f55f72f8bb80f92837a56f9f2f8 b/git_old/objects/c8/b44f49c3890f55f72f8bb80f92837a56f9f2f8 new file mode 100644 index 0000000..3bf3f69 Binary files /dev/null and b/git_old/objects/c8/b44f49c3890f55f72f8bb80f92837a56f9f2f8 differ diff --git a/git_old/objects/c8/fea53ca9e8ac90de5b6af0b1e32cf905cf15f0 b/git_old/objects/c8/fea53ca9e8ac90de5b6af0b1e32cf905cf15f0 new file mode 100644 index 0000000..35b44a2 Binary files /dev/null and b/git_old/objects/c8/fea53ca9e8ac90de5b6af0b1e32cf905cf15f0 differ diff --git a/git_old/objects/ca/10374ac7aae3859cf12109c89281b183e3b9d9 b/git_old/objects/ca/10374ac7aae3859cf12109c89281b183e3b9d9 new file mode 100644 index 0000000..23899cf Binary files /dev/null and b/git_old/objects/ca/10374ac7aae3859cf12109c89281b183e3b9d9 differ diff --git a/git_old/objects/ca/4352f7db49bb8bad596f66fdfbb4e4c962f8b5 b/git_old/objects/ca/4352f7db49bb8bad596f66fdfbb4e4c962f8b5 new file mode 100644 index 0000000..70c7f85 Binary files /dev/null and b/git_old/objects/ca/4352f7db49bb8bad596f66fdfbb4e4c962f8b5 differ diff --git a/git_old/objects/ca/5979a451af5cf867ea153267bb20a99a8b20b1 b/git_old/objects/ca/5979a451af5cf867ea153267bb20a99a8b20b1 new file mode 100644 index 0000000..26e52c0 Binary files /dev/null and b/git_old/objects/ca/5979a451af5cf867ea153267bb20a99a8b20b1 differ diff --git a/git_old/objects/cc/d20aecd0b6c73474df26c40fadebddeb088ab2 b/git_old/objects/cc/d20aecd0b6c73474df26c40fadebddeb088ab2 new file mode 100644 index 0000000..17107f4 Binary files /dev/null and b/git_old/objects/cc/d20aecd0b6c73474df26c40fadebddeb088ab2 differ diff --git a/git_old/objects/ce/83a03f442a3aae3f8f2f55891f189f25d5c613 b/git_old/objects/ce/83a03f442a3aae3f8f2f55891f189f25d5c613 new file mode 100644 index 0000000..b5c559e --- /dev/null +++ b/git_old/objects/ce/83a03f442a3aae3f8f2f55891f189f25d5c613 @@ -0,0 +1,5 @@ +xOs0{֧xLBSHum:7O#2zʻofgve]w^)P*w]ɸCW幅9STeBFǴWmϣ3Q0u4 b~vCx>#GfZVj]N#mOO1F:)e:m¨qu򕎻y nՇ'lj],V|ʿBpUWedz\-/M{pÛ$ׇN?8P \ No newline at end of file diff --git a/git_old/objects/d6/a310c6eb539f4e763a29f5a9a15803dfaeda69 b/git_old/objects/d6/a310c6eb539f4e763a29f5a9a15803dfaeda69 new file mode 100644 index 0000000..a6897cc Binary files /dev/null and b/git_old/objects/d6/a310c6eb539f4e763a29f5a9a15803dfaeda69 differ diff --git a/git_old/objects/d7/3261bf108b33ff3f6c17aedeacaafbf400f3bc b/git_old/objects/d7/3261bf108b33ff3f6c17aedeacaafbf400f3bc new file mode 100644 index 0000000..7a88fe2 Binary files /dev/null and b/git_old/objects/d7/3261bf108b33ff3f6c17aedeacaafbf400f3bc differ diff --git a/git_old/objects/d7/f479a5268de629d9a68316be3aa87956813f0f b/git_old/objects/d7/f479a5268de629d9a68316be3aa87956813f0f new file mode 100644 index 0000000..1ff6c94 Binary files /dev/null and b/git_old/objects/d7/f479a5268de629d9a68316be3aa87956813f0f differ diff --git a/git_old/objects/d8/3f60f02af3400883c021ce3d6a3699ee0e2ebd b/git_old/objects/d8/3f60f02af3400883c021ce3d6a3699ee0e2ebd new file mode 100644 index 0000000..4ed7ed2 Binary files /dev/null and b/git_old/objects/d8/3f60f02af3400883c021ce3d6a3699ee0e2ebd differ diff --git a/git_old/objects/d8/ff9f856a527d6d8ed506966cc74610e83807e1 b/git_old/objects/d8/ff9f856a527d6d8ed506966cc74610e83807e1 new file mode 100644 index 0000000..b7c5e76 Binary files /dev/null and b/git_old/objects/d8/ff9f856a527d6d8ed506966cc74610e83807e1 differ diff --git a/git_old/objects/d9/a583e1c0595d65524b9de762b56eb621a8dc24 b/git_old/objects/d9/a583e1c0595d65524b9de762b56eb621a8dc24 new file mode 100644 index 0000000..3ad546c Binary files /dev/null and b/git_old/objects/d9/a583e1c0595d65524b9de762b56eb621a8dc24 differ diff --git a/git_old/objects/da/080642c964581a1ccfe931b918fef44bb0916e b/git_old/objects/da/080642c964581a1ccfe931b918fef44bb0916e new file mode 100644 index 0000000..8d747bb Binary files /dev/null and b/git_old/objects/da/080642c964581a1ccfe931b918fef44bb0916e differ diff --git a/git_old/objects/da/5e25dec17bc438d960395f9f24a64177250764 b/git_old/objects/da/5e25dec17bc438d960395f9f24a64177250764 new file mode 100644 index 0000000..4cc9eab Binary files /dev/null and b/git_old/objects/da/5e25dec17bc438d960395f9f24a64177250764 differ diff --git a/git_old/objects/da/a36670b98405cce189dce2e8cbb81530322c7c b/git_old/objects/da/a36670b98405cce189dce2e8cbb81530322c7c new file mode 100644 index 0000000..d30bdc6 Binary files /dev/null and b/git_old/objects/da/a36670b98405cce189dce2e8cbb81530322c7c differ diff --git a/git_old/objects/db/32a66f3a5b5e6ec402506ed072daa29c797b0e b/git_old/objects/db/32a66f3a5b5e6ec402506ed072daa29c797b0e new file mode 100644 index 0000000..2682c10 Binary files /dev/null and b/git_old/objects/db/32a66f3a5b5e6ec402506ed072daa29c797b0e differ diff --git a/git_old/objects/db/7d5a8de1ac022b220f8ad506754c42cf814a4e b/git_old/objects/db/7d5a8de1ac022b220f8ad506754c42cf814a4e new file mode 100644 index 0000000..e73291f Binary files /dev/null and b/git_old/objects/db/7d5a8de1ac022b220f8ad506754c42cf814a4e differ diff --git a/git_old/objects/dc/0bf81eac631837d28b596a19704ce1d7052c0a b/git_old/objects/dc/0bf81eac631837d28b596a19704ce1d7052c0a new file mode 100644 index 0000000..a7eee33 Binary files /dev/null and b/git_old/objects/dc/0bf81eac631837d28b596a19704ce1d7052c0a differ diff --git a/git_old/objects/df/46cf559dc4315ac91bb8d78c25f1c2b386e537 b/git_old/objects/df/46cf559dc4315ac91bb8d78c25f1c2b386e537 new file mode 100644 index 0000000..d1e5df6 Binary files /dev/null and b/git_old/objects/df/46cf559dc4315ac91bb8d78c25f1c2b386e537 differ diff --git a/git_old/objects/df/4dc2eef37d4c40cbfd519045ab5c8da814ec65 b/git_old/objects/df/4dc2eef37d4c40cbfd519045ab5c8da814ec65 new file mode 100644 index 0000000..62cf7a6 Binary files /dev/null and b/git_old/objects/df/4dc2eef37d4c40cbfd519045ab5c8da814ec65 differ diff --git a/git_old/objects/df/5893ea5b495e3d24863640a8252133dd039b1a b/git_old/objects/df/5893ea5b495e3d24863640a8252133dd039b1a new file mode 100644 index 0000000..4bca63e Binary files /dev/null and b/git_old/objects/df/5893ea5b495e3d24863640a8252133dd039b1a differ diff --git a/git_old/objects/e0/7ab83c56af04007fc1a7d27bd89155b63929f7 b/git_old/objects/e0/7ab83c56af04007fc1a7d27bd89155b63929f7 new file mode 100644 index 0000000..71684bd Binary files /dev/null and b/git_old/objects/e0/7ab83c56af04007fc1a7d27bd89155b63929f7 differ diff --git a/git_old/objects/e0/d78b24fbd391095717743a992359e496ed9c84 b/git_old/objects/e0/d78b24fbd391095717743a992359e496ed9c84 new file mode 100644 index 0000000..630cdb0 Binary files /dev/null and b/git_old/objects/e0/d78b24fbd391095717743a992359e496ed9c84 differ diff --git a/git_old/objects/e1/79cdd26f52bb0d1f8d14bbebd03ca60c4713e0 b/git_old/objects/e1/79cdd26f52bb0d1f8d14bbebd03ca60c4713e0 new file mode 100644 index 0000000..5296ad1 Binary files /dev/null and b/git_old/objects/e1/79cdd26f52bb0d1f8d14bbebd03ca60c4713e0 differ diff --git a/git_old/objects/e1/d53db7c66f8eb90a8427ef906033a065664674 b/git_old/objects/e1/d53db7c66f8eb90a8427ef906033a065664674 new file mode 100644 index 0000000..f0dd63f Binary files /dev/null and b/git_old/objects/e1/d53db7c66f8eb90a8427ef906033a065664674 differ diff --git a/git_old/objects/e2/11ac93d6c900128d0e5c7a3ff3b3f9699e07b4 b/git_old/objects/e2/11ac93d6c900128d0e5c7a3ff3b3f9699e07b4 new file mode 100644 index 0000000..72919e8 Binary files /dev/null and b/git_old/objects/e2/11ac93d6c900128d0e5c7a3ff3b3f9699e07b4 differ diff --git a/git_old/objects/e2/8b41cbfd090da93683a7d15c493ec7388d0878 b/git_old/objects/e2/8b41cbfd090da93683a7d15c493ec7388d0878 new file mode 100644 index 0000000..e2bccaa Binary files /dev/null and b/git_old/objects/e2/8b41cbfd090da93683a7d15c493ec7388d0878 differ diff --git a/git_old/objects/e2/c2e50c75f4de861f284ff94b26a937c74e0939 b/git_old/objects/e2/c2e50c75f4de861f284ff94b26a937c74e0939 new file mode 100644 index 0000000..0bf2846 --- /dev/null +++ b/git_old/objects/e2/c2e50c75f4de861f284ff94b26a937c74e0939 @@ -0,0 +1,2 @@ +xSN0䜯X`D8UʣUAzI'pȏS\ݝ݌ zWE0$ +K# ϣ>o6aD**x_T°+Fw"Xdhl3!YLjug;d!jb碀16!ַ}#JR~}ytTu.ܒOy T&>qT-E=8㔧%LI9B ՠA&4N53K:؉(ђseʕN#9(\9AJ-}}d~FҮaY= ?sv@V>Q.*`c]og@ѣQyIVr{KJjz ".. \ No newline at end of file diff --git a/git_old/objects/e2/c5a4451888cbec10a1724243e3771f579bb31d b/git_old/objects/e2/c5a4451888cbec10a1724243e3771f579bb31d new file mode 100644 index 0000000..db955df Binary files /dev/null and b/git_old/objects/e2/c5a4451888cbec10a1724243e3771f579bb31d differ diff --git a/git_old/objects/e2/c7e030d221b0bd13e158b39efbaa2059f17376 b/git_old/objects/e2/c7e030d221b0bd13e158b39efbaa2059f17376 new file mode 100644 index 0000000..914680d Binary files /dev/null and b/git_old/objects/e2/c7e030d221b0bd13e158b39efbaa2059f17376 differ diff --git a/git_old/objects/e3/5cec5087e1d478486c07be1c6da6317b7a10bf b/git_old/objects/e3/5cec5087e1d478486c07be1c6da6317b7a10bf new file mode 100644 index 0000000..ffbb9da Binary files /dev/null and b/git_old/objects/e3/5cec5087e1d478486c07be1c6da6317b7a10bf differ diff --git a/git_old/objects/e5/4db4d7ac6f705aeda15630c64001cf35d8f31a b/git_old/objects/e5/4db4d7ac6f705aeda15630c64001cf35d8f31a new file mode 100644 index 0000000..eae3904 --- /dev/null +++ b/git_old/objects/e5/4db4d7ac6f705aeda15630c64001cf35d8f31a @@ -0,0 +1,2 @@ +xMo@{_ޣ/BTbkc.1/'J=Թ#43; lM`Ѭb#]X"mAKƹS#9l'(M[(C؎p qJ؊)JNKŹBTw3onfY7S=;}Yiz""C$Wz8K o +*4J!_E9M_`O]wTua^eMz}x؉aX~=Cuo164>)J4{LÇ 'һ/[eݮ.=u,2 ]~ZOݛYHƩ;ó5^r\S[ݴ/d \ No newline at end of file diff --git a/git_old/objects/e5/601ee6b2bfded8f1e29d16a1f20f0eb3f283df b/git_old/objects/e5/601ee6b2bfded8f1e29d16a1f20f0eb3f283df new file mode 100644 index 0000000..3d4f959 Binary files /dev/null and b/git_old/objects/e5/601ee6b2bfded8f1e29d16a1f20f0eb3f283df differ diff --git a/git_old/objects/e5/b710d18f841cca97097d975a409ef25b34b820 b/git_old/objects/e5/b710d18f841cca97097d975a409ef25b34b820 new file mode 100644 index 0000000..1802130 Binary files /dev/null and b/git_old/objects/e5/b710d18f841cca97097d975a409ef25b34b820 differ diff --git a/git_old/objects/e5/b88d5d557c941e58407777e2821e37fe49aa56 b/git_old/objects/e5/b88d5d557c941e58407777e2821e37fe49aa56 new file mode 100644 index 0000000..2f227ef Binary files /dev/null and b/git_old/objects/e5/b88d5d557c941e58407777e2821e37fe49aa56 differ diff --git a/git_old/objects/e6/4656f352b078e3667bdb0b74b97185d746c206 b/git_old/objects/e6/4656f352b078e3667bdb0b74b97185d746c206 new file mode 100644 index 0000000..298b0ec --- /dev/null +++ b/git_old/objects/e6/4656f352b078e3667bdb0b74b97185d746c206 @@ -0,0 +1 @@ +x+)JMU046e040031Q()N-520*c풸!݊LPldQ,e_}g~=i1?yLŦ3 _RxX#)R7 \ No newline at end of file diff --git a/git_old/objects/e6/84f127e0f66192d416fda8bb686f4521ecedf8 b/git_old/objects/e6/84f127e0f66192d416fda8bb686f4521ecedf8 new file mode 100644 index 0000000..dce4e87 Binary files /dev/null and b/git_old/objects/e6/84f127e0f66192d416fda8bb686f4521ecedf8 differ diff --git a/git_old/objects/e7/1beb228761792c46cf09c8c0748c92992abce5 b/git_old/objects/e7/1beb228761792c46cf09c8c0748c92992abce5 new file mode 100644 index 0000000..440d05f Binary files /dev/null and b/git_old/objects/e7/1beb228761792c46cf09c8c0748c92992abce5 differ diff --git a/git_old/objects/e8/3e82f559d605a41d7942cee1bee0bf2c84044d b/git_old/objects/e8/3e82f559d605a41d7942cee1bee0bf2c84044d new file mode 100644 index 0000000..680b40f Binary files /dev/null and b/git_old/objects/e8/3e82f559d605a41d7942cee1bee0bf2c84044d differ diff --git a/git_old/objects/e8/7d7d9f1882ea91cdeff60b732859b0aa8a0d35 b/git_old/objects/e8/7d7d9f1882ea91cdeff60b732859b0aa8a0d35 new file mode 100644 index 0000000..25533d5 Binary files /dev/null and b/git_old/objects/e8/7d7d9f1882ea91cdeff60b732859b0aa8a0d35 differ diff --git a/git_old/objects/e8/e6d2126ba84f34febc8688c20ab677b3701217 b/git_old/objects/e8/e6d2126ba84f34febc8688c20ab677b3701217 new file mode 100644 index 0000000..127e621 Binary files /dev/null and b/git_old/objects/e8/e6d2126ba84f34febc8688c20ab677b3701217 differ diff --git a/git_old/objects/e8/f07b7de161e601405bdc291505242733aca5ba b/git_old/objects/e8/f07b7de161e601405bdc291505242733aca5ba new file mode 100644 index 0000000..7d73ebb Binary files /dev/null and b/git_old/objects/e8/f07b7de161e601405bdc291505242733aca5ba differ diff --git a/git_old/objects/ea/1c538429bc2d3629e6529e1ee9b3e04011848c b/git_old/objects/ea/1c538429bc2d3629e6529e1ee9b3e04011848c new file mode 100644 index 0000000..006a997 Binary files /dev/null and b/git_old/objects/ea/1c538429bc2d3629e6529e1ee9b3e04011848c differ diff --git a/git_old/objects/eb/86cea1771f6107364b93f32159c42425d7ce54 b/git_old/objects/eb/86cea1771f6107364b93f32159c42425d7ce54 new file mode 100644 index 0000000..04c4ef8 Binary files /dev/null and b/git_old/objects/eb/86cea1771f6107364b93f32159c42425d7ce54 differ diff --git a/git_old/objects/ec/1c49fbac011fd3c9f62a5abdd0436f2088d4f4 b/git_old/objects/ec/1c49fbac011fd3c9f62a5abdd0436f2088d4f4 new file mode 100644 index 0000000..10227e1 Binary files /dev/null and b/git_old/objects/ec/1c49fbac011fd3c9f62a5abdd0436f2088d4f4 differ diff --git a/git_old/objects/ec/c4be487c73d9518cee593be2c0b9862ca25503 b/git_old/objects/ec/c4be487c73d9518cee593be2c0b9862ca25503 new file mode 100644 index 0000000..90116cc Binary files /dev/null and b/git_old/objects/ec/c4be487c73d9518cee593be2c0b9862ca25503 differ diff --git a/git_old/objects/ee/d66b793449ef186fed97e5ecc7bb94f77fb2b2 b/git_old/objects/ee/d66b793449ef186fed97e5ecc7bb94f77fb2b2 new file mode 100644 index 0000000..27f8f1d Binary files /dev/null and b/git_old/objects/ee/d66b793449ef186fed97e5ecc7bb94f77fb2b2 differ diff --git a/git_old/objects/ef/119236f76acf224a71d129daf9cd80b9dda324 b/git_old/objects/ef/119236f76acf224a71d129daf9cd80b9dda324 new file mode 100644 index 0000000..84fdc1a Binary files /dev/null and b/git_old/objects/ef/119236f76acf224a71d129daf9cd80b9dda324 differ diff --git a/git_old/objects/ef/1db0012f66492d0d7f91473396f5b1c5634782 b/git_old/objects/ef/1db0012f66492d0d7f91473396f5b1c5634782 new file mode 100644 index 0000000..fcb9299 Binary files /dev/null and b/git_old/objects/ef/1db0012f66492d0d7f91473396f5b1c5634782 differ diff --git a/git_old/objects/f0/227f3baa688aec499926fe04918f8ee1a9b022 b/git_old/objects/f0/227f3baa688aec499926fe04918f8ee1a9b022 new file mode 100644 index 0000000..135b29e Binary files /dev/null and b/git_old/objects/f0/227f3baa688aec499926fe04918f8ee1a9b022 differ diff --git a/git_old/objects/f0/8f5334d45f92239713bef5c393ab7660173f5c b/git_old/objects/f0/8f5334d45f92239713bef5c393ab7660173f5c new file mode 100644 index 0000000..63090f9 Binary files /dev/null and b/git_old/objects/f0/8f5334d45f92239713bef5c393ab7660173f5c differ diff --git a/git_old/objects/f0/a7c54d88daf3ddaf34b3c274ef2fa8dd8120f6 b/git_old/objects/f0/a7c54d88daf3ddaf34b3c274ef2fa8dd8120f6 new file mode 100644 index 0000000..28886d8 Binary files /dev/null and b/git_old/objects/f0/a7c54d88daf3ddaf34b3c274ef2fa8dd8120f6 differ diff --git a/git_old/objects/f0/d3a98226449237b902ae709f2a72a6cdc5c264 b/git_old/objects/f0/d3a98226449237b902ae709f2a72a6cdc5c264 new file mode 100644 index 0000000..7ec8ab2 Binary files /dev/null and b/git_old/objects/f0/d3a98226449237b902ae709f2a72a6cdc5c264 differ diff --git a/git_old/objects/f0/e5336b1b3f6ff3215f9f1e4f81f606de2fca4d b/git_old/objects/f0/e5336b1b3f6ff3215f9f1e4f81f606de2fca4d new file mode 100644 index 0000000..528e496 Binary files /dev/null and b/git_old/objects/f0/e5336b1b3f6ff3215f9f1e4f81f606de2fca4d differ diff --git a/git_old/objects/f1/8c2905996cf3600a9c1826097e1950ae0f1311 b/git_old/objects/f1/8c2905996cf3600a9c1826097e1950ae0f1311 new file mode 100644 index 0000000..c369eb0 Binary files /dev/null and b/git_old/objects/f1/8c2905996cf3600a9c1826097e1950ae0f1311 differ diff --git a/git_old/objects/f1/be7220bf2fe00dcb29422c263629da103ec397 b/git_old/objects/f1/be7220bf2fe00dcb29422c263629da103ec397 new file mode 100644 index 0000000..9f7c03e Binary files /dev/null and b/git_old/objects/f1/be7220bf2fe00dcb29422c263629da103ec397 differ diff --git a/git_old/objects/f2/16db3849fc1c8884e7f4bc382e87da4ff8ef22 b/git_old/objects/f2/16db3849fc1c8884e7f4bc382e87da4ff8ef22 new file mode 100644 index 0000000..ee140e6 Binary files /dev/null and b/git_old/objects/f2/16db3849fc1c8884e7f4bc382e87da4ff8ef22 differ diff --git a/git_old/objects/f2/5d7526f3dadd0c07d352bc2ca99ab46385783c b/git_old/objects/f2/5d7526f3dadd0c07d352bc2ca99ab46385783c new file mode 100644 index 0000000..fb69789 Binary files /dev/null and b/git_old/objects/f2/5d7526f3dadd0c07d352bc2ca99ab46385783c differ diff --git a/git_old/objects/f2/c535f413aca1190220bae759feafc52acae529 b/git_old/objects/f2/c535f413aca1190220bae759feafc52acae529 new file mode 100644 index 0000000..cecbd2a Binary files /dev/null and b/git_old/objects/f2/c535f413aca1190220bae759feafc52acae529 differ diff --git a/git_old/objects/f3/1bb83319e82129fb2853b14bf2deb63f8fc373 b/git_old/objects/f3/1bb83319e82129fb2853b14bf2deb63f8fc373 new file mode 100644 index 0000000..8eb3ad0 Binary files /dev/null and b/git_old/objects/f3/1bb83319e82129fb2853b14bf2deb63f8fc373 differ diff --git a/git_old/objects/f3/aad7497a13f6cceb185801d7c5b85e973d9f97 b/git_old/objects/f3/aad7497a13f6cceb185801d7c5b85e973d9f97 new file mode 100644 index 0000000..91e192e Binary files /dev/null and b/git_old/objects/f3/aad7497a13f6cceb185801d7c5b85e973d9f97 differ diff --git a/git_old/objects/f3/d338201566ce1c15ee3a13ff2ffac04f4feff1 b/git_old/objects/f3/d338201566ce1c15ee3a13ff2ffac04f4feff1 new file mode 100644 index 0000000..adcf2f1 Binary files /dev/null and b/git_old/objects/f3/d338201566ce1c15ee3a13ff2ffac04f4feff1 differ diff --git a/git_old/objects/f4/3288a93f582147df288539babdd2dfce24f4e9 b/git_old/objects/f4/3288a93f582147df288539babdd2dfce24f4e9 new file mode 100644 index 0000000..bcc8cef Binary files /dev/null and b/git_old/objects/f4/3288a93f582147df288539babdd2dfce24f4e9 differ diff --git a/git_old/objects/f4/9182e08c8df10fc9556982f2a2255b8b28a309 b/git_old/objects/f4/9182e08c8df10fc9556982f2a2255b8b28a309 new file mode 100644 index 0000000..7818936 Binary files /dev/null and b/git_old/objects/f4/9182e08c8df10fc9556982f2a2255b8b28a309 differ diff --git a/git_old/objects/f5/591d2b824162c749bb998142bb2a234a9ac7ce b/git_old/objects/f5/591d2b824162c749bb998142bb2a234a9ac7ce new file mode 100644 index 0000000..b98ee0f Binary files /dev/null and b/git_old/objects/f5/591d2b824162c749bb998142bb2a234a9ac7ce differ diff --git a/git_old/objects/f5/7d2590743b6ebe1dbd7bc69a2bb4caafb83121 b/git_old/objects/f5/7d2590743b6ebe1dbd7bc69a2bb4caafb83121 new file mode 100644 index 0000000..13c5c5a --- /dev/null +++ b/git_old/objects/f5/7d2590743b6ebe1dbd7bc69a2bb4caafb83121 @@ -0,0 +1,2 @@ +x+)JMU026c040031QK//620 JMLMMa9`|\to>&*IC3 DZE<_|5 PH,.N-)fdU+޶8x! +2 *ֽ9y݌&_& \QQ[XZ ~Y{gܾs!E}Z~:%={Ўl&aK \ No newline at end of file diff --git a/git_old/objects/f6/320f88fe5e41bcc219889a1ebdcf1b2356410c b/git_old/objects/f6/320f88fe5e41bcc219889a1ebdcf1b2356410c new file mode 100644 index 0000000..c501c48 Binary files /dev/null and b/git_old/objects/f6/320f88fe5e41bcc219889a1ebdcf1b2356410c differ diff --git a/git_old/objects/f6/53e494d0c267419504e2f57754b502298034af b/git_old/objects/f6/53e494d0c267419504e2f57754b502298034af new file mode 100644 index 0000000..147dd22 Binary files /dev/null and b/git_old/objects/f6/53e494d0c267419504e2f57754b502298034af differ diff --git a/git_old/objects/f7/548197856f576335af5ed06216717f149a8db4 b/git_old/objects/f7/548197856f576335af5ed06216717f149a8db4 new file mode 100644 index 0000000..14ab320 Binary files /dev/null and b/git_old/objects/f7/548197856f576335af5ed06216717f149a8db4 differ diff --git a/git_old/objects/f7/765fd3eecbc1545137ec10715439798af98bf6 b/git_old/objects/f7/765fd3eecbc1545137ec10715439798af98bf6 new file mode 100644 index 0000000..1f27346 Binary files /dev/null and b/git_old/objects/f7/765fd3eecbc1545137ec10715439798af98bf6 differ diff --git a/git_old/objects/f7/7b6a69ee50b10b0eb8709a1a823ad6a62d9a62 b/git_old/objects/f7/7b6a69ee50b10b0eb8709a1a823ad6a62d9a62 new file mode 100644 index 0000000..5aea7ad Binary files /dev/null and b/git_old/objects/f7/7b6a69ee50b10b0eb8709a1a823ad6a62d9a62 differ diff --git a/git_old/objects/f8/7830d6bc9160608b1c587096accd07810b564d b/git_old/objects/f8/7830d6bc9160608b1c587096accd07810b564d new file mode 100644 index 0000000..787241c Binary files /dev/null and b/git_old/objects/f8/7830d6bc9160608b1c587096accd07810b564d differ diff --git a/git_old/objects/f9/b9e07742e3bb47cce7a76e2f4ad36845a38294 b/git_old/objects/f9/b9e07742e3bb47cce7a76e2f4ad36845a38294 new file mode 100644 index 0000000..b8ff486 Binary files /dev/null and b/git_old/objects/f9/b9e07742e3bb47cce7a76e2f4ad36845a38294 differ diff --git a/git_old/objects/fa/2ba69a897a0dff6effa2c26d87ecda8b340c68 b/git_old/objects/fa/2ba69a897a0dff6effa2c26d87ecda8b340c68 new file mode 100644 index 0000000..0a0b558 Binary files /dev/null and b/git_old/objects/fa/2ba69a897a0dff6effa2c26d87ecda8b340c68 differ diff --git a/git_old/objects/fa/f2f7c602893e3a3562ae77f050e2b513807111 b/git_old/objects/fa/f2f7c602893e3a3562ae77f050e2b513807111 new file mode 100644 index 0000000..80b443a --- /dev/null +++ b/git_old/objects/fa/f2f7c602893e3a3562ae77f050e2b513807111 @@ -0,0 +1 @@ +xr0{S|/Z N)8Oal|رq8uN;SOfvfwU{<6=P!>VkSDsPWq,hIdIKI-ǒql:>|cv\WP}& Ɯ|g.>'؇;1F7zKWk*PMKXMWͧ%OfGAX,I9i0ɚv?uhqx]`E֑dž% &5QKmx\h8N8y%1qt&uL616=C2gM|nG/0nժUku#FiQ \ No newline at end of file diff --git a/git_old/objects/fb/0299e9151aebb6acfe4e98ca1f279045a5e0ae b/git_old/objects/fb/0299e9151aebb6acfe4e98ca1f279045a5e0ae new file mode 100644 index 0000000..eaf246a Binary files /dev/null and b/git_old/objects/fb/0299e9151aebb6acfe4e98ca1f279045a5e0ae differ diff --git a/git_old/objects/fb/7d9d8f61c3b49be049431e11eba5b78ac8493e b/git_old/objects/fb/7d9d8f61c3b49be049431e11eba5b78ac8493e new file mode 100644 index 0000000..ae3eaa8 Binary files /dev/null and b/git_old/objects/fb/7d9d8f61c3b49be049431e11eba5b78ac8493e differ diff --git a/git_old/objects/fb/f565fc4cb4c31fb5c09f1361616bbe85ba99b4 b/git_old/objects/fb/f565fc4cb4c31fb5c09f1361616bbe85ba99b4 new file mode 100644 index 0000000..954b300 --- /dev/null +++ b/git_old/objects/fb/f565fc4cb4c31fb5c09f1361616bbe85ba99b4 @@ -0,0 +1,2 @@ +x+)JMU041a040031Q(K//520+.Ha\Y)Wٱ?W( +C\*3ĎElJByLq7\1՛G\U޽̢r/ϩ6 \ No newline at end of file diff --git a/git_old/objects/fc/d27def4d62364d82cdf25a05a050f13b18c8c7 b/git_old/objects/fc/d27def4d62364d82cdf25a05a050f13b18c8c7 new file mode 100644 index 0000000..5010139 Binary files /dev/null and b/git_old/objects/fc/d27def4d62364d82cdf25a05a050f13b18c8c7 differ diff --git a/git_old/objects/fc/eb3cf9e4e33a4282f5956ace703cadf46674c3 b/git_old/objects/fc/eb3cf9e4e33a4282f5956ace703cadf46674c3 new file mode 100644 index 0000000..6c86ed4 Binary files /dev/null and b/git_old/objects/fc/eb3cf9e4e33a4282f5956ace703cadf46674c3 differ diff --git a/git_old/objects/fe/56697d4e0dbd2d717e38018dfa9ca76f473e9e b/git_old/objects/fe/56697d4e0dbd2d717e38018dfa9ca76f473e9e new file mode 100644 index 0000000..07ed76d Binary files /dev/null and b/git_old/objects/fe/56697d4e0dbd2d717e38018dfa9ca76f473e9e differ diff --git a/git_old/objects/fe/9d57b57b36d601164ccfdf88cc5e3e8f643703 b/git_old/objects/fe/9d57b57b36d601164ccfdf88cc5e3e8f643703 new file mode 100644 index 0000000..ef4fbbe Binary files /dev/null and b/git_old/objects/fe/9d57b57b36d601164ccfdf88cc5e3e8f643703 differ diff --git a/git_old/objects/fe/ae8cda9546c70c71a42e6786c582310bcc88a9 b/git_old/objects/fe/ae8cda9546c70c71a42e6786c582310bcc88a9 new file mode 100644 index 0000000..595d9a8 Binary files /dev/null and b/git_old/objects/fe/ae8cda9546c70c71a42e6786c582310bcc88a9 differ diff --git a/git_old/objects/fe/fe122359d22f6ae90c9082807cdb605d500ef2 b/git_old/objects/fe/fe122359d22f6ae90c9082807cdb605d500ef2 new file mode 100644 index 0000000..5cbbcc6 Binary files /dev/null and b/git_old/objects/fe/fe122359d22f6ae90c9082807cdb605d500ef2 differ diff --git a/git_old/objects/ff/234edcaa0784f524f294869343bc82cd801b4a b/git_old/objects/ff/234edcaa0784f524f294869343bc82cd801b4a new file mode 100644 index 0000000..64704c7 Binary files /dev/null and b/git_old/objects/ff/234edcaa0784f524f294869343bc82cd801b4a differ diff --git a/git_old/objects/pack/pack-d294ede28c7839a932793d09a401cad241e0e439.idx b/git_old/objects/pack/pack-d294ede28c7839a932793d09a401cad241e0e439.idx new file mode 100644 index 0000000..04662f1 Binary files /dev/null and b/git_old/objects/pack/pack-d294ede28c7839a932793d09a401cad241e0e439.idx differ diff --git a/git_old/objects/pack/pack-d294ede28c7839a932793d09a401cad241e0e439.pack b/git_old/objects/pack/pack-d294ede28c7839a932793d09a401cad241e0e439.pack new file mode 100644 index 0000000..e61cbc7 Binary files /dev/null and b/git_old/objects/pack/pack-d294ede28c7839a932793d09a401cad241e0e439.pack differ diff --git a/git_old/packed-refs b/git_old/packed-refs new file mode 100644 index 0000000..1390b42 --- /dev/null +++ b/git_old/packed-refs @@ -0,0 +1,6 @@ +# pack-refs with: peeled fully-peeled sorted +812cdcdce8ac2ac45125eb02b40a6e8ae9739bbb refs/remotes/origin/az-cli +4a0b4ab1fb88e3c87565bc645a2837593612d56f refs/remotes/origin/main +b2bf917d661e3e1f48a3ca9414ebd3c8dcef2513 refs/remotes/origin/rust-cargo +6c68f398472e6efc44975c55ff57e8d917d77e11 refs/remotes/origin/vs-22-sign +200b8266f74272c5fc54e48b1a6017d4b86198d3 refs/remotes/origin/windows-25 diff --git a/git_old/refs/heads/main b/git_old/refs/heads/main new file mode 100644 index 0000000..99b2cf1 --- /dev/null +++ b/git_old/refs/heads/main @@ -0,0 +1 @@ +4a0b4ab1fb88e3c87565bc645a2837593612d56f diff --git a/git_old/refs/heads/windows-25 b/git_old/refs/heads/windows-25 new file mode 100644 index 0000000..fda49ef --- /dev/null +++ b/git_old/refs/heads/windows-25 @@ -0,0 +1 @@ +b09bb0af932e8d7ef36ede9a15c95cf84b316b44 diff --git a/git_old/refs/remotes/origin/HEAD b/git_old/refs/remotes/origin/HEAD new file mode 100644 index 0000000..4b0a875 --- /dev/null +++ b/git_old/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +ref: refs/remotes/origin/main diff --git a/git_old/refs/remotes/origin/windows-25 b/git_old/refs/remotes/origin/windows-25 new file mode 100644 index 0000000..fda49ef --- /dev/null +++ b/git_old/refs/remotes/origin/windows-25 @@ -0,0 +1 @@ +b09bb0af932e8d7ef36ede9a15c95cf84b316b44 diff --git a/helpers/CreateAzureVMFromPackerTemplate.ps1 b/helpers/CreateAzureVMFromPackerTemplate.ps1 new file mode 100644 index 0000000..588eeaf --- /dev/null +++ b/helpers/CreateAzureVMFromPackerTemplate.ps1 @@ -0,0 +1,85 @@ +Function CreateAzureVMFromPackerTemplate { + <# + .SYNOPSIS + A helper function to deploy a VM from a generated image. + + .DESCRIPTION + Creates an Azure VM from a template. Also generates network resources in Azure to make the VM accessible. + + .PARAMETER SubscriptionId + The Azure subscription Id where resources will be created. + + .PARAMETER ResourceGroupName + The Azure resource group name where the Azure virtual machine will be created. + + .PARAMETER ManagedImageName + The name of the managed image to be used to create the virtual machine. + + .PARAMETER VirtualMachineName + The name of the virtual machine to be generated. + + .PARAMETER AdminUserName + The administrator username for the virtual machine to be created. + + .PARAMETER AdminPassword + The administrator password for the virtual machine to be created. + + .PARAMETER AzureLocation + The location where the Azure virtual machine will be provisioned. Example: "eastus" + + .EXAMPLE + CreateAzureVMFromPackerTemplate -SubscriptionId {SubscriptionId} -ResourceGroupName {ResourceGroupName} -VirtualMachineName "testvm1" -ManagedImageName {ManagedImageName} -AdminUsername "shady1" -AdminPassword "SomeSecurePassword1" -AzureLocation "eastus" + #> + param ( + [Parameter(Mandatory = $True)] + [string] $SubscriptionId, + [Parameter(Mandatory = $True)] + [string] $ResourceGroupName, + [Parameter(Mandatory = $True)] + [string] $ManagedImageName, + [Parameter(Mandatory = $True)] + [string] $VirtualMachineName, + [Parameter(Mandatory = $True)] + [string] $AdminUsername, + [Parameter(Mandatory = $True)] + [string] $AdminPassword, + [Parameter(Mandatory = $True)] + [string] $AzureLocation + ) + + $vmSize = "Standard_DS2_v2" + $guid = [System.GUID]::NewGuid().ToString().ToUpper() + $vnetName = $env:UserName + "vnet-" + $guid + $subnetName = $env:UserName + "subnet-" + $guid + $nicName = $env:UserName + "nic-" + $guid + $publicIpName = $env:UserName + "pip-" + $guid + + Write-Host "Creating a virtual network and subnet" + ($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) + $subnetId = ($vnet | ConvertFrom-Json).newVNet.subnets[0].id + + Write-Host "`nCreating a network interface controller (NIC)" + ($nic = az network nic create -g $ResourceGroupName -l $AzureLocation -n $nicName --subnet $subnetId --subscription $subscriptionId -o json) + $networkId = ($nic | ConvertFrom-Json).NewNIC.id + + Write-Host "`nCreating a public IP address" + ($publicIp = az network public-ip create -g $ResourceGroupName -l $AzureLocation -n $publicIpName --allocation-method Static --sku Basic --version IPv4 --subscription $subscriptionId -o json) + $publicIpId = ($publicIp | ConvertFrom-Json).publicIp.id + + Write-Host "`nAdding the public IP to the NIC" + az network nic ip-config update -g $ResourceGroupName -n ipconfig1 --nic-name $nicName --public-ip-address $publicIpId --subscription $subscriptionId + + Write-Host "`nCreating the VM" + az vm create ` + --resource-group $ResourceGroupName ` + --name $VirtualMachineName ` + --image $ManagedImageName ` + --size $vmSize ` + --admin-username $AdminUsername ` + --admin-password $AdminPassword ` + --nics $networkId ` + --subscription $subscriptionId ` + --location $AzureLocation + + Write-Host "`nCreated in ${ResourceGroupName}:`n vnet ${vnetName}`n subnet ${subnetName}`n nic ${nicName}`n publicip ${publicIpName}`n vm ${VirtualMachineName}" +} diff --git a/helpers/GenerateResourcesAndImage.ps1 b/helpers/GenerateResourcesAndImage.ps1 new file mode 100644 index 0000000..021bb9c --- /dev/null +++ b/helpers/GenerateResourcesAndImage.ps1 @@ -0,0 +1,396 @@ +$ErrorActionPreference = 'Stop' + +enum ImageType { + Windows2019 = 1 + Windows2022 = 2 + Ubuntu2004 = 3 + Ubuntu2204 = 4 + Ubuntu2404 = 5 + UbuntuMinimal = 6 + Windows2025 = 7 +} + +Function Get-PackerTemplatePath { + param ( + [Parameter(Mandatory = $True)] + [string] $RepositoryRoot, + [Parameter(Mandatory = $True)] + [ImageType] $ImageType + ) + + switch ($ImageType) { + # Note: Double Join-Path is required to support PowerShell 5.1 + ([ImageType]::Windows2019) { + $relativeTemplatePath = Join-Path (Join-Path "windows" "templates") "windows-2019.pkr.hcl" + } + ([ImageType]::Windows2022) { + $relativeTemplatePath = Join-Path (Join-Path "windows" "templates") "windows-2022.pkr.hcl" + } + ([ImageType]::Windows2025) { + $relativeTemplatePath = Join-Path (Join-Path "windows" "templates") "windows-2025.pkr.hcl" + } + ([ImageType]::Ubuntu2004) { + $relativeTemplatePath = Join-Path (Join-Path "ubuntu" "templates") "ubuntu-20.04.pkr.hcl" + } + ([ImageType]::Ubuntu2204) { + $relativeTemplatePath = Join-Path (Join-Path "ubuntu" "templates") "ubuntu-22.04.pkr.hcl" + } + ([ImageType]::Ubuntu2404) { + $relativeTemplatePath = Join-Path (Join-Path "ubuntu" "templates") "ubuntu-24.04.pkr.hcl" + } + ([ImageType]::UbuntuMinimal) { + $relativeTemplatePath = Join-Path (Join-Path "ubuntu" "templates") "ubuntu-minimal.pkr.hcl" + } + default { throw "Unknown type of image" } + } + + $imageTemplatePath = [IO.Path]::Combine($RepositoryRoot, "images", $relativeTemplatePath) + + if (-not (Test-Path $imageTemplatePath)) { + throw "Template for image '$ImageType' doesn't exist on path '$imageTemplatePath'." + } + + return $imageTemplatePath; +} + +Function Show-LatestCommit { + [CmdletBinding()] + param() + + process { + $latestCommit = (git --no-pager log --pretty=format:"Date: %cd; Commit: %H - %s; Author: %an <%ae>" -1) + Write-Host "Latest commit: $latestCommit." + } +} + +function Start-Sleep($seconds) { + $doneDT = (Get-Date).AddSeconds($seconds) + while ($doneDT -gt (Get-Date)) { + $secondsLeft = $doneDT.Subtract((Get-Date)).TotalSeconds + $percent = ($seconds - $secondsLeft) / $seconds * 100 + Write-Progress -Activity "Sleeping" -Status "Sleeping..." -SecondsRemaining $secondsLeft -PercentComplete $percent + [System.Threading.Thread]::Sleep(500) + } + Write-Progress -Activity "Sleeping" -Status "Sleeping..." -SecondsRemaining 0 -Completed +} + +Function GenerateResourcesAndImage { + <# + .SYNOPSIS + A helper function to help generate an image. + .DESCRIPTION + This function will generate the Azure resources and image for the specified image type. + .PARAMETER SubscriptionId + The Azure subscription id where the Azure resources will be created. + .PARAMETER ResourceGroupName + The name of the resource group to create the Azure resources in. + .PARAMETER ImageType + The type of image to generate. Valid values are: Windows2019, Windows2022, Ubuntu2004, Ubuntu2204, UbuntuMinimal. + .PARAMETER ManagedImageName + The name of the managed image to create. The default is "Runner-Image-{{ImageType}}". + .PARAMETER AzureLocation + The Azure location where the Azure resources will be created. For example: "East US" + .PARAMETER ImageGenerationRepositoryRoot + The root directory of the image generation repository. This is used to locate the packer template. + .PARAMETER SecondsToWaitForServicePrincipalSetup + The number of seconds to wait for the service principal to be setup. The default is 120 seconds. + .PARAMETER AzureClientId + The Azure client id to use to authenticate with Azure. If not specified, the current user's credentials will be used. + .PARAMETER AzureClientSecret + The Azure client secret to use to authenticate with Azure. If not specified, the current user's credentials will be used. + .PARAMETER AzureTenantId + The Azure tenant id to use to authenticate with Azure. If not specified, the current user's credentials will be used. + .PARAMETER RestrictToAgentIpAddress + If set, access to the VM used by packer to generate the image is restricted to the public IP address this script is run from. + This parameter cannot be used in combination with the virtual_network_name packer parameter. + .PARAMETER Force + Delete the resource group if it exists without user confirmation. + This parameter is deprecated and will be removed in a future release. + .PARAMETER ReuseResourceGroup + Reuse the resource group if it exists without user confirmation. + This parameter is deprecated and will be removed in a future release. + .PARAMETER OnError + Specify how packer handles an error during image creation. + Options: + abort - abort immediately + ask - ask user for input + cleanup - attempt to cleanup and then abort + run-cleanup-provisioner - run the cleanup provisioner and then abort + The default is 'ask'. + .PARAMETER Tags + Tags to be applied to the Azure resources created. + .EXAMPLE + GenerateResourcesAndImage -SubscriptionId {YourSubscriptionId} -ResourceGroupName "shsamytest1" -ImageGenerationRepositoryRoot "C:\runner-images" -ImageType Ubuntu2004 -AzureLocation "East US" + #> + param ( + [Parameter(Mandatory = $True)] + [string] $SubscriptionId, + [Parameter(Mandatory = $True)] + [string] $ResourceGroupName, + [Parameter(Mandatory = $True)] + [ImageType] $ImageType, + [Parameter(Mandatory = $False)] + [string] $ManagedImageName = "Runner-Image-$($ImageType)", + [Parameter(Mandatory = $True)] + [string] $AzureLocation, + [Parameter(Mandatory = $False)] + [string] $ImageGenerationRepositoryRoot = $pwd, + [Parameter(Mandatory = $False)] + [int] $SecondsToWaitForServicePrincipalSetup = 120, + [Parameter(Mandatory = $False)] + [string] $AzureClientId, + [Parameter(Mandatory = $False)] + [string] $AzureClientSecret, + [Parameter(Mandatory = $False)] + [string] $AzureTenantId, + [Parameter(Mandatory = $False)] + [switch] $RestrictToAgentIpAddress, + [Parameter(Mandatory = $False)] + [switch] $Force, + [Parameter(Mandatory = $False)] + [switch] $ReuseResourceGroup, + [Parameter(Mandatory = $False)] + [ValidateSet("abort", "ask", "cleanup", "run-cleanup-provisioner")] + [string] $OnError = "ask", + [Parameter(Mandatory = $False)] + [hashtable] $Tags = @{} + ) + + if ($Force -or $ReuseResourceGroup) { + Write-Warning "The `ReuseResourceGroup` and `Force` parameters are deprecated and will be removed in a future release. The resource group will be reused when it already exists and an error will be thrown when it doesn't. If you want to delete the resource group, please delete it manually." + } + + if ($Force -and $ReuseResourceGroup) { + throw "Force and ReuseResourceGroup cannot be used together." + } + + Show-LatestCommit -ErrorAction SilentlyContinue + + # Validate packer is installed + $PackerBinary = Get-Command "packer" + if (-not ($PackerBinary)) { + throw "'packer' binary is not found on PATH." + } + + # Get template path + $TemplatePath = Get-PackerTemplatePath -RepositoryRoot $ImageGenerationRepositoryRoot -ImageType $ImageType + Write-Debug "Template path: $TemplatePath." + + # Prepare list of allowed inbound IP addresses + if ($RestrictToAgentIpAddress) { + $AgentIp = (Invoke-RestMethod http://ipinfo.io/json).ip + if (-not $AgentIp) { + throw "Unable to determine agent IP address." + } + + Write-Host "Access to packer generated VM will be restricted to agent IP Address: $AgentIp." + if ($PSVersionTable.PSVersion.Major -eq 5) { + Write-Verbose "PowerShell 5 detected. Replacing double quotes with escaped double quotes in allowed inbound IP addresses." + $AllowedInboundIpAddresses = '[\"{0}\"]' -f $AgentIp + } + elseif ($PSVersionTable.PSVersion.Major -eq 7 -and $PSVersionTable.PSVersion.Minor -le 2) { + Write-Verbose "PowerShell 7.0-7.2 detected. Replacing double quotes with escaped double quotes in allowed inbound IP addresses." + $AllowedInboundIpAddresses = '[\"{0}\"]' -f $AgentIp + } + else { + $AllowedInboundIpAddresses = '["{0}"]' -f $AgentIp + } + } + else { + $AllowedInboundIpAddresses = "[]" + } + Write-Debug "Allowed inbound IP addresses: $AllowedInboundIpAddresses." + + # Prepare tags + $TagsList = $Tags.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" } + Write-Debug "Tags list: $TagsList." + $TagsJson = $Tags | ConvertTo-Json -Compress + if ($PSVersionTable.PSVersion.Major -eq 5) { + Write-Verbose "PowerShell 5 detected. Replacing double quotes with escaped double quotes in tags JSON." + $TagsJson = $TagsJson -replace '"', '\"' + } + elseif ($PSVersionTable.PSVersion.Major -eq 7 -and $PSVersionTable.PSVersion.Minor -le 2) { + Write-Verbose "PowerShell 7.0-7.2 detected. Replacing double quotes with escaped double quotes in tags JSON." + $TagsJson = $TagsJson -replace '"', '\"' + } + Write-Debug "Tags JSON: $TagsJson." + + $InstallPassword = $env:UserName + [System.GUID]::NewGuid().ToString().ToUpper() + + Write-Host "Downloading packer plugins..." + & $PackerBinary init $TemplatePath + + if ($LastExitCode -ne 0) { + throw "Packer plugins download failed." + } + + Write-Host "Validating packer template..." + & $PackerBinary validate ` + "-var=client_id=fake" ` + "-var=client_secret=fake" ` + "-var=subscription_id=$($SubscriptionId)" ` + "-var=tenant_id=fake" ` + "-var=location=$($AzureLocation)" ` + "-var=managed_image_name=$($ManagedImageName)" ` + "-var=managed_image_resource_group_name=$($ResourceGroupName)" ` + "-var=install_password=$($InstallPassword)" ` + "-var=allowed_inbound_ip_addresses=$($AllowedInboundIpAddresses)" ` + "-var=azure_tags=$($TagsJson)" ` + $TemplatePath + + if ($LastExitCode -ne 0) { + throw "Packer template validation failed." + } + + try { + # Login to Azure subscription + if ([string]::IsNullOrEmpty($AzureClientId)) { + Write-Verbose "No AzureClientId was provided, will use interactive login." + az login --output none + } + else { + Write-Verbose "AzureClientId was provided, will use service principal login." + az login --service-principal --username $AzureClientId --password=$AzureClientSecret --tenant $AzureTenantId --output none + } + az account set --subscription $SubscriptionId + if ($LastExitCode -ne 0) { + throw "Failed to login to Azure subscription '$SubscriptionId'." + } + + # Check resource group + $ResourceGroupExists = [System.Convert]::ToBoolean((az group exists --name $ResourceGroupName)); + if ($ResourceGroupExists) { + Write-Verbose "Resource group '$ResourceGroupName' already exists." + } + + # Remove resource group if it exists and we are not reusing it + if ($ResourceGroupExists -and -not $ReuseResourceGroup) { + if ($Force) { + # Delete and recreate the resource group + Write-Host "Deleting resource group '$ResourceGroupName'..." + az group delete --name $ResourceGroupName --yes --output none + if ($LastExitCode -ne 0) { + throw "Failed to delete resource group '$ResourceGroupName'." + } + Write-Host "Resource group '$ResourceGroupName' was deleted." + $ResourceGroupExists = $false + } + else { + # are we running in a non-interactive session? + # https://stackoverflow.com/questions/9738535/powershell-test-for-noninteractive-mode + if ([System.Console]::IsOutputRedirected -or ![Environment]::UserInteractive -or !!([Environment]::GetCommandLineArgs() | Where-Object { $_ -ilike '-noni*' })) { + throw "Non-interactive mode, resource group '$ResourceGroupName' already exists, either specify -Force to delete it, or -ReuseResourceGroup to reuse." + } + else { + # Resource group already exists, ask the user what to do + $title = "Resource group '$ResourceGroupName' already exists" + $message = "Do you want to delete the resource group and all resources in it?" + + $options = @( + [System.Management.Automation.Host.ChoiceDescription]::new("&Yes", "Delete the resource group and all resources in it."), + [System.Management.Automation.Host.ChoiceDescription]::new("&No", "Keep the resource group and continue."), + [System.Management.Automation.Host.ChoiceDescription]::new("&Abort", "Abort execution.") + ) + $result = $Host.UI.PromptForChoice($title, $message, $options, 0) + } + + switch ($result) { + 0 { + # Delete and recreate the resource group + Write-Host "Deleting resource group '$ResourceGroupName'..." + az group delete --name $ResourceGroupName --yes + if ($LastExitCode -ne 0) { + throw "Failed to delete resource group '$ResourceGroupName'." + } + Write-Host "Resource group '$ResourceGroupName' was deleted." + $ResourceGroupExists = $false + } + 1 { + # Keep the resource group and continue + } + 2 { + # Stop the current action + Write-Error "User stopped the action." + exit 1 + } + } + } + } + + # Create resource group + if (-not $ResourceGroupExists) { + Write-Host "Creating resource group '$ResourceGroupName' in location '$AzureLocation'..." + if ($TagsList) { + az group create --name $ResourceGroupName --location $AzureLocation --tags $TagsList --query id + } + else { + az group create --name $ResourceGroupName --location $AzureLocation --query id + } + if ($LastExitCode -ne 0) { + throw "Failed to create resource group '$ResourceGroupName'." + } + } + + # Create service principal + if ([string]::IsNullOrEmpty($AzureClientId)) { + Write-Host "Creating service principal for packer..." + $ADCleanupRequired = $true + + $ServicePrincipalName = "packer-" + [System.GUID]::NewGuid().ToString().ToUpper() + $ServicePrincipal = az ad sp create-for-rbac --name $ServicePrincipalName --role Contributor --scopes /subscriptions/$SubscriptionId --only-show-errors | ConvertFrom-Json + if ($LastExitCode -ne 0) { + throw "Failed to create service principal '$ServicePrincipalName'." + } + + $ServicePrincipalAppId = $ServicePrincipal.appId + $ServicePrincipalPassword = $ServicePrincipal.password + $TenantId = $ServicePrincipal.tenant + + Write-Verbose "Waiting for service principal to propagate..." + Start-Sleep $SecondsToWaitForServicePrincipalSetup + Write-Host "Service principal created with id '$ServicePrincipalAppId'. It will be deleted after the build." + } + else { + $ServicePrincipalAppId = $AzureClientId + $ServicePrincipalPassword = $AzureClientSecret + $TenantId = $AzureTenantId + } + Write-Debug "Service principal app id: $ServicePrincipalAppId." + Write-Debug "Tenant id: $TenantId." + + & $PackerBinary build -on-error="$($OnError)" ` + -var "client_id=$($ServicePrincipalAppId)" ` + -var "client_secret=$($ServicePrincipalPassword)" ` + -var "subscription_id=$($SubscriptionId)" ` + -var "tenant_id=$($TenantId)" ` + -var "location=$($AzureLocation)" ` + -var "managed_image_name=$($ManagedImageName)" ` + -var "managed_image_resource_group_name=$($ResourceGroupName)" ` + -var "install_password=$($InstallPassword)" ` + -var "allowed_inbound_ip_addresses=$($AllowedInboundIpAddresses)" ` + -var "azure_tags=$($TagsJson)" ` + $TemplatePath + + if ($LastExitCode -ne 0) { + throw "Failed to build image." + } + } catch { + Write-Error $_ + } finally { + Write-Verbose "`nCleaning up..." + + # Remove ADServicePrincipal and ADApplication + if ($ADCleanupRequired) { + Write-Host "Removing ADServicePrincipal..." + if (az ad sp show --id $ServicePrincipalAppId --query id) { + az ad sp delete --id $ServicePrincipalAppId + } + + Write-Host "Removing ADApplication..." + if (az ad app show --id $ServicePrincipalAppId --query id) { + az ad app delete --id $ServicePrincipalAppId + } + } + Write-Verbose "Cleanup completed." + } +} diff --git a/helpers/software-report-base/Calculate-ImagesDifference.ps1 b/helpers/software-report-base/Calculate-ImagesDifference.ps1 new file mode 100644 index 0000000..f7a0216 --- /dev/null +++ b/helpers/software-report-base/Calculate-ImagesDifference.ps1 @@ -0,0 +1,66 @@ +using module ./SoftwareReport.psm1 +using module ./SoftwareReport.DifferenceCalculator.psm1 + +<# +.SYNOPSIS + Calculates the difference between two software reports and saves it to a file. +.PARAMETER PreviousJsonReportPath + Path to the previous software report. +.PARAMETER CurrentJsonReportPath + Path to the current software report. +.PARAMETER OutputFile + Path to the file where the difference will be saved. +.PARAMETER ReleaseBranchName + Name of the release branch to build image docs URL. +.PARAMETER ReadmePath + Path to the README file in repository to build image docs URL. +#> + +Param ( + [Parameter(Mandatory=$true)] + [string] $PreviousJsonReportPath, + [Parameter(Mandatory=$true)] + [string] $CurrentJsonReportPath, + [Parameter(Mandatory=$true)] + [string] $OutputFile, + [Parameter(Mandatory=$false)] + [string] $ReleaseBranchName, + [Parameter(Mandatory=$false)] + [string] $ReadmePath +) + +$ErrorActionPreference = "Stop" +$global:ErrorView = "NormalView" + +function Read-SoftwareReport { + Param ( + [Parameter(Mandatory=$true)] + [string] $JsonReportPath + ) + + if (-not (Test-Path $JsonReportPath)) { + throw "File '$JsonReportPath' does not exist" + } + + $jsonReport = Get-Content -Path $JsonReportPath -Raw + $report = [SoftwareReport]::FromJson($jsonReport) + return $report +} + +$previousReport = Read-SoftwareReport -JsonReportPath $PreviousJsonReportPath +$currentReport = Read-SoftwareReport -JsonReportPath $CurrentJsonReportPath + +$comparer = [SoftwareReportDifferenceCalculator]::new($previousReport, $currentReport) +$comparer.CompareReports() +$diff = $comparer.GetMarkdownReport() + +if ($ReleaseBranchName -and $ReadmePath) { + # https://github.com/actions/runner-images/blob/releases/macOS-12/20221215/images/macos/macos-12-Readme.md + $ImageDocsUrl = "https://github.com/actions/runner-images/blob/${ReleaseBranchName}/${ReadmePath}" + $diff += "`n`n`nFor comprehensive list of software installed on this image please click [here]($ImageDocsUrl)." +} + +$parentDirectory = Split-Path $OutputFile -Parent +if (-not (Test-Path $parentDirectory)) { New-Item -Path $parentDirectory -ItemType Directory | Out-Null } + +$diff | Out-File -Path $OutputFile -Encoding utf8NoBOM diff --git a/helpers/software-report-base/SoftwareReport.BaseNodes.psm1 b/helpers/software-report-base/SoftwareReport.BaseNodes.psm1 new file mode 100644 index 0000000..5aabf3b --- /dev/null +++ b/helpers/software-report-base/SoftwareReport.BaseNodes.psm1 @@ -0,0 +1,56 @@ +############################ +### Abstract base nodes #### +############################ + +# Abstract base class for all nodes +class BaseNode { + [Boolean] ShouldBeIncludedToDiff() { + return $false + } + + [String] ToMarkdown() { + return $this.ToMarkdown(1) + } + + [String] ToMarkdown([Int32] $Level) { + throw "Abstract method 'ToMarkdown(level)' is not implemented for '$($this.GetType().Name)'" + } + + [Boolean] IsSimilarTo([BaseNode] $OtherNode) { + throw "Abstract method 'IsSimilarTo' is not implemented for '$($this.GetType().Name)'" + } + + [Boolean] IsIdenticalTo([BaseNode] $OtherNode) { + throw "Abstract method 'IsIdenticalTo' is not implemented for '$($this.GetType().Name)'" + } +} + +# Abstract base class for all nodes that describe a tool and should be rendered inside diff table +class BaseToolNode: BaseNode { + [ValidateNotNullOrEmpty()] + [String] $ToolName + + BaseToolNode([String] $ToolName) { + $this.ToolName = $ToolName + } + + [Boolean] ShouldBeIncludedToDiff() { + return $true + } + + [String] GetValue() { + throw "Abstract method 'GetValue' is not implemented for '$($this.GetType().Name)'" + } + + [Boolean] IsSimilarTo([BaseNode] $OtherNode) { + if ($this.GetType() -ne $OtherNode.GetType()) { + return $false + } + + return $this.ToolName -eq $OtherNode.ToolName + } + + [Boolean] IsIdenticalTo([BaseNode] $OtherNode) { + return $this.IsSimilarTo($OtherNode) -and ($this.GetValue() -eq $OtherNode.GetValue()) + } +} diff --git a/helpers/software-report-base/SoftwareReport.DifferenceCalculator.psm1 b/helpers/software-report-base/SoftwareReport.DifferenceCalculator.psm1 new file mode 100644 index 0000000..38a1df6 --- /dev/null +++ b/helpers/software-report-base/SoftwareReport.DifferenceCalculator.psm1 @@ -0,0 +1,136 @@ +using module ./SoftwareReport.psm1 +using module ./SoftwareReport.BaseNodes.psm1 +using module ./SoftwareReport.Nodes.psm1 +using module ./SoftwareReport.DifferenceRender.psm1 + +class SoftwareReportDifferenceCalculator { + [ValidateNotNullOrEmpty()] + hidden [SoftwareReport] $PreviousReport + [ValidateNotNullOrEmpty()] + hidden [SoftwareReport] $CurrentReport + + hidden [Collections.Generic.List[ReportDifferenceItem]] $AddedItems + hidden [Collections.Generic.List[ReportDifferenceItem]] $ChangedItems + hidden [Collections.Generic.List[ReportDifferenceItem]] $DeletedItems + + SoftwareReportDifferenceCalculator([SoftwareReport] $PreviousReport, [SoftwareReport] $CurrentReport) { + $this.PreviousReport = $PreviousReport + $this.CurrentReport = $CurrentReport + } + + [void] CompareReports() { + $this.AddedItems = @() + $this.ChangedItems = @() + $this.DeletedItems = @() + + $this.CompareInternal($this.PreviousReport.Root, $this.CurrentReport.Root, @()) + } + + [String] GetMarkdownReport() { + $reporter = [SoftwareReportDifferenceRender]::new() + $report = $reporter.GenerateMarkdownReport($this.CurrentReport, $this.PreviousReport, $this.AddedItems, $this.ChangedItems, $this.DeletedItems) + return $report + } + + hidden [void] CompareInternal([HeaderNode] $previousReportPointer, [HeaderNode] $currentReportPointer, [String[]] $Headers) { + $currentReportPointer.Children ?? @() | Where-Object { $_.ShouldBeIncludedToDiff() -and $this.FilterExcludedNodes($_) } | ForEach-Object { + $currentReportNode = $_ + $sameNodeInPreviousReport = $previousReportPointer ? $previousReportPointer.FindSimilarChildNode($currentReportNode) : $null + + if ($currentReportNode -is [HeaderNode]) { + # Compare HeaderNode recursively + $this.CompareInternal($sameNodeInPreviousReport, $currentReportNode, $Headers + $currentReportNode.Title) + } else { + if ($sameNodeInPreviousReport -and ($currentReportNode.IsIdenticalTo($sameNodeInPreviousReport))) { + # Nodes are identical, nothing changed, just ignore it + } elseif ($sameNodeInPreviousReport) { + # Nodes are equal but not identical, something was changed + if ($currentReportNode -is [TableNode]) { + $this.CompareSimilarTableNodes($sameNodeInPreviousReport, $currentReportNode, $Headers) + } elseif ($currentReportNode -is [ToolVersionsListNode]) { + $this.CompareSimilarToolVersionsListNodes($sameNodeInPreviousReport, $currentReportNode, $Headers) + } else { + $this.ChangedItems.Add([ReportDifferenceItem]::new($sameNodeInPreviousReport, $currentReportNode, $Headers)) + } + } else { + # Node was not found in previous report, new node was added + $this.AddedItems.Add([ReportDifferenceItem]::new($null, $currentReportNode, $Headers)) + } + } + } + + # Detecting nodes that were removed + $previousReportPointer.Children ?? @() | Where-Object { $_.ShouldBeIncludedToDiff() -and $this.FilterExcludedNodes($_) } | ForEach-Object { + $previousReportNode = $_ + $sameNodeInCurrentReport = $currentReportPointer ? $currentReportPointer.FindSimilarChildNode($previousReportNode) : $null + + if (-not $sameNodeInCurrentReport) { + if ($previousReportNode -is [HeaderNode]) { + # Compare removed HeaderNode recursively + $this.CompareInternal($previousReportNode, $null, $Headers + $previousReportNode.Title) + } else { + # Node was not found in current report, node was removed + $this.DeletedItems.Add([ReportDifferenceItem]::new($previousReportNode, $null, $Headers)) + } + } + } + } + + hidden [void] CompareSimilarTableNodes([TableNode] $PreviousReportNode, [TableNode] $CurrentReportNode, [String[]] $Headers) { + $addedRows = $CurrentReportNode.Rows | Where-Object { $_ -notin $PreviousReportNode.Rows } + $deletedRows = $PreviousReportNode.Rows | Where-Object { $_ -notin $CurrentReportNode.Rows } + + if (($addedRows.Count -eq 0) -and ($deletedRows.Count -eq 0)) { + # Unexpected state: TableNodes are identical + return + } + + if ($PreviousReportNode.Headers -ne $CurrentReportNode.Headers) { + # If headers are changed and rows are changed at the same time, we should track it as removing table and adding new one + $this.DeletedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $null, $Headers)) + $this.AddedItems.Add([ReportDifferenceItem]::new($null, $CurrentReportNode, $Headers)) + } elseif (($addedRows.Count -gt 0) -and ($deletedRows.Count -eq 0)) { + # If new rows were added and no rows were deleted, then it is AddedItem + $this.AddedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $CurrentReportNode, $Headers)) + } elseif (($deletedRows.Count -gt 0) -and ($addedRows.Count -eq 0)) { + # If no rows were added and some rows were deleted, then it is DeletedItem + $this.DeletedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $CurrentReportNode, $Headers)) + } else { + # If some rows were added and some rows were removed, then it is UpdatedItem + $this.ChangedItems.Add([ReportDifferenceItem]::new($PreviousReportNode, $CurrentReportNode, $Headers)) + } + } + + hidden [void] CompareSimilarToolVersionsListNodes([ToolVersionsListNode] $PreviousReportNode, [ToolVersionsListNode] $CurrentReportNode, [String[]] $Headers) { + $previousReportMajorVersions = $PreviousReportNode.Versions | ForEach-Object { $PreviousReportNode.ExtractMajorVersion($_) } + $currentReportMajorVersion = $CurrentReportNode.Versions | ForEach-Object { $CurrentReportNode.ExtractMajorVersion($_) } + + $addedVersions = $CurrentReportNode.Versions | Where-Object { $CurrentReportNode.ExtractMajorVersion($_) -notin $previousReportMajorVersions } + $deletedVersions = $PreviousReportNode.Versions | Where-Object { $PreviousReportNode.ExtractMajorVersion($_) -notin $currentReportMajorVersion } + $changedPreviousVersions = $PreviousReportNode.Versions | Where-Object { ($PreviousReportNode.ExtractMajorVersion($_) -in $currentReportMajorVersion) -and ($_ -notin $CurrentReportNode.Versions) } + $changedCurrentVersions = $CurrentReportNode.Versions | Where-Object { ($CurrentReportNode.ExtractMajorVersion($_) -in $previousReportMajorVersions) -and ($_ -notin $PreviousReportNode.Versions) } + + if ($addedVersions.Count -gt 0) { + $this.AddedItems.Add([ReportDifferenceItem]::new($null, [ToolVersionsListNode]::new($CurrentReportNode.ToolName, $addedVersions, $CurrentReportNode.MajorVersionRegex, "List"), $Headers)) + } + + if ($deletedVersions.Count -gt 0) { + $this.DeletedItems.Add([ReportDifferenceItem]::new([ToolVersionsListNode]::new($PreviousReportNode.ToolName, $deletedVersions, $PreviousReportNode.MajorVersionRegex, "List"), $null, $Headers)) + } + + $previousChangedNode = ($changedPreviousVersions.Count -gt 0) ? [ToolVersionsListNode]::new($PreviousReportNode.ToolName, $changedPreviousVersions, $PreviousReportNode.MajorVersionRegex, "List") : $null + $currentChangedNode = ($changedCurrentVersions.Count -gt 0) ? [ToolVersionsListNode]::new($CurrentReportNode.ToolName, $changedCurrentVersions, $CurrentReportNode.MajorVersionRegex, "List") : $null + if ($previousChangedNode -and $currentChangedNode) { + $this.ChangedItems.Add([ReportDifferenceItem]::new($previousChangedNode, $currentChangedNode, $Headers)) + } + } + + hidden [Boolean] FilterExcludedNodes([BaseNode] $Node) { + # We shouldn't show "Image Version" diff because it is already shown in report header + if (($Node -is [ToolVersionNode]) -and ($Node.ToolName -eq "Image Version:")) { + return $false + } + + return $true + } +} \ No newline at end of file diff --git a/helpers/software-report-base/SoftwareReport.DifferenceRender.psm1 b/helpers/software-report-base/SoftwareReport.DifferenceRender.psm1 new file mode 100644 index 0000000..91d51e3 --- /dev/null +++ b/helpers/software-report-base/SoftwareReport.DifferenceRender.psm1 @@ -0,0 +1,225 @@ +using module ./SoftwareReport.psm1 +using module ./SoftwareReport.BaseNodes.psm1 +using module ./SoftwareReport.Nodes.psm1 + +class SoftwareReportDifferenceRender { + [String] GenerateMarkdownReport([SoftwareReport] $CurrentReport, [SoftwareReport] $PreviousReport, [ReportDifferenceItem[]] $AddedItems, [ReportDifferenceItem[]] $ChangedItems, [ReportDifferenceItem[]] $DeletedItems) { + $sb = [System.Text.StringBuilder]::new() + + $rootNode = $CurrentReport.Root + $imageVersion = $CurrentReport.GetImageVersion() + $previousImageVersion = $PreviousReport.GetImageVersion() + + ############################# + ### Render report header #### + ############################# + + $sb.AppendLine("# :desktop_computer: Actions Runner Image: $($rootNode.Title)") + + # ToolVersionNodes on root level contains main image description so just copy-paste them to final report + $rootNode.Children | Where-Object { $_ -is [ToolVersionNode] } | ForEach-Object { + $sb.AppendLine($_.ToMarkdown()) + } + $sb.AppendLine() + + $sb.AppendLine("## :mega: What's changed?").AppendLine() + + ########################### + ### Render added items #### + ########################### + + [ReportDifferenceItem[]] $addedItemsBaseTools = $AddedItems | Where-Object { $_.IsBaseToolNode() } + [ReportDifferenceItem[]] $addedItemsTables = $AddedItems | Where-Object { $_.IsTableNode() } + if ($addedItemsBaseTools.Count + $addedItemsTables.Count -gt 0) { + $sb.AppendLine("### Added :heavy_plus_sign:").AppendLine() + } + if ($addedItemsBaseTools.Count -gt 0) { + $tableItems = $addedItemsBaseTools | ForEach-Object { + [PSCustomObject]@{ + "Category" = $this.RenderCategory($_.Headers, $true); + "Tool name" = $this.RenderToolName($_.CurrentReportNode.ToolName); + "Current ($imageVersion)" = $_.CurrentReportNode.GetValue(); + } + } + $sb.AppendLine($this.RenderHtmlTable($tableItems, "Category")) + } + if ($addedItemsTables.Count -gt 0) { + $addedItemsTables | ForEach-Object { + $sb.AppendLine($this.RenderTableNodesDiff($_)) + } + } + + ############################# + ### Render deleted items #### + ############################# + + [ReportDifferenceItem[]] $deletedItemsBaseTools = $DeletedItems | Where-Object { $_.IsBaseToolNode() } + [ReportDifferenceItem[]] $deletedItemsTables = $DeletedItems | Where-Object { $_.IsTableNode() } + if ($deletedItemsBaseTools.Count + $deletedItemsTables.Count -gt 0) { + $sb.AppendLine("### Deleted :heavy_minus_sign:").AppendLine() + } + if ($deletedItemsBaseTools.Count -gt 0) { + $tableItems = $deletedItemsBaseTools | ForEach-Object { + [PSCustomObject]@{ + "Category" = $this.RenderCategory($_.Headers, $true); + "Tool name" = $this.RenderToolName($_.PreviousReportNode.ToolName); + "Previous ($previousImageVersion)" = $_.PreviousReportNode.GetValue(); + } + } + $sb.AppendLine($this.RenderHtmlTable($tableItems, "Category")) + } + if ($deletedItemsTables.Count -gt 0) { + $deletedItemsTables | ForEach-Object { + $sb.AppendLine($this.RenderTableNodesDiff($_)) + } + } + + ############################# + ### Render updated items #### + ############################# + + [ReportDifferenceItem[]] $changedItemsBaseTools = $ChangedItems | Where-Object { $_.IsBaseToolNode() } + [ReportDifferenceItem[]] $changedItemsTables = $ChangedItems | Where-Object { $_.IsTableNode() } + if ($changedItemsBaseTools.Count + $changedItemsTables.Count -gt 0) { + $sb.AppendLine("### Updated").AppendLine() + } + if ($changedItemsBaseTools.Count -gt 0) { + $tableItems = $changedItemsBaseTools | ForEach-Object { + [PSCustomObject]@{ + "Category" = $this.RenderCategory($_.Headers, $true); + "Tool name" = $this.RenderToolName($_.CurrentReportNode.ToolName); + "Previous ($previousImageVersion)" = $_.PreviousReportNode.GetValue(); + "Current ($imageVersion)" = $_.CurrentReportNode.GetValue(); + } + } + $sb.AppendLine($this.RenderHtmlTable($tableItems, "Category")) + } + if ($changedItemsTables.Count -gt 0) { + $changedItemsTables | ForEach-Object { + $sb.AppendLine($this.RenderTableNodesDiff($_)) + } + } + + return $sb.ToString() + } + + [String] RenderHtmlTable([PSCustomObject[]] $Table, [String] $RowSpanColumnName) { + $headers = $Table[0].PSObject.Properties.Name + + $sb = [System.Text.StringBuilder]::new() + $sb.AppendLine("") + $sb.AppendLine(" ") + $headers | ForEach-Object { + $sb.AppendLine(" ") + } + $sb.AppendLine(" ") + $sb.AppendLine(" ") + + $tableRowSpans = $this.CalculateHtmlTableRowSpan($Table, $RowSpanColumnName) + for ($rowIndex = 0; $rowIndex -lt $Table.Count; $rowIndex++) { + $row = $Table[$rowIndex] + + $sb.AppendLine(" ") + $headers | ForEach-Object { + if ($_ -eq $RowSpanColumnName) { + if ($tableRowSpans[$rowIndex] -gt 0) { + $sb.AppendLine(" ") + } else { + # Skip rendering this cell at all + } + } else { + $sb.AppendLine(" ") + } + } + $sb.AppendLine(" ") + } + $sb.AppendLine(" ") + $sb.AppendLine("
$_
$($row.$_)$($row.$_)
") + + return $sb.ToString() + } + + [int[]] CalculateHtmlTableRowSpan([PSCustomObject[]] $Table, [String] $keyColumn) { + $result = @(0) * $Table.Count + + for ($rowIndex = $Table.Count - 1; $rowIndex -ge 0; $rowIndex--) { + if (($rowIndex -lt ($Table.Count - 1)) -and ($Table[$rowIndex].$keyColumn -eq $Table[$rowIndex + 1].$keyColumn)) { + # If the current row is the same as the next row + # Then rowspan of current row should be equal to rowspan of the next row + 1 + # And rowspan of the next row should be 0 because it is already included in the rowspan of the current row + $result[$rowIndex] = $result[$rowIndex + 1] + 1 + $result[$rowIndex + 1] = 0 + } else { + $result[$rowIndex] = 1 + } + } + + return $result + } + + [String] RenderTableNodesDiff([ReportDifferenceItem] $DiffItem) { + # Use the simplest approach for now: first, print all removed lines. Then print added lines + # It will work well for most cases like changing existing rows, adding new rows and removing rows + # But can produce not so pretty results for cases when some rows are changed and some rows are added at the same time + # Let's see how it works in practice and improve it later if needed + + [String] $tableHeaders = ($DiffItem.CurrentReportNode ?? $DiffItem.PreviousReportNode).Headers + [Collections.Generic.List[String]] $tableRows = @() + $DiffItem.PreviousReportNode.Rows ?? @() | Where-Object { $_ -notin $DiffItem.CurrentReportNode.Rows } | ForEach-Object { + $tableRows.Add($this.StrikeTableRow($_)) + } + $DiffItem.CurrentReportNode.Rows ?? @() | Where-Object { $_ -notin $DiffItem.PreviousReportNode.Rows } | ForEach-Object { + $tableRows.Add($_) + } + + $sb = [System.Text.StringBuilder]::new() + $sb.AppendLine("#### $($this.RenderCategory($DiffItem.Headers, $false))") + $sb.AppendLine([TableNode]::new($tableHeaders, $tableRows).ToMarkdown()) + return $sb.ToString() + } + + [String] RenderCategory([String[]] $Headers, [Boolean] $AddLineSeparator) { + # Always skip the first header because it is "Installed Software" + [String[]] $takeHeaders = $Headers | Select-Object -Skip 1 + if ($takeHeaders.Count -eq 0) { + return "" + } + + $lineSeparator = $AddLineSeparator ? "
": "" + return [String]::Join(" >$lineSeparator ", $takeHeaders) + } + + [String] RenderToolName([String] $ToolName) { + return $ToolName.TrimEnd(":") + } + + [String] StrikeTableRow([String] $Row) { + # Convert "a|b|c" to "~~a~~|~~b~~|~~c~~ + $cells = $Row.Split("|") + $strikedCells = $cells | ForEach-Object { "~~$($_)~~"} + return [String]::Join("|", $strikedCells) + } +} + +# Temporary structure to store the single difference between two reports +class ReportDifferenceItem { + [BaseNode] $PreviousReportNode + [BaseNode] $CurrentReportNode + [String[]] $Headers + + ReportDifferenceItem([BaseNode] $PreviousReportNode, [BaseNode] $CurrentReportNode, [String[]] $Headers) { + $this.PreviousReportNode = $PreviousReportNode + $this.CurrentReportNode = $CurrentReportNode + $this.Headers = $Headers + } + + [Boolean] IsBaseToolNode() { + $node = $this.CurrentReportNode ?? $this.PreviousReportNode + return $node -is [BaseToolNode] + } + + [Boolean] IsTableNode() { + $node = $this.CurrentReportNode ?? $this.PreviousReportNode + return $node -is [TableNode] + } +} \ No newline at end of file diff --git a/helpers/software-report-base/SoftwareReport.Nodes.psm1 b/helpers/software-report-base/SoftwareReport.Nodes.psm1 new file mode 100644 index 0000000..b77e1c1 --- /dev/null +++ b/helpers/software-report-base/SoftwareReport.Nodes.psm1 @@ -0,0 +1,439 @@ +using module ./SoftwareReport.BaseNodes.psm1 + +######################################### +### Nodes to describe image software #### +######################################### + +# NodesFactory is used to simplify parsing different types of notes +# Every node has own logic of parsing and this method just invokes "FromJsonObject" of correct node type +class NodesFactory { + static [BaseNode] ParseNodeFromObject([object] $JsonObj) { + if ($JsonObj.NodeType -eq [HeaderNode].Name) { + return [HeaderNode]::FromJsonObject($JsonObj) + } elseif ($JsonObj.NodeType -eq [ToolVersionNode].Name) { + return [ToolVersionNode]::FromJsonObject($JsonObj) + } elseif ($JsonObj.NodeType -eq [ToolVersionsListNode].Name) { + return [ToolVersionsListNode]::FromJsonObject($JsonObj) + } elseif ($JsonObj.NodeType -eq [TableNode].Name) { + return [TableNode]::FromJsonObject($JsonObj) + } elseif ($JsonObj.NodeType -eq [NoteNode].Name) { + return [NoteNode]::FromJsonObject($JsonObj) + } + + throw "Unknown node type in ParseNodeFromObject '$($JsonObj.NodeType)'" + } +} + +class HeaderNode: BaseNode { + [ValidateNotNullOrEmpty()] + [String] $Title + [Collections.Generic.List[BaseNode]] $Children + + HeaderNode([String] $Title) { + $this.Title = $Title + $this.Children = @() + } + + [Boolean] ShouldBeIncludedToDiff() { + return $true + } + + [void] AddNode([BaseNode] $node) { + $similarNode = $this.FindSimilarChildNode($node) + if ($similarNode) { + throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.`nFound node: $($similarNode.ToJsonObject() | ConvertTo-Json)`nNew node: $($node.ToJsonObject() | ConvertTo-Json)" + } + + if (-not $this.IsNodeHasMarkdownHeader($node)) { + # If the node doesn't print own header to markdown, we should check that there is no other nodes that print header to markdown before it. + # It is done to avoid unexpected situation like this: + # + # HeaderNode A -> # A + # HeaderNode B -> ## B + # ToolVersionNode C -> - C + # ToolVersionNode D -> - D + # + # In this example, we add 'HeaderNode B" to 'HeaderNode A' and add 'ToolVersionNode C' to 'HeaderNode B'. + # Then we add 'ToolVersionNode D' to 'HeaderNode A'. + # But the result markdown will look like 'ToolVersionNode D' belongs to 'HeaderNode B' instead of 'HeaderNode A'. + $this.Children | Where-Object { $this.IsNodeHasMarkdownHeader($_) } | ForEach-Object { + throw "It is not allowed to add the non-header node after the header node. Consider adding the separate HeaderNode for this node" + } + } + + $this.Children.Add($node) + } + + [void] AddNodes([BaseNode[]] $nodes) { + $nodes | ForEach-Object { + $this.AddNode($_) + } + } + + [HeaderNode] AddHeader([String] $Title) { + $node = [HeaderNode]::new($Title) + $this.AddNode($node) + return $node + } + + [void] AddToolVersion([String] $ToolName, [String] $Version) { + $this.AddNode([ToolVersionNode]::new($ToolName, $Version)) + } + + [void] AddToolVersionsList([String] $ToolName, [String[]] $Version, [String] $MajorVersionRegex) { + $this.AddNode([ToolVersionsListNode]::new($ToolName, $Version, $MajorVersionRegex, "List")) + } + + [void] AddToolVersionsListInline([String] $ToolName, [String[]] $Version, [String] $MajorVersionRegex) { + $this.AddNode([ToolVersionsListNode]::new($ToolName, $Version, $MajorVersionRegex, "Inline")) + } + + [void] AddTable([PSCustomObject[]] $Table) { + $this.AddNode([TableNode]::FromObjectsArray($Table)) + } + + [void] AddNote([String] $Content) { + $this.AddNode([NoteNode]::new($Content)) + } + + [String] ToMarkdown([Int32] $Level) { + $sb = [System.Text.StringBuilder]::new() + $sb.AppendLine() + $sb.AppendLine("$("#" * $Level) $($this.Title)") + $this.Children | ForEach-Object { + $sb.AppendLine($_.ToMarkdown($Level + 1)) + } + + return $sb.ToString().TrimEnd() + } + + [PSCustomObject] ToJsonObject() { + return [PSCustomObject]@{ + NodeType = $this.GetType().Name + Title = $this.Title + Children = $this.Children | ForEach-Object { $_.ToJsonObject() } + } + } + + static [HeaderNode] FromJsonObject([Object] $JsonObj) { + $node = [HeaderNode]::new($JsonObj.Title) + $JsonObj.Children | Where-Object { $_ } | ForEach-Object { $node.AddNode([NodesFactory]::ParseNodeFromObject($_)) } + return $node + } + + [Boolean] IsSimilarTo([BaseNode] $OtherNode) { + if ($OtherNode.GetType() -ne [HeaderNode]) { + return $false + } + + return $this.Title -eq $OtherNode.Title + } + + [Boolean] IsIdenticalTo([BaseNode] $OtherNode) { + return $this.IsSimilarTo($OtherNode) + } + + [BaseNode] FindSimilarChildNode([BaseNode] $Find) { + foreach ($childNode in $this.Children) { + if ($childNode.IsSimilarTo($Find)) { + return $childNode + } + } + + return $null + } + + hidden [Boolean] IsNodeHasMarkdownHeader([BaseNode] $node) { + if ($node -is [HeaderNode]) { + return $true + } + + if (($node -is [ToolVersionsListNode]) -and ($node.ListType -eq "List")) { + return $true + } + + return $false + } +} + +class ToolVersionNode: BaseToolNode { + [ValidateNotNullOrEmpty()] + [String] $Version + + ToolVersionNode([String] $ToolName, [String] $Version): base($ToolName) { + + if ([String]::IsNullOrEmpty($Version)) { + throw "ToolVersionNode '$($this.ToolName)' has empty version" + } + + $this.Version = $Version + } + + [String] ToMarkdown([Int32] $Level) { + return "- $($this.ToolName) $($this.Version)" + } + + [String] GetValue() { + return $this.Version + } + + [PSCustomObject] ToJsonObject() { + return [PSCustomObject]@{ + NodeType = $this.GetType().Name + ToolName = $this.ToolName + Version = $this.Version + } + } + + static [BaseNode] FromJsonObject([Object] $JsonObj) { + return [ToolVersionNode]::new($JsonObj.ToolName, $JsonObj.Version) + } +} + +class ToolVersionsListNode: BaseToolNode { + [ValidateNotNullOrEmpty()] + [String[]] $Versions + + [Regex] $MajorVersionRegex + + [ValidateSet("List", "Inline")] + [String] $ListType + + ToolVersionsListNode([String] $ToolName, [String[]] $Versions, [String] $MajorVersionRegex, [String] $ListType): base($ToolName) { + $this.Versions = $Versions + + if ([String]::IsNullOrEmpty($Versions)) { + throw "ToolVersionsListNode '$($this.ToolName)' has empty versions list" + } + + $this.MajorVersionRegex = [Regex]::new($MajorVersionRegex) + $this.ListType = $ListType + $this.ValidateMajorVersionRegex() + } + + [String] ToMarkdown([Int32] $Level) { + if ($this.ListType -eq "Inline") { + return "- $($this.ToolName): $($this.Versions -join ', ')" + } + + $sb = [System.Text.StringBuilder]::new() + $sb.AppendLine() + $sb.AppendLine("$("#" * $Level) $($this.ToolName)") + $this.Versions | ForEach-Object { + $sb.AppendLine("- $_") + } + + return $sb.ToString().TrimEnd() + } + + [String] GetValue() { + return $this.Versions -join ', ' + } + + [String] ExtractMajorVersion([String] $Version) { + $match = $this.MajorVersionRegex.Match($Version) + if (($match.Success -ne $true) -or [String]::IsNullOrEmpty($match.Groups[0].Value)) { + throw "Version '$Version' doesn't match regex '$($this.PrimaryVersionRegex)'" + } + + return $match.Groups[0].Value + } + + [PSCustomObject] ToJsonObject() { + return [PSCustomObject]@{ + NodeType = $this.GetType().Name + ToolName = $this.ToolName + Versions = $this.Versions + MajorVersionRegex = $this.MajorVersionRegex.ToString() + ListType = $this.ListType + } + } + + static [ToolVersionsListNode] FromJsonObject([Object] $JsonObj) { + return [ToolVersionsListNode]::new($JsonObj.ToolName, $JsonObj.Versions, $JsonObj.MajorVersionRegex, $JsonObj.ListType) + } + + hidden [void] ValidateMajorVersionRegex() { + $this.Versions | Group-Object { $this.ExtractMajorVersion($_) } | ForEach-Object { + if ($_.Count -gt 1) { + throw "Multiple versions from list '$($this.GetValue())' return the same result from regex '$($this.MajorVersionRegex)': $($_.Name)" + } + } + } +} + +class TableNode: BaseNode { + # It is easier to store the table as rendered lines because it will simplify finding differences in rows later + [ValidateNotNullOrEmpty()] + [String] $Headers + [ValidateNotNullOrEmpty()] + [String[]] $Rows + + TableNode([String] $Headers, [String[]] $Rows) { + $this.Headers = $Headers + $this.Rows = $Rows + + $columnsCount = $this.Headers.Split("|").Count + $this.Rows | ForEach-Object { + if ($_.Split("|").Count -ne $columnsCount) { + throw "Table has different number of columns in different rows" + } + } + } + + [Boolean] ShouldBeIncludedToDiff() { + return $true + } + + [String] ToMarkdown([Int32] $Level) { + $maxColumnWidths = $this.CalculateColumnsWidth() + $columnsCount = $maxColumnWidths.Count + + $delimiterLine = [String]::Join("|", @("-") * $columnsCount) + + $sb = [System.Text.StringBuilder]::new() + @($this.Headers) + @($delimiterLine) + $this.Rows | ForEach-Object { + $sb.Append("|") + $row = $_.Split("|") + + for ($colIndex = 0; $colIndex -lt $columnsCount; $colIndex++) { + $padSymbol = $row[$colIndex] -eq "-" ? "-" : " " + $cellContent = $row[$colIndex].PadRight($maxColumnWidths[$colIndex], $padSymbol) + $sb.Append(" $($cellContent) |") + } + + $sb.AppendLine() + } + + return $sb.ToString().TrimEnd() + } + + hidden [Int32[]] CalculateColumnsWidth() { + $maxColumnWidths = $this.Headers.Split("|") | ForEach-Object { $_.Length } + $columnsCount = $maxColumnWidths.Count + + $this.Rows | ForEach-Object { + $columnWidths = $_.Split("|") | ForEach-Object { $_.Length } + for ($colIndex = 0; $colIndex -lt $columnsCount; $colIndex++) { + $maxColumnWidths[$colIndex] = [Math]::Max($maxColumnWidths[$colIndex], $columnWidths[$colIndex]) + } + } + + return $maxColumnWidths + } + + [PSCustomObject] ToJsonObject() { + return [PSCustomObject]@{ + NodeType = $this.GetType().Name + Headers = $this.Headers + Rows = $this.Rows + } + } + + static [TableNode] FromJsonObject([Object] $JsonObj) { + return [TableNode]::new($JsonObj.Headers, $JsonObj.Rows) + } + + [Boolean] IsSimilarTo([BaseNode] $OtherNode) { + if ($OtherNode.GetType() -ne [TableNode]) { + return $false + } + + # We don't support having multiple TableNode instances on the same header level so such check is fine + return $true + } + + [Boolean] IsIdenticalTo([BaseNode] $OtherNode) { + if (-not $this.IsSimilarTo($OtherNode)) { + return $false + } + + # We don't compare $this.Headers intentionally + # It is fine to ignore the tables where headers are changed but rows are not changed + + if ($this.Rows.Count -ne $OtherNode.Rows.Count) { + return $false + } + + for ($rowIndex = 0; $rowIndex -lt $this.Rows.Count; $rowIndex++) { + if ($this.Rows[$rowIndex] -ne $OtherNode.Rows[$rowIndex]) { + return $false + } + } + + return $true + } + + static [TableNode] FromObjectsArray([PSCustomObject[]] $Table) { + if ($Table.Count -eq 0) { + throw "Failed to create TableNode from empty objects array" + } + + [String] $tableHeaders = [TableNode]::ArrayToTableRow($Table[0].PSObject.Properties.Name) + [Collections.Generic.List[String]] $tableRows = @() + + $Table | ForEach-Object { + $rowHeaders = [TableNode]::ArrayToTableRow($_.PSObject.Properties.Name) + if (($rowHeaders -ne $tableHeaders)) { + throw "Failed to create TableNode from objects array because objects have different properties" + } + + $tableRows.Add([TableNode]::ArrayToTableRow($_.PSObject.Properties.Value)) + } + + return [TableNode]::new($tableHeaders, $tableRows) + } + + hidden static [String] ArrayToTableRow([String[]] $Values) { + if ($Values.Count -eq 0) { + throw "Failed to create TableNode because some objects are empty" + } + $Values | ForEach-Object { + if ($_.Contains("|")) { + throw "Failed to create TableNode because some cells '$_' contains forbidden symbol '|'" + } + } + + return [String]::Join("|", $Values) + } +} + +class NoteNode: BaseNode { + [ValidateNotNullOrEmpty()] + [String] $Content + + NoteNode([String] $Content) { + $this.Content = $Content + } + + [String] ToMarkdown([Int32] $Level) { + return @( + '```', + $this.Content, + '```' + ) -join "`n" + } + + [PSCustomObject] ToJsonObject() { + return [PSCustomObject]@{ + NodeType = $this.GetType().Name + Content = $this.Content + } + } + + static [NoteNode] FromJsonObject([Object] $JsonObj) { + return [NoteNode]::new($JsonObj.Content) + } + + [Boolean] IsSimilarTo([BaseNode] $OtherNode) { + if ($OtherNode.GetType() -ne [NoteNode]) { + return $false + } + + return $this.Content -eq $OtherNode.Content + } + + [Boolean] IsIdenticalTo([BaseNode] $OtherNode) { + return $this.IsSimilarTo($OtherNode) + } +} diff --git a/helpers/software-report-base/SoftwareReport.psm1 b/helpers/software-report-base/SoftwareReport.psm1 new file mode 100644 index 0000000..c78757f --- /dev/null +++ b/helpers/software-report-base/SoftwareReport.psm1 @@ -0,0 +1,34 @@ +using module ./SoftwareReport.BaseNodes.psm1 +using module ./SoftwareReport.Nodes.psm1 + +class SoftwareReport { + [ValidateNotNullOrEmpty()] + [HeaderNode] $Root + + SoftwareReport([String] $Title) { + $this.Root = [HeaderNode]::new($Title) + } + + SoftwareReport([HeaderNode] $Root) { + $this.Root = $Root + } + + [String] ToJson() { + return $this.Root.ToJsonObject() | ConvertTo-Json -Depth 10 + } + + static [SoftwareReport] FromJson([String] $JsonString) { + $jsonObj = $JsonString | ConvertFrom-Json + $rootNode = [NodesFactory]::ParseNodeFromObject($jsonObj) + return [SoftwareReport]::new($rootNode) + } + + [String] ToMarkdown() { + return $this.Root.ToMarkdown().Trim() + } + + [String] GetImageVersion() { + $imageVersionNode = $this.Root.Children ?? @() | Where-Object { ($_ -is [ToolVersionNode]) -and ($_.ToolName -eq "Image Version:") } | Select-Object -First 1 + return $imageVersionNode.Version ?? "Unknown version" + } +} \ No newline at end of file diff --git a/helpers/software-report-base/tests/SoftwareReport.Difference.E2E.Tests.ps1 b/helpers/software-report-base/tests/SoftwareReport.Difference.E2E.Tests.ps1 new file mode 100644 index 0000000..1e76e24 --- /dev/null +++ b/helpers/software-report-base/tests/SoftwareReport.Difference.E2E.Tests.ps1 @@ -0,0 +1,558 @@ +using module ../SoftwareReport.psm1 +using module ../SoftwareReport.DifferenceCalculator.psm1 + +Describe "Comparer.E2E" { + It "Some tools are updated" { + # Previous report + $prevSoftwareReport = [SoftwareReport]::new("macOS 11") + $prevSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.1 (20G817)") + $prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1") + $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software") + $prevTools = $prevInstalledSoftware.AddHeader("Tools") + $prevTools.AddToolVersion("ToolWillBeUpdated1", "1.0.0") + $prevTools.AddToolVersion("ToolWillBeUpdated2", "3.0.1") + $prevTools.AddToolVersionsList("ToolWillBeUpdated3", @("14.0.0", "15.5.1"), "^\d+") + + # Next report + $nextSoftwareReport = [SoftwareReport]::new("macOS 11") + $nextSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.1 (20G817)") + $nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.1") + $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software") + $nextTools = $nextInstalledSoftware.AddHeader("Tools") + $nextTools.AddToolVersion("ToolWillBeUpdated1", "2.5.0") + $nextTools.AddToolVersion("ToolWillBeUpdated2", "3.0.2") + $nextTools.AddToolVersionsList("ToolWillBeUpdated3", @("14.2.0", "15.5.1"), "^\d+") + + # Compare reports + $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport) + $comparer.CompareReports() + $comparer.GetMarkdownReport() | Should -BeExactly @' +# :desktop_computer: Actions Runner Image: macOS 11 +- OS Version: macOS 11.7.1 (20G817) +- Image Version: 20220922.1 + +## :mega: What's changed? + +### Updated + + + + + + + + + + + + + + + + + + + + + + + + + + +
CategoryTool namePrevious (20220918.1)Current (20220922.1)
ToolsToolWillBeUpdated11.0.02.5.0
ToolWillBeUpdated23.0.13.0.2
ToolWillBeUpdated314.0.014.2.0
+ + +'@ + } + + It "Some tools are updated, added and removed" { + # Previous report + $prevSoftwareReport = [SoftwareReport]::new("macOS 11") + $prevSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.1 (20G817)") + $prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1") + $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software") + + $prevLanguagesAndRuntimes = $prevInstalledSoftware.AddHeader("Language and Runtime") + $prevLanguagesAndRuntimes.AddToolVersion("ToolWillBeRemoved", "5.1.16(1)-release") + $prevLanguagesAndRuntimes.AddToolVersionsListInline("ToolWithMultipleVersions3", @("1.2.100", "1.2.200", "1.3.500", "1.4.100", "1.4.200"), "^\d+\.\d+\.\d") + $prevLanguagesAndRuntimes.AddToolVersion("ToolWithoutChanges", "5.34.0") + $prevLanguagesAndRuntimes.AddToolVersion("ToolWillBeUpdated", "8.1.0") + + $prevCachedTools = $prevInstalledSoftware.AddHeader("Cached Tools") + $prevCachedTools.AddToolVersionsList("ToolWithMultipleVersions1", @("2.7.3", "2.8.1", "3.1.2"), "^\d+\.\d+") + $prevCachedTools.AddToolVersionsList("ToolWithMultipleVersions2", @("14.8.0", "15.1.0", "16.4.2"), "^\d+") + + $prevSQLSection = $prevInstalledSoftware.AddHeader("Databases") + $prevSQLSection.AddToolVersion("MineSQL", "6.1.0") + $prevSQLSection.AddNote("First Note") + + # Next report + $nextSoftwareReport = [SoftwareReport]::new("macOS 11") + $nextSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.2 (20G922)") + $nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.0") + $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software") + + $nextLanguagesAndRuntimes = $nextInstalledSoftware.AddHeader("Language and Runtime") + $nextLanguagesAndRuntimes.AddToolVersion("ToolWillBeAdded", "16.18.0") + $nextLanguagesAndRuntimes.AddToolVersionsListInline("ToolWithMultipleVersions3", @("1.2.200", "1.3.515", "1.4.100", "1.4.200", "1.5.800"), "^\d+\.\d+\.\d") + $nextLanguagesAndRuntimes.AddToolVersion("ToolWithoutChanges", "5.34.0") + $nextLanguagesAndRuntimes.AddToolVersion("ToolWillBeUpdated", "8.3.0") + + $nextCachedTools = $nextInstalledSoftware.AddHeader("Cached Tools") + $nextCachedTools.AddToolVersionsList("ToolWithMultipleVersions1", @("2.7.3", "2.8.1", "3.1.2"), "^\d+\.\d+") + $nextCachedTools.AddToolVersionsList("ToolWithMultipleVersions2", @("15.1.0", "16.4.2", "17.0.1"), "^\d+") + + $nextSQLSection = $nextInstalledSoftware.AddHeader("Databases") + $nextSQLSection.AddToolVersion("MineSQL", "6.1.1") + $nextSQLSection.AddNote("Second Note") + + # Compare reports + $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport) + $comparer.CompareReports() + $comparer.GetMarkdownReport() | Should -BeExactly @' +# :desktop_computer: Actions Runner Image: macOS 11 +- OS Version: macOS 11.7.2 (20G922) +- Image Version: 20220922.0 + +## :mega: What's changed? + +### Added :heavy_plus_sign: + + + + + + + + + + + + + + + + + + + + + + + +
CategoryTool nameCurrent (20220922.0)
Language and RuntimeToolWillBeAdded16.18.0
ToolWithMultipleVersions31.5.800
Cached ToolsToolWithMultipleVersions217.0.1
+ +### Deleted :heavy_minus_sign: + + + + + + + + + + + + + + + + + + + + + + + +
CategoryTool namePrevious (20220918.1)
Language and RuntimeToolWithMultipleVersions31.2.100
ToolWillBeRemoved5.1.16(1)-release
Cached ToolsToolWithMultipleVersions214.8.0
+ +### Updated + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CategoryTool namePrevious (20220918.1)Current (20220922.0)
OS VersionmacOS 11.7.1 (20G817)macOS 11.7.2 (20G922)
Language and RuntimeToolWithMultipleVersions31.3.5001.3.515
ToolWillBeUpdated8.1.08.3.0
DatabasesMineSQL6.1.06.1.1
+ + +'@ + } + + It "Header tree changes" { + # Previous report + $prevSoftwareReport = [SoftwareReport]::new("macOS 11") + $prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1") + $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software") + $prevInstalledSoftware.AddToolVersion("ToolWithoutChanges", "5.34.0") + $prevInstalledSoftware.AddHeader("HeaderWillBeRemoved").AddHeader("SubheaderWillBeRemoved").AddToolVersion("ToolWillBeRemoved", "1.0.0") + $prevInstalledSoftware.AddHeader("Header1").AddToolVersion("ToolWillBeMovedToAnotherHeader", "3.0.0") + + # Next report + $nextSoftwareReport = [SoftwareReport]::new("macOS 11") + $nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.0") + $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software") + $nextInstalledSoftware.AddToolVersion("ToolWithoutChanges", "5.34.0") + $nextInstalledSoftware.AddHeader("HeaderWillBeAdded").AddHeader("SubheaderWillBeAdded").AddToolVersion("ToolWillBeAdded", "5.0.0") + $nextInstalledSoftware.AddHeader("Header2").AddToolVersion("ToolWillBeMovedToAnotherHeader", "3.0.0") + + # Compare reports + $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport) + $comparer.CompareReports() + $comparer.GetMarkdownReport() | Should -BeExactly @' +# :desktop_computer: Actions Runner Image: macOS 11 +- Image Version: 20220922.0 + +## :mega: What's changed? + +### Added :heavy_plus_sign: + + + + + + + + + + + + + + + + + + + +
CategoryTool nameCurrent (20220922.0)
HeaderWillBeAdded >
SubheaderWillBeAdded
ToolWillBeAdded5.0.0
Header2ToolWillBeMovedToAnotherHeader3.0.0
+ +### Deleted :heavy_minus_sign: + + + + + + + + + + + + + + + + + + + +
CategoryTool namePrevious (20220918.1)
HeaderWillBeRemoved >
SubheaderWillBeRemoved
ToolWillBeRemoved1.0.0
Header1ToolWillBeMovedToAnotherHeader3.0.0
+ + +'@ + } + + It "Tables are added and removed" { + # Previous report + $prevSoftwareReport = [SoftwareReport]::new("macOS 11") + $prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1") + $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software") + $prevInstalledSoftware.AddHeader("HeaderWillExist").AddTable(@( + [PSCustomObject]@{TableInExistingHeaderWillBeRemoved = "Q"; Value = "25"}, + [PSCustomObject]@{TableInExistingHeaderWillBeRemoved = "O"; Value = "24"} + )) + + $prevTools = $prevInstalledSoftware.AddHeader("Tools") + $prevTools.AddHeader("HeaderWillBeRemoved").AddTable(@( + [PSCustomObject]@{TableWillBeRemovedWithHeader = "Z"; Value = "30"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "W"; Value = "29"} + )) + + # Next report + $nextSoftwareReport = [SoftwareReport]::new("macOS 11") + $nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.1") + $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software") + $nextInstalledSoftware.AddHeader("HeaderWillExist") + $nextTools = $nextInstalledSoftware.AddHeader("Tools") + $nextTools.AddToolVersion("ToolWillBeAdded", "3.0.1") + $nextTools.AddTable(@( + [PSCustomObject]@{NewTableInExistingHeader = "A"; Value = "1"}, + [PSCustomObject]@{NewTableInExistingHeader = "B"; Value = "2"} + )) + $nextTools.AddHeader("NewHeaderWithTable").AddTable(@( + [PSCustomObject]@{NewTableInNewHeader = "C"; Value = "3"}, + [PSCustomObject]@{NewTableInNewHeader = "D"; Value = "4"} + )) + + # Compare reports + $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport) + $comparer.CompareReports() + $comparer.GetMarkdownReport() | Should -BeExactly @' +# :desktop_computer: Actions Runner Image: macOS 11 +- Image Version: 20220922.1 + +## :mega: What's changed? + +### Added :heavy_plus_sign: + + + + + + + + + + + + + + +
CategoryTool nameCurrent (20220922.1)
ToolsToolWillBeAdded3.0.1
+ +#### Tools +| NewTableInExistingHeader | Value | +| ------------------------ | ----- | +| A | 1 | +| B | 2 | + +#### Tools > NewHeaderWithTable +| NewTableInNewHeader | Value | +| ------------------- | ----- | +| C | 3 | +| D | 4 | + +### Deleted :heavy_minus_sign: + +#### HeaderWillExist +| TableInExistingHeaderWillBeRemoved | Value | +| ---------------------------------- | ------ | +| ~~Q~~ | ~~25~~ | +| ~~O~~ | ~~24~~ | + +#### Tools > HeaderWillBeRemoved +| TableWillBeRemovedWithHeader | Value | +| ---------------------------- | ------ | +| ~~Z~~ | ~~30~~ | +| ~~W~~ | ~~29~~ | + + +'@ + } + + It "Tables are changed" { + # Previous report + $prevSoftwareReport = [SoftwareReport]::new("macOS 11") + $prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1") + $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software") + $prevTools = $prevInstalledSoftware.AddHeader("Tools") + $prevTools.AddHeader("TableWithAddedRows").AddTable(@( + [PSCustomObject]@{TableWillBeRemovedWithHeader = "AA"; Value = "10"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "AB"; Value = "11"} + )) + $prevTools.AddHeader("TableWithRemovedRows").AddTable(@( + [PSCustomObject]@{TableWillBeRemovedWithHeader = "BA"; Value = "32"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "BB"; Value = "33"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "BC"; Value = "34"} + )) + $prevTools.AddHeader("TableWithUpdatedRow").AddTable(@( + [PSCustomObject]@{TableWillBeRemovedWithHeader = "CA"; Value = "42"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "CB"; Value = "43"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "CC"; Value = "44"} + )) + $prevTools.AddHeader("TableWithUpdatedRows").AddTable(@( + [PSCustomObject]@{TableWillBeRemovedWithHeader = "DA"; Value = "50"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "DB"; Value = "51"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "DC"; Value = "52"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "DD"; Value = "53"} + )) + $prevTools.AddHeader("TableWithComplexChanges").AddTable(@( + [PSCustomObject]@{TableWillBeRemovedWithHeader = "EA"; Value = "62"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "EB"; Value = "63"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "EC"; Value = "64"} + [PSCustomObject]@{TableWillBeRemovedWithHeader = "ED"; Value = "65"} + )) + + $prevTools.AddHeader("TableWithOnlyHeaderChanged").AddTable(@( + [PSCustomObject]@{TableWithOnlyHeaderChanged = "FA"; Value = "72"}, + [PSCustomObject]@{TableWithOnlyHeaderChanged = "FB"; Value = "73"} + )) + + $prevTools.AddHeader("TableWithHeaderAndRowsChanges").AddTable(@( + [PSCustomObject]@{TableWithHeaderAndRowsChanges = "GA"; Value = "82"}, + [PSCustomObject]@{TableWithHeaderAndRowsChanges = "GB"; Value = "83"}, + [PSCustomObject]@{TableWithHeaderAndRowsChanges = "GC"; Value = "84"} + )) + + # Next report + $nextSoftwareReport = [SoftwareReport]::new("macOS 11") + $nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.1") + $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software") + $nextTools = $nextInstalledSoftware.AddHeader("Tools") + $nextTools.AddHeader("TableWithAddedRows").AddTable(@( + [PSCustomObject]@{TableWillBeRemovedWithHeader = "AA"; Value = "10"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "AB"; Value = "11"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "AC"; Value = "12"} + )) + $nextTools.AddHeader("TableWithRemovedRows").AddTable(@( + [PSCustomObject]@{TableWillBeRemovedWithHeader = "BB"; Value = "33"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "BC"; Value = "34"} + )) + $nextTools.AddHeader("TableWithUpdatedRow").AddTable(@( + [PSCustomObject]@{TableWillBeRemovedWithHeader = "CA"; Value = "42"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "CB"; Value = "500"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "CC"; Value = "44"} + )) + $nextTools.AddHeader("TableWithUpdatedRows").AddTable(@( + [PSCustomObject]@{TableWillBeRemovedWithHeader = "DA"; Value = "50"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "DB"; Value = "5100"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "DC"; Value = "5200"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "DD"; Value = "53"} + )) + $nextTools.AddHeader("TableWithComplexChanges").AddTable(@( + [PSCustomObject]@{TableWillBeRemovedWithHeader = "EB"; Value = "63"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "EC"; Value = "640"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "ED"; Value = "65"}, + [PSCustomObject]@{TableWillBeRemovedWithHeader = "EE"; Value = "66"} + )) + + $nextTools.AddHeader("TableWithOnlyHeaderChanged").AddTable(@( + [PSCustomObject]@{TableWithOnlyHeaderChanged2 = "FA"; Value = "72"}, + [PSCustomObject]@{TableWithOnlyHeaderChanged2 = "FB"; Value = "73"} + )) + + $nextTools.AddHeader("TableWithHeaderAndRowsChanges").AddTable(@( + [PSCustomObject]@{TableWithHeaderAndRowsChanges2 = "GA"; Value = "82"}, + [PSCustomObject]@{TableWithHeaderAndRowsChanges2 = "GE"; Value = "850"}, + [PSCustomObject]@{TableWithHeaderAndRowsChanges2 = "GC"; Value = "840"} + )) + + # Compare reports + $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport) + $comparer.CompareReports() + $comparer.GetMarkdownReport() | Should -BeExactly @' +# :desktop_computer: Actions Runner Image: macOS 11 +- Image Version: 20220922.1 + +## :mega: What's changed? + +### Added :heavy_plus_sign: + +#### Tools > TableWithAddedRows +| TableWillBeRemovedWithHeader | Value | +| ---------------------------- | ----- | +| AC | 12 | + +#### Tools > TableWithHeaderAndRowsChanges +| TableWithHeaderAndRowsChanges2 | Value | +| ------------------------------ | ----- | +| GA | 82 | +| GE | 850 | +| GC | 840 | + +### Deleted :heavy_minus_sign: + +#### Tools > TableWithRemovedRows +| TableWillBeRemovedWithHeader | Value | +| ---------------------------- | ------ | +| ~~BA~~ | ~~32~~ | + +#### Tools > TableWithHeaderAndRowsChanges +| TableWithHeaderAndRowsChanges | Value | +| ----------------------------- | ------ | +| ~~GA~~ | ~~82~~ | +| ~~GB~~ | ~~83~~ | +| ~~GC~~ | ~~84~~ | + +### Updated + +#### Tools > TableWithUpdatedRow +| TableWillBeRemovedWithHeader | Value | +| ---------------------------- | ------ | +| ~~CB~~ | ~~43~~ | +| CB | 500 | + +#### Tools > TableWithUpdatedRows +| TableWillBeRemovedWithHeader | Value | +| ---------------------------- | ------ | +| ~~DB~~ | ~~51~~ | +| ~~DC~~ | ~~52~~ | +| DB | 5100 | +| DC | 5200 | + +#### Tools > TableWithComplexChanges +| TableWillBeRemovedWithHeader | Value | +| ---------------------------- | ------ | +| ~~EA~~ | ~~62~~ | +| ~~EC~~ | ~~64~~ | +| EC | 640 | +| EE | 66 | + + +'@ + } + + It "Reports are identical" { + # Previous report + $prevSoftwareReport = [SoftwareReport]::new("macOS 11") + $prevSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.1 (20G817)") + $prevSoftwareReport.Root.AddToolVersion("Image Version:", "20220918.1") + $prevInstalledSoftware = $prevSoftwareReport.Root.AddHeader("Installed Software") + $prevTools = $prevInstalledSoftware.AddHeader("Tools") + $prevTools.AddToolVersion("ToolA", "1.0.0") + $prevTools.AddToolVersion("ToolB", "3.0.1") + + # Next report + $nextSoftwareReport = [SoftwareReport]::new("macOS 11") + $nextSoftwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7.1 (20G817)") + $nextSoftwareReport.Root.AddToolVersion("Image Version:", "20220922.1") + $nextInstalledSoftware = $nextSoftwareReport.Root.AddHeader("Installed Software") + $nextTools = $nextInstalledSoftware.AddHeader("Tools") + $nextTools.AddToolVersion("ToolA", "1.0.0") + $nextTools.AddToolVersion("ToolB", "3.0.1") + + # Compare reports + $comparer = [SoftwareReportDifferenceCalculator]::new($prevSoftwareReport, $nextSoftwareReport) + $comparer.CompareReports() + $comparer.GetMarkdownReport() | Should -BeExactly @' +# :desktop_computer: Actions Runner Image: macOS 11 +- OS Version: macOS 11.7.1 (20G817) +- Image Version: 20220922.1 + +## :mega: What's changed? + + +'@ + } +} \ No newline at end of file diff --git a/helpers/software-report-base/tests/SoftwareReport.DifferenceCalculator.Unit.Tests.ps1 b/helpers/software-report-base/tests/SoftwareReport.DifferenceCalculator.Unit.Tests.ps1 new file mode 100644 index 0000000..31e690e --- /dev/null +++ b/helpers/software-report-base/tests/SoftwareReport.DifferenceCalculator.Unit.Tests.ps1 @@ -0,0 +1,603 @@ +using module ../SoftwareReport.Nodes.psm1 +using module ../SoftwareReport.DifferenceCalculator.psm1 + +BeforeDiscovery { + Import-Module $(Join-Path $PSScriptRoot "TestHelpers.psm1") -DisableNameChecking +} + +Describe "Comparer.UnitTests" { + Describe "Headers Tree" { + It "Add Node to existing header" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddToolVersion("MyTool1", "2.1.3") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 1 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 0 + + $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty + $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.AddedItems[0].CurrentReportNode.Version | Should -Be "2.1.3" + $comparer.AddedItems[0].Headers | Should -BeArray @("MyHeader") + } + + It "Add new header with Node" { + $prevReport = [HeaderNode]::new("Version 1") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddHeader("MySubHeader").AddToolVersion("MyTool1", "2.1.3") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 1 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 0 + + $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty + $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.AddedItems[0].CurrentReportNode.Version | Should -Be "2.1.3" + $comparer.AddedItems[0].Headers | Should -BeArray @("MyHeader", "MySubHeader") + } + + It "Remove Node from existing header" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddToolVersion("MyTool1", "2.1.3") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 1 + + $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be "2.1.3" + $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty + $comparer.DeletedItems[0].Headers | Should -BeArray @("MyHeader") + } + + It "Remove header with Node" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.3") + + $nextReport = [HeaderNode]::new("Version 2") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 1 + + $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be "2.1.3" + $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty + $comparer.DeletedItems[0].Headers | Should -BeArray @("MyHeader", "MySubheader") + } + + It "Node with minor changes" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.3") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.4") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 1 + $comparer.DeletedItems | Should -HaveCount 0 + + $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].PreviousReportNode.Version | Should -Be "2.1.3" + $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].CurrentReportNode.Version | Should -Be "2.1.4" + $comparer.ChangedItems[0].Headers | Should -BeArray @("MyHeader", "MySubHeader") + } + + It "Node without changes" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.3") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.3") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 0 + } + + It "Node is moved to different header" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddHeader("MySubheader").AddToolVersion("MyTool1", "2.1.3") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddHeader("MySubheader2").AddToolVersion("MyTool1", "2.1.3") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 1 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 1 + + $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty + $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.AddedItems[0].CurrentReportNode.Version | Should -Be "2.1.3" + $comparer.AddedItems[0].Headers | Should -BeArray @("MyHeader", "MySubheader2") + + $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be "2.1.3" + $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty + $comparer.DeletedItems[0].Headers | Should -BeArray @("MyHeader", "MySubheader") + } + + It "Complex structure" { + $prevReport = [HeaderNode]::new("Version 1") + $prevSubHeader = $prevReport.AddHeader("MyHeader").AddHeader("MySubheader") + $prevSubHeader.AddToolVersion("MyTool1", "2.1.3") + $prevSubHeader.AddHeader("MySubSubheader").AddToolVersion("MyTool2", "2.9.1") + $prevReport.AddHeader("MyHeader2") + $prevReport.AddHeader("MyHeader3").AddHeader("MySubheader3").AddToolVersion("MyTool3", "14.2.1") + + $nextReport = [HeaderNode]::new("Version 2") + $nextSubHeader = $nextReport.AddHeader("MyHeader").AddHeader("MySubheader") + $nextSubHeader.AddToolVersion("MyTool1", "2.1.4") + $nextSubSubHeader = $nextSubHeader.AddHeader("MySubSubheader") + $nextSubSubHeader.AddToolVersion("MyTool2", "2.9.1") + $nextSubSubHeader.AddToolVersion("MyTool4", "2.7.6") + $nextReport.AddHeader("MyHeader2") + $nextReport.AddHeader("MyHeader3") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 1 + $comparer.ChangedItems | Should -HaveCount 1 + $comparer.DeletedItems | Should -HaveCount 1 + + $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty + $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool4" + $comparer.AddedItems[0].CurrentReportNode.Version | Should -Be "2.7.6" + $comparer.AddedItems[0].Headers | Should -BeArray @("MyHeader", "MySubheader", "MySubSubheader") + + $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].PreviousReportNode.Version | Should -Be "2.1.3" + $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].CurrentReportNode.Version | Should -Be "2.1.4" + $comparer.ChangedItems[0].Headers | Should -BeArray @("MyHeader", "MySubheader") + + $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool3" + $comparer.DeletedItems[0].PreviousReportNode.Version | Should -Be "14.2.1" + $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty + $comparer.DeletedItems[0].Headers | Should -BeArray @("MyHeader3", "MySubheader3") + } + } + + Describe "ToolVersionNode" { + It "ToolVersionNode is updated" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddToolVersion("MyTool1", "2.1.3") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddToolVersion("MyTool1", "2.1.4") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 1 + $comparer.DeletedItems | Should -HaveCount 0 + + $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].PreviousReportNode.Version | Should -Be "2.1.3" + $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionNode]) + $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].CurrentReportNode.Version | Should -Be "2.1.4" + $comparer.ChangedItems[0].Headers | Should -BeArray @("MyHeader") + } + } + + Describe "ToolVersionsListNode" { + It "Single version is not changed" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3"), "^.+") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3"), "^.+") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 0 + } + + It "Single version is changed" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3"), "^\d+") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.4"), "^\d+") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 1 + $comparer.DeletedItems | Should -HaveCount 0 + + $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @("2.1.3") + $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @("2.1.4") + } + + It "Major version is added" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3"), "^\d+") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3", "3.1.4"), "^\d+") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 1 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 0 + + $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty + $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @("3.1.4") + } + + It "Major version is removed" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3", "3.1.4"), "^\d+") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("3.1.4"), "^\d+") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 1 + + $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @("2.1.3") + $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty + } + + It "Major version is changed" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("3.1.4"), "^\d+") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("3.2.0"), "^\d+") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 1 + $comparer.DeletedItems | Should -HaveCount 0 + + $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @("3.1.4") + $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @("3.2.0") + } + + It "Major version is added, removed and updated at the same time" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("1.0.0", "2.1.3", "3.1.4", "4.0.2"), "^\d+") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.1.3", "3.2.0", "4.0.2", "5.1.0"), "^\d+") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 1 + $comparer.ChangedItems | Should -HaveCount 1 + $comparer.DeletedItems | Should -HaveCount 1 + + $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty + $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @("5.1.0") + + $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @("3.1.4") + $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @("3.2.0") + + $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @("1.0.0") + $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty + } + + It "Minor version is added, removed and updated at the same time" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.3.8", "2.4.9", "2.5.3", "2.6.0", "2.7.4", "2.8.0"), "^\d+\.\d+") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.5.3", "2.6.2", "2.7.5", "2.8.0", "2.9.2", "2.10.3"), "^\d+\.\d+") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 1 + $comparer.ChangedItems | Should -HaveCount 1 + $comparer.DeletedItems | Should -HaveCount 1 + + $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty + $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @("2.9.2", "2.10.3") + + $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.ChangedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].PreviousReportNode.Versions | Should -BeArray @("2.6.0", "2.7.4") + $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.ChangedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.ChangedItems[0].CurrentReportNode.Versions | Should -BeArray @("2.6.2", "2.7.5") + + $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @("2.3.8", "2.4.9") + $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty + } + + It "Patch version is added, removed and updated at the same time" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.3.8", "2.4.9", "2.5.3", "2.6.0", "2.7.4"), "^\d+\.\d+\.\d+") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddToolVersionsList("MyTool1", @("2.4.9", "2.5.4", "2.6.0", "2.7.5", "2.8.2"), "^\d+\.\d+\.\d+") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 1 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 1 + + $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty + $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.AddedItems[0].CurrentReportNode.ToolName | Should -Be "MyTool1" + $comparer.AddedItems[0].CurrentReportNode.Versions | Should -BeArray @("2.5.4", "2.7.5", "2.8.2") + + $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([ToolVersionsListNode]) + $comparer.DeletedItems[0].PreviousReportNode.ToolName | Should -Be "MyTool1" + $comparer.DeletedItems[0].PreviousReportNode.Versions | Should -BeArray @("2.3.8", "2.5.3", "2.7.4") + $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty + } + } + + Describe "TableNode" { + It "Rows are added" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2"))) + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2", "C1|C2", "D1|D2"))) + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 1 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 0 + + $comparer.AddedItems[0].PreviousReportNode | Should -BeOfType ([TableNode]) + $comparer.AddedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value" + $comparer.AddedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2") + $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([TableNode]) + $comparer.AddedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value" + $comparer.AddedItems[0].CurrentReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2", "C1|C2", "D1|D2") + } + + It "Rows are deleted" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2", "C1|C2", "D1|D2"))) + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("C1|C2", "D1|D2"))) + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 1 + + $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([TableNode]) + $comparer.DeletedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value" + $comparer.DeletedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2", "C1|C2", "D1|D2") + $comparer.DeletedItems[0].CurrentReportNode | Should -BeOfType ([TableNode]) + $comparer.DeletedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value" + $comparer.DeletedItems[0].CurrentReportNode.Rows | Should -BeArray @("C1|C2", "D1|D2") + } + + It "Rows are changed" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2"))) + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B3|B4"))) + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 1 + $comparer.DeletedItems | Should -HaveCount 0 + + $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([TableNode]) + $comparer.ChangedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value" + $comparer.ChangedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2") + $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([TableNode]) + $comparer.ChangedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value" + $comparer.ChangedItems[0].CurrentReportNode.Rows | Should -BeArray @("A1|A2", "B3|B4") + } + + It "Rows are changed and updated at the same time" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2"))) + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B3|B4", "C1|C2"))) + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 1 + $comparer.DeletedItems | Should -HaveCount 0 + + $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([TableNode]) + $comparer.ChangedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value" + $comparer.ChangedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2") + $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([TableNode]) + $comparer.ChangedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value" + $comparer.ChangedItems[0].CurrentReportNode.Rows | Should -BeArray @("A1|A2", "B3|B4", "C1|C2") + } + + It "Rows are changed and removed at the same time" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2", "C1|C2"))) + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B3|B4"))) + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 1 + $comparer.DeletedItems | Should -HaveCount 0 + + $comparer.ChangedItems[0].PreviousReportNode | Should -BeOfType ([TableNode]) + $comparer.ChangedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value" + $comparer.ChangedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2", "C1|C2") + $comparer.ChangedItems[0].CurrentReportNode | Should -BeOfType ([TableNode]) + $comparer.ChangedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value" + $comparer.ChangedItems[0].CurrentReportNode.Rows | Should -BeArray @("A1|A2", "B3|B4") + } + + It "Rows are not changed" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2"))) + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2"))) + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 0 + } + + It "Rows are not changed but header is changed" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2"))) + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value2", @("A1|A2", "B1|B2"))) + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 0 + } + + It "Rows are changed and header is changed at the same time" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value", @("A1|A2", "B1|B2"))) + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddHeader("MyHeader").AddNode([TableNode]::new("Name|Value2", @("A1|A2", "B1|B2", "C1|C2"))) + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 1 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 1 + + $comparer.AddedItems[0].PreviousReportNode | Should -BeNullOrEmpty + $comparer.AddedItems[0].CurrentReportNode | Should -BeOfType ([TableNode]) + $comparer.AddedItems[0].CurrentReportNode.Headers | Should -Be "Name|Value2" + $comparer.AddedItems[0].CurrentReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2", "C1|C2") + + $comparer.DeletedItems[0].PreviousReportNode | Should -BeOfType ([TableNode]) + $comparer.DeletedItems[0].PreviousReportNode.Headers | Should -Be "Name|Value" + $comparer.DeletedItems[0].PreviousReportNode.Rows | Should -BeArray @("A1|A2", "B1|B2") + $comparer.DeletedItems[0].CurrentReportNode | Should -BeNullOrEmpty + } + } + + Describe "NoteNode" { + It "NoteNode is ignored from report" { + $prevReport = [HeaderNode]::new("Version 1") + $prevReport.AddNote("MyFirstNote") + $prevReport.AddHeader("MyFirstHeader").AddNote("MyFirstSubNote") + + $nextReport = [HeaderNode]::new("Version 2") + $nextReport.AddNote("MySecondNote") + $nextReport.AddHeader("MySecondHeader").AddNote("MySecondSubNote") + + $comparer = [SoftwareReportDifferenceCalculator]::new($prevReport, $nextReport) + $comparer.CompareReports() + + $comparer.AddedItems | Should -HaveCount 0 + $comparer.ChangedItems | Should -HaveCount 0 + $comparer.DeletedItems | Should -HaveCount 0 + } + } +} \ No newline at end of file diff --git a/helpers/software-report-base/tests/SoftwareReport.DifferenceRender.Unit.Tests.ps1 b/helpers/software-report-base/tests/SoftwareReport.DifferenceRender.Unit.Tests.ps1 new file mode 100644 index 0000000..92723f2 --- /dev/null +++ b/helpers/software-report-base/tests/SoftwareReport.DifferenceRender.Unit.Tests.ps1 @@ -0,0 +1,291 @@ +using module ../SoftwareReport.Nodes.psm1 +using module ../SoftwareReport.DifferenceRender.psm1 + +BeforeDiscovery { + Import-Module $(Join-Path $PSScriptRoot "TestHelpers.psm1") -DisableNameChecking +} + +Describe "ComparerReport.UnitTests" { + BeforeAll { + $script:DifferenceRender = [SoftwareReportDifferenceRender]::new() + } + + Context "CalculateHtmlTableRowSpan" { + It "Without the equal cells" { + $table = @( + [PSCustomObject]@{ Key = "A"; Value = "1" } + [PSCustomObject]@{ Key = "B"; Value = "2" } + [PSCustomObject]@{ Key = "C"; Value = "3" } + ) + + $actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, "Key") + $actual | Should -BeArray @(1, 1, 1) + } + + It "Only equal cells" { + $table = @( + [PSCustomObject]@{ Key = "A"; Value = "D" } + [PSCustomObject]@{ Key = "B"; Value = "D" } + [PSCustomObject]@{ Key = "C"; Value = "D" } + ) + + $actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, "Value") + $actual | Should -BeArray @(3, 0, 0) + } + + It "Single row" { + $table = @( + [PSCustomObject]@{ Key = "A"; Value = "1" } + ) + + $actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, "Key") + $actual | Should -BeArray @(1) + } + + It "Different cells" { + $table = @( + [PSCustomObject]@{ Key = "A"; Value = "1" } + [PSCustomObject]@{ Key = "B"; Value = "2" } + [PSCustomObject]@{ Key = "B"; Value = "3" } + [PSCustomObject]@{ Key = "C"; Value = "4" } + [PSCustomObject]@{ Key = "C"; Value = "5" } + [PSCustomObject]@{ Key = "C"; Value = "6" } + [PSCustomObject]@{ Key = "D"; Value = "7" } + [PSCustomObject]@{ Key = "E"; Value = "8" } + [PSCustomObject]@{ Key = "E"; Value = "9" } + [PSCustomObject]@{ Key = "F"; Value = "10" } + ) + + $actual = $DifferenceRender.CalculateHtmlTableRowSpan($table, "Key") + $actual | Should -BeArray @(1, 2, 0, 3, 0, 0, 1, 2, 0, 1) + } + } + + Context "RenderCategory" { + It "With line separator" { + $actual = $DifferenceRender.RenderCategory(@("Header 1", "Header 2", "Header 3"), $true) + $actual | Should -Be "Header 2 >
Header 3" + } + + It "Without line separator" { + $actual = $DifferenceRender.RenderCategory(@("Header 1", "Header 2", "Header 3"), $false) + $actual | Should -Be "Header 2 > Header 3" + } + + It "One header" { + $actual = $DifferenceRender.RenderCategory(@("Header 1"), $false) + $actual | Should -Be "" + } + + It "Empty headers" { + $actual = $DifferenceRender.RenderCategory(@(), $false) + $actual | Should -Be "" + } + } + + Context "RenderToolName" { + It "Clear tool name" { + $actual = $DifferenceRender.RenderToolName("My Tool 1") + $actual | Should -Be "My Tool 1" + } + + It "Name with colon symbol" { + $actual = $DifferenceRender.RenderToolName("My Tool 1:") + $actual | Should -Be "My Tool 1" + } + } + + Context "StrikeTableRow" { + It "Simple row" { + $actual = $DifferenceRender.StrikeTableRow("Test1|Test2|Test3") + $actual | Should -Be "~~Test1~~|~~Test2~~|~~Test3~~" + } + + It "Row with spaces" { + $actual = $DifferenceRender.StrikeTableRow("Test 1|Test 2|Test 3") + $actual | Should -Be "~~Test 1~~|~~Test 2~~|~~Test 3~~" + } + } + + Context "RenderHtmlTable" { + It "Simple table" { + $table = @( + [PSCustomObject]@{ "Category" = "A"; "Tool name" = "My Tool 1"; "Version" = "1.0" }, + [PSCustomObject]@{ "Category" = "B"; "Tool name" = "My Tool 2"; "Version" = "2.0" }, + [PSCustomObject]@{ "Category" = "C"; "Tool name" = "My Tool 3"; "Version" = "3.0" } + ) + + $renderedTable = $DifferenceRender.RenderHtmlTable($table, "Category") + $renderedTable | Should -Be @' + + + + + + + + + + + + + + + + + + + + + + + +
CategoryTool nameVersion
AMy Tool 11.0
BMy Tool 22.0
CMy Tool 33.0
+ +'@ + + } + + It "Table with the same category" { + $table = @( + [PSCustomObject]@{ "Category" = "A"; "Tool name" = "My Tool 1"; "Version" = "1.0" }, + [PSCustomObject]@{ "Category" = "A"; "Tool name" = "My Tool 2"; "Version" = "2.0" }, + [PSCustomObject]@{ "Category" = "A"; "Tool name" = "My Tool 3"; "Version" = "3.0" }, + [PSCustomObject]@{ "Category" = "B"; "Tool name" = "My Tool 4"; "Version" = "4.0" } + ) + + $renderedTable = $DifferenceRender.RenderHtmlTable($table, "Category") + $renderedTable | Should -Be @' + + + + + + + + + + + + + + + + + + + + + + + + + + +
CategoryTool nameVersion
AMy Tool 11.0
My Tool 22.0
My Tool 33.0
BMy Tool 44.0
+ +'@ + + } + } + + Context "RenderTableNodesDiff" { + It "Add new table" { + $previousNode = $null + $currentNode = [TableNode]::new("Name|Value", @("A|1", "B|2")) + $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3")) + + $actual = $DifferenceRender.RenderTableNodesDiff($reportItem) + $actual | Should -Be @' +#### Header 2 > Header 3 +| Name | Value | +| ---- | ----- | +| A | 1 | +| B | 2 | + +'@ + } + + It "Remove existing table" { + $previousNode = [TableNode]::new("Name|Value", @("A|1", "B|2")) + $currentNode = $null + $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3")) + + $actual = $DifferenceRender.RenderTableNodesDiff($reportItem) + $actual | Should -Be @' +#### Header 2 > Header 3 +| Name | Value | +| ----- | ----- | +| ~~A~~ | ~~1~~ | +| ~~B~~ | ~~2~~ | + +'@ + } + + It "Add new rows to existing table" { + $previousNode = [TableNode]::new("Name|Value", @("A|1", "B|2")) + $currentNode = [TableNode]::new("Name|Value", @("A|1", "B|2", "C|3", "D|4")) + $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3")) + + $actual = $DifferenceRender.RenderTableNodesDiff($reportItem) + $actual | Should -Be @' +#### Header 2 > Header 3 +| Name | Value | +| ---- | ----- | +| C | 3 | +| D | 4 | + +'@ + } + + It "Remove rows from existing table" { + $previousNode = [TableNode]::new("Name|Value", @("A|1", "B|2", "C|3", "D|4")) + $currentNode = [TableNode]::new("Name|Value", @("C|3", "D|4")) + $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3")) + + $actual = $DifferenceRender.RenderTableNodesDiff($reportItem) + $actual | Should -Be @' +#### Header 2 > Header 3 +| Name | Value | +| ----- | ----- | +| ~~A~~ | ~~1~~ | +| ~~B~~ | ~~2~~ | + +'@ + } + + It "Row is changed in existing table" { + $previousNode = [TableNode]::new("Name|Value", @("A|1", "B|2")) + $currentNode = [TableNode]::new("Name|Value", @("A|1", "B|3")) + $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3")) + + $actual = $DifferenceRender.RenderTableNodesDiff($reportItem) + $actual | Should -Be @' +#### Header 2 > Header 3 +| Name | Value | +| ----- | ----- | +| ~~B~~ | ~~2~~ | +| B | 3 | + +'@ + } + + It "Row is changed, added and removed at the same time in existing table" { + $previousNode = [TableNode]::new("Name|Value", @("A|1", "B|2", "C|3", "D|4")) + $currentNode = [TableNode]::new("Name|Value", @("B|2", "C|4", "D|4", "E|5")) + $reportItem = [ReportDifferenceItem]::new($previousNode, $currentNode, @("Header 1", "Header 2", "Header 3")) + + $actual = $DifferenceRender.RenderTableNodesDiff($reportItem) + $actual | Should -Be @' +#### Header 2 > Header 3 +| Name | Value | +| ----- | ----- | +| ~~A~~ | ~~1~~ | +| ~~C~~ | ~~3~~ | +| C | 4 | +| E | 5 | + +'@ + } + } +} \ No newline at end of file diff --git a/helpers/software-report-base/tests/SoftwareReport.E2E.Tests.ps1 b/helpers/software-report-base/tests/SoftwareReport.E2E.Tests.ps1 new file mode 100644 index 0000000..ca8b462 --- /dev/null +++ b/helpers/software-report-base/tests/SoftwareReport.E2E.Tests.ps1 @@ -0,0 +1,93 @@ +using module ../SoftwareReport.psm1 +using module ../SoftwareReport.Nodes.psm1 + +Describe "SoftwareReport.E2E" { + Context "Report example 1" { + BeforeEach { + $softwareReport = [SoftwareReport]::new("macOS 11") + $softwareReport.Root.AddToolVersion("OS Version:", "macOS 11.7 (20G817)") + $softwareReport.Root.AddToolVersion("Image Version:", "20220918.1") + $installedSoftware = $softwareReport.Root.AddHeader("Installed Software") + + $languagesAndRuntimes = $installedSoftware.AddHeader("Language and Runtime") + $languagesAndRuntimes.AddToolVersion("Bash", "5.1.16(1)-release") + $languagesAndRuntimes.AddToolVersionsListInline(".NET Core SDK", @("1.2.100", "1.2.200", "3.1.414"), "^\d+\.\d+\.\d") + $languagesAndRuntimes.AddNode([ToolVersionNode]::new("Perl", "5.34.0")) + + $cachedTools = $installedSoftware.AddHeader("Cached Tools") + $cachedTools.AddToolVersionsList("Ruby", @("2.7.3", "2.8.1", "3.1.2"), "^\d+\.\d+") + $cachedTools.AddToolVersionsList("Node.js", @("14.8.0", "15.1.0", "16.4.2"), "^\d+") + + $javaSection = $installedSoftware.AddHeader("Java") + $javaSection.AddTable(@( + [PSCustomObject] @{ Version = "8.0.125"; Vendor = "My Vendor"; "Environment Variable" = "JAVA_HOME_8_X64" }, + [PSCustomObject] @{ Version = "11.3.103"; Vendor = "My Vendor"; "Environment Variable" = "JAVA_HOME_11_X64" } + )) + + $sqlSection = $installedSoftware.AddHeader("MySQL") + $sqlSection.AddToolVersion("MySQL", "6.1.0") + $sqlSection.AddNote("MySQL service is disabled by default.`nUse the following command as a part of your job to start the service: 'sudo systemctl start mysql.service'") + + $expectedMarkdown = @' +# macOS 11 +- OS Version: macOS 11.7 (20G817) +- Image Version: 20220918.1 + +## Installed Software + +### Language and Runtime +- Bash 5.1.16(1)-release +- .NET Core SDK: 1.2.100, 1.2.200, 3.1.414 +- Perl 5.34.0 + +### Cached Tools + +#### Ruby +- 2.7.3 +- 2.8.1 +- 3.1.2 + +#### Node.js +- 14.8.0 +- 15.1.0 +- 16.4.2 + +### Java +| Version | Vendor | Environment Variable | +| -------- | --------- | -------------------- | +| 8.0.125 | My Vendor | JAVA_HOME_8_X64 | +| 11.3.103 | My Vendor | JAVA_HOME_11_X64 | + +### MySQL +- MySQL 6.1.0 +``` +MySQL service is disabled by default. +Use the following command as a part of your job to start the service: 'sudo systemctl start mysql.service' +``` +'@ + } + + It "ToMarkdown" { + $softwareReport.ToMarkdown() | Should -Be $expectedMarkdown + } + + It "Serialization + Deserialization" { + $json = $softwareReport.ToJson() + $deserializedReport = [SoftwareReport]::FromJson($json) + $deserializedReport.ToMarkdown() | Should -Be $expectedMarkdown + } + } + + Context "GetImageVersion" { + It "Image version exists" { + $softwareReport = [SoftwareReport]::new("MyReport") + $softwareReport.Root.AddToolVersion("Image Version:", "123.4") + $softwareReport.GetImageVersion() | Should -Be "123.4" + } + + It "Empty report" { + $softwareReport = [SoftwareReport]::new("MyReport") + $softwareReport.GetImageVersion() | Should -Be "Unknown version" + } + } +} \ No newline at end of file diff --git a/helpers/software-report-base/tests/SoftwareReport.Nodes.Unit.Tests.ps1 b/helpers/software-report-base/tests/SoftwareReport.Nodes.Unit.Tests.ps1 new file mode 100644 index 0000000..d8f2324 --- /dev/null +++ b/helpers/software-report-base/tests/SoftwareReport.Nodes.Unit.Tests.ps1 @@ -0,0 +1,511 @@ +using module ../SoftwareReport.Nodes.psm1 + +BeforeDiscovery { + Import-Module $(Join-Path $PSScriptRoot "TestHelpers.psm1") -DisableNameChecking +} + +Describe "Nodes.UnitTests" { + Context "ToolVersionNode" { + It "ToMarkdown" { + $node = [ToolVersionNode]::new("MyTool", "2.1.3") + $node.ToMarkdown() | Should -Be "- MyTool 2.1.3" + } + + It "GetValue" { + $node = [ToolVersionNode]::new("MyTool", "2.1.3") + $node.GetValue() | Should -Be "2.1.3" + } + + It "Serialization" { + $node = [ToolVersionNode]::new("MyTool", "2.1.3") + $json = $node.ToJsonObject() + $json.NodeType | Should -Be "ToolVersionNode" + $json.ToolName | Should -Be "MyTool" + $json.Version | Should -Be "2.1.3" + } + + It "Deserialization" { + { [ToolVersionNode]::FromJsonObject(@{ NodeType = "ToolVersionNode"; ToolName = ""; Version = "2.1.3" }) } | Should -Throw '*Exception setting "ToolName": "The argument is null or empty.*' + { [ToolVersionNode]::FromJsonObject(@{ NodeType = "ToolVersionNode"; ToolName = "MyTool"; Version = "" }) } | Should -Throw 'ToolVersionNode ''MyTool'' has empty version' + { [ToolVersionNode]::FromJsonObject(@{ NodeType = "ToolVersionNode"; ToolName = "MyTool"; Version = "2.1.3" }) } | Should -Not -Throw + } + + It "Serialization + Deserialization" { + $node = [ToolVersionNode]::new("MyTool", "2.1.3") + $json = $node.ToJsonObject() + $node2 = [ToolVersionNode]::FromJsonObject($json) + $json2 = $node2.ToJsonObject() + $($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json) + } + + It "IsSimilarTo" { + [ToolVersionNode]::new("MyTool", "2.1.3").IsSimilarTo([ToolVersionNode]::new("MyTool", "2.1.3")) | Should -BeTrue + [ToolVersionNode]::new("MyTool", "2.1.3").IsSimilarTo([ToolVersionNode]::new("MyTool", "1.0.0")) | Should -BeTrue + [ToolVersionNode]::new("MyTool", "2.1.3").IsSimilarTo([ToolVersionNode]::new("MyTool2", "2.1.3")) | Should -BeFalse + } + + It "IsIdenticalTo" { + [ToolVersionNode]::new("MyTool", "2.1.3").IsIdenticalTo([ToolVersionNode]::new("MyTool", "2.1.3")) | Should -BeTrue + [ToolVersionNode]::new("MyTool", "2.1.3").IsIdenticalTo([ToolVersionNode]::new("MyTool", "1.0.0")) | Should -BeFalse + [ToolVersionNode]::new("MyTool", "2.1.3").IsIdenticalTo([ToolVersionNode]::new("MyTool2", "2.1.3")) | Should -BeFalse + } + } + + Context "ToolVersionsListNode" { + It "ToMarkdown - List" { + $node = [ToolVersionsListNode]::new("MyTool", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "List") + $expected = @( + "", + "# MyTool" + "- 2.7.7" + "- 3.0.5" + "- 3.1.3" + ) -join "`n" + $node.ToMarkdown() | Should -Be $expected + } + + It "ToMarkdown - Inline" { + $node = [ToolVersionsListNode]::new("MyTool", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "Inline") + $node.ToMarkdown() | Should -Be "- MyTool: 2.7.7, 3.0.5, 3.1.3" + } + + It "GetValue" { + $node = [ToolVersionsListNode]::new("MyTool", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "List") + $node.GetValue() | Should -Be "2.7.7, 3.0.5, 3.1.3" + } + + It "Serialization - List" { + $node = [ToolVersionsListNode]::new("Ruby", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "List") + $json = $node.ToJsonObject() + $json.NodeType | Should -Be "ToolVersionsListNode" + $json.ToolName | Should -Be "Ruby" + $json.Versions | Should -BeArray @("2.7.7", "3.0.5", "3.1.3") + $json.MajorVersionRegex | Should -Be "^.+" + $json.ListType | Should -Be "List" + } + + It "Serialization - Inline" { + $node = [ToolVersionsListNode]::new("Ruby", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "Inline") + $json = $node.ToJsonObject() + $json.NodeType | Should -Be "ToolVersionsListNode" + $json.ToolName | Should -Be "Ruby" + $json.Versions | Should -BeArray @("2.7.7", "3.0.5", "3.1.3") + $json.MajorVersionRegex | Should -Be "^.+" + $json.ListType | Should -Be "Inline" + } + + It "Deserialization" { + { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = ""; Versions = @("2.1.3", "3.1.4"); MajorVersionRegex = "^\d+"; ListType = "List" }) } | Should -Throw '*Exception setting "ToolName": "The argument is null or empty.*' + { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; MajorVersionRegex = "^\d+"; ListType = "List" }) } | Should -Throw '*Exception setting "Versions": "The argument is null or empty.*' + { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @(); MajorVersionRegex = "^\d+"; ListType = "List" }) } | Should -Throw '*Exception setting "Versions": "The argument is null, empty,*' + { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @("2.1.3", '2.2.4'); MajorVersionRegex = "^\d+"; ListType = "List" }) } | Should -Throw 'Multiple versions from list * return the same result from regex *' + { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @("2.1.3", "3.1.4"); MajorVersionRegex = ""; ListType = "List" }) } | Should -Throw 'Version * doesn''t match regex *' + { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @("2.1.3", "3.1.4"); MajorVersionRegex = "^\d+"; ListType = "Fake" }) } | Should -Throw '*Exception setting "ListType": "The argument * does not belong to the set*' + { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @("2.1.3", "3.1.4"); MajorVersionRegex = "^\d+"; ListType = "List" }) } | Should -Not -Throw + { [ToolVersionsListNode]::FromJsonObject(@{ NodeType = "ToolVersionsListNode"; ToolName = "MyTool"; Versions = @("2.1.3", "3.1.4"); MajorVersionRegex = "^\d+"; ListType = "Inline" }) } | Should -Not -Throw + } + + It "Serialization + Deserialization" { + $node = [ToolVersionsListNode]::new("Ruby", @("2.7.7", "3.0.5", "3.1.3"), "^.+", "List") + $json = $node.ToJsonObject() + $node2 = [ToolVersionsListNode]::FromJsonObject($json) + $json2 = $node2.ToJsonObject() + $($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json) + } + + It "IsSimilarTo" { + [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsSimilarTo( + [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List") + ) | Should -BeTrue + [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsSimilarTo( + [ToolVersionsListNode]::new("MyTool", @("2.1.5", "5.0.0"), "^.+", "List") + ) | Should -BeTrue + [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsSimilarTo( + [ToolVersionsListNode]::new("MyTool2", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List") + ) | Should -BeFalse + } + + It "IsIdenticalTo" { + [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsIdenticalTo( + [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List") + ) | Should -BeTrue + [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsIdenticalTo( + [ToolVersionsListNode]::new("MyTool", @("2.1.5", "5.0.0"), "^.+", "List") + ) | Should -BeFalse + [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List").IsIdenticalTo( + [ToolVersionsListNode]::new("MyTool2", @("2.1.3", "3.1.5", "4.0.0"), "^.+", "List") + ) | Should -BeFalse + } + + It "ExtractMajorVersion" { + $node = [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^\d+\.\d+", "List") + $node.ExtractMajorVersion("2.1.3") | Should -Be "2.1" + $node.ExtractMajorVersion("3.1.5") | Should -Be "3.1" + $node.ExtractMajorVersion("4.0.0") | Should -Be "4.0" + } + + Context "ValidateMajorVersionRegex" { + It "Major version regex - unique versions" { + $node = [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "4.0.0"), "^\d+", "List") + $node.Versions | Should -BeArray @("2.1.3", "3.1.5", "4.0.0") + } + + It "Major version regex - non-unique versions" { + { [ToolVersionsListNode]::new("MyTool", @("2.1.3", "3.1.5", "3.2.0", "4.0.0"), "^\d+", "List") } | Should -Throw "Multiple versions from list * return the same result from regex *" + } + + It "Minor version regex - unique versions" { + $node = [ToolVersionsListNode]::new("MyTool", @("2.1.3", "2.4.0", "3.1.2"), "^\d+\.\d+", "List") + $node.Versions | Should -BeArray @("2.1.3", "2.4.0", "3.1.2") + } + + It "Minor version regex - non-unique versions" { + { [ToolVersionsListNode]::new("MyTool", @("2.1.3", "2.1.4", "3.1.2"), "^\d+\.\d+", "List") } | Should -Throw "Multiple versions from list * return the same result from regex *" + } + + It "Patch version regex - unique versions" { + $node = [ToolVersionsListNode]::new("MyTool", @("2.1.3", "2.1.4", "2.1.5"), "^\d+\.\d+\.\d+", "List") + $node.Versions | Should -BeArray @("2.1.3", "2.1.4", "2.1.5") + } + + It "Patch version regex - non-unique versions" { + { [ToolVersionsListNode]::new("MyTool", @("2.1.3", "2.1.4", "2.1.4"), "^\d+\.\d+\.\d+", "List") } | Should -Throw "Multiple versions from list * return the same result from regex *" + } + + It ".NET Core version regex - unique versions" { + $node = [ToolVersionsListNode]::new("MyTool", @("2.1.100", "2.1.205", "2.1.303"), "^\d+\.\d+\.\d", "List") + $node.Versions | Should -BeArray @("2.1.100", "2.1.205", "2.1.303") + } + + It ".NET Core version regex - non-unique versions" { + { [ToolVersionsListNode]::new("MyTool", @("2.1.100", "2.1.205", "2.1.230", "3.1.0"), "^\d+\.\d+\.\d", "List") } | Should -Throw "Multiple versions from list * return the same result from regex *" + } + } + } + + Context "TableNode" { + It "ToMarkdown (Simple table)" { + $node = [TableNode]::new("Name|Value", @("A|B", "C|D")) + $node.ToMarkdown() | Should -Be @' +| Name | Value | +| ---- | ----- | +| A | B | +| C | D | +'@ + } + + It "ToMarkdown (Wide cells)" { + $node = [TableNode]::new("Name|Value", @("Very long value here|B", "C|And very long value here too")) + $node.ToMarkdown() | Should -Be @' +| Name | Value | +| -------------------- | ---------------------------- | +| Very long value here | B | +| C | And very long value here too | +'@ + } + + It "CalculateColumnsWidth" { + [TableNode]::new("Name|Value", @("A|B", "C|D")).CalculateColumnsWidth() | Should -BeArray @(4, 5) + [TableNode]::new("Name|Value", @("Very long value here|B", "C|And very long value here too")).CalculateColumnsWidth() | Should -BeArray @(20, 28) + } + + It "Serialization" { + $node = [TableNode]::new("Name|Value", @("A|B", "C|D")) + $json = $node.ToJsonObject() + $json.NodeType | Should -Be "TableNode" + $json.Headers | Should -Be "Name|Value" + $json.Rows | Should -BeArray @("A|B", "C|D") + } + + It "Deserialization" { + { [TableNode]::FromJsonObject(@{ NodeType = "TableNode"; Headers = ""; Rows = @("A|1", "B|2") }) } | Should -Throw 'Exception setting "Headers": "The argument is null or empty. *' + { [TableNode]::FromJsonObject(@{ NodeType = "TableNode"; Headers = "Name|Value"; Rows = @() }) } | Should -Throw 'Exception setting "Rows": "The argument is null, empty, *' + { [TableNode]::FromJsonObject(@{ NodeType = "TableNode"; Headers = "Name|Value"; Rows = @("A|1", "B|2|T", "C|3") }) } | Should -Throw 'Table has different number of columns in different rows' + { [TableNode]::FromJsonObject(@{ NodeType = "TableNode"; Headers = "Name|Value"; Rows = @("A|1", "B|2") }) } | Should -Not -Throw + } + + It "Serialization + Deserialization" { + $node = [TableNode]::new("Name|Value", @("A|B", "C|D")) + $json = $node.ToJsonObject() + $node2 = [TableNode]::FromJsonObject($json) + $json2 = $node2.ToJsonObject() + $($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json) + } + + It "IsSimilarTo" { + [TableNode]::new("Name|Value", @("A|B", "C|D")).IsSimilarTo([TableNode]::new("Name|Value", @("A|B", "C|D"))) | Should -BeTrue + [TableNode]::new("Name|Value", @("A|B", "C|D")).IsSimilarTo([TableNode]::new("Name|Value", @("A|B", "C|D", "F|W"))) | Should -BeTrue + [TableNode]::new("Name|Value", @("A|B", "C|D")).IsSimilarTo([TableNode]::new("Name|Value", @("A|B", "C|E"))) | Should -BeTrue + [TableNode]::new("Name|Value", @("A|B", "C|D")).IsSimilarTo([TableNode]::new("Name|Key", @("A|B", "C|D"))) | Should -BeTrue + } + + It "IsIdenticalTo" { + [TableNode]::new("Name|Value", @("A|B", "C|D")).IsIdenticalTo([TableNode]::new("Name|Value", @("A|B", "C|D"))) | Should -BeTrue + [TableNode]::new("Name|Value", @("A|B", "C|D")).IsIdenticalTo([TableNode]::new("Name|Key", @("A|B", "C|D"))) | Should -BeTrue + [TableNode]::new("Name|Value", @("A|B", "C|D")).IsIdenticalTo([TableNode]::new("Name|Value", @("A|B", "C|D", "F|W"))) | Should -BeFalse + [TableNode]::new("Name|Value", @("A|B", "C|D")).IsIdenticalTo([TableNode]::new("Name|Value", @("A|B", "C|E"))) | Should -BeFalse + } + + Context "FromObjectsArray" { + It "Correct table" { + $table = @( + [PSCustomObject]@{Name = "A"; Value = "B"} + [PSCustomObject]@{Name = "C"; Value = "D"} + ) + + $tableNode = [TableNode]::FromObjectsArray($table) + $tableNode.Headers | Should -Be "Name|Value" + $tableNode.Rows | Should -BeArray @("A|B", "C|D") + } + + It "Correct table with spaces" { + $table = @( + [PSCustomObject]@{Name = "A B"; "My Value" = "1 2"} + [PSCustomObject]@{Name = "C D"; "My Value" = "3 4"} + ) + + $tableNode = [TableNode]::FromObjectsArray($table) + $tableNode.Headers | Should -Be "Name|My Value" + $tableNode.Rows | Should -BeArray @("A B|1 2", "C D|3 4") + } + + It "Throw on empty table" { + { [TableNode]::FromObjectsArray(@()) } | Should -Throw "Failed to create TableNode from empty objects array" + } + + It "Throw on table with different columns" { + $table = @( + [PSCustomObject]@{Name = "A"; Value = "B"} + [PSCustomObject]@{Name = "C"; Value2 = "D"} + ) + + { [TableNode]::FromObjectsArray($table) } | Should -Throw "Failed to create TableNode from objects array because objects have different properties" + } + + It "Throw on empty row" { + $table = @( + [PSCustomObject]@{Name = "A"; Value = "B"}, + [PSCustomObject]@{}, + [PSCustomObject]@{Name = "C"; Value2 = "D"} + ) + + { [TableNode]::FromObjectsArray($table) } | Should -Throw "Failed to create TableNode because some objects are empty" + } + + It "Throw on incorrect symbols in table column names" { + $table = @( + [PSCustomObject]@{"Name|War" = "A"; Value = "B"} + [PSCustomObject]@{"Name|War" = "C"; Value = "D"} + ) + + { [TableNode]::FromObjectsArray($table) } | Should -Throw "Failed to create TableNode because some cells * contains forbidden symbol*" + } + + It "Throw on incorrect symbols in table rows" { + $table = @( + [PSCustomObject]@{Name = "A"; Value = "B|AA"} + [PSCustomObject]@{Name = "C"; Value = "D"} + ) + + { [TableNode]::FromObjectsArray($table) } | Should -Throw "Failed to create TableNode because some cells * contains forbidden symbol*" + } + } + } + + Context "NoteNode" { + It "ToMarkdown" { + $node = [NoteNode]::new("Hello world`nGood Bye world") + $node.ToMarkdown() | Should -Be @' +``` +hello world +Good Bye world +``` +'@ + } + + It "Serialization" { + $node = [NoteNode]::new("MyContent`nMyContent2") + $json = $node.ToJsonObject() + $json.NodeType | Should -Be "NoteNode" + $json.Content | Should -Be "MyContent`nMyContent2" + } + + It "Deserialization" { + { [NoteNode]::FromJsonObject(@{ NodeType = "NoteNode" }) } | Should -Throw '*Exception setting "Content": "The argument is null or empty.*' + { [NoteNode]::FromJsonObject(@{ NodeType = "NoteNode"; Content = "" }) } | Should -Throw '*Exception setting "Content": "The argument is null or empty.*' + { [NoteNode]::FromJsonObject(@{ NodeType = "NoteNode"; Content = "MyTool" }) } | Should -Not -Throw + } + + It "Serialization + Deserialization" { + $node = [NoteNode]::new("MyContent`nMyContent2") + $json = $node.ToJsonObject() + $node2 = [NoteNode]::FromJsonObject($json) + $json2 = $node2.ToJsonObject() + $($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json) + } + + It "IsSimilarTo" { + [NoteNode]::new("MyContent").IsSimilarTo([NoteNode]::new("MyContent")) | Should -BeTrue + [NoteNode]::new("MyContent").IsSimilarTo([NoteNode]::new("MyContent2")) | Should -BeFalse + } + + It "IsIdenticalTo" { + [NoteNode]::new("MyContent").IsIdenticalTo([NoteNode]::new("MyContent")) | Should -BeTrue + [NoteNode]::new("MyContent").IsIdenticalTo([NoteNode]::new("MyContent2")) | Should -BeFalse + } + } + + Context "HeaderNode" { + It "ToMarkdown" { + $node = [HeaderNode]::new("MyHeader") + $node.AddToolVersion("MyTool", "2.1.3") + $node.ToMarkdown(1) | Should -Be @' + +# MyHeader +- MyTool 2.1.3 +'@ + } + + It "ToMarkdown (level 3)" { + $node = [HeaderNode]::new("MyHeader") + $node.AddToolVersion("MyTool", "2.1.3") + $node.ToMarkdown(3) | Should -Be @' + +### MyHeader +- MyTool 2.1.3 +'@ + } + + It "ToMarkdown (multiple levels)" { + $node = [HeaderNode]::new("MyHeader") + $node.AddHeader("MyHeader 2").AddHeader("MyHeader 3").AddHeader("MyHeader 4").AddToolVersion("MyTool", "2.1.3") + $node.ToMarkdown(1) | Should -Be @' + +# MyHeader + +## MyHeader 2 + +### MyHeader 3 + +#### MyHeader 4 +- MyTool 2.1.3 +'@ + } + + It "Serialization" { + $node = [HeaderNode]::new("MyHeader") + $node.AddToolVersion("MyTool", "2.1.3") + $json = $node.ToJsonObject() + $json.NodeType | Should -Be "HeaderNode" + $json.Title | Should -Be "MyHeader" + $json.Children | Should -HaveCount 1 + } + + It "Deserialization" { + { [HeaderNode]::FromJsonObject(@{ NodeType = "HeaderNode" }) } | Should -Throw '*Exception setting "Title": "The argument is null or empty.*' + { [HeaderNode]::FromJsonObject(@{ NodeType = "HeaderNode"; Title = "" }) } | Should -Throw '*Exception setting "Title": "The argument is null or empty.*' + { [HeaderNode]::FromJsonObject(@{ NodeType = "HeaderNode"; Title = "MyHeader" }) } | Should -Not -Throw + } + + It "Serialization + Deserialization" { + $node = [HeaderNode]::new("MyHeader") + $node.AddToolVersion("MyTool", "2.1.3") + $json = $node.ToJsonObject() + $node2 = [HeaderNode]::FromJsonObject($json) + $json2 = $node2.ToJsonObject() + $($json | ConvertTo-Json) | Should -Be $($json2 | ConvertTo-Json) + } + + It "IsSimilarTo" { + [HeaderNode]::new("MyHeader").IsSimilarTo([HeaderNode]::new("MyHeader")) | Should -BeTrue + [HeaderNode]::new("MyHeader").IsSimilarTo([HeaderNode]::new("MyHeader2")) | Should -BeFalse + } + + It "IsIdenticalTo" { + [HeaderNode]::new("MyHeader").IsIdenticalTo([HeaderNode]::new("MyHeader")) | Should -BeTrue + [HeaderNode]::new("MyHeader").IsIdenticalTo([HeaderNode]::new("MyHeader2")) | Should -BeFalse + } + + It "FindSimilarChildNode" { + $node = [HeaderNode]::new("MyHeader") + $node.AddToolVersion("MyTool", "2.1.3") + + $node.FindSimilarChildNode([ToolVersionNode]::new("MyTool", "1.0.0")) | Should -Not -BeNullOrEmpty + $node.FindSimilarChildNode([ToolVersionNode]::New("MyTool2", "1.0.0")) | Should -BeNullOrEmpty + } + + Context "Detect node duplicates" { + It "Similar HeaderNode on the same header" { + $node = [HeaderNode]::new("MyHeader") + $node.AddHeader("MySubHeader1") + $node.AddHeader("MySubHeader2") + { $node.AddHeader("MySubHeader1") } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*" + } + + It "Similar ToolVersionNode on the same header" { + $node = [HeaderNode]::new("MyHeader") + $node.AddToolVersion("MyTool", "2.1.3") + $node.AddToolVersion("MyTool2", "2.1.3") + { $node.AddToolVersion("MyTool", "2.1.3") } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*" + } + + It "Similar ToolVersionsListNode on the same header" { + $node = [HeaderNode]::new("MyHeader") + $node.AddToolVersionsListInline("MyTool", @("2.1.3", "3.0.0"), "^\d+") + $node.AddToolVersionsListInline("MyTool2", @("2.1.3", "3.0.0"), "^\d+") + { $node.AddToolVersionsList("MyTool", @("2.1.3", "3.0.0"), "^\d+") } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*" + } + + It "Similar TableNode on the same header" { + $node = [HeaderNode]::new("MyHeader") + $node.AddTable(@( + [PSCustomObject]@{Name = "Value1"}, + [PSCustomObject]@{Name = "Value2"} + )) + { + $node.AddTable(@( + [PSCustomObject]@{Name = "Value1"}, + [PSCustomObject]@{Name = "Value2"} + )) + } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*" + } + + It "Similar NoteNode on the same header" { + $node = [HeaderNode]::new("MyHeader") + $node.AddNote("MyContent") + $node.AddNote("MyContent2") + { $node.AddNote("MyContent") } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*" + } + + It "AddNode detects duplicates" { + $node = [HeaderNode]::new("MyHeader") + $node.AddNode([ToolVersionNode]::new("MyTool", "2.1.3")) + { $node.AddNode([ToolVersionNode]::new("MyTool", "2.1.3")) } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*" + } + + It "AddNodes detects duplicates" { + $node = [HeaderNode]::new("MyHeader") + $node.AddNodes(@( + [ToolVersionNode]::new("MyTool", "2.1.3"), + [ToolVersionNode]::new("MyTool2", "2.1.4") + )) + { + $node.AddNodes(@( + [ToolVersionNode]::new("MyTool3", "2.1.5"), + [ToolVersionNode]::new("MyTool", "2.1.3") + )) + } | Should -Throw "This HeaderNode already contains the similar child node. It is not allowed to add the same node twice.*" + } + + It "Doesn't allow adding non-header nodes after header node" { + $node = [HeaderNode]::new("MyHeader") + { $node.AddToolVersion("MyTool", "2.1.3") } | Should -Not -Throw + { $node.AddHeader("MySubHeader") } | Should -Not -Throw + { $node.AddToolVersion("MyTool2", "2.1.4") } | Should -Throw "It is not allowed to add the non-header node after the header node. Consider adding the separate HeaderNode for this node" + { $node.AddHeader("MySubHeader2") } | Should -Not -Throw + { $node.AddToolVersionsListInline("MyTool3", @("2.1.4", "2.1.5"), "^.+") } | Should -Throw "It is not allowed to add the non-header node after the header node. Consider adding the separate HeaderNode for this node" + { $node.AddToolVersionsList("MyTool4", @("2.1.4", "2.1.5"), "^.+") } | Should -Not -Throw + } + } + } +} \ No newline at end of file diff --git a/helpers/software-report-base/tests/TestHelpers.psm1 b/helpers/software-report-base/tests/TestHelpers.psm1 new file mode 100644 index 0000000..8918a29 --- /dev/null +++ b/helpers/software-report-base/tests/TestHelpers.psm1 @@ -0,0 +1,34 @@ +function ShouldBeArray([Array] $ActualValue, [Array]$ExpectedValue, [Switch] $Negate, [String] $Because) { + if ($Negate) { + throw "Negation is not supported for Should-BeArray" + } + + if ($ExpectedValue.Count -eq 0) { + throw "Expected array cannot be empty. Use Should-BeNullOrEmpty instead." + } + + $ExpectedValue | ForEach-Object { + if ($_.GetType() -notin @([String], [Int32])) { + throw "Only string or int arrays are supported in Should-BeArray" + } + } + + $actualValueJson = $ActualValue | ConvertTo-Json + $expectedValueJson = $ExpectedValue | ConvertTo-Json + + $succeeded = ($ActualValue.Count -eq $ExpectedValue.Count) -and ($actualValueJson -eq $expectedValueJson) + + if (-not $succeeded) { + $failureMessage = "Expected array '$actualValueJson' to be equal to '$expectedValueJson'" + } + + return [PSCustomObject]@{ + Succeeded = $succeeded + FailureMessage = $failureMessage + } +} + +Add-ShouldOperator -Name BeArray ` + -InternalName 'ShouldBeArray' ` + -Test ${function:ShouldBeArray} ` + -SupportsArrayInput \ No newline at end of file diff --git a/images.CI/credscan-exclusions.json b/images.CI/credscan-exclusions.json new file mode 100644 index 0000000..979dfa6 --- /dev/null +++ b/images.CI/credscan-exclusions.json @@ -0,0 +1,9 @@ +{ + "tool": "Credential Scanner", + "suppressions": [ + { + "placeholder": "P@ssword!!", + "_justification": "Password used by SQL Express. It is required to interact with database." + } + ] +} \ No newline at end of file diff --git a/images.CI/download-repo.ps1 b/images.CI/download-repo.ps1 new file mode 100644 index 0000000..1437470 --- /dev/null +++ b/images.CI/download-repo.ps1 @@ -0,0 +1,14 @@ +param( + [String] [Parameter (Mandatory=$true)] $RepoUrl, + [String] [Parameter (Mandatory=$true)] $RepoBranch +) + +Write-Host "Clean up default repository" +Remove-Item -path './*' -Recurse -Force + +Write-Host "Download ${RepoBranch} branch from ${RepoUrl}" +$env:GIT_REDIRECT_STDERR = '2>&1' +git clone $RepoUrl . -b $RepoBranch --single-branch --depth 1 + +Write-Host "Latest commit:" +git --no-pager log --pretty=format:"Date: %cd; Commit: %H - %s; Author: %an <%ae>" -1 \ No newline at end of file diff --git a/images.CI/linux-and-win/azure-pipelines/image-generation.yml b/images.CI/linux-and-win/azure-pipelines/image-generation.yml new file mode 100644 index 0000000..62af3d3 --- /dev/null +++ b/images.CI/linux-and-win/azure-pipelines/image-generation.yml @@ -0,0 +1,181 @@ +# Ideally we would use GitHub Actions for this, but since we use self-hosted machines to run image builds +# we need the following features to use GitHub Actions for Images CI: +# - https://github.community/t5/GitHub-Actions/Make-secrets-available-to-builds-of-forks/m-p/30678#M508 +# - https://github.community/t5/GitHub-Actions/GitHub-Actions-Manual-Trigger-Approvals/td-p/31504 +# - https://github.community/t5/GitHub-Actions/Protecting-github-workflows/td-p/30290 + +parameters: + - name: job_id + type: string + default: 'generate_image' + + - name: image_type + type: string + + - name: image_template_name + type: string + + - name: image_readme_name + type: string + + - name: agent_pool + type: object + default: + name: 'ci-agent-pool' + + - name: variable_group_name + type: string + default: 'Image Generation Variables' + + - name: create_release + type: boolean + default: true + + - name: repository_ref + type: string + default: 'self' + +jobs: +- job: ${{ parameters.job_id }} + displayName: Image Generation (${{ parameters.image_type }}) + timeoutInMinutes: 600 + cancelTimeoutInMinutes: 30 + pool: ${{ parameters.agent_pool }} + variables: + - group: ${{ parameters.variable_group_name }} + + steps: + - checkout: ${{ parameters.repository_ref }} + clean: true + fetchDepth: 0 + fetchTags: false + + - task: PowerShell@2 + displayName: 'Download custom repository' + condition: and(ne(variables['CUSTOM_REPOSITORY_URL'], ''), ne(variables['CUSTOM_REPOSITORY_BRANCH'], '')) + inputs: + targetType: 'filePath' + filePath: ./images.CI/download-repo.ps1 + arguments: -RepoUrl $(CUSTOM_REPOSITORY_URL) ` + -RepoBranch $(CUSTOM_REPOSITORY_BRANCH) + + - task: AzureCLI@2 + displayName: 'Set variables' + inputs: + azureSubscription: 'spn-hosted-runners' + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + $ImageType = "${{ parameters.image_type }}" + $TemplateDirectoryName = if ($ImageType.StartsWith("ubuntu")) { "ubuntu/templates" } else { "windows/templates" } + $TemplateDirectoryPath = Join-Path "images" $TemplateDirectoryName | Resolve-Path + + $TemplateFileName = "${{ parameters.image_template_name }}" + $TemplatePath = Join-Path $TemplateDirectoryPath $TemplateFileName + Write-Host "##vso[task.setvariable variable=TemplateDirectoryPath;]$TemplateDirectoryPath" + Write-Host "##vso[task.setvariable variable=TemplatePath;]$TemplatePath" + + $ManagedImageName = "${{ parameters.image_type }}-$(Build.BuildId)" + Write-Host "##vso[task.setvariable variable=ManagedImageName;]$ManagedImageName" + + $TempResourceGroupName = "packer-temp-$ManagedImageName" + Write-Host "##vso[task.setvariable variable=TempResourceGroupName;]$TempResourceGroupName" + + $clientSecret = $(az keyvault secret show --name "spnhostedrunners" --vault-name "gh-imagegeneration" --query value -o tsv) + Write-Host "##vso[task.setvariable variable=ClientSecret;issecret=true]$clientSecret" + + - task: PowerShell@2 + displayName: 'Build VM' + inputs: + targetType: filePath + filePath: ./images.CI/linux-and-win/build-image.ps1 + arguments: -ClientId $(CLIENT_ID) ` + -ClientSecret "$(ClientSecret)" ` + -TemplatePath $(TemplatePath) ` + -ImageName "$(ManagedImageName)" ` + -ImageResourceGroupName $(AZURE_RESOURCE_GROUP) ` + -TempResourceGroupName "$(TempResourceGroupName)" ` + -SubscriptionId $(AZURE_SUBSCRIPTION) ` + -TenantId $(AZURE_TENANT) ` + -Location $(AZURE_LOCATION) ` + -VirtualNetworkName $(BUILD_AGENT_VNET_NAME) ` + -VirtualNetworkRG $(BUILD_AGENT_VNET_RESOURCE_GROUP) ` + -VirtualNetworkSubnet $(BUILD_AGENT_SUBNET_NAME) + + env: + PACKER_LOG: 1 + PACKER_LOG_PATH: "$(Agent.TempDirectory)/packer-log.txt" + + - task: PowerShell@2 + displayName: 'Copy image artifacts to the separate directory' + inputs: + targetType: 'inline' + script: | + $ImageType = "${{ parameters.image_type }}" + $rootDirectoryName = if ($ImageType.StartsWith("ubuntu")) { "ubuntu" } else { "windows" } + $rootDirectoryPath = Join-Path "images" $rootDirectoryName | Resolve-Path + + $readmePath = Join-Path $rootDirectoryPath "${{ parameters.image_readme_name }}" + $softwareReportPath = Join-Path $rootDirectoryPath "software-report.json" + + Copy-Item -Path $readmePath -Destination "$(Build.ArtifactStagingDirectory)/" + if (Test-Path $softwareReportPath) { + Copy-Item -Path $softwareReportPath -Destination "$(Build.ArtifactStagingDirectory)/" + } + + - task: PowerShell@2 + displayName: 'Print markdown software report' + inputs: + targetType: 'inline' + script: | + Get-Content -Path "$(Build.ArtifactStagingDirectory)/${{ parameters.image_readme_name }}" + + - task: PowerShell@2 + displayName: 'Print json software report' + inputs: + targetType: 'inline' + script: | + $softwareReportPath = "$(Build.ArtifactStagingDirectory)/software-report.json" + if (Test-Path $softwareReportPath) { + Get-Content -Path $softwareReportPath + } + + - task: PublishBuildArtifacts@1 + inputs: + ArtifactName: 'Built_VM_Artifacts' + displayName: Publish Artifacts + + - task: PowerShell@2 + displayName: 'Print provisioners duration' + inputs: + targetType: 'filePath' + filePath: ./images.CI/measure-provisioners-duration.ps1 + arguments: -PackerLogPath "$(Agent.TempDirectory)/packer-log.txt" ` + -PrefixToPathTrim "$(TemplateDirectoryPath)" ` + -PrintTopNLongest 25 + + - ${{ if eq(parameters.create_release, true) }}: + - task: PowerShell@2 + displayName: 'Create release for VM deployment' + inputs: + targetType: filePath + filePath: ./images.CI/linux-and-win/create-release.ps1 + arguments: -BuildId $(Build.BuildId) ` + -Organization $(RELEASE_TARGET_ORGANIZATION) ` + -DefinitionId $(RELEASE_TARGET_DEFINITION_ID) ` + -Project $(RELEASE_TARGET_PROJECT) ` + -ImageType "${{ parameters.image_type }}" ` + -ManagedImageName "$(ManagedImageName)" ` + -AccessToken $(RELEASE_TARGET_TOKEN) + + - task: PowerShell@2 + displayName: 'Clean up resources' + condition: always() + inputs: + targetType: filePath + filePath: ./images.CI/linux-and-win/cleanup.ps1 + arguments: -TempResourceGroupName "$(TempResourceGroupName)" ` + -SubscriptionId $(AZURE_SUBSCRIPTION) ` + -ClientId $(CLIENT_ID) ` + -ClientSecret "$(ClientSecret)" ` + -TenantId $(AZURE_TENANT) diff --git a/images.CI/linux-and-win/azure-pipelines/ubuntu2004.yml b/images.CI/linux-and-win/azure-pipelines/ubuntu2004.yml new file mode 100644 index 0000000..948e764 --- /dev/null +++ b/images.CI/linux-and-win/azure-pipelines/ubuntu2004.yml @@ -0,0 +1,21 @@ +schedules: +- cron: "0 0 * * *" + displayName: Daily + branches: + include: + - main + always: true + +trigger: none +pr: + autoCancel: true + branches: + include: + - main + +jobs: +- template: image-generation.yml + parameters: + image_type: ubuntu2004 + image_readme_name: Ubuntu2004-Readme.md + image_template_name: ubuntu-20.04.pkr.hcl diff --git a/images.CI/linux-and-win/azure-pipelines/ubuntu2204.yml b/images.CI/linux-and-win/azure-pipelines/ubuntu2204.yml new file mode 100644 index 0000000..b9e1281 --- /dev/null +++ b/images.CI/linux-and-win/azure-pipelines/ubuntu2204.yml @@ -0,0 +1,21 @@ +schedules: +- cron: "0 0 * * *" + displayName: Daily + branches: + include: + - main + always: true + +trigger: none +pr: + autoCancel: true + branches: + include: + - main + +jobs: +- template: image-generation.yml + parameters: + image_type: ubuntu2204 + image_readme_name: Ubuntu2204-Readme.md + image_template_name: ubuntu-22.04.pkr.hcl diff --git a/images.CI/linux-and-win/azure-pipelines/ubuntu2404.yml b/images.CI/linux-and-win/azure-pipelines/ubuntu2404.yml new file mode 100644 index 0000000..5856d31 --- /dev/null +++ b/images.CI/linux-and-win/azure-pipelines/ubuntu2404.yml @@ -0,0 +1,21 @@ +schedules: +- cron: "0 0 * * *" + displayName: Daily + branches: + include: + - main + always: true + +trigger: none +pr: + autoCancel: true + branches: + include: + - main + +jobs: +- template: image-generation.yml + parameters: + image_type: ubuntu2404 + image_readme_name: Ubuntu2404-Readme.md + image_template_name: ubuntu-24.04.pkr.hcl diff --git a/images.CI/linux-and-win/azure-pipelines/windows2019.yml b/images.CI/linux-and-win/azure-pipelines/windows2019.yml new file mode 100644 index 0000000..c8bb7f9 --- /dev/null +++ b/images.CI/linux-and-win/azure-pipelines/windows2019.yml @@ -0,0 +1,21 @@ +schedules: +- cron: "0 0 * * *" + displayName: Daily + branches: + include: + - main + always: true + +trigger: none +pr: + autoCancel: true + branches: + include: + - main + +jobs: +- template: image-generation.yml + parameters: + image_type: windows2019 + image_readme_name: Windows2019-Readme.md + image_template_name: windows-2019.pkr.hcl diff --git a/images.CI/linux-and-win/azure-pipelines/windows2022.yml b/images.CI/linux-and-win/azure-pipelines/windows2022.yml new file mode 100644 index 0000000..b24e1d4 --- /dev/null +++ b/images.CI/linux-and-win/azure-pipelines/windows2022.yml @@ -0,0 +1,21 @@ +schedules: +- cron: "0 0 * * *" + displayName: Daily + branches: + include: + - main + always: true + +trigger: none +pr: + autoCancel: true + branches: + include: + - main + +jobs: +- template: image-generation.yml + parameters: + image_type: windows2022 + image_readme_name: Windows2022-Readme.md + image_template_name: windows-2022.pkr.hcl diff --git a/images.CI/linux-and-win/build-image.ps1 b/images.CI/linux-and-win/build-image.ps1 new file mode 100644 index 0000000..f1c4ce6 --- /dev/null +++ b/images.CI/linux-and-win/build-image.ps1 @@ -0,0 +1,66 @@ +param( + [String] [Parameter (Mandatory=$true)] $TemplatePath, + [String] [Parameter (Mandatory=$true)] $ClientId, + [String] [Parameter (Mandatory=$true)] $ClientSecret, + [String] [Parameter (Mandatory=$true)] $Location, + [String] [Parameter (Mandatory=$true)] $ImageName, + [String] [Parameter (Mandatory=$true)] $ImageResourceGroupName, + [String] [Parameter (Mandatory=$true)] $TempResourceGroupName, + [String] [Parameter (Mandatory=$true)] $SubscriptionId, + [String] [Parameter (Mandatory=$true)] $TenantId, + [String] [Parameter (Mandatory=$false)] $VirtualNetworkName, + [String] [Parameter (Mandatory=$false)] $VirtualNetworkRG, + [String] [Parameter (Mandatory=$false)] $VirtualNetworkSubnet, + [String] [Parameter (Mandatory=$false)] $AllowedInboundIpAddresses = "[]" +) + +if (-not (Test-Path $TemplatePath)) +{ + Write-Error "'-TemplatePath' parameter is not valid. You have to specify correct Template Path" + exit 1 +} + +$ImageTemplateName = [io.path]::GetFileName($TemplatePath).Split(".")[0] +$InstallPassword = [System.GUID]::NewGuid().ToString().ToUpper() + +$SensitiveData = @( + 'OSType', + 'StorageAccountLocation', + 'OSDiskUri', + 'OSDiskUriReadOnlySas', + 'TemplateUri', + 'TemplateUriReadOnlySas', + ': ->' +) + +Write-Host "Show Packer Version" +packer --version + +Write-Host "Download packer plugins" +packer init $TemplatePath + +Write-Host "Validate packer template" +packer validate -syntax-only $TemplatePath + +Write-Host "Build $ImageTemplateName VM" +packer build -var "client_id=$ClientId" ` + -var "client_secret=$ClientSecret" ` + -var "install_password=$InstallPassword" ` + -var "location=$Location" ` + -var "managed_image_name=$ImageName" ` + -var "managed_image_resource_group_name=$ImageResourceGroupName" ` + -var "subscription_id=$SubscriptionId" ` + -var "temp_resource_group_name=$TempResourceGroupName" ` + -var "tenant_id=$TenantId" ` + -var "virtual_network_name=$VirtualNetworkName" ` + -var "virtual_network_resource_group_name=$VirtualNetworkRG" ` + -var "virtual_network_subnet_name=$VirtualNetworkSubnet" ` + -var "allowed_inbound_ip_addresses=$($AllowedInboundIpAddresses)" ` + -color=false ` + $TemplatePath ` + | Where-Object { + #Filter sensitive data from Packer logs + $currentString = $_ + $sensitiveString = $SensitiveData | Where-Object { $currentString -match $_ } + $sensitiveString -eq $null + } diff --git a/images.CI/linux-and-win/cleanup.ps1 b/images.CI/linux-and-win/cleanup.ps1 new file mode 100644 index 0000000..7feac04 --- /dev/null +++ b/images.CI/linux-and-win/cleanup.ps1 @@ -0,0 +1,19 @@ +param( + [Parameter (Mandatory=$true)] [string] $TempResourceGroupName, + [Parameter (Mandatory=$true)] [string] $SubscriptionId, + [Parameter (Mandatory=$true)] [string] $ClientId, + [Parameter (Mandatory=$true)] [string] $ClientSecret, + [Parameter (Mandatory=$true)] [string] $TenantId +) + +az login --service-principal --username $ClientId --password=$ClientSecret --tenant $TenantId | Out-Null +az account set --subscription $SubscriptionId | Out-Null + +$groupExist = az group exists --name $TempResourceGroupName +if ($groupExist -eq "true") { + Write-Host "Found a match, deleting temporary files" + az group delete --name $TempResourceGroupName --yes | Out-Null + Write-Host "Temporary group was deleted successfully" +} else { + Write-Host "No temporary groups found" +} diff --git a/images.CI/linux-and-win/create-release.ps1 b/images.CI/linux-and-win/create-release.ps1 new file mode 100644 index 0000000..ebed561 --- /dev/null +++ b/images.CI/linux-and-win/create-release.ps1 @@ -0,0 +1,36 @@ +param( + [Parameter (Mandatory)] [UInt32] $BuildId, + [Parameter (Mandatory)] [string] $Organization, + [Parameter (Mandatory)] [string] $Project, + [Parameter (Mandatory)] [string] $ImageType, + [Parameter (Mandatory)] [string] $ManagedImageName, + [Parameter (Mandatory)] [string] $DefinitionId, + [Parameter (Mandatory)] [string] $AccessToken +) + +$Body = @{ + definitionId = $DefinitionId + variables = @{ + ImageBuildId = @{ + value = $BuildId + } + ImageType = @{ + value = $ImageType + } + ManagedImageName = @{ + value = $ManagedImageName + } + } + isDraft = "false" +} | ConvertTo-Json -Depth 3 + +$URL = "https://vsrm.dev.azure.com/$Organization/$Project/_apis/release/releases?api-version=5.1" +$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("'':${AccessToken}")) +$headers = @{ + Authorization = "Basic ${base64AuthInfo}" +} + +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13 +$NewRelease = Invoke-RestMethod $URL -Body $Body -Method "POST" -Headers $headers -ContentType "application/json" + +Write-Host "Created release: $($NewRelease._links.web.href)" diff --git a/images.CI/macos/anka/Anka.Helpers.psm1 b/images.CI/macos/anka/Anka.Helpers.psm1 new file mode 100644 index 0000000..f566e8c --- /dev/null +++ b/images.CI/macos/anka/Anka.Helpers.psm1 @@ -0,0 +1,248 @@ +function Push-AnkaTemplateToRegistry { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $RegistryUrl, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $TagName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $TemplateName + ) + + # if registry uuid doesn't match then delete an image in registry + $AnkaCaCrtPath="$HOME/.config/anka/certs/anka-ca-crt.pem" + $images = anka --machine-readable registry --cacert $AnkaCaCrtPath --registry-path $RegistryUrl list | ConvertFrom-Json | ForEach-Object body + $images | Where-Object name -eq $TemplateName | ForEach-Object { + $id = $_.uuid + Show-StringWithFormat "Deleting '$TemplateName[$id]' VM and '$TagName' tag" + $curlCommand='curl -s -X DELETE -k "{0}/registry/vm?id={1}"' -f $RegistryUrl, $id + Invoke-AnkaCommand -Command $curlCommand + } + + $command = "anka registry --cacert $AnkaCaCrtPath --registry-path $RegistryUrl push --force --tag $TagName $TemplateName" + Invoke-AnkaCommand -Command $command +} + +function Get-AnkaVM { + param( + [string] $VMName + ) + + $command = "anka --machine-readable list" + if (-not [string]::IsNullOrEmpty($VMName)) { + $command = "anka --machine-readable show $VMName" + } + Invoke-AnkaCommand -Command $command | ConvertFrom-Json | Foreach-Object body +} + +function Get-AnkaVMStatus { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $VMName + ) + + $command = "anka --machine-readable list $VMName" + Invoke-AnkaCommand -Command $command | ConvertFrom-Json | Foreach-Object { $_.body.status } +} + +function Get-AnkaVMIPAddress { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $VMName + ) + + Get-AnkaVM -VMName $VMName | Foreach-Object ip +} + +function Invoke-AnkaCommand { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Command + ) + + $result = bash -c "$Command 2>&1" + if ($LASTEXITCODE -ne 0) { + Write-Error "There is an error during command execution:`n$result" + exit 1 + } + $result +} + +function New-AnkaVMTemplate { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $InstallerPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $TemplateName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $TemplateUsername, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $TemplatePassword, + + [Parameter(Mandatory)] + [int] $CPUCount, + + [Parameter(Mandatory)] + [int] $RamSizeGb, + + [Parameter(Mandatory)] + [int] $DiskSizeGb + ) + + $env:ANKA_DEFAULT_USER = $TemplateUsername + $env:ANKA_DEFAULT_PASSWD = $TemplatePassword + $env:ANKA_CREATE_SUSPEND = 0 + $command = "anka create --cpu-count '$CPUCount' --ram-size '${RamSizeGb}G' --disk-size '${DiskSizeGb}G' --app '$InstallerPath' $TemplateName" + Invoke-AnkaCommand -Command $command +} + +function Remove-AnkaVM { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $VMName + ) + + $command = "anka delete $VMName --yes" + $isTemplateExists = Get-AnkaVM | Where-Object name -eq $VMName + if ($isTemplateExists) { + $null = Invoke-AnkaCommand -Command $command + } +} + +function Set-AnkaVMVideoController { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $VMName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $ShortMacOSVersion, + + [ValidateSet("fbuf", "pg")] + [string] $Controller = "pg" + ) + + $command = "anka modify $VMName set display -c $Controller" + $null = Invoke-AnkaCommand -Command $command +} + +function Set-AnkaVMDisplayResolution { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $VMName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $DisplayResolution + ) + + $command = "anka modify $VMName set display -r $DisplayResolution" + $null = Invoke-AnkaCommand -Command $command +} + +function Start-AnkaVM { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $VMName + ) + + $command = "anka start $VMName" + $vmStatus = Get-AnkaVMStatus -VMName $VMName + if ($vmStatus -eq "stopped") { + $null = Invoke-AnkaCommand -Command $command + } +} + +function Stop-AnkaVM { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $VMName + ) + + $command = "anka stop $VMName" + $vmStatus = Get-AnkaVMStatus -VMName $VMName + if ($vmStatus -eq "running") { + $null = Invoke-AnkaCommand -Command $command + } +} + +function Wait-AnkaVMIPAddress { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $VMName, + + [int] $RetryCount = 20, + [int] $Seconds = 60 + ) + + $condition = { + $vmStatus = Get-AnkaVMStatus -VMName $VMName + if ($vmStatus -eq "failed") { + Write-Host "`t [-] $VMName is in failed status" + exit 1 + } + Get-AnkaVMIPAddress -VMName $VMName + } + $null = Invoke-WithRetry -BreakCondition $condition -RetryCount $RetryCount -Seconds $Seconds +} + +function Wait-AnkaVMSSHService { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $VMName, + + [int] $RetryCount = 20, + [int] $Seconds = 60 + ) + + Start-Sleep -Seconds $Seconds + Write-Host "`t[*] Waiting for '$VMName' VM to get an IP address" + Wait-AnkaVMIPAddress -VMName $VMName -RetryCount $RetryCount -Seconds $Seconds + + $ipAddress = Get-AnkaVMIPAddress -VMName $VMName + Write-Host "`t[*] The '$ipAddress' IP address for '$VMName' VM" + + Write-Host "`t[*] Checking if SSH on a port is open" + $isSSHPortOpen = Test-SSHPort -IPAddress $ipAddress + if (-not $isSSHPortOpen) { + Write-Host "`t[x] SSH port is closed" + exit 1 + } +} + +function Set-AnkaVMUuid { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $VMName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Uuid + ) + + $command = "anka modify $VMName set custom-variable hw.uuid $Uuid" + Write-Host "`t[*] Setting $VMName uuid to $Uuid" + Invoke-AnkaCommand -Command $command +} diff --git a/images.CI/macos/anka/CreateCleanAnkaTemplate.ps1 b/images.CI/macos/anka/CreateCleanAnkaTemplate.ps1 new file mode 100644 index 0000000..4d7e545 --- /dev/null +++ b/images.CI/macos/anka/CreateCleanAnkaTemplate.ps1 @@ -0,0 +1,227 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [version] $MacOSVersion, + + [ValidateNotNullOrEmpty()] + [string] $TemplateUsername, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $TemplatePassword, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $RegistryUrl, + + [ValidateNotNullOrEmpty()] + [string] $TemplateName, + + [bool] $DownloadLatestVersion = $true, + [bool] $PushToRegistry = $true, + [bool] $BetaSearch = $false, + [bool] $InstallSoftwareUpdate = $true, + [bool] $EnableAutoLogon = $true, + [int] $CPUCount = 6, + [int] $RamSizeGb = 7, + [int] $DiskSizeGb = 325, + [string] $DisplayResolution = "1920x1080", + [string] $TagName = [DateTimeOffset]::Now.ToUnixTimeSeconds(), + [string] $Uuid = "4203018E-580F-C1B5-9525-B745CECA79EB" +) + +$ErrorActionPreference = "Stop" +$WarningPreference = "SilentlyContinue" + +# Import helper modules +Import-Module "$PSScriptRoot/Anka.Helpers.psm1" +Import-Module "$PSScriptRoot/Service.Helpers.psm1" + +# Helper functions +function Invoke-EnableAutoLogon { + if (-not $EnableAutoLogon) { + Write-Host "`t[*] Skip configuring AutoLogon" + return + } + + $ipAddress = Get-AnkaVMIPAddress -VMName $TemplateName + + Wait-AnkaVMSSHService -VMName $TemplateName -Seconds 30 + + Write-Host "`t[*] Enable AutoLogon" + Enable-AutoLogon -HostName $ipAddress -UserName $TemplateUsername -Password $TemplatePassword + + Write-Host "`t[*] Reboot '$TemplateName' VM to enable AutoLogon" + Restart-VMSSH -HostName $ipAddress | Show-StringWithFormat + + Wait-AnkaVMSSHService -VMName $TemplateName -Seconds 30 + + Write-Host "`t[*] Checking if AutoLogon is enabled" + Test-AutoLogon -VMName $TemplateName -UserName $TemplateUsername +} + +function Invoke-SoftwareUpdate { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Password + ) + + if (-not $InstallSoftwareUpdate) { + Write-Host "`t[*] Skip installing software updates" + return + } + + $ipAddress = Get-AnkaVMIPAddress -VMName $TemplateName + + # Unenroll Seed + Write-Host "`t[*] Resetting the seed before requesting stable versions" + Remove-CurrentBetaSeed -HostName $ipAddress | Show-StringWithFormat + + # Install Software Updates + # Security updates may not be able to install(hang, freeze) when AutoLogon is turned off + Write-Host "`t[*] Finding available software" + $newUpdates = Get-SoftwareUpdate -HostName $ipAddress + + if (-not $newUpdates) { + Write-Host "`t[*] No Updates Available" + return + } + + # Define the next macOS version + $command = "sw_vers" + $guestMacosVersion = Invoke-SSHPassCommand -HostName $ipAddress -Command $command + switch -regex ($guestMacosVersion[1]) { + '12.\d' { $nextOSVersion = 'macOS Ventura|macOS Sonoma|macOS Sequoia' } + '13.\d' { $nextOSVersion = 'macOS Sonoma|macOS Sequoia' } + '14.\d' { $nextOSVersion = 'macOS Sequoia' } + } + + Write-Host "`t[*] Fetching Software Updates ready to install on '$TemplateName' VM:" + Show-StringWithFormat $newUpdates + $listOfNewUpdates = $($($newUpdates.Split("*")).Split("Title").where({$_ -match "Label:"}).Replace("Label: ", '').where({$_ -notmatch $nextOSVersion})) + Write-Host "`t[*] Installing Software Updates on '$TemplateName' VM:" + Install-SoftwareUpdate -HostName $ipAddress -listOfUpdates $listOfNewUpdates -Password $Password | Show-StringWithFormat + Write-Host "`t[*] Sleep 60 seconds before the software updates have been installed" + Start-Sleep -Seconds 60 + Write-Host "`t[*] Waiting for loginwindow process" + Wait-LoginWindow -HostName $ipAddress | Show-StringWithFormat + # Re-enable AutoLogon after installing a new security software update + Invoke-EnableAutoLogon + + foreach ($newupdate in $listOfNewUpdates) { + # Check software updates have been installed + $updates = Get-SoftwareUpdate -HostName $ipAddress + if ($updates.Contains("Action: restart") -and !($updates -match $nextOSVersion)) { + Write-Host "`t[x] Software updates failed to install: " + Show-StringWithFormat $updates + exit 1 + } + } + + Write-Host "`t[*] Show the install history:" + $hUpdates = Get-SoftwareUpdateHistory -HostName $ipAddress + Show-StringWithFormat $hUpdates + + Write-Host "`t[*] The current macOS version:" + $command = "sw_vers" + Invoke-SSHPassCommand -HostName $ipAddress -Command $command | Show-StringWithFormat +} + + +function Invoke-UpdateSettings { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Password + ) + $isConfRequired = $InstallSoftwareUpdate -or $EnableAutoLogon + if (-not $isConfRequired) { + Write-Host "`t[*] Skip additional configuration" + return + } + + Write-Host "`t[*] Starting '$TemplateName' VM" + Start-AnkaVM -VMName $TemplateName + + Write-Host "`t[*] Waiting for SSH service on '$TemplateName' VM" + Wait-AnkaVMSSHService -VMName $TemplateName -Seconds 30 + + # Configure AutoLogon + Invoke-EnableAutoLogon + + # Install software updates + Invoke-SoftwareUpdate -Password $Password + + Write-Host "`t[*] Stopping '$TemplateName' VM" + Stop-AnkaVM -VMName $TemplateName +} + +function Test-VMStopped { + $vmStatus = Get-AnkaVMStatus -VMName $TemplateName + if ($vmStatus -ne "stopped") { + Write-Host "`t[x] VM '$TemplateName' state is not stopped. The current state is '$vmStatus'" + exit 1 + } +} + +# Password is passed as env-var "SSHPASS" +$env:SSHUSER = $TemplateUsername +$env:SSHPASS = $TemplatePassword + +Write-Host "`n[#1] Download macOS application installer:" +$shortMacOSVersion = Get-ShortMacOSVersion -MacOSVersion $MacOSVersion +if ([string]::IsNullOrEmpty($TemplateName)) { + $osArch = $(arch) + if ($osArch -eq "arm64") { + $macOSInstaller = Get-MacOSIPSWInstaller -MacOSVersion $MacOSVersion -DownloadLatestVersion $DownloadLatestVersion -BetaSearch $BetaSearch + $TemplateName = "clean_macos_${shortMacOSVersion}_${osArch}" + } else { + $macOSInstaller = Get-MacOSInstaller -MacOSVersion $MacOSVersion -DownloadLatestVersion $DownloadLatestVersion -BetaSearch $BetaSearch + $TemplateName = "clean_macos_${shortMacOSVersion}" + } +} + +Write-Host "`n[#2] Create a VM template:" +Write-Host "`t[*] Deleting existed template with name '$TemplateName' before creating a new one" +Remove-AnkaVM -VMName $TemplateName + +# Temporary disable VNC for macOS 14 +# It's probably Anka's bug fixed in 3.3.2 +if ($shortMacOSVersion -eq "14") { + $env:ANKA_CREATE_VNC = 0 +} + +Write-Host "`t[*] Creating Anka VM template with name '$TemplateName' and '$TemplateUsername' user" +Write-Host "`t[*] CPU Count: $CPUCount, RamSize: ${RamSizeGb}G, DiskSizeGb: ${DiskSizeGb}G, InstallerPath: $macOSInstaller, TemplateName: $TemplateName" +New-AnkaVMTemplate -InstallerPath "$macOSInstaller" ` + -TemplateName $TemplateName ` + -TemplateUsername $TemplateUsername ` + -TemplatePassword $TemplatePassword ` + -CPUCount $CPUCount ` + -RamSizeGb $RamSizeGb ` + -DiskSizeGb $DiskSizeGb | Show-StringWithFormat + +Write-Host "`n[#3] Configure AutoLogon and/or install software updates:" +Invoke-UpdateSettings -Password $TemplatePassword + +Write-Host "`n[#4] Finalization '$TemplateName' configuration and push to the registry:" +Write-Host "`t[*] The '$TemplateName' VM status is stopped" +Test-VMStopped + +# Configure graphics settings +Write-Host "`t[*] Enabling Graphics Acceleration with Apple Metal for '$TemplateName' VM" +Set-AnkaVMVideoController -VMName $TemplateName -ShortMacOSVersion $ShortMacOSVersion + +Write-Host "`t[*] Setting screen resolution to $DisplayResolution for $TemplateName" +Set-AnkaVMDisplayResolution -VMName $TemplateName -DisplayResolution $DisplayResolution + +# Set static UUID +Set-AnkaVMUuid -VMName $TemplateName -Uuid $Uuid + +if ($PushToRegistry) { + # Push a VM template (and tag) to the Cloud + Write-Host "`t[*] Pushing '$TemplateName' image with '$TagName' tag to the '$RegistryUrl' registry..." + Push-AnkaTemplateToRegistry -RegistryUrl $registryUrl -TagName $TagName -TemplateName $TemplateName +} diff --git a/images.CI/macos/anka/Service.Helpers.psm1 b/images.CI/macos/anka/Service.Helpers.psm1 new file mode 100644 index 0000000..f8368b2 --- /dev/null +++ b/images.CI/macos/anka/Service.Helpers.psm1 @@ -0,0 +1,478 @@ +function Enable-AutoLogon { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $HostName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $UserName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Password + ) + + $url = "https://raw.githubusercontent.com/actions/runner-images/main/images/macos/assets/bootstrap-provisioner/setAutoLogin.sh" + $script = Invoke-RestMethod -Uri $url + $base64 = [Convert]::ToBase64String($script.ToCharArray()) + $command = "echo $base64 | base64 --decode > ./setAutoLogin.sh;sudo bash ./setAutoLogin.sh '${UserName}' '${Password}';rm ./setAutoLogin.sh" + Invoke-SSHPassCommand -HostName $HostName -Command $command +} + +function Invoke-SoftwareUpdateArm64 { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $HostName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Password, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [array] $ListOfUpdates + ) + + # Define the next macOS version + $command = "sw_vers" + $guestMacosVersion = Invoke-SSHPassCommand -HostName $HostName -Command $command + switch -regex ($guestMacosVersion[1]) { + '12.\d' { $nextOSVersion = 'macOS Ventura|macOS Sonoma|macOS Sequoia' } + '13.\d' { $nextOSVersion = 'macOS Sonoma|macOS Sequoia' } + '14.\d' { $nextOSVersion = 'macOS Sequoia' } + } + + $url = "https://raw.githubusercontent.com/actions/runner-images/main/images/macos/assets/auto-software-update-arm64.exp" + $script = Invoke-RestMethod -Uri $url + foreach ($update in $ListOfUpdates) { + if ($update -notmatch $nextOSVersion) { + $updatedScript = $script.Replace("MACOSUPDATE", $($($update.trim()).Replace(" ","\ "))) + $base64 = [Convert]::ToBase64String($updatedScript.ToCharArray()) + $command = "echo $base64 | base64 --decode > ./auto-software-update-arm64.exp;chmod +x ./auto-software-update-arm64.exp; ./auto-software-update-arm64.exp ${Password};rm ./auto-software-update-arm64.exp" + Invoke-SSHPassCommand -HostName $HostName -Command $command + } + } +} + +function Get-AvailableVersions { + param ( + [bool] $IsBeta = $false + ) + + if ($IsBeta) { + $searchPostfix = " beta" + } + + $command = { /usr/sbin/softwareupdate --list-full-installers | grep "macOS" } + $condition = { $LASTEXITCODE -eq 0 } + $softwareUpdates = Invoke-WithRetry -Command $command -BreakCondition $condition | Where-Object { $_.Contains("Title: macOS") -and $_ -match $searchPostfix } + $allVersions = $softwareUpdates -replace "(\* )?(Title|Version|Size):" | ConvertFrom-Csv -Header OSName, OSVersion | Select-Object OSName, OSVersion -Unique + + $allVersions +} + +function Get-AvailableIPSWVersions { + param ( + [bool] $IsBeta = $false, + [bool] $IsLatest = $true, + [string] $MacOSCodeNameOrVersion + ) + + if ($IsBeta) { + $command = { mist list firmware "$MacOSCodeNameOrVersion" --compatible --include-betas --latest --export "/Applications/export.json" } + } elseif ($IsLatest) { + $command = { mist list firmware "$MacOSCodeNameOrVersion" --compatible --latest --export "/Applications/export.json" } + } else { + $command = { mist list firmware "$MacOSCodeNameOrVersion" --compatible --export "/Applications/export.json" } + } + + $condition = { $LASTEXITCODE -eq 0 } + Invoke-WithRetry -Command $command -BreakCondition $condition | Out-Null + $softwareList = get-content -Path "/Applications/export.json" + $availableBuilds = ($softwareList | ConvertFrom-Json).build + if ($null -eq $availableBuilds) { + Write-Host "Requested macOS '$MacOSCodeNameOrVersion' version not found in the list of available installers." + $command = { mist list firmware "$($MacOSCodeNameOrVersion.split('.')[0])" } + Invoke-WithRetry -Command $command -BreakCondition $condition + exit 1 + } + return $availableBuilds +} + +function Get-MacOSIPSWInstaller { + param ( + [Parameter(Mandatory)] + [version] $MacOSVersion, + + [bool] $DownloadLatestVersion = $false, + [bool] $BetaSearch = $false + ) + + if ($MacOSVersion -eq [version] "12.0") { + $MacOSName = "macOS Monterey" + } elseif ($MacOSVersion -eq [version] "13.0") { + $MacOSName = "macOS Ventura" + } elseif ($MacOSVersion -eq [version] "14.0") { + $MacOSName = "macOS Sonoma" + } else { + $MacOSName = $MacOSVersion.ToString() + } + + Write-Host "`t[*] Finding available full installers" + if ($DownloadLatestVersion -eq $true) { + $targetBuild = Get-AvailableIPSWVersions -IsLatest $true -MacOSCodeNameOrVersion $MacOSName + Write-Host "`t[*] The 'DownloadLatestVersion' flag is set to true. Latest compatible macOS build of '$MacOSName' is '$targetBuild'" + } elseif ($BetaSearch -eq $true) { + $targetBuild = Get-AvailableIPSWVersions -IsBeta $true -MacOSCodeNameOrVersion $MacOSName + Write-Host "`t[*] The 'BetaSearch' flag is set to true. Latest compatible beta macOS build of '$MacOSName' is '$targetBuild'" + } else { + $targetBuild = Get-AvailableIPSWVersions -MacOSCodeNameOrVersion $MacOSName -IsLatest $false + Write-Host "`t[*] Available compatible macOS builds of '$MacOSName' are: $($targetBuild -join ', ')" + if ($targetBuild.Count -gt 1) { + Write-Error "`t[*] Please specify the exact build number of macOS you want to install" + exit 1 + } + } + + $installerPathPattern = "/Applications/Install ${macOSName}*.ipsw" + if (Test-Path $installerPathPattern) { + $previousInstallerPath = Get-Item -Path $installerPathPattern + Write-Host "`t[*] Removing '$previousInstallerPath' installation app before downloading the new one" + sudo rm -rf "$previousInstallerPath" + } + + # Download macOS installer + $installerDir = "/Applications/" + $installerName = "Install ${macOSName}.ipsw" + Write-Host "`t[*] Requested macOS '$targetBuild' version installer found, fetching it from mist database" + Invoke-WithRetry { mist download firmware "$targetBuild" --output-directory $installerDir --firmware-name "$installerName" } { $LASTEXITCODE -eq 0 } | Out-Null + if (Test-Path "$installerDir$installerName") { + $result = "$installerDir$installerName" + } else { + Write-Error "`t[*] Requested macOS '$targetBuild' version installer failed to download" + exit 1 + } + return $result +} + +function Get-MacOSInstaller { + param ( + [Parameter(Mandatory)] + [version] $MacOSVersion, + + [bool] $DownloadLatestVersion = $false, + [bool] $BetaSearch = $false + ) + + # Enroll machine to DeveloperSeed if we need beta and unenroll otherwise + $seedutil = "/System/Library/PrivateFrameworks/Seeding.framework/Versions/Current/Resources/seedutil" + if ($BetaSearch) { + Write-Host "`t[*] Beta Version requested. Enrolling machine to DeveloperSeed" + sudo $seedutil enroll DeveloperSeed | Out-Null + } else { + Write-Host "`t[*] Resetting the seed before requesting stable versions" + sudo $seedutil unenroll | Out-Null + } + + # Validate there is no software update at the moment + Test-SoftwareUpdate + + # Validate availability OSVersion + Write-Host "`t[*] Finding available full installers" + $availableVersions = Get-AvailableVersions -IsBeta $BetaSearch + if ($DownloadLatestVersion) { + $shortMacOSVersion = Get-ShortMacOSVersion -MacOSVersion $MacOSVersion + $filterSearch = "${shortMacOSVersion}." + $filteredVersions = $availableVersions.Where{ $_.OSVersion.StartsWith($filterSearch) } + if (-not $filteredVersions) { + Write-Host "`t[x] Failed to find any macOS versions using '$filterSearch' search condition" + Show-StringWithFormat $availableVersions + exit 1 + } + Show-StringWithFormat $filteredVersions + $osVersions = $filteredVersions.OSVersion | Sort-Object { [version]$_ } + $MacOSVersion = $osVersions | Select-Object -Last 1 + Write-Host "`t[*] The 'DownloadLatestVersion' flag is set. Latest macOS version is '$MacOSVersion' now" + } + + $macOSName = $availableVersions.Where{ $MacOSVersion -eq $_.OSVersion }.OSName.Split(" ")[1] + if (-not $macOSName) { + Write-Host "`t[x] Requested macOS '$MacOSVersion' version not found in the list of available installers. Available versions are:`n$($availableVersions.OSVersion)" + Write-Host "`t[x] Make sure to pass '-BetaSearch `$true' if you need a beta version installer" + exit 1 + } + + # Clear LastRecommendedMajorOSBundleIdentifier to prevent error during fetching updates + # Install failed with error: Update not found + Update-SoftwareBundle + + # Download macOS installer + Write-Host "`t[*] Requested macOS '$MacOSVersion' version installer found, fetching it from Apple Software Update" + Invoke-WithRetry -Command { sudo /usr/local/bin/mist download installer $MacOSVersion application --force --export installer.json --output-directory /Applications } -BreakCondition { $LASTEXITCODE -eq 0 } | Out-Null + if (-not(Test-Path installer.json -PathType leaf)) { + Write-Host "`t[x] Failed to fetch $MacOSVersion macOS" + exit 1 + } + + $installerPath = (Get-Content installer.json | Out-String | ConvertFrom-Json).options.applicationPath + if (-not $installerPath) { + Write-Host "`t[x] Path not found using '$installerPathPattern'" + exit 1 + } + Write-Host "`t[*] Installer successfully downloaded to '$installerPath'" + + $installerPath +} + +function Get-ShortMacOSVersion { + param ( + [Parameter(Mandatory)] + [version] $MacOSVersion + ) + + # Take Major.Minor version for macOS 10 (10.14 or 10.15) and Major for all further versions + $MacOSVersion.Major -eq 10 ? $MacOSVersion.ToString(2) : $MacOSVersion.ToString(1) +} + +function Get-SoftwareUpdate { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $HostName + ) + + $command = "/usr/sbin/softwareupdate --list" + $result = Invoke-SSHPassCommand -HostName $HostName -Command $command + $result | Where-Object { $_ -match "(Label|Title):" } | Out-String +} + +function Get-SoftwareUpdateHistory { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $HostName + ) + + $command = "/usr/sbin/softwareupdate --history" + Invoke-SSHPassCommand -HostName $HostName -Command $command | Out-String +} + +function Install-SoftwareUpdate { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $HostName, + [array] $listOfUpdates, + [string] $Password + ) + # If an update is happening on macOS arm64 we will use the additional tool to install updates. + $osArch = $(arch) + if ($osArch -eq "arm64") { + Invoke-SoftwareUpdateArm64 -HostName $HostName -Password $Password -ListOfUpdates $listOfUpdates + } else { + foreach ($update in $listOfUpdates) { + $command = "sudo /usr/sbin/softwareupdate --restart --verbose --install '$($update.trim())'" + Invoke-SSHPassCommand -HostName $HostName -Command $command + } + } +} + +function Invoke-SSHPassCommand { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $HostName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Command, + + [int] $ConnectTimeout = 10, + [int] $ConnectionAttempts = 10, + [int] $ServerAliveInterval = 30 + ) + + $sshArg = @( + "sshpass" + "-e" + "ssh" + "-o UserKnownHostsFile=/dev/null" + "-o StrictHostKeyChecking=no" + "-o ConnectTimeout=$ConnectTimeout" + "-o ConnectionAttempts=$ConnectionAttempts" + "-o LogLevel=ERROR" + "-o ServerAliveInterval=$ServerAliveInterval" + "${env:SSHUSER}@${HostName}" + ) + $sshPassOptions = $sshArg -join " " + if ($PSVersionTable.PSVersion.Major -eq 7 -and $PSVersionTable.PSVersion.Minor -le 2) { + $result = bash -c "$sshPassOptions \""$Command\"" 2>&1" + } else { + $result = bash -c "$sshPassOptions `"$Command`" 2>&1" + } + if ($LASTEXITCODE -ne 0) { + Write-Error "There is an error during command execution:`n$result" + exit 1 + } + $result +} + +function Invoke-WithRetry { + param( + [scriptblock] $Command, + [scriptblock] $BreakCondition, + [int] $RetryCount = 20, + [int] $Seconds = 60 + ) + while ($RetryCount -gt 0) { + try { + if ($Command) { + $result = & $Command + } + + if (& $BreakCondition) { + return $result + } + } catch { + Write-Host "`t [!] Error during command execution: $_" + } + + $RetryCount-- + if ($RetryCount -eq 0) { + Write-Error "No more attempts left: $BreakCondition" + } + Write-Host "`t [/] Waiting $Seconds seconds before retrying. Retries left: $RetryCount" + Start-Sleep -Seconds $Seconds + } + + $result +} + +function Restart-VMSSH { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $HostName + ) + + # + # https://unix.stackexchange.com/questions/58271/closing-connection-after-executing-reboot-using-ssh-command + # + $command = '(sleep 1 && sudo reboot &) && exit' + Invoke-SSHPassCommand -HostName $HostName -Command $command +} + +function Show-StringWithFormat { + param( + [Parameter(ValuefromPipeline)] + [object] $string + ) + + process { + ($string | Out-String).Trim().split("`n") | ForEach-Object { Write-Host "`t $_" } + } +} + +function Remove-CurrentBetaSeed { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $HostName + ) + + $command = "sudo /System/Library/PrivateFrameworks/Seeding.framework/Versions/Current/Resources/seedutil unenroll" + Invoke-SSHPassCommand -HostName $HostName -Command $command | Out-String +} + +function Test-AutoLogon { + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $VMName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $UserName + ) + + Invoke-WithRetry -BreakCondition { + # pwsh crashes if it invokes directly + # https://github.com/dotnet/runtime/issues/59059 + $ankaUser = "" | bash -c "anka run $VMName /usr/bin/id -nu" + $UserName -eq $ankaUser + } +} + +function Test-SoftwareUpdate { + param ( + [string] $UpdateProcessName = "softwareupdate" + ) + + $command = { + $updateProcess = (Get-Process -Name $UpdateProcessName -ErrorAction SilentlyContinue).id + if ($updateProcess) { + # Workaround to get commandline param as it doesn't work for macOS atm https://github.com/PowerShell/PowerShell/issues/13943 + $processName = /bin/ps -o command= $updateProcess + Write-Host "`t[*] Another software update process with '$updateProcess' id is in place with the following arguments '$processName'" + } + } + $condition = { + $null -eq (Get-Process -Name $UpdateProcessName -ErrorAction SilentlyContinue) + } + + Invoke-WithRetry -Command $command -BreakCondition $condition +} + +function Test-SSHPort { + param( + [Parameter(Mandatory)] + [ipaddress] $IPAddress, + + [int] $Port = 22, + [int] $Timeout = 2000 + ) + + Invoke-WithRetry -Command {$true} -BreakCondition { + try { + $client = [System.Net.Sockets.TcpClient]::new() + $client.ConnectAsync($IPAddress, $Port).Wait($Timeout) + } + catch { + $false + } + finally { + $client.Close() + } + } +} + +function Wait-LoginWindow { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $HostName, + + [int] $RetryCount = 60, + [int] $Seconds = 60 + ) + + $condition = { + $psCommand = "/bin/ps auxww" + $lw = "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow" + $ctk = "/System/Library/Frameworks/CryptoTokenKit.framework/ctkahp.bundle/Contents/MacOS/ctkahp" + $proc = Invoke-SSHPassCommand -HostName $HostName -Command $psCommand | Out-String + $proc.Contains($lw) -and $proc.Contains($ctk) + } + Invoke-WithRetry -RetryCount $RetryCount -Seconds $Seconds -BreakCondition $condition +} + +function Update-SoftwareBundle { + $productVersion = sw_vers -productVersion + + if ( $productVersion.StartsWith('11.') ) { + sudo rm -rf /Library/Preferences/com.apple.commerce.plist + sudo /usr/bin/defaults delete /Library/Preferences/com.apple.SoftwareUpdate.plist LastRecommendedMajorOSBundleIdentifier | Out-Null + } +} diff --git a/images.CI/measure-provisioners-duration.ps1 b/images.CI/measure-provisioners-duration.ps1 new file mode 100644 index 0000000..bd07a5b --- /dev/null +++ b/images.CI/measure-provisioners-duration.ps1 @@ -0,0 +1,122 @@ +param( + [Parameter(Mandatory=$true)] + [string]$PackerLogPath, + [string]$PrefixToPathTrim, + [int]$PrintTopNLongest = 25 +) + +$DateTimeRegex = "(\d+\/\d+\/\d+ \d+:\d+:\d+)" +$TelemetryLineRegex = "\[INFO\] \(telemetry\)" +$StartProvisionerRegex = "^${DateTimeRegex} ${TelemetryLineRegex} Starting provisioner (.+)$" +$EndProvisionerRegex = "^${DateTimeRegex} ${TelemetryLineRegex} ending (.+)$" +$ShellScriptSubItemRegex = "${DateTimeRegex} ui: ==> .+: Provisioning with \w+ script: (.+)" +$DownloadUploadSubItemRegex = "${DateTimeRegex} ui: ==> .+: (Downloading .+|Uploading .+)" + +function Start-ProvisionerItem { + param([string]$ProvisionerType, [string]$StartTime) + + return @{ + ProvisionerType = $ProvisionerType + StartTime = [DateTime]::Parse($StartTime) + SubItems = @() + } +} + +function End-ProvisionerItem { + param([object]$Provisioner, [string]$EndTime) + + $Provisioner.EndTime = [DateTime]::Parse($EndTime) + $Provisioner.Duration = New-TimeSpan -Start $Provisioner.StartTime -End $Provisioner.EndTime +} + +function Add-ProvisionerSubItem { + param([object]$Provisioner, [string]$Command, [string]$DateTime) + + $lastItem = $Provisioner.SubItems | Select-Object -Last 1 + if ($lastItem) { + $lastItem.EndTime = [DateTime]::Parse($DateTime) + $lastItem.Duration = New-TimeSpan -Start $lastItem.StartTime -End $lastItem.EndTime + } + + if ($Command) { + if ($PrefixToPathTrim) { $Command = $Command.Replace($PrefixToPathTrim, ".") } + $Provisioner.SubItems += @{ + Command = $Command + StartTime = [DateTime]::Parse($DateTime) + } + } +} + +function Invoke-TryFindProvisionerSubItem { + param([object]$Provisioner, [string] $Line) + + if ($Provisioner.ProvisionerType -in "powershell", "shell", "windows-shell") { + if ($Line -match $ShellScriptSubItemRegex) { + Add-ProvisionerSubItem -Provisioner $Provisioner -Command $Matches[2] -DateTime $Matches[1] + } + } elseif ($Provisioner.ProvisionerType -eq "file") { + if ($Line -match $DownloadUploadSubItemRegex) { + Add-ProvisionerSubItem -Provisioner $Provisioner -Command $Matches[2] -DateTime $Matches[1] + } + } +} + +function Assert-StartProvisioner { + param([object]$Provisioner, [string]$ProvisionerType) + if ($null -ne $Provisioner) { + throw "New provisioner '$ProvisionerType' has been started but previous '$($Provisioner.ProvisionerType)' was not finished yet" + } +} + +function Assert-EndProvisioner { + param([object]$Provisioner, [string]$ProvisionerType) + if (($null -ne $Provisioner) -and ($Provisioner.ProvisionerType -ne $ProvisionerType)) { + throw "Expected end of '$($Provisioner.ProvisionerType)' provisioner but found end of '$ProvisionerType'" + } +} + +$provisionersList = @() +$currentProvisioner = $null + +if ((Get-Content $PackerLogPath -Raw) -notmatch $TelemetryLineRegex) { + throw "Packer log doesn't contain diagnostic information. Env PACKER_LOG must be set to 1" +} + +Get-Content $PackerLogPath | ForEach-Object { + if ($_ -match $StartProvisionerRegex) { + Assert-StartProvisioner -Provisioner $currentProvisioner -ProvisionerType $Matches[2] + + $currentProvisioner = Start-ProvisionerItem -ProvisionerType $Matches[2] -StartTime $Matches[1] + } elseif (($_ -match $EndProvisionerRegex) -and $currentProvisioner) { + Assert-EndProvisioner -Provisioner $currentProvisioner -ProvisionerType $Matches[2] + + End-ProvisionerItem -Provisioner $currentProvisioner -EndTime $Matches[1] + Add-ProvisionerSubItem -Provisioner $currentProvisioner -Command $null -DateTime $Matches[1] + $provisionersList += $currentProvisioner + $currentProvisioner = $null + } elseif ($currentProvisioner) { + Invoke-TryFindProvisionerSubItem -Provisioner $currentProvisioner -Line $_ + } +} +$totalProvisionersTime = New-TimeSpan +$provisionersList | ForEach-Object { $totalProvisionersTime = $totalProvisionersTime.Add($_.Duration) } + +# Print information about provisioners in order of execution +Write-Host "Build timeline:" +$provisionersList | ForEach-Object { + Write-Host "- $($_.Duration) | $($_.ProvisionerType)" + $_.SubItems | ForEach-Object { + Write-Host " $($_.Duration) | $($_.Command)" + } + Write-Host "" +} +Write-Host "Total provisioners time: $totalProvisionersTime" + +if ($PrintTopNLongest -gt 0) { + Write-Host "`n`nTop longest provisioners:" + $provisionersList | ForEach-Object { + if ($_.SubItems.Length -gt 0) { $_.SubItems } else { @{ Command = $_.ProvisionerType; Duration = $_.Duration } } + } | Sort-Object { $_.Duration } | Select-Object -Last $PrintTopNLongest | ForEach-Object { + Write-Host "- $($_.Duration) | $($_.Command)" + } +} diff --git a/images.CI/shebang-linter.ps1 b/images.CI/shebang-linter.ps1 new file mode 100644 index 0000000..08f8c19 --- /dev/null +++ b/images.CI/shebang-linter.ps1 @@ -0,0 +1,43 @@ +$ErrorActionPreference = "Stop" + +function Validate-Scripts { + Param ( + [Parameter(Mandatory=$true)] + [string[]]$Path, + [Parameter(Mandatory=$true)] + [string]$ExpectedShebang + ) + $ScriptWithoutShebangLine = @() + Get-ChildItem $path -Recurse -File -Filter "*.sh" | ForEach-Object { + $relativePath = Resolve-Path $_.FullName -Relative + $shebangLine = Get-Content -Path $_.FullName | Select-Object -First 1 + if ($shebangLine -eq $ExpectedShebang) { + Write-Host "[+] '$relativePath'" + } + else { + Write-Host "[-] '$relativePath'" + $ScriptWithoutShebangLine += $relativePath + } + } + return $ScriptWithoutShebangLine +} + +$PathUbuntu = "./images/ubuntu/scripts" +$PathMacOS = "./images/macos" +$PatternUbuntu = "#!/bin/bash -e" +$PatternMacOS = "#!/bin/bash -e -o pipefail" +$ScriptsWithBrokenShebang = @() +$ScriptsWithBrokenShebang += Validate-Scripts -Path $PathUbuntu -ExpectedShebang $PatternUbuntu +$ScriptsWithBrokenShebang += Validate-Scripts -Path $PathMacOS -ExpectedShebang $PatternMacOS +if ($ScriptsWithBrokenShebang.Length -gt 0) { + Write-Host "`n`n`n##[error] The following scripts have incorrect shebang:" + $ScriptsWithBrokenShebang | ForEach-Object { + Write-Host "##[error] '$_'" + } + Write-Host "`n`n##[error] Expected shebang for scripts in 'images/ubuntu' folder is '$PatternUbuntu'" + Write-Host "##[error] Expected shebang for scripts in 'images/macos' folder is '$PatternMacOS'" + exit 1 + else { + Write-Host "All scripts have correct shebang." + } +} diff --git a/images/macos/assets/add-certificate.swift b/images/macos/assets/add-certificate.swift new file mode 100644 index 0000000..fb8209b --- /dev/null +++ b/images/macos/assets/add-certificate.swift @@ -0,0 +1,64 @@ +import Foundation +import Security + +let certInfo: CFDictionary + +enum SecurityError: Error { + case generalError +} + +func deleteCertificateFromKeyChain(_ certificateLabel: String) -> Bool { + let delQuery: [NSString: Any] = [ + kSecClass: kSecClassCertificate, + kSecAttrLabel: certificateLabel, + ] + let delStatus: OSStatus = SecItemDelete(delQuery as CFDictionary) + + return delStatus == errSecSuccess +} + +func saveCertificateToKeyChain(_ certificate: SecCertificate, certificateLabel: String) throws { + SecKeychainSetPreferenceDomain(SecPreferencesDomain.system) + deleteCertificateFromKeyChain(certificateLabel) + + let setQuery: [NSString: AnyObject] = [ + kSecClass: kSecClassCertificate, + kSecValueRef: certificate, + kSecAttrLabel: certificateLabel as AnyObject, + kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked, + ] + let addStatus: OSStatus = SecItemAdd(setQuery as CFDictionary, nil) + + guard addStatus == errSecSuccess else { + throw SecurityError.generalError + } + + var status = SecTrustSettingsSetTrustSettings(certificate, SecTrustSettingsDomain.admin, nil) +} + +func getCertificateFromString(stringData: String) throws -> SecCertificate { + if let data = NSData(base64Encoded: stringData, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) { + if let certificate = SecCertificateCreateWithData(kCFAllocatorDefault, data) { + return certificate + } + } + throw SecurityError.generalError +} + +if CommandLine.arguments.count > 1 { + let fileURL = URL(fileURLWithPath: CommandLine.arguments[1]) + do { + let certData = try Data(contentsOf: fileURL) + let certificate = SecCertificateCreateWithData(nil, certData as CFData) + if certificate != nil { + print("Saving certificate") + try? saveCertificateToKeyChain(certificate!, certificateLabel: "Test") + } else { + print("Certificate can't be read") + } + } catch { + print("Unable to read the file \(CommandLine.arguments[1])") + } +} else { + print("Usage: \(CommandLine.arguments[0]) [cert.file]") +} diff --git a/images/macos/assets/auto-software-update-arm64.exp b/images/macos/assets/auto-software-update-arm64.exp new file mode 100644 index 0000000..f848acc --- /dev/null +++ b/images/macos/assets/auto-software-update-arm64.exp @@ -0,0 +1,7 @@ +#! /usr/bin/expect -f + +set timeout -1 +spawn sudo /usr/sbin/softwareupdate --restart --verbose --install "MACOSUPDATE" +expect "Password*" +send "[lindex $argv 0]\r" +expect eof diff --git a/images/macos/assets/bashprofile b/images/macos/assets/bashprofile new file mode 100644 index 0000000..ab4f94c --- /dev/null +++ b/images/macos/assets/bashprofile @@ -0,0 +1 @@ +[ -f $HOME/.bashrc ] && source $HOME/.bashrc diff --git a/images/macos/assets/bashrc b/images/macos/assets/bashrc new file mode 100644 index 0000000..ca021fd --- /dev/null +++ b/images/macos/assets/bashrc @@ -0,0 +1,33 @@ +export LC_CTYPE=en_US.UTF-8 +export LC_ALL=en_US.UTF-8 +export LANG=en_US.UTF-8 + +export ANDROID_HOME=${HOME}/Library/Android/sdk +export ANDROID_SDK_ROOT=${HOME}/Library/Android/sdk + +export VM_ASSETS=/usr/local/opt/$USER/scripts + +export NUNIT_BASE_PATH=/Library/Developer/nunit +export NUNIT3_PATH=/Library/Developer/nunit/3.6.0 + +export AGENT_TOOLSDIRECTORY=$HOME/hostedtoolcache +export RUNNER_TOOL_CACHE=$HOME/hostedtoolcache +export ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE=$HOME/actionarchivecache + +export PATH=/Library/Frameworks/Mono.framework/Versions/Current/Commands:$PATH +export PATH=/Library/Frameworks/Python.framework/Versions/Current/bin:$PATH +export PATH=$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$PATH +export PATH=/usr/local/bin:/usr/local/sbin:~/bin:~/.yarn/bin:$PATH +export PATH="/usr/local/opt/curl/bin:$PATH" +export PATH=$HOME/.cargo/bin:$PATH + +export RCT_NO_LAUNCH_PACKAGER=1 +export DOTNET_ROOT=$HOME/.dotnet +export DOTNET_MULTILEVEL_LOOKUP=0 + +export HOMEBREW_NO_AUTO_UPDATE=1 +export HOMEBREW_NO_INSTALL_CLEANUP=1 +export HOMEBREW_CASK_OPTS="--no-quarantine" + +export BOOTSTRAP_HASKELL_NONINTERACTIVE=1 +export BOOTSTRAP_HASKELL_INSTALL_NO_STACK_HOOK=1 diff --git a/images/macos/assets/bootstrap-provisioner/change_password b/images/macos/assets/bootstrap-provisioner/change_password new file mode 100755 index 0000000..eda1884 --- /dev/null +++ b/images/macos/assets/bootstrap-provisioner/change_password @@ -0,0 +1,13 @@ +#!/bin/bash +USERNAME="$1" +OLD_PASSWD="$2" +NEW_PASSWD="$3" +UPDATE_LOGIN_KEYCHAIN="${4:-true}" + +sudo /usr/sbin/sysadminctl -resetPasswordFor $USERNAME -newPassword "$NEW_PASSWD" -adminUser $USERNAME -adminPassword "$OLD_PASSWD" +sudo /usr/bin/python3 /Users/$USERNAME/bootstrap/kcpassword.py "$NEW_PASSWD" +sudo /usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser "$USERNAME" + +if [[ $UPDATE_LOGIN_KEYCHAIN == "true" ]]; then + /usr/bin/security set-keychain-password -o "$OLD_PASSWD" -p "$NEW_PASSWD" /Users/$USERNAME/Library/Keychains/login.keychain +fi diff --git a/images/macos/assets/bootstrap-provisioner/installNewProvisioner.sh b/images/macos/assets/bootstrap-provisioner/installNewProvisioner.sh new file mode 100644 index 0000000..81cdbdf --- /dev/null +++ b/images/macos/assets/bootstrap-provisioner/installNewProvisioner.sh @@ -0,0 +1,45 @@ +#!/bin/bash -e -o pipefail +BOOTSTRAP_PATH="$1" +ProvisionerPackageUri="$2" +ProvisionerScriptUri="$3" +ScriptName="$4" +ScriptParam="$5" +Username="$6" +arch=$(arch) + +if [[ $arch == "arm64" ]]; then + export PATH=/usr/bin:/usr/sbin:/usr/local/bin:/bin:/sbin:/opt/homebrew/bin +else + export PATH=/usr/bin:/usr/sbin:/usr/local/bin:/bin:/sbin +fi + +PROVISIONER_ROOT=/usr/local/opt/${Username} +sudo mkdir -p ${PROVISIONER_ROOT} +sudo chown ${Username} ${PROVISIONER_ROOT} + +tee -a ${PROVISIONER_ROOT}/runprovisioner.sh > /dev/null <<\EOF +#!/bin/bash + +. ${HOME}/.bashrc + +/usr/local/opt/$USER/provisioner/provisioner +EOF + +chmod +x $PROVISIONER_ROOT/runprovisioner.sh + +aria2c \ + --enable-color=false \ + --file-allocation=none \ + -d ${BOOTSTRAP_PATH} "${ProvisionerPackageUri}" >> ${BOOTSTRAP_PATH}/download.log + +aria2c \ + --enable-color=false \ + --file-allocation=none \ + -d ${BOOTSTRAP_PATH} "${ProvisionerScriptUri}" >> ${BOOTSTRAP_PATH}/download.log + +chmod +x ${BOOTSTRAP_PATH}/${ScriptName} + +# Install Provisioner with provided scripts +eval "$BOOTSTRAP_PATH/$ScriptName $BOOTSTRAP_PATH/$ScriptParam $Username" 2>&1 | tee "$BOOTSTRAP_PATH/install.log" +# State File +touch $BOOTSTRAP_PATH/provisionerDone \ No newline at end of file diff --git a/images/macos/assets/bootstrap-provisioner/kcpassword.py b/images/macos/assets/bootstrap-provisioner/kcpassword.py new file mode 100755 index 0000000..8b50a3e --- /dev/null +++ b/images/macos/assets/bootstrap-provisioner/kcpassword.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +# Port of Gavin Brock's Perl kcpassword generator to Python, by Tom Taylor +# . +# Perl version: http://www.brock-family.org/gavin/perl/kcpassword.html +# This script was taken from https://github.com/timsutton/osx-vm-templates/blob/master/scripts/support/set_kcpassword.py +# Distributed by MIT license, license can be found at the bottom of this script + +import sys +import os + +def encode_data(passwd): + # The magic 11 bytes - these are just repeated + # 0x7D 0x89 0x52 0x23 0xD2 0xBC 0xDD 0xEA 0xA3 0xB9 0x1F + key = [125,137,82,35,210,188,221,234,163,185,31] + key_len = len(key) + + passwd = [ord(x) for x in list(passwd)] + # pad passwd length out to an even multiple of key length + r = len(passwd) % key_len + if len(passwd) == 11: + passwd += [0] + elif (r > 0): + passwd = passwd + [0] * (key_len - r) + + for n in range(0, len(passwd), len(key)): + ki = 0 + for j in range(n, min(n+len(key), len(passwd))): + passwd[j] = passwd[j] ^ key[ki] + ki += 1 + + return bytearray(passwd) + +if __name__ == "__main__": + runner_pwd_encoded = encode_data(sys.argv[1]) + fd = os.open('/etc/kcpassword', os.O_WRONLY | os.O_CREAT, 0o600) + file = os.fdopen(fd, 'wb') + file.truncate(0) + file.write(runner_pwd_encoded) + file.close() + +""" +The MIT License (MIT) +Copyright (c) 2013-2017 Timothy Sutton +Permission 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: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE 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. +""" \ No newline at end of file diff --git a/images/macos/assets/bootstrap-provisioner/setAutoLogin.sh b/images/macos/assets/bootstrap-provisioner/setAutoLogin.sh new file mode 100644 index 0000000..17a9d4d --- /dev/null +++ b/images/macos/assets/bootstrap-provisioner/setAutoLogin.sh @@ -0,0 +1,62 @@ +#!/bin/bash -e -o pipefail +: <<-LICENSE_BLOCK +setAutoLogin (20210911) - Copyright (c) 2021 Joel Bruner (https://github.com/brunerd) +Licensed under the MIT License + +Permission 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: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE 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. +LICENSE_BLOCK + +USERNAME="${1}" +PW="${2}" + +kcpasswordEncode() { + #ascii string + local thisString="${1}" + local i + + #macOS cipher hex ascii representation array + local cipherHex_array=( 7D 89 52 23 D2 BC DD EA A3 B9 1F ) + + #converted to hex representation with spaces + local thisStringHex_array=( $(echo -n "${thisString}" | xxd -p -u | sed 's/../& /g') ) + + #get padding by subtraction if under 11 + local r=$(( ${#thisStringHex_array[@]} % 11 )) + local padding=0 + if [ ${#thisStringHex_array[@]} -eq 11 ]; then + local padding=1 + elif [ $r -gt 0 ]; then + local padding=$(( 11 - $r )) + fi + + #cycle through each element of the array + padding + for ((i=0; i < $(( ${#thisStringHex_array[@]} + ${padding})); i++)); do + #use modulus to loop through the cipher array elements + local charHex_cipher=${cipherHex_array[$(( $i % 11 ))]} + + #get the current hex representation element + local charHex=${thisStringHex_array[$i]} + + #use $(( shell Aritmethic )) to ^ XOR the two 0x## values (extra padding is 0x00) + #take decimal value and printf convert to two char hex value + #use xxd to convert hex to actual value and append to the encodedString variable + local encodedString+=$(printf "%02X" "$(( 0x${charHex_cipher} ^ 0x${charHex:-00} ))") + done + + #return the string without a newline + echo -n "${encodedString}" +} + +#encode password and write file +kcpasswordEncode "${PW}" | xxd -r -p > /etc/kcpassword + +#ensure ownership and permissions (600) +chown root:wheel /etc/kcpassword +chmod u=rw,go= /etc/kcpassword + +#turn on auto login +/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser -string "${USERNAME}" +/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUserScreenLocked -bool false +echo "Auto login enabled for '${USERNAME}'" diff --git a/images/macos/assets/xamarin-selector/select-xamarin-sdk-v2.sh b/images/macos/assets/xamarin-selector/select-xamarin-sdk-v2.sh new file mode 100644 index 0000000..153b52e --- /dev/null +++ b/images/macos/assets/xamarin-selector/select-xamarin-sdk-v2.sh @@ -0,0 +1,53 @@ +#!/bin/bash -e -o pipefail + +# Select any Xamarin SDK versions as the default ones independently by specifying only those versions that need to be changed. +# Examples: +# 1. Change all versions: $VM_ASSETS/select-xamarin-sdkv-v2.sh --mono=6.12 --ios=14.8 --android=10.2 --mac=7.8 +# 2. Change default Mono and iOS only: $VM_ASSETS/select-xamarin-sdkv-v2.sh --mono=6.12 --ios=14.8 +# 3. Change default Android only: $VM_ASSETS/select-xamarin-sdkv-v2.sh --android=11.1 + +get_framework_path() { + case $1 in + --mono) echo '/Library/Frameworks/Mono.framework/Versions' ;; + --ios) echo '/Library/Frameworks/Xamarin.iOS.framework/Versions' ;; + --android) echo '/Library/Frameworks/Xamarin.Android.framework/Versions' ;; + --mac) echo '/Library/Frameworks/Xamarin.Mac.framework/Versions' ;; + *) ;; + esac +} + +change_framework_version() { + local framework=$1 + local version=$2 + + echo "Select $framework $version" + + local countDigit=$(echo "${version}" | grep -o "\." | grep -c "\.") + + if [[ countDigit -gt 1 ]]; then + echo "[WARNING] It is not recommended to specify the exact framework version because your build can be broken with the next patch update. Consider using "major.minor" only format." + fi + + local framework_path=$(get_framework_path "$framework") + + if [ -d "${framework_path}/${version}" ]; then + sudo rm -f ${framework_path}/Current + sudo ln -s "${framework_path}/${version}" "${framework_path}/Current" + else + echo "Invalid framework version ${framework_path}/${version}" + exit 1 + fi +} + +for arg in "$@"; do + key=$(echo $arg | cut -f1 -d=) + value=$(echo $arg | cut -f2 -d=) + + case $key in + --mono | --ios | --android | --mac) change_framework_version $key $value ;; + *) + echo "Invalid parameter <${key}>" + exit 1 + ;; + esac +done diff --git a/images/macos/assets/xamarin-selector/select-xamarin-sdk.sh b/images/macos/assets/xamarin-selector/select-xamarin-sdk.sh new file mode 100644 index 0000000..014075c --- /dev/null +++ b/images/macos/assets/xamarin-selector/select-xamarin-sdk.sh @@ -0,0 +1,22 @@ +#!/bin/bash -e -o pipefail +if [ -z "$1" ]; then + echo "No Xamarin SDK specified." + exit 0 +fi + +XAMARIN_SDK=$1 + +echo "Set Xamarin SDK to ${XAMARIN_SDK}" +FOLDERS_LIST=( + '/Library/Frameworks/Mono.framework/Versions' + '/Library/Frameworks/Xamarin.iOS.framework/Versions' + '/Library/Frameworks/Xamarin.Android.framework/Versions' + '/Library/Frameworks/Xamarin.Mac.framework/Versions' + ) + +for FOLDER in "${FOLDERS_LIST[@]}" +do + echo "Set Current folder for ${FOLDER}" + sudo rm -f ${FOLDER}/Current + sudo ln -s ${FOLDER}/${XAMARIN_SDK} ${FOLDER}/Current +done diff --git a/images/macos/macos-12-Readme.md b/images/macos/macos-12-Readme.md new file mode 100644 index 0000000..86e0b71 --- /dev/null +++ b/images/macos/macos-12-Readme.md @@ -0,0 +1,378 @@ +| Announcements | +|-| +| [macOS 15 (Sequoia) is now available as a public beta in GitHub Actions](https://github.com/actions/runner-images/issues/10686) | +| [[Macos 13 and 14] Android NDK versions <=25 will be removed from images on October 07,2024](https://github.com/actions/runner-images/issues/10614) | +| [[Macos 13 and 14] Go version 1.20.0 will be removed on October 07,2024.](https://github.com/actions/runner-images/issues/10612) | +*** +# macOS 12 +- OS Version: macOS 12.7.6 (21H1320) +- Kernel Version: Darwin 21.6.0 +- Image Version: 20240926.174 + +## Installed Software + +### Language and Runtime +- .NET Core SDK: 6.0.425, 7.0.102, 7.0.202, 7.0.306, 7.0.410, 8.0.101, 8.0.204, 8.0.303, 8.0.402 +- Bash 3.2.57(1)-release +- Clang/LLVM 14.0.0 +- Clang/LLVM (Homebrew) 15.0.7 - available on `$(brew --prefix llvm@15)/bin/clang` +- GCC 12 (Homebrew GCC 12.4.0) - available by `gcc-12` alias +- GCC 13 (Homebrew GCC 13.3.0) - available by `gcc-13` alias +- GCC 14 (Homebrew GCC 14.2.0) - available by `gcc-14` alias +- GNU Fortran 12 (Homebrew GCC 12.4.0) - available by `gfortran-12` alias +- GNU Fortran 13 (Homebrew GCC 13.3.0) - available by `gfortran-13` alias +- GNU Fortran 14 (Homebrew GCC 14.2.0) - available by `gfortran-14` alias +- Julia 1.10.5 +- Kotlin 2.0.20-release-360 +- Go 1.21.13 +- Mono 6.12.0.188 +- Node.js 18.20.4 +- MSBuild 16.10.1.51301 (Mono 6.12.0.188) +- NVM 0.39.7 +- NVM - Cached node versions: 16.20.2, 18.20.4, 20.17.0 +- Perl 5.38.2 +- PHP 8.3.11 +- Python 2.7.18 +- Python3 3.12.6 +- R 4.4.1 +- Ruby 3.0.7p220 + +### Package Management +- Bundler 2.5.20 +- Carthage 0.40.0 +- CocoaPods 1.15.2 +- Composer 2.7.9 +- Homebrew 4.3.24 +- Miniconda 24.7.1 +- NPM 10.7.0 +- NuGet 6.3.1.1 +- Pip 20.3.4 (python 2.7) +- Pip3 24.2 (python 3.12) +- Pipx 1.7.1 +- RubyGems 3.5.20 +- Vcpkg 2024 (build from commit 3d8959985) +- Yarn 1.22.22 + +#### Environment variables +| Name | Value | +| ----------------------- | ---------------------- | +| CONDA | /usr/local/miniconda | +| VCPKG_INSTALLATION_ROOT | /usr/local/share/vcpkg | + +### Project Management +- Apache Ant 1.10.15 +- Apache Maven 3.9.9 +- Gradle 8.10.2 +- Sbt 1.10.2 + +### Utilities +- 7-Zip 17.05 +- aria2 1.37.0 +- azcopy 10.26.0 +- bazel 7.3.1 +- bazelisk 1.21.0 +- bsdtar 3.5.1 - available by 'tar' alias +- Curl 8.10.1 +- Git 2.46.2 +- Git LFS 3.5.1 +- GitHub CLI 2.57.0 +- GNU Tar 1.35 - available by 'gtar' alias +- GNU Wget 1.24.5 +- gpg (GnuPG) 2.4.5 +- ImageMagick 7.1.1-38 +- jq 1.7.1 +- mongo 5.0.29 +- mongod 5.0.29 +- OpenSSL 1.1.1w 11 Sep 2023 +- Packer 1.9.4 +- pkg-config 0.29.2 +- PostgreSQL 14.13 (Homebrew) +- psql (PostgreSQL) 14.13 (Homebrew) +- Sox 14.4.2 +- Subversion (SVN) 1.14.3 +- Switchaudio-osx 1.2.2 +- Vagrant 2.4.1 +- VirtualBox 6.1.38r153438 +- yq 4.44.3 +- zstd 1.5.6 + +### Tools +- App Center CLI 3.0.1 +- AWS CLI 2.17.59 +- AWS SAM CLI 1.124.0 +- AWS Session Manager CLI 1.2.650.0 +- Azure CLI 2.64.0 +- Azure CLI (azure-devops) 1.0.1 +- Bicep CLI 0.30.23 +- Cabal 3.10.3.0 +- Cmake 3.30.3 +- CodeQL Action Bundle 2.19.0 +- Colima 0.7.5 +- Fastlane 2.222.0 +- GHC 9.10.1 +- GHCup 0.1.30.0 +- Jazzy 0.15.2 +- Stack 3.1.1 +- SwiftFormat 0.54.5 +- Swig 4.2.1 +- Xcbeautify 1.6.0 +- Xcode Command Line Tools 14.2.0.0.1.1668646533 +- Xcodes 1.5.0 + +### Linters +- SwiftLint 0.53.0 +- Yamllint 1.35.1 + +### Browsers +- Safari 17.6 (17618.3.11.11.7) +- SafariDriver 17.6 (17618.3.11.11.7) +- Google Chrome 129.0.6668.71 +- Google Chrome for Testing 129.0.6668.70 +- ChromeDriver 129.0.6668.70 +- Microsoft Edge 129.0.2792.52 +- Microsoft Edge WebDriver 129.0.2792.46 +- Mozilla Firefox 130.0.1 +- geckodriver 0.35.0 +- Selenium server 4.25.0 + +#### Environment variables +| Name | Value | +| --------------- | ------------------------------------- | +| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-x64 | +| EDGEWEBDRIVER | /usr/local/share/edge_driver | +| GECKOWEBDRIVER | /usr/local/opt/geckodriver/bin | + +### Java +| Version | Environment Variable | +| --------------------- | -------------------- | +| 8.0.422+5.1 (default) | JAVA_HOME_8_X64 | +| 11.0.24+8 | JAVA_HOME_11_X64 | +| 17.0.12+7 | JAVA_HOME_17_X64 | +| 21.0.4+7.0 | JAVA_HOME_21_X64 | + +### Cached Tools + +#### PyPy +- 2.7.18 [PyPy 7.3.17] +- 3.7.13 [PyPy 7.3.9] +- 3.8.16 [PyPy 7.3.11] +- 3.9.19 [PyPy 7.3.16] +- 3.10.14 [PyPy 7.3.17] + +#### Ruby +- 3.0.7 +- 3.1.6 +- 3.2.5 +- 3.3.5 + +#### Python +- 3.7.17 +- 3.8.18 +- 3.9.20 +- 3.10.15 +- 3.11.9 +- 3.12.6 + +#### Node.js +- 16.20.2 +- 18.20.4 +- 20.17.0 + +#### Go +- 1.20.14 +- 1.21.13 +- 1.22.7 +- 1.23.1 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- Bindgen 0.70.1 +- Cargo-audit 0.20.1 +- Cargo-outdated 0.15.0 +- Cbindgen 0.27.0 +- Clippy 0.1.81 +- Rustfmt 1.7.1-stable + +### PowerShell Tools +- PowerShell 7.4.5 + +#### PowerShell Modules +- Az: 12.3.0 +- MarkdownPS: 1.10 +- Pester: 5.6.1 +- PSScriptAnalyzer: 1.22.0 + +### Web Servers +| Name | Version | ConfigFile | ServiceStatus | ListenPort | +| ----- | ------- | ------------------------------- | ------------- | ---------- | +| httpd | 2.4.62 | /usr/local/etc/httpd/httpd.conf | none | 80 | +| nginx | 1.27.1 | /usr/local/etc/nginx/nginx.conf | none | 80 | + +### Xamarin + +#### Visual Studio for Mac +| Version | Build | Path | +| -------------- | ----------- | ------------------------------------ | +| 2019 | 8.10.25.2 | /Applications/Visual Studio 2019.app | +| 2022 (default) | 17.6.14.413 | /Applications/Visual Studio.app | + +##### Notes +``` +To use Visual Studio 2019 by default rename the app: +mv "/Applications/Visual Studio.app" "/Applications/Visual Studio 2022.app" +mv "/Applications/Visual Studio 2019.app" "/Applications/Visual Studio.app" +``` + +#### Xamarin bundles +| symlink | Xamarin.Mono | Xamarin.iOS | Xamarin.Mac | Xamarin.Android | +| ----------------- | ------------ | ----------- | ----------- | --------------- | +| 6_12_25 | 6.12 | 16.4 | 9.3 | 13.2 | +| 6_12_24 | 6.12 | 16.2 | 9.1 | 13.2 | +| 6_12_23 | 6.12 | 16.2 | 9.1 | 13.1 | +| 6_12_22 | 6.12 | 16.1 | 9.0 | 13.1 | +| 6_12_21 (default) | 6.12 | 16.0 | 8.12 | 13.1 | +| 6_12_20 | 6.12 | 16.0 | 8.12 | 13.0 | +| 6_12_19 | 6.12 | 15.12 | 8.12 | 13.0 | +| 6_12_18 | 6.12 | 15.10 | 8.10 | 12.3 | +| 6_12_17 | 6.12 | 15.10 | 8.10 | 12.2 | +| 6_12_16 | 6.12 | 15.8 | 8.8 | 12.2 | +| 6_12_15 | 6.12 | 15.8 | 8.8 | 12.1 | +| 6_12_14 | 6.12 | 15.8 | 8.8 | 12.0 | +| 6_12_13 | 6.12 | 15.6 | 8.6 | 12.0 | +| 6_12_12 | 6.12 | 15.4 | 8.4 | 12.0 | +| 6_12_11 | 6.12 | 15.2 | 8.2 | 12.0 | +| 6_12_10 | 6.12 | 15.0 | 7.14 | 11.3 | + +#### Unit Test Framework +- NUnit 3.6.1 + +### Xcode +| Version | Build | Path | +| -------------- | -------- | ------------------------------ | +| 14.2 (default) | 14C18 | /Applications/Xcode_14.2.app | +| 14.1 | 14B47b | /Applications/Xcode_14.1.app | +| 14.0.1 | 14A400 | /Applications/Xcode_14.0.1.app | +| 13.4.1 | 13F100 | /Applications/Xcode_13.4.1.app | +| 13.3.1 | 13E500a | /Applications/Xcode_13.3.1.app | +| 13.2.1 | 13C100 | /Applications/Xcode_13.2.1.app | +| 13.1 | 13A1030d | /Applications/Xcode_13.1.app | + +#### Xcode Support Tools +- xcpretty 0.3.0 +- xcversion 2.8.1 + +#### Installed SDKs +| SDK | SDK Name | Xcode Version | +| ----------------------- | -------------------- | ---------------------- | +| macOS 12.0 | macosx12.0 | 13.1 | +| macOS 12.1 | macosx12.1 | 13.2.1 | +| macOS 12.3 | macosx12.3 | 13.3.1, 13.4.1, 14.0.1 | +| macOS 13.0 | macosx13.0 | 14.1 | +| macOS 13.1 | macosx13.1 | 14.2 | +| iOS 15.0 | iphoneos15.0 | 13.1 | +| iOS 15.2 | iphoneos15.2 | 13.2.1 | +| iOS 15.4 | iphoneos15.4 | 13.3.1 | +| iOS 15.5 | iphoneos15.5 | 13.4.1 | +| iOS 16.0 | iphoneos16.0 | 14.0.1 | +| iOS 16.1 | iphoneos16.1 | 14.1 | +| iOS 16.2 | iphoneos16.2 | 14.2 | +| Simulator - iOS 15.0 | iphonesimulator15.0 | 13.1 | +| Simulator - iOS 15.2 | iphonesimulator15.2 | 13.2.1 | +| Simulator - iOS 15.4 | iphonesimulator15.4 | 13.3.1 | +| Simulator - iOS 15.5 | iphonesimulator15.5 | 13.4.1 | +| Simulator - iOS 16.0 | iphonesimulator16.0 | 14.0.1 | +| Simulator - iOS 16.1 | iphonesimulator16.1 | 14.1 | +| Simulator - iOS 16.2 | iphonesimulator16.2 | 14.2 | +| tvOS 15.0 | appletvos15.0 | 13.1 | +| tvOS 15.2 | appletvos15.2 | 13.2.1 | +| tvOS 15.4 | appletvos15.4 | 13.3.1, 13.4.1 | +| tvOS 16.0 | appletvos16.0 | 14.0.1 | +| tvOS 16.1 | appletvos16.1 | 14.1, 14.2 | +| Simulator - tvOS 15.0 | appletvsimulator15.0 | 13.1 | +| Simulator - tvOS 15.2 | appletvsimulator15.2 | 13.2.1 | +| Simulator - tvOS 15.4 | appletvsimulator15.4 | 13.3.1, 13.4.1 | +| Simulator - tvOS 16.0 | appletvsimulator16.0 | 14.0.1 | +| Simulator - tvOS 16.1 | appletvsimulator16.1 | 14.1, 14.2 | +| watchOS 8.0 | watchos8.0 | 13.1 | +| watchOS 8.3 | watchos8.3 | 13.2.1 | +| watchOS 8.5 | watchos8.5 | 13.3.1, 13.4.1 | +| watchOS 9.0 | watchos9.0 | 14.0.1 | +| watchOS 9.1 | watchos9.1 | 14.1, 14.2 | +| Simulator - watchOS 8.0 | watchsimulator8.0 | 13.1 | +| Simulator - watchOS 8.3 | watchsimulator8.3 | 13.2.1 | +| Simulator - watchOS 8.5 | watchsimulator8.5 | 13.3.1, 13.4.1 | +| Simulator - watchOS 9.0 | watchsimulator9.0 | 14.0.1 | +| Simulator - watchOS 9.1 | watchsimulator9.1 | 14.1, 14.2 | +| DriverKit 21.0.1 | driverkit21.0.1 | 13.1 | +| DriverKit 21.2 | driverkit21.2 | 13.2.1 | +| DriverKit 21.4 | driverkit21.4 | 13.3.1, 13.4.1, 14.0.1 | +| DriverKit 22.1 | driverkit22.1 | 14.1 | +| DriverKit 22.2 | driverkit22.2 | 14.2 | + +#### Installed Simulators +| OS | Simulators | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| iOS 15.0 | iPod touch (7th generation)
iPhone 11
iPhone 11 Pro
iPhone 11 Pro Max
iPhone 12
iPhone 12 mini
iPhone 12 Pro
iPhone 12 Pro Max
iPhone 13
iPhone 13 mini
iPhone 13 Pro
iPhone 13 Pro Max
iPhone 8
iPhone 8 Plus
iPhone SE (2nd generation)
iPad (9th generation)
iPad Air (4th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (3rd generation)
iPad Pro (12.9-inch) (5th generation)
iPad Pro (9.7-inch) | +| iOS 15.2 | iPod touch (7th generation)
iPhone 11
iPhone 11 Pro
iPhone 11 Pro Max
iPhone 12
iPhone 12 mini
iPhone 12 Pro
iPhone 12 Pro Max
iPhone 13
iPhone 13 mini
iPhone 13 Pro
iPhone 13 Pro Max
iPhone 8
iPhone 8 Plus
iPhone SE (2nd generation)
iPad (9th generation)
iPad Air (4th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (3rd generation)
iPad Pro (12.9-inch) (5th generation)
iPad Pro (9.7-inch) | +| iOS 15.4 | iPod touch (7th generation)
iPhone 11
iPhone 11 Pro
iPhone 11 Pro Max
iPhone 12
iPhone 12 mini
iPhone 12 Pro
iPhone 12 Pro Max
iPhone 13
iPhone 13 mini
iPhone 13 Pro
iPhone 13 Pro Max
iPhone 8
iPhone 8 Plus
iPhone SE (2nd generation)
iPhone SE (3rd generation)
iPad (9th generation)
iPad Air (4th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (3rd generation)
iPad Pro (12.9-inch) (5th generation)
iPad Pro (9.7-inch) | +| iOS 15.5 | iPod touch (7th generation)
iPhone 11
iPhone 11 Pro
iPhone 11 Pro Max
iPhone 12
iPhone 12 mini
iPhone 12 Pro
iPhone 12 Pro Max
iPhone 13
iPhone 13 mini
iPhone 13 Pro
iPhone 13 Pro Max
iPhone 8
iPhone 8 Plus
iPhone SE (2nd generation)
iPhone SE (3rd generation)
iPad (9th generation)
iPad Air (4th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (3rd generation)
iPad Pro (12.9-inch) (5th generation)
iPad Pro (9.7-inch) | +| iOS 16.0 | iPhone 11
iPhone 11 Pro
iPhone 11 Pro Max
iPhone 12
iPhone 12 mini
iPhone 12 Pro
iPhone 12 Pro Max
iPhone 13
iPhone 13 mini
iPhone 13 Pro
iPhone 13 Pro Max
iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 8
iPhone 8 Plus
iPhone SE (2nd generation)
iPhone SE (3rd generation)
iPad (9th generation)
iPad Air (4th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (3rd generation)
iPad Pro (12.9-inch) (5th generation)
iPad Pro (9.7-inch) | +| iOS 16.1 | iPhone 11
iPhone 11 Pro
iPhone 11 Pro Max
iPhone 12
iPhone 12 mini
iPhone 12 Pro
iPhone 12 Pro Max
iPhone 13
iPhone 13 mini
iPhone 13 Pro
iPhone 13 Pro Max
iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 8
iPhone 8 Plus
iPhone SE (2nd generation)
iPhone SE (3rd generation)
iPad (10th generation)
iPad (9th generation)
iPad Air (4th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (3rd generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (5th generation)
iPad Pro (12.9-inch) (6th generation)
iPad Pro (9.7-inch) | +| iOS 16.2 | iPod touch (7th generation)
iPhone 11
iPhone 11 Pro
iPhone 11 Pro Max
iPhone 12
iPhone 12 mini
iPhone 12 Pro
iPhone 12 Pro Max
iPhone 13
iPhone 13 mini
iPhone 13 Pro
iPhone 13 Pro Max
iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 8
iPhone 8 Plus
iPhone SE (2nd generation)
iPhone SE (3rd generation)
iPad (10th generation)
iPad (9th generation)
iPad Air (4th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (3rd generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (5th generation)
iPad Pro (12.9-inch) (6th generation)
iPad Pro (9.7-inch) | +| tvOS 15.0 | Apple TV
Apple TV 4K (2nd generation)
Apple TV 4K (at 1080p) (2nd generation) | +| tvOS 15.2 | Apple TV
Apple TV 4K (2nd generation)
Apple TV 4K (at 1080p) (2nd generation) | +| tvOS 15.4 | Apple TV
Apple TV 4K (2nd generation)
Apple TV 4K (at 1080p) (2nd generation) | +| tvOS 16.0 | Apple TV
Apple TV 4K (2nd generation)
Apple TV 4K (at 1080p) (2nd generation) | +| tvOS 16.1 | Apple TV
Apple TV 4K (2nd generation)
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p)
Apple TV 4K (at 1080p) (2nd generation) | +| watchOS 8.0 | Apple Watch Series 5 - 40mm
Apple Watch Series 5 - 44mm
Apple Watch Series 6 - 40mm
Apple Watch Series 6 - 44mm
Apple Watch Series 7 - 41mm
Apple Watch Series 7 - 45mm | +| watchOS 8.3 | Apple Watch Series 5 - 40mm
Apple Watch Series 5 - 44mm
Apple Watch Series 6 - 40mm
Apple Watch Series 6 - 44mm
Apple Watch Series 7 - 41mm
Apple Watch Series 7 - 45mm | +| watchOS 8.5 | Apple Watch Series 5 - 40mm
Apple Watch Series 5 - 44mm
Apple Watch Series 6 - 40mm
Apple Watch Series 6 - 44mm
Apple Watch Series 7 - 41mm
Apple Watch Series 7 - 45mm | +| watchOS 9.0 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Ultra (49mm) | +| watchOS 9.1 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Ultra (49mm) | + +### Android +| Package Name | Version | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Command Line Tools | 8.0 | +| Android Emulator | 35.1.21 | +| Android SDK Build-tools | 35.0.0
34.0.0
33.0.0 33.0.1 33.0.2 33.0.3
32.0.0
31.0.0 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-34 (rev 3)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1)
android-33 (rev 3)
android-32 (rev 1)
android-31 (rev 1) | +| Android SDK Platform-Tools | 35.0.2 | +| Android Support Repository | 47.0.0 | +| CMake | 3.18.1
3.22.1 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 24.0.8215888
25.2.9519653 (default)
26.3.11579264
27.1.12297006 | + +#### Environment variables +| Name | Value | +| ----------------------- | --------------------------------------------------- | +| ANDROID_HOME | /Users/runner/Library/Android/sdk | +| ANDROID_NDK | /Users/runner/Library/Android/sdk/ndk/25.2.9519653 | +| ANDROID_NDK_HOME | /Users/runner/Library/Android/sdk/ndk/25.2.9519653 | +| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_ROOT | /Users/runner/Library/Android/sdk/ndk/25.2.9519653 | +| ANDROID_SDK_ROOT | /Users/runner/Library/Android/sdk | + +### Miscellaneous +- libXext 1.3.6 +- libXft 2.3.8 +- Tcl/Tk 8.6.15 +- Zlib 1.3.1 + +#### Environment variables +| Name | Value | +| ----------------- | ----------------------------------------------------------------------------------------- | +| PARALLELS_DMG_URL | https://download.parallels.com/desktop/v20/20.0.1-55659/ParallelsDesktop-20.0.1-55659.dmg | + +##### Notes +``` +If you want to use Parallels Desktop you should download a package from URL stored in +PARALLELS_DMG_URL environment variable. A system extension is allowed for this version. +``` + diff --git a/images/macos/macos-13-Readme.md b/images/macos/macos-13-Readme.md new file mode 100644 index 0000000..056f5fd --- /dev/null +++ b/images/macos/macos-13-Readme.md @@ -0,0 +1,279 @@ +| Announcements | +|-| +| [[macOS] The macOS 12 Actions runner image will begin deprecation on 10/7/24 and will be fully unsupported by 12/3/24 for GitHub and ADO](https://github.com/actions/runner-images/issues/10721) | +| [[macOS] Support policy changes; Xcode 14 and 16 will be removed from macOS 14 on October 28](https://github.com/actions/runner-images/issues/10703) | +| [macOS 15 (Sequoia) is now available as a public beta in GitHub Actions](https://github.com/actions/runner-images/issues/10686) | +| [[Macos 13 and 14] Android NDK versions <=25 will be removed from images on October 07,2024](https://github.com/actions/runner-images/issues/10614) | +| [[Macos 13 and 14] Go version 1.20.0 will be removed on October 07,2024.](https://github.com/actions/runner-images/issues/10612) | +*** +# macOS 13 +- OS Version: macOS 13.7 (22H123) +- Kernel Version: Darwin 22.6.0 +- Image Version: 20241008.186 + +## Installed Software + +### Language and Runtime +- .NET Core SDK: 7.0.102, 7.0.202, 7.0.306, 7.0.410, 8.0.101, 8.0.204, 8.0.303, 8.0.403 +- Bash 3.2.57(1)-release +- Clang/LLVM 14.0.0 +- Clang/LLVM (Homebrew) 15.0.7 - available on `$(brew --prefix llvm@15)/bin/clang` +- GCC 12 (Homebrew GCC 12.4.0) - available by `gcc-12` alias +- GCC 13 (Homebrew GCC 13.3.0) - available by `gcc-13` alias +- GCC 14 (Homebrew GCC 14.2.0) - available by `gcc-14` alias +- GNU Fortran 12 (Homebrew GCC 12.4.0) - available by `gfortran-12` alias +- GNU Fortran 13 (Homebrew GCC 13.3.0) - available by `gfortran-13` alias +- GNU Fortran 14 (Homebrew GCC 14.2.0) - available by `gfortran-14` alias +- Kotlin 2.0.20-release-360 +- Mono 6.12.0.188 +- Node.js 20.18.0 +- Perl 5.38.2 +- PHP 8.3.12 +- Python3 3.12.7 +- Ruby 3.0.7p220 + +### Package Management +- Bundler 2.5.21 +- Carthage 0.40.0 +- CocoaPods 1.15.2 +- Composer 2.8.1 +- Homebrew 4.4.0 +- NPM 10.8.2 +- NuGet 6.3.1.1 +- Pip3 24.2 (python 3.12) +- Pipx 1.7.1 +- RubyGems 3.5.21 +- Vcpkg 2024 (build from commit 2ddc10c8a) +- Yarn 1.22.22 + +### Project Management +- Apache Ant 1.10.15 +- Apache Maven 3.9.9 +- Gradle 8.10.2 + +### Utilities +- 7-Zip 17.05 +- aria2 1.37.0 +- azcopy 10.26.0 +- bazel 7.3.2 +- bazelisk 1.22.0 +- bsdtar 3.5.3 - available by 'tar' alias +- Curl 8.10.1 +- Git 2.47.0 +- Git LFS 3.5.1 +- GitHub CLI 2.58.0 +- GNU Tar 1.35 - available by 'gtar' alias +- GNU Wget 1.24.5 +- gpg (GnuPG) 2.4.5 +- jq 1.7.1 +- OpenSSL 1.1.1w 11 Sep 2023 +- Packer 1.11.2 +- pkg-config 0.29.2 +- yq 4.44.3 +- zstd 1.5.6 + +### Tools +- AWS CLI 2.18.1 +- AWS SAM CLI 1.125.0 +- AWS Session Manager CLI 1.2.650.0 +- Azure CLI 2.65.0 +- Azure CLI (azure-devops) 1.0.1 +- Bicep CLI 0.30.23 +- Cmake 3.30.4 +- CodeQL Action Bundle 2.19.1 +- Fastlane 2.224.0 +- SwiftFormat 0.54.5 +- Xcbeautify 2.11.0 +- Xcode Command Line Tools 14.3.1.0.1.1683849156 +- Xcodes 1.5.0 + +### Linters +- SwiftLint 0.57.0 + +### Browsers +- Safari 18.0.1 (18619.1.26.111.11) +- SafariDriver 18.0.1 (18619.1.26.111.11) +- Google Chrome 129.0.6668.90 +- Google Chrome for Testing 129.0.6668.89 +- ChromeDriver 129.0.6668.89 +- Microsoft Edge 129.0.2792.79 +- Microsoft Edge WebDriver 129.0.2792.82 +- Mozilla Firefox 131.0 +- geckodriver 0.35.0 +- Selenium server 4.25.0 + +#### Environment variables +| Name | Value | +| --------------- | ------------------------------------- | +| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-x64 | +| EDGEWEBDRIVER | /usr/local/share/edge_driver | +| GECKOWEBDRIVER | /usr/local/opt/geckodriver/bin | + +### Java +| Version | Environment Variable | +| ------------------- | -------------------- | +| 8.0.422+5.1 | JAVA_HOME_8_X64 | +| 11.0.24+8 | JAVA_HOME_11_X64 | +| 17.0.12+7 (default) | JAVA_HOME_17_X64 | +| 21.0.4+7.0 | JAVA_HOME_21_X64 | + +### Cached Tools + +#### PyPy +- 2.7.18 [PyPy 7.3.17] +- 3.7.13 [PyPy 7.3.9] +- 3.8.16 [PyPy 7.3.11] +- 3.9.19 [PyPy 7.3.16] +- 3.10.14 [PyPy 7.3.17] + +#### Ruby +- 3.0.7 +- 3.1.6 +- 3.2.5 +- 3.3.5 + +#### Python +- 3.8.18 +- 3.9.20 +- 3.10.15 +- 3.11.9 +- 3.12.7 + +#### Node.js +- 16.20.2 +- 18.20.4 +- 20.18.0 + +#### Go +- 1.21.13 +- 1.22.8 +- 1.23.2 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- Clippy 0.1.81 +- Rustfmt 1.7.1-stable + +### PowerShell Tools +- PowerShell 7.4.5 + +#### PowerShell Modules +- Az: 12.4.0 +- Pester: 5.6.1 +- PSScriptAnalyzer: 1.22.0 + +### Xcode +| Version | Build | Path | +| -------------- | ------- | ------------------------------ | +| 15.2 (default) | 15C500b | /Applications/Xcode_15.2.app | +| 15.1 | 15C65 | /Applications/Xcode_15.1.app | +| 15.0.1 | 15A507 | /Applications/Xcode_15.0.1.app | +| 14.3.1 | 14E300c | /Applications/Xcode_14.3.1.app | +| 14.2 | 14C18 | /Applications/Xcode_14.2.app | +| 14.1 | 14B47b | /Applications/Xcode_14.1.app | + +#### Installed SDKs +| SDK | SDK Name | Xcode Version | +| ------------------------------------------------------- | --------------------------------------------- | ------------- | +| macOS 13.0 | macosx13.0 | 14.1 | +| macOS 13.1 | macosx13.1 | 14.2 | +| macOS 13.3 | macosx13.3 | 14.3.1 | +| macOS 14.0 | macosx14.0 | 15.0.1 | +| macOS 14.2 | macosx14.2 | 15.1, 15.2 | +| iOS 16.1 | iphoneos16.1 | 14.1 | +| iOS 16.2 | iphoneos16.2 | 14.2 | +| iOS 16.4 | iphoneos16.4 | 14.3.1 | +| iOS 17.0 | iphoneos17.0 | 15.0.1 | +| iOS 17.2 | iphoneos17.2 | 15.1, 15.2 | +| Simulator - iOS 16.1 | iphonesimulator16.1 | 14.1 | +| Simulator - iOS 16.2 | iphonesimulator16.2 | 14.2 | +| Simulator - iOS 16.4 | iphonesimulator16.4 | 14.3.1 | +| Simulator - iOS 17.0 | iphonesimulator17.0 | 15.0.1 | +| Simulator - iOS 17.2 | iphonesimulator17.2 | 15.1, 15.2 | +| tvOS 16.1 | appletvos16.1 | 14.1, 14.2 | +| tvOS 16.4 | appletvos16.4 | 14.3.1 | +| tvOS 17.0 | appletvos17.0 | 15.0.1 | +| tvOS 17.2 | appletvos17.2 | 15.1, 15.2 | +| Simulator - tvOS 16.1 | appletvsimulator16.1 | 14.1, 14.2 | +| Simulator - tvOS 16.4 | appletvsimulator16.4 | 14.3.1 | +| Simulator - tvOS 17.0 | appletvsimulator17.0 | 15.0.1 | +| Simulator - tvOS 17.2 | appletvsimulator17.2 | 15.1, 15.2 | +| watchOS 9.1 | watchos9.1 | 14.1, 14.2 | +| watchOS 9.4 | watchos9.4 | 14.3.1 | +| watchOS 10.0 | watchos10.0 | 15.0.1 | +| watchOS 10.2 | watchos10.2 | 15.1, 15.2 | +| Simulator - watchOS 9.1 | watchsimulator9.1 | 14.1, 14.2 | +| Simulator - watchOS 9.4 | watchsimulator9.4 | 14.3.1 | +| Simulator - watchOS 10.0 | watchsimulator10.0 | 15.0.1 | +| Simulator - watchOS 10.2 | watchsimulator10.2 | 15.1, 15.2 | +| visionOS 1.0 | xros1.0 | 15.2 | +| Simulator - visionOS 1.0 | xrsimulator1.0 | 15.2 | +| Asset Runtime SDK for macOS hosts targeting watchOS 9.4 | assetruntime.host.macosx.target.watchos9.4 | 14.3.1 | +| Asset Runtime SDK for macOS hosts targeting tvOS 16.4 | assetruntime.host.macosx.target.appletvos16.4 | 14.3.1 | +| Asset Runtime SDK for macOS hosts targeting iOS 16.4 | assetruntime.host.macosx.target.iphoneos16.4 | 14.3.1 | +| DriverKit 22.1 | driverkit22.1 | 14.1 | +| DriverKit 22.2 | driverkit22.2 | 14.2 | +| DriverKit 22.4 | driverkit22.4 | 14.3.1 | +| DriverKit 23.0 | driverkit23.0 | 15.0.1 | +| DriverKit 23.2 | driverkit23.2 | 15.1, 15.2 | + +#### Installed Simulators +| OS | Simulators | +| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| iOS 16.1 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 16.2 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 16.4 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 17.0 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 17.2 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| tvOS 16.1 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 16.4 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.0 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.2 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| watchOS 9.1 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Ultra (49mm) | +| watchOS 9.4 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Ultra (49mm) | +| watchOS 10.0 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 10.2 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | + +### Android +| Package Name | Version | +| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Command Line Tools | 11.0 | +| Android Emulator | 35.2.10 | +| Android SDK Build-tools | 35.0.0
34.0.0
33.0.2 33.0.3 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-34 (rev 3)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1)
android-33 (rev 3) | +| Android SDK Platform-Tools | 35.0.2 | +| Android Support Repository | 47.0.0 | +| CMake | 3.22.1 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264 (default)
27.1.12297006 | + +#### Environment variables +| Name | Value | +| ----------------------- | --------------------------------------------------- | +| ANDROID_HOME | /Users/runner/Library/Android/sdk | +| ANDROID_NDK | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_NDK_HOME | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_ROOT | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_SDK_ROOT | /Users/runner/Library/Android/sdk | + +### Miscellaneous +- Tcl/Tk 8.6.15 + +#### Environment variables +| Name | Value | +| ----------------- | ----------------------------------------------------------------------------------------- | +| PARALLELS_DMG_URL | https://download.parallels.com/desktop/v20/20.1.0-55732/ParallelsDesktop-20.1.0-55732.dmg | + +##### Notes +``` +If you want to use Parallels Desktop you should download a package from URL stored in +PARALLELS_DMG_URL environment variable. A system extension is allowed for this version. +``` + diff --git a/images/macos/macos-13-arm64-Readme.md b/images/macos/macos-13-arm64-Readme.md new file mode 100644 index 0000000..0868d04 --- /dev/null +++ b/images/macos/macos-13-arm64-Readme.md @@ -0,0 +1,246 @@ +| Announcements | +|-| +| [[macOS] The macOS 12 Actions runner image will begin deprecation on 10/7/24 and will be fully unsupported by 12/3/24 for GitHub and ADO](https://github.com/actions/runner-images/issues/10721) | +| [[macOS] Support policy changes; Xcode 14 and 16 will be removed from macOS 14 on October 28](https://github.com/actions/runner-images/issues/10703) | +| [macOS 15 (Sequoia) is now available as a public beta in GitHub Actions](https://github.com/actions/runner-images/issues/10686) | +| [[Macos 13 and 14] Android NDK versions <=25 will be removed from images on October 07,2024](https://github.com/actions/runner-images/issues/10614) | +| [[Macos 13 and 14] Go version 1.20.0 will be removed on October 07,2024.](https://github.com/actions/runner-images/issues/10612) | +*** +# macOS 13 +- OS Version: macOS 13.7 (22H123) +- Kernel Version: Darwin 22.6.0 +- Image Version: 20241008.189 + +## Installed Software + +### Language and Runtime +- .NET Core SDK: 7.0.102, 7.0.202, 7.0.306, 7.0.410, 8.0.101, 8.0.204, 8.0.303, 8.0.403 +- Bash 3.2.57(1)-release +- Clang/LLVM 14.0.0 +- Clang/LLVM (Homebrew) 15.0.7 - available on `$(brew --prefix llvm@15)/bin/clang` +- GCC 12 (Homebrew GCC 12.4.0) - available by `gcc-12` alias +- GCC 13 (Homebrew GCC 13.3.0) - available by `gcc-13` alias +- GCC 14 (Homebrew GCC 14.2.0) - available by `gcc-14` alias +- GNU Fortran 12 (Homebrew GCC 12.4.0) - available by `gfortran-12` alias +- GNU Fortran 13 (Homebrew GCC 13.3.0) - available by `gfortran-13` alias +- GNU Fortran 14 (Homebrew GCC 14.2.0) - available by `gfortran-14` alias +- Kotlin 2.0.20-release-360 +- Mono 6.12.0.188 +- Node.js 20.18.0 +- Perl 5.38.2 +- Python3 3.12.7 +- Ruby 3.0.7p220 + +### Package Management +- Bundler 2.5.21 +- Carthage 0.40.0 +- CocoaPods 1.15.2 +- Homebrew 4.4.0 +- NPM 10.8.2 +- NuGet 6.3.1.1 +- Pip3 24.2 (python 3.12) +- Pipx 1.7.1 +- RubyGems 3.5.21 +- Yarn 1.22.22 + +### Project Management +- Apache Ant 1.10.15 +- Apache Maven 3.9.9 +- Gradle 8.10.2 + +### Utilities +- 7-Zip 17.05 +- aria2 1.37.0 +- azcopy 10.26.0 +- bazel 7.3.2 +- bazelisk 1.22.0 +- bsdtar 3.5.3 - available by 'tar' alias +- Curl 8.7.1 +- Git 2.47.0 +- Git LFS 3.5.1 +- GitHub CLI 2.58.0 +- GNU Tar 1.35 - available by 'gtar' alias +- GNU Wget 1.24.5 +- gpg (GnuPG) 2.4.5 +- jq 1.7.1 +- OpenSSL 1.1.1w 11 Sep 2023 +- Packer 1.11.2 +- pkg-config 0.29.2 +- yq 4.44.3 +- zstd 1.5.6 + +### Tools +- AWS CLI 2.18.1 +- AWS SAM CLI 1.125.0 +- AWS Session Manager CLI 1.2.650.0 +- Azure CLI 2.65.0 +- Azure CLI (azure-devops) 1.0.1 +- Bicep CLI 0.30.23 +- Cmake 3.30.4 +- CodeQL Action Bundle 2.19.1 +- Fastlane 2.224.0 +- SwiftFormat 0.54.5 +- Xcbeautify 2.11.0 +- Xcode Command Line Tools 14.3.1.0.1.1683849156 +- Xcodes 1.5.0 + +### Linters + +### Browsers +- Safari 18.0.1 (18619.1.26.111.11) +- SafariDriver 18.0.1 (18619.1.26.111.11) +- Google Chrome 129.0.6668.90 +- Google Chrome for Testing 129.0.6668.89 +- ChromeDriver 129.0.6668.89 +- Selenium server 4.25.0 + +#### Environment variables +| Name | Value | +| --------------- | --------------------------------------- | +| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-arm64 | +| EDGEWEBDRIVER | | +| GECKOWEBDRIVER | | + +### Java +| Version | Environment Variable | +| ------------------- | -------------------- | +| 11.0.24+8 | JAVA_HOME_11_arm64 | +| 17.0.12+7 (default) | JAVA_HOME_17_arm64 | +| 21.0.4+7.0 | JAVA_HOME_21_arm64 | + +### Cached Tools + +#### Python +- 3.9.13 +- 3.10.11 +- 3.11.9 +- 3.12.7 + +#### Node.js +- 16.20.1 +- 18.20.4 +- 20.18.0 + +#### Go +- 1.21.13 +- 1.22.8 +- 1.23.2 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- Clippy 0.1.81 +- Rustfmt 1.7.1-stable + +### PowerShell Tools +- PowerShell 7.4.5 + +#### PowerShell Modules +- Az: 12.4.0 +- Pester: 5.6.1 +- PSScriptAnalyzer: 1.22.0 + +### Xcode +| Version | Build | Path | +| -------------- | ------- | ------------------------------ | +| 15.2 (default) | 15C500b | /Applications/Xcode_15.2.app | +| 15.1 | 15C65 | /Applications/Xcode_15.1.app | +| 15.0.1 | 15A507 | /Applications/Xcode_15.0.1.app | +| 14.3.1 | 14E300c | /Applications/Xcode_14.3.1.app | +| 14.2 | 14C18 | /Applications/Xcode_14.2.app | +| 14.1 | 14B47b | /Applications/Xcode_14.1.app | + +#### Installed SDKs +| SDK | SDK Name | Xcode Version | +| ------------------------------------------------------- | --------------------------------------------- | ------------- | +| macOS 13.0 | macosx13.0 | 14.1 | +| macOS 13.1 | macosx13.1 | 14.2 | +| macOS 13.3 | macosx13.3 | 14.3.1 | +| macOS 14.0 | macosx14.0 | 15.0.1 | +| macOS 14.2 | macosx14.2 | 15.1, 15.2 | +| iOS 16.1 | iphoneos16.1 | 14.1 | +| iOS 16.2 | iphoneos16.2 | 14.2 | +| iOS 16.4 | iphoneos16.4 | 14.3.1 | +| iOS 17.0 | iphoneos17.0 | 15.0.1 | +| iOS 17.2 | iphoneos17.2 | 15.1, 15.2 | +| Simulator - iOS 16.1 | iphonesimulator16.1 | 14.1 | +| Simulator - iOS 16.2 | iphonesimulator16.2 | 14.2 | +| Simulator - iOS 16.4 | iphonesimulator16.4 | 14.3.1 | +| Simulator - iOS 17.0 | iphonesimulator17.0 | 15.0.1 | +| Simulator - iOS 17.2 | iphonesimulator17.2 | 15.1, 15.2 | +| tvOS 16.1 | appletvos16.1 | 14.1, 14.2 | +| tvOS 16.4 | appletvos16.4 | 14.3.1 | +| tvOS 17.0 | appletvos17.0 | 15.0.1 | +| tvOS 17.2 | appletvos17.2 | 15.1, 15.2 | +| Simulator - tvOS 16.1 | appletvsimulator16.1 | 14.1, 14.2 | +| Simulator - tvOS 16.4 | appletvsimulator16.4 | 14.3.1 | +| Simulator - tvOS 17.0 | appletvsimulator17.0 | 15.0.1 | +| Simulator - tvOS 17.2 | appletvsimulator17.2 | 15.1, 15.2 | +| watchOS 9.1 | watchos9.1 | 14.1, 14.2 | +| watchOS 9.4 | watchos9.4 | 14.3.1 | +| watchOS 10.0 | watchos10.0 | 15.0.1 | +| watchOS 10.2 | watchos10.2 | 15.1, 15.2 | +| Simulator - watchOS 9.1 | watchsimulator9.1 | 14.1, 14.2 | +| Simulator - watchOS 9.4 | watchsimulator9.4 | 14.3.1 | +| Simulator - watchOS 10.0 | watchsimulator10.0 | 15.0.1 | +| Simulator - watchOS 10.2 | watchsimulator10.2 | 15.1, 15.2 | +| Simulator - visionOS 1.0 | xrsimulator1.0 | 15.2 | +| visionOS 1.0 | xros1.0 | 15.2 | +| Asset Runtime SDK for macOS hosts targeting watchOS 9.4 | assetruntime.host.macosx.target.watchos9.4 | 14.3.1 | +| Asset Runtime SDK for macOS hosts targeting tvOS 16.4 | assetruntime.host.macosx.target.appletvos16.4 | 14.3.1 | +| Asset Runtime SDK for macOS hosts targeting iOS 16.4 | assetruntime.host.macosx.target.iphoneos16.4 | 14.3.1 | +| DriverKit 22.1 | driverkit22.1 | 14.1 | +| DriverKit 22.2 | driverkit22.2 | 14.2 | +| DriverKit 22.4 | driverkit22.4 | 14.3.1 | +| DriverKit 23.0 | driverkit23.0 | 15.0.1 | +| DriverKit 23.2 | driverkit23.2 | 15.1, 15.2 | + +#### Installed Simulators +| OS | Simulators | +| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| iOS 16.1 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 16.2 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 16.4 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 17.0 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 17.2 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| tvOS 16.1 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 16.4 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.0 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.2 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| watchOS 9.1 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Ultra (49mm) | +| watchOS 9.4 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Ultra (49mm) | +| watchOS 10.0 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 10.2 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| visionOS 1.0 | Apple Vision Pro | + +### Android +| Package Name | Version | +| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Command Line Tools | 11.0 | +| Android Emulator | 35.2.10 | +| Android SDK Build-tools | 35.0.0
34.0.0
33.0.2 33.0.3 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-34 (rev 3)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1)
android-33 (rev 3) | +| Android SDK Platform-Tools | 35.0.2 | +| Android Support Repository | 47.0.0 | +| CMake | 3.22.1 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264 (default)
27.1.12297006 | + +#### Environment variables +| Name | Value | +| ----------------------- | --------------------------------------------------- | +| ANDROID_HOME | /Users/runner/Library/Android/sdk | +| ANDROID_NDK | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_NDK_HOME | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_ROOT | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_SDK_ROOT | /Users/runner/Library/Android/sdk | + +### Miscellaneous +- Tcl/Tk 8.6.15 + diff --git a/images/macos/macos-14-Readme.md b/images/macos/macos-14-Readme.md new file mode 100644 index 0000000..ebb3028 --- /dev/null +++ b/images/macos/macos-14-Readme.md @@ -0,0 +1,306 @@ +| Announcements | +|-| +| [[macOS] The macOS 12 Actions runner image will begin deprecation on 10/7/24 and will be fully unsupported by 12/3/24 for GitHub and ADO](https://github.com/actions/runner-images/issues/10721) | +| [[macOS] Support policy changes; Xcode 14 and 16 will be removed from macOS 14 on October 28](https://github.com/actions/runner-images/issues/10703) | +| [macOS 15 (Sequoia) is now available as a public beta in GitHub Actions](https://github.com/actions/runner-images/issues/10686) | +| [[Macos 13 and 14] Android NDK versions <=25 will be removed from images on October 07,2024](https://github.com/actions/runner-images/issues/10614) | +| [[Macos 13 and 14] Go version 1.20.0 will be removed on October 07,2024.](https://github.com/actions/runner-images/issues/10612) | +*** +# macOS 14 +- OS Version: macOS 14.7 (23H124) +- Kernel Version: Darwin 23.6.0 +- Image Version: 20241007.165 + +## Installed Software + +### Language and Runtime +- .NET Core SDK: 7.0.102, 7.0.202, 7.0.306, 7.0.410, 8.0.101, 8.0.204, 8.0.303, 8.0.402 +- Bash 3.2.57(1)-release +- Clang/LLVM 14.0.3 +- Clang/LLVM (Homebrew) 15.0.7 - available on `$(brew --prefix llvm@15)/bin/clang` +- GCC 12 (Homebrew GCC 12.4.0) - available by `gcc-12` alias +- GCC 13 (Homebrew GCC 13.3.0) - available by `gcc-13` alias +- GCC 14 (Homebrew GCC 14.2.0) - available by `gcc-14` alias +- GNU Fortran 12 (Homebrew GCC 12.4.0) - available by `gfortran-12` alias +- GNU Fortran 13 (Homebrew GCC 13.3.0) - available by `gfortran-13` alias +- GNU Fortran 14 (Homebrew GCC 14.2.0) - available by `gfortran-14` alias +- Kotlin 2.0.20-release-360 +- Mono 6.12.0.188 +- Node.js 20.18.0 +- Perl 5.38.2 +- PHP 8.3.12 +- Python3 3.12.7 +- Ruby 3.0.7p220 + +### Package Management +- Bundler 2.5.21 +- Carthage 0.40.0 +- CocoaPods 1.15.2 +- Composer 2.8.1 +- Homebrew 4.4.0 +- NPM 10.8.2 +- NuGet 6.3.1.1 +- Pip3 24.2 (python 3.12) +- Pipx 1.7.1 +- RubyGems 3.5.21 +- Yarn 1.22.22 + +### Project Management +- Apache Ant 1.10.15 +- Apache Maven 3.9.9 +- Gradle 8.10.2 + +### Utilities +- 7-Zip 17.05 +- aria2 1.37.0 +- azcopy 10.26.0 +- bazel 7.3.2 +- bazelisk 1.22.0 +- bsdtar 3.5.3 - available by 'tar' alias +- Curl 8.10.1 +- Git 2.46.2 +- Git LFS 3.5.1 +- GitHub CLI 2.58.0 +- GNU Tar 1.35 - available by 'gtar' alias +- GNU Wget 1.24.5 +- gpg (GnuPG) 2.4.5 +- jq 1.7.1 +- OpenSSL 1.1.1w 11 Sep 2023 +- Packer 1.11.2 +- pkg-config 0.29.2 +- yq 4.44.3 +- zstd 1.5.6 + +### Tools +- AWS CLI 2.18.0 +- AWS SAM CLI 1.125.0 +- AWS Session Manager CLI 1.2.650.0 +- Azure CLI 2.64.0 +- Azure CLI (azure-devops) 1.0.1 +- Bicep CLI 0.30.23 +- Cmake 3.30.4 +- CodeQL Action Bundle 2.19.0 +- Fastlane 2.224.0 +- SwiftFormat 0.54.5 +- Xcbeautify 2.11.0 +- Xcode Command Line Tools 16.0.0.0.1.1724870825 +- Xcodes 1.5.0 + +### Linters +- SwiftLint 0.57.0 + +### Browsers +- Safari 18.0.1 (19619.1.26.111.11) +- SafariDriver 18.0.1 (19619.1.26.111.11) +- Google Chrome 129.0.6668.90 +- Google Chrome for Testing 129.0.6668.89 +- ChromeDriver 129.0.6668.89 +- Microsoft Edge 129.0.2792.79 +- Microsoft Edge WebDriver 129.0.2792.82 +- Mozilla Firefox 131.0 +- geckodriver 0.35.0 +- Selenium server 4.25.0 + +#### Environment variables +| Name | Value | +| --------------- | ------------------------------------- | +| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-x64 | +| EDGEWEBDRIVER | /usr/local/share/edge_driver | +| GECKOWEBDRIVER | /usr/local/opt/geckodriver/bin | + +### Java +| Version | Environment Variable | +| -------------------- | -------------------- | +| 8.0.422+5.1 | JAVA_HOME_8_X64 | +| 11.0.24+8 | JAVA_HOME_11_X64 | +| 17.0.12+7 | JAVA_HOME_17_X64 | +| 21.0.4+7.0 (default) | JAVA_HOME_21_X64 | + +### Cached Tools + +#### Ruby +- 3.0.7 +- 3.1.6 +- 3.2.5 +- 3.3.5 + +#### Python +- 3.9.20 +- 3.10.15 +- 3.11.9 +- 3.12.7 + +#### Node.js +- 18.20.4 +- 20.17.0 + +#### Go +- 1.21.13 +- 1.22.8 +- 1.23.2 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- Clippy 0.1.81 +- Rustfmt 1.7.1-stable + +### PowerShell Tools +- PowerShell 7.4.5 + +#### PowerShell Modules +- Az: 12.3.0 +- Pester: 5.6.1 +- PSScriptAnalyzer: 1.22.0 + +### Xcode +| Version | Build | Path | +| -------------- | -------- | ----------------------------------- | +| 16.1 (beta) | 16B5014f | /Applications/Xcode_16.1_beta_2.app | +| 16.0 | 16A242d | /Applications/Xcode_16.app | +| 15.4 (default) | 15F31d | /Applications/Xcode_15.4.app | +| 15.3 | 15E204a | /Applications/Xcode_15.3.app | +| 15.2 | 15C500b | /Applications/Xcode_15.2.app | +| 15.1 | 15C65 | /Applications/Xcode_15.1.app | +| 15.0.1 | 15A507 | /Applications/Xcode_15.0.1.app | +| 14.3.1 | 14E300c | /Applications/Xcode_14.3.1.app | + +#### Installed SDKs +| SDK | SDK Name | Xcode Version | +| ------------------------------------------------------- | --------------------------------------------- | ------------- | +| macOS 13.3 | macosx13.3 | 14.3.1 | +| macOS 14.0 | macosx14.0 | 15.0.1 | +| macOS 14.2 | macosx14.2 | 15.1, 15.2 | +| macOS 14.4 | macosx14.4 | 15.3 | +| macOS 14.5 | macosx14.5 | 15.4 | +| macOS 15.0 | macosx15.0 | 16.0 | +| macOS 15.1 | macosx15.1 | 16.1 | +| iOS 16.4 | iphoneos16.4 | 14.3.1 | +| iOS 17.0 | iphoneos17.0 | 15.0.1 | +| iOS 17.2 | iphoneos17.2 | 15.1, 15.2 | +| iOS 17.4 | iphoneos17.4 | 15.3 | +| iOS 17.5 | iphoneos17.5 | 15.4 | +| iOS 18.0 | iphoneos18.0 | 16.0 | +| iOS 18.1 | iphoneos18.1 | 16.1 | +| Simulator - iOS 16.4 | iphonesimulator16.4 | 14.3.1 | +| Simulator - iOS 17.0 | iphonesimulator17.0 | 15.0.1 | +| Simulator - iOS 17.2 | iphonesimulator17.2 | 15.1, 15.2 | +| Simulator - iOS 17.4 | iphonesimulator17.4 | 15.3 | +| Simulator - iOS 17.5 | iphonesimulator17.5 | 15.4 | +| Simulator - iOS 18.0 | iphonesimulator18.0 | 16.0 | +| Simulator - iOS 18.1 | iphonesimulator18.1 | 16.1 | +| tvOS 16.4 | appletvos16.4 | 14.3.1 | +| tvOS 17.0 | appletvos17.0 | 15.0.1 | +| tvOS 17.2 | appletvos17.2 | 15.1, 15.2 | +| tvOS 17.4 | appletvos17.4 | 15.3 | +| tvOS 17.5 | appletvos17.5 | 15.4 | +| tvOS 18.0 | appletvos18.0 | 16.0 | +| tvOS 18.1 | appletvos18.1 | 16.1 | +| Simulator - tvOS 16.4 | appletvsimulator16.4 | 14.3.1 | +| Simulator - tvOS 17.0 | appletvsimulator17.0 | 15.0.1 | +| Simulator - tvOS 17.2 | appletvsimulator17.2 | 15.1, 15.2 | +| Simulator - tvOS 17.4 | appletvsimulator17.4 | 15.3 | +| Simulator - tvOS 17.5 | appletvsimulator17.5 | 15.4 | +| Simulator - tvOS 18.0 | appletvsimulator18.0 | 16.0 | +| Simulator - tvOS 18.1 | appletvsimulator18.1 | 16.1 | +| watchOS 9.4 | watchos9.4 | 14.3.1 | +| watchOS 10.0 | watchos10.0 | 15.0.1 | +| watchOS 10.2 | watchos10.2 | 15.1, 15.2 | +| watchOS 10.4 | watchos10.4 | 15.3 | +| watchOS 10.5 | watchos10.5 | 15.4 | +| watchOS 11.0 | watchos11.0 | 16.0 | +| watchOS 11.1 | watchos11.1 | 16.1 | +| Simulator - watchOS 9.4 | watchsimulator9.4 | 14.3.1 | +| Simulator - watchOS 10.0 | watchsimulator10.0 | 15.0.1 | +| Simulator - watchOS 10.2 | watchsimulator10.2 | 15.1, 15.2 | +| Simulator - watchOS 10.4 | watchsimulator10.4 | 15.3 | +| Simulator - watchOS 10.5 | watchsimulator10.5 | 15.4 | +| Simulator - watchOS 11.0 | watchsimulator11.0 | 16.0 | +| Simulator - watchOS 11.1 | watchsimulator11.1 | 16.1 | +| Simulator - visionOS 1.0 | xrsimulator1.0 | 15.2 | +| visionOS 1.0 | xros1.0 | 15.2 | +| visionOS 1.1 | xros1.1 | 15.3 | +| Simulator - visionOS 1.1 | xrsimulator1.1 | 15.3 | +| visionOS 1.2 | xros1.2 | 15.4 | +| Simulator - visionOS 1.2 | xrsimulator1.2 | 15.4 | +| Simulator - visionOS 2.0 | xrsimulator2.0 | 16.0 | +| visionOS 2.0 | xros2.0 | 16.0 | +| Simulator - visionOS 2.1 | xrsimulator2.1 | 16.1 | +| visionOS 2.1 | xros2.1 | 16.1 | +| Asset Runtime SDK for macOS hosts targeting watchOS 9.4 | assetruntime.host.macosx.target.watchos9.4 | 14.3.1 | +| Asset Runtime SDK for macOS hosts targeting tvOS 16.4 | assetruntime.host.macosx.target.appletvos16.4 | 14.3.1 | +| Asset Runtime SDK for macOS hosts targeting iOS 16.4 | assetruntime.host.macosx.target.iphoneos16.4 | 14.3.1 | +| DriverKit 22.4 | driverkit22.4 | 14.3.1 | +| DriverKit 23.0 | driverkit23.0 | 15.0.1 | +| DriverKit 23.2 | driverkit23.2 | 15.1, 15.2 | +| DriverKit 23.4 | driverkit23.4 | 15.3 | +| DriverKit 23.5 | driverkit23.5 | 15.4 | +| DriverKit 24.0 | driverkit24.0 | 16.0 | +| DriverKit 24.1 | driverkit24.1 | 16.1 | + +#### Installed Simulators +| OS | Simulators | +| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| iOS 16.4 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 17.0 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 17.2 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 17.4 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| iOS 17.5 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| iOS 18.0 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone 16
iPhone 16 Plus
iPhone 16 Pro
iPhone 16 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| iOS 18.1 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone 16
iPhone 16 Plus
iPhone 16 Pro
iPhone 16 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| tvOS 16.4 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.0 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.2 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.4 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.5 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 18.0 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 18.1 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| watchOS 9.4 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Ultra (49mm) | +| watchOS 10.0 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 10.2 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 10.4 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 10.5 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 11.0 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 10 (42mm)
Apple Watch Series 10 (46mm)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 11.1 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 10 (42mm)
Apple Watch Series 10 (46mm)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | + +### Android +| Package Name | Version | +| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Command Line Tools | 11.0 | +| Android Emulator | 35.2.10 | +| Android SDK Build-tools | 35.0.0
34.0.0
33.0.2 33.0.3 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-34 (rev 3)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1)
android-33 (rev 3) | +| Android SDK Platform-Tools | 35.0.2 | +| Android Support Repository | 47.0.0 | +| CMake | 3.22.1 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264 (default)
27.1.12297006 | + +#### Environment variables +| Name | Value | +| ----------------------- | --------------------------------------------------- | +| ANDROID_HOME | /Users/runner/Library/Android/sdk | +| ANDROID_NDK | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_NDK_HOME | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_ROOT | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_SDK_ROOT | /Users/runner/Library/Android/sdk | + +### Miscellaneous + +#### Environment variables +| Name | Value | +| ----------------- | ----------------------------------------------------------------------------------------- | +| PARALLELS_DMG_URL | https://download.parallels.com/desktop/v20/20.0.1-55659/ParallelsDesktop-20.0.1-55659.dmg | + +##### Notes +``` +If you want to use Parallels Desktop you should download a package from URL stored in +PARALLELS_DMG_URL environment variable. A system extension is allowed for this version. +``` + diff --git a/images/macos/macos-14-arm64-Readme.md b/images/macos/macos-14-arm64-Readme.md new file mode 100644 index 0000000..4bede38 --- /dev/null +++ b/images/macos/macos-14-arm64-Readme.md @@ -0,0 +1,279 @@ +| Announcements | +|-| +| [[macOS] The macOS 12 Actions runner image will begin deprecation on 10/7/24 and will be fully unsupported by 12/3/24 for GitHub and ADO](https://github.com/actions/runner-images/issues/10721) | +| [[macOS] Support policy changes; Xcode 14 and 16 will be removed from macOS 14 on October 28](https://github.com/actions/runner-images/issues/10703) | +| [macOS 15 (Sequoia) is now available as a public beta in GitHub Actions](https://github.com/actions/runner-images/issues/10686) | +| [[Macos 13 and 14] Android NDK versions <=25 will be removed from images on October 07,2024](https://github.com/actions/runner-images/issues/10614) | +| [[Macos 13 and 14] Go version 1.20.0 will be removed on October 07,2024.](https://github.com/actions/runner-images/issues/10612) | +*** +# macOS 14 +- OS Version: macOS 14.7 (23H124) +- Kernel Version: Darwin 23.6.0 +- Image Version: 20241007.259 + +## Installed Software + +### Language and Runtime +- .NET Core SDK: 7.0.102, 7.0.202, 7.0.306, 7.0.410, 8.0.101, 8.0.204, 8.0.303, 8.0.402 +- Bash 3.2.57(1)-release +- Clang/LLVM 14.0.3 +- Clang/LLVM (Homebrew) 15.0.7 - available on `$(brew --prefix llvm@15)/bin/clang` +- GCC 12 (Homebrew GCC 12.4.0) - available by `gcc-12` alias +- GCC 13 (Homebrew GCC 13.3.0) - available by `gcc-13` alias +- GCC 14 (Homebrew GCC 14.2.0) - available by `gcc-14` alias +- GNU Fortran 12 (Homebrew GCC 12.4.0) - available by `gfortran-12` alias +- GNU Fortran 13 (Homebrew GCC 13.3.0) - available by `gfortran-13` alias +- GNU Fortran 14 (Homebrew GCC 14.2.0) - available by `gfortran-14` alias +- Kotlin 2.0.20-release-360 +- Mono 6.12.0.188 +- Node.js 20.18.0 +- Perl 5.38.2 +- Python3 3.12.7 +- Ruby 3.0.7p220 + +### Package Management +- Bundler 2.5.21 +- Carthage 0.40.0 +- CocoaPods 1.15.2 +- Homebrew 4.4.0 +- NPM 10.8.2 +- NuGet 6.3.1.1 +- Pip3 24.2 (python 3.12) +- Pipx 1.7.1 +- RubyGems 3.5.21 +- Yarn 1.22.22 + +### Project Management +- Apache Ant 1.10.15 +- Apache Maven 3.9.9 +- Gradle 8.10.2 + +### Utilities +- 7-Zip 17.05 +- aria2 1.37.0 +- azcopy 10.26.0 +- bazel 7.3.2 +- bazelisk 1.22.0 +- bsdtar 3.5.3 - available by 'tar' alias +- Curl 8.7.1 +- Git 2.46.2 +- Git LFS 3.5.1 +- GitHub CLI 2.58.0 +- GNU Tar 1.35 - available by 'gtar' alias +- GNU Wget 1.24.5 +- gpg (GnuPG) 2.4.5 +- jq 1.7.1 +- OpenSSL 1.1.1w 11 Sep 2023 +- Packer 1.11.2 +- pkg-config 0.29.2 +- yq 4.44.3 +- zstd 1.5.6 + +### Tools +- AWS CLI 2.18.0 +- AWS SAM CLI 1.125.0 +- AWS Session Manager CLI 1.2.650.0 +- Azure CLI 2.64.0 +- Azure CLI (azure-devops) 1.0.1 +- Bicep CLI 0.30.23 +- Cmake 3.30.4 +- CodeQL Action Bundle 2.19.0 +- Fastlane 2.224.0 +- SwiftFormat 0.54.5 +- Xcbeautify 2.11.0 +- Xcode Command Line Tools 16.0.0.0.1.1724870825 +- Xcodes 1.5.0 + +### Linters + +### Browsers +- Safari 18.0.1 (19619.1.26.111.11) +- SafariDriver 18.0.1 (19619.1.26.111.11) +- Google Chrome 129.0.6668.90 +- Google Chrome for Testing 129.0.6668.89 +- ChromeDriver 129.0.6668.89 +- Selenium server 4.25.0 + +#### Environment variables +| Name | Value | +| --------------- | --------------------------------------- | +| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-arm64 | +| EDGEWEBDRIVER | | +| GECKOWEBDRIVER | | + +### Java +| Version | Environment Variable | +| -------------------- | -------------------- | +| 11.0.24+8 | JAVA_HOME_11_arm64 | +| 17.0.12+7 | JAVA_HOME_17_arm64 | +| 21.0.4+7.0 (default) | JAVA_HOME_21_arm64 | + +### Cached Tools + +#### Python +- 3.9.13 +- 3.10.11 +- 3.11.9 +- 3.12.7 + +#### Node.js +- 18.20.4 +- 20.17.0 + +#### Go +- 1.21.13 +- 1.22.8 +- 1.23.2 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- Clippy 0.1.81 +- Rustfmt 1.7.1-stable + +### PowerShell Tools +- PowerShell 7.4.5 + +#### PowerShell Modules +- Az: 12.3.0 +- Pester: 5.6.1 +- PSScriptAnalyzer: 1.22.0 + +### Xcode +| Version | Build | Path | +| -------------- | -------- | ----------------------------------- | +| 16.1 (beta) | 16B5014f | /Applications/Xcode_16.1_beta_2.app | +| 16.0 | 16A242d | /Applications/Xcode_16.app | +| 15.4 (default) | 15F31d | /Applications/Xcode_15.4.app | +| 15.3 | 15E204a | /Applications/Xcode_15.3.app | +| 15.2 | 15C500b | /Applications/Xcode_15.2.app | +| 15.1 | 15C65 | /Applications/Xcode_15.1.app | +| 15.0.1 | 15A507 | /Applications/Xcode_15.0.1.app | +| 14.3.1 | 14E300c | /Applications/Xcode_14.3.1.app | + +#### Installed SDKs +| SDK | SDK Name | Xcode Version | +| ------------------------------------------------------- | --------------------------------------------- | ------------- | +| macOS 13.3 | macosx13.3 | 14.3.1 | +| macOS 14.0 | macosx14.0 | 15.0.1 | +| macOS 14.2 | macosx14.2 | 15.1, 15.2 | +| macOS 14.4 | macosx14.4 | 15.3 | +| macOS 14.5 | macosx14.5 | 15.4 | +| macOS 15.0 | macosx15.0 | 16.0 | +| macOS 15.1 | macosx15.1 | 16.1 | +| iOS 16.4 | iphoneos16.4 | 14.3.1 | +| iOS 17.0 | iphoneos17.0 | 15.0.1 | +| iOS 17.2 | iphoneos17.2 | 15.1, 15.2 | +| iOS 17.4 | iphoneos17.4 | 15.3 | +| iOS 17.5 | iphoneos17.5 | 15.4 | +| iOS 18.0 | iphoneos18.0 | 16.0 | +| iOS 18.1 | iphoneos18.1 | 16.1 | +| Simulator - iOS 16.4 | iphonesimulator16.4 | 14.3.1 | +| Simulator - iOS 17.0 | iphonesimulator17.0 | 15.0.1 | +| Simulator - iOS 17.2 | iphonesimulator17.2 | 15.1, 15.2 | +| Simulator - iOS 17.4 | iphonesimulator17.4 | 15.3 | +| Simulator - iOS 17.5 | iphonesimulator17.5 | 15.4 | +| Simulator - iOS 18.0 | iphonesimulator18.0 | 16.0 | +| Simulator - iOS 18.1 | iphonesimulator18.1 | 16.1 | +| tvOS 16.4 | appletvos16.4 | 14.3.1 | +| tvOS 17.0 | appletvos17.0 | 15.0.1 | +| tvOS 17.2 | appletvos17.2 | 15.1, 15.2 | +| tvOS 17.4 | appletvos17.4 | 15.3 | +| tvOS 17.5 | appletvos17.5 | 15.4 | +| tvOS 18.0 | appletvos18.0 | 16.0 | +| tvOS 18.1 | appletvos18.1 | 16.1 | +| Simulator - tvOS 16.4 | appletvsimulator16.4 | 14.3.1 | +| Simulator - tvOS 17.0 | appletvsimulator17.0 | 15.0.1 | +| Simulator - tvOS 17.2 | appletvsimulator17.2 | 15.1, 15.2 | +| Simulator - tvOS 17.4 | appletvsimulator17.4 | 15.3 | +| Simulator - tvOS 17.5 | appletvsimulator17.5 | 15.4 | +| Simulator - tvOS 18.0 | appletvsimulator18.0 | 16.0 | +| Simulator - tvOS 18.1 | appletvsimulator18.1 | 16.1 | +| watchOS 9.4 | watchos9.4 | 14.3.1 | +| watchOS 10.0 | watchos10.0 | 15.0.1 | +| watchOS 10.2 | watchos10.2 | 15.1, 15.2 | +| watchOS 10.4 | watchos10.4 | 15.3 | +| watchOS 10.5 | watchos10.5 | 15.4 | +| watchOS 11.0 | watchos11.0 | 16.0 | +| watchOS 11.1 | watchos11.1 | 16.1 | +| Simulator - watchOS 9.4 | watchsimulator9.4 | 14.3.1 | +| Simulator - watchOS 10.0 | watchsimulator10.0 | 15.0.1 | +| Simulator - watchOS 10.2 | watchsimulator10.2 | 15.1, 15.2 | +| Simulator - watchOS 10.4 | watchsimulator10.4 | 15.3 | +| Simulator - watchOS 10.5 | watchsimulator10.5 | 15.4 | +| Simulator - watchOS 11.0 | watchsimulator11.0 | 16.0 | +| Simulator - watchOS 11.1 | watchsimulator11.1 | 16.1 | +| visionOS 1.0 | xros1.0 | 15.2 | +| Simulator - visionOS 1.0 | xrsimulator1.0 | 15.2 | +| visionOS 1.1 | xros1.1 | 15.3 | +| Simulator - visionOS 1.1 | xrsimulator1.1 | 15.3 | +| Simulator - visionOS 1.2 | xrsimulator1.2 | 15.4 | +| visionOS 1.2 | xros1.2 | 15.4 | +| Simulator - visionOS 2.0 | xrsimulator2.0 | 16.0 | +| visionOS 2.0 | xros2.0 | 16.0 | +| visionOS 2.1 | xros2.1 | 16.1 | +| Simulator - visionOS 2.1 | xrsimulator2.1 | 16.1 | +| Asset Runtime SDK for macOS hosts targeting watchOS 9.4 | assetruntime.host.macosx.target.watchos9.4 | 14.3.1 | +| Asset Runtime SDK for macOS hosts targeting tvOS 16.4 | assetruntime.host.macosx.target.appletvos16.4 | 14.3.1 | +| Asset Runtime SDK for macOS hosts targeting iOS 16.4 | assetruntime.host.macosx.target.iphoneos16.4 | 14.3.1 | +| DriverKit 22.4 | driverkit22.4 | 14.3.1 | +| DriverKit 23.0 | driverkit23.0 | 15.0.1 | +| DriverKit 23.2 | driverkit23.2 | 15.1, 15.2 | +| DriverKit 23.4 | driverkit23.4 | 15.3 | +| DriverKit 23.5 | driverkit23.5 | 15.4 | +| DriverKit 24.0 | driverkit24.0 | 16.0 | +| DriverKit 24.1 | driverkit24.1 | 16.1 | + +#### Installed Simulators +| OS | Simulators | +| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| iOS 16.4 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 17.0 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 17.2 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation) | +| iOS 17.4 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| iOS 17.5 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| iOS 18.0 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone 16
iPhone 16 Plus
iPhone 16 Pro
iPhone 16 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| iOS 18.1 | iPhone 14
iPhone 14 Plus
iPhone 14 Pro
iPhone 14 Pro Max
iPhone 15
iPhone 15 Plus
iPhone 15 Pro
iPhone 15 Pro Max
iPhone 16
iPhone 16 Plus
iPhone 16 Pro
iPhone 16 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air (5th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro (11-inch) (4th generation)
iPad Pro (12.9-inch) (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| tvOS 16.4 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.0 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.2 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.4 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 17.5 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 18.0 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 18.1 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| watchOS 9.4 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Ultra (49mm) | +| watchOS 10.0 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 10.2 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 10.4 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 10.5 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 11.0 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 10 (42mm)
Apple Watch Series 10 (46mm)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 11.1 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 10 (42mm)
Apple Watch Series 10 (46mm)
Apple Watch Series 5 (40mm)
Apple Watch Series 5 (44mm)
Apple Watch Series 6 (40mm)
Apple Watch Series 6 (44mm)
Apple Watch Series 7 (41mm)
Apple Watch Series 7 (45mm)
Apple Watch Series 8 (41mm)
Apple Watch Series 8 (45mm)
Apple Watch Series 9 (41mm)
Apple Watch Series 9 (45mm)
Apple Watch Ultra (49mm)
Apple Watch Ultra 2 (49mm) | + +### Android +| Package Name | Version | +| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Command Line Tools | 11.0 | +| Android Emulator | 35.2.10 | +| Android SDK Build-tools | 35.0.0
34.0.0
33.0.2 33.0.3 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-34 (rev 3)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1)
android-33 (rev 3) | +| Android SDK Platform-Tools | 35.0.2 | +| Android Support Repository | 47.0.0 | +| CMake | 3.22.1 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264 (default)
27.1.12297006 | + +#### Environment variables +| Name | Value | +| ----------------------- | --------------------------------------------------- | +| ANDROID_HOME | /Users/runner/Library/Android/sdk | +| ANDROID_NDK | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_NDK_HOME | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_ROOT | /Users/runner/Library/Android/sdk/ndk/26.3.11579264 | +| ANDROID_SDK_ROOT | /Users/runner/Library/Android/sdk | + diff --git a/images/macos/macos-15-Readme.md b/images/macos/macos-15-Readme.md new file mode 100644 index 0000000..a342aae --- /dev/null +++ b/images/macos/macos-15-Readme.md @@ -0,0 +1,218 @@ +| Announcements | +|-| +| [[macOS] The macOS 12 Actions runner image will begin deprecation on 10/7/24 and will be fully unsupported by 12/3/24 for GitHub and ADO](https://github.com/actions/runner-images/issues/10721) | +| [[macOS] Support policy changes; Xcode 14 and 16 will be removed from macOS 14 on October 28](https://github.com/actions/runner-images/issues/10703) | +| [macOS 15 (Sequoia) is now available as a public beta in GitHub Actions](https://github.com/actions/runner-images/issues/10686) | +| [[Macos 13 and 14] Android NDK versions <=25 will be removed from images on October 07,2024](https://github.com/actions/runner-images/issues/10614) | +| [[Macos 13 and 14] Go version 1.20.0 will be removed on October 07,2024.](https://github.com/actions/runner-images/issues/10612) | +*** +# macOS 15 +- OS Version: macOS 15.0 (24A335) +- Kernel Version: Darwin 24.0.0 +- Image Version: 20241007.173 + +## Installed Software + +### Language and Runtime +- .NET Core SDK: 8.0.101, 8.0.204, 8.0.303, 8.0.402 +- Bash 3.2.57(1)-release +- Clang/LLVM 16.0.0 +- Clang/LLVM (Homebrew) 18.1.8 - available on `$(brew --prefix llvm@18)/bin/clang` +- GCC 12 (Homebrew GCC 12.4.0) - available by `gcc-12` alias +- GCC 13 (Homebrew GCC 13.3.0) - available by `gcc-13` alias +- GCC 14 (Homebrew GCC 14.2.0) - available by `gcc-14` alias +- GNU Fortran 12 (Homebrew GCC 12.4.0) - available by `gfortran-12` alias +- GNU Fortran 13 (Homebrew GCC 13.3.0) - available by `gfortran-13` alias +- GNU Fortran 14 (Homebrew GCC 14.2.0) - available by `gfortran-14` alias +- Kotlin 2.0.20-release-360 +- Node.js 22.9.0 +- Perl 5.38.2 +- PHP 8.3.12 +- Python3 3.12.7 +- Ruby 3.3.5 + +### Package Management +- Bundler 2.5.21 +- Carthage 0.40.0 +- CocoaPods 1.15.2 +- Composer 2.8.1 +- Homebrew 4.4.0 +- NPM 10.8.3 +- Pip3 24.2 (python 3.12) +- Pipx 1.7.1 +- RubyGems 3.5.21 +- Yarn 1.22.22 + +### Project Management +- Apache Ant 1.10.15 +- Apache Maven 3.9.9 +- Gradle 8.10.2 + +### Utilities +- 7-Zip 17.05 +- aria2 1.37.0 +- azcopy 10.26.0 +- bazel 7.3.2 +- bazelisk 1.22.0 +- bsdtar 3.5.3 - available by 'tar' alias +- Curl 8.10.1 +- Git 2.46.2 +- Git LFS 3.5.1 +- GitHub CLI 2.58.0 +- GNU Tar 1.35 - available by 'gtar' alias +- GNU Wget 1.24.5 +- gpg (GnuPG) 2.4.5 +- jq 1.7.1 +- OpenSSL 1.1.1w 11 Sep 2023 +- Packer 1.11.2 +- pkg-config 0.29.2 +- yq 4.44.3 +- zstd 1.5.6 + +### Tools +- AWS CLI 2.18.0 +- AWS SAM CLI 1.125.0 +- AWS Session Manager CLI 1.2.650.0 +- Azure CLI 2.64.0 +- Azure CLI (azure-devops) 1.0.1 +- Bicep CLI 0.30.23 +- Cmake 3.30.4 +- CodeQL Action Bundle 2.19.0 +- Fastlane 2.224.0 +- SwiftFormat 0.54.5 +- Xcbeautify 2.11.0 +- Xcode Command Line Tools 16.0.0.0.1.1724870825 +- Xcodes 1.5.0 + +### Linters +- SwiftLint 0.57.0 + +### Browsers +- Safari 18.0 (20619.1.26.31.6) +- SafariDriver 18.0 (20619.1.26.31.6) +- Google Chrome 129.0.6668.90 +- Google Chrome for Testing 129.0.6668.89 +- ChromeDriver 129.0.6668.89 +- Microsoft Edge 129.0.2792.79 +- Microsoft Edge WebDriver 129.0.2792.82 +- Mozilla Firefox 131.0 +- geckodriver 0.35.0 +- Selenium server 4.25.0 + +#### Environment variables +| Name | Value | +| --------------- | ------------------------------------- | +| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-x64 | +| EDGEWEBDRIVER | /usr/local/share/edge_driver | +| GECKOWEBDRIVER | /usr/local/opt/geckodriver/bin | + +### Java +| Version | Environment Variable | +| -------------------- | -------------------- | +| 11.0.24+8 | JAVA_HOME_11_X64 | +| 17.0.12+7 | JAVA_HOME_17_X64 | +| 21.0.4+7.0 (default) | JAVA_HOME_21_X64 | + +### Cached Tools + +#### Ruby +- 3.1.6 +- 3.2.5 + +#### Python +- 3.9.20 +- 3.10.15 +- 3.11.9 +- 3.12.7 + +#### Node.js +- 18.20.4 +- 20.17.0 + +#### Go +- 1.21.13 +- 1.22.8 +- 1.23.2 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- Clippy 0.1.81 +- Rustfmt 1.7.1-stable + +### PowerShell Tools +- PowerShell 7.4.5 + +#### PowerShell Modules +- Az: 12.3.0 +- Pester: 5.6.1 +- PSScriptAnalyzer: 1.22.0 + +### Xcode +| Version | Build | Path | +| -------------- | -------- | ----------------------------------- | +| 16.1 (beta) | 16B5014f | /Applications/Xcode_16.1_beta_2.app | +| 16.0 (default) | 16A242d | /Applications/Xcode_16.app | + +#### Installed SDKs +| SDK | SDK Name | Xcode Version | +| ------------------------ | -------------------- | ------------- | +| macOS 15.0 | macosx15.0 | 16.0 | +| macOS 15.1 | macosx15.1 | 16.1 | +| iOS 18.0 | iphoneos18.0 | 16.0 | +| iOS 18.1 | iphoneos18.1 | 16.1 | +| Simulator - iOS 18.0 | iphonesimulator18.0 | 16.0 | +| Simulator - iOS 18.1 | iphonesimulator18.1 | 16.1 | +| tvOS 18.0 | appletvos18.0 | 16.0 | +| tvOS 18.1 | appletvos18.1 | 16.1 | +| Simulator - tvOS 18.0 | appletvsimulator18.0 | 16.0 | +| Simulator - tvOS 18.1 | appletvsimulator18.1 | 16.1 | +| watchOS 11.0 | watchos11.0 | 16.0 | +| watchOS 11.1 | watchos11.1 | 16.1 | +| Simulator - watchOS 11.0 | watchsimulator11.0 | 16.0 | +| Simulator - watchOS 11.1 | watchsimulator11.1 | 16.1 | +| visionOS 2.0 | xros2.0 | 16.0 | +| Simulator - visionOS 2.0 | xrsimulator2.0 | 16.0 | +| Simulator - visionOS 2.1 | xrsimulator2.1 | 16.1 | +| visionOS 2.1 | xros2.1 | 16.1 | +| DriverKit 24.0 | driverkit24.0 | 16.0 | +| DriverKit 24.1 | driverkit24.1 | 16.1 | + +#### Installed Simulators +| OS | Simulators | +| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| iOS 18.0 | iPhone 16
iPhone 16 Plus
iPhone 16 Pro
iPhone 16 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| iOS 18.1 | iPhone 16
iPhone 16 Plus
iPhone 16 Pro
iPhone 16 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| tvOS 18.0 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 18.1 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| watchOS 11.0 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 10 (42mm)
Apple Watch Series 10 (46mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 11.1 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 10 (42mm)
Apple Watch Series 10 (46mm)
Apple Watch Ultra 2 (49mm) | + +### Android +| Package Name | Version | +| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Emulator | 35.2.10 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1) | +| Android SDK Platform-Tools | 35.0.2 | +| Android Support Repository | 47.0.0 | +| CMake | 3.22.1 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264
27.1.12297006 (default) | + +#### Environment variables +| Name | Value | +| ----------------------- | --------------------------------------------------- | +| ANDROID_HOME | /Users/runner/Library/Android/sdk | +| ANDROID_NDK | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_HOME | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_ROOT | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_SDK_ROOT | /Users/runner/Library/Android/sdk | + +### Miscellaneous + diff --git a/images/macos/macos-15-arm64-Readme.md b/images/macos/macos-15-arm64-Readme.md new file mode 100644 index 0000000..fc52ccd --- /dev/null +++ b/images/macos/macos-15-arm64-Readme.md @@ -0,0 +1,205 @@ +| Announcements | +|-| +| [[macOS] The macOS 12 Actions runner image will begin deprecation on 10/7/24 and will be fully unsupported by 12/3/24 for GitHub and ADO](https://github.com/actions/runner-images/issues/10721) | +| [[macOS] Support policy changes; Xcode 14 and 16 will be removed from macOS 14 on October 28](https://github.com/actions/runner-images/issues/10703) | +| [macOS 15 (Sequoia) is now available as a public beta in GitHub Actions](https://github.com/actions/runner-images/issues/10686) | +| [[Macos 13 and 14] Android NDK versions <=25 will be removed from images on October 07,2024](https://github.com/actions/runner-images/issues/10614) | +| [[Macos 13 and 14] Go version 1.20.0 will be removed on October 07,2024.](https://github.com/actions/runner-images/issues/10612) | +*** +# macOS 15 +- OS Version: macOS 15.0 (24A335) +- Kernel Version: Darwin 24.0.0 +- Image Version: 20241007.159 + +## Installed Software + +### Language and Runtime +- .NET Core SDK: 8.0.101, 8.0.204, 8.0.303, 8.0.402 +- Bash 3.2.57(1)-release +- Clang/LLVM 16.0.0 +- Clang/LLVM (Homebrew) 18.1.8 - available on `$(brew --prefix llvm@18)/bin/clang` +- GCC 12 (Homebrew GCC 12.4.0) - available by `gcc-12` alias +- GCC 13 (Homebrew GCC 13.3.0) - available by `gcc-13` alias +- GCC 14 (Homebrew GCC 14.2.0) - available by `gcc-14` alias +- GNU Fortran 12 (Homebrew GCC 12.4.0) - available by `gfortran-12` alias +- GNU Fortran 13 (Homebrew GCC 13.3.0) - available by `gfortran-13` alias +- GNU Fortran 14 (Homebrew GCC 14.2.0) - available by `gfortran-14` alias +- Kotlin 2.0.20-release-360 +- Node.js 22.9.0 +- Perl 5.38.2 +- Python3 3.12.7 +- Ruby 3.3.5 + +### Package Management +- Bundler 2.5.21 +- Carthage 0.40.0 +- CocoaPods 1.15.2 +- Homebrew 4.4.0 +- NPM 10.8.3 +- Pip3 24.2 (python 3.12) +- Pipx 1.7.1 +- RubyGems 3.5.21 +- Yarn 1.22.22 + +### Project Management +- Apache Ant 1.10.15 +- Apache Maven 3.9.9 +- Gradle 8.10.2 + +### Utilities +- 7-Zip 17.05 +- aria2 1.37.0 +- azcopy 10.26.0 +- bazel 7.3.2 +- bazelisk 1.22.0 +- bsdtar 3.5.3 - available by 'tar' alias +- Curl 8.7.1 +- Git 2.46.2 +- Git LFS 3.5.1 +- GitHub CLI 2.58.0 +- GNU Tar 1.35 - available by 'gtar' alias +- GNU Wget 1.24.5 +- gpg (GnuPG) 2.4.5 +- jq 1.7.1 +- OpenSSL 1.1.1w 11 Sep 2023 +- Packer 1.11.2 +- pkg-config 0.29.2 +- yq 4.44.3 +- zstd 1.5.6 + +### Tools +- AWS CLI 2.18.0 +- AWS SAM CLI 1.125.0 +- AWS Session Manager CLI 1.2.650.0 +- Azure CLI 2.64.0 +- Azure CLI (azure-devops) 1.0.1 +- Bicep CLI 0.30.23 +- Cmake 3.30.4 +- CodeQL Action Bundle 2.19.0 +- Fastlane 2.224.0 +- SwiftFormat 0.54.5 +- Xcbeautify 2.11.0 +- Xcode Command Line Tools 16.0.0.0.1.1724870825 +- Xcodes 1.5.0 + +### Linters + +### Browsers +- Safari 18.0 (20619.1.26.31.6) +- SafariDriver 18.0 (20619.1.26.31.6) +- Google Chrome 129.0.6668.90 +- Google Chrome for Testing 129.0.6668.89 +- ChromeDriver 129.0.6668.89 +- Selenium server 4.25.0 + +#### Environment variables +| Name | Value | +| --------------- | --------------------------------------- | +| CHROMEWEBDRIVER | /usr/local/share/chromedriver-mac-arm64 | +| EDGEWEBDRIVER | | +| GECKOWEBDRIVER | | + +### Java +| Version | Environment Variable | +| -------------------- | -------------------- | +| 11.0.24+8 | JAVA_HOME_11_arm64 | +| 17.0.12+7 | JAVA_HOME_17_arm64 | +| 21.0.4+7.0 (default) | JAVA_HOME_21_arm64 | + +### Cached Tools + +#### Python +- 3.11.9 +- 3.12.7 + +#### Node.js +- 18.20.4 +- 20.17.0 + +#### Go +- 1.21.13 +- 1.22.8 +- 1.23.2 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- Clippy 0.1.81 +- Rustfmt 1.7.1-stable + +### PowerShell Tools +- PowerShell 7.4.5 + +#### PowerShell Modules +- Az: 12.3.0 +- Pester: 5.6.1 +- PSScriptAnalyzer: 1.22.0 + +### Xcode +| Version | Build | Path | +| -------------- | -------- | ----------------------------------- | +| 16.1 (beta) | 16B5014f | /Applications/Xcode_16.1_beta_2.app | +| 16.0 (default) | 16A242d | /Applications/Xcode_16.app | + +#### Installed SDKs +| SDK | SDK Name | Xcode Version | +| ------------------------ | -------------------- | ------------- | +| macOS 15.0 | macosx15.0 | 16.0 | +| macOS 15.1 | macosx15.1 | 16.1 | +| iOS 18.0 | iphoneos18.0 | 16.0 | +| iOS 18.1 | iphoneos18.1 | 16.1 | +| Simulator - iOS 18.0 | iphonesimulator18.0 | 16.0 | +| Simulator - iOS 18.1 | iphonesimulator18.1 | 16.1 | +| tvOS 18.0 | appletvos18.0 | 16.0 | +| tvOS 18.1 | appletvos18.1 | 16.1 | +| Simulator - tvOS 18.0 | appletvsimulator18.0 | 16.0 | +| Simulator - tvOS 18.1 | appletvsimulator18.1 | 16.1 | +| watchOS 11.0 | watchos11.0 | 16.0 | +| watchOS 11.1 | watchos11.1 | 16.1 | +| Simulator - watchOS 11.0 | watchsimulator11.0 | 16.0 | +| Simulator - watchOS 11.1 | watchsimulator11.1 | 16.1 | +| visionOS 2.0 | xros2.0 | 16.0 | +| Simulator - visionOS 2.0 | xrsimulator2.0 | 16.0 | +| Simulator - visionOS 2.1 | xrsimulator2.1 | 16.1 | +| visionOS 2.1 | xros2.1 | 16.1 | +| DriverKit 24.0 | driverkit24.0 | 16.0 | +| DriverKit 24.1 | driverkit24.1 | 16.1 | + +#### Installed Simulators +| OS | Simulators | +| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| iOS 18.0 | iPhone 16
iPhone 16 Plus
iPhone 16 Pro
iPhone 16 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| iOS 18.1 | iPhone 16
iPhone 16 Plus
iPhone 16 Pro
iPhone 16 Pro Max
iPhone SE (3rd generation)
iPad (10th generation)
iPad Air 11-inch (M2)
iPad Air 13-inch (M2)
iPad mini (6th generation)
iPad Pro 11-inch (M4)
iPad Pro 13-inch (M4) | +| tvOS 18.0 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| tvOS 18.1 | Apple TV
Apple TV 4K (3rd generation)
Apple TV 4K (3rd generation) (at 1080p) | +| watchOS 11.0 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 10 (42mm)
Apple Watch Series 10 (46mm)
Apple Watch Ultra 2 (49mm) | +| watchOS 11.1 | Apple Watch SE (40mm) (2nd generation)
Apple Watch SE (44mm) (2nd generation)
Apple Watch Series 10 (42mm)
Apple Watch Series 10 (46mm)
Apple Watch Ultra 2 (49mm) | +| visionOS 2.0 | Apple Vision Pro | +| visionOS 2.1 | Apple Vision Pro | + +### Android +| Package Name | Version | +| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Emulator | 35.2.10 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1) | +| Android SDK Platform-Tools | 35.0.2 | +| Android Support Repository | 47.0.0 | +| CMake | 3.22.1 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264
27.1.12297006 (default) | + +#### Environment variables +| Name | Value | +| ----------------------- | --------------------------------------------------- | +| ANDROID_HOME | /Users/runner/Library/Android/sdk | +| ANDROID_NDK | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_HOME | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_LATEST_HOME | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_ROOT | /Users/runner/Library/Android/sdk/ndk/27.1.12297006 | +| ANDROID_SDK_ROOT | /Users/runner/Library/Android/sdk | + diff --git a/images/macos/scripts/build/Configure-Toolset.ps1 b/images/macos/scripts/build/Configure-Toolset.ps1 new file mode 100644 index 0000000..4580d02 --- /dev/null +++ b/images/macos/scripts/build/Configure-Toolset.ps1 @@ -0,0 +1,44 @@ +################################################################################ +## File: Configure-Toolset.ps1 +## Team: CI-Build +## Desc: Configure toolset +################################################################################ + +Import-Module "~/image-generation/helpers/Common.Helpers.psm1" + +function Get-ToolsetToolFullPath { + param + ( + [Parameter(Mandatory)] [string] $ToolName, + [Parameter(Mandatory)] [string] $ToolVersion, + [Parameter(Mandatory)] [string] $ToolArchitecture + ) + + $toolPath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath $toolName + $toolPathVersion = Join-Path -Path $toolPath -ChildPath $toolVersion + $foundVersion = Get-Item $toolPathVersion | Sort-Object -Property {[version]$_.name} -Descending | Select-Object -First 1 + $installationDir = Join-Path -Path $foundVersion -ChildPath $toolArchitecture + return $installationDir +} + +$arch = Get-Architecture +$toolcache = (Get-ToolsetContent).toolcache + +foreach ($tool in $toolcache) { + $toolName = $tool.name + $toolEnvironment = $tool.arch.$arch.variable_template + + if (-not $toolEnvironment) { + continue + } + + foreach ($toolVersion in $tool.arch.$arch.versions) { + Write-Host "Set $toolName $toolVersion environment variable..." + $toolPath = Get-ToolsetToolFullPath -ToolName $toolName -ToolVersion $toolVersion -ToolArchitecture $arch + $envName = $toolEnvironment -f $toolVersion.split(".") + + # Add environment variable name=value + $envVar = "export {0}={1}" -f $envName, $toolPath + Add-Content -Path "${env:HOME}/.bashrc" -Value $envVar + } +} diff --git a/images/macos/scripts/build/Configure-Xcode-Simulators.ps1 b/images/macos/scripts/build/Configure-Xcode-Simulators.ps1 new file mode 100644 index 0000000..d4bcd8c --- /dev/null +++ b/images/macos/scripts/build/Configure-Xcode-Simulators.ps1 @@ -0,0 +1,68 @@ +################################################################################ +## File: Configure-Xcode-Simulators.ps1 +## Team: CI-Build +## Desc: CHeck and remove duplicate simulators +################################################################################ + +Import-Module "~/image-generation/helpers/Common.Helpers.psm1" +Import-Module "~/image-generation/helpers/Xcode.Helpers.psm1" +$arch = Get-Architecture +$xcodeVersions = (Get-ToolsetContent).xcode.${arch}.versions + +# Switch to each Xcode version +foreach ($xcodeVersion in $xcodeVersions.link) { + write-host "Switching to Xcode $xcodeVersion" + Switch-Xcode -Version $XcodeVersion + + # Make object of all simulators + $devicesList = $(xcrun simctl list -j devices | ConvertFrom-Json) + $devicesObject = [System.Collections.ArrayList]@() + foreach ($runtime in $devicesList.devices.psobject.Properties.name) { + foreach ($device in $devicesList.devices.$runtime) { + $devicesObject += [PSCustomObject]@{ + runtime = $runtime + DeviceName = $($device.name) + DeviceId = $($device.udid) + DeviceCreationTime = (Get-Item $HOME/Library/Developer/CoreSimulator/Devices/$($device.udid)).CreationTime + } + } + } + + # Remove duplicates + foreach ($simRuntume in $devicesObject.runtime | Sort-Object -Unique) { + [System.Collections.ArrayList]$sameRuntimeDevices = [array]$($devicesObject | Where-Object {$_.runtime -eq $simRuntume} | Sort-Object -Property DeviceName) + Write-Host "///////////////////////////////////////////////////////////////////" + Write-Host "// Checking for duplicates in $simRuntume " + $devicesAsHashTable = $sameRuntimeDevices | Group-Object -Property DeviceName -AsHashTable -AsString + foreach ($key in $devicesAsHashTable.Keys) { + if ( $devicesAsHashTable[$key].count -gt 1) { + Write-Host "// Duplicates for $key - $($devicesAsHashTable[$key].count)" + } + } + Write-Host "///////////////////////////////////////////////////////////////////" + for ($i = 0; $i -lt $sameRuntimeDevices.Count; $i++) { + if ( [string]::IsNullOrEmpty($($sameRuntimeDevices[$i+1].DeviceName)) ){ + Write-Host "No more devices to compare in $simRuntume" + Write-Host "-------------------------------------------------------------------" + continue + } + Write-Host "$($sameRuntimeDevices[$i].DeviceName) - DeviceId $($sameRuntimeDevices[$i].DeviceId) comparing with" + Write-Host "$($sameRuntimeDevices[$i+1].DeviceName) - DeviceId $($sameRuntimeDevices[$i+1].DeviceId)" + Write-Host "-------------------------------------------------------------------" + if ($sameRuntimeDevices[$i].DeviceName -eq $sameRuntimeDevices[$i+1].DeviceName) { + write-host "*******************************************************************" + write-host "** Duplicate found" + if ($sameRuntimeDevices[$i].DeviceCreationTime -lt $sameRuntimeDevices[$i+1].DeviceCreationTime) { + Write-Host "** will be removed $($sameRuntimeDevices[$i+1].DeviceName) with id $($sameRuntimeDevices[$i+1].DeviceId)" + xcrun simctl delete $sameRuntimeDevices[$i+1].DeviceId + $sameRuntimeDevices.RemoveAt($i+1) + } else { + Write-Host "** will be removed $($sameRuntimeDevices[$i].DeviceName) with id $($sameRuntimeDevices[$i].DeviceId)" + xcrun simctl delete $sameRuntimeDevices[$i].DeviceId + $sameRuntimeDevices.RemoveAt($i) + } + write-host "*******************************************************************" + } + } + } +} diff --git a/images/macos/scripts/build/Install-Toolset.ps1 b/images/macos/scripts/build/Install-Toolset.ps1 new file mode 100644 index 0000000..1e81cd2 --- /dev/null +++ b/images/macos/scripts/build/Install-Toolset.ps1 @@ -0,0 +1,57 @@ +################################################################################ +## File: Install-Toolset.ps1 +## Team: CI-Build +## Desc: Install toolset +################################################################################ + +Import-Module "~/image-generation/tests/Helpers.psm1" +Import-Module "~/image-generation/helpers/Common.Helpers.psm1" + +Function Install-Asset { + param( + [Parameter(Mandatory=$true)] + [object] $ReleaseAsset + ) + + Write-Host "Download $($ReleaseAsset.filename) archive..." + $assetArchivePath = Invoke-DownloadWithRetry $ReleaseAsset.download_url + + Write-Host "Extract $($ReleaseAsset.filename) content..." + $assetFolderPath = Join-Path "/tmp" "$($ReleaseAsset.filename)-temp-dir" + New-Item -ItemType Directory -Path $assetFolderPath | Out-Null + tar -xzf $assetArchivePath -C $assetFolderPath + + Write-Host "Invoke installation script..." + Push-Location -Path $assetFolderPath + Invoke-Expression "bash ./setup.sh" + Pop-Location +} + +$arch = Get-Architecture + +# Get toolcache content from toolset +$toolsToInstall = @("Python", "Node", "Go") +$tools = (Get-ToolsetContent).toolcache | Where-Object {$toolsToInstall -contains $_.Name} + +foreach ($tool in $tools) { + # Get versions manifest for current tool + $assets = Invoke-RestMethod $tool.url -MaximumRetryCount 10 -RetryIntervalSec 30 + + # Get github release asset for each version + foreach ($version in $tool.arch.$arch.versions) { + $asset = $assets | Where-Object version -like $version ` + | Select-Object -ExpandProperty files ` + | Where-Object { ($_.platform -eq $tool.platform) -and ($_.arch -eq $arch)} ` + | Select-Object -First 1 + + Write-Host "Installing $($tool.name) $version..." + if ($null -ne $asset) { + Install-Asset -ReleaseAsset $asset + } else { + Write-Host "Asset was not found in versions manifest" + exit 1 + } + } +} + +Invoke-PesterTests "Toolcache" diff --git a/images/macos/scripts/build/Install-Xcode.ps1 b/images/macos/scripts/build/Install-Xcode.ps1 new file mode 100644 index 0000000..3552741 --- /dev/null +++ b/images/macos/scripts/build/Install-Xcode.ps1 @@ -0,0 +1,71 @@ +################################################################################ +## File: Install-Xcode.ps1 +## Desc: Install Xcode +################################################################################ + +$ErrorActionPreference = "Stop" + +Import-Module "$env:HOME/image-generation/helpers/Common.Helpers.psm1" +Import-Module "$env:HOME/image-generation/helpers/Xcode.Installer.psm1" -DisableNameChecking + +$arch = Get-Architecture +[Array]$xcodeVersions = (Get-ToolsetContent).xcode.$arch.versions +write-host $xcodeVersions +$defaultXcode = (Get-ToolsetContent).xcode.default +[Array]::Reverse($xcodeVersions) +$threadCount = "5" + +Write-Host "Installing Xcode versions..." +$xcodeVersions | ForEach-Object -ThrottleLimit $threadCount -Parallel { + $ErrorActionPreference = "Stop" + Import-Module "$env:HOME/image-generation/helpers/Common.Helpers.psm1" + Import-Module "$env:HOME/image-generation/helpers/Xcode.Installer.psm1" -DisableNameChecking + + Install-XcodeVersion -Version $_.version -LinkTo $_.link -Sha256Sum $_.sha256 + Confirm-XcodeIntegrity -Version $_.link +} + +$xcodeVersions | ForEach-Object { + Approve-XcodeLicense -Version $_.link +} + +Write-Host "Configuring Xcode versions..." +$xcodeVersions | ForEach-Object { + Write-Host "Configuring Xcode $($_.link) ..." + Invoke-XcodeRunFirstLaunch -Version $_.link + + if ($_.install_runtimes -eq 'true') { + # Additional simulator runtimes are included by default for Xcode < 14 + Install-AdditionalSimulatorRuntimes -Version $_.link + } + + ForEach($runtime in $_.runtimes) { + Write-Host "Installing Additional runtimes for Xcode '$runtime' ..." + $xcodebuildPath = Get-XcodeToolPath -Version $_.link -ToolName 'xcodebuild' + Invoke-ValidateCommand "sudo $xcodebuildPath -downloadPlatform $runtime" | Out-Null + } +} + +Invoke-XcodeRunFirstLaunch -Version $defaultXcode + +Write-Host "Configuring Xcode symlinks..." +$xcodeVersions | ForEach-Object { + Build-XcodeSymlinks -Version $_.link -Symlinks $_.symlinks + + # Skip creating symlink to install multiple releases of the same Xcode version side-by-side + if ($_."skip-symlink" -ne "true") { + Build-ProvisionatorSymlink -Version $_.link + } +} + +Write-Host "Rebuilding Launch Services database ..." +$xcodeVersions | ForEach-Object { + Initialize-XcodeLaunchServicesDb -Version $_.link +} + +Write-Host "Setting default Xcode to $defaultXcode" +Switch-Xcode -Version $defaultXcode +New-Item -Path "/Applications/Xcode.app" -ItemType SymbolicLink -Value (Get-XcodeRootPath -Version $defaultXcode) | Out-Null + +Write-Host "Setting environment variables 'XCODE__DEVELOPER_DIR'" +Set-XcodeDeveloperDirEnvironmentVariables -XcodeList $xcodeVersions.link diff --git a/images/macos/scripts/build/Update-XcodeSimulators.ps1 b/images/macos/scripts/build/Update-XcodeSimulators.ps1 new file mode 100644 index 0000000..7cc42fb --- /dev/null +++ b/images/macos/scripts/build/Update-XcodeSimulators.ps1 @@ -0,0 +1,56 @@ +################################################################################ +## File: Update-XcodeSimulators.ps1 +## Desc: Check available Xcode simulators and create missing ones +################################################################################ + +$ErrorActionPreference = "Stop" + +Import-Module "$env:HOME/image-generation/helpers/Xcode.Helpers.psm1" -DisableNameChecking +Import-Module "$env:HOME/image-generation/software-report/SoftwareReport.Xcode.psm1" -DisableNameChecking + +function Test-SimulatorInstalled { + param( + [Parameter(Mandatory)] + [string] $RuntimeId, + [Parameter(Mandatory)] + [string] $DeviceId, + [Parameter(Mandatory)] + [string] $SimulatorName, + [Parameter(Mandatory)] + [string] $XcodeVersion + ) + + $simctlPath = Get-XcodeToolPath -Version $XcodeVersion -ToolName "simctl" + if (-not (Test-Path $simctlPath)) { + Write-Host "Skip validating simulator '$SimulatorName [$RuntimeId]' because Xcode $XcodeVersion is not installed" + return + } + + $simulatorFullNameDebug = "$SimulatorName [$RuntimeId]" + Write-Host "Checking Xcode simulator '$simulatorFullNameDebug' (Xcode $XcodeVersion)..." + + # Get all available devices + [string]$rawDevicesInfo = Invoke-Expression "$simctlPath list devices --json" + $jsonDevicesInfo = ($rawDevicesInfo | ConvertFrom-Json).devices + + # Checking if simulator already exists + $existingSimulator = $jsonDevicesInfo.$RuntimeId | Where-Object { $_.deviceTypeIdentifier -eq $DeviceId } | Select-Object -First 1 + + if ($null -eq $existingSimulator) { + Write-Host "Simulator '$simulatorFullNameDebug' is missed. Creating it..." + Invoke-Expression "$simctlPath create '$SimulatorName' '$DeviceId' '$RuntimeId'" + } elseif ($existingSimulator.name -ne $SimulatorName) { + Write-Host "Simulator '$simulatorFullNameDebug' is named incorrectly. Renaming it from '$($existingSimulator.name)' to '$SimulatorName'..." + Invoke-Expression "$simctlPath rename '$($existingSimulator.udid)' '$SimulatorName'" + } else { + Write-Host "Simulator '$simulatorFullNameDebug' is installed correctly." + } +} + +# First run doesn't provide full data about devices +Get-XcodeInfoList | Out-Null + +Write-Host "Validating and fixing Xcode simulators..." +Get-BrokenXcodeSimulatorsList | ForEach-Object { + Test-SimulatorInstalled -RuntimeId $_.RuntimeId -DeviceId $_.DeviceId -SimulatorName $_.SimulatorName -XcodeVersion $_.XcodeVersion +} diff --git a/images/macos/scripts/build/configure-auto-updates.sh b/images/macos/scripts/build/configure-auto-updates.sh new file mode 100644 index 0000000..c11525b --- /dev/null +++ b/images/macos/scripts/build/configure-auto-updates.sh @@ -0,0 +1,11 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-auto-updates.sh +## Desc: Disabling automatic updates +################################################################################ + +sudo softwareupdate --schedule off +defaults write com.apple.SoftwareUpdate AutomaticDownload -int 0 +defaults write com.apple.SoftwareUpdate CriticalUpdateInstall -int 0 +defaults write com.apple.commerce AutoUpdate -bool false +defaults write com.apple.SoftwareUpdate AutomaticCheckEnabled -bool false diff --git a/images/macos/scripts/build/configure-autologin.sh b/images/macos/scripts/build/configure-autologin.sh new file mode 100644 index 0000000..0047174 --- /dev/null +++ b/images/macos/scripts/build/configure-autologin.sh @@ -0,0 +1,20 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-autologin.sh +## Desc: add a Daemon to re-detect the attached network interfaces after vm is booted. +## Maintainer: @timsutton +## script was taken from https://github.com/timsutton/osx-vm-templates/blob/master/scripts/autologin.sh +################################################################################ + +echo "Enabling automatic GUI login for the '$USERNAME' user.." +python3 $HOME/bootstrap/kcpassword.py "$PASSWORD" +/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser "$USERNAME" +/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUserScreenLocked -bool false + +: ' +The MIT License (MIT) +Copyright (c) 2013-2017 Timothy Sutton +Permission 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: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE 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. +' diff --git a/images/macos/scripts/build/configure-hostname.sh b/images/macos/scripts/build/configure-hostname.sh new file mode 100644 index 0000000..7b44557 --- /dev/null +++ b/images/macos/scripts/build/configure-hostname.sh @@ -0,0 +1,32 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-hostname.sh +## Desc: Change the hostname at startup to prevent duplicates +## Hostname and Computername should contain .local in name to avoid name resolution issues +################################################################################ + +tee -a /usr/local/bin/change_hostname.sh > /dev/null <<\EOF +#!/bin/bash -e -o pipefail + +name="Mac-$(python3 -c 'from time import time; print(int(round(time() * 1000)))')" +scutil --set HostName "${name}.local" +scutil --set LocalHostName $name +scutil --set ComputerName "${name}.local" +EOF + +chmod +x /usr/local/bin/change_hostname.sh + +sudo tee -a /Library/LaunchDaemons/change_hostname.plist > /dev/null <<\EOF + + + + + Label + change-hostname + Program + /usr/local/bin/change_hostname.sh + RunAtLoad + + + +EOF diff --git a/images/macos/scripts/build/configure-machine.sh b/images/macos/scripts/build/configure-machine.sh new file mode 100644 index 0000000..abd1f98 --- /dev/null +++ b/images/macos/scripts/build/configure-machine.sh @@ -0,0 +1,99 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-machine.sh +## Desc: Configure guest OS settings +################################################################################ + +source ~/utils/utils.sh + +echo "Enabling developer mode..." +sudo /usr/sbin/DevToolsSecurity --enable + +# Turn off hibernation and get rid of the sleepimage +sudo pmset hibernatemode 0 +sudo rm -f /var/vm/sleepimage + +# Disable App Nap System Wide +defaults write NSGlobalDomain NSAppSleepDisabled -bool YES + +# Disable Keyboard Setup Assistant window +sudo defaults write /Library/Preferences/com.apple.keyboardtype "keyboardtype" -dict-add "3-7582-0" -int 40 + +# Update VoiceOver Utility to allow VoiceOver to be controlled with AppleScript +# by creating a special Accessibility DB file (SIP must be disabled) and +# updating the user defaults system to reflect this change. +if csrutil status | grep -Eq "System Integrity Protection status: (disabled|unknown)"; then + sudo bash -c 'echo -n "a" > /private/var/db/Accessibility/.VoiceOverAppleScriptEnabled' +fi +defaults write com.apple.VoiceOver4/default SCREnableAppleScript -bool YES + +# https://developer.apple.com/support/expiration/ +# Enterprise iOS Distribution Certificates generated between February 7 and September 1st, 2020 will expire on February 7, 2023. +# Rotate the certificate before expiration to ensure your apps are installed and signed with an active certificate. +# Confirm that the correct intermediate certificate is installed by verifying the expiration date is set to 2030. +# sudo security delete-certificate -Z FF6797793A3CD798DC5B2ABEF56F73EDC9F83A64 /Library/Keychains/System.keychain + +swiftc -suppress-warnings "${HOME}/image-generation/add-certificate.swift" + +certs=( + AppleWWDRCAG3.cer + DeveloperIDG2CA.cer +) +for cert in ${certs[@]}; do + echo "Adding ${cert} certificate" + cert_path="${HOME}/${cert}" + curl -fsSL "https://www.apple.com/certificateauthority/${cert}" --output ${cert_path} + sudo ./add-certificate ${cert_path} + rm ${cert_path} +done + +rm -f ./add-certificate + +# enable-automationmode-without-authentication +brew install expect +retry=10 +while [[ $retry -gt 0 ]]; do +{ + /usr/bin/expect < ${Launch_Daemons}/limit.maxfiles.plist << EOF + + + + + Label + limit.maxfiles + Program + /Users/runner/limit-maxfiles.sh + RunAtLoad + + ServiceIPC + + + +EOF + +# Creating script for applying workaround https://developer.apple.com/forums/thread/735798 + +cat > /Users/runner/limit-maxfiles.sh << EOF +#!/bin/bash +sudo launchctl limit maxfiles 256 unlimited +sudo launchctl limit maxfiles 65536 524288 +EOF + +echo "limit.maxfiles.sh permissions changing" +chmod +x /Users/runner/limit-maxfiles.sh + +echo "limit.maxfiles.plist permissions changing" +chown root:wheel "${Launch_Daemons}/limit.maxfiles.plist" +chmod 0644 ${Launch_Daemons}/limit.maxfiles.plist + +echo "Done, limit.maxfiles has been updated" diff --git a/images/macos/scripts/build/configure-network-interface-detection.sh b/images/macos/scripts/build/configure-network-interface-detection.sh new file mode 100644 index 0000000..1a09bc2 --- /dev/null +++ b/images/macos/scripts/build/configure-network-interface-detection.sh @@ -0,0 +1,39 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-network-interface-detection.sh +## Desc: add a Daemon to re-detect the attached network interfaces after vm is booted. +## Maintainer: @timsutton +## script was taken from https://github.com/timsutton/osx-vm-templates/blob/master/scripts/add-network-interface-detection.sh +################################################################################ + +PLIST=/Library/LaunchDaemons/sonoma.detectnewhardware.plist +cat < ${PLIST} + + + + + Label + sonoma.detectnewhardware + ProgramArguments + + /usr/sbin/networksetup + -detectnewhardware + + RunAtLoad + + + +EOF + +# These should be already set as follows, but since they're required +# in order to load properly, we set them explicitly. +/bin/chmod 644 ${PLIST} +/usr/sbin/chown root:wheel ${PLIST} + +: ' +The MIT License (MIT) +Copyright (c) 2013-2017 Timothy Sutton +Permission 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: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE 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. +' diff --git a/images/macos/scripts/build/configure-ntpconf.sh b/images/macos/scripts/build/configure-ntpconf.sh new file mode 100644 index 0000000..e629c61 --- /dev/null +++ b/images/macos/scripts/build/configure-ntpconf.sh @@ -0,0 +1,19 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-ntpconf.sh +## Desc: Configure NTP servers and set the timezone to UTC +################################################################################ + +echo Additional NTP servers adding into /etc/ntp.conf file... +cat > /etc/ntp.conf << EOF +server 0.pool.ntp.org +server 1.pool.ntp.org +server 2.pool.ntp.org +server 3.pool.ntp.org +server time.apple.com +server time.windows.com +EOF + +# Set the timezone to UTC. +echo "The Timezone setting to UTC..." +ln -sf /usr/share/zoneinfo/UTC /etc/localtime diff --git a/images/macos/scripts/build/configure-preimagedata.sh b/images/macos/scripts/build/configure-preimagedata.sh new file mode 100644 index 0000000..d7492d2 --- /dev/null +++ b/images/macos/scripts/build/configure-preimagedata.sh @@ -0,0 +1,40 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-preimagedata.sh +## Desc: Configure data used in the image +################################################################################ + +source ~/utils/utils.sh + +arch=$(get_arch) +imagedata_file="$HOME/imagedata.json" +image_version=$(echo $IMAGE_VERSION | cut -d _ -f 2) +os_name=$(sw_vers -productName) +os_version=$(sw_vers -productVersion) +os_build=$(sw_vers -buildVersion) +label_version=$(echo $os_version | cut -d. -f1) + +if [[ $arch == "arm64" ]]; then + image_label="macos-${label_version}-arm64" +else + image_label="macos-${label_version}" +fi + +software_url="https://github.com/actions/runner-images/blob/${image_label}/${image_version}/images/macos/${image_label}-Readme.md" +releaseUrl="https://github.com/actions/runner-images/releases/tag/${image_label}%2F${image_version}" + +cat < $imagedata_file + [ + { + "group": "Operating System", + "detail": "${os_name}\n${os_version}\n${os_build}" + }, + { + "group": "Runner Image", + "detail": "Image: ${image_label}\nVersion: ${image_version}\nIncluded Software: ${software_url}\nImage Release: ${releaseUrl}" + } + ] +EOF + +echo "export ImageVersion=$image_version" >> $HOME/.bashrc +echo "export ImageOS=$IMAGE_OS" >> $HOME/.bashrc diff --git a/images/macos/scripts/build/configure-screensaver.sh b/images/macos/scripts/build/configure-screensaver.sh new file mode 100644 index 0000000..6873028 --- /dev/null +++ b/images/macos/scripts/build/configure-screensaver.sh @@ -0,0 +1,30 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-screensaver.sh +## Desc: Configure screensaver +################################################################################ + +# set screensaver idleTime to 0, to prevent turning screensaver on +macUUID=$(ioreg -rd1 -c IOPlatformExpertDevice | grep -i "UUID" | cut -c27-62) + +rm -rf /Users/$USERNAME/Library/Preferences/com.apple.screensaver.$macUUID.plist +rm -rf /Users/$USERNAME/Library/Preferences/ByHost/com.apple.screensaver.$macUUID.plist +rm -rf /Users/$USERNAME/Library/Preferences/com.apple.screensaver.plist +rm -rf /Users/$USERNAME/Library/Preferences/ByHost/com.apple.screensaver.plist + +defaults write /Users/$USERNAME/Library/Preferences/com.apple.screensaver.$macUUID.plist idleTime -string 0 +defaults write /Users/$USERNAME/Library/Preferences/com.apple.screensaver.$macUUID.plist CleanExit "YES" +defaults write /Users/$USERNAME/Library/Preferences/ByHost/com.apple.screensaver.$macUUID.plist idleTime -string 0 +defaults write /Users/$USERNAME/Library/Preferences/ByHost/com.apple.screensaver.$macUUID.plist CleanExit "YES" +defaults write /Users/$USERNAME/Library/Preferences/com.apple.screensaver.plist idleTime -string 0 +defaults write /Users/$USERNAME/Library/Preferences/com.apple.screensaver.plist CleanExit "YES" +defaults write /Users/$USERNAME/Library/Preferences/ByHost/com.apple.screensaver.plist idleTime -string 0 +defaults write /Users/$USERNAME/Library/Preferences/ByHost/com.apple.screensaver.plist CleanExit "YES" + +chown -R $USERNAME:staff /Users/$USERNAME/Library/Preferences/ByHost/ +chown -R $USERNAME:staff /Users/$USERNAME/Library/Preferences/ + +killall cfprefsd + +# Set values to 0, to prevent sleep at all +pmset -a displaysleep 0 sleep 0 disksleep 0 diff --git a/images/macos/scripts/build/configure-shell.sh b/images/macos/scripts/build/configure-shell.sh new file mode 100644 index 0000000..0f7643a --- /dev/null +++ b/images/macos/scripts/build/configure-shell.sh @@ -0,0 +1,19 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-shell.sh +## Desc: Configure shell to use bash +################################################################################ + +source ~/utils/utils.sh +arch=$(get_arch) + +echo "Changing shell to bash" +sudo chsh -s /bin/bash $USERNAME +sudo chsh -s /bin/bash root + +# Check MacOS architecture and add HOMEBREW PATH to bashrc +if [[ $arch == "arm64" ]]; then + echo "Adding Homebrew environment to bash" + # Discussed here: https://github.com/Homebrew/brew/pull/18366 + echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.bashrc +fi diff --git a/images/macos/scripts/build/configure-ssh.sh b/images/macos/scripts/build/configure-ssh.sh new file mode 100644 index 0000000..aea34c7 --- /dev/null +++ b/images/macos/scripts/build/configure-ssh.sh @@ -0,0 +1,11 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-ssh.sh +## Desc: Configure ssh +################################################################################ + +[[ ! -d ~/.ssh ]] && mkdir ~/.ssh 2>/dev/null +chmod 777 ~/.ssh + +ssh-keyscan -t rsa,ecdsa,ed25519 github.com >> ~/.ssh/known_hosts +ssh-keyscan -t rsa ssh.dev.azure.com >> ~/.ssh/known_hosts diff --git a/images/macos/scripts/build/configure-system.sh b/images/macos/scripts/build/configure-system.sh new file mode 100644 index 0000000..23fd09f --- /dev/null +++ b/images/macos/scripts/build/configure-system.sh @@ -0,0 +1,55 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-system.sh +## Desc: Post deployment system configuration actions +################################################################################ + +source ~/utils/utils.sh + +# Close all finder windows because they can interfere with UI tests +close_finder_window + +# Remove Parallels Desktop +# https://github.com/actions/runner-images/issues/6105 +# https://github.com/actions/runner-images/issues/10143 +if is_Monterey || is_SonomaX64 || is_VenturaX64; then + brew uninstall parallels +fi + +# Put documentation to $HOME root +cp $HOME/image-generation/output/software-report/systeminfo.* $HOME/ + +# Put build vm assets (xamarin-selector) scripts to proper directory +if is_Monterey || is_Sonoma || is_Ventura; then + mkdir -p /usr/local/opt/$USER/scripts + mv $HOME/image-generation/assets/* /usr/local/opt/$USER/scripts + find /usr/local/opt/$USER/scripts -type f -name "*\.sh" -exec chmod +x {} \; +fi + +# Remove fastlane cached cookie +rm -rf ~/.fastlane + +# Clean up npm cache which collected during image-generation +# we have to do that here because `npm install` is run in a few different places during image-generation +npm cache clean --force + +# Clean yarn cache +yarn cache clean + +# Clean up temporary directories +sudo rm -rf ~/utils /tmp/* + +# Erase all indexes and wait until the rebuilding process ends, +# for now there is no way to get status of indexing process, it takes around 3 minutes to accomplish +sudo mdutil -E / +sudo log stream | grep -q -E 'mds.*Released.*BackgroundTask' || true +echo "Indexing completed" + +# delete symlink for tests running +sudo rm -f /usr/local/bin/invoke_tests + +# Clean Homebrew downloads +sudo rm -rf /Users/$USER/Library/Caches/Homebrew/downloads/* + +# Uninstall expect used in configure-machine.sh +brew uninstall expect diff --git a/images/macos/scripts/build/configure-tccdb-macos.sh b/images/macos/scripts/build/configure-tccdb-macos.sh new file mode 100644 index 0000000..b9063d0 --- /dev/null +++ b/images/macos/scripts/build/configure-tccdb-macos.sh @@ -0,0 +1,75 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-tccdb-macos.sh +## Desc: Configure permissions to the TCC.db +################################################################################ + +source ~/utils/utils.sh + +# /Library/Application\ Support/com.apple.TCC/TCC.db +systemValuesArray=( + "'kTCCServiceAccessibility','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1566321319" + "'kTCCServicePostEvent','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1566321326" + "'kTCCServiceSystemPolicyAllFiles','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993" + "'kTCCServiceAccessibility','com.apple.dt.Xcode-Helper',0,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1551941368" + "'kTCCServiceSystemPolicyAllFiles','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993" + "'kTCCServiceSystemPolicyAllFiles','/usr/libexec/sshd-keygen-wrapper',1,0,4,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,0,'UNUSED',NULL,0,1639660695" + "'kTCCServiceSystemPolicyAllFiles','com.apple.Terminal',0,2,4,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,0,'UNUSED',NULL,0,1678990068" + "'kTCCServiceAccessibility','/usr/libexec/sshd-keygen-wrapper',1,2,4,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,0,'UNUSED',NULL,0,1644564233" + "'kTCCServiceAccessibility','com.apple.Terminal',0,2,0,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,NULL,'UNUSED',NULL,0,1591180502" + "'kTCCServiceAccessibility','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993" + "'kTCCServiceMicrophone','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1576661342" + "'kTCCServiceScreenCapture','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1599831148" + "'kTCCServiceScreenCapture','com.devexpress.testcafe-browser-tools',0,2,3,1,X'fade0c0000000068000000010000000700000007000000080000001443fa4ca5141baeda21aeca1f50894673b440d4690000000800000014f8afcf6e69791b283e55bd0b03e39e422745770e0000000800000014bf4fc1aed64c871a49fc6bc9dd3878ce5d4d17c6',NULL,0,'UNUSED',NULL,0,1687952810" + "'kTCCServicePostEvent','/Library/Application Support/Veertu/Anka/addons/ankarund',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1644565949" + "'kTCCServiceScreenCapture','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" + "'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1592919552" + "'kTCCServiceAccessibility','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,NULL,1592919552" + # Allow Full Disk Access for "Microsoft Defender for macOS" to bypass installation on-flight + "'kTCCServiceSystemPolicyAllFiles','com.microsoft.wdav',0,2,4,1,NULL,NULL,NULL,'UNUSED',NULL,0,1643970979" + "'kTCCServiceSystemPolicyAllFiles','com.microsoft.wdav.epsext',0,2,4,1,NULL,NULL,NULL,'UNUSED',NULL,0,1643970979" + "'kTCCServiceSystemPolicyNetworkVolumes','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993" + "'kTCCServiceSystemPolicyNetworkVolumes','com.apple.Terminal',0,2,4,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,0,'UNUSED',NULL,0,1678990068" +) +for values in "${systemValuesArray[@]}"; do + if is_Sonoma || is_Sequoia; then + # TCC access table in Sonoma has extra 4 columns: pid, pid_version, boot_uuid, last_reminded + configure_system_tccdb "$values,NULL,NULL,'UNUSED',${values##*,}" + else + configure_system_tccdb "$values" + fi +done + +# $HOME/Library/Application\ Support/com.apple.TCC/TCC.db +userValuesArray=( + "'kTCCServiceUbiquity','com.apple.mail',0,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1551941469" + "'kTCCServiceUbiquity','com.apple.TextEdit',0,2,0,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465787445646974000000000003',NULL,NULL,'UNUSED',NULL,0,1566368356" + "'kTCCServiceUbiquity','com.apple.CloudDocs.MobileDocumentsFileProvider',0,2,0,1,X'fade0c000000004c0000000100000006000000020000002f636f6d2e6170706c652e436c6f7564446f63732e4d6f62696c65446f63756d656e747346696c6550726f76696465720000000003',NULL,NULL,'UNUSED',NULL,0,1570793290" + "'kTCCServiceAppleEvents','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,0,'com.apple.systemevents',NULL,NULL,1574241374" + "'kTCCServiceSystemPolicyAllFiles','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993" + "'kTCCServiceAppleEvents','/usr/libexec/sshd-keygen-wrapper',1,2,3,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,0,'com.apple.systemevents',X'fade0c000000003400000001000000060000000200000016636f6d2e6170706c652e73797374656d6576656e7473000000000003',NULL,1644564201" + "'kTCCServiceAppleEvents','com.apple.Terminal',0,2,0,1,X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,0,'com.apple.systemevents',X'fade0c000000003400000001000000060000000200000016636f6d2e6170706c652e73797374656d6576656e7473000000000003',NULL,1591180478" + "'kTCCServiceAppleEvents','/usr/libexec/sshd-keygen-wrapper',1,2,0,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1591357685" + "'kTCCServiceAppleEvents','/bin/bash',1,2,0,1,NULL,NULL,0,'com.apple.systemevents',NULL,NULL,1591532620" + "'kTCCServiceAppleEvents','/bin/bash',1,2,0,1,NULL,NULL,0,'com.apple.finder',NULL,NULL,1592919552" + "'kTCCServiceMicrophone','com.apple.CoreSimulator.SimulatorTrampoline',0,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1576347152" + "'kTCCServiceMicrophone','/usr/local/opt/runner/runprovisioner.sh',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,NULL,1576661342" + "'kTCCServiceUbiquity','/System/Library/PrivateFrameworks/PhotoLibraryServices.framework/Versions/A/Support/photolibraryd',1,2,5,1,NULL,NULL,NULL,'UNUSED',NULL,0,1619461750" + "'kTCCServiceUbiquity','com.apple.PassKitCore',0,2,5,1,NULL,NULL,NULL,'UNUSED',NULL,0,1619516250" + "'kTCCServiceAppleEvents','/Library/Application Support/Veertu/Anka/addons/ankarund',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1629294900" + "'kTCCServiceAppleEvents','/Library/Application Support/Veertu/Anka/addons/ankarund',1,2,3,1,NULL,NULL,0,'com.apple.systemevents',X'fade0c000000003400000001000000060000000200000016636f6d2e6170706c652e73797374656d6576656e7473000000000003',NULL,164456761" + "'kTCCServiceAppleEvents','/Library/Application Support/Veertu/Anka/addons/ankarund',1,2,3,1,NULL,NULL,0,'com.apple.Terminal',X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,1655808179" + "'kTCCServiceAppleEvents','/usr/libexec/sshd-keygen-wrapper',1,2,3,1,X'fade0c000000003c0000000100000006000000020000001d636f6d2e6170706c652e737368642d6b657967656e2d7772617070657200000000000003',NULL,0,'com.apple.Terminal',X'fade0c000000003000000001000000060000000200000012636f6d2e6170706c652e5465726d696e616c000000000003',NULL,1650386089" + "'kTCCServicePostEvent','/bin/bash',1,2,0,1,NULL,NULL,NULL,'UNUSED',NULL,0,1583997993" + "'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1592919552" + "'kTCCServiceScreenCapture','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159" + "'kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,3,1,NULL,NULL,0,'com.apple.systemevents',X'fade0c000000003400000001000000060000000200000016636f6d2e6170706c652e73797374656d6576656e7473000000000003',NULL,1592919552" +) +for values in "${userValuesArray[@]}"; do + if is_Sonoma || is_Sequoia; then + # TCC access table in Sonoma has extra 4 columns: pid, pid_version, boot_uuid, last_reminded + configure_user_tccdb "$values,NULL,NULL,'UNUSED',${values##*,}" + else + configure_user_tccdb "$values" + fi +done diff --git a/images/macos/scripts/build/configure-windows.sh b/images/macos/scripts/build/configure-windows.sh new file mode 100644 index 0000000..9a753a5 --- /dev/null +++ b/images/macos/scripts/build/configure-windows.sh @@ -0,0 +1,42 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-windows.sh +## Desc: Close open windows +################################################################################ + +source ~/utils/utils.sh + +# Close System Preferences window because in Ventura arm64 it is opened by default on Apperance tab +if is_Arm64; then + echo "Close System Preferences window" + osascript -e 'tell application "System Preferences" to quit' +fi + +retry=10 +while [[ $retry -gt 0 ]]; do + openwindows=$(osascript -e 'tell application "System Events" to get every window of (every process whose class of windows contains window)') && break + retry=$((retry-1)) + if [[ $retry -eq 0 ]]; then + echo "No retry attempts left" + exit 1 + fi + sleep 30 +done +IFS=',' read -r -a windowslist <<< "$openwindows" + +if [[ -n ${openwindows} ]]; then + echo "Found opened window:" +fi + +for key in ${!windowslist[@]}; do + if [[ ${windowslist[$key]} =~ "NotificationCenter" ]]; then + echo "[Warning] ${windowslist[$key]}" + else + echo " - ${windowslist[$key]}" | xargs + scripterror=true + fi +done + +if [[ ${scripterror} = true ]]; then + exit 1 +fi diff --git a/images/macos/scripts/build/configure-xcode-simulators.rb b/images/macos/scripts/build/configure-xcode-simulators.rb new file mode 100644 index 0000000..a585acc --- /dev/null +++ b/images/macos/scripts/build/configure-xcode-simulators.rb @@ -0,0 +1,144 @@ +#!/usr/bin/env ruby +################################################################################ +## File: configure-xcode-simulators.rb +## Desc: List all simulators, find duplicate type and delete them. +## Maintainer: @vlas-voloshin +## script was taken from https://gist.github.com/vlas-voloshin/f9982128200345cd3fb7 +################################################################################ + +class SimDevice + + attr_accessor :runtime + attr_accessor :name + attr_accessor :identifier + attr_accessor :timestamp + + def initialize(runtime, name, identifier, timestamp) + @runtime = runtime + @name = name + @identifier = identifier + @timestamp = timestamp + end + + def to_s + return "#{@name} - #{@runtime} (#{@identifier}) [#{@timestamp}]" + end + + def equivalent_to_device(device) + return @runtime == device.runtime && @name == device.name + end + + end + + # Executes a shell command and returns the result from stdout + def execute_simctl_command(command) + return %x[xcrun simctl #{command}] + end + + # Retrieves the creation date/time of simulator with specified identifier + def simulator_creation_date(identifier) + directory = Dir.home() + "/Library/Developer/CoreSimulator/Devices/" + identifier + if (Dir.exists?(directory)) + if (File::Stat.method_defined?(:birthtime)) + return File.stat(directory).birthtime + else + return File.stat(directory).ctime + end + else + # Simulator directory is not yet created - treat it as if it was created right now (happens with new iOS 9 sims) + return Time.now + end + end + + # Deletes specified simulator + def delete_device(device) + execute_simctl_command("delete #{device.identifier}") + end + + puts("Searching for simulators...") + + # Retrieve the list of existing simulators + devices = [] + runtime = "" + execute_simctl_command("list devices").lines.each do |line| + case line[0] + when '=' + # First header, skip it + when '-' + # Runtime header + runtime = line.scan(/-- (.+?) --/).flatten[0] + else + name_and_identifier = line.scan(/\s+(.+?) \(([\w\d]+-[\w\d]+-[\w\d-]+)\)/)[0] + name = name_and_identifier[0] + identifier = name_and_identifier[1] + timestamp = simulator_creation_date(identifier) + device = SimDevice.new(runtime, name, identifier, timestamp) + devices.push(device) + end + end + + # Sort the simulators by their creation timestamp, ascending + devices = devices.sort { |a, b| a.timestamp <=> b.timestamp } + + duplicates = {} + # Enumerate all devices except for the last one + for i in 0..devices.count-2 + device = devices[i] + # Enumerate all devices *after* this one (created *later*) + for j in i+1..devices.count-1 + potential_duplicate = devices[j] + if potential_duplicate.equivalent_to_device(device) + duplicates[potential_duplicate] = device + # Break out of the inner loop if a duplicate is found - if another duplicate exists, + # it will be found when this one is reached in the outer loop + break + end + end + end + + if duplicates.count == 0 + puts("You don't have duplicate simulators!") + exit() + end + + puts("Looks like you have #{duplicates.count} duplicate simulator#{duplicates.count > 1 ? "s" : ""}:") + duplicates.each_pair do |duplicate, original| + puts + puts("#{duplicate}") + puts("--- duplicate of ---") + puts("#{original}") + end + puts + + puts("Each duplicate was determined as the one created later than the 'original'.") + + puts("Deleting...") + duplicates.each_key do |duplicate| + delete_device(duplicate) + end + + puts("Done!") + +=begin +MIT License + +Copyright (c) 2015-2019 Vlas Voloshin + +Permission 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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE 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. +=end diff --git a/images/macos/scripts/build/configure-xcode.sh b/images/macos/scripts/build/configure-xcode.sh new file mode 100644 index 0000000..f707679 --- /dev/null +++ b/images/macos/scripts/build/configure-xcode.sh @@ -0,0 +1,34 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: configure-xcode.sh +## Desc: Configure Xcode after installation +################################################################################ + +source ~/utils/utils.sh + +XCODE_LIST=($(get_toolset_value '.xcode.versions | reverse | .[].link')) +DEFAULT_XCODE_VERSION=$(get_toolset_value '.xcode.default') + +# https://github.com/microsoft/appcenter/issues/847 +# Assets.xcassets : error : CoreData: error: (6922) I/O error for database +# at $HOME/Library/Developer/Xcode/UserData/IB Support/Simulator Devices/{GUID} +echo "Erase a device's contents and settings:" +for XCODE_VERSION in ${XCODE_LIST[@]}; do + echo " Xcode Version: ${XCODE_VERSION}" + launchctl remove com.apple.CoreSimulator.CoreSimulatorService || true + #add sleep to let CoreSimulatorService to exit + sleep 3 + + # Select xcode version by default + sudo xcode-select -s /Applications/Xcode_${XCODE_VERSION}.app/Contents/Developer + + # Erase a device's contents and settings + xcrun simctl erase all + + #add sleep due to sometimes "xcrun simctl list" takes more than a few moments and script fails when trying to remove CoreSimulatorSerivce + sleep 10 +done + +# Select xcode version by default +echo "Setting Xcode ${DEFAULT_XCODE_VERSION} as default" +sudo xcode-select -s /Applications/Xcode_${DEFAULT_XCODE_VERSION}.app/Contents/Developer diff --git a/images/macos/scripts/build/install-actions-cache.sh b/images/macos/scripts/build/install-actions-cache.sh new file mode 100644 index 0000000..fe5dfb4 --- /dev/null +++ b/images/macos/scripts/build/install-actions-cache.sh @@ -0,0 +1,20 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-actions-cache.sh +## Desc: Download latest release from https://github.com/actions/action-versions +## Maintainer: #actions-runtime and @TingluoHuang +################################################################################ + +source ~/utils/utils.sh + +echo "Check if ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE folder exist..." +if [[ ! -d $ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE ]]; then + mkdir -p $ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE +fi + +download_url=$(resolve_github_release_asset_url "actions/action-versions" "contains(\"action-versions.tar.gz\")" "latest" "$API_PAT") +echo "Downloading action-versions $download_url" +archive_path=$(download_with_retry $download_url) +tar -xzf $archive_path -C $ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE + +invoke_tests "ActionArchiveCache" diff --git a/images/macos/scripts/build/install-android-sdk.sh b/images/macos/scripts/build/install-android-sdk.sh new file mode 100644 index 0000000..1cabb1c --- /dev/null +++ b/images/macos/scripts/build/install-android-sdk.sh @@ -0,0 +1,132 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-android-sdk.sh +## Desc: Install Android SDK, NDK and tools +################################################################################ + +source ~/utils/utils.sh + +add_filtered_installation_components() { + local minimum_version=$1 + shift + local tools_array=("$@") + + for item in ${tools_array[@]}; do + # take the last argument after splitting string by ';'' and '-'' + version=$(echo "${item##*[-;]}") + if [[ "$(printf "${minimum_version}\n${version}\n" | sort -V | head -n1)" == "$minimum_version" ]]; then + components+=($item) + fi + done +} + +get_full_ndk_version() { + local majorVersion=$1 + + ndkVersion=$(${SDKMANAGER} --list | grep "ndk;${majorVersion}.*" | awk '{gsub("ndk;", ""); print $1}' | sort -V | tail -n1) + echo "$ndkVersion" +} + +components=() + +android_platform=$(get_toolset_value '.android.platform_min_version') +android_build_tool=$(get_toolset_value '.android.build_tools_min_version') +android_extra_list=($(get_toolset_value '.android."extras"[]')) +android_addon_list=($(get_toolset_value '.android."addons"[]')) +android_additional_tools=($(get_toolset_value '.android."additional_tools"[]')) +android_ndk_major_versions=($(get_toolset_value '.android.ndk."versions"[]')) +android_ndk_major_default=$(get_toolset_value '.android.ndk.default') +android_ndk_major_latest=$(get_toolset_value '.android.ndk."versions"[-1]') +# Get the latest command line tools from https://developer.android.com/studio#cmdline-tools +# Newer version(s) require Java 11 by default +# See https://github.com/actions/runner-images/issues/6960 +ANDROID_HOME=$HOME/Library/Android/sdk + +# Download the latest command line tools so that we can accept all of the licenses. +# See https://developer.android.com/studio/#command-tools +cmdlineToolsVersion=$(get_toolset_value '.android."cmdline-tools"') + +if [[ $cmdlineToolsVersion == "latest" ]]; then + repository_xml_url="https://dl.google.com/android/repository/repository2-1.xml" + repository_xml_path=$(download_with_retry $repository_xml_url) + cmdlineToolsVersion=$( + yq -p=xml \ + '.sdk-repository.remotePackage[] | select(."+@path" == "cmdline-tools;latest" and .channelRef."+@ref" == "channel-0").archives.archive[].complete.url | select(contains("commandlinetools-mac"))' \ + "$repository_xml_path" + ) + + if [[ -z $cmdlineToolsVersion ]]; then + echo "Failed to parse latest command-line tools version" + exit 1 + fi +fi + +echo "Downloading android command line tools..." +archive_path=$(download_with_retry "https://dl.google.com/android/repository/${cmdlineToolsVersion}") + +echo "Uncompressing android command line tools..." +mkdir -p "$ANDROID_HOME" +unzip -q "$archive_path" -d "$ANDROID_HOME/cmdline-tools" +# Command line tools need to be placed in $ANDROID_HOME/cmdline-tools/latest to function properly +mv "$ANDROID_HOME/cmdline-tools/cmdline-tools" "$ANDROID_HOME/cmdline-tools/latest" + +echo ANDROID_HOME is "$ANDROID_HOME" +export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest:$ANDROID_HOME/cmdline-tools/latest/bin + +SDKMANAGER=$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager + +echo "Installing latest tools & platform tools..." +echo y | $SDKMANAGER "tools" "platform-tools" + +echo "Installing latest ndk..." +for ndk_version in "${android_ndk_major_versions[@]}" +do + ndk_full_version=$(get_full_ndk_version $ndk_version) + echo y | $SDKMANAGER "ndk;$ndk_full_version" +done + +ndkDefault=$(get_full_ndk_version $android_ndk_major_default) +ANDROID_NDK_HOME=$ANDROID_HOME/ndk/$ndkDefault +ndkLatest=$(get_full_ndk_version $android_ndk_major_latest) +ANDROID_NDK_LATEST_HOME=$ANDROID_HOME/ndk/$ndkLatest +# ANDROID_NDK, ANDROID_NDK_HOME, and ANDROID_NDK_ROOT variables should be set as many customer builds depend on them https://github.com/actions/runner-images/issues/5879 +echo "export ANDROID_NDK=$ANDROID_NDK_HOME" >> "${HOME}/.bashrc" +echo "export ANDROID_NDK_HOME=$ANDROID_NDK_HOME" >> "${HOME}/.bashrc" +echo "export ANDROID_NDK_ROOT=$ANDROID_NDK_HOME" >> "${HOME}/.bashrc" +echo "export ANDROID_NDK_LATEST_HOME=$ANDROID_NDK_LATEST_HOME" >> "${HOME}/.bashrc" + +availablePlatforms=($($SDKMANAGER --list | grep "platforms;android-[0-9]" | cut -d"|" -f 1 | sort -u)) +add_filtered_installation_components $android_platform "${availablePlatforms[@]}" + +allBuildTools=($($SDKMANAGER --list --include_obsolete | grep "build-tools;" | cut -d"|" -f 1 | sort -u)) +availableBuildTools=$(echo ${allBuildTools[@]//*rc[0-9]/}) +add_filtered_installation_components $android_build_tool "${availableBuildTools[@]}" + +echo "y" | $SDKMANAGER ${components[@]} + +for extra_name in "${android_extra_list[@]}" +do + echo "Installing extra $extra_name ..." + echo y | $SDKMANAGER "extras;$extra_name" +done + +for addon_name in "${android_addon_list[@]}" +do + echo "Installing add-on $addon_name ..." + echo y | $SDKMANAGER "add-ons;$addon_name" +done + +for tool_name in "${android_additional_tools[@]}" +do + echo "Installing additional tool $tool_name ..." + echo y | $SDKMANAGER "$tool_name" +done + +# Download SDK tools to preserve backward compatibility +sdk_tools_version=$(get_toolset_value '.android."sdk-tools"') +if [ "$sdk_tools_version" != "null" ]; then + sdk_tools_archive_path=$(download_with_retry "https://dl.google.com/android/repository/${sdk_tools_version}") + unzip -o -qq "$sdk_tools_archive_path" -d "${ANDROID_SDK_ROOT}" +fi + +invoke_tests "Android" diff --git a/images/macos/scripts/build/install-apache.sh b/images/macos/scripts/build/install-apache.sh new file mode 100644 index 0000000..b87345e --- /dev/null +++ b/images/macos/scripts/build/install-apache.sh @@ -0,0 +1,12 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-apache.sh +## Desc: Install Apache HTTP Server +################################################################################ + +source ~/utils/utils.sh + +brew_smart_install httpd +sudo sed -Ei '' 's/Listen .*/Listen 80/' $(brew --prefix)/etc/httpd/httpd.conf + +invoke_tests "WebServers" "Apache" diff --git a/images/macos/scripts/build/install-audiodevice.sh b/images/macos/scripts/build/install-audiodevice.sh new file mode 100644 index 0000000..b9ccc2d --- /dev/null +++ b/images/macos/scripts/build/install-audiodevice.sh @@ -0,0 +1,15 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-audiodevice.sh +## Desc: Install audio device +################################################################################ + +source ~/utils/utils.sh + +echo "install switchaudio-osx" +brew_smart_install "switchaudio-osx" + +echo "install sox" +brew_smart_install "sox" + +invoke_tests "System" "Audio Device" diff --git a/images/macos/scripts/build/install-aws-tools.sh b/images/macos/scripts/build/install-aws-tools.sh new file mode 100644 index 0000000..db4565b --- /dev/null +++ b/images/macos/scripts/build/install-aws-tools.sh @@ -0,0 +1,20 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-aws-tools.sh +## Desc: Install the AWS CLI, Session Manager plugin for the AWS CLI, and AWS SAM CLI +################################################################################ + +source ~/utils/utils.sh + +echo "Installing aws..." +awscliv2_pkg_path=$(download_with_retry "https://awscli.amazonaws.com/AWSCLIV2.pkg") +sudo installer -pkg "$awscliv2_pkg_path" -target / + +echo "Installing aws sam cli..." +brew tap aws/tap +brew_smart_install aws-sam-cli + +echo "Install aws cli session manager" +brew install --cask session-manager-plugin + +invoke_tests "Common" "AWS" diff --git a/images/macos/scripts/build/install-azcopy.sh b/images/macos/scripts/build/install-azcopy.sh new file mode 100644 index 0000000..8c6ca81 --- /dev/null +++ b/images/macos/scripts/build/install-azcopy.sh @@ -0,0 +1,23 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-azcopy.sh +## Desc: Install AzCopy +################################################################################ + +source ~/utils/utils.sh + +if is_Arm64; then + url="https://aka.ms/downloadazcopy-v10-mac-arm64" +else + url="https://aka.ms/downloadazcopy-v10-mac" +fi + +# Install AzCopy +archive_path=$(download_with_retry ${url}) +unzip -qq $archive_path -d /tmp/azcopy +extract_path=$(echo /tmp/azcopy/azcopy*) +cp $extract_path/azcopy /usr/local/bin/azcopy +chmod +x /usr/local/bin/azcopy + + +invoke_tests "Common" "AzCopy" diff --git a/images/macos/scripts/build/install-bicep.sh b/images/macos/scripts/build/install-bicep.sh new file mode 100644 index 0000000..6a5a3dd --- /dev/null +++ b/images/macos/scripts/build/install-bicep.sh @@ -0,0 +1,13 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-bicep.sh +## Desc: Install bicep cli +################################################################################ + +source ~/utils/utils.sh + +echo "Installing bicep cli..." +brew tap azure/bicep +brew_smart_install bicep + +invoke_tests "Common" "Bicep" diff --git a/images/macos/scripts/build/install-chrome.sh b/images/macos/scripts/build/install-chrome.sh new file mode 100644 index 0000000..c5c396b --- /dev/null +++ b/images/macos/scripts/build/install-chrome.sh @@ -0,0 +1,52 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-chrome.sh +## Desc: Install chrome and chrome for testing browsers +################################################################################ + +source ~/utils/utils.sh +arch=$(get_arch) + +echo "Installing Google Chrome..." +brew install --cask google-chrome + +# Parse Google Chrome version +full_chrome_version=$("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --version) +full_chrome_version=${full_chrome_version#Google Chrome } +chrome_version=${full_chrome_version%.*} +echo "Google Chrome version is $full_chrome_version" + +# Get Google Chrome versions information +chrome_platform="mac-$arch" +CHROME_VERSIONS_URL="https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build-with-downloads.json" +chrome_versions_json=$(download_with_retry "$CHROME_VERSIONS_URL") + +# Download and unpack the latest release of Chrome Driver +chromedriver_version=$(cat $chrome_versions_json | jq -r '.builds["'"$chrome_version"'"].version') +echo "Installing Chrome Driver version $chromedriver_version" + +chromedriver_url=$(cat $chrome_versions_json | jq -r '.builds["'"$chrome_version"'"].downloads.chromedriver[] | select(.platform=="'"${chrome_platform}"'").url') +chromedriver_dir="/usr/local/share/chromedriver-$chrome_platform" +chromedriver_bin="$chromedriver_dir/chromedriver" + +chromedriver_archive_path=$(download_with_retry $chromedriver_url) +unzip -qq $chromedriver_archive_path -d /tmp/ +sudo mv /tmp/chromedriver-$chrome_platform $chromedriver_dir +ln -s $chromedriver_bin /usr/local/bin/chromedriver +echo "export CHROMEWEBDRIVER=$chromedriver_dir" >> ${HOME}/.bashrc + +# Download and unpack the latest release of Google Chrome for Testing +chrome_for_testing_version=$(cat $chrome_versions_json | jq -r '.builds["'"$chrome_version"'"].version') +echo "Installing Google Chrome for Testing version $chrome_for_testing_version" + +chrome_for_testing_url=$(cat $chrome_versions_json | jq -r '.builds["'"$chrome_version"'"].downloads.chrome[] | select(.platform=="'"${chrome_platform}"'").url') +chrome_for_testing_app="Google Chrome for Testing.app" + +chrome_for_testing_archive_path=$(download_with_retry $chrome_for_testing_url) +unzip -qq $chrome_for_testing_archive_path -d /tmp/ +mv "/tmp/chrome-$chrome_platform/$chrome_for_testing_app" "/Applications/$chrome_for_testing_app" + +echo "Installing Selenium" +brew_smart_install "selenium-server" + +invoke_tests "Browsers" "Chrome" diff --git a/images/macos/scripts/build/install-cocoapods.sh b/images/macos/scripts/build/install-cocoapods.sh new file mode 100644 index 0000000..de12774 --- /dev/null +++ b/images/macos/scripts/build/install-cocoapods.sh @@ -0,0 +1,14 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-cocoapods.sh +## Desc: Install Cocoapods +################################################################################ + +# Setup the Cocoapods +echo "Installing Cocoapods..." +pod setup + +# Create a symlink to /usr/local/bin since it was removed due to Homebrew change. +ln -sf $(which pod) /usr/local/bin/pod + +invoke_tests "Common" "CocoaPods" diff --git a/images/macos/scripts/build/install-codeql-bundle.sh b/images/macos/scripts/build/install-codeql-bundle.sh new file mode 100644 index 0000000..ca05d23 --- /dev/null +++ b/images/macos/scripts/build/install-codeql-bundle.sh @@ -0,0 +1,32 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-codeql-bundle.sh +## Desc: Install CodeQL bundle +################################################################################ + +source ~/utils/utils.sh + +# Retrieve the CLI version of the latest CodeQL bundle. +defaults_json_path=$(download_with_retry https://raw.githubusercontent.com/github/codeql-action/v2/src/defaults.json) +bundle_version=$(jq -r '.cliVersion' $defaults_json_path) +bundle_tag_name="codeql-bundle-v$bundle_version" + +echo "Downloading CodeQL bundle $bundle_version..." +# Note that this is the all-platforms CodeQL bundle, to support scenarios where customers run +# different operating systems within containers. +archive_path=$(download_with_retry "https://github.com/github/codeql-action/releases/download/$bundle_tag_name/codeql-bundle.tar.gz") + +codeql_toolcache_path=$AGENT_TOOLSDIRECTORY/CodeQL/$bundle_version/x64 +mkdir -p $codeql_toolcache_path + +echo "Unpacking the downloaded CodeQL bundle archive..." +tar -xzf $archive_path -C $codeql_toolcache_path + +# Touch a file to indicate to the CodeQL Action that this bundle shipped with the toolcache. This is +# to support overriding the CodeQL version specified in defaults.json on GitHub Enterprise. +touch $codeql_toolcache_path/pinned-version + +# Touch a file to indicate to the toolcache that setting up CodeQL is complete. +touch $codeql_toolcache_path.complete + +invoke_tests "Common" "CodeQL Bundle" diff --git a/images/macos/scripts/build/install-common-utils.sh b/images/macos/scripts/build/install-common-utils.sh new file mode 100644 index 0000000..96a4682 --- /dev/null +++ b/images/macos/scripts/build/install-common-utils.sh @@ -0,0 +1,118 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-common-utils.sh +## Desc: Install utils listed in toolset file +################################################################################ + +source ~/utils/utils.sh + +# Monterey needs future review: +# aliyun-cli, gnupg, helm have issues with building from the source code. +# Added gmp for now, because toolcache ruby needs its libs. Remove it when php starts to build from source code. +common_packages=$(get_toolset_value '.brew.common_packages[]') +for package in $common_packages; do + echo "Installing $package..." + if is_Monterey && [[ $package == "xcbeautify" ]]; then + # Pin the version on Monterey as 2.0.x requires Xcode >=15.0 which is not available on OS12 + xcbeautify_path=$(download_with_retry "https://raw.githubusercontent.com/Homebrew/homebrew-core/d3653e83f9c029a3fddb828ac804b07ac32f7b3b/Formula/x/xcbeautify.rb") + brew install "$xcbeautify_path" + else + if [[ $package == "packer" ]]; then + # Packer has been deprecated in Homebrew. Use tap to install Packer. + brew install hashicorp/tap/packer + else + brew_smart_install "$package" + fi + fi +done + +cask_packages=$(get_toolset_value '.brew.cask_packages[]') +for package in $cask_packages; do + echo "Installing $package..." + if is_Monterey && [[ $package == "virtualbox" ]]; then + # Do not update VirtualBox on macOS 12 due to the issue with VMs in gurumediation state which blocks Vagrant on macOS: https://github.com/actions/runner-images/issues/8730 + # macOS host: Dropped all kernel extensions. VirtualBox relies fully on the hypervisor and vmnet frameworks provided by Apple now. + virtualbox_cask_path=$(download_with_retry "https://raw.githubusercontent.com/Homebrew/homebrew-cask/aa3c55951fc9d687acce43e5c0338f42c1ddff7b/Casks/virtualbox.rb") + brew install $virtualbox_cask_path + else + if is_Arm64 && [[ $package == "parallels" ]]; then + echo "Parallels installation is skipped for arm64 architecture" + else + brew install --cask $package + fi + fi +done + +# Load "Parallels International GmbH" +if is_Monterey || is_SonomaX64 || is_VenturaX64; then + sudo kextload /Applications/Parallels\ Desktop.app/Contents/Library/Extensions/10.9/prl_hypervisor.kext || true +fi + +# Execute AppleScript to change security preferences for macOS12, macOS13 and macOS14 +# System Preferences -> Security & Privacy -> General -> Unlock -> Allow -> Not now +if is_Monterey || is_SonomaX64 || is_VenturaX64; then + for retry in {4..0}; do + echo "Executing AppleScript to change security preferences. Retries left: $retry" + { + set -e + osascript -e 'tell application "System Events" to get application processes where visible is true' + if is_Monterey; then + osascript $HOME/utils/confirm-identified-developers.scpt $USER_PASSWORD + fi + + if is_VenturaX64; then + osascript $HOME/utils/confirm-identified-developers-macos13.scpt $USER_PASSWORD + fi + + if is_SonomaX64; then + osascript $HOME/utils/confirm-identified-developers-macos14.scpt $USER_PASSWORD + fi + } && break + + if [[ $retry -eq 0 ]]; then + echo "Executing AppleScript failed. No retries left" + exit 1 + fi + + echo "Executing AppleScript failed. Sleeping for 10 seconds and retrying" + sleep 10 + done +fi + +# Validate "Parallels International GmbH" kext +if is_Monterey || is_SonomaX64 || is_VenturaX64; then + + if is_Monterey; then + echo "Closing System Preferences window if it is still opened" + killall "System Preferences" || true + fi + + if is_SonomaX64 || is_VenturaX64; then + echo "Closing System Settings window if it is still opened" + killall "System Settings" || true + fi + + echo "Checking parallels kexts" + dbName="/var/db/SystemPolicyConfiguration/KextPolicy" + dbQuery="SELECT * FROM kext_policy WHERE bundle_id LIKE 'com.parallels.kext.%';" + kext=$(sudo sqlite3 $dbName "$dbQuery") + + if [[ -z $kext ]]; then + echo "Parallels International GmbH not found" + exit 1 + fi + + # Create env variable + url=$(brew info --json=v2 --installed | jq -r '.casks[] | select(.name[] == "Parallels Desktop").url') + if [[ -z $url ]]; then + echo "Unable to parse url for Parallels Desktop cask" + exit 1 + fi + echo "export PARALLELS_DMG_URL=$url" >> ${HOME}/.bashrc +fi + +# Install Azure DevOps extension for Azure Command Line Interface +az extension add -n azure-devops + +# Invoke tests for all basic tools +invoke_tests "BasicTools" diff --git a/images/macos/scripts/build/install-compilable-brew-packages.sh b/images/macos/scripts/build/install-compilable-brew-packages.sh new file mode 100644 index 0000000..b0f22c5 --- /dev/null +++ b/images/macos/scripts/build/install-compilable-brew-packages.sh @@ -0,0 +1,15 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-compilable-brew-packages.sh +## Desc: Install compilable brew packages +################################################################################ + +source ~/utils/utils.sh + +compilable_packages=$(get_toolset_value '.brew.compilable_packages[]') +for package in $compilable_packages; do + echo "Installing $package..." + brew_smart_install "$package" +done + +invoke_tests "Common" "Compiled" diff --git a/images/macos/scripts/build/install-dotnet.sh b/images/macos/scripts/build/install-dotnet.sh new file mode 100644 index 0000000..212c404 --- /dev/null +++ b/images/macos/scripts/build/install-dotnet.sh @@ -0,0 +1,57 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-dotnet.sh +## Desc: Install dotnet +################################################################################ + +source ~/utils/utils.sh + +export DOTNET_CLI_TELEMETRY_OPTOUT=1 + +arch=$(get_arch) + +# Download installer from dot.net and keep it locally +DOTNET_INSTALL_SCRIPT="https://dot.net/v1/dotnet-install.sh" +install_script_path=$(download_with_retry $DOTNET_INSTALL_SCRIPT) +chmod +x $install_script_path + +args_list=() +echo "Parsing dotnet SDK (except rc and preview versions) from .json..." + +dotnet_versions=($(get_toolset_value ".dotnet.arch[\"$arch\"].versions | .[]")) + +for dotnet_version in ${dotnet_versions[@]}; do + release_url="https://raw.githubusercontent.com/dotnet/core/main/release-notes/${dotnet_version}/releases.json" + releases_json_file=$(download_with_retry "$release_url") + + if [[ $dotnet_version == "6.0" ]]; then + args_list+=( + $(cat $releases_json_file | jq -r 'first(.releases[].sdks[]?.version | select(contains("preview") or contains("rc") | not))') + ) + else + args_list+=( + $(cat $releases_json_file | \ + jq -r '.releases[].sdk."version"' | grep -v -E '\-(preview|rc)\d*' | \ + sort -r | rev | uniq -s 2 | rev) + ) + fi +done + +for ARGS in ${args_list[@]}; do + $install_script_path --version $ARGS -NoPath --arch $arch +done + +# dotnet installer doesn't create symlink to executable in /user/local/bin +# Moreover at that moment /user/local/bin doesn't exist (though already added to $PATH) +ln -s ~/.dotnet/dotnet /usr/local/bin/dotnet + +# Validate installation +if [[ $(dotnet --list-sdks | wc -l) -lt "1" ]]; then + echo "The .NET Core SDK is not installed" + exit 1 +fi + +echo 'export PATH="$PATH:$HOME/.dotnet/tools"' >> $HOME/.bashrc +echo "Dotnet operations have been completed successfully..." + +invoke_tests "Common" ".NET" diff --git a/images/macos/scripts/build/install-edge.sh b/images/macos/scripts/build/install-edge.sh new file mode 100644 index 0000000..c423787 --- /dev/null +++ b/images/macos/scripts/build/install-edge.sh @@ -0,0 +1,62 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-edge.sh +## Desc: Install edge browser +################################################################################ + +source ~/utils/utils.sh + +echo "Installing Microsoft Edge..." +brew install --cask microsoft-edge + +EDGE_INSTALLATION_PATH="/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge" +edge_version=$("$EDGE_INSTALLATION_PATH" --version | cut -d' ' -f 3) +edge_version_major=$(echo $edge_version | cut -d'.' -f 1) + +echo "Version of Microsoft Edge: ${edge_version}" + +echo "Installing Microsoft Edge WebDriver..." + +edge_driver_version_file_path=$(download_with_retry "https://msedgedriver.azureedge.net/LATEST_RELEASE_${edge_version_major}_MACOS") +edge_driver_latest_version=$(iconv -f utf-16 -t utf-8 "$edge_driver_version_file_path" | tr -d '\r') +edge_driver_url="https://msedgedriver.azureedge.net/${edge_driver_latest_version}/edgedriver_mac64.zip" + +echo "Compatible version of WebDriver: ${edge_driver_latest_version}" + +edge_driver_archive_path=$(download_with_retry "$edge_driver_url") + +# Move webdriver to the separate directory to be consistent with the docs +# 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 + +EDGE_DRIVER_DIR="/usr/local/share/edge_driver" +mkdir -p $EDGE_DRIVER_DIR +unzip -qq $edge_driver_archive_path -d $EDGE_DRIVER_DIR +ln -s $EDGE_DRIVER_DIR/msedgedriver /usr/local/bin/msedgedriver + +echo "export EDGEWEBDRIVER=${EDGE_DRIVER_DIR}" >> ${HOME}/.bashrc + +# Configure Edge Updater to prevent auto update +# https://learn.microsoft.com/en-us/deployedge/edge-learnmore-edgeupdater-for-macos + +sudo mkdir "/Library/Managed Preferences" + +cat < /dev/null + + + + + updatePolicies + + global + + UpdateDefault + 3 + + + + +EOF + +sudo chown root:wheel "/Library/Managed Preferences/com.microsoft.EdgeUpdater.plist" + +invoke_tests "Browsers" "Edge" diff --git a/images/macos/scripts/build/install-firefox.sh b/images/macos/scripts/build/install-firefox.sh new file mode 100644 index 0000000..c20e253 --- /dev/null +++ b/images/macos/scripts/build/install-firefox.sh @@ -0,0 +1,19 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-firefox.sh +## Desc: Install firefox browser +################################################################################ + +source ~/utils/utils.sh + +echo "Installing Firefox..." +brew install --cask firefox + +echo "Installing Geckodriver..." +brew_smart_install "geckodriver" +geckoPath="$(brew --prefix geckodriver)/bin" + +echo "Add GECKOWEBDRIVER to bashrc..." +echo "export GECKOWEBDRIVER=${geckoPath}" >> ${HOME}/.bashrc + +invoke_tests "Browsers" "Firefox" diff --git a/images/macos/scripts/build/install-gcc.sh b/images/macos/scripts/build/install-gcc.sh new file mode 100644 index 0000000..9647d56 --- /dev/null +++ b/images/macos/scripts/build/install-gcc.sh @@ -0,0 +1,21 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-gcc.sh +## Desc: Install GCC +################################################################################ + +source ~/utils/utils.sh + +gccVersions=$(get_toolset_value '.gcc.versions | .[]') + +for gccVersion in $gccVersions; do + brew_smart_install "gcc@${gccVersion}" +done + +# Delete default gfortran link if it exists https://github.com/actions/runner-images/issues/1280 +gfortranPath=$(which gfortran) || true +if [[ $gfortranPath ]]; then + rm $gfortranPath +fi + +invoke_tests "Common" "GCC" diff --git a/images/macos/scripts/build/install-git.sh b/images/macos/scripts/build/install-git.sh new file mode 100644 index 0000000..adc3ecc --- /dev/null +++ b/images/macos/scripts/build/install-git.sh @@ -0,0 +1,38 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-git.sh +## Desc: Install Git and Git LFS +################################################################################ + +source ~/utils/utils.sh + +echo "Installing Git..." +brew_smart_install "git" + +git config --global --add safe.directory "*" + +echo "Installing Git LFS" +brew_smart_install "git-lfs" + +# Update global git config +git lfs install +# Update system git config +sudo git lfs install --system + +echo "Disable all the Git help messages..." +git config --global advice.pushUpdateRejected false +git config --global advice.pushNonFFCurrent false +git config --global advice.pushNonFFMatching false +git config --global advice.pushAlreadyExists false +git config --global advice.pushFetchFirst false +git config --global advice.pushNeedsForce false +git config --global advice.statusHints false +git config --global advice.statusUoption false +git config --global advice.commitBeforeMerge false +git config --global advice.resolveConflict false +git config --global advice.implicitIdentity false +git config --global advice.detachedHead false +git config --global advice.amWorkDir false +git config --global advice.rmHints false + +invoke_tests "Git" diff --git a/images/macos/scripts/build/install-golang.sh b/images/macos/scripts/build/install-golang.sh new file mode 100644 index 0000000..7a28868 --- /dev/null +++ b/images/macos/scripts/build/install-golang.sh @@ -0,0 +1,16 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-golang.sh +## Desc: Install Go +################################################################################ + +source ~/utils/utils.sh + +default_go_version=$(get_toolset_value '.go.default') +echo "Installing Go..." +brew_smart_install "go@${default_go_version}" + +# Create symlinks to preserve backward compatibility. Symlinks are not created when non-latest go is being installed +ln -sf $(brew --prefix go@${default_go_version})/bin/* /usr/local/bin/ + +invoke_tests "Common" "Go" diff --git a/images/macos/scripts/build/install-haskell.sh b/images/macos/scripts/build/install-haskell.sh new file mode 100644 index 0000000..d88bf61 --- /dev/null +++ b/images/macos/scripts/build/install-haskell.sh @@ -0,0 +1,31 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-haskell.sh +## Desc: Install Haskell +################################################################################ + +source ~/utils/utils.sh + +curl --proto '=https' --tlsv1.2 -fsSL https://get-ghcup.haskell.org | bash +export PATH="$HOME/.ghcup/bin:$PATH" +echo 'export PATH="$PATH:$HOME/.ghcup/bin"' >> $HOME/.bashrc + +# ghcup output looks like this "ghc 8.6.4 base-4.12.0.0 hls-powered", need to take all the first versions only(8.6.4 in that case) and avoid pre-release ones +availableVersions=$(ghcup list -t ghc -r | grep -v "prerelease" | awk '{print $2}') + +# Install 3 latest major versions(For instance 8.6.5, 8.8.4, 8.10.2) +minorMajorVersions=$(echo "$availableVersions" | cut -d"." -f 1,2 | uniq | tail -n3) +for majorMinorVersion in $minorMajorVersions; do + fullVersion=$(echo "$availableVersions" | grep "$majorMinorVersion." | tail -n1) + echo "install ghc version $fullVersion..." + ghcup install $fullVersion + ghcup set $fullVersion +done + +echo "install cabal..." +ghcup install-cabal + +echo "Updating stack..." +ghcup install stack latest + +invoke_tests "Haskell" diff --git a/images/macos/scripts/build/install-homebrew.sh b/images/macos/scripts/build/install-homebrew.sh new file mode 100644 index 0000000..4a72cf2 --- /dev/null +++ b/images/macos/scripts/build/install-homebrew.sh @@ -0,0 +1,43 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-homebrew.sh +## Desc: Install Homebrew +################################################################################ + +source ~/utils/utils.sh + +arch=$(get_arch) + +echo "Installing Homebrew..." +homebrew_installer_path=$(download_with_retry "https://raw.githubusercontent.com/Homebrew/install/master/install.sh") +/bin/bash $homebrew_installer_path + +if [[ $arch == "arm64" ]]; then + /opt/homebrew/bin/brew update + /opt/homebrew/bin/brew upgrade + /opt/homebrew/bin/brew upgrade --cask + /opt/homebrew/bin/brew cleanup + eval "$(/opt/homebrew/bin/brew shellenv)" +fi + +git clone https://github.com/Homebrew/homebrew-cask $(brew --repository)/Library/Taps/homebrew/homebrew-cask --origin=origin --template= --config core.fsmonitor=false --depth 1 +git clone https://github.com/Homebrew/homebrew-core $(brew --repository)/Library/Taps/homebrew/homebrew-core --origin=origin --template= --config core.fsmonitor=false --depth 1 + +brew tap homebrew/cask +brew tap homebrew/core + +echo "Disabling Homebrew analytics..." +brew analytics off + +# jq is required for further installation scripts +echo "Installing jq..." +brew_smart_install jq + +echo "Installing curl..." +brew_smart_install curl + +echo "Installing wget..." +brew_smart_install "wget" + +# init brew bundle feature +brew tap Homebrew/bundle diff --git a/images/macos/scripts/build/install-llvm.sh b/images/macos/scripts/build/install-llvm.sh new file mode 100644 index 0000000..3ba7bff --- /dev/null +++ b/images/macos/scripts/build/install-llvm.sh @@ -0,0 +1,13 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-llvm.sh +## Desc: Install LLVM +################################################################################ + +source ~/utils/utils.sh + +llvmVersion=$(get_toolset_value '.llvm.version') + +brew_smart_install "llvm@${llvmVersion}" + +invoke_tests "LLVM" diff --git a/images/macos/scripts/build/install-miniconda.sh b/images/macos/scripts/build/install-miniconda.sh new file mode 100644 index 0000000..7d9ea2f --- /dev/null +++ b/images/macos/scripts/build/install-miniconda.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-miniconda.sh +## Desc: Install Miniconda +################################################################################ + +source ~/utils/utils.sh + +miniconda_installer_path=$(download_with_retry "https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh") +chmod +x $miniconda_installer_path +sudo $miniconda_installer_path -b -p /usr/local/miniconda + +# Chmod with full permissions recursively to avoid permissions restrictions +sudo chmod -R 777 /usr/local/miniconda + +sudo ln -s /usr/local/miniconda/bin/conda /usr/local/bin/conda + +if [[ -d $HOME/.conda ]]; then + sudo chown -R $USER $HOME/.conda +fi + +echo "export CONDA=/usr/local/miniconda" >> $HOME/.bashrc + +invoke_tests "Common" "Miniconda" diff --git a/images/macos/scripts/build/install-mongodb.sh b/images/macos/scripts/build/install-mongodb.sh new file mode 100644 index 0000000..c603d38 --- /dev/null +++ b/images/macos/scripts/build/install-mongodb.sh @@ -0,0 +1,23 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-mongodb.sh +## Desc: Install MongoDB +################################################################################ + +source ~/utils/utils.sh + +# MongoDB object-value database +# Install latest release version of MongoDB Community Edition +# https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x +toolsetVersion=$(get_toolset_value '.mongodb.version') + +brew tap mongodb/brew +versionToInstall=$(brew search --formulae /mongodb-community@$toolsetVersion/ | awk -F'/' '{print $3}' | tail -1) +echo "Installing mongodb $versionToInstall" +brew_smart_install $versionToInstall + +if ! which mongo ; then + brew link $versionToInstall +fi + +invoke_tests "Databases" "MongoDB" diff --git a/images/macos/scripts/build/install-mono.sh b/images/macos/scripts/build/install-mono.sh new file mode 100644 index 0000000..3ac147f --- /dev/null +++ b/images/macos/scripts/build/install-mono.sh @@ -0,0 +1,51 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-mono.sh +## Desc: Install Mono Framework +################################################################################ + +source ~/utils/utils.sh + +# Install Mono Framework +mono_version_full=$(get_toolset_value '.mono.framework.version') +mono_pkg_sha256=$(get_toolset_value '.mono.framework.sha256') +mono_version=$(echo $mono_version_full | cut -d. -f 1,2,3) +mono_version_short=$(echo $mono_version_full | cut -d. -f 1,2) +mono_pkg_url="https://download.mono-project.com/archive/${mono_version}/macos-10-universal/MonoFramework-MDK-${mono_version_full}.macos10.xamarin.universal.pkg" +MONO_VERSIONS_PATH='/Library/Frameworks/Mono.framework/Versions' + +mono_pkg_path=$(download_with_retry $mono_pkg_url) +use_checksum_comparison $mono_pkg_path $mono_pkg_sha256 +echo "Installing Mono Framework ${mono_version_full}..." +sudo installer -pkg $mono_pkg_path -target / + +# Download and install NUnit console +nunit_version=$(get_toolset_value '.mono.nunit.version') +nunit_archive_url="https://github.com/nunit/nunit-console/releases/download/${nunit_version}/NUnit.Console-${nunit_version}.zip" +nunit_archive_sha256=$(get_toolset_value '.mono.nunit.sha256') +NUNIT_PATH="/Library/Developer/nunit" +nunit_version_path=$NUNIT_PATH/$nunit_version + +nunit_archive_path=$(download_with_retry $nunit_archive_url) +use_checksum_comparison $nunit_archive_path $nunit_archive_sha256 +echo "Installing NUnit ${nunit_version}..." +sudo mkdir -p $nunit_version_path +sudo unzip -q $nunit_archive_path -d $nunit_version_path + +# Create a wrapper script for nunit3-console +echo "Creating nunit3-console wrapper..." +nunit3_console_wrapper=$(mktemp) +cat < "$nunit3_console_wrapper" +#!/bin/bash -e -o pipefail +exec ${MONO_VERSIONS_PATH}/${mono_version}/bin/mono --debug \$MONO_OPTIONS $nunit_version_path/nunit3-console.exe "\$@" +EOF +cat $nunit3_console_wrapper +sudo chmod +x $nunit3_console_wrapper +sudo mv $nunit3_console_wrapper "${MONO_VERSIONS_PATH}/${mono_version}/Commands/nunit3-console" + +# Create a symlink for the short version of Mono (e.g., 6.12) +echo "Creating short symlink '${mono_version_short}'..." +sudo ln -s ${MONO_VERSIONS_PATH}/${mono_version} ${MONO_VERSIONS_PATH}/${mono_version_short} + +# Invoke tests for Xamarin and Mono +invoke_tests "Xamarin" "Mono" diff --git a/images/macos/scripts/build/install-nginx.sh b/images/macos/scripts/build/install-nginx.sh new file mode 100644 index 0000000..0816b7e --- /dev/null +++ b/images/macos/scripts/build/install-nginx.sh @@ -0,0 +1,12 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-nginx.sh +## Desc: Install Nginx +################################################################################ + +source ~/utils/utils.sh + +brew_smart_install nginx +sudo sed -Ei '' 's/listen.*/listen 80;/' $(brew --prefix)/etc/nginx/nginx.conf + +invoke_tests "WebServers" "Nginx" diff --git a/images/macos/scripts/build/install-node.sh b/images/macos/scripts/build/install-node.sh new file mode 100644 index 0000000..9e8a084 --- /dev/null +++ b/images/macos/scripts/build/install-node.sh @@ -0,0 +1,27 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-node.sh +## Desc: Install Node.js +################################################################################ + +source ~/utils/utils.sh + +defaultVersion=$(get_toolset_value '.node.default') + +echo "Installing Node.js $defaultVersion" +brew_smart_install "node@$defaultVersion" +brew link node@$defaultVersion --force --overwrite + +echo Installing yarn... +yarn_installer_path=$(download_with_retry "https://yarnpkg.com/install.sh") +bash $yarn_installer_path + +if is_Monterey; then + npm_global_packages=$(get_toolset_value '.npm.global_packages[].name') + for module in ${npm_global_packages[@]}; do + echo "Install $module" + npm install -g $module +done +fi + +invoke_tests "Node" "Node.js" diff --git a/images/macos/scripts/build/install-nvm.sh b/images/macos/scripts/build/install-nvm.sh new file mode 100644 index 0000000..35731cf --- /dev/null +++ b/images/macos/scripts/build/install-nvm.sh @@ -0,0 +1,36 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-nvm.sh +## Desc: Install node version manager +################################################################################ + +source ~/utils/utils.sh + +[[ -n $API_PAT ]] && authString=(-H "Authorization: token ${API_PAT}") + +nvm_version=$(get_toolset_value '.node.nvm_installer') +if [[ -z $nvm_version || "$nvm_version" == "latest" ]]; then + nvm_version=$(curl "${authString[@]}" -fsSL https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name') +fi + +if [[ $nvm_version != "v*" ]]; then + nvm_version="v${nvm_version}" +fi + +nvm_installer_path=$(download_with_retry "https://raw.githubusercontent.com/nvm-sh/nvm/$nvm_version/install.sh") + +if bash $nvm_installer_path; then + source ~/.bashrc + nvm --version + for version in $(get_toolset_value '.node.nvm_versions[]'); do + nvm install "v${version}" + done + + # set system node as default + nvm alias default system + echo "Node version manager has been installed successfully" +else + echo "Node version manager installation failed" +fi + +invoke_tests "Node" "nvm" diff --git a/images/macos/scripts/build/install-openjdk.sh b/images/macos/scripts/build/install-openjdk.sh new file mode 100644 index 0000000..6c7ec36 --- /dev/null +++ b/images/macos/scripts/build/install-openjdk.sh @@ -0,0 +1,101 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-openjdk.sh +## Desc: Install openjdk +################################################################################ + +source ~/utils/utils.sh + +createEnvironmentVariable() { + local JAVA_VERSION=$1 + local DEFAULT=$2 + + if [[ $arch == "arm64" ]]; then + INSTALL_PATH_PATTERN=$(echo ${AGENT_TOOLSDIRECTORY}/Java_Temurin-Hotspot_jdk/${JAVA_VERSION}*/arm64/Contents/Home/) + else + INSTALL_PATH_PATTERN=$(echo ${AGENT_TOOLSDIRECTORY}/Java_Temurin-Hotspot_jdk/${JAVA_VERSION}*/x64/Contents/Home/) + fi + + if [[ ${DEFAULT} == "True" ]]; then + echo "Setting up JAVA_HOME variable to ${INSTALL_PATH_PATTERN}" + echo "export JAVA_HOME=${INSTALL_PATH_PATTERN}" >> ${HOME}/.bashrc + fi + + if [[ $arch == "arm64" ]]; then + echo "Setting up JAVA_HOME_${JAVA_VERSION}_arm64 variable to ${INSTALL_PATH_PATTERN}" + echo "export JAVA_HOME_${JAVA_VERSION}_arm64=${INSTALL_PATH_PATTERN}" >> ${HOME}/.bashrc + else + echo "Setting up JAVA_HOME_${JAVA_VERSION}_X64 variable to ${INSTALL_PATH_PATTERN}" + echo "export JAVA_HOME_${JAVA_VERSION}_X64=${INSTALL_PATH_PATTERN}" >> ${HOME}/.bashrc + fi +} + +installOpenJDK() { + local JAVA_VERSION=$1 + + # Get link for Java binaries and Java version + hotspot_json_path=$(download_with_retry "https://api.adoptium.net/v3/assets/latest/${JAVA_VERSION}/hotspot") + + if [[ $arch == "arm64" ]]; then + asset=$(jq -r '.[] | select(.binary.os=="mac" and .binary.image_type=="jdk" and .binary.architecture=="aarch64")' "$hotspot_json_path") + else + asset=$(jq -r '.[] | select(.binary.os=="mac" and .binary.image_type=="jdk" and .binary.architecture=="x64")' "$hotspot_json_path") + fi + + archive_url=$(echo "$asset" | jq -r '.binary.package.link') + fullVersion=$(echo "$asset" | jq -r '.version.semver' | tr '+' '-') + + # Remove 'LTS' suffix from the version if present + fullVersion="${fullVersion//.LTS/}" + + JAVA_TOOLCACHE_PATH=${AGENT_TOOLSDIRECTORY}/Java_Temurin-Hotspot_jdk + javaToolcacheVersionPath=$JAVA_TOOLCACHE_PATH/${fullVersion} + + if [[ $arch == "arm64" ]]; then + javaToolcacheVersionArchPath=${javaToolcacheVersionPath}/arm64 + else + javaToolcacheVersionArchPath=${javaToolcacheVersionPath}/x64 + fi + + # Download and extract Java binaries + archive_path=$(download_with_retry $archive_url) + + echo "Creating ${javaToolcacheVersionArchPath} directory" + mkdir -p ${javaToolcacheVersionArchPath} + + tar -xf $archive_path -C ${javaToolcacheVersionArchPath} --strip-components=1 + + # Create complete file + if [[ $arch == "arm64" ]]; then + touch ${javaToolcacheVersionPath}/arm64.complete + else + touch ${javaToolcacheVersionPath}/x64.complete + fi + + # Create a symlink to '/Library/Java/JavaVirtualMachines' + # so '/usr/libexec/java_home' will be able to find Java + sudo ln -sf ${javaToolcacheVersionArchPath} /Library/Java/JavaVirtualMachines/Temurin-Hotspot-${JAVA_VERSION}.jdk +} + +arch=$(get_arch) +defaultVersion=$(get_toolset_value '.java.'$arch'.default') +jdkVersionsToInstall=($(get_toolset_value ".java.${arch}.versions[]")) + +for jdkVersionToInstall in ${jdkVersionsToInstall[@]}; do + installOpenJDK ${jdkVersionToInstall} + + if [[ ${jdkVersionToInstall} == ${defaultVersion} ]] + then + createEnvironmentVariable ${jdkVersionToInstall} True + else + createEnvironmentVariable ${jdkVersionToInstall} False + fi +done + +echo Installing Maven... +brew_smart_install "maven" + +echo Installing Gradle ... +brew_smart_install "gradle" + +invoke_tests "Java" diff --git a/images/macos/scripts/build/install-openssl.sh b/images/macos/scripts/build/install-openssl.sh new file mode 100644 index 0000000..16f11fe --- /dev/null +++ b/images/macos/scripts/build/install-openssl.sh @@ -0,0 +1,25 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-openssl.sh +## Desc: Install openssl +################################################################################ + +source ~/utils/utils.sh + +echo "Install openssl@1.1" +brew_smart_install "openssl@1.1" + +if ! is_Arm64; then + # Symlink brew openssl@1.1 to `/usr/local/bin` as Homebrew refuses + ln -sf $(brew --prefix openssl@1.1)/bin/openssl /usr/local/bin/openssl +else + # arm64 has a different installation prefix for brew + ln -sf $(brew --prefix openssl@1.1)/bin/openssl /opt/homebrew/bin/openssl +fi + +if ! is_Arm64; then + # Most of build systems and scripts look up ssl here + ln -sf $(brew --cellar openssl@1.1)/1.1* /usr/local/opt/openssl +fi + +invoke_tests "OpenSSL" diff --git a/images/macos/scripts/build/install-php.sh b/images/macos/scripts/build/install-php.sh new file mode 100644 index 0000000..757f8f5 --- /dev/null +++ b/images/macos/scripts/build/install-php.sh @@ -0,0 +1,16 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-php.sh +## Desc: Install PHP +################################################################################ + +source ~/utils/utils.sh + +echo Installing PHP +phpVersionToolset=$(get_toolset_value '.php.version') +brew_smart_install "php@${phpVersionToolset}" + +echo Installing composer +brew_smart_install "composer" + +invoke_tests "PHP" diff --git a/images/macos/scripts/build/install-pipx-packages.sh b/images/macos/scripts/build/install-pipx-packages.sh new file mode 100644 index 0000000..0b4a8f9 --- /dev/null +++ b/images/macos/scripts/build/install-pipx-packages.sh @@ -0,0 +1,18 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-pipx-packages.sh +## Desc: Install Pipx Packages +################################################################################ + +source ~/utils/utils.sh + +export PATH="$PATH:/opt/pipx_bin" + +pipx_packages=$(get_toolset_value '.pipx[].package') + +for package in $pipx_packages; do + echo "Install $package into default python" + pipx install $package +done + +invoke_tests "PipxPackages" diff --git a/images/macos/scripts/build/install-postgresql.sh b/images/macos/scripts/build/install-postgresql.sh new file mode 100644 index 0000000..0f28b75 --- /dev/null +++ b/images/macos/scripts/build/install-postgresql.sh @@ -0,0 +1,38 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-postgresql.sh +## Desc: Install PostgreSQL +################################################################################ + +source ~/utils/utils.sh + +# Fetch PostgreSQL version to install from the toolset +toolsetVersion=$(get_toolset_value '.postgresql.version') + +# Install latest version of PostgreSQL +brew_smart_install postgresql@$toolsetVersion + +# Service PostgreSQL should be started before use +postgreService=$(brew services list | grep -oe "postgresql\S*") +brew services start $postgreService + +# Verify PostgreSQL is ready for accept incoming connections +echo "Check PostgreSQL service is running" +i=10 +COMMAND='pg_isready' +while [[ $i -gt 0 ]]; do + echo "Check PostgreSQL service status" + eval $COMMAND && break + ((i--)) + if [[ $i == 0 ]]; then + echo "PostgreSQL service not ready, all attempts exhausted" + exit 1 + fi + echo "PostgreSQL service not ready, wait 10 more sec, attempts left: $i" + sleep 10 +done + +# Stop PostgreSQL +brew services stop $postgreService + +invoke_tests "Databases" "PostgreSQL" diff --git a/images/macos/scripts/build/install-powershell.sh b/images/macos/scripts/build/install-powershell.sh new file mode 100644 index 0000000..d6c2a9f --- /dev/null +++ b/images/macos/scripts/build/install-powershell.sh @@ -0,0 +1,67 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-powershell.sh +## Desc: Install PowerShell +################################################################################ + +source ~/utils/utils.sh + +echo Installing PowerShell... +arch=$(get_arch) + +metadata_json_path=$(download_with_retry "https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/metadata.json") +pwshVersionToolset=$(get_toolset_value '.pwsh.version') +pwshVersions=$(jq -r '.LTSReleaseTag[]' $metadata_json_path) + +for version in ${pwshVersions[@]}; do + if [[ "$version" =~ "$pwshVersionToolset" ]]; then + download_url=$(resolve_github_release_asset_url "PowerShell/PowerShell" "contains(\"osx-$arch.pkg\")" "$version" "$API_PAT") + break + fi +done + +pkg_path=$(download_with_retry $download_url) + +# 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 +sudo xattr -rd com.apple.quarantine $pkg_path + +sudo installer -pkg $pkg_path -target / + +# Install PowerShell modules +psModules=$(get_toolset_value '.powershellModules[].name') +for module in ${psModules[@]}; do + echo "Installing $module module" + moduleVersions="$(get_toolset_value ".powershellModules[] | select(.name==\"$module\") | .versions[]?")" + if [[ -z $moduleVersions ]];then + # Check MacOS architecture and sudo on Arm64 + if [[ $arch == "arm64" ]]; then + sudo pwsh -command "& {Install-Module $module -Force -Scope AllUsers}" + else + pwsh -command "& {Install-Module $module -Force -Scope AllUsers}" + fi + else + for version in ${moduleVersions[@]}; do + # Check MacOS architecture and sudo on Arm64 + if [[ $arch == "arm64" ]]; then + echo " - $version" + sudo pwsh -command "& {Install-Module $module -RequiredVersion $version -Force -Scope AllUsers}" + else + echo " - $version" + pwsh -command "& {Install-Module $module -RequiredVersion $version -Force -Scope AllUsers}" + fi + done + fi +done + +# Fix permission root => runner after installing powershell for arm64 arch +if [[ $arch == "arm64" ]]; then + sudo chown -R $USER ~/.local ~/.cache ~/.config +fi + +# A dummy call to initialize .IdentityService directory +pwsh -command "& {Import-Module Az}" + +# powershell link was removed in powershell-6.0.0-beta9 +sudo ln -s /usr/local/bin/pwsh /usr/local/bin/powershell + +invoke_tests "Powershell" diff --git a/images/macos/scripts/build/install-pypy.sh b/images/macos/scripts/build/install-pypy.sh new file mode 100644 index 0000000..08f4ca7 --- /dev/null +++ b/images/macos/scripts/build/install-pypy.sh @@ -0,0 +1,87 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-pypy.sh +## Desc: Install PyPy +################################################################################ + +source ~/utils/utils.sh + +InstallPyPy() { + local package_url=$1 + + PACKAGE_TAR_NAME=$(basename $package_url) + echo "Downloading tar archive '$PACKAGE_TAR_NAME'" + archive_path=$(download_with_retry $package_url) + + echo "Expand '$PACKAGE_TAR_NAME' to the /tmp folder" + tar xf $archive_path -C /tmp + + # Get Python version + PACKAGE_NAME=${PACKAGE_TAR_NAME/.tar.bz2/} + MAJOR_VERSION=$(echo ${PACKAGE_NAME/pypy/} | cut -d. -f1) + PYTHON_MAJOR="python$MAJOR_VERSION" + + if [[ $MAJOR_VERSION != 2 ]]; then + PYPY_MAJOR="pypy$MAJOR_VERSION" + else + PYPY_MAJOR="pypy" + fi + + PACKAGE_TEMP_FOLDER="/tmp/$PACKAGE_NAME" + 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]))") + PYPY_FULL_VERSION=$("$PACKAGE_TEMP_FOLDER/bin/$PYPY_MAJOR" -c "import sys;print('{}.{}.{}'.format(*sys.pypy_version_info[0:3]))") + echo "Put '$PYPY_FULL_VERSION' to PYPY_VERSION file" + echo $PYPY_FULL_VERSION > "$PACKAGE_TEMP_FOLDER/PYPY_VERSION" + + # PyPy folder structure + PYPY_TOOLCACHE_PATH=$AGENT_TOOLSDIRECTORY/PyPy + PYPY_TOOLCACHE_VERSION_PATH=$PYPY_TOOLCACHE_PATH/$PYTHON_FULL_VERSION + PYPY_TOOLCACHE_VERSION_ARCH_PATH=$PYPY_TOOLCACHE_VERSION_PATH/x64 + + echo "Check if PyPy hostedtoolcache folder exist..." + if [[ ! -d $PYPY_TOOLCACHE_PATH ]]; then + mkdir -p $PYPY_TOOLCACHE_PATH + fi + + echo "Create PyPy '$PYPY_TOOLCACHE_VERSION_PATH' folder" + mkdir $PYPY_TOOLCACHE_VERSION_PATH + + echo "Move PyPy $PACKAGE_TEMP_FOLDER binaries to $PYPY_TOOLCACHE_VERSION_ARCH_PATH folder" + mv $PACKAGE_TEMP_FOLDER $PYPY_TOOLCACHE_VERSION_ARCH_PATH + + echo "Create additional symlinks (Required for UsePythonVersion Azure DevOps task)" + cd $PYPY_TOOLCACHE_VERSION_ARCH_PATH/bin + + PYPY_FULL_VERSION=$(./$PYPY_MAJOR -c "import sys;print('{}.{}.{}'.format(*sys.pypy_version_info[0:3]))") + echo "PYPY_FULL_VERSION is $PYPY_FULL_VERSION" + echo $PYPY_FULL_VERSION > "PYPY_VERSION" + + # Starting from PyPy 7.3.4 these links are already included in the package + [[ -f ./$PYTHON_MAJOR ]] || ln -s $PYPY_MAJOR $PYTHON_MAJOR + [[ -f ./python ]] || ln -s $PYTHON_MAJOR python + + chmod +x ./python ./$PYTHON_MAJOR + + echo "Install latest Pip" + ./python -m ensurepip + ./python -m pip install --ignore-installed pip + + echo "Create complete file" + touch $PYPY_TOOLCACHE_VERSION_PATH/x64.complete +} + +arch=$(get_arch) +versions_json_path=$(download_with_retry "https://downloads.python.org/pypy/versions.json") +toolsetVersions=$(get_toolset_value '.toolcache[] | select(.name | contains("PyPy")) | .arch.'$arch'.versions[]') + +for toolsetVersion in $toolsetVersions; do + latestMajorPyPyVersion=$(cat $versions_json_path | + jq -r --arg toolsetVersion $toolsetVersion '.[] + | select((.python_version | startswith($toolsetVersion)) and .stable == true).files[] + | select(.platform == "darwin").download_url' | head -1) + if [[ -z $latestMajorPyPyVersion ]]; then + echo "Failed to get PyPy version $toolsetVersion" + exit 1 + fi + InstallPyPy $latestMajorPyPyVersion +done diff --git a/images/macos/scripts/build/install-python.sh b/images/macos/scripts/build/install-python.sh new file mode 100644 index 0000000..426cc43 --- /dev/null +++ b/images/macos/scripts/build/install-python.sh @@ -0,0 +1,57 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-python.sh +## Desc: Install Python +################################################################################ + +source ~/utils/utils.sh + +echo "Installing Python Tooling" + +if is_Monterey; then + echo "Install latest Python 2" + python2_pkg=$(download_with_retry "https://www.python.org/ftp/python/2.7.18/python-2.7.18-macosx10.9.pkg") + python2_pkg_sha256="c570f38b05dd8b112ad21b418cdf51a9816d62f9f44746452739d421be24d50c" + use_checksum_comparison $python2_pkg $python2_pkg_sha256 + + choice_changes_xml=$(mktemp /tmp/python2_choice_changes.xml.XXXXXX) + sudo installer -showChoiceChangesXML -pkg $python2_pkg -target / | tee $choice_changes_xml > /dev/null + + # To avoid symlink conflicts, remove tools installation in /usr/local/bin using installer choices + xmllint --shell $choice_changes_xml <> ${HOME}/.bashrc +echo "export PIPX_HOME=${PIPX_HOME}" >> ${HOME}/.bashrc +echo 'export PATH="$PIPX_BIN_DIR:$PATH"' >> ${HOME}/.bashrc + +invoke_tests "Python" diff --git a/images/macos/scripts/build/install-rosetta.sh b/images/macos/scripts/build/install-rosetta.sh new file mode 100644 index 0000000..3eeec7f --- /dev/null +++ b/images/macos/scripts/build/install-rosetta.sh @@ -0,0 +1,8 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-rosetta.sh +## Desc: Install Rosetta +################################################################################ + +echo "Installing Rosetta" +/usr/sbin/softwareupdate --install-rosetta --agree-to-license diff --git a/images/macos/scripts/build/install-ruby.sh b/images/macos/scripts/build/install-ruby.sh new file mode 100644 index 0000000..e93ca6a --- /dev/null +++ b/images/macos/scripts/build/install-ruby.sh @@ -0,0 +1,60 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-ruby.sh +## Desc: Install Ruby +################################################################################ + +source ~/utils/utils.sh + +arch=$(get_arch) +DEFAULT_RUBY_VERSION=$(get_toolset_value '.ruby.default') +echo "Installing Ruby..." +brew_smart_install "ruby@${DEFAULT_RUBY_VERSION}" +if [[ $arch == "arm64" ]]; then + export PATH=/opt/homebrew/opt/ruby@${DEFAULT_RUBY_VERSION}/bin:$PATH +else + export PATH=/usr/local/opt/ruby@${DEFAULT_RUBY_VERSION}/bin:$PATH +fi + +GEM_PATH=$(gem env|awk '/EXECUTABLE DIRECTORY/ {print $4}') +echo "GEM_PATH=$GEM_PATH" >> $HOME/.bashrc +if [[ $arch == "arm64" ]]; then + echo 'export PATH="$GEM_PATH:/opt/homebrew/opt/ruby@'${DEFAULT_RUBY_VERSION}'/bin:$PATH"' >> $HOME/.bashrc +else + echo 'export PATH="$GEM_PATH:/usr/local/opt/ruby@'${DEFAULT_RUBY_VERSION}'/bin:$PATH"' >> $HOME/.bashrc +fi + +if ! is_Arm64; then + echo "Install Ruby from toolset..." + [ -n "$API_PAT" ] && authString=(-H "Authorization: token ${API_PAT}") + PACKAGE_TAR_NAMES=$(curl "${authString[@]}" -fsSL "https://api.github.com/repos/ruby/ruby-builder/releases/latest" | jq -r '.assets[].name') + TOOLSET_VERSIONS=$(get_toolset_value '.toolcache[] | select(.name | contains("Ruby")) | .arch.'$arch'.versions[]') + RUBY_PATH=$AGENT_TOOLSDIRECTORY/Ruby + + echo "Check if Ruby hostedtoolcache folder exists..." + if [[ ! -d $RUBY_PATH ]]; then + mkdir -p $RUBY_PATH + fi + echo "ruby path - $RUBY_PATH " + for TOOLSET_VERSION in ${TOOLSET_VERSIONS[@]}; do + PACKAGE_TAR_NAME=$(echo "$PACKAGE_TAR_NAMES" | grep "^ruby-${TOOLSET_VERSION}-macos-latest.tar.gz$" | egrep -v "rc|preview" | sort -V | tail -1) + RUBY_VERSION=$(echo "$PACKAGE_TAR_NAME" | cut -d'-' -f 2) + RUBY_VERSION_PATH="$RUBY_PATH/$RUBY_VERSION" + + echo "Create Ruby $RUBY_VERSION directory..." + mkdir -p $RUBY_VERSION_PATH + + echo "Downloading tar archive $PACKAGE_TAR_NAME" + ARCHIVE_PATH=$(download_with_retry "https://github.com/ruby/ruby-builder/releases/download/toolcache/${PACKAGE_TAR_NAME}") + + echo "Expand $PACKAGE_TAR_NAME to the $RUBY_VERSION_PATH folder" + tar xf $ARCHIVE_PATH -C $RUBY_VERSION_PATH + COMPLETE_FILE_PATH=$RUBY_VERSION_PATH/x64.complete + if [[ ! -f $COMPLETE_FILE_PATH ]]; then + echo "Create complete file" + touch $COMPLETE_FILE_PATH + fi + done +fi + +invoke_tests "Ruby.$arch" diff --git a/images/macos/scripts/build/install-rubygems.sh b/images/macos/scripts/build/install-rubygems.sh new file mode 100644 index 0000000..02caefe --- /dev/null +++ b/images/macos/scripts/build/install-rubygems.sh @@ -0,0 +1,23 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-rubygems.sh +## Desc: Install RubyGems +################################################################################ + +source ~/utils/utils.sh + +echo "Updating RubyGems..." +gem update --system + +# Temporarily install activesupport 7.0.8 due to compatibility issues with cocoapods https://github.com/CocoaPods/CocoaPods/issues/12081 +gem install activesupport -v 7.0.8 + +gemsToInstall=$(get_toolset_value '.ruby.rubygems | .[]') +if [[ -n $gemsToInstall ]]; then + for gem in $gemsToInstall; do + echo "Installing gem $gem" + gem install $gem + done +fi + +invoke_tests "RubyGem" diff --git a/images/macos/scripts/build/install-rust.sh b/images/macos/scripts/build/install-rust.sh new file mode 100644 index 0000000..eb9f9f1 --- /dev/null +++ b/images/macos/scripts/build/install-rust.sh @@ -0,0 +1,28 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-rust.sh +## Desc: Install Rust +################################################################################ + +source ~/utils/utils.sh + +echo "Installing Rustup..." +brew_smart_install "rustup-init" + +echo "Installing Rust language..." +rustup-init -y --no-modify-path --default-toolchain=stable --profile=minimal + +echo "Initialize environment variables..." +CARGO_HOME=$HOME/.cargo + +echo "Install common tools..." +rustup component add rustfmt clippy + +if is_Monterey; then + cargo install bindgen-cli cbindgen cargo-audit cargo-outdated +fi + +echo "Cleanup Cargo registry cached data..." +rm -rf $CARGO_HOME/registry/* + +invoke_tests "Rust" diff --git a/images/macos/scripts/build/install-safari.sh b/images/macos/scripts/build/install-safari.sh new file mode 100644 index 0000000..a00ef48 --- /dev/null +++ b/images/macos/scripts/build/install-safari.sh @@ -0,0 +1,20 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-safari.sh +## Desc: Install Safari browser +################################################################################ + +echo "Enabling safari driver..." +# https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari +# Safari’s executable is located at /usr/bin/safaridriver +# Configure Safari to Enable WebDriver Support +sudo safaridriver --enable + +echo "Enabling the 'Allow Remote Automation' option in Safari's Develop menu" +mkdir -p $HOME/Library/WebDriver +safari_plist="$HOME/Library/WebDriver/com.apple.Safari.plist" +# "|| true" is needed to suppress exit code 1 in case if property or file doesn't exist +/usr/libexec/PlistBuddy -c 'delete AllowRemoteAutomation' $safari_plist || true +/usr/libexec/PlistBuddy -c 'add AllowRemoteAutomation bool true' $safari_plist + +invoke_tests "Browsers" "Safari" diff --git a/images/macos/scripts/build/install-swiftlint.sh b/images/macos/scripts/build/install-swiftlint.sh new file mode 100644 index 0000000..1e781ca --- /dev/null +++ b/images/macos/scripts/build/install-swiftlint.sh @@ -0,0 +1,21 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-swiftlint.sh +## Desc: Install SwiftLint +################################################################################ + +source ~/utils/utils.sh + +echo "Installing Swiftlint..." +if is_Monterey; then + # SwiftLint now requires Xcode 15.3 or higher to build https://github.com/realm/SwiftLint/releases/tag/0.55.1 + COMMIT=d91dabd087cb0b906c92a825df9e5e5e1a4f59f8 + FORMULA_URL="https://raw.githubusercontent.com/Homebrew/homebrew-core/$COMMIT/Formula/s/swiftlint.rb" + + curl -fsSL $FORMULA_URL > $(find $(brew --repository) -name swiftlint.rb) + HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_FROM_API=1 brew install swiftlint +else + brew_smart_install "swiftlint" +fi + +invoke_tests "Linters" "SwiftLint" diff --git a/images/macos/scripts/build/install-vcpkg.sh b/images/macos/scripts/build/install-vcpkg.sh new file mode 100644 index 0000000..13acc0f --- /dev/null +++ b/images/macos/scripts/build/install-vcpkg.sh @@ -0,0 +1,27 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-vcpkg.sh +## Desc: Install vcpkg +################################################################################ + +source ~/utils/utils.sh + +# Set env variable for vcpkg +VCPKG_INSTALLATION_ROOT=/usr/local/share/vcpkg +echo "export VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" | tee -a ~/.bashrc + +# workaround https://github.com/microsoft/vcpkg/issues/27786 + +mkdir -p /Users/runner/.vcpkg +touch /Users/runner/.vcpkg/vcpkg.path.txt + +# Install vcpkg +git clone https://github.com/Microsoft/vcpkg $VCPKG_INSTALLATION_ROOT +$VCPKG_INSTALLATION_ROOT/bootstrap-vcpkg.sh +$VCPKG_INSTALLATION_ROOT/vcpkg integrate install +chmod -R 0777 $VCPKG_INSTALLATION_ROOT +ln -sf $VCPKG_INSTALLATION_ROOT/vcpkg /usr/local/bin + +rm -rf /Users/runner/.vcpkg + +invoke_tests "Common" "vcpkg" diff --git a/images/macos/scripts/build/install-visualstudio.sh b/images/macos/scripts/build/install-visualstudio.sh new file mode 100644 index 0000000..e4fa7a3 --- /dev/null +++ b/images/macos/scripts/build/install-visualstudio.sh @@ -0,0 +1,52 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-visualstudio.sh +## Desc: Install Visual Studio +################################################################################ + +source ~/utils/utils.sh +source ~/utils/xamarin-utils.sh + +install_vsmac() { + local vsmac_version=$1 + local vsmac_default=$2 + if [[ $vsmac_version == "2019" ]]; then + vsmac_download_url=$(curl -fsSL "https://aka.ms/manifest/stable" | jq -r '.items[] | select(.genericName=="VisualStudioMac").url') + elif [[ $vsmac_version == "2022" ]]; then + vsmac_download_url=$(curl -fsSL "https://aka.ms/manifest/stable-2022" | jq -r '.items[] | select(.genericName=="VisualStudioMac").url') + elif [[ $vsmac_version == "preview" ]]; then + vsmac_download_url=$(curl -fsSL "https://aka.ms/manifest/preview" | jq -r '.items[] | select(.genericName=="VisualStudioMac").url') + else + vsmac_download_url=$(buildVSMacDownloadUrl $vsmac_version) + fi + + echo "Installing Visual Studio ${vsmac_version} for Mac" + TMPMOUNT=$(/usr/bin/mktemp -d /tmp/visualstudio.XXXX) + mkdir -p "$TMPMOUNT/downloads" + + vsmac_installer=$(download_with_retry $vsmac_download_url "$TMPMOUNT/downloads/${vsmac_download_url##*/}") + + echo "Mounting Visual Studio..." + hdiutil attach $vsmac_installer -mountpoint $TMPMOUNT + + echo "Moving Visual Studio to /Applications/..." + pushd $TMPMOUNT + tar cf - "./Visual Studio.app" | tar xf - -C /Applications/ + + if [[ $vsmac_version != $vsmac_default ]]; then + mv "/Applications/Visual Studio.app" "/Applications/Visual Studio ${vsmac_version}.app" + fi + + popd + sudo hdiutil detach $TMPMOUNT + sudo rm -rf $TMPMOUNT +} + +vsmac_versions=($(get_toolset_value '.xamarin.vsmac.versions[]')) +default_vsmac_version=$(get_toolset_value '.xamarin.vsmac.default') + +for version in ${vsmac_versions[@]}; do + install_vsmac $version $default_vsmac_version +done + +invoke_tests "Common" "VSMac" diff --git a/images/macos/scripts/build/install-xamarin.sh b/images/macos/scripts/build/install-xamarin.sh new file mode 100644 index 0000000..faa0b70 --- /dev/null +++ b/images/macos/scripts/build/install-xamarin.sh @@ -0,0 +1,92 @@ +#!/bin/bash -e -o pipefail +################################################################################ +## File: install-xamarin.sh +## Desc: Install Xamarin +################################################################################ + +source ~/utils/utils.sh +source ~/utils/xamarin-utils.sh + +mono_versions=($(get_toolset_value '.xamarin."mono_versions" | reverse | .[]')) +xamarin_ios_versions=($(get_toolset_value '.xamarin."ios_versions" | reverse | .[]')) +xamarin_mac_versions=($(get_toolset_value '.xamarin."mac_versions" | reverse | .[]')) +xamarin_android_versions=($(get_toolset_value '.xamarin."android_versions" | reverse | .[]')) +latest_sdk_symlink=$(get_toolset_value '.xamarin.bundles[0].symlink') +current_sdk_symlink=$(get_toolset_value '.xamarin."bundle_default"') +default_xcode_version=$(get_toolset_value '.xcode.default') + +if [[ $current_sdk_symlink == "latest" ]]; then + current_sdk_symlink=$latest_sdk_symlink +fi + +MONO_VERSIONS_PATH="/Library/Frameworks/Mono.framework/Versions" +IOS_VERSIONS_PATH="/Library/Frameworks/Xamarin.iOS.framework/Versions" +ANDROID_VERSIONS_PATH="/Library/Frameworks/Xamarin.Android.framework/Versions" +MAC_VERSIONS_PATH="/Library/Frameworks/Xamarin.Mac.framework/Versions" + +TMPMOUNT=$(/usr/bin/mktemp -d /tmp/visualstudio.XXXX) +TMPMOUNT_FRAMEWORKS=$TMPMOUNT/frameworks +createBackupFolders + +pushd $TMPMOUNT + +# Download NUnit console +downloadNUnitConsole + +# Install Mono sdks +for version in ${mono_versions[@]}; do installMono $version; done +sudo mv -v $TMPMOUNT_FRAMEWORKS/mono/* $MONO_VERSIONS_PATH/ + +# Install Xamarin.iOS sdks +for version in ${xamarin_ios_versions[@]}; do installXamarinIOS $version; done +sudo mv -v $TMPMOUNT_FRAMEWORKS/ios/* $IOS_VERSIONS_PATH/ + +# Install Xamarin.Mac sdks +for version in ${xamarin_mac_versions[@]}; do installXamarinMac $version; done +sudo mv -v $TMPMOUNT_FRAMEWORKS/mac/* $MAC_VERSIONS_PATH/ + +# Install Xamarin.Android sdks +for version in ${xamarin_android_versions[@]}; do installXamarinAndroid $version; done +sudo mv -v $TMPMOUNT_FRAMEWORKS/android/* $ANDROID_VERSIONS_PATH/ + + +# Create bundles +bundles_count=$(get_toolset_value '.xamarin.bundles | length') +for ((bundle_index=0; bundle_index&1) + [[ -d $clt_path ]] +} + +install_clt() { + echo "Searching online for the Command Line Tools" + # This temporary file prompts the 'softwareupdate' utility to list the Command Line Tools + clt_placeholder="/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress" + sudo touch $clt_placeholder + cltPattern="Command Line Tools" + + clt_label_command="/usr/sbin/softwareupdate -l | + grep -B 1 -E '${cltPattern}' | + awk -F'*' '/^ *\\*/ {print \$2}' | + sed -e 's/^ *Label: //' -e 's/^ *//' | + sort -V | + tail -n1" + clt_label=$(eval $clt_label_command) || true + if [[ -n "$clt_label" ]]; then + echo "Installing $clt_label" + sudo "/usr/sbin/softwareupdate" "-i" "$clt_label" + fi + sudo "/bin/rm" "-f" "$clt_placeholder" +} + +echo "Installing Command Line Tools..." +install_clt + +# Retry the installation if tools are not installed from the first attempt +retries=30 +sleepInterval=60 +while ! is_clt_installed; do + if [[ $retries -eq 0 ]]; then + echo "Unable to find the Command Line Tools, all the attempts exhausted" + exit 1 + fi + echo "Command Line Tools not found, trying to install them via software updates, $retries attempts left" + install_clt + ((retries--)) + echo "Wait $sleepInterval seconds before the next check for installed Command Line Tools" + sleep $sleepInterval +done diff --git a/images/macos/scripts/docs-gen/Generate-SoftwareReport.ps1 b/images/macos/scripts/docs-gen/Generate-SoftwareReport.ps1 new file mode 100644 index 0000000..a7a8d6b --- /dev/null +++ b/images/macos/scripts/docs-gen/Generate-SoftwareReport.ps1 @@ -0,0 +1,323 @@ +using module ./software-report-base/SoftwareReport.psm1 +using module ./software-report-base/SoftwareReport.Nodes.psm1 + +param ( + [Parameter(Mandatory)][string] + $OutputDirectory, + $ImageName +) + +$ErrorActionPreference = "Stop" + +Import-Module "$PSScriptRoot/SoftwareReport.Common.psm1" -DisableNameChecking +Import-Module "$PSScriptRoot/SoftwareReport.Xcode.psm1" -DisableNameChecking +Import-Module "$PSScriptRoot/SoftwareReport.Android.psm1" -DisableNameChecking +Import-Module "$PSScriptRoot/SoftwareReport.Java.psm1" -DisableNameChecking +Import-Module "$PSScriptRoot/SoftwareReport.Xamarin.psm1" -DisableNameChecking +Import-Module "$PSScriptRoot/SoftwareReport.Toolcache.psm1" -DisableNameChecking +Import-Module "$PSScriptRoot/SoftwareReport.Browsers.psm1" -DisableNameChecking +Import-Module "$PSScriptRoot/SoftwareReport.WebServers.psm1" -DisableNameChecking +Import-Module "$PSScriptRoot/SoftwareReport.Helpers.psm1" -DisableNameChecking +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/../helpers/Xcode.Helpers.psm1" + +# Operating System info +$os = Get-OSVersion + +# OS info +$osInfo = Build-OSInfoSection $ImageName + +# Software report +$softwareReport = [SoftwareReport]::new($osInfo) +$installedSoftware = $softwareReport.Root.AddHeader("Installed Software") + +# Language and Runtime +$languageAndRuntime = $installedSoftware.AddHeader("Language and Runtime") +$languageAndRuntime.AddToolVersionsListInline(".NET Core SDK", $(Get-DotnetVersionList), '^\d+\.\d+\.\d') +$languageAndRuntime.AddToolVersion("Bash", $(Get-BashVersion)) +$languageAndRuntime.AddNodes($(Get-ClangLLVMVersions)) +$languageAndRuntime.AddNodes($(Get-GccVersions)) +$languageAndRuntime.AddNodes($(Get-FortranVersions)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $languageAndRuntime.AddToolVersion("Julia", $(Get-JuliaVersion)) +} +$languageAndRuntime.AddToolVersion("Kotlin", $(Get-KotlinVersion)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $languageAndRuntime.AddToolVersion("Go", $(Get-GoVersion)) +} +if ((-not $os.IsSequoia)) { + $languageAndRuntime.AddToolVersion("Mono", $(Get-MonoVersion)) +} +$languageAndRuntime.AddToolVersion("Node.js", $(Get-NodeVersion)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $languageAndRuntime.AddToolVersion("MSBuild", $(Get-MSBuildVersion)) + $languageAndRuntime.AddToolVersion("NVM", $(Get-NVMVersion)) + $languageAndRuntime.AddToolVersionsListInline("NVM - Cached node versions", $(Get-NVMNodeVersionList), '^\d+') +} +$languageAndRuntime.AddToolVersion("Perl", $(Get-PerlVersion)) +if ((-not $os.IsVenturaArm64) -and (-not $os.IsSonomaArm64) -and (-not $os.IsSequoiaArm64)) { + $languageAndRuntime.AddToolVersion("PHP", $(Get-PHPVersion)) +} + +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $languageAndRuntime.AddToolVersion("Python", $(Get-PythonVersion)) +} + +$languageAndRuntime.AddToolVersion("Python3", $(Get-Python3Version)) + +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $languageAndRuntime.AddToolVersion("R", $(Get-RVersion)) +} + +$languageAndRuntime.AddToolVersion("Ruby", $(Get-RubyVersion)) + +# Package Management +$packageManagement = $installedSoftware.AddHeader("Package Management") +$packageManagement.AddToolVersion("Bundler", $(Get-BundlerVersion)) +$packageManagement.AddToolVersion("Carthage", $(Get-CarthageVersion)) +$packageManagement.AddToolVersion("CocoaPods", $(Get-CocoaPodsVersion)) +if ((-not $os.IsVenturaArm64) -and (-not $os.IsSonomaArm64) -and (-not $os.IsSequoiaArm64)) { + $packageManagement.AddToolVersion("Composer", $(Get-ComposerVersion)) +} +$packageManagement.AddToolVersion("Homebrew", $(Get-HomebrewVersion)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $packageManagement.AddToolVersion("Miniconda", $(Get-CondaVersion)) +} +$packageManagement.AddToolVersion("NPM", $(Get-NPMVersion)) +if ((-not $os.IsSequoia)) { + $packageManagement.AddToolVersion("NuGet", $(Get-NuGetVersion)) +} +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $packageManagement.AddToolVersion("Pip", $(Get-PipVersion -Version 2)) +} + +$packageManagement.AddToolVersion("Pip3", $(Get-PipVersion -Version 3)) +$packageManagement.AddToolVersion("Pipx", $(Get-PipxVersion)) + +$packageManagement.AddToolVersion("RubyGems", $(Get-RubyGemsVersion)) +if ((-not $os.IsVenturaArm64) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $packageManagement.AddToolVersion("Vcpkg", $(Get-VcpkgVersion)) +} +$packageManagement.AddToolVersion("Yarn", $(Get-YarnVersion)) + +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $packageManagement.AddNode($(Build-PackageManagementEnvironmentTable)) +} +# Project Management +$projectManagement = $installedSoftware.AddHeader("Project Management") +$projectManagement.AddToolVersion("Apache Ant", $(Get-ApacheAntVersion)) +$projectManagement.AddToolVersion("Apache Maven", $(Get-MavenVersion)) +$projectManagement.AddToolVersion("Gradle", $(Get-GradleVersion)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $projectManagement.AddToolVersion("Sbt", $(Get-SbtVersion)) +} + +# Utilities +$utilities = $installedSoftware.AddHeader("Utilities") +$utilities.AddToolVersion("7-Zip", $(Get-7zipVersion)) +$utilities.AddToolVersion("aria2", $(Get-Aria2Version)) +$utilities.AddToolVersion("azcopy", $(Get-AzcopyVersion)) +$utilities.AddToolVersion("bazel", $(Get-BazelVersion)) +$utilities.AddToolVersion("bazelisk", $(Get-BazeliskVersion)) +$utilities.AddToolVersion("bsdtar", $(Get-BsdtarVersion)) +$utilities.AddToolVersion("Curl", $(Get-CurlVersion)) +$utilities.AddToolVersion("Git", $(Get-GitVersion)) +$utilities.AddToolVersion("Git LFS", $(Get-GitLFSVersion)) +$utilities.AddToolVersion("GitHub CLI", $(Get-GitHubCLIVersion)) +$utilities.AddToolVersion("GNU Tar", $(Get-GnuTarVersion)) +$utilities.AddToolVersion("GNU Wget", $(Get-WgetVersion)) +$utilities.AddToolVersion("gpg (GnuPG)", $(Get-GPGVersion)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $utilities.AddToolVersion("ImageMagick", $(Get-ImageMagickVersion)) +} +$utilities.AddToolVersion("jq", $(Get-JqVersion)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $utilities.AddToolVersion("mongo", $(Get-MongoVersion)) + $utilities.AddToolVersion("mongod", $(Get-MongodVersion)) +} +$utilities.AddToolVersion("OpenSSL", $(Get-OpenSSLVersion)) +$utilities.AddToolVersion("Packer", $(Get-PackerVersion)) +$utilities.AddToolVersion("pkg-config", $(Get-PKGConfigVersion)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $utilities.AddToolVersion("PostgreSQL", $(Get-PostgresServerVersion)) + $utilities.AddToolVersion("psql (PostgreSQL)", $(Get-PostgresClientVersion)) + $utilities.AddToolVersion("Sox", $(Get-SoxVersion)) + $utilities.AddToolVersion("Subversion (SVN)", $(Get-SVNVersion)) + $utilities.AddToolVersion("Switchaudio-osx", $(Get-SwitchAudioOsxVersion)) +} +if ($os.IsMonterey) { + $utilities.AddToolVersion("Vagrant", $(Get-VagrantVersion)) + $utilities.AddToolVersion("VirtualBox", $(Get-VirtualBoxVersion)) +} +$utilities.AddToolVersion("yq", $(Get-YqVersion)) +$utilities.AddToolVersion("zstd", $(Get-ZstdVersion)) + +# Tools +$tools = $installedSoftware.AddHeader("Tools") +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $tools.AddToolVersion("App Center CLI", $(Get-AppCenterCLIVersion)) +} +$tools.AddToolVersion("AWS CLI", $(Get-AWSCLIVersion)) +$tools.AddToolVersion("AWS SAM CLI", $(Get-AWSSAMCLIVersion)) +$tools.AddToolVersion("AWS Session Manager CLI", $(Get-AWSSessionManagerCLIVersion)) +$tools.AddToolVersion("Azure CLI", $(Get-AzureCLIVersion)) +$tools.AddToolVersion("Azure CLI (azure-devops)", $(Get-AzureDevopsVersion)) +$tools.AddToolVersion("Bicep CLI", $(Get-BicepVersion)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $tools.AddToolVersion("Cabal", $(Get-CabalVersion)) +} +$tools.AddToolVersion("Cmake", $(Get-CmakeVersion)) +$tools.AddToolVersion("CodeQL Action Bundle", $(Get-CodeQLBundleVersion)) +if ($os.IsMonterey) { + $tools.AddToolVersion("Colima", $(Get-ColimaVersion)) +} +$tools.AddToolVersion("Fastlane", $(Get-FastlaneVersion)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $tools.AddToolVersion("GHC", $(Get-GHCVersion)) + $tools.AddToolVersion("GHCup", $(Get-GHCupVersion)) + $tools.AddToolVersion("Jazzy", $(Get-JazzyVersion)) + $tools.AddToolVersion("Stack", $(Get-StackVersion)) +} +$tools.AddToolVersion("SwiftFormat", $(Get-SwiftFormatVersion)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $tools.AddToolVersion("Swig", $(Get-SwigVersion)) +} +$tools.AddToolVersion("Xcbeautify", $(Get-XcbeautifyVersion)) +$tools.AddToolVersion("Xcode Command Line Tools", $(Get-XcodeCommandLineToolsVersion)) +$tools.AddToolVersion("Xcodes", $(Get-XcodesVersion)) + +# Linters +$linters = $installedSoftware.AddHeader("Linters") +if ((-not $os.IsVenturaArm64) -and (-not $os.IsSonomaArm64) -and (-not $os.IsSequoiaArm64)) { + $linters.AddToolVersion("SwiftLint", $(Get-SwiftLintVersion)) +} +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $linters.AddToolVersion("Yamllint", $(Get-YamllintVersion)) +} + +# Browsers +$browsers = $installedSoftware.AddHeader("Browsers") +$browsers.AddNodes($(Build-BrowserSection)) +$browsers.AddNode($(Build-BrowserWebdriversEnvironmentTable)) + +# Java +$java = $installedSoftware.AddHeader("Java") +$java.AddTable($(Get-JavaVersions)) + +# Toolcache +$toolcache = $installedSoftware.AddHeader("Cached Tools") +$toolcache.AddNodes($(Build-ToolcacheSection)) + +# Rust +$rust = $installedSoftware.AddHeader("Rust Tools") +$rust.AddToolVersion("Cargo", $(Get-RustCargoVersion)) +$rust.AddToolVersion("Rust", $(Get-RustVersion)) +$rust.AddToolVersion("Rustdoc", $(Get-RustdocVersion)) +$rust.AddToolVersion("Rustup", $(Get-RustupVersion)) + +$rustPackages = $rust.AddHeader("Packages") +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $rustPackages.AddToolVersion("Bindgen", $(Get-Bindgen)) + $rustPackages.AddToolVersion("Cargo-audit", $(Get-Cargoaudit)) + $rustPackages.AddToolVersion("Cargo-outdated", $(Get-Cargooutdated)) + $rustPackages.AddToolVersion("Cbindgen", $(Get-Cbindgen)) +} +$rustPackages.AddToolVersion("Clippy", $(Get-RustClippyVersion)) +$rustPackages.AddToolVersion("Rustfmt", $(Get-RustfmtVersion)) + +# PowerShell +$powerShell = $installedSoftware.AddHeader("PowerShell Tools") +$powerShell.AddToolVersion("PowerShell", $(Get-PowershellVersion)) + +$powerShellModules = $powerShell.AddHeader("PowerShell Modules") +$powerShellModules.AddNodes($(Get-PowerShellModules)) + +# Web Servers +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $webServers = $installedSoftware.AddHeader("Web Servers") + $webServers.AddTable($(Build-WebServersSection)) +} + +# Xamarin section +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $xamarin = $installedSoftware.AddHeader("Xamarin") + $vsForMac = $xamarin.AddHeader("Visual Studio for Mac") + $vsForMac.AddTable($(Build-VSMacTable)) + $note = + @' +To use Visual Studio 2019 by default rename the app: +mv "/Applications/Visual Studio.app" "/Applications/Visual Studio 2022.app" +mv "/Applications/Visual Studio 2019.app" "/Applications/Visual Studio.app" +'@ + $vsForMacNotes = $vsForMac.AddHeader("Notes") + $vsForMacNotes.AddNote($note) + + $xamarinBundles = $xamarin.AddHeader("Xamarin bundles") + $xamarinBundles.AddTable($(Build-XamarinTable)) + + $unitTestFramework = $xamarin.AddHeader("Unit Test Framework") + $unitTestFramework.AddToolVersion("NUnit", $(Get-NUnitVersion)) +} + +# Xcode section +$xcode = $installedSoftware.AddHeader("Xcode") +# First run doesn't provide full data about devices and runtimes +Get-XcodeInfoList | Out-Null + +$xcodeInfo = Get-XcodeInfoList +$xcode.AddTable($(Build-XcodeTable $xcodeInfo)) +if ((-not $os.IsVentura) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $xcodeTools = $xcode.AddHeader("Xcode Support Tools") + $xcodeTools.AddNodes($(Build-XcodeSupportToolsSection)) +} + +$installedSdks = $xcode.AddHeader("Installed SDKs") +$installedSdks.AddTable($(Build-XcodeSDKTable $xcodeInfo)) + +$installedSimulators = $xcode.AddHeader("Installed Simulators") +$installedSimulators.AddTable($(Build-XcodeSimulatorsTable $xcodeInfo)) + +# Android section +$android = $installedSoftware.AddHeader("Android") +$androidTable = Build-AndroidTable +$android.AddTable($androidTable) + +$androidEnv = $android.AddHeader("Environment variables") +$androidEnv.AddTable($(Build-AndroidEnvironmentTable)) + +if ($os.IsMonterey) { + $miscellaneous = $installedSoftware.AddHeader("Miscellaneous") + $miscellaneous.AddToolVersion("libXext", $(Get-LibXextVersion)) + $miscellaneous.AddToolVersion("libXft", $(Get-LibXftVersion)) + $miscellaneous.AddToolVersion("Tcl/Tk", $(Get-TclTkVersion)) + $miscellaneous.AddToolVersion("Zlib", $(Get-ZlibVersion)) +} + +if ($os.IsSonoma -or $os.IsVentura) { + $miscellaneous = $installedSoftware.AddHeader("Miscellaneous") + $miscellaneous.AddToolVersion("Tcl/Tk", $(Get-TclTkVersion)) +} + +if ($os.IsMonterey -or $os.IsSonomaX64 -or $os.IsVenturaX64) { + + Write-Host "Adding environment variables for parallels" + + $miscellaneousEnv = $miscellaneous.AddHeader("Environment variables") + $miscellaneousEnv.AddTable($(Build-MiscellaneousEnvironmentTable)) + + $notes = @' +If you want to use Parallels Desktop you should download a package from URL stored in +PARALLELS_DMG_URL environment variable. A system extension is allowed for this version. +'@ + $miscellaneousEnvNotes = $miscellaneousEnv.AddHeader("Notes") + $miscellaneousEnvNotes.AddNote($notes) +} + +if (-not (Test-Path $OutputDirectory)) { New-Item -Path $OutputDirectory -ItemType Directory | Out-Null } + +# +# Write final reports +# +Write-Host $markdownExtended +$softwareReport.ToJson() | Out-File -FilePath "${OutputDirectory}/systeminfo.json" -Encoding UTF8NoBOM +$softwareReport.ToMarkdown() | Out-File -FilePath "${OutputDirectory}/systeminfo.md" -Encoding UTF8NoBOM diff --git a/images/macos/scripts/docs-gen/SoftwareReport.Android.psm1 b/images/macos/scripts/docs-gen/SoftwareReport.Android.psm1 new file mode 100644 index 0000000..66e6881 --- /dev/null +++ b/images/macos/scripts/docs-gen/SoftwareReport.Android.psm1 @@ -0,0 +1,204 @@ +Import-Module "$PSScriptRoot/SoftwareReport.Helpers.psm1" -DisableNameChecking +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +function Split-TableRowByColumns { + param ( + [string] $Row + ) + + return $Row.Split("|") | ForEach-Object { $_.trim() } +} + +function Get-AndroidSDKRoot { + return Join-Path $env:HOME "Library" "Android" "sdk" +} + +function Get-AndroidSDKManagerPath { + $androidSDKDir = Get-AndroidSDKRoot + return Join-Path $androidSDKDir "cmdline-tools" "latest" "bin" "sdkmanager" +} + +function Get-AndroidInstalledPackages { + $androidSDKManagerPath = Get-AndroidSDKManagerPath + $androidSDKManagerList = Invoke-Expression "$androidSDKManagerPath --list_installed" + return $androidSDKManagerList +} + +function Get-AndroidPackages { + $androidSDKDir = Get-AndroidSDKRoot + $androidSDKManagerPath = Get-AndroidSDKManagerPath + + $packagesListFile = Join-Path $androidSDKDir "packages-list.txt" + + if (-Not (Test-Path -Path $packagesListFile -PathType Leaf)) { + (& $androidSDKManagerPath --list --verbose) | + Where-Object { $_ -Match "^[^\s]" } | + Where-Object { $_ -NotMatch "^(Loading |Info: Parsing |---|\[=+|Installed |Available )" } | + Where-Object { $_ -NotMatch "^[^;]*$" } | + Out-File -FilePath $packagesListFile + + Write-Host Android packages list: + Get-Content $packagesListFile + } + + return Get-Content $packagesListFile +} + +function Build-AndroidTable { + Write-Host "Build-AndroidTable" + $packageInfo = Get-AndroidInstalledPackages + + return @( + @{ + "Package" = "Android Command Line Tools" + "Version" = Get-AndroidCommandLineToolsVersion + }, + @{ + "Package" = "Android Emulator" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android Emulator" + }, + @{ + "Package" = "Android SDK Build-tools" + "Version" = Get-AndroidBuildToolVersions -PackageInfo $packageInfo + }, + @{ + "Package" = "Android SDK Platforms" + "Version" = Get-AndroidPlatformVersions -PackageInfo $packageInfo + }, + @{ + "Package" = "Android SDK Platform-Tools" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android SDK Platform-Tools" + }, + @{ + "Package" = "Android SDK Tools" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android SDK Tools" + }, + @{ + "Package" = "Android Support Repository" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android Support Repository" + }, + @{ + "Package" = "CMake" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "cmake" + }, + @{ + "Package" = "Google APIs" + "Version" = Get-AndroidGoogleAPIsVersions -PackageInfo $packageInfo + }, + @{ + "Package" = "Google Play services" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Google Play services" + }, + @{ + "Package" = "Google Repository" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Google Repository" + }, + @{ + "Package" = "NDK" + "Version" = Get-AndroidNDKVersions + }, + @{ + "Package" = "SDK Patch Applier v4" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "SDK Patch Applier v4" + } + ) | Where-Object { $_.Version } | ForEach-Object { + [PSCustomObject] @{ + "Package Name" = $_.Package + "Version" = $_.Version + } + } +} + +function Build-AndroidEnvironmentTable { + $androidVersions = Get-Item env:ANDROID_* + + $shoulddResolveLink = 'ANDROID_NDK', 'ANDROID_NDK_HOME', 'ANDROID_NDK_ROOT', 'ANDROID_NDK_LATEST_HOME' + + return $androidVersions | Sort-Object -Property Name | ForEach-Object { + [PSCustomObject] @{ + "Name" = $_.Name + "Value" = if ($shoulddResolveLink.Contains($_.Name )) { Get-PathWithLink($_.Value) } else { $_.Value } + } + } +} + +function Get-AndroidPackageVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo, + [Parameter(Mandatory)] + [object] $MatchedString + ) + + $versions = $packageInfo | Where-Object { $_ -Match $MatchedString } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + return $packageInfoParts[1] + } + return ($versions -Join "
") +} + +function Get-AndroidPlatformVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo + ) + + $versions = $packageInfo | Where-Object { $_ -Match "Android SDK Platform " } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + $revision = $packageInfoParts[1] + $version = $packageInfoParts[0].split(";")[1] + return "$version (rev $revision)" + } + [array]::Reverse($versions) + return ($versions -Join "
") +} + +function Get-AndroidCommandLineToolsVersion { + $commandLineTools = Get-AndroidSDKManagerPath + (& $commandLineTools --version | Out-String).Trim() -match "(?^(\d+\.){1,}\d+$)" | Out-Null + $commandLineToolsVersion = $Matches.Version + return $commandLineToolsVersion +} + +function Get-AndroidBuildToolVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo + ) + + $versions = $packageInfo | Where-Object { $_ -Match "Android SDK Build-Tools" } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + return $packageInfoParts[1] + } + $groupVersions = @() + $versions | ForEach-Object { + $majorVersion = $_.Split(".")[0] + $groupVersions += $versions | Where-Object { $_.StartsWith($majorVersion) } | Join-String -Separator " " + } + return ($groupVersions | Sort-Object -Descending -Unique | Join-String -Separator "
") +} + +function Get-AndroidGoogleAPIsVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo + ) + + $versions = $packageInfo | Where-Object { $_ -Match "Google APIs" } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + return $packageInfoParts[0].split(";")[1] + } + return ($versions -Join "
") +} + +function Get-AndroidNDKVersions { + $ndkFolderPath = Join-Path (Get-AndroidSDKRoot) "ndk" + $versions += Get-ChildItem -Path $ndkFolderPath -Name + $ndkDefaultVersion = (Get-ToolsetContent).android.ndk.default + $ndkDefaultFullVersion = Get-ChildItem "$env:ANDROID_HOME/ndk/$ndkDefaultVersion.*" -Name | Select-Object -Last 1 + + return ($versions | ForEach-Object { + $defaultPostfix = ( $_ -eq $ndkDefaultFullVersion ) ? " (default)" : "" + $_ + $defaultPostfix + } | Join-String -Separator "
") +} diff --git a/images/macos/scripts/docs-gen/SoftwareReport.Browsers.psm1 b/images/macos/scripts/docs-gen/SoftwareReport.Browsers.psm1 new file mode 100644 index 0000000..837c331 --- /dev/null +++ b/images/macos/scripts/docs-gen/SoftwareReport.Browsers.psm1 @@ -0,0 +1,117 @@ +function Build-BrowserSection { + + $nodes = @() + $os = Get-OSVersion + + $nodes += @( + [ToolVersionNode]::new("Safari", $(Get-SafariVersion)) + [ToolVersionNode]::new("SafariDriver", $(Get-SafariDriverVersion)) + [ToolVersionNode]::new("Google Chrome", $(Get-ChromeVersion)) + [ToolVersionNode]::new("Google Chrome for Testing", $(Get-ChromeForTestingVersion)) + [ToolVersionNode]::new("ChromeDriver", $(Get-ChromeDriverVersion)) + ) + + if ((-not $os.IsVenturaArm64) -and (-not $os.IsSonomaArm64) -and (-not $os.IsSequoiaArm64)) { + $nodes += @( + [ToolVersionNode]::new("Microsoft Edge", $(Get-EdgeVersion)) + [ToolVersionNode]::new("Microsoft Edge WebDriver", $(Get-EdgeDriverVersion)) + [ToolVersionNode]::new("Mozilla Firefox", $(Get-FirefoxVersion)) + [ToolVersionNode]::new("geckodriver", $(Get-GeckodriverVersion)) + ) + } + + $nodes += @( + [ToolVersionNode]::new("Selenium server", $(Get-SeleniumVersion)) + ) + + return $nodes +} + +function Get-SafariVersion { + $version = Run-Command "defaults read /Applications/Safari.app/Contents/Info CFBundleShortVersionString" + $build = Run-Command "defaults read /Applications/Safari.app/Contents/Info CFBundleVersion" + return "$version ($build)" +} + +function Get-SafariDriverVersion { + $version = Run-Command "safaridriver --version" | Take-Part -Part 3, 4 + return $version +} + +function Get-ChromeVersion { + $chromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" + $version = Run-Command "'${chromePath}' --version" + return ($version -replace ("^Google Chrome")).Trim() +} + +function Get-ChromeForTestingVersion { + $chromePath = "/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing" + $version = Run-Command "'${chromePath}' --version" + return ($version -replace ("^Google Chrome for Testing")).Trim() +} + +function Get-ChromeDriverVersion { + $rawOutput = Run-Command "chromedriver --version" + $version = $rawOutput | Take-Part -Part 1 + return $version +} + +function Get-EdgeVersion { + $edgePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge" + $version = Run-Command "'${edgePath}' --version" + return ($version -replace ("^Microsoft Edge")).Trim() +} + +function Get-EdgeDriverVersion { + return Run-Command "msedgedriver --version" | Take-Part -Part 3 +} + +function Get-FirefoxVersion { + $firefoxPath = "/Applications/Firefox.app/Contents/MacOS/firefox" + $version = Run-Command "'${firefoxPath}' --version" + return ($version -replace "^Mozilla Firefox").Trim() +} + +function Get-GeckodriverVersion { + $version = Run-Command "geckodriver --version" | Select-Object -First 1 + return ($version -replace "^geckodriver").Trim() +} + +function Get-SeleniumVersion { + $os = Get-OSVersion + if ($os.IsVenturaArm64 -or $os.IsSonomaArm64 -or $os.IsSequoiaArm64) { + $cellarPath = "/opt/homebrew/Cellar" + } else { + $cellarPath = "/usr/local/Cellar" + } + $seleniumVersion = (Get-ChildItem -Path "$cellarPath/selenium-server*/*").Name + return $seleniumVersion +} + +function Build-BrowserWebdriversEnvironmentTable { + $node = [HeaderNode]::new("Environment variables") + + $table = @( + @{ + "Name" = "CHROMEWEBDRIVER" + "Value" = $env:CHROMEWEBDRIVER + }, + @{ + "Name" = "EDGEWEBDRIVER" + "Value" = $env:EDGEWEBDRIVER + }, + @{ + "Name" = "GECKOWEBDRIVER" + "Value" = $env:GECKOWEBDRIVER + } + ) | ForEach-Object { + [PSCustomObject] @{ + "Name" = $_.Name + "Value" = $_.Value + } + } + + $node.AddTable($table) + + return $node +} diff --git a/images/macos/scripts/docs-gen/SoftwareReport.Common.psm1 b/images/macos/scripts/docs-gen/SoftwareReport.Common.psm1 new file mode 100644 index 0000000..e58fd1a --- /dev/null +++ b/images/macos/scripts/docs-gen/SoftwareReport.Common.psm1 @@ -0,0 +1,621 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +function Get-BashVersion { + $version = bash -c 'echo ${BASH_VERSION}' + return $version +} + +function Get-DotnetVersionList { + $sdkRawList = Run-Command "dotnet --list-sdks" + return $sdkRawList | ForEach-Object { Take-Part $_ -Part 0 } +} + +function Get-GoVersion { + $goOutput = Run-Command "go version" | Take-Part -Part 2 + if ($goOutput.StartsWith("go")) { + $goOutput = $goOutput.Substring(2) + } + + return $goOutput +} + +function Get-RVersion { + $rVersion = Run-Command "R --version | grep 'R version'" | Take-Part -Part 2 + return $rVersion +} + +function Get-RustVersion { + $rustVersion = Run-Command "rustc --version" | Take-Part -Part 1 + return $rustVersion +} + +function Get-RustfmtVersion { + $version = Run-Command "rustfmt --version" | Take-Part -Part 1 + return $version +} + +function Get-RustdocVersion { + $version = Run-Command "rustdoc --version" | Take-Part -Part 1 + return $version +} + +function Get-RustCargoVersion { + $version = Run-Command "cargo --version" | Take-Part -Part 1 + return $version +} + +function Get-RustClippyVersion { + $version = Run-Command "cargo clippy --version" | Take-Part -Part 1 + return $version +} + +function Get-Bindgen { + $bindgenVersion = Run-Command "bindgen --version" | Take-Part -Part 1 + return $bindgenVersion +} + +function Get-Cbindgen { + $cbindgenVersion = Run-Command "cbindgen --version" | Take-Part -Part 1 + return $cbindgenVersion +} + +function Get-Cargooutdated { + $cargoOutdatedVersion = Run-Command "cargo outdated --version" | Take-Part -Part 1 + return $cargoOutdatedVersion +} + +function Get-Cargoaudit { + $cargoAuditVersion = Run-Command "cargo-audit --version" | Take-Part -Part 1 + return $cargoAuditVersion +} + +function Get-RustupVersion { + $rustupVersion = Run-Command "rustup --version" | Select-Object -First 1 | Take-Part -Part 1 + return $rustupVersion +} + +function Get-VcpkgVersion { + $vcpkgVersion = Run-Command "vcpkg version" | Select-Object -First 1 | Take-Part -Part 5 | Take-Part -Part 0 -Delimiter "-" + $commitId = git -C "/usr/local/share/vcpkg" rev-parse --short HEAD + return "$vcpkgVersion (build from commit $commitId)" +} + +function Get-GccVersions { + $versionList = (Get-ToolsetContent).gcc.versions + $versionList | Foreach-Object { + $nameVersion = Run-Command "gcc-${_} --version" | Select-Object -First 1 + $version = ($nameVersion -replace "^gcc-${_}").Trim() -replace '\).*$', ')' + return [ToolVersionNode]::new("GCC ${_}", "$version - available by ``gcc-${_}`` alias") + } +} + +function Get-FortranVersions { + $versionList = (Get-ToolsetContent).gcc.versions + $versionList | Foreach-Object { + $nameVersion = Run-Command "gfortran-${_} --version" | Select-Object -First 1 + $version = ($nameVersion -replace "^GNU Fortran").Trim() -replace '\).*$', ')' + return [ToolVersionNode]::new("GNU Fortran ${_}", "$version - available by ``gfortran-${_}`` alias") + } +} + +function Get-ClangLLVMVersions { + $clangVersionRegex = [Regex]::new("(?\d+\.\d+\.\d+)") + + $defaultClangOutput = Run-Command "clang --version" | Out-String + $defaultClangVersion = $clangVersionRegex.Match($defaultClangOutput).Groups['version'].Value + + $homebrewClangPath = '$(brew --prefix llvm@{0})/bin/clang' -f ((Get-ToolsetContent).llvm.version) + $homebrewClangOutput = Run-Command "$homebrewClangPath --version" | Out-String + $homebrewClangVersion = $clangVersionRegex.Match($homebrewClangOutput).Groups['version'].Value + + return @( + [ToolVersionNode]::new("Clang/LLVM", $defaultClangVersion) + [ToolVersionNode]::new("Clang/LLVM (Homebrew)", "$homebrewClangVersion - available on ``$homebrewClangPath``") + ) +} + +function Get-NVMVersion { + $nvmPath = Join-Path $env:HOME ".nvm" "nvm.sh" + $nvmInitCommand = ". ${nvmPath} > /dev/null 2>&1 || true" + $nodejsVersion = Run-Command "${nvmInitCommand} && nvm --version" + return $nodejsVersion +} + +function Get-PipVersion { + param ( + [Parameter(Mandatory)][ValidateRange(2, 3)] + [int] $Version + ) + + $command = If ($Version -eq 2) { "/Library/Frameworks/Python.framework/Versions/2.7/bin/pip --version" } Else { "pip3 --version" } + $commandOutput = Run-Command $command + $versionPart1 = $commandOutput | Take-Part -Part 1 + $versionPart2 = $commandOutput | Take-Part -Part 4 + $versionPart3 = $commandOutput | Take-Part -Part 5 + return "${versionPart1} ${versionPart2} ${versionPart3}" +} + +function Get-PipxVersion { + $pipxVersion = Run-Command "pipx --version" -SuppressStderr + return $pipxVersion +} + +function Get-NVMNodeVersionList { + $nvmPath = Join-Path $env:HOME ".nvm" "nvm.sh" + $nvmInitCommand = ". ${nvmPath} > /dev/null 2>&1 || true" + $nodejsVersionsRaw = Run-Command "${nvmInitCommand} && nvm ls" + $nodeVersions = $nodejsVersionsRaw | ForEach-Object { $_.TrimStart(" ").TrimEnd(" *") } | Where-Object { $_.StartsWith("v") } + return $nodeVersions | ForEach-Object { $_.TrimStart("v") } +} + +function Build-OSInfoSection { + param ( + [string] $ImageName + ) + + $fieldsToInclude = @("System Version:", "Kernel Version:") + $rawSystemInfo = Run-Command "system_profiler SPSoftwareDataType" + $parsedSystemInfo = $rawSystemInfo | Where-Object { -not ($_ | Select-String -NotMatch $fieldsToInclude) } | ForEach-Object { $_.Trim() } + $parsedSystemInfo[0] -match "System Version: macOS (?\d+)" | Out-Null + $version = $Matches.Version + $systemVersion = $parsedSystemInfo[0].Replace($fieldsToInclude[0],"").Trim() + $kernelVersion = $parsedSystemInfo[1].Replace($fieldsToInclude[1],"").Trim() + + $osInfoNode = [HeaderNode]::new("macOS $version") + $osInfoNode.AddToolVersion("OS Version:", $systemVersion) + $osInfoNode.AddToolVersion("Kernel Version:", $kernelVersion) + $osInfoNode.AddToolVersion("Image Version:", $ImageName.Split('_')[1]) + return $osInfoNode +} + +function Get-MonoVersion { + $monoVersion = Run-Command "mono --version" | Out-String | Take-Part -Part 4 + return $monoVersion +} + +function Get-MSBuildVersion { + $msbuildVersion = Run-Command "msbuild -version" | Select-Object -Last 1 + $monoVersion = Get-MonoVersion + return "$msbuildVersion (Mono $monoVersion)" +} + +function Get-NodeVersion { + $nodeVersion = Run-Command "node --version" + return $nodeVersion.TrimStart("v") +} + +function Get-PerlVersion { + $version = Run-Command "perl -e 'print substr(`$^V,1)'" + return $version +} + +function Get-PythonVersion { + $pythonVersion = Run-Command "/Library/Frameworks/Python.framework/Versions/2.7/bin/python --version" + return ($pythonVersion -replace "^Python").Trim() +} + +function Get-Python3Version { + $python3Version = Run-Command "python3 --version" + return ($python3Version -replace "^Python").Trim() +} + +function Get-RubyVersion { + $rubyVersion = Run-Command "ruby --version" | Take-Part -Part 1 + return $rubyVersion +} + +function Get-PHPVersion { + $PHPVersion = Run-Command "php --version" | Select-Object -First 1 | Take-Part -Part 0,1 + return ($PHPVersion -replace "^PHP").Trim() +} + +function Get-JuliaVersion { + $juliaVersion = Run-Command "julia --version" | Take-Part -Part 0,2 + return ($juliaVersion -replace "^Julia").Trim() +} + +function Get-BundlerVersion { + $bundlerVersion = Run-Command "bundle --version" + return ($bundlerVersion -replace "^Bundler version").Trim() +} + +function Get-CarthageVersion { + $carthageVersion = Run-Command "carthage version" -SuppressStderr + return $carthageVersion +} + +function Get-CocoaPodsVersion { + $cocoaPodsVersion = Run-Command "pod --version" + return $cocoaPodsVersion +} + +function Get-HomebrewVersion { + $homebrewVersion = Run-Command "brew --version" | Select-Object -First 1 + return ($homebrewVersion -replace "^Homebrew").Trim() +} + +function Get-NPMVersion { + $NPMVersion = Run-Command "npm --version" + return $NPMVersion +} + +function Get-YarnVersion { + $yarmVersion = Run-Command "yarn --version" + return $yarmVersion +} + +function Get-NuGetVersion { + $nugetVersion = Run-Command "nuget help" | Select-Object -First 1 | Take-Part -Part 2 + return $nugetVersion +} + +function Get-CondaVersion { + $condaVersion = Run-Command "conda --version" + return ($condaVersion -replace "^conda").Trim() +} + +function Get-RubyGemsVersion { + $rubyGemsVersion = Run-Command "gem --version" + return $rubyGemsVersion +} + +function Get-ComposerVersion { + $composerVersion = Run-Command "composer --version" | Select-Object -First 1 | Take-Part -Part 2 + return $composerVersion +} + +function Get-MavenVersion { + $mavenVersion = Run-Command "mvn -version" | Select-Object -First 1 | Take-Part -Part 2 + return $mavenVersion +} + +#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 +function Get-GradleVersion { + $gradleVersion = (Run-Command "gradle --version" | Select-String "Gradle")[-1] + return ($gradleVersion.Line -replace "^Gradle").Trim() +} + +function Get-ApacheAntVersion { + $apacheAntVersion = Run-Command "ant -version" | Take-Part -Part 0,1,3 + return ($apacheAntVersion -replace "^Apache Ant\(TM\)").Trim() +} + +function Get-CurlVersion { + $curlVersion = Run-Command "curl --version" | Select-Object -First 1 | Take-Part -Part 1 + return $curlVersion +} + +function Get-GitVersion { + $gitVersion = Run-Command "git --version" | Take-Part -Part -1 + return $gitVersion +} + +function Get-GitLFSVersion { + $gitLFSVersion = Run-Command "git-lfs version" | Take-Part -Part 0 | Take-Part -Part 1 -Delimiter "/" + return $gitLFSVersion +} + +function Get-GitHubCLIVersion { + $ghVersion = Run-Command "gh --version" | Select-String "gh version" | Select-Object -First 1 | Take-Part -Part 2 + return $ghVersion +} + +function Get-WgetVersion { + $wgetVersion = Run-Command "wget --version" | Select-String "GNU Wget" | Take-Part -Part 2 + return $wgetVersion +} + +function Get-SVNVersion { + $svnVersion = Run-Command "svn --version --quiet" + return $svnVersion +} + +function Get-PackerVersion { + # Packer 1.7.1 has a bug and outputs version to stderr instead of stdout https://github.com/hashicorp/packer/issues/10855 + $result = Run-Command "packer --version" + $packerVersion = [regex]::matches($result, "(\d+.){2}\d+").Value + return $packerVersion +} + +function Get-OpenSSLVersion { + $opensslVersion = Run-Command "openssl version" + return ($opensslVersion -replace "^OpenSSL").Trim() +} + +function Get-JqVersion { + $jqVersion = Run-Command "jq --version" | Take-Part -Part 1 -Delimiter "-" + return $jqVersion +} + +function Get-GPGVersion { + $gpgVersion = Run-Command "gpg --version" | Select-String 'gpg (GnuPG)' -SimpleMatch + return ($gpgVersion.Line -replace "^gpg \(GnuPG\)").Trim() +} + +function Get-PostgresClientVersion { + $postgresClientVersion = Run-Command "psql --version" + return ($postgresClientVersion -replace "^psql \(PostgreSQL\)").Trim() +} + +function Get-PostgresServerVersion { + $postgresServerVersion = Run-Command "pg_config --version" + return ($postgresServerVersion -replace "^PostgreSQL").Trim() +} + +function Get-Aria2Version { + $aria2Version = Run-Command "aria2c --version" | Select-Object -First 1 | Take-Part -Part 2 + return $aria2Version +} + +function Get-AzcopyVersion { + $azcopyVersion = [string]$(Run-Command "azcopy --version") | Take-Part -Part 2 + return $azcopyVersion +} + +function Get-ZstdVersion { + $zstdVersion = Run-Command "zstd --version" | Take-Part -Part 1 -Delimiter "v" | Take-Part -Part 0 -Delimiter "," + return $zstdVersion +} + +function Get-BazelVersion { + $bazelVersion = Run-Command "bazel --version" | Take-Part -Part 0 -Delimiter "-" + return ($bazelVersion -replace "^bazel").Trim() +} + +function Get-BazeliskVersion { + $bazeliskVersion = Run-Command "brew list bazelisk --versions" + return ($bazeliskVersion -replace "^bazelisk").Trim() +} + +function Get-MongoVersion { + $mongo = Run-Command "mongo --version" | Select-String "MongoDB shell version" | Take-Part -Part 3 + return $mongo.TrimStart("v").Trim() +} + +function Get-MongodVersion { + $mongod = Run-Command "mongod --version" | Select-String "db version " | Take-Part -Part 2 + return $mongod.TrimStart("v").Trim() +} + +function Get-7zipVersion { + $7zip = Run-Command "7z i" | Select-String "7-Zip" | Take-Part -Part 0,2 + return ($7zip -replace "^7-Zip").Trim() +} + +function Get-GnuTarVersion { + $gnuTar = Run-Command "gtar --version" | Select-String "tar" | Take-Part -Part 3 + return "$gnuTar - available by 'gtar' alias" +} + +function Get-BsdtarVersion { + $bsdtar = Run-Command "tar --version" | Take-Part -Part 1 + return "$bsdtar - available by 'tar' alias" +} + +function Get-VirtualBoxVersion { + $virtualBox = Run-Command "vboxmanage -v" + return $virtualBox +} + +function Get-VagrantVersion { + $vagrant = Run-Command "vagrant -v" + return ($vagrant -replace "^Vagrant").Trim() +} + +function Get-ParallelVersion { + $parallelVersion = Run-Command "parallel --version" | Select-String "GNU parallel" | Select-Object -First 1 + return ($parallelVersion -replace "^GNU parallel").Trim() +} + +function Get-FastlaneVersion { + $fastlaneVersion = Run-Command "fastlane --version" | Select-String "^fastlane [0-9]" | Take-Part -Part 1 + return $fastlaneVersion +} + +function Get-CmakeVersion { + $cmakeVersion = Run-Command "cmake --version" | Select-Object -First 1 | Take-Part -Part 2 + return $cmakeVersion +} + +function Get-AppCenterCLIVersion { + $appcenterCLIVersion = Run-Command "appcenter --version" | Take-Part -Part 2 + return $appcenterCLIVersion +} + +function Get-AzureCLIVersion { + $azureCLIVersion = (az version | ConvertFrom-Json).'azure-cli' + return $azureCLIVersion +} + +function Get-AzureDevopsVersion { + $azdevopsVersion = (az version | ConvertFrom-Json).extensions.'azure-devops' + return $azdevopsVersion +} + +function Get-AWSCLIVersion { + $awsVersion = Run-Command "aws --version" | Take-Part -Part 0 | Take-Part -Delimiter "/" -Part 1 + return $awsVersion +} + +function Get-AWSSAMCLIVersion { + $awsSamVersion = Run-Command "sam --version" | Take-Part -Part 3 + return $awsSamVersion +} + +function Get-AWSSessionManagerCLIVersion { + $awsSessionManagerVersion = Run-Command "session-manager-plugin --version" + return $awsSessionManagerVersion +} + +function Get-GHCupVersion { + $ghcUpVersion = (Run-Command "ghcup --version" | Take-Part -Part 5).Replace('v','') + return $ghcUpVersion +} + +function Get-GHCVersion { + $ghcVersion = Run-Command "ghc --version" | Take-Part -Part 7 + return $ghcVersion +} + +function Get-CabalVersion { + $cabalVersion = Run-Command "cabal --version" | Take-Part -Part 3 + return $cabalVersion +} + +function Get-SwitchAudioOsxVersion { + $switchAudioVersion = Get-BrewPackageVersion -CommandName "SwitchAudioSource" + return $switchAudioVersion +} + +function Get-SoxVersion { + $soxVersion = Get-BrewPackageVersion -CommandName "sox" + return $soxVersion +} + +function Get-StackVersion { + $stackVersion = Run-Command "stack --version" | Take-Part -Part 1 | ForEach-Object {$_.replace(",","")} + return $stackVersion +} + +function Get-SwiftFormatVersion { + $swiftFormatVersion = Run-Command "swiftformat --version" + return $swiftFormatVersion +} + +function Get-YamllintVersion { + $yamllintVersion = Run-Command "yamllint --version" + return ($yamllintVersion -replace "^Yamllint").Trim() +} + +function Get-SwiftLintVersion { + $swiftlintVersion = Run-Command "swiftlint version" + return $swiftlintVersion +} + +function Get-PowershellVersion { + $powershellVersion = Run-Command "powershell --version" + return ($powershellVersion -replace "^PowerShell").Trim() +} + +function Get-SwigVersion { + $swigVersion = Run-Command "swig -version" | Select-Object -First 2 | Take-Part -Part 2 + return $swigVersion +} + +function Get-BicepVersion { + $bicepVersion = Run-Command "bicep --version" | Take-Part -Part 3 + return $bicepVersion +} + +function Get-KotlinVersion { + $kotlinVersion = Run-Command "kotlin -version" | Take-Part -Part 2 + return $kotlinVersion +} + +function Get-SbtVersion { + $sbtVersion = Run-Command "sbt -version" | Take-Part -Part 3 + return $sbtVersion +} + +function Get-JazzyVersion { + $jazzyVersion = Run-Command "jazzy --version" | Take-Part -Part 2 + return $jazzyVersion +} + +function Get-ZlibVersion { + $zlibVersion = (Run-Command "brew info --json zlib" | ConvertFrom-Json).installed.version + return $zlibVersion +} + +function Get-LibXftVersion { + $libXftVersion = (Run-Command "brew info --json libxft" | ConvertFrom-Json).installed.version + return $libXftVersion +} + +function Get-LibXextVersion { + $libXextVersion = (Run-Command "brew info --json libxext" | ConvertFrom-Json).installed.version + return $libXextVersion +} + +function Get-TclTkVersion { + $tcltkVersion = (Run-Command "brew info --json tcl-tk" | ConvertFrom-Json).installed.version + return $tcltkVersion +} + +function Get-YqVersion { + $yqVersion = Run-Command "yq --version" + $yqVersion -match "\d{1,2}\.\d{1,2}\.\d{1,2}" | Out-Null + return ($Matches[0]) +} + +function Get-ImageMagickVersion { + $imagemagickVersion = Run-Command "magick --version" | Select-Object -First 1 | Take-Part -Part 1,2 + return ($imagemagickVersion -replace "^ImageMagick").Trim() +} + +function Build-PackageManagementEnvironmentTable { + $node = [HeaderNode]::new("Environment variables") + + $table = @( + @{ + "Name" = "CONDA" + "Value" = $env:CONDA + }, + @{ + "Name" = "VCPKG_INSTALLATION_ROOT" + "Value" = $env:VCPKG_INSTALLATION_ROOT + } + ) | ForEach-Object { + [PSCustomObject] @{ + "Name" = $_.Name + "Value" = $_.Value + } + } + + $node.AddTable($table) + + return $node +} + +function Build-MiscellaneousEnvironmentTable { + return @( + @{ + "Name" = "PARALLELS_DMG_URL" + "Value" = $env:PARALLELS_DMG_URL + } + ) | ForEach-Object { + [PSCustomObject] @{ + "Name" = $_.Name + "Value" = $_.Value + } + } +} + + +function Get-CodeQLBundleVersion { + $CodeQLVersionWildcard = Join-Path $Env:AGENT_TOOLSDIRECTORY -ChildPath "CodeQL" | Join-Path -ChildPath "*" + $CodeQLVersionPath = Get-ChildItem $CodeQLVersionWildcard | Select-Object -First 1 -Expand FullName + $CodeQLPath = Join-Path $CodeQLVersionPath -ChildPath "x64" | Join-Path -ChildPath "codeql" | Join-Path -ChildPath "codeql" + $CodeQLVersion = & $CodeQLPath version --quiet + return $CodeQLVersion +} + +function Get-ColimaVersion { + $colimaVersion = Run-Command "colima version" | Select-String "colima version" | Take-Part -Part 2 + return $colimaVersion +} + +function Get-PKGConfigVersion { + $pkgconfigVersion = Run-Command "pkg-config --version" + return $pkgconfigVersion +} + +function Get-XcbeautifyVersion { + $XcbeautifyVersion = Run-Command "xcbeautify --version" + return $XcbeautifyVersion +} + +function Get-XcodesVersion { + $XcodesVersion = Run-Command "xcodes version" + return $XcodesVersion +} diff --git a/images/macos/scripts/docs-gen/SoftwareReport.Helpers.psm1 b/images/macos/scripts/docs-gen/SoftwareReport.Helpers.psm1 new file mode 100644 index 0000000..cd92f7b --- /dev/null +++ b/images/macos/scripts/docs-gen/SoftwareReport.Helpers.psm1 @@ -0,0 +1,54 @@ +function Run-Command { + param ( + [Parameter(Mandatory=$true)] + [string] $Command, + [switch] $SuppressStderr + ) + # Bash trick to suppress and show error output because some commands write to stderr (for example, "python --version") + $redirectOutputArguments = If ($SuppressStderr) { "2> /dev/null" } Else { "2>&1" } + $stdout = & bash -c "${Command} ${redirectOutputArguments}" + + return $stdout +} + +function Take-Part { + param ( + [Parameter(ValueFromPipeline)] + [string] $toolOutput, + [string] $Delimiter = " ", + [int[]] $Part + ) + $parts = $toolOutput.Split($Delimiter, [System.StringSplitOptions]::RemoveEmptyEntries) + $selectedParts = $parts[$Part] + return [string]::Join($Delimiter, $selectedParts) +} + +function Get-LinkTarget { + param ( + [string] $inputPath + ) + $link = Get-Item $inputPath | Select-Object -ExpandProperty Target + if ($link) { + return " -> $link" + } + return "" +} + +function Get-PathWithLink { + param ( + [string] $inputPath + ) + $link = Get-LinkTarget($inputPath) + return "${inputPath}${link}" +} + +function Get-BrewPackageVersion { + param ( + [string] $CommandName + ) + + (Get-LinkTarget (Get-Command $CommandName).Source | Out-String) -match "(?(\d+.){2}\d+)" | Out-Null + $packageVersion = $Matches.Version + + return $packageVersion +} diff --git a/images/macos/scripts/docs-gen/SoftwareReport.Java.psm1 b/images/macos/scripts/docs-gen/SoftwareReport.Java.psm1 new file mode 100644 index 0000000..af76240 --- /dev/null +++ b/images/macos/scripts/docs-gen/SoftwareReport.Java.psm1 @@ -0,0 +1,28 @@ +function Get-JavaVersions { + $defaultJavaPath = (Get-Item env:JAVA_HOME).value + + $os = Get-OSVersion + if ($os.IsVenturaArm64 -or $os.IsSonomaArm64 -or $os.IsSequoiaArm64) { + $javaVersions = Get-Item env:JAVA_HOME_*_arm64 + } else { + $javaVersions = Get-Item env:JAVA_HOME_*_X64 + } + + $sortRules = @{ + Expression = { [Int32]$_.Name.Split("_")[2] } + Descending = $false + } + + return $javaVersions | Sort-Object $sortRules | ForEach-Object { + $javaPath = $_.Value + # Take semver from the java path + $version = $javaPath.split('/')[5] + $fullVersion = $version.Replace('-', '+') + $defaultPostfix = ($javaPath -eq $defaultJavaPath) ? " (default)" : "" + + [PSCustomObject] @{ + "Version" = $fullVersion + $defaultPostfix + "Environment Variable" = $_.Name + } + } +} diff --git a/images/macos/scripts/docs-gen/SoftwareReport.Toolcache.psm1 b/images/macos/scripts/docs-gen/SoftwareReport.Toolcache.psm1 new file mode 100644 index 0000000..40c028c --- /dev/null +++ b/images/macos/scripts/docs-gen/SoftwareReport.Toolcache.psm1 @@ -0,0 +1,67 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +function Get-ToolcacheRubyVersions { + $toolcachePath = Join-Path $env:HOME "hostedtoolcache" "Ruby" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ } +} + +function Get-ToolcachePythonVersions { + $toolcachePath = Join-Path $env:HOME "hostedtoolcache" "Python" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ } +} + +function Get-ToolcachePyPyVersions { + $toolcachePath = Join-Path $env:HOME "hostedtoolcache/PyPy/*/x64" + Get-ChildItem -Path $toolcachePath | Sort-Object { [Version] $_.Parent.Name } | ForEach-Object { + $foundVersionPath = $_.FullName + $foundVersionName = (Get-Item ($foundVersionPath -replace "x64") | Sort-Object -Property {[version]$_.name} -Descending | Select-Object -First 1).name + $arrPyPyVersion = ((& "$foundVersionPath/bin/python" -c "import sys;print(sys.version.split('\n')[1])") -split " ") + $pypyVersion = "$($arrPyPyVersion[0]) $($arrPyPyVersion[1])" + return "{0} {1}]" -f $foundVersionName, $pypyVersion + } +} + +function Get-ToolcacheNodeVersions { + $toolcachePath = Join-Path $env:HOME "hostedtoolcache" "Node" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ } +} + +function Get-ToolcacheGoVersions { + $toolcachePath = Join-Path $env:HOME "hostedtoolcache" "Go" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version]$_ } +} + +function Build-ToolcacheSection { + + $nodes = @() + + if ((-not $os.IsVenturaArm64) -and (-not $os.IsSonoma) -and (-not $os.IsSequoia)) { + $nodes += @( + [ToolVersionsListNode]::new("PyPy", $(Get-ToolcachePyPyVersions), '^\d+\.\d+', "List") + ) + } + if ((-not $os.IsVenturaArm64) -and (-not $os.IsSonomaArm64) -and (-not $os.IsSequoiaArm64)) { + $nodes += @( + [ToolVersionsListNode]::new("Ruby", $(Get-ToolcacheRubyVersions), '^\d+\.\d+', "List") + ) + } + + $nodes += @( + [ToolVersionsListNode]::new("Python", $(Get-ToolcachePythonVersions), '^\d+\.\d+', "List"), + [ToolVersionsListNode]::new("Node.js", $(Get-ToolcacheNodeVersions), '^\d+', "List"), + [ToolVersionsListNode]::new("Go", $(Get-ToolcacheGoVersions), '^\d+\.\d+', "List") + ) + + return $nodes +} + +function Get-PowerShellModules { + $modules = ((Get-ToolsetContent).powershellModules).name + $modules | ForEach-Object { + $moduleName = $_ + $moduleVersions = Get-Module -Name $moduleName -ListAvailable | Select-Object -ExpandProperty Version | Sort-Object -Unique + return [ToolVersionsListNode]::new($moduleName, $moduleVersions, '^\d+', "Inline") + } +} diff --git a/images/macos/scripts/docs-gen/SoftwareReport.WebServers.psm1 b/images/macos/scripts/docs-gen/SoftwareReport.WebServers.psm1 new file mode 100644 index 0000000..7958826 --- /dev/null +++ b/images/macos/scripts/docs-gen/SoftwareReport.WebServers.psm1 @@ -0,0 +1,36 @@ +function Get-ApacheVersion { + $name = "httpd" + $port = 80 + $version = brew list $name --versions | Take-Part -Part 1 + $serviceStatus = (brew services list) -match $name | Take-Part -Part 1 + $configFile = "$(brew --prefix)/etc/httpd/httpd.conf" + return [PsCustomObject]@{ + "Name" = $name + "Version" = $version + "ConfigFile" = $configFile + "ServiceStatus" = $serviceStatus + "ListenPort" = $port + } +} + +function Get-NginxVersion { + $name = "nginx" + $port = 80 + $version = brew list $name --versions | Take-Part -Part 1 + $serviceStatus = (brew services list) -match $name | Take-Part -Part 1 + $configFile = "$(brew --prefix)/etc/nginx/nginx.conf" + return [PsCustomObject]@{ + "Name" = $name + "Version" = $version + "ConfigFile" = $configFile + "ServiceStatus" = $serviceStatus + "ListenPort" = $port + } +} + +function Build-WebServersSection { + return @( + (Get-ApacheVersion), + (Get-NginxVersion) + ) +} diff --git a/images/macos/scripts/docs-gen/SoftwareReport.Xamarin.psm1 b/images/macos/scripts/docs-gen/SoftwareReport.Xamarin.psm1 new file mode 100644 index 0000000..5275c32 --- /dev/null +++ b/images/macos/scripts/docs-gen/SoftwareReport.Xamarin.psm1 @@ -0,0 +1,48 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +function Build-VSMacTable { + $vsMacVersions = (Get-ToolsetContent).xamarin.vsmac.versions + $defaultVSMacVersion = (Get-ToolsetContent).xamarin.vsmac.default + + return $vsMacVersions | ForEach-Object { + $isDefault = $_ -eq $defaultVSMacVersion + $vsPath = "/Applications/Visual Studio $_.app" + if ($isDefault) { + $vsPath = "/Applications/Visual Studio.app" + } + + $plistPath = "$vsPath/Contents/Info.plist" + $build = Run-Command "/usr/libexec/PlistBuddy -c 'Print CFBundleVersion' '$plistPath'" + $defaultPostfix = $isDefault ? " (default)" : "" + + [PSCustomObject] @{ + "Version" = $_ + $defaultPostfix + "Build" = $build + "Path" = $vsPath + } + } +} + +function Get-NUnitVersion { + $version = Run-Command "nunit3-console --version" | Select-Object -First 1 | Take-Part -Part 3 + return $version +} + +function Build-XamarinTable { + $xamarinBundles = (Get-ToolsetContent).xamarin.bundles + $defaultSymlink = (Get-ToolsetContent).xamarin.bundle_default + if ($defaultSymlink -eq "latest") { + $defaultSymlink = $xamarinBundles[0].symlink + } + + return $xamarinBundles | ForEach-Object { + $defaultPostfix = ($_.symlink -eq $defaultSymlink ) ? " (default)" : "" + [PSCustomObject] @{ + "symlink" = $_.symlink + $defaultPostfix + "Xamarin.Mono" = $_.mono + "Xamarin.iOS" = $_.ios + "Xamarin.Mac" = $_.mac + "Xamarin.Android" = $_.android + } + } +} diff --git a/images/macos/scripts/docs-gen/SoftwareReport.Xcode.psm1 b/images/macos/scripts/docs-gen/SoftwareReport.Xcode.psm1 new file mode 100644 index 0000000..b3f9186 --- /dev/null +++ b/images/macos/scripts/docs-gen/SoftwareReport.Xcode.psm1 @@ -0,0 +1,258 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/../helpers/Xcode.Helpers.psm1" + +$os = Get-OSVersion + +function Get-XcodePaths { + $xcodePaths = Get-ChildItem -Path "/Applications" -Filter "Xcode_*.app" | Where-Object { !$_.LinkType } + return $xcodePaths | Select-Object -ExpandProperty Fullname +} + +function Get-XcodeSDKList { + param( + [Parameter(Mandatory)] + [string]$XcodeRootPath + ) + + $versionInfo = Get-XcodeVersionInfo -XcodeRootPath $XcodeRootPath + $xcodebuildPath = Get-XcodeToolPath -XcodeRootPath $XcodeRootPath -ToolName "xcodebuild" + if ($versionInfo.Version -le [System.Version]::Parse("9.4.1")) { + $output = Invoke-Expression "$xcodebuildPath -showsdks" + $sdkList = $output | Where-Object { $_ -Match "-sdk" } + + return $sdkList | ForEach-Object { + $displayName, $canonicalName = $_.Split("-sdk") + return @{ + canonicalName = $canonicalName.Trim() + displayName = $displayName.Trim() + } + } + } + + [string]$output = Invoke-Expression "$xcodebuildPath -showsdks -json" + return $output | ConvertFrom-Json +} + +function Get-XcodeInfoList { + $defaultXcodeRootPath = Get-DefaultXcodeRootPath + + $xcodeInfo = @{} + Get-XcodePaths | ForEach-Object { + $xcodeRootPath = $_ + Switch-Xcode -XcodeRootPath $xcodeRootPath + + $versionInfo = Get-XcodeVersionInfo -XcodeRootPath $xcodeRootPath + $versionInfo.Path = $xcodeRootPath + $versionInfo.IsDefault = ($xcodeRootPath -eq $defaultXcodeRootPath) + $versionInfo.IsStable = Test-XcodeStableRelease -XcodeRootPath $xcodeRootPath + + $xcodeInfo.Add($xcodeRootPath, [PSCustomObject] @{ + VersionInfo = $versionInfo + SDKInfo = Get-XcodeSDKList -XcodeRootPath $xcodeRootPath + SimulatorsInfo = Get-XcodeSimulatorsInfo + }) + } + + Switch-Xcode -XcodeRootPath $defaultXcodeRootPath + + return $xcodeInfo +} + +function Get-XcodePlatformOrder { + param ( + [Parameter(Mandatory)] + [string] $PlatformName + ) + + Switch ($PlatformName) { + "macOS" { 1 } + "iOS" { 2 } + "Simulator - iOS" { 3 } + "tvOS" { 4 } + "Simulator - tvOS" { 5 } + "watchOS" { 6 } + "Simulator - watchOS" { 7 } + Default { 100 } + } +} + +function Get-XcodeCommandLineToolsVersion { + $xcodeCommandLineToolsVersion = Run-Command "pkgutil --pkg-info com.apple.pkg.CLTools_Executables" | Select -Index 1 | Take-Part -Part 1 + return $xcodeCommandLineToolsVersion +} + +function Build-XcodeTable { + param ( + [Parameter(Mandatory)] + [hashtable] $xcodeInfo + ) + + $sortRules = @{ + Expression = { $_.Version } + Descending = $true + } + + $xcodeList = $xcodeInfo.Values | ForEach-Object { $_.VersionInfo } | Sort-Object $sortRules + return $xcodeList | ForEach-Object { + $defaultPostfix = if ($_.IsDefault) { " (default)" } else { "" } + $betaPostfix = if ($_.IsStable) { "" } else { " (beta)" } + $targetPath = $_.Path + $symlinks = @() + Get-ChildItem -Path "/Applications" | ForEach-Object { + if ($_.LinkType -eq 'SymbolicLink') { + $linkTarget = & readlink $_.FullName + if ($linkTarget -eq $targetPath) { + $symlinks += $_.FullName + } + } + } + if ($null -eq $symlinks) { + $symlinks = @("N/A") + } + return [PSCustomObject] @{ + "Version" = $_.Version.ToString() + $betaPostfix + $defaultPostfix + "Build" = $_.Build + "Path" = $_.Path + "Symlinks" = [String]::Join("
", $symlinks) + } + } +} + +function Build-XcodeDevicesList { + param ( + [Parameter(Mandatory)][object] $XcodeInfo, + [Parameter(Mandatory)][object] $Runtime + ) + + $runtimeId = $Runtime.identifier + $runtimeName = $Runtime.name + $output = $XcodeInfo.SimulatorsInfo.devices.$runtimeId + if ($null -eq $output) { + $output = $XcodeInfo.SimulatorsInfo.devices.$runtimeName + } + + return $output +} + +function Build-XcodeSDKTable { + param ( + [Parameter(Mandatory)] + [hashtable] $xcodeInfo + ) + + $sdkNames = @() + $xcodeInfo.Values | ForEach-Object { + $_.SDKInfo | ForEach-Object { + $sdkNames += $_.canonicalName + } + } + + $sdkNames = $sdkNames | Select-Object -Unique + return $sdkNames | ForEach-Object { + $sdkName = $_ + $sdkDisplayName = "" + $xcodeList = @() + $xcodeInfo.Values | ForEach-Object { + $sdk = $_.SDKInfo | Where-Object { $_.canonicalName -eq $sdkName } | Select-Object -First 1 + if ($sdk) { + $sdkDisplayName = $sdk.displayName + $xcodeList += $_.VersionInfo.Version + } + } + + $xcodeList = $xcodeList | Sort-Object + + return [PSCustomObject] @{ + "SDK" = $sdkDisplayName + "SDK Name" = $sdkName + "Xcode Version" = [String]::Join(", ", $xcodeList) + } + } | Sort-Object { + # Sort rule 1 + $sdkNameParts = $_."SDK".Split(" ") + $platformName = [String]::Join(" ", $sdkNameParts[0..($sdkNameParts.Length - 2)]) + return Get-XcodePlatformOrder $platformName + }, { + # Sort rule 2 + $sdkNameParts = $_."SDK".Split(" ") + return [System.Version]::Parse($sdkNameParts[-1]) + } +} + +function Format-XcodeSimulatorName { + param( + [Parameter(Mandatory)][string] $Device + ) + + $formattedDeviceName = $Device.Replace("ʀ", "R") + return $formattedDeviceName +} + +function Build-XcodeSimulatorsTable { + param ( + [Parameter(Mandatory)] + [hashtable] $xcodeInfo + ) + + $runtimes = @() + $xcodeInfo.Values | ForEach-Object { + $_.SimulatorsInfo.runtimes | ForEach-Object { + $runtimes += $_ + } + } + $runtimes = $runtimes | Sort-Object @{ Expression = { $_.identifier } } -Unique + return $runtimes | ForEach-Object { + $runtime = $_ + $runtimeDevices = @() + $xcodeInfo.Values | ForEach-Object { + $runtimeFound = $_.SimulatorsInfo.runtimes | Where-Object { $_.identifier -eq $runtime.identifier } | Select-Object -First 1 + if ($runtimeFound) { + $devicesToAdd = Build-XcodeDevicesList -XcodeInfo $_ -Runtime $runtimeFound + $runtimeDevices += $devicesToAdd | Select-Object -ExpandProperty name + } + } + $runtimeDevices = $runtimeDevices | ForEach-Object { Format-XcodeSimulatorName $_ } | Select-Object -Unique + If (($runtimeDevices | Where-Object { -not ([string]::IsNullOrWhitespace($_)) }).Count -eq 0) { + $sortedRuntimeDevices = @("N/A") + } else { + $sortedRuntimeDevices = $runtimeDevices | Sort-Object @{ + Expression = { $_.Split(" ")[0] }; + Descending = $true; + }, { + $_.Split(" ") | Select-Object -Skip 1 | Join-String -Separator " " + } + } + return [PSCustomObject] @{ + "OS" = $runtime.name + "Simulators" = [String]::Join("
", $sortedRuntimeDevices) + } + } | Sort-Object { + # Sort rule 1 + $sdkNameParts = $_."OS".Split(" ") + $platformName = [String]::Join(" ", $sdkNameParts[0..($sdkNameParts.Length - 2)]) + return Get-XcodePlatformOrder $platformName + }, { + # Sort rule 2 + $sdkNameParts = $_."OS".Split(" ") + return [System.Version]::Parse($sdkNameParts[-1]) + } +} + +function Build-XcodeSupportToolsSection { + $toolNodes = @() + + $xcpretty = Run-Command "xcpretty --version" + $xcversion = Run-Command "xcversion --version" | Select-String "^[0-9]" + + $toolNodes += [ToolVersionNode]::new("xcpretty", $xcpretty) + if ($os.IsMonterey) { + $toolNodes += [ToolVersionNode]::new("xcversion", $xcversion) + } + + $nomadOutput = Run-Command "gem list nomad-cli" + $nomadCLI = [regex]::matches($nomadOutput, "(\d+.){2}\d+").Value + $nomadShenzhenOutput = Run-Command "ipa -version" + $nomadShenzhen = [regex]::matches($nomadShenzhenOutput, "(\d+.){2}\d+").Value + + return $toolNodes +} diff --git a/images/macos/scripts/helpers/Common.Helpers.psm1 b/images/macos/scripts/helpers/Common.Helpers.psm1 new file mode 100644 index 0000000..359f597 --- /dev/null +++ b/images/macos/scripts/helpers/Common.Helpers.psm1 @@ -0,0 +1,116 @@ +function Get-CommandResult { + param ( + [Parameter(Mandatory=$true)] + [string] $Command, + [switch] $Multiline + ) + # Bash trick to suppress and show error output because some commands write to stderr (for example, "python --version") + $stdout = & bash -c "$Command 2>&1" + $exitCode = $LASTEXITCODE + + return @{ + Output = If ($Multiline -eq $true) { $stdout } else { [string]$stdout } + ExitCode = $exitCode + } +} + +# Gets path to the tool, analogue of 'which tool' +function Get-ToolPath($tool) { + return (Get-Command $tool).Path +} + +# Returns the object with information about current OS +# It can be used for OS-specific tests +function Get-OSVersion { + $osVersion = [Environment]::OSVersion + $processorArchitecture = arch + + return [PSCustomObject]@{ + Version = $osVersion.Version + Platform = $osVersion.Platform + IsArm64 = $processorArchitecture -eq "arm64" + IsMonterey = $osVersion.Version.Major -eq "12" + IsVentura = $($osVersion.Version.Major -eq "13") + IsVenturaArm64 = $($osVersion.Version.Major -eq "13" -and $processorArchitecture -eq "arm64") + IsVenturaX64 = $($osVersion.Version.Major -eq "13" -and $processorArchitecture -ne "arm64") + IsSonoma = $($osVersion.Version.Major -eq "14") + IsSonomaArm64 = $($osVersion.Version.Major -eq "14" -and $processorArchitecture -eq "arm64") + IsSonomaX64 = $($osVersion.Version.Major -eq "14" -and $processorArchitecture -ne "arm64") + IsSequoia = $($osVersion.Version.Major -eq "15") + IsSequoiaArm64 = $($osVersion.Version.Major -eq "15" -and $processorArchitecture -eq "arm64") + IsSequoiaX64 = $($osVersion.Version.Major -eq "15" -and $processorArchitecture -ne "arm64") + } +} + +function Get-ToolsetContent { + <# + .SYNOPSIS + Retrieves the content of the toolset.json file. + + .DESCRIPTION + This function reads the toolset.json in path provided by IMAGE_FOLDER + environment variable and returns the content as a PowerShell object. + #> + + $toolsetPath = Join-Path $env:IMAGE_FOLDER "toolset.json" + $toolsetJson = Get-Content -Path $toolsetPath -Raw + ConvertFrom-Json -InputObject $toolsetJson +} + +function Invoke-DownloadWithRetry { + Param + ( + [Parameter(Mandatory)] + [string] $Url, + [Alias("Destination")] + [string] $Path + ) + + if (-not $Path) { + $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join '' + $re = "[{0}]" -f [RegEx]::Escape($invalidChars) + $fileName = [IO.Path]::GetFileName($Url) -replace $re + + if ([String]::IsNullOrEmpty($fileName)) { + $fileName = [System.IO.Path]::GetRandomFileName() + } + $Path = Join-Path -Path "/tmp" -ChildPath $fileName + } + + Write-Host "Downloading package from $Url to $Path..." + + $interval = 30 + $downloadStartTime = Get-Date + for ($retries = 20; $retries -gt 0; $retries--) { + try { + $attemptStartTime = Get-Date + (New-Object System.Net.WebClient).DownloadFile($Url, $Path) + $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2) + Write-Host "Package downloaded in $attemptSeconds seconds" + break + } catch { + $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2) + Write-Warning "Package download failed in $attemptSeconds seconds" + Write-Warning $_.Exception.Message + } + + if ($retries -eq 0) { + $totalSeconds = [math]::Round(($(Get-Date) - $downloadStartTime).TotalSeconds, 2) + throw "Package download failed after $totalSeconds seconds" + } + + Write-Warning "Waiting $interval seconds before retrying (retries left: $retries)..." + Start-Sleep -Seconds $interval + } + + return $Path +} + +function Get-Architecture { + $arch = arch + if ($arch -ne "arm64") { + $arch = "x64" + } + + return $arch +} diff --git a/images/macos/scripts/helpers/Xcode.Helpers.psm1 b/images/macos/scripts/helpers/Xcode.Helpers.psm1 new file mode 100644 index 0000000..a55d8de --- /dev/null +++ b/images/macos/scripts/helpers/Xcode.Helpers.psm1 @@ -0,0 +1,312 @@ +function Get-XcodeRootPath { + Param ( + [Parameter(Mandatory)] + [string] $Version + ) + + return "/Applications/Xcode_$Version.app" +} + +function Get-DefaultXcodeRootPath { + return (Get-Item -Path "/Applications/Xcode.app").Target +} + +function Get-XcodeToolPath { + param ( + [Parameter(ParameterSetName = 'Version')] + [string] $Version, + [Parameter(ParameterSetName = 'Path')] + [string] $XcodeRootPath, + [string] $ToolName + ) + + if ($PSCmdlet.ParameterSetName -eq "Version") { + $XcodeRootPath = Get-XcodeRootPath $Version + } + + return Join-Path $XcodeRootPath "Contents/Developer/usr/bin" $ToolName +} + +function Get-XcodeVersionInfo { + param ( + [Parameter(Mandatory)] + [string] $XcodeRootPath + ) + + $xcodebuildPath = Get-XcodeToolPath -XcodeRootPath $XcodeRootPath -ToolName "xcodebuild" + [string]$output = Invoke-Expression "$xcodebuildPath -version" + $versionOutputParts = $output.Split(" ") + + return @{ + Version = [System.Version]::Parse($versionOutputParts[1]) + Build = $versionOutputParts[4] + } +} + +function Switch-Xcode { + param ( + [Parameter(ParameterSetName = 'Version')] + [string] $Version, + [Parameter(ParameterSetName = 'Path')] + [string] $XcodeRootPath + ) + + if ($PSCmdlet.ParameterSetName -eq "Version") { + $XcodeRootPath = Get-XcodeRootPath $Version + } + + Write-Verbose "Switching Xcode to '${XcodeRootPath}'" + Invoke-Expression "sudo xcode-select --switch ${XcodeRootPath}" +} + +function Test-XcodeStableRelease { + param ( + [Parameter(ParameterSetName = 'Version')] + [string] $Version, + [Parameter(ParameterSetName = 'Path')] + [string] $XcodeRootPath + ) + + if ($PSCmdlet.ParameterSetName -eq "Version") { + $XcodeRootPath = Get-XcodeRootPath $Version + } + + $licenseInfoPlistPath = Join-Path $XcodeRootPath "Contents" "Resources" "LicenseInfo.plist" + $releaseType = & defaults read $licenseInfoPlistPath "licenseType" + + return -not ($releaseType -match "beta") +} + +function Get-XcodeSimulatorsInfo { + param ( + [string] $Filter + ) + + [string]$rawSimulatorsInfo = Invoke-Expression "xcrun simctl list --json" + $jsonSimulatorsInfo = $rawSimulatorsInfo | ConvertFrom-Json + + if ($Filter) { + return $jsonSimulatorsInfo | Select-Object -ExpandProperty $Filter + } + + return $jsonSimulatorsInfo +} + +function Get-XcodeDevicesList { + $result = @() + + $runtimes = Get-XcodeSimulatorsInfo -Filter "devices" + $runtimes.PSObject.Properties | ForEach-Object { + $runtimeName = $_.Name + $devices = $_.Value + $devices | Where-Object { + $availability = $_.availability + $isAvailable = $_.isAvailable + return (($availability -eq "(available)") -or ($isAvailable -eq "YES") -or ($isAvailable -eq $true)) + } | ForEach-Object { + $deviceName = $_.name + $result += "$runtimeName $deviceName" + } + } + + return $result +} + +function Get-XcodePairsList { + $result = @() + + $runtimes = Get-XcodeSimulatorsInfo -Filter "pairs" + $runtimes.PSObject.Properties | Where-Object { + return $_.Value.state -match "active" + } | ForEach-Object { + $watchName = $_.Value.watch.name + $phoneName = $_.Value.phone.name + $result += "$watchName $phoneName" + } + + return $result +} + +#Helper function for execution of xcversion due to: https://github.com/fastlane/fastlane/issues/18161 +function Invoke-XCVersion { + param ( + [Parameter(Mandatory)] + [string] $Arguments, + [Parameter()] + [int] $RetryAttempts = 7, + [Parameter()] + [int] $PauseDurationSecs = 1 + ) + + $Command = "xcversion $Arguments" + Write-Host "Execute [$Command]" + for ($Attempt=1; $Attempt -le $RetryAttempts; $Attempt++) { + Write-Host "Current attempt: [$Attempt]" + $result = Get-CommandResult -Command $Command -Multiline + Write-Host "Exit code: [$($result.ExitCode)]" + Write-Host "Output message: " + $result.Output | ForEach-Object { Write-Host $_ } + if ($result.ExitCode -ne 0) { + Start-Sleep -Seconds $PauseDurationSecs + } else { + return $result.Output + } + } + if ($result.ExitCode -ne 0) { + throw "Command [$Command] has finished with non-zero exit code." + } +} + +function Get-BrokenXcodeSimulatorsList { + return @( + # tvOS Simulators + @{ + SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-15-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-15-2"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-15-4"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-16-0"; + XcodeVersion = "14.2" + }, + # watchOS-8-0 Simulators + @{ + SimulatorName = "Apple Watch Series 5 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple Watch Series 5 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 41mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-41mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 45mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-45mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + # watchOS-8-3 Simulators + @{ + SimulatorName = "Apple Watch Series 5 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple Watch Series 5 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 41mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-41mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 45mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-45mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + # watchOS-8-5 Simulators + @{ + SimulatorName = "Apple Watch Series 5 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple Watch Series 5 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 41mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-41mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 45mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-45mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + # watchOS-9-0 Simulators + @{ + SimulatorName = "Apple Watch SE (40mm) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-SE-40mm-2nd-generation"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-9-0"; + XcodeVersion = "14.2" + }, + @{ + SimulatorName = "Apple Watch SE (44mm) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-SE-44mm-2nd-generation"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-9-0"; + XcodeVersion = "14.2" + } + ) +} diff --git a/images/macos/scripts/helpers/Xcode.Installer.psm1 b/images/macos/scripts/helpers/Xcode.Installer.psm1 new file mode 100644 index 0000000..bbca20b --- /dev/null +++ b/images/macos/scripts/helpers/Xcode.Installer.psm1 @@ -0,0 +1,289 @@ +Import-Module "$PSScriptRoot/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/Xcode.Helpers.psm1" + +function Install-XcodeVersion { + param ( + [Parameter(Mandatory)] + [string] $Version, + [Parameter(Mandatory)] + [string] $LinkTo, + [Parameter(Mandatory)] + [string] $Sha256Sum + ) + + $xcodeDownloadDirectory = "$env:HOME/Library/Caches/XcodeInstall" + $xcodeTargetPath = Get-XcodeRootPath -Version $LinkTo + $xcodeXipDirectory = Invoke-DownloadXcodeArchive -DownloadDirectory $xcodeDownloadDirectory -Version $Version + Expand-XcodeXipArchive -DownloadDirectory $xcodeXipDirectory.FullName -TargetPath $xcodeTargetPath + Remove-Item -Path $xcodeXipDirectory -Force -Recurse +} + +function Invoke-DownloadXcodeArchive { + param ( + [Parameter(Mandatory)] + [string] $DownloadDirectory, + [Parameter(Mandatory)] + [string] $Version + ) + + Write-Host "Downloading Xcode $Version" + $tempXipDirectory = New-Item -Path $DownloadDirectory -Name "Xcode$Version" -ItemType "Directory" + $xcodeFileName = 'Xcode-{0}.xip' -f $Version + $xcodeUri = '{0}{1}?{2}'-f ${env:XCODE_INSTALL_STORAGE_URL}, $xcodeFileName, ${env:XCODE_INSTALL_SAS} + $xcodeFullPath = Join-Path $tempXipDirectory.FullName $xcodeFileName + Invoke-DownloadWithRetry -Url $xcodeUri -Path $xcodeFullPath | Out-Null + + # Validating checksum + $xcodeSha256 = Get-FileHash -Path $xcodeFullPath -Algorithm SHA256 | Select-Object -ExpandProperty Hash + if ($xcodeSha256 -ne $Sha256Sum) { + throw "Xcode $Version checksum mismatch. Expected: $Sha256Sum, Actual: $xcodeSha256" + } + + return $tempXipDirectory +} + +function Resolve-ExactXcodeVersion { + param ( + [Parameter(Mandatory)] + [string] $Version + ) + + # if toolset string contains spaces, consider it as a full name of Xcode + if ($Version -match "\s") { + return $Version + } + + $semverVersion = [SemVer]::Parse($Version) + $availableVersions = Get-AvailableXcodeVersions + $satisfiedVersions = $availableVersions | Where-Object { $semverVersion -eq $_.stableSemver } + + return $satisfiedVersions | Select-Object -Last 1 -ExpandProperty rawVersion +} + +function Get-AvailableXcodeVersions { + $rawVersionsList = Invoke-XCVersion -Arguments "list" | ForEach-Object { $_.Trim() } | Where-Object { $_ -match "^\d" } + $availableVersions = $rawVersionsList | ForEach-Object { + $partStable,$partMajor = $_.Split(" ", 2) + $semver = $stableSemver = [SemVer]::Parse($partStable) + + if ($partMajor) { + # Convert 'beta 3' -> 'beta.3', 'Release Candidate' -> 'releasecandidate', 'GM Seed 2' -> 'gmseed.2' + $normalizedLabel = $partMajor.toLower() -replace " (\d)", '.$1' -replace " ([a-z])", '$1' + $semver = [SemVer]::new($stableSemver.Major, $stableSemver.Minor, $stableSemver.Patch, $normalizedLabel) + } + + return [PSCustomObject]@{ + semver = $semver + rawVersion = $_ + stableSemver = $stableSemver + } + } + + return $availableVersions | Sort-Object -Property semver +} + +function Expand-XcodeXipArchive { + param ( + [Parameter(Mandatory)] + [string] $DownloadDirectory, + [Parameter(Mandatory)] + [string] $TargetPath + ) + + $xcodeXipPath = Get-ChildItem -Path $DownloadDirectory -Filter "Xcode-*.xip" | Select-Object -First 1 + + Write-Host "Extracting Xcode from '$xcodeXipPath'" + Push-Location $DownloadDirectory + if ([boolean] (Get-Command 'unxip' -ErrorAction 'SilentlyContinue')) { + Invoke-ValidateCommand "unxip $xcodeXipPath" + } else { + Invoke-ValidateCommand "xip -x $xcodeXipPath" + } + Pop-Location + + if (Test-Path "$DownloadDirectory/Xcode-beta.app") { + Write-Host "Renaming Xcode-beta.app to Xcode.app" + Rename-Item -Path "$DownloadDirectory/Xcode-beta.app" -NewName "Xcode.app" + } + + if (-not (Test-Path "$DownloadDirectory/Xcode.app")) { + throw "XIP archive '$xcodeXipPath' doesn't contain 'Xcode.app'" + } + + Write-Host "Moving '$DownloadDirectory/Xcode.app' to '$TargetPath'" + Move-Item -Path "$DownloadDirectory/Xcode.app" -Destination $TargetPath +} + +function Confirm-XcodeIntegrity { + param ( + [Parameter(Mandatory)] + [string] $Version + ) + + $XcodeRootPath = Get-XcodeRootPath -Version $Version + if (Test-XcodeStableRelease -XcodeRootPath $XcodeRootPath) { + Write-Host "Validating Xcode integrity for '$XcodeRootPath'..." + Invoke-ValidateCommand "spctl --assess --raw $XcodeRootPath" + } +} + +function Approve-XcodeLicense { + param ( + [Parameter(Mandatory)] + [string] $Version + ) + + $os = Get-OSVersion + $XcodeRootPath = Get-XcodeRootPath -Version $Version + Write-Host "Approving Xcode license for '$XcodeRootPath'..." + $xcodeBuildPath = Get-XcodeToolPath -XcodeRootPath $XcodeRootPath -ToolName "xcodebuild" + + if ($os.IsVentura -or $os.IsSonoma) { + Invoke-ValidateCommand -Command "sudo $xcodeBuildPath -license accept" -Timeout 15 + } else { + Invoke-ValidateCommand -Command "sudo $xcodeBuildPath -license accept" + } +} + +function Install-XcodeAdditionalPackages { + param ( + [Parameter(Mandatory)] + [string] $Version + ) + + Write-Host "Installing additional packages for Xcode $Version..." + $xcodeRootPath = Get-XcodeRootPath -Version $Version + $packages = Get-ChildItem -Path "$xcodeRootPath/Contents/Resources/Packages" -Filter "*.pkg" -File + $packages | ForEach-Object { + Invoke-ValidateCommand "sudo installer -pkg $($_.FullName) -target / -allowUntrusted" + } +} + +function Invoke-XcodeRunFirstLaunch { + param ( + [Parameter(Mandatory)] + [string] $Version + ) + + if ($Version.StartsWith("8") -or $Version.StartsWith("9")) { + return + } + + Write-Host "Running 'runFirstLaunch' for Xcode $Version..." + $xcodeRootPath = Get-XcodeToolPath -Version $Version -ToolName "xcodebuild" + Invoke-ValidateCommand "sudo $xcodeRootPath -runFirstLaunch" +} + +function Install-AdditionalSimulatorRuntimes { + param ( + [Parameter(Mandatory)] + [string] $Version + ) + + Write-Host "Installing Simulator Runtimes for Xcode $Version ..." + $xcodebuildPath = Get-XcodeToolPath -Version $Version -ToolName "xcodebuild" + Invoke-ValidateCommand "$xcodebuildPath -downloadAllPlatforms" | Out-Null +} + +function Build-XcodeSymlinks { + param ( + [Parameter(Mandatory)] + [string] $Version, + [string[]] $Symlinks + ) + + $sourcePath = Get-XcodeRootPath -Version $Version + $Symlinks | Where-Object { $_ } | ForEach-Object { + $targetPath = Get-XcodeRootPath -Version $_ + Write-Host "Creating symlink: '$targetPath' -> '$sourcePath'" + New-Item -Path $targetPath -ItemType SymbolicLink -Value $sourcePath | Out-Null + } +} + +function Initialize-XcodeLaunchServicesDb { + param ( + [Parameter(Mandatory)] + [string] $Version + ) + + $xcodePath = Get-XcodeRootPath -Version $Version + $lsregister = '/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister' + Get-ChildItem -Recurse -Filter "*.app" $xcodePath | Foreach-Object { & $lsregister -f $_.FullName} +} + +function Build-ProvisionatorSymlink { + param ( + [Parameter(Mandatory)] + [string] $Version + ) + + $sourcePath = Get-XcodeRootPath -Version $Version + $versionInfo = Get-XcodeVersionInfo -XcodeRootPath $sourcePath + + $targetVersion = [SemVer]::Parse($versionInfo.Version).ToString() + $targetPath = Get-XcodeRootPath -Version $targetVersion + if ($sourcePath -ne $targetPath) { + Write-Host "Creating symlink: '$targetPath' -> '$sourcePath'" + New-Item -Path $targetPath -ItemType SymbolicLink -Value $sourcePath | Out-Null + } +} + +function Set-XcodeDeveloperDirEnvironmentVariables { + param ( + [Parameter(Mandatory)] + [string[]] $XcodeList + ) + + $exactVersionsList = $XcodeList | Where-Object { Test-XcodeStableRelease -Version $_ } | ForEach-Object { + $xcodeRootPath = Get-XcodeRootPath -Version $_ + $xcodeVersionInfo = Get-XcodeVersionInfo -XcodeRootPath $xcodeRootPath + return @{ + RootPath = $xcodeRootPath + Version = [SemVer]::Parse($xcodeVersionInfo.Version) + } + } | Sort-Object -Property Version -Descending + + $majorVersions = $exactVersionsList.Version.Major | Select-Object -Unique + $majorVersions | ForEach-Object { + $majorVersion = $_ + $latestXcodeVersion = $exactVersionsList | Where-Object { $_.Version.Major -eq $majorVersion } | Select-Object -First 1 + $variableName = "XCODE_${_}_DEVELOPER_DIR" + $variableValue = "$($latestXcodeVersion.RootPath)/Contents/Developer" + Write-Host "Set ${variableName}=${variableValue}" + "export ${variableName}=${variableValue}" | Out-File "$env:HOME/.bashrc" -Append + } +} + +function Invoke-ValidateCommand { + param ( + [Parameter(Mandatory)] + [string] $Command, + [Uint] $Timeout = 0 + ) + + if ($Timeout -eq 0) { + $output = Invoke-Expression -Command $Command + if ($LASTEXITCODE -ne 0) { + throw "Command '$Command' has finished with exit code $LASTEXITCODE" + } + return $output + } else { + $job = $command | Start-Job -ScriptBlock { + $output = Invoke-Expression -Command $input + if ($LASTEXITCODE -ne 0) { + throw 'Command failed' + } + return $output + } + $waitObject = $job | Wait-Job -Timeout $Timeout + + if (-not $waitObject) { + throw "Command '$Command' has timed out" + } + + if ($waitObject.State -eq 'Failed') { + throw "Command '$Command' has failed" + } + Receive-Job -Job $job + } +} diff --git a/images/macos/scripts/helpers/confirm-identified-developers-macos13.scpt b/images/macos/scripts/helpers/confirm-identified-developers-macos13.scpt new file mode 100644 index 0000000..6ab8351 --- /dev/null +++ b/images/macos/scripts/helpers/confirm-identified-developers-macos13.scpt @@ -0,0 +1,34 @@ +# This AppleScript clicks "Allow" for "System Software from developer "Parallels International GmbH" +# Steps: +# - Open System Settings -> Privacy & Security +# - Click 'Allow' for 'System Software from developer "Parallels International GmbH' +# - Enter password for runner + +on run argv + set userpassword to item 1 of argv + + tell application "System Settings" + activate + delay 5 + end tell + + tell application "System Events" + tell process "System Settings" + set frontmost to true + repeat until exists window 1 + delay 2 + end repeat + + tell splitter group 1 of group 1 of window 1 + select row 20 of outline 1 of scroll area 1 of group 1 + delay 5 + click UI Element 2 of group 4 of scroll area 1 of group 1 of group 2 + delay 5 + keystroke userpassword + delay 5 + keystroke return + delay 5 + end tell + end tell + end tell +end run diff --git a/images/macos/scripts/helpers/confirm-identified-developers-macos14.scpt b/images/macos/scripts/helpers/confirm-identified-developers-macos14.scpt new file mode 100644 index 0000000..4c19233 --- /dev/null +++ b/images/macos/scripts/helpers/confirm-identified-developers-macos14.scpt @@ -0,0 +1,34 @@ +# This AppleScript clicks "Allow" for "System Software from developer "Parallels International GmbH" +# Steps: +# - Open System Settings -> Privacy & Security +# - Click 'Allow' for 'System Software from developer "Parallels International GmbH' +# - Enter password for runner + +on run argv + set userpassword to item 1 of argv + + tell application "System Settings" + activate + delay 5 + end tell + + tell application "System Events" + tell process "System Settings" + set frontmost to true + repeat until exists window 1 + delay 2 + end repeat + + tell splitter group 1 of group 1 of window 1 + select row 18 of outline 1 of scroll area 1 of group 1 + delay 5 + click UI Element 2 of group 5 of scroll area 1 of group 1 of group 2 + delay 5 + keystroke userpassword + delay 5 + keystroke return + delay 5 + end tell + end tell + end tell +end run diff --git a/images/macos/scripts/helpers/confirm-identified-developers.scpt b/images/macos/scripts/helpers/confirm-identified-developers.scpt new file mode 100644 index 0000000..31a650d --- /dev/null +++ b/images/macos/scripts/helpers/confirm-identified-developers.scpt @@ -0,0 +1,49 @@ +# This AppleScript confirms developers in security preferences via macOS UI. +# It uses after VirtualBox installation to add 'Oracle Inc' as identified developer. +# Steps: +# - Close security preferences pop-up (it can be open after VirtualBox installation) +# - Open System Preferences -> Security & Privacy -> General +# - Unlock security preferences with user password (button 'Click the lock to make changes') +# - Click 'Allow' or 'Details…' button to confirm developers +# - Click 'Not now' button on restarting pop-up +# - Close System Preferences + +on run argv + set userpassword to item 1 of argv + set secpane to "Security & Privacy" + + activate application "System Preferences" + delay 5 + + tell application "System Events" + tell process "System Preferences" + set frontmost to true + delay 2 + click menu item secpane of menu "View" of menu bar 1 + delay 5 + click button 1 of window 1 + delay 5 + keystroke userpassword + delay 5 + keystroke return + delay 5 + click radio button "General" of tab group 1 of window 1 + delay 5 + if exists of UI element "Details…" of tab group 1 of window 1 then + click button "Details…" of tab group 1 of window 1 + delay 5 + keystroke return + delay 5 + keystroke return + delay 5 + end if + if exists of UI element "Allow" of tab group 1 of window 1 then + click button "Allow" of tab group 1 of window 1 + delay 5 + keystroke return + delay 5 + end if + click button 5 of window 1 + end tell + end tell +end run \ No newline at end of file diff --git a/images/macos/scripts/helpers/invoke-tests.sh b/images/macos/scripts/helpers/invoke-tests.sh new file mode 100644 index 0000000..232d3c0 --- /dev/null +++ b/images/macos/scripts/helpers/invoke-tests.sh @@ -0,0 +1,5 @@ +#!/bin/bash -e -o pipefail + +source $HOME/.bashrc +pwsh -Command "Import-Module '$HOME/image-generation/tests/Helpers.psm1' -DisableNameChecking + Invoke-PesterTests -TestFile \"$1\" -TestName \"$2\"" diff --git a/images/macos/scripts/helpers/utils.sh b/images/macos/scripts/helpers/utils.sh new file mode 100644 index 0000000..6dbb231 --- /dev/null +++ b/images/macos/scripts/helpers/utils.sh @@ -0,0 +1,218 @@ +#!/bin/bash -e -o pipefail + +download_with_retry() { + url=$1 + download_path=$2 + + if [ -z "$download_path" ]; then + download_path="/tmp/$(basename "$url")" + fi + + echo "Downloading package from $url to $download_path..." >&2 + + interval=30 + download_start_time=$(date +%s) + + for ((retries=20; retries>0; retries--)); do + attempt_start_time=$(date +%s) + if http_code=$(curl -4sSLo "$download_path" "$url" -w '%{http_code}'); then + attempt_seconds=$(($(date +%s) - attempt_start_time)) + if [ "$http_code" -eq 200 ]; then + echo "Package downloaded in $attempt_seconds seconds" >&2 + break + else + echo "Received HTTP status code $http_code after $attempt_seconds seconds" >&2 + fi + else + attempt_seconds=$(($(date +%s) - attempt_start_time)) + echo "Package download failed in $attempt_seconds seconds" >&2 + fi + + if [ $retries -eq 0 ]; then + total_seconds=$(($(date +%s) - download_start_time)) + echo "Package download failed after $total_seconds seconds" >&2 + exit 1 + fi + + echo "Waiting $interval seconds before retrying (retries left: $retries)..." >&2 + sleep $interval + done + + echo "$download_path" +} + +is_Arm64() { + [ "$(arch)" = "arm64" ] +} + +is_Sequoia() { + [ "$OSTYPE" = "darwin24" ] +} + +is_SequoiaArm64() { + is_Sequoia && is_Arm64 +} + +is_SequoiaX64() { + is_Sequoia && ! is_Arm64 +} + +is_Sonoma() { + [ "$OSTYPE" = "darwin23" ] +} + +is_SonomaArm64() { + is_Sonoma && is_Arm64 +} + +is_SonomaX64() { + is_Sonoma && ! is_Arm64 +} + +is_Ventura() { + [ "$OSTYPE" = "darwin22" ] +} + +is_VenturaArm64() { + is_Ventura && is_Arm64 +} + +is_VenturaX64() { + is_Ventura && ! is_Arm64 +} + +is_Monterey() { + [ "$OSTYPE" = "darwin21" ] +} + +get_toolset_value() { + local toolset_path=$(echo "$IMAGE_FOLDER/toolset.json") + local query=$1 + echo "$(jq -r "$query" $toolset_path)" +} + +# brew provides package bottles for different macOS versions +# The 'brew install' command will fail if a package bottle does not exist +# Use the '--build-from-source' option to build from source in this case +brew_smart_install() { + local tool_name=$1 + + echo "Downloading $tool_name..." + + # get deps & cache em + + failed=true + for i in {1..10}; do + brew deps $tool_name > /tmp/$tool_name && failed=false || sleep 60 + [ "$failed" = false ] && break + done + + if [ "$failed" = true ]; then + echo "Failed: brew deps $tool_name" + exit 1; + fi + + for dep in $(cat /tmp/$tool_name) $tool_name; do + + failed=true + for i in {1..10}; do + brew --cache $dep >/dev/null && failed=false || sleep 60 + [ "$failed" = false ] && break + done + + if [ "$failed" = true ]; then + echo "Failed: brew --cache $dep" + exit 1; + fi + done + + failed=true + for i in {1..10}; do + brew install $tool_name && failed=false || sleep 60 + [ "$failed" = false ] && break + done + + if [ "$failed" = true ]; then + echo "Failed: brew install $tool_name" + exit 1; + fi +} + +configure_system_tccdb () { + local values=$1 + local dbPath="/Library/Application Support/com.apple.TCC/TCC.db" + local sqlQuery="INSERT OR IGNORE INTO access VALUES($values);" + sudo sqlite3 "$dbPath" "$sqlQuery" +} + +configure_user_tccdb () { + local values=$1 + local dbPath="$HOME/Library/Application Support/com.apple.TCC/TCC.db" + local sqlQuery="INSERT OR IGNORE INTO access VALUES($values);" + sqlite3 "$dbPath" "$sqlQuery" +} + +resolve_github_release_asset_url() { + local repo=$1 + local filter=$2 + local version=${3:-"*"} + local api_pat=$4 + + page_size="100" + + [ -n "$api_pat" ] && authString=(-H "Authorization: token ${api_pat}") + + json=$(curl "${authString[@]}" -fsSL "https://api.github.com/repos/${repo}/releases?per_page=${page_size}") + + if [[ $version == "latest" ]]; then + tag_name=$(echo $json | jq -r '.[].tag_name' | sort --unique --version-sort | egrep -v ".*-[a-z]|beta" | tail -n 1) + elif [[ $version == *"*"* ]]; then + tag_name=$(echo $json | jq -r '.[].tag_name' | sort --unique --version-sort | egrep -v ".*-[a-z]|beta" | egrep "${version}" | tail -n 1) + else + tag_name=$(echo $json | jq -r '.[].tag_name' | sort --unique --version-sort | egrep -v ".*-[a-z]|beta" | egrep "${version}") + fi + + download_url=$(echo $json | jq -r ".[] | select(.tag_name==\"${tag_name}\").assets[].browser_download_url | select(${filter})" | head -n 1) + if [ -z "$download_url" ]; then + echo "Failed to parse a download url for the '${tag_name}' tag using '${filter}' filter" + exit 1 + fi + + echo $download_url +} + +# Close all finder windows because they can interfere with UI tests +close_finder_window() { + osascript -e 'tell application "Finder" to close windows' +} + +get_arch() { + arch=$(arch) + if [[ $arch == "arm64" ]]; then + echo "arm64" + else + echo "x64" + fi +} + +use_checksum_comparison() { + local file_path=$1 + local checksum=$2 + local sha_type=${3:-"256"} + + echo "Performing checksum verification" + + if [[ ! -f "$file_path" ]]; then + echo "File not found: $file_path" + exit 1 + fi + + local_file_hash=$(shasum --algorithm "$sha_type" "$file_path" | awk '{print $1}') + + if [[ "$local_file_hash" != "$checksum" ]]; then + echo "Checksum verification failed. Expected hash: $checksum; Actual hash: $local_file_hash." + exit 1 + else + echo "Checksum verification passed" + fi +} diff --git a/images/macos/scripts/helpers/xamarin-utils.sh b/images/macos/scripts/helpers/xamarin-utils.sh new file mode 100644 index 0000000..20e3b9d --- /dev/null +++ b/images/macos/scripts/helpers/xamarin-utils.sh @@ -0,0 +1,215 @@ +#!/bin/bash -e -o pipefail + +source ~/utils/utils.sh + +# Xamarin can clean their SDKs while updating to newer versions, +# so we should be able to detect it during image generation + +buildVSMacDownloadUrl() { + echo "https://dl.xamarin.com/VsMac/VisualStudioForMac-${1}.dmg" +} + +buildMonoDownloadUrl() { + echo "https://dl.xamarin.com/MonoFrameworkMDK/Macx86/MonoFramework-MDK-${1}.macos10.xamarin.universal.pkg" +} + +buildXamariniIOSDownloadUrl() { + echo "https://dl.xamarin.com/MonoTouch/Mac/xamarin.ios-${1}.pkg" +} + +buildXamarinMacDownloadUrl() { + echo "https://dl.xamarin.com/XamarinforMac/Mac/xamarin.mac-${1}.pkg" +} + +buildXamarinAndroidDownloadUrl() { + echo "https://dl.xamarin.com/MonoforAndroid/Mac/xamarin.android-${1}.pkg" +} + +installMono() { + local VERSION=$1 + + echo "Installing Mono ${VERSION}..." + local MONO_FOLDER_NAME=$(echo $VERSION | cut -d. -f 1,2,3) + local SHORT_VERSION=$(echo $VERSION | cut -d. -f 1,2) + local PKG_URL=$(buildMonoDownloadUrl $VERSION) + + sudo installer -pkg "$(download_with_retry "$PKG_URL")" -target / + + echo "Installing nunit3-console for Mono "$VERSION + installNunitConsole $MONO_FOLDER_NAME + + echo "Creating short symlink '${SHORT_VERSION}'" + sudo ln -s ${MONO_VERSIONS_PATH}/${MONO_FOLDER_NAME} ${MONO_VERSIONS_PATH}/${SHORT_VERSION} + + echo "Move to backup folder" + sudo mv -v $MONO_VERSIONS_PATH/* $TMPMOUNT_FRAMEWORKS/mono/ +} + +installXamarinIOS() { + local VERSION=$1 + + echo "Installing Xamarin.iOS ${VERSION}..." + local SHORT_VERSION=$(echo $VERSION | cut -d. -f 1,2) + local PKG_URL=$(buildXamariniIOSDownloadUrl $VERSION) + + sudo installer -pkg "$(download_with_retry "$PKG_URL")" -target / + + echo "Creating short symlink '${SHORT_VERSION}'" + sudo ln -s ${IOS_VERSIONS_PATH}/${VERSION} ${IOS_VERSIONS_PATH}/${SHORT_VERSION} + + echo "Move to backup folder" + sudo mv -v $IOS_VERSIONS_PATH/* $TMPMOUNT_FRAMEWORKS/ios/ +} + +installXamarinMac() { + local VERSION=$1 + + echo "Installing Xamarin.Mac ${VERSION}..." + local SHORT_VERSION=$(echo $VERSION | cut -d. -f 1,2) + local PKG_URL=$(buildXamarinMacDownloadUrl $VERSION) + + sudo installer -pkg "$(download_with_retry "$PKG_URL")" -target / + + echo "Creating short symlink '${SHORT_VERSION}'" + sudo ln -s ${MAC_VERSIONS_PATH}/${VERSION} ${MAC_VERSIONS_PATH}/${SHORT_VERSION} + + echo "Move to backup folder" + sudo mv -v $MAC_VERSIONS_PATH/* $TMPMOUNT_FRAMEWORKS/mac/ +} + +installXamarinAndroid() { + local VERSION=$1 + + echo "Installing Xamarin.Android ${VERSION}..." + local SHORT_VERSION=$(echo $VERSION | cut -d. -f 1,2) + local PKG_URL=$(buildXamarinAndroidDownloadUrl $VERSION) + + sudo installer -pkg "$(download_with_retry "$PKG_URL")" -target / + + if [ "$VERSION" == "9.4.1.0" ]; then + # Fix symlinks for broken Xamarin.Android + fixXamarinAndroidSymlinksInLibDir $VERSION + fi + + echo "Creating short symlink '${SHORT_VERSION}'" + sudo ln -s ${ANDROID_VERSIONS_PATH}/${VERSION} ${ANDROID_VERSIONS_PATH}/${SHORT_VERSION} + + echo "Move to backup folder" + sudo mv -v $ANDROID_VERSIONS_PATH/* $TMPMOUNT_FRAMEWORKS/android/ +} + +createBundle() { + local SYMLINK=$1 + local MONO_SDK=$2 + local IOS_SDK=$3 + local MAC_SDK=$4 + local ANDROID_SDK=$5 + + echo "Creating bundle '$SYMLINK' (Mono $MONO_SDK; iOS $IOS_SDK; Mac $MAC_SDK; Android $ANDROID_SDK" + deleteSymlink ${SYMLINK} + sudo ln -s ${MONO_VERSIONS_PATH}/${MONO_SDK} ${MONO_VERSIONS_PATH}/${SYMLINK} + sudo ln -s ${IOS_VERSIONS_PATH}/${IOS_SDK} ${IOS_VERSIONS_PATH}/${SYMLINK} + sudo ln -s ${MAC_VERSIONS_PATH}/${MAC_SDK} ${MAC_VERSIONS_PATH}/${SYMLINK} + sudo ln -s ${ANDROID_VERSIONS_PATH}/${ANDROID_SDK} ${ANDROID_VERSIONS_PATH}/${SYMLINK} +} + +createBundleLink() { + local SOURCE=$1 + local TARGET=$2 + echo "Creating bundle symlink '$SOURCE' -> '$TARGET'" + deleteSymlink ${TARGET} + sudo ln -s ${MONO_VERSIONS_PATH}/$SOURCE ${MONO_VERSIONS_PATH}/$TARGET + sudo ln -s ${IOS_VERSIONS_PATH}/$SOURCE ${IOS_VERSIONS_PATH}/$TARGET + sudo ln -s ${MAC_VERSIONS_PATH}/$SOURCE ${MAC_VERSIONS_PATH}/$TARGET + sudo ln -s ${ANDROID_VERSIONS_PATH}/$SOURCE ${ANDROID_VERSIONS_PATH}/$TARGET +} + +# https://github.com/xamarin/xamarin-android/issues/3457 +# Recreate missing symlinks in lib for new Xamarin.Android package +# Symlink path /Library/Frameworks/Xamarin.Android.framework/Libraries +# xbuild -> xamarin.android/xbuild +# xbuild-frameworks -> xamarin.android/xbuild-frameworks +fixXamarinAndroidSymlinksInLibDir() { + local XAMARIN_ANDROID_VERSION=$1 + local XAMARIN_ANDROID_LIB_PATH="${ANDROID_VERSIONS_PATH}/${XAMARIN_ANDROID_VERSION}/lib" + + if [ -d "${XAMARIN_ANDROID_LIB_PATH}" ]; then + pushd "${XAMARIN_ANDROID_LIB_PATH}" > /dev/null + + local XAMARIN_ANDROID_XBUILD_DIR="${XAMARIN_ANDROID_LIB_PATH}/xbuild" + if [ ! -d "${XAMARIN_ANDROID_XBUILD_DIR}" ]; then + echo "${XAMARIN_ANDROID_XBUILD_DIR}" + sudo ln -sf xamarin.android/xbuild xbuild + fi + + local XAMARIN_ANDROID_XBUILD_FRAMEWORKS_DIR="${XAMARIN_ANDROID_LIB_PATH}/xbuild-frameworks" + if [ ! -d "${XAMARIN_ANDROID_XBUILD_FRAMEWORKS_DIR}" ]; then + echo "${XAMARIN_ANDROID_XBUILD_FRAMEWORKS_DIR}" + sudo ln -sf xamarin.android/xbuild-frameworks xbuild-frameworks + fi + + popd > /dev/null + fi +} + +installNunitConsole() { + local MONO_VERSION=$1 + local TMP_WRAPPER_PATH=$(mktemp) + + cat < "$TMP_WRAPPER_PATH" +#!/bin/bash -e -o pipefail +exec /Library/Frameworks/Mono.framework/Versions/${MONO_VERSION}/bin/mono --debug \$MONO_OPTIONS $NUNIT3_PATH/nunit3-console.exe "\$@" +EOF + sudo chmod +x $TMP_WRAPPER_PATH + sudo mv "$TMP_WRAPPER_PATH" "${MONO_VERSIONS_PATH}/${MONO_VERSION}/Commands/nunit3-console" +} + +downloadNUnitConsole() { + echo "Downloading NUnit 3..." + local NUNIT3_VERSION='3.6.1' + local NUNIT3_LOCATION="https://github.com/nunit/nunit-console/releases/download/${NUNIT3_VERSION}/NUnit.Console-${NUNIT3_VERSION}.zip" + local NUNIT3_PATH="/Library/Developer/nunit/${NUNIT3_VERSION}" + + NUNIT3_ARCHIVE=$(download_with_retry $NUNIT3_LOCATION) + + echo "Installing NUnit 3..." + sudo mkdir -p $NUNIT3_PATH + sudo unzip "$NUNIT3_ARCHIVE" -d $NUNIT3_PATH +} + +createUWPShim() { + echo "Creating UWP Shim to hack UWP build failure..." + cat < ${TMPMOUNT}/Microsoft.Windows.UI.Xaml.CSharp.Targets + + + + +EOF + + local UWPTARGET_PATH=/Library/Frameworks/Mono.framework/External/xbuild/Microsoft/WindowsXaml + + sudo mkdir -p $UWPTARGET_PATH/v11.0/ + sudo cp ${TMPMOUNT}/Microsoft.Windows.UI.Xaml.CSharp.Targets $UWPTARGET_PATH/v11.0/ + sudo mkdir -p $UWPTARGET_PATH/v12.0/ + sudo cp ${TMPMOUNT}/Microsoft.Windows.UI.Xaml.CSharp.Targets $UWPTARGET_PATH/v12.0/ + sudo mkdir -p $UWPTARGET_PATH/v14.0/ + sudo cp ${TMPMOUNT}/Microsoft.Windows.UI.Xaml.CSharp.Targets $UWPTARGET_PATH/v14.0/ + sudo mkdir -p $UWPTARGET_PATH/v15.0/ + sudo cp ${TMPMOUNT}/Microsoft.Windows.UI.Xaml.CSharp.Targets $UWPTARGET_PATH/v15.0/ + sudo mkdir -p $UWPTARGET_PATH/v16.0/ + sudo cp ${TMPMOUNT}/Microsoft.Windows.UI.Xaml.CSharp.Targets $UWPTARGET_PATH/v16.0/ +} + +createBackupFolders() { + mkdir -p $TMPMOUNT_FRAMEWORKS/mono + mkdir -p $TMPMOUNT_FRAMEWORKS/ios + mkdir -p $TMPMOUNT_FRAMEWORKS/mac + mkdir -p $TMPMOUNT_FRAMEWORKS/android +} + +deleteSymlink() { + sudo rm -f ${MONO_VERSIONS_PATH}/${1} + sudo rm -f ${IOS_VERSIONS_PATH}/${1} + sudo rm -f ${MAC_VERSIONS_PATH}/${1} + sudo rm -f ${ANDROID_VERSIONS_PATH}/${1} +} diff --git a/images/macos/scripts/tests/ActionArchiveCache.Tests.ps1 b/images/macos/scripts/tests/ActionArchiveCache.Tests.ps1 new file mode 100644 index 0000000..d45755d --- /dev/null +++ b/images/macos/scripts/tests/ActionArchiveCache.Tests.ps1 @@ -0,0 +1,15 @@ +Describe "ActionArchiveCache" { + Context "Action archive cache directory not empty" { + It "$HOME/actionarchivecache not empty" { + (Get-ChildItem -Path "$env:HOME/actionarchivecache/*.tar.gz" -Recurse).Count | Should -BeGreaterThan 0 + } + } + + Context "Action tarball not empty" { + $testCases = Get-ChildItem -Path "$env:HOME/actionarchivecache/*.tar.gz" -Recurse | ForEach-Object { @{ ActionTarball = $_.FullName } } + It "" -TestCases $testCases { + param ([string] $ActionTarball) + (Get-Item "$ActionTarball").Length | Should -BeGreaterThan 0 + } + } +} diff --git a/images/macos/scripts/tests/Android.Tests.ps1 b/images/macos/scripts/tests/Android.Tests.ps1 new file mode 100644 index 0000000..654a05e --- /dev/null +++ b/images/macos/scripts/tests/Android.Tests.ps1 @@ -0,0 +1,82 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking +Import-Module "$PSScriptRoot/../software-report/SoftwareReport.Android.psm1" -DisableNameChecking + +$os = Get-OSVersion + +Describe "Android" { + $androidSdkManagerPackages = Get-AndroidPackages + [int]$platformMinVersion = (Get-ToolsetContent).android.platform_min_version + [version]$buildToolsMinVersion = (Get-ToolsetContent).android.build_tools_min_version + [array]$ndkVersions = (Get-ToolsetContent).android.ndk.versions + $ndkFullVersions = $ndkVersions | ForEach-Object { Get-ChildItem "$env:ANDROID_HOME/ndk/${_}.*" -Name | Select-Object -Last 1 } | ForEach-Object { "ndk/${_}" } + # Platforms starting with a letter are the preview versions, which is not installed on the image + $platformVersionsList = ($androidSdkManagerPackages | Where-Object { "$_".StartsWith("platforms;") }) -replace 'platforms;android-', '' | Where-Object { $_ -match "^\d" } | Sort-Object -Unique + $platformsInstalled = $platformVersionsList | Where-Object { [int]($_.Split("-")[0]) -ge $platformMinVersion } | ForEach-Object { "platforms/android-${_}" } + + $buildToolsList = ($androidSdkManagerPackages | Where-Object { "$_".StartsWith("build-tools;") }) -replace 'build-tools;', '' + $buildTools = $buildToolsList | Where-Object { $_ -match "\d+(\.\d+){2,}$" } | Where-Object { [version]$_ -ge $buildToolsMinVersion } | Sort-Object -Unique | + ForEach-Object { "build-tools/${_}" } + + $androidPackages = @( + "tools", + "platform-tools", + "cmake", + $platformsInstalled, + $buildTools, + $ndkFullVersions, + ((Get-ToolsetContent).android.extras | ForEach-Object { "extras/${_}" }), + ((Get-ToolsetContent).android.addons | ForEach-Object { "add-ons/${_}" }), + ((Get-ToolsetContent).android.additional_tools) + ) | ForEach-Object { $_ } + + # Remove empty strings from array to avoid possible issues + $androidPackages = $androidPackages | Where-Object { $_ } + + BeforeAll { + $ANDROID_SDK_DIR = Join-Path $env:HOME "Library" "Android" "sdk" + + function Confirm-AndroidPackage { + param ( + [Parameter(Mandatory = $true)] + [string] $PackageName + ) + + # Convert 'm2repository;com;android;support;constraint;constraint-layout-solver;1.0.0-beta1' -> + # 'm2repository/com/android/support/constraint/constraint-layout-solver/1.0.0-beta1' + $PackageName = $PackageName.Replace(";", "/") + $targetPath = Join-Path $ANDROID_SDK_DIR $PackageName + $targetPath | Should -Exist + } + } + + Context "SDKManagers" { + $testCases = @( + @{ + PackageName = "Command-line tools" + Sdkmanager = "$env:ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" + } + ) + if ($os.IsMonterey) { + $testCases += @( + @{ + PackageName = "SDK tools" + Sdkmanager = "$env:ANDROID_HOME/tools/bin/sdkmanager" + } + ) + } + + It "Sdkmanager from is available" -TestCases $testCases { + "$Sdkmanager --version" | Should -ReturnZeroExitCode + } + } + + Context "Packages" { + $testCases = $androidPackages | ForEach-Object { @{ PackageName = $_ } } + + It "" -TestCases $testCases { + param ([string] $PackageName) + Confirm-AndroidPackage $PackageName + } + } +} diff --git a/images/macos/scripts/tests/BasicTools.Tests.ps1 b/images/macos/scripts/tests/BasicTools.Tests.ps1 new file mode 100644 index 0000000..e6cfeac --- /dev/null +++ b/images/macos/scripts/tests/BasicTools.Tests.ps1 @@ -0,0 +1,180 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "Azure CLI" { + It "Azure CLI" { + "az -v" | Should -ReturnZeroExitCode + } +} + +Describe "Azure DevOps CLI" { + It "az devops" { + "az devops -h" | Should -ReturnZeroExitCode + } +} + +Describe "Carthage" { + It "Carthage" { + "carthage version" | Should -ReturnZeroExitCode + } +} + +Describe "cmake" { + It "cmake" { + "cmake --version" | Should -ReturnZeroExitCode + } +} + +Describe "Subversion" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Subversion" { + "svn --version" | Should -ReturnZeroExitCode + } +} + +Describe "SwiftFormat" { + It "SwiftFormat" { + "swiftformat --version" | Should -ReturnZeroExitCode + } +} + +Describe "GnuPG" { + It "GnuPG" { + "gpg --version" | Should -ReturnZeroExitCode + } +} + +Describe "zstd" { + It "zstd" { + "zstd --version" | Should -ReturnZeroExitCode + } +} + +Describe "Packer" { + It "Packer" { + "packer --version" | Should -ReturnZeroExitCode + } +} + +Describe "Perl" { + It "Perl" { + "perl -e 'print substr($^V,1)'" | Should -ReturnZeroExitCode + } +} + +Describe "Tcl/Tk" { + It "libtcl" { + "file /usr/local/lib/libtcl8.6.dylib" | Should -ReturnZeroExitCode + "file /usr/local/lib/libtk8.6.dylib" | Should -ReturnZeroExitCode + } +} + +Describe "bazelisk" { + It "bazelisk" { + "bazelisk version" | Should -ReturnZeroExitCode + } +} + +Describe "Github CLI" { + It "GitHub CLI" { + "gh --version" | Should -ReturnZeroExitCode + } +} + +Describe "7-Zip" { + It "7-Zip" { + "7z i" | Should -ReturnZeroExitCode + } +} + +Describe "Apache Ant" -Skip:($os.IsMonterey) { + It "Apache Ant" { + "ant -version" | Should -ReturnZeroExitCode + } +} + +Describe "Aria2" { + It "Aria2" { + "aria2c --version" | Should -ReturnZeroExitCode + } +} + +Describe "GNU Tar" { + It "GNU Tar" { + "gtar --version" | Should -ReturnZeroExitCode + } +} + +Describe "bazel" { + It "bazel" { + "bazel --version" | Should -ReturnZeroExitCode + } +} + +Describe "Julia" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Julia" { + "julia --version" | Should -ReturnZeroExitCode + } +} + +Describe "jq" { + It "jq" { + "jq --version" | Should -ReturnZeroExitCode + } +} + +Describe "curl" { + It "curl" { + "curl --version" | Should -ReturnZeroExitCode + } +} + +Describe "wget" { + It "wget" { + "wget --version" | Should -ReturnZeroExitCode + } +} + +Describe "vagrant" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "vagrant" { + "vagrant --version" | Should -ReturnZeroExitCode + } +} + +Describe "virtualbox" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "virtualbox" { + "vboxmanage -v" | Should -ReturnZeroExitCode + } +} + +Describe "R" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "R" { + "R --version" | Should -ReturnZeroExitCode + } +} + +Describe "Homebrew" { + It "Homebrew" { + "brew --version" | Should -ReturnZeroExitCode + } +} + +Describe "Kotlin" -Skip:($os.IsMonterey) { + $kotlinPackages = @("kapt", "kotlin", "kotlinc", "kotlinc-jvm", "kotlin-dce-js") + + It " is available" -TestCases ($kotlinPackages | ForEach-Object { @{ toolName = $_ } }) { + "$toolName -version" | Should -ReturnZeroExitCode + } +} + +Describe "yq" { + It "yq" { + "yq --version" | Should -ReturnZeroExitCode + } +} + +Describe "imagemagick" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "imagemagick" { + "magick -version" | Should -ReturnZeroExitCode + } +} diff --git a/images/macos/scripts/tests/Browsers.Tests.ps1 b/images/macos/scripts/tests/Browsers.Tests.ps1 new file mode 100644 index 0000000..192339a --- /dev/null +++ b/images/macos/scripts/tests/Browsers.Tests.ps1 @@ -0,0 +1,76 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "Chrome" { + BeforeAll { + $chromeLocation = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" + $chromeForTestingLocation = "/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing" + } + + It "Chrome" { + $chromeLocation | Should -Exist + "'$chromeLocation' --version" | Should -ReturnZeroExitCode + } + + It "Chrome for Testing" { + $chromeForTestingLocation | Should -Exist + "'$chromeForTestingLocation' --version" | Should -ReturnZeroExitCode + } + + It "Chrome Driver" { + "chromedriver --version" | Should -ReturnZeroExitCode + } + + It "Chrome for Testing and Chrome Driver major versions are the same" { + $chromeMajor = (& $chromeForTestingLocation --version).Trim("Google Chrome for Testing ").Split(".")[0] + $chromeDriverMajor = (chromedriver --version).Trim("ChromeDriver ").Split(".")[0] + $chromeMajor | Should -BeExactly $chromeDriverMajor + } +} + +Describe "Selenium server" { + It "Selenium server" { + $os = Get-OSVersion + if ($os.IsArm64) { + $cellarPath = "/opt/homebrew/Cellar" + } else { + $cellarPath = "/usr/local/Cellar" + } + (Get-ChildItem -Path "$cellarPath/selenium-server*/*").Name | Should -BeLike "4.*" + } +} + +Describe "Edge" -Skip:($os.IsVenturaArm64 -or $os.IsSonomaArm64 -or $os.IsSequoiaArm64) { + It "Microsoft Edge" { + $edgeLocation = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge" + $edgeLocation | Should -Exist + "'$edgeLocation' --version" | Should -ReturnZeroExitCode + } + + It "Microsoft Edge Driver" { + "msedgedriver --version" | Should -ReturnZeroExitCode + } +} + +Describe "Firefox" -Skip:($os.IsVenturaArm64 -or $os.IsSonomaArm64 -or $os.IsSequoiaArm64) { + It "Firefox" { + $firefoxLocation = "/Applications/Firefox.app/Contents/MacOS/firefox" + $firefoxLocation | Should -Exist + "'$firefoxLocation' --version" | Should -ReturnZeroExitCode + } + It "Geckodriver" { + "geckodriver --version" | Should -ReturnZeroExitCode + } +} + +Describe "Safari" { + It "'Allow Remote Automation' option is activated" { + $plistPath = "$env:HOME/Library/WebDriver/com.apple.Safari.plist" + $command = "/usr/libexec/PlistBuddy -c 'print AllowRemoteAutomation' $plistPath" + $plistPath | Should -Exist + $commandResult = Get-CommandResult $command + $commandResult.ExitCode | Should -Be 0 + $commandResult.Output | Should -Be "true" + } +} diff --git a/images/macos/scripts/tests/Common.Tests.ps1 b/images/macos/scripts/tests/Common.Tests.ps1 new file mode 100644 index 0000000..f830aa8 --- /dev/null +++ b/images/macos/scripts/tests/Common.Tests.ps1 @@ -0,0 +1,164 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +$os = Get-OSVersion + +Describe ".NET" { + It ".NET" { + "dotnet --version" | Should -ReturnZeroExitCode + } +} + +Describe "GCC" { + $testCases = (Get-ToolsetContent).gcc.versions | ForEach-Object { @{Version = $_ } } + + It "GCC " -TestCases $testCases { + param ( + [string] $Version + ) + + "gcc-$Version --version" | Should -ReturnZeroExitCode + } + + It "Gfortran " -TestCases $testCases { + param ( + [string] $Version + ) + + "gfortran-$Version --version" | Should -ReturnZeroExitCode + } + + It "Gfortran is not found in the default path" { + "$(which gfortran)" | Should -BeNullOrEmpty + } +} + +Describe "vcpkg" -Skip:($os.IsVenturaArm64 -or $os.IsSonoma -or $os.IsSequoia) { + It "vcpkg" { + "vcpkg version" | Should -ReturnZeroExitCode + } +} + +Describe "AWS" { + It "AWS CLI" { + "aws --version" | Should -ReturnZeroExitCode + } + It "AWS SAM CLI" { + "sam --version" | Should -ReturnZeroExitCode + } + + It "AWS Session Manager CLI" { + "session-manager-plugin --version" | Should -ReturnZeroExitCode + } +} + +Describe "AzCopy" { + It "AzCopy" { + "azcopy --version" | Should -ReturnZeroExitCode + } +} + +Describe "Miniconda" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Conda" { + [System.Environment]::GetEnvironmentVariable("CONDA") | Should -Not -BeNullOrEmpty + $condaBinPath = Join-Path $env:CONDA "bin" "conda" + "$condaBinPath --version" | Should -ReturnZeroExitCode + } +} + +Describe "Stack" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Stack" { + "stack --version" | Should -ReturnZeroExitCode + } +} + +Describe "CocoaPods" { + It "CocoaPods" { + "pod --version" | Should -ReturnZeroExitCode + } +} + +Describe "VSMac" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + $vsMacVersions = (Get-ToolsetContent).xamarin.vsmac.versions + $defaultVSMacVersion = (Get-ToolsetContent).xamarin.vsmac.default + + $testCases = $vsMacVersions | ForEach-Object { + $vsPath = "/Applications/Visual Studio $_.app" + if ($_ -eq $defaultVSMacVersion) { + $vsPath = "/Applications/Visual Studio.app" + } + + @{ vsversion = $_ ; vspath = $vsPath } + } + + It "Visual Studio for Mac is installed" -TestCases $testCases { + $vstoolPath = Join-Path $vsPath "Contents/MacOS/vstool" + $vsPath | Should -Exist + $vstoolPath | Should -Exist + } + + It "Visual Studio $defaultVSMacVersion for Mac is default" { + $vsPath = "/Applications/Visual Studio.app" + $vstoolPath = Join-Path $vsPath "Contents/MacOS/vstool" + $vsPath | Should -Exist + $vstoolPath | Should -Exist + } +} + +Describe "Swig" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Swig" { + "swig -version" | Should -ReturnZeroExitCode + } +} + +Describe "Bicep" { + It "Bicep" { + "bicep --version" | Should -ReturnZeroExitCode + } +} + +Describe "Go" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Go" { + "go version" | Should -ReturnZeroExitCode + } +} + +Describe "VirtualBox" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Check kext kernel modules" { + kextstat | Out-String | Should -Match "org.virtualbox.kext" + } +} + +Describe "CodeQL Bundle" { + It "Is installed" { + $CodeQLVersionWildcard = Join-Path $Env:AGENT_TOOLSDIRECTORY -ChildPath "CodeQL" | Join-Path -ChildPath "*" + $CodeQLVersionPath = Get-ChildItem $CodeQLVersionWildcard | Select-Object -First 1 -Expand FullName + $CodeQLPath = Join-Path $CodeQLVersionPath -ChildPath "x64" | Join-Path -ChildPath "codeql" | Join-Path -ChildPath "codeql" + "$CodeQLPath version --quiet" | Should -ReturnZeroExitCode + + $CodeQLPacksPath = Join-Path $CodeQLVersionPath -ChildPath "x64" | Join-Path -ChildPath "codeql" | Join-Path -ChildPath "qlpacks" + $CodeQLPacksPath | Should -Exist + } +} + +Describe "Colima" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Colima" { + "colima version" | Should -ReturnZeroExitCode + } +} + +Describe "Compiled" -Skip:(-not $os.IsMonterey) { + It "Apache Ant" { + "ant -version" | Should -ReturnZeroExitCode + } + + $kotlinPackages = @("kapt", "kotlin", "kotlinc", "kotlinc-jvm", "kotlin-dce-js") + + It " is available" -TestCases ($kotlinPackages | ForEach-Object { @{ toolName = $_ } }) { + "$toolName -version" | Should -ReturnZeroExitCode + } + + It "sbt" { + "sbt -version" | Should -ReturnZeroExitCode + } +} diff --git a/images/macos/scripts/tests/Databases.Tests.ps1 b/images/macos/scripts/tests/Databases.Tests.ps1 new file mode 100644 index 0000000..3853913 --- /dev/null +++ b/images/macos/scripts/tests/Databases.Tests.ps1 @@ -0,0 +1,23 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "MongoDB" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "" -TestCases @( + @{ ToolName = "mongo" } + @{ ToolName = "mongod" } + ) { + $toolsetVersion = (Get-ToolsetContent).mongodb.version + (&$ToolName --version)[2].Split('"')[-2] | Should -BeLike "$toolsetVersion*" + } +} + +Describe "PostgreSQL" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "PostgreSQL version should correspond to the version in the toolset" { + $toolsetVersion = (Get-ToolsetContent).postgresql.version + # Client version + (psql --version).split()[-2] | Should -BeLike "$toolsetVersion*" + # Server version + (pg_config --version).split()[-2] | Should -BeLike "$toolsetVersion*" + } +} diff --git a/images/macos/scripts/tests/Git.Tests.ps1 b/images/macos/scripts/tests/Git.Tests.ps1 new file mode 100644 index 0000000..c66719a --- /dev/null +++ b/images/macos/scripts/tests/Git.Tests.ps1 @@ -0,0 +1,12 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "Git" { + It "git is installed" { + "git --version" | Should -ReturnZeroExitCode + } + It "git lfs is installed" { + "git lfs version" | Should -ReturnZeroExitCode + } +} diff --git a/images/macos/scripts/tests/Haskell.Tests.ps1 b/images/macos/scripts/tests/Haskell.Tests.ps1 new file mode 100644 index 0000000..c4ff729 --- /dev/null +++ b/images/macos/scripts/tests/Haskell.Tests.ps1 @@ -0,0 +1,30 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "Haskell" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + Context "GHCup" { + It "GHCup" { + "ghcup --version" | Should -ReturnZeroExitCode + } + } + Context "GHC" { + It "GHC" { + "ghc --version" | Should -ReturnZeroExitCode + } + } + Context "Cabal" { + It "Cabal" { + "cabal --version" | Should -ReturnZeroExitCode + } + } + Context "Stack" { + It "Stack" { + "stack --version" | Should -ReturnZeroExitCode + } + + It "Stack hook is not installed" { + "$HOME/.stack/hooks/ghc-install.sh" | Should -Not -Exist + } + } +} diff --git a/images/macos/scripts/tests/Helpers.psm1 b/images/macos/scripts/tests/Helpers.psm1 new file mode 100644 index 0000000..56d689c --- /dev/null +++ b/images/macos/scripts/tests/Helpers.psm1 @@ -0,0 +1,146 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +function Confirm-ArrayWithoutDuplicates { + param ( + [AllowEmptyCollection()] + [Parameter(Mandatory = $true)] + [array] $Items, + [string] $Because + ) + $uniqueList = @() + $Items | ForEach-Object { + $uniqueList | Should -Not -Contain $_ -Because $Because + $uniqueList += $_ + } +} + +function Confirm-UrlAvailability { + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Url + ) + + $result = $true + try { + $requestResult = Invoke-WebRequest $Url -DisableKeepAlive -UseBasicParsing -Method Head + $result = ($requestResult.StatusCode -eq 200) + } catch { + $result = $false + } + + $result | Should -BeTrue -Because "'$Url' should be available, but it is not" +} + +function Confirm-IdenticalFileContent { + param ( + [Parameter(Mandatory)] + [string] $File1, + [Parameter(Mandatory)] + [string] $File2 + ) + + $File1 | Should -Exist -Because "The content of '$File1' should be identical with content of '$File2' but '$File1' doesn't exist" + $File2 | Should -Exist -Because "The content of '$File1' should be identical with content of '$File2' but '$File2' doesn't exist" + + $content1 = Get-Content -Raw $File1 + $content2 = Get-Content -Raw $File2 + $content1 | Should -Be $content2 -Because "The content of '$File1' should be identical with content of '$File2' but they are different" +} + +function ShouldReturnZeroExitCode { + Param ( + [String] $ActualValue, + [switch] $Negate, + [string] $Because # This parameter is unused by we need it to match Pester asserts signature + ) + + $result = Get-CommandResult $ActualValue + + [bool]$succeeded = $result.ExitCode -eq 0 + if ($Negate) { $succeeded = -not $succeeded } + + if (-not $succeeded) { + $commandOutputIndent = " " * 4 + $commandOutput = ($result.Output | ForEach-Object { "${commandOutputIndent}${_}" }) -join "`n" + $failureMessage = "Command '${ActualValue}' has finished with exit code ${actualExitCode}`n${commandOutput}" + } + + return [PSCustomObject] @{ + Succeeded = $succeeded + FailureMessage = $failureMessage + } +} + +function ShouldMatchCommandOutput { + Param ( + [String] $ActualValue, + [String] $RegularExpression, + [switch] $Negate + ) + + $output = (Get-CommandResult $ActualValue).Output | Out-String + [bool] $succeeded = $output -cmatch $RegularExpression + + if ($Negate) { + $succeeded = -not $succeeded + } + + $failureMessage = '' + + if (-not $succeeded) { + if ($Negate) { + $failureMessage = "Expected regular expression '$RegularExpression' for '$ActualValue' command to not match '$output', but it did match." + } + else { + $failureMessage = "Expected regular expression '$RegularExpression' for '$ActualValue' command to match '$output', but it did not match." + } + } + + return [PSCustomObject] @{ + Succeeded = $succeeded + FailureMessage = $failureMessage + } +} + +If (Get-Command -Name Add-ShouldOperator -ErrorAction SilentlyContinue) { + Add-ShouldOperator -Name ReturnZeroExitCode -InternalName ShouldReturnZeroExitCode -Test ${function:ShouldReturnZeroExitCode} + Add-ShouldOperator -Name MatchCommandOutput -InternalName ShouldMatchCommandOutput -Test ${function:ShouldMatchCommandOutput} +} + +function Invoke-PesterTests { + Param ( + [Parameter(Mandatory)][string] $TestFile, + [string] $TestName + ) + + $testPath = "$HOME/image-generation/tests/${TestFile}.Tests.ps1" + if (-not (Test-Path $testPath)) { + throw "Unable to find test file '$TestFile' on '$testPath'." + } + + # Check that Pester module is imported + if (!(Get-Module "Pester")) { + Import-Module Pester + } + + $configuration = [PesterConfiguration] @{ + Run = @{ Path = $testPath; PassThru = $true } + Output = @{ Verbosity = "Detailed"; RenderMode = "Plaintext" } + } + if ($TestName) { + $configuration.Filter.FullName = $TestName + } + + # Switch ErrorActionPreference to make sure that tests will fail on silent errors too + $backupErrorActionPreference = $ErrorActionPreference + $ErrorActionPreference = "Stop" + $results = Invoke-Pester -Configuration $configuration + $ErrorActionPreference = $backupErrorActionPreference + + # Fail in case if no tests are run + if (-not ($results -and ($results.FailedCount -eq 0) -and ($results.PassedCount -gt 0))) { + $results + throw "Test run has failed" + } +} diff --git a/images/macos/scripts/tests/Java.Tests.ps1 b/images/macos/scripts/tests/Java.Tests.ps1 new file mode 100644 index 0000000..148b046 --- /dev/null +++ b/images/macos/scripts/tests/Java.Tests.ps1 @@ -0,0 +1,83 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +$os = Get-OSVersion +$arch = Get-Architecture + +function Get-NativeVersionFormat { + param($Version) + if ($Version -in "8") { + return "1.${Version}" + } + return $Version +} + +Describe "Java" { + BeforeAll { + function Confirm-JavaVersion { + param($JavaCommand, $ExpectedVersion) + + $commandResult = Get-CommandResult $JavaCommand + $commandResult.ExitCode | Should -Be 0 + $matchResult = $commandResult.Output | Select-String '^openjdk version \"([\d\._]+)\"' + $matchResult.Matches.Success | Should -BeTrue + $version = $matchResult.Matches.Groups[1].Value + $version | Should -BeLike "${ExpectedVersion}*" + } + } + + $toolsetJava = (Get-ToolsetContent).java + $defaultVersion = $toolsetJava.$arch.default + $jdkVersions = $toolsetJava.$arch.versions + + if ($os.IsArm64) { + $testCases = $jdkVersions | ForEach-Object { @{ Title = $_; Version = (Get-NativeVersionFormat $_); EnvVariable = "JAVA_HOME_${_}_arm64" } } + } else { + $testCases = $jdkVersions | ForEach-Object { @{ Title = $_; Version = (Get-NativeVersionFormat $_); EnvVariable = "JAVA_HOME_${_}_X64" } } + } + $testCases += @{ Title = "Default"; Version = (Get-NativeVersionFormat $defaultVersion); EnvVariable = "JAVA_HOME" } + + $testCases | ForEach-Object { + Context $_.Title { + It "Version is found by 'java_home'" -TestCases $_ { + "/usr/libexec/java_home -v${Version}" | Should -ReturnZeroExitCode + } + + It "Java " -TestCases $_ { + $envVariablePath = [System.Environment]::GetEnvironmentVariable($EnvVariable) + $javaBinPath = Join-Path $envVariablePath "/bin/java" + Confirm-JavaVersion -JavaCommand "$javaBinPath -version" -ExpectedVersion $Version + } + + if ($_.Title -eq "Default") { + It "Version is default" -TestCases $_ { + Confirm-JavaVersion -JavaCommand "java -version" -ExpectedVersion $Version + } + } + } + } + + Context "Maven" { + Describe "Maven" { + It "Maven" { + "mvn --version" | Should -ReturnZeroExitCode + } + } + } + + Context "Gradle" { + Describe "Gradle" { + It "Gradle is installed" { + "gradle --version" | Should -ReturnZeroExitCode + } + + It "Gradle is installed to /usr/local/bin" -Skip:($os.IsArm64) { + (Get-Command "gradle").Path | Should -BeExactly "/usr/local/bin/gradle" + } + + It "Gradle is installed to /opt/homebrew/bin/gradle" -Skip:(-not $os.IsArm64) { + (Get-Command "gradle").Path | Should -BeExactly "/opt/homebrew/bin/gradle" + } + } + } +} diff --git a/images/macos/scripts/tests/LLVM.Tests.ps1 b/images/macos/scripts/tests/LLVM.Tests.ps1 new file mode 100644 index 0000000..9996b86 --- /dev/null +++ b/images/macos/scripts/tests/LLVM.Tests.ps1 @@ -0,0 +1,12 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +Describe "Clang/LLVM" { + BeforeAll { + $toolsetVersion = (Get-ToolsetContent).llvm.version + } + + It "Clang/LLVM is installed and version is correct" { + $clangVersion = & "$(brew --prefix llvm@$toolsetVersion)/bin/clang" --version + $clangVersion[0] | Should -BeLike "*${toolsetVersion}*" + } +} diff --git a/images/macos/scripts/tests/Linters.Tests.ps1 b/images/macos/scripts/tests/Linters.Tests.ps1 new file mode 100644 index 0000000..93ac37e --- /dev/null +++ b/images/macos/scripts/tests/Linters.Tests.ps1 @@ -0,0 +1,9 @@ +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +$os = Get-OSVersion + +Describe "SwiftLint" -Skip:($os.IsVenturaArm64 -or $os.IsSonomaArm64 -or $os.IsSequoiaArm64) { + It "SwiftLint" { + "swiftlint version" | Should -ReturnZeroExitCode + } +} diff --git a/images/macos/scripts/tests/Node.Tests.ps1 b/images/macos/scripts/tests/Node.Tests.ps1 new file mode 100644 index 0000000..a48008e --- /dev/null +++ b/images/macos/scripts/tests/Node.Tests.ps1 @@ -0,0 +1,56 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +$os = Get-OSVersion + +Describe "Node.js" { + It "Node.js is installed" { + "node --version" | Should -ReturnZeroExitCode + } + + It "Node.js version should correspond to the version in the toolset" { + node --version | Should -BeLike "v$((Get-ToolsetContent).node.default)*" + } + + It "NPM is installed" { + "npm --version" | Should -ReturnZeroExitCode + } + + It "Yarn is installed" { + "yarn --version" | Should -ReturnZeroExitCode + } +} + +Describe "nvm" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + BeforeAll { + $nvmPath = Join-Path $env:HOME ".nvm" "nvm.sh" + $nvmInitCommand = ". $nvmPath > /dev/null 2>&1 || true" + } + + It "nvm is installed" { + $nvmPath | Should -Exist + "$nvmInitCommand && nvm --version" | Should -ReturnZeroExitCode + } + + Context "nvm versions" { + [array]$nvmVersions = (Get-ToolsetContent).node.nvm_versions + $testCases = $nvmVersions | ForEach-Object { @{NvmVersion = $_} } + + It "" -TestCases $testCases { + param ( + [string] $NvmVersion + ) + + "$nvmInitCommand && nvm ls $($NvmVersion)" | Should -ReturnZeroExitCode + } + } +} + +Describe "Global NPM Packages" { + $globalNpmPackages = (Get-ToolsetContent).npm.global_packages + $globalNpmPackagesWithTests = $globalNpmPackages | Where-Object { $_.test } | ForEach-Object { @{ Name = $_.name; Test = $_.test } } + + It "" -TestCases $globalNpmPackagesWithTests { + $Test | Should -ReturnZeroExitCode + } +} diff --git a/images/macos/scripts/tests/OpenSSL.Tests.ps1 b/images/macos/scripts/tests/OpenSSL.Tests.ps1 new file mode 100644 index 0000000..1fb8886 --- /dev/null +++ b/images/macos/scripts/tests/OpenSSL.Tests.ps1 @@ -0,0 +1,23 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +Describe "OpenSSL" { + Context "OpenSSL Version" { + It "OpenSSL is available" { + "openssl version" | Should -ReturnZeroExitCode + } + } + + Context "OpenSSL 1.1 Path Check" { + It "OpenSSL 1.1 path exists" { + $openSSLpath = brew --prefix openssl@1.1 + $openSSLpath | Should -Exist + } + } + + Context "OpenSSL 1.1 is default" { + It "Default OpenSSL version is 1.1" { + $commandResult = Get-CommandResult "openssl version" + $commandResult.Output | Should -Match "OpenSSL 1.1" + } + } +} diff --git a/images/macos/scripts/tests/PHP.Tests.ps1 b/images/macos/scripts/tests/PHP.Tests.ps1 new file mode 100644 index 0000000..2a4ff87 --- /dev/null +++ b/images/macos/scripts/tests/PHP.Tests.ps1 @@ -0,0 +1,22 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "PHP" { + Context "PHP" -Skip:($os.IsVenturaArm64 -or $os.IsSonomaArm64 -or $os.IsSequoiaArm64) { + It "PHP Path" { + Get-ToolPath "php" | Should -Not -BeLike "/usr/bin/php*" + } + It "PHP version" { + $phpVersionToolset = (Get-ToolsetContent).php.version + $phpInstalledVersion = php --version | Out-String | Select-String "${phpVersionToolset}" + $phpInstalledVersion | Should -BeLike "PHP ${phpVersionToolset}*" + } + } + + Context "Composer" -Skip:($os.IsVenturaArm64 -or $os.IsSonomaArm64 -or $os.IsSequoiaArm64) { + It "Composer" { + "composer --version" | Should -ReturnZeroExitCode + } + } +} diff --git a/images/macos/scripts/tests/PipxPackages.Tests.ps1 b/images/macos/scripts/tests/PipxPackages.Tests.ps1 new file mode 100644 index 0000000..8a7cb0d --- /dev/null +++ b/images/macos/scripts/tests/PipxPackages.Tests.ps1 @@ -0,0 +1,12 @@ + +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "PipxPackages" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + $pipxToolset = (Get-ToolsetContent).pipx + $testCases = $pipxToolset | ForEach-Object { @{package = $_.package; cmd = $_.cmd} } + It "" -TestCases $testCases { + "$cmd" | Should -ReturnZeroExitCode + } +} diff --git a/images/macos/scripts/tests/Powershell.Tests.ps1 b/images/macos/scripts/tests/Powershell.Tests.ps1 new file mode 100644 index 0000000..2892c24 --- /dev/null +++ b/images/macos/scripts/tests/Powershell.Tests.ps1 @@ -0,0 +1,43 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +Describe "Powershell" { + Context "Powershell is installed" { + It "Powershell is installed" { + "pwsh -v" | Should -ReturnZeroExitCode + } + } + + Context "Powershell Modules" { + $modules = (Get-ToolsetContent).powershellModules + $withoutVersionsModules = $modules | Where-Object {-not $_.versions} | ForEach-Object { + @{moduleName = $_.name} + } + + $withVersionsModules = $modules | Where-Object {$_.versions} | ForEach-Object { + $moduleName = $_.name + $_.versions | ForEach-Object { + @{moduleName = $moduleName; expectedVersion = $_} + } + } + + It " is installed" -TestCases $withoutVersionsModules { + param ( + [string] $moduleName + ) + + Get-Module -Name $moduleName -ListAvailable | Should -BeTrue + } + + if ($withVersionsModules) { + It " with is installed" -TestCases $withVersionsModules { + param ( + [string] $moduleName, + [string] $expectedVersion + ) + + (Get-Module -Name $moduleName -ListAvailable).Version -contains $expectedVersion | Should -BeTrue + } + } + } +} diff --git a/images/macos/scripts/tests/Python.Tests.ps1 b/images/macos/scripts/tests/Python.Tests.ps1 new file mode 100644 index 0000000..079f76b --- /dev/null +++ b/images/macos/scripts/tests/Python.Tests.ps1 @@ -0,0 +1,50 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +$os = Get-OSVersion + +Describe "Python3" { + It "Python 3 is available" { + "python3 --version" | Should -ReturnZeroExitCode + } + + if ($os.IsVenturaArm64 -or $os.IsSonomaArm64 -or $os.IsSequoiaArm64) { + It "Python 3 is installed under /opt/homebrew/bin/" { + Get-ToolPath "python3" | Should -BeLike "/opt/homebrew/bin/*" + } + } else { + It "Python 3 is installed under /usr/local/bin" { + Get-ToolPath "python3" | Should -BeLike "/usr/local/bin*" + } + } + + It "Pip 3 is available" { + "pip3 --version" | Should -ReturnZeroExitCode + } + + It "Pipx is available" { + "pipx --version" | Should -ReturnZeroExitCode + } + + It "Pip 3 and Python 3 came from the same brew formula" { + $pip3Path = Split-Path (readlink (which pip3)) + $python3Path = Split-Path (readlink (which python3)) + $pip3Path | Should -BeExactly $python3Path + } + +} + +Describe "Python2" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Python 2 is available" { + "/Library/Frameworks/Python.framework/Versions/2.7/bin/python --version" | Should -ReturnZeroExitCode + } + + It "Pip 2 is available" { + "/Library/Frameworks/Python.framework/Versions/2.7/bin/pip --version" | Should -ReturnZeroExitCode + } + + It "2to3 symlink does not point to Python 2" { + $2to3path = (Get-ChildItem (Get-Command 2to3).Path).Target + $2to3path | Should -Not -BeLike '/Frameworks/Python.framework/Versions/2.*' + } +} diff --git a/images/macos/scripts/tests/Rosetta.Tests.ps1 b/images/macos/scripts/tests/Rosetta.Tests.ps1 new file mode 100644 index 0000000..e65f22e --- /dev/null +++ b/images/macos/scripts/tests/Rosetta.Tests.ps1 @@ -0,0 +1,11 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "Rosetta" -Skip:(-not $os.IsArm64) { + It "Rosetta is available" { + $commandResult = Get-CommandResult "/usr/bin/pgrep oahd" + $commandResult.Output | Should -Match "\d+" + $commandResult.ExitCode | Should -Be 0 + } +} diff --git a/images/macos/scripts/tests/Ruby.arm64.Tests.ps1 b/images/macos/scripts/tests/Ruby.arm64.Tests.ps1 new file mode 100644 index 0000000..45cc879 --- /dev/null +++ b/images/macos/scripts/tests/Ruby.arm64.Tests.ps1 @@ -0,0 +1,26 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +$os = Get-OSVersion + +Describe "Ruby" -Skip:(-not $os.IsArm64) { + It "Ruby is available" { + "ruby --version" | Should -ReturnZeroExitCode + } + + It "Ruby is installed via HomeBrew" { + Get-ToolPath "ruby" | Should -Not -BeLike "/usr/bin/ruby*" + } + + It "Ruby tools are consistent" { + $expectedPrefix = "/opt/homebrew" + Get-ToolPath "ruby" | Should -Match "$($expectedPrefix)*" + Get-ToolPath "gem" | Should -Match "$($expectedPrefix)*" + Get-ToolPath "bundler" | Should -Match "$($expectedPrefix)*" + } + + It "Ruby gems permissions are valid" { + "gem install bundle" | Should -ReturnZeroExitCode + "gem uninstall bundle" | Should -ReturnZeroExitCode + } +} diff --git a/images/macos/scripts/tests/Ruby.x64.Tests.ps1 b/images/macos/scripts/tests/Ruby.x64.Tests.ps1 new file mode 100644 index 0000000..b458278 --- /dev/null +++ b/images/macos/scripts/tests/Ruby.x64.Tests.ps1 @@ -0,0 +1,26 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +$os = Get-OSVersion + +Describe "Ruby" -Skip:($os.IsArm64) { + It "Ruby is available" { + "ruby --version" | Should -ReturnZeroExitCode + } + + It "Ruby is installed via HomeBrew" { + Get-ToolPath "ruby" | Should -Not -BeLike "/usr/bin/ruby*" + } + + It "Ruby tools are consistent" { + $expectedPrefix = "/usr/local" + Get-ToolPath "ruby" | Should -Match "$($expectedPrefix)*" + Get-ToolPath "gem" | Should -Match "$($expectedPrefix)*" + Get-ToolPath "bundler" | Should -Match "$($expectedPrefix)*" + } + + It "Ruby gems permissions are valid" { + "gem install bundle" | Should -ReturnZeroExitCode + "gem uninstall bundle" | Should -ReturnZeroExitCode + } +} diff --git a/images/macos/scripts/tests/RubyGem.Tests.ps1 b/images/macos/scripts/tests/RubyGem.Tests.ps1 new file mode 100644 index 0000000..13a3a6f --- /dev/null +++ b/images/macos/scripts/tests/RubyGem.Tests.ps1 @@ -0,0 +1,39 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "RubyGems" { + $gemTestCases = (Get-ToolsetContent).ruby.rubygems | ForEach-Object { + @{gemName = $_} + } + + if ($gemTestCases) { + It "Gem is installed" -TestCases $gemTestCases { + "gem list -i '^$gemName$'" | Should -MatchCommandOutput "true" + } + } +} + +Describe "Bundler" { + It "Bundler" { + "bundler --version" | Should -ReturnZeroExitCode + } +} + +Describe "Fastlane" { + It "Fastlane" { + "fastlane --version" | Should -ReturnZeroExitCode + } +} + +Describe "xcpretty" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "xcpretty" { + "xcpretty --version" | Should -ReturnZeroExitCode + } +} + +Describe "jazzy" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "jazzy" { + "jazzy --version" | Should -ReturnZeroExitCode + } +} diff --git a/images/macos/scripts/tests/RunAll-Tests.ps1 b/images/macos/scripts/tests/RunAll-Tests.ps1 new file mode 100644 index 0000000..7ac5460 --- /dev/null +++ b/images/macos/scripts/tests/RunAll-Tests.ps1 @@ -0,0 +1,3 @@ +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +Invoke-PesterTests "*" diff --git a/images/macos/scripts/tests/Rust.Tests.ps1 b/images/macos/scripts/tests/Rust.Tests.ps1 new file mode 100644 index 0000000..34eab12 --- /dev/null +++ b/images/macos/scripts/tests/Rust.Tests.ps1 @@ -0,0 +1,38 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "Rust" { + Context "Rust" { + It "Rustup is installed" { + "rustup --version" | Should -ReturnZeroExitCode + } + + It "Rustc is installed" { + "rustc --version" | Should -ReturnZeroExitCode + } + } + + Context "Cargo" { + It "Cargo is installed" { + "cargo --version" | Should -ReturnZeroExitCode + } + } + Context "Cargo dependencies" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "bindgen" { + "bindgen --version" | Should -ReturnZeroExitCode + } + + It "cbindgen" { + "cbindgen --version" | Should -ReturnZeroExitCode + } + + It "Cargo audit" { + "cargo audit --version" | Should -ReturnZeroExitCode + } + + It "Cargo outdated" { + "cargo outdated --version" | Should -ReturnZeroExitCode + } + } +} diff --git a/images/macos/scripts/tests/System.Tests.ps1 b/images/macos/scripts/tests/System.Tests.ps1 new file mode 100644 index 0000000..206ac3b --- /dev/null +++ b/images/macos/scripts/tests/System.Tests.ps1 @@ -0,0 +1,43 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "Disk free space" { + It "Image has more than 14GB free space" { + # we should have at least 14 GB of free space on macOS images + # 10GB here: https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops#capabilities-and-limitations + # 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 + $freeSpace = (Get-PSDrive "/").Free + $freeSpace | Should -BeGreaterOrEqual 14GB + } +} + +Describe "Certificate" { + It "Apple Worldwide Developer Relations Certification Authority[expired: 2030-02] is installed" { + $sha1Hash = "06EC06599F4ED0027CC58956B4D3AC1255114F35" + $certs = security find-certificate -a -c Worldwide -p -Z | Out-String + $certs | Should -Match $sha1Hash + } + + It "Developer ID Certification Authority[expired: 2031-09] is installed" { + $sha1Hash = "5B45F61068B29FCC8FFFF1A7E99B78DA9E9C4635" + $certs = security find-certificate -a -c "Developer ID" -p -Z | Out-String + $certs | Should -Match $sha1Hash + } +} + +Describe "Audio device" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Sox is installed" { + "sox --version" | Should -ReturnZeroExitCode + } + + It "SwitchAudioSource is installed" { + "SwitchAudioSource -c" | Should -ReturnZeroExitCode + } +} + +Describe "AutomationModeTool" { + It "Does not require user authentication" { + automationmodetool | Out-String | Should -Match "DOES NOT REQUIRE" + } +} diff --git a/images/macos/scripts/tests/Toolcache.Tests.ps1 b/images/macos/scripts/tests/Toolcache.Tests.ps1 new file mode 100644 index 0000000..6a07313 --- /dev/null +++ b/images/macos/scripts/tests/Toolcache.Tests.ps1 @@ -0,0 +1,232 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +$arch = Get-Architecture +$os = Get-OSVersion + +Describe "Toolcache" { + $toolcacheDirectory = Join-Path $env:HOME "hostedtoolcache" + [array]$packages += (Get-ToolsetContent).toolcache | ForEach-Object { + return [PSCustomObject] @{ + ToolName = ($_.name).ToLower() + Arch = $arch + Versions = $_.arch.$arch | Where-Object{ $_ } | ForEach-Object { $_.versions.Replace(".*", "") } + } + } + + Context "Python" { + $pythonDirectory = Join-Path $toolcacheDirectory "Python" + $pythonPackage = $packages | Where-Object { $_.ToolName -eq "python" } | Select-Object -First 1 + $testCase = @{ PythonDirectory = $pythonDirectory } + + It "Toolcache directory exists" -TestCases $testCase { + param ( [string] $PythonDirectory ) + + $PythonDirectory | Should -Exist + } + + It "Toolcache directory contains at least one version of Python" -TestCases $testCase { + param ( [string] $PythonDirectory ) + + (Get-ChildItem -Path $PythonDirectory -Directory).Count | Should -BeGreaterThan 0 + } + + $pythonPackage.Versions | Where-Object { $_ } | ForEach-Object { + Context "$_" { + $versionDirectory = Get-ChildItem -Path $pythonDirectory -Directory -Filter "$_*" | Select-Object -First 1 + $pythonBinPath = Join-Path $versionDirectory.FullName $pythonPackage.Arch "python" + $testCase = @{ PythonVersion = $_; PythonBinPath = $pythonBinPath } + + It "Version" -TestCases $testCase { + param ( + [string] $PythonVersion, + [string] $PythonBinPath + ) + + $result = Get-CommandResult "$PythonBinPath --version" + $result.Output | Should -BeLike "*$PythonVersion*" + $result.ExitCode | Should -Be 0 + } + + It "Run test script" -TestCases $testCase { + param ( [string] $PythonBinPath ) + + "$PythonBinPath -c 'import sys;print(sys.version)'" | Should -ReturnZeroExitCode + } + } + } + } + + Context "Ruby" -Skip:($os.IsVenturaArm64 -or $os.IsSonomaArm64 -or $os.IsSequoiaArm64) { + $rubyDirectory = Join-Path $toolcacheDirectory "Ruby" + $rubyPackage = $packages | Where-Object { $_.ToolName -eq "Ruby" } | Select-Object -First 1 + $testCase = @{ RubyDirectory = $rubyDirectory } + + It "Toolcache directory exists" -TestCases $testCase { + param ( [string] $RubyDirectory ) + + $RubyDirectory | Should -Exist + } + + It "Toolcache directory contains at least one version of Ruby" -TestCases $testCase { + param ( [string] $RubyDirectory ) + + (Get-ChildItem -Path $RubyDirectory -Directory).Count | Should -BeGreaterThan 0 + } + + $rubyPackage.Versions | Where-Object { $_ } | ForEach-Object { + Context "$_" { + $versionDirectory = Get-ChildItem -Path $rubyDirectory -Directory -Filter "$_*" | Select-Object -First 1 + $rubyBinPath = Join-Path $versionDirectory.FullName $rubyPackage.Arch "bin" "ruby" + $testCase = @{ RubyVersion = $_; RubyBinPath = $rubyBinPath } + + It "Version" -TestCases $testCase { + param ( + [string] $RubyVersion, + [string] $RubyBinPath + ) + + $result = Get-CommandResult "$RubyBinPath --version" + $result.Output | Should -BeLike "*$RubyVersion*" + $result.ExitCode | Should -Be 0 + } + + It "Run test script" -TestCases $testCase { + param ( [string] $RubyBinPath ) + + "$RubyBinPath -e 'puts RUBY_VERSION'" | Should -ReturnZeroExitCode + } + } + } + } + Context "PyPy" -Skip:($os.IsVenturaArm64 -or $os.IsSonoma -or $os.IsSequoia) { + $pypyDirectory = Join-Path $toolcacheDirectory "PyPy" + $pypyPackage = $packages | Where-Object { $_.ToolName -eq "pypy" } | Select-Object -First 1 + $testCase = @{ PypyDirectory = $pypyDirectory } + + It "Toolcache directory exists" -TestCases $testCase { + param ( [string] $PypyDirectory ) + + $PypyDirectory | Should -Exist + } + + It "Toolcache directory contains at least one version of PyPy" -TestCases $testCase { + param ( [string] $PypyDirectory ) + + (Get-ChildItem -Path $PypyDirectory -Directory).Count | Should -BeGreaterThan 0 + } + + $pypyPackage.Versions | Where-Object { $_ } | ForEach-Object { + Context "$_" { + $versionDirectory = Get-ChildItem -Path $pypyDirectory -Directory -Filter "$_*" | Select-Object -First 1 + $binFilename = If ($_.StartsWith("3")) { "pypy3" } else { "pypy" } + $pypyBinPath = Join-Path $versionDirectory.FullName $pypyPackage.Arch "bin" $binFilename + $testCase = @{ PypyVersion = $_; PypyBinPath = $pypyBinPath } + + It "Version" -TestCases $testCase { + param ( + [string] $PypyVersion, + [string] $PypyBinPath + ) + + $result = Get-CommandResult "$PypyBinPath --version" + $result.Output | Should -BeLike "*$PypyVersion*" + $result.ExitCode | Should -Be 0 + } + + It "Run test script" -TestCases $testCase { + param ( [string] $PypyBinPath ) + + "$PypyBinPath -c 'import sys;print(sys.version)'" | Should -ReturnZeroExitCode + } + } + } + } + + Context "Node" { + $nodeDirectory = Join-Path $toolcacheDirectory "node" + $nodePackage = $packages | Where-Object { $_.ToolName -eq "node" } | Select-Object -First 1 + $testCase = @{ NodeDirectory = $nodeDirectory } + + It "Toolcache directory exists" -TestCases $testCase { + param ( [string] $NodeDirectory ) + + $NodeDirectory | Should -Exist + } + + It "Toolcache directory contains at least one version of Node" -TestCases $testCase { + param ( [string] $NodeDirectory ) + + (Get-ChildItem -Path $NodeDirectory -Directory).Count | Should -BeGreaterThan 0 + } + + $nodePackage.Versions | Where-Object { $_ } | ForEach-Object { + Context "$_" { + $versionDirectory = Get-ChildItem -Path $nodeDirectory -Directory -Filter "$_*" | Select-Object -First 1 + $nodeBinPath = Join-Path $versionDirectory.FullName $nodePackage.Arch "bin" "node" + $npmBinPath = Join-Path $versionDirectory.FullName $nodePackage.Arch "bin" "npm" + $testCase = @{ NodeVersion = $_; NodeBinPath = $nodeBinPath; NpmBinPath = $npmBinPath } + + It "Version Node" -TestCases $testCase { + param ( + [string] $NodeVersion, + [string] $NodeBinPath + ) + + $result = Get-CommandResult "$NodeBinPath --version" + $result.Output | Should -BeLike "*$NodeVersion*" + $result.ExitCode | Should -Be 0 + } + + It "Version Npm" -TestCases $testCase { + param ( [string] $NpmBinPath ) + + "$NpmBinPath --version" | Should -ReturnZeroExitCode + } + + It "Run test script" -TestCases $testCase { + param ( [string] $NodeBinPath ) + + "$NodeBinPath -e 'console.log(process.version)'" | Should -ReturnZeroExitCode + } + } + } + } + + Context "Go" { + $goDirectory = Join-Path $toolcacheDirectory "go" + $goPackage = $packages | Where-Object { $_.ToolName -eq "go" } | Select-Object -First 1 + $testCase = @{ GoDirectory = $goDirectory } + + It "Toolcache directory exists" -TestCases $testCase { + param ( [string] $GoDirectory ) + + $GoDirectory | Should -Exist + } + + It "Toolcache directory contains at least one version of Go" -TestCases $testCase { + param ( [string] $GoDirectory ) + + (Get-ChildItem -Path $GoDirectory -Directory).Count | Should -BeGreaterThan 0 + } + + $goPackage.Versions | Where-Object { $_ } | ForEach-Object { + Context "$_" { + $versionDirectory = Get-ChildItem -Path $goDirectory -Directory -Filter "$_*" | Select-Object -First 1 + $goBinPath = Join-Path $versionDirectory.FullName $goPackage.Arch "bin" "go" + $testCase = @{ GoVersion = $_; GoBinPath = $goBinPath } + + It "Version Go" -TestCases $testCase { + param ( + [string] $GoVersion, + [string] $GoBinPath + ) + + $result = Get-CommandResult "$GoBinPath version" + $result.Output | Should -BeLike "*$GoVersion*" + $result.ExitCode | Should -Be 0 + } + } + } + } +} diff --git a/images/macos/scripts/tests/Toolset.Tests.ps1 b/images/macos/scripts/tests/Toolset.Tests.ps1 new file mode 100644 index 0000000..f2aa3cd --- /dev/null +++ b/images/macos/scripts/tests/Toolset.Tests.ps1 @@ -0,0 +1,141 @@ +Import-Module "$PSScriptRoot/Helpers.psm1" + +$toolsets = Get-ChildItem -Path $PSScriptRoot -Filter "toolset-*.json" + +function Get-ShortVersion([System.Version] $Version) { + return [System.Version]::Parse($Version).ToString(2) +} + +function Invoke-BashUtilsFunction([string] $FunctionName, [string]$parameter) { + $xamarinUtilsPath = "$PSScriptRoot/../provision/utils/xamarin-utils.sh" + return Invoke-Expression "bash -c `"source $xamarinUtilsPath && $FunctionName $parameter`"" +} + +Describe "Toolset JSON validation" { + $toolsets | ForEach-Object { + It "$($_.Name) is valid" { + $jsonContent = Get-Content -Raw $_.Fullname + $jsonContent | Test-Json | Should -BeTrue + } + } +} + +$toolsets | ForEach-Object { + Describe "$($_.Name)" { + $toolset = Get-Content -Raw $_.Fullname | ConvertFrom-Json + + Context "Xcode" { + It "Default Xcode should be defined" { + $toolset.xcode.default | Should -BeTrue + } + + It "Default Xcode is listed in Xcode list" { + $toolset.xcode.versions | Should -Contain $toolset.xcode.default + } + } + + Context "VSMac" { + $vsmacVersion = $toolset.xamarin.vsmac + + It "Version '$vsmacVersion' is available and can be downloaded" { + $vsmacUrl = Invoke-BashUtilsFunction("buildVSMacDownloadUrl", $vsmacVersion) + Confirm-UrlAvailability $vsmacUrl + } + } + + Context "Mono" { + $sdkVersions = $toolset.xamarin.mono_versions + + $sdkVersions | ForEach-Object { + It "Version '$_' is available and can be downloaded" { + $sdkUrl = Invoke-BashUtilsFunction("buildMonoDownloadUrl", $_) + Confirm-UrlAvailability $sdkUrl + } + } + + It "Version list doesn't contain versions with the same major/minor version" { + $versions = $sdkVersions | ForEach-Object { Get-ShortVersion $_ } + Confirm-ArrayWithoutDuplicates $versions -Because "It doesn't allow to install more than one version with the same major/minor" + } + } + + Context "Xamarin.iOS" { + $sdkVersions = $toolset.xamarin.ios_versions + + $sdkVersions | ForEach-Object { + It "Version '$_' is available and can be downloaded" { + $sdkUrl = Invoke-BashUtilsFunction("buildXamariniIOSDownloadUrl", $_) + Confirm-UrlAvailability $sdkUrl + } + } + + It "Version list doesn't contain versions with the same major/minor version" { + $versions = $sdkVersions | ForEach-Object { Get-ShortVersion $_ } + Confirm-ArrayWithoutDuplicates $versions -Because "It doesn't allow to install more than one version with the same major/minor" + } + } + + Context "Xamarin.Mac" { + $sdkVersions = $toolset.xamarin.mac_versions + + $sdkVersions | ForEach-Object { + It "Version '$_' is available and can be downloaded" { + $sdkUrl = Invoke-BashUtilsFunction("buildXamarinMacDownloadUrl", $_) + Confirm-UrlAvailability $sdkUrl + } + } + + It "Version list doesn't contain versions with the same major/minor version" { + $versions = $sdkVersions | ForEach-Object { Get-ShortVersion $_ } + Confirm-ArrayWithoutDuplicates $versions -Because "It doesn't allow to install more than one version with the same major/minor" + } + } + + Context "Xamarin.Android" { + $sdkVersions = $toolset.xamarin.android_versions + + $sdkVersions | ForEach-Object { + It "Version '$_' is available and can be downloaded" { + $sdkUrl = Invoke-BashUtilsFunction("buildXamarinAndroidDownloadUrl", $_) + Confirm-UrlAvailability $sdkUrl + } + } + + It "Version list doesn't contain versions with the same major/minor version" { + $versions = $sdkVersions | ForEach-Object { $_.Replace("-", ".") } | ForEach-Object { Get-ShortVersion $_ } + Confirm-ArrayWithoutDuplicates $versions -Because "It doesn't allow to install more than one version with the same major/minor" + } + } + + Context "Xamarin bundles" { + $monoVersions = $toolset.xamarin.mono_versions | ForEach-Object { Get-ShortVersion $_ } + $iOSVersions = $toolset.xamarin.ios_versions | ForEach-Object { Get-ShortVersion $_ } + $macVersions = $toolset.xamarin.mac_versions | ForEach-Object { Get-ShortVersion $_ } + # Old Xamarin.Android version looks like "9.0.0-18" that doesn't support by System.Version + $androidVersions = $toolset.xamarin.android_versions | ForEach-Object { Get-ShortVersion $_.Replace("-", ".") } + $bundles = $toolset.xamarin.bundles + + $bundles | ForEach-Object { + It "'$($_.symlink)' is valid" { + $monoVersions | Should -Contain $_.mono + $iOSVersions | Should -Contain $_.ios + $macVersions | Should -Contain $_.mac + $androidVersions | Should -Contain $_.android + } + } + + It "Each bundle has unique symlink" { + $symlinks = $bundles | ForEach-Object { $_.symlink } + Confirm-ArrayWithoutDuplicates $symlinks -Because "Bundle symlinks should be unique" + } + + It "Current bundle is valid" { + $currentBundleSymlink = $toolset.xamarin.bundle_default + if ($currentBundleSymlink -ne "latest") { + $bundleSymlinks = $bundles | ForEach-Object { $_.symlink } + $bundleSymlinks | Should -Contain $currentBundleSymlink -Because "Current bundle should be installed" + } + } + } + } +} diff --git a/images/macos/scripts/tests/WebServers.Tests.ps1 b/images/macos/scripts/tests/WebServers.Tests.ps1 new file mode 100644 index 0000000..303fa38 --- /dev/null +++ b/images/macos/scripts/tests/WebServers.Tests.ps1 @@ -0,0 +1,25 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +$os = Get-OSVersion + +Describe "Apache" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Apache CLI" { + "httpd -v" | Should -ReturnZeroExitCode + } + + It "Apache Service" { + $service = brew services list --json | ConvertFrom-Json | Where-Object { $_.name -eq "httpd" } + $service.status | Should -Match "(stopped|none)" + } +} + +Describe "Nginx" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Nginx CLI" { + "nginx -v" | Should -ReturnZeroExitCode + } + + It "Nginx Service" { + $service = brew services list --json | ConvertFrom-Json | Where-Object { $_.name -eq "nginx" } + $service.status | Should -Match "(stopped|none)" + } +} diff --git a/images/macos/scripts/tests/Xamarin.Tests.ps1 b/images/macos/scripts/tests/Xamarin.Tests.ps1 new file mode 100644 index 0000000..a09f87f --- /dev/null +++ b/images/macos/scripts/tests/Xamarin.Tests.ps1 @@ -0,0 +1,317 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +$os = Get-OSVersion + +if ($os.IsVentura -or $os.IsSonoma) { + $MONO_VERSIONS = @((Get-ToolsetContent).mono.framework.version) + $XAMARIN_IOS_VERSIONS = @() + $XAMARIN_MAC_VERSIONS = @() + $XAMARIN_ANDROID_VERSIONS = @() +} elseif ($os.IsMonterey) { + $MONO_VERSIONS = (Get-ToolsetContent).xamarin.mono_versions + $XAMARIN_IOS_VERSIONS = (Get-ToolsetContent).xamarin.ios_versions + $XAMARIN_MAC_VERSIONS = (Get-ToolsetContent).xamarin.mac_versions + $XAMARIN_ANDROID_VERSIONS = (Get-ToolsetContent).xamarin.android_versions +} elseif ($os.IsSequoia) { + Write-Host "Skipping all the Mono and Xamarin tests as deprecated" + # Dummy workaround for the issue with the tests discovery + $MONO_VERSIONS = @() + $XAMARIN_IOS_VERSIONS = @() + $XAMARIN_MAC_VERSIONS = @() + $XAMARIN_ANDROID_VERSIONS = @() +} + +BeforeAll { + function Get-ShortSymlink { + param ( + [string] $Version + ) + + $versionParts = $Version.Split(".") + return [String]::Join(".", $versionParts[0..1]) + } +} + +Describe "Mono" -Skip:($os.IsSequoia) { + $MONO_VERSIONS | ForEach-Object { + Context "$_" { + $MONO_VERSIONS_PATH = "/Library/Frameworks/Mono.framework/Versions" + $versionFolderPath = Join-Path $MONO_VERSIONS_PATH ([System.Version]::Parse($_).ToString(3)) + $testCase = @{ MonoVersion = $_; VersionFolderPath = $versionFolderPath; MonoVersionsPath = $MONO_VERSIONS_PATH } + + It "is installed" -TestCases $testCase { + param ( [string] $VersionFolderPath ) + + $monoBinPath = Join-Path $VersionFolderPath "bin" "mono" + $VersionFolderPath | Should -Exist + $monoBinPath | Should -Exist + } + + It "is available via short link" -TestCases $testCase { + param ( + [string] $MonoVersion, + [string] $MonoVersionsPath, + [string] $VersionFolderPath + ) + + $shortSymlink = Get-ShortSymlink $MonoVersion # only 'major.minor' + $shortSymlinkFolderPath = Join-Path $MonoVersionsPath $shortSymlink + if ($shortSymlink -eq "4.8") { return } # Skip this test for Mono 4.8 because it doesn't contain VERSION file + $shortVersionPath = Join-Path $shortSymlinkFolderPath "VERSION" + $fullVersionPath = Join-Path $VersionFolderPath "VERSION" + + Confirm-IdenticalFileContent -File1 $shortVersionPath -File2 $fullVersionPath + } + + It "NUnit console is installed" -TestCases $testCase { + param ( [string] $VersionFolderPath ) + + $nunitPath = Join-Path $VersionFolderPath "Commands" "nunit3-console" + $nunitPath | Should -Exist + } + + It "Nuget is installed" -TestCases $testCase { + param ( [string] $VersionFolderPath ) + + $nugetBinaryPath = Join-Path $VersionFolderPath "lib" "mono" "nuget" "nuget.exe" + $nugetBinaryWrapperPath = Join-Path $VersionFolderPath "bin" "nuget" + $nugetCommandPath = Join-Path $VersionFolderPath "Commands" "nuget" + + $nugetBinaryPath | Should -Exist + $nugetCommandPath | Should -Exist + $nugetBinaryWrapperPath | Should -Exist + } + + It "Nuget is valid" -TestCases $testCase { + param ( [string] $VersionFolderPath ) + + $nugetBinaryWrapperPath = Join-Path $VersionFolderPath "bin" "nuget" + "$nugetBinaryWrapperPath" | Should -ReturnZeroExitCode + } + } + } + + It "MSBuild is available" { + "msbuild -version" | Should -ReturnZeroExitCode + } +} + +Describe "Xamarin.iOS" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + $XAMARIN_IOS_VERSIONS | ForEach-Object { + Context "$_" { + $XAMARIN_IOS_VERSIONS_PATH = "/Library/Frameworks/Xamarin.iOS.framework/Versions" + $versionFolderPath = Join-Path $XAMARIN_IOS_VERSIONS_PATH $_ + $testCase = @{ XamarinIosVersion = $_; VersionFolderPath = $versionFolderPath; IosVersionsPath = $XAMARIN_IOS_VERSIONS_PATH } + + It "is installed" -TestCases $testCase { + param ( [string] $VersionFolderPath ) + + $xamarinBinPath = Join-Path $VersionFolderPath "bin" + $VersionFolderPath | Should -Exist + $xamarinBinPath | Should -Exist + } + + It "is available via short link" -TestCases $testCase { + param ( + [string] $XamarinIosVersion, + [string] $IosVersionsPath, + [string] $VersionFolderPath + ) + + $shortSymlink = Get-ShortSymlink $XamarinIosVersion # only 'major.minor' + $shortSymlinkFolderPath = Join-Path $IosVersionsPath $shortSymlink + $shortVersionPath = Join-Path $shortSymlinkFolderPath "VERSION" + $fullVersionPath = Join-Path $VersionFolderPath "VERSION" + + Confirm-IdenticalFileContent -File1 $shortVersionPath -File2 $fullVersionPath + } + } + } +} + +Describe "Xamarin.Mac" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + $XAMARIN_MAC_VERSIONS | ForEach-Object { + Context "$_" { + $XAMARIN_MAC_VERSIONS_PATH = "/Library/Frameworks/Xamarin.Mac.framework/Versions" + $versionFolderPath = Join-Path $XAMARIN_MAC_VERSIONS_PATH $_ + $testCase = @{ XamarinMacVersion = $_; VersionFolderPath = $versionFolderPath; MacVersionsPath = $XAMARIN_MAC_VERSIONS_PATH } + + It "is installed" -TestCases $testCase { + param ( [string] $VersionFolderPath ) + + $xamarinBinPath = Join-Path $VersionFolderPath "bin" + $VersionFolderPath | Should -Exist + $xamarinBinPath | Should -Exist + } + + It "is available via short link" -TestCases $testCase { + param ( + [string] $XamarinMacVersion, + [string] $MacVersionsPath, + [string] $VersionFolderPath + ) + + $shortSymlink = Get-ShortSymlink $XamarinMacVersion # only 'major.minor' + $shortSymlinkFolderPath = Join-Path $MacVersionsPath $shortSymlink + $shortVersionPath = Join-Path $shortSymlinkFolderPath "VERSION" + $fullVersionPath = Join-Path $VersionFolderPath "VERSION" + + Confirm-IdenticalFileContent -File1 $shortVersionPath -File2 $fullVersionPath + } + } + } +} + +Describe "Xamarin.Android" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + $XAMARIN_ANDROID_VERSIONS | ForEach-Object { + Context "$_" { + $XAMARIN_ANDROID_VERSIONS_PATH = "/Library/Frameworks/Xamarin.Android.framework/Versions" + $versionFolderPath = Join-Path $XAMARIN_ANDROID_VERSIONS_PATH $_ + $testCase = @{ XamarinAndroidVersion = $_; VersionFolderPath = $versionFolderPath; AndroidVersionsPath = $XAMARIN_ANDROID_VERSIONS_PATH } + + It "is installed" -TestCases $testCase { + param ( [string] $VersionFolderPath ) + + $xamarinLibPath = Join-Path $VersionFolderPath "lib" + $xamarinLibPath | Should -Exist + } + + It "is available via short link" -TestCases $testCase { + param ( + [string] $XamarinAndroidVersion, + [string] $AndroidVersionsPath, + [string] $VersionFolderPath + ) + + $shortSymlink = Get-ShortSymlink $XamarinAndroidVersion # only 'major.minor' + $shortSymlinkFolderPath = Join-Path $AndroidVersionsPath $shortSymlink + $shortVersionPath = Join-Path $shortSymlinkFolderPath "VERSION" + $fullVersionPath = Join-Path $VersionFolderPath "VERSION" + + Confirm-IdenticalFileContent -File1 $shortVersionPath -File2 $fullVersionPath + } + + It "has correct symlinks" -TestCases $testCase { + param ( [string] $VersionFolderPath ) + + $xamarinLibPath = Join-Path $VersionFolderPath "lib" + Join-Path $xamarinLibPath "xbuild" | Should -Exist + Join-Path $xamarinLibPath "xbuild-frameworks" | Should -Exist + } + } + } +} + +Describe "Xamarin Bundles" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + BeforeAll { + $MONO_VERSIONS_PATH = "/Library/Frameworks/Mono.framework/Versions" + $XAMARIN_IOS_VERSIONS_PATH = "/Library/Frameworks/Xamarin.iOS.framework/Versions" + $XAMARIN_MAC_VERSIONS_PATH = "/Library/Frameworks/Xamarin.Mac.framework/Versions" + $XAMARIN_ANDROID_VERSIONS_PATH = "/Library/Frameworks/Xamarin.Android.framework/Versions" + } + + If ($XAMARIN_BUNDLES.Count -eq 0) { return } # Skip this test if there are no bundles + + [array]$XAMARIN_BUNDLES = (Get-ToolsetContent).xamarin.bundles + $XAMARIN_DEFAULT_BUNDLE = (Get-ToolsetContent).xamarin.bundle_default + If ($XAMARIN_DEFAULT_BUNDLE -eq "latest") { $XAMARIN_DEFAULT_BUNDLE = $XAMARIN_BUNDLES[0].symlink } + + $currentBundle = [PSCustomObject] @{ + symlink = "Current" + mono = $XAMARIN_DEFAULT_BUNDLE + ios = $XAMARIN_DEFAULT_BUNDLE + mac = $XAMARIN_DEFAULT_BUNDLE + android = $XAMARIN_DEFAULT_BUNDLE + } + + $latestBundle = [PSCustomObject] @{ + symlink = "Latest" + mono = $XAMARIN_BUNDLES[0].mono + ios = $XAMARIN_BUNDLES[0].ios + mac = $XAMARIN_BUNDLES[0].mac + android = $XAMARIN_BUNDLES[0].android + } + + $bundles = $XAMARIN_BUNDLES + $currentBundle + $latestBundle + $allBundles = $bundles | ForEach-Object { @{BundleSymlink = $_.symlink; BundleMono = $_.mono; BundleIos = $_.ios; BundleMac = $_.mac; BundleAndroid = $_.android } } + + It "Mono symlink exists" -TestCases $allBundles { + param ( [string] $BundleSymlink ) + + (Join-Path $MONO_VERSIONS_PATH $BundleSymlink) | Should -Exist + } + + It "Mono symlink points to the correct version" -TestCases $allBundles { + param ( + [string] $BundleSymlink, + [string] $BundleMono + ) + + if ($BundleMono -eq "4.8") { return } # Skip this test for Mono 4.8 because it doesn't contain VERSION file + $sourceVersionPath = Join-Path $MONO_VERSIONS_PATH $BundleMono "VERSION" + $targetVersionPath = Join-Path $MONO_VERSIONS_PATH $BundleSymlink "VERSION" + + Confirm-IdenticalFileContent -File1 $targetVersionPath -File2 $sourceVersionPath + } + + It "iOS symlink exists" -TestCases $allBundles { + param ( [string] $BundleSymlink ) + + (Join-Path $XAMARIN_IOS_VERSIONS_PATH $BundleSymlink) | Should -Exist + } + + It "iOS symlink points to the correct version" -TestCases $allBundles { + param ( + [string] $BundleSymlink, + [string] $BundleIos + ) + + $sourceVersionPath = Join-Path $XAMARIN_IOS_VERSIONS_PATH $BundleIos "VERSION" + $targetVersionPath = Join-Path $XAMARIN_IOS_VERSIONS_PATH $BundleSymlink "VERSION" + + Confirm-IdenticalFileContent -File1 $targetVersionPath -File2 $sourceVersionPath + } + + It "Mac symlink exists" -TestCases $allBundles { + param ( [string] $BundleSymlink ) + + (Join-Path $XAMARIN_MAC_VERSIONS_PATH $BundleSymlink) | Should -Exist + } + + It "Mac symlink points to the correct version" -TestCases $allBundles { + param ( + [string] $BundleSymlink, + [string] $BundleMac + ) + + $sourceVersionPath = Join-Path $XAMARIN_MAC_VERSIONS_PATH $BundleMac "VERSION" + $targetVersionPath = Join-Path $XAMARIN_MAC_VERSIONS_PATH $BundleSymlink "VERSION" + + Confirm-IdenticalFileContent -File1 $targetVersionPath -File2 $sourceVersionPath + } + + It "Xamarin.Android symlink exists" -TestCases $allBundles { + param ( [string] $BundleSymlink ) + + (Join-Path $XAMARIN_ANDROID_VERSIONS_PATH $BundleSymlink) | Should -Exist + } + + It "Android symlink points to the correct version" -TestCases $allBundles { + param ( + [string] $BundleSymlink, + [string] $BundleAndroid + ) + + $sourceVersionPath = Join-Path $XAMARIN_ANDROID_VERSIONS_PATH $BundleAndroid "VERSION" + $targetVersionPath = Join-Path $XAMARIN_ANDROID_VERSIONS_PATH $BundleSymlink "VERSION" + + Confirm-IdenticalFileContent -File1 $targetVersionPath -File2 $sourceVersionPath + } +} + +Describe "Nuget" -Skip:($os.IsVentura -or $os.IsSonoma -or $os.IsSequoia) { + It "Nuget config contains nuget.org feed" { + Get-Content $env:HOME/.config/NuGet/NuGet.Config | Out-String | Should -Match "nuget.org" + } +} diff --git a/images/macos/scripts/tests/Xcode.Tests.ps1 b/images/macos/scripts/tests/Xcode.Tests.ps1 new file mode 100644 index 0000000..4489b9a --- /dev/null +++ b/images/macos/scripts/tests/Xcode.Tests.ps1 @@ -0,0 +1,141 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Import-Module "$PSScriptRoot/../helpers/Xcode.Helpers.psm1" +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +$arch = Get-Architecture +$xcodeVersions = (Get-ToolsetContent).xcode.${arch}.versions +$defaultXcode = (Get-ToolsetContent).xcode.default +$latestXcodeVersion = $xcodeVersions | Select-Object -First 1 +$os = Get-OSVersion + +Describe "Xcode" { + $testCases = $xcodeVersions | ForEach-Object { + @{ + XcodeVersion = $_.link; + LatestXcodeVersion = $xcodeVersions[0].link; + Symlinks = $_.symlinks + } + } + + Context "Versions" { + It "" -TestCases $testCases { + $xcodebuildPath = Get-XcodeToolPath -Version $XcodeVersion -ToolName "xcodebuild" + "$xcodebuildPath -version" | Should -ReturnZeroExitCode + } + } + + Context "Default" { + $defaultXcodeTestCase = @{ DefaultXcode = $defaultXcode } + It "Default Xcode is " -TestCases $defaultXcodeTestCase { + "xcodebuild -version" | Should -ReturnZeroExitCode + If ($DefaultXcode -ilike "*_*") { + Write-Host "Composite version detected (beta/RC/preview)" + $DefaultXcode = $DefaultXcode.split("_")[0] + If ($DefaultXcode -notlike "*.*") { + $DefaultXcode = "${DefaultXcode}.0" + } + } + (Get-CommandResult "xcodebuild -version").Output | Should -BeLike "Xcode ${DefaultXcode}*" + } + + It "Xcode.app points to default Xcode" -TestCases $defaultXcodeTestCase { + $xcodeApp = "/Applications/Xcode.app" + $expectedTarget = Get-XcodeRootPath -Version $DefaultXcode + $xcodeApp | Should -Exist + $expectedTarget | Should -Exist + (Get-Item $xcodeApp).Target | Should -Be $expectedTarget + } + } + + Context "Additional tools" { + It "Xcode tools are installed" -TestCases $testCases { + $TOOLS_NOT_INSTALLED_EXIT_CODE = 69 + $xcodebuildPath = Get-XcodeToolPath -Version $XcodeVersion -ToolName "xcodebuild" + $result = Get-CommandResult "$xcodebuildPath -checkFirstLaunchStatus" + + if ($XcodeVersion -ne $LatestXcodeVersion) { + $result.ExitCode | Should -Not -Be $TOOLS_NOT_INSTALLED_EXIT_CODE + } else { + $result.ExitCode | Should -BeIn (0, $TOOLS_NOT_INSTALLED_EXIT_CODE) + } + } + } + + Context "Symlinks" { + It "Xcode has correct symlinks" -TestCases $testCases { + $sourcePath = Get-XcodeRootPath -Version $XcodeVersion + $Symlinks | Where-Object { $_ } | ForEach-Object { + $targetPath = Get-XcodeRootPath -Version $_ + $targetPath | Should -Exist + (Get-Item $targetPath).Target | Should -Be $sourcePath + } + } + } + + It "/Applications/Xcode* symlinks are valid" { + $symlinks = Get-ChildItem "/Applications" -Filter "Xcode*" | Where-Object { $_.LinkType } + + $symlinks.Target | ForEach-Object { + $_ | Should -Exist + } + } +} + +Describe "XCODE_DEVELOPER_DIR variables" { + $exactVersionsList = $xcodeVersions.link | Where-Object { Test-XcodeStableRelease -Version $_ } | ForEach-Object { + $xcodeRootPath = Get-XcodeRootPath -Version $_ + $xcodeVersionInfo = Get-XcodeVersionInfo -XcodeRootPath $xcodeRootPath + return @{ + RootPath = $xcodeRootPath + Version = [SemVer]::Parse($xcodeVersionInfo.Version) + } + } | Sort-Object -Property Version -Descending + $majorVersions = $exactVersionsList.Version.Major | Select-Object -Unique + $testCases = $majorVersions | ForEach-Object { @{ MajorVersion = $_; VersionsList = $exactVersionsList } } + + It "XCODE__DEVELOPER_DIR" -TestCases $testCases { + $variableName = "XCODE_${MajorVersion}_DEVELOPER_DIR" + $actualPath = [System.Environment]::GetEnvironmentVariable($variableName) + $expectedVersion = $VersionsList | Where-Object { $_.Version.Major -eq $MajorVersion } | Select-Object -First 1 + $expectedPath = "$($expectedVersion.RootPath)/Contents/Developer" + $actualPath | Should -Exist + $actualPath | Should -Be $expectedPath + } +} + +Describe "Xcode simulators" { + $xcodeVersions.link | Where-Object { Test-XcodeStableRelease -Version $_ } | ForEach-Object { + Context "$_" { + $testCase = @{ XcodeVersion = $_ } + It "No duplicates in devices" -TestCases $testCase { + Switch-Xcode -Version $XcodeVersion + [array]$devicesList = @(Get-XcodeDevicesList | Where-Object { $_ }) + Write-Host "Devices for $XcodeVersion" + Write-Host ($devicesList -join "`n") + Confirm-ArrayWithoutDuplicates $devicesList -Because "Found duplicate device simulators" + } + } + } + + AfterEach { + $defaultXcode = (Get-ToolsetContent).xcode.default + Switch-Xcode -Version $defaultXcode + } +} + +Describe "Xcode Simulators Naming" -Skip:(-not $os.IsMonterey) { + $testCases = Get-BrokenXcodeSimulatorsList + It "Simulator ' []'" -TestCases $testCases { + $simctlPath = Get-XcodeToolPath -Version $XcodeVersion -ToolName "simctl" + [string]$rawDevicesInfo = Invoke-Expression "$simctlPath list devices --json" + $jsonDevicesInfo = ($rawDevicesInfo | ConvertFrom-Json).devices + + $foundSimulators = $jsonDevicesInfo.$RuntimeId | Where-Object { $_.deviceTypeIdentifier -eq $DeviceId } + $foundSimulators | Should -HaveCount 1 + $foundSimulators[0].name | Should -Be $SimulatorName + + $foundSimulators = $jsonDevicesInfo.$RuntimeId | Where-Object { $_.name -eq $SimulatorName } + $foundSimulators | Should -HaveCount 1 + $foundSimulators[0].deviceTypeIdentifier | Should -Be $DeviceId + } +} diff --git a/images/macos/templates/macOS-12.anka.pkr.hcl b/images/macos/templates/macOS-12.anka.pkr.hcl new file mode 100644 index 0000000..398623b --- /dev/null +++ b/images/macos/templates/macOS-12.anka.pkr.hcl @@ -0,0 +1,315 @@ +packer { + required_plugins { + veertu-anka = { + version = ">= v3.2.0" + source = "github.com/veertuinc/veertu-anka" + } + } +} + +locals { + image_folder = "/Users/${var.vm_username}/image-generation" +} + +variable "builder_type" { + type = string + default = "veertu-anka-vm-clone" + validation { + condition = contains(["veertu-anka-vm-clone", "null"], var.builder_type) + error_message = "The builder_type value must be one of [veertu-anka-vm-clone, null]." + } +} + +variable "source_vm_name" { + type = string +} + +variable "source_vm_port" { + type = number + default = 22 +} + +variable "source_vm_tag" { + type = string + default = "" +} + +variable "socks_proxy" { + type = string + default = "" +} + +variable "build_id" { + type = string +} + +variable "vm_username" { + type = string + sensitive = true +} + +variable "vm_password" { + type = string + sensitive = true +} + +variable "github_api_pat" { + type = string + sensitive = true + default = "" +} + +variable "xcode_install_storage_url" { + type = string + sensitive = true +} + +variable "xcode_install_sas" { + type = string + sensitive = true +} + +variable "vcpu_count" { + type = string + default = "6" +} + +variable "ram_size" { + type = string + default = "24G" +} + +variable "image_os" { + type = string + default = "macos12" +} + +source "veertu-anka-vm-clone" "template" { + vm_name = "${var.build_id}" + source_vm_name = "${var.source_vm_name}" + source_vm_tag = "${var.source_vm_tag}" + vcpu_count = "${var.vcpu_count}" + ram_size = "${var.ram_size}" + stop_vm = "true" +} + +source "null" "template" { + ssh_host = "${var.source_vm_name}" + ssh_port = "${var.source_vm_port}" + ssh_username = "${var.vm_username}" + ssh_password = "${var.vm_password}" + ssh_proxy_host = "${var.socks_proxy}" +} + +build { + sources = ["source.${var.builder_type}.template"] + + provisioner "shell" { + inline = ["mkdir ${local.image_folder}"] + } + + provisioner "file" { + destination = "${local.image_folder}/" + sources = [ + "${path.root}/../assets/xamarin-selector", + "${path.root}/../scripts/tests", + "${path.root}/../scripts/docs-gen", + "${path.root}/../scripts/helpers" + ] + } + + provisioner "file" { + destination = "${local.image_folder}/docs-gen/" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "file" { + destination = "${local.image_folder}/add-certificate.swift" + source = "${path.root}/../assets/add-certificate.swift" + } + + provisioner "file" { + destination = ".bashrc" + source = "${path.root}/../assets/bashrc" + } + + provisioner "file" { + destination = ".bash_profile" + source = "${path.root}/../assets/bashprofile" + } + + provisioner "shell" { + inline = [ "mkdir ~/bootstrap" ] + } + + provisioner "file" { + destination = "bootstrap" + source = "${path.root}/../assets/bootstrap-provisioner/" + } + + provisioner "file" { + destination = "${local.image_folder}/toolset.json" + source = "${path.root}/../toolsets/toolset-12.json" + } + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report", + "mv ${local.image_folder}/xamarin-selector ${local.image_folder}/assets", + "mkdir ~/utils", + "mv ${local.image_folder}/helpers/confirm-identified-developers.scpt ~/utils", + "mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils", + "mv ${local.image_folder}/helpers/utils.sh ~/utils", + "mv ${local.image_folder}/helpers/xamarin-utils.sh ~/utils" + ] + } + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-xcode-clt.sh", + "${path.root}/../scripts/build/install-homebrew.sh" + ] + } + + provisioner "shell" { + environment_vars = ["PASSWORD=${var.vm_password}", "USERNAME=${var.vm_username}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-tccdb-macos.sh", + "${path.root}/../scripts/build/configure-network-interface-detection.sh", + "${path.root}/../scripts/build/configure-autologin.sh", + "${path.root}/../scripts/build/configure-auto-updates.sh", + "${path.root}/../scripts/build/configure-screensaver.sh", + "${path.root}/../scripts/build/configure-ntpconf.sh", + "${path.root}/../scripts/build/configure-max-files-limitation.sh", + "${path.root}/../scripts/build/configure-shell.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.build_id}", "IMAGE_OS=${var.image_os}", "PASSWORD=${var.vm_password}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-preimagedata.sh", + "${path.root}/../scripts/build/configure-ssh.sh", + "${path.root}/../scripts/build/configure-machine.sh" + ] + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "USER_PASSWORD=${var.vm_password}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + pause_before = "30s" + scripts = [ + "${path.root}/../scripts/build/configure-windows.sh", + "${path.root}/../scripts/build/install-powershell.sh", + "${path.root}/../scripts/build/install-dotnet.sh", + "${path.root}/../scripts/build/install-python.sh", + "${path.root}/../scripts/build/install-azcopy.sh", + "${path.root}/../scripts/build/install-openssl.sh", + "${path.root}/../scripts/build/install-ruby.sh", + "${path.root}/../scripts/build/install-rubygems.sh", + "${path.root}/../scripts/build/install-git.sh", + "${path.root}/../scripts/build/install-mongodb.sh", + "${path.root}/../scripts/build/install-node.sh", + "${path.root}/../scripts/build/install-common-utils.sh" + ] + } + + provisioner "shell" { + environment_vars = ["XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}", "XCODE_INSTALL_SAS=${var.xcode_install_sas}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Install-Xcode.ps1" + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-actions-cache.sh", + "${path.root}/../scripts/build/install-llvm.sh", + "${path.root}/../scripts/build/install-golang.sh", + "${path.root}/../scripts/build/install-swiftlint.sh", + "${path.root}/../scripts/build/install-openjdk.sh", + "${path.root}/../scripts/build/install-php.sh", + "${path.root}/../scripts/build/install-aws-tools.sh", + "${path.root}/../scripts/build/install-rust.sh", + "${path.root}/../scripts/build/install-gcc.sh", + "${path.root}/../scripts/build/install-haskell.sh", + "${path.root}/../scripts/build/install-cocoapods.sh", + "${path.root}/../scripts/build/install-android-sdk.sh", + "${path.root}/../scripts/build/install-xamarin.sh", + "${path.root}/../scripts/build/install-visualstudio.sh", + "${path.root}/../scripts/build/install-nvm.sh", + "${path.root}/../scripts/build/install-apache.sh", + "${path.root}/../scripts/build/install-nginx.sh", + "${path.root}/../scripts/build/install-postgresql.sh", + "${path.root}/../scripts/build/install-audiodevice.sh", + "${path.root}/../scripts/build/install-vcpkg.sh", + "${path.root}/../scripts/build/install-miniconda.sh", + "${path.root}/../scripts/build/install-safari.sh", + "${path.root}/../scripts/build/install-chrome.sh", + "${path.root}/../scripts/build/install-edge.sh", + "${path.root}/../scripts/build/install-firefox.sh", + "${path.root}/../scripts/build/install-pypy.sh", + "${path.root}/../scripts/build/install-pipx-packages.sh", + "${path.root}/../scripts/build/install-bicep.sh", + "${path.root}/../scripts/build/install-codeql-bundle.sh", + "${path.root}/../scripts/build/install-compilable-brew-packages.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/Install-Toolset.ps1", + "${path.root}/../scripts/build/Configure-Toolset.ps1" + ] + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; ruby {{ .Path }}" + script = "${path.root}/../scripts/build/configure-xcode-simulators.rb" + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Update-XcodeSimulators.ps1" + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + inline = [ + "pwsh -File \"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\" -OutputDirectory \"${local.image_folder}/output/software-report\" -ImageName ${var.build_id}", + "pwsh -File \"${local.image_folder}/tests/RunAll-Tests.ps1\"" + ] + } + + provisioner "file" { + destination = "${path.root}/../../image-output/" + direction = "download" + source = "${local.image_folder}/output/" + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-hostname.sh", + "${path.root}/../scripts/build/configure-system.sh" + ] + } +} diff --git a/images/macos/templates/macOS-13.anka.pkr.hcl b/images/macos/templates/macOS-13.anka.pkr.hcl new file mode 100644 index 0000000..352b1d8 --- /dev/null +++ b/images/macos/templates/macOS-13.anka.pkr.hcl @@ -0,0 +1,302 @@ +packer { + required_plugins { + veertu-anka = { + version = ">= v3.2.0" + source = "github.com/veertuinc/veertu-anka" + } + } +} + +locals { + image_folder = "/Users/${var.vm_username}/image-generation" +} + +variable "builder_type" { + type = string + default = "veertu-anka-vm-clone" + validation { + condition = contains(["veertu-anka-vm-clone", "null"], var.builder_type) + error_message = "The builder_type value must be one of [veertu-anka-vm-clone, null]." + } +} + +variable "source_vm_name" { + type = string +} + +variable "source_vm_port" { + type = number + default = 22 +} + +variable "source_vm_tag" { + type = string + default = "" +} + +variable "socks_proxy" { + type = string + default = "" +} + +variable "build_id" { + type = string +} + +variable "vm_username" { + type = string + sensitive = true +} + +variable "vm_password" { + type = string + sensitive = true +} + +variable "github_api_pat" { + type = string + sensitive = true + default = "" +} + +variable "xcode_install_storage_url" { + type = string + sensitive = true +} + +variable "xcode_install_sas" { + type = string + sensitive = true +} + +variable "vcpu_count" { + type = string + default = "6" +} + +variable "ram_size" { + type = string + default = "24G" +} + +variable "image_os" { + type = string + default = "macos13" +} + +source "veertu-anka-vm-clone" "template" { + vm_name = "${var.build_id}" + source_vm_name = "${var.source_vm_name}" + source_vm_tag = "${var.source_vm_tag}" + vcpu_count = "${var.vcpu_count}" + ram_size = "${var.ram_size}" + stop_vm = "true" +} + +source "null" "template" { + ssh_host = "${var.source_vm_name}" + ssh_port = "${var.source_vm_port}" + ssh_username = "${var.vm_username}" + ssh_password = "${var.vm_password}" + ssh_proxy_host = "${var.socks_proxy}" +} + +build { + sources = ["source.${var.builder_type}.template"] + + provisioner "shell" { + inline = ["mkdir ${local.image_folder}"] + } + + provisioner "file" { + destination = "${local.image_folder}/" + sources = [ + "${path.root}/../assets/xamarin-selector", + "${path.root}/../scripts/tests", + "${path.root}/../scripts/docs-gen", + "${path.root}/../scripts/helpers" + ] + } + + provisioner "file" { + destination = "${local.image_folder}/docs-gen/" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "file" { + destination = "${local.image_folder}/add-certificate.swift" + source = "${path.root}/../assets/add-certificate.swift" + } + + provisioner "file" { + destination = ".bashrc" + source = "${path.root}/../assets/bashrc" + } + + provisioner "file" { + destination = ".bash_profile" + source = "${path.root}/../assets/bashprofile" + } + + provisioner "shell" { + inline = ["mkdir ~/bootstrap"] + } + + provisioner "file" { + destination = "bootstrap" + source = "${path.root}/../assets/bootstrap-provisioner/" + } + + provisioner "file" { + destination = "${local.image_folder}/toolset.json" + source = "${path.root}/../toolsets/toolset-13.json" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report", + "mv ${local.image_folder}/xamarin-selector ${local.image_folder}/assets", + "mkdir ~/utils", + "mv ${local.image_folder}/helpers/confirm-identified-developers-macos13.scpt ~/utils", + "mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils", + "mv ${local.image_folder}/helpers/utils.sh ~/utils", + "mv ${local.image_folder}/helpers/xamarin-utils.sh ~/utils" + ] + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-xcode-clt.sh", + "${path.root}/../scripts/build/install-homebrew.sh" + ] + } + + provisioner "shell" { + environment_vars = ["PASSWORD=${var.vm_password}", "USERNAME=${var.vm_username}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-tccdb-macos.sh", + "${path.root}/../scripts/build/configure-autologin.sh", + "${path.root}/../scripts/build/configure-auto-updates.sh", + "${path.root}/../scripts/build/configure-ntpconf.sh", + "${path.root}/../scripts/build/configure-shell.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.build_id}", "IMAGE_OS=${var.image_os}", "PASSWORD=${var.vm_password}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-preimagedata.sh", + "${path.root}/../scripts/build/configure-ssh.sh", + "${path.root}/../scripts/build/configure-machine.sh" + ] + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "USER_PASSWORD=${var.vm_password}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + pause_before = "30s" + scripts = [ + "${path.root}/../scripts/build/configure-windows.sh", + "${path.root}/../scripts/build/install-powershell.sh", + "${path.root}/../scripts/build/install-mono.sh", + "${path.root}/../scripts/build/install-dotnet.sh", + "${path.root}/../scripts/build/install-python.sh", + "${path.root}/../scripts/build/install-azcopy.sh", + "${path.root}/../scripts/build/install-openssl.sh", + "${path.root}/../scripts/build/install-ruby.sh", + "${path.root}/../scripts/build/install-rubygems.sh", + "${path.root}/../scripts/build/install-git.sh", + "${path.root}/../scripts/build/install-node.sh", + "${path.root}/../scripts/build/install-common-utils.sh" + ] + } + + provisioner "shell" { + environment_vars = ["XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}", "XCODE_INSTALL_SAS=${var.xcode_install_sas}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Install-Xcode.ps1" + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-actions-cache.sh", + "${path.root}/../scripts/build/install-llvm.sh", + "${path.root}/../scripts/build/install-swiftlint.sh", + "${path.root}/../scripts/build/install-openjdk.sh", + "${path.root}/../scripts/build/install-php.sh", + "${path.root}/../scripts/build/install-aws-tools.sh", + "${path.root}/../scripts/build/install-rust.sh", + "${path.root}/../scripts/build/install-gcc.sh", + "${path.root}/../scripts/build/install-cocoapods.sh", + "${path.root}/../scripts/build/install-android-sdk.sh", + "${path.root}/../scripts/build/install-vcpkg.sh", + "${path.root}/../scripts/build/install-safari.sh", + "${path.root}/../scripts/build/install-chrome.sh", + "${path.root}/../scripts/build/install-edge.sh", + "${path.root}/../scripts/build/install-firefox.sh", + "${path.root}/../scripts/build/install-pypy.sh", + "${path.root}/../scripts/build/install-bicep.sh", + "${path.root}/../scripts/build/install-codeql-bundle.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/Install-Toolset.ps1", + "${path.root}/../scripts/build/Configure-Toolset.ps1" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1" + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + inline = [ + "pwsh -File \"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\" -OutputDirectory \"${local.image_folder}/output/software-report\" -ImageName ${var.build_id}", + "pwsh -File \"${local.image_folder}/tests/RunAll-Tests.ps1\"" + ] + } + + provisioner "file" { + destination = "${path.root}/../../image-output/" + direction = "download" + source = "${local.image_folder}/output/" + } + + provisioner "shell" { + inline = ["rm -rf \"$(brew --cache)\""] + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-hostname.sh", + "${path.root}/../scripts/build/configure-system.sh" + ] + } +} diff --git a/images/macos/templates/macOS-13.arm64.anka.pkr.hcl b/images/macos/templates/macOS-13.arm64.anka.pkr.hcl new file mode 100644 index 0000000..238ab13 --- /dev/null +++ b/images/macos/templates/macOS-13.arm64.anka.pkr.hcl @@ -0,0 +1,291 @@ +packer { + required_plugins { + veertu-anka = { + version = ">= v3.2.0" + source = "github.com/veertuinc/veertu-anka" + } + } +} + +locals { + image_folder = "/Users/${var.vm_username}/image-generation" +} + +variable "builder_type" { + type = string + default = "veertu-anka-vm-clone" + validation { + condition = contains(["veertu-anka-vm-clone", "null"], var.builder_type) + error_message = "The builder_type value must be one of [veertu-anka-vm-clone, null]." + } +} + +variable "source_vm_name" { + type = string +} + +variable "source_vm_port" { + type = number + default = 22 +} + +variable "source_vm_tag" { + type = string + default = "" +} + +variable "socks_proxy" { + type = string + default = "" +} + +variable "build_id" { + type = string +} + +variable "vm_username" { + type = string + sensitive = true +} + +variable "vm_password" { + type = string + sensitive = true +} + +variable "github_api_pat" { + type = string + sensitive = true + default = "" +} + +variable "xcode_install_storage_url" { + type = string + sensitive = true +} + +variable "xcode_install_sas" { + type = string + sensitive = true +} + +variable "vcpu_count" { + type = string + default = "6" +} + +variable "ram_size" { + type = string + default = "8G" +} + +variable "image_os" { + type = string + default = "macos13" +} + +source "veertu-anka-vm-clone" "template" { + vm_name = "${var.build_id}" + source_vm_name = "${var.source_vm_name}" + source_vm_tag = "${var.source_vm_tag}" + vcpu_count = "${var.vcpu_count}" + ram_size = "${var.ram_size}" + stop_vm = "true" + log_level = "debug" +} + +source "null" "template" { + ssh_host = "${var.source_vm_name}" + ssh_port = "${var.source_vm_port}" + ssh_username = "${var.vm_username}" + ssh_password = "${var.vm_password}" + ssh_proxy_host = "${var.socks_proxy}" +} + +build { + sources = ["source.${var.builder_type}.template"] + + provisioner "shell" { + inline = ["mkdir ${local.image_folder}"] + } + + provisioner "file" { + destination = "${local.image_folder}/" + sources = [ + "${path.root}/../assets/xamarin-selector", + "${path.root}/../scripts/tests", + "${path.root}/../scripts/docs-gen", + "${path.root}/../scripts/helpers" + ] + } + + provisioner "file" { + destination = "${local.image_folder}/docs-gen/" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "file" { + destination = "${local.image_folder}/add-certificate.swift" + source = "${path.root}/../assets/add-certificate.swift" + } + + provisioner "file" { + destination = ".bashrc" + source = "${path.root}/../assets/bashrc" + } + + provisioner "file" { + destination = ".bash_profile" + source = "${path.root}/../assets/bashprofile" + } + + provisioner "shell" { + inline = ["mkdir ~/bootstrap"] + } + + provisioner "file" { + destination = "bootstrap" + source = "${path.root}/../assets/bootstrap-provisioner/" + } + + provisioner "file" { + destination = "${local.image_folder}/toolset.json" + source = "${path.root}/../toolsets/toolset-13.json" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report", + "mv ${local.image_folder}/xamarin-selector ${local.image_folder}/assets", + "mkdir ~/utils", + "mv ${local.image_folder}/helpers/confirm-identified-developers.scpt ~/utils", + "mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils", + "mv ${local.image_folder}/helpers/utils.sh ~/utils", + "mv ${local.image_folder}/helpers/xamarin-utils.sh ~/utils" + ] + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-xcode-clt.sh", + "${path.root}/../scripts/build/install-homebrew.sh" + ] + } + + provisioner "shell" { + environment_vars = ["PASSWORD=${var.vm_password}", "USERNAME=${var.vm_username}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-tccdb-macos.sh", + "${path.root}/../scripts/build/configure-autologin.sh", + "${path.root}/../scripts/build/configure-auto-updates.sh", + "${path.root}/../scripts/build/configure-ntpconf.sh", + "${path.root}/../scripts/build/configure-shell.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.build_id}", "IMAGE_OS=${var.image_os}", "PASSWORD=${var.vm_password}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-preimagedata.sh", + "${path.root}/../scripts/build/configure-ssh.sh", + "${path.root}/../scripts/build/configure-machine.sh" + ] + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "USER_PASSWORD=${var.vm_password}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + pause_before = "30s" + scripts = [ + "${path.root}/../scripts/build/install-rosetta.sh", + "${path.root}/../scripts/build/configure-windows.sh", + "${path.root}/../scripts/build/install-powershell.sh", + "${path.root}/../scripts/build/install-mono.sh", + "${path.root}/../scripts/build/install-dotnet.sh", + "${path.root}/../scripts/build/install-python.sh", + "${path.root}/../scripts/build/install-azcopy.sh", + "${path.root}/../scripts/build/install-openssl.sh", + "${path.root}/../scripts/build/install-ruby.sh", + "${path.root}/../scripts/build/install-rubygems.sh", + "${path.root}/../scripts/build/install-git.sh", + "${path.root}/../scripts/build/install-node.sh", + "${path.root}/../scripts/build/install-common-utils.sh" + ] + } + + provisioner "shell" { + environment_vars = ["XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}", "XCODE_INSTALL_SAS=${var.xcode_install_sas}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Install-Xcode.ps1" + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-actions-cache.sh", + "${path.root}/../scripts/build/install-llvm.sh", + "${path.root}/../scripts/build/install-openjdk.sh", + "${path.root}/../scripts/build/install-aws-tools.sh", + "${path.root}/../scripts/build/install-rust.sh", + "${path.root}/../scripts/build/install-gcc.sh", + "${path.root}/../scripts/build/install-cocoapods.sh", + "${path.root}/../scripts/build/install-android-sdk.sh", + "${path.root}/../scripts/build/install-safari.sh", + "${path.root}/../scripts/build/install-chrome.sh", + "${path.root}/../scripts/build/install-bicep.sh", + "${path.root}/../scripts/build/install-codeql-bundle.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/Install-Toolset.ps1", + "${path.root}/../scripts/build/Configure-Toolset.ps1" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1" + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + inline = [ + "pwsh -File \"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\" -OutputDirectory \"${local.image_folder}/output/software-report\" -ImageName ${var.build_id}", + "pwsh -File \"${local.image_folder}/tests/RunAll-Tests.ps1\"" + ] + } + + provisioner "file" { + destination = "${path.root}/../../image-output/" + direction = "download" + source = "${local.image_folder}/output/" + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = ["${path.root}/../scripts/build/configure-hostname.sh"] + } +} diff --git a/images/macos/templates/macOS-14.anka.pkr.hcl b/images/macos/templates/macOS-14.anka.pkr.hcl new file mode 100644 index 0000000..a29cd92 --- /dev/null +++ b/images/macos/templates/macOS-14.anka.pkr.hcl @@ -0,0 +1,300 @@ +packer { + required_plugins { + veertu-anka = { + version = ">= v3.2.0" + source = "github.com/veertuinc/veertu-anka" + } + } +} + +locals { + image_folder = "/Users/${var.vm_username}/image-generation" +} + +variable "builder_type" { + type = string + default = "veertu-anka-vm-clone" + validation { + condition = contains(["veertu-anka-vm-clone", "null"], var.builder_type) + error_message = "The builder_type value must be one of [veertu-anka-vm-clone, null]." + } +} + +variable "source_vm_name" { + type = string +} + +variable "source_vm_port" { + type = number + default = 22 +} + +variable "source_vm_tag" { + type = string + default = "" +} + +variable "socks_proxy" { + type = string + default = "" +} + +variable "build_id" { + type = string +} + +variable "vm_username" { + type = string + sensitive = true +} + +variable "vm_password" { + type = string + sensitive = true +} + +variable "github_api_pat" { + type = string + sensitive = true + default = "" +} + +variable "xcode_install_storage_url" { + type = string + sensitive = true +} + +variable "xcode_install_sas" { + type = string + sensitive = true +} + +variable "vcpu_count" { + type = string + default = "6" +} + +variable "ram_size" { + type = string + default = "24G" +} + +variable "image_os" { + type = string + default = "macos14" +} + +source "veertu-anka-vm-clone" "template" { + vm_name = "${var.build_id}" + source_vm_name = "${var.source_vm_name}" + source_vm_tag = "${var.source_vm_tag}" + vcpu_count = "${var.vcpu_count}" + ram_size = "${var.ram_size}" + stop_vm = "true" +} + +source "null" "template" { + ssh_host = "${var.source_vm_name}" + ssh_port = "${var.source_vm_port}" + ssh_username = "${var.vm_username}" + ssh_password = "${var.vm_password}" + ssh_proxy_host = "${var.socks_proxy}" +} + +build { + sources = ["source.${var.builder_type}.template"] + + provisioner "shell" { + inline = ["mkdir ${local.image_folder}"] + } + + provisioner "file" { + destination = "${local.image_folder}/" + sources = [ + "${path.root}/../assets/xamarin-selector", + "${path.root}/../scripts/tests", + "${path.root}/../scripts/docs-gen", + "${path.root}/../scripts/helpers" + ] + } + + provisioner "file" { + destination = "${local.image_folder}/docs-gen/" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "file" { + destination = "${local.image_folder}/add-certificate.swift" + source = "${path.root}/../assets/add-certificate.swift" + } + + provisioner "file" { + destination = ".bashrc" + source = "${path.root}/../assets/bashrc" + } + + provisioner "file" { + destination = ".bash_profile" + source = "${path.root}/../assets/bashprofile" + } + + provisioner "shell" { + inline = ["mkdir ~/bootstrap"] + } + + provisioner "file" { + destination = "bootstrap" + source = "${path.root}/../assets/bootstrap-provisioner/" + } + + provisioner "file" { + destination = "${local.image_folder}/toolset.json" + source = "${path.root}/../toolsets/toolset-14.json" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report", + "mv ${local.image_folder}/xamarin-selector ${local.image_folder}/assets", + "mkdir ~/utils", + "mv ${local.image_folder}/helpers/confirm-identified-developers-macos14.scpt ~/utils", + "mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils", + "mv ${local.image_folder}/helpers/utils.sh ~/utils", + "mv ${local.image_folder}/helpers/xamarin-utils.sh ~/utils" + ] + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-xcode-clt.sh", + "${path.root}/../scripts/build/install-homebrew.sh" + ] + } + + provisioner "shell" { + environment_vars = ["PASSWORD=${var.vm_password}", "USERNAME=${var.vm_username}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-tccdb-macos.sh", + "${path.root}/../scripts/build/configure-autologin.sh", + "${path.root}/../scripts/build/configure-auto-updates.sh", + "${path.root}/../scripts/build/configure-ntpconf.sh", + "${path.root}/../scripts/build/configure-shell.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.build_id}", "IMAGE_OS=${var.image_os}", "PASSWORD=${var.vm_password}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-preimagedata.sh", + "${path.root}/../scripts/build/configure-ssh.sh", + "${path.root}/../scripts/build/configure-machine.sh" + ] + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "USER_PASSWORD=${var.vm_password}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + pause_before = "30s" + scripts = [ + "${path.root}/../scripts/build/configure-windows.sh", + "${path.root}/../scripts/build/install-powershell.sh", + "${path.root}/../scripts/build/install-mono.sh", + "${path.root}/../scripts/build/install-dotnet.sh", + "${path.root}/../scripts/build/install-python.sh", + "${path.root}/../scripts/build/install-azcopy.sh", + "${path.root}/../scripts/build/install-openssl.sh", + "${path.root}/../scripts/build/install-ruby.sh", + "${path.root}/../scripts/build/install-rubygems.sh", + "${path.root}/../scripts/build/install-git.sh", + "${path.root}/../scripts/build/install-node.sh", + "${path.root}/../scripts/build/install-common-utils.sh" + ] + } + + provisioner "shell" { + environment_vars = ["XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}", "XCODE_INSTALL_SAS=${var.xcode_install_sas}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Install-Xcode.ps1" + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-actions-cache.sh", + "${path.root}/../scripts/build/install-llvm.sh", + "${path.root}/../scripts/build/install-swiftlint.sh", + "${path.root}/../scripts/build/install-openjdk.sh", + "${path.root}/../scripts/build/install-php.sh", + "${path.root}/../scripts/build/install-aws-tools.sh", + "${path.root}/../scripts/build/install-rust.sh", + "${path.root}/../scripts/build/install-gcc.sh", + "${path.root}/../scripts/build/install-cocoapods.sh", + "${path.root}/../scripts/build/install-android-sdk.sh", + "${path.root}/../scripts/build/install-safari.sh", + "${path.root}/../scripts/build/install-chrome.sh", + "${path.root}/../scripts/build/install-edge.sh", + "${path.root}/../scripts/build/install-firefox.sh", + "${path.root}/../scripts/build/install-bicep.sh", + "${path.root}/../scripts/build/install-codeql-bundle.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/Install-Toolset.ps1", + "${path.root}/../scripts/build/Configure-Toolset.ps1" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1" + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + inline = [ + "pwsh -File \"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\" -OutputDirectory \"${local.image_folder}/output/software-report\" -ImageName ${var.build_id}", + "pwsh -File \"${local.image_folder}/tests/RunAll-Tests.ps1\"" + ] + } + + provisioner "file" { + destination = "${path.root}/../../image-output/" + direction = "download" + source = "${local.image_folder}/output/" + } + + provisioner "shell" { + inline = ["rm -rf \"$(brew --cache)\""] + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-hostname.sh", + "${path.root}/../scripts/build/configure-system.sh" + ] + } +} diff --git a/images/macos/templates/macOS-14.arm64.anka.pkr.hcl b/images/macos/templates/macOS-14.arm64.anka.pkr.hcl new file mode 100644 index 0000000..be90e10 --- /dev/null +++ b/images/macos/templates/macOS-14.arm64.anka.pkr.hcl @@ -0,0 +1,291 @@ +packer { + required_plugins { + veertu-anka = { + version = ">= v3.2.0" + source = "github.com/veertuinc/veertu-anka" + } + } +} + +locals { + image_folder = "/Users/${var.vm_username}/image-generation" +} + +variable "builder_type" { + type = string + default = "veertu-anka-vm-clone" + validation { + condition = contains(["veertu-anka-vm-clone", "null"], var.builder_type) + error_message = "The builder_type value must be one of [veertu-anka-vm-clone, null]." + } +} + +variable "source_vm_name" { + type = string +} + +variable "source_vm_port" { + type = number + default = 22 +} + +variable "source_vm_tag" { + type = string + default = "" +} + +variable "socks_proxy" { + type = string + default = "" +} + +variable "build_id" { + type = string +} + +variable "vm_username" { + type = string + sensitive = true +} + +variable "vm_password" { + type = string + sensitive = true +} + +variable "github_api_pat" { + type = string + sensitive = true + default = "" +} + +variable "xcode_install_storage_url" { + type = string + sensitive = true +} + +variable "xcode_install_sas" { + type = string + sensitive = true +} + +variable "vcpu_count" { + type = string + default = "6" +} + +variable "ram_size" { + type = string + default = "8G" +} + +variable "image_os" { + type = string + default = "macos14" +} + +source "veertu-anka-vm-clone" "template" { + vm_name = "${var.build_id}" + source_vm_name = "${var.source_vm_name}" + source_vm_tag = "${var.source_vm_tag}" + vcpu_count = "${var.vcpu_count}" + ram_size = "${var.ram_size}" + stop_vm = "true" + log_level = "debug" +} + +source "null" "template" { + ssh_host = "${var.source_vm_name}" + ssh_port = "${var.source_vm_port}" + ssh_username = "${var.vm_username}" + ssh_password = "${var.vm_password}" + ssh_proxy_host = "${var.socks_proxy}" +} + +build { + sources = ["source.${var.builder_type}.template"] + + provisioner "shell" { + inline = ["mkdir ${local.image_folder}"] + } + + provisioner "file" { + destination = "${local.image_folder}/" + sources = [ + "${path.root}/../assets/xamarin-selector", + "${path.root}/../scripts/tests", + "${path.root}/../scripts/docs-gen", + "${path.root}/../scripts/helpers" + ] + } + + provisioner "file" { + destination = "${local.image_folder}/docs-gen/" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "file" { + destination = "${local.image_folder}/add-certificate.swift" + source = "${path.root}/../assets/add-certificate.swift" + } + + provisioner "file" { + destination = ".bashrc" + source = "${path.root}/../assets/bashrc" + } + + provisioner "file" { + destination = ".bash_profile" + source = "${path.root}/../assets/bashprofile" + } + + provisioner "shell" { + inline = ["mkdir ~/bootstrap"] + } + + provisioner "file" { + destination = "bootstrap" + source = "${path.root}/../assets/bootstrap-provisioner/" + } + + provisioner "file" { + destination = "${local.image_folder}/toolset.json" + source = "${path.root}/../toolsets/toolset-14.json" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report", + "mv ${local.image_folder}/xamarin-selector ${local.image_folder}/assets", + "mkdir ~/utils", + "mv ${local.image_folder}/helpers/confirm-identified-developers.scpt ~/utils", + "mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils", + "mv ${local.image_folder}/helpers/utils.sh ~/utils", + "mv ${local.image_folder}/helpers/xamarin-utils.sh ~/utils" + ] + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-xcode-clt.sh", + "${path.root}/../scripts/build/install-homebrew.sh", + "${path.root}/../scripts/build/install-rosetta.sh" + ] + } + + provisioner "shell" { + environment_vars = ["PASSWORD=${var.vm_password}", "USERNAME=${var.vm_username}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-tccdb-macos.sh", + "${path.root}/../scripts/build/configure-autologin.sh", + "${path.root}/../scripts/build/configure-auto-updates.sh", + "${path.root}/../scripts/build/configure-ntpconf.sh", + "${path.root}/../scripts/build/configure-shell.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.build_id}", "IMAGE_OS=${var.image_os}", "PASSWORD=${var.vm_password}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-preimagedata.sh", + "${path.root}/../scripts/build/configure-ssh.sh", + "${path.root}/../scripts/build/configure-machine.sh" + ] + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "USER_PASSWORD=${var.vm_password}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + pause_before = "30s" + scripts = [ + "${path.root}/../scripts/build/configure-windows.sh", + "${path.root}/../scripts/build/install-powershell.sh", + "${path.root}/../scripts/build/install-mono.sh", + "${path.root}/../scripts/build/install-dotnet.sh", + "${path.root}/../scripts/build/install-python.sh", + "${path.root}/../scripts/build/install-azcopy.sh", + "${path.root}/../scripts/build/install-openssl.sh", + "${path.root}/../scripts/build/install-ruby.sh", + "${path.root}/../scripts/build/install-rubygems.sh", + "${path.root}/../scripts/build/install-git.sh", + "${path.root}/../scripts/build/install-node.sh", + "${path.root}/../scripts/build/install-common-utils.sh" + ] + } + + provisioner "shell" { + environment_vars = ["XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}", "XCODE_INSTALL_SAS=${var.xcode_install_sas}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Install-Xcode.ps1" + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-actions-cache.sh", + "${path.root}/../scripts/build/install-llvm.sh", + "${path.root}/../scripts/build/install-openjdk.sh", + "${path.root}/../scripts/build/install-aws-tools.sh", + "${path.root}/../scripts/build/install-rust.sh", + "${path.root}/../scripts/build/install-gcc.sh", + "${path.root}/../scripts/build/install-cocoapods.sh", + "${path.root}/../scripts/build/install-android-sdk.sh", + "${path.root}/../scripts/build/install-safari.sh", + "${path.root}/../scripts/build/install-chrome.sh", + "${path.root}/../scripts/build/install-bicep.sh", + "${path.root}/../scripts/build/install-codeql-bundle.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/Install-Toolset.ps1", + "${path.root}/../scripts/build/Configure-Toolset.ps1" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1" + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + inline = [ + "pwsh -File \"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\" -OutputDirectory \"${local.image_folder}/output/software-report\" -ImageName ${var.build_id}", + "pwsh -File \"${local.image_folder}/tests/RunAll-Tests.ps1\"" + ] + } + + provisioner "file" { + destination = "${path.root}/../../image-output/" + direction = "download" + source = "${local.image_folder}/output/" + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = ["${path.root}/../scripts/build/configure-hostname.sh"] + } +} diff --git a/images/macos/templates/macOS-15.anka.pkr.hcl b/images/macos/templates/macOS-15.anka.pkr.hcl new file mode 100644 index 0000000..6e93ecc --- /dev/null +++ b/images/macos/templates/macOS-15.anka.pkr.hcl @@ -0,0 +1,295 @@ +packer { + required_plugins { + veertu-anka = { + version = ">= v3.2.0" + source = "github.com/veertuinc/veertu-anka" + } + } +} + +locals { + image_folder = "/Users/${var.vm_username}/image-generation" +} + +variable "builder_type" { + type = string + default = "veertu-anka-vm-clone" + validation { + condition = contains(["veertu-anka-vm-clone", "null"], var.builder_type) + error_message = "The builder_type value must be one of [veertu-anka-vm-clone, null]." + } +} + +variable "source_vm_name" { + type = string +} + +variable "source_vm_port" { + type = number + default = 22 +} + +variable "source_vm_tag" { + type = string + default = "" +} + +variable "socks_proxy" { + type = string + default = "" +} + +variable "build_id" { + type = string +} + +variable "vm_username" { + type = string + sensitive = true +} + +variable "vm_password" { + type = string + sensitive = true +} + +variable "github_api_pat" { + type = string + sensitive = true + default = "" +} + +variable "xcode_install_storage_url" { + type = string + sensitive = true +} + +variable "xcode_install_sas" { + type = string + sensitive = true +} + +variable "vcpu_count" { + type = string + default = "6" +} + +variable "ram_size" { + type = string + default = "8G" +} + +variable "image_os" { + type = string + default = "macos15" +} + +source "veertu-anka-vm-clone" "template" { + vm_name = "${var.build_id}" + source_vm_name = "${var.source_vm_name}" + source_vm_tag = "${var.source_vm_tag}" + vcpu_count = "${var.vcpu_count}" + ram_size = "${var.ram_size}" + stop_vm = "true" +} + +source "null" "template" { + ssh_host = "${var.source_vm_name}" + ssh_port = "${var.source_vm_port}" + ssh_username = "${var.vm_username}" + ssh_password = "${var.vm_password}" + ssh_proxy_host = "${var.socks_proxy}" +} + +build { + sources = ["source.${var.builder_type}.template"] + + provisioner "shell" { + inline = ["mkdir ${local.image_folder}"] + } + + provisioner "file" { + destination = "${local.image_folder}/" + sources = [ + "${path.root}/../scripts/tests", + "${path.root}/../scripts/docs-gen", + "${path.root}/../scripts/helpers" + ] + } + + provisioner "file" { + destination = "${local.image_folder}/docs-gen/" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "file" { + destination = "${local.image_folder}/add-certificate.swift" + source = "${path.root}/../assets/add-certificate.swift" + } + + provisioner "file" { + destination = ".bashrc" + source = "${path.root}/../assets/bashrc" + } + + provisioner "file" { + destination = ".bash_profile" + source = "${path.root}/../assets/bashprofile" + } + + provisioner "shell" { + inline = ["mkdir ~/bootstrap"] + } + + provisioner "file" { + destination = "bootstrap" + source = "${path.root}/../assets/bootstrap-provisioner/" + } + + provisioner "file" { + destination = "${local.image_folder}/toolset.json" + source = "${path.root}/../toolsets/toolset-15.json" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report", + "mkdir ~/utils", + "mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils", + "mv ${local.image_folder}/helpers/utils.sh ~/utils" + ] + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-xcode-clt.sh", + "${path.root}/../scripts/build/install-homebrew.sh" + ] + } + + provisioner "shell" { + environment_vars = ["PASSWORD=${var.vm_password}", "USERNAME=${var.vm_username}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-tccdb-macos.sh", + "${path.root}/../scripts/build/configure-autologin.sh", + "${path.root}/../scripts/build/configure-auto-updates.sh", + "${path.root}/../scripts/build/configure-ntpconf.sh", + "${path.root}/../scripts/build/configure-shell.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.build_id}", "IMAGE_OS=${var.image_os}", "PASSWORD=${var.vm_password}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-preimagedata.sh", + "${path.root}/../scripts/build/configure-ssh.sh", + "${path.root}/../scripts/build/configure-machine.sh" + ] + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "USER_PASSWORD=${var.vm_password}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + pause_before = "30s" + scripts = [ + "${path.root}/../scripts/build/configure-windows.sh", + "${path.root}/../scripts/build/install-powershell.sh", + "${path.root}/../scripts/build/install-dotnet.sh", + "${path.root}/../scripts/build/install-python.sh", + "${path.root}/../scripts/build/install-azcopy.sh", + "${path.root}/../scripts/build/install-openssl.sh", + "${path.root}/../scripts/build/install-ruby.sh", + "${path.root}/../scripts/build/install-rubygems.sh", + "${path.root}/../scripts/build/install-git.sh", + "${path.root}/../scripts/build/install-node.sh", + "${path.root}/../scripts/build/install-common-utils.sh" + ] + } + + provisioner "shell" { + environment_vars = ["XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}", "XCODE_INSTALL_SAS=${var.xcode_install_sas}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Install-Xcode.ps1" + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-actions-cache.sh", + "${path.root}/../scripts/build/install-llvm.sh", + "${path.root}/../scripts/build/install-swiftlint.sh", + "${path.root}/../scripts/build/install-openjdk.sh", + "${path.root}/../scripts/build/install-php.sh", + "${path.root}/../scripts/build/install-aws-tools.sh", + "${path.root}/../scripts/build/install-rust.sh", + "${path.root}/../scripts/build/install-gcc.sh", + "${path.root}/../scripts/build/install-cocoapods.sh", + "${path.root}/../scripts/build/install-android-sdk.sh", + "${path.root}/../scripts/build/install-safari.sh", + "${path.root}/../scripts/build/install-chrome.sh", + "${path.root}/../scripts/build/install-edge.sh", + "${path.root}/../scripts/build/install-firefox.sh", + "${path.root}/../scripts/build/install-bicep.sh", + "${path.root}/../scripts/build/install-codeql-bundle.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/Install-Toolset.ps1", + "${path.root}/../scripts/build/Configure-Toolset.ps1" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1" + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + inline = [ + "pwsh -File \"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\" -OutputDirectory \"${local.image_folder}/output/software-report\" -ImageName ${var.build_id}", + "pwsh -File \"${local.image_folder}/tests/RunAll-Tests.ps1\"" + ] + } + + provisioner "file" { + destination = "${path.root}/../../image-output/" + direction = "download" + source = "${local.image_folder}/output/" + } + + provisioner "shell" { + inline = ["rm -rf \"$(brew --cache)\""] + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-hostname.sh", + "${path.root}/../scripts/build/configure-system.sh" + ] + } +} diff --git a/images/macos/templates/macOS-15.arm64.anka.pkr.hcl b/images/macos/templates/macOS-15.arm64.anka.pkr.hcl new file mode 100644 index 0000000..8e72eac --- /dev/null +++ b/images/macos/templates/macOS-15.arm64.anka.pkr.hcl @@ -0,0 +1,286 @@ +packer { + required_plugins { + veertu-anka = { + version = ">= v3.2.0" + source = "github.com/veertuinc/veertu-anka" + } + } +} + +locals { + image_folder = "/Users/${var.vm_username}/image-generation" +} + +variable "builder_type" { + type = string + default = "veertu-anka-vm-clone" + validation { + condition = contains(["veertu-anka-vm-clone", "null"], var.builder_type) + error_message = "The builder_type value must be one of [veertu-anka-vm-clone, null]." + } +} + +variable "source_vm_name" { + type = string +} + +variable "source_vm_port" { + type = number + default = 22 +} + +variable "source_vm_tag" { + type = string + default = "" +} + +variable "socks_proxy" { + type = string + default = "" +} + +variable "build_id" { + type = string +} + +variable "vm_username" { + type = string + sensitive = true +} + +variable "vm_password" { + type = string + sensitive = true +} + +variable "github_api_pat" { + type = string + sensitive = true + default = "" +} + +variable "xcode_install_storage_url" { + type = string + sensitive = true +} + +variable "xcode_install_sas" { + type = string + sensitive = true +} + +variable "vcpu_count" { + type = string + default = "6" +} + +variable "ram_size" { + type = string + default = "8G" +} + +variable "image_os" { + type = string + default = "macos15" +} + +source "veertu-anka-vm-clone" "template" { + vm_name = "${var.build_id}" + source_vm_name = "${var.source_vm_name}" + source_vm_tag = "${var.source_vm_tag}" + vcpu_count = "${var.vcpu_count}" + ram_size = "${var.ram_size}" + stop_vm = "true" + log_level = "debug" +} + +source "null" "template" { + ssh_host = "${var.source_vm_name}" + ssh_port = "${var.source_vm_port}" + ssh_username = "${var.vm_username}" + ssh_password = "${var.vm_password}" + ssh_proxy_host = "${var.socks_proxy}" +} + +build { + sources = ["source.${var.builder_type}.template"] + + provisioner "shell" { + inline = ["mkdir ${local.image_folder}"] + } + + provisioner "file" { + destination = "${local.image_folder}/" + sources = [ + "${path.root}/../scripts/tests", + "${path.root}/../scripts/docs-gen", + "${path.root}/../scripts/helpers" + ] + } + + provisioner "file" { + destination = "${local.image_folder}/docs-gen/" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "file" { + destination = "${local.image_folder}/add-certificate.swift" + source = "${path.root}/../assets/add-certificate.swift" + } + + provisioner "file" { + destination = ".bashrc" + source = "${path.root}/../assets/bashrc" + } + + provisioner "file" { + destination = ".bash_profile" + source = "${path.root}/../assets/bashprofile" + } + + provisioner "shell" { + inline = ["mkdir ~/bootstrap"] + } + + provisioner "file" { + destination = "bootstrap" + source = "${path.root}/../assets/bootstrap-provisioner/" + } + + provisioner "file" { + destination = "${local.image_folder}/toolset.json" + source = "${path.root}/../toolsets/toolset-15.json" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "mv ${local.image_folder}/docs-gen ${local.image_folder}/software-report", + "mkdir ~/utils", + "mv ${local.image_folder}/helpers/invoke-tests.sh ~/utils", + "mv ${local.image_folder}/helpers/utils.sh ~/utils" + ] + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-xcode-clt.sh", + "${path.root}/../scripts/build/install-homebrew.sh", + "${path.root}/../scripts/build/install-rosetta.sh" + ] + } + + provisioner "shell" { + environment_vars = ["PASSWORD=${var.vm_password}", "USERNAME=${var.vm_username}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-tccdb-macos.sh", + "${path.root}/../scripts/build/configure-autologin.sh", + "${path.root}/../scripts/build/configure-auto-updates.sh", + "${path.root}/../scripts/build/configure-ntpconf.sh", + "${path.root}/../scripts/build/configure-shell.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.build_id}", "IMAGE_OS=${var.image_os}", "PASSWORD=${var.vm_password}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/configure-preimagedata.sh", + "${path.root}/../scripts/build/configure-ssh.sh", + "${path.root}/../scripts/build/configure-machine.sh" + ] + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "USER_PASSWORD=${var.vm_password}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + pause_before = "30s" + scripts = [ + "${path.root}/../scripts/build/configure-windows.sh", + "${path.root}/../scripts/build/install-powershell.sh", + "${path.root}/../scripts/build/install-dotnet.sh", + "${path.root}/../scripts/build/install-python.sh", + "${path.root}/../scripts/build/install-azcopy.sh", + "${path.root}/../scripts/build/install-openssl.sh", + "${path.root}/../scripts/build/install-ruby.sh", + "${path.root}/../scripts/build/install-rubygems.sh", + "${path.root}/../scripts/build/install-git.sh", + "${path.root}/../scripts/build/install-node.sh", + "${path.root}/../scripts/build/install-common-utils.sh" + ] + } + + provisioner "shell" { + environment_vars = ["XCODE_INSTALL_STORAGE_URL=${var.xcode_install_storage_url}", "XCODE_INSTALL_SAS=${var.xcode_install_sas}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Install-Xcode.ps1" + } + + provisioner "shell" { + execute_command = "source $HOME/.bash_profile; sudo {{ .Vars }} {{ .Path }}" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "shutdown -r now"] + } + + provisioner "shell" { + environment_vars = ["API_PAT=${var.github_api_pat}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/install-actions-cache.sh", + "${path.root}/../scripts/build/install-llvm.sh", + "${path.root}/../scripts/build/install-openjdk.sh", + "${path.root}/../scripts/build/install-aws-tools.sh", + "${path.root}/../scripts/build/install-rust.sh", + "${path.root}/../scripts/build/install-gcc.sh", + "${path.root}/../scripts/build/install-cocoapods.sh", + "${path.root}/../scripts/build/install-android-sdk.sh", + "${path.root}/../scripts/build/install-safari.sh", + "${path.root}/../scripts/build/install-chrome.sh", + "${path.root}/../scripts/build/install-bicep.sh", + "${path.root}/../scripts/build/install-codeql-bundle.sh" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + scripts = [ + "${path.root}/../scripts/build/Install-Toolset.ps1", + "${path.root}/../scripts/build/Configure-Toolset.ps1" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} pwsh -f {{ .Path }}" + script = "${path.root}/../scripts/build/Configure-Xcode-Simulators.ps1" + } + + provisioner "shell" { + environment_vars = ["IMAGE_FOLDER=${local.image_folder}"] + execute_command = "source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + inline = [ + "pwsh -File \"${local.image_folder}/software-report/Generate-SoftwareReport.ps1\" -OutputDirectory \"${local.image_folder}/output/software-report\" -ImageName ${var.build_id}", + "pwsh -File \"${local.image_folder}/tests/RunAll-Tests.ps1\"" + ] + } + + provisioner "file" { + destination = "${path.root}/../../image-output/" + direction = "download" + source = "${local.image_folder}/output/" + } + + provisioner "shell" { + execute_command = "chmod +x {{ .Path }}; source $HOME/.bash_profile; {{ .Vars }} {{ .Path }}" + scripts = ["${path.root}/../scripts/build/configure-hostname.sh"] + } +} diff --git a/images/macos/toolsets/Readme.md b/images/macos/toolsets/Readme.md new file mode 100644 index 0000000..7eb63ca --- /dev/null +++ b/images/macos/toolsets/Readme.md @@ -0,0 +1,105 @@ +# Toolset JSON structure + +## Xcode + +- `versions` - the array of objects that will present installed Xcode versions + - `link` property points to the place where Xcode will be located on image. `/Applications/Xcode_.app` + - `version` points to Xcode version that will be downloaded and installed + - `symlinks` describes the list of aliases where symlinks will be created to + - `install_runtimes` is boolean function to control over the related simulator runtimes + - `sha256` used to check integrity of the Xcode installer file +- `default` - version of Xcode to set as default (should be metched with any `link` in `versions` property) + **Example:** `"11.2"` + +**Note:** + +- If `version` is specified with spaces, it is considered as exact Xcode name like `12.1.1 Release Candidate`. +- If `version` doesn't contain spaces, the latest version will be resolved with the following priority: + - stable version like `12.1` + - release candidate version like `12.1 Release Candidate N` (the latest N will be chosen) + - GM version like `12.1 GM Seed N` (the latest N will be chosen) + - beta version like `12.1 beta N` (the latest N will be chosen) + +**Example:** + +```json +"versions": [ + { "link": "16_beta_4", "version": "16.0.0-Beta.4+16A5211f", "symlinks": ["16.0"], "install_runtimes": "false", "sha256": "4270cd8021b2f7f512ce91bfc4423b25bccab36cdab21834709d798c8daade72"}, + { "link": "15.4", "version": "15.4.0+15F31d", "install_runtimes": "true", "sha256": "82d3d61804ff3f4c7c82085e91dc701037ddaa770e542848b2477e22f4e8aa7a"} +] +``` + +## Xamarin + +- `vsmac` - version of Visual Studio For Mac to install. + **Example:** `"8.3.11.1"` + +- `mono_versions` - the array of Mono versions to install. + **Example:** `[ "6.4.0.208", "6.0.0.334" ]` + +- `ios_versions` - the array of Xamarin iOS versions to install. + **Example:** `[ "13.6.0.12", "13.4.0.2", "13.2.0.47" ]` + +- `mac_versions` - the array of Xamarin iOS versions to install. + **Example:** `[ "6.6.0.12", "6.4.0.2", "6.2.0.47" ]` + +- `android_versions` - the array of Xamarin iOS versions to install. + **Example:** `[ "10.0.6.2", "9.4.1.0" ]` + +**Note:** More than one version of SDK with the same major.minor version should not be installed. It applies to `mono_versions`, `ios_versions`, `mac_versions`, `android_versions` fields. +For example, if Mono `6.4.0.100` is installed and Mono `6.4.1.2` was released recently, we should not install both, just update `6.4.0.100` -> `6.4.1.2`. Only major and minor version changes can break backward compatibility so it is safe. + +- `bundle_default` - the symlink of the bundle that will be set as default on the image. +This bundle will be set as `Current`. + **Example:** `"5_12_0"` (set bundle with symlink `5_12_0` as default) + **Example:** `"latest"` (set latest bundle as default) + +- `bundles` - the array of objects that describe bundles that will be created on image after sdk installation. +The first bundle in the list will be as `Latest`. + + Each object should contain the following fields: + - `symlink` - unique id of the bundle (usually looks like "__") + - `mono` - version of Mono that will be set in this bundle. Only two numbers (`major.minor`) should be specified. + - `ios` - version of Xamarin.iOS that will be set in this bundle. Only two numbers (`major.minor`) should be specified. + - `mac` - version of Xamarin.Mac that will be set in this bundle. Only two numbers (`major.minor`) should be specified. + - `android` - version of Xamarin.Android that will be set in this bundle. Only two numbers (`major.minor`) should be specified. + +**Example:** + +```json + [ + { + "symlink": "6_4_2", + "mono": "6.4", + "ios": "13.6", + "mac": "6.6", + "android": "10.0" + }, + { + "symlink": "6_4_1", + "mono": "6.4", + "ios": "13.4", + "mac": "6.4", + "android": "10.0" + } + ] +``` + +## Android + +- `platform-list` - the array of android platforms to install. + **Example:** `[ "android-29", "android-28", "android-27" ]` + +- `build-tools` - the array of android build tools to install. + **Example:** `[ "29.0.2", "29.0.1", "29.0.0", "28.0.3" ]` + +- `extras` - the array of android extra items to install. + **Example:** `[ "google;google_play_services", "intel;Hardware_Accelerated_Execution_Manager" ]` + +- `addons` - the array of android addons to install. + **Example:** `[ "addon-google_apis-google-24", "addon-google_apis-google-23" ]` + +## Toolset JSON validation + +File `Toolset.Tests.ps1` contains PowerShell [Pester](https://github.com/Pester/Pester) tests that validate JSON toolset files. +Type `Invoke-Pester` in the current folder in PowerShell to run tests. diff --git a/images/macos/toolsets/toolset-12.json b/images/macos/toolsets/toolset-12.json new file mode 100644 index 0000000..e63383d --- /dev/null +++ b/images/macos/toolsets/toolset-12.json @@ -0,0 +1,368 @@ +{ + "xcode": { + "default": "14.2", + "x64": { + "versions": [ + { "link": "14.2", "version": "14.2.0+14C18", "install_runtimes": "true", "sha256": "686B9D53CA49E50D563BC0104B1E8B4F7CCFE80064A6D689965FB819BF8EFE72" }, + { "link": "14.1", "version": "14.1.0+14B47b", "install_runtimes": "true", "sha256": "12F8A3AEF78BF354470AD8B351ADDD925C8EDAD888137D138CA50A8130EB9F2F" }, + { "link": "14.0.1", "version": "14.0.1+14A400", "symlinks": ["14.0"], "install_runtimes": "true", "sha256": "EDB4DDCE02F92338E3D10B011FC86CD26520E3238585F06F3C182880DDD3B2AF" }, + { "link": "13.4.1", "version": "13.4.1+13F100", "symlinks": ["13.4"], "sha256": "A1E0DBD6D5A96C4A6D3D63600B58486759AA836C2D9F7E8FA6D7DA4C7399638B" }, + { "link": "13.3.1", "version": "13.3.1+13E500a", "symlinks": ["13.3"], "sha256": "D10B4644DB84BA43F7B18CE94FB3CA1ACD255D39781F4AF8FC88BD8581E08F97" }, + { "link": "13.2.1", "version": "13.2.1+13C100", "symlinks": ["13.2"], "sha256": "D3BFCC6225D531587490C0DFC0926C80B7D50D17671DC8F25868F965F5D65F9D" }, + { "link": "13.1", "version": "13.1.0+13A1030d", "sha256": "4EFDEEA0EEEDA1957BB394128CCCD1DAAC3CB0A3D074224E0FAB90855CCA09C4" } + ] + } + }, + "xamarin": { + "vsmac": { + "default": "2022", + "versions": [ "2019", "2022" ] + }, + "mono_versions": [ + "6.12.0.188" + ], + "ios_versions": [ + "16.4.0.23", "16.2.0.5", "16.1.1.27", "16.0.0.72", "15.12.0.2", "15.10.0.5", "15.8.0.3", "15.6.0.3", "15.4.0.0", "15.2.0.17", "15.0.0.6" + ], + "mac_versions": [ + "9.3.0.23", "9.1.0.5", "9.0.0.27", "8.12.0.2", "8.10.0.5", "8.8.0.3", "8.6.0.3", "8.4.0.0", "8.2.0.17", "7.14.0.27" + ], + "android_versions": [ + "13.2.2.0", "13.1.0.1", "13.0.0.0", "12.3.3.3", "12.2.8.3", "12.1.0.2", "12.0.0.3", "11.3.0.4" + ], + "bundle_default": "6_12_21", + "bundles": [ + { + "symlink": "6_12_25", + "mono":"6.12", + "ios": "16.4", + "mac": "9.3", + "android": "13.2" + }, + { + "symlink": "6_12_24", + "mono":"6.12", + "ios": "16.2", + "mac": "9.1", + "android": "13.2" + }, + { + "symlink": "6_12_23", + "mono":"6.12", + "ios": "16.2", + "mac": "9.1", + "android": "13.1" + }, + { + "symlink": "6_12_22", + "mono":"6.12", + "ios": "16.1", + "mac": "9.0", + "android": "13.1" + }, + { + "symlink": "6_12_21", + "mono":"6.12", + "ios": "16.0", + "mac": "8.12", + "android": "13.1" + }, + { + "symlink": "6_12_20", + "mono":"6.12", + "ios": "16.0", + "mac": "8.12", + "android": "13.0" + }, + { + "symlink": "6_12_19", + "mono":"6.12", + "ios": "15.12", + "mac": "8.12", + "android": "13.0" + }, + { + "symlink": "6_12_18", + "mono":"6.12", + "ios": "15.10", + "mac": "8.10", + "android": "12.3" + }, + { + "symlink": "6_12_17", + "mono":"6.12", + "ios": "15.10", + "mac": "8.10", + "android": "12.2" + }, + { + "symlink": "6_12_16", + "mono":"6.12", + "ios": "15.8", + "mac": "8.8", + "android": "12.2" + }, + { + "symlink": "6_12_15", + "mono":"6.12", + "ios": "15.8", + "mac": "8.8", + "android": "12.1" + }, + { + "symlink": "6_12_14", + "mono":"6.12", + "ios": "15.8", + "mac": "8.8", + "android": "12.0" + }, + { + "symlink": "6_12_13", + "mono":"6.12", + "ios": "15.6", + "mac": "8.6", + "android": "12.0" + }, + { + "symlink": "6_12_12", + "mono":"6.12", + "ios": "15.4", + "mac": "8.4", + "android": "12.0" + }, + { + "symlink": "6_12_11", + "mono":"6.12", + "ios": "15.2", + "mac": "8.2", + "android": "12.0" + }, + { + "symlink": "6_12_10", + "mono":"6.12", + "ios": "15.0", + "mac": "7.14", + "android": "11.3" + } + ] + }, + "java": { + "x64": { + "default": "8", + "versions": [ "8", "11", "17", "21"] + } + }, + "android": { + "cmdline-tools": "commandlinetools-mac-9123335_latest.zip", + "platform_min_version": "31", + "build_tools_min_version": "31.0.0", + "extras": [ + "android;m2repository", "google;m2repository", "google;google_play_services" + ], + "addons": [], + "additional_tools": [ + "cmake;3.18.1", + "cmake;3.22.1" + ], + "ndk": { + "default": "25", + "versions": [ + "24", "25", "26", "27" + ] + } + }, + "powershellModules": [ + { "name": "Az" }, + { "name": "MarkdownPS" }, + { "name": "Pester" }, + { "name": "PSScriptAnalyzer" } + ], + "npm": { + "global_packages": [ + { "name": "appcenter-cli", "test": "appcenter --version" } + ] + }, + "brew": { + "common_packages": [ + "aria2", + "azure-cli", + "bazelisk", + "carthage", + "cmake", + "colima", + "gh", + "gnupg", + "gnu-tar", + "libpq", + "p7zip", + "packer", + "perl", + "pkg-config", + "subversion", + "swiftformat", + "swig", + "zstd", + "gmp", + "zlib", + "libxext", + "libxft", + "tcl-tk", + "r", + "yq", + "imagemagick", + "unxip", + "xcbeautify", + "xcodes" + ], + "cask_packages": [ + "julia", + "vagrant", + "virtualbox", + "parallels" + ], + "compilable_packages": [ + "ant", + "kotlin", + "sbt" + ] + }, + "gcc": { + "versions": [ + "12", + "13", + "14" + ] + }, + "toolcache": [ + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "versions": [ + "3.7.*", + "3.8.*", + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*" + ] + } + } + }, + { + "name": "PyPy", + "platform" : "darwin", + "arch": { + "x64": { + "versions": [ + "2.7", + "3.7", + "3.8", + "3.9", + "3.10" + ] + } + } + }, + { + "name": "Node", + "url" : "https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "versions": [ + "16.*", + "18.*", + "20.*" + ] + } + } + }, + { + "name": "Go", + "url" : "https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "variable_template" : "GOROOT_{0}_{1}_X64", + "versions": [ + "1.20.*", + "1.21.*", + "1.22.*", + "1.23.*" + ] + } + } + }, + { + "name": "Ruby", + "arch": { + "x64": { + "versions": [ + "3.0.*", + "3.1.*", + "3.2.*", + "3.3.*" + ] + } + } + } + ], + "pipx": [ + { + "package": "yamllint", + "cmd": "yamllint --version" + } + ], + "dotnet": { + "arch":{ + "x64": { + "versions": [ + "6.0", + "7.0", + "8.0" + ] + } + } + }, + "ruby": { + "default": "3.0", + "rubygems": [ + "xcode-install", + "cocoapods", + "xcpretty", + "bundler", + "fastlane", + "jazzy" + ] + }, + "go": { + "default": "1.21" + }, + "node": { + "default": "18", + "nvm_installer": "0.39.7", + "nvm_versions": [ + "16", + "18", + "20" + ] + }, + "llvm": { + "version": "15" + }, + "php": { + "version": "8.3" + }, + "mongodb": { + "version": "5.0" + }, + "postgresql": { + "version": "14" + }, + "pwsh": { + "version": "7.4" + } +} diff --git a/images/macos/toolsets/toolset-13.json b/images/macos/toolsets/toolset-13.json new file mode 100644 index 0000000..9beae56 --- /dev/null +++ b/images/macos/toolsets/toolset-13.json @@ -0,0 +1,242 @@ +{ + "xcode": { + "default": "15.2", + "x64": { + "versions": [ + { "link": "15.2", "version": "15.2.0+15C500b", "install_runtimes": "true", "sha256": "04E93680C6DDBEC84666531BE412DE778AFC8EAC6AB2037F4C2BE7290818B59B"}, + { "link": "15.1", "version": "15.1.0+15C65", "install_runtimes": "true", "sha256": "857D8DB537BAC82BF99DE0E1D3895D214D4D02101C1340CEF3DAF6E821BA1D05"}, + { "link": "15.0.1", "version": "15.0.1+15A507", "symlinks": ["15.0"], "install_runtimes": "true", "sha256": "5AC17AE6060CAFC3C7112C6DA0B153450BE21F1DE6632777FBA9FBC9D999C9E8"}, + { "link": "14.3.1", "version": "14.3.1+14E300c","symlinks": ["14.3"], "install_runtimes": "true", "sha256": "B5CC7BF37447C32A971B37D71C7DA1AF7ABB45CEE4B96FE126A1D3B0D2C260AF"}, + { "link": "14.2", "version": "14.2.0+14C18", "install_runtimes": "true", "sha256": "686B9D53CA49E50D563BC0104B1E8B4F7CCFE80064A6D689965FB819BF8EFE72"}, + { "link": "14.1", "version": "14.1.0+14B47b", "install_runtimes": "true", "sha256": "12F8A3AEF78BF354470AD8B351ADDD925C8EDAD888137D138CA50A8130EB9F2F"} + ] + }, + "arm64":{ + "versions": [ + { "link": "15.2", "version": "15.2.0+15C500b", "install_runtimes": "true", "sha256": "04E93680C6DDBEC84666531BE412DE778AFC8EAC6AB2037F4C2BE7290818B59B"}, + { "link": "15.1", "version": "15.1.0+15C65", "install_runtimes": "true", "sha256": "857D8DB537BAC82BF99DE0E1D3895D214D4D02101C1340CEF3DAF6E821BA1D05"}, + { "link": "15.0.1", "version": "15.0.1+15A507", "symlinks": ["15.0"], "install_runtimes": "true", "sha256": "5AC17AE6060CAFC3C7112C6DA0B153450BE21F1DE6632777FBA9FBC9D999C9E8"}, + { "link": "14.3.1", "version": "14.3.1+14E300c","symlinks": ["14.3"], "install_runtimes": "true", "sha256": "B5CC7BF37447C32A971B37D71C7DA1AF7ABB45CEE4B96FE126A1D3B0D2C260AF"}, + { "link": "14.2", "version": "14.2.0+14C18", "install_runtimes": "true", "sha256": "686B9D53CA49E50D563BC0104B1E8B4F7CCFE80064A6D689965FB819BF8EFE72"}, + { "link": "14.1", "version": "14.1.0+14B47b", "install_runtimes": "true", "sha256": "12F8A3AEF78BF354470AD8B351ADDD925C8EDAD888137D138CA50A8130EB9F2F"} + ] + } + }, + "java": { + "x64": { + "default": "17", + "versions": [ "8", "11", "17", "21"] + }, + "arm64": { + "default": "17", + "versions": [ "11", "17", "21"] + } + }, + "android": { + "cmdline-tools": "commandlinetools-mac-10406996_latest.zip", + "sdk-tools": "sdk-tools-darwin-4333796.zip", + "platform_min_version": "33", + "build_tools_min_version": "33.0.2", + "extras": [ + "android;m2repository", "google;m2repository", "google;google_play_services" + ], + "addons": [], + "additional_tools": [ + "cmake;3.22.1" + ], + "ndk": { + "default": "26", + "versions": [ + "26", "27" + ] + } + }, + "powershellModules": [ + { "name": "Az" }, + { "name": "Pester" }, + { "name": "PSScriptAnalyzer" } + ], + "brew": { + "common_packages": [ + "ant", + "aria2", + "azure-cli", + "bazelisk", + "carthage", + "cmake", + "gh", + "gnupg", + "gnu-tar", + "kotlin", + "libpq", + "p7zip", + "packer", + "perl", + "pkg-config", + "swiftformat", + "tcl-tk", + "zstd", + "gmp", + "yq", + "unxip", + "xcbeautify", + "xcodes" + ], + "cask_packages": [ + "parallels" + ] + }, + "gcc": { + "versions": [ + "12", + "13", + "14" + ] + }, + "dotnet": { + "arch":{ + "x64": { + "versions": [ + "7.0", + "8.0" + ] + }, + "arm64": { + "versions": [ + "7.0", + "8.0" + ] + } + } + }, + "ruby": { + "default": "3.0", + "rubygems": [ + "cocoapods", + "bundler", + "fastlane" + ] + }, + "toolcache": [ + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "versions": [ + "3.8.*", + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*", + "3.13.*" + ] + }, + "arm64": { + "versions": [ + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*", + "3.13.*" + ] + } + } + }, + { + "name": "PyPy", + "platform" : "darwin", + "arch": { + "x64": { + "versions": [ + "2.7", + "3.7", + "3.8", + "3.9", + "3.10" + ] + } + } + }, + { + "name": "Node", + "url" : "https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "versions": [ + "16.*", + "18.*", + "20.*" + ] + }, + "arm64": { + "versions": [ + "16.*", + "18.*", + "20.*" + ] + } + } + }, + { + "name": "Go", + "url" : "https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "variable_template" : "GOROOT_{0}_{1}_X64", + "versions": [ + "1.21.*", + "1.22.*", + "1.23.*" + ] + }, + "arm64": { + "variable_template" : "GOROOT_{0}_{1}_ARM64", + "versions": [ + "1.21.*", + "1.22.*", + "1.23.*" + ] + } + } + }, + { + "name": "Ruby", + "arch": { + "x64": { + "versions": [ + "3.0.*", + "3.1.*", + "3.2.*", + "3.3.*" + ] + } + } + } + ], + "node": { + "default": "20" + }, + "llvm": { + "version": "15" + }, + "php": { + "version": "8.3" + }, + "mono": { + "framework":{ + "version": "6.12.0.188", + "sha256": "07cdd4e5e72b562892960b7fc73af470db7a4ffc2f68bb834eb3d0a874bbd12c" + }, + "nunit": { + "version": "3.15.4", + "sha256": "356dab61433b5be76b76fd0e2e979bda56d164f6d85a900e55c3a4a5fffa28de" + } + }, + "pwsh": { + "version": "7.4" + } +} diff --git a/images/macos/toolsets/toolset-14.json b/images/macos/toolsets/toolset-14.json new file mode 100644 index 0000000..f067e85 --- /dev/null +++ b/images/macos/toolsets/toolset-14.json @@ -0,0 +1,229 @@ +{ + "xcode": { + "default": "15.4", + "x64": { + "versions": [ + { "link": "16.1_beta_3", "version": "16.1.0-Beta.3+16B5029d", "symlinks": ["16.1"], "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "e92ffd3cf7bb3ecb830ba94e8b2cbeb62743a80c6d73a5ae52ae792e4a897e5f"}, + { "link": "16", "version": "16.0.0+16A242d", "symlinks": ["16.0"], "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "4a26c3d102a55c7222fb145e0ee1503249c9c26c6e02dc64d783c8810b37b1e3"}, + { "link": "15.4", "version": "15.4.0+15F31d", "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "82d3d61804ff3f4c7c82085e91dc701037ddaa770e542848b2477e22f4e8aa7a"}, + { "link": "15.3", "version": "15.3.0+15E204a", "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "f13f6a2e2df432c3008e394640b8549a18c285acd7fd148d6c4bac8c3a5af234"}, + { "link": "15.2", "version": "15.2.0+15C500b", "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "04E93680C6DDBEC84666531BE412DE778AFC8EAC6AB2037F4C2BE7290818B59B"}, + { "link": "15.1", "version": "15.1.0+15C65", "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "857D8DB537BAC82BF99DE0E1D3895D214D4D02101C1340CEF3DAF6E821BA1D05"}, + { "link": "15.0.1", "version": "15.0.1+15A507", "symlinks": ["15.0"], "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "5AC17AE6060CAFC3C7112C6DA0B153450BE21F1DE6632777FBA9FBC9D999C9E8"}, + { "link": "14.3.1", "version": "14.3.1+14E300c","symlinks": ["14.3"], "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "B5CC7BF37447C32A971B37D71C7DA1AF7ABB45CEE4B96FE126A1D3B0D2C260AF"} + ] + }, + "arm64":{ + "versions": [ + { "link": "16.1_beta_3", "version": "16.1.0-Beta.3+16B5029d", "symlinks": ["16.1"], "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "e92ffd3cf7bb3ecb830ba94e8b2cbeb62743a80c6d73a5ae52ae792e4a897e5f"}, + { "link": "16", "version": "16.0.0+16A242d", "symlinks": ["16.0"], "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "4a26c3d102a55c7222fb145e0ee1503249c9c26c6e02dc64d783c8810b37b1e3"}, + { "link": "15.4", "version": "15.4.0+15F31d", "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "82d3d61804ff3f4c7c82085e91dc701037ddaa770e542848b2477e22f4e8aa7a"}, + { "link": "15.3", "version": "15.3.0+15E204a", "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "f13f6a2e2df432c3008e394640b8549a18c285acd7fd148d6c4bac8c3a5af234"}, + { "link": "15.2", "version": "15.2.0+15C500b", "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "04E93680C6DDBEC84666531BE412DE778AFC8EAC6AB2037F4C2BE7290818B59B"}, + { "link": "15.1", "version": "15.1.0+15C65", "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "857D8DB537BAC82BF99DE0E1D3895D214D4D02101C1340CEF3DAF6E821BA1D05"}, + { "link": "15.0.1", "version": "15.0.1+15A507", "symlinks": ["15.0"], "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "5AC17AE6060CAFC3C7112C6DA0B153450BE21F1DE6632777FBA9FBC9D999C9E8"}, + { "link": "14.3.1", "version": "14.3.1+14E300c","symlinks": ["14.3"], "runtimes": ["iOS", "watchOS", "tvOS"], "sha256": "B5CC7BF37447C32A971B37D71C7DA1AF7ABB45CEE4B96FE126A1D3B0D2C260AF"} + ] + } + }, + "java": { + "x64": { + "default": "21", + "versions": [ "8", "11", "17", "21"] + }, + "arm64": { + "default": "21", + "versions": [ "11", "17", "21"] + } + }, + "android": { + "cmdline-tools": "commandlinetools-mac-10406996_latest.zip", + "sdk-tools": "sdk-tools-darwin-4333796.zip", + "platform_min_version": "33", + "build_tools_min_version": "33.0.2", + "extras": [ + "android;m2repository", "google;m2repository", "google;google_play_services" + ], + "addons": [], + "additional_tools": [ + "cmake;3.22.1" + ], + "ndk": { + "default": "26", + "versions": [ + "26", "27" + ] + } + }, + "powershellModules": [ + { "name": "Az" }, + { "name": "Pester" }, + { "name": "PSScriptAnalyzer" } + ], + "brew": { + "common_packages": [ + "ant", + "aria2", + "azure-cli", + "bazelisk", + "carthage", + "cmake", + "gh", + "gnupg", + "gnu-tar", + "kotlin", + "libpq", + "libsodium", + "p7zip", + "packer", + "perl", + "pkg-config", + "swiftformat", + "tcl-tk", + "zstd", + "gmp", + "yq", + "unxip", + "xcbeautify", + "xcodes" + ], + "cask_packages": [ + "parallels" + ] + }, + "gcc": { + "versions": [ + "12", + "13", + "14" + ] + }, + "dotnet": { + "arch":{ + "x64": { + "versions": [ + "7.0", + "8.0" + ] + }, + "arm64": { + "versions": [ + "7.0", + "8.0" + ] + } + } + }, + "ruby": { + "default": "3.0", + "rubygems": [ + "cocoapods", + "bundler", + "fastlane" + ] + }, + "toolcache": [ + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "versions": [ + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*", + "3.13.*" + ] + }, + "arm64": { + "versions": [ + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*", + "3.13.*" + ] + } + } + }, + { + "name": "Node", + "url" : "https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "versions": [ + "18.*", + "20.*" + ] + }, + "arm64": { + "versions": [ + "18.*", + "20.*" + ] + } + } + }, + { + "name": "Go", + "url" : "https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "variable_template" : "GOROOT_{0}_{1}_X64", + "versions": [ + "1.21.*", + "1.22.*", + "1.23.*" + ] + }, + "arm64": { + "variable_template" : "GOROOT_{0}_{1}_ARM64", + "versions": [ + "1.21.*", + "1.22.*", + "1.23.*" + ] + } + } + }, + { + "name": "Ruby", + "arch": { + "x64": { + "versions": [ + "3.0.*", + "3.1.*", + "3.2.*", + "3.3.*" + ] + } + } + } + ], + "node": { + "default": "20" + }, + "llvm": { + "version": "15" + }, + "php": { + "version": "8.3" + }, + "pwsh": { + "version": "7.4" + }, + "mono": { + "framework":{ + "version": "6.12.0.188", + "sha256": "07cdd4e5e72b562892960b7fc73af470db7a4ffc2f68bb834eb3d0a874bbd12c" + }, + "nunit": { + "version": "3.15.4", + "sha256": "356dab61433b5be76b76fd0e2e979bda56d164f6d85a900e55c3a4a5fffa28de" + } + } +} diff --git a/images/macos/toolsets/toolset-15.json b/images/macos/toolsets/toolset-15.json new file mode 100644 index 0000000..99f6eab --- /dev/null +++ b/images/macos/toolsets/toolset-15.json @@ -0,0 +1,200 @@ +{ + "xcode": { + "default": "16", + "x64": { + "versions": [ + { "link": "16.1_beta_3", "version": "16.1.0-Beta.3+16B5029d", "symlinks": ["16.1"], "install_runtimes": "true", "sha256": "e92ffd3cf7bb3ecb830ba94e8b2cbeb62743a80c6d73a5ae52ae792e4a897e5f"}, + { "link": "16", "version": "16.0.0+16A242d", "symlinks": ["16.0"], "install_runtimes": "true", "sha256": "4a26c3d102a55c7222fb145e0ee1503249c9c26c6e02dc64d783c8810b37b1e3"} + ] + }, + "arm64":{ + "versions": [ + { "link": "16.1_beta_3", "version": "16.1.0-Beta.3+16B5029d", "symlinks": ["16.1"], "install_runtimes": "true", "sha256": "e92ffd3cf7bb3ecb830ba94e8b2cbeb62743a80c6d73a5ae52ae792e4a897e5f"}, + { "link": "16", "version": "16.0.0+16A242d", "symlinks": ["16.0"], "install_runtimes": "true", "sha256": "4a26c3d102a55c7222fb145e0ee1503249c9c26c6e02dc64d783c8810b37b1e3"} + ] + } + }, + "java": { + "x64": { + "default": "21", + "versions": [ "11", "17", "21"] + }, + "arm64": { + "default": "21", + "versions": [ "11", "17", "21"] + } + }, + "android": { + "cmdline-tools": "commandlinetools-mac-12172612_latest.zip", + "sdk-tools": "sdk-tools-darwin-4333796.zip", + "platform_min_version": "35", + "build_tools_min_version": "35.0.2", + "extras": [ + "android;m2repository", "google;m2repository", "google;google_play_services" + ], + "addons": [], + "additional_tools": [ + "cmake;3.22.1" + ], + "ndk": { + "default": "27", + "versions": [ + "26", "27" + ] + } + }, + "powershellModules": [ + { "name": "Az" }, + { "name": "Pester" }, + { "name": "PSScriptAnalyzer" } + ], + "brew": { + "common_packages": [ + "ant", + "aria2", + "azure-cli", + "bazelisk", + "carthage", + "cmake", + "gh", + "gnupg", + "gnu-tar", + "kotlin", + "libpq", + "libsodium", + "p7zip", + "packer", + "perl", + "pkg-config", + "swiftformat", + "zstd", + "gmp", + "yq", + "unxip", + "xcbeautify", + "xcodes" + ], + "cask_packages": [ + "" + ] + }, + "gcc": { + "versions": [ + "12", + "13", + "14" + ] + }, + "dotnet": { + "arch":{ + "x64": { + "versions": [ + "8.0" + ] + }, + "arm64": { + "versions": [ + "8.0" + ] + } + } + }, + "ruby": { + "default": "3.3", + "rubygems": [ + "cocoapods", + "bundler", + "fastlane" + ] + }, + "toolcache": [ + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "versions": [ + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*", + "3.13.*" + ] + }, + "arm64": { + "versions": [ + "3.11.*", + "3.12.*", + "3.13.*" + ] + } + } + }, + { + "name": "Node", + "url" : "https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "versions": [ + "18.*", + "20.*" + ] + }, + "arm64": { + "versions": [ + "18.*", + "20.*" + ] + } + } + }, + { + "name": "Go", + "url" : "https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json", + "platform" : "darwin", + "arch": { + "x64": { + "variable_template" : "GOROOT_{0}_{1}_X64", + "versions": [ + "1.21.*", + "1.22.*", + "1.23.*" + ] + }, + "arm64": { + "variable_template" : "GOROOT_{0}_{1}_ARM64", + "versions": [ + "1.21.*", + "1.22.*", + "1.23.*" + ] + } + } + }, + { + "name": "Ruby", + "arch": { + "x64": { + "versions": [ + "3.1.*", + "3.2.*" + ] + } + } + } + ], + "node": { + "default": "22" + }, + "llvm": { + "version": "18" + }, + "php": { + "version": "8.3" + }, + "pwsh": { + "version": "7.4" + } +} diff --git a/images/ubuntu/Ubuntu2004-Readme.md b/images/ubuntu/Ubuntu2004-Readme.md new file mode 100644 index 0000000..599124e --- /dev/null +++ b/images/ubuntu/Ubuntu2004-Readme.md @@ -0,0 +1,397 @@ +| Announcements | +|-| +| [[Ubuntu 24 ] Node.js version 16 will be removed on October 21,2024.](https://github.com/actions/runner-images/issues/10677) | +| [Ubuntu-latest workflows will use Ubuntu-24.04 image](https://github.com/actions/runner-images/issues/10636) | +| [[Ubuntu 20 and 22] Go version 1.20.0 will be removed on October 14,2024.](https://github.com/actions/runner-images/issues/10607) | +*** +# Ubuntu 20.04 +- OS Version: 20.04.6 LTS +- Kernel Version: 5.15.0-1073-azure +- Image Version: 20241006.1.0 +- Systemd version: 245.4-4ubuntu3.24 + +## Installed Software + +### Language and Runtime +- Bash 5.0.17(1)-release +- Clang: 10.0.0, 11.0.0, 12.0.0 +- Clang-format: 10.0.0, 11.0.0, 12.0.0 +- Clang-tidy: 10.0.0, 11.0.0, 12.0.0 +- Dash 0.5.10.2-6 +- Erlang 25.3 (Eshell 13.2) +- Erlang rebar3 3.24.0 +- GNU C++: 10.5.0 +- GNU Fortran: 10.5.0 +- Julia 1.10.5 +- Kotlin 2.0.20-release-360 +- Mono 6.12.0.200 +- MSBuild 16.10.1.31701 (Mono 6.12.0.200) +- Node.js 18.20.4 +- Perl 5.30.0 +- Python 3.8.10 +- Ruby 2.7.0p0 +- Swift 6.0.1 + +### Package Management +- cpan 1.64 +- Helm 3.16.1 +- Homebrew 4.4.0 +- Miniconda 24.7.1 +- Npm 10.7.0 +- NuGet 6.6.1.2 +- Pip 20.0.2 +- Pip3 20.0.2 +- Pipx 1.7.1 +- RubyGems 3.1.2 +- Vcpkg (build from commit 7adc2e4d4) +- Yarn 1.22.22 + +#### Environment variables +| Name | Value | +| ----------------------- | ---------------------- | +| CONDA | /usr/share/miniconda | +| VCPKG_INSTALLATION_ROOT | /usr/local/share/vcpkg | + +#### Homebrew note +``` +Location: /home/linuxbrew +Note: Homebrew is pre-installed on image but not added to PATH. +run the eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" command +to accomplish this. +``` + +### Project Management +- Ant 1.10.7 +- Gradle 8.10.2 +- Lerna 8.1.8 +- Maven 3.8.8 +- Sbt 1.10.2 + +### Tools +- Ansible 2.13.13 +- apt-fast 1.10.0 +- AzCopy 10.26.0 - available by `azcopy` and `azcopy10` aliases +- Bazel 7.3.2 +- Bazelisk 1.22.0 +- Bicep 0.30.23 +- Buildah 1.22.3 +- CMake 3.30.4 +- CodeQL Action Bundle 2.19.0 +- Docker Amazon ECR Credential Helper 0.9.0 +- Docker Compose v2 2.27.1 +- Docker-Buildx 0.17.1 +- Docker Client 26.1.3 +- Docker Server 26.1.3 +- Fastlane 2.224.0 +- Git 2.46.2 +- Git LFS 3.5.1 +- Git-ftp 1.6.0 +- Haveged 1.9.1 +- Heroku 9.3.0 +- HHVM (HipHop VM) 4.172.1 +- jq 1.6 +- Kind 0.24.0 +- Kubectl 1.31.1 +- Kustomize 5.4.3 +- Leiningen 2.11.2 +- MediaInfo 19.09 +- Mercurial 5.3.1 +- Minikube 1.34.0 +- n 10.0.0 +- Newman 6.2.1 +- nvm 0.40.1 +- OpenSSL 1.1.1f-1ubuntu2.23 +- Packer 1.11.2 +- Parcel 2.12.0 +- PhantomJS 2.1.1 2.1.1 +- Podman 3.4.2 +- Pulumi 3.135.1 +- R 4.4.1 +- Skopeo 1.5.0 +- Sphinx Open Source Search Server 2.2.11 +- SVN 1.13.0 +- Terraform 1.9.7 +- yamllint 1.35.1 +- yq 4.44.3 +- zstd 1.5.6 + +### CLI Tools +- Alibaba Cloud CLI 3.0.174 +- AWS CLI 2.18.0 +- AWS CLI Session Manager Plugin 1.2.650.0 +- AWS SAM CLI 1.125.0 +- Azure CLI 2.64.0 +- Azure CLI (azure-devops) 1.0.1 +- GitHub CLI 2.58.0 +- Google Cloud CLI 495.0.0 +- Netlify CLI 17.36.4 +- OpenShift CLI 4.15.19 +- ORAS CLI 1.2.0 +- Vercel CLI 37.6.1 + +### Java +| Version | Environment Variable | +| ------------------- | -------------------- | +| 8.0.422+5 | JAVA_HOME_8_X64 | +| 11.0.24+8 (default) | JAVA_HOME_11_X64 | +| 17.0.12+7 | JAVA_HOME_17_X64 | +| 21.0.4+7 | JAVA_HOME_21_X64 | + +### PHP Tools +- PHP: 7.4.33, 8.0.30, 8.1.30, 8.2.24, 8.3.12 +- Composer 2.8.1 +- PHPUnit 8.5.40 +``` +Both Xdebug and PCOV extensions are installed, but only Xdebug is enabled. +``` + +### Haskell Tools +- Cabal 3.12.1.0 +- GHC 9.10.1 +- GHCup 0.1.30.0 +- Stack 3.1.1 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- Bindgen 0.70.1 +- Cargo audit 0.20.1 +- Cargo clippy 0.1.81 +- Cargo outdated 0.15.0 +- Cbindgen 0.27.0 +- Rustfmt 1.7.1 + +### Browsers and Drivers +- Google Chrome 129.0.6668.89 +- ChromeDriver 129.0.6668.89 +- Chromium 129.0.6668.0 +- Microsoft Edge 129.0.2792.79 +- Microsoft Edge WebDriver 129.0.2792.73 +- Selenium server 4.25.0 +- Mozilla Firefox 131.0 +- Geckodriver 0.35.0 + +#### Environment variables +| Name | Value | +| ----------------- | ------------------------------------- | +| CHROMEWEBDRIVER | /usr/local/share/chromedriver-linux64 | +| EDGEWEBDRIVER | /usr/local/share/edge_driver | +| GECKOWEBDRIVER | /usr/local/share/gecko_driver | +| SELENIUM_JAR_PATH | /usr/share/java/selenium-server.jar | + +### .NET Tools +- .NET Core SDK: 6.0.425, 7.0.410, 8.0.402 +- nbgv 3.6.143+57d4199a9c + +### Databases +- MongoDB 5.0.29 +- sqlite3 3.31.1 + +#### PostgreSQL +- PostgreSQL 14.13 +``` +User: postgres +PostgreSQL service is disabled by default. +Use the following command as a part of your job to start the service: 'sudo systemctl start postgresql.service' +``` + +#### MySQL +- MySQL 8.0.39-0ubuntu0.20.04.1 +``` +User: root +Password: root +MySQL service is disabled by default. +Use the following command as a part of your job to start the service: 'sudo systemctl start mysql.service' +``` + +#### MS SQL +- sqlcmd 17.10.0001.1 +- SqlPackage 162.4.92.3 + +### Cached Tools + +#### Go +- 1.20.14 +- 1.21.13 +- 1.22.8 + +#### Node.js +- 16.20.2 +- 18.20.4 +- 20.17.0 + +#### Python +- 3.7.17 +- 3.8.18 +- 3.9.20 +- 3.10.15 +- 3.11.10 +- 3.12.7 + +#### PyPy +- 2.7.18 [PyPy 7.3.17] +- 3.6.12 [PyPy 7.3.3] +- 3.7.13 [PyPy 7.3.9] +- 3.8.16 [PyPy 7.3.11] +- 3.9.19 [PyPy 7.3.16] +- 3.10.14 [PyPy 7.3.17] + +#### Ruby +- 3.0.7 +- 3.1.6 +- 3.2.5 + +### PowerShell Tools +- PowerShell 7.4.5 + +#### PowerShell Modules +- Az: 12.1.0 +- MarkdownPS: 1.10 +- Microsoft.Graph: 2.24.0 +- Pester: 5.6.1 +- PSScriptAnalyzer: 1.22.0 + +### Web Servers +| Name | Version | ConfigFile | ServiceStatus | ListenPort | +| --------- | ------- | ------------------------- | ------------- | ---------- | +| apache2 | 2.4.41 | /etc/apache2/apache2.conf | inactive | 80 | +| mono-xsp4 | 4.7.1 | /etc/default/mono-xsp4 | active | 8084 | +| nginx | 1.18.0 | /etc/nginx/nginx.conf | inactive | 80 | + +### Android +| Package Name | Version | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Command Line Tools | 9.0 | +| Android SDK Build-tools | 35.0.0
34.0.0
33.0.0 33.0.1 33.0.2 33.0.3
32.0.0
31.0.0 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-34 (rev 3)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1)
android-33 (rev 3)
android-32 (rev 1)
android-31 (rev 1) | +| Android Support Repository | 47.0.0 | +| CMake | 3.10.2
3.18.1
3.22.1 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264
27.1.12297006 (default) | + +#### Environment variables +| Name | Value | +| ----------------------- | -------------------------------------------- | +| ANDROID_HOME | /usr/local/lib/android/sdk | +| ANDROID_NDK | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_HOME | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_LATEST_HOME | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_ROOT | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_SDK_ROOT | /usr/local/lib/android/sdk | + +### Cached Docker images +| Repository:Tag | Digest | Created | +| -------------------- | ------------------------------------------------------------------------ | ---------- | +| alpine:3.16 | sha256:452e7292acee0ee16c332324d7de05fa2c99f9994ecc9f0779c602916a672ae4 | 2024-01-27 | +| alpine:3.17 | sha256:3451da08fc6ef554a100da3e2df5ac6d598c82f2a774d5f6ed465c3d80cd163a | 2024-09-06 | +| alpine:3.18 | sha256:3ddf7bf1d408188f9849efbf4f902720ae08f5131bb39013518b918aa056d0de | 2024-09-06 | +| alpine:3.19 | sha256:ae65dbf8749a7d4527648ccee1fa3deb6bfcae34cbc30fc67aa45c44dcaa90ee | 2024-09-06 | +| debian:10 | sha256:58ce6f1271ae1c8a2006ff7d3e54e9874d839f573d8009c20154ad0f2fb0a225 | 2024-06-13 | +| debian:11 | sha256:152b9a5dc2a03f18ddfd88fbe7b1df41bd2b16be9f2df573a373caf46ce78c08 | 2024-09-27 | +| debian:9 | sha256:c5c5200ff1e9c73ffbf188b4a67eb1c91531b644856b4aefe86a58d2f0cb05be | 2022-06-23 | +| moby/buildkit:latest | sha256:bc1fe18224dbcb92599139db0c745696c48ba9fd4ac24038d1fa81fdd7dcac27 | 2024-09-10 | +| node:16 | sha256:f77a1aef2da8d83e45ec990f45df50f1a286c5fe8bbfb8c6e4246c6389705c0b | 2023-09-07 | +| node:16-alpine | sha256:a1f9d027912b58a7c75be7716c97cfbc6d3099f3a97ed84aa490be9dee20e787 | 2023-08-10 | +| node:18 | sha256:f910225c96b0f77b0149f350a3184568a9ba6cddba2a7c7805cc125a50591605 | 2024-07-09 | +| node:18-alpine | sha256:02376a266c84acbf45bd19440e08e48b1c8b98037417334046029ab585de03e2 | 2024-07-09 | +| node:20 | sha256:fffa89e023a3351904c04284029105d9e2ac7020886d683775a298569591e5bb | 2024-10-04 | +| node:20-alpine | sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3cc8e44a8def9 | 2024-10-04 | +| ubuntu:20.04 | sha256:6d8d9799fe6ab3221965efac00b4c34a2bcc102c086a58dff9e19a08b913c7ef | 2024-09-18 | + +### Installed apt packages +| Name | Version | +| ---------------------- | --------------------------------- | +| acl | 2.2.53-6 | +| aria2 | 1.35.0-1build1 | +| autoconf | 2.69-11.1 | +| automake | 1:1.16.1-4ubuntu6 | +| binutils | 2.34-6ubuntu1.9 | +| bison | 2:3.5.1+dfsg-1 | +| brotli | 1.0.7-6ubuntu0.1 | +| bzip2 | 1.0.8-2 | +| coreutils | 8.30-3ubuntu2 | +| curl | 7.68.0-1ubuntu2.24 | +| dbus | 1.12.16-2ubuntu2.3 | +| dnsutils | 1:9.18.28-0ubuntu0.20.04.1 | +| dpkg | 1.19.7ubuntu3.2 | +| dpkg-dev | 1.19.7ubuntu3.2 | +| fakeroot | 1.24-1 | +| file | 1:5.38-4 | +| findutils | 4.7.0-1ubuntu1 | +| flex | 2.6.4-6.2 | +| fonts-noto-color-emoji | 0\~20200916-1\~ubuntu20.04.1 | +| ftp | 0.17-34.1 | +| g++ | 4:9.3.0-1ubuntu2 | +| gcc | 4:9.3.0-1ubuntu2 | +| gnupg2 | 2.2.19-3ubuntu2.2 | +| haveged | 1.9.1-6ubuntu1 | +| imagemagick | 8:6.9.10.23+dfsg-2.1ubuntu11.10 | +| iproute2 | 5.5.0-1ubuntu1 | +| iputils-ping | 3:20190709-3ubuntu1 | +| jq | 1.6-1ubuntu0.20.04.1 | +| lib32z1 | 1:1.2.11.dfsg-2ubuntu1.5 | +| libc++-dev | 1:10.0-50\~exp1 | +| libc++abi-dev | 1:10.0-50\~exp1 | +| libc6-dev | 2.31-0ubuntu9.16 | +| libcurl4 | 7.68.0-1ubuntu2.24 | +| libgbm-dev | 21.2.6-0ubuntu0.1\~20.04.2 | +| libgconf-2-4 | 3.2.6-6ubuntu1 | +| libgsl-dev | 2.5+dfsg-6+deb10u1build0.20.04.1 | +| libgtk-3-0 | 3.24.20-0ubuntu1.2 | +| libmagic-dev | 1:5.38-4 | +| libmagickcore-dev | 8:6.9.10.23+dfsg-2.1ubuntu11.10 | +| libmagickwand-dev | 8:6.9.10.23+dfsg-2.1ubuntu11.10 | +| libsecret-1-dev | 0.20.4-0ubuntu1 | +| libsqlite3-dev | 3.31.1-4ubuntu0.6 | +| libtool | 2.4.6-14 | +| libunwind8 | 1.2.1-9ubuntu0.1 | +| libxkbfile-dev | 1:1.1.0-1 | +| libxss1 | 1:1.2.3-1 | +| libyaml-dev | 0.2.2-1 | +| locales | 2.31-0ubuntu9.16 | +| m4 | 1.4.18-4 | +| make | 4.2.1-1.2 | +| mediainfo | 19.09-1build1 | +| mercurial | 5.3.1-1ubuntu1 | +| net-tools | 1.60+git20180626.aebd88e-1ubuntu1 | +| netcat | 1.206-1ubuntu1 | +| openssh-client | 1:8.2p1-4ubuntu0.11 | +| p7zip-full | 16.02+dfsg-7build1 | +| p7zip-rar | 16.02-3build1 | +| parallel | 20161222-1.1 | +| pass | 1.7.3-2 | +| patchelf | 0.10-2build1 | +| pigz | 2.4-1 | +| pkg-config | 0.29.1-0ubuntu4 | +| pollinate | 4.33-3ubuntu1.20.04.1 | +| python-is-python3 | 3.8.2-4 | +| rpm | 4.14.2.1+dfsg1-1build2 | +| rsync | 3.1.3-8ubuntu0.7 | +| shellcheck | 0.7.0-2build2 | +| sphinxsearch | 2.2.11-2ubuntu2 | +| sqlite3 | 3.31.1-4ubuntu0.6 | +| ssh | 1:8.2p1-4ubuntu0.11 | +| sshpass | 1.06-1 | +| subversion | 1.13.0-3ubuntu0.2 | +| sudo | 1.8.31-1ubuntu1.5 | +| swig | 4.0.1-5build1 | +| tar | 1.30+dfsg-7ubuntu0.20.04.4 | +| telnet | 0.17-41.2build1 | +| texinfo | 6.7.0.dfsg.2-5 | +| time | 1.7-25.1build1 | +| tk | 8.6.9+1 | +| tzdata | 2024a-0ubuntu0.20.04.1 | +| unzip | 6.0-25ubuntu1.2 | +| upx | 3.95-2build1 | +| wget | 1.20.3-1ubuntu2.1 | +| xorriso | 1.5.2-1 | +| xvfb | 2:1.20.13-1ubuntu1\~20.04.17 | +| xz-utils | 5.2.4-1ubuntu1.1 | +| zip | 3.0-11build1 | +| zsync | 0.6.2-3ubuntu1 | + diff --git a/images/ubuntu/Ubuntu2204-Readme.md b/images/ubuntu/Ubuntu2204-Readme.md new file mode 100644 index 0000000..a071911 --- /dev/null +++ b/images/ubuntu/Ubuntu2204-Readme.md @@ -0,0 +1,390 @@ +| Announcements | +|-| +| [[Ubuntu 24 ] Node.js version 16 will be removed on October 21,2024.](https://github.com/actions/runner-images/issues/10677) | +| [Ubuntu-latest workflows will use Ubuntu-24.04 image](https://github.com/actions/runner-images/issues/10636) | +| [[Ubuntu 20 and 22] Go version 1.20.0 will be removed on October 14,2024.](https://github.com/actions/runner-images/issues/10607) | +*** +# Ubuntu 22.04 +- OS Version: 22.04.5 LTS +- Kernel Version: 6.5.0-1025-azure +- Image Version: 20241006.1.0 +- Systemd version: 249.11-0ubuntu3.12 + +## Installed Software + +### Language and Runtime +- Bash 5.1.16(1)-release +- Clang: 13.0.1, 14.0.0, 15.0.7 +- Clang-format: 13.0.1, 14.0.0, 15.0.7 +- Clang-tidy: 13.0.1, 14.0.0, 15.0.7 +- Dash 0.5.11+git20210903+057cd650a4ed-3build1 +- GNU C++: 10.5.0, 11.4.0, 12.3.0 +- GNU Fortran: 10.5.0, 11.4.0, 12.3.0 +- Julia 1.10.5 +- Kotlin 2.0.20-release-360 +- Mono 6.12.0.200 +- MSBuild 16.10.1.31701 (Mono 6.12.0.200) +- Node.js 18.20.4 +- Perl 5.34.0 +- Python 3.10.12 +- Ruby 3.0.2p107 +- Swift 6.0.1 + +### Package Management +- cpan 1.64 +- Helm 3.16.1 +- Homebrew 4.4.0 +- Miniconda 24.7.1 +- Npm 10.7.0 +- NuGet 6.6.1.2 +- Pip 22.0.2 +- Pip3 22.0.2 +- Pipx 1.7.1 +- RubyGems 3.3.5 +- Vcpkg (build from commit 7adc2e4d4) +- Yarn 1.22.22 + +#### Environment variables +| Name | Value | +| ----------------------- | ---------------------- | +| CONDA | /usr/share/miniconda | +| VCPKG_INSTALLATION_ROOT | /usr/local/share/vcpkg | + +#### Homebrew note +``` +Location: /home/linuxbrew +Note: Homebrew is pre-installed on image but not added to PATH. +run the eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" command +to accomplish this. +``` + +### Project Management +- Ant 1.10.12 +- Gradle 8.10.2 +- Lerna 8.1.8 +- Maven 3.8.8 +- Sbt 1.10.2 + +### Tools +- Ansible 2.17.4 +- apt-fast 1.10.0 +- AzCopy 10.26.0 - available by `azcopy` and `azcopy10` aliases +- Bazel 7.3.2 +- Bazelisk 1.22.0 +- Bicep 0.30.23 +- Buildah 1.23.1 +- CMake 3.30.4 +- CodeQL Action Bundle 2.19.0 +- Docker Amazon ECR Credential Helper 0.9.0 +- Docker Compose v2 2.27.1 +- Docker-Buildx 0.17.1 +- Docker Client 26.1.3 +- Docker Server 26.1.3 +- Fastlane 2.224.0 +- Git 2.46.2 +- Git LFS 3.5.1 +- Git-ftp 1.6.0 +- Haveged 1.9.14 +- Heroku 9.3.0 +- jq 1.6 +- Kind 0.24.0 +- Kubectl 1.31.1 +- Kustomize 5.4.3 +- Leiningen 2.11.2 +- MediaInfo 21.09 +- Mercurial 6.1.1 +- Minikube 1.34.0 +- n 10.0.0 +- Newman 6.2.1 +- nvm 0.40.1 +- OpenSSL 3.0.2-0ubuntu1.18 +- Packer 1.11.2 +- Parcel 2.12.0 +- Podman 3.4.4 +- Pulumi 3.135.1 +- R 4.4.1 +- Skopeo 1.4.1 +- Sphinx Open Source Search Server 2.2.11 +- SVN 1.14.1 +- Terraform 1.9.7 +- yamllint 1.35.1 +- yq 4.44.3 +- zstd 1.5.6 + +### CLI Tools +- Alibaba Cloud CLI 3.0.224 +- AWS CLI 2.18.0 +- AWS CLI Session Manager Plugin 1.2.650.0 +- AWS SAM CLI 1.125.0 +- Azure CLI 2.64.0 +- Azure CLI (azure-devops) 1.0.1 +- GitHub CLI 2.58.0 +- Google Cloud CLI 495.0.0 +- Netlify CLI 17.36.4 +- OpenShift CLI 4.17.0 +- ORAS CLI 1.2.0 +- Vercel CLI 37.6.1 + +### Java +| Version | Environment Variable | +| ------------------- | -------------------- | +| 8.0.422+5 | JAVA_HOME_8_X64 | +| 11.0.24+8 (default) | JAVA_HOME_11_X64 | +| 17.0.12+7 | JAVA_HOME_17_X64 | +| 21.0.4+7 | JAVA_HOME_21_X64 | + +### PHP Tools +- PHP: 8.1.2 +- Composer 2.8.1 +- PHPUnit 8.5.40 +``` +Both Xdebug and PCOV extensions are installed, but only Xdebug is enabled. +``` + +### Haskell Tools +- Cabal 3.12.1.0 +- GHC 9.10.1 +- GHCup 0.1.30.0 +- Stack 3.1.1 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- Bindgen 0.70.1 +- Cargo audit 0.20.1 +- Cargo clippy 0.1.81 +- Cargo outdated 0.15.0 +- Cbindgen 0.27.0 +- Rustfmt 1.7.1 + +### Browsers and Drivers +- Google Chrome 129.0.6668.89 +- ChromeDriver 129.0.6668.89 +- Chromium 129.0.6668.0 +- Microsoft Edge 129.0.2792.79 +- Microsoft Edge WebDriver 129.0.2792.73 +- Selenium server 4.25.0 +- Mozilla Firefox 131.0 +- Geckodriver 0.35.0 + +#### Environment variables +| Name | Value | +| ----------------- | ------------------------------------- | +| CHROMEWEBDRIVER | /usr/local/share/chromedriver-linux64 | +| EDGEWEBDRIVER | /usr/local/share/edge_driver | +| GECKOWEBDRIVER | /usr/local/share/gecko_driver | +| SELENIUM_JAR_PATH | /usr/share/java/selenium-server.jar | + +### .NET Tools +- .NET Core SDK: 6.0.425, 7.0.410, 8.0.402 +- nbgv 3.6.143+57d4199a9c + +### Databases +- sqlite3 3.37.2 + +#### PostgreSQL +- PostgreSQL 14.13 +``` +User: postgres +PostgreSQL service is disabled by default. +Use the following command as a part of your job to start the service: 'sudo systemctl start postgresql.service' +``` + +#### MySQL +- MySQL 8.0.39-0ubuntu0.22.04.1 +``` +User: root +Password: root +MySQL service is disabled by default. +Use the following command as a part of your job to start the service: 'sudo systemctl start mysql.service' +``` + +#### MS SQL +- sqlcmd 17.10.0001.1 +- SqlPackage 162.4.92.3 + +### Cached Tools + +#### Go +- 1.20.14 +- 1.21.13 +- 1.22.8 + +#### Node.js +- 16.20.2 +- 18.20.4 +- 20.17.0 + +#### Python +- 3.7.17 +- 3.8.18 +- 3.9.20 +- 3.10.15 +- 3.11.10 +- 3.12.7 + +#### PyPy +- 3.7.13 [PyPy 7.3.9] +- 3.8.16 [PyPy 7.3.11] +- 3.9.19 [PyPy 7.3.16] +- 3.10.14 [PyPy 7.3.17] + +#### Ruby +- 3.1.6 +- 3.2.5 + +### PowerShell Tools +- PowerShell 7.4.5 + +#### PowerShell Modules +- Az: 12.1.0 +- MarkdownPS: 1.10 +- Microsoft.Graph: 2.24.0 +- Pester: 5.6.1 +- PSScriptAnalyzer: 1.22.0 + +### Web Servers +| Name | Version | ConfigFile | ServiceStatus | ListenPort | +| ------- | ------- | ------------------------- | ------------- | ---------- | +| apache2 | 2.4.52 | /etc/apache2/apache2.conf | inactive | 80 | +| nginx | 1.18.0 | /etc/nginx/nginx.conf | inactive | 80 | + +### Android +| Package Name | Version | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Command Line Tools | 9.0 | +| Android SDK Build-tools | 35.0.0
34.0.0
33.0.0 33.0.1 33.0.2 33.0.3
32.0.0
31.0.0 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-34 (rev 3)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1)
android-33 (rev 3)
android-32 (rev 1)
android-31 (rev 1) | +| Android Support Repository | 47.0.0 | +| CMake | 3.10.2
3.18.1
3.22.1 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264
27.1.12297006 (default) | + +#### Environment variables +| Name | Value | +| ----------------------- | -------------------------------------------- | +| ANDROID_HOME | /usr/local/lib/android/sdk | +| ANDROID_NDK | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_HOME | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_LATEST_HOME | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_ROOT | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_SDK_ROOT | /usr/local/lib/android/sdk | + +### Cached Docker images +| Repository:Tag | Digest | Created | +| -------------------- | ------------------------------------------------------------------------ | ---------- | +| alpine:3.16 | sha256:452e7292acee0ee16c332324d7de05fa2c99f9994ecc9f0779c602916a672ae4 | 2024-01-27 | +| alpine:3.17 | sha256:3451da08fc6ef554a100da3e2df5ac6d598c82f2a774d5f6ed465c3d80cd163a | 2024-09-06 | +| alpine:3.18 | sha256:3ddf7bf1d408188f9849efbf4f902720ae08f5131bb39013518b918aa056d0de | 2024-09-06 | +| alpine:3.19 | sha256:ae65dbf8749a7d4527648ccee1fa3deb6bfcae34cbc30fc67aa45c44dcaa90ee | 2024-09-06 | +| debian:10 | sha256:58ce6f1271ae1c8a2006ff7d3e54e9874d839f573d8009c20154ad0f2fb0a225 | 2024-06-13 | +| debian:11 | sha256:152b9a5dc2a03f18ddfd88fbe7b1df41bd2b16be9f2df573a373caf46ce78c08 | 2024-09-27 | +| moby/buildkit:latest | sha256:bc1fe18224dbcb92599139db0c745696c48ba9fd4ac24038d1fa81fdd7dcac27 | 2024-09-10 | +| node:16 | sha256:f77a1aef2da8d83e45ec990f45df50f1a286c5fe8bbfb8c6e4246c6389705c0b | 2023-09-07 | +| node:16-alpine | sha256:a1f9d027912b58a7c75be7716c97cfbc6d3099f3a97ed84aa490be9dee20e787 | 2023-08-10 | +| node:18 | sha256:f910225c96b0f77b0149f350a3184568a9ba6cddba2a7c7805cc125a50591605 | 2024-07-09 | +| node:18-alpine | sha256:02376a266c84acbf45bd19440e08e48b1c8b98037417334046029ab585de03e2 | 2024-07-09 | +| node:20 | sha256:fffa89e023a3351904c04284029105d9e2ac7020886d683775a298569591e5bb | 2024-10-04 | +| node:20-alpine | sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3cc8e44a8def9 | 2024-10-04 | +| ubuntu:20.04 | sha256:6d8d9799fe6ab3221965efac00b4c34a2bcc102c086a58dff9e19a08b913c7ef | 2024-09-18 | +| ubuntu:22.04 | sha256:58b87898e82351c6cf9cf5b9f3c20257bb9e2dcf33af051e12ce532d7f94e3fe | 2024-09-11 | + +### Installed apt packages +| Name | Version | +| ---------------------- | ----------------------------------- | +| acl | 2.3.1-1 | +| aria2 | 1.36.0-1 | +| autoconf | 2.71-2 | +| automake | 1:1.16.5-1.3 | +| binutils | 2.38-4ubuntu2.6 | +| bison | 2:3.8.2+dfsg-1build1 | +| brotli | 1.0.9-2build6 | +| bzip2 | 1.0.8-5build1 | +| coreutils | 8.32-4.1ubuntu1.2 | +| curl | 7.81.0-1ubuntu1.18 | +| dbus | 1.12.20-2ubuntu4.1 | +| dnsutils | 1:9.18.28-0ubuntu0.22.04.1 | +| dpkg | 1.21.1ubuntu2.3 | +| dpkg-dev | 1.21.1ubuntu2.3 | +| fakeroot | 1.28-1ubuntu1 | +| file | 1:5.41-3ubuntu0.1 | +| findutils | 4.8.0-1ubuntu3 | +| flex | 2.6.4-8build2 | +| fonts-noto-color-emoji | 2.042-0ubuntu0.22.04.1 | +| ftp | 20210827-4build1 | +| g++ | 4:11.2.0-1ubuntu1 | +| gcc | 4:11.2.0-1ubuntu1 | +| gnupg2 | 2.2.27-3ubuntu2.1 | +| haveged | 1.9.14-1ubuntu1 | +| imagemagick | 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.5 | +| iproute2 | 5.15.0-1ubuntu2 | +| iputils-ping | 3:20211215-1 | +| jq | 1.6-2.1ubuntu3 | +| lib32z1 | 1:1.2.11.dfsg-2ubuntu9.2 | +| libc++-dev | 1:14.0-55\~exp2 | +| libc++abi-dev | 1:14.0-55\~exp2 | +| libc6-dev | 2.35-0ubuntu3.8 | +| libcurl4 | 7.81.0-1ubuntu1.18 | +| libgbm-dev | 23.2.1-1ubuntu3.1\~22.04.2 | +| libgconf-2-4 | 3.2.6-7ubuntu2 | +| libgsl-dev | 2.7.1+dfsg-3 | +| libgtk-3-0 | 3.24.33-1ubuntu2.2 | +| libmagic-dev | 1:5.41-3ubuntu0.1 | +| libmagickcore-dev | 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.5 | +| libmagickwand-dev | 8:6.9.11.60+dfsg-1.3ubuntu0.22.04.5 | +| libsecret-1-dev | 0.20.5-2 | +| libsqlite3-dev | 3.37.2-2ubuntu0.3 | +| libssl-dev | 3.0.2-0ubuntu1.18 | +| libtool | 2.4.6-15build2 | +| libunwind8 | 1.3.2-2build2.1 | +| libxkbfile-dev | 1:1.1.0-1build3 | +| libxss1 | 1:1.2.3-1build2 | +| libyaml-dev | 0.2.2-1build2 | +| locales | 2.35-0ubuntu3.8 | +| lz4 | 1.9.3-2build2 | +| m4 | 1.4.18-5ubuntu2 | +| make | 4.3-4.1build1 | +| mediainfo | 22.03-1 | +| mercurial | 6.1.1-1ubuntu1 | +| net-tools | 1.60+git20181103.0eebece-1ubuntu5 | +| netcat | 1.218-4ubuntu1 | +| openssh-client | 1:8.9p1-3ubuntu0.10 | +| p7zip-full | 16.02+dfsg-8 | +| p7zip-rar | 16.02-3build1 | +| parallel | 20210822+ds-2 | +| pass | 1.7.4-5 | +| patchelf | 0.14.3-1 | +| pigz | 2.6-1 | +| pkg-config | 0.29.2-1ubuntu3 | +| pollinate | 4.33-3ubuntu2 | +| python-is-python3 | 3.9.2-2 | +| rpm | 4.17.0+dfsg1-4build1 | +| rsync | 3.2.7-0ubuntu0.22.04.2 | +| shellcheck | 0.8.0-2 | +| sphinxsearch | 2.2.11-8 | +| sqlite3 | 3.37.2-2ubuntu0.3 | +| ssh | 1:8.9p1-3ubuntu0.10 | +| sshpass | 1.09-1 | +| subversion | 1.14.1-3ubuntu0.22.04.1 | +| sudo | 1.9.9-1ubuntu2.4 | +| swig | 4.0.2-1ubuntu1 | +| tar | 1.34+dfsg-1ubuntu0.1.22.04.2 | +| telnet | 0.17-44build1 | +| texinfo | 6.8-4build1 | +| time | 1.9-0.1build2 | +| tk | 8.6.11+1build2 | +| tzdata | 2024a-0ubuntu0.22.04.1 | +| unzip | 6.0-26ubuntu3.2 | +| upx | 3.96-3 | +| wget | 1.21.2-2ubuntu1.1 | +| xorriso | 1.5.4-2 | +| xvfb | 2:21.1.4-2ubuntu1.7\~22.04.11 | +| xz-utils | 5.2.5-2ubuntu1 | +| zip | 3.0-12build2 | +| zsync | 0.6.2-3ubuntu1 | + diff --git a/images/ubuntu/Ubuntu2404-Readme.md b/images/ubuntu/Ubuntu2404-Readme.md new file mode 100644 index 0000000..65792a9 --- /dev/null +++ b/images/ubuntu/Ubuntu2404-Readme.md @@ -0,0 +1,315 @@ +| Announcements | +|-| +| [[Ubuntu 24 ] Node.js version 16 will be removed on October 21,2024.](https://github.com/actions/runner-images/issues/10677) | +| [Ubuntu-latest workflows will use Ubuntu-24.04 image](https://github.com/actions/runner-images/issues/10636) | +| [[Ubuntu 20 and 22] Go version 1.20.0 will be removed on October 14,2024.](https://github.com/actions/runner-images/issues/10607) | +*** +# Ubuntu 24.04 +- OS Version: 24.04.1 LTS +- Kernel Version: 6.8.0-1015-azure +- Image Version: 20241006.1.0 +- Systemd version: 255.4-1ubuntu8.4 + +## Installed Software + +### Language and Runtime +- Bash 5.2.21(1)-release +- Clang: 16.0.6, 17.0.6, 18.1.3 +- Clang-format: 16.0.6, 17.0.6, 18.1.3 +- Clang-tidy: 16.0.6, 17.0.6, 18.1.3 +- Dash 0.5.12-6ubuntu5 +- GNU C++: 12.3.0, 13.2.0, 14.0.1 +- GNU Fortran: 12.3.0, 13.2.0, 14.0.1 +- Julia 1.10.5 +- Kotlin 2.0.20-release-360 +- Node.js 20.18.0 +- Perl 5.38.2 +- Python 3.12.3 +- Ruby 3.2.3 +- Swift 6.0.1 + +### Package Management +- cpan 1.64 +- Helm 3.16.1 +- Homebrew 4.4.0 +- Miniconda 24.7.1 +- Npm 10.8.2 +- Pip 24.0 +- Pip3 24.0 +- Pipx 1.4.3 +- RubyGems 3.4.20 +- Vcpkg (build from commit 7adc2e4d4) +- Yarn 1.22.22 + +#### Environment variables +| Name | Value | +| ----------------------- | ---------------------- | +| CONDA | /usr/share/miniconda | +| VCPKG_INSTALLATION_ROOT | /usr/local/share/vcpkg | + +#### Homebrew note +``` +Location: /home/linuxbrew +Note: Homebrew is pre-installed on image but not added to PATH. +run the eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" command +to accomplish this. +``` + +### Project Management +- Ant 1.10.14 +- Gradle 8.10.2 +- Lerna 8.1.8 +- Maven 3.8.8 + +### Tools +- Ansible 2.17.4 +- AzCopy 10.26.0 - available by `azcopy` and `azcopy10` aliases +- Bazel 7.3.2 +- Bazelisk 1.22.0 +- Bicep 0.30.23 +- Buildah 1.33.7 +- CMake 3.30.4 +- CodeQL Action Bundle 2.19.0 +- Docker Amazon ECR Credential Helper 0.9.0 +- Docker Compose v2 2.27.1 +- Docker-Buildx 0.17.1 +- Docker Client 26.1.3 +- Docker Server 26.1.3 +- Fastlane 2.224.0 +- Git 2.46.2 +- Git LFS 3.5.1 +- Git-ftp 1.6.0 +- Haveged 1.9.14 +- jq 1.7 +- Kind 0.24.0 +- Kubectl 1.31.1 +- Kustomize 5.4.3 +- MediaInfo 24.01 +- Mercurial 6.7.2 +- Minikube 1.34.0 +- n 10.0.0 +- Newman 6.2.1 +- OpenSSL 3.0.13-0ubuntu3.4 +- Packer 1.11.2 +- Parcel 2.12.0 +- Podman 4.9.3 +- Pulumi 3.135.1 +- Skopeo 1.13.3 +- Sphinx Open Source Search Server 2.2.11 +- yamllint 1.35.1 +- yq 4.44.3 +- zstd 1.5.6 + +### CLI Tools +- AWS CLI 2.18.0 +- AWS CLI Session Manager Plugin 1.2.650.0 +- AWS SAM CLI 1.125.0 +- Azure CLI 2.64.0 +- Azure CLI (azure-devops) 1.0.1 +- GitHub CLI 2.58.0 + +### Java +| Version | Environment Variable | +| ------------------- | -------------------- | +| 8.0.422+5 | JAVA_HOME_8_X64 | +| 11.0.24+8 | JAVA_HOME_11_X64 | +| 17.0.12+7 (default) | JAVA_HOME_17_X64 | +| 21.0.4+7 | JAVA_HOME_21_X64 | + +### PHP Tools +- PHP: 8.3.6 +- Composer 2.8.1 +- PHPUnit 8.5.40 +``` +Both Xdebug and PCOV extensions are installed, but only Xdebug is enabled. +``` + +### Haskell Tools +- Cabal 3.12.1.0 +- GHC 9.10.1 +- GHCup 0.1.30.0 +- Stack 3.1.1 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- Rustfmt 1.7.1 + +### Browsers and Drivers +- Google Chrome 129.0.6668.89 +- ChromeDriver 129.0.6668.89 +- Chromium 129.0.6668.0 +- Microsoft Edge 129.0.2792.79 +- Microsoft Edge WebDriver 129.0.2792.73 +- Selenium server 4.25.0 +- Mozilla Firefox 131.0 +- Geckodriver 0.35.0 + +#### Environment variables +| Name | Value | +| ----------------- | ------------------------------------- | +| CHROMEWEBDRIVER | /usr/local/share/chromedriver-linux64 | +| EDGEWEBDRIVER | /usr/local/share/edge_driver | +| GECKOWEBDRIVER | /usr/local/share/gecko_driver | +| SELENIUM_JAR_PATH | /usr/share/java/selenium-server.jar | + +### .NET Tools +- .NET Core SDK: 8.0.108 +- nbgv 3.6.143+57d4199a9c + +### Databases +- sqlite3 3.45.1 + +#### PostgreSQL +- PostgreSQL 16.4 +``` +User: postgres +PostgreSQL service is disabled by default. +Use the following command as a part of your job to start the service: 'sudo systemctl start postgresql.service' +``` + +#### MySQL +- MySQL 8.0.39-0ubuntu0.24.04.2 +``` +User: root +Password: root +MySQL service is disabled by default. +Use the following command as a part of your job to start the service: 'sudo systemctl start mysql.service' +``` + +### Cached Tools + +#### Go +- 1.21.13 +- 1.22.8 +- 1.23.2 + +#### Node.js +- 16.20.2 +- 18.20.4 +- 20.17.0 + +#### Python +- 3.9.20 +- 3.10.15 +- 3.11.10 +- 3.12.7 + +#### PyPy +- 3.9.19 [PyPy 7.3.16] +- 3.10.14 [PyPy 7.3.17] + +### PowerShell Tools +- PowerShell 7.4.2 + +#### PowerShell Modules +- Az: 12.1.0 +- Microsoft.Graph: 2.24.0 +- Pester: 5.6.1 +- PSScriptAnalyzer: 1.22.0 + +### Web Servers +| Name | Version | ConfigFile | ServiceStatus | ListenPort | +| ------- | ------- | ------------------------- | ------------- | ---------- | +| apache2 | 2.4.58 | /etc/apache2/apache2.conf | inactive | 80 | +| nginx | 1.24.0 | /etc/nginx/nginx.conf | inactive | 80 | + +### Android +| Package Name | Version | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Command Line Tools | 12.0 | +| Android SDK Build-tools | 35.0.0
34.0.0 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-34 (rev 3)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1) | +| Android Support Repository | 47.0.0 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264
27.1.12297006 (default) | + +#### Environment variables +| Name | Value | +| ----------------------- | -------------------------------------------- | +| ANDROID_HOME | /usr/local/lib/android/sdk | +| ANDROID_NDK | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_HOME | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_LATEST_HOME | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_NDK_ROOT | /usr/local/lib/android/sdk/ndk/27.1.12297006 | +| ANDROID_SDK_ROOT | /usr/local/lib/android/sdk | + +### Installed apt packages +| Name | Version | +| ---------------------- | --------------------------- | +| acl | 2.3.2-1build1 | +| aria2 | 1.37.0+debian-1build3 | +| autoconf | 2.71-3 | +| automake | 1:1.16.5-1.3ubuntu1 | +| binutils | 2.42-4ubuntu2 | +| bison | 2:3.8.2+dfsg-1build2 | +| brotli | 1.1.0-2build2 | +| bzip2 | 1.0.8-5.1build0.1 | +| coreutils | 9.4-3ubuntu6 | +| curl | 8.5.0-2ubuntu10.4 | +| dbus | 1.14.10-4ubuntu4.1 | +| dnsutils | 1:9.18.28-0ubuntu0.24.04.1 | +| dpkg | 1.22.6ubuntu6.1 | +| dpkg-dev | 1.22.6ubuntu6.1 | +| fakeroot | 1.33-1 | +| file | 1:5.45-3build1 | +| findutils | 4.9.0-5build1 | +| flex | 2.6.4-8.2build1 | +| fonts-noto-color-emoji | 2.042-1 | +| ftp | 20230507-2build3 | +| g++ | 4:13.2.0-7ubuntu1 | +| gcc | 4:13.2.0-7ubuntu1 | +| gnupg2 | 2.4.4-2ubuntu17 | +| haveged | 1.9.14-1ubuntu2 | +| iproute2 | 6.1.0-1ubuntu6 | +| iputils-ping | 3:20240117-1build1 | +| jq | 1.7.1-3build1 | +| libssl-dev | 3.0.13-0ubuntu3.4 | +| libtool | 2.4.7-7build1 | +| libyaml-dev | 0.2.5-1build1 | +| locales | 2.39-0ubuntu8.3 | +| lz4 | 1.9.4-1build1.1 | +| m4 | 1.4.19-4build1 | +| make | 4.3-4.1build2 | +| mediainfo | 24.01.1-1build2 | +| mercurial | 6.7.2-1ubuntu2 | +| net-tools | 2.10-0.1ubuntu4 | +| netcat | 1.226-1ubuntu2 | +| openssh-client | 1:9.6p1-3ubuntu13.5 | +| p7zip-full | 16.02+transitional.1 | +| p7zip-rar | 16.02+transitional.1 | +| parallel | 20231122+ds-1 | +| patchelf | 0.18.0-1.1build1 | +| pigz | 2.8-1 | +| pkg-config | 1.8.1-2build1 | +| pollinate | 4.33-3.1ubuntu1 | +| python-is-python3 | 3.11.4-1 | +| rpm | 4.18.2+dfsg-2.1build2 | +| rsync | 3.2.7-1ubuntu1 | +| shellcheck | 0.9.0-1 | +| sphinxsearch | 2.2.11-8build1 | +| sqlite3 | 3.45.1-1ubuntu2 | +| ssh | 1:9.6p1-3ubuntu13.5 | +| sshpass | 1.09-1 | +| sudo | 1.9.15p5-3ubuntu5 | +| swig | 4.2.0-2ubuntu1 | +| tar | 1.35+dfsg-3build1 | +| telnet | 0.17+2.5-3ubuntu4 | +| texinfo | 7.1-3build2 | +| time | 1.9-0.2build1 | +| tk | 8.6.14build1 | +| tree | 2.1.1-2ubuntu3 | +| tzdata | 2024a-3ubuntu1.1 | +| unzip | 6.0-28ubuntu4.1 | +| upx | 4.2.2-3 | +| wget | 1.21.4-1ubuntu4.1 | +| xvfb | 2:21.1.12-1ubuntu1 | +| xz-utils | 5.6.1+really5.4.5-1build0.1 | +| zip | 3.0-13build1 | +| zsync | 0.6.2-5build1 | + diff --git a/images/ubuntu/assets/post-gen/cleanup-logs.sh b/images/ubuntu/assets/post-gen/cleanup-logs.sh new file mode 100644 index 0000000..335f848 --- /dev/null +++ b/images/ubuntu/assets/post-gen/cleanup-logs.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# journalctl +if command -v journalctl; then + journalctl --rotate + journalctl --vacuum-time=1s +fi + +# delete all .gz and rotated file +find /var/log -type f -regex ".*\.gz$" -delete +find /var/log -type f -regex ".*\.[0-9]$" -delete + +# wipe log files +find /var/log/ -type f -exec cp /dev/null {} \; \ No newline at end of file diff --git a/images/ubuntu/assets/post-gen/environment-variables.sh b/images/ubuntu/assets/post-gen/environment-variables.sh new file mode 100644 index 0000000..975c8a4 --- /dev/null +++ b/images/ubuntu/assets/post-gen/environment-variables.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Replace $HOME with the default user's home directory for environmental variables related to the default user home directory + +homeDir=$(cut -d: -f6 /etc/passwd | tail -1) +sed -i "s|\$HOME|$homeDir|g" /etc/environment \ No newline at end of file diff --git a/images/ubuntu/assets/post-gen/systemd-linger.sh b/images/ubuntu/assets/post-gen/systemd-linger.sh new file mode 100644 index 0000000..294c8f1 --- /dev/null +++ b/images/ubuntu/assets/post-gen/systemd-linger.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +# Enable user session on boot, not on login +UserId=$(cut -d: -f3 /etc/passwd | tail -1) +loginctl enable-linger $UserId diff --git a/images/ubuntu/assets/ubuntu2004.conf b/images/ubuntu/assets/ubuntu2004.conf new file mode 100644 index 0000000..63c41c2 --- /dev/null +++ b/images/ubuntu/assets/ubuntu2004.conf @@ -0,0 +1,2 @@ +# Name of pool supported by this image +POOL_NAME="Ubuntu 2004" diff --git a/images/ubuntu/assets/ubuntu2204.conf b/images/ubuntu/assets/ubuntu2204.conf new file mode 100644 index 0000000..8a7f60e --- /dev/null +++ b/images/ubuntu/assets/ubuntu2204.conf @@ -0,0 +1,2 @@ +# Name of pool supported by this image +POOL_NAME="Ubuntu 2204" diff --git a/images/ubuntu/scripts/build/Configure-Toolset.ps1 b/images/ubuntu/scripts/build/Configure-Toolset.ps1 new file mode 100644 index 0000000..df7e8ef --- /dev/null +++ b/images/ubuntu/scripts/build/Configure-Toolset.ps1 @@ -0,0 +1,83 @@ +################################################################################ +## File: Configure-Toolset.ps1 +## Team: CI-Build +## Desc: Configure toolset +################################################################################ + +Import-Module "$env:HELPER_SCRIPTS/../tests/Helpers.psm1" + +function Get-TCToolVersionPath { + param( + [Parameter(Mandatory)] + [string] $ToolName, + [Parameter(Mandatory)] + [string] $ToolVersion, + [Parameter(Mandatory)] + [string] $ToolArchitecture + ) + + $toolPath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath $ToolName + $toolPathVersion = Join-Path -Path $toolPath -ChildPath $ToolVersion + $foundVersion = Get-Item $toolPathVersion | Sort-Object -Property { [version] $_.name } -Descending | Select-Object -First 1 + $installationDir = Join-Path -Path $foundVersion -ChildPath $ToolArchitecture + + return $installationDir +} + +function Add-GlobalEnvironmentVariable { + param( + [Parameter(Mandatory)] + [string] $Name, + [Parameter(Mandatory)] + [string] $Value, + [string] $FilePath = "/etc/environment" + ) + + $envVar = "{0}={1}" -f $Name, $Value + Tee-Object -InputObject $envVar -FilePath $FilePath -Append +} + +$ErrorActionPreference = "Stop" + +Write-Host "Configure toolcache tools environment..." +$toolEnvConfigs = @{ + go = @{ + command = "ln -s {0}/bin/* /usr/bin/" + variableTemplate = "GOROOT_{0}_{1}_X64" + } +} + +# Get toolcache content from toolset +$tools = (Get-ToolsetContent).toolcache | Where-Object { $toolEnvConfigs.Keys -contains $_.name } + +foreach ($tool in $tools) { + $toolEnvConfig = $toolEnvConfigs[$tool.name] + + if (-not ([string]::IsNullOrEmpty($toolEnvConfig.variableTemplate))) { + foreach ($toolVersion in $tool.versions) { + Write-Host "Set $($tool.name) $toolVersion environment variable..." + $toolPath = Get-TCToolVersionPath -ToolName $tool.name -ToolVersion $toolVersion -ToolArchitecture $tool.arch + $envVariableName = $toolEnvConfig.variableTemplate -f $toolVersion.split(".") + + Add-GlobalEnvironmentVariable -Name $envVariableName -Value $toolPath + } + } + + # Invoke command and add env variable for the default tool version + if (-not ([string]::IsNullOrEmpty($tool.default))) { + $toolDefaultPath = Get-TCToolVersionPath -ToolName $tool.name -ToolVersion $tool.default -ToolArchitecture $tool.arch + + if (-not ([string]::IsNullOrEmpty($toolEnvConfig.defaultVariable))) { + Write-Host "Set default $($toolEnvConfig.defaultVariable) for $($tool.name) $($tool.default) environment variable..." + Add-GlobalEnvironmentVariable -Name $toolEnvConfig.defaultVariable -Value $toolDefaultPath + } + + if (-not ([string]::IsNullOrEmpty($toolEnvConfig.command))) { + $command = $toolEnvConfig.command -f $toolDefaultPath + Write-Host "Invoke $command command for default $($tool.name) $($tool.default) ..." + Invoke-Expression -Command $command + } + } +} + +Invoke-PesterTests -TestFile "Toolset" -TestName "Toolset" diff --git a/images/ubuntu/scripts/build/Install-PowerShellAzModules.ps1 b/images/ubuntu/scripts/build/Install-PowerShellAzModules.ps1 new file mode 100644 index 0000000..626dc2f --- /dev/null +++ b/images/ubuntu/scripts/build/Install-PowerShellAzModules.ps1 @@ -0,0 +1,27 @@ +################################################################################ +## File: Install-PowerShellAzModules.ps1 +## Desc: Install Az modules for PowerShell +################################################################################ + +$ErrorActionPreference = "Stop" +$ProgressPreference = "SilentlyContinue" + +Import-Module "$env:HELPER_SCRIPTS/../tests/Helpers.psm1" + +# Get modules content from toolset +$modules = (Get-ToolsetContent).azureModules +$installPSModulePath = "/usr/share" + +foreach ($module in $modules) { + $moduleName = $module.name + + Write-Host "Installing ${moduleName} to the ${installPSModulePath} path..." + foreach ($version in $module.versions) { + $modulePath = Join-Path -Path $installPSModulePath -ChildPath "${moduleName}_${version}" + Write-Host " - $version [$modulePath]" + Save-Module -Path $modulePath -Name $moduleName -RequiredVersion $version -Force + } + +} + +Invoke-PesterTests -TestFile "PowerShellModules" -TestName "AzureModules" diff --git a/images/ubuntu/scripts/build/Install-PowerShellModules.ps1 b/images/ubuntu/scripts/build/Install-PowerShellModules.ps1 new file mode 100644 index 0000000..14bb757 --- /dev/null +++ b/images/ubuntu/scripts/build/Install-PowerShellModules.ps1 @@ -0,0 +1,35 @@ +################################################################################ +## File: Install-PowerShellModules.ps1 +## Desc: Install modules for PowerShell +################################################################################ + +$ErrorActionPreference = "Stop" +$ProgressPreference = "SilentlyContinue" + +Import-Module "$env:HELPER_SCRIPTS/../tests/Helpers.psm1" + +# Specifies the installation policy +Set-PSRepository -InstallationPolicy Trusted -Name PSGallery + +# Try to update PowerShellGet before the actual installation +Install-Module -Name PowerShellGet -Force +Update-Module -Name PowerShellGet -Force + +# Install PowerShell modules +$modules = (Get-ToolsetContent).powershellModules + +foreach($module in $modules) { + $moduleName = $module.name + + Write-Host "Installing ${moduleName} module" + if ($module.versions) { + foreach ($version in $module.versions) { + Write-Host " - $version" + Install-Module -Name $moduleName -RequiredVersion $version -Scope AllUsers -SkipPublisherCheck -Force + } + } else { + Install-Module -Name $moduleName -Scope AllUsers -SkipPublisherCheck -Force + } +} + +Invoke-PesterTests -TestFile "PowerShellModules" -TestName "PowerShellModules" diff --git a/images/ubuntu/scripts/build/Install-Toolset.ps1 b/images/ubuntu/scripts/build/Install-Toolset.ps1 new file mode 100644 index 0000000..2ebec67 --- /dev/null +++ b/images/ubuntu/scripts/build/Install-Toolset.ps1 @@ -0,0 +1,55 @@ +################################################################################ +## File: Install-Toolset.ps1 +## Team: CI-Build +## Desc: Install toolset +################################################################################ + +Import-Module "$env:HELPER_SCRIPTS/../tests/Helpers.psm1" + +function Install-Asset { + param( + [Parameter(Mandatory = $true)] + [object] $ReleaseAsset + ) + + Write-Host "Download $($ReleaseAsset.filename)" + $assetArchivePath = Invoke-DownloadWithRetry $ReleaseAsset.download_url + + Write-Host "Extract $($ReleaseAsset.filename) content..." + $assetFolderPath = Join-Path "/tmp" "$($ReleaseAsset.filename)-temp-dir" + New-Item -ItemType Directory -Path $assetFolderPath | Out-Null + tar -xzf $assetArchivePath -C $assetFolderPath + + Write-Host "Invoke installation script..." + Push-Location -Path $assetFolderPath + Invoke-Expression "bash ./setup.sh" + Pop-Location +} + +$ErrorActionPreference = "Stop" + +# Get toolcache content from toolset +$tools = (Get-ToolsetContent).toolcache | Where-Object { $_.url -ne $null } + +foreach ($tool in $tools) { + # Get versions manifest for current tool + $assets = Invoke-RestMethod $tool.url + + # Get github release asset for each version + foreach ($toolVersion in $tool.versions) { + $asset = $assets | Where-Object version -like $toolVersion ` + | Select-Object -ExpandProperty files ` + | Where-Object { ($_.platform -eq $tool.platform) -and ($_.arch -eq $tool.arch) -and ($_.platform_version -eq $tool.platform_version)} ` + | Select-Object -First 1 + + if (-not $asset) { + Write-Host "Asset for $($tool.name) $toolVersion $($tool.arch) not found in versions manifest" + exit 1 + } + + Write-Host "Installing $($tool.name) $toolVersion $($tool.arch)..." + Install-Asset -ReleaseAsset $asset + } + + chown -R "$($env:SUDO_USER):$($env:SUDO_USER)" "/opt/hostedtoolcache/$($tool.name)" +} diff --git a/images/ubuntu/scripts/build/cleanup.sh b/images/ubuntu/scripts/build/cleanup.sh new file mode 100644 index 0000000..17c7ccc --- /dev/null +++ b/images/ubuntu/scripts/build/cleanup.sh @@ -0,0 +1,44 @@ +#!/bin/bash -e +################################################################################ +## File: cleanup.sh +## Desc: Perform cleanup +################################################################################ + +# before cleanup +before=$(df / -Pm | awk 'NR==2{print $4}') + +# clears out the local repository of retrieved package files +# It removes everything but the lock file from /var/cache/apt/archives/ and /var/cache/apt/archives/partial +apt-get clean +rm -rf /tmp/* +rm -rf /root/.cache + +# journalctl +if command -v journalctl; then + journalctl --rotate + journalctl --vacuum-time=1s +fi + +# delete all .gz and rotated file +find /var/log -type f -regex ".*\.gz$" -delete +find /var/log -type f -regex ".*\.[0-9]$" -delete + +# wipe log files +find /var/log/ -type f -exec cp /dev/null {} \; + +# delete symlink for tests running +rm -f /usr/local/bin/invoke_tests + +# remove apt mock +prefix=/usr/local/bin +for tool in apt apt-get apt-key;do + sudo rm -f $prefix/$tool +done + +# after cleanup +after=$(df / -Pm | awk 'NR==2{print $4}') + +# display size +echo "Before: $before MB" +echo "After : $after MB" +echo "Delta : $(($after-$before)) MB" diff --git a/images/ubuntu/scripts/build/configure-apt-mock.sh b/images/ubuntu/scripts/build/configure-apt-mock.sh new file mode 100644 index 0000000..ece6b89 --- /dev/null +++ b/images/ubuntu/scripts/build/configure-apt-mock.sh @@ -0,0 +1,54 @@ +#!/bin/bash -e +################################################################################ +## File: configure-apt-mock.sh +## Desc: A temporary workaround for https://github.com/Azure/azure-linux-extensions/issues/1238. +## Cleaned up during cleanup.sh. +################################################################################ + +prefix=/usr/local/bin + +for real_tool in /usr/bin/apt /usr/bin/apt-get /usr/bin/apt-key; do + tool=$(basename $real_tool) + cat >$prefix/$tool <\$err + + # no errors, break the loop and continue normal flow + test -f \$err || break + cat \$err >&2 + + retry=false + + if grep -q 'Could not get lock' \$err;then + # apt db locked needs retry + retry=true + elif grep -q 'Could not open file /var/lib/apt/lists' \$err;then + # apt update is not completed, needs retry + retry=true + elif grep -q 'IPC connect call failed' \$err;then + # the delay should help with gpg-agent not ready + retry=true + elif grep -q 'Temporary failure in name resolution' \$err;then + # It looks like DNS is not updated with random generated hostname yet + retry=true + elif grep -q 'dpkg frontend is locked by another process' \$err;then + # dpkg process is busy by another process + retry=true + fi + + rm \$err + if [ \$retry = false ]; then + break + fi + + sleep 5 + echo "...retry \$i" + i=\$((i + 1)) +done +EOT + chmod +x $prefix/$tool +done diff --git a/images/ubuntu/scripts/build/configure-apt-sources.sh b/images/ubuntu/scripts/build/configure-apt-sources.sh new file mode 100644 index 0000000..b5afda4 --- /dev/null +++ b/images/ubuntu/scripts/build/configure-apt-sources.sh @@ -0,0 +1,16 @@ +#!/bin/bash -e +################################################################################ +## File: configure-apt-sources.sh +## Desc: Configure apt sources with failover from Azure to Ubuntu archives. +################################################################################ + +touch /etc/apt/apt-mirrors.txt + +printf "http://azure.archive.ubuntu.com/ubuntu/\tpriority:1\n" | tee -a /etc/apt/apt-mirrors.txt +printf "http://archive.ubuntu.com/ubuntu/\tpriority:2\n" | tee -a /etc/apt/apt-mirrors.txt +printf "http://security.ubuntu.com/ubuntu/\tpriority:3\n" | tee -a /etc/apt/apt-mirrors.txt + +sed -i 's/http:\/\/azure.archive.ubuntu.com\/ubuntu\//mirror+file:\/etc\/apt\/apt-mirrors.txt/' /etc/apt/sources.list + +# Apt changes to survive Cloud Init +cp -f /etc/apt/sources.list /etc/cloud/templates/sources.list.ubuntu.tmpl diff --git a/images/ubuntu/scripts/build/configure-apt.sh b/images/ubuntu/scripts/build/configure-apt.sh new file mode 100644 index 0000000..c75bcc3 --- /dev/null +++ b/images/ubuntu/scripts/build/configure-apt.sh @@ -0,0 +1,55 @@ +#!/bin/bash -e +################################################################################ +## File: configure-apt.sh +## Desc: Configure apt, install jq and apt-fast packages. +################################################################################ + +source $HELPER_SCRIPTS/os.sh + +# Stop and disable apt-daily upgrade services; +systemctl stop apt-daily.timer +systemctl disable apt-daily.timer +systemctl disable apt-daily.service +systemctl stop apt-daily-upgrade.timer +systemctl disable apt-daily-upgrade.timer +systemctl disable apt-daily-upgrade.service + +# Enable retry logic for apt up to 10 times +echo "APT::Acquire::Retries \"10\";" > /etc/apt/apt.conf.d/80-retries + +# Configure apt to always assume Y +echo "APT::Get::Assume-Yes \"true\";" > /etc/apt/apt.conf.d/90assumeyes + +# APT understands a field called Phased-Update-Percentage which can be used to control the rollout of a new version. It is an integer between 0 and 100. +# In case you have multiple systems that you want to receive the same set of updates, +# you can set APT::Machine-ID to a UUID such that they all phase the same, +# or set APT::Get::Never-Include-Phased-Updates or APT::Get::Always-Include-Phased-Updates to true such that APT will never/always consider phased updates. +# apt-cache policy pkgname +echo 'APT::Get::Always-Include-Phased-Updates "true";' > /etc/apt/apt.conf.d/99-phased-updates + +# Fix bad proxy and http headers settings +cat <> /etc/apt/apt.conf.d/99bad_proxy +Acquire::http::Pipeline-Depth 0; +Acquire::http::No-Cache true; +Acquire::BrokenProxy true; +EOF + +# Uninstall unattended-upgrades +apt-get purge unattended-upgrades + +echo 'APT sources' +if ! is_ubuntu24; then + cat /etc/apt/sources.list +else + cat /etc/apt/sources.list.d/ubuntu.sources +fi + +apt-get update +# Install jq +apt-get install jq + +if ! is_ubuntu24; then + # Install apt-fast using quick-install.sh + # https://github.com/ilikenwf/apt-fast + bash -c "$(curl -fsSL https://raw.githubusercontent.com/ilikenwf/apt-fast/master/quick-install.sh)" +fi diff --git a/images/ubuntu/scripts/build/configure-dpkg.sh b/images/ubuntu/scripts/build/configure-dpkg.sh new file mode 100644 index 0000000..2c52372 --- /dev/null +++ b/images/ubuntu/scripts/build/configure-dpkg.sh @@ -0,0 +1,32 @@ +#!/bin/bash -e +################################################################################ +## File: configure-dpkg.sh +## Desc: Configure dpkg +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/etc-environment.sh + +# This is the anti-frontend. It never interacts with you at all, +# and makes the default answers be used for all questions. It +# might mail error messages to root, but that's it; otherwise it +# is completely silent and unobtrusive, a perfect frontend for +# automatic installs. If you are using this front-end, and require +# non-default answers to questions, you will need to pre-seed the +# debconf database +set_etc_environment_variable "DEBIAN_FRONTEND" "noninteractive" + +# dpkg can be instructed not to ask for confirmation +# when replacing a configuration file (with the --force-confdef --force-confold options) +cat <> /etc/apt/apt.conf.d/10dpkg-options +Dpkg::Options { + "--force-confdef"; + "--force-confold"; +} +EOF + +# hide information about packages that are no longer required +cat <> /etc/apt/apt.conf.d/10apt-autoremove +APT::Get::AutomaticRemove "0"; +APT::Get::HideAutoRemove "1"; +EOF diff --git a/images/ubuntu/scripts/build/configure-environment.sh b/images/ubuntu/scripts/build/configure-environment.sh new file mode 100644 index 0000000..3c14929 --- /dev/null +++ b/images/ubuntu/scripts/build/configure-environment.sh @@ -0,0 +1,71 @@ +#!/bin/bash -e +################################################################################ +## File: configure-environment.sh +## Desc: Configure system and environment +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/os.sh +source $HELPER_SCRIPTS/etc-environment.sh + +# Set ImageVersion and ImageOS env variables +set_etc_environment_variable "ImageVersion" "${IMAGE_VERSION}" +set_etc_environment_variable "ImageOS" "${IMAGE_OS}" + +# Set the ACCEPT_EULA variable to Y value to confirm your acceptance of the End-User Licensing Agreement +set_etc_environment_variable "ACCEPT_EULA" "Y" + +# This directory is supposed to be created in $HOME and owned by user(https://github.com/actions/runner-images/issues/491) +mkdir -p /etc/skel/.config/configstore +set_etc_environment_variable "XDG_CONFIG_HOME" '$HOME/.config' + +# Change waagent entries to use /mnt for swap file +sed -i 's/ResourceDisk.Format=n/ResourceDisk.Format=y/g' /etc/waagent.conf +sed -i 's/ResourceDisk.EnableSwap=n/ResourceDisk.EnableSwap=y/g' /etc/waagent.conf +sed -i 's/ResourceDisk.SwapSizeMB=0/ResourceDisk.SwapSizeMB=4096/g' /etc/waagent.conf + +# Add localhost alias to ::1 IPv6 +sed -i 's/::1 ip6-localhost ip6-loopback/::1 localhost ip6-localhost ip6-loopback/g' /etc/hosts + +# Prepare directory and env variable for toolcache +AGENT_TOOLSDIRECTORY=/opt/hostedtoolcache +mkdir $AGENT_TOOLSDIRECTORY +set_etc_environment_variable "AGENT_TOOLSDIRECTORY" "${AGENT_TOOLSDIRECTORY}" +set_etc_environment_variable "RUNNER_TOOL_CACHE" "${AGENT_TOOLSDIRECTORY}" +chmod -R 777 $AGENT_TOOLSDIRECTORY + +# https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html +# https://www.suse.com/support/kb/doc/?id=000016692 +echo 'vm.max_map_count=262144' | tee -a /etc/sysctl.conf + +# https://kind.sigs.k8s.io/docs/user/known-issues/#pod-errors-due-to-too-many-open-files +echo 'fs.inotify.max_user_watches=655360' | tee -a /etc/sysctl.conf +echo 'fs.inotify.max_user_instances=1280' | tee -a /etc/sysctl.conf + +# https://github.com/actions/runner-images/issues/9491 +echo 'vm.mmap_rnd_bits=28' | tee -a /etc/sysctl.conf + +# https://github.com/actions/runner-images/pull/7860 +netfilter_rule='/etc/udev/rules.d/50-netfilter.rules' +rules_directory="$(dirname "${netfilter_rule}")" +mkdir -p $rules_directory +touch $netfilter_rule +echo 'ACTION=="add", SUBSYSTEM=="module", KERNEL=="nf_conntrack", RUN+="/usr/sbin/sysctl net.netfilter.nf_conntrack_tcp_be_liberal=1"' | tee -a $netfilter_rule + +# Create symlink for tests running +chmod +x $HELPER_SCRIPTS/invoke-tests.sh +ln -s $HELPER_SCRIPTS/invoke-tests.sh /usr/local/bin/invoke_tests + +# Disable motd updates metadata +sed -i 's/ENABLED=1/ENABLED=0/g' /etc/default/motd-news + +if [[ -f "/etc/fwupd/daemon.conf" ]]; then + sed -i 's/UpdateMotd=true/UpdateMotd=false/g' /etc/fwupd/daemon.conf + systemctl mask fwupd-refresh.timer +fi + +# Disable to load providers +# https://github.com/microsoft/azure-pipelines-agent/issues/3834 +if is_ubuntu22; then + sed -i 's/openssl_conf = openssl_init/#openssl_conf = openssl_init/g' /etc/ssl/openssl.cnf +fi diff --git a/images/ubuntu/scripts/build/configure-image-data.sh b/images/ubuntu/scripts/build/configure-image-data.sh new file mode 100644 index 0000000..fb873d1 --- /dev/null +++ b/images/ubuntu/scripts/build/configure-image-data.sh @@ -0,0 +1,32 @@ +#!/bin/bash -e +################################################################################ +## File: configure-image-data.sh +## Desc: Create a file with image data and documentation links +################################################################################ + +imagedata_file=$IMAGEDATA_FILE +image_version=$IMAGE_VERSION +image_version_major=${image_version/.*/} +image_version_minor=$(echo $image_version | cut -d "." -f 2) +os_name=$(lsb_release -ds | sed "s/ /\\\n/g") +os_version=$(lsb_release -rs) +image_label="ubuntu-${os_version}" +version_major=${os_version/.*/} +version_wo_dot=${os_version/./} +github_url="https://github.com/actions/runner-images/blob" + +software_url="${github_url}/ubuntu${version_major}/${image_version_major}.${image_version_minor}/images/ubuntu/Ubuntu${version_wo_dot}-Readme.md" +releaseUrl="https://github.com/actions/runner-images/releases/tag/ubuntu${version_major}%2F${image_version_major}.${image_version_minor}" + +cat < $imagedata_file +[ + { + "group": "Operating System", + "detail": "${os_name}" + }, + { + "group": "Runner Image", + "detail": "Image: ${image_label}\nVersion: ${image_version}\nIncluded Software: ${software_url}\nImage Release: ${releaseUrl}" + } +] +EOF diff --git a/images/ubuntu/scripts/build/configure-limits.sh b/images/ubuntu/scripts/build/configure-limits.sh new file mode 100644 index 0000000..e6cb0fe --- /dev/null +++ b/images/ubuntu/scripts/build/configure-limits.sh @@ -0,0 +1,18 @@ +#!/bin/bash -e +################################################################################ +## File: configure-limits.sh +## Desc: Configure limits +################################################################################ + +echo 'session required pam_limits.so' >> /etc/pam.d/common-session +echo 'session required pam_limits.so' >> /etc/pam.d/common-session-noninteractive +echo 'DefaultLimitNOFILE=65536' >> /etc/systemd/system.conf +echo 'DefaultLimitSTACK=16M:infinity' >> /etc/systemd/system.conf + +# Raise Number of File Descriptors +echo '* soft nofile 65536' >> /etc/security/limits.conf +echo '* hard nofile 65536' >> /etc/security/limits.conf + +# Double stack size from default 8192KB +echo '* soft stack 16384' >> /etc/security/limits.conf +echo '* hard stack 16384' >> /etc/security/limits.conf diff --git a/images/ubuntu/scripts/build/configure-snap.sh b/images/ubuntu/scripts/build/configure-snap.sh new file mode 100644 index 0000000..e1858f6 --- /dev/null +++ b/images/ubuntu/scripts/build/configure-snap.sh @@ -0,0 +1,26 @@ +#!/bin/bash -e +################################################################################ +## File: configure-snap.sh +## Desc: Configure snap +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/etc-environment.sh + +# Update /etc/environment to include /snap/bin in PATH +# because /etc/profile.d is ignored by `--norc` shell launch option + +prepend_etc_environment_path "/snap/bin" + +# Put snapd auto refresh on hold +# as it may generate too much traffic on Canonical's snap server +# when they are rolling a new major update out. +# Hold is calculated as today's date + 60 days + +# snapd is started automatically, but during image generation +# a unix socket may die, restart snapd.service (and therefore snapd.socket) +# to make sure the socket is alive. + +systemctl restart snapd.socket +systemctl restart snapd +snap set system refresh.hold="$(date --date='today+60 days' +%Y-%m-%dT%H:%M:%S%:z)" diff --git a/images/ubuntu/scripts/build/configure-system.sh b/images/ubuntu/scripts/build/configure-system.sh new file mode 100644 index 0000000..4c82e35 --- /dev/null +++ b/images/ubuntu/scripts/build/configure-system.sh @@ -0,0 +1,40 @@ +#!/bin/bash -e +################################################################################ +## File: configure-system.sh +## Desc: Post deployment system configuration actions +################################################################################ + +source $HELPER_SCRIPT_FOLDER/etc-environment.sh +source $HELPER_SCRIPT_FOLDER/os.sh + +mv -f /imagegeneration/post-generation /opt + +echo "chmod -R 777 /opt" +chmod -R 777 /opt +echo "chmod -R 777 /usr/share" +chmod -R 777 /usr/share + +chmod 755 $IMAGE_FOLDER + +# Remove quotes around PATH +ENVPATH=$(grep 'PATH=' /etc/environment | head -n 1 | sed -z 's/^PATH=*//') +ENVPATH=${ENVPATH#"\""} +ENVPATH=${ENVPATH%"\""} +replace_etc_environment_variable "PATH" "${ENVPATH}" +echo "Updated /etc/environment: $(cat /etc/environment)" + +# Clean yarn and npm cache +if yarn --version > /dev/null; then + yarn cache clean +fi + +if npm --version; then + npm cache clean --force +fi + +if is_ubuntu24; then +# Prevent needrestart from restarting the provisioner service. +# Currently only happens on Ubuntu 24.04, so make it conditional for the time being +# as configuration is too different between Ubuntu versions. + sed -i '/^\s*};/i \ qr(^runner-provisioner) => 0,' /etc/needrestart/needrestart.conf +fi diff --git a/images/ubuntu/scripts/build/install-actions-cache.sh b/images/ubuntu/scripts/build/install-actions-cache.sh new file mode 100644 index 0000000..22c2f71 --- /dev/null +++ b/images/ubuntu/scripts/build/install-actions-cache.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e +################################################################################ +## File: install-actions-cache.sh +## Desc: Download latest release from https://github.com/actions/action-versions +## Maintainer: #actions-runtime and @TingluoHuang +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh +source $HELPER_SCRIPTS/etc-environment.sh + +# Prepare directory and env variable for ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE +ACTION_ARCHIVE_CACHE_DIR=/opt/actionarchivecache +mkdir -p $ACTION_ARCHIVE_CACHE_DIR +chmod -R 777 $ACTION_ARCHIVE_CACHE_DIR +echo "Setting up ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE variable to ${ACTION_ARCHIVE_CACHE_DIR}" +set_etc_environment_variable "ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE" "${ACTION_ARCHIVE_CACHE_DIR}" + +# Download latest release from github.com/actions/action-versions and untar to /opt/actionarchivecache +download_url=$(resolve_github_release_asset_url "actions/action-versions" "endswith(\"action-versions.tar.gz\")" "latest") +archive_path=$(download_with_retry "$download_url") +tar -xzf "$archive_path" -C $ACTION_ARCHIVE_CACHE_DIR + +invoke_tests "ActionArchiveCache" diff --git a/images/ubuntu/scripts/build/install-aliyun-cli.sh b/images/ubuntu/scripts/build/install-aliyun-cli.sh new file mode 100644 index 0000000..13d959d --- /dev/null +++ b/images/ubuntu/scripts/build/install-aliyun-cli.sh @@ -0,0 +1,36 @@ +#!/bin/bash -e +################################################################################ +## File: install-aliyun-cli.sh +## Desc: Install Alibaba Cloud CLI +## Supply chain security: Alibaba Cloud CLI - checksum validation +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/os.sh +source $HELPER_SCRIPTS/install.sh + +# Install Alibaba Cloud CLI +# Pin tool version on ubuntu20 due to issues with GLIBC_2.32 not available +if is_ubuntu20; then + toolset_version=$(get_toolset_value '.aliyunCli.version') + download_url="https://github.com/aliyun/aliyun-cli/releases/download/v$toolset_version/aliyun-cli-linux-$toolset_version-amd64.tgz" +else + download_url=$(resolve_github_release_asset_url "aliyun/aliyun-cli" "contains(\"aliyun-cli-linux\") and endswith(\"amd64.tgz\")" "latest") + hash_url="https://github.com/aliyun/aliyun-cli/releases/latest/download/SHASUMS256.txt" +fi + +archive_path=$(download_with_retry "$download_url") + +# Supply chain security - Alibaba Cloud CLI +if is_ubuntu20; then + external_hash=$(get_toolset_value '.aliyunCli.sha256') +else + external_hash=$(get_checksum_from_url "$hash_url" "aliyun-cli-linux.*amd64.tgz" "SHA256") +fi + +use_checksum_comparison "$archive_path" "$external_hash" + +tar xzf "$archive_path" +mv aliyun /usr/local/bin + +invoke_tests "CLI.Tools" "Aliyun CLI" diff --git a/images/ubuntu/scripts/build/install-android-sdk.sh b/images/ubuntu/scripts/build/install-android-sdk.sh new file mode 100644 index 0000000..51c9d81 --- /dev/null +++ b/images/ubuntu/scripts/build/install-android-sdk.sh @@ -0,0 +1,121 @@ +#!/bin/bash -e +################################################################################ +## File: install-android-sdk.sh +## Desc: Install Android SDK and tools +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/os.sh +source $HELPER_SCRIPTS/install.sh +source $HELPER_SCRIPTS/etc-environment.sh + +add_filtered_installation_components() { + local minimum_version=$1 + shift + local tools_array=("$@") + + for item in ${tools_array[@]}; do + # Take the last argument after splitting string by ';'' and '-'' + item_version=$(echo "${item##*[-;]}") + + # Semver 'comparison'. Add item to components array, if item's version is greater than or equal to minimum version + if [[ "$(printf "${minimum_version}\n${item_version}\n" | sort -V | head -n1)" == "$minimum_version" ]]; then + components+=($item) + fi + done +} + +get_full_ndk_version() { + local major_version=$1 + + ndk_version=$($SDKMANAGER --list | grep "ndk;${major_version}\." | awk '{gsub("ndk;", ""); print $1}' | sort -V | tail -n1) + echo "$ndk_version" +} + +# Set env variable for SDK Root (https://developer.android.com/studio/command-line/variables) +ANDROID_ROOT=/usr/local/lib/android +ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk +SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager +set_etc_environment_variable "ANDROID_SDK_ROOT" "${ANDROID_SDK_ROOT}" + +# ANDROID_HOME is deprecated, but older versions of Gradle rely on it +set_etc_environment_variable "ANDROID_HOME" "${ANDROID_SDK_ROOT}" + +# Create android sdk directory +mkdir -p ${ANDROID_SDK_ROOT} + +# Download the latest command line tools so that we can accept all of the licenses. +# See https://developer.android.com/studio/#command-tools +cmdline_tools_package=$(get_toolset_value '.android."cmdline-tools"') +if [[ $cmdline_tools_version == "latest" ]]; then + REPOSITORY_XML_URL="https://dl.google.com/android/repository/repository2-1.xml" + repository_xml_file=$(download_with_retry "$REPOSITORY_XML_URL") + cmdline_tools_package=$( + yq -p=xml \ + '.sdk-repository.remotePackage[] | select(."+@path" == "cmdline-tools;latest" and .channelRef."+@ref" == "channel-0").archives.archive[].complete.url | select(contains("commandlinetools-linux"))' \ + "${repository_xml_file}" + ) + + if [[ -z $cmdline_tools_package ]]; then + echo "Failed to parse latest command-line tools version" + exit 1 + fi +fi + +# Install command line tools +archive_path=$(download_with_retry "https://dl.google.com/android/repository/${cmdline_tools_package}") +unzip -qq "$archive_path" -d ${ANDROID_SDK_ROOT}/cmdline-tools +# Command line tools need to be placed in ${ANDROID_SDK_ROOT}/sdk/cmdline-tools/latest to determine SDK root +mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/latest + +# Check sdk manager installation +if ${SDKMANAGER} --list 1>/dev/null; then + echo "Android SDK manager was installed" +else + echo "Android SDK manager was not installed" + exit 1 +fi + +# Get toolset values and prepare environment variables +minimum_build_tool_version=$(get_toolset_value '.android.build_tools_min_version') +minimum_platform_version=$(get_toolset_value '.android.platform_min_version') +android_ndk_major_default=$(get_toolset_value '.android.ndk.default') +android_ndk_major_versions=($(get_toolset_value '.android.ndk.versions[]')) +android_ndk_major_latest=(${android_ndk_major_versions[-1]}) + +ndk_default_full_version=$(get_full_ndk_version $android_ndk_major_default) +ndk_latest_full_version=$(get_full_ndk_version $android_ndk_major_latest) +ANDROID_NDK=${ANDROID_SDK_ROOT}/ndk/${ndk_default_full_version} +# 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 +set_etc_environment_variable "ANDROID_NDK" "${ANDROID_NDK}" +set_etc_environment_variable "ANDROID_NDK_HOME" "${ANDROID_NDK}" +set_etc_environment_variable "ANDROID_NDK_ROOT" "${ANDROID_NDK}" +set_etc_environment_variable "ANDROID_NDK_LATEST_HOME" "${ANDROID_SDK_ROOT}/ndk/${ndk_latest_full_version}" + +# Prepare components for installation +extras=$(get_toolset_value '.android.extra_list[] | "extras;" + .') +addons=$(get_toolset_value '.android.addon_list[] | "add-ons;" + .') +additional=$(get_toolset_value '.android.additional_tools[]') +components=("${extras[@]}" "${addons[@]}" "${additional[@]}") + +for ndk_major_version in "${android_ndk_major_versions[@]}"; do + ndk_full_version=$(get_full_ndk_version $ndk_major_version) + components+=("ndk;$ndk_full_version") +done + +available_platforms=($($SDKMANAGER --list | sed -n '/Available Packages:/,/^$/p' | grep "platforms;android-[0-9]" | cut -d"|" -f 1)) +all_build_tools=($($SDKMANAGER --list | grep "build-tools;" | cut -d"|" -f 1 | sort -u)) +available_build_tools=$(echo ${all_build_tools[@]//*rc[0-9]/}) + +add_filtered_installation_components $minimum_platform_version "${available_platforms[@]}" +add_filtered_installation_components $minimum_build_tool_version "${available_build_tools[@]}" + +# Install components +echo "y" | $SDKMANAGER ${components[@]} + +# Add required permissions +chmod -R a+rwx ${ANDROID_SDK_ROOT} + +reload_etc_environment + +invoke_tests "Android" diff --git a/images/ubuntu/scripts/build/install-apache.sh b/images/ubuntu/scripts/build/install-apache.sh new file mode 100644 index 0000000..7e2cc93 --- /dev/null +++ b/images/ubuntu/scripts/build/install-apache.sh @@ -0,0 +1,14 @@ +#!/bin/bash -e +################################################################################ +## File: install-apache.sh +## Desc: Install Apache HTTP Server +################################################################################ + +# Install Apache +apt-get install apache2 + +# Disable apache2.service +systemctl is-active --quiet apache2.service && systemctl stop apache2.service +systemctl disable apache2.service + +invoke_tests "WebServers" "Apache" diff --git a/images/ubuntu/scripts/build/install-apt-common.sh b/images/ubuntu/scripts/build/install-apt-common.sh new file mode 100644 index 0000000..bb6aa55 --- /dev/null +++ b/images/ubuntu/scripts/build/install-apt-common.sh @@ -0,0 +1,18 @@ +#!/bin/bash -e +################################################################################ +## File: install-apt-common.sh +## Desc: Install basic command line utilities and dev packages +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +common_packages=$(get_toolset_value .apt.common_packages[]) +cmd_packages=$(get_toolset_value .apt.cmd_packages[]) + +for package in $common_packages $cmd_packages; do + echo "Install $package" + apt-get install --no-install-recommends $package +done + +invoke_tests "Apt" diff --git a/images/ubuntu/scripts/build/install-apt-vital.sh b/images/ubuntu/scripts/build/install-apt-vital.sh new file mode 100644 index 0000000..bb80b00 --- /dev/null +++ b/images/ubuntu/scripts/build/install-apt-vital.sh @@ -0,0 +1,11 @@ +#!/bin/bash -e +################################################################################ +## File: install-apt-vital.sh +## Desc: Install vital command line utilities +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +vital_packages=$(get_toolset_value .apt.vital_packages[]) +apt-get install --no-install-recommends $vital_packages diff --git a/images/ubuntu/scripts/build/install-aws-tools.sh b/images/ubuntu/scripts/build/install-aws-tools.sh new file mode 100644 index 0000000..0f62149 --- /dev/null +++ b/images/ubuntu/scripts/build/install-aws-tools.sh @@ -0,0 +1,32 @@ +#!/bin/bash -e +################################################################################ +## File: install-aws-tools.sh +## Desc: Install the AWS CLI, Session Manager plugin for the AWS CLI, and AWS SAM CLI +## Supply chain security: AWS SAM CLI - checksum validation +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/os.sh +source $HELPER_SCRIPTS/install.sh + +awscliv2_archive_path=$(download_with_retry "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip") +unzip -qq "$awscliv2_archive_path" -d /tmp +/tmp/aws/install -i /usr/local/aws-cli -b /usr/local/bin + +smplugin_deb_path=$(download_with_retry "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb") +apt-get install "$smplugin_deb_path" + +# Download the latest aws sam cli release +aws_sam_cli_archive_name="aws-sam-cli-linux-x86_64.zip" +sam_cli_download_url=$(resolve_github_release_asset_url "aws/aws-sam-cli" "endswith(\"$aws_sam_cli_archive_name\")" "latest") +aws_sam_cli_archive_path=$(download_with_retry "$sam_cli_download_url") + +# Supply chain security - AWS SAM CLI +aws_sam_cli_hash=$(get_checksum_from_github_release "aws/aws-sam-cli" "${aws_sam_cli_archive_name}.. " "latest" "SHA256") +use_checksum_comparison "$aws_sam_cli_archive_path" "$aws_sam_cli_hash" + +# Install the latest aws sam cli release +unzip "$aws_sam_cli_archive_path" -d /tmp +/tmp/install + +invoke_tests "CLI.Tools" "AWS" diff --git a/images/ubuntu/scripts/build/install-azcopy.sh b/images/ubuntu/scripts/build/install-azcopy.sh new file mode 100644 index 0000000..640b0a4 --- /dev/null +++ b/images/ubuntu/scripts/build/install-azcopy.sh @@ -0,0 +1,18 @@ +#!/bin/bash -e +################################################################################ +## File: install-azcopy.sh +## Desc: Install AzCopy +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Install AzCopy10 +archive_path=$(download_with_retry "https://aka.ms/downloadazcopy-v10-linux") +tar xzf "$archive_path" --strip-components=1 -C /tmp +install /tmp/azcopy /usr/local/bin/azcopy + +# Create azcopy 10 alias for backward compatibility +ln -sf /usr/local/bin/azcopy /usr/local/bin/azcopy10 + +invoke_tests "Tools" "azcopy" diff --git a/images/ubuntu/scripts/build/install-azure-cli.sh b/images/ubuntu/scripts/build/install-azure-cli.sh new file mode 100644 index 0000000..0004f7b --- /dev/null +++ b/images/ubuntu/scripts/build/install-azure-cli.sh @@ -0,0 +1,15 @@ +#!/bin/bash -e +################################################################################ +## File: install-azure-cli.sh +## Desc: Install Azure CLI (az) +################################################################################ + +# Install Azure CLI (instructions taken from https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) +curl -fsSL https://aka.ms/InstallAzureCLIDeb | sudo bash + +echo "azure-cli https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-linux?pivots=apt" >> $HELPER_SCRIPTS/apt-sources.txt + +rm -f /etc/apt/sources.list.d/azure-cli.list +rm -f /etc/apt/sources.list.d/azure-cli.list.save + +invoke_tests "CLI.Tools" "Azure CLI" diff --git a/images/ubuntu/scripts/build/install-azure-devops-cli.sh b/images/ubuntu/scripts/build/install-azure-devops-cli.sh new file mode 100644 index 0000000..81948ac --- /dev/null +++ b/images/ubuntu/scripts/build/install-azure-devops-cli.sh @@ -0,0 +1,18 @@ +#!/bin/bash -e +################################################################################ +## File: install-azure-devops-cli.sh +## Desc: Install Azure DevOps CLI (az devops) +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/etc-environment.sh + +# AZURE_EXTENSION_DIR shell variable defines where modules are installed +# https://docs.microsoft.com/en-us/cli/azure/azure-cli-extensions-overview +export AZURE_EXTENSION_DIR=/opt/az/azcliextensions +set_etc_environment_variable "AZURE_EXTENSION_DIR" "${AZURE_EXTENSION_DIR}" + +# install azure devops Cli extension +az extension add -n azure-devops + +invoke_tests "CLI.Tools" "Azure DevOps CLI" diff --git a/images/ubuntu/scripts/build/install-bazel.sh b/images/ubuntu/scripts/build/install-bazel.sh new file mode 100644 index 0000000..70fe46f --- /dev/null +++ b/images/ubuntu/scripts/build/install-bazel.sh @@ -0,0 +1,16 @@ +#!/bin/bash -e +################################################################################ +## File: install-bazel.sh +## Desc: Install Bazel and Bazelisk (A user-friendly launcher for Bazel) +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Install bazelisk +npm install -g @bazel/bazelisk + +# run bazelisk once in order to install /usr/local/bin/bazel binary +sudo -u $SUDO_USER bazel version + +invoke_tests "Tools" "Bazel" diff --git a/images/ubuntu/scripts/build/install-bicep.sh b/images/ubuntu/scripts/build/install-bicep.sh new file mode 100644 index 0000000..5cf996b --- /dev/null +++ b/images/ubuntu/scripts/build/install-bicep.sh @@ -0,0 +1,17 @@ +#!/bin/bash -e +################################################################################ +## File: install-bicep.sh +## Desc: Install bicep cli +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Install Bicep CLI +download_url=$(resolve_github_release_asset_url "Azure/bicep" "endswith(\"bicep-linux-x64\")" "latest") +bicep_binary_path=$(download_with_retry "${download_url}") + +# Mark it as executable +install "$bicep_binary_path" /usr/local/bin/bicep + +invoke_tests "Tools" "Bicep" diff --git a/images/ubuntu/scripts/build/install-clang.sh b/images/ubuntu/scripts/build/install-clang.sh new file mode 100644 index 0000000..ee3fc20 --- /dev/null +++ b/images/ubuntu/scripts/build/install-clang.sh @@ -0,0 +1,40 @@ +#!/bin/bash -e +################################################################################ +## File: install-clang.sh +## Desc: Install Clang compiler +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +install_clang() { + local version=$1 + + echo "Installing clang-$version..." + apt-get install "clang-$version" "lldb-$version" "lld-$version" "clang-format-$version" "clang-tidy-$version" +} + +set_default_clang() { + local version=$1 + + echo "Make Clang ${version} default" + update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${version} 100 + update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${version} 100 + update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-${version} 100 + update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-${version} 100 + update-alternatives --install /usr/bin/run-clang-tidy run-clang-tidy /usr/bin/run-clang-tidy-${version} 100 +} + +versions=$(get_toolset_value '.clang.versions[]') +default_clang_version=$(get_toolset_value '.clang.default_version') + +for version in ${versions[*]}; do + if [[ $version != $default_clang_version ]]; then + install_clang $version + fi +done + +install_clang $default_clang_version +set_default_clang $default_clang_version + +invoke_tests "Tools" "clang" diff --git a/images/ubuntu/scripts/build/install-cmake.sh b/images/ubuntu/scripts/build/install-cmake.sh new file mode 100644 index 0000000..3a53eec --- /dev/null +++ b/images/ubuntu/scripts/build/install-cmake.sh @@ -0,0 +1,31 @@ +#!/bin/bash -e +################################################################################ +## File: install-cmake.sh +## Desc: Install CMake +## Supply chain security: CMake - checksum validation +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Test to see if the software in question is already installed, if not install it +echo "Checking to see if the installer script has already been run" +if command -v cmake; then + echo "cmake is already installed" +else + # Download script to install CMake + download_url=$(resolve_github_release_asset_url "Kitware/CMake" "endswith(\"inux-x86_64.sh\")" "latest") + curl -fsSL "${download_url}" -o cmakeinstall.sh + + # Supply chain security - CMake + hash_url=$(resolve_github_release_asset_url "Kitware/CMake" "endswith(\"SHA-256.txt\")" "latest") + external_hash=$(get_checksum_from_url "$hash_url" "linux-x86_64.sh" "SHA256") + use_checksum_comparison "cmakeinstall.sh" "$external_hash" + + # Install CMake and remove the install script + chmod +x cmakeinstall.sh \ + && ./cmakeinstall.sh --prefix=/usr/local --exclude-subdir \ + && rm cmakeinstall.sh +fi + +invoke_tests "Tools" "Cmake" diff --git a/images/ubuntu/scripts/build/install-codeql-bundle.sh b/images/ubuntu/scripts/build/install-codeql-bundle.sh new file mode 100644 index 0000000..a92933a --- /dev/null +++ b/images/ubuntu/scripts/build/install-codeql-bundle.sh @@ -0,0 +1,31 @@ +#!/bin/bash -e +################################################################################ +## File: install-codeql-bundle.sh +## Desc: Install CodeQL CLI Bundle to the toolcache. +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Retrieve the CLI version of the latest CodeQL bundle. +base_url="$(curl -fsSL https://raw.githubusercontent.com/github/codeql-action/v2/src/defaults.json)" +bundle_version="$(echo "$base_url" | jq -r '.cliVersion')" +bundle_tag_name="codeql-bundle-v$bundle_version" + +echo "Downloading CodeQL bundle $bundle_version..." +# Note that this is the all-platforms CodeQL bundle, to support scenarios where customers run +# different operating systems within containers. +codeql_archive=$(download_with_retry "https://github.com/github/codeql-action/releases/download/$bundle_tag_name/codeql-bundle.tar.gz") + +codeql_toolcache_path="$AGENT_TOOLSDIRECTORY/CodeQL/$bundle_version/x64" +mkdir -p "$codeql_toolcache_path" + +echo "Unpacking the downloaded CodeQL bundle archive..." +tar -xzf "$codeql_archive" -C "$codeql_toolcache_path" + +# Touch a file to indicate to the CodeQL Action that this bundle shipped with the toolcache. This is +# to support overriding the CodeQL version specified in defaults.json on GitHub Enterprise. +touch "$codeql_toolcache_path/pinned-version" + +# Touch a file to indicate to the toolcache that setting up CodeQL is complete. +touch "$codeql_toolcache_path.complete" diff --git a/images/ubuntu/scripts/build/install-container-tools.sh b/images/ubuntu/scripts/build/install-container-tools.sh new file mode 100644 index 0000000..f47720d --- /dev/null +++ b/images/ubuntu/scripts/build/install-container-tools.sh @@ -0,0 +1,45 @@ +#!/bin/bash -e +################################################################################ +## File: install-container-tools.sh +## Desc: Install container tools: podman, buildah and skopeo onto the image +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/os.sh + +# +# pin podman due to https://github.com/actions/runner-images/issues/7753 +# https://bugs.launchpad.net/ubuntu/+source/libpod/+bug/2024394 +# +if ! is_ubuntu22; then + install_packages=(podman buildah skopeo) +else + install_packages=(podman=3.4.4+ds1-1ubuntu1 buildah skopeo) +fi + +# Packages is available in the official Ubuntu upstream starting from Ubuntu 21 +if is_ubuntu20; then + REPO_URL="https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_$(lsb_release -rs)" + GPG_KEY="/usr/share/keyrings/devel_kubic_libcontainers_stable.gpg" + REPO_PATH="/etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list" + + curl -fsSL "${REPO_URL}/Release.key" | gpg --dearmor -o $GPG_KEY + echo "deb [arch=amd64 signed-by=$GPG_KEY] ${REPO_URL}/ /" > $REPO_PATH +fi + +# Install podman, buildah, skopeo container's tools +apt-get update +apt-get install ${install_packages[@]} +mkdir -p /etc/containers +printf "[registries.search]\nregistries = ['docker.io', 'quay.io']\n" | tee /etc/containers/registries.conf + +if is_ubuntu20; then + # Remove source repo + rm $GPG_KEY + rm $REPO_PATH + + # Document source repo + echo "containers $REPO_URL" >> $HELPER_SCRIPTS/apt-sources.txt +fi + +invoke_tests "Tools" "Containers" diff --git a/images/ubuntu/scripts/build/install-docker.sh b/images/ubuntu/scripts/build/install-docker.sh new file mode 100644 index 0000000..35b55f2 --- /dev/null +++ b/images/ubuntu/scripts/build/install-docker.sh @@ -0,0 +1,107 @@ +#!/bin/bash -e +################################################################################ +## File: install-docker.sh +## Desc: Install docker onto the image +## Supply chain security: amazon-ecr-credential-helper - dynamic checksum validation +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +REPO_URL="https://download.docker.com/linux/ubuntu" +GPG_KEY="/usr/share/keyrings/docker.gpg" +REPO_PATH="/etc/apt/sources.list.d/docker.list" +os_codename=$(lsb_release -cs) + +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o $GPG_KEY +echo "deb [arch=amd64 signed-by=$GPG_KEY] $REPO_URL ${os_codename} stable" > $REPO_PATH +apt-get update + +# Install docker components which available via apt-get +# Using toolsets keep installation order to install dependencies before the package in order to control versions + +components=$(get_toolset_value '.docker.components[] .package') +for package in $components; do + version=$(get_toolset_value ".docker.components[] | select(.package == \"$package\") | .version") + if [[ $version == "latest" ]]; then + apt-get install --no-install-recommends "$package" + else + version_string=$(apt-cache madison "$package" | awk '{ print $3 }' | grep "$version" | grep "$os_codename" | head -1) + apt-get install --no-install-recommends "${package}=${version_string}" + fi +done + +# Install plugins that are best installed from the GitHub repository +# Be aware that `url` built from github repo name and plugin name because of current repo naming for those plugins + +plugins=$(get_toolset_value '.docker.plugins[] .plugin') +for plugin in $plugins; do + version=$(get_toolset_value ".docker.plugins[] | select(.plugin == \"$plugin\") | .version") + filter=$(get_toolset_value ".docker.plugins[] | select(.plugin == \"$plugin\") | .asset") + url=$(resolve_github_release_asset_url "docker/$plugin" "endswith(\"$filter\")" "$version") + binary_path=$(download_with_retry "$url" "/tmp/docker-$plugin") + mkdir -pv "/usr/libexec/docker/cli-plugins" + install "$binary_path" "/usr/libexec/docker/cli-plugins/docker-$plugin" +done + +# docker from official repo introduced different GID generation: https://github.com/actions/runner-images/issues/8157 +gid=$(cut -d ":" -f 3 /etc/group | grep "^1..$" | sort -n | tail -n 1 | awk '{ print $1+1 }') +groupmod -g "$gid" docker + +# Create systemd-tmpfiles configuration for Docker +cat < Canonical repos dependencies mix up. +# Give Microsoft's repo higher priority to avoid collisions. +# See: https://github.com/dotnet/core/issues/7699 +cat << EOF > /etc/apt/preferences.d/dotnet +Package: *net* +Pin: origin packages.microsoft.com +Pin-Priority: 1001 +EOF + +apt-get update + +for latest_package in ${latest_dotnet_packages[@]}; do + echo "Determining if .NET Core ($latest_package) is installed" + if ! dpkg -S $latest_package &> /dev/null; then + echo "Could not find .NET Core ($latest_package), installing..." + apt-get install $latest_package + else + echo ".NET Core ($latest_package) is already installed" + fi +done + +rm /etc/apt/preferences.d/dotnet + +apt-get update + +# Install .NET SDK from home repository +# Get list of all released SDKs from channels which are not end-of-life or preview +sdks=() +for version in ${dotnet_versions[@]}; do + release_url="https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/${version}/releases.json" + releases=$(cat "$(download_with_retry "$release_url")") + if [[ $version == "6.0" ]]; then + sdks=("${sdks[@]}" $(echo "${releases}" | jq -r 'first(.releases[].sdks[]?.version | select(contains("preview") or contains("rc") | not))')) + else + sdks=("${sdks[@]}" $(echo "${releases}" | jq -r '.releases[].sdk.version | select(contains("preview") or contains("rc") | not)')) + sdks=("${sdks[@]}" $(echo "${releases}" | jq -r '.releases[].sdks[]?.version | select(contains("preview") or contains("rc") | not)')) + fi +done + +sorted_sdks=$(echo ${sdks[@]} | tr ' ' '\n' | sort -r | uniq -w 5) + +# Download/install additional SDKs in parallel +export -f download_with_retry +export -f extract_dotnet_sdk + +parallel --jobs 0 --halt soon,fail=1 \ + 'url="https://dotnetcli.blob.core.windows.net/dotnet/Sdk/{}/dotnet-sdk-{}-linux-x64.tar.gz"; \ + download_with_retry $url' ::: "${sorted_sdks[@]}" + +find . -name "*.tar.gz" | parallel --halt soon,fail=1 'extract_dotnet_sdk {}' + +# NuGetFallbackFolder at /usr/share/dotnet/sdk/NuGetFallbackFolder is warmed up by smoke test +# Additional FTE will just copy to ~/.dotnet/NuGet which provides no benefit on a fungible machine +set_etc_environment_variable DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1 +set_etc_environment_variable DOTNET_NOLOGO 1 +set_etc_environment_variable DOTNET_MULTILEVEL_LOOKUP 0 +prepend_etc_environment_path '$HOME/.dotnet/tools' + +# Install .Net tools +for dotnet_tool in ${dotnet_tools[@]}; do + echo "Installing dotnet tool $dotnet_tool" + dotnet tool install $dotnet_tool --tool-path '/etc/skel/.dotnet/tools' +done + +invoke_tests "DotnetSDK" diff --git a/images/ubuntu/scripts/build/install-erlang.sh b/images/ubuntu/scripts/build/install-erlang.sh new file mode 100644 index 0000000..d5308a5 --- /dev/null +++ b/images/ubuntu/scripts/build/install-erlang.sh @@ -0,0 +1,29 @@ +#!/bin/bash -e +################################################################################ +## File: install-erlang.sh +## Desc: Install erlang and rebar3 +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +source_list=/etc/apt/sources.list.d/eslerlang.list +source_key=/usr/share/keyrings/eslerlang.gpg + +# Install Erlang +wget -q -O - https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc | gpg --dearmor > $source_key +echo "deb [signed-by=$source_key] https://packages.erlang-solutions.com/ubuntu $(lsb_release -cs) contrib" > $source_list +apt-get update + +apt-get install --no-install-recommends esl-erlang + +# Install rebar3 +rebar3_url=$(resolve_github_release_asset_url "erlang/rebar3" "endswith(\"rebar3\")" "latest") +binary_path=$(download_with_retry "$rebar3_url") +install "$binary_path" /usr/local/bin/rebar3 + +# Clean up source list +rm $source_list +rm $source_key + +invoke_tests "Tools" "erlang" diff --git a/images/ubuntu/scripts/build/install-firefox.sh b/images/ubuntu/scripts/build/install-firefox.sh new file mode 100644 index 0000000..0d59ebd --- /dev/null +++ b/images/ubuntu/scripts/build/install-firefox.sh @@ -0,0 +1,48 @@ +#!/bin/bash -e +################################################################################ +## File: install-firefox.sh +## Desc: Install Firefox +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh +source $HELPER_SCRIPTS/etc-environment.sh + +# Mozillateam PPA is added manually because sometimes +# launchpad portal sends empty answer when trying to add it automatically + +REPO_URL="http://ppa.launchpad.net/mozillateam/ppa/ubuntu" +GPG_FINGERPRINT="0ab215679c571d1c8325275b9bdb3d89ce49ec21" +GPG_KEY="/etc/apt/trusted.gpg.d/mozillateam_ubuntu_ppa.gpg" +REPO_PATH="/etc/apt/sources.list.d/mozillateam-ubuntu-ppa-focal.list" + +# Install Firefox +curl -fsSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x${GPG_FINGERPRINT}" | sudo gpg --dearmor -o $GPG_KEY +echo "deb $REPO_URL $(lsb_release -cs) main" > $REPO_PATH + +apt-get update +apt-get install --target-release 'o=LP-PPA-mozillateam' firefox +rm $REPO_PATH + +# Document apt source repo's +echo "mozillateam $REPO_URL" >> $HELPER_SCRIPTS/apt-sources.txt + +# add to global system preferences for firefox locale en_US, because other browsers have en_US local. +# Default firefox local is en_GB +echo 'pref("intl.locale.requested","en_US");' >> "/usr/lib/firefox/browser/defaults/preferences/syspref.js" + +# Download and unpack latest release of geckodriver +download_url=$(resolve_github_release_asset_url "mozilla/geckodriver" "test(\"linux64.tar.gz$\")" "latest") +driver_archive_path=$(download_with_retry "$download_url") + +GECKODRIVER_DIR="/usr/local/share/gecko_driver" +GECKODRIVER_BIN="$GECKODRIVER_DIR/geckodriver" + +mkdir -p $GECKODRIVER_DIR +tar -xzf "$driver_archive_path" -C $GECKODRIVER_DIR + +chmod +x $GECKODRIVER_BIN +ln -s "$GECKODRIVER_BIN" /usr/bin/ +set_etc_environment_variable "GECKOWEBDRIVER" "${GECKODRIVER_DIR}" + +invoke_tests "Browsers" "Firefox" diff --git a/images/ubuntu/scripts/build/install-gcc-compilers.sh b/images/ubuntu/scripts/build/install-gcc-compilers.sh new file mode 100644 index 0000000..b0739aa --- /dev/null +++ b/images/ubuntu/scripts/build/install-gcc-compilers.sh @@ -0,0 +1,17 @@ +#!/bin/bash -e +################################################################################ +## File: install-gcc-compilers.sh +## Desc: Install GNU C++ compilers +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +versions=$(get_toolset_value '.gcc.versions[]') + +for version in ${versions[*]}; do + echo "Installing $version..." + apt-get install $version +done + +invoke_tests "Tools" "gcc" diff --git a/images/ubuntu/scripts/build/install-gfortran.sh b/images/ubuntu/scripts/build/install-gfortran.sh new file mode 100644 index 0000000..701c966 --- /dev/null +++ b/images/ubuntu/scripts/build/install-gfortran.sh @@ -0,0 +1,20 @@ +#!/bin/bash -e +################################################################################ +## File: install-gfortran.sh +## Desc: Install GNU Fortran +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +versions=$(get_toolset_value '.gfortran.versions[]') + +for version in ${versions[*]}; do + echo "Installing $version..." + apt-get install $version +done + +echo "Install versionless gfortran (latest)" +apt-get install gfortran + +invoke_tests "Tools" "gfortran" diff --git a/images/ubuntu/scripts/build/install-git-lfs.sh b/images/ubuntu/scripts/build/install-git-lfs.sh new file mode 100644 index 0000000..3c15967 --- /dev/null +++ b/images/ubuntu/scripts/build/install-git-lfs.sh @@ -0,0 +1,22 @@ +#!/bin/bash -e +################################################################################ +## File: install-git-lfs.sh +## Desc: Install Git-lfs +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +GIT_LFS_REPO="https://packagecloud.io/install/repositories/github/git-lfs" + +# Install git-lfs +curl -fsSL $GIT_LFS_REPO/script.deb.sh | bash +apt-get install git-lfs + +# Remove source repo's +rm /etc/apt/sources.list.d/github_git-lfs.list + +# Document apt source repo's +echo "git-lfs $GIT_LFS_REPO" >> $HELPER_SCRIPTS/apt-sources.txt + +invoke_tests "Tools" "Git-lfs" diff --git a/images/ubuntu/scripts/build/install-git.sh b/images/ubuntu/scripts/build/install-git.sh new file mode 100644 index 0000000..25dc93b --- /dev/null +++ b/images/ubuntu/scripts/build/install-git.sh @@ -0,0 +1,36 @@ +#!/bin/bash -e +################################################################################ +## File: install-git.sh +## Desc: Install Git and Git-FTP +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +GIT_REPO="ppa:git-core/ppa" + +## Install git +add-apt-repository $GIT_REPO -y +apt-get update +apt-get install git + +# Git version 2.35.2 introduces security fix that breaks action\checkout https://github.com/actions/checkout/issues/760 +cat <> /etc/gitconfig +[safe] + directory = * +EOF + +# Install git-ftp +apt-get install git-ftp + +# Remove source repo's +add-apt-repository --remove $GIT_REPO + +# Document apt source repo's +echo "git-core $GIT_REPO" >> $HELPER_SCRIPTS/apt-sources.txt + +# Add well-known SSH host keys to known_hosts +ssh-keyscan -t rsa,ecdsa,ed25519 github.com >> /etc/ssh/ssh_known_hosts +ssh-keyscan -t rsa ssh.dev.azure.com >> /etc/ssh/ssh_known_hosts + +invoke_tests "Tools" "Git" diff --git a/images/ubuntu/scripts/build/install-github-cli.sh b/images/ubuntu/scripts/build/install-github-cli.sh new file mode 100644 index 0000000..0abb860 --- /dev/null +++ b/images/ubuntu/scripts/build/install-github-cli.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e +################################################################################ +## File: install-github-cli.sh +## Desc: Install GitHub CLI +## Must be run as non-root user after homebrew +## Supply chain security: GitHub CLI - checksum validation +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Download GitHub CLI +gh_cli_url=$(resolve_github_release_asset_url "cli/cli" "contains(\"linux\") and contains(\"amd64\") and endswith(\".deb\")" "latest") +gh_cli_deb_path=$(download_with_retry "$gh_cli_url") + +# Supply chain security - GitHub CLI +hash_url=$(resolve_github_release_asset_url "cli/cli" "endswith(\"checksums.txt\")" "latest") +external_hash=$(get_checksum_from_url "$hash_url" "linux_amd64.deb" "SHA256") +use_checksum_comparison "$gh_cli_deb_path" "$external_hash" + +# Install GitHub CLI +apt-get install "$gh_cli_deb_path" + +invoke_tests "CLI.Tools" "GitHub CLI" diff --git a/images/ubuntu/scripts/build/install-google-chrome.sh b/images/ubuntu/scripts/build/install-google-chrome.sh new file mode 100644 index 0000000..f69090e --- /dev/null +++ b/images/ubuntu/scripts/build/install-google-chrome.sh @@ -0,0 +1,86 @@ +#!/bin/bash -e +################################################################################ +## File: install-google-chrome.sh +## Desc: Install google-chrome, chromedriver and chromium +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh +source $HELPER_SCRIPTS/etc-environment.sh + +get_chromium_revision() { + local chrome_revision=$1 + + # Take the first part of the revision variable to search not only for a specific version, + # but also for similar ones, so that we can get a previous one if the required revision is not found + chrome_revision_prefix=${chrome_revision:0:${#chrome_revision}/2+1} + SEARCH_URL="https://www.googleapis.com/storage/v1/b/chromium-browser-snapshots/o?delimiter=/&prefix=Linux_x64" + # Revision can include a hash instead of a number. Need to filter it out https://github.com/actions/runner-images/issues/5256 + 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) + + # If required Chromium revision is not found in the list + # we should have to decrement the revision number until we find one. + # This is mentioned in the documentation we use for this installation: + # https://www.chromium.org/getting-involved/download-chromium + latest_valid_revision=$(echo $revisions_available | cut -f 1 -d " ") + for revision in $revisions_available; do + if [ "$chrome_revision" -lt "$revision" ]; then + break + fi + + latest_valid_revision=$revision + done + + echo $latest_valid_revision +} + +# Download and install Google Chrome +CHROME_DEB_URL="https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb" +chrome_deb_path=$(download_with_retry "$CHROME_DEB_URL") +apt-get install "$chrome_deb_path" -f +set_etc_environment_variable "CHROME_BIN" "/usr/bin/google-chrome" + +# Remove Google Chrome repo +rm -f /etc/cron.daily/google-chrome /etc/apt/sources.list.d/google-chrome.list /etc/apt/sources.list.d/google-chrome.list.save + +# Parse Google Chrome version +full_chrome_version=$(google-chrome --product-version) +chrome_version=${full_chrome_version%.*} +echo "Chrome version is $full_chrome_version" + +# Get chrome versions information +CHROME_PLATFORM="linux64" +CHROME_VERSIONS_URL="https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build-with-downloads.json" +chrome_versions_json=$(curl -fsSL "${CHROME_VERSIONS_URL}") + +# Download and unpack the latest release of chromedriver +chromedriver_version=$(echo "${chrome_versions_json}" | jq -r '.builds["'"$chrome_version"'"].version') +chromedriver_url=$(echo "${chrome_versions_json}" | jq -r '.builds["'"$chrome_version"'"].downloads.chromedriver[] | select(.platform=="'"${CHROME_PLATFORM}"'").url') +CHROMEDRIVER_DIR="/usr/local/share/chromedriver-linux64" +chromedriver_bin="$CHROMEDRIVER_DIR/chromedriver" + +echo "Installing chromedriver version $chromedriver_version" +driver_archive_path=$(download_with_retry "$chromedriver_url") +unzip -qq "$driver_archive_path" -d /usr/local/share + +chmod +x $chromedriver_bin +ln -s "$chromedriver_bin" /usr/bin/ +set_etc_environment_variable "CHROMEWEBDRIVER" "${CHROMEDRIVER_DIR}" + +# Download and unpack Chromium +chrome_revision=$(echo "${chrome_versions_json}" | jq -r '.builds["'"$chrome_version"'"].revision') +chromium_revision=$(get_chromium_revision $chrome_revision) +chromium_url="https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${chromium_revision}%2Fchrome-linux.zip?alt=media" +CHROMIUM_DIR="/usr/local/share/chromium" +chromium_bin="${CHROMIUM_DIR}/chrome-linux/chrome" + +echo "Installing chromium revision $chromium_revision" +chromium_archive_path=$(download_with_retry "$chromium_url") +mkdir $CHROMIUM_DIR +unzip -qq "$chromium_archive_path" -d $CHROMIUM_DIR + +ln -s $chromium_bin /usr/bin/chromium +ln -s $chromium_bin /usr/bin/chromium-browser + +invoke_tests "Browsers" "Chrome" +invoke_tests "Browsers" "Chromium" diff --git a/images/ubuntu/scripts/build/install-google-cloud-cli.sh b/images/ubuntu/scripts/build/install-google-cloud-cli.sh new file mode 100644 index 0000000..2df071d --- /dev/null +++ b/images/ubuntu/scripts/build/install-google-cloud-cli.sh @@ -0,0 +1,22 @@ +#!/bin/bash -e +################################################################################ +## File: install-google-cloud-cli.sh +## Desc: Install the Google Cloud CLI +################################################################################ + +REPO_URL="https://packages.cloud.google.com/apt" + +# Install the Google Cloud CLI +echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] $REPO_URL cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list +wget -qO- https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor > /usr/share/keyrings/cloud.google.gpg +apt-get update +apt-get install google-cloud-cli + +# remove apt +rm /etc/apt/sources.list.d/google-cloud-sdk.list +rm /usr/share/keyrings/cloud.google.gpg + +# add repo to the apt-sources.txt +echo "google-cloud-sdk $REPO_URL" >> $HELPER_SCRIPTS/apt-sources.txt + +invoke_tests "CLI.Tools" "Google Cloud CLI" diff --git a/images/ubuntu/scripts/build/install-haskell.sh b/images/ubuntu/scripts/build/install-haskell.sh new file mode 100644 index 0000000..b5a6b09 --- /dev/null +++ b/images/ubuntu/scripts/build/install-haskell.sh @@ -0,0 +1,44 @@ +#!/bin/bash -e +################################################################################ +## File: install-haskell.sh +## Desc: Install Haskell, GHCup, Cabal and Stack +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/etc-environment.sh + +# Any nonzero value for non-interactive installation +export BOOTSTRAP_HASKELL_NONINTERACTIVE=1 +export BOOTSTRAP_HASKELL_INSTALL_NO_STACK_HOOK=1 +export GHCUP_INSTALL_BASE_PREFIX=/usr/local +export BOOTSTRAP_HASKELL_GHC_VERSION=0 +ghcup_bin=$GHCUP_INSTALL_BASE_PREFIX/.ghcup/bin +set_etc_environment_variable "BOOTSTRAP_HASKELL_NONINTERACTIVE" $BOOTSTRAP_HASKELL_NONINTERACTIVE +set_etc_environment_variable "GHCUP_INSTALL_BASE_PREFIX" $GHCUP_INSTALL_BASE_PREFIX + +# Install GHCup +curl --proto '=https' --tlsv1.2 -fsSL https://get-ghcup.haskell.org | sh > /dev/null 2>&1 || true +export PATH="$ghcup_bin:$PATH" +prepend_etc_environment_path $ghcup_bin + +available_versions=$(ghcup list -t ghc -r | grep -v "prerelease" | awk '{print $2}') + +# Install 2 latest Haskell Major.Minor versions +major_minor_versions=$(echo "$available_versions" | cut -d"." -f 1,2 | uniq | tail -n2) +for major_minor_version in $major_minor_versions; do + full_version=$(echo "$available_versions" | grep "$major_minor_version." | tail -n1) + echo "install ghc version $full_version..." + ghcup install ghc $full_version + ghcup set ghc $full_version +done + +echo "install cabal..." +ghcup install cabal latest + +chmod -R 777 $GHCUP_INSTALL_BASE_PREFIX/.ghcup +ln -s $GHCUP_INSTALL_BASE_PREFIX/.ghcup /etc/skel/.ghcup + +# Install the latest stable release of haskell stack +curl -fsSL https://get.haskellstack.org/ | bash + +invoke_tests "Haskell" diff --git a/images/ubuntu/scripts/build/install-heroku.sh b/images/ubuntu/scripts/build/install-heroku.sh new file mode 100644 index 0000000..b1d42c6 --- /dev/null +++ b/images/ubuntu/scripts/build/install-heroku.sh @@ -0,0 +1,23 @@ +#!/bin/bash -e +################################################################################ +## File: install-heroku.sh +## Desc: Install Heroku CLI. Based on instructions found here: https://devcenter.heroku.com/articles/heroku-cli +################################################################################ + +REPO_URL="https://cli-assets.heroku.com/channels/stable/apt" +GPG_KEY="/usr/share/keyrings/heroku.gpg" +REPO_PATH="/etc/apt/sources.list.d/heroku.list" + +# add heroku repository to apt +curl -fsSL "${REPO_URL}/release.key" | gpg --dearmor -o $GPG_KEY +echo "deb [trusted=yes] $REPO_URL ./" > $REPO_PATH + +# install heroku +apt-get update +apt-get install heroku + +# remove heroku's apt repository +rm $REPO_PATH +rm $GPG_KEY + +invoke_tests "Tools" "Heroku" diff --git a/images/ubuntu/scripts/build/install-hhvm.sh b/images/ubuntu/scripts/build/install-hhvm.sh new file mode 100644 index 0000000..4056265 --- /dev/null +++ b/images/ubuntu/scripts/build/install-hhvm.sh @@ -0,0 +1,23 @@ +#!/bin/bash -e +################################################################################ +## File: install-hhvm.sh +## Desc: Install HHVM +################################################################################ + +REPO_URL="https://dl.hhvm.com/ubuntu" +GPG_KEY="/usr/share/keyrings/hhvm.gpg" +REPO_PATH="/etc/apt/sources.list.d/hhvm.list" + +# add HHVM repository to apt +curl -fsSL https://dl.hhvm.com/conf/hhvm.gpg.key | gpg --dearmor -o $GPG_KEY +echo "deb [signed-by=$GPG_KEY] $REPO_URL $(lsb_release -cs) main" > $REPO_PATH + +# install HHVM +apt-get update +apt-get install hhvm + +# remove HHVM's apt repository +rm $REPO_PATH +rm $GPG_KEY + +invoke_tests "Tools" "HHVM" diff --git a/images/ubuntu/scripts/build/install-homebrew.sh b/images/ubuntu/scripts/build/install-homebrew.sh new file mode 100644 index 0000000..c8f2b68 --- /dev/null +++ b/images/ubuntu/scripts/build/install-homebrew.sh @@ -0,0 +1,31 @@ +#!/bin/bash -e +################################################################################ +## File: install-homebrew.sh +## Desc: Install Homebrew on Linux +## Caveat: Brew MUST NOT be used to install any tool during the image build to avoid dependencies, which may come along with the tool +################################################################################ + +# Source the helpers +source $HELPER_SCRIPTS/etc-environment.sh +source $HELPER_SCRIPTS/install.sh + +# Install the Homebrew on Linux +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" + +# Invoke shellenv to make brew available during running session +eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + +set_etc_environment_variable HOMEBREW_NO_AUTO_UPDATE 1 +set_etc_environment_variable HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS 3650 + +# Validate the installation ad hoc +echo "Validate the installation reloading /etc/environment" +reload_etc_environment + +gfortran=$(brew --prefix)/bin/gfortran +# Remove gfortran symlink, not to conflict with system gfortran +if [[ -e $gfortran ]]; then + rm $gfortran +fi + +invoke_tests "Tools" "Homebrew" diff --git a/images/ubuntu/scripts/build/install-java-tools.sh b/images/ubuntu/scripts/build/install-java-tools.sh new file mode 100644 index 0000000..0cc976d --- /dev/null +++ b/images/ubuntu/scripts/build/install-java-tools.sh @@ -0,0 +1,119 @@ +#!/bin/bash -e +################################################################################ +## File: install-java-tools.sh +## Desc: Install Java and related tooling (Ant, Gradle, Maven) +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh +source $HELPER_SCRIPTS/etc-environment.sh + +create_java_environment_variable() { + local java_version=$1 + local default=$2 + + local install_path_pattern="/usr/lib/jvm/temurin-${java_version}-jdk-amd64" + + if [[ ${default} == "True" ]]; then + echo "Setting up JAVA_HOME variable to ${install_path_pattern}" + set_etc_environment_variable "JAVA_HOME" "${install_path_pattern}" + echo "Setting up default symlink" + update-java-alternatives -s ${install_path_pattern} + fi + + echo "Setting up JAVA_HOME_${java_version}_X64 variable to ${install_path_pattern}" + set_etc_environment_variable "JAVA_HOME_${java_version}_X64" "${install_path_pattern}" +} + +install_open_jdk() { + local java_version=$1 + + # Install Java from PPA repositories. + apt-get -y install temurin-${java_version}-jdk=\* + java_version_path="/usr/lib/jvm/temurin-${java_version}-jdk-amd64" + + java_toolcache_path="${AGENT_TOOLSDIRECTORY}/Java_Temurin-Hotspot_jdk" + + full_java_version=$(cat "${java_version_path}/release" | grep "^SEMANTIC" | cut -d "=" -f 2 | tr -d "\"" | tr "+" "-") + + # If there is no semver in java release, then extract java version from -fullversion + [[ -z ${full_java_version} ]] && full_java_version=$(${java_version_path}/bin/java -fullversion 2>&1 | tr -d "\"" | tr "+" "-" | awk '{print $4}') + + # Convert non valid semver like 11.0.14.1-9 -> 11.0.14-9 + # https://github.com/adoptium/temurin-build/issues/2248 + [[ ${full_java_version} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] && full_java_version=$(echo $full_java_version | sed -E 's/\.[0-9]+-/-/') + + # When version string is too short, add extra ".0" to make it valid semver + [[ ${full_java_version} =~ ^[0-9]+- ]] && full_java_version=$(echo $full_java_version | sed -E 's/-/.0-/') + [[ ${full_java_version} =~ ^[0-9]+\.[0-9]+- ]] && full_java_version=$(echo $full_java_version | sed -E 's/-/.0-/') + + java_toolcache_version_path="${java_toolcache_path}/${full_java_version}" + echo "Java ${java_version} Toolcache Version Path: ${java_toolcache_version_path}" + mkdir -p "${java_toolcache_version_path}" + + # Create a complete file + touch "${java_toolcache_version_path}/x64.complete" + + # Create symlink for Java + ln -s ${java_version_path} "${java_toolcache_version_path}/x64" + + # add extra permissions to be able execute command without sudo + chmod -R 777 /usr/lib/jvm +} + +# Add Adoptium PPA +# apt-key is deprecated, dearmor and add manually +wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor > /usr/share/keyrings/adoptium.gpg +echo "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 + +# Get all the updates from enabled repositories. +apt-get update + +# While Ubuntu 24.04 binaries are not released in the Adoptium repo, we will not install Java +defaultVersion=$(get_toolset_value '.java.default') +jdkVersionsToInstall=($(get_toolset_value ".java.versions[]")) + +for jdkVersionToInstall in ${jdkVersionsToInstall[@]}; do + install_open_jdk ${jdkVersionToInstall} + + if [[ ${jdkVersionToInstall} == ${defaultVersion} ]] + then + create_java_environment_variable ${jdkVersionToInstall} True + else + create_java_environment_variable ${jdkVersionToInstall} False + fi +done + +# Install Ant +apt-get install --no-install-recommends ant ant-optional +set_etc_environment_variable "ANT_HOME" "/usr/share/ant" + +# Install Maven +mavenVersion=$(get_toolset_value '.java.maven') +mavenDownloadUrl="https://dlcdn.apache.org/maven/maven-3/${mavenVersion}/binaries/apache-maven-${mavenVersion}-bin.zip" +maven_archive_path=$(download_with_retry "$mavenDownloadUrl") +unzip -qq -d /usr/share "$maven_archive_path" +ln -s /usr/share/apache-maven-${mavenVersion}/bin/mvn /usr/bin/mvn + +# Install Gradle +# This script founds the latest gradle release from https://services.gradle.org/versions/all +# The release is downloaded, extracted, a symlink is created that points to it, and GRADLE_HOME is set. +gradleJson=$(curl -fsSL https://services.gradle.org/versions/all) +gradleLatestVersion=$(echo ${gradleJson} | jq -r '.[] | select(.version | contains("-") | not).version' | sort -V | tail -n1) +gradleDownloadUrl=$(echo ${gradleJson} | jq -r ".[] | select(.version==\"$gradleLatestVersion\") | .downloadUrl") +echo "gradleUrl=${gradleDownloadUrl}" +echo "gradleVersion=${gradleLatestVersion}" +gradle_archive_path=$(download_with_retry "$gradleDownloadUrl") +unzip -qq -d /usr/share "$gradle_archive_path" +ln -s /usr/share/gradle-"${gradleLatestVersion}"/bin/gradle /usr/bin/gradle +gradle_home_dir=$(find /usr/share -depth -maxdepth 1 -name "gradle*") +set_etc_environment_variable "GRADLE_HOME" "${gradle_home_dir}" + +# Delete java repositories and keys +rm -f /etc/apt/sources.list.d/adoptium.list +rm -f /etc/apt/sources.list.d/zulu.list +rm -f /usr/share/keyrings/adoptium.gpg +rm -f /usr/share/keyrings/zulu.gpg + +reload_etc_environment +invoke_tests "Java" diff --git a/images/ubuntu/scripts/build/install-julia.sh b/images/ubuntu/scripts/build/install-julia.sh new file mode 100644 index 0000000..46c4aaf --- /dev/null +++ b/images/ubuntu/scripts/build/install-julia.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e +################################################################################ +## File: install-julia.sh +## Desc: Install Julia and add to the path +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# get the latest julia version +json=$(curl -fsSL "https://julialang-s3.julialang.org/bin/versions.json") +julia_version=$(echo $json | jq -r '.[].files[] | select(.triplet=="x86_64-linux-gnu" and (.version | contains("-") | not)).version' | sort -V | tail -n1) + +# download julia archive +julia_tar_url=$(echo $json | jq -r ".[].files[].url | select(endswith(\"julia-${julia_version}-linux-x86_64.tar.gz\"))") +julia_archive_path=$(download_with_retry "$julia_tar_url") + +# extract files and make symlink +julia_installation_path="/usr/local/julia${julia_version}" +mkdir -p "${julia_installation_path}" +tar -C "${julia_installation_path}" -xzf "$julia_archive_path" --strip-components=1 +ln -s "${julia_installation_path}/bin/julia" /usr/bin/julia + +invoke_tests "Tools" "Julia" diff --git a/images/ubuntu/scripts/build/install-kotlin.sh b/images/ubuntu/scripts/build/install-kotlin.sh new file mode 100644 index 0000000..7b35212 --- /dev/null +++ b/images/ubuntu/scripts/build/install-kotlin.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e +################################################################################ +## File: install-kotlin.sh +## Desc: Install Kotlin +## Supply chain security: Kotlin - checksum validation +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +KOTLIN_ROOT="/usr/share" +download_url=$(resolve_github_release_asset_url "JetBrains/kotlin" "contains(\"kotlin-compiler\") and endswith(\".zip\")" "latest") +archive_path=$(download_with_retry "$download_url") + +# Supply chain security - Kotlin +kotlin_hash_file=$(download_with_retry "${download_url}.sha256") +kotlin_hash=$(cat "$kotlin_hash_file") +use_checksum_comparison "$archive_path" "$kotlin_hash" + +unzip -qq "$archive_path" -d $KOTLIN_ROOT +rm $KOTLIN_ROOT/kotlinc/bin/*.bat +ln -sf $KOTLIN_ROOT/kotlinc/bin/* /usr/bin + +invoke_tests "Tools" "Kotlin" diff --git a/images/ubuntu/scripts/build/install-kubernetes-tools.sh b/images/ubuntu/scripts/build/install-kubernetes-tools.sh new file mode 100644 index 0000000..90f68fe --- /dev/null +++ b/images/ubuntu/scripts/build/install-kubernetes-tools.sh @@ -0,0 +1,48 @@ +#!/bin/bash -e +################################################################################ +## File: install-kubernetes-tools.sh +## Desc: Installs kubectl, helm, kustomize +## Supply chain security: KIND, minikube - checksum validation +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Download KIND +kind_url=$(resolve_github_release_asset_url "kubernetes-sigs/kind" "endswith(\"kind-linux-amd64\")" "latest") +kind_binary_path=$(download_with_retry "${kind_url}") + +# Supply chain security - KIND +kind_external_hash=$(get_checksum_from_url "${kind_url}.sha256sum" "kind-linux-amd64" "SHA256") +use_checksum_comparison "${kind_binary_path}" "${kind_external_hash}" + +# Install KIND +install "${kind_binary_path}" /usr/local/bin/kind + +## Install kubectl +kubectl_minor_version=$(curl -fsSL "https://dl.k8s.io/release/stable.txt" | cut -d'.' -f1,2 ) +curl -fsSL https://pkgs.k8s.io/core:/stable:/$kubectl_minor_version/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg +echo '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 +apt-get update +apt-get install kubectl +rm -f /etc/apt/sources.list.d/kubernetes.list + +# Install Helm +curl -fsSL https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash + +# Download minikube +curl -fsSL -O https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 + +# Supply chain security - minikube +minikube_hash=$(get_checksum_from_github_release "kubernetes/minikube" "linux-amd64" "latest" "SHA256") +use_checksum_comparison "minikube-linux-amd64" "${minikube_hash}" + +# Install minikube +install minikube-linux-amd64 /usr/local/bin/minikube + +# Install kustomize +download_url="https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" +curl -fsSL "$download_url" | bash +mv kustomize /usr/local/bin + +invoke_tests "Tools" "Kubernetes tools" diff --git a/images/ubuntu/scripts/build/install-leiningen.sh b/images/ubuntu/scripts/build/install-leiningen.sh new file mode 100644 index 0000000..6d61d78 --- /dev/null +++ b/images/ubuntu/scripts/build/install-leiningen.sh @@ -0,0 +1,22 @@ +#!/bin/bash -e +################################################################################ +## File: install-leiningen.sh +## Desc: Install Leiningen +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/etc-environment.sh + +LEIN_BIN=/usr/local/bin/lein +curl -fsSL https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein > $LEIN_BIN +chmod 0755 $LEIN_BIN + +# Run lein to trigger self-install +export LEIN_HOME=/usr/local/lib/lein +lein + +LEIN_JAR=$(find $LEIN_HOME -name "leiningen-*-standalone.jar") +set_etc_environment_variable "LEIN_JAR" "${LEIN_JAR}" +set_etc_environment_variable "LEIN_HOME" "${LEIN_HOME}" + +invoke_tests "Tools" "Leiningen" diff --git a/images/ubuntu/scripts/build/install-microsoft-edge.sh b/images/ubuntu/scripts/build/install-microsoft-edge.sh new file mode 100644 index 0000000..c46d562 --- /dev/null +++ b/images/ubuntu/scripts/build/install-microsoft-edge.sh @@ -0,0 +1,50 @@ +#!/bin/bash -e +################################################################################ +## File: install-microsoft-edge.sh +## Desc: Install Microsoft Edge and WebDriver +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh +source $HELPER_SCRIPTS/etc-environment.sh + +REPO_URL="https://packages.microsoft.com/repos/edge" +GPG_KEY="/usr/share/keyrings/microsoft-edge.gpg" +REPO_PATH="/etc/apt/sources.list.d/microsoft-edge.list" + +wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > $GPG_KEY +# Specify an arch as Microsoft repository supports armhf and arm64 as well +echo "deb [arch=amd64 signed-by=$GPG_KEY] $REPO_URL stable main" > $REPO_PATH + +apt-get update +apt-get install --no-install-recommends microsoft-edge-stable + +rm $GPG_KEY +rm $REPO_PATH + +echo "microsoft-edge $REPO_URL" >> $HELPER_SCRIPTS/apt-sources.txt + +# Install Microsoft Edge Webdriver + +EDGEDRIVER_DIR="/usr/local/share/edge_driver" +edgedriver_bin="$EDGEDRIVER_DIR/msedgedriver" + +mkdir -p $EDGEDRIVER_DIR + +edge_version=$(microsoft-edge --version | cut -d' ' -f 3) +edge_version_major=$(echo $edge_version | cut -d'.' -f 1) + +edgedriver_version_url="https://msedgedriver.azureedge.net/LATEST_RELEASE_${edge_version_major}_LINUX" +# Convert a resulting file to normal UTF-8 +edgedriver_latest_version=$(curl -fsSL "$edgedriver_version_url" | iconv -f utf-16 -t utf-8 | tr -d '\r') + +edgedriver_url="https://msedgedriver.azureedge.net/${edgedriver_latest_version}/edgedriver_linux64.zip" +edgedriver_archive_path=$(download_with_retry "$edgedriver_url") + +unzip -qq "$edgedriver_archive_path" -d "$EDGEDRIVER_DIR" +chmod +x $edgedriver_bin +ln -s $edgedriver_bin /usr/bin + +set_etc_environment_variable "EDGEWEBDRIVER" "${EDGEDRIVER_DIR}" + +invoke_tests "Browsers" "Edge" diff --git a/images/ubuntu/scripts/build/install-miniconda.sh b/images/ubuntu/scripts/build/install-miniconda.sh new file mode 100644 index 0000000..2e8e7b4 --- /dev/null +++ b/images/ubuntu/scripts/build/install-miniconda.sh @@ -0,0 +1,21 @@ +#!/bin/bash -e +################################################################################ +## File: install-miniconda.sh +## Desc: Install miniconda +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/etc-environment.sh + +# Install Miniconda +curl -fsSL https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -o miniconda.sh \ + && chmod +x miniconda.sh \ + && ./miniconda.sh -b -p /usr/share/miniconda \ + && rm miniconda.sh + +CONDA=/usr/share/miniconda +set_etc_environment_variable "CONDA" "${CONDA}" + +ln -s $CONDA/bin/conda /usr/bin/conda + +invoke_tests "Tools" "Conda" diff --git a/images/ubuntu/scripts/build/install-mongodb.sh b/images/ubuntu/scripts/build/install-mongodb.sh new file mode 100644 index 0000000..8fb682a --- /dev/null +++ b/images/ubuntu/scripts/build/install-mongodb.sh @@ -0,0 +1,30 @@ +#!/bin/bash -e +################################################################################ +## File: install-mongodb.sh +## Desc: Install Mongo DB +################################################################################ + +# Source the helpers +source $HELPER_SCRIPTS/install.sh + +toolset_version=$(get_toolset_value '.mongodb.version') +REPO_URL="https://repo.mongodb.org/apt/ubuntu" +GPG_KEY="/usr/share/keyrings/mongodb-org-$toolset_version.gpg" +REPO_PATH="/etc/apt/sources.list.d/mongodb-org-$toolset_version.list" + +# add Mongo DB repository to apt +curl -fsSL https://www.mongodb.org/static/pgp/server-$toolset_version.asc | gpg --dearmor -o $GPG_KEY +echo "deb [ arch=amd64,arm64 signed-by=$GPG_KEY ] $REPO_URL $(lsb_release -cs)/mongodb-org/$toolset_version multiverse" > $REPO_PATH + +# Install Mongo DB +sudo apt-get update +sudo apt-get install mongodb-org + +# remove Mongo DB's apt repository +rm $REPO_PATH +rm $GPG_KEY + +# Document source repo +echo "mongodb $REPO_URL" >> $HELPER_SCRIPTS/apt-sources.txt + +invoke_tests "Databases" "MongoDB" diff --git a/images/ubuntu/scripts/build/install-mono.sh b/images/ubuntu/scripts/build/install-mono.sh new file mode 100644 index 0000000..0c3eea3 --- /dev/null +++ b/images/ubuntu/scripts/build/install-mono.sh @@ -0,0 +1,36 @@ +#!/bin/bash -e +################################################################################ +## File: install-mono.sh +## Desc: Install Mono +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/os.sh + +os_label=$(lsb_release -cs) +REPO_URL="https://download.mono-project.com/repo/ubuntu" +GPG_KEY="/usr/share/keyrings/mono-official-stable.gpg" +REPO_PATH="/etc/apt/sources.list.d/mono-official-stable.list" + +# There are no packages for Ubuntu 22 in the repo, but developers confirmed that packages from Ubuntu 20 should work +if is_ubuntu22; then + os_label="focal" +fi + +# Install Mono repo +curl -fsSL https://download.mono-project.com/repo/xamarin.gpg | gpg --dearmor -o $GPG_KEY +echo "deb [signed-by=$GPG_KEY] $REPO_URL stable-$os_label main" > $REPO_PATH + +# Install Mono +apt-get update +apt-get install --no-install-recommends apt-transport-https mono-complete nuget + +# Remove Mono's apt repo +rm $REPO_PATH +rm -f "${REPO_PATH}.save" +rm $GPG_KEY + +# Document source repo +echo "mono https://download.mono-project.com/repo/ubuntu stable-$os_label main" >> $HELPER_SCRIPTS/apt-sources.txt + +invoke_tests "Tools" "Mono" diff --git a/images/ubuntu/scripts/build/install-ms-repos.sh b/images/ubuntu/scripts/build/install-ms-repos.sh new file mode 100644 index 0000000..b4a56e8 --- /dev/null +++ b/images/ubuntu/scripts/build/install-ms-repos.sh @@ -0,0 +1,16 @@ +#!/bin/bash -e +################################################################################ +## File: install-ms-repos.sh +## Desc: Install official Microsoft package repos for the distribution +################################################################################ + +os_label=$(lsb_release -rs) + +# Install Microsoft repository +wget https://packages.microsoft.com/config/ubuntu/$os_label/packages-microsoft-prod.deb +dpkg -i packages-microsoft-prod.deb + +# update +apt-get install apt-transport-https ca-certificates curl software-properties-common +apt-get update +apt-get dist-upgrade diff --git a/images/ubuntu/scripts/build/install-mssql-tools.sh b/images/ubuntu/scripts/build/install-mssql-tools.sh new file mode 100644 index 0000000..3517577 --- /dev/null +++ b/images/ubuntu/scripts/build/install-mssql-tools.sh @@ -0,0 +1,14 @@ +#!/bin/bash -e +################################################################################ +## File: install-mssql-tools.sh +## Desc: Install MS SQL Server client tools (https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-tools?view=sql-server-2017) +################################################################################ + +export ACCEPT_EULA=Y + +apt-get update +apt-get install mssql-tools unixodbc-dev +apt-get -f install +ln -s /opt/mssql-tools/bin/* /usr/local/bin/ + +invoke_tests "Tools" "MSSQLCommandLineTools" diff --git a/images/ubuntu/scripts/build/install-mysql.sh b/images/ubuntu/scripts/build/install-mysql.sh new file mode 100644 index 0000000..82c7334 --- /dev/null +++ b/images/ubuntu/scripts/build/install-mysql.sh @@ -0,0 +1,30 @@ +#!/bin/bash -e +################################################################################ +## File: install-mysql.sh +## Desc: Install MySQL Client +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/os.sh + +# Mysql setting up root password +MYSQL_ROOT_PASSWORD=root +echo "mysql-server mysql-server/root_password password $MYSQL_ROOT_PASSWORD" | debconf-set-selections +echo "mysql-server mysql-server/root_password_again password $MYSQL_ROOT_PASSWORD" | debconf-set-selections + +export ACCEPT_EULA=Y + +# Install MySQL Client +apt-get install mysql-client + +# Install MySQL Server +apt-get install mysql-server + +# Install MySQL Dev tools +apt-get install libmysqlclient-dev + +# Disable mysql.service +systemctl is-active --quiet mysql.service && systemctl stop mysql.service +systemctl disable mysql.service + +invoke_tests "Databases" "MySQL" diff --git a/images/ubuntu/scripts/build/install-nginx.sh b/images/ubuntu/scripts/build/install-nginx.sh new file mode 100644 index 0000000..90708e8 --- /dev/null +++ b/images/ubuntu/scripts/build/install-nginx.sh @@ -0,0 +1,14 @@ +#!/bin/bash -e +################################################################################ +## File: install-nginx.sh +## Desc: Install Nginx +################################################################################ + +# Install Nginx +apt-get install nginx + +# Disable nginx.service +systemctl is-active --quiet nginx.service && systemctl stop nginx.service +systemctl disable nginx.service + +invoke_tests "WebServers" "Nginx" diff --git a/images/ubuntu/scripts/build/install-nodejs.sh b/images/ubuntu/scripts/build/install-nodejs.sh new file mode 100644 index 0000000..9cd0073 --- /dev/null +++ b/images/ubuntu/scripts/build/install-nodejs.sh @@ -0,0 +1,29 @@ +#!/bin/bash -e +################################################################################ +## File: install-nodejs.sh +## Desc: Install Node.js LTS and related tooling (Gulp, Grunt) +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Install default Node.js +default_version=$(get_toolset_value '.node.default') +curl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n -o ~/n +bash ~/n $default_version + +# Install node modules +node_modules=$(get_toolset_value '.node_modules[].name') +npm install -g $node_modules + +echo "Creating the symlink for [now] command to vercel CLI" +ln -s /usr/local/bin/vercel /usr/local/bin/now + +# fix global modules installation as regular user +# related issue https://github.com/actions/runner-images/issues/3727 +sudo chmod -R 777 /usr/local/lib/node_modules +sudo chmod -R 777 /usr/local/bin + +rm -rf ~/n + +invoke_tests "Node" "Node.js" diff --git a/images/ubuntu/scripts/build/install-nvm.sh b/images/ubuntu/scripts/build/install-nvm.sh new file mode 100644 index 0000000..def0d5d --- /dev/null +++ b/images/ubuntu/scripts/build/install-nvm.sh @@ -0,0 +1,22 @@ +#!/bin/bash -e +################################################################################ +## File: install-nvm.sh +## Desc: Install Nvm +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/etc-environment.sh + +export NVM_DIR="/etc/skel/.nvm" +mkdir $NVM_DIR +nvm_version=$(curl -fsSL https://api.github.com/repos/nvm-sh/nvm/releases/latest | jq -r '.tag_name') +curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/$nvm_version/install.sh | bash +set_etc_environment_variable "NVM_DIR" '$HOME/.nvm' + +echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm' | tee -a /etc/skel/.bash_profile +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + +invoke_tests "Tools" "nvm" + +# set system node.js as default one +nvm alias default system diff --git a/images/ubuntu/scripts/build/install-oc-cli.sh b/images/ubuntu/scripts/build/install-oc-cli.sh new file mode 100644 index 0000000..a20f427 --- /dev/null +++ b/images/ubuntu/scripts/build/install-oc-cli.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e +################################################################################ +## File: install-oc-cli.sh +## Desc: Install the OC CLI +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/os.sh +source $HELPER_SCRIPTS/install.sh + +if is_ubuntu20; then + toolset_version=$(get_toolset_value '.ocCli.version') + download_url="https://mirror.openshift.com/pub/openshift-v4/clients/ocp/$toolset_version/openshift-client-linux-$toolset_version.tar.gz" +else + + # Install the oc CLI + download_url="https://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest/openshift-client-linux.tar.gz" +fi + +archive_path=$(download_with_retry "$download_url") + +tar xzf "$archive_path" -C "/usr/local/bin" oc + +invoke_tests "CLI.Tools" "OC CLI" diff --git a/images/ubuntu/scripts/build/install-oras-cli.sh b/images/ubuntu/scripts/build/install-oras-cli.sh new file mode 100644 index 0000000..119bea0 --- /dev/null +++ b/images/ubuntu/scripts/build/install-oras-cli.sh @@ -0,0 +1,25 @@ +#!/bin/bash -e +################################################################################ +## File: install-oras-cli.sh +## Desc: Install ORAS CLI +## Supply chain security: ORAS CLI - checksum validation +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Determine latest ORAS CLI version +download_url=$(resolve_github_release_asset_url "oras-project/oras" "endswith(\"linux_amd64.tar.gz\")" "latest") + +# Download ORAS CLI +archive_path=$(download_with_retry "$download_url") + +# Supply chain security - ORAS CLI +hash_url=$(resolve_github_release_asset_url "oras-project/oras" "endswith(\"checksums.txt\")" "latest") +external_hash=$(get_checksum_from_url "${hash_url}" "linux_amd64.tar.gz" "SHA256") +use_checksum_comparison "$archive_path" "${external_hash}" + +# Unzip ORAS CLI +tar xzf "$archive_path" -C /usr/local/bin oras + +invoke_tests "CLI.Tools" "Oras CLI" diff --git a/images/ubuntu/scripts/build/install-packer.sh b/images/ubuntu/scripts/build/install-packer.sh new file mode 100644 index 0000000..c9cc638 --- /dev/null +++ b/images/ubuntu/scripts/build/install-packer.sh @@ -0,0 +1,15 @@ +#!/bin/bash -e +################################################################################ +## File: install-packer.sh +## Desc: Install packer +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Install Packer +download_url=$(curl -fsSL https://api.releases.hashicorp.com/v1/releases/packer/latest | jq -r '.builds[] | select((.arch=="amd64") and (.os=="linux")).url') +archive_path=$(download_with_retry "$download_url") +unzip -o -qq "$archive_path" -d /usr/local/bin + +invoke_tests "Tools" "Packer" diff --git a/images/ubuntu/scripts/build/install-phantomjs.sh b/images/ubuntu/scripts/build/install-phantomjs.sh new file mode 100644 index 0000000..0cce25d --- /dev/null +++ b/images/ubuntu/scripts/build/install-phantomjs.sh @@ -0,0 +1,26 @@ +#!/bin/bash -e +################################################################################ +## File: install-phantomjs.sh +## Desc: Install PhantomJS +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Install required dependencies +apt-get install chrpath libssl-dev libxft-dev libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev + +# Define the version and hash of PhantomJS to be installed +DIR_NAME=phantomjs-2.1.1-linux-x86_64 +ARCHIVE_HASH="86dd9a4bf4aee45f1a84c9f61cf1947c1d6dce9b9e8d2a907105da7852460d2f" + +# Download the archive and verify its integrity using checksum comparison +download_url="https://bitbucket.org/ariya/phantomjs/downloads/$DIR_NAME.tar.bz2" +archive_path=$(download_with_retry "$download_url") +use_checksum_comparison "$archive_path" "$ARCHIVE_HASH" + +# Extract the archive and create a symbolic link to the executable +tar xjf "$archive_path" -C /usr/local/share +ln -sf /usr/local/share/$DIR_NAME/bin/phantomjs /usr/local/bin + +invoke_tests "Tools" "Phantomjs" diff --git a/images/ubuntu/scripts/build/install-php.sh b/images/ubuntu/scripts/build/install-php.sh new file mode 100644 index 0000000..46be138 --- /dev/null +++ b/images/ubuntu/scripts/build/install-php.sh @@ -0,0 +1,113 @@ +#!/bin/bash -e +################################################################################ +## File: install-php.sh +## Desc: Install php +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/etc-environment.sh +source $HELPER_SCRIPTS/os.sh +source $HELPER_SCRIPTS/install.sh + +# add repository for old Ubuntu images +# details in thread: https://github.com/actions/runner-images/issues/6331 +if is_ubuntu20; then + apt-add-repository ppa:ondrej/php -y + apt-get update +fi + +# Install PHP +php_versions=$(get_toolset_value '.php.versions[]') + +for version in $php_versions; do + echo "Installing PHP $version" + apt-get install --no-install-recommends \ + php$version \ + php$version-amqp \ + php$version-apcu \ + php$version-bcmath \ + php$version-bz2 \ + php$version-cgi \ + php$version-cli \ + php$version-common \ + php$version-curl \ + php$version-dba \ + php$version-dev \ + php$version-enchant \ + php$version-fpm \ + php$version-gd \ + php$version-gmp \ + php$version-igbinary \ + php$version-imagick \ + php$version-imap \ + php$version-interbase \ + php$version-intl \ + php$version-ldap \ + php$version-mbstring \ + php$version-memcache \ + php$version-memcached \ + php$version-mongodb \ + php$version-mysql \ + php$version-odbc \ + php$version-opcache \ + php$version-pgsql \ + php$version-phpdbg \ + php$version-pspell \ + php$version-readline \ + php$version-redis \ + php$version-snmp \ + php$version-soap \ + php$version-sqlite3 \ + php$version-sybase \ + php$version-tidy \ + php$version-xdebug \ + php$version-xml \ + php$version-xsl \ + php$version-yaml \ + php$version-zip \ + php$version-zmq + + apt-get install --no-install-recommends php$version-pcov + + # Disable PCOV, as Xdebug is enabled by default + # https://github.com/krakjoe/pcov#interoperability + phpdismod -v $version pcov + + if [[ $version == "7.2" || $version == "7.3" || $version == "7.4" ]]; then + apt-get install --no-install-recommends php$version-recode + fi + + if [[ $version != "8.0" && $version != "8.1" && $version != "8.2" && $version != "8.3" ]]; then + apt-get install --no-install-recommends php$version-xmlrpc php$version-json + fi +done + +apt-get install --no-install-recommends php-pear + +apt-get install --no-install-recommends snmp + +# Install composer +php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" +php -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;" +php composer-setup.php +sudo mv composer.phar /usr/bin/composer +php -r "unlink('composer-setup.php');" + +# Add composer bin folder to path +prepend_etc_environment_path '$HOME/.config/composer/vendor/bin' + +#Create composer folder for user to preserve folder permissions +mkdir -p /etc/skel/.composer + +# Install phpunit (for PHP) +wget -q -O phpunit https://phar.phpunit.de/phpunit-8.phar +install phpunit /usr/local/bin/phpunit + +# ubuntu 20.04 libzip-dev is libzip5 based and is not compatible libzip-dev of ppa:ondrej/php +# see https://github.com/actions/runner-images/issues/1084 +if is_ubuntu20; then + rm /etc/apt/sources.list.d/ondrej-*.list + apt-get update +fi + +invoke_tests "Common" "PHP" diff --git a/images/ubuntu/scripts/build/install-pipx-packages.sh b/images/ubuntu/scripts/build/install-pipx-packages.sh new file mode 100644 index 0000000..40356b6 --- /dev/null +++ b/images/ubuntu/scripts/build/install-pipx-packages.sh @@ -0,0 +1,25 @@ +#!/bin/bash -e +################################################################################ +## File: install-pipx-packages.sh +## Desc: Install tools via pipx +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +export PATH="$PATH:/opt/pipx_bin" + +pipx_packages=$(get_toolset_value ".pipx[] .package") + +for package in $pipx_packages; do + echo "Install $package into default python" + pipx install $package + + # https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html + # Install ansible into an existing ansible-core Virtual Environment + if [[ $package == "ansible-core" ]]; then + pipx inject $package ansible + fi +done + +invoke_tests "Common" "PipxPackages" diff --git a/images/ubuntu/scripts/build/install-postgresql.sh b/images/ubuntu/scripts/build/install-postgresql.sh new file mode 100644 index 0000000..05e2a7c --- /dev/null +++ b/images/ubuntu/scripts/build/install-postgresql.sh @@ -0,0 +1,37 @@ +#!/bin/bash -e +################################################################################ +## File: install-postgresql.sh +## Desc: Install PostgreSQL +################################################################################ + +# Source the helpers +source $HELPER_SCRIPTS/os.sh +source $HELPER_SCRIPTS/install.sh + +REPO_URL="https://apt.postgresql.org/pub/repos/apt/" + +# Preparing repo for PostgreSQL +wget -qO - https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor > /usr/share/keyrings/postgresql.gpg +echo "deb [signed-by=/usr/share/keyrings/postgresql.gpg] $REPO_URL $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list + +# Fetch PostgreSQL version to install from the toolset +toolset_version=$(get_toolset_value '.postgresql.version') + +# Install PostgreSQL +echo "Install PostgreSQL" +apt update +apt-get install postgresql-$toolset_version + +echo "Install libpq-dev" +apt-get install libpq-dev + +# Disable postgresql.service +systemctl is-active --quiet postgresql.service && systemctl stop postgresql.service +systemctl disable postgresql.service + +rm /etc/apt/sources.list.d/pgdg.list +rm /usr/share/keyrings/postgresql.gpg + +echo "postgresql $REPO_URL" >> $HELPER_SCRIPTS/apt-sources.txt + +invoke_tests "Databases" "PostgreSQL" diff --git a/images/ubuntu/scripts/build/install-powershell.sh b/images/ubuntu/scripts/build/install-powershell.sh new file mode 100644 index 0000000..a3802f3 --- /dev/null +++ b/images/ubuntu/scripts/build/install-powershell.sh @@ -0,0 +1,21 @@ +#!/bin/bash -e +################################################################################ +## File: install-powershell.sh +## Desc: Install PowerShell Core +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh +source $HELPER_SCRIPTS/os.sh + +pwsh_version=$(get_toolset_value .pwsh.version) + +# Install Powershell +if is_ubuntu24; then + dependency_path=$(download_with_retry "http://mirrors.kernel.org/ubuntu/pool/main/i/icu/libicu72_72.1-3ubuntu2_amd64.deb") + sudo dpkg -i "$dependency_path" + package_path=$(download_with_retry "https://github.com/PowerShell/PowerShell/releases/download/v7.4.2/powershell-lts_7.4.2-1.deb_amd64.deb") + sudo dpkg -i "$package_path" +else + apt-get install powershell=$pwsh_version* +fi diff --git a/images/ubuntu/scripts/build/install-pulumi.sh b/images/ubuntu/scripts/build/install-pulumi.sh new file mode 100644 index 0000000..9310466 --- /dev/null +++ b/images/ubuntu/scripts/build/install-pulumi.sh @@ -0,0 +1,23 @@ +#!/bin/bash -e +################################################################################ +## File: install-pulumi.sh +## Desc: Install Pulumi +## Supply chain security: Pulumi - checksum validation +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Dowload Pulumi +version=$(curl -fsSL "https://www.pulumi.com/latest-version") +download_url="https://get.pulumi.com/releases/sdk/pulumi-v${version}-linux-x64.tar.gz" +archive_path=$(download_with_retry "$download_url") + +# Supply chain security - Pulumi +external_hash=$(get_checksum_from_url "https://github.com/pulumi/pulumi/releases/download/v${version}/SHA512SUMS" "linux-x64.tar.gz" "SHA512") +use_checksum_comparison "$archive_path" "$external_hash" "512" + +# Unzipping Pulumi +tar --strip=1 -xf "$archive_path" -C /usr/local/bin + +invoke_tests "Tools" "Pulumi" diff --git a/images/ubuntu/scripts/build/install-pypy.sh b/images/ubuntu/scripts/build/install-pypy.sh new file mode 100644 index 0000000..61808c1 --- /dev/null +++ b/images/ubuntu/scripts/build/install-pypy.sh @@ -0,0 +1,93 @@ +#!/bin/bash -e +################################################################################ +## File: install-pypy.sh +## Desc: Install PyPy +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# This function installs PyPy using the specified arguments: +# $1=package_url +install_pypy() { + local package_url=$1 + + package_tar_name=$(echo "$package_url" | awk -F/ '{print $NF}') + package_name=${package_tar_name/.tar.bz2/} + + echo "Downloading tar archive '$package_name'" + package_tar_temp_path=$(download_with_retry $package_url) + + echo "Expand '$package_name' to the /tmp folder" + tar xf "$package_tar_temp_path" -C /tmp + + # Get Python version + major_version=$(echo ${package_name/pypy/} | cut -d. -f1) + python_major="python$major_version" + + if [ $major_version != 2 ]; then + pypy_major="pypy$major_version" + else + pypy_major="pypy" + fi + + package_temp_folder="/tmp/$package_name" + 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]))") + pypy_full_version=$("$package_temp_folder/bin/$pypy_major" -c "import sys;print('{}.{}.{}'.format(*sys.pypy_version_info[0:3]))") + echo "Put '$pypy_full_version' to PYPY_VERSION file" + echo $pypy_full_version > "$package_temp_folder/PYPY_VERSION" + + # PyPy folder structure + pypy_toolcache_path=$AGENT_TOOLSDIRECTORY/PyPy + pypy_toolcache_version_path=$pypy_toolcache_path/$python_full_version + pypy_toolcache_version_arch_path=$pypy_toolcache_version_path/x64 + + echo "Check if PyPy hostedtoolcache folder exist..." + if [ ! -d $pypy_toolcache_path ]; then + mkdir -p $pypy_toolcache_path + fi + + echo "Create PyPy '$pypy_toolcache_version_path' folder" + mkdir $pypy_toolcache_version_path + + echo "Move PyPy '$package_temp_folder' binaries to '$pypy_toolcache_version_arch_path' folder" + mv $package_temp_folder $pypy_toolcache_version_arch_path + + echo "Create additional symlinks (Required for UsePythonVersion Azure DevOps task)" + cd $pypy_toolcache_version_arch_path/bin + + # Starting from PyPy 7.3.4 these links are already included in the package + [ -f ./$python_major ] || ln -s $pypy_major $python_major + [ -f ./python ] || ln -s $python_major python + + chmod +x ./python ./$python_major + + echo "Install latest Pip" + ./python -m ensurepip + ./python -m pip install --ignore-installed pip + + echo "Create complete file" + touch $pypy_toolcache_version_path/x64.complete + + echo "Remove '$package_tar_temp_path'" + rm -f $package_tar_temp_path +} + +# Installation PyPy +pypy_versions_json=$(curl -fsSL https://downloads.python.org/pypy/versions.json) +toolset_versions=$(get_toolset_value '.toolcache[] | select(.name | contains("PyPy")) | .versions[]') + +for toolset_version in $toolset_versions; do + latest_major_pypy_version=$(echo $pypy_versions_json | + jq -r --arg toolset_version $toolset_version '.[] + | select((.python_version | startswith($toolset_version)) and .stable == true).files[] + | select(.arch == "x64" and .platform == "linux").download_url' | head -1) + if [[ -z "$latest_major_pypy_version" ]]; then + echo "Failed to get PyPy version '$toolset_version'" + exit 1 + fi + + install_pypy $latest_major_pypy_version +done + +chown -R "$SUDO_USER:$SUDO_USER" "$AGENT_TOOLSDIRECTORY/PyPy" diff --git a/images/ubuntu/scripts/build/install-python.sh b/images/ubuntu/scripts/build/install-python.sh new file mode 100644 index 0000000..c87dac5 --- /dev/null +++ b/images/ubuntu/scripts/build/install-python.sh @@ -0,0 +1,40 @@ +#!/bin/bash -e +################################################################################ +## File: install-python.sh +## Desc: Install Python 3 +################################################################################ + +set -e +# Source the helpers for use with the script +source $HELPER_SCRIPTS/etc-environment.sh +source $HELPER_SCRIPTS/os.sh + +# Install Python, Python 3, pip, pip3 +apt-get install --no-install-recommends python3 python3-dev python3-pip python3-venv + +# Install pipx +# Set pipx custom directory +export PIPX_BIN_DIR=/opt/pipx_bin +export PIPX_HOME=/opt/pipx +if is_ubuntu24; then + apt-get install --no-install-recommends pipx + pipx ensurepath +else + python3 -m pip install pipx + python3 -m pipx ensurepath +fi +# Update /etc/environment +set_etc_environment_variable "PIPX_BIN_DIR" $PIPX_BIN_DIR +set_etc_environment_variable "PIPX_HOME" $PIPX_HOME +prepend_etc_environment_path $PIPX_BIN_DIR + +# Test pipx +if ! command -v pipx; then + echo "pipx was not installed or not found on PATH" + exit 1 +fi + +# Adding this dir to PATH will make installed pip commands are immediately available. +prepend_etc_environment_path '$HOME/.local/bin' + +invoke_tests "Tools" "Python" diff --git a/images/ubuntu/scripts/build/install-rlang.sh b/images/ubuntu/scripts/build/install-rlang.sh new file mode 100644 index 0000000..42871ca --- /dev/null +++ b/images/ubuntu/scripts/build/install-rlang.sh @@ -0,0 +1,19 @@ +#!/bin/bash -e +################################################################################ +## File: install-rlang.sh +## Desc: Install R +################################################################################ + +# install R +os_label=$(lsb_release -cs) + +wget -qO- https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc | gpg --dearmor > /usr/share/keyrings/rlang.gpg +echo "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 + +apt-get update +apt-get install r-base + +rm /etc/apt/sources.list.d/rlang.list +rm /usr/share/keyrings/rlang.gpg + +invoke_tests "Tools" "R" diff --git a/images/ubuntu/scripts/build/install-ruby.sh b/images/ubuntu/scripts/build/install-ruby.sh new file mode 100644 index 0000000..a2afcf0 --- /dev/null +++ b/images/ubuntu/scripts/build/install-ruby.sh @@ -0,0 +1,63 @@ +#!/bin/bash -e +################################################################################ +## File: install-ruby.sh +## Desc: Install Ruby requirements and ruby gems +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/os.sh +source $HELPER_SCRIPTS/install.sh + +apt-get install ruby-full + +# temporary fix for fastlane installation https://github.com/sporkmonger/addressable/issues/541 +if is_ubuntu20; then + gem install public_suffix -v 5.1.1 +fi + +# Install ruby gems from toolset +gems_to_install=$(get_toolset_value ".rubygems[] .name") +if [[ -n "$gems_to_install" ]]; then + for gem in $gems_to_install; do + echo "Installing gem $gem" + gem install $gem + done +fi + +# Install Ruby requirements +apt-get install libz-dev openssl libssl-dev + +echo "Install Ruby from toolset..." +package_tar_names=$(curl -fsSL "https://api.github.com/repos/ruby/ruby-builder/releases/latest" | jq -r '.assets[].name') +toolset_versions=$(get_toolset_value '.toolcache[] | select(.name | contains("Ruby")) | .versions[]') +platform_version=$(get_toolset_value '.toolcache[] | select(.name | contains("Ruby")) | .platform_version') +ruby_path="$AGENT_TOOLSDIRECTORY/Ruby" + +echo "Check if Ruby hostedtoolcache folder exist..." +if [[ ! -d $ruby_path ]]; then + mkdir -p $ruby_path +fi + +for toolset_version in ${toolset_versions[@]}; do + package_tar_name=$(echo "$package_tar_names" | grep "^ruby-${toolset_version}-ubuntu-${platform_version}.tar.gz$" | sort -V | tail -1) + ruby_version=$(echo "$package_tar_name" | cut -d'-' -f 2) + ruby_version_path="$ruby_path/$ruby_version" + + echo "Create Ruby $ruby_version directory..." + mkdir -p $ruby_version_path + + echo "Downloading tar archive $package_tar_name" + download_url="https://github.com/ruby/ruby-builder/releases/download/toolcache/${package_tar_name}" + package_archive_path=$(download_with_retry "$download_url") + + echo "Expand '$package_tar_name' to the '$ruby_version_path' folder" + tar xf "$package_archive_path" -C $ruby_version_path + + complete_file_path="$ruby_version_path/x64.complete" + if [[ ! -f $complete_file_path ]]; then + echo "Create complete file" + touch $complete_file_path + fi +done + +invoke_tests "Tools" "Ruby" diff --git a/images/ubuntu/scripts/build/install-runner-package.sh b/images/ubuntu/scripts/build/install-runner-package.sh new file mode 100644 index 0000000..019689e --- /dev/null +++ b/images/ubuntu/scripts/build/install-runner-package.sh @@ -0,0 +1,15 @@ +#!/bin/bash -e +################################################################################ +## File: install-runner-package.sh +## Desc: Download and Install runner package +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +download_url=$(resolve_github_release_asset_url "actions/runner" 'test("actions-runner-linux-x64-[0-9]+\\.[0-9]{3}\\.[0-9]+\\.tar\\.gz$")' "latest") +archive_name="${download_url##*/}" +archive_path=$(download_with_retry "$download_url") + +mkdir -p /opt/runner-cache +mv "$archive_path" "/opt/runner-cache/$archive_name" diff --git a/images/ubuntu/scripts/build/install-rust.sh b/images/ubuntu/scripts/build/install-rust.sh new file mode 100644 index 0000000..f27a071 --- /dev/null +++ b/images/ubuntu/scripts/build/install-rust.sh @@ -0,0 +1,36 @@ +#!/bin/bash -e +################################################################################ +## File: install-rust.sh +## Desc: Install Rust +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/etc-environment.sh +source $HELPER_SCRIPTS/os.sh + +export RUSTUP_HOME=/etc/skel/.rustup +export CARGO_HOME=/etc/skel/.cargo + +curl -fsSL https://sh.rustup.rs | sh -s -- -y --default-toolchain=stable --profile=minimal + +# Initialize environment variables +source $CARGO_HOME/env + +# Install common tools +rustup component add rustfmt clippy + +if is_ubuntu22; then + cargo install bindgen-cli cbindgen cargo-audit cargo-outdated +fi + +if is_ubuntu20; then + cargo install bindgen-cli cbindgen cargo-audit cargo-outdated +fi + +# Cleanup Cargo cache +rm -rf ${CARGO_HOME}/registry/* + +# Update /etc/environment +prepend_etc_environment_path '$HOME/.cargo/bin' + +invoke_tests "Tools" "Rust" diff --git a/images/ubuntu/scripts/build/install-sbt.sh b/images/ubuntu/scripts/build/install-sbt.sh new file mode 100644 index 0000000..037b8e5 --- /dev/null +++ b/images/ubuntu/scripts/build/install-sbt.sh @@ -0,0 +1,15 @@ +#!/bin/bash -e +################################################################################ +## File: install-sbt.sh +## Desc: Install sbt +################################################################################ + +source $HELPER_SCRIPTS/install.sh + +# Install latest sbt release +download_url=$(resolve_github_release_asset_url "sbt/sbt" "endswith(\".tgz\")" "latest") +archive_path=$(download_with_retry "$download_url") +tar zxf "$archive_path" -C /usr/share +ln -s /usr/share/sbt/bin/sbt /usr/bin/sbt + +invoke_tests "Tools" "Sbt" diff --git a/images/ubuntu/scripts/build/install-selenium.sh b/images/ubuntu/scripts/build/install-selenium.sh new file mode 100644 index 0000000..eb6f008 --- /dev/null +++ b/images/ubuntu/scripts/build/install-selenium.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e +################################################################################ +## File: install-selenium.sh +## Desc: Install selenium server +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh +source $HELPER_SCRIPTS/etc-environment.sh + +selenium_major_version=$(get_toolset_value '.selenium.version') + +# Download Selenium server +selenium_download_url=$(resolve_github_release_asset_url "SeleniumHQ/selenium" "contains(\"selenium-server-\") and endswith(\".jar\")" "$selenium_major_version\.+" "" "true") +selenium_jar_path=$(download_with_retry "$selenium_download_url" "/usr/share/java/selenium-server.jar") + +# Create an empty file to retrieve selenium version +selenium_full_version=$(echo $selenium_download_url | awk -F"selenium-server-|.jar" '{print $2}') +touch "/usr/share/java/selenium-server-$selenium_full_version" + +# Add SELENIUM_JAR_PATH environment variable +set_etc_environment_variable "SELENIUM_JAR_PATH" "$selenium_jar_path" + +invoke_tests "Tools" "Selenium" diff --git a/images/ubuntu/scripts/build/install-sqlpackage.sh b/images/ubuntu/scripts/build/install-sqlpackage.sh new file mode 100644 index 0000000..b9c979e --- /dev/null +++ b/images/ubuntu/scripts/build/install-sqlpackage.sh @@ -0,0 +1,29 @@ +#!/bin/bash -e +################################################################################ +## File: install-sqlpackage.sh +## Desc: Install SqlPackage CLI to DacFx (https://docs.microsoft.com/sql/tools/sqlpackage/sqlpackage-download#get-sqlpackage-net-core-for-linux) +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh +source $HELPER_SCRIPTS/os.sh + +# Install libssl1.1 dependency +if is_ubuntu22; then + focal_list=/etc/apt/sources.list.d/focal-security.list + echo "deb http://archive.ubuntu.com/ubuntu/ focal-security main" | tee "${focal_list}" + apt-get update --quiet + + apt-get install --no-install-recommends libssl1.1 + + rm "${focal_list}" + apt-get update --quiet +fi + +# Install SqlPackage +archive_path=$(download_with_retry "https://aka.ms/sqlpackage-linux") +unzip -qq "$archive_path" -d /usr/local/sqlpackage +chmod +x /usr/local/sqlpackage/sqlpackage +ln -sf /usr/local/sqlpackage/sqlpackage /usr/local/bin + +invoke_tests "Tools" "SqlPackage" diff --git a/images/ubuntu/scripts/build/install-swift.sh b/images/ubuntu/scripts/build/install-swift.sh new file mode 100644 index 0000000..8627106 --- /dev/null +++ b/images/ubuntu/scripts/build/install-swift.sh @@ -0,0 +1,57 @@ +#!/bin/bash -e +################################################################################ +## File: install-swift.sh +## Desc: Install Swift +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh +source $HELPER_SCRIPTS/etc-environment.sh + +# Install +image_label="ubuntu$(lsb_release -rs)" +swift_version=$(curl -fsSL "https://api.github.com/repos/apple/swift/releases/latest" | jq -r '.tag_name | match("[0-9.]+").string') +swift_release_name="swift-${swift_version}-RELEASE-${image_label}" + +archive_url="https://swift.org/builds/swift-${swift_version}-release/${image_label//./}/swift-${swift_version}-RELEASE/${swift_release_name}.tar.gz" +archive_path=$(download_with_retry "$archive_url") + +# Verifying PGP signature using official Swift PGP key. Referring to https://www.swift.org/install/linux/#Installation-via-Tarball +# Download and import Swift PGP keys +gpg --keyserver hkp://keyserver.ubuntu.com \ + --recv-keys \ + '7463 A81A 4B2E EA1B 551F FBCF D441 C977 412B 37AD' \ + '1BE1 E29A 084C B305 F397 D62A 9F59 7F4D 21A5 6D5F' \ + 'A3BA FD35 56A5 9079 C068 94BD 63BC 1CFE 91D3 06C6' \ + '5E4D F843 FB06 5D7F 7E24 FBA2 EF54 30F0 71E1 B235' \ + '8513 444E 2DA3 6B7C 1659 AF4D 7638 F1FB 2B2B 08C4' \ + 'A62A E125 BBBF BB96 A6E0 42EC 925C C1CC ED3D 1561' \ + '8A74 9566 2C3C D4AE 18D9 5637 FAF6 989E 1BC1 6FEA' \ + 'E813 C892 820A 6FA1 3755 B268 F167 DF1A CF9C E069' \ + '52BB 7E3D E28A 71BE 22EC 05FF EF80 A866 B47A 981F' +gpg --keyserver hkp://keyserver.ubuntu.com --refresh-keys Swift + +# Download and verify signature +signature_path=$(download_with_retry "${archive_url}.sig") +gpg --verify "$signature_path" "$archive_path" + +# Remove Swift PGP public key with temporary keyring +rm -rf ~/.gnupg + +# Extract and install swift +tar xzf "$archive_path" -C /tmp + +SWIFT_INSTALL_ROOT="/usr/share/swift" +swift_bin_root="$SWIFT_INSTALL_ROOT/usr/bin" +swift_lib_root="$SWIFT_INSTALL_ROOT/usr/lib" + +mv "/tmp/${swift_release_name}" $SWIFT_INSTALL_ROOT +mkdir -p /usr/local/lib + +ln -s "$swift_bin_root/swift" /usr/local/bin/swift +ln -s "$swift_bin_root/swiftc" /usr/local/bin/swiftc +ln -s "$swift_lib_root/libsourcekitdInProc.so" /usr/local/lib/libsourcekitdInProc.so + +set_etc_environment_variable "SWIFT_PATH" "${swift_bin_root}" + +invoke_tests "Common" "Swift" diff --git a/images/ubuntu/scripts/build/install-terraform.sh b/images/ubuntu/scripts/build/install-terraform.sh new file mode 100644 index 0000000..7392eb8 --- /dev/null +++ b/images/ubuntu/scripts/build/install-terraform.sh @@ -0,0 +1,14 @@ +#!/bin/bash -e +################################################################################ +## File: install-terraform.sh +## Desc: Install terraform +################################################################################ + +source $HELPER_SCRIPTS/install.sh + +# Install Terraform +download_url=$(curl -fsSL https://api.releases.hashicorp.com/v1/releases/terraform/latest | jq -r '.builds[] | select((.arch=="amd64") and (.os=="linux")).url') +archive_path=$(download_with_retry "${download_url}") +unzip -qq "$archive_path" -d /usr/local/bin + +invoke_tests "Tools" "Terraform" diff --git a/images/ubuntu/scripts/build/install-vcpkg.sh b/images/ubuntu/scripts/build/install-vcpkg.sh new file mode 100644 index 0000000..5fd65f3 --- /dev/null +++ b/images/ubuntu/scripts/build/install-vcpkg.sh @@ -0,0 +1,30 @@ +#!/bin/bash -e +################################################################################ +## File: install-vcpkg.sh +## Desc: Install vcpkg +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/etc-environment.sh + +# Set env variable for vcpkg +VCPKG_INSTALLATION_ROOT=/usr/local/share/vcpkg +set_etc_environment_variable "VCPKG_INSTALLATION_ROOT" "${VCPKG_INSTALLATION_ROOT}" + +# Install vcpkg +git clone https://github.com/Microsoft/vcpkg $VCPKG_INSTALLATION_ROOT + +$VCPKG_INSTALLATION_ROOT/bootstrap-vcpkg.sh + +# workaround https://github.com/microsoft/vcpkg/issues/27786 + +mkdir -p /root/.vcpkg/ $HOME/.vcpkg +touch /root/.vcpkg/vcpkg.path.txt $HOME/.vcpkg/vcpkg.path.txt + +$VCPKG_INSTALLATION_ROOT/vcpkg integrate install +chmod 0777 -R $VCPKG_INSTALLATION_ROOT +ln -sf $VCPKG_INSTALLATION_ROOT/vcpkg /usr/local/bin + +rm -rf /root/.vcpkg $HOME/.vcpkg + +invoke_tests "Tools" "Vcpkg" diff --git a/images/ubuntu/scripts/build/install-yq.sh b/images/ubuntu/scripts/build/install-yq.sh new file mode 100644 index 0000000..9caf52a --- /dev/null +++ b/images/ubuntu/scripts/build/install-yq.sh @@ -0,0 +1,23 @@ +#!/bin/bash -e +################################################################################ +## File: install-yq.sh +## Desc: Install yq - a command-line YAML, JSON and XML processor +## Supply chain security: yq - checksum validation +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Download yq +yq_url=$(resolve_github_release_asset_url "mikefarah/yq" "endswith(\"yq_linux_amd64\")" "latest") +binary_path=$(download_with_retry "${yq_url}") + +# Supply chain security - yq +hash_url=$(resolve_github_release_asset_url "mikefarah/yq" "endswith(\"checksums\")" "latest") +external_hash=$(get_checksum_from_url "${hash_url}" "yq_linux_amd64 " "SHA256" "true" " " "19") +use_checksum_comparison "$binary_path" "$external_hash" + +# Install yq +install "$binary_path" /usr/bin/yq + +invoke_tests "Tools" "yq" diff --git a/images/ubuntu/scripts/build/install-zstd.sh b/images/ubuntu/scripts/build/install-zstd.sh new file mode 100644 index 0000000..1f26c24 --- /dev/null +++ b/images/ubuntu/scripts/build/install-zstd.sh @@ -0,0 +1,38 @@ +#!/bin/bash -e +################################################################################ +## File: install-zstd.sh +## Desc: Install zstd +## Supply chain security: zstd - checksum validation +################################################################################ + +# Source the helpers for use with the script +source $HELPER_SCRIPTS/install.sh + +# Download zstd +release_tag=$(curl -fsSL https://api.github.com/repos/facebook/zstd/releases/latest | jq -r '.tag_name') +release_name="zstd-${release_tag//v}" +download_url="https://github.com/facebook/zstd/releases/download/${release_tag}/${release_name}.tar.gz" +archive_path=$(download_with_retry "${download_url}") + +# Supply chain security - zstd +external_hash=$(get_checksum_from_url "${download_url}.sha256" "${release_name}.tar.gz" "SHA256") +use_checksum_comparison "$archive_path" "$external_hash" + +# Install zstd +apt-get install liblz4-dev +tar xzf "$archive_path" -C /tmp + +make -C "/tmp/${release_name}/contrib/pzstd" all +make -C "/tmp/${release_name}" zstd-release + +for copyprocess in zstd zstdless zstdgrep; do + cp "/tmp/${release_name}/programs/${copyprocess}" /usr/local/bin/ +done + +cp "/tmp/${release_name}/contrib/pzstd/pzstd" /usr/local/bin/ + +for symlink in zstdcat zstdmt unzstd; do + ln -sf /usr/local/bin/zstd /usr/local/bin/${symlink} +done + +invoke_tests "Tools" "Zstd" diff --git a/images/ubuntu/scripts/docs-gen/Generate-SoftwareReport.ps1 b/images/ubuntu/scripts/docs-gen/Generate-SoftwareReport.ps1 new file mode 100644 index 0000000..b06946a --- /dev/null +++ b/images/ubuntu/scripts/docs-gen/Generate-SoftwareReport.ps1 @@ -0,0 +1,278 @@ +using module ./software-report-base/SoftwareReport.psm1 +using module ./software-report-base/SoftwareReport.Nodes.psm1 + +param ( + [Parameter(Mandatory)] + [string] $OutputDirectory +) + +$global:ErrorActionPreference = "Stop" +$global:ErrorView = "NormalView" +Set-StrictMode -Version Latest + +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Android.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Browsers.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.CachedTools.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Common.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Databases.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Helpers.psm1") -DisableNameChecking +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Java.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Rust.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Tools.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.WebServers.psm1") -DisableNameChecking + +# Restore file owner in user profile +sudo chown -R ${env:USER}: $env:HOME + +# Software report +$softwareReport = [SoftwareReport]::new("Ubuntu $(Get-OSVersionShort)") +$softwareReport.Root.AddToolVersion("OS Version:", $(Get-OSVersionFull)) +$softwareReport.Root.AddToolVersion("Kernel Version:", $(Get-KernelVersion)) +$softwareReport.Root.AddToolVersion("Image Version:", $env:IMAGE_VERSION) +$softwareReport.Root.AddToolVersion("Systemd version:", $(Get-SystemdVersion)) + +$installedSoftware = $softwareReport.Root.AddHeader("Installed Software") + +# Language and Runtime +$languageAndRuntime = $installedSoftware.AddHeader("Language and Runtime") +$languageAndRuntime.AddToolVersion("Bash", $(Get-BashVersion)) +$languageAndRuntime.AddToolVersionsListInline("Clang", $(Get-ClangToolVersions -ToolName "clang"), "^\d+") +$languageAndRuntime.AddToolVersionsListInline("Clang-format", $(Get-ClangToolVersions -ToolName "clang-format"), "^\d+") +$languageAndRuntime.AddToolVersionsListInline("Clang-tidy", $(Get-ClangTidyVersions), "^\d+") +$languageAndRuntime.AddToolVersion("Dash", $(Get-DashVersion)) +if (Test-IsUbuntu20) { + $languageAndRuntime.AddToolVersion("Erlang", $(Get-ErlangVersion)) + $languageAndRuntime.AddToolVersion("Erlang rebar3", $(Get-ErlangRebar3Version)) +} +$languageAndRuntime.AddToolVersionsListInline("GNU C++", $(Get-CPPVersions), "^\d+") +$languageAndRuntime.AddToolVersionsListInline("GNU Fortran", $(Get-FortranVersions), "^\d+") +$languageAndRuntime.AddToolVersion("Julia", $(Get-JuliaVersion)) +$languageAndRuntime.AddToolVersion("Kotlin", $(Get-KotlinVersion)) +if (-not $(Test-IsUbuntu24)) { + $languageAndRuntime.AddToolVersion("Mono", $(Get-MonoVersion)) + $languageAndRuntime.AddToolVersion("MSBuild", $(Get-MsbuildVersion)) +} +$languageAndRuntime.AddToolVersion("Node.js", $(Get-NodeVersion)) +$languageAndRuntime.AddToolVersion("Perl", $(Get-PerlVersion)) +$languageAndRuntime.AddToolVersion("Python", $(Get-PythonVersion)) +$languageAndRuntime.AddToolVersion("Ruby", $(Get-RubyVersion)) +$languageAndRuntime.AddToolVersion("Swift", $(Get-SwiftVersion)) + + +# Package Management +$packageManagement = $installedSoftware.AddHeader("Package Management") +$packageManagement.AddToolVersion("cpan", $(Get-CpanVersion)) +$packageManagement.AddToolVersion("Helm", $(Get-HelmVersion)) +$packageManagement.AddToolVersion("Homebrew", $(Get-HomebrewVersion)) +$packageManagement.AddToolVersion("Miniconda", $(Get-MinicondaVersion)) +$packageManagement.AddToolVersion("Npm", $(Get-NpmVersion)) +if (-not $(Test-IsUbuntu24)) { + $packageManagement.AddToolVersion("NuGet", $(Get-NuGetVersion)) +} +$packageManagement.AddToolVersion("Pip", $(Get-PipVersion)) +$packageManagement.AddToolVersion("Pip3", $(Get-Pip3Version)) +$packageManagement.AddToolVersion("Pipx", $(Get-PipxVersion)) +$packageManagement.AddToolVersion("RubyGems", $(Get-GemVersion)) +$packageManagement.AddToolVersion("Vcpkg", $(Get-VcpkgVersion)) +$packageManagement.AddToolVersion("Yarn", $(Get-YarnVersion)) +$packageManagement.AddHeader("Environment variables").AddTable($(Build-PackageManagementEnvironmentTable)) +$packageManagement.AddHeader("Homebrew note").AddNote(@' +Location: /home/linuxbrew +Note: Homebrew is pre-installed on image but not added to PATH. +run the eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" command +to accomplish this. +'@) + +# Project Management +$projectManagement = $installedSoftware.AddHeader("Project Management") +$projectManagement.AddToolVersion("Ant", $(Get-AntVersion)) +$projectManagement.AddToolVersion("Gradle", $(Get-GradleVersion)) +$projectManagement.AddToolVersion("Lerna", $(Get-LernaVersion)) +$projectManagement.AddToolVersion("Maven", $(Get-MavenVersion)) +if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) { + $projectManagement.AddToolVersion("Sbt", $(Get-SbtVersion)) +} + +# Tools +$tools = $installedSoftware.AddHeader("Tools") +$tools.AddToolVersion("Ansible", $(Get-AnsibleVersion)) +if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) { + $tools.AddToolVersion("apt-fast", $(Get-AptFastVersion)) +} +$tools.AddToolVersion("AzCopy", $(Get-AzCopyVersion)) +$tools.AddToolVersion("Bazel", $(Get-BazelVersion)) +$tools.AddToolVersion("Bazelisk", $(Get-BazeliskVersion)) +$tools.AddToolVersion("Bicep", $(Get-BicepVersion)) +$tools.AddToolVersion("Buildah", $(Get-BuildahVersion)) +$tools.AddToolVersion("CMake", $(Get-CMakeVersion)) +$tools.AddToolVersion("CodeQL Action Bundle", $(Get-CodeQLBundleVersion)) +$tools.AddToolVersion("Docker Amazon ECR Credential Helper", $(Get-DockerAmazonECRCredHelperVersion)) +$tools.AddToolVersion("Docker Compose v2", $(Get-DockerComposeV2Version)) +$tools.AddToolVersion("Docker-Buildx", $(Get-DockerBuildxVersion)) +$tools.AddToolVersion("Docker Client", $(Get-DockerClientVersion)) +$tools.AddToolVersion("Docker Server", $(Get-DockerServerVersion)) +$tools.AddToolVersion("Fastlane", $(Get-FastlaneVersion)) +$tools.AddToolVersion("Git", $(Get-GitVersion)) +$tools.AddToolVersion("Git LFS", $(Get-GitLFSVersion)) +$tools.AddToolVersion("Git-ftp", $(Get-GitFTPVersion)) +$tools.AddToolVersion("Haveged", $(Get-HavegedVersion)) +if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) { + $tools.AddToolVersion("Heroku", $(Get-HerokuVersion)) +} +if (Test-IsUbuntu20) { + $tools.AddToolVersion("HHVM (HipHop VM)", $(Get-HHVMVersion)) +} +$tools.AddToolVersion("jq", $(Get-JqVersion)) +$tools.AddToolVersion("Kind", $(Get-KindVersion)) +$tools.AddToolVersion("Kubectl", $(Get-KubectlVersion)) +$tools.AddToolVersion("Kustomize", $(Get-KustomizeVersion)) +if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) { + $tools.AddToolVersion("Leiningen", $(Get-LeiningenVersion)) +} +$tools.AddToolVersion("MediaInfo", $(Get-MediainfoVersion)) +$tools.AddToolVersion("Mercurial", $(Get-HGVersion)) +$tools.AddToolVersion("Minikube", $(Get-MinikubeVersion)) +$tools.AddToolVersion("n", $(Get-NVersion)) +$tools.AddToolVersion("Newman", $(Get-NewmanVersion)) +if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) { + $tools.AddToolVersion("nvm", $(Get-NvmVersion)) +} +$tools.AddToolVersion("OpenSSL", $(Get-OpensslVersion)) +$tools.AddToolVersion("Packer", $(Get-PackerVersion)) +$tools.AddToolVersion("Parcel", $(Get-ParcelVersion)) +if (Test-IsUbuntu20) { + $tools.AddToolVersion("PhantomJS", $(Get-PhantomJSVersion)) +} +$tools.AddToolVersion("Podman", $(Get-PodManVersion)) +$tools.AddToolVersion("Pulumi", $(Get-PulumiVersion)) +if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) { + $tools.AddToolVersion("R", $(Get-RVersion)) +} +$tools.AddToolVersion("Skopeo", $(Get-SkopeoVersion)) +$tools.AddToolVersion("Sphinx Open Source Search Server", $(Get-SphinxVersion)) +if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) { + $tools.AddToolVersion("SVN", $(Get-SVNVersion)) + $tools.AddToolVersion("Terraform", $(Get-TerraformVersion)) +} +$tools.AddToolVersion("yamllint", $(Get-YamllintVersion)) +$tools.AddToolVersion("yq", $(Get-YqVersion)) +$tools.AddToolVersion("zstd", $(Get-ZstdVersion)) + +# CLI Tools +$cliTools = $installedSoftware.AddHeader("CLI Tools") +if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) { + $cliTools.AddToolVersion("Alibaba Cloud CLI", $(Get-AlibabaCloudCliVersion)) +} +$cliTools.AddToolVersion("AWS CLI", $(Get-AWSCliVersion)) +$cliTools.AddToolVersion("AWS CLI Session Manager Plugin", $(Get-AWSCliSessionManagerPluginVersion)) +$cliTools.AddToolVersion("AWS SAM CLI", $(Get-AWSSAMVersion)) +$cliTools.AddToolVersion("Azure CLI", $(Get-AzureCliVersion)) +$cliTools.AddToolVersion("Azure CLI (azure-devops)", $(Get-AzureDevopsVersion)) +$cliTools.AddToolVersion("GitHub CLI", $(Get-GitHubCliVersion)) +if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) { + $cliTools.AddToolVersion("Google Cloud CLI", $(Get-GoogleCloudCLIVersion)) + $cliTools.AddToolVersion("Netlify CLI", $(Get-NetlifyCliVersion)) + $cliTools.AddToolVersion("OpenShift CLI", $(Get-OCCliVersion)) + $cliTools.AddToolVersion("ORAS CLI", $(Get-ORASCliVersion)) + $cliTools.AddToolVersion("Vercel CLI", $(Get-VerselCliversion)) +} + +# Java +$installedSoftware.AddHeader("Java").AddTable($(Get-JavaVersionsTable)) + +# PHP Tools +$phpTools = $installedSoftware.AddHeader("PHP Tools") +$phpTools.AddToolVersionsListInline("PHP", $(Get-PHPVersions), "^\d+\.\d+") +$phpTools.AddToolVersion("Composer", $(Get-ComposerVersion)) +$phpTools.AddToolVersion("PHPUnit", $(Get-PHPUnitVersion)) +$phpTools.AddNote("Both Xdebug and PCOV extensions are installed, but only Xdebug is enabled.") + +# Haskell Tools +$haskellTools = $installedSoftware.AddHeader("Haskell Tools") +$haskellTools.AddToolVersion("Cabal", $(Get-CabalVersion)) +$haskellTools.AddToolVersion("GHC", $(Get-GHCVersion)) +$haskellTools.AddToolVersion("GHCup", $(Get-GHCupVersion)) +$haskellTools.AddToolVersion("Stack", $(Get-StackVersion)) + +# Rust Tools +Initialize-RustEnvironment +$rustTools = $installedSoftware.AddHeader("Rust Tools") +$rustTools.AddToolVersion("Cargo", $(Get-CargoVersion)) +$rustTools.AddToolVersion("Rust", $(Get-RustVersion)) +$rustTools.AddToolVersion("Rustdoc", $(Get-RustdocVersion)) +$rustTools.AddToolVersion("Rustup", $(Get-RustupVersion)) + +# Packages +$rustToolsPackages = $rustTools.AddHeader("Packages") +if ((Test-IsUbuntu20) -or (Test-IsUbuntu22)) { + $rustToolsPackages.AddToolVersion("Bindgen", $(Get-BindgenVersion)) + $rustToolsPackages.AddToolVersion("Cargo audit", $(Get-CargoAuditVersion)) + $rustToolsPackages.AddToolVersion("Cargo clippy", $(Get-CargoClippyVersion)) + $rustToolsPackages.AddToolVersion("Cargo outdated", $(Get-CargoOutdatedVersion)) + $rustToolsPackages.AddToolVersion("Cbindgen", $(Get-CbindgenVersion)) +} +$rustToolsPackages.AddToolVersion("Rustfmt", $(Get-RustfmtVersion)) + +# Browsers and Drivers +$browsersTools = $installedSoftware.AddHeader("Browsers and Drivers") +$browsersTools.AddToolVersion("Google Chrome", $(Get-ChromeVersion)) +$browsersTools.AddToolVersion("ChromeDriver", $(Get-ChromeDriverVersion)) +$browsersTools.AddToolVersion("Chromium", $(Get-ChromiumVersion)) +$browsersTools.AddToolVersion("Microsoft Edge", $(Get-EdgeVersion)) +$browsersTools.AddToolVersion("Microsoft Edge WebDriver", $(Get-EdgeDriverVersion)) + +$browsersTools.AddToolVersion("Selenium server", $(Get-SeleniumVersion)) +$browsersTools.AddToolVersion("Mozilla Firefox", $(Get-FirefoxVersion)) +$browsersTools.AddToolVersion("Geckodriver", $(Get-GeckodriverVersion)) + + +# Environment variables +$browsersTools.AddHeader("Environment variables").AddTable($(Build-BrowserWebdriversEnvironmentTable)) + +# .NET Tools +$netCoreTools = $installedSoftware.AddHeader(".NET Tools") +$netCoreTools.AddToolVersionsListInline(".NET Core SDK", $(Get-DotNetCoreSdkVersions), "^\d+\.\d+\.\d") +$netCoreTools.AddNodes($(Get-DotnetTools)) + +# Databases +$databasesTools = $installedSoftware.AddHeader("Databases") +if (Test-IsUbuntu20) { + $databasesTools.AddToolVersion("MongoDB", $(Get-MongoDbVersion)) +} +$databasesTools.AddToolVersion("sqlite3", $(Get-SqliteVersion)) +$databasesTools.AddNode($(Build-PostgreSqlSection)) +$databasesTools.AddNode($(Build-MySQLSection)) +if (-not $(Test-IsUbuntu24)) { + $databasesTools.AddNode($(Build-MSSQLToolsSection)) +} + +# Cached Tools +$cachedTools = $installedSoftware.AddHeader("Cached Tools") +$cachedTools.AddToolVersionsList("Go", $(Get-ToolcacheGoVersions), "^\d+\.\d+") +$cachedTools.AddToolVersionsList("Node.js", $(Get-ToolcacheNodeVersions), "^\d+") +$cachedTools.AddToolVersionsList("Python", $(Get-ToolcachePythonVersions), "^\d+\.\d+") +$cachedTools.AddToolVersionsList("PyPy", $(Get-ToolcachePyPyVersions), "^\d+\.\d+") +if (-not $(Test-IsUbuntu24)) { + $cachedTools.AddToolVersionsList("Ruby", $(Get-ToolcacheRubyVersions), "^\d+\.\d+") +} + +# PowerShell Tools +$powerShellTools = $installedSoftware.AddHeader("PowerShell Tools") +$powerShellTools.AddToolVersion("PowerShell", $(Get-PowershellVersion)) +$powerShellTools.AddHeader("PowerShell Modules").AddNodes($(Get-PowerShellModules)) + +$installedSoftware.AddHeader("Web Servers").AddTable($(Build-WebServersTable)) + +$androidTools = $installedSoftware.AddHeader("Android") +$androidTools.AddTable($(Build-AndroidTable)) + +$androidTools.AddHeader("Environment variables").AddTable($(Build-AndroidEnvironmentTable)) + +if (-not $(Test-IsUbuntu24)) { + $installedSoftware.AddHeader("Cached Docker images").AddTable($(Get-CachedDockerImagesTableData)) +} +$installedSoftware.AddHeader("Installed apt packages").AddTable($(Get-AptPackages)) + +$softwareReport.ToJson() | Out-File -FilePath "${OutputDirectory}/software-report.json" -Encoding UTF8NoBOM +$softwareReport.ToMarkdown() | Out-File -FilePath "${OutputDirectory}/software-report.md" -Encoding UTF8NoBOM diff --git a/images/ubuntu/scripts/docs-gen/SoftwareReport.Android.psm1 b/images/ubuntu/scripts/docs-gen/SoftwareReport.Android.psm1 new file mode 100644 index 0000000..5ecc901 --- /dev/null +++ b/images/ubuntu/scripts/docs-gen/SoftwareReport.Android.psm1 @@ -0,0 +1,173 @@ +function Split-TableRowByColumns { + param( + [string] $Row + ) + return $Row.Split("|") | ForEach-Object { $_.trim() } +} + +function Get-AndroidSDKRoot { + return "/usr/local/lib/android/sdk" +} + +function Get-AndroidSDKManagerPath { + $androidSDKDir = Get-AndroidSDKRoot + return Join-Path $androidSDKDir "cmdline-tools" "latest" "bin" "sdkmanager" +} + +function Get-AndroidInstalledPackages { + $androidSDKManagerPath = Get-AndroidSDKManagerPath + $androidSDKManagerList = Invoke-Expression "$androidSDKManagerPath --list_installed --include_obsolete" + return $androidSDKManagerList +} + +function Build-AndroidTable { + $packageInfo = Get-AndroidInstalledPackages + return @( + @{ + "Package" = "Android Command Line Tools" + "Version" = Get-AndroidCommandLineToolsVersion + }, + @{ + "Package" = "Android Emulator" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android Emulator" + }, + @{ + "Package" = "Android SDK Build-tools" + "Version" = Get-AndroidBuildToolVersions -PackageInfo $packageInfo + }, + @{ + "Package" = "Android SDK Platform-Tools" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android SDK Platform-Tools" + }, + @{ + "Package" = "Android SDK Platforms" + "Version" = Get-AndroidPlatformVersions -PackageInfo $packageInfo + }, + @{ + "Package" = "Android Support Repository" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android Support Repository" + }, + @{ + "Package" = "CMake" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "cmake" + }, + @{ + "Package" = "Google APIs" + "Version" = Get-AndroidGoogleAPIsVersions -PackageInfo $packageInfo + }, + @{ + "Package" = "Google Play services" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Google Play services" + }, + @{ + "Package" = "Google Repository" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Google Repository" + }, + @{ + "Package" = "NDK" + "Version" = Get-AndroidNDKVersions + }, + @{ + "Package" = "SDK Patch Applier v4" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "SDK Patch Applier v4" + } + ) | Where-Object { $_.Version } | ForEach-Object { + [PSCustomObject] @{ + "Package Name" = $_.Package + "Version" = $_.Version + } + } +} + +function Get-AndroidPackageVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo, + [Parameter(Mandatory)] + [object] $MatchedString + ) + + $versions = $PackageInfo | Where-Object { $_ -Match $MatchedString } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + return $packageInfoParts[1] + } + return ($versions -Join "
") +} + +function Get-AndroidPlatformVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo + ) + + $versions = $PackageInfo | Where-Object { $_ -Match "Android SDK Platform " } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + $revision = $packageInfoParts[1] + $version = $packageInfoParts[0].split(";")[1] + return "$version (rev $revision)" + } + [array]::Reverse($versions) + return ($versions -Join "
") +} + +function Get-AndroidCommandLineToolsVersion { + $commandLineTools = Get-AndroidSDKManagerPath + (& $commandLineTools --version | Out-String).Trim() -match "(?^(\d+\.){1,}\d+$)" | Out-Null + $commandLineToolsVersion = $Matches.Version + return $commandLineToolsVersion +} + +function Get-AndroidBuildToolVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo + ) + + $versions = $PackageInfo | Where-Object { $_ -Match "Android SDK Build-Tools" } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + return $packageInfoParts[1] + } + $groupVersions = @() + $versions | ForEach-Object { + $majorVersion = $_.Split(".")[0] + $groupVersions += $versions | Where-Object { $_.StartsWith($majorVersion) } | Join-String -Separator " " + } + return ($groupVersions | Sort-Object -Descending -Unique | Join-String -Separator "
") +} + +function Get-AndroidGoogleAPIsVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo + ) + + $versions = $PackageInfo | Where-Object { $_ -Match "Google APIs" } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + return $packageInfoParts[0].split(";")[1] + } + return ($versions -Join "
") +} + +function Get-AndroidNDKVersions { + $ndkFolderPath = Join-Path (Get-AndroidSDKRoot) "ndk" + $versions = Get-ChildItem -Path $ndkFolderPath -Name + $ndkDefaultVersion = (Get-ToolsetContent).android.ndk.default + $ndkDefaultFullVersion = Get-ChildItem "$env:ANDROID_HOME/ndk/$ndkDefaultVersion.*" -Name | Select-Object -Last 1 + + return ($versions | ForEach-Object { + $defaultPostfix = ( $_ -eq $ndkDefaultFullVersion ) ? " (default)" : "" + $_ + $defaultPostfix + } | Join-String -Separator "
") +} + +function Build-AndroidEnvironmentTable { + $androidVersions = Get-Item env:ANDROID_* + + $shouldResolveLink = 'ANDROID_NDK', 'ANDROID_NDK_HOME', 'ANDROID_NDK_ROOT', 'ANDROID_NDK_LATEST_HOME' + return $androidVersions | Sort-Object -Property Name | ForEach-Object { + [PSCustomObject] @{ + "Name" = $_.Name + "Value" = if ($shouldResolveLink.Contains($_.Name )) { Get-PathWithLink($_.Value) } else {$_.Value} + } + } +} diff --git a/images/ubuntu/scripts/docs-gen/SoftwareReport.Browsers.psm1 b/images/ubuntu/scripts/docs-gen/SoftwareReport.Browsers.psm1 new file mode 100644 index 0000000..687d693 --- /dev/null +++ b/images/ubuntu/scripts/docs-gen/SoftwareReport.Browsers.psm1 @@ -0,0 +1,60 @@ +function Get-ChromeVersion { + $googleChromeVersion = google-chrome --version | Get-StringPart -Part 2 + return $googleChromeVersion +} + +function Get-ChromeDriverVersion { + $chromeDriverVersion = chromedriver --version | Get-StringPart -Part 1 + return $chromeDriverVersion +} + +function Get-FirefoxVersion { + $firefoxVersion = $(firefox --version) | Get-StringPart -Part 2 + return $firefoxVersion +} + +function Get-GeckodriverVersion { + $geckodriverVersion = geckodriver --version | Select-Object -First 1 | Get-StringPart -Part 1 + return $geckodriverVersion +} + +function Get-ChromiumVersion { + $chromiumVersion = chromium-browser --version | Get-StringPart -Part 1 + return $chromiumVersion +} + +function Get-EdgeVersion { + $edgeVersion = (microsoft-edge --version).Trim() | Get-StringPart -Part 2 + return $edgeVersion +} + +function Get-EdgeDriverVersion { + $edgeDriverVersion = msedgedriver --version | Get-StringPart -Part 3 + return $edgeDriverVersion +} + +function Get-SeleniumVersion { + $fullSeleniumVersion = (Get-ChildItem "/usr/share/java/selenium-server-*").Name -replace "selenium-server-" + return $fullSeleniumVersion +} + +function Build-BrowserWebdriversEnvironmentTable { + return @( + [PSCustomObject] @{ + "Name" = "CHROMEWEBDRIVER" + "Value" = $env:CHROMEWEBDRIVER + }, + [PSCustomObject] @{ + "Name" = "EDGEWEBDRIVER" + "Value" = $env:EDGEWEBDRIVER + }, + [PSCustomObject] @{ + "Name" = "GECKOWEBDRIVER" + "Value" = $env:GECKOWEBDRIVER + }, + [PSCustomObject] @{ + "Name" = "SELENIUM_JAR_PATH" + "Value" = $env:SELENIUM_JAR_PATH + } + ) +} diff --git a/images/ubuntu/scripts/docs-gen/SoftwareReport.CachedTools.psm1 b/images/ubuntu/scripts/docs-gen/SoftwareReport.CachedTools.psm1 new file mode 100644 index 0000000..87905ae --- /dev/null +++ b/images/ubuntu/scripts/docs-gen/SoftwareReport.CachedTools.psm1 @@ -0,0 +1,29 @@ +function Get-ToolcacheRubyVersions { + $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Ruby" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } +} + +function Get-ToolcachePythonVersions { + $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Python" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } +} + +function Get-ToolcachePyPyVersions { + $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "PyPy" + Get-ChildItem -Path $toolcachePath -Name | Sort-Object { [Version] $_ } | ForEach-Object { + $pypyRootPath = Join-Path $toolcachePath $_ "x64" + [string] $pypyVersionOutput = & "$pypyRootPath/bin/python" -c "import sys;print(sys.version)" + $pypyVersionOutput -match "^([\d\.]+) \(.+\) \[PyPy ([\d\.]+\S*) .+]$" | Out-Null + return "{0} [PyPy {1}]" -f $Matches[1], $Matches[2] + } +} + +function Get-ToolcacheNodeVersions { + $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "node" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } +} + +function Get-ToolcacheGoVersions { + $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "go" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } +} diff --git a/images/ubuntu/scripts/docs-gen/SoftwareReport.Common.psm1 b/images/ubuntu/scripts/docs-gen/SoftwareReport.Common.psm1 new file mode 100644 index 0000000..73e3e1f --- /dev/null +++ b/images/ubuntu/scripts/docs-gen/SoftwareReport.Common.psm1 @@ -0,0 +1,358 @@ +function Get-BashVersion { + $version = bash -c 'echo ${BASH_VERSION}' + return $version +} + +function Get-DashVersion { + $version = dpkg-query -W -f '${Version}' dash + return $version +} + +function Get-CPPVersions { + $result = Get-CommandResult "apt list --installed" -Multiline + $cppVersions = $result.Output | Where-Object { $_ -match "g\+\+-\d\d\/" } | ForEach-Object { + & $_.Split("/")[0] --version | Select-Object -First 1 | Get-StringPart -Part 3 + } | Sort-Object {[Version] $_} + return $cppVersions +} + +function Get-FortranVersions { + $result = Get-CommandResult "apt list --installed" -Multiline + $fortranVersions = $result.Output | Where-Object { $_ -match "^gfortran-\d\d\/" } | ForEach-Object { + & $_.Split("/")[0] --version | Select-Object -First 1 | Get-StringPart -Part 4 + } | Sort-Object {[Version] $_} + return $fortranVersions +} + +function Get-ClangToolVersions { + param ( + [Parameter(Mandatory = $true)] + [string] $ToolName, + [string] $VersionLineMatcher = "${ToolName} version", + [string] $VersionPattern = "\d+\.\d+\.\d+)" + ) + + $result = Get-CommandResult "apt list --installed" -Multiline + $toolVersions = $result.Output | Where-Object { $_ -match "^${ToolName}-\d+" } | ForEach-Object { + $clangCommand = ($_ -Split "/")[0] + Invoke-Expression "$clangCommand --version" | Where-Object { $_ -match "${VersionLineMatcher}" } | ForEach-Object { + $_ -match "${VersionLineMatcher} (?${VersionPattern}" | Out-Null + $Matches.version + } + } | Sort-Object {[Version] $_} + + return $toolVersions +} + + +function Get-ClangTidyVersions { + $clangVersions = Get-ClangToolVersions -ToolName "clang-tidy" -VersionLineMatcher "LLVM version" -VersionPattern "\d+\.\d+\.\d+)" + return $clangVersions +} + + +function Get-ErlangVersion { + $erlangVersion = (erl -eval '{ok, Version} = file:read_file(filename:join([code:root_dir(), "releases", erlang:system_info(otp_release), ''OTP_VERSION''])), io:fwrite(Version), halt().' -noshell) + $shellVersion = (erl -eval 'erlang:display(erlang:system_info(version)), halt().' -noshell).Trim('"') + return "$erlangVersion (Eshell $shellVersion)" +} + +function Get-ErlangRebar3Version { + $result = Get-CommandResult "rebar3 --version" + $result.Output -match "rebar (?(\d+.){2}\d+)" | Out-Null + return $Matches.version +} + +function Get-MonoVersion { + $monoVersion = mono --version | Out-String | Get-StringPart -Part 4 + return $monoVersion +} + +function Get-MsbuildVersion { + $msbuildVersion = msbuild -version | Select-Object -Last 1 + $monoVersion = Get-MonoVersion + return "$msbuildVersion (Mono $monoVersion)" +} + +function Get-NuGetVersion { + $nugetVersion = nuget help | Select-Object -First 1 | Get-StringPart -Part 2 + return $nugetVersion +} + +function Get-NodeVersion { + $nodeVersion = $(node --version).Substring(1) + return $nodeVersion +} + +function Get-OpensslVersion { + $opensslVersion = $(dpkg-query -W -f '${Version}' openssl) + return $opensslVersion +} + +function Get-PerlVersion { + $version = $(perl -e 'print substr($^V,1)') + return $version +} + +function Get-PythonVersion { + $result = Get-CommandResult "python --version" + $version = $result.Output | Get-StringPart -Part 1 + return $version +} + +function Get-PowershellVersion { + $pwshVersion = $(pwsh --version) | Get-StringPart -Part 1 + return $pwshVersion +} + +function Get-RubyVersion { + $rubyVersion = ruby --version | Out-String | Get-StringPart -Part 1 + return $rubyVersion +} + +function Get-SwiftVersion { + $swiftVersion = swift --version | Out-String | Get-StringPart -Part 2 + return $swiftVersion +} + +function Get-KotlinVersion { + $kotlinVersion = kotlin -version | Out-String | Get-StringPart -Part 2 + return $kotlinVersion +} + +function Get-JuliaVersion { + $juliaVersion = julia --version | Get-StringPart -Part 2 + return $juliaVersion +} + +function Get-LernaVersion { + $version = lerna -v + return $version +} + +function Get-HomebrewVersion { + $result = Get-CommandResult "/home/linuxbrew/.linuxbrew/bin/brew --version" + $result.Output -match "Homebrew (?\d+\.\d+\.\d+)" | Out-Null + return $Matches.version +} + +function Get-CpanVersion { + $result = Get-CommandResult "cpan --version" -ExpectedExitCode @(25, 255) + $result.Output -match "version (?\d+\.\d+) " | Out-Null + return $Matches.version +} + +function Get-GemVersion { + $result = Get-CommandResult "gem --version" + $result.Output -match "(?\d+\.\d+\.\d+)" | Out-Null + return $Matches.version +} + +function Get-MinicondaVersion { + $condaVersion = conda --version | Get-StringPart -Part 1 + return $condaVersion +} + +function Get-HelmVersion { + $(helm version) -match 'Version:"v(?\d+\.\d+\.\d+)"' | Out-Null + return $Matches.version +} + +function Get-NpmVersion { + $npmVersion = npm --version + return $npmVersion +} + +function Get-YarnVersion { + $yarnVersion = yarn --version + return $yarnVersion +} + +function Get-ParcelVersion { + $parcelVersion = parcel --version + return $parcelVersion +} + +function Get-PipVersion { + $pipVersion = pip --version | Get-StringPart -Part 1 + return $pipVersion +} + +function Get-Pip3Version { + $pip3Version = pip3 --version | Get-StringPart -Part 1 + return $pip3Version +} + +function Get-VcpkgVersion { + $commitId = git -C "/usr/local/share/vcpkg" rev-parse --short HEAD + return "(build from commit $commitId)" +} + +function Get-AntVersion { + $result = ant -version | Out-String + $result -match "version (?\d+\.\d+\.\d+)" | Out-Null + return $Matches.version +} + +function Get-GradleVersion { + $gradleVersion = (gradle -v) -match "^Gradle \d" | Get-StringPart -Part 1 + return $gradleVersion +} + +function Get-MavenVersion { + $result = mvn -version | Out-String + $result -match "Apache Maven (?\d+\.\d+\.\d+)" | Out-Null + return $Matches.version +} + +function Get-SbtVersion { + $result = Get-CommandResult "sbt -version" + $result.Output -match "sbt script version: (?\d+\.\d+\.\d+)" | Out-Null + return $Matches.version +} + +function Get-PHPVersions { + $result = Get-CommandResult "apt list --installed" -Multiline + return $result.Output | Where-Object { $_ -match "^php\d+\.\d+/" } | ForEach-Object { + $_ -match "now (\d+:)?(?\d+\.\d+\.\d+)" | Out-Null + $Matches.version + } +} + +function Get-ComposerVersion { + $composerVersion = (composer --version) -replace " version" | Get-StringPart -Part 1 + return $composerVersion +} + +function Get-PHPUnitVersion { + $(phpunit --version | Out-String) -match "PHPUnit (?\d+\.\d+\.\d+)\s" | Out-Null + return $Matches.version +} + +function Get-GHCVersion { + $(ghc --version) -match "version (?\d+\.\d+\.\d+)" | Out-Null + return $Matches.version +} + +function Get-GHCupVersion { + $(ghcup --version) -match "version (?\d+(\.\d+){2,})" | Out-Null + return $Matches.version +} + +function Get-CabalVersion { + $(cabal --version | Out-String) -match "cabal-install version (?\d+\.\d+\.\d+\.\d+)" | Out-Null + return $Matches.version +} + +function Get-StackVersion { + $(stack --version | Out-String) -match "Version (?\d+\.\d+\.\d+)" | Out-Null + return $Matches.version +} + +function Get-AzModuleVersions { + $azModuleVersions = Get-ChildItem /usr/share | Where-Object { $_ -match "az_\d+" } | Foreach-Object { + $_.Name.Split("_")[1] + } + + $azModuleVersions = $azModuleVersions -join " " + return $azModuleVersions +} + +function Get-PowerShellModules { + [Array] $result = @() + + [Array] $azureInstalledModules = Get-ChildItem -Path "/usr/share/az_*" -Directory | ForEach-Object { $_.Name.Split("_")[1] } + if ($azureInstalledModules.Count -gt 0) { + $result += [ToolVersionsListNode]::new("Az", $azureInstalledModules, "^\d+\.\d+", "Inline") + } + + [Array] $azureCachedModules = Get-ChildItem /usr/share/az_*.zip -File | ForEach-Object { $_.Name.Split("_")[1] } + if ($azureCachedModules.Count -gt 0) { + $result += [ToolVersionsListNode]::new("Az (Cached)", $azureCachedModules, "^\d+\.\d+", "Inline") + } + + (Get-ToolsetContent).powershellModules.name | ForEach-Object { + $moduleName = $_ + $moduleVersions = Get-Module -Name $moduleName -ListAvailable | Select-Object -ExpandProperty Version | Sort-Object -Unique + $result += [ToolVersionsListNode]::new($moduleName, $moduleVersions, "^\d+", "Inline") + } + + return $result +} + +function Get-DotNetCoreSdkVersions { + $dotNetCoreSdkVersion = dotnet --list-sdks list | ForEach-Object { $_ | Get-StringPart -Part 0 } + return $dotNetCoreSdkVersion +} + +function Get-DotnetTools { + $env:PATH = "/etc/skel/.dotnet/tools:$($env:PATH)" + $dotnetTools = (Get-ToolsetContent).dotnet.tools + + return $dotnetTools | ForEach-Object { + $version = Invoke-Expression $_.getversion + return [ToolVersionNode]::new($_.name, $version) + } +} + +function Get-CachedDockerImages { + $toolsetJson = Get-ToolsetContent + $images = $toolsetJson.docker.images + return $images +} + +function Get-CachedDockerImagesTableData { + $allImages = sudo docker images --digests --format "*{{.Repository}}:{{.Tag}}|{{.Digest}} |{{.CreatedAt}}" + $allImages.Split("*") | Where-Object { $_ } | ForEach-Object { + $parts = $_.Split("|") + [PSCustomObject] @{ + "Repository:Tag" = $parts[0] + "Digest" = $parts[1] + "Created" = $parts[2].split(' ')[0] + } + } | Sort-Object -Property "Repository:Tag" +} + +function Get-AptPackages { + $apt = (Get-ToolsetContent).Apt + $output = @() + ForEach ($pkg in ($apt.vital_packages + $apt.common_packages + $apt.cmd_packages)) { + $version = $(dpkg-query -W -f '${Version}' $pkg) + if ($null -eq $version) { + $version = $(dpkg-query -W -f '${Version}' "$pkg*") + } + + $version = $version -replace '~','\~' + + $output += [PSCustomObject] @{ + Name = $pkg + Version = $version + } + } + return ($output | Sort-Object Name) +} + +function Get-PipxVersion { + $result = (Get-CommandResult "pipx --version").Output + $result -match "(?\d+\.\d+\.\d+\.?\d*)" | Out-Null + return $Matches.Version +} + +function Build-PackageManagementEnvironmentTable { + return @( + [PSCustomObject] @{ + "Name" = "CONDA" + "Value" = $env:CONDA + }, + [PSCustomObject] @{ + "Name" = "VCPKG_INSTALLATION_ROOT" + "Value" = $env:VCPKG_INSTALLATION_ROOT + } + ) +} + +function Get-SystemdVersion { + $matchCollection = [regex]::Matches((systemctl --version | head -n 1), "\((.*?)\)") + $result = foreach ($match in $matchCollection) {$match.Groups[1].Value} + return $result +} diff --git a/images/ubuntu/scripts/docs-gen/SoftwareReport.Databases.psm1 b/images/ubuntu/scripts/docs-gen/SoftwareReport.Databases.psm1 new file mode 100644 index 0000000..9adfa2f --- /dev/null +++ b/images/ubuntu/scripts/docs-gen/SoftwareReport.Databases.psm1 @@ -0,0 +1,61 @@ +function Get-PostgreSqlVersion { + $postgreSQLVersion = psql --version | Get-StringPart -Part 2 + return $postgreSQLVersion +} + +function Get-MongoDbVersion { + $mongoDBVersion = mongod --version | Select-Object -First 1 | Get-StringPart -Part 2 -Delimiter "v" + return $mongoDBVersion +} + +function Get-SqliteVersion { + $sqliteVersion = sqlite3 --version | Get-StringPart -Part 0 + return $sqliteVersion +} + +function Get-MySQLVersion { + $mySQLVersion = mysqld --version | Get-StringPart -Part 2 + return $mySQLVersion +} + +function Get-SQLCmdVersion { + $sqlcmdVersion = sqlcmd -? | Select-String -Pattern "Version" | Get-StringPart -Part 1 + return $sqlcmdVersion +} + +function Get-SqlPackageVersion { + $sqlPackageVersion = sqlpackage /version + return $sqlPackageVersion +} + +function Build-PostgreSqlSection { + $node = [HeaderNode]::new("PostgreSQL") + $node.AddToolVersion("PostgreSQL", $(Get-PostgreSqlVersion)) + $node.AddNote(@( + "User: postgres", + "PostgreSQL service is disabled by default.", + "Use the following command as a part of your job to start the service: 'sudo systemctl start postgresql.service'" + ) -join "`n") + + return $node +} + +function Build-MySQLSection { + $node = [HeaderNode]::new("MySQL") + $node.AddToolVersion("MySQL", $(Get-MySQLVersion)) + $node.AddNote(@( + "User: root", + "Password: root", + "MySQL service is disabled by default.", + "Use the following command as a part of your job to start the service: 'sudo systemctl start mysql.service'" + ) -join "`n") + + return $node +} + +function Build-MSSQLToolsSection { + $node = [HeaderNode]::new("MS SQL") + $node.AddToolVersion("sqlcmd", $(Get-SQLCmdVersion)) + $node.AddToolVersion("SqlPackage", $(Get-SqlPackageVersion)) + return $node +} diff --git a/images/ubuntu/scripts/docs-gen/SoftwareReport.Helpers.psm1 b/images/ubuntu/scripts/docs-gen/SoftwareReport.Helpers.psm1 new file mode 100644 index 0000000..c74b856 --- /dev/null +++ b/images/ubuntu/scripts/docs-gen/SoftwareReport.Helpers.psm1 @@ -0,0 +1,37 @@ +function Get-StringPart { + param ( + [Parameter(ValueFromPipeline)] + [string] $ToolOutput, + [string] $Delimiter = " ", + [int[]] $Part + ) + + $parts = $ToolOutput.Split($Delimiter, [System.StringSplitOptions]::RemoveEmptyEntries) + $selectedParts = $parts[$Part] + return [string]::Join($Delimiter, $selectedParts) +} + +function Get-PathWithLink { + param ( + [string] $InputPath + ) + + $link = Get-Item $InputPath | Select-Object -ExpandProperty Target + if (-not [string]::IsNullOrEmpty($link)) { + return "${InputPath} -> ${link}" + } + return "${InputPath}" +} + +function Get-OSVersionShort { + $(Get-OSVersionFull) | Get-StringPart -Delimiter '.' -Part 0,1 +} + +function Get-OSVersionFull { + lsb_release -ds | Get-StringPart -Part 1, 2 +} + +function Get-KernelVersion { + $kernelVersion = uname -r + return $kernelVersion +} diff --git a/images/ubuntu/scripts/docs-gen/SoftwareReport.Java.psm1 b/images/ubuntu/scripts/docs-gen/SoftwareReport.Java.psm1 new file mode 100644 index 0000000..fb653ab --- /dev/null +++ b/images/ubuntu/scripts/docs-gen/SoftwareReport.Java.psm1 @@ -0,0 +1,17 @@ +function Get-JavaVersionsTable { + $javaToolcacheVersions = Get-ChildItem $env:AGENT_TOOLSDIRECTORY/Java*/* -Directory | Sort-Object { [int] $_.Name.Split(".")[0] } + + return $javaToolcacheVersions | ForEach-Object { + $majorVersion = $_.Name.split(".")[0] + $fullVersion = $_.Name.Replace("-", "+") + $defaultJavaPath = $env:JAVA_HOME + $javaPath = Get-Item env:JAVA_HOME_${majorVersion}_X64 + + $defaultPostfix = ($javaPath.Value -eq $defaultJavaPath) ? " (default)" : "" + + [PSCustomObject] @{ + "Version" = $fullVersion + $defaultPostfix + "Environment Variable" = $javaPath.Name + } + } +} diff --git a/images/ubuntu/scripts/docs-gen/SoftwareReport.Rust.psm1 b/images/ubuntu/scripts/docs-gen/SoftwareReport.Rust.psm1 new file mode 100644 index 0000000..d4c0ca1 --- /dev/null +++ b/images/ubuntu/scripts/docs-gen/SoftwareReport.Rust.psm1 @@ -0,0 +1,55 @@ +function Initialize-RustEnvironment { + $env:PATH = "/etc/skel/.cargo/bin:/etc/skel/.rustup/bin:$($env:PATH)" + $env:RUSTUP_HOME = "/etc/skel/.rustup" + $env:CARGO_HOME = "/etc/skel/.cargo" +} + +function Get-RustVersion { + $rustVersion = $(rustc --version) | Get-StringPart -Part 1 + return $rustVersion +} + +function Get-BindgenVersion { + $bindgenVersion = $(bindgen --version) | Get-StringPart -Part 1 + return $bindgenVersion +} + +function Get-CargoVersion { + $cargoVersion = $(cargo --version) | Get-StringPart -Part 1 + return $cargoVersion +} + +function Get-CargoAuditVersion { + $cargoAuditVersion = $(cargo-audit --version) | Get-StringPart -Part 1 + return $cargoAuditVersion +} + +function Get-CargoOutdatedVersion { + $cargoOutdatedVersion = cargo outdated --version | Get-StringPart -Part 1 + return $cargoOutdatedVersion +} + +function Get-CargoClippyVersion { + $cargoClippyVersion = $(cargo-clippy --version) | Get-StringPart -Part 1 + return $cargoClippyVersion +} + +function Get-CbindgenVersion { + $cbindgenVersion = $(cbindgen --version) | Get-StringPart -Part 1 + return $cbindgenVersion +} + +function Get-RustupVersion { + $rustupVersion = $(rustup --version) | Get-StringPart -Part 1 + return $rustupVersion +} + +function Get-RustdocVersion { + $rustdocVersion = $(rustdoc --version) | Get-StringPart -Part 1 + return $rustdocVersion +} + +function Get-RustfmtVersion { + $rustfmtVersion = $(rustfmt --version) | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter "-" + return $rustfmtVersion +} diff --git a/images/ubuntu/scripts/docs-gen/SoftwareReport.Tools.psm1 b/images/ubuntu/scripts/docs-gen/SoftwareReport.Tools.psm1 new file mode 100644 index 0000000..b4656b7 --- /dev/null +++ b/images/ubuntu/scripts/docs-gen/SoftwareReport.Tools.psm1 @@ -0,0 +1,281 @@ +function Get-AnsibleVersion { + $ansibleVersion = (ansible --version)[0] -replace "[^\d.]" + return $ansibleVersion +} + +function Get-AptFastVersion { + $versionFileContent = Get-Content (which apt-fast) -Raw + $match = [Regex]::Match($versionFileContent, '# apt-fast v(.+)\n') + return $match.Groups[1].Value +} + +function Get-AzCopyVersion { + $azcopyVersion = [string]$(azcopy --version) | Get-StringPart -Part 2 + return "$azcopyVersion - available by ``azcopy`` and ``azcopy10`` aliases" +} + +function Get-BazelVersion { + $bazelVersion = bazel --version | Select-String "bazel" | Get-StringPart -Part 1 + return $bazelVersion +} + +function Get-BazeliskVersion { + $result = Get-CommandResult "bazelisk version" -Multiline + $bazeliskVersion = $result.Output | Select-String "Bazelisk version:" | Get-StringPart -Part 2 | Get-StringPart -Part 0 -Delimiter "v" + return $bazeliskVersion +} + +function Get-BicepVersion { + (bicep --version | Out-String) -match "bicep cli version (?\d+\.\d+\.\d+)" | Out-Null + return $Matches.Version +} + +function Get-CodeQLBundleVersion { + $CodeQLVersionsWildcard = Join-Path $Env:AGENT_TOOLSDIRECTORY -ChildPath "CodeQL" | Join-Path -ChildPath "*" + $CodeQLVersionPath = Get-ChildItem $CodeQLVersionsWildcard | Select-Object -First 1 -Expand FullName + $CodeQLPath = Join-Path $CodeQLVersionPath -ChildPath "x64" | Join-Path -ChildPath "codeql" | Join-Path -ChildPath "codeql" + $CodeQLVersion = & $CodeQLPath version --quiet + return $CodeQLVersion +} + +function Get-PodManVersion { + $podmanVersion = podman --version | Get-StringPart -Part 2 + return $podmanVersion +} + +function Get-BuildahVersion { + $buildahVersion = buildah --version | Get-StringPart -Part 2 + return $buildahVersion +} + +function Get-SkopeoVersion { + $skopeoVersion = skopeo --version | Get-StringPart -Part 2 + return $skopeoVersion +} + +function Get-CMakeVersion { + $cmakeVersion = cmake --version | Select-Object -First 1 | Get-StringPart -Part 2 + return $cmakeVersion +} + + + +function Get-DockerComposeV2Version { + $composeVersion = docker compose version | Get-StringPart -Part 3 | Get-StringPart -Part 0 -Delimiter "v" + return $composeVersion +} + +function Get-DockerClientVersion { + $dockerClientVersion = sudo docker version --format '{{.Client.Version}}' + return $dockerClientVersion +} + +function Get-DockerServerVersion { + $dockerServerVersion = sudo docker version --format '{{.Server.Version}}' + return $dockerServerVersion +} + +function Get-DockerBuildxVersion { + $buildxVersion = docker buildx version | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter "v" + return $buildxVersion +} + +function Get-DockerAmazonECRCredHelperVersion { + $ecrVersion = docker-credential-ecr-login -v | Select-String "Version:" | Get-StringPart -Part 1 + return $ecrVersion +} + +function Get-GitVersion { + $gitVersion = git --version | Get-StringPart -Part -1 + return $gitVersion +} + +function Get-GitLFSVersion { + $result = Get-CommandResult "git-lfs --version" + $gitlfsversion = $result.Output | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter "/" + return $gitlfsversion +} + +function Get-GitFTPVersion { + $gitftpVersion = git-ftp --version | Get-StringPart -Part 2 + return $gitftpVersion +} + +function Get-GoogleCloudCLIVersion { + return (gcloud --version | Select-Object -First 1) | Get-StringPart -Part 3 +} + +function Get-HavegedVersion { + $havegedVersion = dpkg-query --showformat='${Version}' --show haveged | Get-StringPart -Part 0 -Delimiter "-" + return $havegedVersion +} + +function Get-HerokuVersion { + $herokuVersion = heroku version | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter "/" + return $herokuVersion +} + +function Get-HHVMVersion { + $hhvmVersion = hhvm --version | Select-Object -First 1 | Get-StringPart -Part 2 + return $hhvmVersion +} + +function Get-SVNVersion { + $svnVersion = svn --version | Select-Object -First 1 | Get-StringPart -Part 2 + return $svnVersion +} + +function Get-KustomizeVersion { + $kustomizeVersion = kustomize version --short | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter "v" + return $kustomizeVersion +} + +function Get-KindVersion { + $kindVersion = kind version | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter "v" + return $kindVersion +} + +function Get-KubectlVersion { + $kubectlVersion = (kubectl version --client --output=json | ConvertFrom-Json).clientVersion.gitVersion.Replace('v','') + return $kubectlVersion +} + +function Get-MinikubeVersion { + $minikubeVersion = minikube version --short | Get-StringPart -Part 0 -Delimiter "v" + return $minikubeVersion +} + +function Get-HGVersion { + $hgVersion = hg --version | Select-Object -First 1 | Get-StringPart -Part -1 | Get-StringPart -Part 0 -Delimiter ")" + return $hgVersion +} + +function Get-LeiningenVersion { + return "$(lein -v | Get-StringPart -Part 1)" +} + +function Get-MediainfoVersion { + $mediainfoVersion = (mediainfo --version | Select-Object -Index 1 | Get-StringPart -Part 2).Replace('v', '') + return $mediainfoVersion +} + +function Get-NewmanVersion { + return $(newman --version) +} + +function Get-NVersion { + $nVersion = (n --version).Replace('v', '') + return $nVersion +} + +function Get-NvmVersion { + $nvmVersion = bash -c "source /etc/skel/.nvm/nvm.sh && nvm --version" + return $nvmVersion +} + +function Get-PackerVersion { + $packerVersion = (packer --version | Select-String "^Packer").Line.Replace('v','') | Get-StringPart -Part 1 + return $packerVersion +} + +function Get-PhantomJSVersion { + $env:OPENSSL_CONF="/etc/ssl"; phantomjs --version + return $(phantomjs --version) +} + +function Get-TerraformVersion { + return (terraform version | Select-String "^Terraform").Line.Replace('v','') | Get-StringPart -Part 1 +} + +function Get-JqVersion { + $jqVersion = jq --version | Get-StringPart -Part 1 -Delimiter "-" + return $jqVersion +} + +function Get-AzureCliVersion { + $azcliVersion = (az version | ConvertFrom-Json).'azure-cli' + return $azcliVersion +} + +function Get-AzureDevopsVersion { + $azdevopsVersion = (az version | ConvertFrom-Json).extensions.'azure-devops' + return $azdevopsVersion +} + +function Get-AlibabaCloudCliVersion { + return $(aliyun version) +} + +function Get-AWSCliVersion { + $result = Get-CommandResult "aws --version" + $awsVersion = $result.Output | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter "/" + return $awsVersion +} + +function Get-AWSCliSessionManagerPluginVersion { + $result = (Get-CommandResult "session-manager-plugin --version").Output + return $result +} + +function Get-AWSSAMVersion { + return $(sam --version | Get-StringPart -Part -1) +} + +function Get-FastlaneVersion { + $fastlaneVersion = fastlane --version | Select-String "^fastlane [0-9]" | Get-StringPart -Part 1 + return $fastlaneVersion +} + +function Get-GitHubCliVersion { + $ghVersion = gh --version | Select-String "gh version" | Get-StringPart -Part 2 + return $ghVersion +} + +function Get-NetlifyCliVersion { + $netlifyVersion = netlify --version | Get-StringPart -Part 0 | Get-StringPart -Part 1 -Delimiter "/" + return $netlifyVersion +} + +function Get-OCCliVersion { + $ocVersion = oc version -o=json | jq -r '.releaseClientVersion' + return $ocVersion +} + +function Get-ORASCliVersion { + $orasVersion = oras version | Select-String "^Version:" | Get-StringPart -Part 1 + return $orasVersion +} + +function Get-VerselCliversion { + $result = Get-CommandResult "vercel --version" -Multiline + return $result.Output | Select-Object -Skip 1 -First 1 +} + +function Get-PulumiVersion { + $pulumiVersion = pulumi version | Get-StringPart -Part 0 -Delimiter "v" + return $pulumiVersion +} + +function Get-RVersion { + $rVersion = (Get-CommandResult "R --version | grep 'R version'").Output | Get-StringPart -Part 2 + return $rVersion +} + +function Get-SphinxVersion { + $sphinxVersion = searchd -h | Select-Object -First 1 | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter "-" + return $sphinxVersion +} + +function Get-YamllintVersion { + return $(yamllint --version) | Get-StringPart -Part 1 +} + +function Get-ZstdVersion { + $zstdVersion = zstd --version | Get-StringPart -Part 1 -Delimiter "v" | Get-StringPart -Part 0 -Delimiter "," + return "$zstdVersion" +} + +function Get-YqVersion { + $yqVersion = $(yq -V) | Get-StringPart -Part 3 + return $yqVersion.TrimStart("v").Trim() +} diff --git a/images/ubuntu/scripts/docs-gen/SoftwareReport.WebServers.psm1 b/images/ubuntu/scripts/docs-gen/SoftwareReport.WebServers.psm1 new file mode 100644 index 0000000..bfb76e0 --- /dev/null +++ b/images/ubuntu/scripts/docs-gen/SoftwareReport.WebServers.psm1 @@ -0,0 +1,55 @@ +function Get-ApacheVersion { + $name = "apache2" + $port = 80 + $version = bash -c "apache2 -v | grep -Po 'Apache/(\d+.){2}\d+'" | Get-StringPart -Part 1 -Delimiter "/" + $serviceStatus = systemctl status apache2 | grep "Active:" | Get-StringPart -Part 1 + $configFile = "/etc/apache2/apache2.conf" + return [PsCustomObject]@{ + "Name" = $name + "Version" = $version + "ConfigFile" = $configFile + "ServiceStatus" = $serviceStatus + "ListenPort" = $port + } +} + +function Get-NginxVersion { + $name = "nginx" + $port = 80 + $version = (dpkg-query --showformat='${Version}' --show nginx).Split('-')[0] + $serviceStatus = systemctl status nginx | grep "Active:" | Get-StringPart -Part 1 + $configFile = "/etc/nginx/nginx.conf" + return [PsCustomObject]@{ + "Name" = $name + "Version" = $version + "ConfigFile" = $configFile + "ServiceStatus" = $serviceStatus + "ListenPort" = $port + } +} + +function Get-Xsp4Version { + $name = "mono-xsp4" + $port = (grep '^port=' /etc/default/mono-xsp4).Split('=')[1] + $version = (dpkg-query --showformat='${Version}' --show mono-xsp4).Split('-')[0] + $serviceStatus = systemctl show -p ActiveState --value mono-xsp4 + $configFile = "/etc/default/mono-xsp4" + return [PsCustomObject]@{ + "Name" = $name + "Version" = $version + "ConfigFile" = $configFile + "ServiceStatus" = $serviceStatus + "ListenPort" = $port + } +} + +function Build-WebServersTable { + $servers = @() + $servers += (Get-ApacheVersion) + if (Test-IsUbuntu20) { + $servers += (Get-Xsp4Version) + } + $servers += (Get-NginxVersion) + + return $servers +} diff --git a/images/ubuntu/scripts/helpers/Common.Helpers.psm1 b/images/ubuntu/scripts/helpers/Common.Helpers.psm1 new file mode 100644 index 0000000..75dcdb7 --- /dev/null +++ b/images/ubuntu/scripts/helpers/Common.Helpers.psm1 @@ -0,0 +1,156 @@ +function Get-CommandResult { + <# + .SYNOPSIS + Runs a command in bash and returns the output and exit code. + + .DESCRIPTION + Function runs a provided command in bash and returns the output and exit code as hashtable. + + .PARAMETER Command + The command to run. + + .PARAMETER ExpectedExitCode + The expected exit code. If the actual exit code does not match, an exception is thrown. + + .PARAMETER Multiline + If true, the output is returned as an array of strings. Otherwise, the output is returned as a single string. + + .PARAMETER ValidateExitCode + If true, the actual exit code is compared to the expected exit code. + + .EXAMPLE + $result = Get-CommandResult "ls -la" + + This command runs "ls -la" in bash and returns the output and exit code as hashtable. + + #> + param( + [Parameter(Mandatory=$true)] + [string] $Command, + [int[]] $ExpectedExitCode = 0, + [switch] $Multiline, + [bool] $ValidateExitCode = $true + ) + + # Bash trick to suppress and show error output because some commands write to stderr (for example, "python --version") + $stdout = & bash -c "$Command 2>&1" + $exitCode = $LASTEXITCODE + + if ($ValidateExitCode) { + if ($ExpectedExitCode -notcontains $exitCode) { + try { + throw "StdOut: '$stdout' ExitCode: '$exitCode'" + } catch { + Write-Host $_.Exception.Message + Write-Host $_.ScriptStackTrace + exit $LASTEXITCODE + } + } + } + + return @{ + Output = If ($Multiline -eq $true) { $stdout } else { [string] $stdout } + ExitCode = $exitCode + } +} + +function Test-IsUbuntu20 { + return (lsb_release -rs) -eq "20.04" +} + +function Test-IsUbuntu22 { + return (lsb_release -rs) -eq "22.04" +} + +function Test-IsUbuntu24 { + return (lsb_release -rs) -eq "24.04" +} + +function Get-ToolsetContent { + <# + .SYNOPSIS + Retrieves the content of the toolset.json file. + + .DESCRIPTION + This function reads the toolset.json in path provided by INSTALLER_SCRIPT_FOLDER + environment variable and returns the content as a PowerShell object. + #> + + $toolsetPath = Join-Path $env:INSTALLER_SCRIPT_FOLDER "toolset.json" + $toolsetJson = Get-Content -Path $toolsetPath -Raw + ConvertFrom-Json -InputObject $toolsetJson +} + +function Invoke-DownloadWithRetry { + <# + .SYNOPSIS + Downloads a file from a given URL with retry functionality. + + .DESCRIPTION + The Invoke-DownloadWithRetry function downloads a file from the specified URL + to the specified path. It includes retry functionality in case the download fails. + + .PARAMETER Url + The URL of the file to download. + + .PARAMETER Path + The path where the downloaded file will be saved. If not provided, a temporary path + will be used. + + .EXAMPLE + Invoke-DownloadWithRetry -Url "https://example.com/file.zip" -Path "/usr/local/bin" + Downloads the file from the specified URL and saves it to the specified path. + + .EXAMPLE + Invoke-DownloadWithRetry -Url "https://example.com/file.zip" + Downloads the file from the specified URL and saves it to a temporary path. + + .OUTPUTS + The path where the downloaded file is saved. + #> + param( + [Parameter(Mandatory)] + [string] $Url, + [Alias("Destination")] + [string] $DestinationPath + ) + + if (-not $DestinationPath) { + $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join '' + $re = "[{0}]" -f [RegEx]::Escape($invalidChars) + $fileName = [IO.Path]::GetFileName($Url) -replace $re + + if ([String]::IsNullOrEmpty($fileName)) { + $fileName = [System.IO.Path]::GetRandomFileName() + } + $DestinationPath = Join-Path -Path "/tmp" -ChildPath $fileName + } + + Write-Host "Downloading package from $Url to $DestinationPath..." + + $interval = 30 + $downloadStartTime = Get-Date + for ($retries = 20; $retries -gt 0; $retries--) { + try { + $attemptStartTime = Get-Date + (New-Object System.Net.WebClient).DownloadFile($Url, $DestinationPath) + $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2) + Write-Host "Package downloaded in $attemptSeconds seconds" + break + } catch { + $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2) + Write-Warning "Package download failed in $attemptSeconds seconds" + Write-Warning $_.Exception.Message + } + + if ($retries -eq 0) { + $totalSeconds = [math]::Round(($(Get-Date) - $downloadStartTime).TotalSeconds, 2) + throw "Package download failed after $totalSeconds seconds" + } + + Write-Warning "Waiting $interval seconds before retrying (retries left: $retries)..." + Start-Sleep -Seconds $interval + } + + return $DestinationPath +} diff --git a/images/ubuntu/scripts/helpers/etc-environment.sh b/images/ubuntu/scripts/helpers/etc-environment.sh new file mode 100644 index 0000000..6ffafd0 --- /dev/null +++ b/images/ubuntu/scripts/helpers/etc-environment.sh @@ -0,0 +1,89 @@ +#!/bin/bash -e +################################################################################ +## File: etc-environment.sh +## Desc: Helper functions for source and modify /etc/environment +################################################################################ + +# NB: sed expression use '%' as a delimiter in order to simplify handling +# values containg slashes (i.e. directory path) +# The values containing '%' will break the functions + +get_etc_environment_variable() { + local variable_name=$1 + + # remove `variable_name=` and possible quotes from the line + grep "^${variable_name}=" /etc/environment | sed -E "s%^${variable_name}=\"?([^\"]+)\"?.*$%\1%" +} + +add_etc_environment_variable() { + local variable_name=$1 + local variable_value=$2 + + echo "${variable_name}=${variable_value}" | sudo tee -a /etc/environment +} + +replace_etc_environment_variable() { + local variable_name=$1 + local variable_value=$2 + + # modify /etc/environemnt in place by replacing a string that begins with variable_name + sudo sed -i -e "s%^${variable_name}=.*$%${variable_name}=${variable_value}%" /etc/environment +} + +set_etc_environment_variable() { + local variable_name=$1 + local variable_value=$2 + + if grep "^${variable_name}=" /etc/environment > /dev/null; then + replace_etc_environment_variable $variable_name $variable_value + else + add_etc_environment_variable $variable_name $variable_value + fi +} + +prepend_etc_environment_variable() { + local variable_name=$1 + local element=$2 + + # TODO: handle the case if the variable does not exist + existing_value=$(get_etc_environment_variable "${variable_name}") + set_etc_environment_variable "${variable_name}" "${element}:${existing_value}" +} + +append_etc_environment_variable() { + local variable_name=$1 + local element=$2 + + # TODO: handle the case if the variable does not exist + existing_value=$(get_etc_environment_variable "${variable_name}") + set_etc_environment_variable "${variable_name}" "${existing_value}:${element}" +} + +prepend_etc_environment_path() { + local element=$1 + + prepend_etc_environment_variable PATH "${element}" +} + +append_etc_environment_path() { + local element=$1 + + append_etc_environment_variable PATH "${element}" +} + +# Process /etc/environment as if it were shell script with `export VAR=...` expressions +# The PATH variable is handled specially in order to do not override the existing PATH +# variable. The value of PATH variable read from /etc/environment is added to the end +# of value of the exiting PATH variable exactly as it would happen with real PAM app read +# /etc/environment +# +# TODO: there might be the others variables to be processed in the same way as "PATH" variable +# ie MANPATH, INFOPATH, LD_*, etc. In the current implementation the values from /etc/evironments +# replace the values of the current environment +reload_etc_environment() { + # add `export ` to every variable of /etc/environemnt except PATH and eval the result shell script + eval $(grep -v '^PATH=' /etc/environment | sed -e 's%^%export %') + # handle PATH specially + etc_path=$(get_etc_environment_variable PATH) + export PATH="$PATH:$etc_path" +} diff --git a/images/ubuntu/scripts/helpers/install.sh b/images/ubuntu/scripts/helpers/install.sh new file mode 100644 index 0000000..edc6cee --- /dev/null +++ b/images/ubuntu/scripts/helpers/install.sh @@ -0,0 +1,242 @@ +#!/bin/bash -e +################################################################################ +## File: install.sh +## Desc: Helper functions for installing tools +################################################################################ + +download_with_retry() { + local url=$1 + local download_path=$2 + + if [ -z "$download_path" ]; then + download_path="/tmp/$(basename "$url")" + fi + + echo "Downloading package from $url to $download_path..." >&2 + + interval=30 + download_start_time=$(date +%s) + + for ((retries=20; retries>0; retries--)); do + attempt_start_time=$(date +%s) + if http_code=$(curl -4sSLo "$download_path" "$url" -w '%{http_code}'); then + attempt_seconds=$(($(date +%s) - attempt_start_time)) + if [ "$http_code" -eq 200 ]; then + echo "Package downloaded in $attempt_seconds seconds" >&2 + break + else + echo "Received HTTP status code $http_code after $attempt_seconds seconds" >&2 + fi + else + attempt_seconds=$(($(date +%s) - attempt_start_time)) + echo "Package download failed in $attempt_seconds seconds" >&2 + fi + + if [ "$retries" -le 1 ]; then + total_seconds=$(($(date +%s) - download_start_time)) + echo "Package download failed after $total_seconds seconds" >&2 + exit 1 + fi + + echo "Waiting $interval seconds before retrying (retries left: $retries)..." >&2 + sleep $interval + done + + echo "$download_path" +} + +get_toolset_value() { + local toolset_path="${INSTALLER_SCRIPT_FOLDER}/toolset.json" + local query=$1 + + echo "$(jq -r "$query" $toolset_path)" +} + +get_github_releases_by_version() { + local repo=$1 + local version=${2:-".+"} + local allow_pre_release=${3:-false} + local with_assets_only=${4:-false} + + page_size="100" + + json=$(curl -fsSL "https://api.github.com/repos/${repo}/releases?per_page=${page_size}") + + if [[ -z "$json" ]]; then + echo "Failed to get releases" >&2 + exit 1 + fi + + if [[ $with_assets_only == "true" ]]; then + json=$(echo $json | jq -r '.[] | select(.assets | length > 0)') + else + json=$(echo $json | jq -r '.[]') + fi + + if [[ $allow_pre_release == "true" ]]; then + json=$(echo $json | jq -r '.') + else + json=$(echo $json | jq -r '. | select(.prerelease==false)') + fi + + # Filter out rc/beta/etc releases, convert to numeric version and sort + 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))') + + # Select releases matching version + if [[ $version == "latest" ]]; then + json_filtered=$(echo $json | jq .[-1]) + elif [[ $version == *"+"* ]] || [[ $version == *"*"* ]]; then + json_filtered=$(echo $json | jq --arg version $version '.[] | select(.tag_name | test($version))') + else + json_filtered=$(echo $json | jq --arg version $version '.[] | select(.tag_name | contains($version))') + fi + + if [[ -z "$json_filtered" ]]; then + echo "Failed to get releases from ${repo} matching version ${version}" >&2 + echo "Available versions: $(echo "$json" | jq -r '.tag_name')" >&2 + exit 1 + fi + + echo $json_filtered +} + +resolve_github_release_asset_url() { + local repo=$1 + local url_filter=$2 + local version=${3:-".+"} + local allow_pre_release=${4:-false} + local allow_multiple_matches=${5:-false} + + matching_releases=$(get_github_releases_by_version "${repo}" "${version}" "${allow_pre_release}" "true") + matched_url=$(echo $matching_releases | jq -r ".assets[].browser_download_url | select(${url_filter})") + + if [[ -z "$matched_url" ]]; then + echo "Found no download urls matching pattern: ${url_filter}" >&2 + echo "Available download urls: $(echo "$matching_releases" | jq -r '.assets[].browser_download_url')" >&2 + exit 1 + fi + + if [[ "$(echo "$matched_url" | wc -l)" -gt 1 ]]; then + if [[ $allow_multiple_matches == "true" ]]; then + matched_url=$(echo "$matched_url" | tail -n 1) + else + echo "Multiple matches found for ${version} version and ${url_filter} URL filter. Please make filters more specific" >&2 + exit 1 + fi + fi + + echo $matched_url +} + +get_checksum_from_github_release() { + local repo=$1 + local file_name=$2 + local version=${3:-".+"} + local hash_type=$4 + local allow_pre_release=${5:-false} + + if [[ -z "$file_name" ]]; then + echo "File name is not specified." >&2 + exit 1 + fi + + if [[ "$hash_type" == "SHA256" ]]; then + hash_pattern="[A-Fa-f0-9]{64}" + elif [[ "$hash_type" == "SHA512" ]]; then + hash_pattern="[A-Fa-f0-9]{128}" + else + echo "Unknown hash type: ${hash_type}" >&2 + exit 1 + fi + + matching_releases=$(get_github_releases_by_version "${repo}" "${version}" "${allow_pre_release}" "true") + matched_line=$(printf "$(echo $matching_releases | jq '.body')\n" | grep "$file_name") + + if [[ -z "$matched_line" ]]; then + echo "File name ${file_name} not found in release body" >&2 + exit 1 + fi + + if [[ "$(echo "$matched_line" | wc -l)" -gt 1 ]]; then + echo "Multiple matches found for ${file_name} in release body: ${matched_line}" >&2 + exit 1 + fi + + hash=$(echo $matched_line | grep -oP "$hash_pattern") + + if [[ -z "$hash" ]]; then + echo "Found ${file_name} in body of release, but failed to get hash from it: ${matched_line}" >&2 + exit 1 + fi + + echo "$hash" +} + +get_checksum_from_url() { + local url=$1 + local file_name=$2 + local hash_type=$3 + local use_custom_search_pattern=${4:-false} + local delimiter=${5:-' '} + local word_number=${6:-1} + + if [[ "$hash_type" == "SHA256" ]]; then + hash_pattern="[A-Fa-f0-9]{64}" + elif [[ "$hash_type" == "SHA512" ]]; then + hash_pattern="[A-Fa-f0-9]{128}" + else + echo "Unknown hash type: ${hash_type}" >&2 + exit 1 + fi + + checksums_file_path=$(download_with_retry "$url") + checksums=$(cat "$checksums_file_path") + rm "$checksums_file_path" + + matched_line=$(printf "$checksums\n" | grep "$file_name") + + if [[ "$(echo "$matched_line" | wc -l)" -gt 1 ]]; then + echo "Found multiple lines matching file name ${file_name} in checksum file." >&2 + exit 1 + fi + + if [[ -z "$matched_line" ]]; then + echo "File name ${file_name} not found in checksum file." >&2 + exit 1 + fi + + if [[ $use_custom_search_pattern == "true" ]]; then + hash=$(echo "$matched_line" | sed 's/ */ /g' | cut -d "$delimiter" -f "$word_number" | tr -d -c '[:alnum:]') + else + hash=$(echo $matched_line | grep -oP "$hash_pattern") + fi + + if [[ -z "$hash" ]]; then + echo "Found ${file_name} in checksum file, but failed to get hash from it: ${matched_line}" >&2 + exit 1 + fi + + echo "$hash" +} + +use_checksum_comparison() { + local file_path=$1 + local checksum=$2 + local sha_type=${3:-"256"} + + echo "Performing checksum verification" + + if [[ ! -f "$file_path" ]]; then + echo "File not found: $file_path" + exit 1 + fi + + local_file_hash=$(shasum --algorithm "$sha_type" "$file_path" | awk '{print $1}') + + if [[ "$local_file_hash" != "$checksum" ]]; then + echo "Checksum verification failed. Expected hash: $checksum; Actual hash: $local_file_hash." + exit 1 + else + echo "Checksum verification passed" + fi +} diff --git a/images/ubuntu/scripts/helpers/invoke-tests.sh b/images/ubuntu/scripts/helpers/invoke-tests.sh new file mode 100644 index 0000000..7bef3c4 --- /dev/null +++ b/images/ubuntu/scripts/helpers/invoke-tests.sh @@ -0,0 +1,8 @@ +#!/bin/bash -e +################################################################################ +## File: invoke-tests.sh +## Desc: Helper function for invoking tests +################################################################################ + +pwsh -Command "Import-Module '$HELPER_SCRIPTS/../tests/Helpers.psm1' -DisableNameChecking + Invoke-PesterTests -TestFile \"$1\" -TestName \"$2\"" diff --git a/images/ubuntu/scripts/helpers/os.sh b/images/ubuntu/scripts/helpers/os.sh new file mode 100644 index 0000000..6838535 --- /dev/null +++ b/images/ubuntu/scripts/helpers/os.sh @@ -0,0 +1,17 @@ +#!/bin/bash -e +################################################################################ +## File: os.sh +## Desc: Helper functions for OS releases +################################################################################ + +is_ubuntu20() { + lsb_release -rs | grep -q '20.04' +} + +is_ubuntu22() { + lsb_release -rs | grep -q '22.04' +} + +is_ubuntu24() { + lsb_release -rs | grep -q '24.04' +} diff --git a/images/ubuntu/scripts/tests/ActionArchiveCache.Tests.ps1 b/images/ubuntu/scripts/tests/ActionArchiveCache.Tests.ps1 new file mode 100644 index 0000000..df4add6 --- /dev/null +++ b/images/ubuntu/scripts/tests/ActionArchiveCache.Tests.ps1 @@ -0,0 +1,18 @@ +Describe "ActionArchiveCache" { + BeforeDiscovery { + $actionArchiveCachePath = "/opt/actionarchivecache" + $tarballTestCases = Get-ChildItem -Path "$actionArchiveCachePath/*.tar.gz" -Recurse | ForEach-Object { @{ ActionTarball = $_.FullName } } + } + + Context "Action archive cache directory not empty" { + It " not empty" -TestCases @{ ActionArchiveCachepath = $actionArchiveCachePath } { + (Get-ChildItem -Path "$ActionArchiveCachepath/*.tar.gz" -Recurse).Count | Should -BeGreaterThan 0 + } + } + + Context "Action tarball not empty" { + It "" -TestCases $tarballTestCases { + (Get-Item "$ActionTarball").Length | Should -BeGreaterThan 0 + } + } +} diff --git a/images/ubuntu/scripts/tests/Android.Tests.ps1 b/images/ubuntu/scripts/tests/Android.Tests.ps1 new file mode 100644 index 0000000..ab2ac2c --- /dev/null +++ b/images/ubuntu/scripts/tests/Android.Tests.ps1 @@ -0,0 +1,105 @@ +Describe "Android" { + function Get-AndroidPackages { + <# + .SYNOPSIS + This function returns a list of available Android packages. + + .DESCRIPTION + The Get-AndroidPackages function checks if a list of packages is already available in a file. + If not, it uses the sdkmanager to generate a list of available packages and saves it to a file. + It then returns the content of this file. + + .PARAMETER SDKRootPath + The root path of the Android SDK installation. + If not specified, the function uses the ANDROID_HOME environment variable. + + .EXAMPLE + Get-AndroidPackages -SDKRootPath "/usr/local/lib/android/sdk" + + This command returns a list of available Android packages for the specified SDK root path. + + .NOTES + This function requires the Android SDK to be installed. + #> + param ( + [Parameter(Mandatory=$false)] + [string] $SDKRootPath + ) + + if (-not $SDKRootPath) { + $SDKRootPath = $env:ANDROID_HOME + } + + $packagesListFile = "$SDKRootPath/packages-list.txt" + + if (-not (Test-Path -Path $packagesListFile -PathType Leaf)) { + (/usr/local/lib/android/sdk/cmdline-tools/latest/bin/sdkmanager --list --verbose 2>&1) | + Where-Object { $_ -Match "^[^\s]" } | + Where-Object { $_ -NotMatch "^(Loading |Info: Parsing |---|\[=+|Installed |Available )" } | + Where-Object { $_ -NotMatch "^[^;]*$" } | + Out-File -FilePath $packagesListFile + + Write-Host "Android packages list:" + Get-Content $packagesListFile + } + + return Get-Content $packagesListFile + } + + $androidSdkManagerPackages = Get-AndroidPackages + [int]$platformMinVersion = (Get-ToolsetContent).android.platform_min_version + [version]$buildToolsMinVersion = (Get-ToolsetContent).android.build_tools_min_version + [array]$ndkVersions = (Get-ToolsetContent).android.ndk.versions + $ndkFullVersions = $ndkVersions | + ForEach-Object { (Get-ChildItem "/usr/local/lib/android/sdk/ndk/${_}.*" | + Select-Object -Last 1).Name } | ForEach-Object { "ndk/${_}" } + # Platforms starting with a letter are the preview versions, which is not installed on the image + $platformVersionsList = ($androidSdkManagerPackages | + Where-Object { "$_".StartsWith("platforms;") }) -replace 'platforms;android-', '' | + Where-Object { $_ -match "^\d" } | Sort-Object -Unique + $platformsInstalled = $platformVersionsList | + Where-Object { [int]($_.Split("-")[0]) -ge $platformMinVersion } | + ForEach-Object { "platforms/android-${_}" } + + $buildToolsList = ($androidSdkManagerPackages | Where-Object { "$_".StartsWith("build-tools;") }) -replace 'build-tools;', '' + $buildTools = $buildToolsList | + Where-Object { $_ -match "\d+(\.\d+){2,}$"} | + Where-Object { [version]$_ -ge $buildToolsMinVersion } | + Sort-Object -Unique | + ForEach-Object { "build-tools/${_}" } + + $androidPackages = @( + $platformsInstalled, + $buildTools, + $ndkFullVersions, + ((Get-ToolsetContent).android.extra_list | ForEach-Object { "extras/${_}" }), + ((Get-ToolsetContent).android.addon_list | ForEach-Object { "add-ons/${_}" }), + ((Get-ToolsetContent).android.additional_tools | ForEach-Object { "${_}" }) + ) + + $androidPackages = $androidPackages | ForEach-Object { $_ } + + Context "SDKManagers" { + $testCases = @( + @{ + PackageName = "Command-line tools" + Sdkmanager = "$env:ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" + } + ) + + It "Sdkmanager from is available" -TestCases $testCases { + "$Sdkmanager --version" | Should -ReturnZeroExitCode + } + } + + Context "Packages" { + $testCases = $androidPackages | ForEach-Object { @{ PackageName = $_ } } + + It "" -TestCases $testCases { + # Convert 'cmake;3.6.4111459' -> 'cmake/3.6.4111459' + $PackageName = $PackageName.Replace(";", "/") + $targetPath = Join-Path $env:ANDROID_HOME $PackageName + $targetPath | Should -Exist + } + } +} diff --git a/images/ubuntu/scripts/tests/Apt.Tests.ps1 b/images/ubuntu/scripts/tests/Apt.Tests.ps1 new file mode 100644 index 0000000..d059613 --- /dev/null +++ b/images/ubuntu/scripts/tests/Apt.Tests.ps1 @@ -0,0 +1,23 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +Describe "Apt" { + $packages = (Get-ToolsetContent).apt.cmd_packages + (Get-ToolsetContent).apt.vital_packages + $testCases = $packages | ForEach-Object { @{ toolName = $_ } } + + It " is available" -TestCases $testCases { + switch ($toolName) { + "acl" { $toolName = "getfacl"; break } + "aria2" { $toolName = "aria2c"; break } + "p7zip-full" { $toolName = "p7zip"; break } + "subversion" { $toolName = "svn"; break } + "sphinxsearch" { $toolName = "searchd"; break } + "binutils" { $toolName = "strings"; break } + "coreutils" { $toolName = "tr"; break } + "net-tools" { $toolName = "netstat"; break } + "mercurial" { $toolName = "hg"; break } + "findutils" { $toolName = "find"; break } + } + + (Get-Command -Name $toolName).CommandType | Should -BeExactly "Application" + } +} diff --git a/images/ubuntu/scripts/tests/Browsers.Tests.ps1 b/images/ubuntu/scripts/tests/Browsers.Tests.ps1 new file mode 100644 index 0000000..1ea5230 --- /dev/null +++ b/images/ubuntu/scripts/tests/Browsers.Tests.ps1 @@ -0,0 +1,41 @@ +Describe "Firefox" { + It "Firefox" { + "firefox --version" | Should -ReturnZeroExitCode + } + + It "Geckodriver" { + "geckodriver --version" | Should -ReturnZeroExitCode + } +} + +Describe "Chrome" { + It "Chrome" { + "google-chrome --version" | Should -ReturnZeroExitCode + } + + It "Chrome Driver" { + "chromedriver --version" | Should -ReturnZeroExitCode + } + + It "Chrome and Chrome Driver major versions are the same" { + $chromeMajor = (google-chrome --version).Trim("Google Chrome ").Split(".")[0] + $chromeDriverMajor = (chromedriver --version).Trim("ChromeDriver ").Split(".")[0] + $chromeMajor | Should -BeExactly $chromeDriverMajor + } +} + +Describe "Edge" { + It "Edge" { + "microsoft-edge --version" | Should -ReturnZeroExitCode + } + + It "Edge Driver" { + "msedgedriver --version" | Should -ReturnZeroExitCode + } +} + +Describe "Chromium" { + It "Chromium" { + "chromium-browser --version" | Should -ReturnZeroExitCode + } +} diff --git a/images/ubuntu/scripts/tests/CLI.Tools.Tests.ps1 b/images/ubuntu/scripts/tests/CLI.Tools.Tests.ps1 new file mode 100644 index 0000000..abbf585 --- /dev/null +++ b/images/ubuntu/scripts/tests/CLI.Tools.Tests.ps1 @@ -0,0 +1,55 @@ +Describe "Azure CLI" { + It "Azure CLI" { + "az --version" | Should -ReturnZeroExitCode + } +} + +Describe "Azure DevOps CLI" { + It "az devops" { + "az devops -h" | Should -ReturnZeroExitCode + } +} + +Describe "Aliyun CLI" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "Aliyun CLI" { + "aliyun version" | Should -ReturnZeroExitCode + } +} + +Describe "AWS" { + It "AWS CLI" { + "aws --version" | Should -ReturnZeroExitCode + } + + It "Session Manager Plugin for the AWS CLI" { + session-manager-plugin 2>&1 | Out-String | Should -Match "plugin was installed successfully" + } + + It "AWS SAM CLI" { + "sam --version" | Should -ReturnZeroExitCode + } +} + +Describe "GitHub CLI" { + It "gh cli" { + "gh --version" | Should -ReturnZeroExitCode + } +} + +Describe "Google Cloud CLI" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "Google Cloud CLI" { + "gcloud --version" | Should -ReturnZeroExitCode + } +} + +Describe "OC CLI" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "OC CLI" { + "oc version" | Should -ReturnZeroExitCode + } +} + +Describe "Oras CLI" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "Oras CLI" { + "oras version" | Should -ReturnZeroExitCode + } +} diff --git a/images/ubuntu/scripts/tests/Common.Tests.ps1 b/images/ubuntu/scripts/tests/Common.Tests.ps1 new file mode 100644 index 0000000..38eaeb2 --- /dev/null +++ b/images/ubuntu/scripts/tests/Common.Tests.ps1 @@ -0,0 +1,47 @@ +Describe "PHP" { + $testCases = (Get-ToolsetContent).php.versions | ForEach-Object { @{PhpVersion = $_} } + + It "php " -TestCases $testCases { + "php${PhpVersion} --version" | Should -ReturnZeroExitCode + "php-config${PhpVersion} --version" | Should -ReturnZeroExitCode + "phpize${PhpVersion} --version" | Should -ReturnZeroExitCode + } + + It "PHPUnit" { + "phpunit --version" | Should -ReturnZeroExitCode + } + + It "Composer" { + "composer --version" | Should -ReturnZeroExitCode + } + + It "Pear" { + "pear" | Should -ReturnZeroExitCode + } + + It "Pecl" { + "pecl" | Should -ReturnZeroExitCode + } +} + +Describe "Swift" { + It "swift" { + "swift --version" | Should -ReturnZeroExitCode + } + + It "swiftc" { + "swiftc --version" | Should -ReturnZeroExitCode + } + + It "libsourcekitd" { + "/usr/local/lib/libsourcekitdInProc.so" | Should -Exist + } +} + +Describe "PipxPackages" { + $testCases = (Get-ToolsetContent).pipx | ForEach-Object { @{package=$_.package; cmd = $_.cmd} } + + It "" -TestCases $testCases { + "$cmd --version" | Should -ReturnZeroExitCode + } +} diff --git a/images/ubuntu/scripts/tests/Databases.Tests.ps1 b/images/ubuntu/scripts/tests/Databases.Tests.ps1 new file mode 100644 index 0000000..6038824 --- /dev/null +++ b/images/ubuntu/scripts/tests/Databases.Tests.ps1 @@ -0,0 +1,39 @@ +Describe "MongoDB" -Skip:(-not (Test-IsUbuntu20)) { + It "" -TestCases @( + @{ ToolName = "mongo" } + @{ ToolName = "mongod" } + ) { + $toolsetVersion = (Get-ToolsetContent).mongodb.version + (& $ToolName --version)[2].Split('"')[-2] | Should -BeLike "$toolsetVersion*" + } +} + +Describe "PostgreSQL" { + It "PostgreSQL Service" { + "sudo systemctl start postgresql" | Should -ReturnZeroExitCode + "pg_isready" | Should -OutputTextMatchingRegex "/var/run/postgresql:5432 - accepting connections" + "sudo systemctl stop postgresql" | Should -ReturnZeroExitCode + } + + It "PostgreSQL version should correspond to the version in the toolset" { + $toolsetVersion = (Get-ToolsetContent).postgresql.version + # Client version + (psql --version).split()[-1] | Should -BeLike "$toolsetVersion*" + # Server version + (pg_config --version).split()[-1] | Should -BeLike "$toolsetVersion*" + } +} + +Describe "MySQL" { + It "MySQL CLI" { + "mysql -V" | Should -ReturnZeroExitCode + } + + It "MySQL Service" { + "sudo systemctl start mysql" | Should -ReturnZeroExitCode + mysql -s -N -h localhost -uroot -proot -e "select count(*) from mysql.user where user='root' and authentication_string is null;" | Should -BeExactly 0 + "sudo mysql -vvv -e 'CREATE DATABASE smoke_test' -uroot -proot" | Should -ReturnZeroExitCode + "sudo mysql -vvv -e 'DROP DATABASE smoke_test' -uroot -proot" | Should -ReturnZeroExitCode + "sudo systemctl stop mysql" | Should -ReturnZeroExitCode + } +} diff --git a/images/ubuntu/scripts/tests/DotnetSDK.Tests.ps1 b/images/ubuntu/scripts/tests/DotnetSDK.Tests.ps1 new file mode 100644 index 0000000..e3d06df --- /dev/null +++ b/images/ubuntu/scripts/tests/DotnetSDK.Tests.ps1 @@ -0,0 +1,40 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" + +Describe "Dotnet and tools" { + BeforeAll { + $env:PATH = "/etc/skel/.dotnet/tools:$($env:PATH)" + $dotnetSDKs = dotnet --list-sdks | ConvertTo-Json + $dotnetRuntimes = dotnet --list-runtimes | ConvertTo-Json + } + + $dotnetVersions = (Get-ToolsetContent).dotnet.versions + + Context "Default" { + It "Default Dotnet SDK is available" { + "dotnet --version" | Should -ReturnZeroExitCode + } + } + + foreach ($version in $dotnetVersions) { + Context "Dotnet $version" { + $dotnet = @{ dotnetVersion = $version } + + It "SDK is available" -TestCases $dotnet { + $dotnetSDKs | Should -Match "$dotnetVersion.[1-9]*" + } + + It "Runtime is available" -TestCases $dotnet { + $dotnetRuntimes | Should -Match "$dotnetVersion.[1-9]*" + } + } + } + + Context "Dotnet tools" { + $dotnetTools = (Get-ToolsetContent).dotnet.tools + $testCases = $dotnetTools | ForEach-Object { @{ ToolName = $_.name; TestInstance = $_.test }} + + It " is available" -TestCases $testCases { + "$TestInstance" | Should -ReturnZeroExitCode + } + } +} diff --git a/images/ubuntu/scripts/tests/Haskell.Tests.ps1 b/images/ubuntu/scripts/tests/Haskell.Tests.ps1 new file mode 100644 index 0000000..a620df5 --- /dev/null +++ b/images/ubuntu/scripts/tests/Haskell.Tests.ps1 @@ -0,0 +1,36 @@ +Describe "Haskell" { + $GHCCommonPath = "/usr/local/.ghcup/ghc" + $GHCVersions = Get-ChildItem -Path $GHCCommonPath | Where-Object { $_.Name -match "\d+\.\d+" } + + $testCase = @{ GHCVersions = $GHCVersions } + + It "GHC directory contains two version of GHC" -TestCases $testCase { + $GHCVersions.Count | Should -Be 2 + } + + $testCases = $GHCVersions | ForEach-Object { @{ GHCPath = "${_}/bin/ghc"} } + + It "GHC version " -TestCases $testCases { + "$GHCPath --version" | Should -ReturnZeroExitCode + } + + It "GHCup" { + "ghcup --version" | Should -ReturnZeroExitCode + } + + It "Default GHC" { + "ghc --version" | Should -ReturnZeroExitCode + } + + It "Cabal" { + "cabal --version" | Should -ReturnZeroExitCode + } + + It "Stack" { + "stack --version" | Should -ReturnZeroExitCode + } + + It "Stack hook is not installed" { + "$HOME/.stack/hooks/ghc-install.sh" | Should -Not -Exist + } +} diff --git a/images/ubuntu/scripts/tests/Helpers.psm1 b/images/ubuntu/scripts/tests/Helpers.psm1 new file mode 100644 index 0000000..31ad527 --- /dev/null +++ b/images/ubuntu/scripts/tests/Helpers.psm1 @@ -0,0 +1,165 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" -DisableNameChecking + +function Invoke-PesterTests { + <# + .SYNOPSIS + Runs Pester tests based on the provided test file and test name. + + .DESCRIPTION + The Invoke-PesterTests function runs Pester tests based on the provided test file and test name. + + .PARAMETER TestFile + The name of the test file to run. This should be the base name of the test file without the extension. + Using "*" will run all tests from all test files. + + .PARAMETER TestName + The name of the specific test to run. If provided, only the test with the matching name will be executed. + + .EXAMPLE + Invoke-PesterTests -TestFile "MyTests" -TestName "Test1" + Runs the test named "Test1" from the test file "MyTests.Tests.ps1". + + .EXAMPLE + Invoke-PesterTests -TestFile "*" + Runs all tests from all test files + + #> + Param( + [Parameter(Mandatory = $true)] + [string] $TestFile, + [string] $TestName + ) + + $testPath = "/imagegeneration/tests/${TestFile}.Tests.ps1" + if (-not (Test-Path $testPath)) { + throw "Unable to find test file '$TestFile' on '$testPath'." + } + + # Check that Pester module is imported + if (-not (Get-Module "Pester")) { + Import-Module Pester + } + + $configuration = [PesterConfiguration] @{ + Run = @{ Path = $testPath; PassThru = $true } + Output = @{ Verbosity = "Detailed"; RenderMode = "Plaintext" } + } + if ($TestName) { + $configuration.Filter.FullName = $TestName + } + + # Switch ErrorActionPreference to Stop temporary to make sure that tests will fail on silent errors too + $backupErrorActionPreference = $ErrorActionPreference + $ErrorActionPreference = "Stop" + $results = Invoke-Pester -Configuration $configuration + $ErrorActionPreference = $backupErrorActionPreference + + # Fail in case if no tests are run + if (-not ($results -and ($results.FailedCount -eq 0) -and (($results.PassedCount + $results.SkippedCount) -gt 0))) { + $results + throw "Test run has failed" + } +} + +function ShouldReturnZeroExitCode { + <# + .SYNOPSIS + Implements a custom Should-operator for the Pester framework. + + .DESCRIPTION + This function is used to check if a command has returned a zero exit code. + It can be used by registering it using the Add-ShouldOperator function in Pester. + + .PARAMETER ActualValue + The actual value to be checked. + + .PARAMETER Negate + A switch parameter that, when specified, negates the result of the check. + + .PARAMETER Because + An optional string that provides additional context or explanation for the check. + + .NOTES + This function is designed to be used with the Pester framework. + + .LINK + https://pester.dev/docs/assertions/custom-assertions + #> + Param( + [string] $ActualValue, + [switch] $Negate, + [string] $Because # This parameter is unused but we need it to match Pester asserts signature + ) + + $result = Get-CommandResult $ActualValue -ValidateExitCode $false + + [bool] $succeeded = $result.ExitCode -eq 0 + if ($Negate) { $succeeded = -not $succeeded } + + if (-not $succeeded) { + $commandOutputIndent = " " * 4 + $commandOutput = ($result.Output | ForEach-Object { "${commandOutputIndent}${_}" }) -join "`n" + $failureMessage = "Command '${ActualValue}' has finished with exit code`n${commandOutput}" + } + + return [PSCustomObject] @{ + Succeeded = $succeeded + FailureMessage = $failureMessage + } +} + +function ShouldOutputTextMatchingRegex { + <# + .SYNOPSIS + Implements a custom Should-operator for the Pester framework. + + .DESCRIPTION + This function is used to check if a command outputs text that matches a regular expression. + It can be used by registering it using the Add-ShouldOperator function in Pester. + + .PARAMETER ActualValue + The actual value to be checked. + + .PARAMETER RegularExpression + The regular expression to be used for the check. + + .PARAMETER Negate + A switch parameter that, when specified, negates the result of the check. + + .NOTES + This function is designed to be used with the Pester framework. + + .LINK + https://pester.dev/docs/assertions/custom-assertions + #> + Param( + [string] $ActualValue, + [string] $RegularExpression, + [switch] $Negate + ) + + $output = (Get-CommandResult $ActualValue -ValidateExitCode $false).Output | Out-String + [bool] $succeeded = $output -cmatch $RegularExpression + + if ($Negate) { $succeeded = -not $succeeded } + + $failureMessage = '' + + if (-not $succeeded) { + if ($Negate) { + $failureMessage = "Expected regular expression '$RegularExpression' for '$ActualValue' command to not match '$output', but it did match." + } else { + $failureMessage = "Expected regular expression '$RegularExpression' for '$ActualValue' command to match '$output', but it did not match." + } + } + + return [PSCustomObject] @{ + Succeeded = $succeeded + FailureMessage = $failureMessage + } +} + +If (Get-Command -Name Add-ShouldOperator -ErrorAction SilentlyContinue) { + Add-ShouldOperator -Name ReturnZeroExitCode -InternalName ShouldReturnZeroExitCode -Test ${function:ShouldReturnZeroExitCode} + Add-ShouldOperator -Name OutputTextMatchingRegex -InternalName ShouldOutputTextMatchingRegex -Test ${function:ShouldOutputTextMatchingRegex} +} diff --git a/images/ubuntu/scripts/tests/Java.Tests.ps1 b/images/ubuntu/scripts/tests/Java.Tests.ps1 new file mode 100644 index 0000000..5480313 --- /dev/null +++ b/images/ubuntu/scripts/tests/Java.Tests.ps1 @@ -0,0 +1,56 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" -DisableNameChecking + +Describe "Java" { + $toolsetJava = (Get-ToolsetContent).java + $defaultVersion = $toolsetJava.default + $jdkVersions = $toolsetJava.versions + + It "Java is default" -TestCases @{ DefaultJavaVersion = $defaultVersion } { + $actualJavaPath = [System.Environment]::GetEnvironmentVariable("JAVA_HOME") + $expectedJavaPath = [System.Environment]::GetEnvironmentVariable("JAVA_HOME_${DefaultJavaVersion}_X64") + + $actualJavaPath | Should -Not -BeNullOrEmpty + $expectedJavaPath | Should -Not -BeNullOrEmpty + $actualJavaPath | Should -Be $expectedJavaPath + } + + It "" -TestCases @( + @{ ToolName = "java" } + @{ ToolName = "javac" } + ) { + "$ToolName -version" | Should -ReturnZeroExitCode + } + + $testCases = $jdkVersions | ForEach-Object { @{Version = $_ } } + + It "Java " -TestCases $testCases { + $javaVariableValue = [System.Environment]::GetEnvironmentVariable("JAVA_HOME_${Version}_X64") + $javaVariableValue | Should -Not -BeNullOrEmpty + $javaPath = Join-Path $javaVariableValue "bin/java" + + "`"$javaPath`" -version" | Should -ReturnZeroExitCode + + if ($Version -eq 8) { + $Version = "1.${Version}" + } + "`"$javaPath`" -version" | Should -OutputTextMatchingRegex "openjdk\ version\ `"${Version}(\.[0-9_\.]+)?`"" + } +} + +Describe "Java-Tools" { + It "Gradle" { + "gradle -version" | Should -ReturnZeroExitCode + + $gradleVariableValue = [System.Environment]::GetEnvironmentVariable("GRADLE_HOME") + $gradleVariableValue | Should -BeLike "/usr/share/gradle-*" + + $gradlePath = Join-Path $env:GRADLE_HOME "bin/gradle" + "`"$GradlePath`" -version" | Should -ReturnZeroExitCode + } + It "" -TestCases @( + @{ ToolName = "mvn" } + @{ ToolName = "ant" } + ) { + "$ToolName -version" | Should -ReturnZeroExitCode + } +} diff --git a/images/ubuntu/scripts/tests/Node.Tests.ps1 b/images/ubuntu/scripts/tests/Node.Tests.ps1 new file mode 100644 index 0000000..3ae9761 --- /dev/null +++ b/images/ubuntu/scripts/tests/Node.Tests.ps1 @@ -0,0 +1,13 @@ +Describe "Node.js" { + $binaries = @("node") + $module_commands = (Get-ToolsetContent).node_modules | ForEach-Object { $_.command } + $testCases = $binaries + $module_commands | ForEach-Object { @{NodeCommand = $_} } + + It "" -TestCases $testCases { + "$NodeCommand --version" | Should -ReturnZeroExitCode + } + + It "Node.js version should correspond to the version in the toolset" { + node --version | Should -BeLike "v$((Get-ToolsetContent).node.default).*" + } +} diff --git a/images/ubuntu/scripts/tests/PowerShellModules.Tests.ps1 b/images/ubuntu/scripts/tests/PowerShellModules.Tests.ps1 new file mode 100644 index 0000000..9a1079e --- /dev/null +++ b/images/ubuntu/scripts/tests/PowerShellModules.Tests.ps1 @@ -0,0 +1,64 @@ +Describe "PowerShellModules" { + $modules = (Get-ToolsetContent).powershellModules + $modulesWithoutVersions = $modules | Where-Object { -not $_.versions } | ForEach-Object { + @{moduleName = $_.name} + } + + $modulesWithVersions = $modules | Where-Object { $_.versions } | ForEach-Object { + $moduleName = $_.name + $_.versions | ForEach-Object { + @{moduleName = $moduleName; expectedVersion = $_} + } + } + + It " is installed" -TestCases $modulesWithoutVersions { + Get-Module -Name $moduleName -ListAvailable | Should -BeTrue + } + + if ($modulesWithVersions) { + It " with is installed" -TestCases $modulesWithVersions { + (Get-Module -Name $moduleName -ListAvailable).Version -contains $expectedVersion | Should -BeTrue + } + } +} + +Describe "AzureModules" { + $modules = (Get-ToolsetContent).azureModules + $modulesRootPath = "/usr/share" + + foreach ($module in $modules) { + $moduleName = $module.name + + Context "$moduleName" { + foreach ($version in $module.versions) { + $modulePath = Join-Path -Path $modulesRootPath -ChildPath "${moduleName}_${version}" + $moduleInfo = @{ moduleName = $moduleName; modulePath = $modulePath; expectedVersion = $version } + + It " exists in modules directory" -TestCases $moduleInfo { + $testJob = Start-Job -ScriptBlock { + param ( + $modulePath, + $moduleName + ) + + $env:PSModulePath = "${modulePath}:${env:PSModulePath}" + (Get-Module -ListAvailable -Name $moduleName).Version.ToString() + } -ArgumentList $modulePath, $moduleName + + $moduleVersion = $testJob | Wait-Job | Receive-Job + Remove-Job $testJob + $moduleVersion | Should -Match $expectedVersion + } + } + + if ($module.default) { + $moduleInfo = @{ moduleName = $moduleName; moduleDefault = $module.default } + + It " set as default" -TestCases $moduleInfo { + $moduleVersion = (Get-Module -ListAvailable -Name $moduleName).Version.ToString() + $moduleVersion | Should -Match $moduleDefault + } + } + } + } +} diff --git a/images/ubuntu/scripts/tests/RunAll-Tests.ps1 b/images/ubuntu/scripts/tests/RunAll-Tests.ps1 new file mode 100644 index 0000000..7ac5460 --- /dev/null +++ b/images/ubuntu/scripts/tests/RunAll-Tests.ps1 @@ -0,0 +1,3 @@ +Import-Module "$PSScriptRoot/Helpers.psm1" -DisableNameChecking + +Invoke-PesterTests "*" diff --git a/images/ubuntu/scripts/tests/RunnerCache.Tests.ps1 b/images/ubuntu/scripts/tests/RunnerCache.Tests.ps1 new file mode 100644 index 0000000..8c8109e --- /dev/null +++ b/images/ubuntu/scripts/tests/RunnerCache.Tests.ps1 @@ -0,0 +1,7 @@ +Describe "RunnerCache" { + Context "runner cache directory not empty" { + It " not empty" -TestCases @{ RunnerCachePath = "/opt/runner-cache" } { + (Get-ChildItem -Path "$RunnerCachePath/*.tar.gz" -Recurse).Count | Should -BeGreaterThan 0 + } + } +} diff --git a/images/ubuntu/scripts/tests/System.Tests.ps1 b/images/ubuntu/scripts/tests/System.Tests.ps1 new file mode 100644 index 0000000..457eae0 --- /dev/null +++ b/images/ubuntu/scripts/tests/System.Tests.ps1 @@ -0,0 +1,8 @@ +# The $env:AGENT_NAME and $env:RUNNER_NAME are predefined variables for the ADO pipelines and for the GitHub actions respectively. +# If the test is running on the ADO pipeline or on the GitHub actions, the test will be skipped +Describe "Disk free space" -Skip:(-not [String]::IsNullOrEmpty($env:AGENT_NAME) -or -not [String]::IsNullOrEmpty($env:RUNNER_NAME)) { + It "Image has enough free space" { + $freeSpace = (Get-PSDrive "/").Free + $freeSpace | Should -BeGreaterOrEqual 17GB + } +} diff --git a/images/ubuntu/scripts/tests/Tools.Tests.ps1 b/images/ubuntu/scripts/tests/Tools.Tests.ps1 new file mode 100644 index 0000000..749e650 --- /dev/null +++ b/images/ubuntu/scripts/tests/Tools.Tests.ps1 @@ -0,0 +1,413 @@ +Import-Module "$PSScriptRoot/../helpers/Common.Helpers.psm1" +Describe "azcopy" { + It "azcopy" { + "azcopy --version" | Should -ReturnZeroExitCode + } + + It "azcopy10 link exists" { + "azcopy10 --version" | Should -ReturnZeroExitCode + } +} + +Describe "Bicep" { + It "Bicep" { + "bicep --version" | Should -ReturnZeroExitCode + } +} + +Describe "Rust" { + BeforeAll { + $env:PATH = "/etc/skel/.cargo/bin:/etc/skel/.rustup/bin:$($env:PATH)" + $env:RUSTUP_HOME = "/etc/skel/.rustup" + $env:CARGO_HOME = "/etc/skel/.cargo" + } + + It "Rustup is installed" { + "rustup --version" | Should -ReturnZeroExitCode + } + + It "Rustc is installed" { + "rustc --version" | Should -ReturnZeroExitCode + } + + It "Rustdoc is installed" { + "rustdoc --version" | Should -ReturnZeroExitCode + } + + It "Rustfmt is installed" { + "rustfmt --version" | Should -ReturnZeroExitCode + } + + It "cargo" { + "cargo --version" | Should -ReturnZeroExitCode + } + + Context "Cargo dependencies" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "bindgen" { + "bindgen --version" | Should -ReturnZeroExitCode + } + + It "cbindgen" { + "cbindgen --version" | Should -ReturnZeroExitCode + } + + It "cargo-clippy" { + "cargo-clippy --version" | Should -ReturnZeroExitCode + } + + It "Cargo audit" { + "cargo audit --version" | Should -ReturnZeroExitCode + } + + It "Cargo outdated" { + "cargo outdated --version" | Should -ReturnZeroExitCode + } + } +} + +Describe "Docker" { + It "docker client" { + $version=(Get-ToolsetContent).docker.components | Where-Object { $_.package -eq 'docker-ce-cli' } | Select-Object -ExpandProperty version + If ($version -ne "latest") { + $(sudo docker version --format '{{.Client.Version}}') | Should -BeLike "*$version*" + }else{ + "sudo docker version --format '{{.Client.Version}}'" | Should -ReturnZeroExitCode + } + } + + It "docker server" { + $version=(Get-ToolsetContent).docker.components | Where-Object { $_.package -eq 'docker-ce' } | Select-Object -ExpandProperty version + If ($version -ne "latest") { + $(sudo docker version --format '{{.Server.Version}}') | Should -BeLike "*$version*" + }else{ + "sudo docker version --format '{{.Server.Version}}'" | Should -ReturnZeroExitCode + } + } + + It "docker client/server versions match" { + $clientVersion = $(sudo docker version --format '{{.Client.Version}}') + $serverVersion = $(sudo docker version --format '{{.Server.Version}}') + $clientVersion | Should -Be $serverVersion + } + + It "docker buildx" { + $version=(Get-ToolsetContent).docker.plugins | Where-Object { $_.plugin -eq 'buildx' } | Select-Object -ExpandProperty version + If ($version -ne "latest") { + $(docker buildx version) | Should -BeLike "*$version*" + }else{ + "docker buildx" | Should -ReturnZeroExitCode + } + } + + It "docker compose v2" { + $version=(Get-ToolsetContent).docker.plugins | Where-Object { $_.plugin -eq 'compose' } | Select-Object -ExpandProperty version + If ($version -ne "latest") { + $(docker compose version --short) | Should -BeLike "*$version*" + }else{ + "docker compose version --short" | Should -ReturnZeroExitCode + } + } + + It "docker-credential-ecr-login" { + "docker-credential-ecr-login -v" | Should -ReturnZeroExitCode + } +} + +Describe "Docker images" { + $testCases = (Get-ToolsetContent).docker.images | ForEach-Object { @{ ImageName = $_ } } + + It "" -TestCases $testCases { + sudo docker images "$ImageName" --format "{{.Repository}}" | Should -Not -BeNullOrEmpty + } +} + +Describe "Ansible" { + It "Ansible" { + "ansible --version" | Should -ReturnZeroExitCode + } +} + +Describe "Bazel" { + It "" -TestCases @( + @{ ToolName = "bazel" } + @{ ToolName = "bazelisk" } + ) { + "$ToolName --version"| Should -ReturnZeroExitCode + } +} + +Describe "clang" { + $testCases = (Get-ToolsetContent).clang.Versions | ForEach-Object { @{ClangVersion = $_} } + + It "clang " -TestCases $testCases { + "clang-$ClangVersion --version" | Should -ReturnZeroExitCode + "clang++-$ClangVersion --version" | Should -ReturnZeroExitCode + "clang-format-$ClangVersion --version" | Should -ReturnZeroExitCode + "clang-tidy-$ClangVersion --version" | Should -ReturnZeroExitCode + "run-clang-tidy-$ClangVersion --help" | Should -ReturnZeroExitCode + } +} + +Describe "Cmake" { + It "cmake" { + "cmake --version" | Should -ReturnZeroExitCode + } +} + +Describe "erlang" -Skip:(-not (Test-IsUbuntu20)) { + $testCases = @("erl -version", "erlc -v", "rebar3 -v") | ForEach-Object { @{ErlangCommand = $_} } + + It "erlang " -TestCases $testCases { + "$ErlangCommand" | Should -ReturnZeroExitCode + } +} + +Describe "gcc" { + $testCases = (Get-ToolsetContent).gcc.Versions | ForEach-Object { @{GccVersion = $_} } + + It "gcc " -TestCases $testCases { + "$GccVersion --version" | Should -ReturnZeroExitCode + } +} + +Describe "gfortran" { + $testCases = (Get-ToolsetContent).gfortran.Versions | ForEach-Object { @{GfortranVersion = $_} } + + It "gfortran " -TestCases $testCases { + "$GfortranVersion --version" | Should -ReturnZeroExitCode + } +} + +Describe "Mono" -Skip:(Test-IsUbuntu24) { + It "mono" { + "mono --version" | Should -ReturnZeroExitCode + } + + It "msbuild" { + "msbuild -version" | Should -ReturnZeroExitCode + } + + It "nuget" { + "nuget" | Should -ReturnZeroExitCode + } +} + +Describe "MSSQLCommandLineTools" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "sqlcmd" { + "sqlcmd -?" | Should -ReturnZeroExitCode + } +} + +Describe "SqlPackage" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "sqlpackage" { + "sqlpackage /version" | Should -ReturnZeroExitCode + } +} + +Describe "R" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "r" { + "R --version" | Should -ReturnZeroExitCode + } +} + +Describe "Sbt" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "sbt" { + "sbt --version" | Should -ReturnZeroExitCode + } +} + +Describe "Selenium" { + It "Selenium is installed" { + $seleniumPath = Join-Path "/usr/share/java" "selenium-server.jar" + $seleniumPath | Should -Exist + } +} + +Describe "Terraform" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "terraform" { + "terraform --version" | Should -ReturnZeroExitCode + } +} + +Describe "Zstd" { + It "zstd" { + "zstd --version" | Should -ReturnZeroExitCode + } + + It "pzstd" { + "pzstd --version" | Should -ReturnZeroExitCode + } +} + +Describe "Vcpkg" { + It "vcpkg" { + "vcpkg version" | Should -ReturnZeroExitCode + } +} + +Describe "Git" { + It "git" { + "git --version" | Should -ReturnZeroExitCode + } + + It "git-ftp" { + "git-ftp --version" | Should -ReturnZeroExitCode + } +} + +Describe "Git-lfs" { + It "git-lfs" { + "git-lfs --version" | Should -ReturnZeroExitCode + } +} + +Describe "Heroku" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "heroku" { + "heroku --version" | Should -ReturnZeroExitCode + } +} + +Describe "HHVM" -Skip:(-not (Test-IsUbuntu20)) { + It "hhvm" { + "hhvm --version" | Should -ReturnZeroExitCode + } +} + +Describe "Homebrew" { + It "homebrew" { + "/home/linuxbrew/.linuxbrew/bin/brew --version" | Should -ReturnZeroExitCode + } +} + +Describe "Julia" { + It "julia" { + "julia --version" | Should -ReturnZeroExitCode + } +} + +Describe "Kubernetes tools" { + It "kind" { + "kind version" | Should -ReturnZeroExitCode + } + + It "kubectl" { + "kubectl version --client=true" | Should -OutputTextMatchingRegex "Client Version: v" + } + + It "helm" { + "helm version --short" | Should -ReturnZeroExitCode + } + + It "minikube" { + "minikube version --short" | Should -ReturnZeroExitCode + } + + It "kustomize" { + "kustomize version" | Should -ReturnZeroExitCode + } +} + +Describe "Leiningen" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "leiningen" { + "lein --version" | Should -ReturnZeroExitCode + } +} + +Describe "Conda" { + It "conda" { + "conda --version" | Should -ReturnZeroExitCode + } +} + +Describe "Packer" { + It "packer" { + "packer --version" | Should -ReturnZeroExitCode + } +} + +Describe "Pulumi" { + It "pulumi" { + "pulumi version" | Should -ReturnZeroExitCode + } +} + +Describe "Phantomjs" -Skip:(-not (Test-IsUbuntu20)) { + It "phantomjs" { + $env:OPENSSL_CONF="/etc/ssl" + "phantomjs --version" | Should -ReturnZeroExitCode + } +} + +Describe "Containers" { + $testCases = @("podman", "buildah", "skopeo") | ForEach-Object { @{ContainerCommand = $_} } + + It "" -TestCases $testCases { + "$ContainerCommand -v" | Should -ReturnZeroExitCode + } + + # https://github.com/actions/runner-images/issues/7753 + It "podman networking" -TestCases "podman CNI plugins" { + "podman network create -d bridge test-net" | Should -ReturnZeroExitCode + "podman network ls" | Should -Not -OutputTextMatchingRegex "Error" + "podman network rm test-net" | Should -ReturnZeroExitCode + } + +} + +Describe "nvm" -Skip:((-not (Test-IsUbuntu20)) -and (-not (Test-IsUbuntu22))) { + It "nvm" { + "source /etc/skel/.nvm/nvm.sh && nvm --version" | Should -ReturnZeroExitCode + } +} + +Describe "Python" { + $testCases = @("python", "pip", "python3", "pip3") | ForEach-Object { @{PythonCommand = $_} } + + It "" -TestCases $testCases { + "$PythonCommand --version" | Should -ReturnZeroExitCode + } +} + +Describe "Ruby" { + $testCases = @("ruby", "gem") | ForEach-Object { @{RubyCommand = $_} } + + It "" -TestCases $testCases { + "$RubyCommand --version" | Should -ReturnZeroExitCode + } + + $gemTestCases = (Get-ToolsetContent).rubygems | ForEach-Object { @{gemName = $_.name} } + + if ($gemTestCases) { + It "Gem is installed" -TestCases $gemTestCases { + "gem list -i '^$gemName$'" | Should -OutputTextMatchingRegex "true" + } + } +} + +Describe "yq" { + It "yq" { + "yq -V" | Should -ReturnZeroExitCode + } +} + +Describe "Kotlin" { + It "kapt" { + "kapt -version" | Should -ReturnZeroExitCode + } + + It "kotlin" { + "kotlin -version" | Should -ReturnZeroExitCode + } + + It "kotlinc" { + "kotlinc -version" | Should -ReturnZeroExitCode + } + + It "kotlinc-jvm" { + "kotlinc-jvm -version" | Should -ReturnZeroExitCode + } + + It "kotlin-dce-js" { + "kotlin-dce-js -version" | Should -ReturnZeroExitCode + } +} diff --git a/images/ubuntu/scripts/tests/Toolset.Tests.ps1 b/images/ubuntu/scripts/tests/Toolset.Tests.ps1 new file mode 100644 index 0000000..488bb42 --- /dev/null +++ b/images/ubuntu/scripts/tests/Toolset.Tests.ps1 @@ -0,0 +1,65 @@ +Describe "Toolset" { + $tools = (Get-ToolsetContent).toolcache + + $toolsExecutables = @{ + Python = @{ + tools = @("python", "bin/pip") + command = "--version" + } + node = @{ + tools = @("bin/node", "bin/npm") + command = "--version" + } + PyPy = @{ + tools = @("bin/python", "bin/pip") + command = "--version" + } + go = @{ + tools = @("bin/go") + command = "version" + } + Ruby = @{ + tools = @("bin/ruby") + command = "--version" + } + CodeQL = @{ + tools = @("codeql/codeql") + command = "version" + } + } + + foreach ($tool in $tools) { + $toolName = $tool.Name + Context "$toolName" { + $toolExecs = $toolsExecutables[$toolName] + + foreach ($version in $tool.versions) { + # Add wildcard if missing + if ($version.Split(".").Length -lt 3) { + $version += ".*" + } + + $expectedVersionPath = Join-Path $env:AGENT_TOOLSDIRECTORY $toolName $version + + It "$version version folder exists" -TestCases @{ ExpectedVersionPath = $expectedVersionPath} { + $ExpectedVersionPath | Should -Exist + } + + $foundVersion = Get-Item $expectedVersionPath ` + | Sort-Object -Property {[SemVer]$_.name} -Descending ` + | Select-Object -First 1 + $foundVersionPath = Join-Path $foundVersion $tool.arch + + if ($toolExecs) { + foreach ($executable in $toolExecs["tools"]) { + $executablePath = Join-Path $foundVersionPath $executable + + It "Validate $executable" -TestCases @{ExecutablePath = $executablePath} { + $ExecutablePath | Should -Exist + } + } + } + } + } + } +} diff --git a/images/ubuntu/scripts/tests/WebServers.Tests.ps1 b/images/ubuntu/scripts/tests/WebServers.Tests.ps1 new file mode 100644 index 0000000..dfdde33 --- /dev/null +++ b/images/ubuntu/scripts/tests/WebServers.Tests.ps1 @@ -0,0 +1,23 @@ +Describe "Apache" { + It "Apache CLI" { + "apache2 -v" | Should -ReturnZeroExitCode + } + + It "Apache Service" { + "sudo systemctl start apache2" | Should -ReturnZeroExitCode + "apachectl configtest" | Should -ReturnZeroExitCode + "sudo systemctl stop apache2" | Should -ReturnZeroExitCode + } +} + +Describe "Nginx" { + It "Nginx CLI" { + "nginx -v" | Should -ReturnZeroExitCode + } + + It "Nginx Service" { + "sudo systemctl start nginx" | Should -ReturnZeroExitCode + "sudo nginx -t" | Should -ReturnZeroExitCode + "sudo systemctl stop nginx" | Should -ReturnZeroExitCode + } +} \ No newline at end of file diff --git a/images/ubuntu/templates/ubuntu-20.04.pkr.hcl b/images/ubuntu/templates/ubuntu-20.04.pkr.hcl new file mode 100644 index 0000000..d8d821b --- /dev/null +++ b/images/ubuntu/templates/ubuntu-20.04.pkr.hcl @@ -0,0 +1,432 @@ +packer { + required_plugins { + azure = { + source = "github.com/hashicorp/azure" + version = "1.4.5" + } + } +} + +locals { + managed_image_name = var.managed_image_name != "" ? var.managed_image_name : "packer-${var.image_os}-${var.image_version}" +} + +variable "allowed_inbound_ip_addresses" { + type = list(string) + default = [] +} + +variable "azure_tags" { + type = map(string) + default = {} +} + +variable "build_resource_group_name" { + type = string + default = "${env("BUILD_RESOURCE_GROUP_NAME")}" +} + +variable "client_cert_path" { + type = string + default = "${env("ARM_CLIENT_CERT_PATH")}" +} + +variable "client_id" { + type = string + default = "${env("ARM_CLIENT_ID")}" +} + +variable "client_secret" { + type = string + default = "${env("ARM_CLIENT_SECRET")}" + sensitive = true +} + +variable "dockerhub_login" { + type = string + default = "${env("DOCKERHUB_LOGIN")}" +} + +variable "dockerhub_password" { + type = string + default = "${env("DOCKERHUB_PASSWORD")}" +} + +variable "helper_script_folder" { + type = string + default = "/imagegeneration/helpers" +} + +variable "image_folder" { + type = string + default = "/imagegeneration" +} + +variable "image_os" { + type = string + default = "ubuntu20" +} + +variable "image_version" { + type = string + default = "dev" +} + +variable "imagedata_file" { + type = string + default = "/imagegeneration/imagedata.json" +} + +variable "installer_script_folder" { + type = string + default = "/imagegeneration/installers" +} + +variable "install_password" { + type = string + default = "" + sensitive = true +} + +variable "location" { + type = string + default = "${env("ARM_RESOURCE_LOCATION")}" +} + +variable "managed_image_name" { + type = string + default = "" +} + +variable "managed_image_resource_group_name" { + type = string + default = "${env("ARM_RESOURCE_GROUP")}" +} + +variable "private_virtual_network_with_public_ip" { + type = bool + default = false +} + +variable "subscription_id" { + type = string + default = "${env("ARM_SUBSCRIPTION_ID")}" +} + +variable "temp_resource_group_name" { + type = string + default = "${env("TEMP_RESOURCE_GROUP_NAME")}" +} + +variable "tenant_id" { + type = string + default = "${env("ARM_TENANT_ID")}" +} + +variable "virtual_network_name" { + type = string + default = "${env("VNET_NAME")}" +} + +variable "virtual_network_resource_group_name" { + type = string + default = "${env("VNET_RESOURCE_GROUP")}" +} + +variable "virtual_network_subnet_name" { + type = string + default = "${env("VNET_SUBNET")}" +} + +variable "vm_size" { + type = string + default = "Standard_D4s_v4" +} + +source "azure-arm" "build_image" { + allowed_inbound_ip_addresses = "${var.allowed_inbound_ip_addresses}" + build_resource_group_name = "${var.build_resource_group_name}" + client_cert_path = "${var.client_cert_path}" + client_id = "${var.client_id}" + client_secret = "${var.client_secret}" + image_offer = "0001-com-ubuntu-server-focal" + image_publisher = "canonical" + image_sku = "20_04-lts" + location = "${var.location}" + managed_image_name = "${local.managed_image_name}" + managed_image_resource_group_name = "${var.managed_image_resource_group_name}" + os_disk_size_gb = "75" + os_type = "Linux" + private_virtual_network_with_public_ip = "${var.private_virtual_network_with_public_ip}" + subscription_id = "${var.subscription_id}" + temp_resource_group_name = "${var.temp_resource_group_name}" + tenant_id = "${var.tenant_id}" + virtual_network_name = "${var.virtual_network_name}" + virtual_network_resource_group_name = "${var.virtual_network_resource_group_name}" + virtual_network_subnet_name = "${var.virtual_network_subnet_name}" + vm_size = "${var.vm_size}" + + dynamic "azure_tag" { + for_each = var.azure_tags + content { + name = azure_tag.key + value = azure_tag.value + } + } +} + +build { + sources = ["source.azure-arm.build_image"] + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = ["mkdir ${var.image_folder}", "chmod 777 ${var.image_folder}"] + } + + provisioner "file" { + destination = "${var.helper_script_folder}" + source = "${path.root}/../scripts/helpers" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + script = "${path.root}/../scripts/build/configure-apt-mock.sh" + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}","DEBIAN_FRONTEND=noninteractive"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = [ + "${path.root}/../scripts/build/install-ms-repos.sh", + "${path.root}/../scripts/build/configure-apt-sources.sh", + "${path.root}/../scripts/build/configure-apt.sh" + ] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + script = "${path.root}/../scripts/build/configure-limits.sh" + } + + provisioner "file" { + destination = "${var.installer_script_folder}" + source = "${path.root}/../scripts/build" + } + + provisioner "file" { + destination = "${var.image_folder}" + sources = [ + "${path.root}/../assets/post-gen", + "${path.root}/../scripts/tests", + "${path.root}/../scripts/docs-gen" + ] + } + + provisioner "file" { + destination = "${var.image_folder}/docs-gen/" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "file" { + destination = "${var.installer_script_folder}/toolset.json" + source = "${path.root}/../toolsets/toolset-2004.json" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "mv ${var.image_folder}/docs-gen ${var.image_folder}/SoftwareReport", + "mv ${var.image_folder}/post-gen ${var.image_folder}/post-generation" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "IMAGEDATA_FILE=${var.imagedata_file}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-image-data.sh"] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "IMAGE_OS=${var.image_os}", "HELPER_SCRIPTS=${var.helper_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-environment.sh"] + } + + provisioner "shell" { + environment_vars = ["DEBIAN_FRONTEND=noninteractive", "HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-apt-vital.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-powershell.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} pwsh -f {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/Install-PowerShellModules.ps1", "${path.root}/../scripts/build/Install-PowerShellAzModules.ps1"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}", "DEBIAN_FRONTEND=noninteractive"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = [ + "${path.root}/../scripts/build/install-actions-cache.sh", + "${path.root}/../scripts/build/install-runner-package.sh", + "${path.root}/../scripts/build/install-apt-common.sh", + "${path.root}/../scripts/build/install-azcopy.sh", + "${path.root}/../scripts/build/install-azure-cli.sh", + "${path.root}/../scripts/build/install-azure-devops-cli.sh", + "${path.root}/../scripts/build/install-bicep.sh", + "${path.root}/../scripts/build/install-aliyun-cli.sh", + "${path.root}/../scripts/build/install-apache.sh", + "${path.root}/../scripts/build/install-aws-tools.sh", + "${path.root}/../scripts/build/install-clang.sh", + "${path.root}/../scripts/build/install-swift.sh", + "${path.root}/../scripts/build/install-cmake.sh", + "${path.root}/../scripts/build/install-codeql-bundle.sh", + "${path.root}/../scripts/build/install-container-tools.sh", + "${path.root}/../scripts/build/install-dotnetcore-sdk.sh", + "${path.root}/../scripts/build/install-erlang.sh", + "${path.root}/../scripts/build/install-firefox.sh", + "${path.root}/../scripts/build/install-microsoft-edge.sh", + "${path.root}/../scripts/build/install-gcc-compilers.sh", + "${path.root}/../scripts/build/install-gfortran.sh", + "${path.root}/../scripts/build/install-git.sh", + "${path.root}/../scripts/build/install-git-lfs.sh", + "${path.root}/../scripts/build/install-github-cli.sh", + "${path.root}/../scripts/build/install-google-chrome.sh", + "${path.root}/../scripts/build/install-google-cloud-cli.sh", + "${path.root}/../scripts/build/install-haskell.sh", + "${path.root}/../scripts/build/install-heroku.sh", + "${path.root}/../scripts/build/install-hhvm.sh", + "${path.root}/../scripts/build/install-java-tools.sh", + "${path.root}/../scripts/build/install-kubernetes-tools.sh", + "${path.root}/../scripts/build/install-oc-cli.sh", + "${path.root}/../scripts/build/install-leiningen.sh", + "${path.root}/../scripts/build/install-miniconda.sh", + "${path.root}/../scripts/build/install-mono.sh", + "${path.root}/../scripts/build/install-kotlin.sh", + "${path.root}/../scripts/build/install-mysql.sh", + "${path.root}/../scripts/build/install-mssql-tools.sh", + "${path.root}/../scripts/build/install-sqlpackage.sh", + "${path.root}/../scripts/build/install-nginx.sh", + "${path.root}/../scripts/build/install-nvm.sh", + "${path.root}/../scripts/build/install-nodejs.sh", + "${path.root}/../scripts/build/install-bazel.sh", + "${path.root}/../scripts/build/install-oras-cli.sh", + "${path.root}/../scripts/build/install-phantomjs.sh", + "${path.root}/../scripts/build/install-php.sh", + "${path.root}/../scripts/build/install-postgresql.sh", + "${path.root}/../scripts/build/install-pulumi.sh", + "${path.root}/../scripts/build/install-ruby.sh", + "${path.root}/../scripts/build/install-rlang.sh", + "${path.root}/../scripts/build/install-rust.sh", + "${path.root}/../scripts/build/install-julia.sh", + "${path.root}/../scripts/build/install-sbt.sh", + "${path.root}/../scripts/build/install-selenium.sh", + "${path.root}/../scripts/build/install-terraform.sh", + "${path.root}/../scripts/build/install-packer.sh", + "${path.root}/../scripts/build/install-vcpkg.sh", + "${path.root}/../scripts/build/configure-dpkg.sh", + "${path.root}/../scripts/build/install-mongodb.sh", + "${path.root}/../scripts/build/install-yq.sh", + "${path.root}/../scripts/build/install-android-sdk.sh", + "${path.root}/../scripts/build/install-pypy.sh", + "${path.root}/../scripts/build/install-python.sh", + "${path.root}/../scripts/build/install-zstd.sh" + ] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}", "DOCKERHUB_LOGIN=${var.dockerhub_login}", "DOCKERHUB_PASSWORD=${var.dockerhub_password}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-docker.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} pwsh -f {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/Install-Toolset.ps1", "${path.root}/../scripts/build/Configure-Toolset.ps1"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-pipx-packages.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "DEBIAN_FRONTEND=noninteractive", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "/bin/sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-homebrew.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-snap.sh"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "sudo reboot"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + pause_before = "1m0s" + scripts = ["${path.root}/../scripts/build/cleanup.sh"] + start_retry_timeout = "10m" + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + inline = [ + "pwsh -Command Write-Host Running Generate-SoftwareReport.ps1 script", + "pwsh -File ${var.image_folder}/SoftwareReport/Generate-SoftwareReport.ps1 -OutputDirectory ${var.image_folder}", + "pwsh -Command Write-Host Running RunAll-Tests.ps1 script", + "pwsh -File ${var.image_folder}/tests/RunAll-Tests.ps1 -OutputDirectory ${var.image_folder}" + ] + max_retries = "3" + start_retry_timeout = "2m" + } + + provisioner "file" { + destination = "${path.root}/../Ubuntu2004-Readme.md" + direction = "download" + source = "${var.image_folder}/software-report.md" + } + + provisioner "file" { + destination = "${path.root}/../software-report.json" + direction = "download" + source = "${var.image_folder}/software-report.json" + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPT_FOLDER=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}", "IMAGE_FOLDER=${var.image_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-system.sh"] + } + + provisioner "file" { + destination = "/tmp/" + source = "${path.root}/../assets/ubuntu2004.conf" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = ["mkdir -p /etc/vsts", "cp /tmp/ubuntu2004.conf /etc/vsts/machine_instance.conf"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = ["sleep 30", "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"] + } + +} diff --git a/images/ubuntu/templates/ubuntu-22.04.pkr.hcl b/images/ubuntu/templates/ubuntu-22.04.pkr.hcl new file mode 100644 index 0000000..6dd0abf --- /dev/null +++ b/images/ubuntu/templates/ubuntu-22.04.pkr.hcl @@ -0,0 +1,421 @@ +packer { + required_plugins { + azure = { + source = "github.com/hashicorp/azure" + version = "1.4.5" + } + } +} + +locals { + managed_image_name = var.managed_image_name != "" ? var.managed_image_name : "packer-${var.image_os}-${var.image_version}" +} + +variable "allowed_inbound_ip_addresses" { + type = list(string) + default = [] +} + +variable "azure_tags" { + type = map(string) + default = {} +} + +variable "build_resource_group_name" { + type = string + default = "${env("BUILD_RESOURCE_GROUP_NAME")}" +} + +variable "client_cert_path" { + type = string + default = "${env("ARM_CLIENT_CERT_PATH")}" +} + +variable "client_id" { + type = string + default = "${env("ARM_CLIENT_ID")}" +} + +variable "client_secret" { + type = string + default = "${env("ARM_CLIENT_SECRET")}" + sensitive = true +} + +variable "dockerhub_login" { + type = string + default = "${env("DOCKERHUB_LOGIN")}" +} + +variable "dockerhub_password" { + type = string + default = "${env("DOCKERHUB_PASSWORD")}" +} + +variable "helper_script_folder" { + type = string + default = "/imagegeneration/helpers" +} + +variable "image_folder" { + type = string + default = "/imagegeneration" +} + +variable "image_os" { + type = string + default = "ubuntu22" +} + +variable "image_version" { + type = string + default = "dev" +} + +variable "imagedata_file" { + type = string + default = "/imagegeneration/imagedata.json" +} + +variable "installer_script_folder" { + type = string + default = "/imagegeneration/installers" +} + +variable "install_password" { + type = string + default = "" + sensitive = true +} + +variable "location" { + type = string + default = "${env("ARM_RESOURCE_LOCATION")}" +} + +variable "managed_image_name" { + type = string + default = "" +} + +variable "managed_image_resource_group_name" { + type = string + default = "${env("ARM_RESOURCE_GROUP")}" +} + +variable "private_virtual_network_with_public_ip" { + type = bool + default = false +} + +variable "subscription_id" { + type = string + default = "${env("ARM_SUBSCRIPTION_ID")}" +} + +variable "temp_resource_group_name" { + type = string + default = "${env("TEMP_RESOURCE_GROUP_NAME")}" +} + +variable "tenant_id" { + type = string + default = "${env("ARM_TENANT_ID")}" +} + +variable "virtual_network_name" { + type = string + default = "${env("VNET_NAME")}" +} + +variable "virtual_network_resource_group_name" { + type = string + default = "${env("VNET_RESOURCE_GROUP")}" +} + +variable "virtual_network_subnet_name" { + type = string + default = "${env("VNET_SUBNET")}" +} + +variable "vm_size" { + type = string + default = "Standard_D4s_v4" +} + +source "azure-arm" "build_image" { + allowed_inbound_ip_addresses = "${var.allowed_inbound_ip_addresses}" + build_resource_group_name = "${var.build_resource_group_name}" + client_cert_path = "${var.client_cert_path}" + client_id = "${var.client_id}" + client_secret = "${var.client_secret}" + image_offer = "0001-com-ubuntu-server-jammy" + image_publisher = "canonical" + image_sku = "22_04-lts" + location = "${var.location}" + managed_image_name = "${local.managed_image_name}" + managed_image_resource_group_name = "${var.managed_image_resource_group_name}" + os_disk_size_gb = "75" + os_type = "Linux" + private_virtual_network_with_public_ip = "${var.private_virtual_network_with_public_ip}" + subscription_id = "${var.subscription_id}" + temp_resource_group_name = "${var.temp_resource_group_name}" + tenant_id = "${var.tenant_id}" + virtual_network_name = "${var.virtual_network_name}" + virtual_network_resource_group_name = "${var.virtual_network_resource_group_name}" + virtual_network_subnet_name = "${var.virtual_network_subnet_name}" + vm_size = "${var.vm_size}" + + dynamic "azure_tag" { + for_each = var.azure_tags + content { + name = azure_tag.key + value = azure_tag.value + } + } +} + +build { + sources = ["source.azure-arm.build_image"] + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = ["mkdir ${var.image_folder}", "chmod 777 ${var.image_folder}"] + } + + provisioner "file" { + destination = "${var.helper_script_folder}" + source = "${path.root}/../scripts/helpers" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + script = "${path.root}/../scripts/build/configure-apt-mock.sh" + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}","DEBIAN_FRONTEND=noninteractive"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = [ + "${path.root}/../scripts/build/install-ms-repos.sh", + "${path.root}/../scripts/build/configure-apt-sources.sh", + "${path.root}/../scripts/build/configure-apt.sh" + ] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + script = "${path.root}/../scripts/build/configure-limits.sh" + } + + provisioner "file" { + destination = "${var.installer_script_folder}" + source = "${path.root}/../scripts/build" + } + + provisioner "file" { + destination = "${var.image_folder}" + sources = [ + "${path.root}/../assets/post-gen", + "${path.root}/../scripts/tests", + "${path.root}/../scripts/docs-gen" + ] + } + + provisioner "file" { + destination = "${var.image_folder}/docs-gen/" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "file" { + destination = "${var.installer_script_folder}/toolset.json" + source = "${path.root}/../toolsets/toolset-2204.json" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "mv ${var.image_folder}/docs-gen ${var.image_folder}/SoftwareReport", + "mv ${var.image_folder}/post-gen ${var.image_folder}/post-generation" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "IMAGEDATA_FILE=${var.imagedata_file}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-image-data.sh"] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "IMAGE_OS=${var.image_os}", "HELPER_SCRIPTS=${var.helper_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-environment.sh"] + } + + provisioner "shell" { + environment_vars = ["DEBIAN_FRONTEND=noninteractive", "HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-apt-vital.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-powershell.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} pwsh -f {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/Install-PowerShellModules.ps1", "${path.root}/../scripts/build/Install-PowerShellAzModules.ps1"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}", "DEBIAN_FRONTEND=noninteractive"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = [ + "${path.root}/../scripts/build/install-actions-cache.sh", + "${path.root}/../scripts/build/install-runner-package.sh", + "${path.root}/../scripts/build/install-apt-common.sh", + "${path.root}/../scripts/build/install-azcopy.sh", + "${path.root}/../scripts/build/install-azure-cli.sh", + "${path.root}/../scripts/build/install-azure-devops-cli.sh", + "${path.root}/../scripts/build/install-bicep.sh", + "${path.root}/../scripts/build/install-aliyun-cli.sh", + "${path.root}/../scripts/build/install-apache.sh", + "${path.root}/../scripts/build/install-aws-tools.sh", + "${path.root}/../scripts/build/install-clang.sh", + "${path.root}/../scripts/build/install-swift.sh", + "${path.root}/../scripts/build/install-cmake.sh", + "${path.root}/../scripts/build/install-codeql-bundle.sh", + "${path.root}/../scripts/build/install-container-tools.sh", + "${path.root}/../scripts/build/install-dotnetcore-sdk.sh", + "${path.root}/../scripts/build/install-firefox.sh", + "${path.root}/../scripts/build/install-microsoft-edge.sh", + "${path.root}/../scripts/build/install-gcc-compilers.sh", + "${path.root}/../scripts/build/install-gfortran.sh", + "${path.root}/../scripts/build/install-git.sh", + "${path.root}/../scripts/build/install-git-lfs.sh", + "${path.root}/../scripts/build/install-github-cli.sh", + "${path.root}/../scripts/build/install-google-chrome.sh", + "${path.root}/../scripts/build/install-google-cloud-cli.sh", + "${path.root}/../scripts/build/install-haskell.sh", + "${path.root}/../scripts/build/install-heroku.sh", + "${path.root}/../scripts/build/install-java-tools.sh", + "${path.root}/../scripts/build/install-kubernetes-tools.sh", + "${path.root}/../scripts/build/install-oc-cli.sh", + "${path.root}/../scripts/build/install-leiningen.sh", + "${path.root}/../scripts/build/install-miniconda.sh", + "${path.root}/../scripts/build/install-mono.sh", + "${path.root}/../scripts/build/install-kotlin.sh", + "${path.root}/../scripts/build/install-mysql.sh", + "${path.root}/../scripts/build/install-mssql-tools.sh", + "${path.root}/../scripts/build/install-sqlpackage.sh", + "${path.root}/../scripts/build/install-nginx.sh", + "${path.root}/../scripts/build/install-nvm.sh", + "${path.root}/../scripts/build/install-nodejs.sh", + "${path.root}/../scripts/build/install-bazel.sh", + "${path.root}/../scripts/build/install-oras-cli.sh", + "${path.root}/../scripts/build/install-php.sh", + "${path.root}/../scripts/build/install-postgresql.sh", + "${path.root}/../scripts/build/install-pulumi.sh", + "${path.root}/../scripts/build/install-ruby.sh", + "${path.root}/../scripts/build/install-rlang.sh", + "${path.root}/../scripts/build/install-rust.sh", + "${path.root}/../scripts/build/install-julia.sh", + "${path.root}/../scripts/build/install-sbt.sh", + "${path.root}/../scripts/build/install-selenium.sh", + "${path.root}/../scripts/build/install-terraform.sh", + "${path.root}/../scripts/build/install-packer.sh", + "${path.root}/../scripts/build/install-vcpkg.sh", + "${path.root}/../scripts/build/configure-dpkg.sh", + "${path.root}/../scripts/build/install-yq.sh", + "${path.root}/../scripts/build/install-android-sdk.sh", + "${path.root}/../scripts/build/install-pypy.sh", + "${path.root}/../scripts/build/install-python.sh", + "${path.root}/../scripts/build/install-zstd.sh" + ] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}", "DOCKERHUB_LOGIN=${var.dockerhub_login}", "DOCKERHUB_PASSWORD=${var.dockerhub_password}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-docker.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} pwsh -f {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/Install-Toolset.ps1", "${path.root}/../scripts/build/Configure-Toolset.ps1"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-pipx-packages.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "DEBIAN_FRONTEND=noninteractive", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "/bin/sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-homebrew.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-snap.sh"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "sudo reboot"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + pause_before = "1m0s" + scripts = ["${path.root}/../scripts/build/cleanup.sh"] + start_retry_timeout = "10m" + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + 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}"] + } + + provisioner "file" { + destination = "${path.root}/../Ubuntu2204-Readme.md" + direction = "download" + source = "${var.image_folder}/software-report.md" + } + + provisioner "file" { + destination = "${path.root}/../software-report.json" + direction = "download" + source = "${var.image_folder}/software-report.json" + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPT_FOLDER=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}", "IMAGE_FOLDER=${var.image_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-system.sh"] + } + + provisioner "file" { + destination = "/tmp/" + source = "${path.root}/../assets/ubuntu2204.conf" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = ["mkdir -p /etc/vsts", "cp /tmp/ubuntu2204.conf /etc/vsts/machine_instance.conf"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = ["sleep 30", "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"] + } + +} diff --git a/images/ubuntu/templates/ubuntu-24.04.pkr.hcl b/images/ubuntu/templates/ubuntu-24.04.pkr.hcl new file mode 100644 index 0000000..634721e --- /dev/null +++ b/images/ubuntu/templates/ubuntu-24.04.pkr.hcl @@ -0,0 +1,398 @@ +packer { + required_plugins { + azure = { + source = "github.com/hashicorp/azure" + version = "1.4.5" + } + } +} + +locals { + managed_image_name = var.managed_image_name != "" ? var.managed_image_name : "packer-${var.image_os}-${var.image_version}" +} + +variable "allowed_inbound_ip_addresses" { + type = list(string) + default = [] +} + +variable "azure_tags" { + type = map(string) + default = {} +} + +variable "build_resource_group_name" { + type = string + default = "${env("BUILD_RESOURCE_GROUP_NAME")}" +} + +variable "client_cert_path" { + type = string + default = "${env("ARM_CLIENT_CERT_PATH")}" +} + +variable "client_id" { + type = string + default = "${env("ARM_CLIENT_ID")}" +} + +variable "client_secret" { + type = string + default = "${env("ARM_CLIENT_SECRET")}" + sensitive = true +} + +variable "dockerhub_login" { + type = string + default = "${env("DOCKERHUB_LOGIN")}" +} + +variable "dockerhub_password" { + type = string + default = "${env("DOCKERHUB_PASSWORD")}" +} + +variable "helper_script_folder" { + type = string + default = "/imagegeneration/helpers" +} + +variable "image_folder" { + type = string + default = "/imagegeneration" +} + +variable "image_os" { + type = string + default = "ubuntu24" +} + +variable "image_version" { + type = string + default = "dev" +} + +variable "imagedata_file" { + type = string + default = "/imagegeneration/imagedata.json" +} + +variable "installer_script_folder" { + type = string + default = "/imagegeneration/installers" +} + +variable "install_password" { + type = string + default = "" + sensitive = true +} + +variable "location" { + type = string + default = "${env("ARM_RESOURCE_LOCATION")}" +} + +variable "managed_image_name" { + type = string + default = "" +} + +variable "managed_image_resource_group_name" { + type = string + default = "${env("ARM_RESOURCE_GROUP")}" +} + +variable "private_virtual_network_with_public_ip" { + type = bool + default = false +} + +variable "subscription_id" { + type = string + default = "${env("ARM_SUBSCRIPTION_ID")}" +} + +variable "temp_resource_group_name" { + type = string + default = "${env("TEMP_RESOURCE_GROUP_NAME")}" +} + +variable "tenant_id" { + type = string + default = "${env("ARM_TENANT_ID")}" +} + +variable "virtual_network_name" { + type = string + default = "${env("VNET_NAME")}" +} + +variable "virtual_network_resource_group_name" { + type = string + default = "${env("VNET_RESOURCE_GROUP")}" +} + +variable "virtual_network_subnet_name" { + type = string + default = "${env("VNET_SUBNET")}" +} + +variable "vm_size" { + type = string + default = "Standard_D4s_v4" +} + +source "azure-arm" "build_image" { + allowed_inbound_ip_addresses = "${var.allowed_inbound_ip_addresses}" + build_resource_group_name = "${var.build_resource_group_name}" + client_cert_path = "${var.client_cert_path}" + client_id = "${var.client_id}" + client_secret = "${var.client_secret}" + image_offer = "ubuntu-24_04-lts" + image_publisher = "canonical" + image_sku = "server-gen1" + location = "${var.location}" + managed_image_name = "${local.managed_image_name}" + managed_image_resource_group_name = "${var.managed_image_resource_group_name}" + os_disk_size_gb = "75" + os_type = "Linux" + private_virtual_network_with_public_ip = "${var.private_virtual_network_with_public_ip}" + subscription_id = "${var.subscription_id}" + temp_resource_group_name = "${var.temp_resource_group_name}" + tenant_id = "${var.tenant_id}" + virtual_network_name = "${var.virtual_network_name}" + virtual_network_resource_group_name = "${var.virtual_network_resource_group_name}" + virtual_network_subnet_name = "${var.virtual_network_subnet_name}" + vm_size = "${var.vm_size}" + + dynamic "azure_tag" { + for_each = var.azure_tags + content { + name = azure_tag.key + value = azure_tag.value + } + } +} + +build { + sources = ["source.azure-arm.build_image"] + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = ["mkdir ${var.image_folder}", "chmod 777 ${var.image_folder}"] + } + + provisioner "file" { + destination = "${var.helper_script_folder}" + source = "${path.root}/../scripts/helpers" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + script = "${path.root}/../scripts/build/configure-apt-mock.sh" + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}","DEBIAN_FRONTEND=noninteractive"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = [ + "${path.root}/../scripts/build/install-ms-repos.sh", + "${path.root}/../scripts/build/configure-apt-sources.sh", + "${path.root}/../scripts/build/configure-apt.sh" + ] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + script = "${path.root}/../scripts/build/configure-limits.sh" + } + + provisioner "file" { + destination = "${var.installer_script_folder}" + source = "${path.root}/../scripts/build" + } + + provisioner "file" { + destination = "${var.image_folder}" + sources = [ + "${path.root}/../assets/post-gen", + "${path.root}/../scripts/tests", + "${path.root}/../scripts/docs-gen" + ] + } + + provisioner "file" { + destination = "${var.image_folder}/docs-gen/" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "file" { + destination = "${var.installer_script_folder}/toolset.json" + source = "${path.root}/../toolsets/toolset-2404.json" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = [ + "mv ${var.image_folder}/docs-gen ${var.image_folder}/SoftwareReport", + "mv ${var.image_folder}/post-gen ${var.image_folder}/post-generation" + ] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "IMAGEDATA_FILE=${var.imagedata_file}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-image-data.sh"] + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "IMAGE_OS=${var.image_os}", "HELPER_SCRIPTS=${var.helper_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-environment.sh"] + } + + provisioner "shell" { + environment_vars = ["DEBIAN_FRONTEND=noninteractive", "HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-apt-vital.sh"] + } + +provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-powershell.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} pwsh -f {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/Install-PowerShellModules.ps1", "${path.root}/../scripts/build/Install-PowerShellAzModules.ps1"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}", "DEBIAN_FRONTEND=noninteractive"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = [ + "${path.root}/../scripts/build/install-actions-cache.sh", + "${path.root}/../scripts/build/install-runner-package.sh", + "${path.root}/../scripts/build/install-apt-common.sh", + "${path.root}/../scripts/build/install-azcopy.sh", + "${path.root}/../scripts/build/install-azure-cli.sh", + "${path.root}/../scripts/build/install-azure-devops-cli.sh", + "${path.root}/../scripts/build/install-bicep.sh", + "${path.root}/../scripts/build/install-apache.sh", + "${path.root}/../scripts/build/install-aws-tools.sh", + "${path.root}/../scripts/build/install-clang.sh", + "${path.root}/../scripts/build/install-swift.sh", + "${path.root}/../scripts/build/install-cmake.sh", + "${path.root}/../scripts/build/install-codeql-bundle.sh", + "${path.root}/../scripts/build/install-container-tools.sh", + "${path.root}/../scripts/build/install-dotnetcore-sdk.sh", + "${path.root}/../scripts/build/install-microsoft-edge.sh", + "${path.root}/../scripts/build/install-gcc-compilers.sh", + "${path.root}/../scripts/build/install-firefox.sh", + "${path.root}/../scripts/build/install-gfortran.sh", + "${path.root}/../scripts/build/install-git.sh", + "${path.root}/../scripts/build/install-git-lfs.sh", + "${path.root}/../scripts/build/install-github-cli.sh", + "${path.root}/../scripts/build/install-google-chrome.sh", + "${path.root}/../scripts/build/install-haskell.sh", + "${path.root}/../scripts/build/install-java-tools.sh", + "${path.root}/../scripts/build/install-kubernetes-tools.sh", + "${path.root}/../scripts/build/install-miniconda.sh", + "${path.root}/../scripts/build/install-kotlin.sh", + "${path.root}/../scripts/build/install-mysql.sh", + "${path.root}/../scripts/build/install-nginx.sh", + "${path.root}/../scripts/build/install-nodejs.sh", + "${path.root}/../scripts/build/install-bazel.sh", + "${path.root}/../scripts/build/install-php.sh", + "${path.root}/../scripts/build/install-postgresql.sh", + "${path.root}/../scripts/build/install-pulumi.sh", + "${path.root}/../scripts/build/install-ruby.sh", + "${path.root}/../scripts/build/install-rust.sh", + "${path.root}/../scripts/build/install-julia.sh", + "${path.root}/../scripts/build/install-selenium.sh", + "${path.root}/../scripts/build/install-packer.sh", + "${path.root}/../scripts/build/install-vcpkg.sh", + "${path.root}/../scripts/build/configure-dpkg.sh", + "${path.root}/../scripts/build/install-yq.sh", + "${path.root}/../scripts/build/install-android-sdk.sh", + "${path.root}/../scripts/build/install-pypy.sh", + "${path.root}/../scripts/build/install-python.sh", + "${path.root}/../scripts/build/install-zstd.sh" + ] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}", "DOCKERHUB_PULL_IMAGES=NO"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-docker.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} pwsh -f {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/Install-Toolset.ps1", "${path.root}/../scripts/build/Configure-Toolset.ps1"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-pipx-packages.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}", "DEBIAN_FRONTEND=noninteractive", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + execute_command = "/bin/sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-homebrew.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${var.helper_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-snap.sh"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "sudo reboot"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + pause_before = "1m0s" + scripts = ["${path.root}/../scripts/build/cleanup.sh"] + start_retry_timeout = "10m" + } + + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}"] + 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}"] + } + + provisioner "file" { + destination = "${path.root}/../Ubuntu2404-Readme.md" + direction = "download" + source = "${var.image_folder}/software-report.md" + } + + provisioner "file" { + destination = "${path.root}/../software-report.json" + direction = "download" + source = "${var.image_folder}/software-report.json" + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPT_FOLDER=${var.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${var.installer_script_folder}", "IMAGE_FOLDER=${var.image_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-system.sh"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = ["sleep 30", "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"] + } + +} diff --git a/images/ubuntu/templates/ubuntu-minimal.pkr.hcl b/images/ubuntu/templates/ubuntu-minimal.pkr.hcl new file mode 100644 index 0000000..634e76d --- /dev/null +++ b/images/ubuntu/templates/ubuntu-minimal.pkr.hcl @@ -0,0 +1,290 @@ +packer { + required_plugins { + azure = { + source = "github.com/hashicorp/azure" + version = "1.4.5" + } + } +} + +locals { + image_os = "ubuntu22" + + toolset_file_name = "toolset-2204.json" + + image_folder = "/imagegeneration" + helper_script_folder = "/imagegeneration/helpers" + installer_script_folder = "/imagegeneration/installers" + imagedata_file = "/imagegeneration/imagedata.json" + + managed_image_name = var.managed_image_name != "" ? var.managed_image_name : "packer-${var.image_os}-${var.image_version}" +} + +variable "allowed_inbound_ip_addresses" { + type = list(string) + default = [] +} + +variable "azure_tags" { + type = map(string) + default = {} +} + +variable "build_resource_group_name" { + type = string + default = "${env("BUILD_RESOURCE_GROUP_NAME")}" +} + +variable "client_cert_path" { + type = string + default = "${env("ARM_CLIENT_CERT_PATH")}" +} + +variable "client_id" { + type = string + default = "${env("ARM_CLIENT_ID")}" +} + +variable "client_secret" { + type = string + default = "${env("ARM_CLIENT_SECRET")}" + sensitive = true +} + +variable "image_version" { + type = string + default = "dev" +} + +variable "install_password" { + type = string + default = "" + sensitive = true +} + +variable "location" { + type = string + default = "${env("ARM_RESOURCE_LOCATION")}" +} + +variable "managed_image_name" { + type = string + default = "" +} + +variable "managed_image_resource_group_name" { + type = string + default = "${env("ARM_RESOURCE_GROUP")}" +} + +variable "private_virtual_network_with_public_ip" { + type = bool + default = false +} + +variable "subscription_id" { + type = string + default = "${env("ARM_SUBSCRIPTION_ID")}" +} + +variable "temp_resource_group_name" { + type = string + default = "${env("TEMP_RESOURCE_GROUP_NAME")}" +} + +variable "tenant_id" { + type = string + default = "${env("ARM_TENANT_ID")}" +} + +variable "virtual_network_name" { + type = string + default = "${env("VNET_NAME")}" +} + +variable "virtual_network_resource_group_name" { + type = string + default = "${env("VNET_RESOURCE_GROUP")}" +} + +variable "virtual_network_subnet_name" { + type = string + default = "${env("VNET_SUBNET")}" +} + +variable "vm_size" { + type = string + default = "Standard_D4s_v4" +} + +source "azure-arm" "build_image" { + location = "${var.location}" + + // Auth + tenant_id = "${var.tenant_id}" + subscription_id = "${var.subscription_id}" + client_id = "${var.client_id}" + client_secret = "${var.client_secret}" + client_cert_path = "${var.client_cert_path}" + + // Base image + image_offer = "0001-com-ubuntu-server-jammy" + image_publisher = "canonical" + image_sku = "22_04-lts" + + // Target location + managed_image_name = "${local.managed_image_name}" + managed_image_resource_group_name = "${var.managed_image_resource_group_name}" + + // Resource group for VM + build_resource_group_name = "${var.build_resource_group_name}" + temp_resource_group_name = "${var.temp_resource_group_name}" + + // Networking for VM + private_virtual_network_with_public_ip = "${var.private_virtual_network_with_public_ip}" + virtual_network_resource_group_name = "${var.virtual_network_resource_group_name}" + virtual_network_name = "${var.virtual_network_name}" + virtual_network_subnet_name = "${var.virtual_network_subnet_name}" + allowed_inbound_ip_addresses = "${var.allowed_inbound_ip_addresses}" + + // VM Configuration + vm_size = "${var.vm_size}" + os_disk_size_gb = "75" + os_type = "Linux" + + dynamic "azure_tag" { + for_each = var.azure_tags + content { + name = azure_tag.key + value = azure_tag.value + } + } +} + +build { + sources = ["source.azure-arm.build_image"] + + // Create folder to store temporary data + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = ["mkdir ${local.image_folder}", "chmod 777 ${local.image_folder}"] + } + + provisioner "file" { + destination = "${local.helper_script_folder}" + source = "${path.root}/../scripts/helpers" + } + + // Add apt wrapper to implement retries + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + script = "${path.root}/../scripts/build/configure-apt-mock.sh" + } + + // Install MS package repos, Configure apt + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${local.helper_script_folder}","DEBIAN_FRONTEND=noninteractive"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = [ + "${path.root}/../scripts/build/install-ms-repos.sh", + "${path.root}/../scripts/build/configure-apt.sh" + ] + } + + // Configure limits + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + script = "${path.root}/../scripts/build/configure-limits.sh" + } + + provisioner "file" { + destination = "${local.installer_script_folder}" + source = "${path.root}/../scripts/build" + } + + provisioner "file" { + destination = "${local.image_folder}" + sources = [ + "${path.root}/../assets/post-gen", + "${path.root}/../scripts/tests" + ] + } + + provisioner "file" { + destination = "${local.installer_script_folder}/toolset.json" + source = "${path.root}/../toolsets/${local.toolset_file_name}" + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = ["mv ${local.image_folder}/post-gen ${local.image_folder}/post-generation"] + } + + // Generate image data file + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "IMAGEDATA_FILE=${local.imagedata_file}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-image-data.sh"] + } + + // Create /etc/environment, configure waagent etc. + provisioner "shell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "IMAGE_OS=${local.image_os}", "HELPER_SCRIPTS=${local.helper_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-environment.sh"] + } + + provisioner "shell" { + environment_vars = ["DEBIAN_FRONTEND=noninteractive", "HELPER_SCRIPTS=${local.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${local.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-apt-vital.sh"] + } + + provisioner "shell" { + environment_vars = ["DEBIAN_FRONTEND=noninteractive", "HELPER_SCRIPTS=${local.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${local.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/install-powershell.sh"] + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPTS=${local.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${local.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} pwsh -f {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/Install-PowerShellModules.ps1"] + } + + provisioner "shell" { + environment_vars = ["DEBIAN_FRONTEND=noninteractive", "HELPER_SCRIPTS=${local.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${local.installer_script_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = [ + "${path.root}/../scripts/build/install-git.sh", + "${path.root}/../scripts/build/install-git-lfs.sh", + "${path.root}/../scripts/build/install-github-cli.sh", + "${path.root}/../scripts/build/install-zstd.sh" + ] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + expect_disconnect = true + inline = ["echo 'Reboot VM'", "sudo reboot"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + pause_before = "1m0s" + scripts = ["${path.root}/../scripts/build/cleanup.sh"] + start_retry_timeout = "10m" + } + + provisioner "shell" { + environment_vars = ["HELPER_SCRIPT_FOLDER=${local.helper_script_folder}", "INSTALLER_SCRIPT_FOLDER=${local.installer_script_folder}", "IMAGE_FOLDER=${local.image_folder}"] + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + scripts = ["${path.root}/../scripts/build/configure-system.sh"] + } + + provisioner "shell" { + execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'" + inline = ["sleep 30", "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"] + } + +} diff --git a/images/ubuntu/toolsets/toolset-2004.json b/images/ubuntu/toolsets/toolset-2004.json new file mode 100644 index 0000000..4f25ed1 --- /dev/null +++ b/images/ubuntu/toolsets/toolset-2004.json @@ -0,0 +1,388 @@ +{ + "toolcache": [ + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "platform" : "linux", + "platform_version": "20.04", + "arch": "x64", + "versions": [ + "3.7.*", + "3.8.*", + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*" + ] + }, + { + "name": "PyPy", + "arch": "x64", + "platform" : "linux", + "versions": [ + "2.7", + "3.6", + "3.7", + "3.8", + "3.9", + "3.10" + ] + }, + { + "name": "node", + "url" : "https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json", + "platform" : "linux", + "arch": "x64", + "versions": [ + "16.*", + "18.*", + "20.*" + ] + }, + { + "name": "go", + "url" : "https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "linux", + "versions": [ + "1.21.*", + "1.22.*", + "1.23.*" + ], + "default": "1.23.*" + }, + { + "name": "Ruby", + "platform_version": "20.04", + "arch": "x64", + "versions": [ + "3.0.*", + "3.1.*", + "3.2.*" + ] + }, + { + "name": "CodeQL", + "platform" : "linux", + "arch": "x64", + "versions": [ + "*" + ] + } + ], + "java": { + "default": "11", + "versions": [ "8", "11", "17", "21"], + "maven": "3.8.8" + }, + "android": { + "cmdline-tools": "commandlinetools-linux-9477386_latest.zip", + "platform_min_version": "31", + "build_tools_min_version": "31.0.0", + "extra_list": [ + "android;m2repository", + "google;m2repository", + "google;google_play_services" + ], + "addon_list": [ + ], + "additional_tools": [ + "cmake;3.10.2.4988404", + "cmake;3.18.1", + "cmake;3.22.1" + ], + "ndk": { + "default": "27", + "versions": [ + "26", "27" + ] + } + }, + "powershellModules": [ + {"name": "MarkdownPS"}, + {"name": "Microsoft.Graph"}, + {"name": "Pester"}, + {"name": "PSScriptAnalyzer"} + ], + "azureModules": [ + { + "name": "az", + "versions": [ + "12.1.0" + ] + } + ], + "apt": { + "vital_packages": [ + "bzip2", + "curl", + "g++", + "gcc", + "make", + "jq", + "tar", + "unzip", + "wget" + ], + "common_packages": [ + "autoconf", + "automake", + "dbus", + "dnsutils", + "dpkg", + "dpkg-dev", + "fakeroot", + "fonts-noto-color-emoji", + "gnupg2", + "imagemagick", + "iproute2", + "iputils-ping", + "lib32z1", + "libc++abi-dev", + "libc++-dev", + "libc6-dev", + "libcurl4", + "libgbm-dev", + "libgconf-2-4", + "libgsl-dev", + "libgtk-3-0", + "libmagic-dev", + "libmagickcore-dev", + "libmagickwand-dev", + "libsecret-1-dev", + "libsqlite3-dev", + "libyaml-dev", + "libtool", + "libunwind8", + "libxkbfile-dev", + "libxss1", + "locales", + "mercurial", + "openssh-client", + "p7zip-rar", + "pkg-config", + "python-is-python3", + "rpm", + "texinfo", + "tk", + "tzdata", + "upx", + "xorriso", + "xvfb", + "xz-utils", + "zsync" + ], + "cmd_packages": [ + "acl", + "aria2", + "binutils", + "bison", + "brotli", + "coreutils", + "file", + "findutils", + "flex", + "ftp", + "haveged", + "m4", + "mediainfo", + "netcat", + "net-tools", + "p7zip-full", + "parallel", + "pass", + "patchelf", + "pigz", + "pollinate", + "rsync", + "shellcheck", + "sphinxsearch", + "sqlite3", + "ssh", + "sshpass", + "subversion", + "sudo", + "swig", + "telnet", + "time", + "zip" + ] + }, + "brew": [ + ], + "docker": { + "images": [ + "alpine:3.16", + "alpine:3.17", + "alpine:3.18", + "alpine:3.19", + "debian:9", + "debian:10", + "debian:11", + "moby/buildkit:latest", + "node:16", + "node:18", + "node:20", + "node:16-alpine", + "node:18-alpine", + "node:20-alpine", + "ubuntu:20.04" + ], + "components": [ + { + "package": "containerd.io", + "version": "latest" + }, + { + "package": "docker-ce-cli", + "version": "26.1.3" + }, + { + "package": "docker-ce", + "version": "26.1.3" + } + ], + "plugins": [ + { + "plugin": "buildx", + "version": "latest", + "asset": "linux-amd64" + }, + { + "plugin": "compose", + "version": "2.27.1", + "asset": "linux-x86_64" + } + ] + }, + "pipx": [ + { + "package": "yamllint", + "cmd": "yamllint" + }, + { + "package": "ansible-core", + "cmd": "ansible" + } + ], + "dotnet": { + "aptPackages": [ + "dotnet-sdk-6.0", + "dotnet-sdk-7.0", + "dotnet-sdk-8.0" + ], + "versions": [ + "6.0", + "7.0", + "8.0" + ], + "tools": [ + { "name": "nbgv", "test": "nbgv --version", "getversion" : "nbgv --version" } + ] + }, + "clang": { + "versions": [ + "10", + "11", + "12" + ], + "default_version": "11" + }, + "gcc": { + "versions": [ + "g++-9", + "g++-10" + ] + }, + "gfortran": { + "versions": [ + "gfortran-9", + "gfortran-10" + ] + }, + "php": { + "versions": [ + "7.4", + "8.0", + "8.1", + "8.2", + "8.3" + ] + }, + "rubygems": [ + {"name": "fastlane"} + ], + "selenium": { + "version": "4" + }, + "node": { + "default": "18" + }, + "node_modules": [ + { + "name": "grunt", + "command": "grunt" + }, + { + "name": "gulp", + "command": "gulp" + }, + { + "name": "n", + "command": "n" + }, + { + "name": "parcel", + "command": "parcel" + }, + { + "name": "typescript", + "command": "tsc" + }, + { + "name": "newman", + "command": "newman" + }, + { + "name": "vercel", + "command": "vercel" + }, + { + "name": "webpack", + "command": "webpack" + }, + { + "name": "webpack-cli", + "command": "webpack-cli" + }, + { + "name": "netlify-cli", + "command": "netlify" + }, + { + "name": "lerna", + "command": "lerna" + }, + { + "name": "yarn", + "command": "yarn" + } + ], + "mongodb": { + "version": "5.0" + }, + "postgresql": { + "version": "14" + }, + "pwsh": { + "version": "7.4" + }, + "aliyunCli": { + "version": "3.0.174", + "sha256": "0c51028a7a32fc02c8de855f73e273556f957115eb5624565738f9b9f83a50ba" + }, + "ocCli": { + "version": "4.15.19" + } + +} diff --git a/images/ubuntu/toolsets/toolset-2204.json b/images/ubuntu/toolsets/toolset-2204.json new file mode 100644 index 0000000..2b07466 --- /dev/null +++ b/images/ubuntu/toolsets/toolset-2204.json @@ -0,0 +1,377 @@ +{ + "toolcache": [ + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "platform" : "linux", + "platform_version": "22.04", + "arch": "x64", + "versions": [ + "3.7.*", + "3.8.*", + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*" + ] + }, + { + "name": "PyPy", + "arch": "x64", + "platform" : "linux", + "versions": [ + "3.7", + "3.8", + "3.9", + "3.10" + ] + }, + { + "name": "node", + "url" : "https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json", + "platform" : "linux", + "arch": "x64", + "versions": [ + "16.*", + "18.*", + "20.*" + ] + }, + { + "name": "go", + "url" : "https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "linux", + "versions": [ + "1.21.*", + "1.22.*", + "1.23.*" + ], + "default": "1.23.*" + }, + { + "name": "Ruby", + "platform_version": "22.04", + "arch": "x64", + "versions": [ + "3.1.*", + "3.2.*" + ] + }, + { + "name": "CodeQL", + "platform" : "linux", + "arch": "x64", + "versions": [ + "*" + ] + } + ], + "java": { + "default": "11", + "versions": [ "8", "11", "17", "21"], + "maven": "3.8.8" + }, + "android": { + "cmdline-tools": "commandlinetools-linux-9477386_latest.zip", + "platform_min_version": "31", + "build_tools_min_version": "31.0.0", + "extra_list": [ + "android;m2repository", + "google;m2repository", + "google;google_play_services" + ], + "addon_list": [ + ], + "additional_tools": [ + "cmake;3.10.2.4988404", + "cmake;3.18.1", + "cmake;3.22.1" + ], + "ndk": { + "default": "27", + "versions": [ + "26", "27" + ] + } + }, + "powershellModules": [ + {"name": "MarkdownPS"}, + {"name": "Microsoft.Graph"}, + {"name": "Pester"}, + {"name": "PSScriptAnalyzer"} + ], + "azureModules": [ + { + "name": "az", + "versions": [ + "12.1.0" + ] + } + ], + "apt": { + "vital_packages": [ + "bzip2", + "curl", + "g++", + "gcc", + "make", + "jq", + "tar", + "unzip", + "wget" + ], + "common_packages": [ + "autoconf", + "automake", + "dbus", + "dnsutils", + "dpkg", + "dpkg-dev", + "fakeroot", + "fonts-noto-color-emoji", + "gnupg2", + "imagemagick", + "iproute2", + "iputils-ping", + "lib32z1", + "libc++abi-dev", + "libc++-dev", + "libc6-dev", + "libcurl4", + "libgbm-dev", + "libgconf-2-4", + "libgsl-dev", + "libgtk-3-0", + "libmagic-dev", + "libmagickcore-dev", + "libmagickwand-dev", + "libsecret-1-dev", + "libsqlite3-dev", + "libyaml-dev", + "libtool", + "libunwind8", + "libxkbfile-dev", + "libxss1", + "libssl-dev", + "locales", + "mercurial", + "openssh-client", + "p7zip-rar", + "pkg-config", + "python-is-python3", + "rpm", + "texinfo", + "tk", + "tzdata", + "upx", + "xorriso", + "xvfb", + "xz-utils", + "zsync" + ], + "cmd_packages": [ + "acl", + "aria2", + "binutils", + "bison", + "brotli", + "coreutils", + "file", + "findutils", + "flex", + "ftp", + "haveged", + "lz4", + "m4", + "mediainfo", + "netcat", + "net-tools", + "p7zip-full", + "parallel", + "pass", + "patchelf", + "pigz", + "pollinate", + "rsync", + "shellcheck", + "sphinxsearch", + "sqlite3", + "ssh", + "sshpass", + "subversion", + "sudo", + "swig", + "telnet", + "time", + "zip" + ] + }, + "brew": [ + ], + "docker": { + "images": [ + "alpine:3.16", + "alpine:3.17", + "alpine:3.18", + "alpine:3.19", + "debian:10", + "debian:11", + "moby/buildkit:latest", + "node:16", + "node:18", + "node:20", + "node:16-alpine", + "node:18-alpine", + "node:20-alpine", + "ubuntu:20.04", + "ubuntu:22.04" + ], + "components": [ + { + "package": "containerd.io", + "version": "latest" + }, + { + "package": "docker-ce-cli", + "version": "26.1.3" + }, + { + "package": "docker-ce", + "version": "26.1.3" + } + ], + "plugins": [ + { + "plugin": "buildx", + "version": "latest", + "asset": "linux-amd64" + }, + { + "plugin": "compose", + "version": "2.27.1", + "asset": "linux-x86_64" + } + ] + }, + "pipx": [ + { + "package": "yamllint", + "cmd": "yamllint" + }, + { + "package": "ansible-core", + "cmd": "ansible" + } + ], + "dotnet": { + "aptPackages": [ + "dotnet-sdk-6.0", + "dotnet-sdk-7.0", + "dotnet-sdk-8.0" + ], + "versions": [ + "6.0", + "7.0", + "8.0" + ], + "tools": [ + { "name": "nbgv", "test": "nbgv --version", "getversion" : "nbgv --version" } + ] + }, + "clang": { + "versions": [ + "13", + "14", + "15" + ], + "default_version": "14" + }, + "gcc": { + "versions": [ + "g++-9", + "g++-10", + "g++-12" + ] + }, + "gfortran": { + "versions": [ + "gfortran-9", + "gfortran-10", + "gfortran-12" + ] + }, + "php": { + "versions": [ + "8.1" + ] + }, + "rubygems": [ + {"name": "fastlane"} + ], + "selenium": { + "version": "4" + }, + "node": { + "default": "18" + }, + "node_modules": [ + { + "name": "grunt", + "command": "grunt" + }, + { + "name": "gulp", + "command": "gulp" + }, + { + "name": "n", + "command": "n" + }, + { + "name": "parcel", + "command": "parcel" + }, + { + "name": "typescript", + "command": "tsc" + }, + { + "name": "newman", + "command": "newman" + }, + { + "name": "vercel", + "command": "vercel" + }, + { + "name": "webpack", + "command": "webpack" + }, + { + "name": "webpack-cli", + "command": "webpack-cli" + }, + { + "name": "netlify-cli", + "command": "netlify" + }, + { + "name": "lerna", + "command": "lerna" + }, + { + "name": "yarn", + "command": "yarn" + } + ], + "mongodb": { + "version": "5.0" + }, + "postgresql": { + "version": "14" + }, + "pwsh": { + "version": "7.4" + } +} diff --git a/images/ubuntu/toolsets/toolset-2404.json b/images/ubuntu/toolsets/toolset-2404.json new file mode 100644 index 0000000..683a243 --- /dev/null +++ b/images/ubuntu/toolsets/toolset-2404.json @@ -0,0 +1,319 @@ +{ + "toolcache": [ + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "platform" : "linux", + "platform_version": "24.04", + "arch": "x64", + "versions": [ + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*" + ] + }, + { + "name": "PyPy", + "arch": "x64", + "platform" : "linux", + "versions": [ + "3.9", + "3.10" + ] + }, + { + "name": "node", + "url" : "https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json", + "platform" : "linux", + "arch": "x64", + "versions": [ + "16.*", + "18.*", + "20.*" + ] + }, + { + "name": "go", + "url" : "https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "linux", + "versions": [ + "1.21.*", + "1.22.*", + "1.23.*" + ], + "default": "1.23.*" + }, + { + "name": "Ruby", + "platform_version": "24.04", + "arch": "x64", + "versions": [] + }, + { + "name": "CodeQL", + "platform" : "linux", + "arch": "x64", + "versions": [ + "*" + ] + } + ], + "java": { + "default": "17", + "versions": [ "8", "11", "17", "21"], + "maven": "3.8.8" + }, + "android": { + "cmdline-tools": "commandlinetools-linux-11076708_latest.zip", + "platform_min_version": "34", + "build_tools_min_version": "34.0.0", + "extra_list": [ + "android;m2repository", + "google;m2repository", + "google;google_play_services" + ], + "addon_list": [ + ], + "additional_tools": [ + ], + "ndk": { + "default": "27", + "versions": [ + "26", "27" + ] + } + }, + "powershellModules": [ + {"name": "Microsoft.Graph"}, + {"name": "Pester"}, + {"name": "PSScriptAnalyzer"} + ], + "azureModules": [ + { + "name": "az", + "versions": [ + "12.1.0" + ] + } + ], + "apt": { + "vital_packages": [ + "bzip2", + "curl", + "g++", + "gcc", + "make", + "jq", + "tar", + "unzip", + "wget" + ], + "common_packages": [ + "autoconf", + "automake", + "dbus", + "dnsutils", + "dpkg", + "dpkg-dev", + "fakeroot", + "fonts-noto-color-emoji", + "gnupg2", + "iproute2", + "iputils-ping", + "libyaml-dev", + "libtool", + "libssl-dev", + "locales", + "mercurial", + "openssh-client", + "p7zip-rar", + "pkg-config", + "python-is-python3", + "rpm", + "texinfo", + "tk", + "tree", + "tzdata", + "upx", + "xvfb", + "xz-utils", + "zsync" + ], + "cmd_packages": [ + "acl", + "aria2", + "binutils", + "bison", + "brotli", + "coreutils", + "file", + "findutils", + "flex", + "ftp", + "haveged", + "lz4", + "m4", + "mediainfo", + "netcat", + "net-tools", + "p7zip-full", + "parallel", + "patchelf", + "pigz", + "pollinate", + "rsync", + "shellcheck", + "sphinxsearch", + "sqlite3", + "ssh", + "sshpass", + "sudo", + "swig", + "telnet", + "time", + "zip" + ] + }, + "brew": [ + ], + "docker": { + "images": [ + ], + "components": [ + { + "package": "containerd.io", + "version": "latest" + }, + { + "package": "docker-ce-cli", + "version": "26.1.3" + }, + { + "package": "docker-ce", + "version": "26.1.3" + } + ], + "plugins": [ + { + "plugin": "buildx", + "version": "latest", + "asset": "linux-amd64" + }, + { + "plugin": "compose", + "version": "2.27.1", + "asset": "linux-x86_64" + } + ] + }, + "pipx": [ + { + "package": "yamllint", + "cmd": "yamllint" + }, + { + "package": "ansible-core", + "cmd": "ansible" + } + ], + "dotnet": { + "aptPackages": [ + "dotnet-sdk-8.0" + ], + "versions": [ + "8.0" + ], + "tools": [ + { "name": "nbgv", "test": "nbgv --version", "getversion" : "nbgv --version" } + ] + }, + "clang": { + "versions": [ + "16", + "17", + "18" + ], + "default_version": "18" + }, + "gcc": { + "versions": [ + "g++-12", + "g++-13", + "g++-14" + ] + }, + "gfortran": { + "versions": [ + "gfortran-12", + "gfortran-13", + "gfortran-14" + ] + }, + "php": { + "versions": [ + "8.3" + ] + }, + "rubygems": [ + {"name": "fastlane"} + ], + "selenium": { + "version": "4" + }, + "node": { + "default": "20" + }, + "node_modules": [ + { + "name": "grunt", + "command": "grunt" + }, + { + "name": "gulp", + "command": "gulp" + }, + { + "name": "n", + "command": "n" + }, + { + "name": "parcel", + "command": "parcel" + }, + { + "name": "typescript", + "command": "tsc" + }, + { + "name": "newman", + "command": "newman" + }, + { + "name": "webpack", + "command": "webpack" + }, + { + "name": "webpack-cli", + "command": "webpack-cli" + }, + { + "name": "lerna", + "command": "lerna" + }, + { + "name": "yarn", + "command": "yarn" + } + ], + "mongodb": { + "version": "7.0" + }, + "postgresql": { + "version": "16" + }, + "pwsh": { + "version": "7.4" + } +} diff --git a/images/windows/Windows2019-Readme.md b/images/windows/Windows2019-Readme.md new file mode 100644 index 0000000..102cc03 --- /dev/null +++ b/images/windows/Windows2019-Readme.md @@ -0,0 +1,554 @@ +# Windows Server 2019 +- OS Version: 10.0.17763 Build 6293 +- Image Version: 20241006.1.0 + +## Windows features +- Windows Subsystem for Linux (WSLv1): Enabled + +## Installed Software + +### Language and Runtime +- Bash 5.2.37(1)-release +- Go 1.21.13 +- Julia 1.10.5 +- Kotlin 2.0.20 +- LLVM 18.1.8 +- Node 18.20.3 +- Perl 5.32.1 +- PHP 8.3.12 +- Python 3.7.9 +- Ruby 3.0.7p220 + +### Package Management +- Chocolatey 2.3.0 +- Composer 2.8.1 +- Helm 3.16.0 +- Miniconda 24.7.1 (pre-installed on the image but not added to PATH) +- NPM 10.7.0 +- NuGet 6.11.1.2 +- pip 24.0 (python 3.7) +- Pipx 1.2.1 +- RubyGems 3.2.33 +- Vcpkg (build from commit 7adc2e4d4) +- Yarn 1.22.22 + +#### Environment variables +| Name | Value | +| ----------------------- | ------------ | +| VCPKG_INSTALLATION_ROOT | C:\vcpkg | +| CONDA | C:\Miniconda | + +### Project Management +- Ant 1.10.14 +- Gradle 8.10 +- Maven 3.8.7 +- sbt 1.10.2 + +### Tools +- 7zip 24.08 +- aria2 1.37.0 +- azcopy 10.26.0 +- Bazel 7.3.2 +- Bazelisk 1.22.0 +- Bicep 0.30.23 +- Cabal 3.12.1.0 +- CMake 3.30.4 +- CodeQL Action Bundle 2.19.0 +- Docker 26.1.3 +- Docker Compose v2 2.27.1 +- Docker-wincred 0.8.2 +- ghc 9.10.1 +- Git 2.46.2.windows.1 +- Git LFS 3.5.1 +- Google Cloud CLI 495.0.0 +- ImageMagick 7.1.1-38 +- InnoSetup 6.3.3 +- jq 1.7.1 +- Kind 0.24.0 +- Kubectl 1.31.1 +- Mercurial 5.0 +- gcc 8.1.0 +- gdb 8.1 +- GNU Binutils 2.30 +- Newman 6.2.1 +- NSIS 3.10 +- OpenSSL 1.1.1w +- Packer 1.11.2 +- Parcel 2.12.0 +- Pulumi 3.135.1 +- R 4.4.1 +- Service Fabric SDK 9.1.1436.9590 +- Stack 3.1.1 +- Subversion (SVN) 1.14.2 +- Swig 4.1.1 +- VSWhere 3.1.7 +- WinAppDriver 1.2.2009.02003 +- WiX Toolset 3.14.1.8722 +- yamllint 1.32.0 +- zstd 1.5.6 + +### CLI Tools +- Alibaba Cloud CLI 3.0.224 +- AWS CLI 2.18.0 +- AWS SAM CLI 1.125.0 +- AWS Session Manager CLI 1.2.650.0 +- Azure CLI 2.64.0 +- Azure DevOps CLI extension 1.0.1 +- Cloud Foundry CLI 8.8.2 +- GitHub CLI 2.58.0 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- bindgen 0.70.1 +- cargo-audit 0.20.1 +- cargo-outdated 0.15.0 +- cbindgen 0.27.0 +- Clippy 0.1.81 +- Rustfmt 1.7.1 + +### Browsers and Drivers +- Google Chrome 129.0.6668.90 +- Chrome Driver 129.0.6668.89 +- Microsoft Edge 129.0.2792.79 +- Microsoft Edge Driver 129.0.2792.79 +- Mozilla Firefox 131.0 +- Gecko Driver 0.35.0 +- IE Driver 4.14.0.0 +- Selenium server 4.25.0 + +#### Environment variables +| Name | Value | +| ----------------- | ---------------------------------- | +| CHROMEWEBDRIVER | C:\SeleniumWebDrivers\ChromeDriver | +| EDGEWEBDRIVER | C:\SeleniumWebDrivers\EdgeDriver | +| GECKOWEBDRIVER | C:\SeleniumWebDrivers\GeckoDriver | +| SELENIUM_JAR_PATH | C:\selenium\selenium-server.jar | + +### Java +| Version | Environment Variable | +| ------------------- | -------------------- | +| 8.0.422+5 (default) | JAVA_HOME_8_X64 | +| 11.0.24+8 | JAVA_HOME_11_X64 | +| 17.0.12+7 | JAVA_HOME_17_X64 | +| 21.0.4+7.0 | JAVA_HOME_21_X64 | + +### Shells +| Name | Target | +| ------------- | --------------------------------- | +| gitbash.exe | C:\Program Files\Git\bin\bash.exe | +| msys2bash.cmd | C:\msys64\usr\bin\bash.exe | +| wslbash.exe | C:\Windows\System32\bash.exe | + +### MSYS2 +- Pacman 6.1.0 + +#### Notes +``` +Location: C:\msys64 + +Note: MSYS2 is pre-installed on image but not added to PATH. +``` + +### BizTalk Server +- BizTalk Server Project Build Component 3.13.832.0 + +### Cached Tools + +#### Go +- 1.20.14 +- 1.21.13 +- 1.22.8 + +#### Node.js +- 16.20.2 +- 18.20.4 +- 20.17.0 + +#### Python +- 3.7.9 +- 3.8.10 +- 3.9.13 +- 3.10.11 +- 3.11.9 +- 3.12.7 + +#### PyPy +- 2.7.18 [PyPy 7.3.17] +- 3.6.12 [PyPy 7.3.3] +- 3.7.13 [PyPy 7.3.9] +- 3.8.16 [PyPy 7.3.11] +- 3.9.19 [PyPy 7.3.16] +- 3.10.14 [PyPy 7.3.17] + +#### Ruby +- 3.0.7 +- 3.1.6 +- 3.2.5 + +### Databases + +#### PostgreSQL +| Property | Value | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| ServiceName | postgresql-x64-14 | +| Version | 14.12 | +| ServiceStatus | Stopped | +| ServiceStartType | Disabled | +| EnvironmentVariables | PGBIN=C:\Program Files\PostgreSQL\14\bin
PGDATA=C:\Program Files\PostgreSQL\14\data
PGROOT=C:\Program Files\PostgreSQL\14 | +| Path | C:\Program Files\PostgreSQL\14 | +| UserName | postgres | +| Password | root | + +#### MongoDB +| Version | ServiceName | ServiceStatus | ServiceStartType | +| -------- | ----------- | ------------- | ---------------- | +| 5.0.29.0 | MongoDB | Stopped | Disabled | + +### Database tools +- Azure CosmosDb Emulator 2.14.20.0 +- DacFx 162.4.92.3 +- MySQL 5.7.44.0 +- SQL OLEDB Driver 18.7.4.0 +- SQLPS 1.0 + +### Web Servers +| Name | Version | ConfigFile | ServiceName | ServiceStatus | ListenPort | +| ------ | ------- | ------------------------------------- | ----------- | ------------- | ---------- | +| Apache | 2.4.55 | C:\tools\Apache24\conf\httpd.conf | Apache | Stopped | 80 | +| Nginx | 1.27.2 | C:\tools\nginx-1.27.2\conf\nginx.conf | nginx | Stopped | 80 | + +### Visual Studio Enterprise 2019 +| Name | Version | Path | +| ----------------------------- | -------------- | -------------------------------------------------------------- | +| Visual Studio Enterprise 2019 | 16.11.35229.62 | C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise | + +#### Workloads, components and extensions +| Package | Version | +| ------------------------------------------------------------------------- | --------------- | +| Component.Android.NDK.R16B | 16.11.35213.202 | +| Component.Android.SDK25.Private | 16.0.28625.61 | +| Component.Android.SDK30 | 16.10.31205.252 | +| Component.Ant | 1.9.3.8 | +| Component.Dotfuscator | 16.10.31205.252 | +| Component.Linux.CMake | 16.2.29003.222 | +| Component.MDD.Android | 16.0.28517.75 | +| Component.MDD.Linux | 16.5.29515.121 | +| Component.MDD.Linux.GCC.arm | 16.5.29515.121 | +| Component.Microsoft.VisualStudio.LiveShare | 1.0.4441 | +| Component.Microsoft.VisualStudio.RazorExtension | 16.10.31205.252 | +| Component.Microsoft.VisualStudio.Tools.Applications | 16.0.31110.1 | +| Component.Microsoft.VisualStudio.Web.AzureFunctions | 16.10.31205.252 | +| Component.Microsoft.Web.LibraryManager | 16.10.31205.180 | +| Component.Microsoft.WebTools.BrowserLink.WebLivePreview | 0.7.22.39845 | +| Component.Microsoft.Windows.DriverKit | 10.0.21381.0 | +| Component.OpenJDK | 16.11.31827.77 | +| Component.UnityEngine.x64 | 16.10.31205.252 | +| Component.Unreal | 16.1.28810.153 | +| Component.Unreal.Android | 16.1.28810.153 | +| Component.VSInstallerProjects | 1.0.2 | +| Component.WixToolset.VisualStudioExtension.Dev16 | 1.0.0.22 | +| Component.WixToolset.VisualStudioExtension.Schemas3 | 1.0.0.22 | +| Component.Xamarin | 16.10.31205.252 | +| Component.Xamarin.RemotedSimulator | 16.11.32629.160 | +| Microsoft.Component.Azure.DataLake.Tools | 16.10.31205.252 | +| Microsoft.Component.ClickOnce | 16.11.31603.221 | +| Microsoft.Component.MSBuild | 16.11.33214.272 | +| Microsoft.Component.NetFX.Native | 16.5.29515.121 | +| Microsoft.Component.PythonTools | 16.11.31314.313 | +| Microsoft.Component.PythonTools.Miniconda | 16.11.31314.313 | +| Microsoft.Component.PythonTools.Web | 16.10.31205.252 | +| Microsoft.Component.VC.Runtime.UCRTSDK | 16.0.28625.61 | +| Microsoft.ComponentGroup.Blend | 16.0.28315.86 | +| Microsoft.ComponentGroup.ClickOnce.Publish | 16.11.31603.221 | +| Microsoft.Net.Component.3.5.DeveloperTools | 16.0.28517.75 | +| Microsoft.Net.Component.4.5.1.TargetingPack | 16.11.31605.320 | +| Microsoft.Net.Component.4.5.2.TargetingPack | 16.0.28517.75 | +| Microsoft.Net.Component.4.5.TargetingPack | 16.11.31605.320 | +| Microsoft.Net.Component.4.6.1.TargetingPack | 16.0.28517.75 | +| Microsoft.Net.Component.4.6.2.TargetingPack | 16.0.28517.75 | +| Microsoft.Net.Component.4.6.TargetingPack | 16.0.28517.75 | +| Microsoft.Net.Component.4.7.1.TargetingPack | 16.10.31205.252 | +| Microsoft.Net.Component.4.7.2.SDK | 16.4.29409.204 | +| Microsoft.Net.Component.4.7.2.TargetingPack | 16.10.31205.252 | +| Microsoft.Net.Component.4.7.TargetingPack | 16.10.31205.252 | +| Microsoft.Net.Component.4.8.SDK | 16.4.29313.120 | +| Microsoft.Net.Component.4.TargetingPack | 16.11.31605.320 | +| Microsoft.Net.ComponentGroup.4.6.2.DeveloperTools | 16.3.29207.166 | +| Microsoft.Net.ComponentGroup.4.7.1.DeveloperTools | 16.3.29207.166 | +| Microsoft.Net.ComponentGroup.4.7.DeveloperTools | 16.3.29207.166 | +| Microsoft.Net.ComponentGroup.DevelopmentPrerequisites | 16.3.29207.166 | +| Microsoft.Net.ComponentGroup.TargetingPacks.Common | 16.0.28516.191 | +| Microsoft.NetCore.Component.DevelopmentTools | 16.11.33214.272 | +| Microsoft.NetCore.Component.Web | 16.11.33214.272 | +| Microsoft.VisualStudio.Component.AppInsights.Tools | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.AspNet45 | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.Azure.AuthoringTools | 16.11.31827.77 | +| Microsoft.VisualStudio.Component.Azure.ClientLibs | 16.11.34829.67 | +| Microsoft.VisualStudio.Component.Azure.Compute.Emulator | 16.11.31827.77 | +| Microsoft.VisualStudio.Component.Azure.Kubernetes.Tools | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.Azure.Powershell | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.Azure.ResourceManager.Tools | 16.4.29409.204 | +| Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools | 16.4.29313.120 | +| Microsoft.VisualStudio.Component.Azure.Storage.Emulator | 16.4.29313.120 | +| Microsoft.VisualStudio.Component.Azure.Waverton | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.Azure.Waverton.BuildTools | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.ClassDesigner | 16.0.28528.71 | +| Microsoft.VisualStudio.Component.CloudExplorer | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.CodeMap | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.Common.Azure.Tools | 16.4.29409.204 | +| Microsoft.VisualStudio.Component.CoreEditor | 16.1.28811.260 | +| Microsoft.VisualStudio.Component.Debugger.JustInTime | 16.0.28517.75 | +| Microsoft.VisualStudio.Component.Debugger.Snapshot | 16.5.29813.82 | +| Microsoft.VisualStudio.Component.Debugger.TimeTravel | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.DiagnosticTools | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.DockerTools | 16.11.33214.272 | +| Microsoft.VisualStudio.Component.DotNetModelBuilder | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.DslTools | 16.0.28315.86 | +| Microsoft.VisualStudio.Component.EntityFramework | 16.0.28315.86 | +| Microsoft.VisualStudio.Component.FSharp | 16.0.28315.86 | +| Microsoft.VisualStudio.Component.FSharp.Desktop | 16.0.28315.86 | +| Microsoft.VisualStudio.Component.FSharp.WebTemplates | 16.3.29207.166 | +| Microsoft.VisualStudio.Component.GraphDocument | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.Graphics | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.Graphics.Tools | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.IISExpress | 16.0.28315.86 | +| Microsoft.VisualStudio.Component.IntelliCode | 16.11.35122.84 | +| Microsoft.VisualStudio.Component.IntelliTrace.FrontEnd | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.JavaScript.Diagnostics | 16.0.28517.75 | +| Microsoft.VisualStudio.Component.JavaScript.TypeScript | 16.11.31404.150 | +| Microsoft.VisualStudio.Component.LinqToSql | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.LiveUnitTesting | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.ManagedDesktop.Core | 16.4.29318.151 | +| Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites | 16.11.33214.272 | +| Microsoft.VisualStudio.Component.Merq | 16.2.29012.281 | +| Microsoft.VisualStudio.Component.MonoDebugger | 16.0.28517.75 | +| Microsoft.VisualStudio.Component.MSODBC.SQL | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.MSSQL.CMDLnUtils | 16.0.28707.177 | +| Microsoft.VisualStudio.Component.Node.Tools | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.NuGet | 16.1.28829.92 | +| Microsoft.VisualStudio.Component.NuGet.BuildTools | 16.1.28829.92 | +| Microsoft.VisualStudio.Component.PortableLibrary | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.Roslyn.Compiler | 16.0.28714.129 | +| Microsoft.VisualStudio.Component.Roslyn.LanguageServices | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.Sharepoint.Tools | 16.4.29409.204 | +| Microsoft.VisualStudio.Component.SQL.ADAL | 16.0.28517.75 | +| Microsoft.VisualStudio.Component.SQL.CLR | 16.0.28315.86 | +| Microsoft.VisualStudio.Component.SQL.DataSources | 16.0.28315.86 | +| Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.SQL.SSDT | 16.3.29207.166 | +| Microsoft.VisualStudio.Component.TeamOffice | 16.4.29409.204 | +| Microsoft.VisualStudio.Component.TestTools.CodedUITest | 16.0.28327.66 | +| Microsoft.VisualStudio.Component.TestTools.WebLoadTest | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.TextTemplating | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.TypeScript.4.3 | 16.0.31506.151 | +| Microsoft.VisualStudio.Component.Unity | 16.0.28315.86 | +| Microsoft.VisualStudio.Component.UWP.VC.ARM64 | 16.3.29207.166 | +| Microsoft.VisualStudio.Component.VC.14.25.x86.x64 | 16.11.32428.96 | +| Microsoft.VisualStudio.Component.VC.140 | 16.11.33801.447 | +| Microsoft.VisualStudio.Component.VC.ASAN | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.VC.ATL | 16.4.29313.120 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM | 16.4.29313.120 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM.Spectre | 16.5.29721.120 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM64 | 16.4.29313.120 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.VC.ATL.Spectre | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.VC.ATLMFC | 16.4.29313.120 | +| Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre | 16.5.29721.120 | +| Microsoft.VisualStudio.Component.VC.CLI.Support | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.VC.CMake.Project | 16.3.29103.31 | +| Microsoft.VisualStudio.Component.VC.CoreIde | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.VC.DiagnosticTools | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.VC.Llvm.Clang | 16.11.31603.221 | +| Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset | 16.3.29207.166 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM | 16.4.29313.120 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM.Spectre | 16.5.29721.120 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM64 | 16.4.29313.120 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre | 16.5.29721.120 | +| Microsoft.VisualStudio.Component.VC.Modules.x86.x64 | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.VC.Redist.14.Latest | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.VC.Redist.MSM | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.VC.Runtimes.ARM.Spectre | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest | 16.0.28517.75 | +| Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest | 16.0.28517.75 | +| Microsoft.VisualStudio.Component.VC.Tools.ARM | 16.11.32406.258 | +| Microsoft.VisualStudio.Component.VC.Tools.ARM64 | 16.11.32406.258 | +| Microsoft.VisualStudio.Component.VC.Tools.ARM64EC | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.VC.Tools.x86.x64 | 16.11.32406.258 | +| Microsoft.VisualStudio.Component.VC.v141.ARM | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.VC.v141.ARM.Spectre | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.VC.v141.ARM64 | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.VC.v141.ARM64.Spectre | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.VC.v141.ATL | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.VC.v141.ATL.ARM | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.VC.v141.ATL.ARM.Spectre | 16.5.29721.120 | +| Microsoft.VisualStudio.Component.VC.v141.ATL.ARM64 | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.VC.v141.ATL.ARM64.Spectre | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.VC.v141.ATL.Spectre | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.VC.v141.MFC | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.VC.v141.MFC.ARM.Spectre | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.VC.v141.MFC.ARM64.Spectre | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.VC.v141.MFC.Spectre | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.VC.v141.x86.x64 | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.VC.v141.x86.x64.Spectre | 16.5.29515.121 | +| Microsoft.VisualStudio.Component.VSSDK | 16.0.28315.86 | +| Microsoft.VisualStudio.Component.Wcf.Tooling | 16.0.28625.61 | +| Microsoft.VisualStudio.Component.Web | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.WebDeploy | 16.0.28517.75 | +| Microsoft.VisualStudio.Component.Windows10SDK | 16.4.29409.204 | +| Microsoft.VisualStudio.Component.Windows10SDK.16299 | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.Windows10SDK.17134 | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.Windows10SDK.17763 | 16.0.28517.75 | +| Microsoft.VisualStudio.Component.Windows10SDK.18362 | 16.1.28829.92 | +| Microsoft.VisualStudio.Component.Windows10SDK.19041 | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.Windows10SDK.20348 | 16.11.31603.221 | +| Microsoft.VisualStudio.Component.Windows11SDK.22000 | 16.11.31727.170 | +| Microsoft.VisualStudio.Component.WinXP | 16.10.31205.252 | +| Microsoft.VisualStudio.Component.Workflow | 16.0.28315.86 | +| Microsoft.VisualStudio.ComponentGroup.ArchitectureTools.Native | 16.0.28621.142 | +| Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices | 16.10.31205.180 | +| Microsoft.VisualStudio.ComponentGroup.Azure.Prerequisites | 16.10.31303.231 | +| Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools | 16.0.28528.71 | +| Microsoft.VisualStudio.ComponentGroup.AzureFunctions | 16.10.31205.180 | +| Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging | 16.11.34827.16 | +| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core | 16.2.29012.281 | +| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang | 16.11.31603.221 | +| Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard | 16.11.33214.272 | +| Microsoft.VisualStudio.ComponentGroup.UWP.Support | 16.11.33214.272 | +| Microsoft.VisualStudio.ComponentGroup.UWP.VC | 16.10.31205.180 | +| Microsoft.VisualStudio.ComponentGroup.UWP.VC.v141 | 16.1.28810.153 | +| Microsoft.VisualStudio.ComponentGroup.UWP.Xamarin | 16.10.31205.180 | +| Microsoft.VisualStudio.ComponentGroup.VisualStudioExtension.Prerequisites | 16.10.31205.180 | +| Microsoft.VisualStudio.ComponentGroup.Web | 16.10.31205.180 | +| Microsoft.VisualStudio.ComponentGroup.Web.Client | 16.10.31205.180 | +| Microsoft.VisualStudio.ComponentGroup.Web.CloudTools | 16.10.31205.180 | +| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions | 16.11.32413.511 | +| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.CMake | 16.11.32413.511 | +| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.TemplateEngine | 16.11.32413.511 | +| Microsoft.VisualStudio.Workload.Azure | 16.11.35026.282 | +| Microsoft.VisualStudio.Workload.CoreEditor | 16.10.31205.180 | +| Microsoft.VisualStudio.Workload.Data | 16.0.28720.110 | +| Microsoft.VisualStudio.Workload.DataScience | 16.10.31205.180 | +| Microsoft.VisualStudio.Workload.ManagedDesktop | 16.11.33214.272 | +| Microsoft.VisualStudio.Workload.ManagedGame | 16.10.31205.180 | +| Microsoft.VisualStudio.Workload.NativeCrossPlat | 16.10.31205.180 | +| Microsoft.VisualStudio.Workload.NativeDesktop | 16.11.31727.170 | +| Microsoft.VisualStudio.Workload.NativeGame | 16.11.31727.170 | +| Microsoft.VisualStudio.Workload.NativeMobile | 16.10.31205.180 | +| Microsoft.VisualStudio.Workload.NetCoreTools | 16.11.33214.272 | +| Microsoft.VisualStudio.Workload.NetCrossPlat | 16.11.33214.272 | +| Microsoft.VisualStudio.Workload.NetWeb | 16.11.33214.272 | +| Microsoft.VisualStudio.Workload.Node | 16.10.31205.180 | +| Microsoft.VisualStudio.Workload.Office | 16.11.33214.272 | +| Microsoft.VisualStudio.Workload.Python | 16.11.33328.57 | +| Microsoft.VisualStudio.Workload.Universal | 16.11.33214.272 | +| Microsoft.VisualStudio.Workload.VisualStudioExtension | 16.10.31205.180 | +| ms-biztalk.BizTalk | 3.13.2.0 | +| ProBITools.MicrosoftAnalysisServicesModelingProjects | 2.9.18 | +| ProBITools.MicrosoftReportProjectsforVisualStudio | 2.6.11 | +| SSIS.SqlServerIntegrationServicesProjects | 4.6 | +| VisualStudioClient.MicrosoftVisualStudio2017InstallerProjects | 1.0.2 | +| Windows Driver Kit | 10.1.22000.1 | +| Windows Driver Kit Visual Studio Extension | 10.0.21381.0 | +| Windows Software Development Kit | 10.1.22621.755 | +| WixToolset.WixToolsetVisualStudio2019Extension | 1.0.0.22 | + +#### Microsoft Visual C++ +| Name | Architecture | Version | +| -------------------------------------------- | ------------ | ----------- | +| Microsoft Visual C++ 2010 Redistributable | x64 | 10.0.40219 | +| Microsoft Visual C++ 2010 Redistributable | x86 | 10.0.40219 | +| Microsoft Visual C++ 2013 Additional Runtime | x64 | 12.0.40660 | +| Microsoft Visual C++ 2013 Minimum Runtime | x64 | 12.0.40660 | +| Microsoft Visual C++ 2013 Additional Runtime | x86 | 12.0.21005 | +| Microsoft Visual C++ 2013 Minimum Runtime | x86 | 12.0.21005 | +| Microsoft Visual C++ 2019 Debug Runtime | x64 | 14.29.30153 | +| Microsoft Visual C++ 2019 Debug Runtime | x86 | 14.29.30153 | +| Microsoft Visual C++ 2022 Additional Runtime | x64 | 14.40.33810 | +| Microsoft Visual C++ 2022 Minimum Runtime | x64 | 14.40.33810 | +| Microsoft Visual C++ 2022 Additional Runtime | x86 | 14.40.33810 | +| Microsoft Visual C++ 2022 Minimum Runtime | x86 | 14.40.33810 | + +#### Installed Windows SDKs +- 10.0.14393.0 +- 10.0.16299.0 +- 10.0.17134.0 +- 10.0.17763.0 +- 10.0.18362.0 +- 10.0.19041.0 +- 10.0.20348.0 +- 10.0.22000.0 +- 10.0.22621.0 + +### .NET Core Tools +- .NET Core SDK: 6.0.133, 6.0.203, 6.0.321, 6.0.425, 7.0.120, 7.0.203, 7.0.317, 7.0.410, 8.0.108, 8.0.206, 8.0.304, 8.0.402 +- .NET Framework: 4.7.2, 4.8 +- Microsoft.AspNetCore.App: 6.0.5, 6.0.26, 6.0.33, 7.0.5, 7.0.20, 8.0.6, 8.0.8 +- Microsoft.NETCore.App: 6.0.5, 6.0.26, 6.0.33, 7.0.5, 7.0.20, 8.0.6, 8.0.8 +- Microsoft.WindowsDesktop.App: 6.0.5, 6.0.26, 6.0.33, 7.0.5, 7.0.20, 8.0.6, 8.0.8 +- nbgv 3.6.143+57d4199a9c + +### PowerShell Tools +- PowerShell 7.4.5 + +#### Powershell Modules +- Az: 12.1.0 +- Azure: 2.1.0 (Default), 5.3.0 +- AzureRM: 2.1.0 (Default), 6.13.1 +- Azure (Cached): 3.8.0.zip, 4.2.1.zip, 5.1.1.zip +- AzureRM (Cached): 3.8.0.zip, 4.2.1.zip, 5.1.1.zip, 6.7.0.zip +- AWSPowershell: 4.1.671 +- DockerMsftProvider: 1.0.0.8 +- MarkdownPS: 1.10 +- Microsoft.Graph: 2.24.0 +- Pester: 3.4.0, 5.6.1 +- PowerShellGet: 1.0.0.1, 2.2.5 +- PSScriptAnalyzer: 1.22.0 +- PSWindowsUpdate: 2.2.1.5 +- SqlServer: 22.2.0 +- VSSetup: 2.2.16 +``` +Azure PowerShell module 2.1.0 and AzureRM PowerShell module 2.1.0 are installed +and are available via 'Get-Module -ListAvailable'. +All other versions are saved but not installed. +``` + +### Android +| Package Name | Version | +| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Command Line Tools | 8.0 | +| Android Emulator | 35.2.10 | +| Android SDK Build-tools | 35.0.0
34.0.0
33.0.0 33.0.1 33.0.2 33.0.3
32.0.0
31.0.0
30.0.2 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-34 (rev 3)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1)
android-33 (rev 3)
android-32 (rev 1)
android-31 (rev 1)
android-30 (rev 3) | +| Android SDK Platform-Tools | 35.0.2 | +| Android Support Repository | 47.0.0 | +| CMake | 3.10.2
3.18.1
3.22.1 | +| Google APIs | addon-google_apis-google-21
addon-google_apis-google-22
addon-google_apis-google-23
addon-google_apis-google-24 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264
27.1.12297006 | + +#### Environment variables +| Name | Value | +| ----------------------- | ---------------------------------------- | +| ANDROID_HOME | C:\Android\android-sdk | +| ANDROID_NDK | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_NDK_HOME | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_NDK_LATEST_HOME | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_NDK_ROOT | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_SDK_ROOT | C:\Android\android-sdk | + +### Cached Docker images +| Repository:Tag | Digest | Created | +| ------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ---------- | +| mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019 | sha256:c643acadeb3c8f13a65817fbe7dfbb478f30e7293d99439918c9907f936b7b44 | 2024-09-10 | +| mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2019 | sha256:0e30e7c4226044ff72eea68884894c397202eaacd73c4cb340a28896e617fee5 | 2024-09-10 | +| mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2019 | sha256:563e2e986c7c3fce79ebb0757c475980e3d3dde05d1d2e53d4323b0e99580840 | 2024-09-10 | +| mcr.microsoft.com/windows/nanoserver:1809 | sha256:b1c10aada142b0729d6cdbddd4d4119499c2cdbc32e75573c36cfe1a6149b3ee | 2024-09-06 | +| mcr.microsoft.com/windows/servercore:ltsc2019 | sha256:9fd56cf2f100d69f66f0de4af2c4fdd154e9e7be65eec3bf789a8088a881475a | 2024-09-06 | diff --git a/images/windows/Windows2022-Readme.md b/images/windows/Windows2022-Readme.md new file mode 100644 index 0000000..8ce6189 --- /dev/null +++ b/images/windows/Windows2022-Readme.md @@ -0,0 +1,549 @@ +# Windows Server 2022 +- OS Version: 10.0.20348 Build 2700 +- Image Version: 20241006.1.0 + +## Windows features +- Windows Subsystem for Linux (WSLv1): Enabled + +## Installed Software + +### Language and Runtime +- Bash 5.2.37(1)-release +- Go 1.21.13 +- Julia 1.10.5 +- Kotlin 2.0.20 +- LLVM 18.1.8 +- Node 18.20.3 +- Perl 5.32.1 +- PHP 8.3.12 +- Python 3.9.13 +- Ruby 3.0.7p220 + +### Package Management +- Chocolatey 2.3.0 +- Composer 2.8.1 +- Helm 3.16.0 +- Miniconda 24.7.1 (pre-installed on the image but not added to PATH) +- NPM 10.7.0 +- NuGet 6.11.1.2 +- pip 24.2 (python 3.9) +- Pipx 1.7.1 +- RubyGems 3.2.33 +- Vcpkg (build from commit 7adc2e4d4) +- Yarn 1.22.22 + +#### Environment variables +| Name | Value | +| ----------------------- | ------------ | +| VCPKG_INSTALLATION_ROOT | C:\vcpkg | +| CONDA | C:\Miniconda | + +### Project Management +- Ant 1.10.14 +- Gradle 8.10 +- Maven 3.8.7 +- sbt 1.10.2 + +### Tools +- 7zip 24.08 +- aria2 1.37.0 +- azcopy 10.26.0 +- Bazel 7.3.2 +- Bazelisk 1.22.0 +- Bicep 0.30.23 +- Cabal 3.12.1.0 +- CMake 3.30.4 +- CodeQL Action Bundle 2.19.0 +- Docker 26.1.3 +- Docker Compose v2 2.27.1 +- Docker-wincred 0.8.2 +- ghc 9.10.1 +- Git 2.46.2.windows.1 +- Git LFS 3.5.1 +- ImageMagick 7.1.1-38 +- InnoSetup 6.3.3 +- jq 1.7.1 +- Kind 0.24.0 +- Kubectl 1.31.1 +- Mercurial 5.0 +- gcc 12.2.0 +- gdb 11.2 +- GNU Binutils 2.39 +- Newman 6.2.1 +- NSIS 3.10 +- OpenSSL 1.1.1w +- Packer 1.11.2 +- Pulumi 3.135.1 +- R 4.4.1 +- Service Fabric SDK 9.1.1436.9590 +- Stack 3.1.1 +- Subversion (SVN) 1.14.2 +- Swig 4.1.1 +- VSWhere 3.1.7 +- WinAppDriver 1.2.2009.02003 +- WiX Toolset 3.14.1.8722 +- yamllint 1.35.1 +- zstd 1.5.6 + +### CLI Tools +- Alibaba Cloud CLI 3.0.224 +- AWS CLI 2.18.0 +- AWS SAM CLI 1.125.0 +- AWS Session Manager CLI 1.2.650.0 +- Azure CLI 2.64.0 +- Azure DevOps CLI extension 1.0.1 +- GitHub CLI 2.58.0 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- bindgen 0.70.1 +- cargo-audit 0.20.1 +- cargo-outdated 0.15.0 +- cbindgen 0.27.0 +- Clippy 0.1.81 +- Rustfmt 1.7.1 + +### Browsers and Drivers +- Google Chrome 129.0.6668.90 +- Chrome Driver 129.0.6668.89 +- Microsoft Edge 129.0.2792.79 +- Microsoft Edge Driver 129.0.2792.79 +- Mozilla Firefox 131.0 +- Gecko Driver 0.35.0 +- IE Driver 4.14.0.0 +- Selenium server 4.25.0 + +#### Environment variables +| Name | Value | +| ----------------- | ---------------------------------- | +| CHROMEWEBDRIVER | C:\SeleniumWebDrivers\ChromeDriver | +| EDGEWEBDRIVER | C:\SeleniumWebDrivers\EdgeDriver | +| GECKOWEBDRIVER | C:\SeleniumWebDrivers\GeckoDriver | +| SELENIUM_JAR_PATH | C:\selenium\selenium-server.jar | + +### Java +| Version | Environment Variable | +| ------------------- | -------------------- | +| 8.0.422+5 (default) | JAVA_HOME_8_X64 | +| 11.0.24+8 | JAVA_HOME_11_X64 | +| 17.0.12+7 | JAVA_HOME_17_X64 | +| 21.0.4+7.0 | JAVA_HOME_21_X64 | + +### Shells +| Name | Target | +| ------------- | --------------------------------- | +| gitbash.exe | C:\Program Files\Git\bin\bash.exe | +| msys2bash.cmd | C:\msys64\usr\bin\bash.exe | +| wslbash.exe | C:\Windows\System32\bash.exe | + +### MSYS2 +- Pacman 6.1.0 + +#### Notes +``` +Location: C:\msys64 + +Note: MSYS2 is pre-installed on image but not added to PATH. +``` + +### Cached Tools + +#### Go +- 1.20.14 +- 1.21.13 +- 1.22.8 + +#### Node.js +- 16.20.2 +- 18.20.4 +- 20.17.0 + +#### Python +- 3.7.9 +- 3.8.10 +- 3.9.13 +- 3.10.11 +- 3.11.9 +- 3.12.7 + +#### PyPy +- 2.7.18 [PyPy 7.3.17] +- 3.7.13 [PyPy 7.3.9] +- 3.8.16 [PyPy 7.3.11] +- 3.9.19 [PyPy 7.3.16] +- 3.10.14 [PyPy 7.3.17] + +#### Ruby +- 3.0.7 +- 3.1.6 +- 3.2.5 + +### Databases + +#### PostgreSQL +| Property | Value | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| ServiceName | postgresql-x64-14 | +| Version | 14.12 | +| ServiceStatus | Stopped | +| ServiceStartType | Disabled | +| EnvironmentVariables | PGBIN=C:\Program Files\PostgreSQL\14\bin
PGDATA=C:\Program Files\PostgreSQL\14\data
PGROOT=C:\Program Files\PostgreSQL\14 | +| Path | C:\Program Files\PostgreSQL\14 | +| UserName | postgres | +| Password | root | + +#### MongoDB +| Version | ServiceName | ServiceStatus | ServiceStartType | +| -------- | ----------- | ------------- | ---------------- | +| 5.0.29.0 | MongoDB | Stopped | Disabled | + +### Database tools +- Azure CosmosDb Emulator 2.14.20.0 +- DacFx 162.4.92.3 +- MySQL 8.0.39.0 +- SQL OLEDB Driver 18.7.4.0 +- SQLPS 1.0 + +### Web Servers +| Name | Version | ConfigFile | ServiceName | ServiceStatus | ListenPort | +| ------ | ------- | ------------------------------------- | ----------- | ------------- | ---------- | +| Apache | 2.4.55 | C:\tools\Apache24\conf\httpd.conf | Apache | Stopped | 80 | +| Nginx | 1.27.2 | C:\tools\nginx-1.27.2\conf\nginx.conf | nginx | Stopped | 80 | + +### Visual Studio Enterprise 2022 +| Name | Version | Path | +| ----------------------------- | --------------- | -------------------------------------------------------- | +| Visual Studio Enterprise 2022 | 17.11.35312.102 | C:\Program Files\Microsoft Visual Studio\2022\Enterprise | + +#### Workloads, components and extensions +| Package | Version | +| ------------------------------------------------------------------------- | --------------- | +| android | 34.0.113.0 | +| aspire | 8.100.24.37302 | +| Component.Android.NDK.R23C | 17.11.35102.94 | +| Component.Android.SDK.MAUI | 17.11.35102.94 | +| Component.Dotfuscator | 17.11.35102.94 | +| Component.Linux.CMake | 17.11.35102.94 | +| Component.Linux.RemoteFileExplorer | 17.11.35102.94 | +| Component.MDD.Android | 17.11.35102.94 | +| Component.MDD.Linux | 17.11.35102.94 | +| Component.MDD.Linux.GCC.arm | 17.11.35102.94 | +| Component.Microsoft.VisualStudio.LiveShare.2022 | 1.0.5919 | +| Component.Microsoft.VisualStudio.RazorExtension | 17.11.35102.94 | +| Component.Microsoft.VisualStudio.Tools.Applications.amd64 | 17.0.33617.0 | +| Component.Microsoft.VisualStudio.Web.AzureFunctions | 17.11.35102.94 | +| Component.Microsoft.Web.LibraryManager | 17.11.35102.94 | +| Component.Microsoft.WebTools.BrowserLink.WebLivePreview | 17.11.3.50567 | +| Component.Microsoft.Windows.DriverKit | 10.0.26100.10 | +| Component.OpenJDK | 17.11.35102.94 | +| Component.UnityEngine.x64 | 17.11.35102.94 | +| Component.Unreal | 17.11.35102.94 | +| Component.Unreal.Android | 17.11.35102.94 | +| Component.Unreal.Ide | 17.11.35102.94 | +| Component.VisualStudio.GitHub.Copilot | 17.11.35312.31 | +| Component.VSInstallerProjects2022 | 2.0.1 | +| Component.WixToolset.VisualStudioExtension.Dev17 | 1.0.0.22 | +| Component.WixToolset.VisualStudioExtension.Schemas3 | 1.0.0.22 | +| Component.Xamarin | 17.11.35102.94 | +| Component.Xamarin.RemotedSimulator | 17.11.35102.94 | +| ios | 17.5.8030.0 | +| maccatalyst | 17.5.8030.0 | +| maui.blazor | 8.0.82.11329 | +| maui.core | 8.0.82.11329 | +| maui.windows | 8.0.82.11329 | +| Microsoft.Component.Azure.DataLake.Tools | 17.11.35102.94 | +| Microsoft.Component.ClickOnce | 17.11.35102.94 | +| Microsoft.Component.CodeAnalysis.SDK | 17.11.35102.94 | +| Microsoft.Component.MSBuild | 17.11.35102.94 | +| Microsoft.Component.NetFX.Native | 17.11.35102.94 | +| Microsoft.Component.PythonTools | 17.11.35102.94 | +| Microsoft.Component.PythonTools.Web | 17.11.35102.94 | +| Microsoft.Component.VC.Runtime.UCRTSDK | 17.11.35102.94 | +| Microsoft.ComponentGroup.Blend | 17.11.35102.94 | +| Microsoft.ComponentGroup.ClickOnce.Publish | 17.11.35102.94 | +| Microsoft.Net.Component.4.5.2.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.6.2.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.6.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.7.1.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.7.2.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.7.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.8.1.SDK | 17.11.35102.94 | +| Microsoft.Net.Component.4.8.1.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.8.SDK | 17.11.35102.94 | +| Microsoft.Net.Component.4.8.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.ComponentGroup.4.8.DeveloperTools | 17.11.35102.94 | +| Microsoft.Net.ComponentGroup.DevelopmentPrerequisites | 17.11.35102.94 | +| Microsoft.Net.ComponentGroup.TargetingPacks.Common | 17.11.35102.94 | +| microsoft.net.runtime.android | 8.0.824.36612 | +| microsoft.net.runtime.android.aot | 8.0.824.36612 | +| microsoft.net.runtime.android.aot.net7 | 8.0.824.36612 | +| microsoft.net.runtime.android.net7 | 8.0.824.36612 | +| microsoft.net.runtime.ios | 8.0.824.36612 | +| microsoft.net.runtime.ios.net7 | 8.0.824.36612 | +| microsoft.net.runtime.maccatalyst | 8.0.824.36612 | +| microsoft.net.runtime.maccatalyst.net7 | 8.0.824.36612 | +| microsoft.net.runtime.mono.tooling | 8.0.824.36612 | +| microsoft.net.runtime.mono.tooling.net7 | 8.0.824.36612 | +| microsoft.net.sdk.emscripten | 8.0.10.35802 | +| Microsoft.NetCore.Component.DevelopmentTools | 17.11.35102.94 | +| Microsoft.NetCore.Component.Runtime.6.0 | 17.11.35303.118 | +| Microsoft.NetCore.Component.Runtime.8.0 | 17.11.35303.118 | +| Microsoft.NetCore.Component.SDK | 17.11.35303.118 | +| Microsoft.NetCore.Component.Web | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.AppInsights.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.AspNet | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.AspNet45 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.AuthoringTools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.ClientLibs | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.Compute.Emulator | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.Powershell | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.ResourceManager.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.Waverton | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.Waverton.BuildTools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.ClassDesigner | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.CodeMap | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Common.Azure.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.CoreEditor | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.CppBuildInsights | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Debugger.JustInTime | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Debugger.Snapshot | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.DiagnosticTools | 17.11.35118.42 | +| Microsoft.VisualStudio.Component.DockerTools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.DotNetModelBuilder | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.DslTools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Embedded | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.EntityFramework | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.FSharp | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.FSharp.Desktop | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.FSharp.WebTemplates | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.GraphDocument | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Graphics | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Graphics.Tools | 17.11.35118.42 | +| Microsoft.VisualStudio.Component.HLSL | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.IISExpress | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.IntelliCode | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.IntelliTrace.FrontEnd | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.JavaScript.Diagnostics | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.JavaScript.TypeScript | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.LinqToSql | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.LiveUnitTesting | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.ManagedDesktop.Core | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Merq | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.MonoDebugger | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.MSODBC.SQL | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.MSSQL.CMDLnUtils | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Node.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.NuGet | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.NuGet.BuildTools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.PortableLibrary | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Roslyn.Compiler | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Roslyn.LanguageServices | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.SecurityIssueAnalysis | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Sharepoint.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.SQL.CLR | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.SQL.DataSources | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.SQL.SSDT | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.TeamOffice | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.TestTools.CodedUITest | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.TestTools.WebLoadTest | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.TextTemplating | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.TypeScript.TSServer | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Unity | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.UWP.VC.ARM64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.UWP.VC.ARM64EC | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.14.29.16.11.ARM | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.14.29.16.11.ARM64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ASAN | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATLMFC | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.CLI.Support | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.CMake.Project | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.CoreIde | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.DiagnosticTools | 17.11.35118.42 | +| Microsoft.VisualStudio.Component.VC.Llvm.Clang | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Modules.x86.x64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Redist.14.Latest | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Redist.MSM | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Runtimes.ARM.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Runtimes.ARM64EC.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Tools.ARM | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Tools.ARM64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Tools.ARM64EC | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Tools.x86.x64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Vcpkg | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VSSDK | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Wcf.Tooling | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Web | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.WebDeploy | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows10SDK | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows10SDK.19041 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows10SDK.20348 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows11SDK.22000 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows11SDK.22621 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows11Sdk.WindowsPerformanceToolkit | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Workflow | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.WslDebugging | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.ArchitectureTools.Native | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Azure.Prerequisites | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.AzureFunctions | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.All | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.Android | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.Blazor | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.iOS | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.MacCatalyst | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.Shared | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.Windows | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142 | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.VC.Tools.142.x86.x64 | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.VisualStudioExtension.Prerequisites | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Web | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Web.CloudTools | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.CMake | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.TemplateEngine | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.WindowsAppDevelopment.Prerequisites | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.WindowsAppSDK.Cs | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Azure | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.CoreEditor | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Data | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.DataScience | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.ManagedDesktop | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.ManagedGame | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NativeCrossPlat | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NativeDesktop | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NativeGame | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NativeMobile | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NetCrossPlat | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NetWeb | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Node | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Office | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Python | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Universal | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.VisualStudioExtension | 17.11.35102.94 | +| runtimes.ios | 8.0.824.36612 | +| runtimes.ios.net7 | 8.0.824.36612 | +| runtimes.maccatalyst | 8.0.824.36612 | +| runtimes.maccatalyst.net7 | 8.0.824.36612 | +| wasm.tools | 8.0.824.36612 | +| ProBITools.MicrosoftAnalysisServicesModelingProjects2022 | 3.0.4 | +| ProBITools.MicrosoftReportProjectsforVisualStudio2022 | 3.0.1 | +| SSIS.MicrosoftDataToolsIntegrationServices | 1.5 | +| VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects | 2.0.1 | +| Windows Driver Kit | 10.1.22621.2428 | +| Windows Driver Kit Visual Studio Extension | 10.0.26100.10 | +| Windows Software Development Kit | 10.1.22621.3233 | +| WixToolset.WixToolsetVisualStudio2022Extension | 1.0.0.22 | + +#### Microsoft Visual C++ +| Name | Architecture | Version | +| -------------------------------------------- | ------------ | ----------- | +| Microsoft Visual C++ 2013 Additional Runtime | x64 | 12.0.40660 | +| Microsoft Visual C++ 2013 Minimum Runtime | x64 | 12.0.40660 | +| Microsoft Visual C++ 2022 Additional Runtime | x64 | 14.40.33810 | +| Microsoft Visual C++ 2022 Debug Runtime | x64 | 14.40.33810 | +| Microsoft Visual C++ 2022 Minimum Runtime | x64 | 14.40.33810 | +| Microsoft Visual C++ 2022 Additional Runtime | x86 | 14.40.33810 | +| Microsoft Visual C++ 2022 Debug Runtime | x86 | 14.40.33810 | +| Microsoft Visual C++ 2022 Minimum Runtime | x86 | 14.40.33810 | + +#### Installed Windows SDKs +- 10.0.17763.0 +- 10.0.19041.0 +- 10.0.20348.0 +- 10.0.22000.0 +- 10.0.22621.0 + +### .NET Core Tools +- .NET Core SDK: 6.0.133, 6.0.203, 6.0.321, 6.0.425, 7.0.120, 7.0.203, 7.0.317, 7.0.410, 8.0.400 +- .NET Framework: 4.8, 4.8.1 +- Microsoft.AspNetCore.App: 6.0.5, 6.0.26, 6.0.32, 6.0.33, 7.0.5, 7.0.20, 8.0.8 +- Microsoft.NETCore.App: 6.0.5, 6.0.26, 6.0.32, 6.0.33, 7.0.5, 7.0.20, 8.0.8 +- Microsoft.WindowsDesktop.App: 6.0.5, 6.0.26, 6.0.33, 7.0.5, 7.0.20, 8.0.8 +- nbgv 3.6.143+57d4199a9c + +### PowerShell Tools +- PowerShell 7.4.5 + +#### Powershell Modules +- Az: 12.1.0 +- Azure: 2.1.0 (Default), 5.3.0 +- AzureRM: 2.1.0 (Default), 6.13.1 +- Azure (Cached): 3.8.0.zip, 4.2.1.zip, 5.1.1.zip +- AzureRM (Cached): 3.8.0.zip, 4.2.1.zip, 5.1.1.zip, 6.7.0.zip +- AWSPowershell: 4.1.671 +- DockerMsftProvider: 1.0.0.8 +- MarkdownPS: 1.10 +- Microsoft.Graph: 2.24.0 +- Pester: 3.4.0, 5.6.1 +- PowerShellGet: 1.0.0.1, 2.2.5 +- PSScriptAnalyzer: 1.22.0 +- PSWindowsUpdate: 2.2.1.5 +- SqlServer: 22.3.0 +- VSSetup: 2.2.16 +``` +Azure PowerShell module 2.1.0 and AzureRM PowerShell module 2.1.0 are installed +and are available via 'Get-Module -ListAvailable'. +All other versions are saved but not installed. +``` + +### Android +| Package Name | Version | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Command Line Tools | 8.0 | +| Android Emulator | 35.2.10 | +| Android SDK Build-tools | 35.0.0
34.0.0
33.0.0 33.0.1 33.0.2 33.0.3
32.0.0
31.0.0 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-34 (rev 3)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1)
android-33 (rev 3)
android-32 (rev 1)
android-31 (rev 1) | +| Android SDK Platform-Tools | 35.0.2 | +| Android Support Repository | 47.0.0 | +| CMake | 3.18.1
3.22.1 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264
27.1.12297006 | + +#### Environment variables +| Name | Value | +| ----------------------- | ---------------------------------------- | +| ANDROID_HOME | C:\Android\android-sdk | +| ANDROID_NDK | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_NDK_HOME | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_NDK_LATEST_HOME | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_NDK_ROOT | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_SDK_ROOT | C:\Android\android-sdk | + +### Cached Docker images +| Repository:Tag | Digest | Created | +| ------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ---------- | +| mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022 | sha256:694e99af0ec996e63e6204e40e51869daf87c42d9bc628255804e32e2858b39c | 2024-09-10 | +| mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2022 | sha256:c9ff4d54c2a82dd154d3624fc8db44a9abe7913ab70f3aca5cb95213ed9621c5 | 2024-09-10 | +| mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2022 | sha256:b6d6f01a449977c597a196a95f7f10e2c2b6438f7f9920744ceb11febeb61f27 | 2024-09-10 | +| mcr.microsoft.com/windows/nanoserver:ltsc2022 | sha256:be3440ee2f36ed823a21df34e8e65026743d801dbc71e9311d03603775fc69c1 | 2024-09-05 | +| mcr.microsoft.com/windows/servercore:ltsc2022 | sha256:b7b2e5b4c2414400c4eef13db747376e0f10ef8e15b8d0587ef5b953ad4e6d43 | 2024-09-06 | diff --git a/images/windows/Windows2025-Readme.md b/images/windows/Windows2025-Readme.md new file mode 100644 index 0000000..8ce6189 --- /dev/null +++ b/images/windows/Windows2025-Readme.md @@ -0,0 +1,549 @@ +# Windows Server 2022 +- OS Version: 10.0.20348 Build 2700 +- Image Version: 20241006.1.0 + +## Windows features +- Windows Subsystem for Linux (WSLv1): Enabled + +## Installed Software + +### Language and Runtime +- Bash 5.2.37(1)-release +- Go 1.21.13 +- Julia 1.10.5 +- Kotlin 2.0.20 +- LLVM 18.1.8 +- Node 18.20.3 +- Perl 5.32.1 +- PHP 8.3.12 +- Python 3.9.13 +- Ruby 3.0.7p220 + +### Package Management +- Chocolatey 2.3.0 +- Composer 2.8.1 +- Helm 3.16.0 +- Miniconda 24.7.1 (pre-installed on the image but not added to PATH) +- NPM 10.7.0 +- NuGet 6.11.1.2 +- pip 24.2 (python 3.9) +- Pipx 1.7.1 +- RubyGems 3.2.33 +- Vcpkg (build from commit 7adc2e4d4) +- Yarn 1.22.22 + +#### Environment variables +| Name | Value | +| ----------------------- | ------------ | +| VCPKG_INSTALLATION_ROOT | C:\vcpkg | +| CONDA | C:\Miniconda | + +### Project Management +- Ant 1.10.14 +- Gradle 8.10 +- Maven 3.8.7 +- sbt 1.10.2 + +### Tools +- 7zip 24.08 +- aria2 1.37.0 +- azcopy 10.26.0 +- Bazel 7.3.2 +- Bazelisk 1.22.0 +- Bicep 0.30.23 +- Cabal 3.12.1.0 +- CMake 3.30.4 +- CodeQL Action Bundle 2.19.0 +- Docker 26.1.3 +- Docker Compose v2 2.27.1 +- Docker-wincred 0.8.2 +- ghc 9.10.1 +- Git 2.46.2.windows.1 +- Git LFS 3.5.1 +- ImageMagick 7.1.1-38 +- InnoSetup 6.3.3 +- jq 1.7.1 +- Kind 0.24.0 +- Kubectl 1.31.1 +- Mercurial 5.0 +- gcc 12.2.0 +- gdb 11.2 +- GNU Binutils 2.39 +- Newman 6.2.1 +- NSIS 3.10 +- OpenSSL 1.1.1w +- Packer 1.11.2 +- Pulumi 3.135.1 +- R 4.4.1 +- Service Fabric SDK 9.1.1436.9590 +- Stack 3.1.1 +- Subversion (SVN) 1.14.2 +- Swig 4.1.1 +- VSWhere 3.1.7 +- WinAppDriver 1.2.2009.02003 +- WiX Toolset 3.14.1.8722 +- yamllint 1.35.1 +- zstd 1.5.6 + +### CLI Tools +- Alibaba Cloud CLI 3.0.224 +- AWS CLI 2.18.0 +- AWS SAM CLI 1.125.0 +- AWS Session Manager CLI 1.2.650.0 +- Azure CLI 2.64.0 +- Azure DevOps CLI extension 1.0.1 +- GitHub CLI 2.58.0 + +### Rust Tools +- Cargo 1.81.0 +- Rust 1.81.0 +- Rustdoc 1.81.0 +- Rustup 1.27.1 + +#### Packages +- bindgen 0.70.1 +- cargo-audit 0.20.1 +- cargo-outdated 0.15.0 +- cbindgen 0.27.0 +- Clippy 0.1.81 +- Rustfmt 1.7.1 + +### Browsers and Drivers +- Google Chrome 129.0.6668.90 +- Chrome Driver 129.0.6668.89 +- Microsoft Edge 129.0.2792.79 +- Microsoft Edge Driver 129.0.2792.79 +- Mozilla Firefox 131.0 +- Gecko Driver 0.35.0 +- IE Driver 4.14.0.0 +- Selenium server 4.25.0 + +#### Environment variables +| Name | Value | +| ----------------- | ---------------------------------- | +| CHROMEWEBDRIVER | C:\SeleniumWebDrivers\ChromeDriver | +| EDGEWEBDRIVER | C:\SeleniumWebDrivers\EdgeDriver | +| GECKOWEBDRIVER | C:\SeleniumWebDrivers\GeckoDriver | +| SELENIUM_JAR_PATH | C:\selenium\selenium-server.jar | + +### Java +| Version | Environment Variable | +| ------------------- | -------------------- | +| 8.0.422+5 (default) | JAVA_HOME_8_X64 | +| 11.0.24+8 | JAVA_HOME_11_X64 | +| 17.0.12+7 | JAVA_HOME_17_X64 | +| 21.0.4+7.0 | JAVA_HOME_21_X64 | + +### Shells +| Name | Target | +| ------------- | --------------------------------- | +| gitbash.exe | C:\Program Files\Git\bin\bash.exe | +| msys2bash.cmd | C:\msys64\usr\bin\bash.exe | +| wslbash.exe | C:\Windows\System32\bash.exe | + +### MSYS2 +- Pacman 6.1.0 + +#### Notes +``` +Location: C:\msys64 + +Note: MSYS2 is pre-installed on image but not added to PATH. +``` + +### Cached Tools + +#### Go +- 1.20.14 +- 1.21.13 +- 1.22.8 + +#### Node.js +- 16.20.2 +- 18.20.4 +- 20.17.0 + +#### Python +- 3.7.9 +- 3.8.10 +- 3.9.13 +- 3.10.11 +- 3.11.9 +- 3.12.7 + +#### PyPy +- 2.7.18 [PyPy 7.3.17] +- 3.7.13 [PyPy 7.3.9] +- 3.8.16 [PyPy 7.3.11] +- 3.9.19 [PyPy 7.3.16] +- 3.10.14 [PyPy 7.3.17] + +#### Ruby +- 3.0.7 +- 3.1.6 +- 3.2.5 + +### Databases + +#### PostgreSQL +| Property | Value | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| ServiceName | postgresql-x64-14 | +| Version | 14.12 | +| ServiceStatus | Stopped | +| ServiceStartType | Disabled | +| EnvironmentVariables | PGBIN=C:\Program Files\PostgreSQL\14\bin
PGDATA=C:\Program Files\PostgreSQL\14\data
PGROOT=C:\Program Files\PostgreSQL\14 | +| Path | C:\Program Files\PostgreSQL\14 | +| UserName | postgres | +| Password | root | + +#### MongoDB +| Version | ServiceName | ServiceStatus | ServiceStartType | +| -------- | ----------- | ------------- | ---------------- | +| 5.0.29.0 | MongoDB | Stopped | Disabled | + +### Database tools +- Azure CosmosDb Emulator 2.14.20.0 +- DacFx 162.4.92.3 +- MySQL 8.0.39.0 +- SQL OLEDB Driver 18.7.4.0 +- SQLPS 1.0 + +### Web Servers +| Name | Version | ConfigFile | ServiceName | ServiceStatus | ListenPort | +| ------ | ------- | ------------------------------------- | ----------- | ------------- | ---------- | +| Apache | 2.4.55 | C:\tools\Apache24\conf\httpd.conf | Apache | Stopped | 80 | +| Nginx | 1.27.2 | C:\tools\nginx-1.27.2\conf\nginx.conf | nginx | Stopped | 80 | + +### Visual Studio Enterprise 2022 +| Name | Version | Path | +| ----------------------------- | --------------- | -------------------------------------------------------- | +| Visual Studio Enterprise 2022 | 17.11.35312.102 | C:\Program Files\Microsoft Visual Studio\2022\Enterprise | + +#### Workloads, components and extensions +| Package | Version | +| ------------------------------------------------------------------------- | --------------- | +| android | 34.0.113.0 | +| aspire | 8.100.24.37302 | +| Component.Android.NDK.R23C | 17.11.35102.94 | +| Component.Android.SDK.MAUI | 17.11.35102.94 | +| Component.Dotfuscator | 17.11.35102.94 | +| Component.Linux.CMake | 17.11.35102.94 | +| Component.Linux.RemoteFileExplorer | 17.11.35102.94 | +| Component.MDD.Android | 17.11.35102.94 | +| Component.MDD.Linux | 17.11.35102.94 | +| Component.MDD.Linux.GCC.arm | 17.11.35102.94 | +| Component.Microsoft.VisualStudio.LiveShare.2022 | 1.0.5919 | +| Component.Microsoft.VisualStudio.RazorExtension | 17.11.35102.94 | +| Component.Microsoft.VisualStudio.Tools.Applications.amd64 | 17.0.33617.0 | +| Component.Microsoft.VisualStudio.Web.AzureFunctions | 17.11.35102.94 | +| Component.Microsoft.Web.LibraryManager | 17.11.35102.94 | +| Component.Microsoft.WebTools.BrowserLink.WebLivePreview | 17.11.3.50567 | +| Component.Microsoft.Windows.DriverKit | 10.0.26100.10 | +| Component.OpenJDK | 17.11.35102.94 | +| Component.UnityEngine.x64 | 17.11.35102.94 | +| Component.Unreal | 17.11.35102.94 | +| Component.Unreal.Android | 17.11.35102.94 | +| Component.Unreal.Ide | 17.11.35102.94 | +| Component.VisualStudio.GitHub.Copilot | 17.11.35312.31 | +| Component.VSInstallerProjects2022 | 2.0.1 | +| Component.WixToolset.VisualStudioExtension.Dev17 | 1.0.0.22 | +| Component.WixToolset.VisualStudioExtension.Schemas3 | 1.0.0.22 | +| Component.Xamarin | 17.11.35102.94 | +| Component.Xamarin.RemotedSimulator | 17.11.35102.94 | +| ios | 17.5.8030.0 | +| maccatalyst | 17.5.8030.0 | +| maui.blazor | 8.0.82.11329 | +| maui.core | 8.0.82.11329 | +| maui.windows | 8.0.82.11329 | +| Microsoft.Component.Azure.DataLake.Tools | 17.11.35102.94 | +| Microsoft.Component.ClickOnce | 17.11.35102.94 | +| Microsoft.Component.CodeAnalysis.SDK | 17.11.35102.94 | +| Microsoft.Component.MSBuild | 17.11.35102.94 | +| Microsoft.Component.NetFX.Native | 17.11.35102.94 | +| Microsoft.Component.PythonTools | 17.11.35102.94 | +| Microsoft.Component.PythonTools.Web | 17.11.35102.94 | +| Microsoft.Component.VC.Runtime.UCRTSDK | 17.11.35102.94 | +| Microsoft.ComponentGroup.Blend | 17.11.35102.94 | +| Microsoft.ComponentGroup.ClickOnce.Publish | 17.11.35102.94 | +| Microsoft.Net.Component.4.5.2.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.6.2.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.6.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.7.1.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.7.2.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.7.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.8.1.SDK | 17.11.35102.94 | +| Microsoft.Net.Component.4.8.1.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.Component.4.8.SDK | 17.11.35102.94 | +| Microsoft.Net.Component.4.8.TargetingPack | 17.11.35102.94 | +| Microsoft.Net.ComponentGroup.4.8.DeveloperTools | 17.11.35102.94 | +| Microsoft.Net.ComponentGroup.DevelopmentPrerequisites | 17.11.35102.94 | +| Microsoft.Net.ComponentGroup.TargetingPacks.Common | 17.11.35102.94 | +| microsoft.net.runtime.android | 8.0.824.36612 | +| microsoft.net.runtime.android.aot | 8.0.824.36612 | +| microsoft.net.runtime.android.aot.net7 | 8.0.824.36612 | +| microsoft.net.runtime.android.net7 | 8.0.824.36612 | +| microsoft.net.runtime.ios | 8.0.824.36612 | +| microsoft.net.runtime.ios.net7 | 8.0.824.36612 | +| microsoft.net.runtime.maccatalyst | 8.0.824.36612 | +| microsoft.net.runtime.maccatalyst.net7 | 8.0.824.36612 | +| microsoft.net.runtime.mono.tooling | 8.0.824.36612 | +| microsoft.net.runtime.mono.tooling.net7 | 8.0.824.36612 | +| microsoft.net.sdk.emscripten | 8.0.10.35802 | +| Microsoft.NetCore.Component.DevelopmentTools | 17.11.35102.94 | +| Microsoft.NetCore.Component.Runtime.6.0 | 17.11.35303.118 | +| Microsoft.NetCore.Component.Runtime.8.0 | 17.11.35303.118 | +| Microsoft.NetCore.Component.SDK | 17.11.35303.118 | +| Microsoft.NetCore.Component.Web | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.AppInsights.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.AspNet | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.AspNet45 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.AuthoringTools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.ClientLibs | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.Compute.Emulator | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.Powershell | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.ResourceManager.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.Waverton | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Azure.Waverton.BuildTools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.ClassDesigner | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.CodeMap | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Common.Azure.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.CoreEditor | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.CppBuildInsights | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Debugger.JustInTime | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Debugger.Snapshot | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.DiagnosticTools | 17.11.35118.42 | +| Microsoft.VisualStudio.Component.DockerTools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.DotNetModelBuilder | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.DslTools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Embedded | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.EntityFramework | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.FSharp | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.FSharp.Desktop | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.FSharp.WebTemplates | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.GraphDocument | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Graphics | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Graphics.Tools | 17.11.35118.42 | +| Microsoft.VisualStudio.Component.HLSL | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.IISExpress | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.IntelliCode | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.IntelliTrace.FrontEnd | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.JavaScript.Diagnostics | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.JavaScript.TypeScript | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.LinqToSql | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.LiveUnitTesting | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.ManagedDesktop.Core | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Merq | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.MonoDebugger | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.MSODBC.SQL | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.MSSQL.CMDLnUtils | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Node.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.NuGet | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.NuGet.BuildTools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.PortableLibrary | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Roslyn.Compiler | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Roslyn.LanguageServices | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.SecurityIssueAnalysis | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Sharepoint.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.SQL.CLR | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.SQL.DataSources | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.SQL.SSDT | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.TeamOffice | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.TestTools.CodedUITest | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.TestTools.WebLoadTest | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.TextTemplating | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.TypeScript.TSServer | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Unity | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.UWP.VC.ARM64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.UWP.VC.ARM64EC | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.14.29.16.11.ARM | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.14.29.16.11.ARM64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ASAN | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATL.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATLMFC | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.CLI.Support | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.CMake.Project | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.CoreIde | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.DiagnosticTools | 17.11.35118.42 | +| Microsoft.VisualStudio.Component.VC.Llvm.Clang | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Modules.x86.x64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Redist.14.Latest | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Redist.MSM | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Runtimes.ARM.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Runtimes.ARM64EC.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Tools.ARM | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Tools.ARM64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Tools.ARM64EC | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VC.Tools.x86.x64 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Vcpkg | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.VSSDK | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Wcf.Tooling | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Web | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.WebDeploy | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows10SDK | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows10SDK.19041 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows10SDK.20348 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows11SDK.22000 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows11SDK.22621 | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Windows11Sdk.WindowsPerformanceToolkit | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.Workflow | 17.11.35102.94 | +| Microsoft.VisualStudio.Component.WslDebugging | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.ArchitectureTools.Native | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Azure.Prerequisites | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.AzureFunctions | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.All | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.Android | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.Blazor | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.iOS | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.MacCatalyst | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.Shared | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Maui.Windows | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142 | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.VC.Tools.142.x86.x64 | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.VisualStudioExtension.Prerequisites | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Web | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.Web.CloudTools | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.CMake | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.TemplateEngine | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.WindowsAppDevelopment.Prerequisites | 17.11.35102.94 | +| Microsoft.VisualStudio.ComponentGroup.WindowsAppSDK.Cs | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Azure | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.CoreEditor | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Data | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.DataScience | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.ManagedDesktop | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.ManagedGame | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NativeCrossPlat | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NativeDesktop | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NativeGame | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NativeMobile | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NetCrossPlat | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.NetWeb | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Node | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Office | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Python | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.Universal | 17.11.35102.94 | +| Microsoft.VisualStudio.Workload.VisualStudioExtension | 17.11.35102.94 | +| runtimes.ios | 8.0.824.36612 | +| runtimes.ios.net7 | 8.0.824.36612 | +| runtimes.maccatalyst | 8.0.824.36612 | +| runtimes.maccatalyst.net7 | 8.0.824.36612 | +| wasm.tools | 8.0.824.36612 | +| ProBITools.MicrosoftAnalysisServicesModelingProjects2022 | 3.0.4 | +| ProBITools.MicrosoftReportProjectsforVisualStudio2022 | 3.0.1 | +| SSIS.MicrosoftDataToolsIntegrationServices | 1.5 | +| VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects | 2.0.1 | +| Windows Driver Kit | 10.1.22621.2428 | +| Windows Driver Kit Visual Studio Extension | 10.0.26100.10 | +| Windows Software Development Kit | 10.1.22621.3233 | +| WixToolset.WixToolsetVisualStudio2022Extension | 1.0.0.22 | + +#### Microsoft Visual C++ +| Name | Architecture | Version | +| -------------------------------------------- | ------------ | ----------- | +| Microsoft Visual C++ 2013 Additional Runtime | x64 | 12.0.40660 | +| Microsoft Visual C++ 2013 Minimum Runtime | x64 | 12.0.40660 | +| Microsoft Visual C++ 2022 Additional Runtime | x64 | 14.40.33810 | +| Microsoft Visual C++ 2022 Debug Runtime | x64 | 14.40.33810 | +| Microsoft Visual C++ 2022 Minimum Runtime | x64 | 14.40.33810 | +| Microsoft Visual C++ 2022 Additional Runtime | x86 | 14.40.33810 | +| Microsoft Visual C++ 2022 Debug Runtime | x86 | 14.40.33810 | +| Microsoft Visual C++ 2022 Minimum Runtime | x86 | 14.40.33810 | + +#### Installed Windows SDKs +- 10.0.17763.0 +- 10.0.19041.0 +- 10.0.20348.0 +- 10.0.22000.0 +- 10.0.22621.0 + +### .NET Core Tools +- .NET Core SDK: 6.0.133, 6.0.203, 6.0.321, 6.0.425, 7.0.120, 7.0.203, 7.0.317, 7.0.410, 8.0.400 +- .NET Framework: 4.8, 4.8.1 +- Microsoft.AspNetCore.App: 6.0.5, 6.0.26, 6.0.32, 6.0.33, 7.0.5, 7.0.20, 8.0.8 +- Microsoft.NETCore.App: 6.0.5, 6.0.26, 6.0.32, 6.0.33, 7.0.5, 7.0.20, 8.0.8 +- Microsoft.WindowsDesktop.App: 6.0.5, 6.0.26, 6.0.33, 7.0.5, 7.0.20, 8.0.8 +- nbgv 3.6.143+57d4199a9c + +### PowerShell Tools +- PowerShell 7.4.5 + +#### Powershell Modules +- Az: 12.1.0 +- Azure: 2.1.0 (Default), 5.3.0 +- AzureRM: 2.1.0 (Default), 6.13.1 +- Azure (Cached): 3.8.0.zip, 4.2.1.zip, 5.1.1.zip +- AzureRM (Cached): 3.8.0.zip, 4.2.1.zip, 5.1.1.zip, 6.7.0.zip +- AWSPowershell: 4.1.671 +- DockerMsftProvider: 1.0.0.8 +- MarkdownPS: 1.10 +- Microsoft.Graph: 2.24.0 +- Pester: 3.4.0, 5.6.1 +- PowerShellGet: 1.0.0.1, 2.2.5 +- PSScriptAnalyzer: 1.22.0 +- PSWindowsUpdate: 2.2.1.5 +- SqlServer: 22.3.0 +- VSSetup: 2.2.16 +``` +Azure PowerShell module 2.1.0 and AzureRM PowerShell module 2.1.0 are installed +and are available via 'Get-Module -ListAvailable'. +All other versions are saved but not installed. +``` + +### Android +| Package Name | Version | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Android Command Line Tools | 8.0 | +| Android Emulator | 35.2.10 | +| Android SDK Build-tools | 35.0.0
34.0.0
33.0.0 33.0.1 33.0.2 33.0.3
32.0.0
31.0.0 | +| Android SDK Platforms | android-35 (rev 1)
android-34-ext8 (rev 1)
android-34-ext12 (rev 1)
android-34-ext11 (rev 1)
android-34-ext10 (rev 1)
android-34 (rev 3)
android-33-ext5 (rev 1)
android-33-ext4 (rev 1)
android-33 (rev 3)
android-32 (rev 1)
android-31 (rev 1) | +| Android SDK Platform-Tools | 35.0.2 | +| Android Support Repository | 47.0.0 | +| CMake | 3.18.1
3.22.1 | +| Google Play services | 49 | +| Google Repository | 58 | +| NDK | 26.3.11579264
27.1.12297006 | + +#### Environment variables +| Name | Value | +| ----------------------- | ---------------------------------------- | +| ANDROID_HOME | C:\Android\android-sdk | +| ANDROID_NDK | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_NDK_HOME | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_NDK_LATEST_HOME | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_NDK_ROOT | C:\Android\android-sdk\ndk\27.1.12297006 | +| ANDROID_SDK_ROOT | C:\Android\android-sdk | + +### Cached Docker images +| Repository:Tag | Digest | Created | +| ------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ---------- | +| mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022 | sha256:694e99af0ec996e63e6204e40e51869daf87c42d9bc628255804e32e2858b39c | 2024-09-10 | +| mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2022 | sha256:c9ff4d54c2a82dd154d3624fc8db44a9abe7913ab70f3aca5cb95213ed9621c5 | 2024-09-10 | +| mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2022 | sha256:b6d6f01a449977c597a196a95f7f10e2c2b6438f7f9920744ceb11febeb61f27 | 2024-09-10 | +| mcr.microsoft.com/windows/nanoserver:ltsc2022 | sha256:be3440ee2f36ed823a21df34e8e65026743d801dbc71e9311d03603775fc69c1 | 2024-09-05 | +| mcr.microsoft.com/windows/servercore:ltsc2022 | sha256:b7b2e5b4c2414400c4eef13db747376e0f10ef8e15b8d0587ef5b953ad4e6d43 | 2024-09-06 | diff --git a/images/windows/assets/post-gen/GenerateIISExpressCertificate.ps1 b/images/windows/assets/post-gen/GenerateIISExpressCertificate.ps1 new file mode 100644 index 0000000..609bbb4 --- /dev/null +++ b/images/windows/assets/post-gen/GenerateIISExpressCertificate.ps1 @@ -0,0 +1,40 @@ +$friendlyName = "IIS Express Development Certificate" +$certStore = "Cert:\LocalMachine\My" +$oldCert = Get-ChildItem $certStore | Where-Object FriendlyName -match $friendlyName + +if(-not $oldCert) { + Write-Host "$friendlyName certificate not found" + return +} + +Write-Host "Removing $($oldCert.Thumbprint) certificate" +Remove-Item -Path $oldCert.PSPath -Confirm:$false + +Write-Host "Creating $friendlyName certificate" +$selfSignedCertParam = @{ + Subject = "localhost" + DnsName = "localhost" + KeyAlgorithm = "RSA" + KeyLength = 2048 + NotBefore = (Get-Date) + NotAfter = (Get-Date).AddYears(5) + CertStoreLocation = $certStore + FriendlyName = $friendlyName + HashAlgorithm = "SHA256" + KeyUsage = "DigitalSignature", "KeyEncipherment", "DataEncipherment" + TextExtension = @("2.5.29.37={text}1.3.6.1.5.5.7.3.1") +} +$cert = New-SelfSignedCertificate @selfSignedCertParam + +# The app ID is the IIS Express app ID +$certThumbprint = $cert.Thumbprint +$appId = "{214124cd-d05b-4309-9af9-9caa44b2b74a}" +$startPort = 44300 +$endPort = 44399 + +Write-Host "Binding ${certThumbprint} certificate using netsh port=${startPort}:${endPort} and appID=${appId}" +$startPort..$endPort | ForEach-Object { + $port = $_ + cmd /c "netsh http delete sslcert ipport=0.0.0.0:$port > nul 2>&1" + cmd /c "netsh http add sslcert ipport=0.0.0.0:$port certhash=$certThumbprint appid=$appId certstorename=MY > nul 2>&1" +} diff --git a/images/windows/assets/post-gen/InternetExplorerConfiguration.ps1 b/images/windows/assets/post-gen/InternetExplorerConfiguration.ps1 new file mode 100644 index 0000000..230df9f --- /dev/null +++ b/images/windows/assets/post-gen/InternetExplorerConfiguration.ps1 @@ -0,0 +1,15 @@ +# https://docs.microsoft.com/en-us/troubleshoot/browsers/enhanced-security-configuration-faq#how-to-disable-internet-explorer-esc-by-using-a-script +# turn off the Internet Explorer Enhanced Security feature +Rundll32 iesetup.dll, IEHardenLMSettings, 1, True +Rundll32 iesetup.dll, IEHardenUser, 1, True +Rundll32 iesetup.dll, IEHardenAdmin, 1, True +$AdminKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" +$UserKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" +Set-ItemProperty -Path $AdminKey -Name "IsInstalled" -Value 0 -Force +Set-ItemProperty -Path $UserKey -Name "IsInstalled" -Value 0 -Force +# restart Explorer process +$ieProcess = Get-Process -Name Explorer -ErrorAction Ignore +if ($ieProcess) { + Stop-Process -Name Explorer -Force -ErrorAction Ignore +} +Write-Host "IE Enhanced Security Configuration (ESC) has been disabled." diff --git a/images/windows/assets/post-gen/Msys2FirstLaunch.ps1 b/images/windows/assets/post-gen/Msys2FirstLaunch.ps1 new file mode 100644 index 0000000..a0c8e34 --- /dev/null +++ b/images/windows/assets/post-gen/Msys2FirstLaunch.ps1 @@ -0,0 +1,2 @@ +# create user profile at the first launch +$null = cmd /c "C:\msys64\usr\bin\bash.exe -leo pipefail -c 'echo $SHELL' 2>&1" \ No newline at end of file diff --git a/images/windows/assets/post-gen/VSConfiguration.ps1 b/images/windows/assets/post-gen/VSConfiguration.ps1 new file mode 100644 index 0000000..9910889 --- /dev/null +++ b/images/windows/assets/post-gen/VSConfiguration.ps1 @@ -0,0 +1,14 @@ +$vsInstallRoot = (Get-VisualStudioInstance).InstallationPath +$devEnvPath = "$vsInstallRoot\Common7\IDE\devenv.exe" + +# Initialize Visual Studio Experimental Instance +# The Out-Null cmdlet is required to ensure PowerShell waits until the '/ResetSettings' command fully completes. +& "$devEnvPath" /RootSuffix Exp /ResetSettings General.vssettings /Command File.Exit | Out-Null + +cmd.exe /c "`"$devEnvPath`" /updateconfiguration" + +# +# https://github.com/actions/runner-images/issues/5301 +# +$warmup_vdproj = $(Join-Path $PSScriptRoot 'warmup.vdproj') +& "$devEnvPath" $warmup_vdproj /build Release | Out-Null diff --git a/images/windows/assets/post-gen/warmup.vdproj b/images/windows/assets/post-gen/warmup.vdproj new file mode 100644 index 0000000..9b4805f --- /dev/null +++ b/images/windows/assets/post-gen/warmup.vdproj @@ -0,0 +1,665 @@ +"DeployProject" +{ +"VSVersion" = "3:800" +"ProjectType" = "8:{978C614F-708E-4E1A-B201-565925725DBA}" +"IsWebType" = "8:FALSE" +"ProjectName" = "8:Setup1" +"LanguageId" = "3:1033" +"CodePage" = "3:1252" +"UILanguageId" = "3:1033" +"SccProjectName" = "8:" +"SccLocalPath" = "8:" +"SccAuxPath" = "8:" +"SccProvider" = "8:" + "Hierarchy" + { + } + "Configurations" + { + "Debug" + { + "DisplayName" = "8:Debug" + "IsDebugOnly" = "11:TRUE" + "IsReleaseOnly" = "11:FALSE" + "OutputFilename" = "8:Debug\\Setup1.msi" + "PackageFilesAs" = "3:2" + "PackageFileSize" = "3:-2147483648" + "CabType" = "3:1" + "Compression" = "3:2" + "SignOutput" = "11:FALSE" + "CertificateFile" = "8:" + "PrivateKeyFile" = "8:" + "TimeStampServer" = "8:" + "InstallerBootstrapper" = "3:2" + "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" + { + "Enabled" = "11:FALSE" + "PromptEnabled" = "11:TRUE" + "PrerequisitesLocation" = "2:1" + "Url" = "8:" + "ComponentsUrl" = "8:" + "Items" + { + "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2" + { + "Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)" + "ProductCode" = "8:.NETFramework,Version=v4.7.2" + } + } + } + } + "Release" + { + "DisplayName" = "8:Release" + "IsDebugOnly" = "11:FALSE" + "IsReleaseOnly" = "11:TRUE" + "OutputFilename" = "8:Release\\Setup1.msi" + "PackageFilesAs" = "3:2" + "PackageFileSize" = "3:-2147483648" + "CabType" = "3:1" + "Compression" = "3:2" + "SignOutput" = "11:FALSE" + "CertificateFile" = "8:" + "PrivateKeyFile" = "8:" + "TimeStampServer" = "8:" + "InstallerBootstrapper" = "3:2" + } + } + "Deployable" + { + "CustomAction" + { + } + "DefaultFeature" + { + "Name" = "8:DefaultFeature" + "Title" = "8:" + "Description" = "8:" + } + "ExternalPersistence" + { + "LaunchCondition" + { + } + } + "File" + { + } + "FileType" + { + } + "Folder" + { + "{1525181F-901A-416C-8A58-119130FE478E}:_2CFC2A48A3B644ED96FB521F59C388D4" + { + "Name" = "8:#1916" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:DesktopFolder" + "Folders" + { + } + } + "{3C67513D-01DD-4637-8A68-80971EB9504F}:_3389E1C43BBB433484C8A1DBE5968F24" + { + "DefaultLocation" = "8:[ProgramFilesFolder][Manufacturer]\\[ProductName]" + "Name" = "8:#1925" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:TARGETDIR" + "Folders" + { + } + } + "{1525181F-901A-416C-8A58-119130FE478E}:_FA2F90B46134404A830C21AAA6741088" + { + "Name" = "8:#1919" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:ProgramMenuFolder" + "Folders" + { + } + } + } + "LaunchCondition" + { + } + "Locator" + { + } + "MsiBootstrapper" + { + "LangId" = "3:1033" + "RequiresElevation" = "11:FALSE" + } + "Product" + { + "Name" = "8:Microsoft Visual Studio" + "ProductName" = "8:Setup1" + "ProductCode" = "8:{22F710BC-F7AA-4E2C-86A3-63647F82EF0D}" + "PackageCode" = "8:{3ADCCB4E-232A-40DF-A810-672AA9AD4B72}" + "UpgradeCode" = "8:{19174734-DD62-4C8E-9716-6DCFC3F06116}" + "AspNetVersion" = "8:4.0.30319.0" + "RestartWWWService" = "11:FALSE" + "RemovePreviousVersions" = "11:FALSE" + "DetectNewerInstalledVersion" = "11:TRUE" + "InstallAllUsers" = "11:FALSE" + "ProductVersion" = "8:1.0.0" + "Manufacturer" = "8:Default Company Name" + "ARPHELPTELEPHONE" = "8:" + "ARPHELPLINK" = "8:" + "Title" = "8:Setup1" + "Subject" = "8:" + "ARPCONTACT" = "8:Default Company Name" + "Keywords" = "8:" + "ARPCOMMENTS" = "8:" + "ARPURLINFOABOUT" = "8:" + "ARPPRODUCTICON" = "8:" + "ARPIconIndex" = "3:0" + "SearchPath" = "8:" + "UseSystemSearchPath" = "11:TRUE" + "TargetPlatform" = "3:0" + "PreBuildEvent" = "8:" + "PostBuildEvent" = "8:" + "RunPostBuildEvent" = "3:0" + } + "Registry" + { + "HKLM" + { + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_AE076FF564A049948DF2258C70AE5799" + { + "Name" = "8:Software" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_47FD8B4AFAAC41A48FAC411F8ABDEB59" + { + "Name" = "8:[Manufacturer]" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + } + "Values" + { + } + } + } + "Values" + { + } + } + } + } + "HKCU" + { + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_98C374DEA4C841988657D6A8CC3F6CC3" + { + "Name" = "8:Software" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_A31118797AC14549912315624636D7C5" + { + "Name" = "8:[Manufacturer]" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + } + "Values" + { + } + } + } + "Values" + { + } + } + } + } + "HKCR" + { + "Keys" + { + } + } + "HKU" + { + "Keys" + { + } + } + "HKPU" + { + "Keys" + { + } + } + } + "Sequences" + { + } + "Shortcut" + { + } + "UserInterface" + { + "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_0F20B65AC09E4AA78D95E5A7A7B152ED" + { + "UseDynamicProperties" = "11:FALSE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdUserInterface.wim" + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_1450B51044A346A59AD67962D438B359" + { + "Name" = "8:#1901" + "Sequence" = "3:1" + "Attributes" = "3:2" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_056EB6D1A4954D7D8D59FCF53B8AF02E" + { + "Sequence" = "3:100" + "DisplayName" = "8:Progress" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdProgressDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "ShowProgress" + { + "Name" = "8:ShowProgress" + "DisplayName" = "8:#1009" + "Description" = "8:#1109" + "Type" = "3:5" + "ContextData" = "8:1;True=1;False=0" + "Attributes" = "3:0" + "Setting" = "3:0" + "Value" = "3:1" + "DefaultValue" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_3F72D8AE479B4820864530F5C9607524" + { + "Name" = "8:#1900" + "Sequence" = "3:1" + "Attributes" = "3:1" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_7DE3964E30704BA99E5B564A29CC3A74" + { + "Sequence" = "3:300" + "DisplayName" = "8:Confirm Installation" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdConfirmDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_B3DAD8BF301A400680846A2BB7451D0D" + { + "Sequence" = "3:200" + "DisplayName" = "8:Installation Folder" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdFolderDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "InstallAllUsersVisible" + { + "Name" = "8:InstallAllUsersVisible" + "DisplayName" = "8:#1059" + "Description" = "8:#1159" + "Type" = "3:5" + "ContextData" = "8:1;True=1;False=0" + "Attributes" = "3:0" + "Setting" = "3:0" + "Value" = "3:1" + "DefaultValue" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_D2FF63E39B7649E6BB76320118D41624" + { + "Sequence" = "3:100" + "DisplayName" = "8:Welcome" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdWelcomeDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "CopyrightWarning" + { + "Name" = "8:CopyrightWarning" + "DisplayName" = "8:#1002" + "Description" = "8:#1102" + "Type" = "3:3" + "ContextData" = "8:" + "Attributes" = "3:0" + "Setting" = "3:1" + "Value" = "8:#1202" + "DefaultValue" = "8:#1202" + "UsePlugInResources" = "11:TRUE" + } + "Welcome" + { + "Name" = "8:Welcome" + "DisplayName" = "8:#1003" + "Description" = "8:#1103" + "Type" = "3:3" + "ContextData" = "8:" + "Attributes" = "3:0" + "Setting" = "3:1" + "Value" = "8:#1203" + "DefaultValue" = "8:#1203" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_6069164ECEB944A3AF499EF7E4AF562E" + { + "UseDynamicProperties" = "11:FALSE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdBasicDialogs.wim" + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_6E590DA0E3F44E7EB06E9B583A05C84F" + { + "Name" = "8:#1900" + "Sequence" = "3:2" + "Attributes" = "3:1" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_38649E4FCD994315918CF9F68EEDFBD4" + { + "Sequence" = "3:200" + "DisplayName" = "8:Installation Folder" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminFolderDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_883E4215F7404FD2B27299E3D09CA1EE" + { + "Sequence" = "3:300" + "DisplayName" = "8:Confirm Installation" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminConfirmDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_99855627211D468E907FCF6B662C325C" + { + "Sequence" = "3:100" + "DisplayName" = "8:Welcome" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminWelcomeDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "CopyrightWarning" + { + "Name" = "8:CopyrightWarning" + "DisplayName" = "8:#1002" + "Description" = "8:#1102" + "Type" = "3:3" + "ContextData" = "8:" + "Attributes" = "3:0" + "Setting" = "3:1" + "Value" = "8:#1202" + "DefaultValue" = "8:#1202" + "UsePlugInResources" = "11:TRUE" + } + "Welcome" + { + "Name" = "8:Welcome" + "DisplayName" = "8:#1003" + "Description" = "8:#1103" + "Type" = "3:3" + "ContextData" = "8:" + "Attributes" = "3:0" + "Setting" = "3:1" + "Value" = "8:#1203" + "DefaultValue" = "8:#1203" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_90155C3C794546069868B80B82B57EE9" + { + "Name" = "8:#1902" + "Sequence" = "3:1" + "Attributes" = "3:3" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_4490713900D94D3B9375F45EE3575B13" + { + "Sequence" = "3:100" + "DisplayName" = "8:Finished" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdFinishedDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "UpdateText" + { + "Name" = "8:UpdateText" + "DisplayName" = "8:#1058" + "Description" = "8:#1158" + "Type" = "3:15" + "ContextData" = "8:" + "Attributes" = "3:0" + "Setting" = "3:1" + "Value" = "8:#1258" + "DefaultValue" = "8:#1258" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_A7B6301BE8B0403C959609D5053EDF6E" + { + "Name" = "8:#1902" + "Sequence" = "3:2" + "Attributes" = "3:3" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_D47DBC39DB33492AA6C089D6D13580FB" + { + "Sequence" = "3:100" + "DisplayName" = "8:Finished" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminFinishedDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_BA6A3FEC255C49059A6576E2F533202F" + { + "Name" = "8:#1901" + "Sequence" = "3:2" + "Attributes" = "3:2" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_A46B26FE90274EBB937E90E163295D0B" + { + "Sequence" = "3:100" + "DisplayName" = "8:Progress" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminProgressDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + "ShowProgress" + { + "Name" = "8:ShowProgress" + "DisplayName" = "8:#1009" + "Description" = "8:#1109" + "Type" = "3:5" + "ContextData" = "8:1;True=1;False=0" + "Attributes" = "3:0" + "Setting" = "3:0" + "Value" = "3:1" + "DefaultValue" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + } + "MergeModule" + { + } + "ProjectOutput" + { + } + } +} diff --git a/images/windows/scripts/build/Configure-BaseImage.ps1 b/images/windows/scripts/build/Configure-BaseImage.ps1 new file mode 100644 index 0000000..b1b963e --- /dev/null +++ b/images/windows/scripts/build/Configure-BaseImage.ps1 @@ -0,0 +1,79 @@ +################################################################################ +## File: Configure-BaseImage.ps1 +## Desc: Prepare the base image for software installation +################################################################################ + +function Disable-InternetExplorerESC { + $adminKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" + $userKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" + Set-ItemProperty -Path $adminKey -Name "IsInstalled" -Value 0 -Force + Set-ItemProperty -Path $userKey -Name "IsInstalled" -Value 0 -Force + + $ieProcess = Get-Process -Name Explorer -ErrorAction SilentlyContinue + + if ($ieProcess) { + Stop-Process -Name Explorer -Force -ErrorAction Continue + } + + Write-Host "IE Enhanced Security Configuration (ESC) has been disabled." +} + +function Disable-InternetExplorerWelcomeScreen { + $adminKey = "HKLM:\Software\Policies\Microsoft\Internet Explorer\Main" + New-Item -Path $adminKey -Value 1 -Force + Set-ItemProperty -Path $adminKey -Name "DisableFirstRunCustomize" -Value 1 -Force + Write-Host "Disabled IE Welcome screen" +} + +function Disable-UserAccessControl { + Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "ConsentPromptBehaviorAdmin" -Value 00000000 -Force + Write-Host "User Access Control (UAC) has been disabled." +} + +function Disable-WindowsUpdate { + $autoUpdatePath = "HKLM:SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" + if (Test-Path -Path $autoUpdatePath) { + Set-ItemProperty -Path $autoUpdatePath -Name NoAutoUpdate -Value 1 + Write-Host "Disabled Windows Update" + } else { + Write-Host "Windows Update key does not exist" + } +} + +# Enable $ErrorActionPreference='Stop' for AllUsersAllHosts +Add-Content -Path $profile.AllUsersAllHosts -Value '$ErrorActionPreference="Stop"' + +Write-Host "Disable Server Manager on Logon" +Get-ScheduledTask -TaskName ServerManager | Disable-ScheduledTask + +Write-Host "Disable 'Allow your PC to be discoverable by other PCs' popup" +New-Item -Path HKLM:\System\CurrentControlSet\Control\Network -Name NewNetworkWindowOff -Force + +Write-Host 'Disable Windows Update Medic Service' +Set-ItemProperty -Path HKLM:\System\CurrentControlSet\Services\WaaSMedicSvc -Name Start -Value 4 -Force + +Write-Host "Disable Windows Update" +Disable-WindowsUpdate + +Write-Host "Disable UAC" +Disable-UserAccessControl + +Write-Host "Disable IE Welcome Screen" +Disable-InternetExplorerWelcomeScreen + +Write-Host "Disable IE ESC" +Disable-InternetExplorerESC + +Write-Host "Setting local execution policy" +Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachine -ErrorAction Continue | Out-Null +Get-ExecutionPolicy -List + +Write-Host "Enable long path behavior" +# See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file#maximum-path-length-limitation +Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1 + +# Expand disk size of OS drive +$driveLetter = "C" +$size = Get-PartitionSupportedSize -DriveLetter $driveLetter +Resize-Partition -DriveLetter $driveLetter -Size $size.SizeMax +Get-Volume | Select-Object DriveLetter, SizeRemaining, Size | Sort-Object DriveLetter diff --git a/images/windows/scripts/build/Configure-DeveloperMode.ps1 b/images/windows/scripts/build/Configure-DeveloperMode.ps1 new file mode 100644 index 0000000..7f0af3c --- /dev/null +++ b/images/windows/scripts/build/Configure-DeveloperMode.ps1 @@ -0,0 +1,13 @@ +################################################################################ +## File: Configure-DeveloperMode.ps1 +## Desc: Enables Developer Mode by toggling registry setting. Developer Mode is required to enable certain tools (e.g. WinAppDriver). +################################################################################ + +# Create AppModelUnlock if it doesn't exist, required for enabling Developer Mode +$registryKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" +if (-not(Test-Path -Path $registryKeyPath)) { + New-Item -Path $registryKeyPath -ItemType Directory -Force +} + +# Add registry value to enable Developer Mode +New-ItemProperty -Path $registryKeyPath -Name AllowDevelopmentWithoutDevLicense -PropertyType DWORD -Value 1 diff --git a/images/windows/scripts/build/Configure-Diagnostics.ps1 b/images/windows/scripts/build/Configure-Diagnostics.ps1 new file mode 100644 index 0000000..06677cd --- /dev/null +++ b/images/windows/scripts/build/Configure-Diagnostics.ps1 @@ -0,0 +1,21 @@ +################################################################################ +## File: Configure-Diagnostics.ps1 +## Desc: Disables Just-In-Time Debugger and Windows Error Reporting +################################################################################ + +Write-Host "Disable Just-In-Time Debugger" + +# Turn off Application Error Debugger +New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug" -Name Debugger -Value "-" -Type String -Force +New-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug" -Name Debugger -Value "-" -Type String -Force + +# Turn off the Debug dialog +New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\.NETFramework" -Name DbgManagedDebugger -Value "-" -Type String -Force +New-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework" -Name DbgManagedDebugger -Value "-" -Type String -Force + +# Disable the WER UI +New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting" -Name DontShowUI -Value 1 -Type DWORD -Force +# Send all reports to the user's queue +New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting" -Name ForceQueue -Value 1 -Type DWORD -Force +# Default consent choice 1 - Always ask (default) +New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting\Consent" -Name DefaultConsent -Value 1 -Type DWORD -Force diff --git a/images/windows/scripts/build/Configure-DotnetSecureChannel.ps1 b/images/windows/scripts/build/Configure-DotnetSecureChannel.ps1 new file mode 100644 index 0000000..54732fe --- /dev/null +++ b/images/windows/scripts/build/Configure-DotnetSecureChannel.ps1 @@ -0,0 +1,16 @@ +################################################################################ +## File: Configure-DotnetSecureChannel.ps1 +## Desc: Configure .NET to use TLS 1.2 +################################################################################ + +$registryPath = "HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319" +$name = "SchUseStrongCrypto" +$value = "1" +if (Test-Path $registryPath) { + Set-ItemProperty -Path $registryPath -Name $name -Value $value -Type DWORD +} + +$registryPath = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319" +if (Test-Path $registryPath) { + Set-ItemProperty -Path $registryPath -Name $name -Value $value -Type DWORD +} diff --git a/images/windows/scripts/build/Configure-DynamicPort.ps1 b/images/windows/scripts/build/Configure-DynamicPort.ps1 new file mode 100644 index 0000000..55864dc --- /dev/null +++ b/images/windows/scripts/build/Configure-DynamicPort.ps1 @@ -0,0 +1,27 @@ +################################################################################ +## File: Configure-DynamicPort.ps1 +## Desc: Configure dynamic port range for TCP and UDP to start at port 49152 +## and to end at the 65536 (16384 ports) +################################################################################ + +# https://support.microsoft.com/en-us/help/929851/the-default-dynamic-port-range-for-tcp-ip-has-changed-in-windows-vista +# The new default start port is 49152, and the new default end port is 65535. +# Default port configuration was changed during image generation by Visual Studio Enterprise Installer to: +# Protocol tcp Dynamic Port Range +# --------------------------------- +# Start Port : 1024 +# Number of Ports : 64511 + +Write-Host "Set the dynamic port range to start at port 49152 and to end at the 65536 (16384 ports)" +foreach ($ipVersion in @("ipv4", "ipv6")) { + foreach ($protocol in @("tcp", "udp")) { + $command = "netsh int $ipVersion set dynamicport $protocol start=49152 num=16384" + Invoke-Expression $command | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Host "Failed to set dynamic port range for $ipVersion $protocol" + exit $LASTEXITCODE + } + } +} + +Invoke-PesterTests -TestFile "WindowsFeatures" -TestName "DynamicPorts" diff --git a/images/windows/scripts/build/Configure-GDIProcessHandleQuota.ps1 b/images/windows/scripts/build/Configure-GDIProcessHandleQuota.ps1 new file mode 100644 index 0000000..7e5cad3 --- /dev/null +++ b/images/windows/scripts/build/Configure-GDIProcessHandleQuota.ps1 @@ -0,0 +1,14 @@ +################################################################################ +## File: Configure-GDIProcessHandleQuota.ps1 +## Desc: Set the GDIProcessHandleQuota value to 20000 +################################################################################ + +# https://docs.microsoft.com/en-us/windows/win32/sysinfo/gdi-objects +# This value can be set to a number between 256 and 65,536 + +$defaultValue = 20000 +Write-Host "Set the GDIProcessHandleQuota value to $defaultValue" +Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" -Name GDIProcessHandleQuota -Value $defaultValue +Set-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows" -Name GDIProcessHandleQuota -Value $defaultValue + +Invoke-PesterTests -TestFile "WindowsFeatures" -TestName "GDIProcessHandleQuota" diff --git a/images/windows/scripts/build/Configure-ImageDataFile.ps1 b/images/windows/scripts/build/Configure-ImageDataFile.ps1 new file mode 100644 index 0000000..71c9bd0 --- /dev/null +++ b/images/windows/scripts/build/Configure-ImageDataFile.ps1 @@ -0,0 +1,50 @@ +################################################################################ +## File: Configure-ImageDataFile.ps1 +## Desc: Creates a JSON file with information about the image +################################################################################ + +$os = Get-CimInstance -ClassName Win32_OperatingSystem +$caption = $os.Caption +$osName = $caption.Substring(0, $caption.LastIndexOf(" ")) +$osEdition = $caption.Substring($caption.LastIndexOf(" ") + 1) +$osVersion = $os.Version +$imageVersion = $env:IMAGE_VERSION +$imageVersionComponents = $imageVersion.Split('.') +$imageMajorVersion = $imageVersionComponents[0] +$imageMinorVersion = $imageVersionComponents[1] +$imageDataFile = $env:IMAGEDATA_FILE +$githubUrl = "https://github.com/actions/runner-images/blob" + +if (Test-IsWin25) { + $imageLabel = "windows-2025" + $softwareUrl = "${githubUrl}/win25/$imageMajorVersion.$imageMinorVersion/images/windows/Windows2025-Readme.md" + $releaseUrl = "https://github.com/actions/runner-images/releases/tag/win25%2F$imageMajorVersion.$imageMinorVersion" + +} elseif (Test-IsWin22) { + $imageLabel = "windows-2022" + $softwareUrl = "${githubUrl}/win22/$imageMajorVersion.$imageMinorVersion/images/windows/Windows2022-Readme.md" + $releaseUrl = "https://github.com/actions/runner-images/releases/tag/win22%2F$imageMajorVersion.$imageMinorVersion" + +} elseif (Test-IsWin19) { + $imageLabel = "windows-2019" + $softwareUrl = "${githubUrl}/win19/$imageMajorVersion.$imageMinorVersion/images/windows/Windows2019-Readme.md" + $releaseUrl = "https://github.com/actions/runner-images/releases/tag/win19%2F$imageMajorVersion.$imageMinorVersion" + +} else { + throw "Invalid platform version is found. Either Windows Server 2019 or 2022 are required" +} + +$json = @" +[ + { + "group": "Operating System", + "detail": "${osName}\n${osVersion}\n${osEdition}" + }, + { + "group": "Runner Image", + "detail": "Image: ${imageLabel}\nVersion: ${imageVersion}\nIncluded Software: ${softwareUrl}\nImage Release: ${releaseUrl}" + } +] +"@ + +$json | Out-File -FilePath $imageDataFile diff --git a/images/windows/scripts/build/Configure-PowerShell.ps1 b/images/windows/scripts/build/Configure-PowerShell.ps1 new file mode 100644 index 0000000..e8df183 --- /dev/null +++ b/images/windows/scripts/build/Configure-PowerShell.ps1 @@ -0,0 +1,38 @@ +################################################################################ +## File: Configure-Powershell.ps1 +## Desc: Manage PowerShell configuration +################################################################################ + +#region System +Write-Host "Setup PowerShellGet" +Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force + +# Specifies the installation policy +Set-PSRepository -InstallationPolicy Trusted -Name PSGallery + +Write-Host 'Warmup PSModuleAnalysisCachePath (speedup first powershell invocation by 20s)' +$PSModuleAnalysisCachePath = 'C:\PSModuleAnalysisCachePath\ModuleAnalysisCache' + +[Environment]::SetEnvironmentVariable('PSModuleAnalysisCachePath', $PSModuleAnalysisCachePath, "Machine") +# make variable to be available in the current session +${env:PSModuleAnalysisCachePath} = $PSModuleAnalysisCachePath + +New-Item -Path $PSModuleAnalysisCachePath -ItemType 'File' -Force | Out-Null +#endregion + +#region User (current user, image generation only) +if (-not (Test-Path $profile)) { + New-Item $profile -ItemType File -Force +} + +@" + if ( -not(Get-Module -ListAvailable -Name PowerHTML)) { + Install-Module PowerHTML -Scope CurrentUser + } + + if ( -not(Get-Module -Name PowerHTML)) { + Import-Module PowerHTML + } +"@ | Add-Content -Path $profile -Force + +#endregion diff --git a/images/windows/scripts/build/Configure-Shell.ps1 b/images/windows/scripts/build/Configure-Shell.ps1 new file mode 100644 index 0000000..f9ac961 --- /dev/null +++ b/images/windows/scripts/build/Configure-Shell.ps1 @@ -0,0 +1,19 @@ +# Create shells folder +$shellPath = "C:\shells" +New-Item -Path $shellPath -ItemType Directory | Out-Null + +# add a wrapper for C:\msys64\usr\bin\bash.exe +@' +@echo off +setlocal +IF NOT DEFINED MSYS2_PATH_TYPE set MSYS2_PATH_TYPE=strict +IF NOT DEFINED MSYSTEM set MSYSTEM=mingw64 +set CHERE_INVOKING=1 +C:\msys64\usr\bin\bash.exe -leo pipefail %* +'@ | Out-File -FilePath "$shellPath\msys2bash.cmd" -Encoding ascii + +# gitbash <--> C:\Program Files\Git\bin\bash.exe +New-Item -ItemType SymbolicLink -Path "$shellPath\gitbash.exe" -Target "$env:ProgramFiles\Git\bin\bash.exe" | Out-Null + +# wslbash <--> C:\Windows\System32\bash.exe +New-Item -ItemType SymbolicLink -Path "$shellPath\wslbash.exe" -Target "$env:SystemRoot\System32\bash.exe" | Out-Null diff --git a/images/windows/scripts/build/Configure-System.ps1 b/images/windows/scripts/build/Configure-System.ps1 new file mode 100644 index 0000000..d02dde2 --- /dev/null +++ b/images/windows/scripts/build/Configure-System.ps1 @@ -0,0 +1,196 @@ +################################################################################ +## File: Configure-System.ps1 +## Desc: Applies various configuration settings to the final image +################################################################################ + +Write-Host "Cleanup WinSxS" +dism.exe /online /Cleanup-Image /StartComponentCleanup /ResetBase +if ($LASTEXITCODE -ne 0) { + throw "Failed to cleanup WinSxS" +} + +# Set default version to 1 for WSL (aka LXSS - Linux Subsystem) +# The value should be set in the default user registry hive +# https://github.com/actions/runner-images/issues/5760 +if (Test-IsWin22) { + Write-Host "Setting WSL default version to 1" + + Mount-RegistryHive ` + -FileName "C:\Users\Default\NTUSER.DAT" ` + -SubKey "HKLM\DEFAULT" + + # Create the key if it doesn't exist + $keyPath = "DEFAULT\Software\Microsoft\Windows\CurrentVersion\Lxss" + if (-not (Test-Path $keyPath)) { + Write-Host "Creating $keyPath key" + New-Item -Path (Join-Path "HKLM:\" $keyPath) -Force | Out-Null + } + + # Set the DefaultVersion value to 1 + $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($keyPath, $true) + $key.SetValue("DefaultVersion", "1", "DWord") + $key.Handle.Close() + [System.GC]::Collect() + + Dismount-RegistryHive "HKLM\DEFAULT" +} + +Write-Host "Clean up various directories" +@( + "$env:SystemDrive\Recovery", + "$env:SystemRoot\logs", + "$env:SystemRoot\winsxs\manifestcache", + "$env:SystemRoot\Temp", + "$env:SystemDrive\Users\$env:INSTALL_USER\AppData\Local\Temp", + "$env:TEMP", + "$env:AZURE_CONFIG_DIR\logs", + "$env:AZURE_CONFIG_DIR\commands", + "$env:AZURE_CONFIG_DIR\telemetry" +) | ForEach-Object { + if (Test-Path $_) { + Write-Host "Removing $_" + cmd /c "takeown /d Y /R /f $_ 2>&1" | Out-Null + if ($LASTEXITCODE -ne 0) { + throw "Failed to take ownership of $_" + } + cmd /c "icacls $_ /grant:r administrators:f /t /c /q 2>&1" | Out-Null + if ($LASTEXITCODE -ne 0) { + throw "Failed to grant administrators full control of $_" + } + Remove-Item $_ -Recurse -Force -ErrorAction SilentlyContinue | Out-Null + } +} + +$winInstallDir = "$env:SystemRoot\Installer" +New-Item -Path $winInstallDir -ItemType Directory -Force | Out-Null + +# Remove AllUsersAllHosts profile +Remove-Item $profile.AllUsersAllHosts -Force -ErrorAction SilentlyContinue | Out-Null + +# Clean yarn and npm cache +cmd /c "yarn cache clean 2>&1" | Out-Null +if ($LASTEXITCODE -ne 0) { + throw "Failed to clean yarn cache" +} + +cmd /c "npm cache clean --force 2>&1" | Out-Null +if ($LASTEXITCODE -ne 0) { + throw "Failed to clean npm cache" +} + +# allow msi to write to temp folder +# see https://github.com/actions/runner-images/issues/1704 +cmd /c "icacls $env:SystemRoot\Temp /grant Users:f /t /c /q 2>&1" | Out-Null +if ($LASTEXITCODE -ne 0) { + throw "Failed to grant Users full control of $env:SystemRoot\Temp" +} + +# Registry settings +$registrySettings = @( + @{Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"; Name = "AUOptions"; Value = 1; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"; Name = "NoAutoUpdate"; Value = 1; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"; Name = "DoNotConnectToWindowsUpdateInternetLocations"; Value = 1; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"; Name = "DisableWindowsUpdateAccess"; Value = 1; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Device Metadata"; Name = "PreventDeviceMetadataFromNetwork"; Value = 1; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection"; Name = "AllowTelemetry"; Value = 0; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Policies\Microsoft\SQMClient\Windows"; Name = "CEIPEnable"; Value = 0; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppCompat"; Name = "AITEnable"; Value = 0; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppCompat"; Name = "DisableUAR"; Value = 1; PropertyType = "DWORD" } + @{Path = "HKLM:\Software\Policies\Microsoft\Windows\DataCollection"; Name = "AllowTelemetry"; Value = 0; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\DataCollection"; Name = "AllowTelemetry"; Value = 0; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\Maintenance"; Name = "MaintenanceDisabled"; Value = 1; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Policies\Microsoft\MRT"; Name = "DontOfferThroughWUAU"; Value = 1; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Policies\Microsoft\MRT"; Name = "DontReportInfectionInformation"; Value = 1; PropertyType = "DWORD" } + @{Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search"; Name = "AllowCortana"; Value = 0; PropertyType = "DWORD" } + @{Path = "HKLM:\SYSTEM\CurrentControlSet\Control"; Name = "ServicesPipeTimeout"; Value = 120000; PropertyType = "DWORD" } + @{Path = "HKLM:\SYSTEM\CurrentControlSet\Control\WMI\AutoLogger\AutoLogger-Diagtrack-Listener"; Name = "Start"; Value = 0; PropertyType = "DWORD" } + @{Path = "HKLM:\SYSTEM\CurrentControlSet\Control\WMI\AutoLogger\SQMLogger"; Name = "Start"; Value = 0; PropertyType = "DWORD" } +) + +$registrySettings | ForEach-Object { + $regPath = $_.Path + if (-not (Test-Path $regPath)) { + New-Item -Path $regPath -Force -ErrorAction Ignore | Out-Null + } + New-ItemProperty @_ -Force -ErrorAction Ignore +} | Out-Null + +# Disable Template Services / User Services added by Desktop Experience +$regUserServicesToDisables = @( + "HKLM:\SYSTEM\CurrentControlSet\Services\CDPUserSvc" + "HKLM:\SYSTEM\CurrentControlSet\Services\OneSyncSvc" + "HKLM:\SYSTEM\CurrentControlSet\Services\PimIndexMaintenanceSvc" + "HKLM:\SYSTEM\CurrentControlSet\Services\UnistoreSvc" + "HKLM:\SYSTEM\CurrentControlSet\Services\UserDataSvc" +) + +$regUserServicesToDisables | ForEach-Object { + $regPath = $_ + if (-not (Test-Path $regPath)) { + New-Item -Path $regPath -Force -ErrorAction Ignore | Out-Null + } + New-ItemProperty -Path $regPath -Name "Start" -Value 4 -PropertyType DWORD -Force -ErrorAction Ignore + New-ItemProperty -Path $regPath -Name "UserServiceFlags" -Value 0 -PropertyType DWORD -Force -ErrorAction Ignore +} | Out-Null + + +Write-Host 'Disable Windows Update Service' +Set-ItemProperty -Path HKLM:\System\CurrentControlSet\Services\wuauserv -Name Start -Value 4 -Force + +# Disabled services +$servicesToDisable = @( + 'wuauserv' + 'DiagTrack' + 'dmwappushservice' + 'PcaSvc' + 'SysMain' + 'gupdate' + 'gupdatem' + 'StorSvc' +) | Get-Service -ErrorAction SilentlyContinue +Stop-Service $servicesToDisable +$servicesToDisable.WaitForStatus('Stopped', "00:01:00") +$servicesToDisable | Set-Service -StartupType Disabled + +# Disable scheduled tasks +$allTasksInTaskPath = @( + "\" + "\Microsoft\Azure\Security\" + "\Microsoft\VisualStudio\" + "\Microsoft\VisualStudio\Updates\" + "\Microsoft\Windows\Application Experience\" + "\Microsoft\Windows\ApplicationData\" + "\Microsoft\Windows\Autochk\" + "\Microsoft\Windows\Chkdsk\" + "\Microsoft\Windows\Customer Experience Improvement Program\" + "\Microsoft\Windows\Data Integrity Scan\" + "\Microsoft\Windows\Defrag\" + "\Microsoft\Windows\Diagnosis\" + "\Microsoft\Windows\DiskCleanup\" + "\Microsoft\Windows\DiskDiagnostic\" + "\Microsoft\Windows\Maintenance\" + "\Microsoft\Windows\PI\" + "\Microsoft\Windows\Power Efficiency Diagnostics\" + "\Microsoft\Windows\Server Manager\" + "\Microsoft\Windows\Speech\" + "\Microsoft\Windows\UpdateOrchestrator\" + "\Microsoft\Windows\Windows Error Reporting\" + "\Microsoft\Windows\WindowsUpdate\" + "\Microsoft\XblGameSave\" +) + +$allTasksInTaskPath | ForEach-Object { + Get-ScheduledTask -TaskPath $_ -ErrorAction Ignore | Disable-ScheduledTask -ErrorAction Ignore +} | Out-Null + +$disableTaskNames = @( + @{TaskPath = "\Microsoft\Windows\.NET Framework\"; TaskName = ".NET Framework NGEN v4.0.30319" } + @{TaskPath = "\Microsoft\Windows\.NET Framework\"; TaskName = ".NET Framework NGEN v4.0.30319 64" } + @{TaskPath = "\Microsoft\Windows\AppID\"; TaskName = "SmartScreenSpecific" } +) + +$disableTaskNames | ForEach-Object { + Disable-ScheduledTask @PSItem -ErrorAction Ignore +} | Out-Null + +Write-Host "Configure-System.ps1 - completed" diff --git a/images/windows/scripts/build/Configure-SystemEnvironment.ps1 b/images/windows/scripts/build/Configure-SystemEnvironment.ps1 new file mode 100644 index 0000000..f085959 --- /dev/null +++ b/images/windows/scripts/build/Configure-SystemEnvironment.ps1 @@ -0,0 +1,15 @@ +################################################################################ +## File: Configure-SystemEnvironment.ps1 +## Desc: Configures system environment variables +################################################################################ + +$variables = @{ + "ImageVersion" = $env:IMAGE_VERSION + "ImageOS" = $env:IMAGE_OS + "AGENT_TOOLSDIRECTORY" = $env:AGENT_TOOLSDIRECTORY + "RUNNER_TOOL_CACHE" = $env:AGENT_TOOLSDIRECTORY +} + +$variables.GetEnumerator() | ForEach-Object { + [Environment]::SetEnvironmentVariable($_.Key, $_.Value, "Machine") +} diff --git a/images/windows/scripts/build/Configure-Toolset.ps1 b/images/windows/scripts/build/Configure-Toolset.ps1 new file mode 100644 index 0000000..de8da36 --- /dev/null +++ b/images/windows/scripts/build/Configure-Toolset.ps1 @@ -0,0 +1,60 @@ +################################################################################ +## File: Configure-Toolset.ps1 +## Team: CI-Build +## Desc: Configure Toolset +################################################################################ + +$toolEnvConfigs = @{ + Python = @{ + pathTemplates = @( + "{0}" + "{0}\Scripts" + ) + } + go = @{ + pathTemplates = @( + "{0}\bin" + ) + envVarTemplate = "GOROOT_{0}_{1}_X64" + } +} + +$tools = Get-ToolsetContent ` +| Select-Object -ExpandProperty toolcache ` +| Where-Object { $toolEnvConfigs.Keys -contains $_.name } + +Write-Host "Configure toolset tools environment..." +foreach ($tool in $tools) { + $toolEnvConfig = $toolEnvConfigs[$tool.name] + + if (-not ([string]::IsNullOrEmpty($toolEnvConfig.envVarTemplate))) { + foreach ($version in $tool.versions) { + Write-Host "Set $($tool.name) $version environment variable..." + + $foundVersionArchPath = Get-TCToolVersionPath -Name $tool.name -Version $version -Arch $tool.arch + $envName = $toolEnvConfig.envVarTemplate -f $version.Split(".") + + Write-Host "Set $envName to $foundVersionArchPath" + [Environment]::SetEnvironmentVariable($envName, $foundVersionArchPath, "Machine") + } + } + + if (-not ([string]::IsNullOrEmpty($tool.default))) { + Write-Host "Use $($tool.name) $($tool.default) as a system $($tool.name)..." + + $toolVersionPath = Get-TCToolVersionPath -Name $tool.name -Version $tool.default -Arch $tool.arch + + foreach ($template in $toolEnvConfig.pathTemplates) { + $toolSystemPath = $template -f $toolVersionPath + Write-Host "Add $toolSystemPath to system PATH..." + Add-MachinePathItem -PathItem $toolSystemPath | Out-Null + } + + if (-not ([string]::IsNullOrEmpty($tool.defaultVariable))) { + Write-Host "Set $($tool.name) $($tool.default) $($tool.defaultVariable) environment variable..." + [Environment]::SetEnvironmentVariable($tool.defaultVariable, $toolVersionPath, "Machine") + } + } +} + +Invoke-PesterTests -TestFile "Toolset" diff --git a/images/windows/scripts/build/Configure-User.ps1 b/images/windows/scripts/build/Configure-User.ps1 new file mode 100644 index 0000000..c110982 --- /dev/null +++ b/images/windows/scripts/build/Configure-User.ps1 @@ -0,0 +1,47 @@ +################################################################################ +## File: Configure-User.ps1 +## Desc: Performs user part of warm up and moves data to C:\Users\Default +################################################################################ + +# +# more: https://github.com/actions/runner-images-internal/issues/5320 +# https://github.com/actions/runner-images/issues/5301#issuecomment-1648292990 +# + +Write-Host "Warmup 'devenv.exe /updateconfiguration'" +$vsInstallRoot = (Get-VisualStudioInstance).InstallationPath +cmd.exe /c "`"$vsInstallRoot\Common7\IDE\devenv.exe`" /updateconfiguration" +if ($LASTEXITCODE -ne 0) { + throw "Failed to warmup 'devenv.exe /updateconfiguration'" +} + +# we are fine if some file is locked and cannot be copied +Copy-Item ${env:USERPROFILE}\AppData\Local\Microsoft\VisualStudio -Destination c:\users\default\AppData\Local\Microsoft\VisualStudio -Recurse -ErrorAction SilentlyContinue + +Mount-RegistryHive ` + -FileName "C:\Users\Default\NTUSER.DAT" ` + -SubKey "HKLM\DEFAULT" + +reg.exe copy HKCU\Software\Microsoft\VisualStudio HKLM\DEFAULT\Software\Microsoft\VisualStudio /s +if ($LASTEXITCODE -ne 0) { + throw "Failed to copy HKCU\Software\Microsoft\VisualStudio to HKLM\DEFAULT\Software\Microsoft\VisualStudio" +} + +if (-not (Test-IsWin25)) { + # disable TSVNCache.exe + $registryKeyPath = 'HKCU:\Software\TortoiseSVN' + if (-not(Test-Path -Path $registryKeyPath)) { + New-Item -Path $registryKeyPath -ItemType Directory -Force + } + + + New-ItemProperty -Path $registryKeyPath -Name CacheType -PropertyType DWORD -Value 0 + reg.exe copy HKCU\Software\TortoiseSVN HKLM\DEFAULT\Software\TortoiseSVN /s + if ($LASTEXITCODE -ne 0) { + throw "Failed to copy HKCU\Software\TortoiseSVN to HKLM\DEFAULT\Software\TortoiseSVN" + } +} + +Dismount-RegistryHive "HKLM\DEFAULT" + +Write-Host "Configure-User.ps1 - completed" diff --git a/images/windows/scripts/build/Configure-WindowsDefender.ps1 b/images/windows/scripts/build/Configure-WindowsDefender.ps1 new file mode 100644 index 0000000..c84d47e --- /dev/null +++ b/images/windows/scripts/build/Configure-WindowsDefender.ps1 @@ -0,0 +1,44 @@ +################################################################################ +## File: Configure-WindowsDefender.ps1 +## Desc: Disables Windows Defender +################################################################################ + +Write-Host "Disable Windows Defender..." +$avPreference = @( + @{DisableArchiveScanning = $true} + @{DisableAutoExclusions = $true} + @{DisableBehaviorMonitoring = $true} + @{DisableBlockAtFirstSeen = $true} + @{DisableCatchupFullScan = $true} + @{DisableCatchupQuickScan = $true} + @{DisableIntrusionPreventionSystem = $true} + @{DisableIOAVProtection = $true} + @{DisablePrivacyMode = $true} + @{DisableScanningNetworkFiles = $true} + @{DisableScriptScanning = $true} + @{MAPSReporting = 0} + @{PUAProtection = 0} + @{SignatureDisableUpdateOnStartupWithoutEngine = $true} + @{SubmitSamplesConsent = 2} + @{ScanAvgCPULoadFactor = 5; ExclusionPath = @("D:\", "C:\")} + @{DisableRealtimeMonitoring = $true} + @{ScanScheduleDay = 8} +) + +$avPreference += @( + @{EnableControlledFolderAccess = "Disable"} + @{EnableNetworkProtection = "Disabled"} +) + +$avPreference | Foreach-Object { + $avParams = $_ + Set-MpPreference @avParams +} + +# https://github.com/actions/runner-images/issues/4277 +# https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/microsoft-defender-antivirus-compatibility?view=o365-worldwide +$atpRegPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows Advanced Threat Protection' +if (Test-Path $atpRegPath) { + Write-Host "Set Microsoft Defender Antivirus to passive mode" + Set-ItemProperty -Path $atpRegPath -Name 'ForceDefenderPassiveMode' -Value '1' -Type 'DWORD' +} diff --git a/images/windows/scripts/build/Install-AWSTools.ps1 b/images/windows/scripts/build/Install-AWSTools.ps1 new file mode 100644 index 0000000..22ad5b4 --- /dev/null +++ b/images/windows/scripts/build/Install-AWSTools.ps1 @@ -0,0 +1,32 @@ +################################################################################ +## File: Install-AWSTools.ps1 +## Desc: Install AWS tools: CLI, Session Manager Plugin, AWS SAM CLI +## Supply chain security: AWS CLI - managed by package manager, Session Manager Plugin for the AWS CLI - missing, AWS SAM CLI - checksum validation +################################################################################ + +# Install AWS CLI +Install-ChocoPackage awscli + +# Install Session Manager Plugin for the AWS CLI +Install-Binary ` + -Url "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/windows/SessionManagerPluginSetup.exe" ` + -InstallArgs ("/silent", "/install") ` + -ExpectedSignature "75A5FB4D02FCB2AB799718F315BAAA3103E9D60C" +$env:Path = $env:Path + ";$env:ProgramFiles\Amazon\SessionManagerPlugin\bin" + +# Install AWS SAM CLI +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "awslabs/aws-sam-cli" ` + -Version "latest" ` + -UrlMatchPattern "AWS_SAM_CLI_64_PY3.msi" +$externalHash = Get-ChecksumFromGithubRelease ` + -Repo "awslabs/aws-sam-cli" ` + -Version "latest" ` + -FileName (Split-Path $downloadUrl -Leaf) ` + -HashType "SHA256" + +Install-Binary ` + -Url $downloadUrl ` + -ExpectedSHA256Sum $externalHash + +Invoke-PesterTests -TestFile "CLI.Tools" -TestName "AWS" diff --git a/images/windows/scripts/build/Install-ActionsCache.ps1 b/images/windows/scripts/build/Install-ActionsCache.ps1 new file mode 100644 index 0000000..118279e --- /dev/null +++ b/images/windows/scripts/build/Install-ActionsCache.ps1 @@ -0,0 +1,27 @@ +################################################################################ +## File: Install-ActionsCache.ps1 +## Desc: Downloads latest release from https://github.com/actions/action-versions +## Maintainer: #actions-runtime and @TingluoHuang +################################################################################ + +$actionArchiveCache = "C:\actionarchivecache\" + +if (-not (Test-Path $actionArchiveCache)) { + Write-Host "Creating action archive cache folder" + New-Item -ItemType Directory -Path $actionArchiveCache | Out-Null +} + +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "actions/action-versions" ` + -Version "latest" ` + -Asset "action-versions.zip" + +Write-Host "Download Latest action-versions archive from $downloadUrl" +$actionVersionsArchivePath = Invoke-DownloadWithRetry $downloadUrl + +Write-Host "Expand action-versions archive" +Expand-7ZipArchive -Path $actionVersionsArchivePath -DestinationPath $actionArchiveCache + +[Environment]::SetEnvironmentVariable("ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE", $actionArchiveCache, "Machine") + +Invoke-PesterTests -TestFile "ActionArchiveCache" diff --git a/images/windows/scripts/build/Install-AliyunCli.ps1 b/images/windows/scripts/build/Install-AliyunCli.ps1 new file mode 100644 index 0000000..a922c72 --- /dev/null +++ b/images/windows/scripts/build/Install-AliyunCli.ps1 @@ -0,0 +1,30 @@ +################################################################################ +## File: Install-AliyunCli.ps1 +## Desc: Install Alibaba Cloud CLI +## Supply chain security: Alibaba Cloud CLI - checksum validation +################################################################################ + +Write-Host "Download Latest aliyun-cli archive" +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "aliyun/aliyun-cli" ` + -Version "latest" ` + -UrlMatchPattern "aliyun-cli-windows-*-amd64.zip" +$packagePath = Invoke-DownloadWithRetry $downloadUrl + +#region Supply chain security - Alibaba Cloud CLI +$packageName = Split-Path $downloadUrl -Leaf +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url ($downloadUrl -replace $packageName, "SHASUMS256.txt") ` + -FileName $packageName +Test-FileChecksum $packagePath -ExpectedSHA256Sum $externalHash +#endregion + +Write-Host "Expand aliyun-cli archive" +$aliyunPath = "C:\aliyun-cli" +New-Item -Path $aliyunPath -ItemType Directory -Force +Expand-7ZipArchive -Path $packagePath -DestinationPath $aliyunPath + +# Add aliyun-cli to path +Add-MachinePathItem $aliyunPath + +Invoke-PesterTests -TestFile "CLI.Tools" -TestName "Aliyun CLI" diff --git a/images/windows/scripts/build/Install-AndroidSDK.ps1 b/images/windows/scripts/build/Install-AndroidSDK.ps1 new file mode 100644 index 0000000..1d5668d --- /dev/null +++ b/images/windows/scripts/build/Install-AndroidSDK.ps1 @@ -0,0 +1,166 @@ +################################################################################ +## File: Install-AndroidSDK.ps1 +## Desc: Install and update Android SDK and tools +## Supply chain security: checksum validation +################################################################################ + +# Actual Android SDK installation directory +$SDKInstallRoot = "C:\Program Files (x86)\Android\android-sdk" + +# Hardlink to the Android SDK installation directory with no spaces in the path. +# ANDROID_NDK* env vars should not contain spaces, otherwise ndk-build.cmd gives an error +# https://github.com/actions/runner-images/issues/1122 +$SDKRootPath = "C:\Android\android-sdk" + +#region functions +function Install-AndroidSDKPackages { + <# + .SYNOPSIS + This function installs the specified Android SDK packages. + + .DESCRIPTION + The Install-AndroidSDKPackages function takes an array of package names as a parameter and installs each of them using the sdkmanager.bat script. + + .PARAMETER Packages + An array of package names in the format of SDK-style paths to be installed. + + .EXAMPLE + Install-AndroidSDKPackages -Packages "platforms;android-29", "build-tools;29.0.2" + + This command installs the Android SDK Platform 29 and Build-Tools 29.0.2. + + #> + Param + ( + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [AllowNull()] + [string[]] $Packages + ) + + # The sdkmanager.bat script is used to install Android SDK packages. + $SDKManager = "$SDKRootPath\cmdline-tools\latest\bin\sdkmanager.bat" + + $errors = @() + + foreach ($package in $Packages) { + & $SDKManager --install "$package" --sdk_root=$SDKRootPath + if ($LASTEXITCODE -ne 0) { + $errors += "Failed to install package $package with exit code $LASTEXITCODE" + } + } + + if ($errors.Count -gt 0) { + throw $errors + } +} +#endregion + +# get packages to install from the toolset +$androidToolset = (Get-ToolsetContent).android +# Newer version(s) require Java 11 by default +# See https://github.com/actions/runner-images/issues/6960 +$cmdlineToolsUrl = $androidToolset.commandline_tools_url +$cmdlineToolsArchPath = Invoke-DownloadWithRetry $cmdlineToolsUrl + +Test-FileChecksum $cmdlineToolsArchPath -ExpectedSHA256Sum $androidToolset.hash + +Expand-7ZipArchive -Path $cmdlineToolsArchPath -DestinationPath "${SDKInstallRoot}\cmdline-tools" + +# cmdline tools should be installed in ${SDKInstallRoot}\cmdline-tools\latest\bin, but archive contains ${SDKInstallRoot}\cmdline-tools\bin +# we need to create the proper folder structure +Invoke-ScriptBlockWithRetry -Command { + Rename-Item "${SDKInstallRoot}\cmdline-tools\cmdline-tools" "latest" -ErrorAction Stop +} + +# Create hardlink at $SDKRootPath pointing to SDK installation directory in Program Files +New-Item -Path (Split-Path $SDKRootPath -Parent) -ItemType Directory -Force +New-Item -Path "$SDKRootPath" -ItemType SymbolicLink -Value "$SDKInstallRoot" + +# Install the standard Android SDK licenses. Currently, there isn't a better way to do this, +# so we are base64-encoded a zip of the licenses directory from another installation. +# To create this base64 string, create a zip file that contains nothing but a 'licenses' folder, +# which folder contains the accepted license files found in 'C:\Program Files (x86)\Android\android-sdk\licenses'. +# Then, run this in PowerShell: +# $LicensesZipFileName = 'C:\Program Files (x86)\Android\android-sdk\Licenses.zip' +# $base64Content = [Convert]::ToBase64String([IO.File]::ReadAllBytes($LicensesZipFileName)) +# echo $base64Content +# Another possible solution that works in powershell core: +# Write-Ouptut "y" | $sdkmanager.bat +$licenseContentBase64 = "UEsDBBQAAAAAAKNK11IAAAAAAAAAAAAAAAAJAAAAbGljZW5zZXMvUEsDBAoAAAAAAJ1K11K7n0IrKgAAACoAAAAhAAAAbGljZW5zZXMvYW5kcm9pZC1nb29nbGV0di1saWNlbnNlDQo2MDEwODViOTRjZDc3ZjBiNTRmZjg2NDA2OTU3MDk5ZWJlNzljNGQ2UEsDBAoAAAAAAKBK11LzQumJKgAAACoAAAAkAAAAbGljZW5zZXMvYW5kcm9pZC1zZGstYXJtLWRidC1saWNlbnNlDQo4NTlmMzE3Njk2ZjY3ZWYzZDdmMzBhNTBhNTU2MGU3ODM0YjQzOTAzUEsDBAoAAAAAAKFK11IKSOJFKgAAACoAAAAcAAAAbGljZW5zZXMvYW5kcm9pZC1zZGstbGljZW5zZQ0KMjQzMzNmOGE2M2I2ODI1ZWE5YzU1MTRmODNjMjgyOWIwMDRkMWZlZVBLAwQKAAAAAACiStdSec1a4SoAAAAqAAAAJAAAAGxpY2Vuc2VzL2FuZHJvaWQtc2RrLXByZXZpZXctbGljZW5zZQ0KODQ4MzFiOTQwOTY0NmE5MThlMzA1NzNiYWI0YzljOTEzNDZkOGFiZFBLAwQKAAAAAACiStdSk6vQKCoAAAAqAAAAGwAAAGxpY2Vuc2VzL2dvb2dsZS1nZGstbGljZW5zZQ0KMzNiNmEyYjY0NjA3ZjExYjc1OWYzMjBlZjlkZmY0YWU1YzQ3ZDk3YVBLAwQKAAAAAACiStdSrE3jESoAAAAqAAAAJAAAAGxpY2Vuc2VzL2ludGVsLWFuZHJvaWQtZXh0cmEtbGljZW5zZQ0KZDk3NWY3NTE2OThhNzdiNjYyZjEyNTRkZGJlZWQzOTAxZTk3NmY1YVBLAwQKAAAAAACjStdSkb1vWioAAAAqAAAAJgAAAGxpY2Vuc2VzL21pcHMtYW5kcm9pZC1zeXNpbWFnZS1saWNlbnNlDQplOWFjYWI1YjVmYmI1NjBhNzJjZmFlY2NlODk0Njg5NmZmNmFhYjlkUEsBAj8AFAAAAAAAo0rXUgAAAAAAAAAAAAAAAAkAJAAAAAAAAAAQAAAAAAAAAGxpY2Vuc2VzLwoAIAAAAAAAAQAYACIHOBcRaNcBIgc4FxFo1wHBTVQTEWjXAVBLAQI/AAoAAAAAAJ1K11K7n0IrKgAAACoAAAAhACQAAAAAAAAAIAAAACcAAABsaWNlbnNlcy9hbmRyb2lkLWdvb2dsZXR2LWxpY2Vuc2UKACAAAAAAAAEAGACUEFUTEWjXAZQQVRMRaNcB6XRUExFo1wFQSwECPwAKAAAAAACgStdS80LpiSoAAAAqAAAAJAAkAAAAAAAAACAAAACQAAAAbGljZW5zZXMvYW5kcm9pZC1zZGstYXJtLWRidC1saWNlbnNlCgAgAAAAAAABABgAsEM0FBFo1wGwQzQUEWjXAXb1MxQRaNcBUEsBAj8ACgAAAAAAoUrXUgpI4kUqAAAAKgAAABwAJAAAAAAAAAAgAAAA/AAAAGxpY2Vuc2VzL2FuZHJvaWQtc2RrLWxpY2Vuc2UKACAAAAAAAAEAGAAsMGUVEWjXASwwZRURaNcB5whlFRFo1wFQSwECPwAKAAAAAACiStdSec1a4SoAAAAqAAAAJAAkAAAAAAAAACAAAABgAQAAbGljZW5zZXMvYW5kcm9pZC1zZGstcHJldmlldy1saWNlbnNlCgAgAAAAAAABABgA7s3WFRFo1wHuzdYVEWjXAfGm1hURaNcBUEsBAj8ACgAAAAAAokrXUpOr0CgqAAAAKgAAABsAJAAAAAAAAAAgAAAAzAEAAGxpY2Vuc2VzL2dvb2dsZS1nZGstbGljZW5zZQoAIAAAAAAAAQAYAGRDRxYRaNcBZENHFhFo1wFfHEcWEWjXAVBLAQI/AAoAAAAAAKJK11KsTeMRKgAAACoAAAAkACQAAAAAAAAAIAAAAC8CAABsaWNlbnNlcy9pbnRlbC1hbmRyb2lkLWV4dHJhLWxpY2Vuc2UKACAAAAAAAAEAGADGsq0WEWjXAcayrRYRaNcBxrKtFhFo1wFQSwECPwAKAAAAAACjStdSkb1vWioAAAAqAAAAJgAkAAAAAAAAACAAAACbAgAAbGljZW5zZXMvbWlwcy1hbmRyb2lkLXN5c2ltYWdlLWxpY2Vuc2UKACAAAAAAAAEAGAA4LjgXEWjXATguOBcRaNcBIgc4FxFo1wFQSwUGAAAAAAgACACDAwAACQMAAAAA" +$licenseContent = [System.Convert]::FromBase64String($licenseContentBase64) +Set-Content -Path "$SDKInstallRoot\android-sdk-licenses.zip" -Value $licenseContent -Encoding Byte +Expand-7ZipArchive -Path "$SDKInstallRoot\android-sdk-licenses.zip" -DestinationPath $SDKInstallRoot + +# Install platform-tools +$platformToolsPath = Join-Path -Path $SDKInstallRoot -ChildPath "platform-tools" +if (Test-Path $platformToolsPath) { + Write-Host "Removing previous platform-tools installation from Visual Studio component" + Remove-Item $platformToolsPath -Recurse -Force +} +Install-AndroidSDKPackages "platform-tools" + +# Get Android SDK packages list +$androidPackages = Get-AndroidPackages -SDKRootPath $SDKRootPath + +# Install Android platform versions +# that are greater than or equal to the minimum version +Write-Host "Installing Android SDK packages for platforms..." +$platformList = Get-AndroidPlatformPackages ` + -SDKRootPath $SDKRootPath ` + -minVersion $androidToolset.platform_min_version +Install-AndroidSDKPackages $platformList + +# Install Android build-tools versions +# that are greater than or equal to the minimum version +Write-Host "Installing Android SDK packages for build tools..." +$buildToolsList = Get-AndroidBuildToolPackages ` + -SDKRootPath $SDKRootPath ` + -minVersion $androidToolset.build_tools_min_version +Install-AndroidSDKPackages $buildToolsList + +# Install Android Emulator +Install-AndroidSDKPackages "emulator" + +# Install extras, add-ons and additional tools +Write-Host "Installing Android SDK extras, add-ons and additional tools..." +Install-AndroidSDKPackages ($androidToolset.extras | ForEach-Object { "extras;$_" }) +Install-AndroidSDKPackages ($androidToolset.addons | ForEach-Object { "add-ons;$_" }) +Install-AndroidSDKPackages ($androidToolset.additional_tools) + +# Install NDKs +$ndkMajorVersions = $androidToolset.ndk.versions +$ndkDefaultMajorVersion = $androidToolset.ndk.default +$ndkLatestMajorVersion = $ndkMajorVersions | Select-Object -Last 1 + +$androidNDKs = @() +foreach ($version in $ndkMajorVersions) { + $packageNamePrefix = "ndk;$version" + $package = $androidPackages | Where-Object { $_.StartsWith($packageNamePrefix) } | Sort-Object -Unique | Select-Object -Last 1 + $androidNDKs += $package +} + +Write-Host "Installing Android SDK packages for NDKs..." +Install-AndroidSDKPackages $androidNDKs + +$ndkLatestVersion = ($androidNDKs | Where-Object { $_ -match "ndk;$ndkLatestMajorVersion" }).Split(';')[1] +$ndkDefaultVersion = ($androidNDKs | Where-Object { $_ -match "ndk;$ndkDefaultMajorVersion" }).Split(';')[1] +$ndkRoot = "$SDKRootPath\ndk\$ndkDefaultVersion" + +# Create env variables +[Environment]::SetEnvironmentVariable("ANDROID_HOME", $SDKRootPath, "Machine") +[Environment]::SetEnvironmentVariable("ANDROID_SDK_ROOT", $SDKRootPath, "Machine") +# 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 +[Environment]::SetEnvironmentVariable("ANDROID_NDK", $ndkRoot, "Machine") +[Environment]::SetEnvironmentVariable("ANDROID_NDK_HOME", $ndkRoot, "Machine") +[Environment]::SetEnvironmentVariable("ANDROID_NDK_ROOT", $ndkRoot, "Machine") + +$ndkLatestPath = "$SDKRootPath\ndk\$ndkLatestVersion" +if (Test-Path $ndkLatestPath) { + [Environment]::SetEnvironmentVariable("ANDROID_NDK_LATEST_HOME", $ndkLatestPath, "Machine") +} else { + Write-Host "Latest NDK $ndkLatestVersion is not installed at path $ndkLatestPath" + exit 1 +} + +Invoke-PesterTests -TestFile "Android" diff --git a/images/windows/scripts/build/Install-Apache.ps1 b/images/windows/scripts/build/Install-Apache.ps1 new file mode 100644 index 0000000..e5b710d --- /dev/null +++ b/images/windows/scripts/build/Install-Apache.ps1 @@ -0,0 +1,23 @@ +################################################################################ +## File: Install-Apache.ps1 +## Desc: Install Apache HTTP Server +################################################################################ + +# Stop w3svc service +# commented below code w3svc service is not present in 2025 +Stop-Service -Name w3svc + +# Install latest apache in chocolatey +$installDir = "C:\tools" +Install-ChocoPackage apache-httpd -ArgumentList "--force", "--params", "/installLocation:$installDir /port:80" + +# Stop and disable Apache service +Stop-Service -Name Apache +Set-Service -Name Apache -StartupType Disabled + +# Start w3svc service +# commented below code w3svc service is not present in 2025 +Start-Service -Name w3svc + +# Invoke Pester Tests +Invoke-PesterTests -TestFile "Apache" diff --git a/images/windows/scripts/build/Install-AzureCli.ps1 b/images/windows/scripts/build/Install-AzureCli.ps1 new file mode 100644 index 0000000..46f5259 --- /dev/null +++ b/images/windows/scripts/build/Install-AzureCli.ps1 @@ -0,0 +1,29 @@ +################################################################################ +## File: Install-AzureCli.ps1 +## Desc: Install and warm-up Azure CLI +################################################################################ + +Write-Host 'Install the latest Azure CLI release' + +$azureCliConfigPath = 'C:\azureCli' +# Store azure-cli cache outside of the provisioning user's profile +[Environment]::SetEnvironmentVariable('AZURE_CONFIG_DIR', $azureCliConfigPath, "Machine") + +$azureCliExtensionPath = Join-Path $env:CommonProgramFiles 'AzureCliExtensionDirectory' +New-Item -ItemType 'Directory' -Path $azureCliExtensionPath | Out-Null +[Environment]::SetEnvironmentVariable('AZURE_EXTENSION_DIR', $azureCliExtensionPath, "Machine") + +Install-Binary -Type MSI ` + -Url 'https://aka.ms/installazurecliwindowsx64' ` + -ExpectedSignature '245D262748012A4FE6CE8BA6C951A4C4AFBC3E5D' #'F9A7CF9FBE13BAC767F4781061332DA6E8B4E0EE' + +Update-Environment + +# Warm-up Azure CLI +Write-Host "Warmup 'az'" +az --help | Out-Null +if ($LASTEXITCODE -ne 0) { + throw "Command 'az --help' failed" +} + +Invoke-PesterTests -TestFile 'CLI.Tools' -TestName 'Azure CLI' diff --git a/images/windows/scripts/build/Install-AzureCosmosDbEmulator.ps1 b/images/windows/scripts/build/Install-AzureCosmosDbEmulator.ps1 new file mode 100644 index 0000000..13772a4 --- /dev/null +++ b/images/windows/scripts/build/Install-AzureCosmosDbEmulator.ps1 @@ -0,0 +1,10 @@ +#################################################################################### +## File: Install-AzureCosmosDbEmulator.ps1 +## Desc: Install Azure CosmosDb Emulator +#################################################################################### + +Install-Binary -Type MSI ` + -Url "https://aka.ms/cosmosdb-emulator" ` + -ExpectedSHA256Sum "DB9D5E496C5FDAE17C12C03385D2BAC973DA61C280023D9FDC9A6020220BEE41" + +Invoke-PesterTests -TestFile "Tools" -TestName "Azure Cosmos DB Emulator" diff --git a/images/windows/scripts/build/Install-AzureDevOpsCli.ps1 b/images/windows/scripts/build/Install-AzureDevOpsCli.ps1 new file mode 100644 index 0000000..f5cbfa5 --- /dev/null +++ b/images/windows/scripts/build/Install-AzureDevOpsCli.ps1 @@ -0,0 +1,35 @@ +################################################################################ +## File: Install-AzureDevOpsCli.ps1 +## Desc: Install Azure DevOps CLI +################################################################################ + +$azureDevOpsCliConfigPath = 'C:\azureDevOpsCli' +# Store azure-devops-cli cache outside of the provisioning user's profile +[Environment]::SetEnvironmentVariable('AZ_DEVOPS_GLOBAL_CONFIG_DIR', $azureDevOpsCliConfigPath, "Machine") + +$azureDevOpsCliCachePath = Join-Path $azureDevOpsCliConfigPath 'cache' +New-Item -ItemType 'Directory' -Path $azureDevOpsCliCachePath | Out-Null +[Environment]::SetEnvironmentVariable('AZURE_DEVOPS_CACHE_DIR', $azureDevOpsCliCachePath, "Machine") + +Update-Environment + +az extension add -n azure-devops +if ($LASTEXITCODE -ne 0) { + throw "Command 'az extension add -n azure-devops' failed" +} + +# Warm-up Azure DevOps CLI +Write-Host "Warmup 'az-devops'" +@('devops', 'pipelines', 'boards', 'repos', 'artifacts') | ForEach-Object { + az $_ --help + if ($LASTEXITCODE -ne 0) { + throw "Command 'az $_ --help' failed" + } +} + +# calling az devops login to force it to install `keyring`. Login will actually fail, redirecting error to null +Write-Output 'fake token' | az devops login | Out-Null +# calling az devops logout to be sure no credentials remain. +az devops logout | out-null + +Invoke-PesterTests -TestFile 'CLI.Tools' -TestName 'Azure DevOps CLI' diff --git a/images/windows/scripts/build/Install-Bazel.ps1 b/images/windows/scripts/build/Install-Bazel.ps1 new file mode 100644 index 0000000..093690d --- /dev/null +++ b/images/windows/scripts/build/Install-Bazel.ps1 @@ -0,0 +1,13 @@ +################################################################################ +## File: Install-Bazel.ps1 +## Desc: Install Bazel and Bazelisk (A user-friendly launcher for Bazel) +################################################################################ + +Install-ChocoPackage bazel + +npm install -g @bazel/bazelisk +if ($LASTEXITCODE -ne 0) { + throw "Command 'npm install -g @bazel/bazelisk' failed" +} + +Invoke-PesterTests -TestFile "Tools" -TestName "Bazel" diff --git a/images/windows/scripts/build/Install-BizTalkBuildComponent.ps1 b/images/windows/scripts/build/Install-BizTalkBuildComponent.ps1 new file mode 100644 index 0000000..a56a663 --- /dev/null +++ b/images/windows/scripts/build/Install-BizTalkBuildComponent.ps1 @@ -0,0 +1,28 @@ +################################################################################ +## File: Install-BizTalkBuildComponent.ps1 +## Desc: Install BizTalk Project Build Component +################################################################################ + +$downloadUrl = "https://aka.ms/BuildComponentSetup.EN" +$signatureThumbprint = "8740DF4ACB749640AD318E4BE842F72EC651AD80" + +Write-Host "Downloading BizTalk Project Build Component archive..." +$zipFile = Invoke-DownloadWithRetry $downloadUrl + +$setupPath = Join-Path $env:TEMP "BizTalkBuildComponent" +if (-not (Test-Path -Path $setupPath)) { + New-Item -Path $setupPath -ItemType Directory -Force | Out-Null +} +Expand-7ZipArchive -Path $zipFile -DestinationPath $setupPath + +Write-Host "Installing BizTalk Project Build Component..." +Install-Binary ` + -LocalPath "$setupPath\Bootstrap.msi" ` + -ExtraInstallArgs ("/l*v", "$setupPath\bootstrap.log") ` + -ExpectedSignature $signatureThumbprint +Install-Binary ` + -LocalPath "$setupPath\BuildComponentSetup.msi" ` + -ExtraInstallArgs ("/l*v", "$setupPath\buildComponentSetup.log") ` + -ExpectedSignature $signatureThumbprint + +Invoke-PesterTests -TestFile "BizTalk" -TestName "BizTalk Build Component Setup" diff --git a/images/windows/scripts/build/Install-Chocolatey.ps1 b/images/windows/scripts/build/Install-Chocolatey.ps1 new file mode 100644 index 0000000..cb5f09f --- /dev/null +++ b/images/windows/scripts/build/Install-Chocolatey.ps1 @@ -0,0 +1,26 @@ +################################################################################ +## File: Install-Chocolatey.ps1 +## Desc: Install Chocolatey package manager +################################################################################ + +Write-Host "Set TLS1.2" +[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor "Tls12" + +Write-Host "Install chocolatey" + +# Add to system PATH +Add-MachinePathItem 'C:\ProgramData\Chocolatey\bin' +Update-Environment + +# Verify and run choco installer +$signatureThumbprint = "B009C875F4E10FFBC62B785BAF4FC4D6BC2D5711" +$installScriptPath = Invoke-DownloadWithRetry 'https://chocolatey.org/install.ps1' +Test-FileSignature -Path $installScriptPath -ExpectedThumbprint $signatureThumbprint +Invoke-Expression $installScriptPath + +# Turn off confirmation +choco feature enable -n allowGlobalConfirmation + +# Initialize environmental variable ChocolateyToolsLocation by invoking choco Get-ToolsLocation function +Import-Module "$env:ChocolateyInstall\helpers\chocolateyInstaller.psm1" -Force +Get-ToolsLocation diff --git a/images/windows/scripts/build/Install-ChocolateyPackages.ps1 b/images/windows/scripts/build/Install-ChocolateyPackages.ps1 new file mode 100644 index 0000000..826e77f --- /dev/null +++ b/images/windows/scripts/build/Install-ChocolateyPackages.ps1 @@ -0,0 +1,12 @@ +################################################################################ +## File: Install-ChocolateyPackages.ps1 +## Desc: Install common Chocolatey packages +################################################################################ + +$commonPackages = (Get-ToolsetContent).choco.common_packages + +foreach ($package in $commonPackages) { + Install-ChocoPackage $package.name -ArgumentList $package.args +} + +Invoke-PesterTests -TestFile "ChocoPackages" diff --git a/images/windows/scripts/build/Install-Chrome.ps1 b/images/windows/scripts/build/Install-Chrome.ps1 new file mode 100644 index 0000000..dcc5d66 --- /dev/null +++ b/images/windows/scripts/build/Install-Chrome.ps1 @@ -0,0 +1,82 @@ +################################################################################ +## File: Install-Chrome.ps1 +## Desc: Install Google Chrome browser and Chrome WebDriver +################################################################################ + +# Download and install latest Chrome browser +Install-Binary ` + -Url 'https://dl.google.com/tag/s/dl/chrome/install/googlechromestandaloneenterprise64.msi' ` + -ExpectedSignature '607A3EDAA64933E94422FC8F0C80388E0590986C' + +# Prepare firewall rules +Write-Host "Adding the firewall rule for Google update blocking..." +New-NetFirewallRule -DisplayName "BlockGoogleUpdate" -Direction Outbound -Action Block -Program "C:\Program Files (x86)\Google\Update\GoogleUpdate.exe" + +$googleServices = Get-Service -Name "GoogleUpdater*" +Stop-Service $googleServices +$googleServices.WaitForStatus('Stopped', "00:01:00") +$googleServices | Set-Service -StartupType Disabled + +$regGoogleUpdatePath = "HKLM:\SOFTWARE\Policies\Google\Update" +$regGoogleUpdateChrome = "HKLM:\SOFTWARE\Policies\Google\Chrome" +($regGoogleUpdatePath, $regGoogleUpdateChrome) | ForEach-Object { + New-Item -Path $_ -Force +} + +$regGoogleParameters = @( + @{ Name = "AutoUpdateCheckPeriodMinutes"; Value = 00000000}, + @{ Name = "UpdateDefault"; Value = 00000000 }, + @{ Name = "DisableAutoUpdateChecksCheckboxValue"; Value = 00000001 }, + @{ Name = "Update{8A69D345-D564-463C-AFF1-A69D9E530F96}"; Value = 00000000 }, + @{ Path = $regGoogleUpdateChrome; Name = "DefaultBrowserSettingEnabled"; Value = 00000000 } +) + +$regGoogleParameters | ForEach-Object { + $arguments = $_ + if (-not ($arguments.Path)) { + $arguments.Add("Path", $regGoogleUpdatePath) + } + $arguments.Add("Force", $true) + New-ItemProperty @arguments +} +# Install Chrome WebDriver +Write-Host "Install Chrome WebDriver..." +$chromeDriverPath = "$($env:SystemDrive)\SeleniumWebDrivers\ChromeDriver" +if (-not (Test-Path -Path $chromeDriverPath)) { + New-Item -Path $chromeDriverPath -ItemType Directory -Force +} + +Write-Host "Get the Chrome WebDriver download URL..." +$registryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths" +$chromePath = (Get-ItemProperty "$registryPath\chrome.exe").'(default)' +[version] $chromeVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($chromePath).ProductVersion +$chromeBuild = "$($chromeVersion.Major).$($chromeVersion.Minor).$($chromeVersion.Build)" +$chromeDriverVersionsUrl = "https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build-with-downloads.json" + +Write-Host "Chrome version is $chromeVersion" +$chromeDriverVersions = Invoke-RestMethod -Uri $chromeDriverVersionsUrl +$chromeDriverVersion = $chromeDriverVersions.builds.$chromeBuild + +if (-not ($chromeDriverVersion)) { + $availableVersions = $chromeDriverVersions.builds | Get-Member | Select-Object -ExpandProperty Name + Write-Host "Available chromedriver builds are $availableVersions" + throw "Can't determine chromedriver version that matches chrome build $chromeBuild" +} + +$chromeDriverVersion.version | Out-File -FilePath "$chromeDriverPath\versioninfo.txt" -Force; + +Write-Host "Chrome WebDriver version to install is $($chromeDriverVersion.version)" +$chromeDriverZipDownloadUrl = ($chromeDriverVersion.downloads.chromedriver | Where-Object platform -eq "win64").url + +Write-Host "Download Chrome WebDriver from $chromeDriverZipDownloadUrl..." +$chromeDriverArchPath = Invoke-DownloadWithRetry $chromeDriverZipDownloadUrl + +Write-Host "Expand Chrome WebDriver archive (without using directory names)..." +Expand-7ZipArchive -Path $chromeDriverArchPath -DestinationPath $chromeDriverPath -ExtractMethod "e" + +Write-Host "Setting the environment variables..." +[Environment]::SetEnvironmentVariable("ChromeWebDriver", $chromeDriverPath, "Machine") +Add-MachinePathItem $chromeDriverPath +Update-Environment + +Invoke-PesterTests -TestFile "Browsers" -TestName "Chrome" diff --git a/images/windows/scripts/build/Install-CloudFoundryCli.ps1 b/images/windows/scripts/build/Install-CloudFoundryCli.ps1 new file mode 100644 index 0000000..fe50246 --- /dev/null +++ b/images/windows/scripts/build/Install-CloudFoundryCli.ps1 @@ -0,0 +1,26 @@ +################################################################################ +## File: Install-CloudFoundryCli.ps1 +## Desc: Install Cloud Foundry CLI +################################################################################ + +# Download the latest cf cli exe +$cloudFoundryCliUrl = "https://packages.cloudfoundry.org/stable?release=windows64-exe&source=github" + +$cloudFoundryArchPath = Invoke-DownloadWithRetry $cloudFoundryCliUrl + +# Create directory for cf cli +$cloudFoundryCliPath = "C:\cf-cli" +New-Item -Path $cloudFoundryCliPath -ItemType Directory -Force + +# Extract the zip archive +Write-Host "Extracting cf cli..." +Expand-7ZipArchive -Path $cloudFoundryArchPath -DestinationPath $cloudFoundryCliPath + +# Add cf to path +Add-MachinePathItem $cloudFoundryCliPath + +# Validate cf signature +$cloudFoundrySignatureThumbprint = "2C6B2F1562698503A6E4A25F2DF058E12E23A190" +Test-FileSignature -Path "$cloudFoundryCliPath\cf.exe" -ExpectedThumbprint $cloudFoundrySignatureThumbprint + +Invoke-PesterTests -TestFile "CLI.Tools" -TestName "CloudFoundry CLI" diff --git a/images/windows/scripts/build/Install-CodeQLBundle.ps1 b/images/windows/scripts/build/Install-CodeQLBundle.ps1 new file mode 100644 index 0000000..5ebcd67 --- /dev/null +++ b/images/windows/scripts/build/Install-CodeQLBundle.ps1 @@ -0,0 +1,36 @@ +################################################################################ +## File: Install-CodeQLBundle.ps1 +## Desc: Install the CodeQL CLI Bundle to the toolcache. +################################################################################ + +# Retrieve the CLI version of the latest CodeQL bundle. +$defaults = (Invoke-RestMethod "https://raw.githubusercontent.com/github/codeql-action/v2/src/defaults.json") +$cliVersion = $defaults.cliVersion +$tagName = "codeql-bundle-v" + $cliVersion + +Write-Host "Downloading CodeQL bundle $($cliVersion)..." +# Note that this is the all-platforms CodeQL bundle, to support scenarios where customers run +# different operating systems within containers. +$codeQLBundlePath = Invoke-DownloadWithRetry "https://github.com/github/codeql-action/releases/download/$($tagName)/codeql-bundle.tar.gz" +$downloadDirectoryPath = (Get-Item $codeQLBundlePath).Directory.FullName + +$codeQLToolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY -ChildPath "CodeQL" | Join-Path -ChildPath $cliVersion | Join-Path -ChildPath "x64" +New-Item -Path $codeQLToolcachePath -ItemType Directory -Force | Out-Null + +Write-Host "Unpacking the downloaded CodeQL bundle archive..." +Expand-7ZipArchive -Path $codeQLBundlePath -DestinationPath $downloadDirectoryPath +$unGzipedCodeQLBundlePath = Join-Path $downloadDirectoryPath "codeql-bundle.tar" +Expand-7ZipArchive -Path $unGzipedCodeQLBundlePath -DestinationPath $codeQLToolcachePath + +Write-Host "CodeQL bundle at $($codeQLToolcachePath) contains the following directories:" +Get-ChildItem -Path $codeQLToolcachePath -Depth 2 + +# Touch a file to indicate to the CodeQL Action that this bundle shipped with the toolcache. This is +# to support overriding the CodeQL version specified in defaults.json on GitHub Enterprise. +New-Item -ItemType file (Join-Path $codeQLToolcachePath -ChildPath "pinned-version") + +# Touch a file to indicate to the toolcache that setting up CodeQL is complete. +New-Item -ItemType file "$codeQLToolcachePath.complete" + +# Test that the tools have been extracted successfully. +Invoke-PesterTests -TestFile "Tools" -TestName "CodeQL Bundle" diff --git a/images/windows/scripts/build/Install-DACFx.ps1 b/images/windows/scripts/build/Install-DACFx.ps1 new file mode 100644 index 0000000..80a70fe --- /dev/null +++ b/images/windows/scripts/build/Install-DACFx.ps1 @@ -0,0 +1,10 @@ +#################################################################################### +## File: Install-DACFx.ps1 +## Desc: Install SQL Server® Data-Tier Application Framework (DacFx) for Windows +#################################################################################### + +Install-Binary -Type MSI ` + -Url 'https://aka.ms/dacfx-msi' ` + -ExpectedSignature 'C2048FB509F1C37A8C3E9EC6648118458AA01780' + +Invoke-PesterTests -TestFile "Tools" -TestName "DACFx" diff --git a/images/windows/scripts/build/Install-Docker.ps1 b/images/windows/scripts/build/Install-Docker.ps1 new file mode 100644 index 0000000..5eabf99 --- /dev/null +++ b/images/windows/scripts/build/Install-Docker.ps1 @@ -0,0 +1,56 @@ +################################################################################ +## File: Install-Docker.ps1 +## Desc: Install Docker. +## Must be an independent step because it requires a restart before we +## can continue. +################################################################################ + +Write-Host "Get latest Moby release" +$toolsetVersion = (Get-ToolsetContent).docker.components.docker +$mobyVersion = (Get-GithubReleasesByVersion -Repo "moby/moby" -Version "${toolsetVersion}").version +$dockerceUrl = "https://download.docker.com/win/static/stable/x86_64/" +$dockerceBinaries = Invoke-WebRequest -Uri $dockerceUrl -UseBasicParsing + +Write-Host "Check Moby version $mobyVersion" +$mobyRelease = $dockerceBinaries.Links.href -match "${mobyVersion}\.zip" | Select-Object -Last 1 +if (-not $mobyRelease) { + Write-Host "Release not found for $mobyLatestRelease version" + $versions = [regex]::Matches($dockerceBinaries.Links.href, "docker-(\d+\.\d+\.\d+)\.zip") | Sort-Object { [version] $_.Groups[1].Value } + $mobyRelease = $versions | Select-Object -ExpandProperty Value -Last 1 + Write-Host "Found $mobyRelease" +} +$mobyReleaseUrl = $dockerceUrl + $mobyRelease + +Write-Host "Install Moby $mobyRelease..." +$mobyArchivePath = Invoke-DownloadWithRetry $mobyReleaseUrl +Expand-Archive -Path $mobyArchivePath -DestinationPath $env:TEMP +$dockerPath = "$env:TEMP\docker\docker.exe" +$dockerdPath = "$env:TEMP\docker\dockerd.exe" + +Write-Host "Install Docker CE" +$instScriptUrl = "https://raw.githubusercontent.com/microsoft/Windows-Containers/Main/helpful_tools/Install-DockerCE/install-docker-ce.ps1" +$instScriptPath = Invoke-DownloadWithRetry $instScriptUrl +& $instScriptPath -DockerPath $dockerPath -DockerDPath $dockerdPath +if ($LastExitCode -ne 0) { + Write-Host "Docker installation failed with exit code $LastExitCode" + exit $exitCode +} + +# Fix AZ CLI DOCKER_COMMAND_ERROR +# cli.azure.cli.command_modules.acr.custom: Could not run 'docker.exe' command. +# https://github.com/Azure/azure-cli/issues/18766 +New-Item -ItemType SymbolicLink -Path "C:\Windows\SysWOW64\docker.exe" -Target "C:\Windows\System32\docker.exe" + +Write-Host "Download docker images" +$dockerImages = (Get-ToolsetContent).docker.images +foreach ($dockerImage in $dockerImages) { + Write-Host "Pulling docker image $dockerImage ..." + docker pull $dockerImage + + if (!$?) { + throw "Docker pull failed with a non-zero exit code ($LastExitCode)" + } +} + +Invoke-PesterTests -TestFile "Docker" -TestName "Docker" +Invoke-PesterTests -TestFile "Docker" -TestName "DockerImages" diff --git a/images/windows/scripts/build/Install-DockerCompose.ps1 b/images/windows/scripts/build/Install-DockerCompose.ps1 new file mode 100644 index 0000000..4075777 --- /dev/null +++ b/images/windows/scripts/build/Install-DockerCompose.ps1 @@ -0,0 +1,13 @@ +################################################################################ +## File: Install-Docker-Compose.ps1 +## Desc: Install Docker Compose. +################################################################################ +Write-Host "Install-Package Docker-Compose v2" +$toolsetVersion = (Get-ToolsetContent).docker.components.compose +$composeVersion = (Get-GithubReleasesByVersion -Repo "docker/compose" -Version "${toolsetVersion}").version +$dockerComposev2Url = "https://github.com/docker/compose/releases/download/v${composeVersion}/docker-compose-windows-x86_64.exe" +$cliPluginsDir = "C:\ProgramData\docker\cli-plugins" +New-Item -Path $cliPluginsDir -ItemType Directory +Invoke-DownloadWithRetry -Url $dockerComposev2Url -Path "$cliPluginsDir\docker-compose.exe" + +Invoke-PesterTests -TestFile "Docker" -TestName "DockerCompose" diff --git a/images/windows/scripts/build/Install-DockerWinCred.ps1 b/images/windows/scripts/build/Install-DockerWinCred.ps1 new file mode 100644 index 0000000..e4d8dd6 --- /dev/null +++ b/images/windows/scripts/build/Install-DockerWinCred.ps1 @@ -0,0 +1,22 @@ +################################################################################ +## File: Install-Docker-WinCred.ps1 +## Desc: Install Docker credential helper. +## Supply chain security: checksum validation +################################################################################ + +Write-Host "Install docker-wincred" +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "docker/docker-credential-helpers" ` + -Version "latest" ` + -UrlMatchPattern "docker-credential-wincred-*amd64.exe" +$binaryPath = Invoke-DownloadWithRetry -Url $downloadUrl -Path "C:\Windows\System32\docker-credential-wincred.exe" + +#region Supply chain security +$binaryName = Split-Path $downloadUrl -Leaf +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url ($downloadUrl -replace $binaryName, "checksums.txt") ` + -FileName $binaryName +Test-FileChecksum -Path $binaryPath -ExpectedSHA256Sum $externalHash +#endregion + +Invoke-PesterTests -TestFile "Docker" -TestName "DockerWinCred" diff --git a/images/windows/scripts/build/Install-DotnetSDK.ps1 b/images/windows/scripts/build/Install-DotnetSDK.ps1 new file mode 100644 index 0000000..493720f --- /dev/null +++ b/images/windows/scripts/build/Install-DotnetSDK.ps1 @@ -0,0 +1,135 @@ +################################################################################ +## File: Install-DotnetSDK.ps1 +## Desc: Install all released versions of the dotnet sdk and populate package +## cache. Should run after VS and Node +## Supply chain security: checksum validation +################################################################################ + +# Set environment variables +[Environment]::SetEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0", "Machine") +[Environment]::SetEnvironmentVariable("DOTNET_NOLOGO", "1", "Machine") +[Environment]::SetEnvironmentVariable("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1", "Machine") + +[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor "Tls12" + +#region "Functions" +function Get-SDKVersionsToInstall { + param ( + [Parameter(Mandatory)] + [string] $DotnetVersion + ) + $releasesJsonUri = "https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/${DotnetVersion}/releases.json" + $releasesData = (Invoke-DownloadWithRetry $releasesJsonUri) | Get-Item | Get-Content | ConvertFrom-Json + # filtering out the preview/rc releases + $releases = $releasesData.'releases' | Where-Object { !$_.'release-version'.Contains('-') } + + $sdks = @() + foreach ($release in $releases) { + $sdks += $release.'sdk' + $sdks += $release.'sdks' + } + + return $sdks.version ` + | Sort-Object { [Version] $_ } -Unique ` + | Group-Object { $_.Substring(0, $_.LastIndexOf('.') + 2) } ` + | ForEach-Object { $_.Group[-1] } +} + +function Invoke-DotnetWarmup { + param ( + [Parameter(Mandatory)] + [string] $SDKVersion + ) + # warm up dotnet for first time experience + $projectTypes = @('console', 'mstest', 'web', 'mvc', 'webapi') + foreach ($template in $projectTypes) { + $projectPath = Join-Path -Path "C:\temp" -ChildPath $template + New-Item -Path $projectPath -Force -ItemType Directory + Push-Location -Path $projectPath + & "$env:ProgramFiles\dotnet\dotnet.exe" new globaljson --sdk-version "$SDKVersion" + if ($LastExitCode -ne 0) { + throw "Dotnet new globaljson failed with exit code $LastExitCode" + } + & "$env:ProgramFiles\dotnet\dotnet.exe" new $template + if ($LastExitCode -ne 0) { + throw "Dotnet new $template failed with exit code $LastExitCode" + } + Pop-Location + Remove-Item $projectPath -Force -Recurse + } +} + +function Install-DotnetSDK { + param ( + [Parameter(Mandatory)] + [string] $InstallScriptPath, + [Parameter(Mandatory)] + [Alias('Version')] + [string] $SDKVersion, + [Parameter(Mandatory)] + [string] $DotnetVersion + ) + + if (Test-Path -Path "C:\Program Files\dotnet\sdk\$SDKVersion") { + Write-Host "Sdk version $SDKVersion already installed" + return + } + + Write-Host "Installing dotnet $SDKVersion" + $zipPath = Join-Path ([IO.Path]::GetTempPath()) ([IO.Path]::GetRandomFileName()) + & $InstallScriptPath -Version $SDKVersion -InstallDir $(Join-Path -Path $env:ProgramFiles -ChildPath 'dotnet') -ZipPath $zipPath -KeepZip + # Installer is PowerShell script that doesn't set exit code on failure + # If installation failed, tests will fail anyway + + #region Supply chain security + $releasesJsonUri = "https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/${DotnetVersion}/releases.json" + $releasesData = (Invoke-DownloadWithRetry $releasesJsonUri) | Get-Item | Get-Content | ConvertFrom-Json + $distributorFileHash = $releasesData.releases.sdks.Where({ $_.version -eq $SDKVersion }).files.Where({ $_.name -eq 'dotnet-sdk-win-x64.zip' }).hash + Test-FileChecksum $zipPath -ExpectedSHA512Sum $distributorFileHash + #endregion +} +#endregion + +$dotnetToolset = (Get-ToolsetContent).dotnet + +# Download installation script. +$installScriptPath = Invoke-DownloadWithRetry -Url "https://dot.net/v1/dotnet-install.ps1" + +# Install and warm up dotnet +foreach ($dotnetVersion in $dotnetToolset.versions) { + $sdkVersionsToInstall = Get-SDKVersionsToInstall -DotnetVersion $dotnetVersion + foreach ($sdkVersion in $sdkVersionsToInstall) { + Install-DotnetSDK -InstallScriptPath $installScriptPath -SDKVersion $sdkVersion -DotnetVersion $dotnetVersion + if ($dotnetToolset.warmup) { + Invoke-DotnetWarmup -SDKVersion $sdkVersion + } + } +} + +# Add dotnet to PATH +Add-MachinePathItem "C:\Program Files\dotnet" + +# Remove NuGet Folder +$nugetPath = "$env:APPDATA\NuGet" +if (Test-Path $nugetPath) { + Remove-Item -Path $nugetPath -Force -Recurse +} + +# Generate and copy new NuGet.Config config +dotnet nuget list source | Out-Null +if ($LastExitCode -ne 0) { + throw "Dotnet nuget list source failed with exit code $LastExitCode" +} +Copy-Item -Path $nugetPath -Destination "C:\Users\Default\AppData\Roaming" -Force -Recurse + +# Install dotnet tools +Write-Host "Installing dotnet tools" +Add-DefaultPathItem "%USERPROFILE%\.dotnet\tools" +foreach ($dotnetTool in $dotnetToolset.tools) { + dotnet tool install $($dotnetTool.name) --tool-path "C:\Users\Default\.dotnet\tools" --add-source "https://api.nuget.org/v3/index.json" | Out-Null + if ($LastExitCode -ne 0) { + throw "Dotnet tool install failed with exit code $LastExitCode" + } +} + +Invoke-PesterTests -TestFile "DotnetSDK" diff --git a/images/windows/scripts/build/Install-EdgeDriver.ps1 b/images/windows/scripts/build/Install-EdgeDriver.ps1 new file mode 100644 index 0000000..64b9830 --- /dev/null +++ b/images/windows/scripts/build/Install-EdgeDriver.ps1 @@ -0,0 +1,39 @@ +################################################################################ +## File: Install-EdgeDriver.ps1 +## Desc: Install Edge WebDriver and configure Microsoft Edge +################################################################################ + +# Disable Edge auto-updates +Rename-Item -Path "C:\Program Files (x86)\Microsoft\EdgeUpdate\MicrosoftEdgeUpdate.exe" -NewName "Disabled_MicrosoftEdgeUpdate.exe" -ErrorAction Stop + +Write-Host "Get the Microsoft Edge WebDriver version..." +$edgeBinaryPath = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\msedge.exe").'(default)' +[version] $edgeVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($edgeBinaryPath).ProductVersion + +$edgeDriverPath = "$($env:SystemDrive)\SeleniumWebDrivers\EdgeDriver" +if (-not (Test-Path -Path $edgeDriverPath)) { + New-Item -Path $edgeDriverPath -ItemType Directory -Force +} + +$versionInfoUrl = "https://msedgedriver.azureedge.net/LATEST_RELEASE_$($edgeVersion.Major)_WINDOWS" +$versionInfoFile = Invoke-DownloadWithRetry -Url $versionInfoUrl -Path "$edgeDriverPath\versioninfo.txt" +$latestVersion = Get-Content -Path $versionInfoFile + +Write-Host "Download Microsoft Edge WebDriver..." +$downloadUrl = "https://msedgedriver.azureedge.net/$latestVersion/edgedriver_win64.zip" +$archivePath = Invoke-DownloadWithRetry $downloadUrl + +Write-Host "Expand Microsoft Edge WebDriver archive..." +Expand-7ZipArchive -Path $archivePath -DestinationPath $edgeDriverPath + +#Validate the EdgeDriver signature + +$signatureThumbprint = "CB9C4FBEA1D87D2D468AC5A9CAAB0163F6AD8401","573EF451A68C33FB904346D44551BEF3BB5BBF68", ` + "7920AC8FB05E0FFFE21E8FF4B4F03093BA6AC16E", "0BD8C56733FDCC06F8CB919FF5A200E39B1ACF71" +Test-FileSignature -Path "$edgeDriverPath\msedgedriver.exe" -ExpectedThumbprint $signatureThumbprint + +Write-Host "Setting the environment variables..." +[Environment]::SetEnvironmentVariable("EdgeWebDriver", $EdgeDriverPath, "Machine") +Add-MachinePathItem "$edgeDriverPath\" + +Invoke-PesterTests -TestFile "Browsers" -TestName "Edge" diff --git a/images/windows/scripts/build/Install-Firefox.ps1 b/images/windows/scripts/build/Install-Firefox.ps1 new file mode 100644 index 0000000..281975b --- /dev/null +++ b/images/windows/scripts/build/Install-Firefox.ps1 @@ -0,0 +1,63 @@ +################################################################################ +## File: Install-Firefox.ps1 +## Desc: Install Mozilla Firefox browser and Gecko WebDriver +## Supply chain security: Firefox browser - checksum validation +################################################################################ + +# Install and configure Firefox browser +Write-Host "Get the latest Firefox version..." +$versionsManifest = Invoke-RestMethod "https://product-details.mozilla.org/1.0/firefox_versions.json" + +Write-Host "Install Firefox browser..." +$installerUrl = "https://download.mozilla.org/?product=firefox-$($versionsManifest.LATEST_FIREFOX_VERSION)&os=win64&lang=en-US" +$hashUrl = "https://archive.mozilla.org/pub/firefox/releases/$($versionsManifest.LATEST_FIREFOX_VERSION)/SHA256SUMS" + +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url $hashUrl ` + -FileName "win64/en-US/Firefox Setup*exe" + +Install-Binary -Type EXE ` + -Url $installerUrl ` + -InstallArgs @("/silent", "/install") ` + -ExpectedSHA256Sum $externalHash + +Write-Host "Disable autoupdate..." +$firefoxDirectoryPath = Join-Path $env:ProgramFiles "Mozilla Firefox" +New-Item -path $firefoxDirectoryPath -Name 'mozilla.cfg' -Value '// +pref("browser.shell.checkDefaultBrowser", false); +pref("app.update.enabled", false);' -ItemType file -force + +$firefoxPreferencesFolder = Join-Path $firefoxDirectoryPath "defaults\pref" +New-Item -path $firefoxPreferencesFolder -Name 'local-settings.js' -Value 'pref("general.config.obscure_value", 0); +pref("general.config.filename", "mozilla.cfg");' -ItemType file -force + +# Download and install Gecko WebDriver +Write-Host "Install Gecko WebDriver..." +$geckoDriverPath = "$($env:SystemDrive)\SeleniumWebDrivers\GeckoDriver" +if (-not (Test-Path -Path $geckoDriverPath)) { + New-Item -Path $geckoDriverPath -ItemType Directory -Force +} + +Write-Host "Get the Gecko WebDriver version..." +$geckoDriverVersion = (Get-GithubReleasesByVersion -Repo "mozilla/geckodriver" -Version "latest").version +$geckoDriverVersion | Out-File -FilePath "$geckoDriverPath\versioninfo.txt" -Force + +Write-Host "Download Gecko WebDriver WebDriver..." +$geckoDriverDownloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "mozilla/geckodriver" ` + -Version $geckoDriverVersion ` + -UrlMatchPattern "geckodriver-*-win64.zip" +$geckoDriverArchPath = Invoke-DownloadWithRetry $geckoDriverDownloadUrl + +Write-Host "Expand Gecko WebDriver archive..." +Expand-7ZipArchive -Path $geckoDriverArchPath -DestinationPath $geckoDriverPath + +# Validate Gecko WebDriver signature +$geckoDriverSignatureThumbprint = "40890F2FE1ACAE18072FA7F3C0AE456AACC8570D" +Test-FileSignature -Path "$geckoDriverPath/geckodriver.exe" -ExpectedThumbprint $geckoDriverSignatureThumbprint + +Write-Host "Setting the environment variables..." +Add-MachinePathItem -PathItem $geckoDriverPath +[Environment]::SetEnvironmentVariable("GeckoWebDriver", $geckoDriverPath, "Machine") + +Invoke-PesterTests -TestFile "Browsers" -TestName "Firefox" diff --git a/images/windows/scripts/build/Install-Git.ps1 b/images/windows/scripts/build/Install-Git.ps1 new file mode 100644 index 0000000..21648ce --- /dev/null +++ b/images/windows/scripts/build/Install-Git.ps1 @@ -0,0 +1,52 @@ +################################################################################ +## File: Install-Git.ps1 +## Desc: Install Git for Windows +## Supply chain security: Git - checksum validation, Hub CLI - managed by package manager +################################################################################ + +# Install the latest version of Git for Windows + +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "git-for-windows/git" ` + -Version "latest" ` + -UrlMatchPattern "Git-*-64-bit.exe" + +$externalHash = Get-ChecksumFromGithubRelease ` + -Repo "git-for-windows/git" ` + -Version "latest" ` + -FileName (Split-Path $downloadUrl -Leaf) ` + -HashType "SHA256" + +Install-Binary ` + -Url $downloadUrl ` + -InstallArgs @(` + "/VERYSILENT", ` + "/NORESTART", ` + "/NOCANCEL", ` + "/SP-", ` + "/CLOSEAPPLICATIONS", ` + "/RESTARTAPPLICATIONS", ` + "/o:PathOption=CmdTools", ` + "/o:BashTerminalOption=ConHost", ` + "/o:EnableSymlinks=Enabled", ` + "/COMPONENTS=gitlfs") ` + -ExpectedSHA256Sum $externalHash + +Update-Environment + +git config --system --add safe.directory "*" +if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to configure safe.directory for Git with exit code $LASTEXITCODE" +} + +# Disable GCM machine-wide +[Environment]::SetEnvironmentVariable("GCM_INTERACTIVE", "Never", "Machine") + +# Add to PATH +Add-MachinePathItem "C:\Program Files\Git\bin" + +# Add well-known SSH host keys to ssh_known_hosts +ssh-keyscan -t rsa, ecdsa, ed25519 github.com >> "C:\Program Files\Git\etc\ssh\ssh_known_hosts" +ssh-keyscan -t rsa ssh.dev.azure.com >> "C:\Program Files\Git\etc\ssh\ssh_known_hosts" + +Invoke-PesterTests -TestFile "Git" diff --git a/images/windows/scripts/build/Install-GitHub-CLI.ps1 b/images/windows/scripts/build/Install-GitHub-CLI.ps1 new file mode 100644 index 0000000..0d1d80f --- /dev/null +++ b/images/windows/scripts/build/Install-GitHub-CLI.ps1 @@ -0,0 +1,28 @@ +################################################################################ +## File: Install-GitHub-CLI.ps1 +## Desc: Install GitHub CLI +## Supply chain security: GitHub CLI - checksum validation +################################################################################ + +Write-Host "Get the latest gh version..." + +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "cli/cli" ` + -Version "latest" ` + -UrlMatchPattern "gh_*_windows_amd64.msi" + +$checksumsUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "cli/cli" ` + -Version "latest" ` + -UrlMatchPattern "gh_*_checksums.txt" +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url $checksumsUrl ` + -FileName (Split-Path $downloadUrl -Leaf) + +Install-Binary ` + -Url $downloadUrl ` + -ExpectedSHA256Sum $externalHash + +Add-MachinePathItem "C:\Program Files (x86)\GitHub CLI" + +Invoke-PesterTests -TestFile "CLI.Tools" -TestName "GitHub CLI" diff --git a/images/windows/scripts/build/Install-GoogleCloudCLI.ps1 b/images/windows/scripts/build/Install-GoogleCloudCLI.ps1 new file mode 100644 index 0000000..dbdb782 --- /dev/null +++ b/images/windows/scripts/build/Install-GoogleCloudCLI.ps1 @@ -0,0 +1,12 @@ +################################################################################ +## File: Install-GoogleCloudCLI.ps1 +## Desc: Install Google Cloud CLI +################################################################################ + +# https://cloud.google.com/sdk/docs/downloads-interactive +Install-Binary ` + -Url 'https://dl.google.com/dl/cloudsdk/channels/rapid/GoogleCloudSDKInstaller.exe' ` + -InstallArgs @("/S", "/allusers", "/noreporting") ` + -ExpectedSignature '607A3EDAA64933E94422FC8F0C80388E0590986C' + +Invoke-PesterTests -TestFile "Tools" -TestName "GoogleCloudCLI" diff --git a/images/windows/scripts/build/Install-Haskell.ps1 b/images/windows/scripts/build/Install-Haskell.ps1 new file mode 100644 index 0000000..c354b40 --- /dev/null +++ b/images/windows/scripts/build/Install-Haskell.ps1 @@ -0,0 +1,64 @@ +################################################################################ +## File: Install-Haskell.ps1 +## Desc: Install Haskell for Windows +################################################################################ + +# install minimal ghcup, utilizing pre-installed msys2 at C:\msys64 +Write-Host 'Installing ghcup...' +$msysPath = "C:\msys64" +$ghcupPrefix = "C:\" +$cabalDir = "C:\cabal" + +$ghcupDownloadURL = "https://downloads.haskell.org/~ghcup/x86_64-mingw64-ghcup.exe" + +# If you want to install a specific version of ghcup, uncomment the following lines +# $ghver = "0.1.19.4" +# $ghcupDownloadURL = "https://downloads.haskell.org/~ghcup/${ghver}/x86_64-mingw64-ghcup-${ghver}.exe" + +# Other option is to download ghcup from GitHub releases: +# https://github.com/haskell/ghcup-hs/releases/latest + +New-Item -Path "$ghcupPrefix\ghcup" -ItemType 'directory' -ErrorAction SilentlyContinue | Out-Null +New-Item -Path "$ghcupPrefix\ghcup\bin" -ItemType 'directory' -ErrorAction SilentlyContinue | Out-Null +Invoke-DownloadWithRetry -Url $ghcupDownloadURL -Path "$ghcupPrefix\ghcup\bin\ghcup.exe" + +[Environment]::SetEnvironmentVariable("GHCUP_INSTALL_BASE_PREFIX", $ghcupPrefix, "Machine") +[Environment]::SetEnvironmentVariable("GHCUP_MSYS2", $msysPath, "Machine") +[Environment]::SetEnvironmentVariable("CABAL_DIR", $cabalDir, "Machine") +Add-MachinePathItem "$ghcupPrefix\ghcup\bin" +Add-MachinePathItem "$cabalDir\bin" +Update-Environment + +# Get 3 latest versions of GHC +$versions = ghcup list -t ghc -r | Where-Object { $_ -notlike "prerelease" } +$versionsOutput = [version[]]($versions | ForEach-Object { $_.Split(' ')[1]; }) +$latestMajorMinor = $versionsOutput | Group-Object { $_.ToString(2) } | Sort-Object { [Version] $_.Name } | Select-Object -last 3 +$versionsList = $latestMajorMinor | ForEach-Object { $_.Group | Select-Object -Last 1 } | Sort-Object + +# The latest version will be installed as a default +foreach ($version in $versionsList) { + Write-Host "Installing ghc $version..." + ghcup install ghc $version + if ($LastExitCode -ne 0) { + throw "GHC installation failed with exit code $LastExitCode" + } + ghcup set ghc $version + if ($LastExitCode -ne 0) { + throw "Setting GHC version failed with exit code $LastExitCode" + } +} + +# Add default version of GHC to path +$defaultGhcVersion = $versionsList | Select-Object -Last 1 +ghcup set ghc $defaultGhcVersion +if ($LastExitCode -ne 0) { + throw "Setting default GHC version failed with exit code $LastExitCode" +} + +Write-Host 'Installing cabal...' +ghcup install cabal latest +if ($LastExitCode -ne 0) { + throw "Cabal installation failed with exit code $LastExitCode" +} + +Invoke-PesterTests -TestFile 'Haskell' diff --git a/images/windows/scripts/build/Install-IEWebDriver.ps1 b/images/windows/scripts/build/Install-IEWebDriver.ps1 new file mode 100644 index 0000000..1008f49 --- /dev/null +++ b/images/windows/scripts/build/Install-IEWebDriver.ps1 @@ -0,0 +1,30 @@ +################################################################################ +## File: Install-IEWebDriver.ps1 +## Desc: Install IE Web Driver +################################################################################ + +$seleniumMajorVersion = (Get-ToolsetContent).selenium.version +$ieDriverUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "SeleniumHQ/selenium" ` + -Version "$seleniumMajorVersion.*" ` + -Asset "IEDriverServer_x64_*.zip" + +# Download IE selenium driver +Write-Host "Selenium IEDriverServer download and install..." +$driverZipFile = Invoke-DownloadWithRetry $ieDriverUrl + +$ieDriverPath = "C:\SeleniumWebDrivers\IEDriver" +if (-not (Test-Path -Path $ieDriverPath)) { + New-Item -Path $ieDriverPath -ItemType Directory -Force | Out-Null +} + +Expand-7ZipArchive -Path $driverZipFile -DestinationPath $ieDriverPath +Remove-Item $driverZipFile + +Write-Host "Get the IEDriver version..." +(Get-Item "$ieDriverPath\IEDriverServer.exe").VersionInfo.FileVersion | Out-File -FilePath "$ieDriverPath\versioninfo.txt" + +Write-Host "Setting the IEWebDriver environment variables" +[Environment]::SetEnvironmentVariable("IEWebDriver", $ieDriverPath, "Machine") + +Invoke-PesterTests -TestFile "Browsers" -TestName "Internet Explorer" diff --git a/images/windows/scripts/build/Install-JavaTools.ps1 b/images/windows/scripts/build/Install-JavaTools.ps1 new file mode 100644 index 0000000..655e6ad --- /dev/null +++ b/images/windows/scripts/build/Install-JavaTools.ps1 @@ -0,0 +1,141 @@ +################################################################################ +## File: Install-JavaTools.ps1 +## Desc: Install various JDKs and java tools +## Supply chain security: JDK - checksum validation +################################################################################ + +function Set-JavaPath { + param ( + [string] $Version, + [string] $Architecture = "x64", + [switch] $Default + ) + + $javaPathPattern = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath "Java_Temurin-Hotspot_jdk/${Version}*/${Architecture}" + $javaPath = (Get-Item -Path $javaPathPattern).FullName + + if ([string]::IsNullOrEmpty($javaPath)) { + Write-Host "Not found path to Java '${Version}'" + exit 1 + } + + Write-Host "Set 'JAVA_HOME_${Version}_X64' environmental variable as $javaPath" + [Environment]::SetEnvironmentVariable("JAVA_HOME_${Version}_X64", $javaPath, "Machine") + + if ($Default) { + # Clean up any other Java folders from PATH to make sure that they won't conflict with each other + $currentPath = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + + $pathSegments = $currentPath.Split(';') + $newPathSegments = @() + + foreach ($pathSegment in $pathSegments) { + if ($pathSegment -notlike '*java*') { + $newPathSegments += $pathSegment + } + } + + $newPath = [string]::Join(';', $newPathSegments) + $newPath = $javaPath + '\bin;' + $newPath + + Write-Host "Add $javaPath\bin to PATH" + [Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine") + + Write-Host "Set JAVA_HOME environmental variable as $javaPath" + [Environment]::SetEnvironmentVariable("JAVA_HOME", $javaPath, "Machine") + } +} + +function Install-JavaJDK { + param( + [string] $JDKVersion, + [string] $Architecture = "x64" + ) + + # Get Java version from api + $assetUrl = Invoke-RestMethod -Uri "https://api.adoptium.net/v3/assets/latest/${JDKVersion}/hotspot" + + $asset = $assetUrl | Where-Object { + $_.binary.os -eq "windows" ` + -and $_.binary.architecture -eq $Architecture ` + -and $_.binary.image_type -eq "jdk" + } + + # Download and extract java binaries to temporary folder + $downloadUrl = $asset.binary.package.link + $archivePath = Invoke-DownloadWithRetry $downloadUrl + Test-FileChecksum $archivePath -ExpectedSHA256Sum $asset.binary.package.checksum + + # 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 + $fullJavaVersion = $asset.version.semver -replace '\+', '-' + # Remove 'LTS' suffix from the version if present + $fullJavaVersion = $fullJavaVersion -replace '\.LTS$', '' + # Create directories in toolcache path + $javaToolcachePath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath "Java_Temurin-Hotspot_jdk" + $javaVersionPath = Join-Path -Path $javaToolcachePath -ChildPath $fullJavaVersion + $javaArchPath = Join-Path -Path $javaVersionPath -ChildPath $Architecture + + if (-not (Test-Path $javaToolcachePath)) { + Write-Host "Creating Temurin-Hotspot toolcache folder" + New-Item -ItemType Directory -Path $javaToolcachePath | Out-Null + } + + Write-Host "Creating Java '${fullJavaVersion}' folder in '${javaVersionPath}'" + New-Item -ItemType Directory -Path $javaVersionPath -Force | Out-Null + + # Complete the installation by extracting Java binaries to toolcache and creating the complete file + Expand-7ZipArchive -Path $archivePath -DestinationPath $javaVersionPath + Invoke-ScriptBlockWithRetry -Command { + Get-ChildItem -Path $javaVersionPath | Rename-Item -NewName $javaArchPath -ErrorAction Stop + } + New-Item -ItemType File -Path $javaVersionPath -Name "$Architecture.complete" | Out-Null +} + +$toolsetJava = (Get-ToolsetContent).java +$defaultVersion = $toolsetJava.default +$jdkVersionsToInstall = $toolsetJava.versions + +foreach ($jdkVersionToInstall in $jdkVersionsToInstall) { + $isDefaultVersion = $jdkVersionToInstall -eq $defaultVersion + + Install-JavaJDK -JDKVersion $jdkVersionToInstall + + if ($isDefaultVersion) { + Set-JavaPath -Version $jdkVersionToInstall -Default + } else { + Set-JavaPath -Version $jdkVersionToInstall + } +} + +# Install Java tools +# Force chocolatey to ignore dependencies on Ant and Maven or else they will download the Oracle JDK +Install-ChocoPackage ant -ArgumentList "--ignore-dependencies" +# Maven 3.9.x has multiple compatibilities problems +$toolsetMavenVersion = (Get-ToolsetContent).maven.version +$versionToInstall = Resolve-ChocoPackageVersion -PackageName "maven" -TargetVersion $toolsetMavenVersion + +Install-ChocoPackage maven -ArgumentList "--version=$versionToInstall" +Install-ChocoPackage gradle + +# Add maven env variables to Machine +[string] $m2Path = ([Environment]::GetEnvironmentVariable("PATH", "Machine")).Split(";") -match "maven" + +$m2RepoPath = 'C:\ProgramData\m2' +New-Item -Path $m2RepoPath -ItemType Directory -Force | Out-Null + +[Environment]::SetEnvironmentVariable("M2", $m2Path, "Machine") +[Environment]::SetEnvironmentVariable("M2_REPO", $m2RepoPath, "Machine") +[Environment]::SetEnvironmentVariable("MAVEN_OPTS", "-Xms256m", "Machine") + +# Download cobertura jars +$uri = 'https://repo1.maven.org/maven2/net/sourceforge/cobertura/cobertura/2.1.1/cobertura-2.1.1-bin.zip' +$sha256sum = '79479DDE416B082F38ECD1F2F7C6DEBD4D0C2249AF80FD046D1CE05D628F2EC6' +$coberturaPath = "C:\cobertura-2.1.1" + +$archivePath = Invoke-DownloadWithRetry $uri +Test-FileChecksum $archivePath -ExpectedSHA256Sum $sha256sum +Expand-7ZipArchive -Path $archivePath -DestinationPath "C:\" + +[Environment]::SetEnvironmentVariable("COBERTURA_HOME", $coberturaPath, "Machine") + +Invoke-PesterTests -TestFile "Java" diff --git a/images/windows/scripts/build/Install-Kotlin.ps1 b/images/windows/scripts/build/Install-Kotlin.ps1 new file mode 100644 index 0000000..e10c269 --- /dev/null +++ b/images/windows/scripts/build/Install-Kotlin.ps1 @@ -0,0 +1,28 @@ +################################################################################ +## File: Install-Kotlin.ps1 +## Desc: Install Kotlin +## Supply chain security: Kotlin - checksum validation +################################################################################ + +# Install Kotlin +$kotlinVersion = (Get-ToolsetContent).kotlin.version + +$kotlinDownloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "JetBrains/kotlin" ` + -Version "$kotlinVersion" ` + -Asset "kotlin-compiler-*.zip" +$kotlinArchivePath = Invoke-DownloadWithRetry $kotlinDownloadUrl + +#region Supply chain security +$externalHash = Get-Content $(Invoke-DownloadWithRetry "$kotlinDownloadUrl.sha256") +Test-FileChecksum $kotlinArchivePath -ExpectedSHA256Sum $externalHash +#endregion + +Write-Host "Expand Kotlin archive" +$kotlinPath = "C:\tools" +Expand-7ZipArchive -Path $kotlinArchivePath -DestinationPath $kotlinPath + +# Add to PATH +Add-MachinePathItem "$kotlinPath\kotlinc\bin" + +Invoke-PesterTests -TestFile "Tools" -TestName "Kotlin" diff --git a/images/windows/scripts/build/Install-KubernetesTools.ps1 b/images/windows/scripts/build/Install-KubernetesTools.ps1 new file mode 100644 index 0000000..59c4087 --- /dev/null +++ b/images/windows/scripts/build/Install-KubernetesTools.ps1 @@ -0,0 +1,37 @@ +################################################################################ +## File: Install-KubernetesTools.ps1 +## Desc: Install tools for K8s. +## Supply chain security: GitHub Kind - checksum validation, Kubectl, Helm, Minikube - by package manager +################################################################################ + +Write-Host "Install Kind" +# Choco installation can't be used because it depends on docker-desktop + +$targetDir = "C:\ProgramData\kind" +New-Item -Path $targetDir -ItemType Directory -Force | Out-Null + +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "kubernetes-sigs/kind" ` + -Version "latest" ` + -UrlMatchPattern "kind-windows-amd64" +$packagePath = Invoke-DownloadWithRetry -Url $downloadUrl -Path "$targetDir\kind.exe" + +#region Supply chain security - Kind +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url "$downloadUrl.sha256sum" ` + -FileName (Split-Path $downloadUrl -Leaf) +Test-FileChecksum $packagePath -ExpectedSHA256Sum $externalHash +#endregion + +Add-MachinePathItem $targetDir + +Write-Host "Install Kubectl" +Install-ChocoPackage kubernetes-cli + +Write-Host "Install Helm" +Install-ChocoPackage kubernetes-helm + +Write-Host "Install Minikube" +Install-ChocoPackage minikube + +Invoke-PesterTests -TestFile "Tools" -TestName "KubernetesTools" diff --git a/images/windows/scripts/build/Install-LLVM.ps1 b/images/windows/scripts/build/Install-LLVM.ps1 new file mode 100644 index 0000000..41b8aef --- /dev/null +++ b/images/windows/scripts/build/Install-LLVM.ps1 @@ -0,0 +1,10 @@ +################################################################################ +## File: Install-LLVM.ps1 +## Desc: Install the latest stable version of llvm and clang compilers +################################################################################ + +$llvmVersion = (Get-ToolsetContent).llvm.version +$latestChocoVersion = Resolve-ChocoPackageVersion -PackageName "llvm" -TargetVersion $llvmVersion +Install-ChocoPackage llvm -ArgumentList '--version', $latestChocoVersion + +Invoke-PesterTests -TestFile "LLVM" diff --git a/images/windows/scripts/build/Install-Mercurial.ps1 b/images/windows/scripts/build/Install-Mercurial.ps1 new file mode 100644 index 0000000..8ab9382 --- /dev/null +++ b/images/windows/scripts/build/Install-Mercurial.ps1 @@ -0,0 +1,11 @@ +################################################################################ +## File: Install-Mercurial.ps1 +## Desc: Install Mercurial +################################################################################ + +Install-ChocoPackage hg -ArgumentList "--version", "5.0.0" + +Add-MachinePathItem "${env:ProgramFiles}\Mercurial\" +Update-Environment + +Invoke-PesterTests -TestFile "Tools" -TestName "Mercurial" diff --git a/images/windows/scripts/build/Install-Mingw64.ps1 b/images/windows/scripts/build/Install-Mingw64.ps1 new file mode 100644 index 0000000..e07ab83 --- /dev/null +++ b/images/windows/scripts/build/Install-Mingw64.ps1 @@ -0,0 +1,70 @@ +################################################################################ +## File: Install-Mingw64.ps1 +## Desc: Install GNU tools for Windows +################################################################################ + +if (Test-IsWin25 -or Test-IsWin19) { + # If Windows 2019, install version 8.1.0 form sourceforge + $baseUrl = "https://download.qt.io/development_releases/prebuilt" + + $("mingw32", "mingw64") | ForEach-Object { + if ($_ -eq "mingw32") { + $url = "$baseUrl/mingw_32/i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z" + $sha256sum = 'adb84b70094c0225dd30187ff995e311d19424b1eb8f60934c60e4903297f946' + } elseif ($_ -eq "mingw64") { + $url = "$baseUrl/mingw_64/x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z" + $sha256sum = '853970527b5de4a55ec8ca4d3fd732c00ae1c69974cc930c82604396d43e79f8' + } else { + throw "Unknown architecture $_" + } + + $packagePath = Invoke-DownloadWithRetry $url + Test-FileChecksum -Path $packagePath -ExpectedSHA256Sum $sha256sum + Expand-7ZipArchive -Path $packagePath -DestinationPath "C:\" + + # Make a copy of mingw-make.exe to make.exe, which is a more discoverable name + # and so the same command line can be used on Windows as on macOS and Linux + $path = "C:\$_\bin\mingw32-make.exe" | Get-Item + Copy-Item -Path $path -Destination (Join-Path $path.Directory 'make.exe') + } + + Add-MachinePathItem "C:\mingw64\bin" + +} + +if (Test-IsWin22 -or Test-IsWin25) { + # If Windows 2022, install version specified in the toolset + $version = (Get-ToolsetContent).mingw.version + $runtime = (Get-ToolsetContent).mingw.runtime + + $("mingw32", "mingw64") | ForEach-Object { + if ($_ -eq "mingw32") { + $arch = "i686" + $threads = "posix" + $exceptions = "dwarf" + } elseif ($_ -eq "mingw64") { + $arch = "x86_64" + $threads = "posix" + $exceptions = "seh" + } else { + throw "Unknown architecture $_" + } + + $url = Resolve-GithubReleaseAssetUrl ` + -Repo "niXman/mingw-builds-binaries" ` + -Version "$version" ` + -Asset "$arch-*-release-$threads-$exceptions-$runtime-*.7z" + + $packagePath = Invoke-DownloadWithRetry $url + Expand-7ZipArchive -Path $packagePath -DestinationPath "C:\" + + # Make a copy of mingw-make.exe to make.exe, which is a more discoverable name + # and so the same command line can be used on Windows as on macOS and Linux + $path = "C:\$_\bin\mingw32-make.exe" | Get-Item + Copy-Item -Path $path -Destination (Join-Path $path.Directory 'make.exe') + } + + Add-MachinePathItem "C:\mingw64\bin" +} + +Invoke-PesterTests -TestFile "Tools" -TestName "Mingw64" diff --git a/images/windows/scripts/build/Install-Miniconda.ps1 b/images/windows/scripts/build/Install-Miniconda.ps1 new file mode 100644 index 0000000..91054a9 --- /dev/null +++ b/images/windows/scripts/build/Install-Miniconda.ps1 @@ -0,0 +1,32 @@ +################################################################################ +## File: Install-Miniconda.ps1 +## Desc: Install the latest version of Miniconda and set $env:CONDA +## Supply chain security: checksum validation +################################################################################ + +$condaDestination = "C:\Miniconda" +$installerName = "Miniconda3-latest-Windows-x86_64.exe" + +#region Supply chain security +$distributorFileHash = $null +$checksums = (ConvertFrom-HTML -Uri 'https://repo.anaconda.com/miniconda/').SelectNodes('//html/body/table/tr') + +foreach ($node in $checksums) { + if ($node.ChildNodes[1].InnerText -eq $installerName) { + $distributorFileHash = $node.ChildNodes[7].InnerText + } +} + +if ($null -eq $distributorFileHash) { + throw "Unable to find checksum for $installerName in https://repo.anaconda.com/miniconda/" +} +#endregion + +Install-Binary ` + -Url "https://repo.anaconda.com/miniconda/${installerName}" ` + -InstallArgs @("/S", "/AddToPath=0", "/RegisterPython=0", "/D=$condaDestination") ` + -ExpectedSHA256Sum $distributorFileHash + +[Environment]::SetEnvironmentVariable("CONDA", $condaDestination, "Machine") + +Invoke-PesterTests -TestFile "Miniconda" diff --git a/images/windows/scripts/build/Install-MongoDB.ps1 b/images/windows/scripts/build/Install-MongoDB.ps1 new file mode 100644 index 0000000..b73e4f0 --- /dev/null +++ b/images/windows/scripts/build/Install-MongoDB.ps1 @@ -0,0 +1,56 @@ +#################################################################################### +## File: Install-MongoDB.ps1 +## Desc: Install MongoDB +#################################################################################### + +# Install mongodb package +$toolsetContent = Get-ToolsetContent +$toolsetVersion = $toolsetContent.mongodb.version + +$getMongoReleases = Invoke-WebRequest -Uri "mongodb.com/docs/manual/release-notes/$toolsetVersion/" -UseBasicParsing +$targetReleases = $getMongoReleases.Links.href | Where-Object { $_ -like "#$toolsetVersion*---*" } + +$minorVersions = @() +foreach ($release in $targetReleases) { + if ($release -notlike "*upcoming*") { + $pattern = '\d+\.\d+\.\d+' + $version = $release | Select-String -Pattern $pattern -AllMatches | ForEach-Object { $_.Matches } | ForEach-Object { $_.Value } + $minorVersions += $version + } +} + +$latestVersion = $minorVersions[0] + +Install-Binary ` + -Url "https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-$latestVersion-signed.msi" ` + -ExtraInstallArgs @('TARGETDIR=C:\PROGRA~1\MongoDB ADDLOCAL=ALL') ` + -ExpectedSignature $toolsetContent.mongodb.signature + +# Add mongodb to the PATH +$mongoPath = (Get-CimInstance Win32_Service -Filter "Name LIKE 'mongodb'").PathName +$mongoBin = Split-Path -Path $mongoPath.split('"')[1] +Add-MachinePathItem "$mongoBin" + +# Wait for mongodb service running +$mongodbService = Get-Service "mongodb" +$mongodbService.WaitForStatus('Running', '00:01:00') + +# Stop and disable mongodb service +Stop-Service $mongodbService +$mongodbService | Set-Service -StartupType Disabled + +# Install mongodb shell for mongodb > 5 +if (Test-IsWin25) { + $mongoshVersion = (Get-GithubReleasesByVersion -Repo "mongodb-js/mongosh" -Version "latest").version + + $mongoshDownloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "mongodb-js/mongosh" ` + -Version $mongoshVersion ` + -UrlMatchPattern "mongosh-*-x64.msi" + + Install-Binary -Type MSI ` + -Url $mongoshDownloadUrl ` + -ExpectedSignature 'F2D7C28591847BB2CB2B1C2A0C59459FDC728A38' +} + +Invoke-PesterTests -TestFile "Databases" -TestName "MongoDB" diff --git a/images/windows/scripts/build/Install-Msys2.ps1 b/images/windows/scripts/build/Install-Msys2.ps1 new file mode 100644 index 0000000..8d97003 --- /dev/null +++ b/images/windows/scripts/build/Install-Msys2.ps1 @@ -0,0 +1,136 @@ +################################################################################ +## File: Install-Msys2.ps1 +## Desc: Install Msys2 and 64 & 32 bit gcc, cmake, & llvm +################################################################################ + +# References +# https://github.com/msys2/MINGW-packages/blob/master/azure-pipelines.yml +# https://packages.msys2.org/group/ + +$logPrefix = "`n" + ("-" * 40) + "`n---" +$origPath = $env:PATH + +function Install-Msys2 { + # We can't use Resolve-GithubReleaseAssetUrl function here + # because msys2-installer releases don't have a consistent versioning scheme + + $assets = (Invoke-RestMethod -Uri "https://api.github.com/repos/msys2/msys2-installer/releases/latest").assets + $downloadUri = ($assets | Where-Object { $_.name -match "^msys2-x86_64" -and $_.name.EndsWith(".exe") }).browser_download_url + $installerName = Split-Path $downloadUri -Leaf + + # Download the latest msys2 x86_64, filename includes release date + Write-Host "Download msys2 installer $installerName" + $installerPath = Invoke-DownloadWithRetry $downloadUri + + #region Supply chain security - MSYS2 + $externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url ($downloadUri -replace $installerName, "msys2-checksums.txt") ` + -FileName $installerName + Test-FileChecksum $installerPath -ExpectedSHA256Sum $externalHash + #endregion + + Write-Host "Starting msys2 installation" + & $installerPath in --confirm-command --accept-messages --root C:/msys64 + if ($LastExitCode -ne 0) { + throw "MSYS2 installation failed with exit code $LastExitCode" + } + Remove-Item $installerPath +} + +function Install-Msys2Packages { + param ( + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [string[]]$Packages + ) + + if (-not $Packages) { + return + } + + Write-Host "$logPrefix Install msys2 packages" + pacman.exe -S --noconfirm --needed --noprogressbar $Packages + if ($LastExitCode -ne 0) { + throw "MSYS2 packages installation failed with exit code $LastExitCode" + } + taskkill /f /fi "MODULES eq msys-2.0.dll" + + Write-Host "$logPrefix Remove p7zip/7z package due to conflicts" + pacman.exe -R --noconfirm --noprogressbar p7zip + if ($LastExitCode -ne 0) { + throw "Removal of p7zip/7z package failed with exit code $LastExitCode" + } +} + +function Install-MingwPackages { + param ( + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [object[]] $Packages + ) + + if (-not $Packages) { + return + } + + Write-Host "$logPrefix Install mingw packages" + $archs = $Packages.arch + + foreach ($arch in $archs) { + Write-Host "Installing $arch packages" + $archPackages = $toolsetContent.mingw | Where-Object { $_.arch -eq $arch } + $runtimePackages = $archPackages.runtime_packages.name | ForEach-Object { "${arch}-$_" } + $additionalPackages = $archPackages.additional_packages | ForEach-Object { "${arch}-$_" } + $packagesToInstall = $runtimePackages + $additionalPackages + Write-Host "The following packages will be installed: $packagesToInstall" + pacman.exe -S --noconfirm --needed --noprogressbar $packagesToInstall + if ($LastExitCode -ne 0) { + throw "Installation of $arch packages failed with exit code $LastExitCode" + } + } + + # clean all packages to decrease image size + Write-Host "$logPrefix Clean packages" + pacman.exe -Scc --noconfirm + if ($LastExitCode -ne 0) { + throw "Cleaning of packages failed with exit code $LastExitCode" + } + + $pkgs = pacman.exe -Q + if ($LastExitCode -ne 0) { + throw "Listing of packages failed with exit code $LastExitCode" + } + + foreach ($arch in $archs) { + Write-Host "$logPrefix Installed $arch packages" + $pkgs | Select-String -Pattern "^${arch}-" + } +} + +Install-Msys2 + +# Add msys2 bin tools folders to PATH temporary +$env:PATH = "C:\msys64\mingw64\bin;C:\msys64\usr\bin;$origPath" + +Write-Host "$logPrefix pacman --noconfirm -Syyuu" +pacman.exe -Syyuu --noconfirm +if ($LastExitCode -ne 0) { + throw "Updating of packages failed with exit code $LastExitCode" +} +taskkill /f /fi "MODULES eq msys-2.0.dll" + +Write-Host "$logPrefix pacman --noconfirm -Syuu (2nd pass)" +pacman.exe -Syuu --noconfirm +if ($LastExitCode -ne 0) { + throw "Second pass updating of packages failed with exit code $LastExitCode" +} +taskkill /f /fi "MODULES eq msys-2.0.dll" + +$toolsetContent = (Get-ToolsetContent).MsysPackages +Install-Msys2Packages -Packages $toolsetContent.msys2 +Install-MingwPackages -Packages $toolsetContent.mingw + +$env:PATH = $origPath +Write-Host "`nMSYS2 installation completed" + +Invoke-PesterTests -TestFile "MSYS2" diff --git a/images/windows/scripts/build/Install-MysqlCli.ps1 b/images/windows/scripts/build/Install-MysqlCli.ps1 new file mode 100644 index 0000000..69d39a6 --- /dev/null +++ b/images/windows/scripts/build/Install-MysqlCli.ps1 @@ -0,0 +1,40 @@ +################################################################################ +## File: Install-MysqlCli.ps1 +## Desc: Install Mysql CLI +## Supply chain security: checksum validation (visual c++ redistributable package) +################################################################################ + +# Installing visual c++ redistributable package. +Install-Binary ` + -Url 'https://download.microsoft.com/download/0/5/6/056dcda9-d667-4e27-8001-8a0c6971d6b1/vcredist_x64.exe' ` + -InstallArgs @("/install", "/quiet", "/norestart") ` + -ExpectedSHA256Sum '20E2645B7CD5873B1FA3462B99A665AC8D6E14AAE83DED9D875FEA35FFDD7D7E' + +# Downloading mysql +[version] $mysqlVersion = (Get-ToolsetContent).mysql.version +$mysqlVersionMajorMinor = $mysqlVersion.ToString(2) + +if ($mysqlVersion.Build -lt 0) { + if ($mysqlVersionMajorMinor -eq "5.7") { + $downloadsPageUrl = "https://downloads.mysql.com/archives/community/" + } else { + $downloadsPageUrl = "https://dev.mysql.com/downloads/mysql/${mysqlVersionMajorMinor}.html" + } + $mysqlVersion = Invoke-RestMethod -Uri $downloadsPageUrl -Headers @{ 'User-Agent' = 'curl/8.4.0' } ` + | Select-String -Pattern "${mysqlVersionMajorMinor}\.\d+" ` + | ForEach-Object { $_.Matches.Value } +} + +$mysqlVersionFull = $mysqlVersion.ToString() +$mysqlVersionUrl = "https://cdn.mysql.com/Downloads/MySQL-${mysqlVersionMajorMinor}/mysql-${mysqlVersionFull}-winx64.msi" + +Install-Binary ` + -Url $mysqlVersionUrl ` + -ExpectedSignature (Get-ToolsetContent).mysql.signature + +# Adding mysql in system environment path +$mysqlPath = $(Get-ChildItem -Path "C:\PROGRA~1\MySQL" -Directory)[0].FullName + +Add-MachinePathItem "${mysqlPath}\bin" + +Invoke-PesterTests -TestFile "Databases" -TestName "MySQL" diff --git a/images/windows/scripts/build/Install-NET48-devpack.ps1 b/images/windows/scripts/build/Install-NET48-devpack.ps1 new file mode 100644 index 0000000..676b762 --- /dev/null +++ b/images/windows/scripts/build/Install-NET48-devpack.ps1 @@ -0,0 +1,13 @@ +################################################################################ +## File: Install-NET48-devpack.ps1 +## Desc: Install .NET 4.8 devpack +## Supply chain security: checksum validation +################################################################################ + +# .NET 4.8 Dev pack +Install-Binary ` + -Url 'https://download.visualstudio.microsoft.com/download/pr/014120d7-d689-4305-befd-3cb711108212/0307177e14752e359fde5423ab583e43/ndp48-devpack-enu.exe' ` + -InstallArgs @("Setup", "/passive", "/norestart") ` + -ExpectedSHA256Sum '0A7AC4A9B44CED6BB7A0EBF3AD9BA29F60BD4D3BEB2047E19F4D8749DE61F5AC' + +Invoke-PesterTests -TestFile "Tools" -TestName "NET48" diff --git a/images/windows/scripts/build/Install-NET48.ps1 b/images/windows/scripts/build/Install-NET48.ps1 new file mode 100644 index 0000000..799428a --- /dev/null +++ b/images/windows/scripts/build/Install-NET48.ps1 @@ -0,0 +1,11 @@ +################################################################################ +## File: Install-NET48.ps1 +## Desc: Install .NET 4.8 +## Supply chain security: checksum validation +################################################################################ + +# .NET 4.8 Dev pack +Install-Binary ` + -Url 'https://download.visualstudio.microsoft.com/download/pr/2d6bb6b2-226a-4baa-bdec-798822606ff1/8494001c276a4b96804cde7829c04d7f/ndp48-x86-x64-allos-enu.exe' ` + -InstallArgs @("Setup", "/passive", "/norestart") ` + -ExpectedSHA256Sum '68C9986A8DCC0214D909AA1F31BEE9FB5461BB839EDCA996A75B08DDFFC1483F' diff --git a/images/windows/scripts/build/Install-NSIS.ps1 b/images/windows/scripts/build/Install-NSIS.ps1 new file mode 100644 index 0000000..bb03ed2 --- /dev/null +++ b/images/windows/scripts/build/Install-NSIS.ps1 @@ -0,0 +1,14 @@ +################################################################################ +## File: Install-NSIS.ps1 +## Desc: Install NSIS +## Supply chain security: NSIS - managed by package manager +################################################################################ + +$nsisVersion = (Get-ToolsetContent).nsis.version + +Install-ChocoPackage nsis -ArgumentList "--version", "$nsisVersion" + +Add-MachinePathItem "${env:ProgramFiles(x86)}\NSIS\" +Update-Environment + +Invoke-PesterTests -TestFile "Tools" -TestName "NSIS" diff --git a/images/windows/scripts/build/Install-NativeImages.ps1 b/images/windows/scripts/build/Install-NativeImages.ps1 new file mode 100644 index 0000000..214a957 --- /dev/null +++ b/images/windows/scripts/build/Install-NativeImages.ps1 @@ -0,0 +1,22 @@ +################################################################################ +## File: Install-NativeImages.ps1 +## Desc: Generate and install native images for .NET assemblies +################################################################################ + +Write-Host "NGen: install Microsoft.PowerShell.Utility.Activities..." +& $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 +if ($LASTEXITCODE -ne 0) { + throw "Installation of Microsoft.PowerShell.Utility.Activities failed with exit code $LASTEXITCODE" +} + +Write-Host "NGen: update x64 native images..." +& $env:SystemRoot\Microsoft.NET\Framework64\v4.0.30319\ngen.exe update | Out-Null +if ($LASTEXITCODE -ne 0) { + throw "Update of x64 native images failed with exit code $LASTEXITCODE" +} + +Write-Host "NGen: update x86 native images..." +& $env:SystemRoot\Microsoft.NET\Framework\v4.0.30319\ngen.exe update | Out-Null +if ($LASTEXITCODE -ne 0) { + throw "Update of x86 native images failed with exit code $LASTEXITCODE" +} diff --git a/images/windows/scripts/build/Install-Nginx.ps1 b/images/windows/scripts/build/Install-Nginx.ps1 new file mode 100644 index 0000000..5453ced --- /dev/null +++ b/images/windows/scripts/build/Install-Nginx.ps1 @@ -0,0 +1,22 @@ +################################################################################ +## File: Install-Nginx.ps1 +## Desc: Install Nginx +################################################################################ + +# Stop w3svc service + +Stop-Service -Name w3svc + +# Install latest nginx in chocolatey +$installDir = "C:\tools" +Install-ChocoPackage nginx -ArgumentList "--force", "--params", "/installLocation:$installDir /port:80" + +# Stop and disable Nginx service +Stop-Service -Name nginx +Set-Service -Name nginx -StartupType Disabled + +# Start w3svc service +Start-Service -Name w3svc + +# Invoke Pester Tests +Invoke-PesterTests -TestFile "Nginx" diff --git a/images/windows/scripts/build/Install-NodeJS.ps1 b/images/windows/scripts/build/Install-NodeJS.ps1 new file mode 100644 index 0000000..05809a1 --- /dev/null +++ b/images/windows/scripts/build/Install-NodeJS.ps1 @@ -0,0 +1,32 @@ +################################################################################ +## File: Install-NodeJS.ps1 +## Desc: Install nodejs-lts and other common node tools. +## Must run after python is configured +################################################################################ + +$prefixPath = 'C:\npm\prefix' +$cachePath = 'C:\npm\cache' + +New-Item -Path $prefixPath -Force -ItemType Directory +New-Item -Path $cachePath -Force -ItemType Directory + +$defaultVersion = (Get-ToolsetContent).node.default +$versionToInstall = Resolve-ChocoPackageVersion -PackageName "nodejs" -TargetVersion $defaultVersion + +Install-ChocoPackage "nodejs" -ArgumentList "--version=$versionToInstall" + +Add-MachinePathItem $prefixPath +Update-Environment + +[Environment]::SetEnvironmentVariable("npm_config_prefix", $prefixPath, "Machine") +$env:npm_config_prefix = $prefixPath + +npm config set cache $cachePath --global +npm config set registry https://registry.npmjs.org/ + +$globalNpmPackages = (Get-ToolsetContent).npm.global_packages +$globalNpmPackages | ForEach-Object { + npm install -g $_.name +} + +Invoke-PesterTests -TestFile "Node" diff --git a/images/windows/scripts/build/Install-OpenSSL.ps1 b/images/windows/scripts/build/Install-OpenSSL.ps1 new file mode 100644 index 0000000..6b60acf --- /dev/null +++ b/images/windows/scripts/build/Install-OpenSSL.ps1 @@ -0,0 +1,44 @@ +################################################################################ +## File: Install-OpenSSL.ps1 +## Desc: Install win64-openssl. +## Supply chain security: checksum validation +################################################################################ + +$arch = 'INTEL' +$bits = '64' +$light = $false +$installerType = "exe" +$version = (Get-ToolsetContent).openssl.version +$installDir = "$env:ProgramFiles\OpenSSL" + +# Fetch available installers list +$jsonUrl = 'https://raw.githubusercontent.com/slproweb/opensslhashes/master/win32_openssl_hashes.json' + +$installersAvailable = (Invoke-RestMethod $jsonUrl).files +$installerNames = $installersAvailable | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name + +$installerUrl = $null +$installerHash = $null + +foreach ($key in $installerNames) { + $installer = $installersAvailable.$key + if (($installer.light -eq $light) -and ($installer.arch -eq $arch) -and ($installer.bits -eq $bits) -and ($installer.installer -eq $installerType) -and ($installer.basever -eq $version)) { + $installerUrl = $installer.url + $installerHash = $installer.sha512 + } +} + +if ($null -eq $installerUrl) { + throw "Installer not found for version $version" +} + +Install-Binary ` + -Url $installerUrl ` + -InstallArgs @('/silent', '/sp-', '/suppressmsgboxes', "/DIR=`"$installDir`"") ` + -ExpectedSHA512Sum $installerHash + +# Update PATH +Add-MachinePathItem "$installDir\bin" +Update-Environment + +Invoke-PesterTests -TestFile "Tools" -TestName "OpenSSL" diff --git a/images/windows/scripts/build/Install-PHP.ps1 b/images/windows/scripts/build/Install-PHP.ps1 new file mode 100644 index 0000000..f4198aa --- /dev/null +++ b/images/windows/scripts/build/Install-PHP.ps1 @@ -0,0 +1,22 @@ +################################################################################ +## File: Install-PHP.ps1 +## Desc: Install PHP +################################################################################ + +# Install latest PHP in chocolatey +$installDir = "c:\tools\php" +$phpMajorMinor = (Get-ToolsetContent).php.version +$phpVersionToInstall = Resolve-ChocoPackageVersion -PackageName "php" -TargetVersion $phpMajorMinor +Install-ChocoPackage php -ArgumentList "--params", "/InstallDir:$installDir", "--version=$phpVersionToInstall" + +# Install latest Composer in chocolatey +Install-ChocoPackage composer -ArgumentList "--install-args", "/DEV=$installDir /PHP=$installDir" + +# update path to extensions and enable curl and mbstring extensions, and enable php openssl extensions. +((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 + +# Set the PHPROOT environment variable. +[Environment]::SetEnvironmentVariable("PHPROOT", $installDir, "Machine") + +# Invoke Pester Tests +Invoke-PesterTests -TestFile "PHP" diff --git a/images/windows/scripts/build/Install-Pipx.ps1 b/images/windows/scripts/build/Install-Pipx.ps1 new file mode 100644 index 0000000..41e9767 --- /dev/null +++ b/images/windows/scripts/build/Install-Pipx.ps1 @@ -0,0 +1,39 @@ +################################################################################ +## File: Install-Pipx.ps1 +## Desc: Install pipx and pipx packages +################################################################################ + +Write-Host "Installing pipx..." +$env:PIPX_BIN_DIR = "${env:ProgramFiles(x86)}\pipx_bin" +$env:PIPX_HOME = "${env:ProgramFiles(x86)}\pipx" + +pip install pipx +if ($LASTEXITCODE -ne 0) { + throw "pipx installation failed with exit code $LASTEXITCODE" +} + +Add-MachinePathItem "${env:PIPX_BIN_DIR}" +[Environment]::SetEnvironmentVariable("PIPX_BIN_DIR", $env:PIPX_BIN_DIR, "Machine") +[Environment]::SetEnvironmentVariable("PIPX_HOME", $env:PIPX_HOME, "Machine") + +Invoke-PesterTests -TestFile "Tools" -TestName "Pipx" + +Write-Host "Installing pipx packages..." + +$pipxToolset = (Get-ToolsetContent).pipx +foreach ($tool in $pipxToolset) { + if ($tool.python) { + $pythonPath = (Get-Item -Path "${env:AGENT_TOOLSDIRECTORY}\Python\${tool.python}.*\x64\python-${tool.python}*").FullName + Write-Host "Install ${tool.package} into python ${tool.python}" + pipx install $tool.package --python $pythonPath + } else { + Write-Host "Install ${tool.package} into default python" + pipx install $tool.package + } + + if ($LASTEXITCODE -ne 0) { + throw "Package ${tool.package} installation failed with exit code $LASTEXITCODE" + } +} + +Invoke-PesterTests -TestFile "PipxPackages" diff --git a/images/windows/scripts/build/Install-PostgreSQL.ps1 b/images/windows/scripts/build/Install-PostgreSQL.ps1 new file mode 100644 index 0000000..2bad885 --- /dev/null +++ b/images/windows/scripts/build/Install-PostgreSQL.ps1 @@ -0,0 +1,97 @@ +################################################################################ +## File: Install-PostgreSQL.ps1 +## Desc: Install PostgreSQL +################################################################################ + +# Define user and password for PostgreSQL database +$pgUser = "postgres" +$pgPwd = "root" + +# Prepare environment variable for validation +[Environment]::SetEnvironmentVariable("PGUSER", $pgUser, "Machine") +[Environment]::SetEnvironmentVariable("PGPASSWORD", $pgPwd, "Machine") + +$toolsetVersion = (Get-ToolsetContent).postgresql.version +if ($null -ne ($toolsetVersion | Select-String -Pattern '\d+\.\d+\.\d+')) { + $majorVersion = ([version]$toolsetVersion).Major + $minorVersion = ([version]$toolsetVersion).Minor + $patchVersion = ([version]$toolsetVersion).Build + $installerUrl = "https://get.enterprisedb.com/postgresql/postgresql-$majorVersion.$minorVersion-$patchVersion-windows-x64.exe" +} else { + # Define latest available version to install based on version specified in the toolset + $getPostgreReleases = Invoke-WebRequest -Uri "https://git.postgresql.org/gitweb/?p=postgresql.git;a=tags" -UseBasicParsing + # Getting all links matched to the pattern (e.g.a=log;h=refs/tags/REL_14) + $targetReleases = $getPostgreReleases.Links.href | Where-Object { $_ -match "a=log;h=refs/tags/REL_$toolsetVersion" } + [Int32] $outNumber = $null + $minorVersions = @() + foreach ($release in $targetReleases) { + $version = $release.split('/')[-1] + # Checking if the latest symbol of the release version is actually a number. If yes, add to $minorVersions array + if ([Int32]::TryParse($($version.Split('_')[-1]), [ref] $outNumber)) { + $minorVersions += $outNumber + } + } + # Sorting and getting the last one + $targetMinorVersions = ($minorVersions | Sort-Object)[-1] + + # In order to get rid of error messages (we know we will have them), force ErrorAction to SilentlyContinue + $errorActionOldValue = $ErrorActionPreference + $ErrorActionPreference = 'SilentlyContinue' + + # Install latest PostgreSQL + # Starting from number 9 and going down, check if the installer is available. If yes, break the loop. + # If an installer with $targetMinorVersions is not to be found, the $targetMinorVersions will be decreased by 1 + $increment = 9 + do { + $url = "https://get.enterprisedb.com/postgresql/postgresql-$toolsetVersion.$targetMinorVersions-$increment-windows-x64.exe" + $checkAccess = [System.Net.WebRequest]::Create($url) + $response = $null + $response = $checkAccess.GetResponse() + if ($response) { + $installerUrl = $response.ResponseUri.OriginalString + } elseif (!$response -and ($increment -eq 0)) { + $increment = 9 + $targetMinorVersions-- + } else { + $increment-- + } + } while (!$response) +} + +# Return the previous value of ErrorAction and invoke Install-Binary function +$ErrorActionPreference = $errorActionOldValue +$installerArgs = @("--install_runtimes 0", "--superpassword root", "--enable_acledit 1", "--unattendedmodeui none", "--mode unattended") +Install-Binary ` + -Url $installerUrl ` + -InstallArgs $installerArgs ` + -ExpectedSignature (Get-ToolsetContent).postgresql.signature + +# Get Path to pg_ctl.exe +$pgPath = (Get-CimInstance Win32_Service -Filter "Name LIKE 'postgresql-%'").PathName + +# Parse output of command above to obtain pure path +$pgBin = Split-Path -Path $pgPath.split('"')[1] +$pgRoot = Split-Path -Path $pgPath.split('"')[5] +$pgData = Join-Path $pgRoot "data" + +# Validate PostgreSQL installation +$pgReadyPath = Join-Path $pgBin "pg_isready.exe" +$pgReady = Start-Process -FilePath $pgReadyPath -Wait -PassThru +$exitCode = $pgReady.ExitCode + +if ($exitCode -ne 0) { + Write-Host -Object "PostgreSQL is not ready. Exitcode: $exitCode" + exit $exitCode +} + +# Added PostgreSQL environment variable +[Environment]::SetEnvironmentVariable("PGBIN", $pgBin, "Machine") +[Environment]::SetEnvironmentVariable("PGROOT", $pgRoot, "Machine") +[Environment]::SetEnvironmentVariable("PGDATA", $pgData, "Machine") + +# Stop and disable PostgreSQL service +$pgService = Get-Service -Name postgresql* +Stop-Service $pgService +$pgService | Set-Service -StartupType Disabled + +Invoke-PesterTests -TestFile "Databases" -TestName "PostgreSQL" diff --git a/images/windows/scripts/build/Install-PowerShellModules.ps1 b/images/windows/scripts/build/Install-PowerShellModules.ps1 new file mode 100644 index 0000000..2017e98 --- /dev/null +++ b/images/windows/scripts/build/Install-PowerShellModules.ps1 @@ -0,0 +1,27 @@ +################################################################################ +## File: Install-PowershellModules.ps1 +## Desc: Install common PowerShell modules +################################################################################ + +# Set TLS1.2 +[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor "Tls12" + +# Install PowerShell modules +$modules = (Get-ToolsetContent).powershellModules + +foreach ($module in $modules) { + $moduleName = $module.name + Write-Host "Installing ${moduleName} module" + + if ($module.versions) { + foreach ($version in $module.versions) { + Write-Host " - $version" + Install-Module -Name $moduleName -RequiredVersion $version -Scope AllUsers -SkipPublisherCheck -Force + } + } else { + Install-Module -Name $moduleName -Scope AllUsers -SkipPublisherCheck -Force + } +} + +Import-Module Pester +Invoke-PesterTests -TestFile "PowerShellModules" -TestName "PowerShellModules" diff --git a/images/windows/scripts/build/Install-PowershellAzModules.ps1 b/images/windows/scripts/build/Install-PowershellAzModules.ps1 new file mode 100644 index 0000000..88da09b --- /dev/null +++ b/images/windows/scripts/build/Install-PowershellAzModules.ps1 @@ -0,0 +1,48 @@ +################################################################################ +## File: Install-PowershellAzModules.ps1 +## Desc: Install PowerShell modules used by AzureFileCopy@4, AzureFileCopy@5, AzurePowerShell@4, AzurePowerShell@5 tasks +## Supply chain security: package manager +################################################################################ + +# The correct Modules need to be saved in C:\Modules +$installPSModulePath = "C:\\Modules" +if (-not (Test-Path -LiteralPath $installPSModulePath)) { + Write-Host "Creating ${installPSModulePath} folder to store PowerShell Azure modules..." + New-Item -Path $installPSModulePath -ItemType Directory | Out-Null +} + +# Get modules content from toolset +$modules = (Get-ToolsetContent).azureModules + +$psModuleMachinePath = "" + +foreach ($module in $modules) { + $moduleName = $module.name + + Write-Host "Installing ${moduleName} to the ${installPSModulePath} path..." + foreach ($version in $module.versions) { + $modulePath = Join-Path -Path $installPSModulePath -ChildPath "${moduleName}_${version}" + Write-Host " - $version [$modulePath]" + Save-Module -Path $modulePath -Name $moduleName -RequiredVersion $version -Force -ErrorAction Stop + } + + foreach ($version in $module.zip_versions) { + $modulePath = Join-Path -Path $installPSModulePath -ChildPath "${moduleName}_${version}" + Save-Module -Path $modulePath -Name $moduleName -RequiredVersion $version -Force -ErrorAction Stop + Compress-Archive -Path $modulePath -DestinationPath "${modulePath}.zip" + Remove-Item $modulePath -Recurse -Force + } + # Append default tool version to machine path + if ($null -ne $module.default) { + $defaultVersion = $module.default + + Write-Host "Use ${moduleName} ${defaultVersion} as default version..." + $psModuleMachinePath += "${installPSModulePath}\${moduleName}_${defaultVersion};" + } +} + +# Add modules to the PSModulePath +$psModuleMachinePath += $env:PSModulePath +[Environment]::SetEnvironmentVariable("PSModulePath", $psModuleMachinePath, "Machine") + +Invoke-PesterTests -TestFile "PowerShellAzModules" -TestName "AzureModules" diff --git a/images/windows/scripts/build/Install-PowershellCore.ps1 b/images/windows/scripts/build/Install-PowershellCore.ps1 new file mode 100644 index 0000000..68a3bc8 --- /dev/null +++ b/images/windows/scripts/build/Install-PowershellCore.ps1 @@ -0,0 +1,43 @@ +################################################################################ +## File: Install-PowershellCore.ps1 +## Desc: Install PowerShell Core +## Supply chain security: checksum validation +################################################################################ + +$ErrorActionPreference = "Stop" + +$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName()) +New-Item -ItemType Directory -Path $tempDir -Force -ErrorAction SilentlyContinue | Out-Null +try { + $originalValue = [Net.ServicePointManager]::SecurityProtocol + [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 + + $metadata = Invoke-RestMethod https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/metadata.json + $pwshMajorMinor = (Get-ToolsetContent).pwsh.version + + $releases = $metadata.LTSReleaseTag -replace '^v' + foreach ($release in $releases) { + if ($release -like "${pwshMajorMinor}*") { + $downloadUrl = "https://github.com/PowerShell/PowerShell/releases/download/v${release}/PowerShell-${release}-win-x64.msi" + break + } + } + + $installerName = Split-Path $downloadUrl -Leaf + $externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url ($downloadUrl -replace $installerName, "hashes.sha256") ` + -FileName $installerName + Install-Binary -Url $downloadUrl -ExpectedSHA256Sum $externalHash +} finally { + # Restore original value + [Net.ServicePointManager]::SecurityProtocol = $originalValue + Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue +} + +# about_update_notifications +# While the update check happens during the first session in a given 24-hour period, for performance reasons, +# the notification will only be shown on the start of subsequent sessions. +# Also for performance reasons, the check will not start until at least 3 seconds after the session begins. +[Environment]::SetEnvironmentVariable("POWERSHELL_UPDATECHECK", "Off", "Machine") + +Invoke-PesterTests -TestFile "Tools" -TestName "PowerShell Core" diff --git a/images/windows/scripts/build/Install-PyPy.ps1 b/images/windows/scripts/build/Install-PyPy.ps1 new file mode 100644 index 0000000..c1d5d9d --- /dev/null +++ b/images/windows/scripts/build/Install-PyPy.ps1 @@ -0,0 +1,108 @@ +################################################################################ +## File: Install-PyPy.ps1 +## Desc: Install PyPy +## Supply chain security: checksum validation +################################################################################ + +function Install-PyPy { + param( + [String] $PackagePath, + [String] $Architecture + ) + + # Create PyPy toolcache folder + $pypyToolcachePath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath "PyPy" + if (-not (Test-Path $pypyToolcachePath)) { + Write-Host "Create PyPy toolcache folder" + New-Item -ItemType Directory -Path $pypyToolcachePath | Out-Null + } + + # Expand archive with binaries + $packageName = [IO.Path]::GetFileNameWithoutExtension((Split-Path -Path $packagePath -Leaf)) + $tempFolder = Join-Path -Path $pypyToolcachePath -ChildPath $packageName + Expand-7ZipArchive -Path $packagePath -DestinationPath $pypyToolcachePath + + # Get Python version from binaries + $pypyApp = Get-ChildItem -Path "$tempFolder\pypy*.exe" | Where-Object Name -match "pypy(\d+)?.exe" | Select-Object -First 1 + $pythonVersion = & $pypyApp -c "import sys;print('{}.{}.{}'.format(sys.version_info[0],sys.version_info[1],sys.version_info[2]))" + + $pypyFullVersion = & $pypyApp -c "import sys;print('{}.{}.{}'.format(*sys.pypy_version_info[0:3]))" + Write-Host "Put '$pypyFullVersion' to PYPY_VERSION file" + New-Item -Path "$tempFolder\PYPY_VERSION" -Value $pypyFullVersion | Out-Null + + if ($pythonVersion) { + Write-Host "Installing PyPy $pythonVersion" + $pypyVersionPath = Join-Path -Path $pypyToolcachePath -ChildPath $pythonVersion + $pypyArchPath = Join-Path -Path $pypyVersionPath -ChildPath $architecture + + Write-Host "Create PyPy '${pythonVersion}' folder in '${pypyVersionPath}'" + New-Item -ItemType Directory -Path $pypyVersionPath -Force | Out-Null + + Write-Host "Move PyPy '${pythonVersion}' files to '${pypyArchPath}'" + Invoke-ScriptBlockWithRetry -Command { + Move-Item -Path $tempFolder -Destination $pypyArchPath -ErrorAction Stop | Out-Null + } + + Write-Host "Install PyPy '${pythonVersion}' in '${pypyArchPath}'" + if (Test-Path "$pypyArchPath\python.exe") { + cmd.exe /c "cd /d $pypyArchPath && python.exe -m ensurepip && python.exe -m pip install --upgrade pip" + } else { + $pypyName = $pypyApp.Name + cmd.exe /c "cd /d $pypyArchPath && mklink python.exe $pypyName && python.exe -m ensurepip && python.exe -m pip install --upgrade pip" + } + + # Create pip.exe if missing + $pipPath = Join-Path -Path $pypyArchPath -ChildPath "Scripts/pip.exe" + if (-not (Test-Path $pipPath)) { + $pip3Path = Join-Path -Path $pypyArchPath -ChildPath "Scripts/pip3.exe" + Copy-Item -Path $pip3Path -Destination $pipPath + } + + if ($LASTEXITCODE -ne 0) { + throw "PyPy installation failed with exit code $LASTEXITCODE" + } + + Write-Host "Create complete file" + New-Item -ItemType File -Path $pypyVersionPath -Name "$architecture.complete" | Out-Null + } else { + throw "PyPy application is not found. Failed to expand '$packagePath' archive" + } +} + +# Get PyPy content from toolset +$toolsetVersions = Get-ToolsetContent | Select-Object -ExpandProperty toolcache | Where-Object Name -eq "PyPy" + +# Get PyPy releases +$pypyVersions = Invoke-RestMethod https://downloads.python.org/pypy/versions.json + +# required for html parsing +$checksums = (Invoke-RestMethod -Uri 'https://www.pypy.org/checksums.html' | ConvertFrom-HTML).SelectNodes('//*[@id="content"]/article/div/pre') + +Write-Host "Start PyPy installation" +foreach ($toolsetVersion in $toolsetVersions.versions) { + # Query latest PyPy version + $latestMajorPyPyVersion = $pypyVersions | + Where-Object { $_.python_version.StartsWith("$toolsetVersion") -and $_.stable -eq $true } | + Select-Object -ExpandProperty files -First 1 | + Where-Object platform -like "win*" + + if (-not $latestMajorPyPyVersion) { + throw "Failed to query PyPy version '$toolsetVersion'" + } + + $filename = $latestMajorPyPyVersion.filename + Write-Host "Found PyPy '$filename' package" + $tempPyPyPackagePath = Invoke-DownloadWithRetry $latestMajorPyPyVersion.download_url + + #region Supply chain security + $distributorFileHash = $null + foreach ($node in $checksums) { + if ($node.InnerText -ilike "*${filename}*") { + $distributorFileHash = $node.InnerText.ToString().Split("`n").Where({ $_ -ilike "*${filename}*" }).Split(' ')[0] + } + } + Test-FileChecksum $tempPyPyPackagePath -ExpectedSHA256Sum $distributorFileHash + #endregion + + Install-PyPy -PackagePath $tempPyPyPackagePath -Architecture $toolsetVersions.arch +} diff --git a/images/windows/scripts/build/Install-R.ps1 b/images/windows/scripts/build/Install-R.ps1 new file mode 100644 index 0000000..96743d7 --- /dev/null +++ b/images/windows/scripts/build/Install-R.ps1 @@ -0,0 +1,11 @@ +################################################################################ +## File: Install-R.ps1 +## Desc: Install R for Windows +################################################################################ + +Install-ChocoPackage R.Project +Install-ChocoPackage rtools + +$rscriptPath = Resolve-Path "C:\Program Files\R\*\bin\x64" +Add-MachinePathItem $rscriptPath +Invoke-PesterTests -TestFile "Tools" -TestName "R" diff --git a/images/windows/scripts/build/Install-RootCA.ps1 b/images/windows/scripts/build/Install-RootCA.ps1 new file mode 100644 index 0000000..4bdb6bc --- /dev/null +++ b/images/windows/scripts/build/Install-RootCA.ps1 @@ -0,0 +1,136 @@ +################################################################################ +## File: Install-RootCA.ps1 +## Desc: Install Root CA certificates +################################################################################ + +# https://www.sysadmins.lv/blog-en/how-to-retrieve-certificate-purposes-property-with-cryptoapi-and-powershell.aspx +# https://www.sysadmins.lv/blog-en/dump-authroot-and-disallowed-certificates-with-powershell.aspx +# https://www.sysadmins.lv/blog-en/constraining-extended-key-usages-in-microsoft-windows.aspx + +function Add-ExtendedCertType { + $signature = @" + [DllImport("Crypt32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool CertGetCertificateContextProperty( + IntPtr pCertContext, + uint dwPropId, + Byte[] pvData, + ref uint pcbData + ); + + [DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool CertSetCertificateContextProperty( + IntPtr pCertContext, + int dwPropId, + uint dwFlags, + IntPtr pvData + ); +"@ + + Add-Type -MemberDefinition $signature -Namespace PKI -Name Cert +} + +function Get-CertificatesWithoutPropId { + # List installed certificates + $certs = Get-ChildItem -Path "Cert:\LocalMachine\Root" + + Write-Host "Certificates without CERT_NOT_BEFORE_FILETIME_PROP_ID property" + $certsWithoutPropId = @{} + $certs | ForEach-Object -Process { + $certHandle = $_.Handle + $isPropertySet = [PKI.Cert]::CertGetCertificateContextProperty( + $certHandle, $CERT_NOT_BEFORE_FILETIME_PROP_ID, $null, [ref] $null + ) + if (-not $isPropertySet) { + Write-Host "Subject: $($_.Subject)" + $certsWithoutPropId[$_.Thumbprint] = $null + } + } + $certsWithoutPropId +} + +function Import-SSTFromWU { + # Serialized Certificate Store File + $sstFile = "$env:TEMP\roots.sst" + # Generate SST from Windows Update + $result = Invoke-ScriptBlockWithRetry -RetryCount 5 -RetryIntervalSeconds 10 -Command { + $r = certutil.exe -generateSSTFromWU $sstFile + if ($LASTEXITCODE -ne 0) { + throw "failed to generate $sstFile sst file`n$o" + } + return $r + } + if ($LASTEXITCODE -ne 0) { + Write-Host "[Error]: failed to generate $sstFile sst file`n$result" + exit $LASTEXITCODE + } + + $result = certutil.exe -dump $sstFile + if ($LASTEXITCODE -ne 0) { + Write-Host "[Error]: failed to dump $sstFile sst file`n$result" + exit $LASTEXITCODE + } + + Import-Certificate -FilePath $sstFile -CertStoreLocation Cert:\LocalMachine\Root +} + +function Clear-CertificatesPropId { + param( + [hashtable] $CertsWithoutPropId + ) + + # List installed certificates + $certs = Get-ChildItem -Path Cert:\LocalMachine\Root + + # Clear property CERT_NOT_BEFORE_FILETIME_PROP_ID + $certs | ForEach-Object -Process { + $thumbprint = $_.Thumbprint + if ($certsWithoutPropId.ContainsKey($thumbprint)) { + $subject = $_.Subject + $certHandle = $_.Handle + $result = [PKI.Cert]::CertSetCertificateContextProperty( + $certHandle, $CERT_NOT_BEFORE_FILETIME_PROP_ID, 0, [System.IntPtr]::Zero + ) + if ($result) { + Write-Host "[Success] Clear CERT_NOT_BEFORE_FILETIME_PROP_ID property $subject" + } else { + Write-Host "[Fail] Clear CERT_NOT_BEFORE_FILETIME_PROP_ID property $subject" + } + } + } +} + +function Disable-RootAutoUpdate { + Write-Host "Disable auto root update mechanism" + $regPath = "HKLM:\Software\Policies\Microsoft\SystemCertificates\AuthRoot" + $regKey = "DisableRootAutoUpdate" + + # Create the registry key if it doesn't exist + if (-not (Test-Path $regPath)) { + Write-Verbose "Creating $regPath" + New-Item $regPath | Out-Null + } + + Set-ItemProperty $regPath -Name $regKey -Type DWord -Value 1 +} + +# Property to remove +$CERT_NOT_BEFORE_FILETIME_PROP_ID = 126 + +# Add extended cert type +Add-ExtendedCertType + +# Get certificates without property CERT_NOT_BEFORE_FILETIME_PROP_ID +$certsWithoutPropId = Get-CertificatesWithoutPropId + +# Download and install the latest version of root ca list +Import-SSTFromWU + +# Clear property CERT_NOT_BEFORE_FILETIME_PROP_ID +if ($certsWithoutPropId.Count -gt 0) { + Clear-CertificatesPropId -CertsWithoutPropId $certsWithoutPropId +} else { + Write-Host "Nothing to clear" +} + +# Disable auto root update mechanism +Disable-RootAutoUpdate diff --git a/images/windows/scripts/build/Install-Ruby.ps1 b/images/windows/scripts/build/Install-Ruby.ps1 new file mode 100644 index 0000000..01bd525 --- /dev/null +++ b/images/windows/scripts/build/Install-Ruby.ps1 @@ -0,0 +1,81 @@ +################################################################################ +## File: Install-Ruby.ps1 +## Desc: Install Ruby using the RubyInstaller2 package and set the default Ruby version +################################################################################ + +# Most of this logic is from +# https://github.com/ruby/setup-ruby/blob/master/windows.js +function Install-Ruby { + param( + [String] $PackagePath, + [String] $Architecture = "x64" + ) + + # Create Ruby toolcache folder + $rubyToolcachePath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath "Ruby" + if (-not (Test-Path $rubyToolcachePath)) { + Write-Host "Creating Ruby toolcache folder" + New-Item -ItemType Directory -Path $rubyToolcachePath | Out-Null + } + + $packageName = [IO.Path]::GetFileNameWithoutExtension((Split-Path -Path $PackagePath -Leaf)) + Write-Host "Expanding Ruby archive $packageName" + $tempFolder = Join-Path -Path $rubyToolcachePath -ChildPath $packageName + Expand-7ZipArchive -Path $PackagePath -DestinationPath $rubyToolcachePath + + # Get Ruby version from binaries + $rubyVersion = & "$tempFolder\bin\ruby.exe" -e "print RUBY_VERSION" + if (($LASTEXITCODE -ne 0) -or (-not $rubyVersion)) { + throw "Unable to determine Ruby version. Exit code: $LASTEXITCODE, output: '$rubyVersion'" + } + Write-Host "Ruby version is $rubyVersion" + + $rubyVersionPath = Join-Path -Path $rubyToolcachePath -ChildPath $rubyVersion + $rubyArchPath = Join-Path -Path $rubyVersionPath -ChildPath $Architecture + + Write-Host "Creating Ruby '${rubyVersion}' folder in '${rubyVersionPath}'" + New-Item -ItemType Directory -Path $rubyVersionPath -Force | Out-Null + + Write-Host "Moving Ruby '${rubyVersion}' files to '${rubyArchPath}'" + Invoke-ScriptBlockWithRetry -Command { + Move-Item -Path $tempFolder -Destination $rubyArchPath -ErrorAction Stop | Out-Null + } + + Write-Host "Removing Ruby '${rubyVersion}' documentation '${rubyArchPath}\share\doc' folder" + Remove-Item -Path "${rubyArchPath}\share\doc" -Force -Recurse -ErrorAction Ignore + + Write-Host "Creating complete file for Ruby $rubyVersion $Architecture" + New-Item -ItemType File -Path $rubyVersionPath -Name "$Architecture.complete" | Out-Null +} + +function Set-DefaultRubyVersion { + param( + [Parameter(Mandatory = $true)] + [version] $Version, + [Alias("Arch")] + [string] $Architecture = "x64" + ) + + $rubyPath = Join-Path $env:AGENT_TOOLSDIRECTORY "/Ruby/${Version}*/${Architecture}/bin" + $rubyDir = (Get-Item -Path $rubyPath).FullName + + Write-Host "Use Ruby ${Version} as a system Ruby" + Add-MachinePathItem -PathItem $rubyDir | Out-Null +} + +# Install Ruby +$rubyTools = (Get-ToolsetContent).toolcache | Where-Object { $_.name -eq "Ruby" } +$rubyToolVersions = $rubyTools.versions + +Write-Host "Starting installation Ruby..." +foreach ($rubyVersion in $rubyToolVersions) { + Write-Host "Starting Ruby $rubyVersion installation" + $downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "oneclick/rubyinstaller2" ` + -Version "$rubyVersion*" ` + -UrlMatchPattern "*-x64.7z" + $packagePath = Invoke-DownloadWithRetry $downloadUrl + Install-Ruby -PackagePath $packagePath +} + +Set-DefaultRubyVersion -Version $rubyTools.default -Arch $rubyTools.arch diff --git a/images/windows/scripts/build/Install-Runner.ps1 b/images/windows/scripts/build/Install-Runner.ps1 new file mode 100644 index 0000000..dc7a6f7 --- /dev/null +++ b/images/windows/scripts/build/Install-Runner.ps1 @@ -0,0 +1,16 @@ +################################################################################ +## File: Install-Runner.ps1 +## Desc: Install Runner for GitHub Actions +## Supply chain security: none +################################################################################ + +Write-Host "Download latest Runner for GitHub Actions" +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "actions/runner" ` + -Version "latest" ` + -UrlMatchPattern "actions-runner-win-x64-*[0-9.].zip" +$fileName = Split-Path $downloadUrl -Leaf +New-Item -Path "C:\ProgramData\runner" -ItemType Directory +Invoke-DownloadWithRetry -Url $downloadUrl -Path "C:\ProgramData\runner\$fileName" + +Invoke-PesterTests -TestFile "RunnerCache" diff --git a/images/windows/scripts/build/Install-Rust.ps1 b/images/windows/scripts/build/Install-Rust.ps1 new file mode 100644 index 0000000..1d4c8b5 --- /dev/null +++ b/images/windows/scripts/build/Install-Rust.ps1 @@ -0,0 +1,51 @@ +################################################################################ +## File: Install-Rust.ps1 +## Desc: Install Rust for Windows +## Supply chain security: checksum validation for bootstrap, managed by rustup for workloads +################################################################################ + +# Rust Env +$env:RUSTUP_HOME = "C:\Users\Default\.rustup" +$env:CARGO_HOME = "C:\Users\Default\.cargo" + +# Download the latest rustup-init.exe for Windows x64 +# See https://rustup.rs/# +$rustupPath = Invoke-DownloadWithRetry "https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe" + +#region Supply chain security +$distributorFileHash = (Invoke-RestMethod -Uri 'https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe.sha256').Trim() +Test-FileChecksum $rustupPath -ExpectedSHA256Sum $distributorFileHash +#endregion + +# Install Rust by running rustup-init.exe (disabling the confirmation prompt with -y) +& $rustupPath -y --default-toolchain=stable --profile=minimal +if ($LASTEXITCODE -ne 0) { + throw "Rust installation failed with exit code $LASTEXITCODE" +} + +# Add %USERPROFILE%\.cargo\bin to USER PATH +Add-DefaultPathItem "%USERPROFILE%\.cargo\bin" +# Add Rust binaries to the path +$env:Path += ";$env:CARGO_HOME\bin" + +# Add i686 target for building 32-bit binaries +rustup target add i686-pc-windows-msvc + +# Add target for building mingw-w64 binaries +rustup target add x86_64-pc-windows-gnu + +# Install common tools +rustup component add rustfmt clippy +if ($LASTEXITCODE -ne 0) { + throw "Rust component installation failed with exit code $LASTEXITCODE" +} +# bindgen-cli commenting it +cargo install cbindgen cargo-audit cargo-outdated +if ($LASTEXITCODE -ne 0) { + throw "Rust tools installation failed with exit code $LASTEXITCODE" +} + +# Cleanup Cargo crates cache +Remove-Item "${env:CARGO_HOME}\registry\*" -Recurse -Force + +Invoke-PesterTests -TestFile "Rust" diff --git a/images/windows/scripts/build/Install-SQLOLEDBDriver.ps1 b/images/windows/scripts/build/Install-SQLOLEDBDriver.ps1 new file mode 100644 index 0000000..82fefc5 --- /dev/null +++ b/images/windows/scripts/build/Install-SQLOLEDBDriver.ps1 @@ -0,0 +1,9 @@ +################################################################################ +## File: Install-SQLOLEDBDriver.ps1 +## Desc: Install OLE DB Driver for SQL Server +################################################################################ + +Install-Binary -Type MSI ` + -Url "https://go.microsoft.com/fwlink/?linkid=2242656" ` + -ExtraInstallArgs @("ADDLOCAL=ALL", "IACCEPTMSOLEDBSQLLICENSETERMS=YES") ` + -ExpectedSignature '6E78B3DCE2998F6C2457C3E54DA90A01034916AE' diff --git a/images/windows/scripts/build/Install-SQLPowerShellTools.ps1 b/images/windows/scripts/build/Install-SQLPowerShellTools.ps1 new file mode 100644 index 0000000..40b4073 --- /dev/null +++ b/images/windows/scripts/build/Install-SQLPowerShellTools.ps1 @@ -0,0 +1,20 @@ +################################################################################ +## File: Install-SQLPowerShellTools.ps1 +## Desc: Install SQL PowerShell tool +################################################################################ + +$baseUrl = "https://download.microsoft.com/download/B/1/7/B1783FE9-717B-4F78-A39A-A2E27E3D679D/ENU/x64" +$signatureThumbrint = "9ACA9419E53D3C9E56396DD2335FF683A8B0B8F3" + +# install required MSIs +Install-Binary ` + -Url "${baseUrl}/SQLSysClrTypes.msi" ` + -ExpectedSignature $signatureThumbrint + +Install-Binary ` + -Url "${baseUrl}/SharedManagementObjects.msi" ` + -ExpectedSignature $signatureThumbrint + +Install-Binary ` + -Url "${baseUrl}/PowerShellTools.msi" ` + -ExpectedSignature $signatureThumbrint diff --git a/images/windows/scripts/build/Install-Sbt.ps1 b/images/windows/scripts/build/Install-Sbt.ps1 new file mode 100644 index 0000000..1843805 --- /dev/null +++ b/images/windows/scripts/build/Install-Sbt.ps1 @@ -0,0 +1,15 @@ +################################################################################ +## File: Install-Sbt.ps1 +## Desc: Install sbt for Windows +################################################################################ + +# Install the latest version of sbt. +# See https://chocolatey.org/packages/sbt +Install-ChocoPackage sbt + +$env:SBT_HOME="${env:ProgramFiles(x86)}\sbt" + +# Add sbt binaries to the path +Add-MachinePathItem "$env:SBT_HOME\bin" + +Invoke-PesterTests -TestFile "Tools" -TestName "Sbt" diff --git a/images/windows/scripts/build/Install-Selenium.ps1 b/images/windows/scripts/build/Install-Selenium.ps1 new file mode 100644 index 0000000..726fd4a --- /dev/null +++ b/images/windows/scripts/build/Install-Selenium.ps1 @@ -0,0 +1,29 @@ +################################################################################ +## File: Install-Selenium.ps1 +## Desc: Install Selenium Server standalone +################################################################################ + +# Create Selenium directory +$seleniumDirectory = "C:\selenium\" +New-Item -ItemType directory -Path $seleniumDirectory + +# Download Selenium +$seleniumMajorVersion = (Get-ToolsetContent).selenium.version + +$seleniumDownloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "SeleniumHQ/selenium" ` + -Version "$seleniumMajorVersion.*" ` + -Asset "selenium-server-*.jar" ` + -AllowMultipleMatches + +$seleniumBinPath = Join-Path $seleniumDirectory "selenium-server.jar" +Invoke-DownloadWithRetry -Url $seleniumDownloadUrl -Path $seleniumBinPath + +# Create an empty file to retrieve Selenium version +$seleniumFullVersion = $seleniumDownloadUrl.Split("-")[1].Split("/")[0] +New-Item -Path $seleniumDirectory -Name "selenium-server-$seleniumFullVersion" + +# Add SELENIUM_JAR_PATH environment variable +[Environment]::SetEnvironmentVariable("SELENIUM_JAR_PATH", $seleniumBinPath, "Machine") + +Invoke-PesterTests -TestFile "Browsers" -TestName "Selenium" diff --git a/images/windows/scripts/build/Install-ServiceFabricSDK.ps1 b/images/windows/scripts/build/Install-ServiceFabricSDK.ps1 new file mode 100644 index 0000000..cc9ae9f --- /dev/null +++ b/images/windows/scripts/build/Install-ServiceFabricSDK.ps1 @@ -0,0 +1,29 @@ +################################################################################ +## File: Install-ServiceFabricSDK.ps1 +## Desc: Install webpicmd and then the service fabric sdk +## must be install after Visual Studio +## Supply chain security: checksum validation +################################################################################ + +# Creating 'Installer' cache folder if it doesn't exist +New-Item -Path 'C:\Windows\Installer' -ItemType Directory -Force + +# Get Service Fabric components versions +$runtimeVersion = (Get-ToolsetContent).serviceFabric.runtime.version +$sdkVersion = (Get-ToolsetContent).serviceFabric.sdk.version + +$urlBase = "https://download.microsoft.com/download/b/8/a/b8a2fb98-0ec1-41e5-be98-9d8b5abf7856" + +# Install Service Fabric Runtime for Windows +Install-Binary ` + -Url "${urlBase}/MicrosoftServiceFabric.${runtimeVersion}.exe" ` + -InstallArgs @("/accepteula ", "/quiet", "/force") ` + -ExpectedSHA256Sum (Get-ToolsetContent).serviceFabric.runtime.checksum + + +# Install Service Fabric SDK +Install-Binary ` + -Url "${urlBase}/MicrosoftServiceFabricSDK.${sdkVersion}.msi" ` + -ExpectedSHA256Sum (Get-ToolsetContent).serviceFabric.sdk.checksum + +Invoke-PesterTests -TestFile "Tools" -TestName "ServiceFabricSDK" diff --git a/images/windows/scripts/build/Install-Stack.ps1 b/images/windows/scripts/build/Install-Stack.ps1 new file mode 100644 index 0000000..2423937 --- /dev/null +++ b/images/windows/scripts/build/Install-Stack.ps1 @@ -0,0 +1,35 @@ +################################################################################ +## File: Install-Stack.ps1 +## Desc: Install Stack for Windows +## Supply chain security: Stack - checksum validation +################################################################################ + +Write-Host "Get the latest Stack version..." + +$version = (Get-GithubReleasesByVersion -Repo "commercialhaskell/stack" -Version "latest" -WithAssetsOnly).version + +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "commercialhaskell/stack" ` + -Version $version ` + -UrlMatchPattern "stack-*-windows-x86_64.zip" + +Write-Host "Download stack archive" +$stackToolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "stack\$version" +$destinationPath = Join-Path $stackToolcachePath "x64" +$stackArchivePath = Invoke-DownloadWithRetry $downloadUrl + +#region Supply chain security - Stack +$externalHash = Get-ChecksumFromUrl -Type "SHA256" ` + -Url "$downloadUrl.sha256" ` + -FileName (Split-Path $downloadUrl -Leaf) +Test-FileChecksum $stackArchivePath -ExpectedSHA256Sum $externalHash +#endregion + +Write-Host "Expand stack archive" +Expand-7ZipArchive -Path $stackArchivePath -DestinationPath $destinationPath + +New-Item -Name "x64.complete" -Path $stackToolcachePath + +Add-MachinePathItem -PathItem $destinationPath + +Invoke-PesterTests -TestFile "Tools" -TestName "Stack" diff --git a/images/windows/scripts/build/Install-Toolset.ps1 b/images/windows/scripts/build/Install-Toolset.ps1 new file mode 100644 index 0000000..aab98f9 --- /dev/null +++ b/images/windows/scripts/build/Install-Toolset.ps1 @@ -0,0 +1,60 @@ +################################################################################ +## File: Install-Toolset.ps1 +## Team: CI-Build +## Desc: Install toolset +################################################################################ + +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13 + +Function Install-Asset { + param( + [Parameter(Mandatory=$true)] + [object] $ReleaseAsset + ) + + $releaseAssetName = [System.IO.Path]::GetFileNameWithoutExtension($ReleaseAsset.filename) + $assetFolderPath = Join-Path $env:TEMP $releaseAssetName + $assetArchivePath = Invoke-DownloadWithRetry $ReleaseAsset.download_url + + Write-Host "Extract $($ReleaseAsset.filename) content..." + if ($assetArchivePath.EndsWith(".tar.gz")) { + $assetTarPath = $assetArchivePath.TrimEnd(".tar.gz") + Expand-7ZipArchive -Path $assetArchivePath -DestinationPath $assetTarPath + Expand-7ZipArchive -Path $assetTarPath -DestinationPath $assetFolderPath + } else { + Expand-7ZipArchive -Path $assetArchivePath -DestinationPath $assetFolderPath + } + + Write-Host "Invoke installation script..." + Push-Location -Path $assetFolderPath + Invoke-Expression .\setup.ps1 + Pop-Location +} + +# Get toolcache content from toolset +$toolsToInstall = @("Python", "Node", "Go") +$tools = Get-ToolsetContent | Select-Object -ExpandProperty toolcache | Where-Object { $toolsToInstall -contains $_.Name } + +foreach ($tool in $tools) { + # Get versions manifest for current tool + # Invoke-RestMethod doesn't support retry in PowerShell 5.1 + $assets = Invoke-ScriptBlockWithRetry -Command { + Invoke-RestMethod $tool.url + } + + # Get github release asset for each version + foreach ($toolVersion in $tool.versions) { + $asset = $assets ` + | Where-Object version -like $toolVersion ` + | Select-Object -ExpandProperty files ` + | Where-Object { ($_.platform -eq $tool.platform) -and ($_.arch -eq $tool.arch) -and ($_.toolset -eq $tool.toolset) } ` + | Select-Object -First 1 + + if (-not $asset) { + throw "Asset for $($tool.name) $toolVersion $($tool.arch) not found in versions manifest" + } + + Write-Host "Installing $($tool.name) $toolVersion $($tool.arch)..." + Install-Asset -ReleaseAsset $asset + } +} diff --git a/images/windows/scripts/build/Install-TortoiseSvn.ps1 b/images/windows/scripts/build/Install-TortoiseSvn.ps1 new file mode 100644 index 0000000..48f0446 --- /dev/null +++ b/images/windows/scripts/build/Install-TortoiseSvn.ps1 @@ -0,0 +1,6 @@ +################################################################################ +## File: Install-TortoiseSvn.ps1 +## Desc: Install TortoiseSvn +################################################################################ + +Install-ChocoPackage tortoisesvn diff --git a/images/windows/scripts/build/Install-VCRedist.ps1 b/images/windows/scripts/build/Install-VCRedist.ps1 new file mode 100644 index 0000000..af93a7c --- /dev/null +++ b/images/windows/scripts/build/Install-VCRedist.ps1 @@ -0,0 +1,20 @@ +################################################################################ +## File: Install-VCRedist.ps1 +## Desc: Install Visual C++ Redistributable +## Supply chain security: checksum validation +################################################################################ + +$baseUrl = "https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC" +$argumentList = ("/install", "/quiet", "/norestart") + +Install-Binary ` + -Url "${baseUrl}/vcredist_x86.exe" ` + -InstallArgs $argumentList ` + -ExpectedSHA256Sum '99DCE3C841CC6028560830F7866C9CE2928C98CF3256892EF8E6CF755147B0D8' + +Install-Binary ` + -Url "${baseUrl}/vcredist_x64.exe" ` + -InstallArgs $argumentList ` + -ExpectedSHA256Sum 'F3B7A76D84D23F91957AA18456A14B4E90609E4CE8194C5653384ED38DADA6F3' + +Invoke-PesterTests -TestFile "Tools" -TestName "VCRedist" diff --git a/images/windows/scripts/build/Install-VSExtensions.ps1 b/images/windows/scripts/build/Install-VSExtensions.ps1 new file mode 100644 index 0000000..3624d80 --- /dev/null +++ b/images/windows/scripts/build/Install-VSExtensions.ps1 @@ -0,0 +1,27 @@ +################################################################################### +## File: Install-VSExtensions.ps1 +## Desc: Install the Visual Studio Extensions from toolset.json +################################################################################### + +$toolset = Get-ToolsetContent +$vsixPackagesList = $toolset.visualStudio.vsix +if (-not $vsixPackagesList) { + Write-Host "No extensions to install" + exit 0 +} + +$vsixPackagesList | ForEach-Object { + # Retrieve cdn endpoint to avoid HTTP error 429 + # https://github.com/actions/runner-images/issues/3074 + $vsixPackage = Get-VsixInfoFromMarketplace $_ + Write-Host "Installing $vsixPackage" + if ($vsixPackage.FileName.EndsWith(".vsix")) { + Install-VSIXFromUrl $vsixPackage.DownloadUri + } else { + Install-Binary ` + -Url $vsixPackage.DownloadUri ` + -InstallArgs @('/install', '/quiet', '/norestart') + } +} + +Invoke-PesterTests -TestFile "Vsix" diff --git a/images/windows/scripts/build/Install-Vcpkg.ps1 b/images/windows/scripts/build/Install-Vcpkg.ps1 new file mode 100644 index 0000000..ce72aaf --- /dev/null +++ b/images/windows/scripts/build/Install-Vcpkg.ps1 @@ -0,0 +1,27 @@ +################################################################################ +## File: Install-Vcpkg.ps1 +## Desc: Install vcpkg +################################################################################ + +$Uri = 'https://github.com/Microsoft/vcpkg.git' +$InstallDir = 'C:\vcpkg' +$VcpkgExecPath = 'vcpkg.exe' + +git clone $Uri $InstallDir -q + +# Build and integrate vcpkg +Invoke-Expression "$InstallDir\bootstrap-vcpkg.bat" +if ($LASTEXITCODE -ne 0) { + throw "vcpkg bootstrap failed with exit code $LASTEXITCODE" +} +Invoke-Expression "$InstallDir\$VcpkgExecPath integrate install" +if ($LASTEXITCODE -ne 0) { + throw "vcpkg integration failed with exit code $LASTEXITCODE" +} + +# Add vcpkg to system environment +Add-MachinePathItem $InstallDir +[Environment]::SetEnvironmentVariable("VCPKG_INSTALLATION_ROOT", $InstallDir, "Machine") +Update-Environment + +Invoke-PesterTests -TestFile "Tools" -TestName "Vcpkg" diff --git a/images/windows/scripts/build/Install-VisualStudio.ps1 b/images/windows/scripts/build/Install-VisualStudio.ps1 new file mode 100644 index 0000000..50c7f42 --- /dev/null +++ b/images/windows/scripts/build/Install-VisualStudio.ps1 @@ -0,0 +1,57 @@ +################################################################################ +## File: Install-VisualStudio.ps1 +## Desc: Install Visual Studio +################################################################################ + +$vsToolset = (Get-ToolsetContent).visualStudio + +# Install VS +Install-VisualStudio ` + -Version $vsToolset.subversion ` + -Edition $vsToolset.edition ` + -Channel $vsToolset.channel ` + -RequiredComponents $vsToolset.workloads ` + -ExtraArgs "--allWorkloads --includeRecommended --remove Component.CPython3.x64" ` + -SignatureThumbprint $vsToolset.signature + +# Find the version of VS installed for this instance +# Only supports a single instance +$vsProgramData = Get-Item -Path "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances" +$instanceFolders = Get-ChildItem -Path $vsProgramData.FullName + +if ($instanceFolders -is [array]) { + throw "More than one instance installed" +} + +# Updating content of MachineState.json file to disable autoupdate of VSIX extensions +$vsInstallRoot = (Get-VisualStudioInstance).InstallationPath +$newContent = '{"Extensions":[{"Key":"1e906ff5-9da8-4091-a299-5c253c55fdc9","Value":{"ShouldAutoUpdate":false}},{"Key":"Microsoft.VisualStudio.Web.AzureFunctions","Value":{"ShouldAutoUpdate":false}}],"ShouldAutoUpdate":false,"ShouldCheckForUpdates":false}' +Set-Content -Path "$vsInstallRoot\Common7\IDE\Extensions\MachineState.json" -Value $newContent + +if (Test-IsWin19) { + # Install Windows 10 SDK version 10.0.14393.795 + Install-Binary -Type EXE ` + -Url 'https://go.microsoft.com/fwlink/p/?LinkId=838916' ` + -InstallArgs @("/q", "/norestart", "/ceip off", "/features OptionId.WindowsSoftwareDevelopmentKit") ` + -ExpectedSignature 'C91545B333C52C4465DE8B90A3FAF4E1D9C58DFA' + + # Install Windows 11 SDK version 10.0.22621.0 + Install-Binary -Type EXE ` + -Url 'https://go.microsoft.com/fwlink/p/?linkid=2196241' ` + -InstallArgs @("/q", "/norestart", "/ceip off", "/features OptionId.UWPManaged OptionId.UWPCPP OptionId.UWPLocalized OptionId.DesktopCPPx86 OptionId.DesktopCPPx64 OptionId.DesktopCPParm64") ` + -ExpectedSignature 'E4C5C5FCDB68B930EE4E19BC25D431EF6D864C51' +} + +if (Test-IsWin22) { + # Install Windows 10 SDK version 10.0.17763 + Install-Binary -Type EXE ` + -Url 'https://go.microsoft.com/fwlink/p/?LinkID=2033908' ` + -InstallArgs @("/q", "/norestart", "/ceip off", "/features OptionId.UWPManaged OptionId.UWPCPP OptionId.UWPLocalized OptionId.DesktopCPPx86 OptionId.DesktopCPPx64 OptionId.DesktopCPParm64") ` + -ExpectedSignature '7535269B94C1FEA4A5EF6D808E371DA242F27936' +} +if (Test-IsWin25){ + Write-Host "It is for windows-25 testing" +} + +# Disable pester tests for now +# Invoke-PesterTests -TestFile "VisualStudio" diff --git a/images/windows/scripts/build/Install-WDK.ps1 b/images/windows/scripts/build/Install-WDK.ps1 new file mode 100644 index 0000000..a37681e --- /dev/null +++ b/images/windows/scripts/build/Install-WDK.ps1 @@ -0,0 +1,40 @@ +################################################################################ +## File: Install-WDK.ps1 +## Desc: Install the Windows Driver Kit +################################################################################ + +# Requires Windows SDK with the same version number as the WDK +if (Test-IsWin19) { + # Install all features without showing the GUI using winsdksetup.exe + Install-Binary -Type EXE ` + -Url 'https://go.microsoft.com/fwlink/?linkid=2173743' ` + -InstallArgs @("/features", "+", "/quiet") ` + -ExpectedSignature '44796EB5BD439B4BFB078E1DC2F8345AE313CBB1' + + $wdkUrl = "https://go.microsoft.com/fwlink/?linkid=2166289" + $wdkSignatureThumbprint = "914A09C2E02C696AF394048BCB8D95449BCD5B9E" + $wdkExtensionPath = "C:\Program Files (x86)\Windows Kits\10\Vsix\VS2019\WDK.vsix" +} elseif (Test-IsWin22) { + # SDK is available through Visual Studio + $wdkUrl = "https://go.microsoft.com/fwlink/?linkid=2249371" + $wdkSignatureThumbprint = "7C94971221A799907BB45665663BBFD587BAC9F8" + $wdkExtensionPath = "C:\Program Files (x86)\Windows Kits\10\Vsix\VS2022\*\WDK.vsix" +} elseif (Test-IsWin25) { + # SDK is available through Visual Studio + $wdkUrl = "https://go.microsoft.com/fwlink/?linkid=2249371" + $wdkSignatureThumbprint = "7C94971221A799907BB45665663BBFD587BAC9F8" + $wdkExtensionPath = "C:\Program Files (x86)\Windows Kits\10\Vsix\VS2022\*\WDK.vsix" +}else { + throw "Invalid version of Visual Studio is found. Either 2019 or 2022 are required" +} + +# Install all features without showing the GUI using wdksetup.exe +Install-Binary -Type EXE ` + -Url $wdkUrl ` + -InstallArgs @("/features", "+", "/quiet") ` + -ExpectedSignature $wdkSignatureThumbprint + +# Need to install the VSIX to get the build targets when running VSBuild +Install-VSIXFromFile (Resolve-Path -Path $wdkExtensionPath) + +Invoke-PesterTests -TestFile "WDK" diff --git a/images/windows/scripts/build/Install-WebPlatformInstaller.ps1 b/images/windows/scripts/build/Install-WebPlatformInstaller.ps1 new file mode 100644 index 0000000..5c7c6fb --- /dev/null +++ b/images/windows/scripts/build/Install-WebPlatformInstaller.ps1 @@ -0,0 +1,10 @@ +################################################################################ +## File: Install-WebPI.ps1 +## Desc: Install WebPlatformInstaller +################################################################################ + +Install-Binary -Type MSI ` + -Url 'http://go.microsoft.com/fwlink/?LinkId=287166' ` + -ExpectedSignature 'C3A3D43788E7ABCD287CB4F5B6583043774F99D2' + +Invoke-PesterTests -TestFile "Tools" -TestName "WebPlatformInstaller" diff --git a/images/windows/scripts/build/Install-WinAppDriver.ps1 b/images/windows/scripts/build/Install-WinAppDriver.ps1 new file mode 100644 index 0000000..e4a6a82 --- /dev/null +++ b/images/windows/scripts/build/Install-WinAppDriver.ps1 @@ -0,0 +1,16 @@ +#################################################################################### +## File: Install-WinAppDriver.ps1 +## Desc: Install Windows Application Driver (WinAppDriver) +#################################################################################### + +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "microsoft/WinAppDriver" ` + -Version "latest" ` + -UrlMatchPattern "WindowsApplicationDriver_*.msi" + +Install-Binary ` + -Url $downloadUrl ` + -ExpectedSignature '2485A7AFA98E178CB8F30C9838346B514AEA4769' + +Invoke-PesterTests -TestFile "WinAppDriver" -TestName "WinAppDriver" diff --git a/images/windows/scripts/build/Install-WindowsFeatures.ps1 b/images/windows/scripts/build/Install-WindowsFeatures.ps1 new file mode 100644 index 0000000..5e7730b --- /dev/null +++ b/images/windows/scripts/build/Install-WindowsFeatures.ps1 @@ -0,0 +1,47 @@ +#################################################################################### +## File: Install-WindowsFeatures.ps1 +## Desc: Install Windows Features +#################################################################################### + +$windowsFeatures = (Get-ToolsetContent).windowsFeatures + +foreach ($feature in $windowsFeatures) { + if ($feature.optionalFeature) { + Write-Host "Activating Windows Optional Feature '$($feature.name)'..." + Enable-WindowsOptionalFeature -Online -FeatureName $feature.name -NoRestart + + $resultSuccess = $? + } else { + Write-Host "Activating Windows Feature '$($feature.name)'..." + $arguments = @{ + Name = $feature.name + IncludeAllSubFeature = [System.Convert]::ToBoolean($feature.includeAllSubFeatures) + IncludeManagementTools = [System.Convert]::ToBoolean($feature.includeManagementTools) + } + + if ($feature.name -eq "NET-Framework-Features") { + $arguments += @{ LogPath = $(Join-Path $env:IMAGE_FOLDER "NET-Framework-Features.log") } + } + $result = Install-WindowsFeature @arguments + $resultSuccess = $result.Success + } + + if ($feature.name -eq "NET-Framework-Features") { + $dotNetFeatureLog = Get-Content -Path $(Join-Path $env:IMAGE_FOLDER "NET-Framework-Features.log") + Write-Host "The log of NET-Framework-Features" + Write-Host $dotNetFeatureLog + } + + if ($resultSuccess) { + Write-Host "Windows Feature '$($feature.name)' was activated successfully" + } else { + throw "Failed to activate Windows Feature '$($feature.name)'" + } +} + +# it improves Android emulator launch on Windows Server +# https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/manage/manage-hyper-v-scheduler-types +bcdedit /set hypervisorschedulertype root +if ($LASTEXITCODE -ne 0) { + throw "Failed to set hypervisorschedulertype to root" +} diff --git a/images/windows/scripts/build/Install-WindowsUpdates.ps1 b/images/windows/scripts/build/Install-WindowsUpdates.ps1 new file mode 100644 index 0000000..6158b5e --- /dev/null +++ b/images/windows/scripts/build/Install-WindowsUpdates.ps1 @@ -0,0 +1,50 @@ +################################################################################ +## File: Install-WindowsUpdates.ps1 +## Desc: Install Windows Updates. +## Should be run at end, just before SoftwareReport and Finalize-VM.ps1. +################################################################################ + +function Install-WindowsUpdates { + Write-Host "Starting wuauserv" + Start-Service -Name wuauserv -PassThru | Out-Host + + # Temporarily exclude Windows update KB5034439 since it throws an error. + # 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) + Write-Host "Getting list of available windows updates" + + # Added KB5044030 to the excluded list since it is failing for 2025 + $excludedKBs = @() + if (Test-IsWin25) { + $excludedKBs = @("KB5034439", "KB5044030", "KB5044284") + } + else { + $excludedKBs = @("KB5034439") + } + + + Get-WindowsUpdate -MicrosoftUpdate -NotKBArticleID $excludedKBs -OutVariable updates | Out-Host + + if ( -not $updates ) { + Write-Host "There are no windows updates to install" + return + } + + Write-Host "Installing windows updates" + + Get-WindowsUpdate -MicrosoftUpdate -NotKBArticleID $excludedKBs -AcceptAll -Install -IgnoreUserInput -IgnoreReboot | Out-Host + + Write-Host "Validating windows updates installation" + # Get-WUHistory doesn't support Windows Server 2022 + $notFailedUpdateNames = Get-WindowsUpdateStates | Where-Object { $_.State -in ("Installed", "Running") } | Select-Object -ExpandProperty Title + # We ignore Microsoft Defender Antivirus updates; Azure service updates AV automatically + $failedUpdates = $updates[0] | Where-Object Title -notmatch "Microsoft Defender Antivirus" | Where-Object { -not ($notFailedUpdateNames -match $_.KB) } + + if ( $failedUpdates ) { + throw "Windows updates failed to install: $($failedUpdates.KB)" + } +} + +Install-WindowsUpdates + +# Create complete windows update file +New-Item -Path $env:windir -Name WindowsUpdateDone.txt -ItemType File | Out-Null diff --git a/images/windows/scripts/build/Install-WindowsUpdatesAfterReboot.ps1 b/images/windows/scripts/build/Install-WindowsUpdatesAfterReboot.ps1 new file mode 100644 index 0000000..ef9b562 --- /dev/null +++ b/images/windows/scripts/build/Install-WindowsUpdatesAfterReboot.ps1 @@ -0,0 +1,12 @@ +################################################################################ +## File: Install-WindowsUpdatesAfterReboot.ps1 +## Desc: Waits for Windows Updates to finish installing after reboot +################################################################################ + +Invoke-ScriptBlockWithRetry -RetryCount 10 -RetryIntervalSeconds 120 -Command { + $inProgress = Get-WindowsUpdateStates | Where-Object State -eq "Running" | Where-Object Title -notmatch "Microsoft Defender Antivirus" + if ( $inProgress ) { + $title = $inProgress.Title -join "`n" + throw "Windows updates are still installing: $title" + } +} diff --git a/images/windows/scripts/build/Install-Wix.ps1 b/images/windows/scripts/build/Install-Wix.ps1 new file mode 100644 index 0000000..d94d03d --- /dev/null +++ b/images/windows/scripts/build/Install-Wix.ps1 @@ -0,0 +1,14 @@ +################################################################################ +## File: Install-Wix.ps1 +## Desc: Install WIX. +################################################################################ + +Install-ChocoPackage wixtoolset -ArgumentList "--force" + +Update-Environment +$currentPath = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") +$newPath = $currentPath + ";$(Join-Path -Path $env:WIX -ChildPath "bin")" +[Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine") +Update-Environment + +Invoke-PesterTests -TestFile "Wix" diff --git a/images/windows/scripts/build/Install-Zstd.ps1 b/images/windows/scripts/build/Install-Zstd.ps1 new file mode 100644 index 0000000..20ae3fd --- /dev/null +++ b/images/windows/scripts/build/Install-Zstd.ps1 @@ -0,0 +1,29 @@ +################################################################################ +## File: Install-Zstd.ps1 +## Desc: Install zstd +################################################################################ + +$downloadUrl = Resolve-GithubReleaseAssetUrl ` + -Repo "facebook/zstd" ` + -Version "latest" ` + -UrlMatchPattern "zstd-*-win64.zip" +$zstdArchivePath = Invoke-DownloadWithRetry $downloadUrl +$zstdName = [IO.Path]::GetFileNameWithoutExtension($zstdArchivePath) + +$toolPath = "C:\tools" +$zstdPath = Join-Path $toolPath zstd +$filesInArchive = 7z l $zstdArchivePath | Out-String + +if ($filesInArchive.Contains($zstdName)) { + Expand-7ZipArchive -Path $zstdArchivePath -DestinationPath $toolPath + Invoke-ScriptBlockWithRetry -Command { + Move-Item -Path "${zstdPath}*" -Destination $zstdPath -ErrorAction Stop + } +} else { + Expand-7ZipArchive -Path $zstdArchivePath -DestinationPath $zstdPath +} + +# Add zstd-win64 to PATH +Add-MachinePathItem $zstdPath + +Invoke-PesterTests -TestFile "Tools" -TestName "Zstd" diff --git a/images/windows/scripts/docs-gen/Generate-SoftwareReport.ps1 b/images/windows/scripts/docs-gen/Generate-SoftwareReport.ps1 new file mode 100644 index 0000000..5501c76 --- /dev/null +++ b/images/windows/scripts/docs-gen/Generate-SoftwareReport.ps1 @@ -0,0 +1,253 @@ +using module ./software-report-base/SoftwareReport.psm1 +using module ./software-report-base/SoftwareReport.Nodes.psm1 + +$global:ErrorActionPreference = "Stop" +$global:ProgressPreference = "SilentlyContinue" +$ErrorView = "NormalView" +Set-StrictMode -Version Latest + +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Android.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Browsers.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.CachedTools.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Common.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Databases.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Helpers.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Tools.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Java.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.WebServers.psm1") -DisableNameChecking +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.VisualStudio.psm1") -DisableNameChecking + +# Software report +$softwareReport = [SoftwareReport]::new($(Build-OSInfoSection)) +$optionalFeatures = $softwareReport.Root.AddHeader("Windows features") +$optionalFeatures.AddToolVersion("Windows Subsystem for Linux (WSLv1):", "Enabled") +$installedSoftware = $softwareReport.Root.AddHeader("Installed Software") + +# Language and Runtime +$languageAndRuntime = $installedSoftware.AddHeader("Language and Runtime") +$languageAndRuntime.AddToolVersion("Bash", $(Get-BashVersion)) +$languageAndRuntime.AddToolVersion("Go", $(Get-GoVersion)) +$languageAndRuntime.AddToolVersion("Julia", $(Get-JuliaVersion)) +$languageAndRuntime.AddToolVersion("Kotlin", $(Get-KotlinVersion)) +$languageAndRuntime.AddToolVersion("LLVM", $(Get-LLVMVersion)) +$languageAndRuntime.AddToolVersion("Node", $(Get-NodeVersion)) +$languageAndRuntime.AddToolVersion("Perl", $(Get-PerlVersion)) +$languageAndRuntime.AddToolVersion("PHP", $(Get-PHPVersion)) +$languageAndRuntime.AddToolVersion("Python", $(Get-PythonVersion)) +$languageAndRuntime.AddToolVersion("Ruby", $(Get-RubyVersion)) + +# Package Management +$packageManagement = $installedSoftware.AddHeader("Package Management") +$packageManagement.AddToolVersion("Chocolatey", $(Get-ChocoVersion)) +$packageManagement.AddToolVersion("Composer", $(Get-ComposerVersion)) +$packageManagement.AddToolVersion("Helm", $(Get-HelmVersion)) +$packageManagement.AddToolVersion("Miniconda", $(Get-CondaVersion)) +$packageManagement.AddToolVersion("NPM", $(Get-NPMVersion)) +$packageManagement.AddToolVersion("NuGet", $(Get-NugetVersion)) +$packageManagement.AddToolVersion("pip", $(Get-PipVersion)) +$packageManagement.AddToolVersion("Pipx", $(Get-PipxVersion)) +$packageManagement.AddToolVersion("RubyGems", $(Get-RubyGemsVersion)) +$packageManagement.AddToolVersion("Vcpkg", $(Get-VcpkgVersion)) +$packageManagement.AddToolVersion("Yarn", $(Get-YarnVersion)) + +$packageManagement.AddHeader("Environment variables").AddTable($(Build-PackageManagementEnvironmentTable)) + +# Project Management +$projectManagement = $installedSoftware.AddHeader("Project Management") +$projectManagement.AddToolVersion("Ant", $(Get-AntVersion)) +$projectManagement.AddToolVersion("Gradle", $(Get-GradleVersion)) +$projectManagement.AddToolVersion("Maven", $(Get-MavenVersion)) +$projectManagement.AddToolVersion("sbt", $(Get-SbtVersion)) + +# Tools +$tools = $installedSoftware.AddHeader("Tools") +$tools.AddToolVersion("7zip", $(Get-7zipVersion)) +$tools.AddToolVersion("aria2", $(Get-Aria2Version)) +$tools.AddToolVersion("azcopy", $(Get-AzCopyVersion)) +$tools.AddToolVersion("Bazel", $(Get-BazelVersion)) +$tools.AddToolVersion("Bazelisk", $(Get-BazeliskVersion)) +$tools.AddToolVersion("Bicep", $(Get-BicepVersion)) +$tools.AddToolVersion("Cabal", $(Get-CabalVersion)) +$tools.AddToolVersion("CMake", $(Get-CMakeVersion)) +$tools.AddToolVersion("CodeQL Action Bundle", $(Get-CodeQLBundleVersion)) +$tools.AddToolVersion("Docker", $(Get-DockerVersion)) +$tools.AddToolVersion("Docker Compose v2", $(Get-DockerComposeVersionV2)) +$tools.AddToolVersion("Docker-wincred", $(Get-DockerWincredVersion)) +$tools.AddToolVersion("ghc", $(Get-GHCVersion)) +$tools.AddToolVersion("Git", $(Get-GitVersion)) +$tools.AddToolVersion("Git LFS", $(Get-GitLFSVersion)) +if (Test-IsWin19) { + $tools.AddToolVersion("Google Cloud CLI", $(Get-GoogleCloudCLIVersion)) +} +$tools.AddToolVersion("ImageMagick", $(Get-ImageMagickVersion)) +if (-not (Test-IsWin25)) { + $tools.AddToolVersion("InnoSetup", $(Get-InnoSetupVersion)) +} +$tools.AddToolVersion("jq", $(Get-JQVersion)) +$tools.AddToolVersion("Kind", $(Get-KindVersion)) +$tools.AddToolVersion("Kubectl", $(Get-KubectlVersion)) +if (-not (Test-IsWin25)) { + $tools.AddToolVersion("Mercurial", $(Get-MercurialVersion)) +} +$tools.AddToolVersion("gcc", $(Get-GCCVersion)) +$tools.AddToolVersion("gdb", $(Get-GDBVersion)) +$tools.AddToolVersion("GNU Binutils", $(Get-GNUBinutilsVersion)) +$tools.AddToolVersion("Newman", $(Get-NewmanVersion)) +if (-not (Test-IsWin25)) { + $tools.AddToolVersion("NSIS", $(Get-NSISVersion)) +} +$tools.AddToolVersion("OpenSSL", $(Get-OpenSSLVersion)) +$tools.AddToolVersion("Packer", $(Get-PackerVersion)) +if (Test-IsWin19) { + $tools.AddToolVersion("Parcel", $(Get-ParcelVersion)) +} +$tools.AddToolVersion("Pulumi", $(Get-PulumiVersion)) +$tools.AddToolVersion("R", $(Get-RVersion)) +$tools.AddToolVersion("Service Fabric SDK", $(Get-ServiceFabricSDKVersion)) +$tools.AddToolVersion("Stack", $(Get-StackVersion)) +#$tools.AddToolVersion("Swift", $(Get-SwiftVersion)) +if (-not (Test-IsWin25)) { + $tools.AddToolVersion("Subversion (SVN)", $(Get-SVNVersion)) +} +$tools.AddToolVersion("Swig", $(Get-SwigVersion)) +$tools.AddToolVersion("VSWhere", $(Get-VSWhereVersion)) +$tools.AddToolVersion("WinAppDriver", $(Get-WinAppDriver)) +$tools.AddToolVersion("WiX Toolset", $(Get-WixVersion)) +$tools.AddToolVersion("yamllint", $(Get-YAMLLintVersion)) +$tools.AddToolVersion("zstd", $(Get-ZstdVersion)) + +# CLI Tools +$cliTools = $installedSoftware.AddHeader("CLI Tools") +if (-not (Test-IsWin25)) { + $cliTools.AddToolVersion("Alibaba Cloud CLI", $(Get-AlibabaCLIVersion)) +} +$cliTools.AddToolVersion("AWS CLI", $(Get-AWSCLIVersion)) +$cliTools.AddToolVersion("AWS SAM CLI", $(Get-AWSSAMVersion)) +$cliTools.AddToolVersion("AWS Session Manager CLI", $(Get-AWSSessionManagerVersion)) +$cliTools.AddToolVersion("Azure CLI", $(Get-AzureCLIVersion)) +$cliTools.AddToolVersion("Azure DevOps CLI extension", $(Get-AzureDevopsExtVersion)) +if (Test-IsWin19) { + $cliTools.AddToolVersion("Cloud Foundry CLI", $(Get-CloudFoundryVersion)) +} +$cliTools.AddToolVersion("GitHub CLI", $(Get-GHVersion)) + +# Rust Tools +Initialize-RustEnvironment +$rustTools = $installedSoftware.AddHeader("Rust Tools") +$rustTools.AddToolVersion("Cargo", $(Get-RustCargoVersion)) +$rustTools.AddToolVersion("Rust", $(Get-RustVersion)) +$rustTools.AddToolVersion("Rustdoc", $(Get-RustdocVersion)) +$rustTools.AddToolVersion("Rustup", $(Get-RustupVersion)) + +$rustToolsPackages = $rustTools.AddHeader("Packages") +if (-not (Test-IsWin25)) { + $rustToolsPackages.AddToolVersion("bindgen", $(Get-BindgenVersion)) +} +$rustToolsPackages.AddToolVersion("cargo-audit", $(Get-CargoAuditVersion)) +$rustToolsPackages.AddToolVersion("cargo-outdated", $(Get-CargoOutdatedVersion)) +$rustToolsPackages.AddToolVersion("cbindgen", $(Get-CbindgenVersion)) +$rustToolsPackages.AddToolVersion("Clippy", $(Get-RustClippyVersion)) +$rustToolsPackages.AddToolVersion("Rustfmt", $(Get-RustfmtVersion)) + +# Browsers and Drivers +$browsersAndWebdrivers = $installedSoftware.AddHeader("Browsers and Drivers") +$browsersAndWebdrivers.AddNodes($(Build-BrowserSection)) +$browsersAndWebdrivers.AddHeader("Environment variables").AddTable($(Build-BrowserWebdriversEnvironmentTable)) + +# Java +$installedSoftware.AddHeader("Java").AddTable($(Get-JavaVersions)) + +# Shells +$installedSoftware.AddHeader("Shells").AddTable($(Get-ShellTarget)) + +# MSYS2 +$msys2 = $installedSoftware.AddHeader("MSYS2") +$msys2.AddToolVersion("Pacman", $(Get-PacmanVersion)) + +$notes = @' +Location: C:\msys64 + +Note: MSYS2 is pre-installed on image but not added to PATH. +'@ +$msys2.AddHeader("Notes").AddNote($notes) + +# BizTalk Server +if (Test-IsWin19) +{ + $installedSoftware.AddHeader("BizTalk Server").AddNode($(Get-BizTalkVersion)) +} + +# Cached Tools +$installedSoftware.AddHeader("Cached Tools").AddNodes($(Build-CachedToolsSection)) + +# Databases +$databases = $installedSoftware.AddHeader("Databases") +$databases.AddHeader("PostgreSQL").AddTable($(Get-PostgreSQLTable)) +$databases.AddHeader("MongoDB").AddTable($(Get-MongoDBTable)) + +# Database tools +$databaseTools = $installedSoftware.AddHeader("Database tools") +$databaseTools.AddToolVersion("Azure CosmosDb Emulator", $(Get-AzCosmosDBEmulatorVersion)) +$databaseTools.AddToolVersion("DacFx", $(Get-DacFxVersion)) +$databaseTools.AddToolVersion("MySQL", $(Get-MySQLVersion)) +$databaseTools.AddToolVersion("SQL OLEDB Driver", $(Get-SQLOLEDBDriverVersion)) +$databaseTools.AddToolVersion("SQLPS", $(Get-SQLPSVersion)) +if (Test-IsWin25) { + $databaseTools.AddToolVersion("MongoDB Shell (mongosh)", $(Get-MongoshVersion)) +} +# Web Servers +$installedSoftware.AddHeader("Web Servers").AddTable($(Build-WebServersSection)) + +# Visual Studio +$vsTable = Get-VisualStudioVersion +$visualStudio = $installedSoftware.AddHeader($vsTable.Name) +$visualStudio.AddTable($vsTable) + +$workloads = $visualStudio.AddHeader("Workloads, components and extensions") +$workloads.AddTable((Get-VisualStudioComponents) + (Get-VisualStudioExtensions)) + +$msVisualCpp = $visualStudio.AddHeader("Microsoft Visual C++") +$msVisualCpp.AddTable($(Get-VisualCPPComponents)) + +$visualStudio.AddToolVersionsList("Installed Windows SDKs", $(Get-WindowsSDKs).Versions, '^.+') + +# .NET Core Tools +$netCoreTools = $installedSoftware.AddHeader(".NET Core Tools") +if (Test-IsWin19) { + # Visual Studio 2019 brings own version of .NET Core which is different from latest official version + $netCoreTools.AddToolVersionsListInline(".NET Core SDK", $(Get-DotnetSdks).Versions, '^\d+\.\d+\.\d{2}') +} else { + $netCoreTools.AddToolVersionsListInline(".NET Core SDK", $(Get-DotnetSdks).Versions, '^\d+\.\d+\.\d') +} +$netCoreTools.AddToolVersionsListInline(".NET Framework", $(Get-DotnetFrameworkVersions), '^.+') +Get-DotnetRuntimes | ForEach-Object { + $netCoreTools.AddToolVersionsListInline($_.Runtime, $_.Versions, '^.+') +} +$netCoreTools.AddNodes($(Get-DotnetTools)) + +# PowerShell Tools +$psTools = $installedSoftware.AddHeader("PowerShell Tools") +$psTools.AddToolVersion("PowerShell", $(Get-PowershellCoreVersion)) + +$psModules = $psTools.AddHeader("Powershell Modules") +$psModules.AddNodes($(Get-PowerShellModules)) + +$azPsNotes = @' +Azure PowerShell module 2.1.0 and AzureRM PowerShell module 2.1.0 are installed +and are available via 'Get-Module -ListAvailable'. +All other versions are saved but not installed. +'@ +$psModules.AddNote($azPsNotes) + +# Android +$android = $installedSoftware.AddHeader("Android") +$android.AddTable($(Build-AndroidTable)) + +$android.AddHeader("Environment variables").AddTable($(Build-AndroidEnvironmentTable)) + +# Cached Docker images +$installedSoftware.AddHeader("Cached Docker images").AddTable($(Get-CachedDockerImagesTableData)) + +# Generate reports +$softwareReport.ToJson() | Out-File -FilePath "C:\software-report.json" -Encoding UTF8NoBOM +$softwareReport.ToMarkdown() | Out-File -FilePath "C:\software-report.md" -Encoding UTF8NoBOM diff --git a/images/windows/scripts/docs-gen/SoftwareReport.Android.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.Android.psm1 new file mode 100644 index 0000000..9aeeb4d --- /dev/null +++ b/images/windows/scripts/docs-gen/SoftwareReport.Android.psm1 @@ -0,0 +1,174 @@ +Import-Module (Join-Path $PSScriptRoot "SoftwareReport.Helpers.psm1") -DisableNameChecking + +function Split-TableRowByColumns { + param( + [string] $Row + ) + return $Row.Split("|") | ForEach-Object { $_.trim() } +} + +function Get-AndroidSDKRoot { + param( + [string] $ComponentName + ) + $path = Join-Path $env:ANDROID_HOME $ComponentName + return "Location $path" +} + +function Build-AndroidTable { + $packageInfo = Get-AndroidInstalledPackages + return @( + @{ + "Package" = "Android Command Line Tools" + "Version" = Get-AndroidCommandLineToolsVersion + }, + @{ + "Package" = "Android Emulator" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android Emulator" + }, + @{ + "Package" = "Android SDK Build-tools" + "Version" = Get-AndroidBuildToolVersions -PackageInfo $packageInfo + }, + @{ + "Package" = "Android SDK Platforms" + "Version" = Get-AndroidPlatformVersions -PackageInfo $packageInfo + }, + @{ + "Package" = "Android SDK Platform-Tools" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android SDK Platform-Tools" + }, + @{ + "Package" = "Android Support Repository" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Android Support Repository" + }, + @{ + "Package" = "CMake" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "cmake" + }, + @{ + "Package" = "Google APIs" + "Version" = Get-AndroidGoogleAPIsVersions -PackageInfo $packageInfo + }, + @{ + "Package" = "Google Play services" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Google Play services" + }, + @{ + "Package" = "Google Repository" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "Google Repository" + }, + @{ + "Package" = "NDK" + "Version" = Get-AndroidNdkVersions -PackageInfo $packageInfo + }, + @{ + "Package" = "SDK Patch Applier v4" + "Version" = Get-AndroidPackageVersions -PackageInfo $packageInfo -MatchedString "SDK Patch Applier v4" + } + ) | Where-Object { $_.Version } | ForEach-Object { + [PSCustomObject] @{ + "Package Name" = $_.Package + "Version" = $_.Version + } + } +} + +function Get-AndroidPackageVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo, + [Parameter(Mandatory)] + [object] $MatchedString + ) + + $versions = $packageInfo | Where-Object { $_ -Match $MatchedString } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + return $packageInfoParts[1] + } + return ($versions -Join "
") +} + +function Get-AndroidPlatformVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo + ) + + $versions = $packageInfo | Where-Object { $_ -Match "Android SDK Platform " } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + $revision = $packageInfoParts[1] + $version = $packageInfoParts[0].split(";")[1] + return "$version (rev $revision)" + } + [array]::Reverse($versions) + return ($versions -Join "
") +} + +function Get-AndroidCommandLineToolsVersion { + $commandLineTools = (Join-Path $env:ANDROID_HOME "cmdline-tools\latest\bin\sdkmanager.bat") + (cmd /c "$commandLineTools --version 2>NUL" | Out-String).Trim() -match "(?^(\d+\.){1,}\d+$)" | Out-Null + $commandLineToolsVersion = $Matches.Version + return $commandLineToolsVersion +} + +function Get-AndroidBuildToolVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo + ) + + $versions = $packageInfo | Where-Object { $_ -Match "Android SDK Build-Tools" } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + return $packageInfoParts[1] + } + $groupVersions = @() + $versions | ForEach-Object { + $majorVersion = $_.Split(".")[0] + $groupVersions += $versions | Where-Object { $_.StartsWith($majorVersion) } | Join-String -Separator " " + } + return ($groupVersions | Sort-Object -Descending -Unique | Join-String -Separator "
") +} + +function Get-AndroidGoogleAPIsVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo + ) + + $versions = $packageInfo | Where-Object { $_ -Match "addon-google_apis" } | ForEach-Object { + $packageInfoParts = Split-TableRowByColumns $_ + return $packageInfoParts[0].split(";")[1] + } + return ($versions -Join "
") +} + +function Get-AndroidNdkVersions { + param ( + [Parameter(Mandatory)] + [object] $PackageInfo + ) + + $ndkDefaultFullVersion = Get-ChildItem $env:ANDROID_NDK_HOME -Name + + $versions = $packageInfo | Where-Object { $_ -Match "ndk;" } | ForEach-Object { + $version = (Split-TableRowByColumns $_)[1] + if ($version -eq $ndkDefaultFullVersion) { + $version += " (default)" + } + $version + } + return ($versions -Join "
") +} + +function Build-AndroidEnvironmentTable { + $androidVersions = Get-Item env:ANDROID_* + + $shoulddResolveLink = 'ANDROID_NDK', 'ANDROID_NDK_HOME', 'ANDROID_NDK_ROOT', 'ANDROID_NDK_LATEST_HOME' + return $androidVersions | Sort-Object -Property Name | ForEach-Object { + [PSCustomObject] @{ + "Name" = $_.Name + "Value" = if ($shoulddResolveLink.Contains($_.Name )) { Get-PathWithLink($_.Value) } else {$_.Value} + } + } +} diff --git a/images/windows/scripts/docs-gen/SoftwareReport.Browsers.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.Browsers.psm1 new file mode 100644 index 0000000..43b0ec8 --- /dev/null +++ b/images/windows/scripts/docs-gen/SoftwareReport.Browsers.psm1 @@ -0,0 +1,100 @@ +$browsers = @{ + chrome = @{ + Name="Google Chrome"; + File="chrome.exe" + }; + edge = @{ + Name="Microsoft Edge"; + File="msedge.exe" + }; + firefox = @{ + Name="Mozilla Firefox"; + File="firefox.exe" + } +} + +$webDrivers = @{ + chrome = @{ + Name="Chrome Driver"; + Path="C:\SeleniumWebDrivers\ChromeDriver" + }; + edge = @{ + Name="Microsoft Edge Driver"; + Path="C:\SeleniumWebDrivers\EdgeDriver" + }; + firefox = @{ + Name="Gecko Driver"; + Path="C:\SeleniumWebDrivers\GeckoDriver" + }; + iexplorer = @{ + Name="IE Driver"; + Path="C:\SeleniumWebDrivers\IEDriver" + } +} + +function Build-BrowserSection { + return @( + $(Get-BrowserVersion -Browser "chrome"), + $(Get-SeleniumWebDriverVersion -Driver "chrome"), + $(Get-BrowserVersion -Browser "edge"), + $(Get-SeleniumWebDriverVersion -Driver "edge"), + $(Get-BrowserVersion -Browser "firefox"), + $(Get-SeleniumWebDriverVersion -Driver "firefox"), + $(Get-SeleniumWebDriverVersion -Driver "iexplorer"), + $(Get-SeleniumVersion) + ) +} + +function Get-BrowserVersion { + param( + [string] $Browser + ) + $browserName = $browsers.$Browser.Name + $browserFile = $browsers.$Browser.File + $registryKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\$browserFile" + $browserVersion = (Get-Item (Get-ItemProperty $registryKey)."(Default)").VersionInfo.FileVersion + return [ToolVersionNode]::new($browserName, $browserVersion) +} + +function Get-SeleniumWebDriverVersion { + param( + [string] $Driver + ) + $driverName = $webDrivers.$Driver.Name + $driverPath = $webDrivers.$Driver.Path + $versionFileName = "versioninfo.txt"; + $webDriverVersion = Get-Content -Path "$driverPath\$versionFileName" + return [ToolVersionNode]::new($driverName, $webDriverVersion) +} + +function Get-SeleniumVersion { + $seleniumBinaryName = "selenium-server" + $fullSeleniumVersion = (Get-ChildItem "C:\selenium\${seleniumBinaryName}-*").Name -replace "${seleniumBinaryName}-" + return [ToolVersionNode]::new("Selenium server", $fullSeleniumVersion) +} + +function Build-BrowserWebdriversEnvironmentTable { + return @( + @{ + "Name" = "CHROMEWEBDRIVER" + "Value" = $env:CHROMEWEBDRIVER + }, + @{ + "Name" = "EDGEWEBDRIVER" + "Value" = $env:EDGEWEBDRIVER + }, + @{ + "Name" = "GECKOWEBDRIVER" + "Value" = $env:GECKOWEBDRIVER + }, + @{ + "Name" = "SELENIUM_JAR_PATH" + "Value" = $env:SELENIUM_JAR_PATH + } + ) | ForEach-Object { + [PSCustomObject] @{ + "Name" = $_.Name + "Value" = $_.Value + } + } +} diff --git a/images/windows/scripts/docs-gen/SoftwareReport.CachedTools.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.CachedTools.psm1 new file mode 100644 index 0000000..a62ed45 --- /dev/null +++ b/images/windows/scripts/docs-gen/SoftwareReport.CachedTools.psm1 @@ -0,0 +1,40 @@ +function Get-ToolcacheGoVersions { + $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Go" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } +} + +function Get-ToolcacheNodeVersions { + $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Node" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } +} + +function Get-ToolcachePythonVersions { + $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Python" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } +} + +function Get-ToolcacheRubyVersions { + $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "Ruby" + return Get-ChildItem $toolcachePath -Name | Sort-Object { [Version] $_ } +} + +function Get-ToolcachePyPyVersions { + $toolcachePath = Join-Path $env:AGENT_TOOLSDIRECTORY "PyPy" + Get-ChildItem -Path $toolcachePath -Name | Sort-Object { [Version] $_ } | ForEach-Object { + $pypyRootPath = Join-Path $toolcachePath $_ "x86" + [string] $pypyVersionOutput = & "$pypyRootPath\python.exe" -c "import sys;print(sys.version)" + $pypyVersionOutput -match "^([\d\.]+) \(.+\) \[PyPy ([\d\.]+\S*) .+]$" | Out-Null + return "{0} [PyPy {1}]" -f $Matches[1], $Matches[2] + } +} + +function Build-CachedToolsSection +{ + return @( + [ToolVersionsListNode]::new("Go", $(Get-ToolcacheGoVersions), '^\d+\.\d+', 'List'), + [ToolVersionsListNode]::new("Node.js", $(Get-ToolcacheNodeVersions), '^\d+', 'List'), + [ToolVersionsListNode]::new("Python", $(Get-ToolcachePythonVersions), '^\d+\.\d+', 'List'), + [ToolVersionsListNode]::new("PyPy", $(Get-ToolcachePyPyVersions), '^\d+\.\d+', 'List'), + [ToolVersionsListNode]::new("Ruby", $(Get-ToolcacheRubyVersions), '^\d+\.\d+', 'List') + ) +} diff --git a/images/windows/scripts/docs-gen/SoftwareReport.Common.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.Common.psm1 new file mode 100644 index 0000000..34a956b --- /dev/null +++ b/images/windows/scripts/docs-gen/SoftwareReport.Common.psm1 @@ -0,0 +1,329 @@ +function Initialize-RustEnvironment { + $env:RUSTUP_HOME = "C:\Users\Default\.rustup" + $env:CARGO_HOME = "C:\Users\Default\.cargo" + $env:Path += ";$env:CARGO_HOME\bin" +} + +function Get-OSName { + return (Get-CimInstance -ClassName Win32_OperatingSystem).Caption | Get-StringPart -Part 1,2,3 +} + +function Get-OSVersion { + $OSVersion = (Get-CimInstance -ClassName Win32_OperatingSystem).Version + $OSBuild = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion' UBR).UBR + return "$OSVersion Build $OSBuild" +} + +function Build-OSInfoSection { + $osInfoNode = [HeaderNode]::new($(Get-OSName)) + $osInfoNode.AddToolVersion("OS Version:", $(Get-OSVersion)) + $osInfoNode.AddToolVersion("Image Version:", $env:IMAGE_VERSION) + return $osInfoNode +} + +function Get-BashVersion { + bash --% -c 'echo ${BASH_VERSION}' +} + +function Get-RustVersion { + rustc --version | Get-StringPart -Part 1 +} + +function Get-RustupVersion { + cmd /c "rustup --version 2>NUL" | Get-StringPart -Part 1 +} + +function Get-RustCargoVersion { + cargo --version | Get-StringPart -Part 1 +} + +function Get-RustdocVersion { + rustdoc --version | Get-StringPart -Part 1 +} + +function Get-RustfmtVersion { + rustfmt --version | Get-StringPart -Part 1 | Get-StringPart -Part 0 -Delimiter ('-') +} + +function Get-RustClippyVersion { + cargo clippy --version | Get-StringPart -Part 1 +} + +function Get-BindgenVersion { + bindgen --version | Get-StringPart -Part 1 +} + +function Get-CbindgenVersion { + cbindgen --version | Get-StringPart -Part 1 +} + +function Get-CargoAuditVersion { + cargo-audit --version | Get-StringPart -Part 1 +} + +function Get-CargoOutdatedVersion { + cargo outdated --version | Get-StringPart -Part 1 +} + +function Get-PythonVersion { + python --version | Get-StringPart -Part 1 +} + +function Get-PowershellCoreVersion { + pwsh --version | Get-StringPart -Part 1 +} + +function Get-RubyVersion { + ruby --version | Get-StringPart -Part 1 +} + +function Get-GoVersion { + go version | Get-StringPart -Part 2 | Get-StringPart -Part 1 -Delimiter ('o') +} + +function Get-KotlinVersion { + cmd /c "kotlinc -version 2>&1" | Get-StringPart -Part 2 +} + +function Get-PHPVersion { + php --version | Out-String | Get-StringPart -Part 1 +} + +function Get-JuliaVersion { + julia --version | Get-StringPart -Part 2 +} + +function Get-LLVMVersion { + (clang --version) -match "clang" | Get-StringPart -Part 2 +} + +function Get-PerlVersion { + ($(perl --version) | Out-String) -match "\(v(?\d+\.\d+\.\d+)\)" | Out-Null + $perlVersion = $Matches.Version + return $perlVersion +} + +function Get-NodeVersion { + node --version | Get-StringPart -Part 0 -Delimiter ('v') +} + +function Get-ChocoVersion { + choco --version +} + +function Get-VcpkgVersion { + $commitId = git -C "C:\vcpkg" rev-parse --short HEAD + return "(build from commit $commitId)" +} + +function Get-NPMVersion { + npm -version +} + +function Get-YarnVersion { + yarn -version +} + +function Get-RubyGemsVersion { + gem --version +} + +function Get-HelmVersion { + ($(helm version --short) | Out-String) -match "v(?\d+\.\d+\.\d+)" | Out-Null + $helmVersion = $Matches.Version + return $helmVersion +} + +function Get-PipVersion { + (pip --version) -match "pip" | Get-StringPart -Part 1, 4, 5 +} + +function Get-CondaVersion { + $condaVersion = ((& "$env:CONDA\Scripts\conda.exe" --version) -replace "^conda").Trim() + return "$condaVersion (pre-installed on the image but not added to PATH)" +} + +function Get-ComposerVersion { + composer --version | Get-StringPart -Part 2 +} + +function Get-NugetVersion { + (nuget help) -match "Nuget Version" | Get-StringPart -Part 2 +} + +function Get-AntVersion { + ant -version | Get-StringPart -Part 3 +} + +function Get-MavenVersion { + (mvn -version) -match "Apache Maven" | Get-StringPart -Part 2 +} + +function Get-GradleVersion { + ($(gradle -version) | Out-String) -match "Gradle (?\d+\.\d+)" | Out-Null + $gradleVersion = $Matches.Version + return $gradleVersion +} + +function Get-SbtVersion { + (sbt -version) -match "sbt script" | Get-StringPart -Part 3 +} + +function Get-DotnetSdks { + $sdksRawList = dotnet --list-sdks + $sdkVersions = $sdksRawList | Foreach-Object { $_.Split()[0] } + $sdkPath = $sdksRawList[0].Split(' ', 2)[1] -replace '\[|]' + [PSCustomObject]@{ + Versions = $sdkVersions + Path = $sdkPath + } +} + +function Get-DotnetTools { + $env:Path += ";C:\Users\Default\.dotnet\tools" + $dotnetTools = (Get-ToolsetContent).dotnet.tools + + $toolsList = @() + + foreach ($dotnetTool in $dotnetTools) { + $version = Invoke-Expression $dotnetTool.getversion + $toolsList += [ToolVersionNode]::new($dotnetTool.name, $version) + } + return $toolsList +} + +function Get-DotnetRuntimes { + $runtimesRawList = dotnet --list-runtimes + $runtimesRawList | Group-Object { $_.Split()[0] } | ForEach-Object { + $runtimeName = $_.Name + $runtimeVersions = $_.Group | Foreach-Object { $_.split()[1] } + $runtimePath = $_.Group[0].Split(' ', 3)[2] -replace '\[|]' + [PSCustomObject]@{ + "Runtime" = $runtimeName + "Versions" = $runtimeVersions + "Path" = $runtimePath + } + } +} + +function Get-DotnetFrameworkVersions { + $path = "${env:ProgramFiles(x86)}\Microsoft SDKs\Windows\*\*\NETFX * Tools" + return Get-ChildItem -Path $path -Directory | ForEach-Object { $_.Name | Get-StringPart -Part 1 } +} + +function Get-PowerShellAzureModules { + [Array] $result = @() + $defaultAzureModuleVersion = "2.1.0" + + [Array] $azInstalledModules = Get-ChildItem -Path "C:\Modules\az_*" -Directory | ForEach-Object { $_.Name.Split("_")[1] } + if ($azInstalledModules.Count -gt 0) { + $result += [ToolVersionsListNode]::new("Az", $($azInstalledModules), '^\d+\.\d+', "Inline") + } + + [Array] $azureInstalledModules = Get-ChildItem -Path "C:\Modules\azure_*" -Directory | ForEach-Object { $_.Name.Split("_")[1] } | ForEach-Object { if ($_ -eq $defaultAzureModuleVersion) { "$($_) (Default)" } else { $_ } } + if ($azureInstalledModules.Count -gt 0) { + $result += [ToolVersionsListNode]::new("Azure", $($azureInstalledModules), '^\d+\.\d+', "Inline") + } + + [Array] $azurermInstalledModules = Get-ChildItem -Path "C:\Modules\azurerm_*" -Directory | ForEach-Object { $_.Name.Split("_")[1] } | ForEach-Object { if ($_ -eq $defaultAzureModuleVersion) { "$($_) (Default)" } else { $_ } } + if ($azurermInstalledModules.Count -gt 0) { + $result += [ToolVersionsListNode]::new("AzureRM", $($azurermInstalledModules), '^\d+\.\d+', "Inline") + } + + [Array] $azCachedModules = Get-ChildItem -Path "C:\Modules\az_*.zip" -File | ForEach-Object { $_.Name.Split("_")[1] } + if ($azCachedModules.Count -gt 0) { + $result += [ToolVersionsListNode]::new("Az (Cached)", $($azCachedModules), '^\d+\.\d+', "Inline") + } + + [Array] $azureCachedModules = Get-ChildItem -Path "C:\Modules\azure_*.zip" -File | ForEach-Object { $_.Name.Split("_")[1] } + if ($azureCachedModules.Count -gt 0) { + $result += [ToolVersionsListNode]::new("Azure (Cached)", $($azureCachedModules), '^\d+\.\d+', "Inline") + } + + [Array] $azurermCachedModules = Get-ChildItem -Path "C:\Modules\azurerm_*.zip" -File | ForEach-Object { $_.Name.Split("_")[1] } + if ($azurermCachedModules.Count -gt 0) { + $result += [ToolVersionsListNode]::new("AzureRM (Cached)", $($azurermCachedModules), '^\d+\.\d+', "Inline") + } + + return $result +} + +function Get-PowerShellModules { + [Array] $result = @() + + $result += Get-PowerShellAzureModules + + $result += (Get-ToolsetContent).powershellModules.name | Sort-Object | ForEach-Object { + $moduleName = $_ + $moduleVersions = Get-Module -Name $moduleName -ListAvailable | Select-Object -ExpandProperty Version | Sort-Object -Unique + return [ToolVersionsListNode]::new($moduleName, $moduleVersions, '^\d+', "Inline") + } + + return $result +} + +function Get-CachedDockerImages { + return (docker images --digests --format "* {{.Repository}}:{{.Tag}}").Split("*") | Where-Object { $_ } +} + +function Get-CachedDockerImagesTableData { + $allImages = docker images --digests --format "*{{.Repository}}:{{.Tag}}|{{.Digest}} |{{.CreatedAt}}" + if (-not $allImages) { + return $null + } + + $allImages.Split("*") | Where-Object { $_ } | ForEach-Object { + $parts = $_.Split("|") + [PSCustomObject] @{ + "Repository:Tag" = $parts[0] + "Digest" = $parts[1] + "Created" = $parts[2].split(' ')[0] + } + } | Sort-Object -Property "Repository:Tag" +} + +function Get-ShellTarget { + return Get-ChildItem C:\shells -File | Select-Object Name, @{n = "Target"; e = { + if ($_.Name -eq "msys2bash.cmd") { + "C:\msys64\usr\bin\bash.exe" + } else { + @($_.Target)[0] + } + } + } | Sort-Object Name +} + +function Get-PacmanVersion { + $msys2BinDir = "C:\msys64\usr\bin" + $pacmanPath = Join-Path $msys2BinDir "pacman.exe" + $rawVersion = & $pacmanPath --version + $rawVersion.Split([System.Environment]::NewLine)[1] -match "\d+\.\d+(\.\d+)?" | Out-Null + $pacmanVersion = $matches[0] + return $pacmanVersion +} + +function Get-YAMLLintVersion { + yamllint --version | Get-StringPart -Part 1 +} + +function Get-BizTalkVersion { + $bizTalkReg = Get-ItemProperty "HKLM:\SOFTWARE\WOW6432Node\Microsoft\BizTalk Server\3.0" + return [ToolVersionNode]::new($bizTalkReg.ProductName, $bizTalkReg.ProductVersion) +} + +function Get-PipxVersion { + pipx --version +} + +function Build-PackageManagementEnvironmentTable { + return @( + [PSCustomObject] @{ + "Name" = "VCPKG_INSTALLATION_ROOT" + "Value" = $env:VCPKG_INSTALLATION_ROOT + }, + [PSCustomObject] @{ + "Name" = "CONDA" + "Value" = $env:CONDA + } + ) +} diff --git a/images/windows/scripts/docs-gen/SoftwareReport.Databases.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.Databases.psm1 new file mode 100644 index 0000000..a831c92 --- /dev/null +++ b/images/windows/scripts/docs-gen/SoftwareReport.Databases.psm1 @@ -0,0 +1,37 @@ +function Get-PostgreSQLTable +{ + $pgService = Get-CimInstance Win32_Service -Filter "Name LIKE 'postgresql-%'" + $pgPath = $pgService.PathName + $pgRoot = $pgPath.split('"')[1].replace("\bin\pg_ctl.exe", "") + $env:Path += ";${env:PGBIN}" + $pgVersion = (postgres --version).split()[2].Trim() + + return @( + [PSCustomObject]@{ Property = "ServiceName"; Value = $pgService.Name }, + [PSCustomObject]@{ Property = "Version"; Value = $pgVersion }, + [PSCustomObject]@{ Property = "ServiceStatus"; Value = $pgService.State }, + [PSCustomObject]@{ Property = "ServiceStartType"; Value = $pgService.StartMode }, + [PSCustomObject]@{ Property = "EnvironmentVariables"; Value = "`PGBIN=$env:PGBIN`
`PGDATA=$env:PGDATA`
`PGROOT=$env:PGROOT` " }, + [PSCustomObject]@{ Property = "Path"; Value = $pgRoot }, + [PSCustomObject]@{ Property = "UserName"; Value = $env:PGUSER }, + [PSCustomObject]@{ Property = "Password"; Value = $env:PGPASSWORD } + ) +} + +function Get-MongoDBTable +{ + $name = "MongoDB" + if (Test-IsWin25) { + $command = "mongod" + } else { + $command = "mongo" + } + $mongoService = Get-Service -Name $name + $mongoVersion = (Get-Command -Name $command).Version.ToString() + return [PSCustomObject]@{ + Version = $mongoVersion + ServiceName = $name + ServiceStatus = $mongoService.Status + ServiceStartType = $mongoService.StartType + } +} diff --git a/images/windows/scripts/docs-gen/SoftwareReport.Helpers.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.Helpers.psm1 new file mode 100644 index 0000000..c96afe9 --- /dev/null +++ b/images/windows/scripts/docs-gen/SoftwareReport.Helpers.psm1 @@ -0,0 +1,30 @@ +function Get-LinkTarget { + param ( + [string] $inputPath + ) + $link = Get-Item $inputPath | Select-Object -ExpandProperty Target + if ($link) { + return " -> $link" + } + return "" +} + +function Get-PathWithLink { + param ( + [string] $inputPath + ) + $link = Get-LinkTarget($inputPath) + return "${inputPath}${link}" +} + +function Get-StringPart { + param ( + [Parameter(ValueFromPipeline)] + [string] $toolOutput, + [string] $Delimiter = " ", + [int[]] $Part + ) + $parts = $toolOutput.Split($Delimiter, [System.StringSplitOptions]::RemoveEmptyEntries) + $selectedParts = $parts[$Part] + return [string]::Join($Delimiter, $selectedParts) +} diff --git a/images/windows/scripts/docs-gen/SoftwareReport.Java.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.Java.psm1 new file mode 100644 index 0000000..cd6c077 --- /dev/null +++ b/images/windows/scripts/docs-gen/SoftwareReport.Java.psm1 @@ -0,0 +1,22 @@ +function Get-JavaVersions { + $defaultJavaPath = $env:JAVA_HOME + $javaVersions = Get-Item env:JAVA_HOME_*_X64 + $sortRules = @{ + Expression = { [Int32] $_.Name.Split("_")[2] } + Descending = $false + } + + return $javaVersions | Sort-Object $sortRules | ForEach-Object { + $javaPath = $_.Value + # Take semver from the java path + # 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 + $versionInPath = (Split-Path $javaPath) -replace "\w:\\.*\\" + $version = $versionInPath -replace '-', '+' + $defaultPostfix = ($javaPath -eq $defaultJavaPath) ? " (default)" : "" + + [PSCustomObject] @{ + "Version" = $version + $defaultPostfix + "Environment Variable" = $_.Name + } + } +} diff --git a/images/windows/scripts/docs-gen/SoftwareReport.Tools.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.Tools.psm1 new file mode 100644 index 0000000..bd83432 --- /dev/null +++ b/images/windows/scripts/docs-gen/SoftwareReport.Tools.psm1 @@ -0,0 +1,320 @@ +function Get-Aria2Version { + (aria2c -v | Out-String) -match "(?(\d+\.){1,}\d+)" | Out-Null + $aria2Version = $Matches.Version + return $aria2Version +} + +function Get-AzCosmosDBEmulatorVersion { + $regKey = gci HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\* | gp | ? { $_.DisplayName -eq 'Azure Cosmos DB Emulator' } + $installDir = $regKey.InstallLocation + $exeFilePath = Join-Path $installDir 'CosmosDB.Emulator.exe' + $version = (Get-Item $exeFilePath).VersionInfo.FileVersion + return $version +} + +function Get-BazelVersion { + ((cmd /c "bazel --version 2>&1") | Out-String) -match "bazel (?\d+\.\d+\.\d+)" | Out-Null + $bazelVersion = $Matches.Version + return $bazelVersion +} + +function Get-BazeliskVersion { + ((cmd /c "bazelisk version 2>&1") | Out-String) -match "Bazelisk version: v(?\d+\.\d+\.\d+)" | Out-Null + $bazeliskVersion = $Matches.Version + return $bazeliskVersion +} + +function Get-BicepVersion { + (bicep --version | Out-String) -match "bicep cli version (?\d+\.\d+\.\d+)" | Out-Null + $bicepVersion = $Matches.Version + return $bicepVersion +} + +function Get-RVersion { + ($(cmd /c "Rscript --version 2>&1") | Out-String) -match "Rscript .* version (?\d+\.\d+\.\d+)" | Out-Null + $rVersion = $Matches.Version + return $rVersion +} + +function Get-CMakeVersion { + ($(cmake -version) | Out-String) -match "cmake version (?\d+\.\d+\.\d+)" | Out-Null + $cmakeVersion = $Matches.Version + return $cmakeVersion +} + +function Get-CodeQLBundleVersion { + $CodeQLVersionsWildcard = Join-Path $env:AGENT_TOOLSDIRECTORY -ChildPath "CodeQL" | Join-Path -ChildPath "*" + $CodeQLVersionPath = Get-ChildItem $CodeQLVersionsWildcard | Select-Object -First 1 -Expand FullName + $CodeQLPath = Join-Path $CodeQLVersionPath -ChildPath "x64" | Join-Path -ChildPath "codeql" | Join-Path -ChildPath "codeql.exe" + $CodeQLVersion = & $CodeQLPath version --quiet + return $CodeQLVersion +} + +function Get-DockerVersion { + $dockerVersion = $(docker version --format "{{.Server.Version}}") + return $dockerVersion +} + +function Get-DockerComposeVersionV2 { + $dockerComposeVersion = docker compose version --short + return $dockerComposeVersion +} + +function Get-DockerWincredVersion { + $dockerCredVersion = docker-credential-wincred version | Get-StringPart -Part 2 | Get-StringPart -Part 0 -Delimiter "v" + return $dockerCredVersion +} + +function Get-GitVersion { + $gitVersion = git --version | Get-StringPart -Part -1 + return $gitVersion +} + +function Get-GitLFSVersion { + $(git-lfs version) -match "git-lfs\/(?\d+\.\d+\.\d+)" | Out-Null + $gitLfsVersion = $Matches.Version + return $gitLfsVersion +} + +function Get-InnoSetupVersion { + $innoSetupVersion = $(choco list innosetup) | Select-String -Pattern "InnoSetup" + return ($innoSetupVersion -replace "^InnoSetup").Trim() +} + +function Get-JQVersion { + $jqVersion = ($(jq --version) -Split "jq-")[1] + return $jqVersion +} + +function Get-KubectlVersion { + $kubectlVersion = (kubectl version --client --output=json | ConvertFrom-Json).clientVersion.gitVersion.Replace('v', '') + return $kubectlVersion +} + +function Get-KindVersion { + $(kind version) -match "kind v(?\d+\.\d+\.\d+)" | Out-Null + $kindVersion = $Matches.Version + return $kindVersion +} + +function Get-GCCVersion { + (gcc --version | Select-String -Pattern "gcc.exe") -match "(?\d+\.\d+\.\d+)" | Out-Null + $mingwVersion = $Matches.Version + return $mingwVersion +} + +function Get-GDBVersion { + (gdb --version | Select-String -Pattern "GNU gdb") -match "(?\d+\.\d+)" | Out-Null + $mingwVersion = $Matches.Version + return $mingwVersion +} + +function Get-GNUBinutilsVersion { + (ld --version | Select-String -Pattern "GNU Binutils") -match "(?\d+\.\d+)" | Out-Null + $mingwVersion = $Matches.Version + return $mingwVersion +} + +function Get-MySQLVersion { + $mysqlCommand = Get-Command -Name "mysql" + $mysqlVersion = $mysqlCommand.Version.ToString() + return $mysqlVersion +} + +function Get-SQLOLEDBDriverVersion { + $SQLOLEDBDriverVersion = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSOLEDBSQL' InstalledVersion).InstalledVersion + return $SQLOLEDBDriverVersion +} + +function Get-MercurialVersion { + ($(hg --version) | Out-String) -match "version (?\d+\.\d+\.?\d*)" | Out-Null + $mercurialVersion = $Matches.Version + return $mercurialVersion +} + +function Get-NSISVersion { + $nsisVersion = & "c:\Program Files (x86)\NSIS\makensis.exe" "/Version" + return $nsisVersion.TrimStart("v") +} + +function Get-OpenSSLVersion { + $(openssl version) -match "OpenSSL (?\d+\.\d+\.\d+\w?) " | Out-Null + $opensslVersion = $Matches.Version + return $opensslVersion +} + +function Get-PackerVersion { + $packerVersion = (packer --version | Select-String "^Packer").Line.Replace('v','') | Get-StringPart -Part 1 + return $packerVersion +} + +function Get-ParcelVersion { + $parcelVersion = parcel --version + return "$parcelVersion" +} + +function Get-PulumiVersion { + return (pulumi version).TrimStart("v") +} + +function Get-SQLPSVersion { + $module = Get-Module -Name SQLPS -ListAvailable + $version = $module.Version + return $version +} + +function Get-SVNVersion { + $svnVersion = $(svn --version --quiet) + return $svnVersion +} + +function Get-VSWhereVersion { + ($(Get-Command -Name vswhere).FileVersionInfo.ProductVersion) -match "(?\d+\.\d+\.\d+)" | Out-Null + $vswhereVersion = $Matches.Version + return $vswhereVersion +} + +function Get-WinAppDriver { + $winAppDriverVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe").FileVersion + return $winAppDriverVersion +} + +function Get-WixVersion { + $regKey = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" + $installedApplications = Get-ItemProperty -Path $regKey + $wixToolsetVersion = ($installedApplications | Where-Object { $_.BundleCachePath -imatch ".*\\WiX\d*\.exe$" } | Select-Object -First 1).DisplayName + return ($wixToolsetVersion -replace "^WiX Toolset v").Trim() +} + +function Get-ZstdVersion { + $(zstd --version) -match "v(?\d+\.\d+\.\d+)" | Out-Null + $zstdVersion = $Matches.Version + return $zstdVersion +} + +function Get-AzureCLIVersion { + $azureCLIVersion = $(az version) | ConvertFrom-Json | Foreach{ $_."azure-cli" } + return $azureCLIVersion +} + +function Get-AzCopyVersion { + return ($(azcopy --version) -replace "^azcopy version").Trim() +} + +function Get-AzureDevopsExtVersion { + $azureDevExtVersion = (az version | ConvertFrom-Json | ForEach-Object { $_."extensions" })."azure-devops" + return $azureDevExtVersion +} + +function Get-AWSCLIVersion { + $(aws --version) -match "aws-cli\/(?\d+\.\d+\.\d+)" | Out-Null + $awscliVersion = $Matches.Version + return $awscliVersion +} + +function Get-AWSSAMVersion { + $(sam --version) -match "version (?\d+\.\d+\.\d+)" | Out-Null + $awssamVersion = $Matches.Version + return $awssamVersion +} + +function Get-AWSSessionManagerVersion { + $awsSessionManagerVersion = $(session-manager-plugin --version) + return $awsSessionManagerVersion +} + +function Get-AlibabaCLIVersion { + $alicliVersion = $(aliyun version) + return $alicliVersion +} + +function Get-CloudFoundryVersion { + $(cf version) -match "(?\d+\.\d+\.\d+)" | Out-Null + $cfVersion = $Matches.Version + return $cfVersion +} + +function Get-7zipVersion { + (7z | Out-String) -match "7-Zip (?\d+\.\d+\.?\d*)" | Out-Null + $version = $Matches.Version + return $version +} + +function Get-GHCVersion { + ((ghc --version) | Out-String) -match "version (?\d+\.\d+\.\d+)" | Out-Null + $ghcVersion = $Matches.Version + return $ghcVersion +} + +function Get-CabalVersion { + ((cabal --version) | Out-String) -match "version (?\d+\.\d+\.\d+\.\d+)" | Out-Null + $cabalVersion = $Matches.Version + return $cabalVersion +} + +function Get-StackVersion { + ((stack --version --quiet) | Out-String) -match "Version (?\d+\.\d+\.\d+)," | Out-Null + $stackVersion = $Matches.Version + return $stackVersion +} + +function Get-GoogleCloudCLIVersion { + return (((cmd /c "gcloud --version") -match "Google Cloud SDK") -replace "Google Cloud SDK").Trim() +} + +function Get-ServiceFabricSDKVersion { + $serviceFabricSDKVersion = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Service Fabric\' -Name FabricVersion + return $serviceFabricSDKVersion +} + +function Get-NewmanVersion { + return $(newman --version) +} + +function Get-GHVersion { + ($(gh --version) | Select-String -Pattern "gh version") -match "gh version (?\d+\.\d+\.\d+)" | Out-Null + $ghVersion = $Matches.Version + return $ghVersion +} + +function Get-VisualCPPComponents { + $regKeys = @( + "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" + "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" + ) + $vcpp = Get-ItemProperty -Path $regKeys | Where-Object DisplayName -like "Microsoft Visual C++*" + $vcpp | Sort-Object DisplayName, DisplayVersion | ForEach-Object { + $isMatch = $_.DisplayName -match "^(?Microsoft Visual C\+\+ \d{4})\s+(?\w{3})\s+(?.+)\s+-" + if ($isMatch) { + $name = '{0} {1}' -f $matches["Name"], $matches["Ext"] + $arch = $matches["Arch"].ToLower() + $version = $_.DisplayVersion + [PSCustomObject]@{ + Name = $name + Architecture = $arch + Version = $version + } + } + } +} + +function Get-DacFxVersion { + $dacfxversion = & "$env:ProgramFiles\Microsoft SQL Server\160\DAC\bin\sqlpackage.exe" /version + return $dacfxversion +} + +function Get-SwigVersion { + (swig -version | Out-String) -match "version (?\d+\.\d+\.\d+)" | Out-Null + $swigVersion = $Matches.Version + return $swigVersion +} + +function Get-ImageMagickVersion { + (magick -version | Select-String -Pattern "Version") -match "(?\d+\.\d+\.\d+-\d+)" | Out-Null + $magickVersion = $Matches.Version + return $magickVersion +} + +function Get-MongoshVersion { + return $(mongosh --version) +} diff --git a/images/windows/scripts/docs-gen/SoftwareReport.VisualStudio.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.VisualStudio.psm1 new file mode 100644 index 0000000..69bad2f --- /dev/null +++ b/images/windows/scripts/docs-gen/SoftwareReport.VisualStudio.psm1 @@ -0,0 +1,71 @@ +function Get-VisualStudioVersion { + $vsInstance = Get-VisualStudioInstance + [PSCustomObject]@{ + Name = $vsInstance.DisplayName + Version = $vsInstance.InstallationVersion + Path = $vsInstance.InstallationPath + } +} + +function Get-SDKVersion { + $regKey = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" + $installedApplications = Get-ItemProperty -Path $regKey + ($installedApplications | Where-Object { $_.DisplayName -eq 'Windows SDK' } | Select-Object -First 1).DisplayVersion +} + +function Get-WDKVersion { + $regKey = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" + $installedApplications = Get-ItemProperty -Path $regKey + ($installedApplications | Where-Object { $_.DisplayName -eq 'Windows Driver Kit' } | Select-Object -First 1).DisplayVersion +} + +function Get-VisualStudioExtensions { + $vsPackages = (Get-VisualStudioInstance).Packages + + # Additional vsixs + $toolset = Get-ToolsetContent + $vsixPackagesList = $toolset.visualStudio.vsix + if ($vsixPackagesList) { + $vsixs = $vsixPackagesList | ForEach-Object { + $vsixPackage = Get-VsixInfoFromMarketplace $_ + $vsixVersion = ($vsPackages | Where-Object { $_.Id -match $vsixPackage.VsixId -and $_.type -eq 'vsix' }).Version + @{ + Package = $vsixPackage.ExtensionName + Version = $vsixVersion + } + } + } + + # SDK + $sdkVersion = Get-SDKVersion + $sdkPackages = @( + @{Package = 'Windows Software Development Kit'; Version = $sdkVersion } + ) + + # WDK + $wdkVersion = Get-WDKVersion + $wdkExtensionVersion = Get-VSExtensionVersion -packageName 'Microsoft.Windows.DriverKit' + $wdkPackages = @( + @{Package = 'Windows Driver Kit'; Version = $wdkVersion } + @{Package = 'Windows Driver Kit Visual Studio Extension'; Version = $wdkExtensionVersion } + ) + + $extensions = @( + $vsixs + $ssdtPackages + $sdkPackages + $wdkPackages + ) + + $extensions | Foreach-Object { + [PSCustomObject] $_ + } | Select-Object Package, Version | Sort-Object Package +} + +function Get-WindowsSDKs { + $path = "${env:ProgramFiles(x86)}\Windows Kits\10\Extension SDKs\WindowsDesktop" + return [PSCustomObject]@{ + Path = $path + Versions = $(Get-ChildItem $path).Name + } +} diff --git a/images/windows/scripts/docs-gen/SoftwareReport.WebServers.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.WebServers.psm1 new file mode 100644 index 0000000..1f77829 --- /dev/null +++ b/images/windows/scripts/docs-gen/SoftwareReport.WebServers.psm1 @@ -0,0 +1,60 @@ +function Get-ApachePath { + return Join-Path "C:\tools\" (Get-Item C:\tools\apache*).Name +} + +function Get-NginxPath { + return Join-Path "C:\tools\" (Get-Item C:\tools\nginx*).Name +} + +function Get-ApacheVersion { + $apacheBinPath = Join-Path (Get-ApachePath) "\bin\httpd" + (. $apacheBinPath -V | Select-String -Pattern "Apache/") -match "Apache/(?\d+\.\d+\.\d+)" | Out-Null + return $Matches.Version +} + +function Get-NginxVersion { + $nginxBinPath = Join-Path (Get-NginxPath) "nginx" + (cmd /c "$nginxBinPath -v 2>&1") -match "nginx/(?\d+\.\d+\.\d+)" | Out-Null + return $Matches.Version +} + +function Get-ApacheSection +{ + $name = "Apache" + $apachePort = "80" + $apacheService = Get-Service -Name $name + $apacheVersion = Get-ApacheVersion + $apacheConfigFile = Join-Path (Get-ApachePath) "\conf\httpd.conf" + return [PSCustomObject]@{ + Name = $name + Version = $apacheVersion + ConfigFile = $apacheConfigFile + ServiceName = $apacheService.Name + ServiceStatus = $apacheService.Status + ListenPort = $apachePort + } +} + +function Get-NginxSection +{ + $name = "Nginx" + $nginxPort = "80" + $nginxService = Get-Service -Name $name + $nginxVersion = Get-NginxVersion + $nginxConfigFile = Join-Path (Get-NginxPath) "\conf\nginx.conf" + return [PSCustomObject]@{ + Name = $name + Version = $nginxVersion + ConfigFile = $nginxConfigFile + ServiceName = $nginxService.Name + ServiceStatus = $nginxService.Status + ListenPort = $nginxPort + } +} + +function Build-WebServersSection { + return @( + (Get-ApacheSection), + (Get-NginxSection) + ) +} \ No newline at end of file diff --git a/images/windows/scripts/helpers/AndroidHelpers.ps1 b/images/windows/scripts/helpers/AndroidHelpers.ps1 new file mode 100644 index 0000000..8ba7a7b --- /dev/null +++ b/images/windows/scripts/helpers/AndroidHelpers.ps1 @@ -0,0 +1,163 @@ +function Get-AndroidPackages { + <# + .SYNOPSIS + This function returns a list of available Android packages. + + .DESCRIPTION + The Get-AndroidPackages function checks if a list of packages is already available in a file. + If not, it uses the sdkmanager.bat script to generate a list of available packages and saves it to a file. + It then returns the content of this file. + + .PARAMETER SDKRootPath + The root path of the Android SDK installation. + If not specified, the function uses the ANDROID_HOME environment variable. + + .EXAMPLE + Get-AndroidPackages -SDKRootPath "C:\Android\SDK" + + This command returns a list of available Android packages for the specified SDK root path. + + .NOTES + This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible. + + #> + Param + ( + [string] $SDKRootPath + ) + + if (-not $SDKRootPath) { + $SDKRootPath = $env:ANDROID_HOME + } + + $packagesListFile = "$SDKRootPath\packages-list.txt" + $sdkManager = "$SDKRootPath\cmdline-tools\latest\bin\sdkmanager.bat" + + if (-Not (Test-Path -Path $packagesListFile -PathType Leaf)) { + (cmd /c "$sdkManager --list --verbose 2>&1") | + Where-Object { $_ -Match "^[^\s]" } | + Where-Object { $_ -NotMatch "^(Loading |Info: Parsing |---|\[=+|Installed |Available )" } | + Where-Object { $_ -NotMatch "^[^;]*$" } | + Out-File -FilePath $packagesListFile + } + + return Get-Content $packagesListFile +} + +function Get-AndroidPlatformPackages { + <# + .SYNOPSIS + This function returns a list of available Android platform packages. + + .DESCRIPTION + The Get-AndroidPlatformPackages function uses the Get-AndroidPackages function to get a list of available packages + and filters it to return only platform packages. + + .PARAMETER SDKRootPath + The root path of the Android SDK installation. + If not specified, the function uses the ANDROID_HOME environment variable. + + .PARAMETER minimumVersion + The minimum version of the platform packages to include in the result. Default is 0. + + .EXAMPLE + Get-AndroidPlatformPackages -SDKRootPath "C:\Android\SDK" -minimumVersion 29 + + This command returns a list of available Android platform packages for the specified SDK root path with a minimum version of 29. + + .NOTES + This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible. + + #> + Param + ( + [string] $SDKRootPath, + [Alias("minVersion")] + [int] $minimumVersion = 0 + ) + + if (-not $SDKRootPath) { + $SDKRootPath = $env:ANDROID_HOME + } + + return (Get-AndroidPackages -SDKRootPath $SDKRootPath) ` + | Where-Object { "$_".StartsWith("platforms;") } ` + | Where-Object { ($_.Split("-")[1] -as [int]) -ge $minimumVersion } ` + | Sort-Object -Unique +} + +function Get-AndroidBuildToolPackages { + <# + .SYNOPSIS + This function returns a list of available Android build tool packages. + + .DESCRIPTION + The Get-AndroidBuildToolPackages function uses the Get-AndroidPackages function to get a list of available packages + and filters it to return only build tool packages. + + .PARAMETER SDKRootPath + The root path of the Android SDK installation. + If not specified, the function uses the ANDROID_HOME environment variable. + + .PARAMETER minimumVersion + The minimum version of the build tool packages to include in the result. Default is 0.0.0. + + .EXAMPLE + Get-AndroidBuildToolPackages -SDKRootPath "C:\Android\SDK" -minimumVersion "30.0.2" + + 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. + + .NOTES + This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible. + + #> + Param + ( + [string] $SDKRootPath, + [Alias("minVersion")] + [version] $minimumVersion = "0.0.0" + ) + + if (-not $SDKRootPath) { + $SDKRootPath = $env:ANDROID_HOME + } + + return (Get-AndroidPackages -SDKRootPath $SDKRootPath) ` + | Where-Object { "$_".StartsWith("build-tools;") } ` + | Where-Object { ($_.Split(";")[1] -as [version]) -ge $minimumVersion } ` + | Sort-Object -Unique +} + +function Get-AndroidInstalledPackages { + <# + .SYNOPSIS + Retrieves a list of installed Android packages. + + .DESCRIPTION + This function retrieves a list of installed Android packages using the specified SDK root path. + + .PARAMETER SDKRootPath + The root path of the Android SDK. + If not specified, the function uses the ANDROID_HOME environment variable. + + .EXAMPLE + Get-AndroidInstalledPackages -SDKRootPath "C:\Android\SDK" + Retrieves a list of installed Android packages using the specified SDK root path. + + .NOTES + This function requires the Android SDK to be installed and the SDK root path to be provided. + #> + + Param + ( + [string] $SDKRootPath + ) + + if (-not $SDKRootPath) { + $SDKRootPath = $env:ANDROID_HOME + } + + $sdkManager = "$SDKRootPath\cmdline-tools\latest\bin\sdkmanager.bat" + + return (cmd /c "$sdkManager --list_installed 2>&1") -notmatch "Warning" +} diff --git a/images/windows/scripts/helpers/ChocoHelpers.ps1 b/images/windows/scripts/helpers/ChocoHelpers.ps1 new file mode 100644 index 0000000..610aee8 --- /dev/null +++ b/images/windows/scripts/helpers/ChocoHelpers.ps1 @@ -0,0 +1,89 @@ +function Install-ChocoPackage { + <# + .SYNOPSIS + A function to install a Chocolatey package with retries. + + .DESCRIPTION + This function attempts to install a specified Chocolatey package. If the + installation fails, it retries a specified number of times. + + .PARAMETER PackageName + The name of the Chocolatey package to install. + + .PARAMETER ArgumentList + An array of arguments to pass to the choco install command. + + .PARAMETER RetryCount + The number of times to retry the installation if it fails. Default is 5. + + .EXAMPLE + Install-ChocoPackage -PackageName "git" -RetryCount 3 + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string] $PackageName, + [string[]] $ArgumentList, + [int] $RetryCount = 5 + ) + + process { + $count = 1 + while ($true) { + Write-Host "Running [#$count]: choco install $packageName -y $argumentList" + choco install $packageName -y @argumentList --no-progress --require-checksums + + $pkg = choco list --localonly $packageName --exact --all --limitoutput + if ($pkg) { + Write-Host "Package installed: $pkg" + break + } else { + $count++ + if ($count -ge $retryCount) { + Write-Host "Could not install $packageName after $count attempts" + exit 1 + } + Start-Sleep -Seconds 30 + } + } + } +} + +function Resolve-ChocoPackageVersion { + <# + .SYNOPSIS + Resolves the latest version of a Chocolatey package. + + .DESCRIPTION + This function takes a package name and a target version as input and returns the latest + version of the package that is greater than or equal to the target version. + + .PARAMETER PackageName + The name of the Chocolatey package. + + .PARAMETER TargetVersion + The target version of the package. + + .EXAMPLE + Resolve-ChocoPackageVersion -PackageName "example-package" -TargetVersion "1.0.0" + Returns the latest version of the "example-package" that is greater than or equal to "1.0.0". + #> + + param( + [Parameter(Mandatory)] + [string] $PackageName, + [Parameter(Mandatory)] + [string] $TargetVersion + ) + + $searchResult = choco search $PackageName --exact --all-versions --approved-only --limit-output | + ConvertFrom-CSV -Delimiter '|' -Header 'Name', 'Version' + + $latestVersion = $searchResult.Version | + Where-Object { $_ -Like "$TargetVersion.*" -or $_ -eq $TargetVersion } | + Sort-Object { [version] $_ } | + Select-Object -Last 1 + + return $latestVersion +} diff --git a/images/windows/scripts/helpers/ImageHelpers.psd1 b/images/windows/scripts/helpers/ImageHelpers.psd1 new file mode 100644 index 0000000..317f86d --- /dev/null +++ b/images/windows/scripts/helpers/ImageHelpers.psd1 @@ -0,0 +1,107 @@ +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'ImageHelpers.psm1' + +# Version number of this module. +ModuleVersion = '0.0.1' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = 'c9334909-16a1-48f1-a94a-c7baf1b961d9' + +# Description of the functionality provided by this module +Description = 'Helper functions for creating vsts images' + +# Minimum version of the Windows PowerShell engine required by this module +# PowerShellVersion = '' + +# Name of the Windows PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the Windows PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# CLRVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +# RequiredModules = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# 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. +FunctionsToExport = '*' + +# 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. +CmdletsToExport = '*' + +# Variables to export from this module +VariablesToExport = '*' + +# 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. +AliasesToExport = '*' + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# 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. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + # ProjectUri = '' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} + + diff --git a/images/windows/scripts/helpers/ImageHelpers.psm1 b/images/windows/scripts/helpers/ImageHelpers.psm1 new file mode 100644 index 0000000..f7af288 --- /dev/null +++ b/images/windows/scripts/helpers/ImageHelpers.psm1 @@ -0,0 +1,57 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\AndroidHelpers.ps1 +Export-ModuleMember -Function @( + 'Get-AndroidPackages' + 'Get-AndroidPlatformPackages' + 'Get-AndroidBuildToolPackages' + 'Get-AndroidInstalledPackages' +) + +. $PSScriptRoot\ChocoHelpers.ps1 +Export-ModuleMember -Function @( + 'Install-ChocoPackage' + 'Resolve-ChocoPackageVersion' +) + +. $PSScriptRoot\InstallHelpers.ps1 +Export-ModuleMember -Function @( + 'Install-Binary' + 'Invoke-DownloadWithRetry' + 'Get-ToolsetContent' + 'Get-TCToolPath' + 'Get-TCToolVersionPath' + 'Test-IsWin25' + 'Test-IsWin22' + 'Test-IsWin19' + 'Expand-7ZipArchive' + 'Get-WindowsUpdateStates' + 'Invoke-ScriptBlockWithRetry' + 'Get-GithubReleasesByVersion' + 'Resolve-GithubReleaseAssetUrl' + 'Get-ChecksumFromGithubRelease' + 'Get-ChecksumFromUrl' + 'Test-FileChecksum' + 'Test-FileSignature' + 'Update-Environment' +) + +. $PSScriptRoot\PathHelpers.ps1 +Export-ModuleMember -Function @( + 'Mount-RegistryHive' + 'Dismount-RegistryHive' + 'Add-MachinePathItem' + 'Add-DefaultPathItem' +) + +. $PSScriptRoot\VisualStudioHelpers.ps1 +Export-ModuleMember -Function @( + 'Install-VisualStudio' + 'Get-VisualStudioInstance' + 'Get-VisualStudioComponents' + 'Get-VsixInfoFromMarketplace' + 'Install-VSIXFromFile' + 'Install-VSIXFromUrl' + 'Get-VSExtensionVersion' +) diff --git a/images/windows/scripts/helpers/InstallHelpers.ps1 b/images/windows/scripts/helpers/InstallHelpers.ps1 new file mode 100644 index 0000000..a361a59 --- /dev/null +++ b/images/windows/scripts/helpers/InstallHelpers.ps1 @@ -0,0 +1,1026 @@ +function Install-Binary { + <# + .SYNOPSIS + A function to install binaries from either a URL or a local path. + + .DESCRIPTION + 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. + + .PARAMETER Url + The URL from which the binary will be downloaded. This parameter is required if LocalPath is not specified. + + .PARAMETER LocalPath + The local path of the binary to be installed. This parameter is required if Url is not specified. + + .PARAMETER Type + 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. + + .PARAMETER InstallArgs + The list of arguments that will be passed to the installer. Cannot be used together with ExtraInstallArgs. + + .PARAMETER ExtraInstallArgs + Additional arguments that will be passed to the installer. Cannot be used together with InstallArgs. + + .PARAMETER ExpectedSignature + The expected signature of the binary. If specified, the binary's signature is checked before installation. + + .PARAMETER ExpectedSHA256Sum + The expected SHA256 sum of the binary. If specified, the binary's SHA256 sum is checked before installation. + + .PARAMETER ExpectedSHA512Sum + The expected SHA512 sum of the binary. If specified, the binary's SHA512 sum is checked before installation. + + .EXAMPLE + Install-Binary -Url "https://go.microsoft.com/fwlink/p/?linkid=2083338" -Type EXE -InstallArgs ("/features", "+", "/quiet") -ExpectedSignature "A5C7D5B7C838D5F89DDBEDB85B2C566B4CDA881F" + #> + + Param + ( + [Parameter(Mandatory, ParameterSetName = "Url")] + [String] $Url, + [Parameter(Mandatory, ParameterSetName = "LocalPath")] + [String] $LocalPath, + [ValidateSet("MSI", "EXE")] + [String] $Type, + [String[]] $InstallArgs, + [String[]] $ExtraInstallArgs, + [String[]] $ExpectedSignature, + [String] $ExpectedSHA256Sum, + [String] $ExpectedSHA512Sum + ) + + if ($PSCmdlet.ParameterSetName -eq "LocalPath") { + if (-not (Test-Path -Path $LocalPath)) { + throw "LocalPath parameter is specified, but the file does not exist." + } + if (-not $Type) { + $Type = ([System.IO.Path]::GetExtension($LocalPath)).Replace(".", "").ToUpper() + if ($Type -ne "MSI" -and $Type -ne "EXE") { + throw "LocalPath parameter is specified, but the file extension is not .msi or .exe. Please specify the Type parameter." + } + } + $filePath = $LocalPath + } else { + if (-not $Type) { + $Type = ([System.IO.Path]::GetExtension($Url)).Replace(".", "").ToUpper() + if ($Type -ne "MSI" -and $Type -ne "EXE") { + throw "Cannot determine the file type from the URL. Please specify the Type parameter." + } + $fileName = [System.IO.Path]::GetFileName($Url) + } else { + $fileName = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetRandomFileName()) + ".$Type".ToLower() + } + $filePath = Invoke-DownloadWithRetry -Url $Url -Path "${env:Temp}\$fileName" + } + + if ($PSBoundParameters.ContainsKey('ExpectedSignature')) { + if ($ExpectedSignature) { + Test-FileSignature -Path $filePath -ExpectedThumbprint $ExpectedSignature + } else { + throw "ExpectedSignature parameter is specified, but no signature is provided." + } + } + + if ($ExpectedSHA256Sum) { + Test-FileChecksum $filePath -ExpectedSHA256Sum $ExpectedSHA256Sum + } + + if ($ExpectedSHA512Sum) { + Test-FileChecksum $filePath -ExpectedSHA512Sum $ExpectedSHA512Sum + } + + if ($ExtraInstallArgs -and $InstallArgs) { + throw "InstallArgs and ExtraInstallArgs parameters cannot be used together." + } + + if ($Type -eq "MSI") { + # MSI binaries should be installed via msiexec.exe + if ($ExtraInstallArgs) { + $InstallArgs = @('/i', $filePath, '/qn', '/norestart') + $ExtraInstallArgs + } elseif (-not $InstallArgs) { + Write-Host "No arguments provided for MSI binary. Using default arguments: /i, /qn, /norestart" + $InstallArgs = @('/i', $filePath, '/qn', '/norestart') + } + $filePath = "msiexec.exe" + } else { + # EXE binaries should be started directly + if ($ExtraInstallArgs) { + $InstallArgs = $ExtraInstallArgs + } + } + + $installStartTime = Get-Date + Write-Host "Starting Install $Name..." + try { + $process = Start-Process -FilePath $filePath -ArgumentList $InstallArgs -Wait -PassThru + $exitCode = $process.ExitCode + $installCompleteTime = [math]::Round(($(Get-Date) - $installStartTime).TotalSeconds, 2) + if ($exitCode -eq 0) { + Write-Host "Installation successful in $installCompleteTime seconds" + } elseif ($exitCode -eq 3010) { + Write-Host "Installation successful in $installCompleteTime seconds. Reboot is required." + } else { + Write-Host "Installation process returned unexpected exit code: $exitCode" + Write-Host "Time elapsed: $installCompleteTime seconds" + exit $exitCode + } + } catch { + $installCompleteTime = [math]::Round(($(Get-Date) - $installStartTime).TotalSeconds, 2) + Write-Host "Installation failed in $installCompleteTime seconds" + } +} + +function Invoke-DownloadWithRetry { + <# + .SYNOPSIS + Downloads a file from a given URL with retry functionality. + + .DESCRIPTION + The Invoke-DownloadWithRetry function downloads a file from the specified URL + to the specified path. It includes retry functionality in case the download fails. + + .PARAMETER Url + The URL of the file to download. + + .PARAMETER Path + The path where the downloaded file will be saved. If not provided, a temporary path + will be used. + + .EXAMPLE + Invoke-DownloadWithRetry -Url "https://example.com/file.zip" -Path "C:\Downloads\file.zip" + Downloads the file from the specified URL and saves it to the specified path. + + .EXAMPLE + Invoke-DownloadWithRetry -Url "https://example.com/file.zip" + Downloads the file from the specified URL and saves it to a temporary path. + + .OUTPUTS + The path where the downloaded file is saved. + #> + + Param + ( + [Parameter(Mandatory)] + [string] $Url, + [Alias("Destination")] + [string] $Path + ) + + if (-not $Path) { + $invalidChars = [IO.Path]::GetInvalidFileNameChars() -join '' + $re = "[{0}]" -f [RegEx]::Escape($invalidChars) + $fileName = [IO.Path]::GetFileName($Url) -replace $re + + if ([String]::IsNullOrEmpty($fileName)) { + $fileName = [System.IO.Path]::GetRandomFileName() + } + $Path = Join-Path -Path "${env:Temp}" -ChildPath $fileName + } + + Write-Host "Downloading package from $Url to $Path..." + + $interval = 30 + $downloadStartTime = Get-Date + for ($retries = 20; $retries -gt 0; $retries--) { + try { + $attemptStartTime = Get-Date + (New-Object System.Net.WebClient).DownloadFile($Url, $Path) + $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2) + Write-Host "Package downloaded in $attemptSeconds seconds" + break + } catch { + $attemptSeconds = [math]::Round(($(Get-Date) - $attemptStartTime).TotalSeconds, 2) + Write-Warning "Package download failed in $attemptSeconds seconds" + Write-Warning $_.Exception.Message + + if ($_.Exception.InnerException.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) { + Write-Warning "Request returned 404 Not Found. Aborting download." + $retries = 0 + } + } + + if ($retries -eq 0) { + $totalSeconds = [math]::Round(($(Get-Date) - $downloadStartTime).TotalSeconds, 2) + throw "Package download failed after $totalSeconds seconds" + } + + Write-Warning "Waiting $interval seconds before retrying (retries left: $retries)..." + Start-Sleep -Seconds $interval + } + + return $Path +} + +function Get-ToolsetContent { + <# + .SYNOPSIS + Retrieves the content of the toolset.json file. + + .DESCRIPTION + This function reads the toolset.json file in path provided by IMAGE_FOLDER + environment variable and returns the content as a PowerShell object. + #> + + $toolsetPath = Join-Path $env:IMAGE_FOLDER "toolset.json" + $toolsetJson = Get-Content -Path $toolsetPath -Raw + ConvertFrom-Json -InputObject $toolsetJson +} + +function Get-TCToolPath { + <# + .SYNOPSIS + This function returns the full path of a tool in the tool cache. + + .DESCRIPTION + The Get-TCToolPath function takes a tool name as a parameter and returns the full path of the tool in the tool cache. + It uses the AGENT_TOOLSDIRECTORY environment variable to determine the root path of the tool cache. + + .PARAMETER ToolName + The name of the tool for which the path is to be returned. + + .EXAMPLE + Get-TCToolPath -ToolName "Tool1" + + This command returns the full path of "Tool1" in the tool cache. + + #> + Param + ( + [string] $ToolName + ) + + $toolcacheRootPath = Resolve-Path $env:AGENT_TOOLSDIRECTORY + return Join-Path $toolcacheRootPath $ToolName +} + +function Get-TCToolVersionPath { + <# + .SYNOPSIS + This function returns the full path of a specific version of a tool in the tool cache. + + .DESCRIPTION + 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. + It uses the Get-TCToolPath function to get the root path of the tool. + + .PARAMETER Name + The name of the tool for which the path is to be returned. + + .PARAMETER Version + 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. + + .PARAMETER Arch + The architecture of the tool for which the path is to be returned. Defaults to "x64". + + .EXAMPLE + Get-TCToolVersionPath -Name "Tool1" -Version "1.0" -Arch "x86" + + This command returns the full path of version "1.0" of "Tool1" for "x86" architecture in the tool cache. + + #> + Param + ( + [Parameter(Mandatory = $true)] + [string] $Name, + [Parameter(Mandatory = $true)] + [string] $Version, + [string] $Arch = "x64" + ) + + $toolPath = Get-TCToolPath -ToolName $Name + + # Add wildcard if missing + if ($Version.Split(".").Length -lt 3) { + $Version += ".*" + } + + $versionPath = Join-Path $toolPath $Version + + # Take latest installed version in case if toolset version contains wildcards + $foundVersion = Get-Item $versionPath ` + | Sort-Object -Property { [version] $_.name } -Descending ` + | Select-Object -First 1 + + if (-not $foundVersion) { + return $null + } + + return Join-Path $foundVersion $Arch +} + +function Test-IsWin25 { + <# + .SYNOPSIS + Checks if the current Windows operating system is Windows Server 2025. + .DESCRIPTION + This function uses the Get-CimInstance cmdlet to retrieve information + about the current Windows operating system. It then checks if the Caption + property of the Win32_OperatingSystem class contains the string "2025", + indicating that the operating system is Windows Server 2025. + .OUTPUTS + Returns $true if the current Windows operating system is Windows Server 2025. + Otherwise, returns $false. + #> + (Get-CimInstance -ClassName Win32_OperatingSystem).Caption -match "2025" +} + +function Test-IsWin22 { + <# + .SYNOPSIS + Checks if the current Windows operating system is Windows Server 2022. + + .DESCRIPTION + This function uses the Get-CimInstance cmdlet to retrieve information + about the current Windows operating system. It then checks if the Caption + property of the Win32_OperatingSystem class contains the string "2022", + indicating that the operating system is Windows Server 2022. + + .OUTPUTS + Returns $true if the current Windows operating system is Windows Server 2022. + Otherwise, returns $false. + #> + (Get-CimInstance -ClassName Win32_OperatingSystem).Caption -match "2022" +} + +function Test-IsWin19 { + <# + .SYNOPSIS + Checks if the current Windows operating system is Windows Server 2019. + + .DESCRIPTION + This function uses the Get-CimInstance cmdlet to retrieve information + about the current Windows operating system. It then checks if the Caption + property of the Win32_OperatingSystem class contains the string "2019", + indicating that the operating system is Windows Server 2019. + + .OUTPUTS + Returns $true if the current Windows operating system is Windows Server 2019. + Otherwise, returns $false. + #> + (Get-CimInstance -ClassName Win32_OperatingSystem).Caption -match "2019" +} + +function Expand-7ZipArchive { + <# + .SYNOPSIS + Extracts files from a 7-Zip archive. + + .DESCRIPTION + This function uses the 7z.exe command-line tool to extract files from an archive. + The archive path, destination path, and extract method are specified as parameters. + + .PARAMETER Path + The path to the archive. + + .PARAMETER DestinationPath + The path to the directory where the files will be extracted. + + .PARAMETER ExtractMethod + The method used to extract the files. + Valid values are "x" (extract with full paths) and "e" (extract without paths). + + .EXAMPLE + Expand-7ZipArchive -Path "C:\archive.7z" -DestinationPath "C:\extracted" -ExtractMethod "x" + + Extracts files from the "C:\archive.7z" archive to the "C:\extracted" directory keeping the full paths. + #> + Param + ( + [Parameter(Mandatory = $true)] + [string] $Path, + [Parameter(Mandatory = $true)] + [string] $DestinationPath, + [ValidateSet("x", "e")] + [char] $ExtractMethod = "x" + ) + + Write-Host "Expand archive '$PATH' to '$DestinationPath' directory" + 7z.exe $ExtractMethod "$Path" -o"$DestinationPath" -y | Out-Null + + if ($LASTEXITCODE -ne 0) { + Write-Host "There is an error during expanding '$Path' to '$DestinationPath' directory" + exit 1 + } +} + +function Get-WindowsUpdateStates { + <# + .SYNOPSIS + Retrieves the status of Windows updates. + + .DESCRIPTION + 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. + + .PARAMETER None + This function does not take any parameters. + + .OUTPUTS + PSCustomObject. This function returns a collection of custom PowerShell objects. Each object has two properties: + - State: A string that represents the state of the update. Possible values are "Installed", "Failed", and "Running". + - Title: A string that represents the title of the update. + + .NOTES + Event IDs used: + - 19: Installation Successful: Windows successfully installed the following update + - 20: Installation Failure: Windows failed to install the following update with error + - 43: Installation Started: Windows has started installing the following update + #> + + $completedUpdates = @{} + $filter = @{ + LogName = "System" + Id = 19, 20, 43 + ProviderName = "Microsoft-Windows-WindowsUpdateClient" + } + $events = Get-WinEvent -FilterHashtable $filter -ErrorAction SilentlyContinue | Sort-Object Id + + foreach ( $event in $events ) { + switch ( $event.Id ) { + 19 { + $state = "Installed" + $title = $event.Properties[0].Value + $completedUpdates[$title] = "" + break + } + 20 { + $state = "Failed" + $title = $event.Properties[1].Value + $completedUpdates[$title] = "" + break + } + 43 { + $state = "Running" + $title = $event.Properties[0].Value + break + } + } + + # Skip update started event if it was already completed + if ( $state -eq "Running" -and $completedUpdates.ContainsKey($title) ) { + continue + } + + [PSCustomObject]@{ + State = $state + Title = $title + } + } +} + +function Invoke-ScriptBlockWithRetry { + <# + .SYNOPSIS + Executes a script block with retry logic. + + .DESCRIPTION + 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. + + .PARAMETER Command + The script block to be executed. + + .PARAMETER RetryCount + The number of times to retry executing the script block. The default value is 10. + + .PARAMETER RetryIntervalSeconds + The interval in seconds between each retry. The default value is 5. + + .EXAMPLE + Invoke-ScriptBlockWithRetry -Command { Get-Process } -RetryCount 3 -RetryIntervalSeconds 10 + This example executes the script block { Get-Process } with 3 retries and a 10-second interval between each retry. + + #> + + param ( + [scriptblock] $Command, + [int] $RetryCount = 10, + [int] $RetryIntervalSeconds = 5 + ) + + while ($RetryCount -gt 0) { + try { + & $Command + return + } catch { + Write-Host "There is an error encountered:`n $_" + $RetryCount-- + + if ($RetryCount -eq 0) { + exit 1 + } + + Write-Host "Waiting $RetryIntervalSeconds seconds before retrying. Retries left: $RetryCount" + Start-Sleep -Seconds $RetryIntervalSeconds + } + } +} + +function Get-GithubReleasesByVersion { + <# + .SYNOPSIS + Retrieves GitHub releases for a specified repository based on version. + + .DESCRIPTION + The function retrieves GitHub releases for a specified repository based on the + version provided. It supports filtering by version, allowing for the retrieval + of specific releases or the latest release. The function utilizes the GitHub REST API + to fetch the releases and caches the results to improve performance and reduce + the number of API calls. + + .PARAMETER Repository + The name of the GitHub repository in the format "owner/repo". + + .PARAMETER Version + The version of the release to retrieve. It can be a specific version number, + "latest" to retrieve the latest release, or a wildcard pattern to match multiple versions. + + .PARAMETER AllowPrerelease + Specifies whether to include prerelease versions in the results. By default, + prerelease versions are excluded. + + .PARAMETER WithAssetsOnly + Specifies whether to exclude releases without assets. By default, releases without + assets are included. + + .EXAMPLE + Get-GithubReleasesByVersion -Repository "Microsoft/PowerShell" -Version "7.2.0" + + Retrieves the GitHub releases for the "Microsoft/PowerShell" repository with the version "7.2.0". + + .EXAMPLE + Get-GithubReleasesByVersion -Repository "Microsoft/PowerShell" -Version "latest" + + Retrieves the latest GitHub release for the "Microsoft/PowerShell" repository. + + .EXAMPLE + Get-GithubReleasesByVersion -Repository "Microsoft/PowerShell" -Version "7.*" + + Retrieves all GitHub releases for the "Microsoft/PowerShell" repository with versions starting with "7.". + #> + + param ( + [Parameter(Mandatory = $true)] + [Alias("Repo")] + [string] $Repository, + [string] $Version = "*", + [switch] $AllowPrerelease, + [switch] $WithAssetsOnly + ) + + $localCacheFile = Join-Path ${env:TEMP} "github-releases_$($Repository -replace "/", "_").json" + + if (Test-Path $localCacheFile) { + $releases = Get-Content $localCacheFile | ConvertFrom-Json + Write-Debug "Found cached releases for ${Repository} in local file" + Write-Debug "Release count: $($releases.Count)" + } else { + $releases = @() + $page = 1 + $pageSize = 100 + do { + $releasesPage = Invoke-RestMethod -Uri "https://api.github.com/repos/${Repository}/releases?per_page=${pageSize}&page=${page}" + $releases += $releasesPage + $page++ + } while ($releasesPage.Count -eq $pageSize) + + Write-Debug "Found $($releases.Count) releases for ${Repository}" + Write-Debug "Caching releases for ${Repository} in local file" + $releases | ConvertTo-Json -Depth 10 | Set-Content $localCacheFile + } + + if (-not $releases) { + throw "Failed to get releases from ${Repository}" + } + + if ($WithAssetsOnly) { + $releases = $releases.Where{ $_.assets } + } + if (-not $AllowPrerelease) { + $releases = $releases.Where{ $_.prerelease -eq $false } + } + Write-Debug "Found $($releases.Count) releases with assets for ${Repository}" + + # Parse version from tag name and put it to parameter Version + foreach ($release in $releases) { + $release | Add-Member -MemberType NoteProperty -Name version -Value ( + $release.tag_name | Select-String -Pattern "\d+.\d+.\d+" | ForEach-Object { $_.Matches.Value } + ) + } + + # Sort releases by version + $releases = $releases | Sort-Object -Descending { [version] $_.version } + + # Select releases matching version + if ($Version -eq "latest") { + $matchingReleases = $releases | Select-Object -First 1 + } elseif ($Version.Contains("*")) { + $matchingReleases = $releases | Where-Object { $_.version -like "$Version" } + } else { + $matchingReleases = $releases | Where-Object { $_.version -eq "$Version" } + } + + if (-not $matchingReleases) { + throw "Failed to get releases from ${Repository} matching version `"${Version}`".`nAvailable versions: $($availableVersions -join ", ")" + } + Write-Debug "Found $($matchingReleases.Count) releases matching version ${Version} for ${Repository}" + + return $matchingReleases +} + +function Resolve-GithubReleaseAssetUrl { + <# + .SYNOPSIS + Resolves the download URL for a specific asset in a GitHub release. + + .DESCRIPTION + This function retrieves the download URL for a specific asset in a GitHub release. + It takes the repository name, version, and a URL match pattern as input parameters. + It searches for releases that match the specified version and then looks + for a download URL that matches the provided pattern. If a matching URL is found, + it returns the URL. If no matching URL is found, an exception is thrown. + + .PARAMETER Repository + The name of the GitHub repository in the format "owner/repo". + + .PARAMETER Version + The version of the release to retrieve. It can be a specific version number, + "latest" to retrieve the latest release, or a wildcard pattern to match multiple versions. + + .PARAMETER AllowPrerelease + Specifies whether to include prerelease versions in the results. By default, + prerelease versions are excluded. + + .PARAMETER UrlMatchPattern + The pattern to match against the download URLs of the release assets. + Wildcards (*) can be used to match any characters. + + .PARAMETER AllowMultipleMatches + Specifies whether to choose one of multiple assets matching the pattern or consider this behavior to be erroneous. + By default, multiple matches are not considered normal behavior and result in an error. + + .EXAMPLE + Resolve-GithubReleaseAssetUrl -Repository "myrepo" -Version "1.0" -UrlMatchPattern "*.zip" + Retrieves the download URL for the asset in the "myrepo" repository with version "1.0" and a file extension of ".zip". + + #> + + param ( + [Parameter(Mandatory = $true)] + [Alias("Repo")] + [string] $Repository, + [string] $Version = "*", + [switch] $AllowPrerelease, + [Parameter(Mandatory = $true)] + [Alias("Pattern", "File", "Asset")] + [string] $UrlMatchPattern, + [switch] $AllowMultipleMatches = $false + ) + + $matchingReleases = Get-GithubReleasesByVersion ` + -Repository $Repository ` + -AllowPrerelease:$AllowPrerelease ` + -Version $Version ` + -WithAssetsOnly + + # Add wildcard to the beginning of the pattern if it's not there + if ($UrlMatchPattern.Substring(0, 2) -ne "*/") { + $UrlMatchPattern = "*/$UrlMatchPattern" + } + + # Loop over releases until we find a download url matching the pattern + foreach ($release in $matchingReleases) { + $matchedVersion = $release.version + $matchedUrl = ([string[]] $release.assets.browser_download_url) -like $UrlMatchPattern + if ($matchedUrl) { + break + } + } + + if (-not $matchedUrl) { + Write-Debug "Found no download urls matching pattern ${UrlMatchPattern}" + Write-Debug "Available download urls:`n$($matchingReleases.assets.browser_download_url -join "`n")" + throw "No assets found in ${Repository} matching version `"${Version}`" and pattern `"${UrlMatchPattern}`"" + } + # If multiple urls match the pattern, sort them and take the last one + # Will only work with simple number series of no more than nine in a row. + if ($matchedUrl.Count -gt 1) { + if ($AllowMultipleMatches) { + Write-Debug "Found multiple download urls matching pattern ${UrlMatchPattern}:`n$($matchedUrl -join "`n")" + Write-Host "Performing sorting of urls to find the most recent version matching the pattern" + $matchedUrl = $matchedUrl | Sort-Object -Descending + $matchedUrl = $matchedUrl[0] + } else { + throw "Found multiple assets in ${Repository} matching version `"${Version}`" and pattern `"${UrlMatchPattern}`".`nAvailable assets:`n$($matchedUrl -join "`n")" + } + } + + Write-Host "Found download url for ${Repository} version ${matchedVersion}: ${matchedUrl}" + + return ($matchedUrl -as [string]) +} + +function Get-ChecksumFromGithubRelease { + <# + .SYNOPSIS + Retrieves the hash value of a specific file from a GitHub release body. + + .DESCRIPTION + The Get-ChecksumFromGithubRelease function retrieves the hash value (SHA256 or SHA512) + of a specific file from a GitHub release. It searches for the file in the release body + and returns the hash value if found. + + .PARAMETER Repository + The name of the GitHub repository in the format "owner/repo". + + .PARAMETER Version + The version of the release to inspect. It can be a specific version number, + "latest" to retrieve the latest release, or a wildcard pattern to match multiple versions. + + .PARAMETER AllowPrerelease + Specifies whether to include prerelease versions in the results. By default, + prerelease versions are excluded. + + .PARAMETER FileName + The name of the file to retrieve the hash value for. + + .PARAMETER HashType + The type of hash value to retrieve. Valid values are "SHA256" and "SHA512". + + .EXAMPLE + Get-ChecksumFromGithubRelease -Repository "MyRepo" -FileName "myfile.txt" -HashType "SHA256" + + Retrieves the SHA256 hash value of "myfile.txt" from the latest release of the "MyRepo" repository. + + .EXAMPLE + Get-ChecksumFromGithubRelease -Repository "MyRepo" -Version "1.0" -FileName "myfile.txt" -HashType "SHA512" + + Retrieves the SHA512 hash value of "myfile.txt" from the release version "1.0" of the "MyRepo" repository. + #> + + param ( + [Parameter(Mandatory = $true)] + [Alias("Repo")] + [string] $Repository, + [string] $Version = "*", + [switch] $AllowPrerelease, + [Parameter(Mandatory = $true)] + [Alias("File", "Asset")] + [string] $FileName, + [Parameter(Mandatory = $true)] + [ValidateSet("SHA256", "SHA512")] + [string] $HashType + ) + + $matchingReleases = Get-GithubReleasesByVersion ` + -Repository $Repository ` + -AllowPrerelease:$AllowPrerelease ` + -Version $Version ` + -WithAssetsOnly + + foreach ($release in $matchingReleases) { + $matchedVersion = $release.version + $matchedBody = $release.body + $matchedLine = $matchedBody.Split("`n") | Where-Object { $_ -like "*$FileName*" } + if ($matchedLine.Count -gt 1) { + throw "Found multiple lines matching file name '${FileName}' in body of release ${matchedVersion}." + } elseif ($matchedLine.Count -ne 0) { + break + } + } + if (-not $matchedLine) { + throw "File name '${FileName}' not found in release body." + } + Write-Debug "Found line matching file name '${FileName}' in body of release ${matchedVersion}:`n${matchedLine}" + + if ($HashType -eq "SHA256") { + $pattern = "[A-Fa-f0-9]{64}" + } elseif ($HashType -eq "SHA512") { + $pattern = "[A-Fa-f0-9]{128}" + } else { + throw "Unknown hash type: ${HashType}" + } + + $hash = $matchedLine | Select-String -Pattern $pattern | ForEach-Object { $_.Matches.Value } + + if ([string]::IsNullOrEmpty($hash)) { + throw "Found '${FileName}' in body of release ${matchedVersion}, but failed to get hash from it.`nLine: ${matchedLine}" + } + Write-Host "Found hash for ${FileName} in release ${matchedVersion}: $hash" + + return $hash +} + +function Get-ChecksumFromUrl { + <# + .SYNOPSIS + Retrieves the checksum hash for a file from a given URL. + + .DESCRIPTION + The Get-ChecksumFromUrl function retrieves the checksum hash for a specified file + from a given URL. It supports SHA256 and SHA512 hash types. + + .PARAMETER Url + The URL of the checksum file. + + .PARAMETER FileName + The name of the file to retrieve the checksum hash for. + + .PARAMETER HashType + The type of hash to retrieve. Valid values are "SHA256" and "SHA512". + + .EXAMPLE + Get-ChecksumFromUrl -Url "https://example.com/checksums.txt" -FileName "file.txt" -HashType "SHA256" + Retrieves the SHA256 checksum hash for the file "file.txt" from the URL "https://example.com/checksums.txt". + #> + + param ( + [Parameter(Mandatory = $true)] + [string] $Url, + [Parameter(Mandatory = $true)] + [Alias("File", "Asset")] + [string] $FileName, + [Parameter(Mandatory = $true)] + [ValidateSet("SHA256", "SHA512")] + [Alias("Type")] + [string] $HashType + ) + + $tempFile = Join-Path -Path $env:TEMP -ChildPath ([System.IO.Path]::GetRandomFileName()) + $checksums = (Invoke-DownloadWithRetry -Url $Url -Path $tempFile | Get-Item | Get-Content) -as [string[]] + Remove-Item -Path $tempFile + + $matchedLine = $checksums | Where-Object { $_ -like "*$FileName*" } + if ($matchedLine.Count -gt 1) { + throw "Found multiple lines matching file name '${FileName}' in checksum file." + } elseif ($matchedLine.Count -eq 0) { + throw "File name '${FileName}' not found in checksum file." + } + + if ($HashType -eq "SHA256") { + $pattern = "[A-Fa-f0-9]{64}" + } elseif ($HashType -eq "SHA512") { + $pattern = "[A-Fa-f0-9]{128}" + } else { + throw "Unknown hash type: ${HashType}" + } + Write-Debug "Found line matching file name '${FileName}' in checksum file:`n${matchedLine}" + + $hash = $matchedLine | Select-String -Pattern $pattern | ForEach-Object { $_.Matches.Value } + if ([string]::IsNullOrEmpty($hash)) { + throw "Found '${FileName}' in checksum file, but failed to get hash from it.`nLine: ${matchedLine}" + } + Write-Host "Found hash for ${FileName} in checksum file: $hash" + + return $hash +} + +function Test-FileChecksum { + <# + .SYNOPSIS + Verifies the checksum of a file. + + .DESCRIPTION + The Test-FileChecksum function verifies the SHA256 or SHA512 checksum of a file against an expected value. + If the checksum does not match the expected value, the function throws an error. + + .PARAMETER Path + The path to the file for which to verify the checksum. + + .PARAMETER ExpectedSHA256Sum + 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. + + .PARAMETER ExpectedSHA512Sum + 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. + + .EXAMPLE + Test-FileChecksum -Path "C:\temp\file.txt" -ExpectedSHA256Sum "ABC123" + + Verifies that the SHA256 checksum of the file at C:\temp\file.txt is ABC123. + + .EXAMPLE + Test-FileChecksum -Path "C:\temp\file.txt" -ExpectedSHA512Sum "DEF456" + + Verifies that the SHA512 checksum of the file at C:\temp\file.txt is DEF456. + + #> + + param ( + [Parameter(Mandatory = $true, Position = 0)] + [string] $Path, + [Parameter(Mandatory = $false)] + [String] $ExpectedSHA256Sum, + [Parameter(Mandatory = $false)] + [String] $ExpectedSHA512Sum + ) + + Write-Verbose "Performing checksum verification" + + if ($ExpectedSHA256Sum -and $ExpectedSHA512Sum) { + throw "Only one of the ExpectedSHA256Sum and ExpectedSHA512Sum parameters can be provided" + } + + if (-not (Test-Path $Path)) { + throw "File not found: $Path" + } + + if ($ExpectedSHA256Sum) { + $fileHash = (Get-FileHash -Path $Path -Algorithm SHA256).Hash + $expectedHash = $ExpectedSHA256Sum + } + + if ($ExpectedSHA512Sum) { + $fileHash = (Get-FileHash -Path $Path -Algorithm SHA512).Hash + $expectedHash = $ExpectedSHA512Sum + } + + if ($fileHash -ne $expectedHash) { + throw "Checksum verification failed: expected $expectedHash, got $fileHash" + } else { + Write-Verbose "Checksum verification passed" + } +} + +function Test-FileSignature { + <# + .SYNOPSIS + Tests the file signature of a given file. + + .DESCRIPTION + The Test-FileSignature function checks the signature of a file against the expected thumbprints. + It uses the Get-AuthenticodeSignature cmdlet to retrieve the signature information of the file. + If the signature status is not valid or the thumbprint does not match the expected thumbprints, an exception is thrown. + + .PARAMETER Path + Specifies the path of the file to test. + + .PARAMETER ExpectedThumbprint + Specifies the expected thumbprints to match against the file's signature. + + .EXAMPLE + Test-FileSignature -Path "C:\Path\To\File.exe" -ExpectedThumbprint "A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0" + + This example tests the signature of the file "C:\Path\To\File.exe" against the expected thumbprint "A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0". + + #> + + param( + [Parameter(Mandatory = $true, Position = 0)] + [string] $Path, + [Parameter(Mandatory = $true)] + [string[]] $ExpectedThumbprint + ) + + $signature = Get-AuthenticodeSignature $Path + + if ($signature.Status -ne "Valid") { + throw "Signature status is not valid. Status: $($signature.Status)" + } + + foreach ($thumbprint in $ExpectedThumbprint) { + if ($signature.SignerCertificate.Thumbprint.Contains($thumbprint)) { + Write-Output "Signature for $Path is valid" + $signatureMatched = $true + return + } + } + + if ($signatureMatched) { + Write-Output "Signature for $Path is valid" + } else { + throw "Signature thumbprint do not match expected. Expected: $ExpectedThumbprint, Actual: $($signature.SignerCertificate.Thumbprint)" + } +} + +function Update-Environment { + <# + .SYNOPSIS + Updates the environment variables by reading values from the registry. + + .DESCRIPTION + This function updates current environment by reading values from the registry. + It is useful when you need to update the environment variables without restarting the current session. + + .NOTES + The function requires administrative privileges to modify the system registry. + #> + + $locations = @( + 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment', + 'HKCU:\Environment' + ) + + # Update PATH variable + $pathItems = $locations | ForEach-Object { + (Get-Item $_).GetValue('PATH').Split(';') + } | Select-Object -Unique + $env:PATH = $pathItems -join ';' + + # Update other variables + $locations | ForEach-Object { + $key = Get-Item $_ + foreach ($name in $key.GetValueNames()) { + $value = $key.GetValue($name) + if (-not ($name -ieq 'PATH')) { + Set-Item -Path Env:$name -Value $value + } + } + } +} diff --git a/images/windows/scripts/helpers/PathHelpers.ps1 b/images/windows/scripts/helpers/PathHelpers.ps1 new file mode 100644 index 0000000..1ddb9bc --- /dev/null +++ b/images/windows/scripts/helpers/PathHelpers.ps1 @@ -0,0 +1,137 @@ + +function Mount-RegistryHive { + <# + .SYNOPSIS + Mounts a registry hive from a file. + + .DESCRIPTION + The Mount-RegistryHive function loads a registry hive from a specified file into a specified subkey. + + .PARAMETER FileName + The path to the file from which to load the registry hive. + + .PARAMETER SubKey + The registry subkey into which to load the hive. + + .EXAMPLE + Mount-RegistryHive -FileName "C:\Path\To\HiveFile.hiv" -SubKey "HKLM\SubKey" + #> + param( + [Parameter(Mandatory = $true)] + [string] $FileName, + [Parameter(Mandatory = $true)] + [string] $SubKey + ) + + Write-Host "Loading the file $FileName to the Key $SubKey" + if (Test-Path $SubKey.Replace("\", ":")) { + Write-Warning "The key $SubKey is already loaded" + return + } + + $result = reg load $SubKey $FileName *>&1 + if ($LASTEXITCODE -ne 0) { + throw "Failed to load file $FileName to the key ${SubKey}: $result" + } +} + +function Dismount-RegistryHive { + <# + .SYNOPSIS + Dismounts a registry hive. + + .DESCRIPTION + The Dismount-RegistryHive function unloads a registry hive from a specified subkey. + + .PARAMETER SubKey + The registry subkey from which to unload the hive. + + .EXAMPLE + Dismount-RegistryHive -SubKey "HKLM\SubKey" + #> + param( + [Parameter(Mandatory = $true)] + [string] $SubKey + ) + + Write-Host "Unloading the hive $SubKey" + if (-not (Test-Path $SubKey.Replace("\", ":"))) { + return + } + + $result = reg unload $SubKey *>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host "Failed to unload hive: $result" + exit 1 + } +} + +function Add-MachinePathItem { + <# + .SYNOPSIS + Adds a new item to the machine-level PATH environment variable. + + .DESCRIPTION + The Add-MachinePathItem function adds a new item to the machine-level PATH environment variable. + It takes a string parameter, $PathItem, which represents the new item to be added to the PATH. + + .PARAMETER PathItem + Specifies the new item to be added to the machine-level PATH environment variable. + + .EXAMPLE + Add-MachinePathItem -PathItem "C:\Program Files\MyApp" + + This example adds "C:\Program Files\MyApp" to the machine-level PATH environment variable. + #> + + param( + [Parameter(Mandatory = $true)] + [string] $PathItem + ) + + $currentPath = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + $newPath = $PathItem + ';' + $currentPath + [Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine") +} + +function Add-DefaultPathItem { + <# + .SYNOPSIS + Adds a path item to the default user profile path. + + .DESCRIPTION + This function adds a specified path item to the default user profile path. + It mounts the NTUSER.DAT file of the default user to the registry, + retrieves the current value of the "Path" environment variable, + appends the new path item to it, and updates the registry with the modified value. + + .PARAMETER PathItem + The path item to be added to the default user profile path. + + .EXAMPLE + Add-DefaultPathItem -PathItem "C:\Program Files\MyApp" + + This example adds "C:\Program Files\MyApp" to the default user profile path. + + .NOTES + This function requires administrative privileges to modify the Windows registry. + #> + + param( + [Parameter(Mandatory = $true)] + [string] $PathItem + ) + + Mount-RegistryHive ` + -FileName "C:\Users\Default\NTUSER.DAT" ` + -SubKey "HKLM\DEFAULT" + + $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("DEFAULT\Environment", $true) + $currentValue = $key.GetValue("Path", "", "DoNotExpandEnvironmentNames") + $updatedValue = $PathItem + ';' + $currentValue + $key.SetValue("Path", $updatedValue, "ExpandString") + $key.Handle.Close() + [System.GC]::Collect() + + Dismount-RegistryHive "HKLM\DEFAULT" +} diff --git a/images/windows/scripts/helpers/VisualStudioHelpers.ps1 b/images/windows/scripts/helpers/VisualStudioHelpers.ps1 new file mode 100644 index 0000000..da7b3da --- /dev/null +++ b/images/windows/scripts/helpers/VisualStudioHelpers.ps1 @@ -0,0 +1,360 @@ +Function Install-VisualStudio { + <# + .SYNOPSIS + A helper function to install Visual Studio. + + .DESCRIPTION + Prepare system environment, and install Visual Studio bootstrapper with selected workloads. + + .PARAMETER Version + The version of Visual Studio that will be installed. Required parameter. + + .PARAMETER Edition + The edition of Visual Studio that will be installed. Required parameter. + + .PARAMETER Channel + The channel of Visual Studio that will be installed. Required parameter. + + .PARAMETER RequiredComponents + The list of required components. Required parameter. + + .PARAMETER ExtraArgs + The extra arguments to pass to the bootstrapper. Optional parameter. + #> + + Param + ( + [Parameter(Mandatory)] [String] $Version, + [Parameter(Mandatory)] [String] $Edition, + [Parameter(Mandatory)] [String] $Channel, + [Parameter(Mandatory)] [String[]] $RequiredComponents, + [String] $ExtraArgs = "", + [Parameter(Mandatory)] [String[]] $SignatureThumbprint + ) + + $bootstrapperUrl = "https://aka.ms/vs/${Version}/${Channel}/vs_${Edition}.exe" + $channelUri = "https://aka.ms/vs/${Version}/${Channel}/channel" + $channelId = "VisualStudio.${Version}.Release" + $productId = "Microsoft.VisualStudio.Product.${Edition}" + + Write-Host "Downloading Bootstrapper ..." + $bootstrapperFilePath = Invoke-DownloadWithRetry $BootstrapperUrl + + # Verify that the bootstrapper is signed by Microsoft + Test-FileSignature -Path $bootstrapperFilePath -ExpectedThumbprint $SignatureThumbprint + + try { + $responseData = @{ + "channelUri" = $channelUri + "channelId" = $channelId + "productId" = $productId + "arch" = "x64" + "add" = $RequiredComponents | ForEach-Object { "$_;includeRecommended" } + } + + # Create json file with response data + $responseDataPath = "$env:TEMP\vs_install_response.json" + $responseData | ConvertTo-Json | Out-File -FilePath $responseDataPath + + $installStartTime = Get-Date + Write-Host "Starting Install ..." + $bootstrapperArgumentList = ('/c', $bootstrapperFilePath, '--in', $responseDataPath, $ExtraArgs, '--quiet', '--norestart', '--wait', '--nocache' ) + Write-Host "Bootstrapper arguments: $bootstrapperArgumentList" + $process = Start-Process -FilePath cmd.exe -ArgumentList $bootstrapperArgumentList -Wait -PassThru + + $exitCode = $process.ExitCode + $installCompleteTime = [math]::Round(($(Get-Date) - $installStartTime).TotalSeconds, 2) + if ($exitCode -eq 0) { + Write-Host "Installation successful in $installCompleteTime seconds" + return $exitCode + } elseif ($exitCode -eq 3010) { + Write-Host "Installation successful in $installCompleteTime seconds. Reboot is required." + return $exitCode + } else { + Write-Host "Non zero exit code returned by the installation process : $exitCode" + + # Try to download tool to collect logs + $collectExeUrl = "https://aka.ms/vscollect.exe" + $collectExePath = Invoke-DownloadWithRetry -Url $collectExeUrl + + # Collect installation logs using the collect.exe tool and check if it is successful + & "$collectExePath" + if ($LastExitCode -ne 0) { + Write-Host "Failed to collect logs using collect.exe tool. Exit code : $LastExitCode" + exit $exitCode + } + + # Expand the zip file + Expand-Archive -Path "$env:TEMP\vslogs.zip" -DestinationPath "$env:TEMP\vslogs" + + # Print logs + $vsLogsPath = "$env:TEMP\vslogs" + $vsLogs = Get-ChildItem -Path $vsLogsPath -Recurse | Where-Object { -not $_.PSIsContainer } | Select-Object -ExpandProperty FullName + foreach ($log in $vsLogs) { + Write-Host "============================" + Write-Host "== Log file : $log " + Write-Host "============================" + Get-Content -Path $log -ErrorAction Continue + } + + exit $exitCode + } + } + catch + { + Write-Host "Failed to install Visual Studio; $($_.Exception.Message)" + exit -1 + } +} + +function Get-VisualStudioInstance { + <# + .SYNOPSIS + Retrieves the Visual Studio instance. + + .DESCRIPTION + This function retrieves the Visual Studio instance + using the Get-VSSetupInstance cmdlet. + It searches for both regular and preview versions + of Visual Studio and returns the first instance found. + #> + + # Use -Prerelease and -All flags to make sure that Preview versions of VS are found correctly + $vsInstance = Get-VSSetupInstance -Prerelease -All | Where-Object { $_.DisplayName -match "Visual Studio" } | Select-Object -First 1 + $vsInstance | Select-VSSetupInstance -Product * +} + +function Get-VisualStudioComponents { + <# + .SYNOPSIS + Retrieves the Visual Studio components. + + .DESCRIPTION + This function retrieves the Visual Studio components + by filtering the packages returned by Get-VisualStudioInstance cmdlet. + It filters the packages based on their type, sorts them by Id and Version, + and excludes packages with GUID-like Ids. + #> + + (Get-VisualStudioInstance).Packages ` + | Where-Object type -in 'Component', 'Workload' ` + | Sort-Object Id, Version ` + | Select-Object @{n = 'Package'; e = { $_.Id } }, Version ` + | 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}" } +} + +function Get-VsixInfoFromMarketplace { + <# + .SYNOPSIS + Retrieves information about a Visual Studio extension from the Visual Studio Marketplace. + + .DESCRIPTION + The Get-VsixInfoFromMarketplace function retrieves information + about a Visual Studio extension from the Visual Studio Marketplace. + It takes the name of the extension as input and returns + the extension's name, VsixId, filename, and download URI. + + .PARAMETER Name + The name of the Visual Studio extension. + + .PARAMETER MarketplaceUri + The URI of the Visual Studio Marketplace. + Default value is "https://marketplace.visualstudio.com/items?itemName=". + + .EXAMPLE + Get-VsixInfoFromMarketplace -Name "ProBITools.MicrosoftReportProjectsforVisualStudio2022" + Retrieves information about the "ProBITools.MicrosoftReportProjectsforVisualStudio2022" extension + from the Visual Studio Marketplace. + #> + + Param + ( + [Parameter(Mandatory)] + [Alias("ExtensionMarketPlaceName")] + [string] $Name, + [string] $MarketplaceUri = "https://marketplace.visualstudio.com/items?itemName=" + ) + + # Invoke-WebRequest doesn't support retry in PowerShell 5.1 + $webResponse = Invoke-ScriptBlockWithRetry -RetryCount 20 -RetryIntervalSeconds 30 -Command { + Invoke-WebRequest -Uri "${MarketplaceUri}${Name}" -UseBasicParsing + } + + $webResponse -match 'UniqueIdentifierValue":"(?[^"]*)' | Out-Null + $extensionName = $Matches.extensionname + + $webResponse -match 'VsixId":"(?[^"]*)' | Out-Null + $vsixId = $Matches.vsixid + + $webResponse -match 'AssetUri":"(?[^"]*)' | Out-Null + $assetUri = $Matches.uri + + $webResponse -match 'Microsoft\.VisualStudio\.Services\.Payload\.FileName":"(?[^"]*)' | Out-Null + $fileName = $Matches.filename + + switch ($Name) { + # ProBITools.MicrosoftReportProjectsforVisualStudio2022 has different URL + # https://github.com/actions/runner-images/issues/5340 + "ProBITools.MicrosoftReportProjectsforVisualStudio2022" { + $assetUri = "https://download.microsoft.com/download/b/b/5/bb57be7e-ae72-4fc0-b528-d0ec224997bd" + $fileName = "Microsoft.DataTools.ReportingServices.vsix" + } + "ProBITools.MicrosoftAnalysisServicesModelingProjects2022" { + $assetUri = "https://download.microsoft.com/download/c/8/9/c896a7f2-d0fd-45ac-90e6-ff61f67523cb" + $fileName = "Microsoft.DataTools.AnalysisServices.vsix" + } + + # Starting from version 4.1 SqlServerIntegrationServicesProjects extension is distributed as exe file + "SSIS.SqlServerIntegrationServicesProjects" { + $fileName = "Microsoft.DataTools.IntegrationServices.exe" + } + } + + $downloadUri = $assetUri + "/" + $fileName + + return [PSCustomObject] @{ + "ExtensionName" = $extensionName + "VsixId" = $vsixId + "FileName" = $fileName + "DownloadUri" = $downloadUri + } +} + +function Install-VSIXFromFile { + <# + .SYNOPSIS + Installs a Visual Studio Extension (VSIX) from a file. + + .DESCRIPTION + This function installs a Visual Studio Extension (VSIX) + from the specified file path. It uses the VSIXInstaller.exe + tool provided by Microsoft Visual Studio. + + .PARAMETER FilePath + The path to the VSIX file that needs to be installed. + + .PARAMETER Retries + The number of retries to attempt if the installation fails. Default is 20. + + .EXAMPLE + Install-VSIXFromFile -FilePath "C:\Extensions\MyExtension.vsix" -Retries 10 + Installs the VSIX file located at "C:\Extensions\MyExtension.vsix" with 10 retries in case of failure. + #> + Param + ( + [Parameter(Mandatory = $true)] + [string] $FilePath, + [int] $Retries = 20 + ) + + Write-Host "Installing VSIX from $FilePath..." + while ($True) { + $installerPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\resources\app\ServiceHub\Services\Microsoft.VisualStudio.Setup.Service\VSIXInstaller.exe" + try { + $process = Start-Process ` + -FilePath $installerPath ` + -ArgumentList @('/quiet', "`"$FilePath`"") ` + -Wait -PassThru + } catch { + Write-Host "Failed to start VSIXInstaller.exe with error:" + $_ + exit 1 + } + + $exitCode = $process.ExitCode + + if ($exitCode -eq 0) { + Write-Host "VSIX installed successfully." + break + } elseif ($exitCode -eq 1001) { + Write-Host "VSIX is already installed." + break + } + + Write-Host "VSIX installation failed with exit code $exitCode." + + $Retries-- + if ($Retries -eq 0) { + Write-Host "VSIX installation failed after $Retries retries." + exit 1 + } + + Write-Host "Waiting 10 seconds before retrying. Retries left: $Retries" + Start-Sleep -Seconds 10 + } +} + +function Install-VSIXFromUrl { + <# + .SYNOPSIS + Installs a Visual Studio extension (VSIX) from a given URL. + + .DESCRIPTION + This function downloads a Visual Studio extension (VSIX) + from the specified URL and installs it. + + .PARAMETER Url + The URL of the VSIX file to download and install. + + .PARAMETER Retries + The number of retries to attempt if the download fails. Default is 20. + + .EXAMPLE + Install-VSIXFromUrl -Url "https://example.com/extension.vsix" -Retries 10 + Downloads and installs the VSIX file from the specified URL with 10 retries. + #> + + Param + ( + [Parameter(Mandatory = $true)] + [string] $Url, + [int] $Retries = 20 + ) + + $filePath = Invoke-DownloadWithRetry $Url + Install-VSIXFromFile -FilePath $filePath -Retries $Retries + Remove-Item -Force -Confirm:$false $filePath +} + +function Get-VSExtensionVersion { + <# + .SYNOPSIS + Retrieves the version of a Visual Studio extension package. + + .DESCRIPTION + This function retrieves the version of a specified Visual Studio extension package. + It searches for the package in the installed instances of Visual Studio and + returns the version number. + + .PARAMETER packageName + The name of the extension package. + + .EXAMPLE + Get-VSExtensionVersion -packageName "MyExtensionPackage" + Retrieves the version of the extension package named "MyExtensionPackage" for Visual Studio. + #> + Param + ( + [Parameter(Mandatory = $true)] + [string] $packageName + ) + + $instanceFolders = Get-ChildItem -Path "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances" + if ($instanceFolders -is [array]) { + Write-Host ($instanceFolders | Out-String) + Write-Host ($instanceFolders | Get-ChildItem | Out-String) + Write-Host "More than one instance installed" + exit 1 + } + + $stateContent = Get-Content -Path (Join-Path $instanceFolders.FullName '\state.packages.json') + $state = $stateContent | ConvertFrom-Json + $packageVersion = ($state.packages | Where-Object { $_.id -eq $packageName }).version + + if (-not $packageVersion) { + Write-Host "Installed package $packageName for Visual Studio was not found" + exit 1 + } + + return $packageVersion +} diff --git a/images/windows/scripts/helpers/test/ImageHelpers.Tests.ps1 b/images/windows/scripts/helpers/test/ImageHelpers.Tests.ps1 new file mode 100644 index 0000000..74af909 --- /dev/null +++ b/images/windows/scripts/helpers/test/ImageHelpers.Tests.ps1 @@ -0,0 +1,13 @@ +$ModuleManifestName = 'ImageHelpers.psd1' +$ModuleManifestPath = "$PSScriptRoot\..\$ModuleManifestName" + + + +Describe 'Module Manifest Tests' { + It 'Passes Test-ModuleManifest' { + Test-ModuleManifest -Path $ModuleManifestPath | Should Not BeNullOrEmpty + $? | Should Be $true + } +} + + diff --git a/images/windows/scripts/tests/ActionArchiveCache.Tests.ps1 b/images/windows/scripts/tests/ActionArchiveCache.Tests.ps1 new file mode 100644 index 0000000..e0bb711 --- /dev/null +++ b/images/windows/scripts/tests/ActionArchiveCache.Tests.ps1 @@ -0,0 +1,15 @@ +Describe "ActionArchiveCache" { + Context "Action archive cache directory not empty" { + It "C:\actionarchivecache not empty" { + (Get-ChildItem -Path "C:\actionarchivecache\*.zip" -Recurse).Count | Should -BeGreaterThan 0 + } + } + + Context "Action zipball not empty" { + $testCases = Get-ChildItem -Path "C:\actionarchivecache\*.zip" -Recurse | ForEach-Object { @{ ActionZipball = $_.FullName } } + It "" -TestCases $testCases { + param ([string] $ActionZipball) + (Get-Item "$ActionZipball").Length | Should -BeGreaterThan 0 + } + } +} diff --git a/images/windows/scripts/tests/Android.Tests.ps1 b/images/windows/scripts/tests/Android.Tests.ps1 new file mode 100644 index 0000000..2716b87 --- /dev/null +++ b/images/windows/scripts/tests/Android.Tests.ps1 @@ -0,0 +1,71 @@ +Describe "Android SDK" { + $androidToolset = (Get-ToolsetContent).android + $androidInstalledPackages = Get-AndroidInstalledPackages + + $platformList = Get-AndroidPlatformPackages -minVersion $androidToolset.platform_min_version + $platformTestCases = $platformList | ForEach-Object { + @{ platformVersion = $_; installedPackages = $androidInstalledPackages } + } + + $buildToolsList = Get-AndroidBuildToolPackages -minVersion $androidToolset.build_tools_min_version + $buildToolsTestCases = $buildToolsList | ForEach-Object { + @{ buildToolsVersion = $_; installedPackages = $androidInstalledPackages } + } + + $extraPackagesTestCases = $androidToolset.extra_list | ForEach-Object { + @{ extraPackage = $_; installedPackages = $androidInstalledPackages } + } + + $addonsTestCases = $androidToolset.addon_list | ForEach-Object { + @{ addonPackage = $_; installedPackages = $androidInstalledPackages } + } + + $additionalToolsTestCases = $androidToolset.additional_tools | ForEach-Object { + @{ additionalToolVersion = $_; installedPackages = $androidInstalledPackages } + } + + $ndkPackagesTestCases = $androidToolset.ndk.versions | ForEach-Object { + @{ ndkPackage = $_; installedPackages = $androidInstalledPackages } + } + + Context "SDKManagers" { + $testCases = @( + @{ + PackageName = "Command-line tools" + Sdkmanager = "$env:ANDROID_HOME\cmdline-tools\latest\bin\sdkmanager.bat" + } + ) + + It "Sdkmanager from is available" -TestCases $testCases { + "$Sdkmanager --list" | Should -ReturnZeroExitCode + } + } + + Context "Packages" { + It "Platform version is installed" -TestCases $platformTestCases { + "$installedPackages" | Should -Match "$platformVersion" + } + + It "Platform build tools is installed" -TestCases $buildToolsTestCases { + "$installedPackages" | Should -Match "$buildToolsVersion" + } + + if (Test-IsWin19) { + It "Extra package is installed" -TestCases $extraPackagesTestCases { + "$installedPackages" | Should -Match "extras;$extraPackage" + } + + It "Addon package is installed" -TestCases $addonsTestCases { + "$installedPackages" | Should -Match "add-ons;$addonPackage" + } + } + + It "Additional tool is installed" -TestCases $additionalToolsTestCases { + "$installedPackages" | Should -Match $additionalToolVersion + } + + It "NDK is installed" -TestCases $ndkPackagesTestCases { + "$installedPackages" | Should -Match "ndk;$ndkPackage" + } + } +} diff --git a/images/windows/scripts/tests/Apache.Tests.ps1 b/images/windows/scripts/tests/Apache.Tests.ps1 new file mode 100644 index 0000000..b8efe9c --- /dev/null +++ b/images/windows/scripts/tests/Apache.Tests.ps1 @@ -0,0 +1,25 @@ +Describe "Apache" { + Context "Path" { + It "Apache" { + $apachePath = Join-Path (Join-Path "C:\tools\" (Get-Item C:\tools\apache*).Name) "\bin\httpd" + "$apachePath -V" | Should -ReturnZeroExitCode + } + } + + Context "Service" { + $apacheService = Get-Service -Name Apache + $apacheServiceTests = @{ + Name = $apacheService.Name + Status = $apacheService.Status + StartType = $apacheService.StartType + } + + It " service is stopped" -TestCases $apacheServiceTests { + $Status | Should -Be "Stopped" + } + + It " service is disabled" -TestCases $apacheServiceTests { + $StartType | Should -Be "Disabled" + } + } +} diff --git a/images/windows/scripts/tests/BizTalk.Tests.ps1 b/images/windows/scripts/tests/BizTalk.Tests.ps1 new file mode 100644 index 0000000..0a3860c --- /dev/null +++ b/images/windows/scripts/tests/BizTalk.Tests.ps1 @@ -0,0 +1,19 @@ +################################################################################ +## File: BizTalk.Tests.ps1 +## Desc: Test BizTalk project build component installed. +################################################################################ + +Describe "BizTalk Build Component Setup" -Skip:(-not (Test-IsWin19)) { + It "BizTalk Registry Check" { + Test-Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\BizTalk Server\3.0" | Should -BeTrue + } + + It "BizTalk Folder Check" { + "${env:ProgramFiles(x86)}\Microsoft BizTalk Server" | Should -Exist + } + + It "BizTalk Build Targets files Check" { + "${env:ProgramFiles(x86)}\MSBuild\Microsoft\BizTalk\BizTalkC.targets" | Should -Exist + "${env:ProgramFiles(x86)}\MSBuild\Microsoft\BizTalk\BizTalkCommon.targets" | Should -Exist + } +} \ No newline at end of file diff --git a/images/windows/scripts/tests/Browsers.Tests.ps1 b/images/windows/scripts/tests/Browsers.Tests.ps1 new file mode 100644 index 0000000..21b30d6 --- /dev/null +++ b/images/windows/scripts/tests/Browsers.Tests.ps1 @@ -0,0 +1,156 @@ +Describe "Chrome" { + Context "WebDriver" { + It "ChromeWebDriver environment variable and path exists" { + $env:ChromeWebDriver | Should -Not -BeNullOrEmpty + $env:ChromeWebDriver | Should -BeExactly "C:\SeleniumWebDrivers\ChromeDriver" + $env:ChromeWebDriver | Should -Exist + } + + It "chromedriver.exe is installed" { + "$env:ChromeWebDriver\chromedriver.exe --version" | Should -ReturnZeroExitCode + } + + It "versioninfo.txt exists" { + "$env:ChromeWebDriver\versioninfo.txt" | Should -Exist + } + } + + Context "Browser" { + $chromeRegPath = "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe" + $chromePath = (Get-ItemProperty $chromeRegPath).'(default)' + + It "Chrome '' registry path exists" -TestCases @{chromeRegPath = $chromeRegPath} { + $chromeRegPath | Should -Exist + } + + It "Chrome VersionInfo registry value exists" -TestCases @{chromePath = $chromePath} { + $versionInfo = (Get-Item $chromePath).VersionInfo + $versionInfo | Should -Not -BeNullOrEmpty + } + + It "GoogleUpdater services is stopped" { + $services = Get-Service -Name "GoogleUpdater*" + foreach ($svc in $services) { + $svc.Status | Should -BeExactly 'Stopped' + } + } + + It "BlockGoogleUpdate firewall rule exists" { + Get-NetFirewallRule -DisplayName BlockGoogleUpdate | Should -Not -BeNullOrEmpty + } + + It " is installed" -TestCases @{chromePath = $chromePath} { + $chromeName = (Get-Item $chromePath).Name + $chromePath | Should -Exist + $chromeName | Should -BeExactly "chrome.exe" + } + + It "Chrome and Chrome Driver major versions are the same" -TestCases @{chromePath = $chromePath} { + $chromeMajor = (Get-Item $chromePath).VersionInfo.ProductMajorPart + $chromeDriverMajor = (Get-Content $env:ChromeWebDriver\versioninfo.txt).Split(".")[0] + $chromeMajor | Should -BeExactly $chromeDriverMajor + } + } +} + +Describe "Edge" { + Context "WebDriver" { + It "EdgeWebDriver environment variable and path exists" { + $env:EdgeWebDriver | Should -Not -BeNullOrEmpty + $env:EdgeWebDriver | Should -BeExactly "C:\SeleniumWebDrivers\EdgeDriver" + $env:EdgeWebDriver | Should -Exist + } + + It "msedgedriver.exe is installed" { + "$env:EdgeWebDriver\msedgedriver.exe --version" | Should -ReturnZeroExitCode + } + + It "versioninfo.txt exists" { + "$env:EdgeWebDriver\versioninfo.txt" | Should -Exist + } + } + + Context "Browser" { + $edgeRegPath = "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\msedge.exe" + + It "Edge '' registry path exists" -TestCases @{edgeRegPath = $edgeRegPath} { + $edgeRegPath | Should -Exist + } + + It "Edge VersionInfo registry value exists" -TestCases @{edgeRegPath = $edgeRegPath} { + $versionInfo = (Get-Item (Get-ItemProperty $edgeRegPath).'(Default)').VersionInfo + $versionInfo | Should -Not -BeNullOrEmpty + } + + It "msedge.exe is installed" { + "${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe" | Should -Exist + } + } +} + +Describe "Firefox" { + Context "WebDriver" { + It "GeckoWebDriver environment variable and path exists" { + $env:GeckoWebDriver | Should -Not -BeNullOrEmpty + $env:GeckoWebDriver | Should -BeExactly "C:\SeleniumWebDrivers\GeckoDriver" + $env:GeckoWebDriver | Should -Exist + } + + It "geckodriver.exe is installed" { + "$env:GeckoWebDriver\geckodriver.exe --version" | Should -ReturnZeroExitCode + } + + It "versioninfo.txt exists" { + "$env:GeckoWebDriver\versioninfo.txt" | Should -Exist + } + } + + Context "Browser" { + $firefoxRegPath = "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\firefox.exe" + + It "Firefox '' registry path exists" -TestCases @{firefoxRegPath = $firefoxRegPath} { + $firefoxRegPath | Should -Exist + } + + It "Firefox VersionInfo registry value exists" -TestCases @{firefoxRegPath = $firefoxRegPath} { + $versionInfo = (Get-Item (Get-ItemProperty $firefoxRegPath).'(Default)').VersionInfo + $versionInfo | Should -Not -BeNullOrEmpty + } + + It "firefox.exe is installed" { + "$env:ProgramFiles\Mozilla Firefox\firefox.exe" | Should -Exist + } + } +} + +Describe "Internet Explorer" { + Context "WebDriver" { + It "IEWebDriver environment variable and path exists" { + $env:IEWebDriver | Should -Not -BeNullOrEmpty + $env:IEWebDriver | Should -BeExactly "C:\SeleniumWebDrivers\IEDriver" + $env:IEWebDriver | Should -Exist + } + + It "iedriverserver.exe is installed" { + "$env:IEWebDriver\IEDriverServer.exe --version" | Should -ReturnZeroExitCode + } + + It "versioninfo.txt exists" { + "$env:IEWebDriver\versioninfo.txt" | Should -Exist + } + } +} + +Describe "Selenium" { + BeforeAll { + $seleniumBinPath = "C:\selenium\selenium-server.jar" + } + + It "Selenium server is installed" { + $seleniumBinPath | Should -Exist + } + + It "SELENIUM_JAR_PATH environment variable exists" { + Get-EnvironmentVariable "SELENIUM_JAR_PATH" | Should -BeExactly "$seleniumBinPath" + } +} diff --git a/images/windows/scripts/tests/CLI.Tools.Tests.ps1 b/images/windows/scripts/tests/CLI.Tools.Tests.ps1 new file mode 100644 index 0000000..e2c2e50 --- /dev/null +++ b/images/windows/scripts/tests/CLI.Tools.Tests.ps1 @@ -0,0 +1,50 @@ + +Describe "Azure CLI" { + It "Azure CLI" { + "az --version" | Should -ReturnZeroExitCode + } +} + +Describe "Azure DevOps CLI" { + It "az devops" { + "az devops -h" | Should -ReturnZeroExitCode + } +} + +Describe "Aliyun CLI" -Skip: (Test-IsWin25){ + It "Aliyun CLI" { + "aliyun version" | Should -ReturnZeroExitCode + } +} + + +Describe "AWS" { + It "AWS CLI" { + "aws --version" | Should -ReturnZeroExitCode + } + + It "Session Manager Plugin for the AWS CLI" { + @(session-manager-plugin) -Match '\S' | Out-String | Should -Match "plugin was installed successfully" + } + + It "AWS SAM CLI" { + "sam --version" | Should -ReturnZeroExitCode + } +} + + +Describe "GitHub CLI" { + It "gh" { + "gh --version" | Should -ReturnZeroExitCode + } +} + +Describe "CloudFoundry CLI" -Skip:(-not (Test-IsWin19)) { + It "cf is located in C:\cf-cli" { + "C:\cf-cli\cf.exe" | Should -Exist + } + + It "cf" { + "cf --version" | Should -ReturnZeroExitCode + } +} diff --git a/images/windows/scripts/tests/ChocoPackages.Tests.ps1 b/images/windows/scripts/tests/ChocoPackages.Tests.ps1 new file mode 100644 index 0000000..3dea5a1 --- /dev/null +++ b/images/windows/scripts/tests/ChocoPackages.Tests.ps1 @@ -0,0 +1,115 @@ +Describe "7-Zip" { + It "7z" { + "7z" | Should -ReturnZeroExitCode + } +} + +Describe "Aria2" { + It "Aria2" { + "aria2c --version" | Should -ReturnZeroExitCode + } +} + +Describe "AzCopy" { + It "AzCopy" { + "azcopy --version" | Should -ReturnZeroExitCode + } +} + +Describe "Bicep" { + It "Bicep" { + "bicep --version" | Should -ReturnZeroExitCode + } +} +# This step is throwing the below error +# CommandNotFoundException: The term 'Test-Is' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. +# 2024-10-25T06:18:52.1215022Z azure-arm.image: at , C:\image\tests\ChocoPackages.Tests.ps1:25 +# 2024-10-25T06:18:53.4267125Z azure-arm.image: Tests completed in 9.83s +# 2024-10-25T06:18:53.4268976Z azure-arm.image: Tests Passed: 4, Failed: 0, Skipped: 0, Inconclusive: 0, NotRun: 0 +# 2024-10-25T06:18:53.4270525Z azure-arm.image: Container failed: 1 +# 2024-10-25T06:18:53.4271341Z azure-arm.image: - C:\image\tests\ChocoPackages.Tests.ps1 +# 2024-10-25T06:18:53.6810753Z ==> azure-arm.image: Provisioning step had errors: Running the cleanup provisioner, if present... +# 2024-10-25T06:18:53.6819141Z ==> azure-arm.image: +# Link to the run - https://github.com/actions/runner-images-ci/actions/runs/11511659781/job/32045443262 +# ****************************************************************************** +# Describe "GitVersion" -Skip:(Test-Is -or Test-IsWin25) { +# It "gitversion is installed" { +# "gitversion /version" | Should -ReturnZeroExitCode +# } +# } + +Describe "InnoSetup" -Skip: (Test-IsWin25){ + It "InnoSetup" { + (Get-Command -Name iscc).CommandType | Should -BeExactly "Application" + } +} + +Describe "Jq" { + It "Jq" { + "jq -n ." | Should -ReturnZeroExitCode + } +} + +Describe "Nuget" { + It "Nuget" { + "nuget" | Should -ReturnZeroExitCode + } +} + +Describe "Packer" { + It "Packer" { + "packer --version" | Should -ReturnZeroExitCode + } +} + +Describe "Perl" { + It "Perl" { + "perl --version" | Should -ReturnZeroExitCode + } +} + +Describe "Pulumi" { + It "pulumi" { + "pulumi version" | Should -ReturnZeroExitCode + } +} + +Describe "Svn" -Skip: (Test-IsWin25) { + It "svn" { + "svn --version --quiet" | Should -ReturnZeroExitCode + } +} + +Describe "Swig" { + It "Swig" { + "swig -version" | Should -ReturnZeroExitCode + } +} + +Describe "VSWhere" { + It "vswhere" { + "vswhere" | Should -ReturnZeroExitCode + } +} + +Describe "Julia" { + It "Julia path exists" { + "C:\Julia" | Should -Exist + } + + It "Julia" { + "julia --version" | Should -ReturnZeroExitCode + } +} + +Describe "CMake" { + It "cmake" { + "cmake --version" | Should -ReturnZeroExitCode + } +} + +Describe "ImageMagick" { + It "ImageMagick" { + "magick -version" | Should -ReturnZeroExitCode + } +} diff --git a/images/windows/scripts/tests/Databases.Tests.ps1 b/images/windows/scripts/tests/Databases.Tests.ps1 new file mode 100644 index 0000000..fa2ba69 --- /dev/null +++ b/images/windows/scripts/tests/Databases.Tests.ps1 @@ -0,0 +1,98 @@ +Describe "MongoDB" { + Context "Version" { + It "" -TestCases @( + if(Test-IsWin25) { + @{ ToolName = "mongos" } + } else { + @{ ToolName = "mongo" } + } + @{ ToolName = "mongod" } + ) { + $toolsetVersion = (Get-ToolsetContent).mongodb.version + (& $ToolName --version)[2].Split('"')[-2] | Should -BeLike "$toolsetVersion*" + } + } + + Context "Service" { + $mongoService = Get-Service -Name mongodb -ErrorAction Ignore + $mongoServiceTests = @{ + Name = $mongoService.Name + Status = $mongoService.Status + StartType = $mongoService.StartType + } + + It " service is stopped" -TestCases $mongoServiceTests { + $Status | Should -Be "Stopped" + } + + It " service is disabled" -TestCases $mongoServiceTests { + $StartType | Should -Be "Disabled" + } + } + + Context "Shell" -Skip:(-not (Test-IsWin19) -or -not (Test-IsWin22)) { + "mongosh --version" | Should -Not -BeNullOrEmpty + } +} + +Describe "PostgreSQL" { + $psqlTests = @( + @{envVar = "PGROOT"; pgPath = Get-EnvironmentVariable "PGROOT" } + @{envVar = "PGBIN"; pgPath = Get-EnvironmentVariable "PGBIN" } + @{envVar = "PGDATA"; pgPath = Get-EnvironmentVariable "PGDATA" } + ) + + Context "Environment variable" { + It "PGUSER contains postgres" { + Get-EnvironmentVariable "PGUSER" | Should -Be "postgres" + } + + It "PGPASSWORD contains root" { + Get-EnvironmentVariable "PGPASSWORD" | Should -Be "root" + } + + It " environment variable exists" -TestCases $psqlTests { + Get-EnvironmentVariable $envVar | Should -Not -BeNullOrEmpty + } + } + + Context "Path" { + It " path exists" -TestCases $psqlTests { + $pgPath | Should -Exist + } + } + + Context "Service" { + $psqlService = Get-Service -Name postgresql* + $psqlServiceTests = @{ + Name = $psqlService.Name + Status = $psqlService.Status + StartType = $psqlService.StartType + } + + It " service is stopped" -TestCases $psqlServiceTests { + $Status | Should -Be "Stopped" + } + + It " service is disabled" -TestCases $psqlServiceTests { + $StartType | Should -Be "Disabled" + } + } + + Context "PostgreSQL version" { + It "PostgreSQL version should correspond to the Major version in the toolset" { + $toolsetVersion = (Get-ToolsetContent).postgresql.version.Split(".")[0] + # Client version + (& $env:PGBIN\psql --version).split()[-1] | Should -BeLike "$toolsetVersion*" + # Server version + (& $env:PGBIN\pg_config --version).split()[-1] | Should -BeLike "$toolsetVersion*" + } + } +} + +Describe "MySQL" { + It "MySQL CLI" { + $MysqlVersion = (Get-ToolsetContent).mysql.version + mysql -V | Should -BeLike "*${MysqlVersion}*" + } +} diff --git a/images/windows/scripts/tests/Docker.Tests.ps1 b/images/windows/scripts/tests/Docker.Tests.ps1 new file mode 100644 index 0000000..d2565f1 --- /dev/null +++ b/images/windows/scripts/tests/Docker.Tests.ps1 @@ -0,0 +1,36 @@ +Describe "Docker" { + It "docker is installed" { + "docker --version" | Should -ReturnZeroExitCode + } + + It "docker service is up" { + "docker images" | Should -ReturnZeroExitCode + } + + It "docker symlink" { + "C:\Windows\SysWOW64\docker.exe ps" | Should -ReturnZeroExitCode + } +} + +Describe "DockerCompose" { + It "docker compose v2" { + "docker compose version" | Should -ReturnZeroExitCode + } + +} + +Describe "DockerWinCred" { + It "docker-wincred" { + "docker-credential-wincred version" | Should -ReturnZeroExitCode + } +} + +Describe "DockerImages" { + Context "docker images" { + $testCases = (Get-ToolsetContent).docker.images | ForEach-Object { @{ ImageName = $_ } } + + It "" -TestCases $testCases { + docker images "$ImageName" --format "{{.Repository}}" | Should -Not -BeNullOrEmpty + } + } +} diff --git a/images/windows/scripts/tests/DotnetSDK.Tests.ps1 b/images/windows/scripts/tests/DotnetSDK.Tests.ps1 new file mode 100644 index 0000000..b7f3649 --- /dev/null +++ b/images/windows/scripts/tests/DotnetSDK.Tests.ps1 @@ -0,0 +1,34 @@ +$dotnetVersions = (Get-ToolsetContent).dotnet.versions +$dotnetTools = (Get-ToolsetContent).dotnet.tools + +Describe "Dotnet SDK and tools" { + + Context "Default" { + It "Default Dotnet SDK is available" { + "dotnet --version" | Should -ReturnZeroExitCode + } + } + + foreach ($version in $dotnetVersions) { + Context "Dotnet $version" { + $dotnet = @{ dotnetVersion = $version } + + It "SDK $version is available" -TestCases $dotnet { + (dotnet --list-sdks | Where-Object { $_ -match "${dotnetVersion}\.[0-9]*" }).Count | Should -BeGreaterThan 0 + } + + It "Runtime $version is available" -TestCases $dotnet { + (dotnet --list-runtimes | Where-Object { $_ -match "${dotnetVersion}\.[0-9]*" }).Count | Should -BeGreaterThan 0 + } + } + } + + Context "Dotnet tools" { + $env:Path += ";C:\Users\Default\.dotnet\tools" + $testCases = $dotnetTools | ForEach-Object { @{ ToolName = $_.name; TestInstance = $_.test }} + + It " is available" -TestCases $testCases { + "$TestInstance" | Should -ReturnZeroExitCode + } + } +} diff --git a/images/windows/scripts/tests/Git.Tests.ps1 b/images/windows/scripts/tests/Git.Tests.ps1 new file mode 100644 index 0000000..00b3d23 --- /dev/null +++ b/images/windows/scripts/tests/Git.Tests.ps1 @@ -0,0 +1,25 @@ +Describe "Git" { + $gitTools = 'bash', 'awk', 'git', 'git-lfs' + $gitTestCases = $gitTools | ForEach-Object { + @{ + toolName = $_ + source = [regex]::Escape("$env:ProgramFiles\Git") + } + } + + It " is installed" -TestCases $gitTestCases { + "$toolName --version" | Should -ReturnZeroExitCode + } + + It " is located in ''" -TestCases $gitTestCases { + (Get-Command -Name $toolName).Source | Should -Match $source + } + + It "Git core.symlinks=true option is enabled" { + git config core.symlinks | Should -BeExactly true + } + + It "GCM_INTERACTIVE environment variable should be equal Never" { + $env:GCM_INTERACTIVE | Should -BeExactly Never + } +} diff --git a/images/windows/scripts/tests/Haskell.Tests.ps1 b/images/windows/scripts/tests/Haskell.Tests.ps1 new file mode 100644 index 0000000..513b71e --- /dev/null +++ b/images/windows/scripts/tests/Haskell.Tests.ps1 @@ -0,0 +1,62 @@ +Describe "Haskell" { + $ghcPackagesPath = "c:\ghcup\ghc" + [array] $ghcVersionList = Get-ChildItem -Path $ghcPackagesPath -Filter "*" | ForEach-Object { $_.Name.Trim() } + $ghcCount = $ghcVersionList.Count + $defaultGhcVersion = $ghcVersionList | Sort-Object {[Version] $_} | Select-Object -Last 1 + $ghcDefaultCases = @{ + defaultGhcVersion = $defaultGhcVersion + defaultGhcShortVersion = ([version] $defaultGhcVersion).ToString(3) + } + + $ghcTestCases = $ghcVersionList | ForEach-Object { + $ghcVersion = $_ + $ghcShortVersion = ([version] $ghcVersion).ToString(3) + $binGhcPath = Join-Path $ghcPackagesPath "$ghcShortVersion\bin\ghc.exe" + @{ + ghcVersion = $ghcVersion + ghcShortVersion = $ghcShortVersion + binGhcPath = $binGhcPath + } + } + + $ghcupEnvExists = @( + @{envVar = "GHCUP_INSTALL_BASE_PREFIX"} + @{envVar = "GHCUP_MSYS2"} + ) + + It " environment variable exists" -TestCases $ghcupEnvExists { + Test-Path env:\$envVar + } + + It "Accurate 3 versions of GHC are installed" -TestCases @{ghcCount = $ghcCount} { + $ghcCount | Should -BeExactly 3 + } + + It "GHC is installed" -TestCases $ghcTestCases { + "$binGhcPath --version" | Should -OutputTextMatchingRegex $ghcShortVersion + } + + It "GHC is the default version and should be the latest installed" -TestCases $ghcDefaultCases { + "ghc --version" | Should -OutputTextMatchingRegex $defaultGhcShortVersion + } + + It "Cabal is installed" { + "cabal --version" | Should -ReturnZeroExitCode + } + + It "cabal folder does not exist" { + $env:CABAL_DIR | Should -Not -Exist + } + + It "CABAL_DIR environment variable exists" { + Get-EnvironmentVariable CABAL_DIR | Should -BeExactly "C:\cabal" + } + + It "ghcup is installed" { + "ghcup --version" | Should -ReturnZeroExitCode + } + + It "ghcup can access msys2" { + "ghcup run --mingw-path -- pacman --version" | Should -ReturnZeroExitCode + } +} diff --git a/images/windows/scripts/tests/Helpers.psm1 b/images/windows/scripts/tests/Helpers.psm1 new file mode 100644 index 0000000..0f00198 --- /dev/null +++ b/images/windows/scripts/tests/Helpers.psm1 @@ -0,0 +1,210 @@ +[CmdletBinding()] +param() + +# Gets value of environment variable by the name +function Get-EnvironmentVariable($variable) { + return [System.Environment]::GetEnvironmentVariable($variable, "Machine") +} + +function Invoke-PesterTests { + <# + .SYNOPSIS + Runs Pester tests based on the provided test file and test name. + + .DESCRIPTION + The Invoke-PesterTests function runs Pester tests based on the provided test file and test name. + It supports filtering tests by name and generating test result output. + + .PARAMETER TestFile + The name of the test file to run. This should be the base name of the test file without the extension. + + .PARAMETER TestName + The name of the specific test to run. If provided, only the test with the matching name will be executed. + + .EXAMPLE + Invoke-PesterTests -TestFile "MyTests" -TestName "Test1" + Runs the test named "Test1" from the test file "MyTests.Tests.ps1". + + .EXAMPLE + Invoke-PesterTests -TestFile "*" -TestName "Test2" + Runs all tests from all test files and generates the test result output. + + .NOTES + This function requires the Pester module to be installed. + + #> + + Param( + [Parameter(Mandatory)][string] $TestFile, + [string] $TestName + ) + + $testPath = "C:\image\tests\${TestFile}.Tests.ps1" + if (-not (Test-Path $testPath)) { + throw "Unable to find test file '$TestFile' on '$testPath'." + } + + $configuration = [PesterConfiguration] @{ + Run = @{ Path = $testPath; PassThru = $true } + Output = @{ Verbosity = "Detailed"; RenderMode = "Plaintext" } + } + if ($TestName) { + $configuration.Filter.FullName = $TestName + } + if ($TestFile -eq "*") { + $configuration.TestResult.Enabled = $true + $configuration.TestResult.OutputPath = "C:\image\tests\testResults.xml" + } + + # Update environment variables without reboot + Update-Environment + + # Switch ErrorActionPreference to Stop temporary to make sure that tests will on silent errors too + $backupErrorActionPreference = $ErrorActionPreference + $ErrorActionPreference = "Stop" + $results = Invoke-Pester -Configuration $configuration + $ErrorActionPreference = $backupErrorActionPreference + + # Fail in case if no tests are run + if (-not ($results -and ($results.FailedCount -eq 0) -and ($results.PassedCount -gt 0))) { + $results + throw "Test run has failed" + } +} + +function ShouldReturnZeroExitCode { + <# + .SYNOPSIS + Implements a custom Should-operator for the Pester framework. + + .DESCRIPTION + This function is used to check if a command has returned a zero exit code. + It can be used by registering it using the Add-ShouldOperator function in Pester. + + .PARAMETER ActualValue + The actual value to be checked. + + .PARAMETER Negate + A switch parameter that, when specified, negates the result of the check. + + .PARAMETER Because + An optional string that provides additional context or explanation for the check. + + .NOTES + This function is designed to be used with the Pester framework. + + .LINK + https://pester.dev/docs/assertions/custom-assertions + #> + + Param( + [string] $ActualValue, + [switch] $Negate, + [string] $Because + ) + + $outputLines = (& $env:comspec /c "$ActualValue 2>&1") -as [string[]] + $exitCode = $LASTEXITCODE + + [bool] $succeeded = $exitCode -eq 0 + if ($Negate) { $succeeded = -not $succeeded } + + if (-not $succeeded) { + $commandOutputIndent = " " * 4 + $commandOutput = ($outputLines | ForEach-Object { "${commandOutputIndent}${_}" }) -join "`n" + $failureMessage = "Command '${ActualValue}' has finished with exit code ${exitCode} and output:`n${commandOutput}" + } + + return [PSCustomObject] @{ + Succeeded = $succeeded + FailureMessage = $failureMessage + } +} + +function ShouldOutputTextMatchingRegex { + <# + .SYNOPSIS + Implements a custom Should-operator for the Pester framework. + + .DESCRIPTION + This function is used to check if a command outputs text that matches a regular expression. + It can be used by registering it using the Add-ShouldOperator function in Pester. + + .PARAMETER ActualValue + The actual value to be checked. + + .PARAMETER Negate + A switch parameter that, when specified, negates the result of the check. + + .PARAMETER Because + An optional string that provides additional context or explanation for the check. + + .NOTES + This function is designed to be used with the Pester framework. + + .LINK + https://pester.dev/docs/assertions/custom-assertions + #> + + Param( + [String] $ActualValue, + [String] $RegularExpression, + [switch] $Negate + ) + + [string] $output = (& $env:comspec /c "$ActualValue 2>&1") + [bool] $succeeded = $output -cmatch $RegularExpression + + if ($Negate) { $succeeded = -not $succeeded } + + $failureMessage = '' + + if (-not $succeeded) { + if ($Negate) { + $failureMessage = "Expected regular expression '$RegularExpression' for '$ActualValue' command to not match '$output', but it did match." + } else { + $failureMessage = "Expected regular expression '$RegularExpression' for '$ActualValue' command to match '$output', but it did not match." + } + } + + return [PSCustomObject] @{ + Succeeded = $succeeded + FailureMessage = $failureMessage + } +} + +If (Get-Command -Name Add-ShouldOperator -ErrorAction SilentlyContinue) { + Add-ShouldOperator -Name ReturnZeroExitCode -InternalName ShouldReturnZeroExitCode -Test ${function:ShouldReturnZeroExitCode} + Add-ShouldOperator -Name OutputTextMatchingRegex -InternalName ShouldOutputTextMatchingRegex -Test ${function:ShouldOutputTextMatchingRegex} +} + +function Get-ModuleVersionAsJob { + Param ( + [Parameter(Mandatory)] + [String] $modulePath, + [Parameter(Mandatory)] + [String] $moduleName + ) + # Script block to run commands in separate PowerShell environment + $testJob = Start-Job -ScriptBlock { + param ( + $modulePath, + $moduleName + ) + # Disable warning messages to prevent additional warnings about Az and Azurerm modules in the same session + $WarningPreference = "SilentlyContinue" + $env:PsModulePath = "$modulePath;$env:PsModulePath" + Import-Module -Name $moduleName + (Get-Module -Name $moduleName).Version.ToString() + + } -ArgumentList $modulePath, $moduleName + + $testJob | Wait-Job | Receive-Job | Out-File -FilePath "${env:TEMP}\module-version.txt" + Remove-Job $testJob +} + +Export-ModuleMember -Function @( + 'Get-EnvironmentVariable' + 'Invoke-PesterTests' + 'Get-ModuleVersionAsJob' +) diff --git a/images/windows/scripts/tests/Java.Tests.ps1 b/images/windows/scripts/tests/Java.Tests.ps1 new file mode 100644 index 0000000..bf40a8d --- /dev/null +++ b/images/windows/scripts/tests/Java.Tests.ps1 @@ -0,0 +1,40 @@ +Describe "Java" { + $toolsetJava = (Get-ToolsetContent).java + $defaultVersion = $toolsetJava.default + $jdkVersions = $toolsetJava.versions + + [array] $testCases = $jdkVersions | ForEach-Object { @{Version = $_ } } + + It "Java is default" -TestCases @(@{ DefaultJavaVersion = $defaultVersion }) { + $actualJavaPath = Get-EnvironmentVariable "JAVA_HOME" + $expectedJavaPath = Get-EnvironmentVariable "JAVA_HOME_${DefaultJavaVersion}_X64" + + $actualJavaPath | Should -Not -BeNullOrEmpty + $expectedJavaPath | Should -Not -BeNullOrEmpty + $actualJavaPath | Should -Be $expectedJavaPath + } + + It "" -TestCases @( + @{ ToolName = "java" } + @{ ToolName = "mvn" } + @{ ToolName = "ant" } + @{ ToolName = "gradle" } + ) { + "$ToolName -version" | Should -ReturnZeroExitCode + } + + It "Java " -TestCases $testCases { + $javaVariableValue = Get-EnvironmentVariable "JAVA_HOME_${Version}_X64" + $javaVariableValue | Should -Not -BeNullOrEmpty + $javaPath = Join-Path $javaVariableValue "bin\java" + + if ($Version -eq 8) { + $Version = "1.${Version}" + } + $outputPattern = "openjdk version `"${Version}" + + $outputLines = (& $env:comspec /c "`"$javaPath`" -version 2>&1") -as [string[]] + $LASTEXITCODE | Should -Be 0 + $outputLines[0] | Should -Match $outputPattern + } +} diff --git a/images/windows/scripts/tests/LLVM.Tests.ps1 b/images/windows/scripts/tests/LLVM.Tests.ps1 new file mode 100644 index 0000000..c7a5df4 --- /dev/null +++ b/images/windows/scripts/tests/LLVM.Tests.ps1 @@ -0,0 +1,10 @@ +Describe "Clang/LLVM" { + BeforeAll { + $toolsetVersion = (Get-ToolsetContent).llvm.version + } + + It "Clang/LLVM installed and version is correct" { + $clangVersion = clang --version + $clangVersion[0] | Should -BeLike "*${toolsetVersion}*" + } +} diff --git a/images/windows/scripts/tests/MSYS2.Tests.ps1 b/images/windows/scripts/tests/MSYS2.Tests.ps1 new file mode 100644 index 0000000..8cca75e --- /dev/null +++ b/images/windows/scripts/tests/MSYS2.Tests.ps1 @@ -0,0 +1,72 @@ +BeforeAll { + $msys2Dir = "C:\msys64\usr\bin" + $originalPath = $env:PATH +} + +Describe "MSYS2 packages" { + BeforeEach { + $env:PATH = "$msys2Dir;$env:PATH" + } + + It "msys2Dir exists" { + $msys2Dir | Should -Exist + } + + $TestCases = @( + @{ ToolName = "bash.exe" } + ) + + if (Test-IsWin19) { + $TestCases += @( + @{ ToolName = "tar.exe" } + @{ ToolName = "make.exe" } + ) + } + + It " is installed in " -TestCases $TestCases { + (Get-Command "$ToolName").Source | Should -BeLike "$msys2Dir*" + } + + It " is avaialable" -TestCases $TestCases { + "$ToolName --version" | Should -ReturnZeroExitCode + } + + AfterEach { + $env:PATH = $originalPath + } +} + +$mingwTypes = (Get-ToolsetContent).MsysPackages.mingw +foreach ($mingwType in $mingwTypes) { + Describe "$($mingwType.arch) packages" { + $tools = $mingwType.runtime_packages + $execDir = Join-Path "C:\msys64" $mingwType.exec_dir | Join-Path -ChildPath "bin" + + foreach ($tool in $tools) { + Context "$($tool.name) package" { + $executables = $tool.executables | ForEach-Object { + @{ + ExecName = $_ + ExecDir = $execDir + } + } + + BeforeEach { + $env:PATH = "$execDir;$env:PATH" + } + + It " is installed in " -TestCases $executables { + (Get-Command "$ExecName").Source | Should -BeLike "$ExecDir*" + } + + It " is available" -TestCases $executables { + "$ExecName --version" | Should -ReturnZeroExitCode + } + + AfterEach { + $env:PATH = $originalPath + } + } + } + } +} diff --git a/images/windows/scripts/tests/Miniconda.Tests.ps1 b/images/windows/scripts/tests/Miniconda.Tests.ps1 new file mode 100644 index 0000000..763a158 --- /dev/null +++ b/images/windows/scripts/tests/Miniconda.Tests.ps1 @@ -0,0 +1,14 @@ +Describe "Miniconda" { + It "Miniconda Environment variables is set. " { + ${env:CONDA} | Should -Not -BeNullOrEmpty + } + + It "Miniconda $env:CONDA\ " -TestCases @( + @{ PathTest = "python.exe" } + @{ PathTest = "Scripts\conda.exe" } + ) { + $condaPath = Join-Path ${env:CONDA} $PathTest + $condaPath | Should -Exist + "$condaPath --version" | Should -ReturnZeroExitCode + } +} \ No newline at end of file diff --git a/images/windows/scripts/tests/Nginx.Tests.ps1 b/images/windows/scripts/tests/Nginx.Tests.ps1 new file mode 100644 index 0000000..8c6e7ec --- /dev/null +++ b/images/windows/scripts/tests/Nginx.Tests.ps1 @@ -0,0 +1,25 @@ +Describe "Nginx" { + Context "Path" { + It "Nginx" { + $nginxPath = Join-Path (Join-Path "C:\tools\" (Get-Item C:\tools\nginx*).Name) "nginx" + "$nginxPath -v" | Should -ReturnZeroExitCode + } + } + + Context "Service" { + $nginxService = Get-Service -Name nginx + $nginxServiceTests = @{ + Name = $nginxService.Name + Status = $nginxService.Status + StartType = $nginxService.StartType + } + + It " service is stopped" -TestCases $nginxServiceTests { + $Status | Should -Be "Stopped" + } + + It " service is disabled" -TestCases $nginxServiceTests { + $StartType | Should -Be "Disabled" + } + } +} diff --git a/images/windows/scripts/tests/Node.Tests.ps1 b/images/windows/scripts/tests/Node.Tests.ps1 new file mode 100644 index 0000000..720e65b --- /dev/null +++ b/images/windows/scripts/tests/Node.Tests.ps1 @@ -0,0 +1,25 @@ +Describe "Node.JS" { + Context "Basic modules"{ + It " " -TestCases @( + @{ ToolName = "node" } + @{ ToolName = "npm" } + ) { + "$ToolName --version" | Should -ReturnZeroExitCode + } + } + + $globalNpmPackages = (Get-ToolsetContent).npm.global_packages + $globalNpmPackagesWithTests = $globalNpmPackages | Where-Object { $_.test } | ForEach-Object { @{ Name = $_.name; Test = $_.test } } + + Context "Global NPM Packages" { + It "" -TestCases $globalNpmPackagesWithTests { + $Test | Should -ReturnZeroExitCode + } + } + + Context "Node.js version" { + It "Node.js version should correspond to the version in the toolset" { + node --version | Should -BeLike "v$((Get-ToolsetContent).node.default)*" + } + } +} \ No newline at end of file diff --git a/images/windows/scripts/tests/PHP.Tests.ps1 b/images/windows/scripts/tests/PHP.Tests.ps1 new file mode 100644 index 0000000..0e10133 --- /dev/null +++ b/images/windows/scripts/tests/PHP.Tests.ps1 @@ -0,0 +1,16 @@ +Describe "PHP" { + It "Check PHP version" { + $phpMajorMinor = (Get-ToolsetContent).php.version + $phpInstalledVersion = php --version | Select-String -Pattern "PHP $phpMajorMinor" + $phpInstalledVersion | Should -BeLike "*${phpMajorMinor}*" + } + + It "Check Composer in the PATH" { + "composer --version" | Should -ReturnZeroExitCode + } + + It "PHP Environment variables is set." { + ${env:PHPROOT} | Should -Not -BeNullOrEmpty + ${env:PHPROOT} | Should -Exist + } +} diff --git a/images/windows/scripts/tests/PipxPackages.Tests.ps1 b/images/windows/scripts/tests/PipxPackages.Tests.ps1 new file mode 100644 index 0000000..4104107 --- /dev/null +++ b/images/windows/scripts/tests/PipxPackages.Tests.ps1 @@ -0,0 +1,7 @@ +Describe "PipxPackages" { + $pipxToolset = (Get-ToolsetContent).pipx + $testCases = $pipxToolset | ForEach-Object { @{package = $_.package; cmd = $_.cmd} } + It "" -TestCases $testCases { + "$cmd" | Should -ReturnZeroExitCode + } +} \ No newline at end of file diff --git a/images/windows/scripts/tests/PowerShellAzModules.Tests.ps1 b/images/windows/scripts/tests/PowerShellAzModules.Tests.ps1 new file mode 100644 index 0000000..0fea778 --- /dev/null +++ b/images/windows/scripts/tests/PowerShellAzModules.Tests.ps1 @@ -0,0 +1,40 @@ +Describe "AzureModules" { + + $modules = (Get-ToolsetContent).azureModules + $modulesRootPath = "C:\\Modules" + + foreach ($module in $modules) { + $moduleName = $module.name + + Context "$moduleName" { + + foreach ($version in $module.versions) { + $modulePath = Join-Path -Path $modulesRootPath -ChildPath "${moduleName}_${version}" + $moduleInfo = @{ moduleName = $moduleName; modulePath = $modulePath; expectedVersion = $version } + It " exists in modules directory" -TestCases $moduleInfo { + $ScriptBlock = { + param ($modulePath, $moduleName) + Import-Module ImageHelpers + Get-ModuleVersionAsJob -modulePath $modulePath -moduleName $moduleName + } + if ($moduleName -eq "Az"){ + Start-Process -FilePath pwsh.exe -ArgumentList "-Command (Invoke-Command -ScriptBlock {$ScriptBlock} -ArgumentList $modulePath, $moduleName)" -Wait -PassThru + } else { + Start-Process -FilePath powershell.exe -ArgumentList "-Command (Invoke-Command -ScriptBlock {$ScriptBlock} -ArgumentList $modulePath, $moduleName)" -Wait -PassThru + } + $moduleVersion = Get-Content -Path "$env:TEMP\module-version.txt" + Remove-Item -Path "${env:TEMP}\module-version.txt" -Force + $moduleVersion | Should -Match $expectedVersion + } + } + + if ($module.default) { + $moduleInfo = @{ moduleName = $moduleName; moduleDefault = $module.default } + It " set as default" -TestCases $moduleInfo { + $moduleVersions = Get-Module -ListAvailable -Name $moduleName | ForEach-Object { $_.Version.ToString() } + $moduleVersions | Should -Contain $moduleDefault + } + } + } + } +} diff --git a/images/windows/scripts/tests/PowerShellModules.Tests.ps1 b/images/windows/scripts/tests/PowerShellModules.Tests.ps1 new file mode 100644 index 0000000..9276609 --- /dev/null +++ b/images/windows/scripts/tests/PowerShellModules.Tests.ps1 @@ -0,0 +1,23 @@ +Describe "PowerShellModules" { + $modules = (Get-ToolsetContent).powershellModules + $withoutVersionsModules = $modules | Where-Object {-not $_.versions} | ForEach-Object { + @{moduleName = $_.name} + } + + $withVersionsModules = $modules | Where-Object {$_.versions} | ForEach-Object { + $moduleName = $_.name + $_.versions | ForEach-Object { + @{moduleName = $moduleName; expectedVersion = $_} + } + } + + It " is installed" -TestCases $withoutVersionsModules { + Get-Module -Name $moduleName -ListAvailable | Should -BeTrue + } + + if ($withVersionsModules) { + It " with is installed" -TestCases $withVersionsModules { + (Get-Module -Name $moduleName -ListAvailable).Version -contains $expectedVersion | Should -BeTrue + } + } +} diff --git a/images/windows/scripts/tests/RunAll-Tests.ps1 b/images/windows/scripts/tests/RunAll-Tests.ps1 new file mode 100644 index 0000000..1d8e835 --- /dev/null +++ b/images/windows/scripts/tests/RunAll-Tests.ps1 @@ -0,0 +1 @@ +Invoke-PesterTests "*" \ No newline at end of file diff --git a/images/windows/scripts/tests/RunnerCache.tests.ps1 b/images/windows/scripts/tests/RunnerCache.tests.ps1 new file mode 100644 index 0000000..8c932fb --- /dev/null +++ b/images/windows/scripts/tests/RunnerCache.tests.ps1 @@ -0,0 +1,7 @@ +Describe "RunnerCache" { + Context "runner cache directory not empty" { + It "C:\ProgramData\runner" { + (Get-ChildItem -Path "C:\ProgramData\runner\*.zip" -Recurse).Count | Should -BeGreaterThan 0 + } + } +} diff --git a/images/windows/scripts/tests/Rust.Tests.ps1 b/images/windows/scripts/tests/Rust.Tests.ps1 new file mode 100644 index 0000000..79535e1 --- /dev/null +++ b/images/windows/scripts/tests/Rust.Tests.ps1 @@ -0,0 +1,37 @@ +Describe "Rust" { + BeforeAll { + $env:RUSTUP_HOME = "C:\Users\Default\.rustup" + $env:CARGO_HOME = "C:\Users\Default\.cargo" + $env:Path += ";$env:CARGO_HOME\bin" + } + + $rustTools = @( + @{ToolName = "rustup"; binPath = "C:\Users\Default\.cargo\bin\rustup.exe"} + @{ToolName = "rustc"; binPath = "C:\Users\Default\.cargo\bin\rustc.exe"} + @{ToolName = "cbindgen.exe"; binPath = "C:\Users\Default\.cargo\bin\cbindgen.exe"} + @{ToolName = "cargo"; binPath = "C:\Users\Default\.cargo\bin\cargo.exe"} + @{ToolName = "cargo audit"; binPath = "C:\Users\Default\.cargo\bin\cargo-audit.exe"} + @{ToolName = "cargo outdated"; binPath = "C:\Users\Default\.cargo\bin\cargo-outdated.exe"} + ) + if (-not (Test-IsWin25)) { + $rustTools += @{ToolName = "bindgen.exe"; binPath = "C:\Users\Default\.cargo\bin\bindgen.exe"} + } + + $rustEnvNotExists = @( + @{envVar = "RUSTUP_HOME"} + @{envVar = "CARGO_HOME"} + ) + + It "C:\Users\Default\.rustup and C:\Users\Default\.cargo folders exist" { + "C:\Users\Default\.rustup", "C:\Users\Default\.cargo" | Should -Exist + } + + It " environment variable does not exist" -TestCases $rustEnvNotExists { + [Environment]::GetEnvironmentVariables("Machine").ContainsKey($envVar) | Should -BeFalse + } + + It " is installed to the '' folder" -TestCases $rustTools { + "$ToolName --version" | Should -ReturnZeroExitCode + $binPath | Should -Exist + } +} diff --git a/images/windows/scripts/tests/SSDTExtensions.Tests.ps1 b/images/windows/scripts/tests/SSDTExtensions.Tests.ps1 new file mode 100644 index 0000000..7d1c6ee --- /dev/null +++ b/images/windows/scripts/tests/SSDTExtensions.Tests.ps1 @@ -0,0 +1,21 @@ +Describe "SSDTExtensions" { +#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 + + if (Test-isWin19) { + $testExtenions = @( + @{id = "04a86fc2-dbd5-4222-848e-911638e487fe"} + @{id = "851E7A09-7B2B-4F06-A15D-BABFCB26B970"} + @{id = "717ad572-c4b7-435c-c166-c2969777f718"} + ) + + It "Extensions id=" -TestCases $testExtenions { + $version = Get-VSExtensionVersion -packageName "${id}" + $version | Should -Not -BeNullOrEmpty + } + } else { + It "Extension SSDT" { + $version = Get-VSExtensionVersion -packageName "SSDT" + $version | Should -Not -BeNullOrEmpty + } + } +} diff --git a/images/windows/scripts/tests/Shell.Tests.ps1 b/images/windows/scripts/tests/Shell.Tests.ps1 new file mode 100644 index 0000000..caddaf7 --- /dev/null +++ b/images/windows/scripts/tests/Shell.Tests.ps1 @@ -0,0 +1,11 @@ +Describe "Shell" { + $shellTestCases = @( + @{Name = "C:\shells\gitbash.exe"; Target = "$env:ProgramFiles\Git\bin\bash.exe"}, + @{Name = "C:\shells\msys2bash.cmd"; Target = $null} + @{Name = "C:\shells\wslbash.exe"; Target = "$env:SystemRoot\System32\bash.exe"} + ) + + It " target to " -TestCases $shellTestCases { + (Get-Item $Name).Target | Should -BeExactly $Target + } +} diff --git a/images/windows/scripts/tests/Tools.Tests.ps1 b/images/windows/scripts/tests/Tools.Tests.ps1 new file mode 100644 index 0000000..89a544d --- /dev/null +++ b/images/windows/scripts/tests/Tools.Tests.ps1 @@ -0,0 +1,231 @@ +Describe "Azure Cosmos DB Emulator" { + $cosmosDbEmulatorRegKey = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" | Get-ItemProperty | Where-Object { $_.DisplayName -eq 'Azure Cosmos DB Emulator' } + $installDir = $cosmosDbEmulatorRegKey.InstallLocation + + It "Azure Cosmos DB Emulator install location registry key exists" -TestCases @{installDir = $installDir} { + $installDir | Should -Not -BeNullOrEmpty + } + + It "Azure Cosmos DB Emulator exe file exists" -TestCases @{installDir = $installDir} { + $exeFilePath = Join-Path $installDir 'CosmosDB.Emulator.exe' + $exeFilePath | Should -Exist + } +} + +Describe "Bazel" { + It "" -TestCases @( + @{ ToolName = "bazel" } + @{ ToolName = "bazelisk" } + ) { + "$ToolName --version"| Should -ReturnZeroExitCode + } +} + +Describe "CodeQL Bundle" { + It "Single distribution installed" { + $CodeQLVersionsWildcard = Join-Path $env:AGENT_TOOLSDIRECTORY -ChildPath "CodeQL" | Join-Path -ChildPath "*" + $CodeQLVersionPath = Get-ChildItem $CodeQLVersionsWildcard | Should -HaveCount 1 + } + + It "Contains CodeQL executable" { + $CodeQLVersionsWildcard = Join-Path $env:AGENT_TOOLSDIRECTORY -ChildPath "CodeQL" | Join-Path -ChildPath "*" + $CodeQLVersionPath = Get-ChildItem $CodeQLVersionsWildcard | Sort-Object -Descending | Select-Object -First 1 -Expand FullName + $CodeQLPath = Join-Path $CodeQLVersionPath -ChildPath "x64" | Join-Path -ChildPath "codeql" | Join-Path -ChildPath "codeql.exe" + "$CodeQLPath version --quiet" | Should -ReturnZeroExitCode + } + + It "Contains CodeQL packs" { + $CodeQLVersionsWildcard = Join-Path $env:AGENT_TOOLSDIRECTORY -ChildPath "CodeQL" | Join-Path -ChildPath "*" + $CodeQLVersionPath = Get-ChildItem $CodeQLVersionsWildcard | Sort-Object -Descending | Select-Object -First 1 -Expand FullName + $CodeQLPacksPath = Join-Path $CodeQLVersionPath -ChildPath "x64" | Join-Path -ChildPath "codeql" | Join-Path -ChildPath "qlpacks" + $CodeQLPacksPath | Should -Exist + } +} + +Describe "R" { + It "Rscript" { + "Rscript --version" | Should -ReturnZeroExitCode + } +} + +Describe "DACFx" { + It "DACFx" { + (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*).DisplayName -Contains "Microsoft SQL Server Data-Tier Application Framework" | Should -BeTrue + $sqlPackagePath = 'C:\Program Files\Microsoft SQL Server\160\DAC\bin\SqlPackage.exe' + "${sqlPackagePath}" | Should -Exist + } + + # Skip on Windows Server 2022 and 2025 + # It "SqlLocalDB" -Skip:(Test-IsWin22 -or Test-IsWin25) { + # $sqlLocalDBPath = 'C:\Program Files\Microsoft SQL Server\130\Tools\Binn\SqlLocalDB.exe' + # "${sqlLocalDBPath}" | Should -Exist + # } +} + +Describe "DotnetTLS" -Skip:(-not (Test-IsWin19)) { + It "Tls 1.2 is enabled" { + [Net.ServicePointManager]::SecurityProtocol -band "Tls12" | Should -Be Tls12 + } +} + +Describe "Mercurial" -Skip: (Test-IsWin25){ + It "Mercurial" { + "hg --version" | Should -ReturnZeroExitCode + } +} + +Describe "KubernetesTools" { + It "Kind" { + "kind version" | Should -ReturnZeroExitCode + } + + It "kubectl" { + "kubectl version --client=true" | Should -ReturnZeroExitCode + } + + It "Helm" { + "helm version --short" | Should -ReturnZeroExitCode + } + + It "minikube" { + "minikube version --short" | Should -ReturnZeroExitCode + } +} + +Describe "Mingw64" { + It "" -TestCases @( + @{ ToolName = "gcc" } + @{ ToolName = "g++" } + @{ ToolName = "make" } + ) { + "$ToolName --version" | Should -ReturnZeroExitCode + } +} + +Describe "GoogleCloudCLI" -Skip:(-not (Test-IsWin19)) { + It "" -TestCases @( + @{ ToolName = "bq" } + @{ ToolName = "gcloud" } + @{ ToolName = "gsutil" } + ) { + "$ToolName version" | Should -ReturnZeroExitCode + } +} + +Describe "NET48" { + It "NET48" { + Get-ChildItem -Path "${env:ProgramFiles(x86)}\Microsoft SDKs\Windows\*\*\NETFX 4.8 Tools" -Directory | Should -HaveCount 1 + } +} + +Describe "NSIS" -Skip: (Test-IsWin25){ + It "NSIS" { + "makensis /VERSION" | Should -ReturnZeroExitCode + } +} + +Describe "PowerShell Core" { + It "pwsh" { + "pwsh --version" | Should -ReturnZeroExitCode + } + + It "Execute 2+2 command" { + pwsh -Command "2+2" | Should -BeExactly 4 + } +} + +Describe "Sbt" { + It "sbt" { + "sbt --version" | Should -ReturnZeroExitCode + } +} + +Describe "ServiceFabricSDK" { + It "PowerShell Module" { + # Ignore PowerShell version check if running in PowerShell Core + # https://github.com/microsoft/service-fabric/issues/1343 + if ($PSVersionTable.PSEdition -eq 'Core') { + Get-Module -Name ServiceFabric -SkipEditionCheck -ListAvailable | Should -Not -BeNullOrEmpty + } else { + Get-Module -Name ServiceFabric -ListAvailable | Should -Not -BeNullOrEmpty + } + } + + It "ServiceFabricSDK version" { + Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Service Fabric\' -Name FabricVersion | Should -Not -BeNullOrEmpty + } +} + +Describe "Stack" { + It "Stack" { + "stack --version" | Should -ReturnZeroExitCode + } +} + +Describe "Vcpkg" { + It "vcpkg" { + "vcpkg version" | Should -ReturnZeroExitCode + } + + It "env variable VCPKG_INSTALLATION_ROOT is set" { + $env:VCPKG_INSTALLATION_ROOT | Should -Not -BeNullOrEmpty + } + + It "VCPKG_INSTALLATION_ROOT directory" { + $env:VCPKG_INSTALLATION_ROOT | Should -Exist + } +} + +Describe "VCRedist" -Skip:(-not (Test-IsWin19)) { + It "vcredist_2010_x64" { + "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1D8E6291-B0D5-35EC-8441-6616F567A0F7}" | Should -Exist + "C:\Windows\System32\msvcr100.dll" | Should -Exist + } +} + +Describe "WebPlatformInstaller" { + It "WebPlatformInstaller" { + "WebPICMD" | Should -ReturnZeroExitCode + } +} + +Describe "Zstd" { + It "zstd" { + "zstd -V" | Should -ReturnZeroExitCode + } +} + +Describe "Pipx" { + It "Pipx" { + "pipx --version" | Should -ReturnZeroExitCode + } +} + +Describe "Kotlin" { + $kotlinPackages = @("kapt", "kotlin", "kotlinc", "kotlin-dce-js", "kotlinc-jvm") + + It " is available" -TestCases ($kotlinPackages | ForEach-Object { @{ toolName = $_ } }) { + "$toolName -version" | Should -ReturnZeroExitCode + } +} + +Describe "SQL OLEDB Driver" { + It "SQL OLEDB Driver" { + "HKLM:\SOFTWARE\Microsoft\MSOLEDBSQL" | Should -Exist + } +} + +Describe "OpenSSL" { + It "OpenSSL Version" { + $OpenSSLVersion = (Get-ToolsetContent).openssl.version + openssl version | Should -BeLike "* ${OpenSSLVersion}*" + } + + It "OpenSSL Path" { + (Get-Command openssl).Source -eq (Join-Path ${env:ProgramFiles} 'OpenSSL\bin\openssl.exe') | Should -Be $true + } + + It "OpenSSL Full package" { + Join-Path ${env:ProgramFiles} 'OpenSSL\include' | Should -Exist + } +} diff --git a/images/windows/scripts/tests/Toolset.Tests.ps1 b/images/windows/scripts/tests/Toolset.Tests.ps1 new file mode 100644 index 0000000..83d4b9a --- /dev/null +++ b/images/windows/scripts/tests/Toolset.Tests.ps1 @@ -0,0 +1,89 @@ +$toolsExecutables = @{ + Python = @( + @{ Binary = "python.exe"; Arguments = "--version" }, + @{ Binary = "Scripts\pip.exe"; Arguments = "--version" } + ) + PyPy = @( + @{ Binary = "python.exe"; Arguments = "--version" }, + @{ Binary = "Scripts\pip.exe"; Arguments = "--version" } + ) + Node = @( + @{ Binary = "node.exe"; Arguments = "--version" }, + @{ Binary = "npm"; Arguments = "--version" } + ) + Go = @( + @{ Binary = "bin\go.exe"; Arguments = "version" } + ) + Ruby = @( + @{ Binary = "bin\ruby.exe"; Arguments = "--version" } + ) +} + +function Get-ToolExecutables { + Param ([String] $Name) + if ($toolsExecutables.ContainsKey($Name)) { $toolsExecutables[$Name] } else { @() } +} + +function Test-Binaries { + Param ( + [String] $Name, + [String] $Version, + [String] $Arch, + [Array] $ToolExecs + ) + $testCases = $ToolExecs | ForEach-Object { + @{ Name = $Name; Version = $Version; Arch = $Arch; Binary = $_.Binary; Arguments = $_.Arguments } + } + It " " -TestCases $testCases { + $binaryFullPath = Join-Path (Get-TCToolVersionPath -Name $Name -Version $Version -Arch $Arch) $Binary + "$binaryFullPath $Arguments" | Should -ReturnZeroExitCode + } +} + +function Test-DefaultVersion { + Param ( + [String] $Name, + [String] $ExpectedVersion, + [Array] $ToolExecs + ) + $binaryName = [IO.Path]::GetFileNameWithoutExtension($ToolExecs[0].Binary) + $testCase = @{ Binary = $binaryName; Arguments = $ToolExecs[0].Arguments; ExpectedVersion = $ExpectedVersion } + It " is default version" -TestCases $testCase { + $outputLines = (& $env:comspec /c "$Binary $Arguments 2>&1") -as [string[]] + $LASTEXITCODE | Should -Be 0 + $outputLines | Should -Match $ExpectedVersion + } + + It "default version is located in tool-cache" -TestCases $testCase { + $binaryFullPath = (Get-Command $Binary).Path + $toolcacheDirectory = Get-TCToolPath -ToolName $Name + $binaryFullPath | Should -Match ([Regex]::Escape($toolcacheDirectory)) + } +} + +$tools = Get-ToolsetContent | Select-Object -ExpandProperty toolcache +foreach ($tool in $tools) { + Describe "$($tool.name) [$($tool.arch)]" { + $toolExecs = Get-ToolExecutables -Name $tool.name + + foreach ($version in $tool.versions) { + Context "$version" { + $toolInfo = @{ Name = $tool.name; Version = $version; Arch = $tool.arch } + It "tool-cache directory exists" -TestCases $toolInfo { + $toolFullPath = Get-TCToolVersionPath -Name $Name -Version $Version -Arch $Arch + $toolFullPath | Should -Exist + } + + if ($toolExecs) { + Test-Binaries -Name $tool.name -Version $version -Arch $tool.arch -ToolExecs $toolExecs + } + } + } + + if ($tool.default -and $toolExecs) { + Context "Default" { + Test-DefaultVersion -Name $tool.name -ExpectedVersion $tool.default -ToolExecs $toolExecs + } + } + } +} diff --git a/images/windows/scripts/tests/VisualStudio.Tests.ps1 b/images/windows/scripts/tests/VisualStudio.Tests.ps1 new file mode 100644 index 0000000..126a3d1 --- /dev/null +++ b/images/windows/scripts/tests/VisualStudio.Tests.ps1 @@ -0,0 +1,38 @@ +Describe "Visual Studio" { + Context "Basic" { + It "Catalog.json" { + $instanceFolder = Get-Item "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances\*" | Select-Object -First 1 + Join-Path $instanceFolder.FullName "catalog.json" | Should -Exist + } + + It "Devenv.exe" { + $vsInstallRoot = (Get-VisualStudioInstance).InstallationPath + $devenvexePath = "${vsInstallRoot}\Common7\IDE\devenv.exe" + $devenvexePath | Should -Exist + } + } + + Context "Visual Studio components" { + $expectedComponents = Get-ToolsetContent | Select-Object -ExpandProperty visualStudio | Select-Object -ExpandProperty workloads + $testCases = $expectedComponents | ForEach-Object { @{ComponentName = $_} } + BeforeAll { + $installedComponents = Get-VisualStudioComponents | Select-Object -ExpandProperty Package + } + + It "" -TestCases $testCases { + $installedComponents | Should -Contain $ComponentName + } + } +} + +Describe "Windows 10 SDK" { + It "Verifies 17763 SDK is installed" -Skip:(-not (Test-IsWin22)) { + "${env:ProgramFiles(x86)}\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.17763.0\UAP.props" | Should -Exist + } +} + +Describe "Windows 11 SDK" { + It "Verifies 22621 SDK is installed" -Skip:(Test-IsWin22) { + "${env:ProgramFiles(x86)}\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.22621.0\UAP.props" | Should -Exist + } +} diff --git a/images/windows/scripts/tests/Vsix.Tests.ps1 b/images/windows/scripts/tests/Vsix.Tests.ps1 new file mode 100644 index 0000000..28d6636 --- /dev/null +++ b/images/windows/scripts/tests/Vsix.Tests.ps1 @@ -0,0 +1,20 @@ +Describe "Vsix" { + $toolset = Get-ToolsetContent + $requiredVsixPackages = $toolset.visualStudio.vsix + + $allPackages = (Get-VisualStudioInstance).Packages + $testCases = $requiredVsixPackages | ForEach-Object { + $vsixPackage = Get-VsixInfoFromMarketplace $_ + @{ + VsixName = $vsixPackage.ExtensionName + VsixId = $vsixPackage.VsixId + AllPackages = $allPackages + } + } + if ($testCases.Count -gt 0) { + It "Extension is installed" -TestCases $testCases { + $objVsix = $AllPackages | Where-Object { $_.id -eq $VsixId } + $objVsix | Should -Not -BeNullOrEmpty + } + } +} diff --git a/images/windows/scripts/tests/WDK.Tests.ps1 b/images/windows/scripts/tests/WDK.Tests.ps1 new file mode 100644 index 0000000..6a5e61d --- /dev/null +++ b/images/windows/scripts/tests/WDK.Tests.ps1 @@ -0,0 +1,13 @@ +Describe "WDK" { + It "WDK exists" { + $regKey = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" + $installedApplications = Get-ItemProperty -Path $regKey + $WDKVersion = $installedApplications | Where-Object DisplayName -eq 'Windows Driver Kit' | Select-Object -First 1 -ExpandProperty DisplayVersion + $WDKVersion | Should -Not -BeNullOrEmpty + } + + It "Windows Driver Kit VSIX extension" { + $version = Get-VSExtensionVersion -packageName "Microsoft.Windows.DriverKit" + $version | Should -Not -BeNullOrEmpty + } +} diff --git a/images/windows/scripts/tests/WinAppDriver.Tests.ps1 b/images/windows/scripts/tests/WinAppDriver.Tests.ps1 new file mode 100644 index 0000000..3a9d0b4 --- /dev/null +++ b/images/windows/scripts/tests/WinAppDriver.Tests.ps1 @@ -0,0 +1,12 @@ +Describe "WinAppDriver" { + It "WinAppDriver directory exists" { + Test-Path -Path "${env:ProgramFiles(x86)}\Windows Application Driver" | Should -BeTrue + } +} + +Describe "Developer Mode" { + It "Developer Mode is enabled" { + $path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"; + Get-ItemProperty -Path $path | Select-Object -ExpandProperty "AllowDevelopmentWithoutDevLicense" | Should -Be 1 + } +} diff --git a/images/windows/scripts/tests/WindowsFeatures.Tests.ps1 b/images/windows/scripts/tests/WindowsFeatures.Tests.ps1 new file mode 100644 index 0000000..03b56a2 --- /dev/null +++ b/images/windows/scripts/tests/WindowsFeatures.Tests.ps1 @@ -0,0 +1,84 @@ +Describe "WindowsFeatures" { + $windowsFeatures = (Get-ToolsetContent).windowsFeatures + $testCases = $windowsFeatures | ForEach-Object { @{ Name = $_.name; OptionalFeature = $_.optionalFeature } } + + It "Windows Feature is installed" -TestCases $testCases { + if ($OptionalFeature) { + (Get-WindowsOptionalFeature -Online -FeatureName $Name).State | Should -Be "Enabled" + } else { + (Get-WindowsFeature -Name $Name).InstallState | Should -Be "Installed" + } + } + + it "Check WSL is on path" { + (Get-Command -Name 'wsl') | Should -BeTrue + } + + it "Check WLAN service is stopped" { + (Get-Service -Name wlansvc).Status | Should -Be "Stopped" + } +} + +Describe "DiskSpace" { + It "The image has enough disk space"{ + $availableSpaceMB = [math]::Round((Get-PSDrive -Name C).Free / 1MB) + $minimumFreeSpaceMB = 18 * 1024 + + $availableSpaceMB | Should -BeGreaterThan $minimumFreeSpaceMB + } +} + +Describe "DynamicPorts" { + It "Test TCP dynamicport start=49152 num=16384" { + $tcpPorts = Get-NetTCPSetting | Where-Object { $_.SettingName -ne "Automatic" } | Where-Object { + $_.DynamicPortRangeStartPort -ne 49152 -or $_.DynamicPortRangeNumberOfPorts -ne 16384 + } + + $tcpPorts | Should -BeNullOrEmpty + } + + It "Test UDP dynamicport start=49152 num=16384" { + $udpPorts = Get-NetUDPSetting | Where-Object { + $_.DynamicPortRangeStartPort -ne 49152 -or $_.DynamicPortRangeNumberOfPorts -ne 16384 + } + + $udpPorts | Should -BeNullOrEmpty + } +} + +Describe "GDIProcessHandleQuota" { + It "The GDIProcessHandleQuota value is 20000" { + $regPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" + $regPath32 = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows" + (Get-ItemProperty $regPath).GDIProcessHandleQuota | Should -BeExactly 20000 + (Get-ItemProperty $regPath32).GDIProcessHandleQuota | Should -BeExactly 20000 + } +} + +Describe "Test Signed Drivers" { + It "bcdedit testsigning should be Yes" { + "$(bcdedit)" | Should -Match "testsigning\s+Yes" + } +} + +Describe "Windows Updates" { + It "WindowsUpdateDone.txt should exist" { + "$env:windir\WindowsUpdateDone.txt" | Should -Exist + } + + $testCases = Get-WindowsUpdateStates | Sort-Object Title | ForEach-Object { + @{ + Title = $_.Title + State = $_.State + } + } + + It "" -TestCases $testCases { + $expect = "Installed" + if ( $Title -match "Microsoft Defender Antivirus" ) { + $expect = "Installed", "Failed", "Running" + } + + $State | Should -BeIn $expect + } +} diff --git a/images/windows/scripts/tests/Wix.Tests.ps1 b/images/windows/scripts/tests/Wix.Tests.ps1 new file mode 100644 index 0000000..cf6fc2f --- /dev/null +++ b/images/windows/scripts/tests/Wix.Tests.ps1 @@ -0,0 +1,24 @@ +Describe "Wix" { + BeforeAll { + $regKey = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" + $installedApplications = Get-ItemProperty -Path $regKey + $version = ($installedApplications | Where-Object { $_.BundleCachePath -imatch ".*\\WiX\d*\.exe$" } | Select-Object -First 1).DisplayName + } + + It "Wix Toolset version from registry" { + $version | Should -Not -BeNullOrEmpty + } + + It "Wix variable exists" { + $env:WIX | Should -Not -BeNullOrEmpty + } + + It "Wix binaries folder exists" { + Test-Path -Path $(Join-Path -Path $env:WIX -ChildPath "bin") | Should -Be $true + } + + It "Wix binaries folder is in PATH" { + $testPath = Join-Path -Path $env:WIX -ChildPath "bin" + $env:PATH -split ";" | Should -Contain "$testPath" + } +} diff --git a/images/windows/templates/windows-2019.pkr.hcl b/images/windows/templates/windows-2019.pkr.hcl new file mode 100644 index 0000000..09acf8d --- /dev/null +++ b/images/windows/templates/windows-2019.pkr.hcl @@ -0,0 +1,466 @@ +packer { + required_plugins { + azure = { + source = "github.com/hashicorp/azure" + version = "1.4.5" + } + } +} + +locals { + managed_image_name = var.managed_image_name != "" ? var.managed_image_name : "packer-${var.image_os}-${var.image_version}" +} + +variable "agent_tools_directory" { + type = string + default = "C:\\hostedtoolcache\\windows" +} + +variable "allowed_inbound_ip_addresses" { + type = list(string) + default = [] +} + +variable "azure_tags" { + type = map(string) + default = {} +} + +variable "build_resource_group_name" { + type = string + default = "${env("BUILD_RESOURCE_GROUP_NAME")}" +} + +variable "client_cert_path" { + type = string + default = "${env("ARM_CLIENT_CERT_PATH")}" +} + +variable "client_id" { + type = string + default = "${env("ARM_CLIENT_ID")}" +} + +variable "client_secret" { + type = string + default = "${env("ARM_CLIENT_SECRET")}" + sensitive = true +} + +variable "helper_script_folder" { + type = string + default = "C:\\Program Files\\WindowsPowerShell\\Modules\\" +} + +variable "image_folder" { + type = string + default = "C:\\image" +} + +variable "image_os" { + type = string + default = "win19" +} + +variable "image_version" { + type = string + default = "dev" +} + +variable "imagedata_file" { + type = string + default = "C:\\imagedata.json" +} + +variable "install_password" { + type = string + default = "" + sensitive = true +} + +variable "install_user" { + type = string + default = "installer" +} + +variable "location" { + type = string + default = "${env("ARM_RESOURCE_LOCATION")}" +} + +variable "managed_image_name" { + type = string + default = "" +} + +variable "managed_image_resource_group_name" { + type = string + default = "${env("ARM_RESOURCE_GROUP")}" +} + +variable "managed_image_storage_account_type" { + type = string + default = "Premium_LRS" +} + +variable "object_id" { + type = string + default = "${env("ARM_OBJECT_ID")}" +} + +variable "private_virtual_network_with_public_ip" { + type = bool + default = false +} + +variable "subscription_id" { + type = string + default = "${env("ARM_SUBSCRIPTION_ID")}" +} + +variable "temp_resource_group_name" { + type = string + default = "${env("TEMP_RESOURCE_GROUP_NAME")}" +} + +variable "tenant_id" { + type = string + default = "${env("ARM_TENANT_ID")}" +} + +variable "virtual_network_name" { + type = string + default = "${env("VNET_NAME")}" +} + +variable "virtual_network_resource_group_name" { + type = string + default = "${env("VNET_RESOURCE_GROUP")}" +} + +variable "virtual_network_subnet_name" { + type = string + default = "${env("VNET_SUBNET")}" +} + +variable "vm_size" { + type = string + default = "Standard_F8s_v2" +} + +source "azure-arm" "image" { + allowed_inbound_ip_addresses = "${var.allowed_inbound_ip_addresses}" + build_resource_group_name = "${var.build_resource_group_name}" + client_cert_path = "${var.client_cert_path}" + client_id = "${var.client_id}" + client_secret = "${var.client_secret}" + communicator = "winrm" + image_offer = "WindowsServer" + image_publisher = "MicrosoftWindowsServer" + image_sku = "2019-Datacenter" + location = "${var.location}" + managed_image_name = "${local.managed_image_name}" + managed_image_resource_group_name = "${var.managed_image_resource_group_name}" + managed_image_storage_account_type = "${var.managed_image_storage_account_type}" + object_id = "${var.object_id}" + os_disk_size_gb = "256" + os_type = "Windows" + private_virtual_network_with_public_ip = "${var.private_virtual_network_with_public_ip}" + subscription_id = "${var.subscription_id}" + temp_resource_group_name = "${var.temp_resource_group_name}" + tenant_id = "${var.tenant_id}" + virtual_network_name = "${var.virtual_network_name}" + virtual_network_resource_group_name = "${var.virtual_network_resource_group_name}" + virtual_network_subnet_name = "${var.virtual_network_subnet_name}" + vm_size = "${var.vm_size}" + winrm_insecure = "true" + winrm_use_ssl = "true" + winrm_username = "packer" + + dynamic "azure_tag" { + for_each = var.azure_tags + content { + name = azure_tag.key + value = azure_tag.value + } + } +} + +build { + sources = ["source.azure-arm.image"] + + provisioner "powershell" { + inline = ["New-Item -Path ${var.image_folder} -ItemType Directory -Force"] + } + + provisioner "file" { + destination = "${var.image_folder}\\" + sources = [ + "${path.root}/../assets", + "${path.root}/../scripts", + "${path.root}/../toolsets" + ] + } + + provisioner "file" { + destination = "${var.image_folder}\\scripts\\docs-gen\\" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "powershell" { + inline = [ + "Move-Item '${var.image_folder}\\assets\\post-gen' 'C:\\post-generation'", + "Remove-Item -Recurse '${var.image_folder}\\assets'", + "Move-Item '${var.image_folder}\\scripts\\docs-gen' '${var.image_folder}\\SoftwareReport'", + "Move-Item '${var.image_folder}\\scripts\\helpers' '${var.helper_script_folder}\\ImageHelpers'", + "New-Item -Type Directory -Path '${var.helper_script_folder}\\TestsHelpers\\'", + "Move-Item '${var.image_folder}\\scripts\\tests\\Helpers.psm1' '${var.helper_script_folder}\\TestsHelpers\\TestsHelpers.psm1'", + "Move-Item '${var.image_folder}\\scripts\\tests' '${var.image_folder}\\tests'", + "Remove-Item -Recurse '${var.image_folder}\\scripts'", + "Move-Item '${var.image_folder}\\toolsets\\toolset-2019.json' '${var.image_folder}\\toolset.json'", + "Remove-Item -Recurse '${var.image_folder}\\toolsets'" + ] + } + + provisioner "windows-shell" { + inline = [ + "net user ${var.install_user} ${var.install_password} /add /passwordchg:no /passwordreq:yes /active:yes /Y", + "net localgroup Administrators ${var.install_user} /add", + "winrm set winrm/config/service/auth @{Basic=\"true\"}", + "winrm get winrm/config/service/auth" + ] + } + + provisioner "powershell" { + inline = ["if (-not ((net localgroup Administrators) -contains '${var.install_user}')) { exit 1 }"] + } + + provisioner "powershell" { + elevated_password = "${var.install_password}" + elevated_user = "${var.install_user}" + inline = ["bcdedit.exe /set TESTSIGNING ON"] + } + + provisioner "powershell" { + elevated_password = "${var.install_password}" + elevated_user = "${var.install_user}" + scripts = ["${path.root}/../scripts/build/Install-NET48.ps1"] + valid_exit_codes = [0, 3010] + } + + provisioner "windows-restart" { + restart_timeout = "10m" + } + + provisioner "powershell" { + 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}"] + execution_policy = "unrestricted" + scripts = [ + "${path.root}/../scripts/build/Configure-WindowsDefender.ps1", + "${path.root}/../scripts/build/Configure-PowerShell.ps1", + "${path.root}/../scripts/build/Install-PowerShellModules.ps1", + "${path.root}/../scripts/build/Install-WindowsFeatures.ps1", + "${path.root}/../scripts/build/Install-Chocolatey.ps1", + "${path.root}/../scripts/build/Configure-BaseImage.ps1", + "${path.root}/../scripts/build/Configure-ImageDataFile.ps1", + "${path.root}/../scripts/build/Configure-SystemEnvironment.ps1", + "${path.root}/../scripts/build/Configure-DotnetSecureChannel.ps1" + ] + } + + provisioner "windows-restart" { + restart_timeout = "30m" + } + + provisioner "powershell" { + inline = ["Set-Service -Name wlansvc -StartupType Manual", "if ($(Get-Service -Name wlansvc).Status -eq 'Running') { Stop-Service -Name wlansvc}"] + } + + provisioner "powershell" { + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-VCRedist.ps1", + "${path.root}/../scripts/build/Install-Docker.ps1", + "${path.root}/../scripts/build/Install-DockerWinCred.ps1", + "${path.root}/../scripts/build/Install-DockerCompose.ps1", + "${path.root}/../scripts/build/Install-PowershellCore.ps1", + "${path.root}/../scripts/build/Install-WebPlatformInstaller.ps1", + "${path.root}/../scripts/build/Install-Runner.ps1", + "${path.root}/../scripts/build/Install-TortoiseSvn.ps1" + ] + } + + provisioner "windows-restart" { + restart_timeout = "10m" + } + + provisioner "powershell" { + elevated_password = "${var.install_password}" + elevated_user = "${var.install_user}" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-VisualStudio.ps1", + "${path.root}/../scripts/build/Install-KubernetesTools.ps1", + "${path.root}/../scripts/build/Install-NET48-devpack.ps1" + ] + valid_exit_codes = [0, 3010] + } + + provisioner "powershell" { + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-Wix.ps1", + "${path.root}/../scripts/build/Install-WDK.ps1", + "${path.root}/../scripts/build/Install-VSExtensions.ps1", + "${path.root}/../scripts/build/Install-AzureCli.ps1", + "${path.root}/../scripts/build/Install-AzureDevOpsCli.ps1", + "${path.root}/../scripts/build/Install-ChocolateyPackages.ps1", + "${path.root}/../scripts/build/Install-JavaTools.ps1", + "${path.root}/../scripts/build/Install-Kotlin.ps1", + "${path.root}/../scripts/build/Install-OpenSSL.ps1" + ] + } + + provisioner "powershell" { + execution_policy = "remotesigned" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = ["${path.root}/../scripts/build/Install-ServiceFabricSDK.ps1"] + } + + provisioner "windows-restart" { + restart_timeout = "10m" + } + + provisioner "windows-shell" { + inline = ["wmic product where \"name like '%%microsoft azure powershell%%'\" call uninstall /nointeractive"] + } + + provisioner "powershell" { + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-ActionsCache.ps1", + "${path.root}/../scripts/build/Install-Ruby.ps1", + "${path.root}/../scripts/build/Install-PyPy.ps1", + "${path.root}/../scripts/build/Install-Toolset.ps1", + "${path.root}/../scripts/build/Configure-Toolset.ps1", + "${path.root}/../scripts/build/Install-NodeJS.ps1", + "${path.root}/../scripts/build/Install-AndroidSDK.ps1", + "${path.root}/../scripts/build/Install-PowershellAzModules.ps1", + "${path.root}/../scripts/build/Install-Pipx.ps1", + "${path.root}/../scripts/build/Install-Git.ps1", + "${path.root}/../scripts/build/Install-GitHub-CLI.ps1", + "${path.root}/../scripts/build/Install-PHP.ps1", + "${path.root}/../scripts/build/Install-Rust.ps1", + "${path.root}/../scripts/build/Install-Sbt.ps1", + "${path.root}/../scripts/build/Install-Chrome.ps1", + "${path.root}/../scripts/build/Install-EdgeDriver.ps1", + "${path.root}/../scripts/build/Install-Firefox.ps1", + "${path.root}/../scripts/build/Install-Selenium.ps1", + "${path.root}/../scripts/build/Install-IEWebDriver.ps1", + "${path.root}/../scripts/build/Install-Apache.ps1", + "${path.root}/../scripts/build/Install-Nginx.ps1", + "${path.root}/../scripts/build/Install-Msys2.ps1", + "${path.root}/../scripts/build/Install-WinAppDriver.ps1", + "${path.root}/../scripts/build/Install-R.ps1", + "${path.root}/../scripts/build/Install-AWSTools.ps1", + "${path.root}/../scripts/build/Install-DACFx.ps1", + "${path.root}/../scripts/build/Install-MysqlCli.ps1", + "${path.root}/../scripts/build/Install-SQLPowerShellTools.ps1", + "${path.root}/../scripts/build/Install-SQLOLEDBDriver.ps1", + "${path.root}/../scripts/build/Install-DotnetSDK.ps1", + "${path.root}/../scripts/build/Install-Mingw64.ps1", + "${path.root}/../scripts/build/Install-Haskell.ps1", + "${path.root}/../scripts/build/Install-Stack.ps1", + "${path.root}/../scripts/build/Install-Miniconda.ps1", + "${path.root}/../scripts/build/Install-AzureCosmosDbEmulator.ps1", + "${path.root}/../scripts/build/Install-Mercurial.ps1", + "${path.root}/../scripts/build/Install-Zstd.ps1", + "${path.root}/../scripts/build/Install-NSIS.ps1", + "${path.root}/../scripts/build/Install-CloudFoundryCli.ps1", + "${path.root}/../scripts/build/Install-Vcpkg.ps1", + "${path.root}/../scripts/build/Install-PostgreSQL.ps1", + "${path.root}/../scripts/build/Install-Bazel.ps1", + "${path.root}/../scripts/build/Install-AliyunCli.ps1", + "${path.root}/../scripts/build/Install-RootCA.ps1", + "${path.root}/../scripts/build/Install-MongoDB.ps1", + "${path.root}/../scripts/build/Install-GoogleCloudCLI.ps1", + "${path.root}/../scripts/build/Install-CodeQLBundle.ps1", + "${path.root}/../scripts/build/Install-BizTalkBuildComponent.ps1", + "${path.root}/../scripts/build/Configure-Diagnostics.ps1", + "${path.root}/../scripts/build/Configure-DynamicPort.ps1", + "${path.root}/../scripts/build/Configure-GDIProcessHandleQuota.ps1", + "${path.root}/../scripts/build/Configure-Shell.ps1", + "${path.root}/../scripts/build/Configure-DeveloperMode.ps1", + "${path.root}/../scripts/build/Install-LLVM.ps1" + ] + } + + provisioner "powershell" { + elevated_password = "${var.install_password}" + elevated_user = "${var.install_user}" + scripts = ["${path.root}/../scripts/build/Install-WindowsUpdates.ps1"] + } + + provisioner "windows-restart" { + check_registry = true + restart_check_command = "powershell -command \"& {if ((-not (Get-Process TiWorker.exe -ErrorAction SilentlyContinue)) -and (-not [System.Environment]::HasShutdownStarted) ) { Write-Output 'Restart complete' }}\"" + restart_timeout = "30m" + } + + provisioner "powershell" { + pause_before = "2m0s" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-WindowsUpdatesAfterReboot.ps1", + "${path.root}/../scripts/tests/RunAll-Tests.ps1" + ] + } + + provisioner "powershell" { + inline = ["if (-not (Test-Path ${var.image_folder}\\tests\\testResults.xml)) { throw '${var.image_folder}\\tests\\testResults.xml not found' }"] + } + + provisioner "powershell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "IMAGE_FOLDER=${var.image_folder}"] + inline = ["pwsh -File '${var.image_folder}\\SoftwareReport\\Generate-SoftwareReport.ps1'"] + } + + provisioner "powershell" { + 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' }"] + } + + provisioner "file" { + destination = "${path.root}/../Windows2019-Readme.md" + direction = "download" + source = "C:\\software-report.md" + } + + provisioner "file" { + destination = "${path.root}/../software-report.json" + direction = "download" + source = "C:\\software-report.json" + } + + provisioner "powershell" { + environment_vars = ["INSTALL_USER=${var.install_user}"] + scripts = [ + "${path.root}/../scripts/build/Install-NativeImages.ps1", + "${path.root}/../scripts/build/Configure-System.ps1", + "${path.root}/../scripts/build/Configure-User.ps1" + ] + skip_clean = true + } + + provisioner "windows-restart" { + restart_timeout = "10m" + } + + provisioner "powershell" { + inline = [ + "if( Test-Path $env:SystemRoot\\System32\\Sysprep\\unattend.xml ){ rm $env:SystemRoot\\System32\\Sysprep\\unattend.xml -Force}", + "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit", + "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 } }" + ] + } + +} diff --git a/images/windows/templates/windows-2022.pkr.hcl b/images/windows/templates/windows-2022.pkr.hcl new file mode 100644 index 0000000..c37a362 --- /dev/null +++ b/images/windows/templates/windows-2022.pkr.hcl @@ -0,0 +1,461 @@ +packer { + required_plugins { + azure = { + source = "github.com/hashicorp/azure" + version = "1.4.5" + } + } +} + +locals { + managed_image_name = var.managed_image_name != "" ? var.managed_image_name : "packer-${var.image_os}-${var.image_version}" +} + +variable "agent_tools_directory" { + type = string + default = "C:\\hostedtoolcache\\windows" +} + +variable "allowed_inbound_ip_addresses" { + type = list(string) + default = [] +} + +variable "azure_tags" { + type = map(string) + default = {} +} + +variable "build_resource_group_name" { + type = string + default = "${env("BUILD_RESOURCE_GROUP_NAME")}" +} + +variable "client_cert_path" { + type = string + default = "${env("ARM_CLIENT_CERT_PATH")}" +} + +variable "client_id" { + type = string + default = "${env("ARM_CLIENT_ID")}" +} + +variable "client_secret" { + type = string + default = "${env("ARM_CLIENT_SECRET")}" + sensitive = true +} + +variable "helper_script_folder" { + type = string + default = "C:\\Program Files\\WindowsPowerShell\\Modules\\" +} + +variable "image_folder" { + type = string + default = "C:\\image" +} + +variable "image_os" { + type = string + default = "win22" +} + +variable "image_version" { + type = string + default = "dev" +} + +variable "imagedata_file" { + type = string + default = "C:\\imagedata.json" +} + +variable "install_password" { + type = string + default = "" + sensitive = true +} + +variable "install_user" { + type = string + default = "installer" +} + +variable "location" { + type = string + default = "${env("ARM_RESOURCE_LOCATION")}" +} + +variable "managed_image_name" { + type = string + default = "" +} + +variable "managed_image_resource_group_name" { + type = string + default = "${env("ARM_RESOURCE_GROUP")}" +} + +variable "managed_image_storage_account_type" { + type = string + default = "Premium_LRS" +} + +variable "object_id" { + type = string + default = "${env("ARM_OBJECT_ID")}" +} + +variable "private_virtual_network_with_public_ip" { + type = bool + default = false +} + +variable "subscription_id" { + type = string + default = "${env("ARM_SUBSCRIPTION_ID")}" +} + +variable "temp_resource_group_name" { + type = string + default = "${env("TEMP_RESOURCE_GROUP_NAME")}" +} + +variable "tenant_id" { + type = string + default = "${env("ARM_TENANT_ID")}" +} + +variable "virtual_network_name" { + type = string + default = "${env("VNET_NAME")}" +} + +variable "virtual_network_resource_group_name" { + type = string + default = "${env("VNET_RESOURCE_GROUP")}" +} + +variable "virtual_network_subnet_name" { + type = string + default = "${env("VNET_SUBNET")}" +} + +variable "vm_size" { + type = string + default = "Standard_F8s_v2" +} + +source "azure-arm" "image" { + allowed_inbound_ip_addresses = "${var.allowed_inbound_ip_addresses}" + build_resource_group_name = "${var.build_resource_group_name}" + client_cert_path = "${var.client_cert_path}" + client_id = "${var.client_id}" + client_secret = "${var.client_secret}" + communicator = "winrm" + image_offer = "WindowsServer" + image_publisher = "MicrosoftWindowsServer" + image_sku = "2022-Datacenter" + location = "${var.location}" + managed_image_name = "${local.managed_image_name}" + managed_image_resource_group_name = "${var.managed_image_resource_group_name}" + managed_image_storage_account_type = "${var.managed_image_storage_account_type}" + object_id = "${var.object_id}" + os_disk_size_gb = "256" + os_type = "Windows" + private_virtual_network_with_public_ip = "${var.private_virtual_network_with_public_ip}" + subscription_id = "${var.subscription_id}" + temp_resource_group_name = "${var.temp_resource_group_name}" + tenant_id = "${var.tenant_id}" + virtual_network_name = "${var.virtual_network_name}" + virtual_network_resource_group_name = "${var.virtual_network_resource_group_name}" + virtual_network_subnet_name = "${var.virtual_network_subnet_name}" + vm_size = "${var.vm_size}" + winrm_insecure = "true" + winrm_use_ssl = "true" + winrm_username = "packer" + + dynamic "azure_tag" { + for_each = var.azure_tags + content { + name = azure_tag.key + value = azure_tag.value + } + } +} + +build { + sources = ["source.azure-arm.image"] + + provisioner "powershell" { + inline = ["New-Item -Path ${var.image_folder} -ItemType Directory -Force"] + } + + provisioner "file" { + destination = "${var.image_folder}\\" + sources = [ + "${path.root}/../assets", + "${path.root}/../scripts", + "${path.root}/../toolsets" + ] + } + + provisioner "file" { + destination = "${var.image_folder}\\scripts\\docs-gen\\" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "powershell" { + inline = [ + "Move-Item '${var.image_folder}\\assets\\post-gen' 'C:\\post-generation'", + "Remove-Item -Recurse '${var.image_folder}\\assets'", + "Move-Item '${var.image_folder}\\scripts\\docs-gen' '${var.image_folder}\\SoftwareReport'", + "Move-Item '${var.image_folder}\\scripts\\helpers' '${var.helper_script_folder}\\ImageHelpers'", + "New-Item -Type Directory -Path '${var.helper_script_folder}\\TestsHelpers\\'", + "Move-Item '${var.image_folder}\\scripts\\tests\\Helpers.psm1' '${var.helper_script_folder}\\TestsHelpers\\TestsHelpers.psm1'", + "Move-Item '${var.image_folder}\\scripts\\tests' '${var.image_folder}\\tests'", + "Remove-Item -Recurse '${var.image_folder}\\scripts'", + "Move-Item '${var.image_folder}\\toolsets\\toolset-2022.json' '${var.image_folder}\\toolset.json'", + "Remove-Item -Recurse '${var.image_folder}\\toolsets'" + ] + } + + provisioner "windows-shell" { + inline = [ + "net user ${var.install_user} ${var.install_password} /add /passwordchg:no /passwordreq:yes /active:yes /Y", + "net localgroup Administrators ${var.install_user} /add", + "winrm set winrm/config/service/auth @{Basic=\"true\"}", + "winrm get winrm/config/service/auth" + ] + } + + provisioner "powershell" { + inline = ["if (-not ((net localgroup Administrators) -contains '${var.install_user}')) { exit 1 }"] + } + + provisioner "powershell" { + elevated_password = "${var.install_password}" + elevated_user = "${var.install_user}" + inline = ["bcdedit.exe /set TESTSIGNING ON"] + } + + provisioner "powershell" { + 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}"] + execution_policy = "unrestricted" + scripts = [ + "${path.root}/../scripts/build/Configure-WindowsDefender.ps1", + "${path.root}/../scripts/build/Configure-PowerShell.ps1", + "${path.root}/../scripts/build/Install-PowerShellModules.ps1", + "${path.root}/../scripts/build/Install-WindowsFeatures.ps1", + "${path.root}/../scripts/build/Install-Chocolatey.ps1", + "${path.root}/../scripts/build/Configure-BaseImage.ps1", + "${path.root}/../scripts/build/Configure-ImageDataFile.ps1", + "${path.root}/../scripts/build/Configure-SystemEnvironment.ps1", + "${path.root}/../scripts/build/Configure-DotnetSecureChannel.ps1" + ] + } + + provisioner "windows-restart" { + check_registry = true + restart_check_command = "powershell -command \"& {while ( (Get-WindowsOptionalFeature -Online -FeatureName Containers -ErrorAction SilentlyContinue).State -ne 'Enabled' ) { Start-Sleep 30; Write-Output 'InProgress' }}\"" + restart_timeout = "10m" + } + + provisioner "powershell" { + inline = ["Set-Service -Name wlansvc -StartupType Manual", "if ($(Get-Service -Name wlansvc).Status -eq 'Running') { Stop-Service -Name wlansvc}"] + } + + provisioner "powershell" { + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-Docker.ps1", + "${path.root}/../scripts/build/Install-DockerWinCred.ps1", + "${path.root}/../scripts/build/Install-DockerCompose.ps1", + "${path.root}/../scripts/build/Install-PowershellCore.ps1", + "${path.root}/../scripts/build/Install-WebPlatformInstaller.ps1", + "${path.root}/../scripts/build/Install-Runner.ps1", + "${path.root}/../scripts/build/Install-TortoiseSvn.ps1" + ] + } + + provisioner "windows-restart" { + restart_timeout = "30m" + } + + provisioner "powershell" { + elevated_password = "${var.install_password}" + elevated_user = "${var.install_user}" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-VisualStudio.ps1", + "${path.root}/../scripts/build/Install-KubernetesTools.ps1" + ] + valid_exit_codes = [0, 3010] + } + + provisioner "windows-restart" { + check_registry = true + restart_timeout = "10m" + } + + provisioner "powershell" { + pause_before = "2m0s" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-Wix.ps1", + "${path.root}/../scripts/build/Install-WDK.ps1", + "${path.root}/../scripts/build/Install-VSExtensions.ps1", + "${path.root}/../scripts/build/Install-AzureCli.ps1", + "${path.root}/../scripts/build/Install-AzureDevOpsCli.ps1", + "${path.root}/../scripts/build/Install-ChocolateyPackages.ps1", + "${path.root}/../scripts/build/Install-JavaTools.ps1", + "${path.root}/../scripts/build/Install-Kotlin.ps1", + "${path.root}/../scripts/build/Install-OpenSSL.ps1" + ] + } + + provisioner "powershell" { + execution_policy = "remotesigned" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = ["${path.root}/../scripts/build/Install-ServiceFabricSDK.ps1"] + } + + provisioner "windows-restart" { + restart_timeout = "10m" + } + + provisioner "windows-shell" { + inline = ["wmic product where \"name like '%%microsoft azure powershell%%'\" call uninstall /nointeractive"] + } + + provisioner "powershell" { + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-ActionsCache.ps1", + "${path.root}/../scripts/build/Install-Ruby.ps1", + "${path.root}/../scripts/build/Install-PyPy.ps1", + "${path.root}/../scripts/build/Install-Toolset.ps1", + "${path.root}/../scripts/build/Configure-Toolset.ps1", + "${path.root}/../scripts/build/Install-NodeJS.ps1", + "${path.root}/../scripts/build/Install-AndroidSDK.ps1", + "${path.root}/../scripts/build/Install-PowershellAzModules.ps1", + "${path.root}/../scripts/build/Install-Pipx.ps1", + "${path.root}/../scripts/build/Install-Git.ps1", + "${path.root}/../scripts/build/Install-GitHub-CLI.ps1", + "${path.root}/../scripts/build/Install-PHP.ps1", + "${path.root}/../scripts/build/Install-Rust.ps1", + "${path.root}/../scripts/build/Install-Sbt.ps1", + "${path.root}/../scripts/build/Install-Chrome.ps1", + "${path.root}/../scripts/build/Install-EdgeDriver.ps1", + "${path.root}/../scripts/build/Install-Firefox.ps1", + "${path.root}/../scripts/build/Install-Selenium.ps1", + "${path.root}/../scripts/build/Install-IEWebDriver.ps1", + "${path.root}/../scripts/build/Install-Apache.ps1", + "${path.root}/../scripts/build/Install-Nginx.ps1", + "${path.root}/../scripts/build/Install-Msys2.ps1", + "${path.root}/../scripts/build/Install-WinAppDriver.ps1", + "${path.root}/../scripts/build/Install-R.ps1", + "${path.root}/../scripts/build/Install-AWSTools.ps1", + "${path.root}/../scripts/build/Install-DACFx.ps1", + "${path.root}/../scripts/build/Install-MysqlCli.ps1", + "${path.root}/../scripts/build/Install-SQLPowerShellTools.ps1", + "${path.root}/../scripts/build/Install-SQLOLEDBDriver.ps1", + "${path.root}/../scripts/build/Install-DotnetSDK.ps1", + "${path.root}/../scripts/build/Install-Mingw64.ps1", + "${path.root}/../scripts/build/Install-Haskell.ps1", + "${path.root}/../scripts/build/Install-Stack.ps1", + "${path.root}/../scripts/build/Install-Miniconda.ps1", + "${path.root}/../scripts/build/Install-AzureCosmosDbEmulator.ps1", + "${path.root}/../scripts/build/Install-Mercurial.ps1", + "${path.root}/../scripts/build/Install-Zstd.ps1", + "${path.root}/../scripts/build/Install-NSIS.ps1", + "${path.root}/../scripts/build/Install-Vcpkg.ps1", + "${path.root}/../scripts/build/Install-PostgreSQL.ps1", + "${path.root}/../scripts/build/Install-Bazel.ps1", + "${path.root}/../scripts/build/Install-AliyunCli.ps1", + "${path.root}/../scripts/build/Install-RootCA.ps1", + "${path.root}/../scripts/build/Install-MongoDB.ps1", + "${path.root}/../scripts/build/Install-CodeQLBundle.ps1", + "${path.root}/../scripts/build/Configure-Diagnostics.ps1" + ] + } + + provisioner "powershell" { + elevated_password = "${var.install_password}" + elevated_user = "${var.install_user}" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-WindowsUpdates.ps1", + "${path.root}/../scripts/build/Configure-DynamicPort.ps1", + "${path.root}/../scripts/build/Configure-GDIProcessHandleQuota.ps1", + "${path.root}/../scripts/build/Configure-Shell.ps1", + "${path.root}/../scripts/build/Configure-DeveloperMode.ps1", + "${path.root}/../scripts/build/Install-LLVM.ps1" + ] + } + + provisioner "windows-restart" { + check_registry = true + restart_check_command = "powershell -command \"& {if ((-not (Get-Process TiWorker.exe -ErrorAction SilentlyContinue)) -and (-not [System.Environment]::HasShutdownStarted) ) { Write-Output 'Restart complete' }}\"" + restart_timeout = "30m" + } + + provisioner "powershell" { + pause_before = "2m0s" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-WindowsUpdatesAfterReboot.ps1", + "${path.root}/../scripts/tests/RunAll-Tests.ps1" + ] + } + + provisioner "powershell" { + inline = ["if (-not (Test-Path ${var.image_folder}\\tests\\testResults.xml)) { throw '${var.image_folder}\\tests\\testResults.xml not found' }"] + } + + provisioner "powershell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "IMAGE_FOLDER=${var.image_folder}"] + inline = ["pwsh -File '${var.image_folder}\\SoftwareReport\\Generate-SoftwareReport.ps1'"] + } + + provisioner "powershell" { + 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' }"] + } + + provisioner "file" { + destination = "${path.root}/../Windows2022-Readme.md" + direction = "download" + source = "C:\\software-report.md" + } + + provisioner "file" { + destination = "${path.root}/../software-report.json" + direction = "download" + source = "C:\\software-report.json" + } + + provisioner "powershell" { + environment_vars = ["INSTALL_USER=${var.install_user}"] + scripts = [ + "${path.root}/../scripts/build/Install-NativeImages.ps1", + "${path.root}/../scripts/build/Configure-System.ps1", + "${path.root}/../scripts/build/Configure-User.ps1" + ] + skip_clean = true + } + + provisioner "windows-restart" { + restart_timeout = "10m" + } + + provisioner "powershell" { + inline = [ + "if( Test-Path $env:SystemRoot\\System32\\Sysprep\\unattend.xml ){ rm $env:SystemRoot\\System32\\Sysprep\\unattend.xml -Force}", + "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /mode:vm /quiet /quit", + "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 } }" + ] + } + +} diff --git a/images/windows/templates/windows-2025.pkr.hcl b/images/windows/templates/windows-2025.pkr.hcl new file mode 100644 index 0000000..a9f3899 --- /dev/null +++ b/images/windows/templates/windows-2025.pkr.hcl @@ -0,0 +1,496 @@ +packer { + required_plugins { + azure = { + source = "github.com/hashicorp/azure" + version = "1.4.5" + } + } +} + +locals { + managed_image_name = var.managed_image_name != "" ? var.managed_image_name : "packer-${var.image_os}-${var.image_version}" +} + +variable "agent_tools_directory" { + type = string + default = "C:\\hostedtoolcache\\windows" +} + +variable "allowed_inbound_ip_addresses" { + type = list(string) + default = [] +} + +variable "azure_tags" { + type = map(string) + default = {} +} + +variable "build_resource_group_name" { + type = string + default = "${env("BUILD_RESOURCE_GROUP_NAME")}" +} + +variable "client_cert_path" { + type = string + default = "${env("ARM_CLIENT_CERT_PATH")}" +} + +variable "client_id" { + type = string + default = "${env("ARM_CLIENT_ID")}" +} + +variable "client_secret" { + type = string + default = "${env("ARM_CLIENT_SECRET")}" + sensitive = true +} + +variable "helper_script_folder" { + type = string + default = "C:\\Program Files\\WindowsPowerShell\\Modules\\" +} + +variable "image_folder" { + type = string + default = "C:\\image" +} + +variable "image_os" { + type = string + default = "win25" +} + +variable "image_version" { + type = string + default = "dev" +} + +variable "imagedata_file" { + type = string + default = "C:\\imagedata.json" +} + +variable "install_password" { + type = string + default = "" + sensitive = true +} + +variable "install_user" { + type = string + default = "installer" +} + +variable "location" { + type = string + default = "${env("ARM_RESOURCE_LOCATION")}" +} + +variable "managed_image_name" { + type = string + default = "" +} + +variable "managed_image_resource_group_name" { + type = string + default = "${env("ARM_RESOURCE_GROUP")}" +} + +variable "managed_image_storage_account_type" { + type = string + default = "Premium_LRS" +} + +variable "object_id" { + type = string + default = "${env("ARM_OBJECT_ID")}" +} + +variable "private_virtual_network_with_public_ip" { + type = bool + default = false +} + +variable "subscription_id" { + type = string + default = "${env("ARM_SUBSCRIPTION_ID")}" +} + +variable "temp_resource_group_name" { + type = string + default = "${env("TEMP_RESOURCE_GROUP_NAME")}" +} + +variable "tenant_id" { + type = string + default = "${env("ARM_TENANT_ID")}" +} + +variable "virtual_network_name" { + type = string + default = "${env("VNET_NAME")}" +} + +variable "virtual_network_resource_group_name" { + type = string + default = "${env("VNET_RESOURCE_GROUP")}" +} + +variable "virtual_network_subnet_name" { + type = string + default = "${env("VNET_SUBNET")}" +} + +variable "vm_size" { + type = string + default = "Standard_F8s_v2" +} + + +source "azure-arm" "image" { + allowed_inbound_ip_addresses = "${var.allowed_inbound_ip_addresses}" + build_resource_group_name = "${var.build_resource_group_name}" + client_cert_path = "${var.client_cert_path}" + client_id = "${var.client_id}" + client_secret = "${var.client_secret}" + communicator = "winrm" + image_offer = "WindowsServer" + image_publisher = "MicrosoftWindowsServer" + image_sku = "2025-Datacenter" + location = "${var.location}" + managed_image_name = "${local.managed_image_name}" + managed_image_resource_group_name = "${var.managed_image_resource_group_name}" + managed_image_storage_account_type = "${var.managed_image_storage_account_type}" + object_id = "${var.object_id}" + os_disk_size_gb = "256" + os_type = "Windows" + private_virtual_network_with_public_ip = "${var.private_virtual_network_with_public_ip}" + subscription_id = "${var.subscription_id}" + temp_resource_group_name = "${var.temp_resource_group_name}" + tenant_id = "${var.tenant_id}" + virtual_network_name = "${var.virtual_network_name}" + virtual_network_resource_group_name = "${var.virtual_network_resource_group_name}" + virtual_network_subnet_name = "${var.virtual_network_subnet_name}" + vm_size = "${var.vm_size}" + winrm_insecure = "true" + winrm_use_ssl = "true" + winrm_username = "packer" + + # plan_info { + # plan_name = "windows-server-2025-azure-edition-hotpatch" + # plan_product = "microsoftserveroperatingsystems-previews" + # plan_publisher = "microsoftwindowsserver" + # } + dynamic "azure_tag" { + for_each = var.azure_tags + content { + name = azure_tag.key + value = azure_tag.value + } + } +} + +build { + sources = ["source.azure-arm.image"] + + provisioner "powershell" { + inline = ["New-Item -Path ${var.image_folder} -ItemType Directory -Force"] + } + + provisioner "file" { + destination = "${var.image_folder}\\" + sources = [ + "${path.root}/../assets", + "${path.root}/../scripts", + "${path.root}/../toolsets" + ] + } + + provisioner "file" { + destination = "${var.image_folder}\\scripts\\docs-gen\\" + source = "${path.root}/../../../helpers/software-report-base" + } + + provisioner "powershell" { + inline = [ + "Move-Item '${var.image_folder}\\assets\\post-gen' 'C:\\post-generation'", + "Remove-Item -Recurse '${var.image_folder}\\assets'", + "Move-Item '${var.image_folder}\\scripts\\docs-gen' '${var.image_folder}\\SoftwareReport'", + "Move-Item '${var.image_folder}\\scripts\\helpers' '${var.helper_script_folder}\\ImageHelpers'", + "New-Item -Type Directory -Path '${var.helper_script_folder}\\TestsHelpers\\'", + "Move-Item '${var.image_folder}\\scripts\\tests\\Helpers.psm1' '${var.helper_script_folder}\\TestsHelpers\\TestsHelpers.psm1'", + "Move-Item '${var.image_folder}\\scripts\\tests' '${var.image_folder}\\tests'", + "Remove-Item -Recurse '${var.image_folder}\\scripts'", + "Move-Item '${var.image_folder}\\toolsets\\toolset-2025.json' '${var.image_folder}\\toolset.json'", + "Remove-Item -Recurse '${var.image_folder}\\toolsets'" + ] + } + + provisioner "windows-shell" { + inline = [ + "net user ${var.install_user} ${var.install_password} /add /passwordchg:no /passwordreq:yes /active:yes /Y", + "net localgroup Administrators ${var.install_user} /add", + "winrm set winrm/config/service/auth @{Basic=\"true\"}", + "winrm get winrm/config/service/auth" + ] + } + + provisioner "powershell" { + inline = ["if (-not ((net localgroup Administrators) -contains '${var.install_user}')) { exit 1 }"] + } + + provisioner "powershell" { + elevated_password = "${var.install_password}" + elevated_user = "${var.install_user}" + inline = [ + "bcdedit.exe /set TESTSIGNING ON", + "bcdedit.exe /set hypervisorlaunchtype auto" + ] + } + + provisioner "windows-restart" { + restart_timeout = "10m" + } + + provisioner "powershell" { + 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}"] + execution_policy = "unrestricted" + scripts = [ + "${path.root}/../scripts/build/Configure-WindowsDefender.ps1", + "${path.root}/../scripts/build/Configure-PowerShell.ps1", + "${path.root}/../scripts/build/Install-WindowsFeatures.ps1", + "${path.root}/../scripts/build/Install-PowerShellModules.ps1", + "${path.root}/../scripts/build/Install-Chocolatey.ps1", + "${path.root}/../scripts/build/Configure-BaseImage.ps1", + "${path.root}/../scripts/build/Configure-ImageDataFile.ps1", + "${path.root}/../scripts/build/Configure-SystemEnvironment.ps1", + "${path.root}/../scripts/build/Configure-DotnetSecureChannel.ps1" + ] + } + + provisioner "windows-restart" { + check_registry = true + restart_check_command = "powershell -command \"& {while ( (Get-WindowsOptionalFeature -Online -FeatureName Containers -ErrorAction SilentlyContinue).State -ne 'Enabled' ) { Start-Sleep 30; Write-Output 'InProgress' }}\"" + restart_timeout = "10m" + } + + provisioner "powershell" { + inline = ["Set-Service -Name wlansvc -StartupType Manual", "if ($(Get-Service -Name wlansvc).Status -eq 'Running') { Stop-Service -Name wlansvc}"] + } + + provisioner "powershell" { + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-Docker.ps1", + "${path.root}/../scripts/build/Install-DockerWinCred.ps1", + "${path.root}/../scripts/build/Install-DockerCompose.ps1", + "${path.root}/../scripts/build/Install-PowershellCore.ps1", + "${path.root}/../scripts/build/Install-WebPlatformInstaller.ps1", + "${path.root}/../scripts/build/Install-Runner.ps1", + #"${path.root}/../scripts/build/Install-TortoiseSvn.ps1" + ] + } + + provisioner "windows-restart" { + restart_timeout = "30m" + } + + provisioner "powershell" { + elevated_password = "${var.install_password}" + elevated_user = "${var.install_user}" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-VisualStudio.ps1", + "${path.root}/../scripts/build/Install-KubernetesTools.ps1" + ] + valid_exit_codes = [0, 3010] + } + + provisioner "windows-restart" { + check_registry = true + restart_timeout = "10m" + } + + provisioner "powershell" { + pause_before = "2m0s" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-Wix.ps1", + "${path.root}/../scripts/build/Install-WDK.ps1", + "${path.root}/../scripts/build/Install-VSExtensions.ps1", + "${path.root}/../scripts/build/Install-AzureCli.ps1", + "${path.root}/../scripts/build/Install-AzureDevOpsCli.ps1", + "${path.root}/../scripts/build/Install-ChocolateyPackages.ps1", + "${path.root}/../scripts/build/Install-JavaTools.ps1", + "${path.root}/../scripts/build/Install-Kotlin.ps1", + "${path.root}/../scripts/build/Install-OpenSSL.ps1" + ] + } + + provisioner "powershell" { + execution_policy = "remotesigned" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = ["${path.root}/../scripts/build/Install-ServiceFabricSDK.ps1"] + } + + provisioner "windows-restart" { + restart_timeout = "10m" + } + + # # wmic which was deprecated seems to have been removed in windows 2025 + # # provisioner "windows-shell" { + # # inline = ["wmic product where \"name like '%%microsoft azure powershell%%'\" call uninstall /nointeractive"] + # # } + + # #Commenting as it is failing in windows 2025 + # # provisioner "windows-shell" { + # # inline = [ + # # "powershell -Command \"Get-Package -Name 'Microsoft Azure PowerShell' | Uninstall-Package -Force\"" + # # ] + # # } + + provisioner "powershell" { + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-ActionsCache.ps1", + "${path.root}/../scripts/build/Install-Ruby.ps1", + "${path.root}/../scripts/build/Install-PyPy.ps1", + "${path.root}/../scripts/build/Install-Toolset.ps1", + "${path.root}/../scripts/build/Configure-Toolset.ps1", + "${path.root}/../scripts/build/Install-NodeJS.ps1", + "${path.root}/../scripts/build/Install-AndroidSDK.ps1", + "${path.root}/../scripts/build/Install-PowershellAzModules.ps1", + "${path.root}/../scripts/build/Install-Pipx.ps1", + "${path.root}/../scripts/build/Install-Git.ps1", + "${path.root}/../scripts/build/Install-GitHub-CLI.ps1", + "${path.root}/../scripts/build/Install-PHP.ps1", + "${path.root}/../scripts/build/Install-Rust.ps1", + "${path.root}/../scripts/build/Install-Sbt.ps1", + "${path.root}/../scripts/build/Install-Chrome.ps1", + "${path.root}/../scripts/build/Install-EdgeDriver.ps1", + "${path.root}/../scripts/build/Install-Firefox.ps1", + "${path.root}/../scripts/build/Install-Selenium.ps1", + "${path.root}/../scripts/build/Install-IEWebDriver.ps1", + "${path.root}/../scripts/build/Install-Apache.ps1", + "${path.root}/../scripts/build/Install-Nginx.ps1", + "${path.root}/../scripts/build/Install-Msys2.ps1", + "${path.root}/../scripts/build/Install-WinAppDriver.ps1", + "${path.root}/../scripts/build/Install-R.ps1", + "${path.root}/../scripts/build/Install-AWSTools.ps1", + "${path.root}/../scripts/build/Install-DACFx.ps1", + "${path.root}/../scripts/build/Install-MysqlCli.ps1", + "${path.root}/../scripts/build/Install-SQLPowerShellTools.ps1", + "${path.root}/../scripts/build/Install-SQLOLEDBDriver.ps1", + "${path.root}/../scripts/build/Install-DotnetSDK.ps1", + "${path.root}/../scripts/build/Install-Mingw64.ps1", + "${path.root}/../scripts/build/Install-Haskell.ps1", + "${path.root}/../scripts/build/Install-Stack.ps1", + "${path.root}/../scripts/build/Install-Miniconda.ps1", + "${path.root}/../scripts/build/Install-AzureCosmosDbEmulator.ps1", + #"${path.root}/../scripts/build/Install-Mercurial.ps1", + "${path.root}/../scripts/build/Install-Zstd.ps1", + #"${path.root}/../scripts/build/Install-NSIS.ps1", + "${path.root}/../scripts/build/Install-Vcpkg.ps1", + "${path.root}/../scripts/build/Install-Bazel.ps1", + #"${path.root}/../scripts/build/Install-AliyunCli.ps1", + "${path.root}/../scripts/build/Install-RootCA.ps1", + "${path.root}/../scripts/build/Install-MongoDB.ps1", + "${path.root}/../scripts/build/Install-CodeQLBundle.ps1", + "${path.root}/../scripts/build/Configure-Diagnostics.ps1" + ] + } + + provisioner "powershell" { + elevated_password = "${var.install_password}" + elevated_user = "${var.install_user}" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-PostgreSQL.ps1", + "${path.root}/../scripts/build/Install-WindowsUpdates.ps1", + "${path.root}/../scripts/build/Configure-DynamicPort.ps1", + "${path.root}/../scripts/build/Configure-GDIProcessHandleQuota.ps1", + "${path.root}/../scripts/build/Configure-Shell.ps1", + "${path.root}/../scripts/build/Configure-DeveloperMode.ps1", + "${path.root}/../scripts/build/Install-LLVM.ps1" + ] + } + + provisioner "windows-restart" { + check_registry = true + restart_check_command = "powershell -command \"& {if ((-not (Get-Process TiWorker.exe -ErrorAction SilentlyContinue)) -and (-not [System.Environment]::HasShutdownStarted) ) { Write-Output 'Restart complete' }}\"" + restart_timeout = "30m" + } + + provisioner "powershell" { + pause_before = "2m0s" + environment_vars = ["IMAGE_FOLDER=${var.image_folder}"] + scripts = [ + "${path.root}/../scripts/build/Install-WindowsUpdatesAfterReboot.ps1", + "${path.root}/../scripts/tests/RunAll-Tests.ps1" + ] + } + + provisioner "powershell" { + inline = ["if (-not (Test-Path ${var.image_folder}\\tests\\testResults.xml)) { throw '${var.image_folder}\\tests\\testResults.xml not found' }"] + } + + provisioner "powershell" { + environment_vars = ["IMAGE_VERSION=${var.image_version}", "IMAGE_FOLDER=${var.image_folder}"] + inline = ["pwsh -File '${var.image_folder}\\SoftwareReport\\Generate-SoftwareReport.ps1'"] + } + + provisioner "powershell" { + 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' }"] + } + + provisioner "file" { + destination = "${path.root}/../Windows2025-Readme.md" + direction = "download" + source = "C:\\software-report.md" + } + + provisioner "file" { + destination = "${path.root}/../software-report.json" + direction = "download" + source = "C:\\software-report.json" + } + + provisioner "powershell" { + environment_vars = ["INSTALL_USER=${var.install_user}"] + scripts = [ + "${path.root}/../scripts/build/Install-NativeImages.ps1", + "${path.root}/../scripts/build/Configure-System.ps1", + "${path.root}/../scripts/build/Configure-User.ps1" + ] + skip_clean = true + } + + provisioner "windows-restart" { + restart_timeout = "10m" + } + + provisioner "powershell" { + inline = [ + "echo '>>> Waiting for GA to start ...'", + "while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }", + # "while ((Get-Service WindowsAzureTelemetryService).Status -ne 'Running') { Start-Sleep -s 5 }", + "while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }", + + "echo '>>> Sysprepping VM ...'", + "if (Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml) { rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force }", + "& $Env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quit", + "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 } }" + ] +} + + # provisioner "powershell" { + # inline = [ + # "if( Test-Path $env:SystemRoot\\System32\\Sysprep\\unattend.xml ){ rm $env:SystemRoot\\System32\\Sysprep\\unattend.xml -Force}", + # "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /mode:vm /quit", + # "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 } }" + # ] + # } + +} diff --git a/images/windows/toolsets/toolset-2019.json b/images/windows/toolsets/toolset-2019.json new file mode 100644 index 0000000..553d8a1 --- /dev/null +++ b/images/windows/toolsets/toolset-2019.json @@ -0,0 +1,484 @@ +{ + "toolcache": [ + { + "name": "Ruby", + "arch": "x64", + "platform" : "win32", + "versions": [ + "3.0", + "3.1", + "3.2" + ], + "default": "3.0" + }, + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "win32", + "versions": [ + "3.7.*", + "3.8.*", + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*" + ], + "default": "3.7.*" + }, + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "arch": "x86", + "platform" : "win32", + "versions": [ + "3.7.*", + "3.8.*", + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*" + ] + }, + { + "name": "PyPy", + "arch": "x86", + "platform" : "win64", + "versions": [ + "2.7", + "3.6", + "3.7", + "3.8", + "3.9", + "3.10" + ] + }, + { + "name": "node", + "url" : "https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "win32", + "versions": [ + "16.*", + "18.*", + "20.*" + ] + }, + { + "name": "go", + "url" : "https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "win32", + "versions": [ + "1.20.*", + "1.21.*", + "1.22.*" + ], + "default": "1.21.*" + } + ], + "powershellModules": [ + {"name": "DockerMsftProvider"}, + {"name": "MarkdownPS"}, + {"name": "Pester"}, + {"name": "PowerShellGet"}, + {"name": "PSScriptAnalyzer"}, + {"name": "PSWindowsUpdate"}, + {"name": "SqlServer", "versions": ["22.2.0"]}, + {"name": "VSSetup"}, + {"name": "Microsoft.Graph"}, + {"name": "AWSPowershell"} + ], + "azureModules": [ + { + "name": "azurerm", + "versions": [ + "2.1.0", + "6.13.1" + ], + "zip_versions": [ + "3.8.0", + "4.2.1", + "5.1.1", + "6.7.0" + ], + "default": "2.1.0" + }, + { + "name": "azure", + "versions": [ + "2.1.0", + "5.3.0" + ], + "zip_versions": [ + "3.8.0", + "4.2.1", + "5.1.1" + ], + "default": "2.1.0" + }, + { + "name": "az", + "versions": [ + "12.1.0" + ], + "zip_versions": [ + ] + } + ], + "java": { + "default": "8", + "versions": [ "8", "11", "17", "21"] + }, + "android": { + "commandline_tools_url": "https://dl.google.com/android/repository/commandlinetools-win-9123335_latest.zip", + "hash": "8A90E6A3DEB2FA13229B2E335EFD07687DCC8A55A3C544DA9F40B41404993E7D", + "platform_min_version": "31", + "build_tools_min_version": "31.0.0", + "extras": [ + "android;m2repository", + "google;m2repository", + "google;google_play_services" + ], + "addons": [ + "addon-google_apis-google-24", + "addon-google_apis-google-23", + "addon-google_apis-google-22", + "addon-google_apis-google-21" + ], + "additional_tools": [ + "cmake;3.10.2.4988404", + "cmake;3.18.1", + "cmake;3.22.1" + ], + "ndk": { + "default": "27", + "versions": [ + "26" , "27" + ] + } + }, + "MsysPackages": { + "msys2": [ + "base-devel", + "compression" + ], + "mingw": [ + { + "arch": "mingw-w64-x86_64", + "exec_dir": "mingw64", + "runtime_packages": [ + { + "name": "clang", + "executables": [ + "clang", + "g++", + "gcc" + ] + }, + { + "name": "cmake", + "executables": [ + "cmake", + "cpack", + "ctest" + ] + }, + { + "name": "ragel", + "executables": [ + "ragel" + ] + } + ], + "additional_packages": [ + "clang-tools-extra", + "libxml2", + "llvm", + "toolchain" + ] + }, + { + "arch": "mingw-w64-i686", + "exec_dir": "mingw32", + "runtime_packages": [ + { + "name": "clang", + "executables": [ + "clang", + "g++", + "gcc" + ] + }, + { + "name": "cmake", + "executables": [ + "cmake", + "cpack", + "ctest" + ] + }, + { + "name": "ragel", + "executables": [ + "ragel" + ] + } + ], + "additional_packages": [ + "libxml2", + "llvm", + "toolchain" + ] + } + ] + }, + "windowsFeatures": [ + { "name": "NET-Framework-Features", "includeAllSubFeatures": true }, + { "name": "NET-Framework-45-Features", "includeAllSubFeatures": true }, + { "name": "FS-iSCSITarget-Server", "includeAllSubFeatures": true, "includeManagementTools": true }, + { "name": "Containers" }, + { "name": "Microsoft-Windows-Subsystem-Linux", "optionalFeature": true }, + { "name": "VirtualMachinePlatform", "optionalFeature": true }, + { "name": "Wireless-Networking" } + ], + "visualStudio": { + "version" : "2019", + "subversion" : "16", + "edition" : "Enterprise", + "channel": "release", + "signature": ["F9A7CF9FBE13BAC767F4781061332DA6E8B4E0EE", + "C2048FB509F1C37A8C3E9EC6648118458AA01780" + ], + "workloads": [ + "Component.Dotfuscator", + "Component.Linux.CMake", + "Component.UnityEngine.x64", + "Component.Unreal.Android", + "Microsoft.Component.Azure.DataLake.Tools", + "Microsoft.Component.PythonTools.Miniconda", + "Microsoft.Component.PythonTools.Web", + "Microsoft.Component.VC.Runtime.UCRTSDK", + "Microsoft.Net.ComponentGroup.4.6.2.DeveloperTools", + "Microsoft.Net.ComponentGroup.4.7.1.DeveloperTools", + "Microsoft.Net.Component.4.7.2.SDK", + "Microsoft.Net.Component.4.7.2.TargetingPack", + "Microsoft.Net.ComponentGroup.4.7.DeveloperTools", + "Microsoft.VisualStudio.Component.AspNet45", + "Microsoft.VisualStudio.Component.Azure.Kubernetes.Tools", + "Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools", + "Microsoft.VisualStudio.Component.Debugger.JustInTime", + "Microsoft.VisualStudio.Component.DslTools", + "Microsoft.VisualStudio.Component.EntityFramework", + "Microsoft.VisualStudio.Component.FSharp.Desktop", + "Microsoft.VisualStudio.Component.LinqToSql", + "Microsoft.VisualStudio.Component.SQL.SSDT", + "Microsoft.VisualStudio.Component.Sharepoint.Tools", + "Microsoft.VisualStudio.Component.PortableLibrary", + "Microsoft.VisualStudio.Component.TeamOffice", + "Microsoft.VisualStudio.Component.TestTools.CodedUITest", + "Microsoft.VisualStudio.Component.TestTools.WebLoadTest", + "Microsoft.VisualStudio.Component.UWP.VC.ARM64", + "Microsoft.VisualStudio.Component.VC.140", + "Microsoft.VisualStudio.Component.VC.ATL.ARM", + "Microsoft.VisualStudio.Component.VC.ATLMFC", + "Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre", + "Microsoft.VisualStudio.Component.VC.CLI.Support", + "Microsoft.VisualStudio.Component.VC.CMake.Project", + "Microsoft.VisualStudio.Component.VC.DiagnosticTools", + "Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset", + "Microsoft.VisualStudio.Component.VC.MFC.ARM", + "Microsoft.VisualStudio.Component.VC.MFC.ARM.Spectre", + "Microsoft.VisualStudio.Component.VC.MFC.ARM64", + "Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre", + "Microsoft.VisualStudio.Component.VC.Modules.x86.x64", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64EC", + "Microsoft.VisualStudio.Component.VC.Redist.MSM", + "Microsoft.VisualStudio.Component.VC.Runtimes.ARM.Spectre", + "Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre", + "Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre", + "Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest", + "Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest", + "Microsoft.VisualStudio.Component.VC.v141.x86.x64", + "Microsoft.VisualStudio.Component.VC.v141.x86.x64.Spectre", + "Microsoft.VisualStudio.Component.VC.v141.ARM.Spectre", + "Microsoft.VisualStudio.Component.VC.v141.ARM64.Spectre", + "Microsoft.VisualStudio.Component.VC.v141.ATL", + "Microsoft.VisualStudio.Component.VC.v141.ATL.ARM", + "Microsoft.VisualStudio.Component.VC.v141.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.v141.ATL.ARM.Spectre", + "Microsoft.VisualStudio.Component.VC.v141.ATL.ARM64.Spectre", + "Microsoft.VisualStudio.Component.VC.v141.ATL.Spectre", + "Microsoft.VisualStudio.Component.VC.v141.MFC", + "Microsoft.VisualStudio.Component.VC.v141.MFC.ARM.Spectre", + "Microsoft.VisualStudio.Component.VC.v141.MFC.ARM64.Spectre", + "Microsoft.VisualStudio.Component.VC.v141.MFC.Spectre", + "Microsoft.VisualStudio.Component.VC.14.25.x86.x64", + "Microsoft.VisualStudio.Component.Windows10SDK.16299", + "Microsoft.VisualStudio.Component.Windows10SDK.17134", + "Microsoft.VisualStudio.Component.Windows10SDK.17763", + "Microsoft.VisualStudio.Component.Windows10SDK.18362", + "Microsoft.VisualStudio.Component.Windows10SDK.19041", + "Microsoft.VisualStudio.Component.Windows10SDK.20348", + "Microsoft.VisualStudio.Component.Windows11SDK.22000", + "Microsoft.VisualStudio.Component.WinXP", + "Microsoft.VisualStudio.Component.Workflow", + "Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices", + "Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools", + "Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang", + "Microsoft.VisualStudio.ComponentGroup.Web.CloudTools", + "Microsoft.VisualStudio.ComponentGroup.UWP.VC", + "Microsoft.VisualStudio.ComponentGroup.UWP.VC.v141", + "Microsoft.VisualStudio.Workload.Azure", + "Microsoft.VisualStudio.Workload.Data", + "Microsoft.VisualStudio.Workload.DataScience", + "Microsoft.VisualStudio.Workload.ManagedDesktop", + "Microsoft.VisualStudio.Workload.ManagedGame", + "Microsoft.VisualStudio.Workload.NativeCrossPlat", + "Microsoft.VisualStudio.Workload.NativeDesktop", + "Microsoft.VisualStudio.Workload.NativeGame", + "Microsoft.VisualStudio.Workload.NativeMobile", + "Microsoft.VisualStudio.Workload.NetCoreTools", + "Microsoft.VisualStudio.Workload.NetCrossPlat", + "Microsoft.VisualStudio.Workload.NetWeb", + "Microsoft.VisualStudio.Workload.Node", + "Microsoft.VisualStudio.Workload.Office", + "Microsoft.VisualStudio.Workload.Python", + "Microsoft.VisualStudio.Workload.Universal", + "Microsoft.VisualStudio.Workload.VisualStudioExtension", + "Component.MDD.Linux", + "Component.MDD.Linux.GCC.arm" + ], + "vsix": [ + "ProBITools.MicrosoftAnalysisServicesModelingProjects", + "SSIS.SqlServerIntegrationServicesProjects", + "ProBITools.MicrosoftReportProjectsforVisualStudio", + "VisualStudioClient.MicrosoftVisualStudio2017InstallerProjects", + "ms-biztalk.BizTalk", + "WixToolset.WixToolsetVisualStudio2019Extension" + ] + }, + "docker": { + "images": [ + "mcr.microsoft.com/windows/servercore:ltsc2019", + "mcr.microsoft.com/windows/nanoserver:1809", + "mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019", + "mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2019", + "mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2019" + ], + "components": { + "docker": "26.1.3", + "compose": "2.27.1" + } + }, + "pipx": [ + { + "package": "yamllint", + "cmd": "yamllint --version" + } + ], + "selenium": { + "version": "4" + }, + "npm": { + "global_packages": [ + { "name": "yarn", "test": "yarn --version" }, + { "name": "newman", "test": "newman --version" }, + { "name": "lerna", "test": "lerna --version" }, + { "name": "typescript", "test": "tsc --version" }, + { "name": "cordova" }, + { "name": "grunt-cli", "test": "grunt --version" }, + { "name": "gulp-cli", "test": "gulp --version" }, + { "name": "parcel", "test": "parcel --version"}, + { "name": "webpack" }, + { "name": "webpack-cli" }, + { "name": "node-sass" } + ] + }, + "serviceFabric": { + "runtime": { + "version": "9.1.1436.9590", + "checksum": "089F9BE6A63AF66C337E507B52082575C8ED330DF765259C6F2F13F900DA96D5" + }, + "sdk": { + "version": "6.1.1436", + "checksum": "D2A6B58315D08B6E46C2D25E91DFA1163136F70A5FA8CEF9DB147F07EE001AD1" + } + }, + "dotnet": { + "versions": [ + "6.0", + "7.0", + "8.0" + ], + "tools": [ + { "name": "nbgv", "test": "nbgv --version", "getversion": "nbgv --version" } + ], + "warmup": true + }, + "choco": { + "common_packages": [ + { "name": "7zip.install" }, + { "name": "aria2" }, + { "name": "azcopy10" }, + { "name": "Bicep" }, + { "name": "gitversion.portable"}, + { "name": "innosetup" }, + { "name": "jq" }, + { "name": "NuGet.CommandLine" }, + { "name": "packer" }, + { + "name": "strawberryperl" , + "args": [ "--version", "5.32.1.1" ] + }, + { "name": "pulumi" }, + { "name": "swig" }, + { "name": "vswhere" }, + { + "name": "julia", + "args": [ "--ia", "/DIR=C:\\Julia" ] + }, + { + "name": "cmake.install", + "args": [ "--installargs", "ADD_CMAKE_TO_PATH=\"System\"" ] + }, + { "name": "imagemagick" } + ] + }, + "node": { + "default": "18" + }, + "maven": { + "version": "3.8" + }, + "mysql": { + "version": "5.7", + "signature": "DD34D7C71F160972DE0CE25B695A0D6A9C95BD91" + }, + "mongodb": { + "version": "5.0", + "signature": "F2D7C28591847BB2CB2B1C2A0C59459FDC728A38" + }, + "nsis": { + "version": "3.10" + }, + "php": { + "version": "8.3" + }, + "llvm": { + "version": "18" + }, + "postgresql": { + "version": "14.12.1", + "signature": "698BA51AA27CC31282AACA5055E4B9190BC6C0E9" + }, + "kotlin": { + "version": "latest" + }, + "openssl": { + "version": "1.1.1" + }, + "pwsh": { + "version": "7.4" + } +} diff --git a/images/windows/toolsets/toolset-2022.json b/images/windows/toolsets/toolset-2022.json new file mode 100644 index 0000000..1a3f742 --- /dev/null +++ b/images/windows/toolsets/toolset-2022.json @@ -0,0 +1,389 @@ +{ + "toolcache": [ + { + "name": "Ruby", + "arch": "x64", + "platform" : "win32", + "versions": [ + "3.0", + "3.1", + "3.2" + ], + "default": "3.0" + }, + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "win32", + "versions": [ + "3.7.*", + "3.8.*", + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*" + ], + "default": "3.9.*" + }, + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "arch": "x86", + "platform" : "win32", + "versions": [ + "3.7.*", + "3.8.*", + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*" + ] + }, + { + "name": "PyPy", + "arch": "x86", + "platform" : "win64", + "versions": [ + "2.7", + "3.7", + "3.8", + "3.9", + "3.10" + ] + }, + { + "name": "node", + "url" : "https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "win32", + "versions": [ + "16.*", + "18.*", + "20.*" + ] + }, + { + "name": "go", + "url" : "https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "win32", + "versions": [ + "1.20.*", + "1.21.*", + "1.22.*" + ], + "default": "1.21.*" + } + ], + "powershellModules": [ + { "name": "DockerMsftProvider" }, + { "name": "MarkdownPS" }, + { "name": "Pester" }, + { "name": "PowerShellGet" }, + { "name": "PSScriptAnalyzer" }, + { "name": "PSWindowsUpdate" }, + { "name": "SqlServer" }, + { "name": "VSSetup" }, + { "name": "Microsoft.Graph" }, + {"name": "AWSPowershell"} + ], + "azureModules": [ + { + "name": "azurerm", + "versions": [ + "2.1.0", + "6.13.1" + ], + "zip_versions": [ + "3.8.0", + "4.2.1", + "5.1.1", + "6.7.0" + ], + "default": "2.1.0" + }, + { + "name": "azure", + "versions": [ + "2.1.0", + "5.3.0" + ], + "zip_versions": [ + "3.8.0", + "4.2.1", + "5.1.1" + ], + "default": "2.1.0" + }, + { + "name": "az", + "versions": [ + "12.1.0" + ], + "zip_versions": [ + + ] + } + ], + "java": { + "default": "8", + "versions": [ "8", "11", "17", "21"] + }, + "android": { + "commandline_tools_url": "https://dl.google.com/android/repository/commandlinetools-win-9123335_latest.zip", + "hash": "8A90E6A3DEB2FA13229B2E335EFD07687DCC8A55A3C544DA9F40B41404993E7D", + "platform_min_version": "31", + "build_tools_min_version": "31.0.0", + "extras": [ + "android;m2repository", + "google;m2repository", + "google;google_play_services" + ], + "addons": [], + "additional_tools": [ + "cmake;3.18.1", + "cmake;3.22.1" + ], + "ndk": { + "default": "27", + "versions": [ + "26", "27" + ] + } + }, + "mingw": { + "version": "12.2.0", + "runtime": "ucrt" + }, + "MsysPackages": { + "msys2": [], + "mingw": [] + }, + "windowsFeatures": [ + { "name": "Containers" }, + { "name": "Microsoft-Windows-Subsystem-Linux", "optionalFeature": true }, + { "name": "VirtualMachinePlatform", "optionalFeature": true }, + { "name": "NET-Framework-45-Features", "includeAllSubFeatures": true }, + { "name": "Client-ProjFS", "optionalFeature": true }, + { "name": "NET-Framework-Features", "includeAllSubFeatures": true }, + { "name": "Hyper-V", "includeAllSubFeatures": true }, + { "name": "HypervisorPlatform", "optionalFeature": true }, + { "name": "Hyper-V-PowerShell" }, + { "name": "Wireless-Networking" } + ], + "visualStudio": { + "version" : "2022", + "subversion" : "17", + "edition" : "Enterprise", + "channel": "release", + "signature": "C2048FB509F1C37A8C3E9EC6648118458AA01780", + "workloads": [ + "Component.Dotfuscator", + "Component.Linux.CMake", + "Component.UnityEngine.x64", + "Component.Unreal.Android", + "Component.Xamarin", + "Microsoft.Component.VC.Runtime.UCRTSDK", + "Microsoft.Net.Component.4.7.TargetingPack", + "Microsoft.Net.Component.4.7.2.TargetingPack", + "Microsoft.Net.Component.4.8.1.SDK", + "Microsoft.Net.Component.4.8.1.TargetingPack", + "Microsoft.VisualStudio.Component.AspNet45", + "Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools", + "Microsoft.VisualStudio.Component.Debugger.JustInTime", + "Microsoft.VisualStudio.Component.EntityFramework", + "Microsoft.VisualStudio.Component.DslTools", + "Microsoft.VisualStudio.Component.LinqToSql", + "Microsoft.VisualStudio.Component.SQL.SSDT", + "Microsoft.VisualStudio.Component.Sharepoint.Tools", + "Microsoft.VisualStudio.Component.PortableLibrary", + "Microsoft.VisualStudio.Component.TeamOffice", + "Microsoft.VisualStudio.Component.TestTools.CodedUITest", + "Microsoft.VisualStudio.Component.TestTools.WebLoadTest", + "Microsoft.VisualStudio.Component.UWP.VC.ARM64", + "Microsoft.VisualStudio.Component.UWP.VC.ARM64EC", + "Microsoft.VisualStudio.Component.VC.CLI.Support", + "Microsoft.VisualStudio.Component.VC.CMake.Project", + "Microsoft.VisualStudio.Component.VC.DiagnosticTools", + "Microsoft.VisualStudio.Component.VC.Llvm.Clang", + "Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset", + "Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest", + "Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest", + "Microsoft.VisualStudio.Component.VC.Tools.ARM", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64EC", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Microsoft.VisualStudio.Component.VC.Modules.x86.x64", + "Microsoft.VisualStudio.Component.VC.Redist.MSM", + "Microsoft.VisualStudio.Component.VC.Runtimes.ARM.Spectre", + "Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre", + "Microsoft.VisualStudio.Component.VC.Runtimes.ARM64EC.Spectre", + "Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre", + "Microsoft.VisualStudio.Component.VC.MFC.ARM", + "Microsoft.VisualStudio.Component.VC.MFC.ARM.Spectre", + "Microsoft.VisualStudio.Component.VC.MFC.ARM64", + "Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre", + "Microsoft.VisualStudio.Component.VC.ATLMFC", + "Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre", + "Microsoft.VisualStudio.Component.VC.ATL", + "Microsoft.VisualStudio.Component.VC.ATL.Spectre", + "Microsoft.VisualStudio.Component.VC.ATL.ARM", + "Microsoft.VisualStudio.Component.VC.ATL.ARM.Spectre", + "Microsoft.VisualStudio.Component.VC.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre", + "Microsoft.VisualStudio.Component.VC.ASAN", + "Microsoft.VisualStudio.Component.Windows10SDK.19041", + "Microsoft.VisualStudio.Component.Windows10SDK.20348", + "Microsoft.VisualStudio.Component.Windows11SDK.22000", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", + "Microsoft.VisualStudio.Component.Workflow", + "Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices", + "Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools", + "Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang", + "Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142", + "Microsoft.VisualStudio.ComponentGroup.Web.CloudTools", + "Microsoft.VisualStudio.Workload.Azure", + "Microsoft.VisualStudio.Workload.Data", + "Microsoft.VisualStudio.Workload.ManagedDesktop", + "Microsoft.VisualStudio.Workload.ManagedGame", + "Microsoft.VisualStudio.Workload.NativeCrossPlat", + "Microsoft.VisualStudio.Workload.NativeDesktop", + "Microsoft.VisualStudio.Workload.NativeGame", + "Microsoft.VisualStudio.Workload.NativeMobile", + "Microsoft.VisualStudio.Workload.NetCrossPlat", + "Microsoft.VisualStudio.Workload.NetWeb", + "Microsoft.VisualStudio.Workload.Node", + "Microsoft.VisualStudio.Workload.Office", + "Microsoft.VisualStudio.Workload.Python", + "Microsoft.VisualStudio.Workload.Universal", + "Microsoft.VisualStudio.Workload.VisualStudioExtension", + "Component.MDD.Linux", + "Component.MDD.Linux.GCC.arm", + "Component.Microsoft.Windows.DriverKit", + "wasm.tools" + ], + "vsix": [ + "SSIS.MicrosoftDataToolsIntegrationServices", + "VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects", + "WixToolset.WixToolsetVisualStudio2022Extension", + "ProBITools.MicrosoftReportProjectsforVisualStudio2022", + "ProBITools.MicrosoftAnalysisServicesModelingProjects2022" + ] + }, + "docker": { + "images": [ + "mcr.microsoft.com/windows/servercore:ltsc2022", + "mcr.microsoft.com/windows/nanoserver:ltsc2022", + "mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022", + "mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2022", + "mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2022" + ], + "components": { + "docker": "26.1.3", + "compose": "2.27.1" + } + }, + "pipx": [ + { + "package": "yamllint", + "cmd": "yamllint --version" + } + ], + "selenium": { + "version": "4" + }, + "npm": { + "global_packages": [ + { "name": "yarn", "test": "yarn --version" }, + { "name": "newman", "test": "newman --version" }, + { "name": "lerna", "test": "lerna --version" }, + { "name": "gulp-cli", "test": "gulp --version" }, + { "name": "grunt-cli", "test": "grunt --version" } + ] + }, + "serviceFabric": { + "runtime": { + "version": "9.1.1436.9590", + "checksum": "089F9BE6A63AF66C337E507B52082575C8ED330DF765259C6F2F13F900DA96D5" + }, + "sdk": { + "version": "6.1.1436", + "checksum": "D2A6B58315D08B6E46C2D25E91DFA1163136F70A5FA8CEF9DB147F07EE001AD1" + } + }, + "dotnet": { + "versions": [ + "6.0", + "7.0" + ], + "tools": [ + { "name": "nbgv", "test": "nbgv --version", "getversion": "nbgv --version" } + ], + "warmup": false + }, + "choco": { + "common_packages": [ + { "name": "7zip.install" }, + { "name": "aria2" }, + { "name": "azcopy10" }, + { "name": "Bicep" }, + { "name": "innosetup" }, + { "name": "jq" }, + { "name": "NuGet.CommandLine" }, + { "name": "packer" }, + { + "name": "strawberryperl" , + "args": [ "--version", "5.32.1.1" ] + }, + { "name": "pulumi" }, + { "name": "swig" }, + { "name": "vswhere" }, + { + "name": "julia", + "args": [ "--ia", "/DIR=C:\\Julia" ] + }, + { + "name": "cmake.install", + "args": [ "--installargs", "ADD_CMAKE_TO_PATH=\"System\"" ] + }, + { "name": "imagemagick" } + ] + }, + "node": { + "default": "18" + }, + "maven": { + "version": "3.8" + }, + "mysql": { + "version": "8.0", + "signature": "DD34D7C71F160972DE0CE25B695A0D6A9C95BD91" + }, + "mongodb": { + "version": "5.0", + "signature": "F2D7C28591847BB2CB2B1C2A0C59459FDC728A38" + }, + "nsis": { + "version": "3.10" + }, + "llvm": { + "version": "18" + }, + "php": { + "version": "8.3" + }, + "postgresql": { + "version": "14.12.1", + "signature": "698BA51AA27CC31282AACA5055E4B9190BC6C0E9" + }, + "kotlin": { + "version": "latest" + }, + "openssl": { + "version": "1.1.1" + }, + "pwsh": { + "version": "7.4" + } +} diff --git a/images/windows/toolsets/toolset-2025.json b/images/windows/toolsets/toolset-2025.json new file mode 100644 index 0000000..066af0a --- /dev/null +++ b/images/windows/toolsets/toolset-2025.json @@ -0,0 +1,358 @@ +{ + "toolcache": [ + { + "name": "Ruby", + "arch": "x64", + "platform" : "win32", + "versions": [ + "3.1", + "3.2", + "3.3" + ], + "default": "3.3" + }, + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "win32", + "versions": [ + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*", + "3.13.*" + ], + "default": "3.9.*" + }, + { + "name": "Python", + "url" : "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json", + "arch": "x86", + "platform" : "win32", + "versions": [ + "3.9.*", + "3.10.*", + "3.11.*", + "3.12.*", + "3.13.*" + ] + }, + { + "name": "PyPy", + "arch": "x86", + "platform" : "win64", + "versions": [ + "2.7", + "3.7", + "3.8", + "3.9", + "3.10" + ] + }, + { + "name": "node", + "url" : "https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "win32", + "versions": [ + "18.*", + "20.*", + "22.*" + ] + }, + { + "name": "go", + "url" : "https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json", + "arch": "x64", + "platform" : "win32", + "versions": [ + "1.21.*", + "1.22.*", + "1.23.*" + ], + "default": "1.23.*" + } + ], + "powershellModules": [ + { "name": "DockerMsftProvider" }, + { "name": "MarkdownPS" }, + { "name": "Pester" }, + { "name": "PowerShellGet" }, + { "name": "PSScriptAnalyzer" }, + { "name": "PSWindowsUpdate" }, + { "name": "SqlServer" }, + { "name": "VSSetup" }, + { "name": "Microsoft.Graph" }, + {"name": "AWSPowershell"} + ], + "azureModules": [ + { + "name": "az", + "versions": [ + "12.4.0" + ], + "zip_versions": [] + } + ], + "java": { + "default": "17", + "versions": [ "8", "11", "17", "21"] + }, + "android": { + "commandline_tools_url": "https://dl.google.com/android/repository/commandlinetools-win-12266719_latest.zip", + "hash": "F9088C04A44F1F37A8A3A228A7663E11AE9445FA07529C96CEF38ACB985A88F3", + "platform_min_version": "35", + "build_tools_min_version": "35.0.0", + "extras": [ + "android;m2repository", + "google;m2repository", + "google;google_play_services" + ], + "addons": [], + "additional_tools": [ + "cmake;3.22.1", + "cmake;3.30.5" + ], + "ndk": { + "default": "27", + "versions": [ + "26", "27" + ] + } + }, + "mingw": { + "version": "14.2.0", + "runtime": "ucrt" + }, + "MsysPackages": { + "msys2": [], + "mingw": [] + }, + "windowsFeatures": [ + { "name": "Containers" }, + { "name": "NET-Framework-Features", "includeAllSubFeatures": true }, + { "name": "Hyper-V", "includeAllSubFeatures": true }, + { "name": "HypervisorPlatform", "optionalFeature": true }, + { "name": "Hyper-V-PowerShell" }, + { "name": "Microsoft-Windows-Subsystem-Linux", "optionalFeature": true }, + { "name": "VirtualMachinePlatform", "optionalFeature": true }, + { "name": "NET-Framework-45-Features", "includeAllSubFeatures": true }, + { "name": "Client-ProjFS", "optionalFeature": true }, + { "name": "Wireless-Networking" } + ], + "visualStudio": { + "version" : "2022", + "subversion" : "17", + "edition" : "Enterprise", + "channel": "release", + "signature": "C2048FB509F1C37A8C3E9EC6648118458AA01780", + "workloads": [ + "Component.Dotfuscator", + "Component.Linux.CMake", + "Component.UnityEngine.x64", + "Component.Unreal.Android", + "Component.Xamarin", + "Microsoft.Component.VC.Runtime.UCRTSDK", + "Microsoft.Net.Component.4.7.TargetingPack", + "Microsoft.Net.Component.4.7.2.TargetingPack", + "Microsoft.Net.Component.4.8.1.SDK", + "Microsoft.Net.Component.4.8.1.TargetingPack", + "Microsoft.VisualStudio.Component.AspNet45", + "Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools", + "Microsoft.VisualStudio.Component.Debugger.JustInTime", + "Microsoft.VisualStudio.Component.EntityFramework", + "Microsoft.VisualStudio.Component.DslTools", + "Microsoft.VisualStudio.Component.LinqToSql", + "Microsoft.VisualStudio.Component.SQL.SSDT", + "Microsoft.VisualStudio.Component.Sharepoint.Tools", + "Microsoft.VisualStudio.Component.PortableLibrary", + "Microsoft.VisualStudio.Component.TeamOffice", + "Microsoft.VisualStudio.Component.TestTools.CodedUITest", + "Microsoft.VisualStudio.Component.TestTools.WebLoadTest", + "Microsoft.VisualStudio.Component.UWP.VC.ARM64", + "Microsoft.VisualStudio.Component.UWP.VC.ARM64EC", + "Microsoft.VisualStudio.Component.VC.CLI.Support", + "Microsoft.VisualStudio.Component.VC.CMake.Project", + "Microsoft.VisualStudio.Component.VC.DiagnosticTools", + "Microsoft.VisualStudio.Component.VC.Llvm.Clang", + "Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset", + "Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest", + "Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest", + "Microsoft.VisualStudio.Component.VC.Tools.ARM", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64EC", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Microsoft.VisualStudio.Component.VC.Modules.x86.x64", + "Microsoft.VisualStudio.Component.VC.Redist.MSM", + "Microsoft.VisualStudio.Component.VC.Runtimes.ARM.Spectre", + "Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre", + "Microsoft.VisualStudio.Component.VC.Runtimes.ARM64EC.Spectre", + "Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre", + "Microsoft.VisualStudio.Component.VC.MFC.ARM", + "Microsoft.VisualStudio.Component.VC.MFC.ARM.Spectre", + "Microsoft.VisualStudio.Component.VC.MFC.ARM64", + "Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre", + "Microsoft.VisualStudio.Component.VC.ATLMFC", + "Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre", + "Microsoft.VisualStudio.Component.VC.ATL", + "Microsoft.VisualStudio.Component.VC.ATL.Spectre", + "Microsoft.VisualStudio.Component.VC.ATL.ARM", + "Microsoft.VisualStudio.Component.VC.ATL.ARM.Spectre", + "Microsoft.VisualStudio.Component.VC.ATL.ARM64", + "Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre", + "Microsoft.VisualStudio.Component.VC.ASAN", + "Microsoft.VisualStudio.Component.Windows10SDK.19041", + "Microsoft.VisualStudio.Component.Windows10SDK.20348", + "Microsoft.VisualStudio.Component.Windows11SDK.22000", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", + "Microsoft.VisualStudio.Component.Windows11SDK.26100", + "Microsoft.VisualStudio.Component.Workflow", + "Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices", + "Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools", + "Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang", + "Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142", + "Microsoft.VisualStudio.ComponentGroup.Web.CloudTools", + "Microsoft.VisualStudio.Workload.Azure", + "Microsoft.VisualStudio.Workload.Data", + "Microsoft.VisualStudio.Workload.ManagedDesktop", + "Microsoft.VisualStudio.Workload.ManagedGame", + "Microsoft.VisualStudio.Workload.NativeCrossPlat", + "Microsoft.VisualStudio.Workload.NativeDesktop", + "Microsoft.VisualStudio.Workload.NativeGame", + "Microsoft.VisualStudio.Workload.NativeMobile", + "Microsoft.VisualStudio.Workload.NetCrossPlat", + "Microsoft.VisualStudio.Workload.NetWeb", + "Microsoft.VisualStudio.Workload.Node", + "Microsoft.VisualStudio.Workload.Office", + "Microsoft.VisualStudio.Workload.Python", + "Microsoft.VisualStudio.Workload.Universal", + "Microsoft.VisualStudio.Workload.VisualStudioExtension", + "Component.MDD.Linux", + "Component.MDD.Linux.GCC.arm", + "Component.Microsoft.Windows.DriverKit", + "wasm.tools" + ], + "vsix": [ + "SSIS.MicrosoftDataToolsIntegrationServices", + "VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects", + "WixToolset.WixToolsetVisualStudio2022Extension", + "ProBITools.MicrosoftReportProjectsforVisualStudio2022", + "ProBITools.MicrosoftAnalysisServicesModelingProjects2022" + ] + }, + "docker": { + "images": [ + "mcr.microsoft.com/windows/servercore:ltsc2022", + "mcr.microsoft.com/windows/nanoserver:ltsc2022", + "mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022", + "mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2022", + "mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2022" + ], + "components": { + "docker": "26.1.3", + "compose": "2.27.1" + } + }, + "pipx": [ + { + "package": "yamllint", + "cmd": "yamllint --version" + } + ], + "selenium": { + "version": "4" + }, + "npm": { + "global_packages": [ + { "name": "yarn", "test": "yarn --version" }, + { "name": "newman", "test": "newman --version" }, + { "name": "lerna", "test": "lerna --version" }, + { "name": "gulp-cli", "test": "gulp --version" }, + { "name": "grunt-cli", "test": "grunt --version" } + ] + }, + "serviceFabric": { + "runtime": { + "version": "10.1.2493.9590", + "checksum": "09C63A971BACDE338282C73B3C9174BED9AAD53E1D3A1B73D44515852C9C00CF" + }, + "sdk": { + "version": "7.0.2712", + "checksum": "69F54790B63488875513964A88AF38C104D3C0D073FC2A63D590F0ADD2773754" + } + }, + "dotnet": { + "versions": [ + "8.0" + ], + "tools": [ + { "name": "nbgv", "test": "nbgv --version", "getversion": "nbgv --version" } + ], + "warmup": false + }, + "choco": { + "common_packages": [ + { "name": "7zip.install" }, + { "name": "aria2" }, + { "name": "azcopy10" }, + { "name": "Bicep" }, + { "name": "innosetup" }, + { "name": "jq" }, + { "name": "NuGet.CommandLine" }, + { "name": "packer" }, + { + "name": "strawberryperl" , + "args": [ "--version", "5.40.0.1" ] + }, + { "name": "pulumi" }, + { "name": "swig" }, + { "name": "vswhere" }, + { + "name": "julia", + "args": [ "--ia", "/DIR=C:\\Julia" ] + }, + { + "name": "cmake.install", + "args": [ "--installargs", "ADD_CMAKE_TO_PATH=\"System\"" ] + }, + { "name": "imagemagick" } + ] + }, + "node": { + "default": "22" + }, + "maven": { + "version": "3.9" + }, + "mysql": { + "version": "8.0", + "signature": "DD34D7C71F160972DE0CE25B695A0D6A9C95BD91" + }, + "mongodb": { + "version": "7.0", + "signature": "F2D7C28591847BB2CB2B1C2A0C59459FDC728A38" + }, + "nsis": { + "version": "3.10" + }, + "llvm": { + "version": "19" + }, + "php": { + "version": "8.3" + }, + "postgresql": { + "version": "17", + "signature": "698BA51AA27CC31282AACA5055E4B9190BC6C0E9" + }, + "kotlin": { + "version": "latest" + }, + "openssl": { + "version": "3.4.0" + }, + "pwsh": { + "version": "7.4" + } +}