-
-
Notifications
You must be signed in to change notification settings - Fork 489
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DevSecOps pipeline with GitHub Actions (#760)
Co-authored-by: AleksIvanovSinglet <Aleksandar.Ivanov@singlet.dev> Co-authored-by: AleksIvanovSinglet <74899441+AleksIvanovSinglet@users.noreply.github.com> Co-authored-by: Svetoslav Zlatkov <62066501+szlatkow@users.noreply.github.com> Co-authored-by: Svetoslav Zlatkov <zlatkov@singlet.dev> Co-authored-by: Aleksandar Ivanov <74899441+aleks-ivanov@users.noreply.github.com>
1 parent
037e739
commit 38dfba1
Showing
43 changed files
with
1,138 additions
and
5 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
version: 2 | ||
updates: | ||
- package-ecosystem: "github-actions" | ||
# default location of `.github/workflows` | ||
directory: "/" | ||
schedule: | ||
interval: "weekly" | ||
|
||
- package-ecosystem: "nuget" | ||
# location of package manifests | ||
directory: "/src/Notepads" | ||
schedule: | ||
interval: "daily" | ||
|
||
- package-ecosystem: "nuget" | ||
# location of package manifests | ||
directory: "/src/Notepads.Controls" | ||
schedule: | ||
interval: "daily" | ||
|
||
# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
name: CodeQL Analysis | ||
|
||
on: | ||
push: | ||
pull_request: | ||
schedule: | ||
- cron: '0 8 * * *' | ||
|
||
jobs: | ||
analyze: | ||
name: codeql-analysis | ||
runs-on: windows-latest | ||
strategy: | ||
matrix: | ||
configuration: [ Production ] | ||
env: | ||
SOLUTION_NAME: src\Notepads.sln | ||
PROJECT_PATH: src\Notepads\Notepads.csproj | ||
CONFIGURATION: ${{ matrix.configuration }} | ||
steps: | ||
- name: Checkout repository | ||
id: checkout_repo | ||
uses: actions/checkout@v2 | ||
|
||
# Due to the insufficient memory allocated by default, CodeQL sometimes requires more to be manually allocated | ||
- name: Configure Pagefile | ||
id: config_pagefile | ||
uses: al-cheb/configure-pagefile-action@v1.2 | ||
with: | ||
minimum-size: 8GB | ||
maximum-size: 32GB | ||
disk-root: "D:" | ||
|
||
- name: Setup MSBuild | ||
id: setup_msbuild | ||
uses: microsoft/setup-msbuild@v1 | ||
|
||
- name: Restore application | ||
id: restore_app | ||
shell: pwsh | ||
run: | | ||
msbuild $env:SOLUTION_NAME ` | ||
/t:Restore ` | ||
/p:Configuration=$env:CONFIGURATION | ||
- name: Initialize CodeQL | ||
id: init_codeql | ||
uses: github/codeql-action/init@v1 | ||
with: | ||
queries: security-and-quality | ||
|
||
- name: Build application | ||
id: build_app | ||
shell: pwsh | ||
run: | | ||
msbuild $env:PROJECT_PATH ` | ||
/p:Platform=$env:PLATFORM ` | ||
/p:Configuration=$env:CONFIGURATION ` | ||
/p:UapAppxPackageBuildMode=$env:APPX_PACKAGE_BUILD_MODE ` | ||
/p:AppxBundle=$env:APPX_BUNDLE ` | ||
/p:AppxPackageSigningEnabled=false ` | ||
/p:AppxBundlePlatforms="$env:APPX_BUNDLE_PLATFORMS" | ||
env: | ||
PLATFORM: x64 | ||
APPX_PACKAGE_BUILD_MODE: StoreUpload | ||
APPX_BUNDLE: Always | ||
APPX_BUNDLE_PLATFORMS: x64 | ||
|
||
- name: Perform CodeQL Analysis | ||
id: analyze_codeql | ||
uses: github/codeql-action/analyze@v1 | ||
|
||
# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
name: Code scanning alerts bulk dismissal | ||
|
||
on: [workflow_dispatch] | ||
|
||
jobs: | ||
dismiss-alerts: | ||
name: Dismiss alerts | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
ALERT_DESC: ['"Calls to unmanaged code"', '"Unmanaged code"'] | ||
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: ${{ secrets.DISMISS_REASON_VAR }} # "false positive", "won't fix" or "used in tests". | ||
ALERTS_PER_PAGE: 100 | ||
ALERT_DESCRIPTION: ${{ matrix.ALERT_DESC }} | ||
steps: | ||
- name: Install jq | ||
id: install_jq | ||
uses: r26d/jq-action@master | ||
with: | ||
cmd: jq -n env | ||
|
||
- name: Run automation | ||
id: run_automation | ||
shell: bash | ||
run: | | ||
page=1 | ||
LIST_OF_ALERTS=$(curl -u $OWNER:$ACCESS_TOKEN -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/$OWNER/$PROJECT_NAME/code-scanning/alerts?state=open&page=$page&per_page=$ALERTS_PER_PAGE"| jq .[].number ) | ||
while [ -n "$LIST_OF_ALERTS" ] | ||
do | ||
echo -n $LIST_OF_ALERTS" " >> "data.json" | ||
((page=page+1)) | ||
LIST_OF_ALERTS=$(curl -u $OWNER:$ACCESS_TOKEN -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/$OWNER/$PROJECT_NAME/code-scanning/alerts?state=open&page=$page&per_page=$ALERTS_PER_PAGE"| jq .[].number ) | ||
done | ||
LIST_OF_INDEXES=$(cat data.json) | ||
for index in $LIST_OF_INDEXES | ||
do | ||
ALERT_DESC=$(curl -u $OWNER:$ACCESS_TOKEN -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/$OWNER/$PROJECT_NAME/code-scanning/alerts/$index" | jq .rule.description) | ||
if [ "$ALERT_DESC" == "$ALERT_DESCRIPTION" ]; then | ||
ALERT_URL="https://api.github.com/repos/$OWNER/$PROJECT_NAME/code-scanning/alerts/$index" | ||
curl -u $OWNER:$ACCESS_TOKEN -X PATCH -H "Accept: application/vnd.github.v3+json" $ALERT_URL -d '{"state":"dismissed","dismissed_reason":"'"$DISMISS_REASON"'"}' | ||
fi | ||
done | ||
rm -f data.json | ||
# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,259 @@ | ||
name: Notepads CI/CD Pipeline | ||
|
||
on: [push, pull_request, workflow_dispatch] | ||
|
||
jobs: | ||
ci: | ||
runs-on: windows-latest | ||
strategy: | ||
matrix: | ||
configuration: [ Production ] | ||
outputs: | ||
new_version: ${{ steps.get_assembly_version.outputs.version_num }} | ||
new_version_tag: ${{ steps.get_assembly_version.outputs.version_tag }} | ||
latest_tag: ${{ steps.get_latest_tag.outputs.tag }} | ||
is_push_to_master: ${{ steps.step_conditionals_handler.outputs.is_push_to_master }} | ||
env: | ||
SOLUTION_NAME: src\Notepads.sln | ||
CONFIGURATION: ${{ matrix.configuration }} | ||
steps: | ||
- name: Steps' conditionals handler | ||
id: step_conditionals_handler | ||
shell: pwsh | ||
run: | | ||
$IS_PUSH_TO_MASTER = 'false' | ||
$IS_NOT_PR = 'true' | ||
if ( ($env:GITHUB_EVENT_NAME -ceq 'push') -and ($env:GITHUB_REF -ceq 'refs/heads/master') ) { | ||
$IS_PUSH_TO_MASTER = 'true' | ||
} | ||
if ( $env:GITHUB_EVENT_NAME -ceq 'pull_request' ) { | ||
$IS_NOT_PR = 'false' | ||
} | ||
echo "::set-output name=is_push_to_master::$(echo $IS_PUSH_TO_MASTER)" | ||
echo "::set-output name=is_not_pr::$(echo $IS_NOT_PR)" | ||
env: | ||
GITHUB_EVENT_NAME: ${{ github.event_name }} | ||
GITHUB_REF: ${{ github.ref }} | ||
BUILD_CONFIGURATION: ${{ matrix.configuration }} | ||
|
||
- if: steps.step_conditionals_handler.outputs.is_not_pr == 'true' | ||
name: Set up JDK 11 | ||
id: Setup_JDK | ||
uses: actions/setup-java@v1 | ||
with: | ||
java-version: 1.11 | ||
|
||
- name: Install .NET Core | ||
id: install_dotnet_dependencies | ||
uses: actions/setup-dotnet@v1 | ||
|
||
- name: Setup MSBuild | ||
id: setup_msbuild | ||
uses: microsoft/setup-msbuild@v1 | ||
|
||
- name: Checkout repository | ||
id: checkout_repo | ||
uses: actions/checkout@v2 | ||
with: | ||
fetch-depth: 50 | ||
token: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- if: steps.step_conditionals_handler.outputs.is_push_to_master == 'true' | ||
name: Get assembly version from appxmanifest | ||
id: get_assembly_version | ||
shell: pwsh | ||
run: | | ||
cd src/Notepads/ | ||
$xml = [xml](Get-Content Package.appxmanifest) | ||
$ASSEMBLY_VERSION_NUMBER = $xml.Package.Identity | Select -ExpandProperty Version | ||
echo "::set-output name=version_num::$(echo $ASSEMBLY_VERSION_NUMBER)" | ||
echo "::set-output name=version_tag::$(echo v"$ASSEMBLY_VERSION_NUMBER")" | ||
- if: steps.step_conditionals_handler.outputs.is_push_to_master == 'true' | ||
name: Get latest tag | ||
id: get_latest_tag | ||
shell: pwsh | ||
run: | | ||
$LATEST_TAG = git -c 'versionsort.suffix=-' ls-remote --exit-code --refs --sort='version:refname' --tags "https://github.com/$env:GIT_URL.git" '*.*.*' | tail --lines=1 | cut --delimiter='/' --fields=3 | ||
echo "::set-output name=tag::$(echo $LATEST_TAG)" | ||
env: | ||
GIT_URL: ${{ github.repository }} | ||
|
||
- if: steps.step_conditionals_handler.outputs.is_push_to_master == 'true' && steps.get_assembly_version.outputs.version_tag != steps.get_latest_tag.outputs.tag | ||
name: Add new tag to repo | ||
id: add_new_tag_to_repo | ||
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 | ||
env: | ||
GIT_USER_NAME: ${{ secrets.GIT_USER_NAME }} | ||
GIT_USER_EMAIL: ${{ secrets.GIT_USER_EMAIL }} | ||
NEW_VERSION_TAG: ${{ steps.get_assembly_version.outputs.version_tag }} | ||
|
||
- if: steps.step_conditionals_handler.outputs.is_not_pr == 'true' | ||
name: Cache SonarCloud packages | ||
id: cache_sonar_packages | ||
uses: actions/cache@v2 | ||
with: | ||
path: ~\sonar\cache | ||
key: ${{ runner.os }}-sonar | ||
restore-keys: ${{ runner.os }}-sonar | ||
|
||
- if: steps.step_conditionals_handler.outputs.is_not_pr == 'true' | ||
name: Cache SonarCloud scanner | ||
id: cache_sonar_scanner | ||
uses: actions/cache@v2 | ||
with: | ||
path: .\.sonar\scanner | ||
key: ${{ runner.os }}-sonar-scanner | ||
restore-keys: ${{ runner.os }}-sonar-scanner | ||
|
||
- if: steps.step_conditionals_handler.outputs.is_not_pr == 'true' && 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: steps.step_conditionals_handler.outputs.is_not_pr == 'true' | ||
name: Lowercase string generator | ||
id: lowercase_string_gen | ||
shell: pwsh | ||
run: | | ||
$LOWERCASE_OWNER = "${{ github.repository_owner }}".ToLower() | ||
echo "::set-output name=owner_name::$LOWERCASE_OWNER" | ||
- if: steps.step_conditionals_handler.outputs.is_not_pr == 'true' | ||
name: Initialize SonarCloud scanner | ||
id: init_sonar_scanner | ||
shell: pwsh | ||
run: | | ||
.\.sonar\scanner\dotnet-sonarscanner begin ` | ||
/k:"${{ github.repository_owner }}_${{ github.event.repository.name }}" ` | ||
/o:"${{ steps.lowercase_string_gen.outputs.owner_name }}" ` | ||
/d:sonar.login="$env:SONAR_TOKEN" ` | ||
/d:sonar.host.url="https://sonarcloud.io" | ||
env: | ||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | ||
|
||
- if: steps.step_conditionals_handler.outputs.is_push_to_master == 'true' | ||
name: Create PFX certificate for AppxBundle | ||
id: create_pfx_cert | ||
shell: pwsh | ||
run: | | ||
$BASE64_STR = $env:BASE64_STR | ||
$TARGET_FILE = "$env:DEFAULT_DIR\cert.pfx" | ||
$FROM_BASE64_STR = [Convert]::FromBase64String($BASE64_STR) | ||
[IO.File]::WriteAllBytes($TARGET_FILE, $FROM_BASE64_STR) | ||
env: | ||
BASE64_STR: ${{ secrets.PACKAGE_CERTIFICATE_BASE64 }} | ||
DEFAULT_DIR: ${{ github.workspace }} | ||
|
||
- name: Restore the application | ||
id: restore_application | ||
shell: pwsh | ||
run: | | ||
msbuild $env:SOLUTION_NAME ` | ||
/t:Restore ` | ||
/p:Configuration=$env:CONFIGURATION | ||
- 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: ${{ steps.step_conditionals_handler.outputs.is_push_to_master }} | ||
APPX_BUNDLE_PLATFORMS: x86|x64|ARM64 | ||
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: steps.step_conditionals_handler.outputs.is_not_pr == 'true' | ||
name: Send SonarCloud results | ||
id: send_sonar_results | ||
shell: pwsh | ||
run: | | ||
.\.sonar\scanner\dotnet-sonarscanner end ` | ||
/d:sonar.login="${{ secrets.SONAR_TOKEN }}" | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.SONAR_GITHUB_TOKEN }} | ||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | ||
|
||
- if: steps.step_conditionals_handler.outputs.is_push_to_master == 'true' | ||
name: Upload build artifacts | ||
id: upload_artifacts | ||
uses: actions/upload-artifact@v1 | ||
with: | ||
name: Build artifacts | ||
path: Artifacts/ | ||
|
||
cd: | ||
# "This job will execute when the workflow is triggered on a 'push event', the target branch is 'master' and the commit is intended to be a release." | ||
if: needs.ci.outputs.is_push_to_master == 'true' && needs.ci.outputs.new_version_tag != needs.ci.outputs.latest_tag | ||
needs: ci | ||
runs-on: windows-latest | ||
env: | ||
NEW_VERSION: ${{ needs.ci.outputs.new_version }} | ||
NEW_TAG: ${{ needs.ci.outputs.new_version_tag }} | ||
steps: | ||
- name: Checkout repository | ||
id: checkout_repo | ||
uses: actions/checkout@v2 | ||
|
||
- name: Download and extract MSIX package | ||
id: dl_package_artifact | ||
uses: actions/download-artifact@v2 | ||
with: | ||
name: Build artifacts | ||
path: Artifacts/ | ||
|
||
- name: Create and publish release | ||
id: create_release | ||
uses: actions/create-release@v1 | ||
with: | ||
tag_name: ${{ env.NEW_TAG }} | ||
release_name: Notepads ${{ env.NEW_TAG }} | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Upload msixbundle as release asset | ||
id: upload_notepads_zip | ||
uses: actions/upload-release-asset@v1 | ||
with: | ||
upload_url: ${{ steps.create_release.outputs.upload_url }} | ||
asset_path: Artifacts/Notepads_${{ env.NEW_VERSION }}_Production_Test/Notepads_${{ env.NEW_VERSION }}_x86_x64_ARM64_Production.msixbundle | ||
asset_name: Notepads_${{ env.NEW_VERSION }}_x86_x64_ARM64.msixbundle | ||
asset_content_type: application/zip | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- 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) |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,334 @@ | ||
# 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 | ||
|
||
 | ||
|
||
- [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 | ||
|
||
<br> | ||
|
||
## 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. Once the workflow run has completed successfully, move on to the next step of the documentation | ||
|
||
NOTE: **screenshots are only exemplary** | ||
|
||
<br> | ||
|
||
## 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 | ||
|
||
 | ||
|
||
 | ||
|
||
### 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. | ||
|
||
 | ||
|
||
 | ||
|
||
- By enabling "Dependabot security updates", you authorize Dependabot to create PRs specifically for **security updates** | ||
|
||
 | ||
|
||
### 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 | ||
|
||
 | ||
|
||
NOTE: **screenshots are only exemplary** | ||
|
||
<br> | ||
|
||
## 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**: | ||
|
||
 | ||
|
||
- on the page of each result, you can see an explanation of what the problem is and also one or more solutions: | ||
|
||
 | ||
|
||
### 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 would be able to that with one click. | ||
|
||
NOTE: This tool executes manual **only**. It won't execute on any other GitHub event like push commit, PR creation etc. | ||
|
||
#### 1. Setup | ||
|
||
1. In the repository, go to the **Settings** tab -> **Secrets** | ||
|
||
 | ||
|
||
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_ACCESS_TOKEN - [create a PAT with "security_events" permission only](#7-how-to-create-a-pat). | ||
|
||
- DISMISS_REASON_VAR - this secret refers to the reason why you dismissed the code scanning alert. Use the appropriate one as the value of this secret, out of the three available options: **false positive**, **won't fix** or **used in tests**. | ||
|
||
#### 2. Execution | ||
|
||
1. In your repo, click on the Actions tab and on the left, in the Workflows list, click on the "Code scanning alerts bulk dismissal" | ||
|
||
 | ||
|
||
2. On the right, click on the "Run workflow" dropdown. Under "Use workflow from" choose your default branch (usually main/master) and click on the **Run workflow** button | ||
|
||
 | ||
|
||
3. 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 | ||
|
||
 | ||
|
||
 | ||
|
||
 | ||
|
||
NOTE: "closed" refers to "dismissed" alerts | ||
|
||
#### 3. Customization | ||
|
||
The "ALERT_DESC" strategy matrix in the pipeline, allows for more precise filtering of alerts to bulk dismiss. It uses the description of the alert to determine if it has to be dismissed or not. We've added the following alert descriptions by default: | ||
|
||
- "Calls to unmanaged code" | ||
- "Unmanaged code" | ||
|
||
To add more descriptions, follow these steps: | ||
|
||
1. In your source code, open ".github/workflows/csa-bulk-dismissal.yml" | ||
|
||
2. On line 11, notice "ALERT_DESC: ['"Calls to unmanaged code"', '"Unmanaged code"']". This is the array of descriptions that the CSABD (Code scanning alerts bulk dismissal) tool uses to filter through the alerts: | ||
|
||
 | ||
|
||
3. To add more descriptions use comma separation, followed by a single space and the description enclosed in double quotes, then enclosed in single quotes: | ||
|
||
 | ||
|
||
<br> | ||
|
||
## 5. Automated GitHub release | ||
|
||
Automatically bumps up the GitHub tag in the repo, creates a GitHub release with the new tag and attaches the msixbundle to it | ||
|
||
#### Setup | ||
|
||
Add the following secrets by going to the repo **Settings** tab -> **Secrets**: | ||
|
||
1. **GIT_USER_NAME** | ||
|
||
- used to add the identity required for creating a tag | ||
- copy and paste your GitHub username as the value of the secret | ||
|
||
2. **GIT_USER_EMAIL** | ||
|
||
- used to add the identity required for creating a tag | ||
- copy and paste your primary GitHub email as the value of the secret | ||
|
||
3. **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 | ||
|
||
4. **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 | ||
|
||
Follow these steps to trigger the automated GitHub release process: | ||
|
||
1. Bump up the **Package.Identity.Version** in the **Package.appxmanifest** | ||
|
||
2. Make a push commit to your master branch or if you've done the previous change in a feature branch, the merge of the PR to the master branch would act as the push commit | ||
|
||
If the setup was done correctly and there are no errors in the pipeline, when the pipeline successfully completes, there should be a new, properly tagged GitHub release with the msixbundle attached to it. | ||
|
||
NOTE: | ||
|
||
- the tag itself is used as the required description of the newly created tag, which appears here: | ||
|
||
 | ||
|
||
- it is replaced by the release description | ||
|
||
 | ||
|
||
<br> | ||
|
||
## 6. Setup automated publishing to the Windows Store | ||
|
||
#### - for the automation to work, at least one submission needs to be already created manually | ||
|
||
- [Create an Azure AD tenant](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-create-new-tenant) or use an existing one | ||
|
||
- Associate your [Microsoft Partner Center with the Azure AD tenant](https://docs.microsoft.com/en-us/windows/uwp/publish/associate-azure-ad-with-partner-center) | ||
|
||
- [Create a new app registration](https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml) or use an existing one from the list in your **portal.azure.com** -> **Azure Active Directory** -> **App registrations** section | ||
|
||
- Add the [Azure AD application to the Microsoft Partner Center](https://docs.microsoft.com/en-us/partner-center/service-principal) and give it "Manager" permissions | ||
|
||
- In the project's GitHub repo, create the following secrets: | ||
|
||
1. **AZURE_AD_TENANT_ID** and **AZURE_AD_APPLICATION_CLIENT_ID** | ||
|
||
- copy and paste the values shown in the screenshot below to the appropriate secret: | ||
|
||
 | ||
|
||
Note: screenshot is taken from **portal.azure.com** -> **Azure AD** -> **App registrations** -> **app-name** page | ||
|
||
2. **AZURE_AD_APPLICATION_SECRET** | ||
|
||
- copy and paste the value you get on the page following from **Account settings** -> **User management** -> **Azure AD applications** -> click on the added application: | ||
|
||
 | ||
|
||
3. **STORE_APP_ID** | ||
|
||
- copy and paste the highlighted code as the value of this secret: | ||
|
||
 | ||
|
||
- If everything was setup correctly, on your next push commit to the `master` branch with a new `Identity.Version` in the `Package.appxmanifest`, a new submission in the Microsoft Partner Center with the new `*.msixupload` package should appear and be automatically submitted if all verifications pass | ||
|
||
<br> | ||
|
||
## 7. How to create a PAT | ||
|
||
- In a new tab open GitHub, at the top right corner, click on your profile picture and click on **Settings** from the dropdown. | ||
|
||
 | ||
|
||
- Go to Developer Settings -> Personal access tokens. | ||
|
||
 | ||
|
||
 | ||
|
||
- Click the **Generate new token** button and enter password if prompted. | ||
|
||
 | ||
|
||
- Name the token, from the permissions list choose the ones needed and at the bottom click on the **Generate token** button. | ||
|
||
 | ||
|
||
- Copy the token value and paste it wherever its needed | ||
|
||
 | ||
|
||
NOTE: once you close or refresh the page, you won't be able to copy the value of the PAT again! | ||
|
||
# | ||
|
||
Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
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
Large diffs are not rendered by default.
Oops, something went wrong.
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
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