Skip to content

Notepads CI/CD Pipeline #2016

Notepads CI/CD Pipeline

Notepads CI/CD Pipeline #2016

Workflow file for this run

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/setup-nuget@v2.0.0
- 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/configure-pagefile-action@v1.4
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/cache@v4.0.2
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/cache@v4.0.2
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/universal-changelog-action@v1.3.2
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/release-action@v1.14.0
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/windows-store-action@1.0
# 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)