diff --git a/src/.gitattributes b/src/.gitattributes new file mode 100644 index 0000000..3937673 --- /dev/null +++ b/src/.gitattributes @@ -0,0 +1,13 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Declare files that will always have CRLF line endings on checkout. +*.cs text eol=crlf +*.xaml text eol=crlf +*.resw text eol=crlf +*.csproj text eol=crlf +*.sln text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary \ No newline at end of file diff --git a/src/.github/FUNDING.yml b/src/.github/FUNDING.yml new file mode 100644 index 0000000..d6aab31 --- /dev/null +++ b/src/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: 0x7c13 +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: jackieliu +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: paypal.me/jackil diff --git a/src/.github/ISSUE_TEMPLATE/bug_report.md b/src/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..0ce21b9 --- /dev/null +++ b/src/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,27 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[Bug]" +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. Windows 10 1809 17763.593] + - Version [e.g. v0.9.3.0] + +**Additional context** +Add any other context about the problem here. diff --git a/src/.github/ISSUE_TEMPLATE/feature_request.md b/src/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..38ac82c --- /dev/null +++ b/src/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[Feature request]" +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/src/.github/PULL_REQUEST_TEMPLATE.md b/src/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..d4c96b6 --- /dev/null +++ b/src/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,25 @@ + + +## PR Type +What kind of change does this PR introduce? + + + + + + + + + + + + + + + + + + + + +## Other information diff --git a/src/.github/RELEASE_TEMPLATE/changelog_config.json b/src/.github/RELEASE_TEMPLATE/changelog_config.json new file mode 100644 index 0000000..a3c4c1a --- /dev/null +++ b/src/.github/RELEASE_TEMPLATE/changelog_config.json @@ -0,0 +1,13 @@ +{ + "conventionalCommitsParserOptions": { + "revertPattern": "/^(?:Revert|revert:)\\s\"?([\\s\\S]+?)\"?\\s*This reverts commit (\\w*)\\./i", + "issuePrefixes": [ "#", "OLDARCH-" ] + }, + "handleBarsOptions": { + "setupFile": null, + "template": ".github/RELEASE_TEMPLATE/changelog_template.hbs", + "compileOptions": { "noEscape": true } + }, + "breakingChangesPattern": "/^breaking\\s+change$/gim", + "hostname": "https://github.com" +} diff --git a/src/.github/RELEASE_TEMPLATE/changelog_template.hbs b/src/.github/RELEASE_TEMPLATE/changelog_template.hbs new file mode 100644 index 0000000..2fdfd15 --- /dev/null +++ b/src/.github/RELEASE_TEMPLATE/changelog_template.hbs @@ -0,0 +1,43 @@ +{{#with release}} +## [{{name}}]({{href}}) +{{/with}} + +{{#commit-list commits heading='### 💥 Breaking Changes' breaking=true }} +- {{#if scope}} **{{scope}}:** {{/if}}{{subject}} ([`{{shorthash}}`]({{html_url}})) +{{/commit-list}} + +{{#commit-list commits heading='### ✨ Features' type='feat' excludeBreaking=true}} +- {{#if scope}} **{{scope}}:** {{/if}}{{subject}} ([`{{shorthash}}`]({{html_url}})) +{{/commit-list}} + +{{#commit-list commits heading='### 🐛 Fixes' type='fix' excludeBreaking=true }} +- {{#if scope}} **{{scope}}:** {{/if}}{{subject}} ([`{{shorthash}}`]({{html_url}})) +{{/commit-list}} + +{{#commit-list commits heading='### 🔥 Refactorings' type='refactor' excludeBreaking=true }} +- {{#if scope}} **{{scope}}:** {{/if}}{{subject}} ([`{{shorthash}}`]({{html_url}})) +{{/commit-list}} + +{{#commit-list commits heading='### 🐎 Performance Improvements' type='perf' excludeBreaking=true }} +- {{#if scope}} **{{scope}}:** {{/if}}{{subject}} ([`{{shorthash}}`]({{html_url}})) +{{/commit-list}} + +{{#commit-list commits heading='### 🛠 Maintenance' types='chore,ci' excludeBreaking=true }} +- {{#if scope}} **{{scope}}:** {{/if}}{{subject}} ([`{{shorthash}}`]({{html_url}})) +{{/commit-list}} + +{{#commit-list commits heading='### ✅ Tests' type='test' excludeBreaking=true }} +- {{#if scope}} **{{scope}}:** {{/if}}{{subject}} ([`{{shorthash}}`]({{html_url}})) +{{/commit-list}} + +{{#commit-list commits heading='### 📚 Documentation' type='doc,docs' excludeBreaking=true }} +- {{#if scope}} **{{scope}}:** {{/if}}{{subject}} ([`{{shorthash}}`]({{html_url}})) +{{/commit-list}} + +{{#commit-list commits heading='### 💄 Style' type='style' excludeBreaking=true }} +- {{#if scope}} **{{scope}}:** {{/if}}{{subject}} ([`{{shorthash}}`]({{html_url}})) +{{/commit-list}} + +{{#commit-list commits heading='### 📢 Translations' type='lang,trans' excludeBreaking=true }} +- {{#if scope}} **{{scope}}:** {{/if}}{{subject}} ([`{{shorthash}}`]({{html_url}})) +{{/commit-list}} diff --git a/src/.github/dependabot.yml b/src/.github/dependabot.yml new file mode 100644 index 0000000..4b4120d --- /dev/null +++ b/src/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + # default location of `.github/workflows` + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: 'action-deps: ' + + - package-ecosystem: "nuget" + # location of package manifests + directory: "/src/Notepads" + schedule: + interval: "daily" + commit-message: + prefix: 'nuget-deps: ' + +# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) \ No newline at end of file diff --git a/src/.github/issue_label_bot.yaml b/src/.github/issue_label_bot.yaml new file mode 100644 index 0000000..b642226 --- /dev/null +++ b/src/.github/issue_label_bot.yaml @@ -0,0 +1,4 @@ +label-alias: + bug: 'bug' + feature_request: 'enhancement' + question: 'question' diff --git a/src/.github/workflows/csa-bulk-dismissal.yml b/src/.github/workflows/csa-bulk-dismissal.yml new file mode 100644 index 0000000..64b5725 --- /dev/null +++ b/src/.github/workflows/csa-bulk-dismissal.yml @@ -0,0 +1,128 @@ +name: Code scanning alerts bulk dismissal + +on: + workflow_run: + workflows: [ "Notepads CI/CD Pipeline" ] + types: + - completed + workflow_dispatch: + inputs: + type: + description: Type of filter to use ("path" for using path and "desc" for using description) + required: true + default: 'path' + reason: + description: Reason for dismissal ("fp" for "false positive", "wf" for "won't fix" and "ut" for "used in tests") + required: true + default: 'wf' + +jobs: + setup: + runs-on: windows-latest + outputs: + matrix: ${{ steps.set_filter_matrix.outputs.matrix }} + steps: + - name: Setup filter matrix + id: set_filter_matrix + shell: pwsh + run: | + $FILTER_TYPE = $env:FILTER_TYPE + if ( !( $env:FILTER_TYPE -ieq 'path' ) -And !( $env:FILTER_TYPE -ieq 'desc' ) ) { + $FILTER_TYPE = 'path' + } + + switch ( $env:REASON ) { + fp { + $REASON = "false positive" + } + wf { + $REASON = "won't fix" + } + ut { + $REASON = "used in tests" + } + default { + $REASON = "won't fix" + } + } + + if ( $FILTER_TYPE -ieq 'path' ) { + $MATRIX = @{ + include = @( + @{ + filter = "*/obj/*" + } + ) + } + } elseif ( $FILTER_TYPE -ieq 'desc' ) { + $MATRIX = @{ + include = @( + @{ + filter = "Calls to unmanaged code" + }, + @{ + filter = "Unmanaged code" + } + ) + } + } else { + throw "Invalid filter type argument" + } + + $MATRIX.include | Foreach-Object { + $_.Add('type',"$FILTER_TYPE") + $_.Add('reason',"$REASON") + } + echo "::set-output name=matrix::$($MATRIX | ConvertTo-Json -depth 32 -Compress)" + env: + FILTER_TYPE: ${{ github.event.inputs.type }} + REASON: ${{ github.event.inputs.reason }} + dismiss-alerts: + name: Dismiss alerts + needs: setup + runs-on: windows-latest + strategy: + matrix: ${{ fromJson(needs.setup.outputs.matrix) }} + env: + # Settings + OWNER: ${{ github.repository_owner }} # verbatim from URL + PROJECT_NAME: ${{ github.event.repository.name }} # verbatim from URL + ACCESS_TOKEN: ${{ secrets.CSA_ACCESS_TOKEN }} # requires security_events read/write permissions + DISMISS_REASON: ${{ matrix.reason }} # "false positive", "won't fix" or "used in tests". + ALERTS_PER_PAGE: 100 # maximum is 100 + FILTER: ${{ matrix.filter }} + FILTER_TYPE: ${{ matrix.type }} + steps: + - name: Run automation + id: run_automation + shell: pwsh + run: | + $HEADERS = @{ + Authorization = 'Basic {0}' -f [System.Convert]::ToBase64String([char[]]"$($env:OWNER):$($env:ACCESS_TOKEN)") + Accept = 'application/vnd.github.v3+json' + } + + $page = 1 + $FETCH_URL = "https://api.github.com/repos/$env:OWNER/$env:PROJECT_NAME/code-scanning/alerts?state=open&page={0}&per_page=$env:ALERTS_PER_PAGE" + $LIST_OF_ALERTS = Invoke-RestMethod -Method Get -Headers $HEADERS -Uri $($FETCH_URL -f $page) + while ( $LIST_OF_ALERTS -ne $null ) { + if ( $env:FILTER_TYPE -ieq 'path' ) { + $MATCHES += $($LIST_OF_ALERTS | Where-Object { $_.most_recent_instance.location.path -like "$env:FILTER" }) + } else { + $MATCHES += $($LIST_OF_ALERTS | Where-Object { $_.rule.description -like "$env:FILTER" }) + } + + $page += 1 + $LIST_OF_ALERTS = Invoke-RestMethod -Method Get -Headers $HEADERS -Uri $($FETCH_URL -f $page) + } + + $ALERT_URL = "https://api.github.com/repos/$env:OWNER/$env:PROJECT_NAME/code-scanning/alerts/{0}" + $BODY = @{ + state = 'dismissed' + dismissed_reason = "$env:DISMISS_REASON" + } | ConvertTo-Json + foreach ($index in $MATCHES.number) { + Invoke-RestMethod -Method Patch -Headers $HEADERS -Uri $($ALERT_URL -f $index) -Body $BODY + } + +# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) \ No newline at end of file diff --git a/src/.github/workflows/main.yml b/src/.github/workflows/main.yml new file mode 100644 index 0000000..4edca64 --- /dev/null +++ b/src/.github/workflows/main.yml @@ -0,0 +1,402 @@ +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.1 + + - 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.2.3 + 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.2.3 + 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 + 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.16.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) diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..a89b1c8 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,355 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- Backup*.rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db diff --git a/src/.whitesource b/src/.whitesource new file mode 100644 index 0000000..e0aaa3e --- /dev/null +++ b/src/.whitesource @@ -0,0 +1,8 @@ +{ + "checkRunSettings": { + "vulnerableCheckRunConclusionLevel": "failure" + }, + "issueSettings": { + "minSeverityLevel": "LOW" + } +} \ No newline at end of file diff --git a/src/CI-CD_DOCUMENTATION.md b/src/CI-CD_DOCUMENTATION.md new file mode 100644 index 0000000..022ff80 --- /dev/null +++ b/src/CI-CD_DOCUMENTATION.md @@ -0,0 +1,419 @@ +# Notepads CI/CD documentation + +- after merging the PR, the first run of the "Notepads CI/CD Pipeline" workflow will not complete successfully, because it requires specific setup explained in this documentation. The two other workflows "CodeQL Analysis" and "Build", should complete successfully. + +## 1. Set up SonarCloud + +### SonarCloud is a cloud-based code quality and security service + +#### Create SonarCloud project + +- Go to https://sonarcloud.io/ + +- Click the "Log in" button and create a new account or connect with GitHub account (recommended) + +- At the top right corner click the "+" sign + +- From the dropdown select "Create new Organization" + +- Click the "Choose an organization on GitHub" button + +- Select an account for the organization setup + +- On Repository Access select "Only select repositories" and select the project and click the "Save" button + +- On the "Create organization page" don't change the Key and click "Continue" + +- Select the Free plan then click the "Create Organization" button to finalize the creation of the Organization + +#### Configure SonarCloud project + +- At the top right corner click the "+" sign and select "Analyze new project" + +- Select the project and click the "Set Up" button in the box on the right + +- Under "Choose your analysis method" click "With GitHub Actions" and **keep the following page open** + +- [Create a new PAT with **repo_deployment** and **read:packages** permissions](#7-how-to-create-a-pat) and copy the value of the generated token + +- In the project's GitHub repository, go to the **Settings** tab -> Secrets + +- Click on **New Repository secret** and create a new secret with the name **SONAR_GITHUB_TOKEN** and the token you just copied as the value + +- Create another secret with the two values from the SonarCloud page you kept open, which you can close after completing this step + +![SonarCloud_1](ScreenShots/CI-CD_DOCUMENTATION/SonarCloud_1.png) + +- [Run the "Notepads CI/CD Pipeline" workflow manually](#2-run-workflow-manually) + +#### Set Quality Gate + +- After the "Notepads CI/CD Pipeline" workflow has executed successfully, go to https://sonarcloud.io/projects and click on the project + +- In the alert bar above the results, click the "Set new code definition" button and select "Previous version" (notice the "New Code definition has been updated" alert at the top) + +- The Quality Gate will become active as soon as the next SonarCloud scan completes successfully + +
+ + +## 2. Run workflow manually + +Once you've set up all the steps above correctly, you should be able to successfully complete a manual execution of the "Notepads CI/CD Pipeline" workflow. + +1. Go to the project's GitHub repository and click on the **Actions** tab + +2. From the "Workflows" list on the left, click on "Notepads CI/CD Pipeline" + +3. On the right, next to the "This workflow has a workflow_dispatch event trigger" label, click on the "Run workflow" dropdown, make sure the default branch is selected (if not manually changed, should be main or master) in the "Use workflow from" dropdown and click the "Run workflow" button + +4. You can optionally fill the argument textbox with "release" to trigger [GitHub Release](#github_release) and [Store Upload](#store_upload) + +![Actions_workflow_dispatch](ScreenShots/CI-CD_DOCUMENTATION/Actions_workflow_dispatch.png) + +5. Once the workflow run has completed successfully, move on to the next step of the documentation + +NOTE: **screenshots are only exemplary** + +
+ +## 3. Set up Dependabot + +Dependabot is a GitHub native security tool that goes through the dependencies in the project and creates alerts, and PRs with updates when a new and/or non-vulnerable version is found. + +- for PRs with version updates, this pipeline comes pre-configured for all current dependency sources in the project, so at "Insights" tab -> "Dependency graph" -> "Dependabot", you should be able to see all tracked sources of dependencies, when they have been checked last and view a full log of the last check + +![Dependabot_tab](ScreenShots/CI-CD_DOCUMENTATION/Dependabot_tab.png) + +![Dependabot_log_page](ScreenShots/CI-CD_DOCUMENTATION/Dependabot_log_page.png) + +### Set up security alerts and updates + +##### - GitHub, through Dependabot, also natively offers a security check for vulnerable dependencies + +1. Go to "Settings" tab of the repo + +2. Go to "Security & analysis" section + +3. Click "Enable" for both "Dependabot alerts" and "Dependabot security updates" + +- By enabling "Dependabot alerts", you would be notified for any vulnerable dependencies in the project. At "Security" tab -> "Dependabot alerts", you can manage all alerts. By clicking on an alert, you would be able to see a detailed explanation of the vulnerability and a viable solution. + +![Dependabot_alerts_page](ScreenShots/CI-CD_DOCUMENTATION/Dependabot_alerts_page.png) + +![Dependabot_alert_page](ScreenShots/CI-CD_DOCUMENTATION/Dependabot_alert_page.png) + +- By enabling "Dependabot security updates", you authorize Dependabot to create PRs specifically for **security updates** + +![Dependabot_PRs](ScreenShots/CI-CD_DOCUMENTATION/Dependabot_PRs.png) + +### Set up Dependency graph + +##### - The "Dependency graph" option should be enabled by default for all public repos, but in case it isn't: + +1. Go to "Settings" tab of the repo + +2. Go to "Security&Analysis" section + +3. Click "Enable" for the "Dependency graph" option + +- this option enables the "Insights" tab -> "Dependency graph" section -> "Dependencies" tab, in which all the dependencies for the project are listed, under the different manifests they are included in + +![Dependabot_dependency_graph](ScreenShots/CI-CD_DOCUMENTATION/Dependabot_dependency_graph.png) + +NOTE: **screenshots are only exemplary** + +
+ +## 4. CodeQL + +CodeQL is GitHub's own industry-leading semantic code analysis engine. CodeQL requires no setup, because it comes fully pre-configured by us. + +To activate it and see its results, only a push commit or a merge of a PR to the default branch of the repository, is required. + +We've also configured CodeQL to run on schedule, so every day at 8:00AM UTC, it automatically scans the code. + +- you can see the results here at **Security** tab -> **Code scanning alerts** -> **CodeQL**: + +![CodeQL_results](ScreenShots/CI-CD_DOCUMENTATION/CodeQL_results.png) + +- on the page of each result, you can see an explanation of what the problem is and also one or more solutions: + +![CodeQL_alert_page](ScreenShots/CI-CD_DOCUMENTATION/CodeQL_alert_page.png) + +### Code scanning alerts bulk dismissal tool + +##### - currently, GitHub allows for only 25 code scanning alerts to be dismissed at a time. Sometimes, you might have hundreds you would like to dismiss, so you will have to click many times and wait for a long time to dismiss them. Via the "csa-bulk-dismissal.yml", you can automatically dismiss unnecessary alerts or manually do that with one click. + +NOTE: This tool executes automatically when **Notepads CI/CD Pipeline** action completes. + +#### 1. Setup + +1. In the repository, go to the **Settings** tab -> **Secrets** + +![CSA_secrets](ScreenShots/CI-CD_DOCUMENTATION/CSA_secrets.png) + +2. Add the following secrets with the name and the corresponding value, by at the upper right of the section, clicking on the **New repository secret** button: + +![CSA_new_secret](ScreenShots/CI-CD_DOCUMENTATION/CSA_new_secret.png) + +![CSA_secret_add](ScreenShots/CI-CD_DOCUMENTATION/CSA_secret_add.png) + +- CSA_ACCESS_TOKEN - [create a PAT with "security_events" permission only](#7-how-to-create-a-pat). + +#### 2. Execution + +1. This tool is automatically triggered when **Notepads CI/CD Pipeline** task completes, if you want to manually execute this follow next steps + +2. In your repo, click on the Actions tab and on the left, in the Workflows list, click on the "Code scanning alerts bulk dismissal" + +![CSA_execute_1](ScreenShots/CI-CD_DOCUMENTATION/CSA_execute_1.png) + +3. On the right, click on the "Run workflow" dropdown. Under "Use workflow from" choose your default branch (usually main/master), in the **Type of filter to use** field type "path"/"desc" depending upon whether dismiss alerts based on predefined paths or description respectively (default is "path"), in the **Reason for dismissal** type "fp"/"wf"/"ut" for "false positive"/"won't fix"/"used in tests" respectively (default is "wf") and click on the **Run workflow** button + + +![CSA_execute_2](ScreenShots/CI-CD_DOCUMENTATION/CSA_execute_2.png) + +NOTE: if any unsupported values are entered default values will be used + +4. If everything was set up currently in the "Setup" phase, the "Code scanning alerts bulk dismissal" workflow is going to be executed successfully, which after some time, would result in **all** previously open code scanning alerts, with a certain description be dismissed + +![CSA_execute_3](ScreenShots/CI-CD_DOCUMENTATION/CSA_execute_3.png) + +![CSA_execute_4](ScreenShots/CI-CD_DOCUMENTATION/CSA_execute_4.png) + +![CSA_execute_5](ScreenShots/CI-CD_DOCUMENTATION/CSA_execute_5.png) + +NOTE: "closed" refers to "dismissed" alerts + +#### 3. Customization + +The "setup" job in the pipeline, allows for more precise filtering of alerts to bulk dismiss. It uses the filter type to choose (filter based on path or description) from the alert to determine if it has to be dismissed or not. We've added the following paths and alert descriptions by default: + +##### Paths: + +- "\*/obj/\*" (if path contains `obj` folder at any position) + +##### Descriptions: + +- "Calls to unmanaged code" +- "Unmanaged code" + +##### To add more paths, follow these steps: + +1. In your source code, open ".github/workflows/csa-bulk-dismissal.yml" + +2. From line 50 to 56, notice "$MATRIX = **". This is the [powershell hashtable](https://docs.microsoft.com/powershell/scripting/learn/deep-dives/everything-about-hashtable?view=powershell-7.1) of filters that the CSABD (Code scanning alerts bulk dismissal) tool uses to filter through the alerts: + +![CSA_custom_3](ScreenShots/CI-CD_DOCUMENTATION/CSA_custom_3.png) + +3. To add more paths under **include** element use comma separation and followed from next line add `@{ filter = "New path" }`. Replace "New path" with the path (with or without [wild cards](https://docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_wildcards?view=powershell-7.1)) you want: + +![CSA_custom_4](ScreenShots/CI-CD_DOCUMENTATION/CSA_custom_4.png) + +##### To add more descriptions, follow these steps: + +1. In your source code, open ".github/workflows/csa-bulk-dismissal.yml" + +2. From line 58 to 67, notice "$MATRIX = **". This is the [powershell hashtable](https://docs.microsoft.com/powershell/scripting/learn/deep-dives/everything-about-hashtable?view=powershell-7.1) of filters that the CSABD (Code scanning alerts bulk dismissal) tool uses to filter through the alerts: + +![CSA_custom_1](ScreenShots/CI-CD_DOCUMENTATION/CSA_custom_1.png) + +3. To add more descriptions under **include** element use comma separation and followed from next line add `@{ filter = "New description" }`. Replace "New description" with the description you want: + +![CSA_custom_2](ScreenShots/CI-CD_DOCUMENTATION/CSA_custom_2.png) + +##### To change default filter type and dismissal reason, follow these steps: + +1. In your source code, open ".github/workflows/csa-bulk-dismissal.yml" + +2. To change default filter type change **$FILTER_TYPE** variable in line 31 to something else (default is "path", supported are: "desc" and "path"): + +![CSA_custom_5](ScreenShots/CI-CD_DOCUMENTATION/CSA_custom_5.png) + +3. To change dismissal reason change **$REASON** variable in line 45 to something else (default is "won't fix", supported are: "false positive", "won't fix" and "used in tests"): + +![CSA_custom_6](ScreenShots/CI-CD_DOCUMENTATION/CSA_custom_6.png) + +NOTE: changing default filter type and dismissal reason won't change dafault value typed when [manually executing](#csa_execute) tool, change values in line 13 and 17 respectively to reflect the change + +![CSA_custom_7](ScreenShots/CI-CD_DOCUMENTATION/CSA_custom_7.png) + +
+ + +## 5. Automated GitHub release + +When triggered bumps up the GitHub tag in the repo and executes the CD job and produces release with changelogs + +Note: **not every commit to your master branch are included in changelog** + +#### Setup + +Add the following secrets by going to the repo **Settings** tab -> **Secrets**: + +1. **PACKAGE_CERTIFICATE_BASE64** + +- used to dynamically create the PFX file required for the signing of the **msixbundle** +- use the following PowerShell code locally to turn your PFX file into Base64: + +``` +# read from PFX as binary +$PFX_FILE = [IO.File]::ReadAllBytes('absolute_path_to_PFX') +# convert to Base64 and write in txt +[System.Convert]::ToBase64String($PFX_FILE) | Out-File 'absolute_path\cert.txt' +``` + +- copy the contents of the **cert.txt** and paste as the value of the secret + +2. **PACKAGE_CERTIFICATE_PWD** + +- used in the build of the project to authenticate the PFX +- copy and paste the password of your PFX as the value of this secret + +NOTE: + +- none of those values are visible in the logs of the pipeline, nor are available to anyone outside of the original repository e.g. forks, anonymous clones etc. +- the dynamically created PFX file lives only for the duration of the pipeline execution + +#### Execution + +[Once you've set up all the steps for manual execution of the "Notepads CI/CD Pipeline" workflow correctly](#workflow_dispatch), you should be able to successfully trigger release with the same workflow. + +1. Go to the project's GitHub repository and click on the **Actions** tab + +2. From the "Workflows" list on the left, click on "Notepads CI/CD Pipeline" + +3. On the right, next to the "This workflow has a workflow_dispatch event trigger" label, click on the "Run workflow" dropdown, make sure the default branch is selected (if not manually changed, should be main or master) in the "Use workflow from" dropdown, type "release" in the argument textbox (By default "test" is typed) and click the "Run workflow" button + +![Actions_workflow_dispatch](ScreenShots/CI-CD_DOCUMENTATION/Actions_workflow_dispatch.png) + +4. The workflow will produce release assets and calculate version, generate changelogs from valid commits since previous tag. + +NOTE: **screenshots are only exemplary** + +
+ +#### - follow these instructions for any commit (push or PR merge) to your master branch, you would like to see in changelog and count towards version change. + +You would need one of three keywords at the start of your commit title. Each of the three keywords corresponds to a number in your release version i.e. v1.2.3. The release versioning uses the ["Conventional Commits" specification](https://www.conventionalcommits.org/en/v1.0.0/): + +- "fix: ..." - this keyword corresponds to the last number v1.2.**3**, also known as PATCH; +- "feat: ..." - this keyword corresponds to the middle number v1.**2**.3, also known as MINOR; +- "perf: ..." - this keyword corresponds to the first number v**1**.2.3, also known as MAJOR. In addition, to trigger a MAJOR release, you would need to write "BREAKING CHANGE: ..." in the description of the commit, with an empty line above it to indicate it is in the