mirror of
https://github.com/actions/versions-package-tools.git
synced 2025-12-10 03:13:23 +00:00
Compare commits
151 Commits
master
...
e7aee39f18
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7aee39f18 | ||
|
|
673d91c21a | ||
|
|
d233269615 | ||
|
|
816a9a3331 | ||
|
|
6c48f687bd | ||
|
|
e60ec8ae98 | ||
|
|
db2ae0a36e | ||
|
|
6fdf7f098f | ||
|
|
4ae553a74d | ||
|
|
13ba3fd9e0 | ||
|
|
552a1a5fda | ||
|
|
1e5d44c7a5 | ||
|
|
817eacd539 | ||
|
|
933aacb90a | ||
|
|
d3c3feee4b | ||
|
|
4028baf988 | ||
|
|
d30950623a | ||
|
|
16b7940fde | ||
|
|
94d413c5d2 | ||
|
|
6d641c49d7 | ||
|
|
447803d00a | ||
|
|
b549ccbeee | ||
|
|
760646fc29 | ||
|
|
a8a2c841ba | ||
|
|
6fbb1f0f20 | ||
|
|
b964a9871b | ||
|
|
d4cf796ec9 | ||
|
|
b8e79c3d16 | ||
|
|
b43c6cc8fd | ||
|
|
5810329d19 | ||
|
|
896369fc7d | ||
|
|
d25937e581 | ||
|
|
c08a90cad6 | ||
|
|
2c15878a9d | ||
|
|
7b3aff2ad5 | ||
|
|
81e64845b7 | ||
|
|
56f47ea626 | ||
|
|
c3143dd446 | ||
|
|
31f01aedc7 | ||
|
|
e6dbb79745 | ||
|
|
fe999f6aa6 | ||
|
|
33b79844a6 | ||
|
|
de5faa4d9c | ||
|
|
a753279554 | ||
|
|
6f1aa3ce73 | ||
|
|
e98e069ca6 | ||
|
|
8e2a68893f | ||
|
|
359c2548cb | ||
|
|
fa49641719 | ||
|
|
b5a05e45d5 | ||
|
|
e71d476320 | ||
|
|
7114ad2b94 | ||
|
|
fb0eac418a | ||
|
|
f07ff8ddf9 | ||
|
|
f72b7a85c7 | ||
|
|
3d09ce1d19 | ||
|
|
a9e00ee513 | ||
|
|
9f82304f9c | ||
|
|
4f7bf4650a | ||
|
|
1eaa091b65 | ||
|
|
5a00becf47 | ||
|
|
9b849b3d5a | ||
|
|
e9ef44e60c | ||
|
|
6b66054ab6 | ||
|
|
8c62a83204 | ||
|
|
ab37396421 | ||
|
|
98826eed53 | ||
|
|
6147177edd | ||
|
|
2a422a2d52 | ||
|
|
46a901fa87 | ||
|
|
f19d5f4b3c | ||
|
|
00039881ba | ||
|
|
878c283d3b | ||
|
|
41a1acf1a4 | ||
|
|
2bd3b2a3c2 | ||
|
|
e81db48281 | ||
|
|
ab218c2436 | ||
|
|
504e746b96 | ||
|
|
4d97edc41a | ||
|
|
80bbe2ac05 | ||
|
|
77b53792b3 | ||
|
|
725c68b777 | ||
|
|
81507e485b | ||
|
|
7ec75e0761 | ||
|
|
590ebcd1ab | ||
|
|
cc2ef5ff7d | ||
|
|
e37209e8d6 | ||
|
|
3d7121daba | ||
|
|
f5bdb61b33 | ||
|
|
96a98e9fb3 | ||
|
|
5f0e0eebc8 | ||
|
|
32fbb87915 | ||
|
|
b22990961a | ||
|
|
fc90da9ef1 | ||
|
|
e9cccdf5ed | ||
|
|
c2935ecf46 | ||
|
|
0624df7ce9 | ||
|
|
11360e1ff9 | ||
|
|
ecc77a65f7 | ||
|
|
c304390e31 | ||
|
|
7e43a35182 | ||
|
|
0d478268bb | ||
|
|
14f9ca7540 | ||
|
|
1575bb86d1 | ||
|
|
1b966031cc | ||
|
|
4a932e134c | ||
|
|
3e3417e894 | ||
|
|
a5e1431377 | ||
|
|
fb5b7794f0 | ||
|
|
ea9029b06d | ||
|
|
6bc823feae | ||
|
|
84daa05fbc | ||
|
|
e1cbdde483 | ||
|
|
d61f870ac7 | ||
|
|
e9a19e1251 | ||
|
|
3bfd520dd0 | ||
|
|
840e3a64d3 | ||
|
|
ab240b2f15 | ||
|
|
2e6d8df345 | ||
|
|
5b7cb28e2e | ||
|
|
4ce7f7efbc | ||
|
|
5e5de280d6 | ||
|
|
236d44a167 | ||
|
|
02afcca5c6 | ||
|
|
f4ff9fb93d | ||
|
|
21cbc1e8ca | ||
|
|
109e6bd009 | ||
|
|
be051a1f12 | ||
|
|
4b0fa42d99 | ||
|
|
5bcb80c333 | ||
|
|
3f2b844267 | ||
|
|
7f90ceb82c | ||
|
|
34c6f7f0ec | ||
|
|
94d8a3af83 | ||
|
|
89ebce9383 | ||
|
|
f8f76caff1 | ||
|
|
8463c1f4c0 | ||
|
|
8e8ae73c1d | ||
|
|
7aa3827a98 | ||
|
|
2ea972432a | ||
|
|
7e2bc9a237 | ||
|
|
3b38e3de4c | ||
|
|
606411ff6f | ||
|
|
5e417400d9 | ||
|
|
15842ce7fe | ||
|
|
45424846c7 | ||
|
|
ebc70e779a | ||
|
|
347402ff31 | ||
|
|
7a56615638 | ||
|
|
68072bedef | ||
|
|
377623de6c |
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* @actions/setup-actions-team
|
||||
39
.github/actions/send-slack-notification/action.yml
vendored
Normal file
39
.github/actions/send-slack-notification/action.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: 'Send Slack notification'
|
||||
description: 'SendSlack notification about new versions of a tool'
|
||||
inputs:
|
||||
url:
|
||||
required: true
|
||||
description: 'Slack channel url'
|
||||
tool-name:
|
||||
required: true
|
||||
description: 'Name of a tool to send notification for. Like Xamarin or Python'
|
||||
default: 'Xamarin'
|
||||
tool-version:
|
||||
required: false
|
||||
description: 'New versions of a tool'
|
||||
pipeline-url:
|
||||
required: false
|
||||
description: 'Url of a pipeline'
|
||||
image-url:
|
||||
required: false
|
||||
description: 'Image url for message'
|
||||
default: 'https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png'
|
||||
text:
|
||||
required: false
|
||||
description: 'Message text'
|
||||
add-to-toolset-flag:
|
||||
required: false
|
||||
description: 'Flag to use notification for adding new versions to toolset'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- id: send-slack-notification
|
||||
name: Send Slack notification
|
||||
shell: pwsh
|
||||
run: ./get-new-tool-versions/send-slack-notification.ps1 -Url "${{ inputs.url }}" `
|
||||
-ToolName "${{ inputs.tool-name }}" `
|
||||
-ToolVersion "${{ inputs.tool-version }}" `
|
||||
-PipelineUrl "${{ inputs.pipeline-url }}" `
|
||||
-ImageUrl "${{ inputs.image-url }}" `
|
||||
-Text "${{ inputs.text }}" `
|
||||
${{ inputs.add-to-toolset-flag }}
|
||||
14
.github/dependabot.yml
vendored
Normal file
14
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# Enable version updates for GitHub Actions
|
||||
- package-ecosystem: 'github-actions'
|
||||
# Workflow files stored in the default location of `.github/workflows`
|
||||
# You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
264
.github/workflows/build-tool-packages.yml
vendored
Normal file
264
.github/workflows/build-tool-packages.yml
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
# This reusable workflow is used by actions/*-versions repositories
|
||||
# It is designed to
|
||||
# - build and test new versions of a tool (Go, Node)
|
||||
# - publish a release with a new tool version
|
||||
# The GITHUB_TOKEN secret is used to trigger workflow runs and publish releases
|
||||
|
||||
name: Generate tool packages
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tool-name:
|
||||
description: "Tool name to build and upload. Supported values are: 'go' and 'node'"
|
||||
required: true
|
||||
type: string
|
||||
tool-version:
|
||||
description: "Tool version to build and upload"
|
||||
required: true
|
||||
type: string
|
||||
publish-release:
|
||||
description: "Whether to publish releases"
|
||||
required: true
|
||||
type: boolean
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: pwsh
|
||||
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Build ${{ inputs.tool-name }} ${{ inputs.tool-version }} [${{ matrix.platform }}] [${{ matrix.architecture }}]
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
ARTIFACT_NAME: ${{ inputs.tool-name }}-${{ inputs.tool-version }}-${{ matrix.platform }}-${{ matrix.architecture }}
|
||||
excludewinarm: ${{ !(inputs.tool-name == 'node' && inputs['tool-version'] < '20.0.0' && matrix.architecture == 'arm64' && matrix.platform == 'win32') }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [linux, darwin, win32]
|
||||
architecture: [x64, arm64]
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
if: env.excludewinarm == 'true'
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Build ${{ inputs.tool-name }} ${{ inputs.tool-version }}
|
||||
if: env.excludewinarm == 'true'
|
||||
run: |
|
||||
./builders/build-${{ inputs.tool-name }}.ps1 -Version ${{ inputs.tool-version }} `
|
||||
-Platform ${{ matrix.platform }} `
|
||||
-Architecture ${{ matrix.architecture }}
|
||||
|
||||
- name: Publish artifact
|
||||
if: env.excludewinarm == 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: ${{ runner.temp }}/artifact
|
||||
|
||||
test:
|
||||
name: Test ${{ inputs.tool-name }} ${{ inputs.tool-version }} [${{ matrix.platform }}] [${{ matrix.architecture }}]
|
||||
needs: build
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
ARTIFACT_NAME: ${{ inputs.tool-name }}-${{ inputs.tool-version }}-${{ matrix.platform }}-${{ matrix.architecture }}
|
||||
excludewinarm: ${{ !(inputs.tool-name == 'node' && inputs['tool-version'] < '20.0.0' && matrix.architecture == 'arm64' && matrix.platform == 'win32') }}
|
||||
RUNNER_TYPE: ${{ matrix.runner_type }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
architecture: x64
|
||||
- os: macos-15-intel
|
||||
platform: darwin
|
||||
architecture: x64
|
||||
- os: windows-latest
|
||||
platform: win32
|
||||
architecture: x64
|
||||
- os: ubuntu-22.04-arm
|
||||
platform: linux
|
||||
architecture: arm64
|
||||
- os: macos-latest
|
||||
platform: darwin
|
||||
architecture: arm64
|
||||
- os: windows-11-arm
|
||||
platform: win32
|
||||
architecture: arm64
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
if: env.excludewinarm == 'true'
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Fully cleanup the toolcache directory before testing
|
||||
if: env.excludewinarm == 'true'
|
||||
run: ./helpers/clean-toolcache.ps1 -ToolName "${{ inputs.tool-name }}"
|
||||
|
||||
- name: Download artifact
|
||||
if: env.excludewinarm == 'true'
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: ${{ runner.temp }}/${{ env.ARTIFACT_NAME }}
|
||||
|
||||
- name: Extract files
|
||||
if: env.excludewinarm == 'true'
|
||||
run: |
|
||||
if ('${{ matrix.platform }}' -eq 'win32') {
|
||||
if ('${{ inputs.tool-name }}' -eq 'node') {
|
||||
$artifactName = "${{ env.ARTIFACT_NAME }}.7z"
|
||||
} elseif ('${{ inputs.tool-name }}' -eq 'go') {
|
||||
$artifactName = "${{ env.ARTIFACT_NAME }}.zip"
|
||||
} else {
|
||||
Write-Host "Unsupported tool - ${{ inputs.tool-name }}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
7z.exe x "$artifactName" -y | Out-Null
|
||||
} else {
|
||||
$artifactName = "${{ env.ARTIFACT_NAME }}.tar.gz"
|
||||
tar -xzf $artifactName
|
||||
}
|
||||
working-directory: ${{ runner.temp }}/${{ env.ARTIFACT_NAME }}
|
||||
|
||||
- name: Apply build artifact to the local machine
|
||||
if: env.excludewinarm == 'true'
|
||||
run: |
|
||||
if ('${{ matrix.platform }}' -eq 'win32') { powershell ./setup.ps1 }
|
||||
else {
|
||||
sh ./setup.sh
|
||||
}
|
||||
working-directory: ${{ runner.temp }}/${{ env.ARTIFACT_NAME }}
|
||||
|
||||
- name: Setup Node.js ${{ inputs.tool-version }}
|
||||
if: env.excludewinarm == 'true' && inputs.tool-name == 'node'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ inputs.tool-version }}
|
||||
|
||||
- name: Setup Go ${{ inputs.tool-version }}
|
||||
if: inputs.tool-name == 'go'
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ inputs.tool-version }}
|
||||
|
||||
- name: Wait for the logs
|
||||
if: env.excludewinarm == 'true'
|
||||
run: |
|
||||
Write-Host "Fake step that does nothing"
|
||||
Write-Host "We need it because log from the previous step 'Setup ${{ inputs.tool-name }}' is not available here yet."
|
||||
Write-Host "In testing step we analyze build log of 'Setup ${{ inputs.tool-name }}' task"
|
||||
Write-Host "to determine if ${{ inputs.tool-name }} version was consumed from cache or if it was downloaded"
|
||||
for ($i = 0; $i -lt 200; $i++) { Get-Random }
|
||||
|
||||
- name: Ensure Pester Installed
|
||||
if: env.excludewinarm == 'true'
|
||||
run: |
|
||||
$module = Get-Module -ListAvailable -Name Pester
|
||||
if (-not $module -or ($module.Version -lt [Version]"5.0.0")) {
|
||||
Install-Module Pester -Force -Scope CurrentUser -SkipPublisherCheck
|
||||
}
|
||||
|
||||
- name: Run tests
|
||||
if: env.excludewinarm == 'true'
|
||||
env:
|
||||
VERSION: ${{ inputs.tool-version }}
|
||||
run: |
|
||||
Import-Module Pester
|
||||
$toolName = (Get-Culture).TextInfo.ToTitleCase("${{ inputs.tool-name }}")
|
||||
Invoke-Pester -Configuration @{
|
||||
Run = @{ Path = "./$toolName.Tests.ps1" }
|
||||
Should = @{ ErrorAction = 'Continue' }
|
||||
Output = @{ EnableExit = $true }
|
||||
}
|
||||
working-directory: ./tests
|
||||
|
||||
publish_release:
|
||||
name: Publish release
|
||||
if: inputs.publish-release
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v5
|
||||
|
||||
- name: Generate release body
|
||||
id: generate-release-body
|
||||
run: |
|
||||
if ('${{ inputs.tool-name }}' -eq 'node') {
|
||||
$releaseBody = 'Node.js ${{ inputs.tool-version }}'
|
||||
} else {
|
||||
$releaseBody = 'Go ${{ inputs.tool-version }}'
|
||||
}
|
||||
echo "RELEASE_BODY=$releaseBody" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Publish Release
|
||||
id: create_release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
tag_name="${{ inputs.tool-version }}-${{ github.run_id }}"
|
||||
gh release create "$tag_name" \
|
||||
--repo="$GITHUB_REPOSITORY" \
|
||||
--title="${{ inputs.tool-version }}" \
|
||||
--notes="${{ steps.generate-release-body.outputs.RELEASE_BODY }}"
|
||||
|
||||
release_id=$(gh release view "$tag_name" --repo "$GITHUB_REPOSITORY" --json databaseId --jq '.databaseId')
|
||||
echo "id=$release_id" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Generate hash for packages
|
||||
run: |
|
||||
$childItems = Get-Childitem -Path '.'
|
||||
$childItems | Foreach-Object {
|
||||
$packageObj = Get-Childitem -Path $_.FullName | Select-Object -First 1
|
||||
Write-Host "Package: $($packageObj.Name)"
|
||||
$actualHash = (Get-FileHash -Path $packageObj.FullName -Algorithm sha256).Hash
|
||||
$hashString = "$actualHash $($packageObj.Name)"
|
||||
Write-Host "$hashString"
|
||||
Add-Content -Path ./hashes.sha256 -Value "$hashString"
|
||||
}
|
||||
|
||||
- name: Upload release assets
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
for (let artifactDir of fs.readdirSync('.')) {
|
||||
let artifactName = fs.lstatSync(artifactDir).isDirectory() ? fs.readdirSync(`${artifactDir}`)[0] : artifactDir;
|
||||
|
||||
console.log(`Upload ${artifactName} asset`);
|
||||
github.rest.repos.uploadReleaseAsset({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: ${{ steps.create_release.outputs.id }},
|
||||
name: artifactName,
|
||||
data: fs.lstatSync(artifactDir).isDirectory() ? fs.readFileSync(`./${artifactDir}/${artifactName}`) : fs.readFileSync(`./${artifactName}`).toString()
|
||||
});
|
||||
}
|
||||
|
||||
trigger_pr:
|
||||
name: Trigger "Create Pull Request" workflow
|
||||
needs: publish_release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger "Create Pull Request" workflow
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'create-pr.yml',
|
||||
ref: 'main'
|
||||
});
|
||||
4
.github/workflows/common_tests.yml
vendored
4
.github/workflows/common_tests.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install Pester
|
||||
shell: pwsh
|
||||
@@ -20,4 +20,4 @@ jobs:
|
||||
run: |
|
||||
Import-Module Pester
|
||||
Import-Module Assert
|
||||
Invoke-Pester -EnableExit
|
||||
Invoke-Pester -EnableExit
|
||||
|
||||
43
.github/workflows/create-pr-to-update-manifest.yml
vendored
Normal file
43
.github/workflows/create-pr-to-update-manifest.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# This reusable workflow is used by actions/*-versions repositories
|
||||
# It is designed to create a PR with update of versions-manifest.json when a new release is published
|
||||
# The GITHUB_TOKEN secret is used to create versions-manifest.json and publish related PR
|
||||
|
||||
name: Create Pull Request
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tool-name:
|
||||
description: 'Name of the tool for which PR is created'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: pwsh
|
||||
|
||||
jobs:
|
||||
create_pr:
|
||||
name: Create Pull Request
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Create versions-manifest.json
|
||||
run: |
|
||||
./helpers/packages-generation/manifest-generator.ps1 -RepositoryFullName "$env:GITHUB_REPOSITORY" `
|
||||
-GitHubAccessToken "${{ secrets.GITHUB_TOKEN }}" `
|
||||
-OutputFile "./versions-manifest.json" `
|
||||
-ConfigurationFile "./config/${{ inputs.tool-name }}-manifest-config.json"
|
||||
|
||||
- name: Create GitHub PR
|
||||
run: |
|
||||
$formattedDate = Get-Date -Format "MM/dd/yyyy"
|
||||
./helpers/github/create-pull-request.ps1 `
|
||||
-RepositoryFullName "$env:GITHUB_REPOSITORY" `
|
||||
-AccessToken "${{ secrets.GITHUB_TOKEN }}" `
|
||||
-BranchName "update-versions-manifest-file" `
|
||||
-CommitMessage "Update versions-manifest" `
|
||||
-PullRequestTitle "[versions-manifest] Update for release from ${formattedDate}" `
|
||||
-PullRequestBody "Update versions-manifest.json for release from ${formattedDate}"
|
||||
109
.github/workflows/get-new-tool-versions.yml
vendored
Normal file
109
.github/workflows/get-new-tool-versions.yml
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
# This reusable workflow is used by actions/*-versions repositories
|
||||
# It is designed to check for new versions of a tool (Python, Node, etc.)
|
||||
# The 'SLACK_CHANNEL_URL' secret must be added to the repository containing the caller workflow
|
||||
# in order to publish messages to Slack.
|
||||
# The 'Get Available Tools Versions - Publishing Approval' environment must be created in the repository containing the caller workflow
|
||||
# The 'trigger_builds' job requires manual approval
|
||||
# The GITHUB_TOKEN secret is used to cancel and trigger workflow runs
|
||||
|
||||
name: Get new tool versions
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tool-name:
|
||||
description: 'Name of the tool for which versions are searched'
|
||||
required: true
|
||||
type: string
|
||||
image-url:
|
||||
description: 'Tool image to be attached to Slack posts'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: pwsh
|
||||
|
||||
jobs:
|
||||
find_new_versions:
|
||||
name: Find new versions
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
versions_output: ${{ steps.Get_new_versions.outputs.TOOL_VERSIONS }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- id: Get_new_versions
|
||||
name: Get new versions
|
||||
run: ./helpers/get-new-tool-versions/get-new-tool-versions.ps1 -ToolName ${{ inputs.tool-name }}
|
||||
|
||||
check_new_versions:
|
||||
name: Check new versions
|
||||
runs-on: ubuntu-latest
|
||||
needs: find_new_versions
|
||||
env:
|
||||
TOOL_VERSIONS: ${{needs.find_new_versions.outputs.versions_output}}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Check Versions
|
||||
if: env.TOOL_VERSIONS == ''
|
||||
run: |
|
||||
Write-Host "No new versions were found"
|
||||
Import-Module "./helpers/github/github-api.psm1"
|
||||
$gitHubApi = Get-GitHubApi -RepositoryFullName "$env:GITHUB_REPOSITORY" `
|
||||
-AccessToken "${{ secrets.GITHUB_TOKEN }}"
|
||||
$gitHubApi.CancelWorkflow("$env:GITHUB_RUN_ID")
|
||||
Start-Sleep -Seconds 60
|
||||
|
||||
- name: Send Slack notification
|
||||
run: |
|
||||
$pipelineUrl = "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID"
|
||||
$message = "The following versions of '${{ inputs.tool-name }}' are available to upload: ${{ env.TOOL_VERSIONS }}\nLink to the pipeline: $pipelineUrl"
|
||||
./helpers/get-new-tool-versions/send-slack-notification.ps1 -Url "${{ secrets.SLACK_CHANNEL_URL }}" `
|
||||
-ToolName "${{ inputs.tool-name }}" `
|
||||
-ImageUrl "${{ inputs.image-url }}" `
|
||||
-Text "$message"
|
||||
trigger_builds:
|
||||
name: Trigger builds
|
||||
runs-on: ubuntu-latest
|
||||
needs: [find_new_versions, check_new_versions]
|
||||
env:
|
||||
TOOL_VERSIONS: ${{needs.find_new_versions.outputs.versions_output}}
|
||||
environment: Get Available Tools Versions - Publishing Approval
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Trigger "Build ${{ inputs.tool-name }} packages" workflow
|
||||
run: |
|
||||
$workflowFileName = "build-${{ inputs.tool-name }}-packages.yml".ToLower()
|
||||
./helpers/github/run-ci-builds.ps1 -RepositoryFullName "$env:GITHUB_REPOSITORY" `
|
||||
-AccessToken "${{ secrets.GITHUB_TOKEN }}" `
|
||||
-WorkflowFileName "$workflowFileName" `
|
||||
-WorkflowDispatchRef "main" `
|
||||
-ToolVersions "${{ env.TOOL_VERSIONS }}" `
|
||||
-PublishReleases "true"
|
||||
|
||||
check_build:
|
||||
name: Check build for failures
|
||||
runs-on: ubuntu-latest
|
||||
needs: [find_new_versions, check_new_versions, trigger_builds]
|
||||
if: failure()
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Send Slack notification if build fails
|
||||
run: |
|
||||
$pipelineUrl = "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID"
|
||||
$message = "The build of the '${{ inputs.tool-name }}' detection pipeline failed :progress-error:\nLink to the pipeline: $pipelineUrl"
|
||||
./helpers/get-new-tool-versions/send-slack-notification.ps1 -Url "${{ secrets.SLACK_CHANNEL_URL }}" `
|
||||
-ToolName "${{ inputs.tool-name }}" `
|
||||
-Text "$message" `
|
||||
-ImageUrl "${{ inputs.image-url }}"
|
||||
89
.github/workflows/get-tools-new-versions.yml
vendored
Normal file
89
.github/workflows/get-tools-new-versions.yml
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
name: Get tools new versions
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 8 * * THU'
|
||||
workflow_dispatch:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: pwsh
|
||||
|
||||
jobs:
|
||||
find-new-tool-versions:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
tool:
|
||||
- name: 'Xamarin'
|
||||
image: 'https://avatars.githubusercontent.com/u/790012?s=200&v=4'
|
||||
releases-url: 'null'
|
||||
filter-parameter: 'null'
|
||||
filter-arch: 'null'
|
||||
- name: 'Python'
|
||||
image: 'https://avatars.githubusercontent.com/u/1525981?s=200&v=4'
|
||||
releases-url: 'https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json'
|
||||
filter-parameter: 'version'
|
||||
filter-arch: 'x64'
|
||||
- name: 'PyPy'
|
||||
image: 'https://avatars.githubusercontent.com/u/318667?s=200&v=4'
|
||||
releases-url: 'https://downloads.python.org/pypy/versions.json'
|
||||
filter-parameter: 'python_version'
|
||||
filter-arch: 'x86'
|
||||
- name: 'Node'
|
||||
image: 'https://avatars.githubusercontent.com/u/9950313?s=200&v=4'
|
||||
releases-url: 'https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json'
|
||||
filter-parameter: 'version'
|
||||
filter-arch: 'x64'
|
||||
- name: 'Go'
|
||||
image: 'https://avatars.githubusercontent.com/u/4314092?s=200&v=4'
|
||||
releases-url: 'https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json'
|
||||
filter-parameter: 'version'
|
||||
filter-arch: 'x64'
|
||||
name: 'Searching for new versions of ${{ matrix.tool.name }}'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- id: get-new-tool-versions
|
||||
name: Get new tool versions
|
||||
run: |
|
||||
$versionsOutput = ./get-new-tool-versions/verify-new-tool-version-added-to-image.ps1 `
|
||||
-ToolName ${{ matrix.tool.name }} `
|
||||
-ReleasesUrl ${{ matrix.tool.releases-url }} `
|
||||
-FilterParameter ${{ matrix.tool.filter-parameter }} `
|
||||
-FilterArch ${{ matrix.tool.filter-arch }}
|
||||
echo "versions-output=$versionsOutput" >> $env:GITHUB_OUTPUT
|
||||
- name: Check versions
|
||||
if: steps.get-new-tool-versions.outputs.versions-output == ''
|
||||
run: Write-Host "No new versions found"
|
||||
- uses: ./.github/actions/send-slack-notification
|
||||
name: Send Slack notification
|
||||
if: steps.get-new-tool-versions.outputs.versions-output != ''
|
||||
with:
|
||||
url: ${{ secrets.SLACK_CHANNEL_HOOK }}
|
||||
tool-name: '${{ matrix.tool.name }}'
|
||||
tool-version: ${{ steps.get-new-tool-versions.outputs.versions-output }}
|
||||
image-url: '${{ matrix.tool.image }}'
|
||||
add-to-toolset-flag: '-AddToToolsetFlag'
|
||||
|
||||
check_build:
|
||||
name: Check build for failures
|
||||
runs-on: ubuntu-latest
|
||||
needs: [find-new-tool-versions]
|
||||
if: failure()
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- id: get-failed-jobs
|
||||
name: Get failed jobs
|
||||
run: |
|
||||
$jobs_url = "$env:GITHUB_API_URL/repos/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID/jobs"
|
||||
$failedJobs = (Invoke-RestMethod -Uri $jobs_url).jobs |
|
||||
Where-Object conclusion -eq "failure" |
|
||||
ForEach-Object {"\n\t" + $_.name.split(" ")[-1] + ": $($_.html_url)"}
|
||||
echo "failed-jobs=$failedJobs" >> $env:GITHUB_OUTPUT
|
||||
- uses: ./.github/actions/send-slack-notification
|
||||
name: Send Slack notification about failure
|
||||
with:
|
||||
url: ${{ secrets.SLACK_CHANNEL_HOOK }}
|
||||
tool-name: 'Tool name'
|
||||
pipeline-url: '$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID'
|
||||
text: "Missing toolset tool versions checker pipeline has failed jobs:/n/t${{ steps.get-failed-jobs.outputs.failed-jobs }}"
|
||||
51
.github/workflows/validate-manifest.yml
vendored
Normal file
51
.github/workflows/validate-manifest.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
# This reusable workflow is used by actions/*-versions repositories
|
||||
# It is designed to validate the versions-manifest.json file
|
||||
# The 'SLACK_CHANNEL_URL' secret must be added to the repository containing the caller workflow
|
||||
# in order to publish messages to Slack
|
||||
|
||||
name: Validate manifest
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tool-name:
|
||||
description: 'Name of the tool for which manifest is validated'
|
||||
required: true
|
||||
type: string
|
||||
image-url:
|
||||
description: 'Tool image to be attached to Slack posts'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: pwsh
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Validate manifest
|
||||
run: .\helpers\packages-generation\manifest-validator.ps1 -ManifestPath '.\versions-manifest.json'
|
||||
|
||||
check_build:
|
||||
name: Check validation for failures
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validation]
|
||||
if: failure()
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Send Slack notification if validation fails
|
||||
run: |
|
||||
$pipelineUrl = "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID"
|
||||
$message = "The validation of ${{ inputs.tool-name }} manifest failed. \nLink to the pipeline: $pipelineUrl"
|
||||
.\helpers\get-new-tool-versions\send-slack-notification.ps1 -Url "${{ secrets.SLACK_CHANNEL_URL }}" `
|
||||
-ToolName "${{ inputs.tool-name }}" `
|
||||
-Text "$message" `
|
||||
-ImageUrl "${{ inputs.image-url }}"
|
||||
30
README.md
30
README.md
@@ -1,5 +1,29 @@
|
||||
# Common tools for generation of packages in the actions/*-versions repositories
|
||||
This repository contains PowerShell modules that are used to generate packages for Actions. The packages are consumed by the images generated through [actions/virtual-environments](https://github.com/actions/virtual-environments) and some of the setup-* Actions
|
||||
This repository contains PowerShell modules that are used to generate packages for Actions. The packages are consumed by the images generated through [actions/runner-images](https://github.com/actions/runner-images) and some of the setup-* Actions
|
||||
|
||||
## Contribution
|
||||
Contributions are welcome! See [Contributor's Guide](./CONTRIBUTING.md) for more details about contribution process and code structure
|
||||
## Recommended permissions
|
||||
|
||||
When using the `versions-package-tools` in your GitHub Actions workflow, it is recommended to set the following permissions to ensure proper functionality:
|
||||
|
||||
```yaml
|
||||
permissions:
|
||||
contents: read # access to read repository's content
|
||||
actions: read # access to reading actions
|
||||
```
|
||||
### Note
|
||||
|
||||
Thank you for your interest in this GitHub action, however, right now we are not taking contributions. Add commentMore actionsAdd commentMore actions
|
||||
|
||||
We continue to focus our resources on strategic areas that help our customers be successful while making developers' lives easier. While GitHub Actions remains a key part of this vision, we are allocating resources towards other areas of Actions and are not taking contributions to this repository at this time. The GitHub public roadmap is the best place to follow along for any updates on features we’re working on and what stage they’re in.
|
||||
|
||||
We are taking the following steps to better direct requests related to GitHub Actions, including:
|
||||
|
||||
1. We will be directing questions and support requests to our [Community Discussions area](https://github.com/orgs/community/discussions/categories/actions)
|
||||
|
||||
2. High Priority bugs can be reported through Community Discussions or you can report these to our support team https://support.github.com/contact/bug-report.
|
||||
|
||||
3. Security Issues should be handled as per our [security.md](security.md)
|
||||
|
||||
We will still provide security updates for this project and fix major breaking changes during this time.
|
||||
|
||||
You are welcome to still raise bugs in this repo.
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
class AzureDevOpsApi
|
||||
{
|
||||
[string] $BaseUrl
|
||||
[string] $RepoOwner
|
||||
[object] $AuthHeader
|
||||
[UInt32] $RetryCount
|
||||
[UInt32] $RetryIntervalSec
|
||||
|
||||
AzureDevOpsApi(
|
||||
[string] $TeamFoundationCollectionUri,
|
||||
[string] $ProjectName,
|
||||
[string] $AccessToken,
|
||||
[UInt32] $RetryCount,
|
||||
[UInt32] $RetryIntervalSec
|
||||
) {
|
||||
$this.BaseUrl = $this.BuildBaseUrl($TeamFoundationCollectionUri, $ProjectName)
|
||||
$this.AuthHeader = $this.BuildAuth($AccessToken)
|
||||
$this.RetryCount = $RetryCount
|
||||
$this.RetryIntervalSec = $RetryIntervalSec
|
||||
}
|
||||
|
||||
[object] hidden BuildAuth([string]$AccessToken) {
|
||||
if ([string]::IsNullOrEmpty($AccessToken)) {
|
||||
return $null
|
||||
}
|
||||
return @{
|
||||
Authorization = "Bearer $AccessToken"
|
||||
}
|
||||
}
|
||||
|
||||
[string] hidden BuildBaseUrl([string]$TeamFoundationCollectionUri, [string]$ProjectName) {
|
||||
return "${TeamFoundationCollectionUri}/${ProjectName}/_apis"
|
||||
}
|
||||
|
||||
[object] QueueBuild([string]$ToolVersion, [string]$SourceBranch, [string]$SourceVersion, [UInt32]$DefinitionId){
|
||||
$url = "build/builds"
|
||||
|
||||
# The content of parameters field should be a json string
|
||||
$buildParameters = @{ VERSION = $ToolVersion } | ConvertTo-Json
|
||||
|
||||
$body = @{
|
||||
definition = @{
|
||||
id = $DefinitionId
|
||||
}
|
||||
sourceBranch = $SourceBranch
|
||||
sourceVersion = $SourceVersion
|
||||
parameters = $buildParameters
|
||||
} | ConvertTo-Json
|
||||
|
||||
return $this.InvokeRestMethod($url, 'POST', $body)
|
||||
}
|
||||
|
||||
[object] GetBuildInfo([UInt32]$BuildId){
|
||||
$url = "build/builds/$BuildId"
|
||||
|
||||
return $this.InvokeRestMethod($url, 'GET', $null)
|
||||
}
|
||||
|
||||
[object] UpdateBuildStatus([UInt32]$BuildId, [string]$BuildStatus){
|
||||
$url = "build/builds/$BuildId"
|
||||
$body = @{
|
||||
status = $BuildStatus
|
||||
} | ConvertTo-Json
|
||||
|
||||
return $this.InvokeRestMethod($url, 'PATCH', $body)
|
||||
}
|
||||
|
||||
[string] hidden BuildUrl([string]$Url) {
|
||||
return "$($this.BaseUrl)/${Url}/?api-version=5.1"
|
||||
}
|
||||
|
||||
[object] hidden InvokeRestMethod(
|
||||
[string] $Url,
|
||||
[string] $Method,
|
||||
[string] $Body
|
||||
) {
|
||||
$requestUrl = $this.BuildUrl($Url)
|
||||
$params = @{
|
||||
Method = $Method
|
||||
ContentType = "application/json"
|
||||
Uri = $requestUrl
|
||||
Headers = @{}
|
||||
}
|
||||
if ($this.AuthHeader) {
|
||||
$params.Headers += $this.AuthHeader
|
||||
}
|
||||
if (![string]::IsNullOrEmpty($body)) {
|
||||
$params.Body = $Body
|
||||
}
|
||||
|
||||
$params.RetryIntervalSec = $this.RetryIntervalSec
|
||||
$params.MaximumRetryCount = $this.RetryCount
|
||||
|
||||
return Invoke-RestMethod @params
|
||||
}
|
||||
}
|
||||
|
||||
function Get-AzureDevOpsApi {
|
||||
param (
|
||||
[string] $TeamFoundationCollectionUri,
|
||||
[string] $ProjectName,
|
||||
[string] $AccessToken,
|
||||
[UInt32] $RetryCount = 3,
|
||||
[UInt32] $RetryIntervalSec = 60
|
||||
)
|
||||
|
||||
return [AzureDevOpsApi]::New($TeamFoundationCollectionUri, $ProjectName, $AccessToken, $RetryCount, $RetryIntervalSec)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
Import-Module (Join-Path $PSScriptRoot "azure-devops-api.ps1")
|
||||
|
||||
class BuildInfo
|
||||
{
|
||||
[AzureDevOpsApi] $AzureDevOpsApi
|
||||
[String] $Name
|
||||
[UInt32] $Id
|
||||
[String] $Status
|
||||
[String] $Result
|
||||
[String] $Link
|
||||
|
||||
BuildInfo([AzureDevOpsApi] $AzureDevOpsApi, [object] $Build)
|
||||
{
|
||||
$this.AzureDevOpsApi = $AzureDevOpsApi
|
||||
$this.Id = $Build.id
|
||||
$this.Name = $Build.buildNumber
|
||||
$this.Link = $Build._links.web.href
|
||||
$this.Status = $Build.status
|
||||
$this.Result = $Build.result
|
||||
}
|
||||
|
||||
[boolean] IsFinished() {
|
||||
return ($this.Status -eq "completed") -or ($this.Status -eq "cancelling")
|
||||
}
|
||||
|
||||
[boolean] IsSuccess() {
|
||||
return $this.Result -eq "succeeded"
|
||||
}
|
||||
|
||||
[void] UpdateBuildInfo() {
|
||||
$buildInfo = $this.AzureDevOpsApi.GetBuildInfo($this.Id)
|
||||
$this.Status = $buildInfo.status
|
||||
$this.Result = $buildInfo.result
|
||||
}
|
||||
}
|
||||
|
||||
function Get-BuildInfo {
|
||||
param (
|
||||
[AzureDevOpsApi] $AzureDevOpsApi,
|
||||
[object] $Build
|
||||
)
|
||||
|
||||
return [BuildInfo]::New($AzureDevOpsApi, $Build)
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
param (
|
||||
[Parameter(Mandatory)] [string] $TeamFoundationCollectionUri,
|
||||
[Parameter(Mandatory)] [string] $AzureDevOpsProjectName,
|
||||
[Parameter(Mandatory)] [string] $AzureDevOpsAccessToken,
|
||||
[Parameter(Mandatory)] [string] $SourceBranch,
|
||||
[Parameter(Mandatory)] [UInt32] $DefinitionId,
|
||||
[Parameter(Mandatory)] [string] $SourceVersion,
|
||||
[Parameter(Mandatory)] [string] $ManifestLink,
|
||||
[Parameter(Mandatory)] [bool] $WaitForBuilds,
|
||||
[string] $ToolVersions,
|
||||
[UInt32] $RetryIntervalSec = 60,
|
||||
[UInt32] $RetryCount = 3
|
||||
)
|
||||
|
||||
Import-Module (Join-Path $PSScriptRoot "azure-devops-api.ps1")
|
||||
Import-Module (Join-Path $PSScriptRoot "build-info.ps1")
|
||||
|
||||
function Get-ToolVersions {
|
||||
param (
|
||||
[Parameter(Mandatory)] [string] $ManifestLink,
|
||||
[Parameter(Mandatory)] [UInt32] $RetryIntervalSec,
|
||||
[Parameter(Mandatory)] [UInt32] $Retries,
|
||||
[string] $ToolVersions
|
||||
)
|
||||
|
||||
[string[]] $versionsList = @()
|
||||
if ($ToolVersions) {
|
||||
$versionsList = $ToolVersions.Split(',')
|
||||
} else {
|
||||
Write-Host "Get the list of releases from $ManifestLink"
|
||||
$releases = Invoke-RestMethod $ManifestLink -MaximumRetryCount $Retries -RetryIntervalSec $RetryIntervalSec
|
||||
$versionsList = $releases.version
|
||||
}
|
||||
|
||||
Write-Host "Versions to build: $versionsList"
|
||||
return $versionsList
|
||||
}
|
||||
|
||||
function Queue-Builds {
|
||||
param (
|
||||
[Parameter(Mandatory)] [AzureDevOpsApi] $AzureDevOpsApi,
|
||||
[Parameter(Mandatory)] [string[]] $ToolVersions,
|
||||
[Parameter(Mandatory)] [string] $SourceBranch,
|
||||
[Parameter(Mandatory)] [string] $SourceVersion,
|
||||
[Parameter(Mandatory)] [UInt32] $DefinitionId
|
||||
)
|
||||
|
||||
[BuildInfo[]]$queuedBuilds = @()
|
||||
|
||||
$ToolVersions | ForEach-Object {
|
||||
$version = $_.Trim()
|
||||
Write-Host "Queue build for $version..."
|
||||
$queuedBuild = $AzureDevOpsApi.QueueBuild($version, $SourceBranch, $SourceVersion, $DefinitionId)
|
||||
$buildInfo = Get-BuildInfo -AzureDevOpsApi $AzureDevOpsApi -Build $queuedBuild
|
||||
Write-Host "Queued build: $($buildInfo.Link)"
|
||||
$queuedBuilds += $buildInfo
|
||||
}
|
||||
|
||||
return $queuedBuilds
|
||||
}
|
||||
|
||||
function Wait-Builds {
|
||||
param (
|
||||
[Parameter(Mandatory)] [BuildInfo[]] $Builds,
|
||||
[Parameter(Mandatory)] [UInt32] $RetryIntervalSec
|
||||
)
|
||||
|
||||
do {
|
||||
# If build is still running - refresh its status
|
||||
foreach($build in $builds) {
|
||||
if (!$build.IsFinished()) {
|
||||
$build.UpdateBuildInfo()
|
||||
|
||||
if ($build.IsFinished()) {
|
||||
Write-Host "The $($build.Name) build was completed: $($build.Link)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$runningBuildsCount = ($builds | Where-Object { !$_.IsFinished() }).Length
|
||||
|
||||
Start-Sleep -Seconds $RetryIntervalSec
|
||||
} while($runningBuildsCount -gt 0)
|
||||
}
|
||||
|
||||
function Make-BuildsOutput {
|
||||
param (
|
||||
[Parameter(Mandatory)] [BuildInfo[]] $Builds
|
||||
)
|
||||
|
||||
Write-Host "`nBuilds info:"
|
||||
$builds | Format-Table -AutoSize -Property Name,Id,Status,Result,Link | Out-String -Width 10000
|
||||
|
||||
# Return exit code based on status of builds
|
||||
$failedBuilds = ($builds | Where-Object { !$_.IsSuccess() })
|
||||
if ($failedBuilds.Length -ne 0) {
|
||||
Write-Host "##vso[task.logissue type=error;]Builds failed"
|
||||
$failedBuilds | ForEach-Object -Process { Write-Host "##vso[task.logissue type=error;]Name: $($_.Name); Link: $($_.Link)" }
|
||||
Write-Host "##vso[task.complete result=Failed]"
|
||||
} else {
|
||||
Write-host "##[section]All builds have been passed successfully"
|
||||
}
|
||||
}
|
||||
|
||||
$azureDevOpsApi = Get-AzureDevOpsApi -TeamFoundationCollectionUri $TeamFoundationCollectionUri `
|
||||
-ProjectName $AzureDevOpsProjectName `
|
||||
-AccessToken $AzureDevOpsAccessToken `
|
||||
-RetryCount $RetryCount `
|
||||
-RetryIntervalSec $RetryIntervalSec
|
||||
|
||||
$toolVersionsList = Get-ToolVersions -ManifestLink $ManifestLink `
|
||||
-RetryIntervalSec $RetryIntervalSec `
|
||||
-Retries $RetryCount `
|
||||
-ToolVersions $ToolVersions
|
||||
|
||||
$queuedBuilds = Queue-Builds -AzureDevOpsApi $azureDevOpsApi `
|
||||
-ToolVersions $toolVersionsList `
|
||||
-SourceBranch $SourceBranch `
|
||||
-SourceVersion $SourceVersion `
|
||||
-DefinitionId $DefinitionId
|
||||
|
||||
if ($WaitForBuilds) {
|
||||
Write-Host "`nWaiting results of builds ..."
|
||||
Wait-Builds -Builds $queuedBuilds -RetryIntervalSec $RetryIntervalSec
|
||||
|
||||
Make-BuildsOutput -Builds $queuedBuilds
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
name: $(date:yyyyMMdd)$(rev:.r)
|
||||
trigger: none
|
||||
pr: none
|
||||
schedules:
|
||||
- cron: "0 3 * * *"
|
||||
displayName: First daily build
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
always: true
|
||||
- cron: "0 15 * * *"
|
||||
displayName: Second daily build
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
always: true
|
||||
|
||||
variables:
|
||||
PoolName: 'Azure Pipelines'
|
||||
VmImage: 'ubuntu-18.04'
|
||||
|
||||
stages:
|
||||
- stage: Get_New_Versions
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- job: Get_Tool_Versions
|
||||
pool:
|
||||
name: $(PoolName)
|
||||
vmImage: $(VmImage)
|
||||
steps:
|
||||
- template: /azure-pipelines/templates/get-tool-versions-steps.yml
|
||||
|
||||
- stage: Trigger_Builds
|
||||
dependsOn: Get_New_Versions
|
||||
jobs:
|
||||
- deployment: Run_Builds
|
||||
pool:
|
||||
name: $(PoolName)
|
||||
vmImage: $(VmImage)
|
||||
variables:
|
||||
ToolVersions: $[ stageDependencies.Get_New_Versions.Get_Tool_Versions.outputs['Get_versions.TOOL_VERSIONS'] ]
|
||||
timeoutInMinutes: 180
|
||||
environment: 'Get Available Tools Versions - Publishing Approval'
|
||||
strategy:
|
||||
runOnce:
|
||||
deploy:
|
||||
steps:
|
||||
- template: /azure-pipelines/templates/run-ci-builds-steps.yml
|
||||
@@ -1,47 +0,0 @@
|
||||
steps:
|
||||
- task: PowerShell@2
|
||||
displayName: 'Get new versions'
|
||||
name: 'Get_versions'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: './get-new-tool-versions/get-new-tool-versions.ps1'
|
||||
arguments: |
|
||||
-DistURL "$(DIST_URL)" `
|
||||
-ManifestLink "$(MANIFEST_URL)" `
|
||||
-VersionFilterToInclude $(INCLUDE_FILTER) `
|
||||
-VersionFilterToExclude $(EXCLUDE_FILTER)
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Cancel build'
|
||||
condition: and(succeeded(), eq(variables['Get_versions.TOOL_VERSIONS'], ''))
|
||||
inputs:
|
||||
TargetType: inline
|
||||
script: |
|
||||
Import-Module "./azure-devops/azure-devops-api.ps1"
|
||||
$azureDevOpsApi = Get-AzureDevOpsApi -TeamFoundationCollectionUri $(System.TeamFoundationCollectionUri) `
|
||||
-ProjectName $(System.TeamProject) `
|
||||
-AccessToken $(System.AccessToken)
|
||||
|
||||
$AzureDevOpsApi.UpdateBuildStatus($(Build.BuildId), 'Cancelling') | Out-Null
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Set env variable'
|
||||
condition: and(succeeded(), ne(variables['Get_versions.TOOL_VERSIONS'], ''))
|
||||
inputs:
|
||||
TargetType: inline
|
||||
script: |
|
||||
$PipelineUrl = "$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)"
|
||||
Write-Output "##vso[task.setvariable variable=PIPELINE_URL]$PipelineUrl"
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Send Slack notification'
|
||||
condition: and(succeeded(), ne(variables['Get_versions.TOOL_VERSIONS'], ''))
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: './get-new-tool-versions/send-slack-notification.ps1'
|
||||
arguments: |
|
||||
-Url "$(SLACK_CHANNEL_URL)" `
|
||||
-ToolName "$(TOOL_NAME)" `
|
||||
-ToolVersion "$(Get_versions.TOOL_VERSIONS)" `
|
||||
-PipelineUrl "$(PIPELINE_URL)" `
|
||||
-ImageUrl "$(IMAGE_URL)"
|
||||
@@ -1,29 +0,0 @@
|
||||
steps:
|
||||
- checkout: self
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Get source version'
|
||||
inputs:
|
||||
TargetType: inline
|
||||
script: |
|
||||
$url = "https://api.github.com/repos/$(REPOSITORY)/commits/$(BRANCH)"
|
||||
$commit = Invoke-RestMethod -Uri $url -Method "GET"
|
||||
Write-Output "##vso[task.setvariable variable=COMMIT_SHA]$($commit.sha)"
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Run builds'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: './azure-devops/run-ci-builds.ps1'
|
||||
arguments: |
|
||||
-TeamFoundationCollectionUri $(System.TeamFoundationCollectionUri) `
|
||||
-AzureDevOpsProjectName $(System.TeamProject) `
|
||||
-AzureDevOpsAccessToken $(System.AccessToken) `
|
||||
-SourceBranch $(BRANCH) `
|
||||
-DefinitionId $(DEFINITION_ID) `
|
||||
-SourceVersion $(COMMIT_SHA) `
|
||||
-ManifestLink $(MANIFEST_URL) `
|
||||
-WaitForBuilds $(WAIT_FOR_BUILDS) `
|
||||
-ToolVersions "$(ToolVersions)" `
|
||||
-RetryIntervalSec $(INTERVAL_SEC) `
|
||||
-RetryCount $(RETRY_COUNT)
|
||||
@@ -3,6 +3,11 @@ param (
|
||||
)
|
||||
|
||||
$targetPath = $env:AGENT_TOOLSDIRECTORY
|
||||
if ([string]::IsNullOrEmpty($targetPath)) {
|
||||
# GitHub Windows images don't have `AGENT_TOOLSDIRECTORY` variable
|
||||
$targetPath = $env:RUNNER_TOOL_CACHE
|
||||
}
|
||||
|
||||
if ($ToolName) {
|
||||
$targetPath = Join-Path $targetPath $ToolName
|
||||
}
|
||||
|
||||
@@ -83,3 +83,26 @@ function IsNixPlatform {
|
||||
|
||||
return ($Platform -match "macos") -or ($Platform -match "darwin") -or ($Platform -match "ubuntu") -or ($Platform -match "linux")
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get root directory of selected tool
|
||||
#>
|
||||
function GetToolDirectory {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String]$ToolName,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String]$Version,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String]$Architecture
|
||||
)
|
||||
$targetPath = $env:AGENT_TOOLSDIRECTORY
|
||||
if ([string]::IsNullOrEmpty($targetPath)) {
|
||||
# GitHub Windows images don't have `AGENT_TOOLSDIRECTORY` variable
|
||||
$targetPath = $env:RUNNER_TOOL_CACHE
|
||||
}
|
||||
$ToolcachePath = Join-Path -Path $targetPath -ChildPath $ToolName
|
||||
$ToolcacheVersionPath = Join-Path -Path $ToolcachePath -ChildPath $Version
|
||||
return Join-Path $ToolcacheVersionPath $Architecture
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
#Requires -Modules Pester
|
||||
|
||||
Import-Module (Join-Path $PSScriptRoot "helpers.psm1") -Force
|
||||
|
||||
Describe "Validate-FiltersFormat" {
|
||||
It "Filter with word" {
|
||||
{ Validate-FiltersFormat -Filters @("1two.2") } | Should -Throw "Invalid filter format"
|
||||
}
|
||||
|
||||
It "Filter with non-word character" {
|
||||
{ Validate-FiltersFormat -Filters @("1,.2") } | Should -Throw "Invalid filter format"
|
||||
}
|
||||
|
||||
It "Valid filters" {
|
||||
{ Validate-FiltersFormat -Filters @("*", "1", "1.*", "1.2", "1.2.*") } | Should -Not -Throw "Invalid filter format"
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Format-Versions" {
|
||||
It "Clean versions" {
|
||||
$actualOutput = Format-Versions -Versions @("14.2.0", "1.14.0")
|
||||
$expectedOutput = @("14.2.0", "1.14.0")
|
||||
$actualOutput | Should -Be $expectedOutput
|
||||
}
|
||||
|
||||
It "Versions with prefixes" {
|
||||
$actualOutput = Format-Versions -Versions @("v14.2.0", "go1.14.0")
|
||||
$expectedOutput = @("14.2.0", "1.14.0")
|
||||
$actualOutput | Should -Be $expectedOutput
|
||||
}
|
||||
|
||||
It "Skip beta and rc versions" {
|
||||
$actualOutput = Format-Versions -Versions @("14.2.0-beta", "v1.14.0-rc-1")
|
||||
$expectedOutput = @()
|
||||
$actualOutput | Should -Be $expectedOutput
|
||||
}
|
||||
|
||||
It "Short version" {
|
||||
$actualOutput = Format-Versions -Versions @("14.2", "v2.0")
|
||||
$expectedOutput = @("14.2.0", "2.0.0")
|
||||
$actualOutput | Should -Be $expectedOutput
|
||||
}
|
||||
|
||||
It "Skip versions with 1 digit" {
|
||||
$actualOutput = Format-Versions -Versions @("14", "v2")
|
||||
$expectedOutput = @()
|
||||
$actualOutput | Should -Be $expectedOutput
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Select-VersionsByFilter" {
|
||||
$inputVersions = @("8.2.1", "9.3.3", "10.0.2", "10.0.3", "10.5.6", "12.4.3", "12.5.1", "14.2.0")
|
||||
|
||||
It "Include filter only" {
|
||||
$includeFilters = @("8.*", "14.*")
|
||||
$excludeFilters = @()
|
||||
$actualOutput = Select-VersionsByFilter -Versions $inputVersions -IncludeFilters $includeFilters -ExcludeFilters $excludeFilters
|
||||
$expectedOutput = @("8.2.1", "14.2.0")
|
||||
$actualOutput | Should -Be $expectedOutput
|
||||
}
|
||||
|
||||
It "Include and exclude filters" {
|
||||
$includeFilters = @("10.*", "12.*")
|
||||
$excludeFilters = @("10.0.*", "12.4.3")
|
||||
$actualOutput = Select-VersionsByFilter -Versions $inputVersions -IncludeFilters $includeFilters -ExcludeFilters $excludeFilters
|
||||
$expectedOutput = @("10.5.6", "12.5.1")
|
||||
$actualOutput | Should -Be $expectedOutput
|
||||
}
|
||||
|
||||
It "Exclude filter only" {
|
||||
$includeFilters = @()
|
||||
$excludeFilters = @("10.*", "12.*")
|
||||
$actualOutput = Select-VersionsByFilter -Versions $inputVersions -IncludeFilters $includeFilters -ExcludeFilters $excludeFilters
|
||||
$expectedOutput = @("8.2.1", "9.3.3", "14.2.0")
|
||||
$actualOutput | Should -Be $expectedOutput
|
||||
}
|
||||
|
||||
It "Include and exclude filters are empty" {
|
||||
$actualOutput = Select-VersionsByFilter -Versions $inputVersions
|
||||
$expectedOutput = @("8.2.1", "9.3.3", "10.0.2", "10.0.3", "10.5.6", "12.4.3", "12.5.1", "14.2.0")
|
||||
$actualOutput | Should -Be $expectedOutput
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Skip-ExistingVersions" {
|
||||
It "Substract versions correctly" {
|
||||
$distInput = @("14.2.0", "14.3.0", "14.4.0", "14.4.1")
|
||||
$manifestInput = @("12.0.0", "14.2.0", "14.4.0")
|
||||
$actualOutput = Skip-ExistingVersions -VersionsFromDist $distInput -VersionsFromManifest $manifestInput
|
||||
$expectedOutput = @("14.3.0", "14.4.1")
|
||||
$actualOutput | Should -Be $expectedOutput
|
||||
}
|
||||
}
|
||||
@@ -2,79 +2,26 @@
|
||||
.SYNOPSIS
|
||||
Check and return list of new available tool versions
|
||||
|
||||
.PARAMETER DistURL
|
||||
Required parameter. Link to the json file included all available tool versions
|
||||
.PARAMETER ManifestLink
|
||||
Required parameter. Link to the the version-manifest.json file
|
||||
.PARAMETER VersionFilterToInclude
|
||||
Optional parameter. List of filters to include particular versions
|
||||
.PARAMETER VersionFilterToExclude
|
||||
Optional parameter. List of filters to exclude particular versions
|
||||
.PARAMETER RetryIntervalSec
|
||||
Optional parameter. Retry interval in seconds
|
||||
.PARAMETER RetryCount
|
||||
Optional parameter. Retry count
|
||||
.PARAMETER ToolName
|
||||
Required parameter. The name of tool for which parser is available (Node, Go, Python)
|
||||
#>
|
||||
|
||||
param (
|
||||
[Parameter(Mandatory)] [string] $DistURL,
|
||||
[Parameter(Mandatory)] [string] $ManifestLink,
|
||||
[string[]] $VersionFilterToInclude,
|
||||
[string[]] $VersionFilterToExclude,
|
||||
[UInt32] $RetryIntervalSec = 60,
|
||||
[UInt32] $RetryCount = 3
|
||||
[Parameter(Mandatory)] [string] $ToolName
|
||||
)
|
||||
|
||||
Import-Module (Join-Path $PSScriptRoot "helpers.psm1")
|
||||
Import-Module "$PSScriptRoot/parsers/parsers-factory.psm1"
|
||||
|
||||
function Get-VersionsByUrl {
|
||||
param (
|
||||
[Parameter(Mandatory)] [string] $ToolPackagesUrl,
|
||||
[Parameter(Mandatory)] [UInt32] $RetryIntervalSec,
|
||||
[Parameter(Mandatory)] [UInt32] $RetryCount
|
||||
)
|
||||
$ToolVersionParser = Get-ToolVersionsParser -ToolName $ToolName
|
||||
$VersionsFromDist = $ToolVersionParser.GetAvailableVersions()
|
||||
$VersionsFromManifest = $ToolVersionParser.GetUploadedVersions()
|
||||
|
||||
$packages = Invoke-RestMethod $ToolPackagesUrl -MaximumRetryCount $RetryCount -RetryIntervalSec $RetryIntervalSec
|
||||
return $packages.version
|
||||
}
|
||||
$VersionsToBuild = $VersionsFromDist | Where-Object { $VersionsFromManifest -notcontains $_ }
|
||||
|
||||
if ($VersionFilterToInclude) {
|
||||
Validate-FiltersFormat -Filters $VersionFilterToInclude
|
||||
}
|
||||
|
||||
if ($VersionFilterToExclude) {
|
||||
Validate-FiltersFormat -Filters $VersionFilterToExclude
|
||||
}
|
||||
|
||||
Write-Host "Get the packages list from $DistURL"
|
||||
$versionsFromDist = Get-VersionsByUrl -ToolPackagesUrl $DistURL `
|
||||
-RetryIntervalSec $RetryIntervalSec `
|
||||
-RetryCount $RetryCount
|
||||
|
||||
Write-Host "Get the packages list from $ManifestLink"
|
||||
[Version[]] $versionsFromManifest = Get-VersionsByUrl -ToolPackagesUrl $ManifestLink `
|
||||
-RetryIntervalSec $RetryIntervalSec `
|
||||
-RetryCount $RetryCount
|
||||
|
||||
[Version[]] $formattedVersions = Format-Versions -Versions $versionsFromDist
|
||||
|
||||
$formattedVersions = Select-VersionsByFilter -Versions $formattedVersions `
|
||||
-IncludeFilters $VersionFilterToInclude `
|
||||
-ExcludeFilters $VersionFilterToExclude
|
||||
|
||||
if (-not $formattedVersions) {
|
||||
Write-Host "Couldn't find available versions with current filters"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$versionsToBuild = Skip-ExistingVersions -VersionsFromManifest $versionsFromManifest `
|
||||
-VersionsFromDist $formattedVersions
|
||||
|
||||
if ($versionsToBuild) {
|
||||
$availableVersions = $versionsToBuild -join ","
|
||||
$toolVersions = $availableVersions.Replace(",",", ")
|
||||
Write-Host "The following versions are available to build:`n$toolVersions"
|
||||
Write-Output "##vso[task.setvariable variable=TOOL_VERSIONS;isOutput=true]$toolVersions"
|
||||
if ($VersionsToBuild) {
|
||||
$availableVersions = $VersionsToBuild -join ", "
|
||||
Write-Host "The following versions are available to build:`n${availableVersions}"
|
||||
"TOOL_VERSIONS=${availableVersions}" >> $env:GITHUB_OUTPUT
|
||||
} else {
|
||||
Write-Host "There aren't versions to build"
|
||||
}
|
||||
|
||||
@@ -1,17 +1,3 @@
|
||||
function Validate-FiltersFormat {
|
||||
param (
|
||||
[Parameter(Mandatory)] [string[]] $Filters
|
||||
)
|
||||
|
||||
foreach($filter in $Filters) {
|
||||
$filter.Split('.') | ForEach-Object {
|
||||
if (($_ -notmatch '^\d+$') -and ($_ -ne '*')) {
|
||||
throw "Invalid filter format - $filter"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Format-Versions {
|
||||
param (
|
||||
[Parameter(Mandatory)] [string[]] $Versions
|
||||
|
||||
31
get-new-tool-versions/parsers/base-parser.psm1
Normal file
31
get-new-tool-versions/parsers/base-parser.psm1
Normal file
@@ -0,0 +1,31 @@
|
||||
class BaseVersionsParser {
|
||||
[Int32]$ApiRetryCount = 3
|
||||
[Int32]$ApiRetryIntervalSeconds = 60
|
||||
|
||||
[SemVer[]] GetAvailableVersions() {
|
||||
$allVersionsRaw = $this.ParseAllAvailableVersions()
|
||||
$allVersions = $allVersionsRaw | ForEach-Object { $this.FormatVersion($_) }
|
||||
$filteredVersions = $allVersions | Where-Object { $this.ShouldIncludeVersion($_) }
|
||||
return $filteredVersions
|
||||
}
|
||||
|
||||
[SemVer[]] GetUploadedVersions() {
|
||||
throw "Method is not implemented in base class"
|
||||
}
|
||||
|
||||
hidden [SemVer[]] ParseAllAvailableVersions() {
|
||||
throw "Method is not implemented in base class"
|
||||
}
|
||||
|
||||
hidden [SemVer] FormatVersion([string]$VersionSpec) {
|
||||
throw "Method is not implemented in base class"
|
||||
}
|
||||
|
||||
hidden [bool] ShouldIncludeVersion([SemVer]$Version) {
|
||||
throw "Method is not implemented in base class"
|
||||
}
|
||||
|
||||
hidden [string] BuildGitHubFileUrl($OrganizationName, $RepositoryName, $BranchName, $FilePath) {
|
||||
return "https://raw.githubusercontent.com/${OrganizationName}/${RepositoryName}/${BranchName}/${FilePath}"
|
||||
}
|
||||
}
|
||||
30
get-new-tool-versions/parsers/go-parser.psm1
Normal file
30
get-new-tool-versions/parsers/go-parser.psm1
Normal file
@@ -0,0 +1,30 @@
|
||||
using module "./base-parser.psm1"
|
||||
|
||||
class GoVersionsParser: BaseVersionsParser {
|
||||
[SemVer[]] GetUploadedVersions() {
|
||||
$url = $this.BuildGitHubFileUrl("actions", "go-versions", "main", "versions-manifest.json")
|
||||
$releases = Invoke-RestMethod $url -MaximumRetryCount $this.ApiRetryCount -RetryIntervalSec $this.ApiRetryIntervalSeconds
|
||||
return $releases.version
|
||||
}
|
||||
|
||||
hidden [string[]] ParseAllAvailableVersions() {
|
||||
$url = "https://golang.org/dl/?mode=json&include=all"
|
||||
$releases = Invoke-RestMethod $url -MaximumRetryCount $this.ApiRetryCount -RetryIntervalSec $this.ApiRetryIntervalSeconds
|
||||
return $releases.version
|
||||
}
|
||||
|
||||
hidden [SemVer] FormatVersion([string]$VersionSpec) {
|
||||
$cleanVersion = $VersionSpec -replace "^go", ""
|
||||
$semanticVersion = $cleanVersion -replace '(\d+\.\d+\.?\d*?)((?:alpha|beta|rc))(\d*)', '$1-$2.$3'
|
||||
return [SemVer]$semanticVersion
|
||||
}
|
||||
|
||||
hidden [bool] ShouldIncludeVersion([SemVer]$Version) {
|
||||
if ($Version.PreReleaseLabel) {
|
||||
return $false
|
||||
}
|
||||
|
||||
# For Go, we include all versions greater than 1.12
|
||||
return $Version -gt [SemVer]"1.12.0"
|
||||
}
|
||||
}
|
||||
30
get-new-tool-versions/parsers/node-parser.psm1
Normal file
30
get-new-tool-versions/parsers/node-parser.psm1
Normal file
@@ -0,0 +1,30 @@
|
||||
using module "./base-parser.psm1"
|
||||
|
||||
class NodeVersionsParser: BaseVersionsParser {
|
||||
[SemVer[]] GetUploadedVersions() {
|
||||
$url = $this.BuildGitHubFileUrl("actions", "node-versions", "main", "versions-manifest.json")
|
||||
$releases = Invoke-RestMethod $url -MaximumRetryCount $this.ApiRetryCount -RetryIntervalSec $this.ApiRetryIntervalSeconds
|
||||
return $releases.version
|
||||
}
|
||||
|
||||
hidden [string[]] ParseAllAvailableVersions() {
|
||||
$url = "https://nodejs.org/dist/index.json"
|
||||
$releases = Invoke-RestMethod $url -MaximumRetryCount $this.ApiRetryCount -RetryIntervalSec $this.ApiRetryIntervalSeconds
|
||||
return $releases.version
|
||||
}
|
||||
|
||||
hidden [SemVer] FormatVersion([string]$VersionSpec) {
|
||||
$cleanVersion = $VersionSpec -replace "^v", ""
|
||||
return [SemVer]$cleanVersion
|
||||
}
|
||||
|
||||
hidden [bool] ShouldIncludeVersion([SemVer]$Version) {
|
||||
if ($Version.Major -lt 8) {
|
||||
return $false
|
||||
}
|
||||
|
||||
# For Node.JS, we should include all LTS versions (all even-numbered releases)
|
||||
# https://nodejs.org/en/about/releases/
|
||||
return $Version.Major % 2 -eq 0
|
||||
}
|
||||
}
|
||||
19
get-new-tool-versions/parsers/parsers-factory.psm1
Normal file
19
get-new-tool-versions/parsers/parsers-factory.psm1
Normal file
@@ -0,0 +1,19 @@
|
||||
using module "./node-parser.psm1"
|
||||
using module "./go-parser.psm1"
|
||||
using module "./python-parser.psm1"
|
||||
|
||||
function Get-ToolVersionsParser {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ToolName
|
||||
)
|
||||
|
||||
switch ($ToolName) {
|
||||
"Node" { return [NodeVersionsParser]::New() }
|
||||
"Go" { return [GoVersionsParser]::New() }
|
||||
"Python" { return [PythonVersionsParser]::New() }
|
||||
Default {
|
||||
throw "Unknown tool name"
|
||||
}
|
||||
}
|
||||
}
|
||||
53
get-new-tool-versions/parsers/python-parser.psm1
Normal file
53
get-new-tool-versions/parsers/python-parser.psm1
Normal file
@@ -0,0 +1,53 @@
|
||||
using module "./base-parser.psm1"
|
||||
|
||||
class PythonVersionsParser: BaseVersionsParser {
|
||||
[SemVer[]] GetUploadedVersions() {
|
||||
$url = $this.BuildGitHubFileUrl("actions", "python-versions", "main", "versions-manifest.json")
|
||||
$releases = Invoke-RestMethod $url -MaximumRetryCount $this.ApiRetryCount -RetryIntervalSec $this.ApiRetryIntervalSeconds
|
||||
return $releases.version
|
||||
}
|
||||
|
||||
hidden [string[]] ParseAllAvailableVersions() {
|
||||
$stableVersionsUrl = "https://www.python.org/ftp/python"
|
||||
$stableVersionsHtmlRaw = Invoke-WebRequest $stableVersionsUrl -MaximumRetryCount $this.ApiRetryCount -RetryIntervalSec $this.ApiRetryIntervalSeconds
|
||||
$stableVersionsList = $stableVersionsHtmlRaw.Links.href | Where-Object {
|
||||
$parsed = $null
|
||||
return $_.EndsWith("/") -and [SemVer]::TryParse($_.Replace("/", ""), [ref]$parsed)
|
||||
}
|
||||
|
||||
return $stableVersionsList | ForEach-Object {
|
||||
$subVersionsUrl = "${stableVersionsUrl}/${_}"
|
||||
$subVersionsHtmlRaw = Invoke-WebRequest $subVersionsUrl -MaximumRetryCount $this.ApiRetryCount -RetryIntervalSec $this.ApiRetryIntervalSeconds
|
||||
return $subVersionsHtmlRaw.Links.href | ForEach-Object {
|
||||
if ($_ -match "^Python-(\d+\.\d+\.\d+[a-z]{0,2}\d*)\.tgz$") {
|
||||
return $Matches[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hidden [SemVer] FormatVersion([string]$VersionSpec) {
|
||||
$VersionSpec -match "^(\d+)\.(\d+)\.(\d+)([a-z]{1,2})?(\d+)?$"
|
||||
|
||||
if ($Matches.Count -gt 4) {
|
||||
$VersionLabel = "{0}.{1}" -f $this.ConvertPythonLabel($Matches[4]), $Matches[5]
|
||||
return [SemVer]::new($Matches[1], $Matches[2], $Matches[3], $VersionLabel)
|
||||
}
|
||||
|
||||
return [SemVer]::new($Matches[1], $Matches[2], $Matches[3])
|
||||
}
|
||||
|
||||
hidden [string] ConvertPythonLabel([string]$Label) {
|
||||
switch ($Label) {
|
||||
"a" { return "alpha" }
|
||||
"b" { return "beta" }
|
||||
}
|
||||
|
||||
return $Label
|
||||
}
|
||||
|
||||
[bool] ShouldIncludeVersion([SemVer]$Version) {
|
||||
# For Python, we include all versions greater than 3.9.0
|
||||
return $Version -gt [SemVer]"3.9.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
function Search-ToolsVersionsNotOnImage {
|
||||
param (
|
||||
[string]$ToolName,
|
||||
[string]$ReleasesUrl,
|
||||
[string]$FilterParameter,
|
||||
[string]$FilterArch
|
||||
)
|
||||
|
||||
$stableReleases = (Invoke-RestMethod $ReleasesUrl) | Where-Object stable -eq $true
|
||||
$stableReleaseVersions = $stableReleases | ForEach-Object {
|
||||
if ($ToolName -eq "Node") {
|
||||
if ($_.lts) {
|
||||
$_.$FilterParameter.split(".")[0] + ".0"
|
||||
}
|
||||
} else {
|
||||
$_.$FilterParameter.split(".")[0,1] -join"."
|
||||
}
|
||||
} | Select-Object -Unique
|
||||
$toolsetUrl = "https://raw.githubusercontent.com/actions/runner-images/main/images/win/toolsets/toolset-2022.json"
|
||||
$latestMinorVersion = (Invoke-RestMethod $toolsetUrl).toolcache |
|
||||
Where-Object {$_.name -eq $ToolName -and $_.arch -eq $FilterArch} |
|
||||
ForEach-Object {$_.versions.Replace("*","0")} |
|
||||
Select-Object -Last 1
|
||||
$versionsToAdd = $stableReleaseVersions | Where-Object {[version]$_ -gt [version]$latestMinorVersion}
|
||||
|
||||
return $versionsToAdd
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
function Search-XamarinVersionsNotOnImage {
|
||||
param (
|
||||
[string]$ReleasesUrl,
|
||||
[array]$FilterProducts
|
||||
)
|
||||
|
||||
$xamarinReleases = (Invoke-RestMethod $ReleasesUrl).items
|
||||
$filteredReleases = $xamarinReleases | Where-Object {$_.name -in $FilterProducts.name} | Sort-Object name | Select-Object name, version
|
||||
$toolsetUrl = "https://raw.githubusercontent.com/actions/runner-images/main/images/macos/toolsets/toolset-12.json"
|
||||
$uploadedReleases = (Invoke-RestMethod $toolsetUrl).xamarin
|
||||
$releasesOnImage = @()
|
||||
foreach ($FilterProduct in $FilterProducts) {
|
||||
$releasesOnImage += @{$FilterProduct.name = $uploadedReleases.($FilterProduct.property)}
|
||||
}
|
||||
$versionsToAdd = $filteredReleases | Where-Object {$releasesOnImage.($_.name) -notcontains $_.version} | ForEach-Object {[string]::Empty} {
|
||||
'{0,-15} : {1}' -f $_.name, $_.version
|
||||
}
|
||||
return $versionsToAdd
|
||||
}
|
||||
@@ -7,11 +7,15 @@ Required parameter. Incoming Webhook URL to post a message
|
||||
.PARAMETER ToolName
|
||||
Required parameter. The name of tool
|
||||
.PARAMETER ToolVersion
|
||||
Required parameter. Specifies the version of tool
|
||||
Optional parameter. Specifies the version of tool
|
||||
.PARAMETER PipelineUrl
|
||||
Required parameter. The pipeline URL
|
||||
Optional parameter. The pipeline URL
|
||||
.PARAMETER ImageUrl
|
||||
Optional parameter. The image URL
|
||||
.PARAMETER Text
|
||||
Optional parameter. The message to post
|
||||
.PARAMETER AddToToolsetFlag
|
||||
Optional parameter. Flag to alternate message text for adding new version of a tool to toolset notification
|
||||
#>
|
||||
|
||||
param(
|
||||
@@ -23,22 +27,28 @@ param(
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[System.String]$ToolName,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[System.String]$ToolVersion,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[System.String]$PipelineUrl,
|
||||
|
||||
[System.String]$ImageUrl = 'https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png'
|
||||
[System.String]$ImageUrl = 'https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png',
|
||||
[System.String]$Text,
|
||||
[Switch]$AddToToolsetFlag
|
||||
)
|
||||
|
||||
# Import helpers module
|
||||
Import-Module $PSScriptRoot/helpers.psm1 -DisableNameChecking
|
||||
|
||||
# Create JSON body
|
||||
$text = "The following versions of '$toolName' are available to upload: $toolVersion\nLink to the pipeline: $pipelineUrl"
|
||||
if ([string]::IsNullOrWhiteSpace($Text)) {
|
||||
if ($AddToToolsetFlag) {
|
||||
$Text = "The following versions of '$toolName' are available, consider adding them to toolset: $toolVersion"
|
||||
} else {
|
||||
$Text = "The following versions of '$toolName' are available to upload: $toolVersion"
|
||||
}
|
||||
}
|
||||
if (-not ([string]::IsNullOrWhiteSpace($PipelineUrl))) {
|
||||
$Text += "\nLink to the pipeline: $pipelineUrl"
|
||||
}
|
||||
|
||||
$jsonBodyMessage = @"
|
||||
{
|
||||
"blocks": [
|
||||
@@ -46,7 +56,7 @@ $jsonBodyMessage = @"
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "$text"
|
||||
"text": "$Text"
|
||||
},
|
||||
"accessory": {
|
||||
"type": "image",
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Check and return list of new available tool versions that not added to toolsets yet
|
||||
|
||||
.PARAMETER ToolName
|
||||
Required parameter. The name of tool for which parser is available (Python, Xamarin, PyPy, Node, Go)
|
||||
#>
|
||||
|
||||
param (
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateSet("Python", "Xamarin", "PyPy", "Node", "Go")]
|
||||
[string] $ToolName,
|
||||
[string] $ReleasesUrl,
|
||||
[string] $FilterParameter,
|
||||
[string] $FilterArch
|
||||
)
|
||||
|
||||
Get-ChildItem "$PSScriptRoot/parsers/verify-added-to-image/" | ForEach-Object {Import-Module $_.FullName}
|
||||
|
||||
if ($ToolName -in "Python", "PyPy", "Node", "Go") {
|
||||
$versionsToAdd = Search-ToolsVersionsNotOnImage -ToolName $ToolName -ReleasesUrl $ReleasesUrl -FilterParameter $FilterParameter -FilterArch $FilterArch
|
||||
}
|
||||
|
||||
if ($ToolName -eq "Xamarin") {
|
||||
$xamarinReleases = "http://aka.ms/manifest/stable-2022"
|
||||
$xamarinProducts = @(
|
||||
[PSCustomObject] @{name = 'Mono Framework'; property = 'mono-versions'}
|
||||
[PSCustomObject] @{name = 'Xamarin.Android'; property = 'android-versions'}
|
||||
[PSCustomObject] @{name = 'Xamarin.iOS'; property = 'ios-versions'}
|
||||
[PSCustomObject] @{name = 'Xamarin.Mac'; property = 'mac-versions'}
|
||||
)
|
||||
$versionsToAdd = Search-XamarinVersionsNotOnImage -ReleasesUrl $xamarinReleases -FilterProducts $xamarinProducts
|
||||
$joinChars = "\n\t"
|
||||
}
|
||||
|
||||
$versionsToAdd = $versionsToAdd -join $joinChars
|
||||
|
||||
return $versionsToAdd
|
||||
@@ -2,10 +2,8 @@
|
||||
.SYNOPSIS
|
||||
Create commit with all unstaged changes in repository and create pull-request
|
||||
|
||||
.PARAMETER RepositoryOwner
|
||||
Required parameter. The organization which tool repository belongs
|
||||
.PARAMETER RepositoryName
|
||||
Optional parameter. The name of tool repository
|
||||
.PARAMETER RepositoryFullName
|
||||
Required parameter. The owner and repository name. For example, 'actions/versions-package-tools'
|
||||
.PARAMETER AccessToken
|
||||
Required parameter. PAT Token to authorize
|
||||
.PARAMETER BranchName
|
||||
@@ -18,8 +16,7 @@ Required parameter. The title of pull-request
|
||||
Required parameter. The description of pull-request
|
||||
#>
|
||||
param (
|
||||
[Parameter(Mandatory)] [string] $RepositoryOwner,
|
||||
[Parameter(Mandatory)] [string] $RepositoryName,
|
||||
[Parameter(Mandatory)] [string] $RepositoryFullName,
|
||||
[Parameter(Mandatory)] [string] $AccessToken,
|
||||
[Parameter(Mandatory)] [string] $BranchName,
|
||||
[Parameter(Mandatory)] [string] $CommitMessage,
|
||||
@@ -46,11 +43,11 @@ function Update-PullRequest {
|
||||
|
||||
$updatedPullRequest = $GitHubApi.UpdatePullRequest($Title, $Body, $BranchName, $PullRequest.number)
|
||||
|
||||
if (($updatedPullRequest -eq $null) -or ($updatedPullRequest.html_url -eq $null)) {
|
||||
Write-Host "##vso[task.logissue type=error;] Unexpected error occurs while updating pull request."
|
||||
if (($null -eq $updatedPullRequest) -or ($null -eq $updatedPullRequest.html_url)) {
|
||||
Write-Host "Unexpected error occurs while updating pull request."
|
||||
exit 1
|
||||
}
|
||||
Write-host "##[section] Pull request updated: $($updatedPullRequest.html_url)"
|
||||
Write-host "Pull request updated: $($updatedPullRequest.html_url)"
|
||||
}
|
||||
|
||||
function Create-PullRequest {
|
||||
@@ -67,12 +64,12 @@ function Create-PullRequest {
|
||||
|
||||
$createdPullRequest = $GitHubApi.CreateNewPullRequest($Title, $Body, $BranchName)
|
||||
|
||||
if (($createdPullRequest -eq $null) -or ($createdPullRequest.html_url -eq $null)) {
|
||||
Write-Host "##vso[task.logissue type=error;] Unexpected error occurs while creating pull request."
|
||||
if (($null -eq $createdPullRequest) -or ($null -eq $createdPullRequest.html_url)) {
|
||||
Write-Host "Unexpected error occurs while creating pull request."
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-host "##[section] Pull request created: $($createdPullRequest.html_url)"
|
||||
Write-host "Pull request created: $($createdPullRequest.html_url)"
|
||||
}
|
||||
|
||||
Write-Host "Configure local git preferences"
|
||||
@@ -87,8 +84,8 @@ Git-CommitAllChanges -Message $CommitMessage
|
||||
Write-Host "Push branch: $BranchName"
|
||||
Git-PushBranch -Name $BranchName -Force $true
|
||||
|
||||
$gitHubApi = Get-GitHubApi -AccountName $RepositoryOwner -ProjectName $RepositoryName -AccessToken $AccessToken
|
||||
$pullRequest = $gitHubApi.GetPullRequest($BranchName, $RepositoryOwner)
|
||||
$gitHubApi = Get-GitHubApi -RepositoryFullName $RepositoryFullName -AccessToken $AccessToken
|
||||
$pullRequest = $gitHubApi.GetPullRequest($BranchName)
|
||||
|
||||
if ($pullRequest.Count -gt 0) {
|
||||
Write-Host "Update pull request"
|
||||
|
||||
86
github/create-release.ps1
Normal file
86
github/create-release.ps1
Normal file
@@ -0,0 +1,86 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Trigger runs on the workflow_dispatch event to create tool release
|
||||
|
||||
.PARAMETER RepositoryFullName
|
||||
Required parameter. The owner and repository name. For example, 'actions/versions-package-tools'
|
||||
.PARAMETER AccessToken
|
||||
Required parameter. PAT Token to authorize
|
||||
.PARAMETER ToolVersion
|
||||
Required parameter. Version of tool
|
||||
.PARAMETER TagName
|
||||
Required parameter. The name of the release tag
|
||||
.PARAMETER ReleaseBody
|
||||
Required parameter. Text describing the contents of the release
|
||||
.PARAMETER EventType
|
||||
Required parameter. The name of the repository dispatch event
|
||||
#>
|
||||
|
||||
param (
|
||||
[Parameter(Mandatory)] [string] $RepositoryFullName,
|
||||
[Parameter(Mandatory)] [string] $AccessToken,
|
||||
[Parameter(Mandatory)] [string] $ToolVersion,
|
||||
[Parameter(Mandatory)] [string] $TagName,
|
||||
[Parameter(Mandatory)] [string] $ReleaseBody,
|
||||
[Parameter(Mandatory)] [string] $EventType,
|
||||
[UInt32] $RetryIntervalSec = 10,
|
||||
[UInt32] $RetryCount = 5
|
||||
)
|
||||
|
||||
Import-Module (Join-Path $PSScriptRoot "github-api.psm1")
|
||||
|
||||
function Create-Release {
|
||||
param (
|
||||
[Parameter(Mandatory)] [object] $GitHubApi,
|
||||
[Parameter(Mandatory)] [string] $ToolVersion,
|
||||
[Parameter(Mandatory)] [string] $TagName,
|
||||
[Parameter(Mandatory)] [string] $ReleaseBody,
|
||||
[Parameter(Mandatory)] [string] $EventType
|
||||
)
|
||||
|
||||
$eventPayload = @{
|
||||
ToolVersion = $ToolVersion
|
||||
TagName = $TagName
|
||||
ReleaseBody = $ReleaseBody
|
||||
}
|
||||
|
||||
Write-Host "Create '$EventType' repository dispatch event"
|
||||
$GitHubApi.CreateRepositoryDispatch($EventType, $eventPayload)
|
||||
}
|
||||
|
||||
function Validate-ReleaseAvailability {
|
||||
param (
|
||||
[Parameter(Mandatory)] [object] $GitHubApi,
|
||||
[Parameter(Mandatory)] [string] $TagName,
|
||||
[Parameter(Mandatory)] [UInt32] $RetryIntervalSec,
|
||||
[Parameter(Mandatory)] [UInt32] $RetryCount
|
||||
)
|
||||
|
||||
do {
|
||||
$createdRelease = $GitHubApi.GetReleases() | Where-Object { $_.tag_name -eq $TagName }
|
||||
if ($createdRelease) {
|
||||
Write-Host "Release was successfully created: $($createdRelease.html_url)"
|
||||
return
|
||||
}
|
||||
|
||||
$RetryCount--
|
||||
Start-Sleep -Seconds $RetryIntervalSec
|
||||
} while($RetryCount -gt 0)
|
||||
|
||||
Write-Host "Release was not created"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$gitHubApi = Get-GitHubApi -RepositoryFullName $RepositoryFullName -AccessToken $AccessToken
|
||||
|
||||
Create-Release -GitHubApi $gitHubApi `
|
||||
-ToolVersion $ToolVersion `
|
||||
-TagName $TagName `
|
||||
-ReleaseBody $ReleaseBody `
|
||||
-EventType $EventType
|
||||
|
||||
Start-Sleep -s $RetryIntervalSec
|
||||
Validate-ReleaseAvailability -GitHubApi $gitHubApi `
|
||||
-TagName $TagName `
|
||||
-RetryIntervalSec $RetryIntervalSec `
|
||||
-RetryCount $RetryCount
|
||||
@@ -5,8 +5,8 @@ The module that contains a bunch of methods to interact with GitHub API V3
|
||||
class GitHubApi
|
||||
{
|
||||
[string] $BaseUrl
|
||||
[string] $RepoOwner
|
||||
[object] $AuthHeader
|
||||
[string] $RepositoryOwner
|
||||
|
||||
GitHubApi(
|
||||
[string] $AccountName,
|
||||
@@ -15,6 +15,7 @@ class GitHubApi
|
||||
) {
|
||||
$this.BaseUrl = $this.BuildBaseUrl($AccountName, $ProjectName)
|
||||
$this.AuthHeader = $this.BuildAuth($AccessToken)
|
||||
$this.RepositoryOwner = $AccountName
|
||||
}
|
||||
|
||||
[object] hidden BuildAuth([string]$AccessToken) {
|
||||
@@ -43,9 +44,9 @@ class GitHubApi
|
||||
return $this.InvokeRestMethod($url, 'Post', $null, $requestBody)
|
||||
}
|
||||
|
||||
[object] GetPullRequest([string]$BranchName, [string]$RepositoryOwner){
|
||||
[object] GetPullRequest([string]$BranchName){
|
||||
$url = "pulls"
|
||||
return $this.InvokeRestMethod($url, 'GET', "head=${RepositoryOwner}:$BranchName&base=main", $null)
|
||||
return $this.InvokeRestMethod($url, 'GET', "head=$($this.RepositoryOwner):${BranchName}&base=main", $null)
|
||||
}
|
||||
|
||||
[object] UpdatePullRequest([string]$Title, [string]$Body, [string]$BranchName, [string]$PullRequestNumber){
|
||||
@@ -82,6 +83,39 @@ class GitHubApi
|
||||
return $releases
|
||||
}
|
||||
|
||||
[void] CreateRepositoryDispatch([string]$EventType, [object]$EventPayload) {
|
||||
$url = "dispatches"
|
||||
$body = @{
|
||||
event_type = $EventType
|
||||
client_payload = $EventPayload
|
||||
} | ConvertTo-Json
|
||||
|
||||
$this.InvokeRestMethod($url, 'POST', $null, $body)
|
||||
}
|
||||
|
||||
[object] GetWorkflowRuns([string]$WorkflowFileName) {
|
||||
$url = "actions/workflows/$WorkflowFileName/runs"
|
||||
return $this.InvokeRestMethod($url, 'GET', $null, $null)
|
||||
}
|
||||
|
||||
[object] GetWorkflowRunJobs([string]$WorkflowRunId) {
|
||||
$url = "actions/runs/$WorkflowRunId/jobs"
|
||||
return $this.InvokeRestMethod($url, 'GET', $null, $null)
|
||||
}
|
||||
|
||||
[void] CreateWorkflowDispatch([string]$WorkflowFileName, [string]$Ref, [object]$Inputs) {
|
||||
$url = "actions/workflows/${WorkflowFileName}/dispatches"
|
||||
$body = @{ ref = $Ref }
|
||||
|
||||
if ($Inputs) {
|
||||
$body.inputs = $Inputs
|
||||
}
|
||||
|
||||
$jsonBody = $body | ConvertTo-Json
|
||||
|
||||
$this.InvokeRestMethod($url, 'POST', $null, $jsonBody)
|
||||
}
|
||||
|
||||
[string] hidden BuildUrl([string]$Url, [string]$RequestParams) {
|
||||
if ([string]::IsNullOrEmpty($RequestParams)) {
|
||||
return "$($this.BaseUrl)/$($Url)"
|
||||
@@ -90,6 +124,11 @@ class GitHubApi
|
||||
}
|
||||
}
|
||||
|
||||
[void] CancelWorkflow([string]$WorkflowId) {
|
||||
$url = "actions/runs/$WorkflowId/cancel"
|
||||
$this.InvokeRestMethod($url, 'POST', $null, $null)
|
||||
}
|
||||
|
||||
[object] hidden InvokeRestMethod(
|
||||
[string] $Url,
|
||||
[string] $Method,
|
||||
@@ -117,10 +156,18 @@ class GitHubApi
|
||||
|
||||
function Get-GitHubApi {
|
||||
param (
|
||||
[string] $AccountName,
|
||||
[string] $ProjectName,
|
||||
[Parameter(ParameterSetName = 'RepositorySingle')]
|
||||
[string] $RepositoryFullName,
|
||||
[Parameter(ParameterSetName = 'RepositorySplitted')]
|
||||
[string] $RepositoryOwner,
|
||||
[Parameter(ParameterSetName = 'RepositorySplitted')]
|
||||
[string] $RepositoryName,
|
||||
[string] $AccessToken
|
||||
)
|
||||
|
||||
return [GitHubApi]::New($AccountName, $ProjectName, $AccessToken)
|
||||
if ($PSCmdlet.ParameterSetName -eq "RepositorySingle") {
|
||||
$RepositoryOwner, $RepositoryName = $RepositoryFullName.Split('/', 2)
|
||||
}
|
||||
|
||||
return [GitHubApi]::New($RepositoryOwner, $RepositoryName, $AccessToken)
|
||||
}
|
||||
91
github/run-ci-builds.ps1
Normal file
91
github/run-ci-builds.ps1
Normal file
@@ -0,0 +1,91 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Trigger runs on the workflow_dispatch event to build and upload tool packages
|
||||
|
||||
.PARAMETER RepositoryFullName
|
||||
Required parameter. The owner and repository name. For example, 'actions/versions-package-tools'
|
||||
.PARAMETER AccessToken
|
||||
Required parameter. PAT to authorize
|
||||
.PARAMETER WorkflowFileName
|
||||
Required parameter. The name of workflow file that will be triggered
|
||||
.PARAMETER WorkflowDispatchRef
|
||||
Required parameter. The reference of the workflow run. The reference can be a branch, tag, or a commit SHA.
|
||||
.PARAMETER ToolVersions
|
||||
Required parameter. List of tool versions to build and upload
|
||||
.PARAMETER PublishReleases
|
||||
Required parameter. Whether to publish releases, true or false
|
||||
#>
|
||||
|
||||
param (
|
||||
[Parameter(Mandatory)] [string] $RepositoryFullName,
|
||||
[Parameter(Mandatory)] [string] $AccessToken,
|
||||
[Parameter(Mandatory)] [string] $WorkflowFileName,
|
||||
[Parameter(Mandatory)] [string] $WorkflowDispatchRef,
|
||||
[Parameter(Mandatory)] [string] $ToolVersions,
|
||||
[Parameter(Mandatory)] [string] $PublishReleases
|
||||
)
|
||||
|
||||
Import-Module (Join-Path $PSScriptRoot "github-api.psm1")
|
||||
|
||||
function Get-WorkflowRunLink {
|
||||
param(
|
||||
[Parameter(Mandatory)] [object] $GitHubApi,
|
||||
[Parameter(Mandatory)] [string] $WorkflowFileName,
|
||||
[Parameter(Mandatory)] [string] $ToolVersion
|
||||
)
|
||||
|
||||
$listWorkflowRuns = $GitHubApi.GetWorkflowRuns($WorkflowFileName).workflow_runs | Sort-Object -Property 'run_number' -Descending
|
||||
|
||||
foreach ($workflowRun in $listWorkflowRuns) {
|
||||
$workflowRunJob = $gitHubApi.GetWorkflowRunJobs($workflowRun.id).jobs | Select-Object -First 1
|
||||
|
||||
if ($workflowRunJob.name -match $ToolVersion) {
|
||||
return $workflowRun.html_url
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Queue-Builds {
|
||||
param (
|
||||
[Parameter(Mandatory)] [object] $GitHubApi,
|
||||
[Parameter(Mandatory)] [string] $ToolVersions,
|
||||
[Parameter(Mandatory)] [string] $WorkflowFileName,
|
||||
[Parameter(Mandatory)] [string] $WorkflowDispatchRef,
|
||||
[Parameter(Mandatory)] [string] $PublishReleases
|
||||
)
|
||||
|
||||
$inputs = @{
|
||||
PUBLISH_RELEASES = $PublishReleases
|
||||
}
|
||||
|
||||
$ToolVersions.Split(',') | ForEach-Object {
|
||||
$version = $_.Trim()
|
||||
$inputs.VERSION = $version
|
||||
|
||||
Write-Host "Queue build for $version..."
|
||||
$GitHubApi.CreateWorkflowDispatch($WorkflowFileName, $WorkflowDispatchRef, $inputs)
|
||||
|
||||
Start-Sleep -s 10
|
||||
$workflowRunLink = Get-WorkflowRunLink -GitHubApi $GitHubApi `
|
||||
-WorkflowFileName $WorkflowFileName `
|
||||
-ToolVersion $version
|
||||
|
||||
if (-not $workflowRunLink) {
|
||||
Write-Host "Could not find build for $version..."
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Link to the build: $workflowRunLink"
|
||||
}
|
||||
}
|
||||
|
||||
$gitHubApi = Get-GitHubApi -RepositoryFullName $RepositoryFullName -AccessToken $AccessToken
|
||||
|
||||
Write-Host "Versions to build: $ToolVersions"
|
||||
Queue-Builds -GitHubApi $gitHubApi `
|
||||
-ToolVersions $ToolVersions `
|
||||
-WorkflowFileName $WorkflowFileName `
|
||||
-WorkflowDispatchRef $WorkflowDispatchRef `
|
||||
-PublishReleases $PublishReleases
|
||||
@@ -1,13 +1,10 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Generate versions manifest based on repository releases
|
||||
|
||||
.DESCRIPTION
|
||||
Versions manifest is needed to find the latest assets for particular version of tool
|
||||
.PARAMETER GitHubRepositoryOwner
|
||||
Required parameter. The organization which tool repository belongs
|
||||
.PARAMETER GitHubRepositoryName
|
||||
Required parameter. The name of tool repository
|
||||
.PARAMETER RepositoryFullName
|
||||
Required parameter. The owner and repository name. For example, 'actions/versions-package-tools'
|
||||
.PARAMETER GitHubAccessToken
|
||||
Required parameter. PAT Token to overcome GitHub API Rate limit
|
||||
.PARAMETER OutputFile
|
||||
@@ -17,8 +14,7 @@ Path to the json file with parsing configuration
|
||||
#>
|
||||
|
||||
param (
|
||||
[Parameter(Mandatory)] [string] $GitHubRepositoryOwner,
|
||||
[Parameter(Mandatory)] [string] $GitHubRepositoryName,
|
||||
[Parameter(Mandatory)] [string] $RepositoryFullName,
|
||||
[Parameter(Mandatory)] [string] $GitHubAccessToken,
|
||||
[Parameter(Mandatory)] [string] $OutputFile,
|
||||
[Parameter(Mandatory)] [string] $ConfigurationFile
|
||||
@@ -29,7 +25,7 @@ Import-Module (Join-Path $PSScriptRoot "manifest-utils.psm1") -Force
|
||||
|
||||
$configuration = Read-ConfigurationFile -Filepath $ConfigurationFile
|
||||
|
||||
$gitHubApi = Get-GitHubApi -AccountName $GitHubRepositoryOwner -ProjectName $GitHubRepositoryName -AccessToken $GitHubAccessToken
|
||||
$gitHubApi = Get-GitHubApi -RepositoryFullName $RepositoryFullName -AccessToken $GitHubAccessToken
|
||||
$releases = $gitHubApi.GetReleases()
|
||||
$versionIndex = Build-VersionsManifest -Releases $releases -Configuration $configuration
|
||||
$versionIndex | ConvertTo-Json -Depth 5 | Out-File $OutputFile -Encoding UTF8NoBOM -Force
|
||||
|
||||
@@ -77,6 +77,7 @@ Describe "Build-VersionsManifest" {
|
||||
$assets = @(
|
||||
@{ name = "python-3.8.3-linux-16.04-x64.tar.gz"; browser_download_url = "fake_url"; }
|
||||
@{ name = "python-3.8.3-linux-18.04-x64.tar.gz"; browser_download_url = "fake_url"; }
|
||||
@{ name = "hashes.sha256"; browser_download_url = "fake_url"; }
|
||||
)
|
||||
$configuration = @{
|
||||
regex = "python-\d+\.\d+\.\d+-(\w+)-([\w\.]+)?-?(x\d+)";
|
||||
@@ -126,7 +127,7 @@ Describe "Build-VersionsManifest" {
|
||||
[PSCustomObject]@{ version = "3.8.3"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFiles }
|
||||
)
|
||||
[array]$actualManifest = Build-VersionsManifest -Releases $releases -Configuration $configuration
|
||||
Assert-Equivalent -Actual $actualManifest -Expected $expectedManifest
|
||||
Assert-Equivalent -Actual $actualManifest -Expected $expectedManifest
|
||||
}
|
||||
|
||||
It "take latest published release for each version" {
|
||||
@@ -141,4 +142,74 @@ Describe "Build-VersionsManifest" {
|
||||
[array]$actualManifest = Build-VersionsManifest -Releases $releases -Configuration $configuration
|
||||
Assert-Equivalent -Actual $actualManifest -Expected $expectedManifest
|
||||
}
|
||||
|
||||
It "build correct manifest if release includes one asset" {
|
||||
$asset = @(
|
||||
@{ name = "python-3.8.3-linux-16.04-x64.tar.gz"; browser_download_url = "fake_url"; }
|
||||
)
|
||||
$expectedManifestFile = @(
|
||||
[PSCustomObject]@{ filename = "python-3.8.3-linux-16.04-x64.tar.gz"; arch = "x64"; platform = "linux"; platform_version = "16.04"; download_url = "fake_url" }
|
||||
)
|
||||
|
||||
$releases = @(
|
||||
@{ name = "3.8.3"; draft = $false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-06T11:43:38Z"; assets = $asset },
|
||||
@{ name = "3.8.1"; draft = $false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-14T09:54:06Z"; assets = $assets }
|
||||
)
|
||||
$expectedManifest = @(
|
||||
[PSCustomObject]@{ version = "3.8.3"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFile },
|
||||
[PSCustomObject]@{ version = "3.8.1"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFiles }
|
||||
)
|
||||
[array]$actualManifest = Build-VersionsManifest -Releases $releases -Configuration $configuration
|
||||
Assert-Equivalent -Actual $actualManifest -Expected $expectedManifest
|
||||
}
|
||||
|
||||
It "set correct lts value for versions" {
|
||||
$releases = @(
|
||||
@{ name = "14.2.1"; draft = false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-14T09:54:06Z"; assets = $assets },
|
||||
@{ name = "12.0.1"; draft = $false; prerelease = false; html_url = "fake_html_url"; published_at = "2020-05-06T11:45:36Z"; assets = $assets },
|
||||
@{ name = "16.2.2"; draft = $false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-06T11:43:38Z"; assets = $assets }
|
||||
)
|
||||
$configuration = @{
|
||||
regex = "python-\d+\.\d+\.\d+-(\w+)-([\w\.]+)?-?(x\d+)";
|
||||
groups = [PSCustomObject]@{ platform = 1; platform_version = 2; arch = "x64"; }
|
||||
lts_rule_expression = "@(@{ Name = '14'; Value = 'Fermium' }, @{ Name = '12'; Value = 'Erbium' })"
|
||||
}
|
||||
$expectedManifest = @(
|
||||
[PSCustomObject]@{ version = "16.2.2"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFiles },
|
||||
[PSCustomObject]@{ version = "14.2.1"; stable = $true; lts = "Fermium"; release_url = "fake_html_url"; files = $expectedManifestFiles },
|
||||
[PSCustomObject]@{ version = "12.0.1"; stable = $true; lts = "Erbium"; release_url = "fake_html_url"; files = $expectedManifestFiles }
|
||||
)
|
||||
[array]$actualManifest = Build-VersionsManifest -Releases $releases -Configuration $configuration
|
||||
Assert-Equivalent -Actual $actualManifest -Expected $expectedManifest
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Get-VersionLtsStatus" {
|
||||
$ltsRules = @(
|
||||
@{ Name = "14"; Value = "Fermium" },
|
||||
@{ Name = "12"; Value = "Erbium" },
|
||||
@{ Name = "10"; Value = $true },
|
||||
@{ Name = "8.3"; Value = "LTS 8.3" }
|
||||
)
|
||||
|
||||
It "lts label is matched" {
|
||||
Get-VersionLtsStatus -Version "14.2.2" -LtsRules $ltsRules | Should -Be "Fermium"
|
||||
Get-VersionLtsStatus -Version "12.3.1" -LtsRules $ltsRules | Should -Be "Erbium"
|
||||
Get-VersionLtsStatus -Version "10.8.1" -LtsRules $ltsRules | Should -Be $true
|
||||
Get-VersionLtsStatus -Version "8.3.2" -LtsRules $ltsRules | Should -Be "LTS 8.3"
|
||||
Get-VersionLtsStatus -Version "14" -LtsRules $ltsRules | Should -Be "Fermium"
|
||||
}
|
||||
|
||||
It "lts label is not matched" {
|
||||
Get-VersionLtsStatus -Version "9.1" -LtsRules $ltsRules | Should -Be $null
|
||||
Get-VersionLtsStatus -Version "13.8" -LtsRules $ltsRules | Should -Be $null
|
||||
Get-VersionLtsStatus -Version "5" -LtsRules $ltsRules | Should -Be $null
|
||||
Get-VersionLtsStatus -Version "8.4" -LtsRules $ltsRules | Should -Be $null
|
||||
Get-VersionLtsStatus -Version "142.5.1" -LtsRules $ltsRules | Should -Be $null
|
||||
}
|
||||
|
||||
It "no rules" {
|
||||
Get-VersionLtsStatus -Version "14.2.2" | Should -Be $null
|
||||
Get-VersionLtsStatus -Version "12.3.1" -LtsRules $null | Should -Be $null
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ function Build-VersionsManifest {
|
||||
)
|
||||
|
||||
$Releases = $Releases | Sort-Object -Property "published_at" -Descending
|
||||
$ltsRules = Get-LtsRules -Configuration $Configuration
|
||||
|
||||
$versionsHash = @{}
|
||||
foreach ($release in $Releases) {
|
||||
@@ -64,15 +65,49 @@ function Build-VersionsManifest {
|
||||
continue
|
||||
}
|
||||
|
||||
$ltsStatus = Get-VersionLtsStatus -Version $versionKey -LtsRules $ltsRules
|
||||
$stable = $version.PreReleaseLabel ? $false : $true
|
||||
$versionsHash.Add($versionKey, [PSCustomObject]@{
|
||||
version = $versionKey
|
||||
stable = $stable
|
||||
release_url = $release.html_url
|
||||
files = $release.assets | ForEach-Object { New-AssetItem -ReleaseAsset $_ -Configuration $Configuration }
|
||||
})
|
||||
[array]$releaseAssets = $release.assets | Where { $_.Name -ne "hashes.sha256" } | ForEach-Object { New-AssetItem -ReleaseAsset $_ -Configuration $Configuration }
|
||||
|
||||
$versionHash = [PSCustomObject]@{}
|
||||
$versionHash | Add-Member -Name "version" -Value $versionKey -MemberType NoteProperty
|
||||
$versionHash | Add-Member -Name "stable" -Value $stable -MemberType NoteProperty
|
||||
if ($ltsStatus) {
|
||||
$versionHash | Add-Member -Name "lts" -Value $ltsStatus -MemberType NoteProperty
|
||||
}
|
||||
$versionHash | Add-Member -Name "release_url" -Value $release.html_url -MemberType NoteProperty
|
||||
$versionHash | Add-Member -Name "files" -Value $releaseAssets -MemberType NoteProperty
|
||||
$versionsHash.Add($versionKey, $versionHash)
|
||||
}
|
||||
|
||||
# Sort versions by descending
|
||||
return $versionsHash.Values | Sort-Object -Property @{ Expression = { [Semver]$_.version }; Descending = $true }
|
||||
}
|
||||
}
|
||||
|
||||
function Get-LtsRules {
|
||||
param (
|
||||
[Parameter(Mandatory)][object]$Configuration
|
||||
)
|
||||
|
||||
$ruleExpression = $Configuration."lts_rule_expression"
|
||||
if ($ruleExpression) {
|
||||
Invoke-Expression $ruleExpression
|
||||
} else {
|
||||
@()
|
||||
}
|
||||
}
|
||||
|
||||
function Get-VersionLtsStatus {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$Version,
|
||||
[array]$LtsRules
|
||||
)
|
||||
|
||||
foreach ($ltsRule in $LtsRules) {
|
||||
if (($Version -eq $ltsRule.Name) -or ($Version.StartsWith("$($ltsRule.Name)."))) {
|
||||
return $ltsRule.Value
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
param (
|
||||
[Parameter(Mandatory)][string] $ManifestUrl,
|
||||
[string] $AccessToken
|
||||
[Parameter(Mandatory)][string] $ManifestPath
|
||||
)
|
||||
|
||||
$authorizationHeaderValue = "Basic $AccessToken"
|
||||
$webRequestHeaders = @{}
|
||||
if ($AccessToken) {
|
||||
$webRequestHeaders.Add("Authorization", $authorizationHeaderValue)
|
||||
}
|
||||
$Global:validationFailed = $false
|
||||
|
||||
function Publish-Error {
|
||||
param(
|
||||
[string] $ErrorDescription,
|
||||
[object] $Exception
|
||||
)
|
||||
Write-Host "##vso[task.logissue type=error]ERROR: $ErrorDescription."
|
||||
Write-Host "##vso[task.logissue type=error] $Exception"
|
||||
Write-Host "##vso[task.complete result=Failed;]"
|
||||
|
||||
Write-Output "::error ::$ErrorDescription"
|
||||
if (-not [string]::IsNullOrEmpty($Exception))
|
||||
{
|
||||
Write-Output "Exception: $Exception"
|
||||
}
|
||||
$Global:validationFailed = $true
|
||||
}
|
||||
|
||||
function Test-DownloadUrl {
|
||||
param([string] $DownloadUrl)
|
||||
param(
|
||||
[string] $DownloadUrl
|
||||
)
|
||||
|
||||
$request = [System.Net.WebRequest]::Create($DownloadUrl)
|
||||
if ($AccessToken) {
|
||||
$request.Headers.Add("Authorization", $authorizationHeaderValue)
|
||||
}
|
||||
try {
|
||||
$response = $request.GetResponse()
|
||||
return ([int]$response.StatusCode -eq 200)
|
||||
@@ -33,19 +32,16 @@ function Test-DownloadUrl {
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Downloading manifest json from '$ManifestUrl'..."
|
||||
try {
|
||||
$manifestResponse = Invoke-WebRequest -Method Get -Uri $ManifestUrl -Headers $webRequestHeaders
|
||||
} catch {
|
||||
Publish-Error "Unable to download manifest json from '$ManifestUrl'" $_
|
||||
if (-not (Test-Path $ManifestPath)) {
|
||||
Publish-Error "Unable to find manifest json file at '$ManifestPath'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Parsing manifest json content from '$ManifestUrl'..."
|
||||
Write-Host "Parsing manifest json content from '$ManifestPath'..."
|
||||
try {
|
||||
$manifestJson = $manifestResponse.Content | ConvertFrom-Json
|
||||
$manifestJson = Get-Content $ManifestPath | ConvertFrom-Json
|
||||
} catch {
|
||||
Publish-Error "Unable to parse manifest json content '$ManifestUrl'" $_
|
||||
Publish-Error "Unable to parse manifest json content '$ManifestPath'" $_
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -61,3 +57,7 @@ $manifestJson | ForEach-Object {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($Global:validationFailed) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -4,30 +4,52 @@ Pester extension that allows to run command and validate exit code
|
||||
.EXAMPLE
|
||||
"python file.py" | Should -ReturnZeroExitCode
|
||||
#>
|
||||
|
||||
function Get-CommandResult {
|
||||
Param (
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string] $Command,
|
||||
[switch] $Multiline
|
||||
)
|
||||
# CMD and bash trick to suppress and show error output because some commands write to stderr (for example, "python --version")
|
||||
If ($IsWindows) {
|
||||
$stdout = & $env:comspec /c "$Command 2>&1"
|
||||
} else {
|
||||
$stdout = & bash -c "$Command 2>&1"
|
||||
}
|
||||
$exitCode = $LASTEXITCODE
|
||||
|
||||
return @{
|
||||
Output = If ($Multiline -eq $true) { $stdout } else { [string]$stdout }
|
||||
ExitCode = $exitCode
|
||||
}
|
||||
}
|
||||
|
||||
function ShouldReturnZeroExitCode {
|
||||
Param(
|
||||
[Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()]
|
||||
[String]$ActualValue,
|
||||
[switch]$Negate
|
||||
[String] $ActualValue,
|
||||
[switch] $Negate,
|
||||
[string] $Because # This parameter is unused by we need it to match Pester asserts signature
|
||||
)
|
||||
|
||||
Write-Host "Run command '${ActualValue}'"
|
||||
Invoke-Expression -Command $ActualValue | ForEach-Object { Write-Host $_ }
|
||||
$actualExitCode = $LASTEXITCODE
|
||||
$result = Get-CommandResult $ActualValue
|
||||
|
||||
[bool]$succeeded = $actualExitCode -eq 0
|
||||
[bool]$succeeded = $result.ExitCode -eq 0
|
||||
if ($Negate) { $succeeded = -not $succeeded }
|
||||
|
||||
if (-not $succeeded)
|
||||
{
|
||||
$failureMessage = "Command '${ActualValue}' has finished with exit code ${actualExitCode}"
|
||||
$commandOutputIndent = " " * 4
|
||||
$commandOutput = ($result.Output | ForEach-Object { "${commandOutputIndent}${_}" }) -join "`n"
|
||||
$failureMessage = "Command '${ActualValue}' has finished with exit code ${actualExitCode}`n${commandOutput}"
|
||||
}
|
||||
|
||||
return New-Object PSObject -Property @{
|
||||
return [PSCustomObject] @{
|
||||
Succeeded = $succeeded
|
||||
FailureMessage = $failureMessage
|
||||
}
|
||||
}
|
||||
|
||||
Add-AssertionOperator -Name ReturnZeroExitCode `
|
||||
-Test $function:ShouldReturnZeroExitCode
|
||||
if (Get-Command -Name Add-AssertionOperator -ErrorAction SilentlyContinue) {
|
||||
Add-AssertionOperator -Name ReturnZeroExitCode -InternalName ShouldReturnZeroExitCode -Test ${function:ShouldReturnZeroExitCode}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user