Notepads CI/CD Pipeline #2228
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Notepads CI/CD Pipeline | |
on: | |
push: | |
#paths-ignore: | |
#- '**.md' | |
#- 'ScreenShots/**' | |
#- '.whitesource' | |
#- 'azure-pipelines.yml' | |
#- '.github/**' | |
#- '!.github/workflows/main.yml' | |
branches-ignore: | |
# PRs made by bots trigger both 'push' and 'pull_request' event, ignore 'push' event in that case | |
- 'dependabot**' | |
- 'imgbot**' | |
tags-ignore: | |
- '**' | |
pull_request: | |
paths-ignore: | |
- '**.md' | |
- 'ScreenShots/**' | |
- '.whitesource' | |
- 'azure-pipelines.yml' | |
- '.github/**' | |
- '!.github/workflows/main.yml' | |
workflow_dispatch: | |
inputs: | |
param: | |
description: Optional parameter for additional actions | |
# Type '(major|maj) (realease|rel)' or '(minor|min) (realease|rel)' or release for major,miner,patch release respectively | |
# Or explicitly provide version number to create release with that version | |
required: false | |
schedule: | |
- cron: '0 8 * * *' | |
jobs: | |
setup: | |
runs-on: windows-latest | |
outputs: | |
matrix: ${{ steps.set_matrix.outputs.matrix }} | |
steps: | |
- name: Setup strategy matrix | |
id: set_matrix | |
shell: pwsh | |
run: | | |
$MATRIX = @{ | |
include = @( [ordered]@{ | |
configuration= "Debug" | |
appxBundlePlatforms = "x86|x64" | |
oldVersion = "" | |
newVersion = "" | |
debug = $true | |
runCodeqlAnalysis = $false | |
runSonarCloudScan = $false | |
}, [ordered]@{ | |
configuration= "Release" | |
appxBundlePlatforms= "x86|x64|ARM64" | |
oldVersion = "" | |
newVersion = "" | |
debug = $true | |
runCodeqlAnalysis= $false | |
runSonarCloudScan= $false | |
}, [ordered]@{ | |
configuration= "Production" | |
appxBundlePlatforms= "x86|x64|ARM64" | |
oldVersion = "" | |
newVersion = "" | |
debug = $true | |
runCodeqlAnalysis= $false | |
runSonarCloudScan= $false | |
} | |
) | |
} | |
if ( ( $env:GITHUB_EVENT -eq 'pull_request' ) ` | |
-or ( $env:GITHUB_EVENT -eq 'schedule' ) ` | |
-or ( $env:FORK -eq 'true' ) ) { | |
$MATRIX.include | Foreach-Object { $_.runSonarCloudScan = $false } | |
} | |
if ( ( $env:GITHUB_EVENT -ne 'push' ) ` | |
-and ( $env:GITHUB_EVENT -ne 'pull_request' ) ) { | |
$MATRIX.include = @($MATRIX.include | Where-Object { $_.configuration -eq "$env:RELEASE_CONFIGURATION" }) | |
if ( ( $env:GITHUB_EVENT -eq 'workflow_dispatch' ) ` | |
-and ( $env:GITHUB_REF -eq 'refs/heads/master' ) ) { | |
$FETCH_URL = "https://api.github.com/repos/$env:GIT_REPOSITORY/tags?per_page=1" | |
$OLD_VER = [System.Version]::Parse($(Invoke-RestMethod -Method Get -Uri $FETCH_URL).name -replace 'v') | |
[System.Int32[]]$VER_INPUT = $($env:PARAM -replace '[a-zA-Z]| ').Split('.') | |
if ( ( $VER_INPUT.Count -gt 1 ) -or ( $VER_INPUT[0] -gt 0 ) ) { | |
$NEW_VER = [System.Version]::new($VER_INPUT[0],` | |
(if ( $VER_INPUT.Count -ge 1 ) { $VER_INPUT[1] } else { 0 }),` | |
(if ( $VER_INPUT.Count -ge 2 ) { $VER_INPUT[2] } else { 0 }),` | |
(if ( $VER_INPUT.Count -ge 3 ) { $VER_INPUT[3] } else { 0 })) | |
} elseif ( $env:PARAM -match 'rel' ) { | |
if ( $env:PARAM -match 'maj' ) { | |
$NEW_VER = [System.Version]::new($OLD_VER.Major + 1, 0, 0, 0) | |
} elseif ( $env:PARAM -match 'min' ) { | |
$NEW_VER = [System.Version]::new($OLD_VER.Major, $OLD_VER.Minor + 1, 0, 0) | |
} else { | |
$NEW_VER = [System.Version]::new($OLD_VER.Major, $OLD_VER.Minor, $OLD_VER.Build + 1, 0) | |
} | |
} | |
if ( ![System.String]::IsNullOrEmpty($OLD_VER) ` | |
-and ![System.String]::IsNullOrEmpty($NEW_VER) ` | |
-and ( $NEW_VER -gt $OLD_VER ) ) { | |
$MATRIX.include | Foreach-Object { $_.oldVersion = $OLD_VER.ToString() } | |
$MATRIX.include | Foreach-Object { $_.newVersion = $NEW_VER.ToString() } | |
} | |
$MATRIX.include | Foreach-Object { $_.runCodeqlAnalysis = $false } | |
$MATRIX.include | Foreach-Object { | |
if ( $_.configuration -eq "$env:RELEASE_CONFIGURATION" ) { $_.release = $true } | |
} | |
} else { | |
$MATRIX.include | Foreach-Object { $_.appxBundlePlatforms = 'x64' } | |
if ( $env:GITHUB_EVENT -ne 'schedule' ) { | |
$MATRIX.include | Foreach-Object { $_.runCodeqlAnalysis = $false } | |
} | |
} | |
} | |
echo "::set-output name=matrix::$($MATRIX | ConvertTo-Json -depth 32 -Compress)" | |
env: | |
FORK: ${{ github.event.repository.fork }} | |
PARAM: ${{ github.event.inputs.param }} | |
GITHUB_REF: ${{ github.ref }} | |
GITHUB_EVENT: ${{ github.event_name }} | |
RELEASE_CONFIGURATION: Production | |
ci: | |
needs: setup | |
runs-on: windows-latest | |
strategy: | |
matrix: ${{ fromJson(needs.setup.outputs.matrix) }} | |
outputs: | |
old_version: ${{ matrix.oldVersion }} | |
new_version: ${{ matrix.newVersion }} | |
env: | |
SOLUTION_NAME: src\Notepads.sln | |
CONFIGURATION: ${{ matrix.configuration }} | |
DEFAULT_DIR: ${{ github.workspace }} | |
steps: | |
- if: matrix.runSonarCloudScan | |
name: Set up JDK 11 | |
id: Setup_JDK | |
uses: actions/setup-java@v4 | |
with: | |
java-version: 1.11 | |
- name: Setup MSBuild | |
id: setup_msbuild | |
uses: microsoft/setup-msbuild@v2 | |
- name: Setup NuGet | |
id: setup-nuget | |
uses: NuGet/[email protected] | |
- name: Checkout repository | |
id: checkout_repo | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 50 | |
token: ${{ secrets.GITHUB_TOKEN }} | |
# Due to the insufficient memory allocated by default, CodeQL sometimes requires more to be manually allocated | |
- if: matrix.runCodeqlAnalysis | |
name: Configure Pagefile | |
id: config_pagefile | |
uses: al-cheb/[email protected] | |
with: | |
minimum-size: 8GB | |
maximum-size: 10GB | |
- if: matrix.newVersion != '' | |
name: Bump GitHub tag and Update manifest | |
id: tag_manifest_generator | |
shell: pwsh | |
run: | | |
git config --global user.name $env:GIT_USER_NAME | |
git config --global user.email $env:GIT_USER_EMAIL | |
git tag -a -m "$env:NEW_VERSION_TAG" $env:NEW_VERSION_TAG | |
git push --follow-tags | |
$xml = [xml](Get-Content $env:APPXMANIFEST_PATH) | |
$xml.Package.Identity.Version = $env:NEW_VERSION | |
$xml.save($env:APPXMANIFEST_PATH) | |
env: | |
GIT_USER_NAME: ${{ secrets.GIT_USER_NAME }} | |
GIT_USER_EMAIL: ${{ secrets.GIT_USER_EMAIL }} | |
APPXMANIFEST_PATH: src\Notepads\Package.appxmanifest | |
NEW_VERSION: ${{ matrix.newVersion }} | |
NEW_VERSION_TAG: v${{ matrix.newVersion }} | |
- if: matrix.runSonarCloudScan | |
name: Cache SonarCloud packages | |
id: cache_sonar_packages | |
uses: actions/[email protected] | |
with: | |
path: ~\sonar\cache | |
key: ${{ runner.os }}-sonar | |
restore-keys: ${{ runner.os }}-sonar | |
- if: matrix.runSonarCloudScan | |
name: Cache SonarCloud scanner | |
id: cache_sonar_scanner | |
uses: actions/[email protected] | |
with: | |
path: .\.sonar\scanner | |
key: ${{ runner.os }}-sonar-scanner | |
restore-keys: ${{ runner.os }}-sonar-scanner | |
- if: matrix.runSonarCloudScan && steps.cache_sonar_scanner.outputs.cache-hit != 'true' | |
name: Install SonarCloud scanner | |
id: install_sonar_scanner | |
shell: pwsh | |
run: | | |
New-Item -Path .\.sonar\scanner -ItemType Directory | |
dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner | |
- if: matrix.runSonarCloudScan | |
name: Initialize SonarCloud scanner | |
id: init_sonar_scanner | |
shell: pwsh | |
run: | | |
$LOWERCASE_REPOSITORY_NAME = "${{ github.event.repository.name }}".ToLower() | |
.\.sonar\scanner\dotnet-sonarscanner begin ` | |
/k:"${{ github.repository_owner }}_${{ github.event.repository.name }}" ` | |
/o:"$LOWERCASE_REPOSITORY_NAME" ` | |
/d:sonar.login="$env:SONAR_TOKEN" ` | |
/d:sonar.host.url="https://sonarcloud.io" | |
env: | |
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
- if: matrix.newVersion != '' | |
name: Create and validate PFX certificate for AppxBundle | |
id: create_validate_pfx_cert | |
shell: pwsh | |
run: | | |
$TARGET_FILE = "$env:DEFAULT_DIR\cert.pfx" | |
$FROM_BASE64_STR = [System.Convert]::FromBase64String($env:BASE64_STR) | |
[System.IO.File]::WriteAllBytes($TARGET_FILE, $FROM_BASE64_STR) | |
$FILE_STREAM = [System.IO.File]::OpenRead($TARGET_FILE) | |
$FILE_STREAM.Position = 0 | |
$SHA256 = [System.Security.Cryptography.SHA256]::Create() | |
$HASH_BUILDER = [System.Text.StringBuilder]::new() | |
$SHA256.ComputeHash($FILE_STREAM) | ForEach-Object { $HASH_BUILDER.Append($_.ToString("x2")) } | |
if ( $HASH_BUILDER.ToString() -cne $env:SHA256_HASH ) { | |
throw [System.Exception]::new("Created certificate hash $($HASH_BUILDER.ToString()) $( | |
)doesn't match provided hash $($env:SHA256_HASH)") | |
} | |
env: | |
BASE64_STR: ${{ secrets.PACKAGE_CERTIFICATE_BASE64 }} | |
SHA256_HASH: ${{ secrets.PACKAGE_CERTIFICATE_SHA256 }} | |
- name: Restore the application | |
id: restore_application | |
shell: pwsh | |
run: | | |
msbuild $env:SOLUTION_NAME /t:Restore | |
nuget restore $env:SOLUTION_NAME | |
- if: matrix.runCodeqlAnalysis | |
name: Initialize CodeQL | |
id: init_codeql | |
uses: github/codeql-action/init@v3 | |
with: | |
queries: security-and-quality | |
languages: csharp | |
- name: Build and generate bundles | |
id: build_app | |
shell: pwsh | |
run: | | |
msbuild $env:SOLUTION_NAME ` | |
/p:Platform=$env:PLATFORM ` | |
/p:Configuration=$env:CONFIGURATION ` | |
/p:UapAppxPackageBuildMode=$env:UAP_APPX_PACKAGE_BUILD_MODE ` | |
/p:AppxBundle=$env:APPX_BUNDLE ` | |
/p:AppxPackageSigningEnabled=$env:APPX_PACKAGE_SIGNING_ENABLED ` | |
/p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS ` | |
/p:AppxPackageDir=$env:ARTIFACTS_DIR ` | |
/p:PackageCertificateKeyFile=$env:PACKAGE_CERTIFICATE_KEYFILE ` | |
/p:PackageCertificatePassword=$env:PACKAGE_CERTIFICATE_PASSWORD ` | |
/p:AppCenterSecret=$env:APP_CENTER_SECRET | |
env: | |
PLATFORM: x64 | |
UAP_APPX_PACKAGE_BUILD_MODE: StoreUpload | |
APPX_BUNDLE: Always | |
APPX_PACKAGE_SIGNING_ENABLED: ${{ matrix.newVersion != '' }} | |
APPX_BUNDLE_PLATFORMS: ${{ matrix.appxBundlePlatforms }} | |
ARTIFACTS_DIR: ${{ github.workspace }}\Artifacts | |
PACKAGE_CERTIFICATE_KEYFILE: ${{ github.workspace }}\cert.pfx | |
PACKAGE_CERTIFICATE_PASSWORD: ${{ secrets.PACKAGE_CERTIFICATE_PWD }} | |
APP_CENTER_SECRET: ${{ secrets.APP_CENTER_SECRET }} | |
- if: matrix.debug && !contains( matrix.appxBundlePlatforms, 'arm64' ) | |
name: Test ARM build in debug configuration | |
id: build_app_arm_debug | |
shell: pwsh | |
run: | | |
msbuild $env:SOLUTION_NAME ` | |
/p:Platform=$env:PLATFORM ` | |
/p:Configuration=$env:CONFIGURATION ` | |
/p:UapAppxPackageBuildMode=$env:UAP_APPX_PACKAGE_BUILD_MODE ` | |
/p:AppxBundle=$env:APPX_BUNDLE ` | |
/p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS | |
env: | |
PLATFORM: ARM64 | |
UAP_APPX_PACKAGE_BUILD_MODE: StoreUpload | |
APPX_BUNDLE: Always | |
APPX_BUNDLE_PLATFORMS: ARM64 | |
- if: matrix.runCodeqlAnalysis | |
name: Perform CodeQL Analysis | |
id: analyze_codeql | |
uses: github/codeql-action/analyze@v3 | |
continue-on-error: true | |
- if: matrix.runSonarCloudScan | |
name: Send SonarCloud results | |
id: send_sonar_results | |
shell: pwsh | |
run: | | |
.\.sonar\scanner\dotnet-sonarscanner end ` | |
/d:sonar.login="$env:SONAR_TOKEN" | |
env: | |
GITHUB_TOKEN: ${{ secrets.SONAR_GITHUB_TOKEN }} | |
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
- if: matrix.newVersion != '' | |
name: Upload build artifacts | |
id: upload_artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: Build artifacts | |
path: Artifacts/ | |
cd: | |
# This job will execute when the workflow is triggered on a 'workflow_dispatch' event, | |
# the target branch is 'master' and required parameter provided for release. | |
if: needs.ci.outputs.new_version != '' | |
needs: [ setup, ci ] | |
runs-on: windows-latest | |
env: | |
OLD_VERSION: ${{ needs.ci.outputs.old_version }} | |
NEW_VERSION: ${{ needs.ci.outputs.new_version }} | |
steps: | |
- name: Checkout repository | |
id: checkout_repo | |
uses: actions/checkout@v4 | |
- name: Download and extract MSIX package | |
id: dl_package_artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: Build artifacts | |
path: Artifacts/ | |
- name: Create deployment payload | |
id: create_notepads_zip | |
shell: pwsh | |
run: | | |
Get-ChildItem -Filter *Production* -Recurse | Rename-Item -NewName { $_.name -replace "_Production|_Test",'' } | |
Compress-Archive -Path "Notepads_$($env:NEW_VERSION)\*" ` | |
-DestinationPath "Notepads_$($env:NEW_VERSION)\Notepads_$($env:NEW_VERSION)_x86_x64_ARM64.zip" | |
working-directory: ./Artifacts | |
- name: Generate changelog | |
id: generate_changlog | |
uses: mrchief/[email protected] | |
with: | |
previousReleaseTagNameOrSha: v${{ env.OLD_VERSION }} | |
nextReleaseTagName: v${{ env.NEW_VERSION }} | |
nextReleaseName: v${{ env.NEW_VERSION }} | |
configFilePath: .github/RELEASE_TEMPLATE/changelog_config.json | |
- name: Create and publish release | |
id: create_release | |
uses: ncipollo/[email protected] | |
with: | |
allowUpdates: true | |
replacesArtifacts: true | |
tag: v${{ env.NEW_VERSION }} | |
name: Notepads v${{ env.NEW_VERSION }} | |
body: ${{ steps.generate_changlog.outputs.changelog }} | |
token: ${{ secrets.GITHUB_TOKEN }} | |
artifacts: | |
Artifacts/Notepads_${{ env.NEW_VERSION }}/Notepads_${{ env.NEW_VERSION }}_x86_x64_ARM64.msixbundle | |
Artifacts/Notepads_${{ env.NEW_VERSION }}/Notepads_${{ env.NEW_VERSION }}_x86_x64_ARM64.zip | |
# - name: Publish to Windows Store | |
# id: publish_to_store | |
# uses: isaacrlevin/[email protected] | |
# with: | |
# tenant-id: ${{ secrets.AZURE_AD_TENANT_ID }} | |
# client-id: ${{ secrets.AZURE_AD_APPLICATION_CLIENT_ID }} | |
# client-secret: ${{ secrets.AZURE_AD_APPLICATION_SECRET }} | |
# app-id: ${{ secrets.STORE_APP_ID }} | |
# package-path: "${{ github.workspace }}/Artifacts/" | |
# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) |