From 3eb30b60133d7172de4a1bda753b3b5f4a90fb38 Mon Sep 17 00:00:00 2001 From: Tine Staric Date: Fri, 13 Sep 2024 13:26:54 +0300 Subject: [PATCH] Clone Github Actions from LinterCop --- .github/actions/build-matrix/action.yml | 47 +++++ .github/actions/dotnet-build/action.yml | 122 +++++++++++++ .github/actions/dotnet-sign/action.yml | 41 +++++ .github/actions/download-artifacts/action.yml | 33 ++++ .../feature-flags/Get-FeatureFlags.ps1 | 74 ++++++++ .github/actions/feature-flags/action.yml | 24 +++ .../actions/marketplace/Get-Marketplace.ps1 | 26 +++ .github/actions/marketplace/action.yml | 44 +++++ .github/actions/release-assets/action.yml | 68 +++++++ .github/workflows/CICD.yml | 128 +++++++++++++ .github/workflows/DailyCheckForUpdates.yml | 72 ++++++++ .github/workflows/NewReleaseAL.yml | 170 ++++++++++++++++++ 12 files changed, 849 insertions(+) create mode 100644 .github/actions/build-matrix/action.yml create mode 100644 .github/actions/dotnet-build/action.yml create mode 100644 .github/actions/dotnet-sign/action.yml create mode 100644 .github/actions/download-artifacts/action.yml create mode 100644 .github/actions/feature-flags/Get-FeatureFlags.ps1 create mode 100644 .github/actions/feature-flags/action.yml create mode 100644 .github/actions/marketplace/Get-Marketplace.ps1 create mode 100644 .github/actions/marketplace/action.yml create mode 100644 .github/actions/release-assets/action.yml create mode 100644 .github/workflows/CICD.yml create mode 100644 .github/workflows/DailyCheckForUpdates.yml create mode 100644 .github/workflows/NewReleaseAL.yml diff --git a/.github/actions/build-matrix/action.yml b/.github/actions/build-matrix/action.yml new file mode 100644 index 0000000..3e5bb94 --- /dev/null +++ b/.github/actions/build-matrix/action.yml @@ -0,0 +1,47 @@ +name: Setup Build Matrix +description: Populate matrix for build job strategy + +inputs: + sources: + required: true + description: Stringified JSON object with VSIXPackages of the AL Language versions + al-version-latest: + required: true + description: Version number of the Latest version of the AL Language + al-version-prerelease: + required: true + description: Version number of the Pre-Release version of the AL Language + +outputs: + matrix: + description: Stringified JSON object with matrix for build job strategy + value: ${{ steps.get-matrix.outputs.matrix }} + isempty: + description: Boolean 'true'/'false' if the matrix is an empty result + value: ${{ steps.get-matrix.outputs.isempty }} + +runs: + using: composite + steps: + - name: Get Matrix + id: get-matrix + shell: pwsh + env: + AL_VERSION_LATEST: ${{ inputs.al-version-latest }} + AL_VERSION_PRERELEASE: ${{ inputs.al-version-prerelease }} + run: | + $matrix = @() + $sources = '${{ inputs.sources }}' | ConvertFrom-Json + foreach ($item in $sources) { + $matrix += [Ordered]@{ + version = $item.version; + assetname = "CopmanialCop.AL-$($item.version).dll"; + assetUri = $item.source; + latest = $($item.version -eq $env:AL_VERSION_LATEST); + prerelease = $($item.version -eq $env:AL_VERSION_PRERELEASE); + } + } + + echo "isempty=$([string]($matrix.Count -eq 0).ToString().ToLower())" >> $env:GITHUB_OUTPUT + $matrix = @{'include' = $matrix } | ConvertTo-Json -Compress + echo "matrix=$($matrix)" >> $env:GITHUB_OUTPUT diff --git a/.github/actions/dotnet-build/action.yml b/.github/actions/dotnet-build/action.yml new file mode 100644 index 0000000..f678f8f --- /dev/null +++ b/.github/actions/dotnet-build/action.yml @@ -0,0 +1,122 @@ +name: dotnet build +description: Builds a .NET project and all of its dependencies. + +inputs: + asset-version-number: + required: false + description: The version number for the artifact + asset-name: + required: false + description: The name for the artifact + asset-publish: + required: false + default: "false" + description: Publish artifact as asset on workflow + al-version-number: + required: true + description: The version number of the corresponding AL Language + al-asset-uri: + required: true + description: The asset uri for retrieving the VSIXPackages of the AL Language + al-latest: + default: "false" + required: false + description: Set build as Latest version of the AL Language + al-prerelease: + default: "false" + required: false + description: Set build as Pre-Release version of the AL Language + +runs: + using: composite + steps: + - name: Restore dependencies + shell: pwsh + run: dotnet restore CompanialCopAnalyzer + + - name: Download platform artifact + shell: pwsh + env: + ASSET_URI: ${{ inputs.al-asset-uri }} + run: Invoke-WebRequest $env:ASSET_URI -OutFile ALLanguage.vsix + + - name: Unzip vsix + shell: pwsh + run: 7z x ALLanguage.vsix "-oALLanguage" "extension/bin/Analyzers" -r + + - name: Set AssemblyInfo + shell: pwsh + if: ${{ inputs.asset-publish == 'true' }} + env: + AL_VERSION: ${{ inputs.al-version-number }} + LC_VERSION: ${{ inputs.asset-version-number }} + run: + (Get-Content CompanialCopAnalyzer/AssemblyInfo.cs) -replace 'Version\("([\d\.]+)"\)]', + ("Version(""" + ($env:LC_VERSION + -replace "v","") + """)]") | Out-File CompanialCopAnalyzer/AssemblyInfo.cs + + (Get-Content CompanialCopAnalyzer/AssemblyInfo.cs) -replace 'AssemblyTitle\("([^"]*)"\)', "AssemblyTitle(`"AL-$env:AL_VERSION`")" | Out-File CompanialCopAnalyzer/AssemblyInfo.cs + + - name: Populate Feature Flags + id: get-feature-flags + uses: ./.github/actions/feature-flags + with: + version-number: ${{ inputs.al-version-number }} + + - name: Build + shell: pwsh + run: dotnet build CompanialCopAnalyzer /p:FeatureFlags=${{ steps.get-feature-flags.outputs.feature-flags }} --no-restore --configuration Release + + - name: Test + shell: pwsh + run: dotnet test CompanialCopAnalyzer.Test /p:FeatureFlags=${{ steps.get-feature-flags.outputs.feature-flags }} + + - name: Upload build artifact + id: upload-build-asset + uses: actions/upload-artifact@v4 + if: ${{ inputs.asset-publish == 'true' }} + with: + name: ${{ inputs.asset-name }} + path: CompanialCopAnalyzer/bin/Release/netstandard2.1/CompanialCop.dll + compression-level: 0 # no compression + + ### Upload Asset as Latest + - name: Upload build artifact (Latest) + id: upload-build-asset-latest + uses: actions/upload-artifact@v4 + if: ${{ inputs.asset-publish == 'true' && inputs.al-latest == 'true' }} + with: + name: CompanialCop.dll + path: CompanialCopAnalyzer/bin/Release/netstandard2.1/CompanialCop.dll + compression-level: 0 # no compression + + ### Upload Asset as Pre-Release + - name: Upload build artifact (Pre-Release) + id: upload-build-asset-prerelease + uses: actions/upload-artifact@v4 + if: ${{ inputs.asset-publish == 'true' && inputs.al-prerelease == 'true' }} + with: + name: CompanialCop.AL-PreRelease.dll + path: CompanialCopAnalyzer/bin/Release/netstandard2.1/CompanialCop.dll + compression-level: 0 # no compression + + ### Compatibility with previous naming of files + ### Release Asset as Current + - name: Upload build artifact (Current) + id: upload-build-asset-current + uses: actions/upload-artifact@v4 + if: ${{ inputs.asset-publish == 'true' && inputs.al-latest == 'true' }} + with: + name: CompanialCop.current.dll + path: CompanialCopAnalyzer/bin/Release/netstandard2.1/CompanialCop.dll + compression-level: 0 # no compression + + ### Release Asset as Next + - name: Upload build artifact (Next) + id: upload-build-asset-next + uses: actions/upload-artifact@v4 + if: ${{ inputs.asset-publish == 'true' && inputs.al-prerelease == 'true' }} + with: + name: CompanialCop.next.dll + path: CompanialCopAnalyzer/bin/Release/netstandard2.1/CompanialCop.dll + compression-level: 0 # no compression diff --git a/.github/actions/dotnet-sign/action.yml b/.github/actions/dotnet-sign/action.yml new file mode 100644 index 0000000..d912100 --- /dev/null +++ b/.github/actions/dotnet-sign/action.yml @@ -0,0 +1,41 @@ +name: Code Sign Assets +description: "" + +inputs: + base-directory: + required: true + description: Path with the artifacts that need to be code signed + description: + required: true + description: "" + description-url: + required: true + description: "" + azure-key-vault-url: + required: true + description: "" + azure-key-vault-certificate: + required: true + description: "" + +runs: + using: composite + steps: + - name: Install Sign CLI tool + shell: pwsh + run: dotnet tool install --tool-path . sign --version 0.9.0-beta.23127.3 + + - name: Sign artifacts + shell: pwsh + env: + KEY_VAULT_URL: ${{ inputs.azure-key-vault-url }} + KEY_VAULT_CERTIFICATE: ${{ inputs.azure-key-vault-certificate }} + run: > + ./sign code azure-key-vault + **/*.dll + --base-directory ${{ inputs.base-directory }} + --description ${{ inputs.description }} + --description-url ${{ inputs.description-url }} + --azure-key-vault-managed-identity true + --azure-key-vault-url $env:KEY_VAULT_URL + --azure-key-vault-certificate $env:KEY_VAULT_CERTIFICATE diff --git a/.github/actions/download-artifacts/action.yml b/.github/actions/download-artifacts/action.yml new file mode 100644 index 0000000..437b9bc --- /dev/null +++ b/.github/actions/download-artifacts/action.yml @@ -0,0 +1,33 @@ +name: Download artifacts +description: Download artifacts + +inputs: + path: + required: true + description: "" + +runs: + using: composite + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: ${{ github.workspace }}\DownloadBuildArtifacts + + - name: Rename artifacts + shell: pwsh + env: + SOURCE_PATH: ${{ github.workspace }}\DownloadBuildArtifacts + TARGET_PATH: ${{ inputs.path }} + run: | + # Get list of artifacts files + $artifacts = Get-ChildItem -Path $env:SOURCE_PATH -Recurse -File + | Where-Object { $_.Name -like 'CompanialCop*.dll' -and $_.Directory.Name -like 'CompanialCop*.dll' } + + # Create folder if not exits + if (!(Test-Path $env:TARGET_PATH)) { + New-Item -Path $env:TARGET_PATH -ItemType Directory -Force | Out-Null + } + + # Move the artifacts (CompanialCop.dll) in every directory to a combined folder and rename the file to the name of it's parent directory + $artifacts | ForEach-Object { Move-Item -Path $_ -Destination (Join-Path $env:TARGET_PATH $_.Directory.Name) } diff --git a/.github/actions/feature-flags/Get-FeatureFlags.ps1 b/.github/actions/feature-flags/Get-FeatureFlags.ps1 new file mode 100644 index 0000000..97158d0 --- /dev/null +++ b/.github/actions/feature-flags/Get-FeatureFlags.ps1 @@ -0,0 +1,74 @@ +# Get-FeatureFlags.ps1 +Param ( + [Parameter(Mandatory = $true)] + [string] $version +) + +function ConvertTo-Version { + [OutputType([System.Version])] + Param ( + [Parameter(Mandatory = $true)] + [string] $version + ) + + $result = $null + if ([System.Version]::TryParse($version, [ref]$result)) { + return $result + } + else { + Write-Error "The value '$($version)' is not a valid input." + } +} + +function Get-FeatureFlags { + [OutputType([System.String])] + Param ( + [Parameter(Mandatory = $true)] + [System.Version] $version + ) + $featureFlags = "" + + $RuntimeVersion = [Ordered]@{ + 'Spring2018' = '1.0' + 'Fall2018' = '2.0' + 'Spring2019' = '3.0' + 'Fall2019' = '4.0' + 'Spring2020' = '5.0' + 'Fall2020' = '6.0' + 'Spring2021' = '7.0' + 'Fall2021' = '8.0' + 'Spring2022' = '9.0' + 'Spring2022RV1' = '9.1' + 'Spring2022RV2' = '9.2' + 'Fall2022' = '10.0' + 'Spring2023' = '11.0' + 'Fall2023' = '12.0' + 'Fall2023RV1' = '12.1' + 'Fall2023RV2' = '12.2' + 'Fall2023RV3' = '12.3' + 'Spring2024' = '13.0' + 'Fall2024' = '14.0' + 'Spring2025' = '15.0' + 'Fall2025' = '16.0' + + 'Spring2024OrGreater' = '13.0.964488' + 'ManifestHelper' = '13.0.937154' + 'PageSystemAction' = '13.0.878831' + } + + $supportedRuntimeVersions = $RuntimeVersion.GetEnumerator() | Where-Object { $(ConvertTo-Version($_.Value)) -le $(ConvertTo-Version($version)) } | Foreach-Object { $_.Key } | ForEach-Object { "#$_" } + if (![string]::IsNullOrEmpty($supportedRuntimeVersions)) { + $featureFlags = [System.String]::Join("", $supportedRuntimeVersions) + } + + return $featureFlags +} + +# Convert input version string to System.Version +$version = ConvertTo-Version -version $version + +# Call Get-FeatureFlags with the converted version +$result = Get-FeatureFlags -version $version + +# Output the result +Write-Output $result diff --git a/.github/actions/feature-flags/action.yml b/.github/actions/feature-flags/action.yml new file mode 100644 index 0000000..134f716 --- /dev/null +++ b/.github/actions/feature-flags/action.yml @@ -0,0 +1,24 @@ +name: Feature Flags +description: Composite Action to populate the Feature Flags based on the AL Language version + +inputs: + version-number: + required: true + description: version number of the AL Language + +outputs: + feature-flags: + description: string of feature flags + value: ${{ steps.populate-feature-flags.outputs.feature-flags }} + +runs: + using: composite + steps: + - name: Populate Feature Flags + id: populate-feature-flags + shell: pwsh + env: + VERSION_NUMBER: ${{ inputs.version-number}} + run: | + $result = ${{github.action_path}}/Get-FeatureFlags.ps1 $env:VERSION_NUMBER + echo "feature-flags=$($result)" >> $env:GITHUB_OUTPUT diff --git a/.github/actions/marketplace/Get-Marketplace.ps1 b/.github/actions/marketplace/Get-Marketplace.ps1 new file mode 100644 index 0000000..5b38fda --- /dev/null +++ b/.github/actions/marketplace/Get-Marketplace.ps1 @@ -0,0 +1,26 @@ +function ConvertTo-Version { + [OutputType([System.Version])] + Param ( + [Parameter(Mandatory = $true)] + [string] $version + ) + + $result = $null + if ([System.Version]::TryParse($version, [ref]$result)) { + return $result + } + else { + Write-Error "The value '$($version)' is not a valid input." + } +} + +$listing = Invoke-WebRequest -Method POST -UseBasicParsing ` + -Uri https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery?api-version=3.0-preview.1 ` + -Body '{"filters":[{"criteria":[{"filterType":8,"value":"Microsoft.VisualStudio.Code"},{"filterType":12,"value":"4096"},{"filterType":7,"value":"ms-dynamics-smb.al"}],"pageNumber":1,"pageSize":50,"sortBy":0,"sortOrder":0}],"assetTypes":[],"flags":0x192}' ` + -ContentType application/json | ConvertFrom-Json + +$listingFiltered = $listing.results | Select-Object -First 1 -ExpandProperty extensions ` +| Select-Object -ExpandProperty versions ` +| Where-Object { $(ConvertTo-Version($_.version)) -gt [System.Version]::Parse("12.0.0") } + +Write-Output $listingFiltered | ConvertTo-Json -Compress -Depth 3 \ No newline at end of file diff --git a/.github/actions/marketplace/action.yml b/.github/actions/marketplace/action.yml new file mode 100644 index 0000000..d7f8948 --- /dev/null +++ b/.github/actions/marketplace/action.yml @@ -0,0 +1,44 @@ +name: Marketplace +description: Get AL Language versions from the Visual Studio Marketplace + +outputs: + sources: + description: Stringified JSON object with VSIXPackages of the AL Language versions + value: ${{ steps.get-marketplace.outputs.sources }} + al-version-latest: + description: Version number of the Latest version + value: ${{ steps.get-marketplace.outputs.latest }} + al-version-prerelease: + description: Version number of the Pre-Release version + value: ${{ steps.get-marketplace.outputs.prerelease }} + +runs: + using: composite + steps: + - name: Get-Marketplace + id: get-marketplace + shell: pwsh + run: | + $listing = ${{github.action_path}}/Get-Marketplace.ps1 | ConvertFrom-Json + + $versionLatest = $listing ` + | Where-Object properties -ne $null ` + | Where-Object { $_.properties.key -notcontains 'Microsoft.VisualStudio.Code.PreRelease' } | Select-Object -First 1 -ExpandProperty version + echo "latest=$($versionLatest)" >> $env:GITHUB_OUTPUT + + $versionPreRelease = $listing ` + | Where-Object properties -ne $null ` + | Where-Object { $_.properties.key -contains 'Microsoft.VisualStudio.Code.PreRelease' } | Select-Object -First 1 -ExpandProperty version + echo "prerelease=$($versionPreRelease)" >> $env:GITHUB_OUTPUT + + # Populate list of sources + $sources = foreach ($item in $listing) { + $VSIXPackage = $item.files | Where-Object { $_.assetType -eq "Microsoft.VisualStudio.Services.VSIXPackage" } + if ($VSIXPackage) { + [PSCustomObject]@{ + version = $item.version + source = $VSIXPackage.source + } + } + } + echo "sources=$($sources | ConvertTo-Json -Compress)" >> $env:GITHUB_OUTPUT diff --git a/.github/actions/release-assets/action.yml b/.github/actions/release-assets/action.yml new file mode 100644 index 0000000..8ae9f96 --- /dev/null +++ b/.github/actions/release-assets/action.yml @@ -0,0 +1,68 @@ +name: LinterCop (Pre)Release Assets +description: Get LinterCop (Pre)Release Assets + +inputs: + release: + required: false + default: "false" + description: Retrieve Release assets + pre-release: + required: false + default: "false" + description: Retrieve Pre-Release assets + +outputs: + tag-name: + description: Tag name of release + value: ${{ steps.get-release-assets.outputs.tag-name }} + upload-url: + description: Target url for uploading assets + value: ${{ steps.get-release-assets.outputs.upload-url }} + al-versions: + description: Stringified JSON object with version numbers of the AL Language + value: ${{ steps.get-release-assets.outputs.versions }} + +runs: + using: composite + steps: + - name: Get (Pre)Release Assets + id: get-release-assets + shell: pwsh + env: + RELEASE: ${{ inputs.release }} + PRERELEASE: ${{ inputs.pre-release }} + run: | + $releaseFlag = [System.Convert]::ToBoolean($env:RELEASE) + $prereleaseFlag = [System.Convert]::ToBoolean($env:PRERELEASE) + + if ($releaseFlag -and $prereleaseFlag) { + throw "Both release and pre-release cannot be true at the same time." + } + if (-not $releaseFlag -and -not $prereleaseFlag) { + throw "Both release and pre-release cannot be false. One must be true." + } + + $prerelease = $prereleaseFlag -and -not $releaseFlag + + $releases = Invoke-RestMethod ` + -Uri https://api.github.com/repos/${{ github.repository }}/releases?per_page=10 ` + -UseBasicParsing + + $releaseLatest = $releases + | Where-Object draft -eq $False + | Where-Object prerelease -eq $prerelease + | Select-Object -First 1 + + # Regular expression pattern to match version numbers + $pattern = 'AL-(\d+\.\d+\.\d+)' + + # Extract version numbers + $versions = $releaseLatest.assets | ForEach-Object { + if ($_ -match $pattern) { + $matches[1] + } + } + + echo "tag-name=$($releaseLatest.tag_name)" >> $env:GITHUB_OUTPUT + echo "upload-url=$($releaseLatest.upload_url -replace '{\?name,label}', '')" >> $env:GITHUB_OUTPUT + echo "versions=$($versions | ConvertTo-Json -Compress)" >> $env:GITHUB_OUTPUT diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml new file mode 100644 index 0000000..bf7a180 --- /dev/null +++ b/.github/workflows/CICD.yml @@ -0,0 +1,128 @@ +name: CICD +on: + push: + branches: + - master + - prerelease + pull_request: + workflow_dispatch: null + +jobs: + setup: + name: Setup + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Create Release + id: create-release + uses: release-drafter/release-drafter@v6 + if: github.event_name != 'pull_request' + with: + prerelease: ${{ github.ref != 'refs/heads/master' }} + commitish: ${{ github.ref }} + disable-autolabeler: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get AL Language versions from Marketplace + id: marketplace + uses: ./.github/actions/marketplace + + - name: Populate matrix for build job strategy + id: setup-build-matrix + uses: ./.github/actions/build-matrix + with: + sources: ${{ steps.marketplace.outputs.sources }} + al-version-latest: ${{ steps.marketplace.outputs.al-version-latest }} + al-version-prerelease: ${{ steps.marketplace.outputs.al-version-prerelease }} + + outputs: + github-event-name: ${{ github.event_name }} + release-tag-name: ${{ steps.create-release.outputs.tag_name }} + release-upload-url: ${{ steps.create-release.outputs.upload_url }} + matrix: ${{ steps.setup-build-matrix.outputs.matrix }} + + build: + name: Build + runs-on: ubuntu-latest + needs: setup + strategy: + matrix: ${{ fromJson(needs.setup.outputs.matrix) }} + fail-fast: false + steps: + - uses: actions/checkout@v4 + + - name: Build artifact + id: dotnet-build + uses: ./.github/actions/dotnet-build + with: + asset-version-number: ${{ needs.setup.outputs.release-tag-name }} + asset-name: ${{ matrix.assetname }} + asset-publish: ${{ needs.setup.outputs.github-event-name != 'pull_request' }} + al-version-number: ${{ matrix.version }} + al-asset-uri: ${{ matrix.assetUri }} + al-latest: ${{ matrix.latest }} + al-prerelease: ${{ matrix.prerelease }} + + publish: + name: Publish + runs-on: windows-latest # Code signing must run on a Windows agent for Authenticode signing (dll/exe) + needs: + - setup + - build + if: github.event_name != 'pull_request' # Exclude this job for validation on the pull-request + steps: + - uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Download artifacts + id: download-artifacts + uses: ./.github/actions/download-artifacts + with: + path: ${{ github.workspace }}\BuildArtifacts + + - name: Code Sign artifacts + id: code-sign + uses: ./.github/actions/dotnet-sign + with: + base-directory: ${{ github.workspace }}\BuildArtifacts + description: "CompanialCop" + description-url: https://github.com/${{ github.repository }} + azure-key-vault-url: ${{ secrets.KEY_VAULT_URL }} + azure-key-vault-certificate: ${{ secrets.KEY_VAULT_CERTIFICATE }} + + - name: Publish Assets + id: upload-release-assets + shell: pwsh + env: + ARTIFACTS_PATH: ${{ github.workspace }}\BuildArtifacts + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_UPLOAD_URL: ${{ needs.setup.outputs.release-upload-url }} + run: | + # The upload-url contains "{?name,label}" at the end, which needs to be removed + $upload_url = $env:RELEASE_UPLOAD_URL -replace '{\?name,label}', '' + Write-Host "upload-url: $($upload_url)" + + # Find all the .dll files in the directory + $artifacts = Get-ChildItem -Path $env:ARTIFACTS_PATH -Depth 0 -Filter *.dll + + # Loop through each artifact and upload it using curl (which handles multipart form data) + $artifacts | ForEach-Object { + $asset_name = $_.Name + $asset_path = $_.FullName + + Write-Host "Uploading $asset_name..." + curl -L ` + -X POST ` + -H "Accept: application/vnd.github+json" ` + -H "Authorization: token $env:GITHUB_TOKEN" ` + -H "X-GitHub-Api-Version: 2022-11-28" ` + -H "Content-Type: application/octet-stream" ` + "$($upload_url)?name=$($asset_name)" ` + --data-binary `@"$asset_path" + } diff --git a/.github/workflows/DailyCheckForUpdates.yml b/.github/workflows/DailyCheckForUpdates.yml new file mode 100644 index 0000000..c5a2236 --- /dev/null +++ b/.github/workflows/DailyCheckForUpdates.yml @@ -0,0 +1,72 @@ +name: Daily check for updates +on: + schedule: + - cron: "0 0,6,9,12,15,18 * * *" + workflow_dispatch: null + +jobs: + job: + name: Run + runs-on: ubuntu-latest + steps: + - name: Generate hash from Marketplace items + id: generate_hash + shell: pwsh + run: | + $results = Invoke-WebRequest -Method POST -UseBasicParsing ` + -Uri https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery?api-version=3.0-preview.1 ` + -Body '{"filters":[{"criteria":[{"filterType":8,"value":"Microsoft.VisualStudio.Code"},{"filterType":12,"value":"4096"},{"filterType":7,"value":"ms-dynamics-smb.al"}],"pageNumber":1,"pageSize":50,"sortBy":0,"sortOrder":0}],"assetTypes":[],"flags":0x192}' ` + -ContentType application/json | ConvertFrom-Json + + # Loop through results and remove the statistics property + foreach ($result in $results.results) { + foreach ($extension in $result.extensions) { + $extension.PSObject.Properties.Remove("statistics") + } + } + $results = $($results | ConvertTo-Json -Compress -Depth 8) + + $md5 = [System.Security.Cryptography.MD5]::Create() + $bytes = [System.Text.Encoding]::UTF8.GetBytes($results) + $hashBytes = $md5.ComputeHash($bytes) + $hashString = [BitConverter]::ToString($hashBytes).ToLower() -replace '-', '' + Write-Host "Calculated hash is: $($hashString)" + echo "hash=$($hashString)" >> $env:GITHUB_OUTPUT + + - uses: actions/cache/restore@v4 + id: get_hash + with: + path: ${{ runner.temp }}/hash.txt + key: al-marketplace-hash-${{ steps.generate_hash.outputs.hash }} + fail-on-cache-miss: false + + - name: Create file with hash value + id: create_file_with_hash + if: steps.get_hash.outputs.cache-hit != 'true' + shell: pwsh + env: + HASH: ${{ steps.generate_hash.outputs.hash }} + RUNNER_TEMP: ${{ runner.temp }} + run: | + $hashFilePath = Join-Path $env:RUNNER_TEMP 'hash.txt' + $env:HASH | Set-Content -Path $hashFilePath + + - name: Add new hash value to cache + id: add_hash + if: steps.get_hash.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: ${{ runner.temp }}/hash.txt + key: al-marketplace-hash-${{ steps.generate_hash.outputs.hash }} + + - uses: actions/checkout@v4 + if: steps.get_hash.outputs.cache-hit != 'true' + + - name: Trigger for executing check of new release of the AL Language + if: steps.get_hash.outputs.cache-hit != 'true' + shell: pwsh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh workflow run NewReleaseAL.yml --ref master -f update-release=true + gh workflow run NewReleaseAL.yml --ref prerelease -f update-pre-release=true diff --git a/.github/workflows/NewReleaseAL.yml b/.github/workflows/NewReleaseAL.yml new file mode 100644 index 0000000..021ced8 --- /dev/null +++ b/.github/workflows/NewReleaseAL.yml @@ -0,0 +1,170 @@ +name: Check for new AL Language release +on: + workflow_dispatch: + inputs: + update-pre-release: + description: "Update Pre-Release assets" + required: true + default: "false" + update-release: + description: "Update Release assets" + required: true + default: "false" +jobs: + setup: + name: Setup + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Get AL Languages from Marketplace + id: marketplace + uses: ./.github/actions/marketplace + + - name: Get LinterCop Release Assets + id: release-assets + uses: ./.github/actions/release-assets + with: + release: "${{ github.event.inputs.update-release }}" + pre-release: "${{ github.event.inputs.update-pre-release }}" + + - name: Populate new sources + id: new-sources + shell: pwsh + run: | + Write-Host "Compare differences between the AL versions of the Marketplace and the current latest Release of the LinterCop" + $marketplace = '${{ steps.marketplace.outputs.sources }}' | ConvertFrom-Json + $assets = '${{ steps.release-assets.outputs.al-versions }}' | ConvertFrom-Json + + $differences = Compare-Object -ReferenceObject $marketplace.version -DifferenceObject $assets -PassThru + $newVersions = $differences | Where-Object { $_.SideIndicator -eq '<=' } + + $sources = $marketplace | Where-Object { $newVersions -contains $_.version } + echo "sources=$($sources | ConvertTo-Json -Compress)" >> $env:GITHUB_OUTPUT + + - name: Populate matrix for build job strategy + id: setup-build-matrix + uses: ./.github/actions/build-matrix + with: + sources: ${{ steps.new-sources.outputs.sources }} + al-version-latest: ${{ steps.marketplace.outputs.al-version-latest }} + al-version-prerelease: ${{ steps.marketplace.outputs.al-version-prerelease }} + + outputs: + release-tag-name: ${{ steps.release-assets.outputs.tag-name }} + release-upload-url: ${{ steps.release-assets.outputs.upload-url }} + matrix: ${{ steps.setup-build-matrix.outputs.matrix }} + matrix-isempty: ${{ steps.setup-build-matrix.outputs.isempty }} + + build: + name: Build + runs-on: ubuntu-latest + needs: setup + if: ${{ needs.setup.outputs.matrix-isempty == 'false' }} + strategy: + matrix: ${{ fromJson(needs.setup.outputs.matrix) }} + fail-fast: false + steps: + - uses: actions/checkout@v4 + + - name: Build artifact + id: dotnet-build + uses: ./.github/actions/dotnet-build + with: + asset-version-number: ${{ needs.setup.outputs.release-tag-name }} + asset-name: ${{ matrix.assetname }} + asset-publish: "true" + al-version-number: ${{ matrix.version }} + al-asset-uri: ${{ matrix.assetUri }} + al-latest: ${{ matrix.latest }} + al-prerelease: ${{ matrix.prerelease }} + + publish: + name: Publish + runs-on: windows-latest # Code signing must run on a Windows agent for Authenticode signing (dll/exe) + needs: + - setup + - build + if: github.event_name != 'pull_request' # Exclude this job for validation on the pull-request + steps: + - uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Download artifacts + id: download-artifacts + uses: ./.github/actions/download-artifacts + with: + path: ${{ github.workspace }}\BuildArtifacts + + - name: Code Sign artifacts + id: code-sign + uses: ./.github/actions/dotnet-sign + with: + base-directory: ${{ github.workspace }}\BuildArtifacts + description: "CompanialCop" + description-url: https://github.com/${{ github.repository }} + azure-key-vault-url: ${{ secrets.KEY_VAULT_URL }} + azure-key-vault-certificate: ${{ secrets.KEY_VAULT_CERTIFICATE }} + + - name: Publish Assets + id: upload-release-assets + shell: pwsh + env: + ARTIFACTS_PATH: ${{ github.workspace }}\BuildArtifacts + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG_NAME: ${{ needs.setup.outputs.release-tag-name }} + RELEASE_UPLOAD_URL: ${{ needs.setup.outputs.release-upload-url }} + run: | + # The upload-url contains "{?name,label}" at the end, which needs to be removed + $upload_url = $env:RELEASE_UPLOAD_URL -replace '{\?name,label}', '' + Write-Host "upload-url: $($upload_url)" + + # Find all the .dll files in the directory + $artifacts = Get-ChildItem -Path $env:ARTIFACTS_PATH -Depth 0 -Filter *.dll + + # Define headers for GitHub API requests + $headers = @{ + 'Accept' = 'application/vnd.github+json' + 'Authorization' = "token $env:GITHUB_TOKEN" + 'X-GitHub-Api-Version' = '2022-11-28' + } + + $release = Invoke-RestMethod ` + -Method Get ` + -Headers $headers ` + -Uri "https://api.github.com/repos/${{ github.repository }}/releases/tags/$env:RELEASE_TAG_NAME" ` + -UseBasicParsing + + # Find existing release assets that match build artifacts name + $existingAssets = $release.assets | Where-Object { $($artifacts | ForEach-Object { $_.Name }) -contains $_.name } + + # Delete the matching assets + $existingAssets | ForEach-Object { + Write-Host "Deleting existing asset $($_.name) (ID: $($_.id))..." + + Invoke-RestMethod ` + -Method Delete ` + -Headers $headers ` + -Uri "https://api.github.com/repos/${{ github.repository }}/releases/assets/$($_.id)" ` + -UseBasicParsing + } + + # Loop through each artifact and upload it using curl (which handles multipart form data) + $artifacts | ForEach-Object { + $asset_name = $_.Name + $asset_path = $_.FullName + + Write-Host "Uploading $asset_name..." + curl -L ` + -X POST ` + -H "Accept: application/vnd.github+json" ` + -H "Authorization: token $env:GITHUB_TOKEN" ` + -H "X-GitHub-Api-Version: 2022-11-28" ` + -H "Content-Type: application/octet-stream" ` + "$($upload_url)?name=$($asset_name)" ` + --data-binary `@"$asset_path" + }