diff --git a/.github/workflows/auto-update-Dockerfiles.yml b/.github/workflows/auto-update-Dockerfiles.yml new file mode 100644 index 000000000..0e9e8dbc1 --- /dev/null +++ b/.github/workflows/auto-update-Dockerfiles.yml @@ -0,0 +1,193 @@ +name: Auto-Update Lambda Dockerfiles Daily + +permissions: + contents: write + pull-requests: write + +on: + # Run daily at midnight UTC + schedule: + - cron: '0 0 * * *' + # Allows to run this workflow manually from the Actions tab for testing + workflow_dispatch: + + + +jobs: + auto-update: + runs-on: ubuntu-latest + env: + NET_8_AMD64_Dockerfile: "LambdaRuntimeDockerfiles/Images/net8/amd64/Dockerfile" + NET_8_ARM64_Dockerfile: "LambdaRuntimeDockerfiles/Images/net8/arm64/Dockerfile" + NET_9_AMD64_Dockerfile: "LambdaRuntimeDockerfiles/Images/net9/amd64/Dockerfile" + NET_9_ARM64_Dockerfile: "LambdaRuntimeDockerfiles/Images/net9/arm64/Dockerfile" + NET_10_AMD64_Dockerfile: "LambdaRuntimeDockerfiles/Images/net10/amd64/Dockerfile" + NET_10_ARM64_Dockerfile: "LambdaRuntimeDockerfiles/Images/net10/arm64/Dockerfile" + + steps: + # Checks-out the repository under $GITHUB_WORKSPACE + - uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2 #v4.2.2 + with: + ref: 'dev' + + # Update .NET 8 AMD64 Dockerfile + - name: Update .NET 8 AMD64 + id: update-net8-amd64 + shell: pwsh + env: + DOCKERFILE_PATH: ${{ env.NET_8_AMD64_Dockerfile }} + run: | + $version = & "./LambdaRuntimeDockerfiles/get-latest-aspnet-versions.ps1" -MajorVersion "8" + if (-not [string]::IsNullOrEmpty($version)) { + & "./LambdaRuntimeDockerfiles/update-dockerfile.ps1" -DockerfilePath "${{ env.DOCKERFILE_PATH }}" -NextVersion $version + } else { + Write-Host "Skipping .NET 8 AMD64 update - No version detected" + } + + # Update .NET 8 ARM64 Dockerfile + - name: Update .NET 8 ARM64 + id: update-net8-arm64 + shell: pwsh + env: + DOCKERFILE_PATH: ${{ env.NET_8_ARM64_Dockerfile }} + run: | + $version = & "./LambdaRuntimeDockerfiles/get-latest-aspnet-versions.ps1" -MajorVersion "8" + if (-not [string]::IsNullOrEmpty($version)) { + & "./LambdaRuntimeDockerfiles/update-dockerfile.ps1" -DockerfilePath "${{ env.DOCKERFILE_PATH }}" -NextVersion $version + } else { + Write-Host "Skipping .NET 8 ARM64 update - No version detected" + } + + # Update .NET 9 AMD64 Dockerfile + - name: Update .NET 9 AMD64 + id: update-net9-amd64 + shell: pwsh + env: + DOCKERFILE_PATH: ${{ env.NET_9_AMD64_Dockerfile }} + run: | + $version = & "./LambdaRuntimeDockerfiles/get-latest-aspnet-versions.ps1" -MajorVersion "9" + if (-not [string]::IsNullOrEmpty($version)) { + & "./LambdaRuntimeDockerfiles/update-dockerfile.ps1" -DockerfilePath "${{ env.DOCKERFILE_PATH }}" -NextVersion $version + } else { + Write-Host "Skipping .NET 9 AMD64 update - No version detected" + } + + # Update .NET 9 ARM64 Dockerfile + - name: Update .NET 9 ARM64 + id: update-net9-arm64 + shell: pwsh + env: + DOCKERFILE_PATH: ${{ env.NET_9_ARM64_Dockerfile }} + run: | + $version = & "./LambdaRuntimeDockerfiles/get-latest-aspnet-versions.ps1" -MajorVersion "9" + if (-not [string]::IsNullOrEmpty($version)) { + & "./LambdaRuntimeDockerfiles/update-dockerfile.ps1" -DockerfilePath "${{ env.DOCKERFILE_PATH }}" -NextVersion $version + } else { + Write-Host "Skipping .NET 9 ARM64 update - No version detected" + } + + # Update .NET 10 AMD64 Dockerfile + - name: Update .NET 10 AMD64 + id: update-net10-amd64 + shell: pwsh + env: + DOCKERFILE_PATH: ${{ env.NET_10_AMD64_Dockerfile }} + run: | + $version = & "./LambdaRuntimeDockerfiles/get-latest-aspnet-versions.ps1" -MajorVersion "10" + if (-not [string]::IsNullOrEmpty($version)) { + & "./LambdaRuntimeDockerfiles/update-dockerfile.ps1" -DockerfilePath "${{ env.DOCKERFILE_PATH }}" -NextVersion $version + } else { + Write-Host "Skipping .NET 10 AMD64 update - No version detected" + } + + # Update .NET 10 ARM64 Dockerfile + - name: Update .NET 10 ARM64 + id: update-net10-arm64 + shell: pwsh + env: + DOCKERFILE_PATH: ${{ env.NET_10_ARM64_Dockerfile }} + run: | + $version = & "./LambdaRuntimeDockerfiles/get-latest-aspnet-versions.ps1" -MajorVersion "10" + if (-not [string]::IsNullOrEmpty($version)) { + & "./LambdaRuntimeDockerfiles/update-dockerfile.ps1" -DockerfilePath "${{ env.DOCKERFILE_PATH }}" -NextVersion $version + } else { + Write-Host "Skipping .NET 10 ARM64 update - No version detected" + } + + # Commit changes and create a branch + - name: Commit and Push + id: commit-push + shell: pwsh + run: | + # Check if there are any changes to commit + if (git status --porcelain) { + git config --global user.email "github-aws-sdk-dotnet-automation@amazon.com" + git config --global user.name "aws-sdk-dotnet-automation" + + # Generate timestamp for unique local branch name + $timestamp = Get-Date -Format "yyyyMMddHHmmss" + $localBranch = "chore/auto-update-Dockerfiles-daily-$timestamp" + $remoteBranch = "chore/auto-update-Dockerfiles-daily" + + # Always create a new unique local branch + git checkout -b $localBranch + + git add "**/*Dockerfile" + git commit -m "chore: Daily ASP.NET Core version update in Dockerfiles" + + # Always delete the remote branch before pushing to avoid stale branch errors + git push origin --delete $remoteBranch 2>$null + + # Push local branch to remote branch (force push to consistent remote branch name) + git push --force origin "${localBranch}:${remoteBranch}" + + # Write the remote branch name to GITHUB_OUTPUT for use in the PR step + Add-Content -Path $env:GITHUB_OUTPUT -Value "BRANCH=$remoteBranch" + Add-Content -Path $env:GITHUB_OUTPUT -Value "CHANGES_MADE=true" + echo "Changes committed to local branch $localBranch and pushed to remote branch $remoteBranch" + } else { + echo "No changes detected in Dockerfiles, skipping PR creation" + } + + # Create a Pull Request + - name: Create Pull Request + id: pull-request + if: ${{ steps.commit-push.outputs.CHANGES_MADE == 'true' }} + uses: repo-sync/pull-request@v2 + with: + source_branch: ${{ steps.commit-push.outputs.BRANCH }} + destination_branch: "dev" + pr_title: 'chore: Daily ASP.NET Core version update in Dockerfiles' + pr_body: "This PR automatically updates the Dockerfiles to use the latest ASP.NET Core version. + + Verify that the Dockerfiles have correct versions and matching SHA512 checksums for ASP.NET Core runtime. + + All .NET versions: https://dotnet.microsoft.com/en-us/download/dotnet + + *Description of changes:* + \n${{ format + ( + '{0}\n{1}\n{2}\n{3}\n{4}\n{5}', + join(steps.update-net8-amd64.outputs.MESSAGE, '\n'), + join(steps.update-net8-arm64.outputs.MESSAGE, '\n'), + join(steps.update-net9-amd64.outputs.MESSAGE, '\n'), + join(steps.update-net9-arm64.outputs.MESSAGE, '\n'), + join(steps.update-net10-amd64.outputs.MESSAGE, '\n'), + join(steps.update-net10-arm64.outputs.MESSAGE, '\n') + ) + }}" + github_token: ${{ secrets.GITHUB_TOKEN }} + + # Add "Release Not Needed" label to the PR + - name: Add Release Not Needed label + if: ${{ steps.pull-request.outputs.pr_number }} + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ steps.pull-request.outputs.pr_number }}, + labels: ['Release Not Needed'] + }) diff --git a/.github/workflows/update-Dockerfiles.yml b/.github/workflows/update-Dockerfiles.yml index 9fbbd0690..87c1bd83c 100644 --- a/.github/workflows/update-Dockerfiles.yml +++ b/.github/workflows/update-Dockerfiles.yml @@ -1,5 +1,9 @@ name: Update Lambda Dockerfiles +permissions: + contents: write + pull-requests: write + on: # Allows to run this workflow manually from the Actions tab workflow_dispatch: @@ -8,12 +12,12 @@ on: description: ".NET 8 AMD64" type: boolean required: true - default: "true" + default: true NET_8_ARM64: description: ".NET 8 ARM64" type: boolean required: true - default: "true" + default: true NET_8_NEXT_VERSION: description: ".NET 8 Next Version" type: string @@ -22,12 +26,12 @@ on: description: ".NET 9 AMD64" type: boolean required: true - default: "true" + default: true NET_9_ARM64: description: ".NET 9 ARM64" type: boolean required: true - default: "true" + default: true NET_9_NEXT_VERSION: description: ".NET 9 Next Version" type: string @@ -36,12 +40,12 @@ on: description: ".NET 10 AMD64" type: boolean required: true - default: "true" + default: true NET_10_ARM64: description: ".NET 10 ARM64" type: boolean required: true - default: "true" + default: true NET_10_NEXT_VERSION: description: ".NET 10 Next Version" type: string @@ -165,4 +169,17 @@ jobs: ) }}" github_token: ${{ secrets.GITHUB_TOKEN }} - \ No newline at end of file + + # Add "Release Not Needed" label to the PR + - name: Add Release Not Needed label + if: ${{ steps.pull-request.outputs.pr_number }} + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ steps.pull-request.outputs.pr_number }}, + labels: ['Release Not Needed'] + }) diff --git a/LambdaRuntimeDockerfiles/get-latest-aspnet-versions.ps1 b/LambdaRuntimeDockerfiles/get-latest-aspnet-versions.ps1 new file mode 100644 index 000000000..1f28c9c7b --- /dev/null +++ b/LambdaRuntimeDockerfiles/get-latest-aspnet-versions.ps1 @@ -0,0 +1,173 @@ +# This script fetches the latest ASP.NET Core runtime version for a specified .NET major version +# It uses the NuGet API to query for Microsoft.AspNetCore.App.Runtime.linux-x64 package versions + +param( + [Parameter(Mandatory=$true)] + [string]$MajorVersion +) + +function Get-LatestAspNetVersion { + param ( + [string]$majorVersion + ) + + Write-Host "Fetching latest ASP.NET Core runtime version for .NET $majorVersion..." + + try { + # Use NuGet API to find latest version + $response = Invoke-RestMethod -Uri "https://api.nuget.org/v3-flatcontainer/microsoft.aspnetcore.app.runtime.linux-x64/index.json" + + # Filter versions matching the major version + $versions = @() + foreach ($ver in $response.versions) { + if ($ver -like "$majorVersion.*") { + $versions += $ver + } + } + + if ($versions.Count -eq 0) { + Write-Error "No versions found for .NET $majorVersion" + return $null + } + + # Separate release and preview versions + $releaseVersions = @() + $previewVersions = @() + $rcVersions = @() + + foreach ($ver in $versions) { + Write-Host "Processing version: $ver" + if ($ver -match '-preview') { + Write-Host " -> Detected preview version" + $previewVersions += $ver + } elseif ($ver -match '-rc') { + Write-Host " -> Detected rc version" + $rcVersions += $ver + } else { + Write-Host " -> Detected release version" + $releaseVersions += $ver + } + } + + # If we have release versions, get the latest + if ($releaseVersions.Count -gt 0) { + $verObjects = @() + foreach ($ver in $releaseVersions) { + Write-Host "Attempting to parse release version: $ver" + try { + $verObj = New-Object PSObject + Add-Member -InputObject $verObj -MemberType NoteProperty -Name "OriginalVersion" -Value $ver + $versionObj = [Version]$ver + Add-Member -InputObject $verObj -MemberType NoteProperty -Name "Version" -Value $versionObj + $verObjects += $verObj + } catch { + Write-Host "Warning: Could not parse release version $ver, skipping." + } + } + $sortedVersions = $verObjects | Sort-Object -Property Version -Descending + if ($sortedVersions.Count -gt 0) { + $latestVersion = $sortedVersions[0].OriginalVersion + } else { + $latestVersion = $null + } + } + # Otherwise get the latest rc version + elseif ($rcVersions.Count -gt 0) { + $rcObjs = @() + $maxRcParts = 0 + foreach ($ver in $rcVersions) { + Write-Host "Attempting to parse rc version: $ver" + if ($ver -match '^(\d+)\.(\d+)\.(\d+)-rc\.(.+)$') { + Write-Host " -> RC regex matched" + $major = [int]$matches[1] + $minor = [int]$matches[2] + $patch = [int]$matches[3] + $rcParts = $matches[4] -split '\.' + $rcNumbers = $rcParts | ForEach-Object { [int]$_ } + Write-Host " -> RC numeric parts: $($rcNumbers -join ',')" + if ($rcNumbers.Count -gt $maxRcParts) { $maxRcParts = $rcNumbers.Count } + $rcObj = New-Object PSObject + Add-Member -InputObject $rcObj -MemberType NoteProperty -Name "OriginalVersion" -Value $ver + Add-Member -InputObject $rcObj -MemberType NoteProperty -Name "Major" -Value $major + Add-Member -InputObject $rcObj -MemberType NoteProperty -Name "Minor" -Value $minor + Add-Member -InputObject $rcObj -MemberType NoteProperty -Name "Patch" -Value $patch + for ($i = 0; $i -lt $rcNumbers.Count; $i++) { + Add-Member -InputObject $rcObj -MemberType NoteProperty -Name ("RC$i") -Value $rcNumbers[$i] + } + $rcObjs += $rcObj + } else { + Write-Host " -> RC regex did NOT match" + } + } + # Pad missing RC fields with 0 for sorting + foreach ($obj in $rcObjs) { + for ($i = 0; $i -lt $maxRcParts; $i++) { + if (-not ($obj.PSObject.Properties.Name -contains ("RC$i"))) { + Add-Member -InputObject $obj -MemberType NoteProperty -Name ("RC$i") -Value 0 + } + } + } + $sortProps = @("Major", "Minor", "Patch") + @(for ($i = 0; $i -lt $maxRcParts; $i++) { "RC$i" }) + Write-Host "Sorting RCs by: $($sortProps -join ', ')" + $sortedRCs = $rcObjs | Sort-Object -Property $sortProps -Descending + if ($sortedRCs.Count -gt 0) { + $latestVersion = $sortedRCs[0].OriginalVersion + } else { + $latestVersion = ($rcVersions | Sort-Object)[-1] + } + } + # Otherwise get the latest preview version + elseif ($previewVersions.Count -gt 0) { + $previewObjs = @() + foreach ($ver in $previewVersions) { + if ($ver -match '(\d+)\.(\d+)\.(\d+)-preview\.(\d+)') { + $major = [int]$matches[1] + $minor = [int]$matches[2] + $patch = [int]$matches[3] + $preview = [int]$matches[4] + $previewObj = New-Object PSObject + Add-Member -InputObject $previewObj -MemberType NoteProperty -Name "OriginalVersion" -Value $ver + Add-Member -InputObject $previewObj -MemberType NoteProperty -Name "Major" -Value $major + Add-Member -InputObject $previewObj -MemberType NoteProperty -Name "Minor" -Value $minor + Add-Member -InputObject $previewObj -MemberType NoteProperty -Name "Patch" -Value $patch + Add-Member -InputObject $previewObj -MemberType NoteProperty -Name "Preview" -Value $preview + $previewObjs += $previewObj + } + } + $sortedPreviews = $previewObjs | Sort-Object -Property Major, Minor, Patch, Preview -Descending + if ($sortedPreviews.Count -gt 0) { + $latestVersion = $sortedPreviews[0].OriginalVersion + } else { + $latestVersion = ($previewVersions | Sort-Object)[-1] + } + } + else { + $latestVersion = $null + } + + if ($latestVersion) { + Write-Host "Latest ASP.NET Core runtime version for .NET $majorVersion is $latestVersion" + return $latestVersion + } else { + Write-Error "Could not determine latest version for .NET $majorVersion" + return $null + } + } + catch { + $errorMessage = "Error fetching versions for .NET $majorVersion " + $_ + Write-Error $errorMessage + return $null + } +} + +# Get latest version for the specified .NET major version +$version = Get-LatestAspNetVersion -majorVersion $MajorVersion + +# Verify we got a valid version +if (-not $version) { + Write-Error "Failed to determine .NET $MajorVersion version" + exit 1 +} + +# Output the version directly +Write-Output $version