First push
ASP.NET AJAX / build_web_app (push) Waiting to run
Angular / build_angular (push) Waiting to run
ASP.NET Core (with Reporting) / build_windows (push) Waiting to run
Blazor (with Reporting) / build_windows (push) Waiting to run
Blazor (with Reporting) / build_linux (push) Waiting to run
Console (.NET) / build_console (arm64, linux) (push) Waiting to run
Console (.NET) / build_console (arm64, win) (push) Waiting to run
Console (.NET) / build_console (x64, linux) (push) Waiting to run
Console (.NET) / build_console (x64, win) (push) Waiting to run
MAUI / Windows Smoketest (push) Waiting to run
MAUI / Android Smoketest (push) Waiting to run
MAUI / iOS Smoketest (push) Waiting to run
MAUI / MacCatalyst Smoketest (push) Waiting to run
WinForms (.NET Framework) / build_desktop (Release, x64) (push) Waiting to run
WinForms (.NET Framework) / build_desktop (Release, x86) (push) Waiting to run
WinUI3 / build-windows (push) Waiting to run
WPF (.NET Framework) / build_desktop (Release, x64) (push) Waiting to run
WPF (.NET Framework) / build_desktop (Release, x86) (push) Waiting to run
ASP.NET Core (with Reporting) - Docker / Microsoft Base - Publish to Docker Hub (push) Waiting to run
ASP.NET Core (with Reporting) - Docker / CentOS Base - Publish to Docker Hub (push) Waiting to run
Blazor (with Reporting) - Docker / Dockerfile Build and Publish (push) Waiting to run

This commit is contained in:
Lance McCarthy
2026-05-21 15:10:03 -04:00
parent b6c6d21876
commit 82e0ef17e9
307 changed files with 58592 additions and 732 deletions
+19
View File
@@ -0,0 +1,19 @@
# Project Overview
This project contains many different applications, whose purpose is to demonstrate how to build Telerik and Kendo applications on CI/CD platforms like GitHub Actions, Azure DevOps, and others. The repo has a variety of project types, from WPF to .NET MAUI that has an accompanying build example in the respective CI YAML file.
## Folder Structure
- `/src`: Contains the project folders, each project folder contains one example of a Telerik product being used in that platform. The folder names are describing which framework/platform that the demo is for. For example, the 'WPF' folder contains a WPF project that uses Telerik UI for WPF... and the 'Blazor' folder contains a project for ASP.NET Blazor that is using Telerik UI for Blazor. That same pattern is followed for all the subfolders of the 'src' folder
## Libraries and Frameworks
- .NET and C# for the WPF, WinForms, ASP.NET, Blazor, MAUI, WinUI and AJAX demos
- Angular and Node.js for the Angular project
- GitHub Actions flavor YAML for the workflows in '/.github/workflows'
- Azure DevOps flavored YAML for the 'azure-pipelines.yaml' file
## CI Examples
- `/.github/workflows`: Contains all the GitHub Actions examples
- `azure-pipelines.yaml`: Contains all the Azure DevOps examples
+16
View File
@@ -0,0 +1,16 @@
# Combines all dependency updates into a single PR
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "npm"
directory: "/src/Kendo/angular_demo"
schedule:
interval: "monthly"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]
groups:
dependencies:
applies-to: version-updates
patterns:
- "*"
@@ -0,0 +1,112 @@
---
name: configure-telerik-nuget
version: 1.1.0
description: Helps setup, configure and manage Telerik NuGet feeds in your repo's nuget.config file.
required_binaries:
- pwsh
- dotnet
author: Lance McCarthy
homepage: https://github.com/DevOpsExamples/.github
source: https://github.com/DevOpsExamples/.github/tree/main/skills/configure-telerik-nuget
---
Use these helper functions to manage Telerik NuGet source configuration.
Get a new api key at https://www.telerik.com/account/downloads/api-keys
## Function: configure
Sets a global user environment variable for the Telerik API key and updates/creates `nuget.config` so credentials use the environment variable instead of a hardcoded secret.
```powershell
function Configure-TelerikNuGetSource {
param(
[Parameter(Mandatory = $true)]
[string]$ApiKey,
[string]$ApiKeyEnvVarName = "TELERIK_NUGET_API_KEY",
[string]$SourceName = "Telerik_NuGet_Server",
[string]$SourceUrl = "https://nuget.telerik.com/v3/index.json",
[string]$NuGetConfigPath = "./nuget.config"
)
# Store API key as a user-level environment variable so it is available globally
[Environment]::SetEnvironmentVariable($ApiKeyEnvVarName, $ApiKey, "User")
$env:$ApiKeyEnvVarName = $ApiKey
if (-not (Test-Path $NuGetConfigPath)) {
@"
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources />
<packageSourceCredentials />
</configuration>
"@ | Set-Content -Path $NuGetConfigPath -Encoding UTF8
}
[xml]$nugetConfig = Get-Content -Path $NuGetConfigPath
if (-not $nugetConfig.configuration.packageSources) {
$packageSourcesNode = $nugetConfig.CreateElement("packageSources")
$nugetConfig.configuration.AppendChild($packageSourcesNode) | Out-Null
}
if (-not $nugetConfig.configuration.packageSourceCredentials) {
$credentialsNode = $nugetConfig.CreateElement("packageSourceCredentials")
$nugetConfig.configuration.AppendChild($credentialsNode) | Out-Null
}
$existingSource = $nugetConfig.configuration.packageSources.add | Where-Object {
$_.key -eq $SourceName
}
if ($existingSource) {
$existingSource.value = $SourceUrl
} else {
$sourceNode = $nugetConfig.CreateElement("add")
$sourceNode.SetAttribute("key", $SourceName)
$sourceNode.SetAttribute("value", $SourceUrl)
$nugetConfig.configuration.packageSources.AppendChild($sourceNode) | Out-Null
}
$existingCredentialNode = $nugetConfig.configuration.packageSourceCredentials.$SourceName
if (-not $existingCredentialNode) {
$existingCredentialNode = $nugetConfig.CreateElement($SourceName)
$nugetConfig.configuration.packageSourceCredentials.AppendChild($existingCredentialNode) | Out-Null
}
$existingCredentialNode.RemoveAll()
$usernameNode = $nugetConfig.CreateElement("add")
$usernameNode.SetAttribute("key", "Username")
$usernameNode.SetAttribute("value", "api-key")
$existingCredentialNode.AppendChild($usernameNode) | Out-Null
$passwordNode = $nugetConfig.CreateElement("add")
$passwordNode.SetAttribute("key", "ClearTextPassword")
$passwordNode.SetAttribute("value", "%$ApiKeyEnvVarName%")
$existingCredentialNode.AppendChild($passwordNode) | Out-Null
$nugetConfig.Save((Resolve-Path $NuGetConfigPath))
Write-Host "Configured Telerik feed '$SourceName' in '$NuGetConfigPath'."
Write-Host "Saved API key in user environment variable '$ApiKeyEnvVarName'."
Write-Host "Restart your terminal/IDE so new processes can read the updated environment variable."
}
```
Example usage:
```powershell
Configure-TelerikNuGetSource -ApiKey "<telerik-api-key>"
```
## Changelog
### 1.1.0 - 2026-03-20
- Added skill package metadata: `required_binaries`, `author`, `homepage`, and `source`.
- Updated configure function to save API key as a user-level environment variable.
- Updated `nuget.config` credential handling to use environment variable expansion instead of hardcoded API key values.
- Added `version` field to frontmatter.
@@ -0,0 +1,17 @@
---
name: github-actions-failure-debugging
description: Guide for debugging failing GitHub Actions workflows. Use this when asked to debug failing GitHub Actions workflows.
version: 1.0.0
author: Lance McCarthy
homepage: https://github.com/DevOpsExamples/.github
source: https://github.com/DevOpsExamples/.github/tree/main/skills/github-actions-failure-debugging
---
To debug failing GitHub Actions workflows in a pull request, follow this process, using tools provided from the GitHub MCP Server:
1. Use the `list_workflow_runs` tool to look up recent workflow runs for the pull request and their status
2. Use the `summarize_job_log_failures` tool to get an AI summary of the logs for failed jobs, to understand what went wrong without filling your context windows with thousands of lines of logs
3. If you still need more information, use the `get_job_logs` or `get_workflow_run_logs` tool to get the full, detailed failure logs
4. Try to reproduce the failure yourself in your own environment.
5. Fix the failing build. If you were able to reproduce the failure yourself, make sure it is fixed before committing your changes.ng to use environment variable expansion instead of hardcoded API key values.
- Added `version` field to frontmatter.
+45
View File
@@ -0,0 +1,45 @@
# This example shows you how you can use the named environment variables in the nuget.config file to set the credentials, for more information see https://github.com/LanceMcCarthy/DevOpsExamples#github-actions-using-secrets-to-set-environment-variables
name: ASP.NET AJAX
on:
workflow_dispatch:
push:
branches:
- main
- "ajax/*"
paths:
- 'src/Ajax/**/*'
- '.github/workflows/main_build-ajax.yml'
env:
CSPROJ_PATH: "src/Ajax/MySite.sln"
NUGETCONFIG_PATH: "src/NuGet.Config"
jobs:
build_web_app:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Nuget.exe
uses: nuget/setup-nuget@v2
with:
nuget-version: '5.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2
# Important: We are using nuget CLI (not dotnet CLI) See https://docs.microsoft.com/en-us/nuget/reference/nuget-exe-cli-reference
- name: NuGet.exe Restore
run: nuget restore ${{env.CSPROJ_PATH}} -ConfigFile ${{env.NUGETCONFIG_PATH}}
env:
TELERIK_USERNAME: "api-key" # Variable name used in the nuget.config file
TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}} # Variable name used in the nuget.config file
- name: Build the AJAX application
run: msbuild ${{env.CSPROJ_PATH}} /t:Restore /p:Configuration=Release /p:RuntimeIdentifier=any
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
+37
View File
@@ -0,0 +1,37 @@
name: Angular
on:
workflow_dispatch:
push:
branches:
- main
- "angular/*"
paths:
- 'src/Kendo/angular_demo/**/*'
- '.github/workflows/main_build-angular.yml'
jobs:
build_angular:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build App
working-directory: src/Kendo/angular_demo
run: |
# 1. Clean the angular cache, to avoid using any expired keys
rm -rf .angular/cache
# 2. Install your project dependencies
npm install
# npm install --save @progress/kendo-licensing; # if missing from package.json
#3. Activate
npx kendo-ui-license activate
#4. Build the project
npm run build --prod
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
@@ -0,0 +1,56 @@
name: ASP.NET Core (with Reporting)
on:
workflow_dispatch:
push:
branches:
- main
- "aspnetcore/*"
paths:
- 'src/AspNetCore/**/*'
- '.github/workflows/main_build-aspnetcore.yml'
permissions:
id-token: write # JWT for Akeyless and Azure auth
env:
DOTNET_VERSION: "10.0.x"
PROJECT_PATH: src/AspNetCore/MyAspNetCoreApp/MyAspNetCoreApp.csproj
NUGETCONFIG_PATH: src/NuGet.Config
jobs:
build_windows:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
# Using AKeyless for secrets in this demo, but you can use github Secrets instead
- name: Fetch secrets from AKeyless
id: akeyless
uses: LanceMcCarthy/akeyless-action@v5
with:
access-id: 'p-4blpeo5zdfeaom'
static-secrets: |
{
"/progress/TELERIK_NUGET_KEY":"TELERIK_NUGET_KEY",
"/progress/TELERIK_LICENSE":"TELERIK_LICENSE_KEY"
}
export-secrets-to-outputs: true
export-secrets-to-environment: false
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.DOTNET_VERSION}}
- name: Restore NuGet Packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
env:
TELERIK_USERNAME: "api-key"
TELERIK_PASSWORD: ${{steps.akeyless.outputs.TELERIK_NUGET_KEY}}
- name: Build ASP.NET Core Project
run: dotnet build ${{env.PROJECT_PATH}} -c Debug --no-restore
env:
TELERIK_LICENSE: ${{steps.akeyless.outputs.TELERIK_LICENSE_KEY}}
@@ -0,0 +1,65 @@
name: ASP.NET Core (with Reporting) - Azure
on:
workflow_dispatch:
env:
DOTNET_VERSION: "10.0.x"
PROJECT_PATH: src/AspNetCore/MyAspNetCoreApp/MyAspNetCoreApp.csproj
NUGETCONFIG_PATH: src/NuGet.Config
AZURE_WEBAPP_NAME: DevOpsExamplesCore
ARTIFACTS_DIRECTORY: "src/AspNetCore/MyAspNetCoreApp/artifacts"
TELERIK_USERNAME: "api-key"
TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}}
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
jobs:
build_windows:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
# ----------------------------------------------------------------------- #
# BUILD
# ----------------------------------------------------------------------- #
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.DOTNET_VERSION}}
- name: Restore NuGet Packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
- name: Build ASP.NET Core Project
run: dotnet publish ${{env.PROJECT_PATH}} -o ${{env.ARTIFACTS_DIRECTORY}} --no-restore
# ----------------------------------------------------------------------- #
# DEPLOY to https://devopsexamplescore.azurewebsites.net/
# ----------------------------------------------------------------------- #
# Option 1
# Deploy using the publish profile saved as a Github Actions secret
- name: Deploy to Azure WebApp
uses: azure/webapps-deploy@v3
with:
app-name: ${{env.AZURE_WEBAPP_NAME}}
publish-profile: ${{secrets.DEVOPSEXAMPLESCORE_PUBLISHSETTINGS}}
package: ${{env.ARTIFACTS_DIRECTORY}}
# Option 2
# Use a service principal to log into Azure, no reliance on GitHub secrets
# - name: Azure Login
# uses: azure/login@v2
# with:
# client-id: ${{secrets.AZUREAPPSERVICE_CLIENTID}}
# tenant-id: ${{secrets.AZUREAPPSERVICE_TENANTID}}
# subscription-id: ${{secrets.AZUREAPPSERVICE_SUBSCRIPTIONID}}
# - name: Deploy to Azure WebApp
# uses: azure/webapps-deploy@v3
# with:
# app-name: ${{env.AZURE_WEBAPP_NAME}}
# package: ${{env.ARTIFACTS_DIRECTORY}}
+108
View File
@@ -0,0 +1,108 @@
# This workflow has three examples (one for IIS, two using containers)
# [Option A] Typical IIS build & publish
# [Option B] DOCKER FILE BUILD - publishes the image to ghcr.io (GitHub container registry)
# [Option C] .NET SDK CONTAINER BUILD - publishes the image to Docker Hub
name: Blazor (with Reporting)
on:
workflow_dispatch:
push:
branches:
- main
- "blazor/*"
paths:
- 'src/Blazor/**/*'
- '.github/workflows/main_build-blazor.yml'
permissions:
contents: read
packages: write # to publish to GitHub container registry
id-token: write # # JWT for Akeyless auth
env:
CONFIGURATION: Release
BLAZOR_PROJ_PATH: src/Blazor/MyBlazorApp/MyBlazorApp.csproj
TEST_PROJ_PATH: src/Blazor/MyBlazorApp.Tests/MyBlazorApp.Tests.csproj
NUGET_CONFIG_PATH: src/NuGet.Config
DOTNET_VERSION: "10.0.x"
jobs:
build_windows:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# Using AKeyless for secrets in this demo
- name: Fetch secrets from AKeyless
id: akeyless
uses: LanceMcCarthy/akeyless-action@v5
with:
access-id: 'p-4blpeo5zdfeaom'
static-secrets: |
{
"/progress/TELERIK_NUGET_KEY":"TELERIK_NUGET_KEY",
"/progress/TELERIK_LICENSE":"TELERIK_LICENSE_KEY"
}
export-secrets-to-outputs: true
export-secrets-to-environment: false
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{env.DOTNET_VERSION}}
- name: Restore NuGet Packages
run: |
dotnet restore ${{env.BLAZOR_PROJ_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
dotnet restore ${{env.TEST_PROJ_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
env:
TELERIK_USERNAME: "api-key"
TELERIK_PASSWORD: ${{steps.akeyless.outputs.TELERIK_NUGET_KEY}}
- name: Build Test Project
run: dotnet build ${{env.TEST_PROJ_PATH}} -c ${{env.CONFIGURATION}} --no-restore
env:
TELERIK_LICENSE: ${{steps.akeyless.outputs.TELERIK_LICENSE_KEY}}
build_linux:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# Using AKeyless for secrets in this demo
- name: Fetch secrets from AKeyless
id: akeyless
uses: LanceMcCarthy/akeyless-action@v5
with:
access-id: 'p-4blpeo5zdfeaom'
static-secrets: |
{
"/progress/TELERIK_NUGET_KEY":"TELERIK_NUGET_KEY",
"/progress/TELERIK_LICENSE":"TELERIK_LICENSE_KEY"
}
export-secrets-to-outputs: true
export-secrets-to-environment: false
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{env.DOTNET_VERSION}}
- name: Restore NuGet Packages
run: |
dotnet restore ${{env.BLAZOR_PROJ_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
dotnet restore ${{env.TEST_PROJ_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
env:
TELERIK_USERNAME: "api-key"
TELERIK_PASSWORD: ${{steps.akeyless.outputs.TELERIK_NUGET_KEY}}
- name: Build Test Project
run: dotnet build ${{env.TEST_PROJ_PATH}} -c ${{env.CONFIGURATION}} --no-restore
env:
TELERIK_LICENSE: ${{steps.akeyless.outputs.TELERIK_LICENSE_KEY}}
+50
View File
@@ -0,0 +1,50 @@
# This example shows you how ot use Akeyless to fetch the required secrets instead of GitHub Actions secrets
name: Console (.NET)
on:
workflow_dispatch:
push:
branches:
- main
- "console/*"
paths:
- 'src/Console/**/*'
- '.github/workflows/main_build-console.yml'
defaults:
run:
shell: pwsh
env:
CSPROJ_PATH: src/Console/MyDocProcApp/MyDocProcApp.csproj
NUGETCONFIG_PATH: src/NuGet.Config
BUILD_CONFIGURATION: Release
jobs:
build_console:
runs-on: ubuntu-22.04
strategy:
matrix:
os: [linux, win]
config: [x64, arm64]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Update NuGet Package Sources
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u "api-key" -p ${{secrets.TELERIK_NUGET_KEY}} --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.CSPROJ_PATH}} --configfile ${{env.NUGETCONFIG_PATH}} --runtime ${{matrix.os}}-${{matrix.config}}
- name: Build project
run: dotnet build ${{env.CSPROJ_PATH}} --configuration ${{env.BUILD_CONFIGURATION}} --runtime ${{matrix.os}}-${{matrix.config}} --no-self-contained --no-restore
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
@@ -0,0 +1,119 @@
name: Console (.NET) - Trusted Signing
on:
workflow_dispatch:
defaults:
run:
shell: pwsh
permissions:
id-token: write # For OIDC auth
env:
CSPROJ_PATH: src/Console/MyDocProcApp/MyDocProcApp.csproj
NUGETCONFIG_PATH: src/NuGet.Config
BUILD_CONFIGURATION: Release
jobs:
build_console:
runs-on: ubuntu-22.04
strategy:
matrix:
os: [linux, win]
config: [x64, arm64]
env:
OUTPUT_DIR: ${{github.workspace}}/output/${{matrix.os}}-${{matrix.config}}/
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Update NuGet Package Sources
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u "api-key" -p ${{secrets.TELERIK_NUGET_KEY}} --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.CSPROJ_PATH}} --configfile ${{env.NUGETCONFIG_PATH}} --runtime ${{matrix.os}}-${{matrix.config}}
- name: Build project
run: dotnet publish ${{env.CSPROJ_PATH}} --configuration ${{env.BUILD_CONFIGURATION}} --runtime ${{matrix.os}}-${{matrix.config}} --no-self-contained --no-restore --output ${{env.OUTPUT_DIR}}
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
- name: Attach artifacts
id: upload-artifact
uses: actions/upload-artifact@v4
with:
name: "Console_${{matrix.os}}-${{matrix.config}}"
path: ${{env.OUTPUT_DIR}}
if-no-files-found: error
retention-days: 30
codesign_release:
name: Codesign Release
if: ${{ success() }}
runs-on: windows-latest
needs: [build_console]
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ${{github.workspace}}/artifacts/
# No secrets needed, uses the GitHub OIDC token to authenticate.
- name: Azure login using OIDC via GitHub
uses: azure/login@v2
id: azlogin
with:
client-id: "32daa13b-f4bb-4809-8ef6-58cb39051acd"
tenant-id: "bd47e796-3473-4b8a-9101-1f4c0c7af31a"
subscription-id: "48ab4839-62af-4ab3-afe6-043ea4d7c137"
# Codesign files with Azure Trusted Signing
- name: Sign files with Trusted Signing
uses: azure/trusted-signing-action@v0.4.0
with:
endpoint: https://eus.codesigning.azure.net/
trusted-signing-account-name: PrimaryCodeSign
certificate-profile-name: lancemccarthylivepublic
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
file-digest: SHA256
files-folder: ${{github.workspace}}/artifacts/
files-folder-filter: exe
files-folder-depth: 3
exclude-azure-cli-credential: false
exclude-environment-credential: true
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-powershell-credential: true
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
- name: Attach signed artifact
id: upload-artifact
uses: actions/upload-artifact@v4
with:
name: "Console_Codesigned"
path: ${{github.workspace}}/artifacts/
if-no-files-found: error
retention-days: 30
- name: Delete unsigned artifacts from run
uses: geekyeggo/delete-artifact@v5
with:
name: |
Console_linux-x64
Console_linux-arm64
Console_win-x64
Console_win-arm64
failOnError: false
@@ -0,0 +1,50 @@
name: Console (.NET) - Artifactory Only
on:
workflow_dispatch:
defaults:
run:
shell: pwsh
jobs:
build_console:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Build Console App w/only Artifactory Source
run: |
# 1. Remove all existing package sources
$sourceList = dotnet nuget list source
$sourceNames = @()
foreach ($line in $sourceList) {
if ($line -match '^\s*\d+\.\s+(.*)\s+\[Enabled\]') {
$sourceNames += $matches[1].Trim()
}
}
foreach ($name in $sourceNames) {
dotnet nuget remove source "$name"
Write-Host "Removed $name ..."
}
# 2. Add Artifactory source with credentials from GitHub Secrets
dotnet nuget add source "https://bed-artifactory.bedford.progress.com/artifactory/api/nuget/v3/dt-nuget-virtual-tierpoint/index.json" `
--name "artifactory_virtual" `
--username ${{secrets.ARTIFACTORY_NUGET_USERNAME}} `
--password ${{secrets.ARTIFACTORY_NUGET_PASSWORD}} `
--store-password-in-clear-text
# 3. List sources to verify
dotnet nuget list source
# 4. Build the project
dotnet build "src/Console/MyDocProcApp/MyDocProcApp.csproj"
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
+137
View File
@@ -0,0 +1,137 @@
name: MAUI
on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'src/MAUI/**/*'
- '.github/workflows/main_build-maui.yml'
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
PROJECT_PATH: "src/MAUI/MauiDemo.csproj"
NUGETCONFIG_PATH: "src/NuGet.Config"
DOTNET_VERSION: "10.0.x"
MAC_DOTNET_VERSION: "10.0.203" # Used to avoid Xcode and MAUI workload problems
TFM_VERSION: "net10.0"
XCODE_VERSION: '26.4'
BUILD_CONFIG: "Debug"
jobs:
maui-windows:
name: Windows Smoketest
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.DOTNET_VERSION}}
# Only needed for WinUI builds
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v3
- name: Install MAUI workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
- name: Build Maui WinUI project
run: dotnet build ${{env.PROJECT_PATH}} -c ${{env.BUILD_CONFIG}} -f "${{env.TFM_VERSION}}-windows10.0.22621.0" --no-restore
maui-android:
name: Android Smoketest
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.DOTNET_VERSION}}
- uses: actions/setup-java@v4
with:
distribution: 'microsoft'
java-version: '11'
- name: Install MAUI workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
- name: Build Maui Android project
run: dotnet build ${{env.PROJECT_PATH}} -c ${{env.BUILD_CONFIG}} -f "${{env.TFM_VERSION}}-android" --no-restore
maui-ios:
name: iOS Smoketest
runs-on: macos-26
steps:
- name: Checkout
uses: actions/checkout@v6
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{env.XCODE_VERSION}}
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.MAC_DOTNET_VERSION}}
- name: Install MAUI workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
- name: Build MAUI iOS project
run: dotnet build ${{env.PROJECT_PATH}} -c ${{env.BUILD_CONFIG}} -f "${{env.TFM_VERSION}}-ios" --no-restore -p:PublishTrimmed=True -p:MtouchLink=SdkOnly
maui-macos:
name: MacCatalyst Smoketest
runs-on: macos-26
steps:
- name: Checkout
uses: actions/checkout@v6
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{env.XCODE_VERSION}}
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.MAC_DOTNET_VERSION}}
- name: Install MAUI workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
- name: Build MAUI MacCatalyst project
run: dotnet build ${{env.PROJECT_PATH}} -c ${{env.BUILD_CONFIG}} -f "${{env.TFM_VERSION}}-maccatalyst" --no-restore -p:PublishTrimmed=True -p:MtouchLink=SdkOnly
@@ -0,0 +1,771 @@
name: MAUI (Distribution)
on:
workflow_dispatch:
defaults:
run:
shell: pwsh
permissions:
actions: write # Needed to delete artifacts after bundle upload
contents: write # Needed to create GitHub Releases
id-token: write # Needed for Azure auth (OIDC)
env:
# ____Global____________________________________________
APP_NAME: "MauiDemo"
PROJECT_PATH: "src/MAUI/MauiDemo.csproj"
PROJECT_DIRECTORY: "src/MAUI"
SDK_VERSION: '10.0.x'
NET_TFM: 'net10.0'
XCODE_VERSION: '26.4'
MAC_DOTNET_VERSION: '10.0.203'
NUGET_CONFIG_PATH: 'src/nuget.config'
TELERIK_NUGET_KEY: ${{secrets.TELERIK_NUGET_KEY}}
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
# ____Android___________________________________________
ANDROID_ARTIFACTS_PATH: "artifacts_android"
ANDROID_KEYSTORE_PATH: "${{github.workspace}}/android-upload.keystore"
JAVA_VERSION: '17'
JAVA_DISTRIBUTION: 'microsoft'
# ____MacCatalyst_______________________________________
APPLE_DEV_ID_APP_CERT_NAME: "Developer ID Application: Lancelot Software, LLC (L65255N3F7)"
APPLE_DEV_ID_INSTALLER_CERT_NAME: "Developer ID Installer: Lancelot Software, LLC (L65255N3F7)"
APPLE_NOTARY_TEAM_ID: "L65255N3F7"
MAC_PACKAGE_NAME: "MauiDemo-MacCatalyst"
MAC_APP_BUNDLE_PATH: "src/Maui/bin/Release/net10.0-maccatalyst/MauiDemo.app"
MAC_ARTIFACTS_PATH: "artifacts/maccatalyst"
# ____iOS______________________________________________ (also check matrix vars)
APPLE_APP_ID: "com.lancelotsoftware.MauiDemoApp"
IOS_RID: ios-arm64
# ____Windows__________________________________________ (also check matrix vars)
WINDOWS_ARTIFACTS_PATH: "artifacts_windows"
SERVICE_PRINCIPAL_CLIENT_ID: "32daa13b-f4bb-4809-8ef6-58cb39051acd"
SERVICE_PRINCIPAL_TENANT_ID: "bd47e796-3473-4b8a-9101-1f4c0c7af31a"
SERVICE_PRINCIPAL_SUBSCRIPTION_ID: "48ab4839-62af-4ab3-afe6-043ea4d7c137"
SIGNING_ACCT_NAME: "PrimaryCodeSign"
SIGNING_ACCT_CERT_PROFILE: "lancemccarthylivepublic"
SIGNING_ACCT_ENDPOINT_URL: "https://eus.codesigning.azure.net/"
# ------------------ REQUIRED SECRETS ---------------- #
# ____Global____________________________________________
# TELERIK_NUGET_KEY
# TELERIK_LICENSE_KEY
# ____Android___________________________________________
# ANDROID_SIGNING_KEYSTORE_BASE64
# ANDROID_SIGNING_KEYSTORE_PASS
# ANDROID_SIGNING_KEY_ALIAS
# ANDROID_SIGNING_KEY_PASS
# ____MacCatalyst_______________________________________
# APPLE_DEVELOPER_ID_INSTALLER_CERT_BASE64
# APPLE_DEVELOPER_ID_INSTALLER_CERT_PASSWORD
# APPLE_DEVELOPER_ID_APPLICATION_CERT_BASE64
# APPLE_NOTARY_APPLE_ID
# APPLE_NOTARY_APP_PASSWORD
# ____iOS______________________________________________
# APPLE_DISTRIBUTION_CERT_BASE64
# APPLE_DISTRIBUTION_CERT_PASSWORD
# APPSTORE_API_ISSUER_ID
# APPSTORE_API_KEY_ID
# APPSTORE_API_PRIVATE_KEY
# ____Windows__________________________________________
# No additional secrets needed, uses Azure Trusted Signing.
jobs:
# ********************************************************************************** #
# Shared Resources #
# ********************************************************************************** #
shared-resources:
name: Create Shared Resources
runs-on: windows-latest
outputs:
app_version: ${{steps.version-creator.outputs.app_version}}
steps:
# Generates a version number using year.monthday.run_number (e.g., 2024.824.1)
- name: Generate version number using date and run number
id: version-creator
shell: pwsh
run: |
$buildDay = Get-Date -Format "yyyy.Mdd"
$runNumber = "$env:GITHUB_RUN_NUMBER"
$ver = $buildDay + "." + $runNumber
echo "app_version=$ver" >> $env:GITHUB_OUTPUT
# ********************************************************************************** #
# Android #
# ********************************************************************************** #
android:
name: Build Android (Store Upload)
runs-on: windows-latest
needs: shared-resources
if: ${{ success() && needs.shared-resources.outputs.app_version != '' }}
steps:
- uses: actions/checkout@v6
- name: Decode the Keystore
shell: pwsh
run: |
$file_bytes = [System.Convert]::FromBase64String("${{secrets.ANDROID_SIGNING_KEYSTORE_BASE64}}")
[IO.File]::WriteAllBytes("${{env.ANDROID_KEYSTORE_PATH}}", $file_bytes)
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.SDK_VERSION}}
- uses: actions/setup-java@v5
with:
distribution: ${{env.JAVA_DISTRIBUTION}}
java-version: ${{env.JAVA_VERSION}}
- name: Install MAUI Workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile '${{env.NUGET_CONFIG_PATH}}' --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
- name: Publish MAUI Android
run: |
dotnet publish ${{env.PROJECT_PATH}} -c Release -f ${{env.NET_TFM}}-android /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=${{env.ANDROID_KEYSTORE_PATH}} /p:AndroidSigningStorePass=${{secrets.ANDROID_SIGNING_KEYSTORE_PASS}} /p:AndroidSigningKeyAlias=${{secrets.ANDROID_SIGNING_KEY_ALIAS}} /p:AndroidSigningKeyPass=${{secrets.ANDROID_SIGNING_KEY_PASS}}
- name: Create artifacts folder
run: |
New-Item -ItemType Directory -Force -Path ${{env.ANDROID_ARTIFACTS_PATH}} | Out-Null
- name: Copy signed APKs & AABs
run: |
$publishRoot = "src/Maui/bin/Release/${{env.NET_TFM}}-android"
$apkFiles = Get-ChildItem -Path $publishRoot -Filter *-Signed.apk -File -Recurse -ErrorAction SilentlyContinue
$aabFiles = Get-ChildItem -Path $publishRoot -Filter *-Signed.aab -File -Recurse -ErrorAction SilentlyContinue
if ($apkFiles.Count -eq 0 -and $aabFiles.Count -eq 0) {
throw "No signed Android APK/AAB files found under $publishRoot"
}
$apkFiles | ForEach-Object { Copy-Item -Path $_.FullName -Destination ${{env.ANDROID_ARTIFACTS_PATH}} -Force }
$aabFiles | ForEach-Object { Copy-Item -Path $_.FullName -Destination ${{env.ANDROID_ARTIFACTS_PATH}} -Force }
- name: Publish Android build artifacts
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_signed-android"
path: "${{env.ANDROID_ARTIFACTS_PATH}}/*"
if-no-files-found: error
retention-days: 60
# ********************************************************************************** #
# Windows (Sideload - msixbundle) #
# ********************************************************************************** #
windows-sideload-packages:
name: Build Windows (Sideload)
needs: shared-resources
runs-on: windows-latest
if: ${{ needs.shared-resources.outputs.app_version != ''}}
strategy:
fail-fast: false
matrix:
include:
- arch: x64
runtime_id: win-x64
platform: x64
- arch: arm64
runtime_id: win-arm64
platform: ARM64
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.SDK_VERSION}}
# Needed only for WinUI builds
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v3
- name: Install MAUI workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile '${{env.NUGET_CONFIG_PATH}}' --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
- name: Update manifest for sideload build
run: |
[xml]$manifest = Get-Content '${{env.PROJECT_DIRECTORY}}\Platforms\Windows\Package.appxmanifest'
$manifest.Package.Identity.Version = "${{needs.shared-resources.outputs.app_version}}.0"
$manifest.Save('${{env.PROJECT_DIRECTORY}}\Platforms\Windows\Package.appxmanifest')
- name: Publish ${{matrix.arch}} Windows package
id: publish-package
run: |
$appVersion = "${{needs.shared-resources.outputs.app_version}}.0"
$artifactDir = "${{github.workspace}}\${{env.WINDOWS_ARTIFACTS_PATH}}\${{matrix.arch}}"
New-Item -ItemType Directory -Force -Path $artifactDir | Out-Null
Write-Host "Publishing Windows ${{matrix.arch}}..."
$publishArgs = @(
'publish'
'${{env.PROJECT_PATH}}'
'-c', 'Release'
'-f', '${{env.NET_TFM}}-windows10.0.19041.0'
'-p:Platform=${{matrix.platform}}'
'-p:AppxPackageBuildPlatform=${{matrix.platform}}'
'-p:RuntimeIdentifierOverride=${{matrix.runtime_id}}'
'-p:GenerateAppxPackageOnBuild=true'
'-p:AppxPackageSigningEnabled=false'
'-p:UapAppxPackageBuildMode=SideloadOnly'
'-p:AppxBundle=Never'
'-p:WindowsPackageType=MSIX'
)
& dotnet @publishArgs
if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed for ${{matrix.arch}} with exit code $LASTEXITCODE" }
# MAUI writes MSIX to src\${{env.APP_NAME}}\AppPackages\<name>_<ver>_<arch>_Test\*.msix
$appPackagesRoot = "src\${{env.APP_NAME}}\AppPackages"
$candidates = Get-ChildItem -Path $appPackagesRoot -Filter *.msix -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -notmatch '\\Dependencies\\' -and $_.Name -match "_${{matrix.arch}}\.msix$" }
Write-Host "Found $($candidates.Count) candidate MSIX file(s) for ${{matrix.arch}}:"
$candidates | ForEach-Object { Write-Host " $($_.FullName)" }
$msix = $candidates | Sort-Object LastWriteTime -Descending | Select-Object -ExpandProperty FullName -First 1
if (-not $msix) { throw "No .msix found after publish for ${{matrix.arch}}" }
Write-Host "Found ${{matrix.arch}} package: $msix"
Copy-Item -Path $msix -Destination "$artifactDir\${{env.APP_NAME}}.${{matrix.arch}}.msix" -Force
echo "PACKAGE_PATH=$artifactDir\${{env.APP_NAME}}.${{matrix.arch}}.msix" >> $env:GITHUB_OUTPUT
- name: Upload ${{matrix.arch}} artifact
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_${{matrix.runtime_id}}"
path: ${{steps.publish-package.outputs.PACKAGE_PATH}}
if-no-files-found: error
retention-days: 30
windows-generate-msixbundle:
name: Bundle MSIX packages
needs: [shared-resources, windows-sideload-packages]
runs-on: windows-latest
steps:
- name: Clean bundle workspace
run: |
Remove-Item -Recurse -Force "${{github.workspace}}\bundle-input" -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force "${{github.workspace}}\bundle-output" -ErrorAction SilentlyContinue
- name: Download x64 artifact
uses: actions/download-artifact@v8
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_win-x64"
path: ${{github.workspace}}\bundle-input
- name: Download arm64 artifact
uses: actions/download-artifact@v8
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_win-arm64"
path: ${{github.workspace}}\bundle-input
- name: Create MSIX bundle
id: bundle
run: |
$appVersion = "${{needs.shared-resources.outputs.app_version}}.0"
$inputDir = "${{github.workspace}}\bundle-input"
$outputDir = "${{github.workspace}}\bundle-output"
New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
# Locate makeappx.exe from the latest installed Windows 10 SDK
$sdkRoot = "C:\Program Files (x86)\Windows Kits\10\bin"
$makeAppx = Get-ChildItem -Path $sdkRoot -Recurse -Filter makeappx.exe -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -match '\\x64\\makeappx\.exe$' } |
Sort-Object FullName -Descending | Select-Object -First 1
if (-not $makeAppx) { throw "makeappx.exe not found under $sdkRoot" }
Write-Host "Using makeappx: $($makeAppx.FullName)"
Get-ChildItem $inputDir -Filter *.msix | ForEach-Object { Write-Host "Input: $($_.FullName)" }
$bundlePath = "$outputDir\${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}.msixbundle"
& $makeAppx.FullName bundle /d $inputDir /p $bundlePath /bv $appVersion /o
if ($LASTEXITCODE -ne 0) { throw "makeappx bundle failed with exit code $LASTEXITCODE" }
echo "BUNDLE_PATH=$bundlePath" >> $env:GITHUB_OUTPUT
# Entra ID App Registration (Akeyless OIDC Provider) > Certificates and Secrets > Federated Credentials
- name: Azure login using OIDC via GitHub
uses: azure/login@v3
id: azlogin
with:
client-id: ${{env.SERVICE_PRINCIPAL_CLIENT_ID}}
tenant-id: ${{env.SERVICE_PRINCIPAL_TENANT_ID}}
subscription-id: ${{env.SERVICE_PRINCIPAL_SUBSCRIPTION_ID}}
- name: Sign MSIX bundle
uses: azure/trusted-signing-action@v1.2.0
with:
endpoint: ${{env.SIGNING_ACCT_ENDPOINT_URL}}
signing-account-name: ${{env.SIGNING_ACCT_NAME}}
certificate-profile-name: ${{env.SIGNING_ACCT_CERT_PROFILE}}
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
file-digest: SHA256
files: ${{steps.bundle.outputs.BUNDLE_PATH}}
exclude-azure-cli-credential: false
exclude-environment-credential: true
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-powershell-credential: true
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
- name: Upload MSIX bundle artifact
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_signed-windows.msixbundle"
path: ${{steps.bundle.outputs.BUNDLE_PATH}}
if-no-files-found: error
retention-days: 30
# ********************************************************************************** #
# Windows (Store - msixupload) #
# ********************************************************************************** #
windows-store:
name: Build Windows (Store Upload)
runs-on: windows-latest
needs: shared-resources
if: ${{ success() && needs.shared-resources.outputs.app_version != '' }}
steps:
- uses: actions/checkout@v6
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.SDK_VERSION}}
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v3
- name: Install MAUI Workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile '${{env.NUGET_CONFIG_PATH}}' --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
- name: Update manifest for store build
run: |
[xml]$manifest = Get-Content '${{env.PROJECT_DIRECTORY}}\Platforms\Windows\Package.appxmanifest'
$manifest.Package.Identity.Version = "${{needs.shared-resources.outputs.app_version}}.0"
$manifest.Save('${{env.PROJECT_DIRECTORY}}\Platforms\Windows\Package.appxmanifest')
- name: Build Maui WinUI project
run: dotnet publish ${{env.PROJECT_PATH}} -c Release -f ${{env.NET_TFM}}-windows10.0.19041.0 -p:AppxPackageSigningEnabled=false -p:AppxSymbolPackageEnabled=false -p:UapAppxPackageBuildMode=StoreUpload -p:AppxBundle=Always "-p:AppxBundlePlatforms=x64|arm64" -p:PlatformTarget=AnyCPU
- name: Publish build artifacts
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_storeupload-windows"
path: "**/*.msixupload"
if-no-files-found: error
retention-days: 60
# ********************************************************************************** #
# MacCatalyst #
# ********************************************************************************** #
maccatalyst:
name: Build MacCatalyst (Signed & Notiarized)
runs-on: macos-26 # https://github.com/actions/runner-images#available-images
needs: shared-resources
if: ${{ success() && needs.shared-resources.outputs.app_version != '' }}
steps:
- uses: actions/checkout@v6
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{env.XCODE_VERSION}}
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.SDK_VERSION}}
- name: Install MAUI Workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile '${{env.NUGET_CONFIG_PATH}}' --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
- name: Import Developer ID Installer Certificates
uses: Apple-Actions/import-codesign-certs@v7
with:
p12-file-base64: "${{secrets.APPLE_DEVELOPER_ID_INSTALLER_CERT_BASE64}}"
p12-password: "${{secrets.APPLE_DEVELOPER_ID_INSTALLER_CERT_PASSWORD}}"
keychain: installer_signing_temp
- name: Import Developer ID Application Certificates
uses: Apple-Actions/import-codesign-certs@v7
with:
p12-file-base64: "${{secrets.APPLE_DEVELOPER_ID_APPLICATION_CERT_BASE64}}"
p12-password: "${{secrets.APPLE_DEVELOPER_ID_APPLICATION_CERT_PASSWORD}}"
keychain: application_signing_temp
# Each import-codesign-certs call rewrites the keychain search list. The second import step (Application cert) drops the first (Installer) keychain, so product build cannot find the "Developer ID Installer" identity. This step re-adds both keychains explicitly before any signing occurs.
- name: Ensure both signing keychains are in the search list
shell: bash
run: |
set -euo pipefail
security list-keychains -d user -s "$HOME/Library/Keychains/installer_signing_temp.keychain-db" "$HOME/Library/Keychains/application_signing_temp.keychain-db" "$HOME/Library/Keychains/login.keychain-db"
echo "Keychain search list:"
security list-keychains -d user
# Finally make sure both certs are available before we build
- name: Verify Developer ID identities are available
shell: bash
run: |
set -euo pipefail
APPLE_DEV_ID_APP_CERT_NAME="${{env.APPLE_DEV_ID_APP_CERT_NAME}}"
APPLE_DEV_ID_INSTALLER_CERT_NAME="${{env.APPLE_DEV_ID_INSTALLER_CERT_NAME}}"
security find-identity -v -p codesigning
security find-certificate -a -c "$APPLE_DEV_ID_INSTALLER_CERT_NAME" || true
if ! security find-identity -v -p codesigning | grep -F "$APPLE_DEV_ID_APP_CERT_NAME" >/dev/null; then
echo "Missing Developer ID Application identity: $APPLE_DEV_ID_APP_CERT_NAME"
exit 1
fi
if ! security find-certificate -a -c "$APPLE_DEV_ID_INSTALLER_CERT_NAME" >/dev/null; then
echo "Missing Developer ID Installer certificate: $APPLE_DEV_ID_INSTALLER_CERT_NAME"
exit 1
fi
- name: Build and sign MacCatalyst artifacts
shell: bash
run: |
set -euo pipefail
cd "$GITHUB_WORKSPACE"
if [[ ! -f "$PROJECT_PATH" ]]; then
echo "Project file not found: $PROJECT_PATH"
echo "Current directory: $(pwd)"
exit 1
fi
mkdir -p "$ARTIFACTS_DIR"
echo "Cleaning project..."
dotnet clean "$PROJECT_PATH" -c "$CONFIGURATION" -f "$TFM"
publish_args=(
"$PROJECT_PATH"
-c "$CONFIGURATION"
-f "$TFM"
"-p:CodesignKey=\"$CODESIGN_KEY\""
"-p:ApplicationVersion=$APP_VERSION"
"-p:ApplicationDisplayVersion=$APP_VERSION"
-p:UseHardenedRuntime=true
)
echo "Publishing project..."
dotnet publish "${publish_args[@]}"
if [[ ! -d "$APP_BUNDLE_PATH" ]]; then
echo "Expected app bundle not found at $APP_BUNDLE_PATH"
exit 1
fi
echo "Verifying app signature..."
codesign -dv --verbose=2 "$APP_BUNDLE_PATH" >/dev/null 2>&1
echo "Creating signed app zip and installer pkg..."
rm -f "$SIGNED_ZIP_PATH" "$SIGNED_PKG_PATH"
ditto -c -k --sequesterRsrc --keepParent "$APP_BUNDLE_PATH" "$SIGNED_ZIP_PATH"
productbuild --component "$APP_BUNDLE_PATH" /Applications --sign "$INSTALLER_SIGN_ID" "$SIGNED_PKG_PATH"
echo "Done. Signed artifacts:"
echo "- $SIGNED_PKG_PATH"
echo "- $SIGNED_ZIP_PATH"
env:
PROJECT_PATH: "${{env.PROJECT_PATH}}"
TFM: "${{env.NET_TFM}}-maccatalyst"
CONFIGURATION: "Release"
APP_VERSION: "${{needs.shared-resources.outputs.app_version}}"
ARTIFACTS_DIR: "${{env.MAC_ARTIFACTS_PATH}}"
APP_BUNDLE_PATH: "${{env.MAC_APP_BUNDLE_PATH}}"
CODESIGN_KEY: "${{env.APPLE_DEV_ID_APP_CERT_NAME}}"
INSTALLER_SIGN_ID: "${{ env.APPLE_DEV_ID_INSTALLER_CERT_NAME}}"
SIGNED_ZIP_PATH: "$ARTIFACTS_DIR/${{env.MAC_PACKAGE_NAME}}-Release-signed.zip"
SIGNED_PKG_PATH: "$ARTIFACTS_DIR/${{env.MAC_PACKAGE_NAME}}-Release-signed.pkg"
- name: Notarize and staple MacCatalyst artifacts
shell: bash
run: |
set -euo pipefail
cd "$GITHUB_WORKSPACE"
if [[ -z "$APPLE_NOTARY_APPLE_ID" || -z "$APPLE_NOTARY_APP_PASSWORD" || -z "$APPLE_NOTARY_TEAM_ID" ]]; then
echo "Missing notarization credentials."
echo "Set APPLE_NOTARY_APPLE_ID, APPLE_NOTARY_APP_PASSWORD, and APPLE_NOTARY_TEAM_ID."
exit 1
fi
if [[ ! -d "$APP_BUNDLE_PATH" ]]; then
echo "Expected app bundle not found at $APP_BUNDLE_PATH"
exit 1
fi
if [[ ! -f "$SIGNED_PKG_PATH" || ! -f "$SIGNED_ZIP_PATH" ]]; then
echo "Expected signed artifacts not found in $ARTIFACTS_DIR"
exit 1
fi
echo "Submitting pkg for notarization..."
pkg_submit_json="$(xcrun notarytool submit "$SIGNED_PKG_PATH" --apple-id "$APPLE_NOTARY_APPLE_ID" --team-id "$APPLE_NOTARY_TEAM_ID" --password "$APPLE_NOTARY_APP_PASSWORD" --wait --output-format json)"
echo "$pkg_submit_json"
if ! grep -Eq '"status"[[:space:]]*:[[:space:]]*"Accepted"' <<< "$pkg_submit_json"; then
echo "Notarization failed for pkg."
exit 1
fi
echo "Stapling and validating pkg..."
xcrun stapler staple "$SIGNED_PKG_PATH"
xcrun stapler validate "$SIGNED_PKG_PATH"
echo "Submitting signed app zip for notarization..."
zip_submit_json="$(xcrun notarytool submit "$SIGNED_ZIP_PATH" --apple-id "$APPLE_NOTARY_APPLE_ID" --team-id "$APPLE_NOTARY_TEAM_ID" --password "$APPLE_NOTARY_APP_PASSWORD" --wait --output-format json)"
echo "$zip_submit_json"
if ! grep -Eq '"status"[[:space:]]*:[[:space:]]*"Accepted"' <<< "$zip_submit_json"; then
echo "Notarization failed for app zip."
exit 1
fi
echo "Stapling and validating app..."
xcrun stapler staple "$APP_BUNDLE_PATH"
xcrun stapler validate "$APP_BUNDLE_PATH"
# Recreate the final distributable zip from the stapled app.
ditto -c -k --sequesterRsrc --keepParent "$APP_BUNDLE_PATH" "$NOTARIZED_ZIP_PATH"
echo "Gatekeeper checks..."
spctl -a -t exec -vv "$APP_BUNDLE_PATH"
spctl -a -t install -vv "$SIGNED_PKG_PATH"
echo "Done. Artifacts:"
echo "- $SIGNED_PKG_PATH"
echo "- $NOTARIZED_ZIP_PATH"
env:
ARTIFACTS_DIR: "${{env.MAC_ARTIFACTS_PATH}}"
APP_BUNDLE_PATH: "${{env.MAC_APP_BUNDLE_PATH}}"
APPLE_NOTARY_APPLE_ID: "${{secrets.APPLE_NOTARY_APPLE_ID}}"
APPLE_NOTARY_APP_PASSWORD: "${{secrets.APPLE_NOTARY_APP_PASSWORD}}"
APPLE_NOTARY_TEAM_ID: "${{env.APPLE_NOTARY_TEAM_ID}}"
SIGNED_ZIP_PATH: "${{env.MAC_ARTIFACTS_PATH}}/${{env.MAC_PACKAGE_NAME}}-Release-signed.zip"
NOTARIZED_ZIP_PATH: "${{env.MAC_ARTIFACTS_PATH}}/${{env.MAC_PACKAGE_NAME}}-Release-notarized.zip"
SIGNED_PKG_PATH: "${{env.MAC_ARTIFACTS_PATH}}/${{env.MAC_PACKAGE_NAME}}-Release-signed.pkg"
- name: Publish MacCatalyst build artifacts
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_signed-maccatalyst"
path: ${{env.MAC_ARTIFACTS_PATH}}/${{env.MAC_PACKAGE_NAME}}-Release-signed.pkg
if-no-files-found: error
retention-days: 30
# ********************************************************************************** #
# iOS #
# ********************************************************************************** #
ios:
name: Build iOS (${{matrix.distribution_name}})
runs-on: macos-26 # https://github.com/actions/runner-images#available-images
needs: shared-resources
if: ${{ success() && needs.shared-resources.outputs.app_version != '' }}
strategy:
fail-fast: false
matrix:
include:
- distribution_name: Store Upload IPA
artifact_suffix: storeupload-ios
provisioning_profile: MauiDemo_AppStore_Distribution
provisioning_profile_type: IOS_APP_STORE
rid: ios-arm64
- distribution_name: Ad Hoc Sideload IPA
artifact_suffix: sideload-ios
provisioning_profile: MauiDemo_AdHoc_Distribution
provisioning_profile_type: IOS_APP_ADHOC
rid: ios-arm64
env:
APPLE_PROV_PROFILE: ${{matrix.provisioning_profile}}
APPLE_PROV_PROFILE_TYPE: ${{matrix.provisioning_profile_type}}
steps:
- uses: actions/checkout@v6
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{env.XCODE_VERSION}}
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.SDK_VERSION}}
- name: Install MAUI Workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile '${{env.NUGET_CONFIG_PATH}}' --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
# Docs https://github.com/Apple-Actions/import-codesign-certs
- name: Import Code-Signing Certificates
uses: Apple-Actions/import-codesign-certs@v7
with:
p12-file-base64: "${{secrets.APPLE_DISTRIBUTION_CERT_BASE64}}"
p12-password: "${{secrets.APPLE_DISTRIBUTION_CERT_PASSWORD}}"
# Docs https://github.com/Apple-Actions/download-provisioning-profiles
- id: provisioning-profiles
uses: Apple-Actions/download-provisioning-profiles@v6
with:
profile-type: "${{env.APPLE_PROV_PROFILE_TYPE}}"
bundle-id: "${{env.APPLE_APP_ID}}"
issuer-id: "${{secrets.APPSTORE_API_ISSUER_ID}}"
api-key-id: "${{secrets.APPSTORE_API_KEY_ID}}"
api-private-key: "${{secrets.APPSTORE_API_PRIVATE_KEY}}"
- name: Verify provisioning profile
run: |
$profiles = '${{steps.provisioning-profiles.outputs.profiles}}' | ConvertFrom-Json
$profile = $profiles | Where-Object { $_.name -eq $env:APPLE_PROV_PROFILE -and $_.type -eq $env:APPLE_PROV_PROFILE_TYPE } | Select-Object -First 1
if ($null -eq $profile) {
$profiles | Format-Table -AutoSize | Out-String | Write-Host
throw "Provisioning profile '$env:APPLE_PROV_PROFILE' with type '$env:APPLE_PROV_PROFILE_TYPE' was not downloaded."
}
- name: Verify iOS signing identity is available
shell: bash
run: |
set -euo pipefail
security find-identity -v -p codesigning
if ! security find-identity -v -p codesigning | grep -F "Apple Distribution" >/dev/null; then
echo "Missing Apple Distribution identity"
exit 1
fi
# Docs https://learn.microsoft.com/en-us/dotnet/maui/ios/deployment/publish-cli?view=net-maui-9.0
- name: Publish MAUI iOS IPA
run: |
# Query the actual signing identity to avoid comma parsing issues
$identityOutput = security find-identity -v -p codesigning | Select-String "Apple Distribution"
if (-not $identityOutput) { throw "No Apple Distribution identity found in keychain" }
$codesignKey = [regex]::Match($identityOutput.Line, '"(?<name>Apple Distribution:[^"]+)"').Groups['name'].Value
if (-not $codesignKey) { throw "Could not parse Apple Distribution identity from keychain output" }
Write-Host "Using codesign key: $codesignKey"
$quotedCodesignKey = '"' + $codesignKey + '"'
$publishArgs = @(
'publish'
'${{env.PROJECT_PATH}}'
'-f', '${{env.NET_TFM}}-ios'
'-c', 'Release'
'-p:ArchiveOnBuild=true'
'-p:RuntimeIdentifier=${{matrix.rid}}'
'-p:MtouchLink=SdkOnly'
'-p:ApplicationId=${{env.APPLE_APP_ID}}'
'-p:ApplicationVersion=${{needs.shared-resources.outputs.app_version}}'
'-p:CodesignProvision=${{env.APPLE_PROV_PROFILE}}'
"-p:CodesignKey=$quotedCodesignKey"
)
& dotnet @publishArgs
if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed with exit code $LASTEXITCODE" }
- name: Publish iOS build artifacts
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_${{matrix.artifact_suffix}}"
path: "${{env.PROJECT_DIRECTORY}}/bin/Release/${{env.NET_TFM}}-ios/${{matrix.rid}}/publish/*.ipa"
if-no-files-found: error
retention-days: 60
# ********************************************************************************** #
# GitHub Release #
# ********************************************************************************** #
# create-release:
# name: Create GitHub Release
# runs-on: ubuntu-latest
# needs: [shared-resources, android, windows-sideload-packages, windows-generate-msixbundle, windows-store, maccatalyst, ios]
# steps:
# - name: Download all artifacts
# uses: actions/download-artifact@v8
# with:
# path: release-artifacts
# - name: List downloaded artifacts
# shell: bash
# run: find release-artifacts -type f | sort
# - name: Prepare release files
# shell: bash
# run: |
# set -euo pipefail
# VER="${{needs.shared-resources.outputs.app_version}}"
# PREFIX="${{env.APP_NAME}}_v${VER}"
# mkdir -p release-upload
# copy_one() {
# local artifact_dir="$1"
# local pattern="$2"
# local destination="$3"
# local -a matches=()
# if [[ ! -d "$artifact_dir" ]]; then
# echo "Expected artifact directory not found: $artifact_dir" >&2
# exit 1
# fi
# mapfile -d '' matches < <(find "$artifact_dir" -type f -name "$pattern" -print0 | sort -z)
# if [[ ${#matches[@]} -ne 1 ]]; then
# echo "Expected exactly one match for '$pattern' under '$artifact_dir', found ${#matches[@]}." >&2
# find "$artifact_dir" -type f | sort >&2
# exit 1
# fi
# cp -- "${matches[0]}" "$destination"
# }
# # Android
# copy_one "release-artifacts/${PREFIX}_signed-android" "*-Signed.apk" "release-upload/${PREFIX}_android-signed.apk"
# copy_one "release-artifacts/${PREFIX}_signed-android" "*-Signed.aab" "release-upload/${PREFIX}_android-signed.aab"
# # Windows msixbundle (signed)
# copy_one "release-artifacts/${PREFIX}_signed-windows.msixbundle" "*.msixbundle" "release-upload/${PREFIX}_windows.msixbundle"
# # Windows Store
# copy_one "release-artifacts/${PREFIX}_storeupload-windows" "*.msixupload" "release-upload/${PREFIX}_msstore.msixupload"
# # MacCatalyst
# copy_one "release-artifacts/${PREFIX}_signed-maccatalyst" "*.pkg" "release-upload/${PREFIX}_mac.pkg"
# # iOS
# copy_one "release-artifacts/${PREFIX}_ios-adhoc" "*.ipa" "release-upload/${PREFIX}_ios-adhoc.ipa"
# copy_one "release-artifacts/${PREFIX}_ios-store" "*.ipa" "release-upload/${PREFIX}_ios-store.ipa"
# echo "Files prepared for release:"
# ls -lh release-upload/
# - name: Create GitHub Release
# uses: softprops/action-gh-release@v3.0.0
# with:
# tag_name: "v${{needs.shared-resources.outputs.app_version}}"
# name: "${{env.APP_NAME}} v${{needs.shared-resources.outputs.app_version}}"
# draft: false
# prerelease: false
# generate_release_notes: true
# files: release-upload/*
+49
View File
@@ -0,0 +1,49 @@
# This example shows you how you can use the named environment variables in the nuget.config file to set the credentials, for more information see https://github.com/LanceMcCarthy/DevOpsExamples#github-actions-using-secrets-to-set-environment-variables
name: WinForms (.NET Framework)
on:
workflow_dispatch:
push:
branches:
- main
- "winforms/*"
paths:
- 'src/WinForms/**/*'
- '.github/workflows/main_build-winforms.yml'
env:
TELERIK_USERNAME: "api-key" # Variable name used in the nuget.config file
TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}} # Variable name used in the nuget.config file
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}} # Used when compiling the project
CSPROJ_PATH: "src/WinForms/MyWinFormsApp/MyWinFormsApp.csproj"
NUGETCONFIG_PATH: "src/NuGet.Config"
jobs:
# A job that builds a .NET Framework WPF application using Telerik UI for WinForms
build_desktop:
runs-on: windows-latest # WinForms apps must be built on Windows runners
strategy:
matrix:
configuration: [Release]
platform: [x86, x64]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2
# We use the dotnet CLI (instead of nuget CLI) to restore the nuget packages before using msbuild
- name: NuGet Restore
run: dotnet restore ${{env.CSPROJ_PATH}} --configfile ${{env.NUGETCONFIG_PATH}} --runtime win-${{matrix.platform}}
# Use msbuild to compile the .NET Framework WinForms project
- name: Build the WinForms application
run: msbuild ${{env.CSPROJ_PATH}} /t:Restore /p:Configuration=${{matrix.configuration}} /p:RuntimeIdentifier=win-${{matrix.platform}}
+57
View File
@@ -0,0 +1,57 @@
name: WinUI3
on:
workflow_dispatch:
push:
branches:
- main
- "winui/*"
paths:
- 'src/WinUI/**/*'
- '.github/workflows/main_build-winui.yml'
env:
TELERIK_USERNAME: "api-key" # Used by the nuget.config file
TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}} # Used by the nuget.config file
SOLUTION_NAME: "src/WinUI/MyDemo.sln"
NUGETCONFIG_PATH: "src/NuGet.Config"
jobs:
build-windows:
runs-on: windows-2022
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
- name: Restore NuGet packages
shell: pwsh
run: dotnet restore ${{env.SOLUTION_NAME}} --configfile ${{env.NUGETCONFIG_PATH}}
# Restore the application to populate the obj folder with RuntimeIdentifiers
- name: Restore RIDs
run: msbuild ${{env.SOLUTION_NAME}} /t:Restore /p:Configuration=Release
- name: Build and Create MSIX
run: |
msbuild ${{env.SOLUTION_NAME}} `
/p:Configuration=Release `
/p:Platform=x64 `
/p:AppxBundlePlatforms="x64|arm64" `
/p:UapAppxPackageBuildMode=CI `
/p:AppxBundle=Never `
/p:AppxPackageDir="${{github.workspace}}/AppPackages" `
/p:GenerateAppxPackageOnBuild=true `
/p:AppxPackageSigningEnabled=false `
/p:SelfContained=true
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
+48
View File
@@ -0,0 +1,48 @@
# This example shows you how you can use the named environment variables in the nuget.config file to set the credentials
name: WPF (.NET Framework)
on:
workflow_dispatch:
push:
branches:
- main
- "wpf/*"
paths:
- 'src/Wpf/**/*'
- '.github/workflows/main_build-wpf.yml'
env:
TELERIK_USERNAME: "api-key" # Variable name used in the nuget.config file
TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}} # Variable name used in the nuget.config file
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}} # Used when compiling the project
CSPROJ_PATH: "src/Wpf/MyWpfApp/MyWpfApp.csproj"
NUGETCONFIG_PATH: "src/NuGet.Config"
jobs:
build_desktop:
runs-on: windows-latest
strategy:
matrix:
configuration: [Release]
platform: [x86, x64]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2
# We use the dotnet CLI (instead of nuget.exe) to restore the nuget packages before using msbuild
- name: NuGet Restore
run: dotnet restore ${{env.CSPROJ_PATH}} --configfile ${{env.NUGETCONFIG_PATH}} --runtime win-${{matrix.platform}}
# Use msbuild to compile the .NET Framework WPF project
- name: Build the WPF application
run: msbuild ${{env.CSPROJ_PATH}} /t:Restore /p:Configuration=${{matrix.configuration}} /p:RuntimeIdentifier=${{matrix.platform}}
+173
View File
@@ -0,0 +1,173 @@
name: ASP.NET Core (with Reporting) - Docker
on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'src/AspNetCore/**/*'
env:
WORKING_DIRECTORY: "src/AspNetCore/MyAspNetCoreApp"
TARGET_PLATFORMS: "linux/amd64,linux/arm64"
jobs:
#################### Example 1 ##########################
# Dockerfile build and publish to Docker Hub
# - Uses mcr.microsoft.com/dotnet/aspnet base image
# - Webook to automatically redeploy stack in Portainer
#########################################################
dockerhub_msftbase_build:
name: Microsoft Base - Publish to Docker Hub
runs-on: ubuntu-latest
env:
CONTAINER_REPOSITORY: "lancemccarthy/aspnetcore-reporting-from-msftbase"
DOCKERFILE_PATH: "src/AspNetCore/MyAspNetCoreApp/Dockerfile_MSRuntimeBase"
CONTAINER_REGISTRY: "docker.io"
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up QEMU for multi-arch support
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Get package metadata from Docker Hub
id: meta
uses: docker/metadata-action@v5
with:
images: ${{env.CONTAINER_REGISTRY}}/${{env.CONTAINER_REPOSITORY}}
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{secrets.DOCKER_HUB_USERNAME}}
password: ${{secrets.DOCKER_HUB_PAT}}
- name: Build and push to Docker Hub
uses: docker/build-push-action@v5
with:
file: ${{env.DOCKERFILE_PATH}}
context: ${{env.WORKING_DIRECTORY}}
platforms: ${{env.TARGET_PLATFORMS}}
push: true
secrets: |
telerik-nuget-key=${{secrets.TELERIK_NUGET_KEY}}
telerik-license-key=${{secrets.TELERIK_LICENSE_KEY}}
tags: ${{steps.meta.outputs.tags}}
- name: Trigger Portainer to pull images and redeploy the stack
run: curl -X POST ${{secrets.PORTAINER_WEBHOOK_ASPNETCORE}}
#################### Example 2 ##########################
# Dockerfile build and publish to Docker Hub
# - Uses a CentOS Base Image
# - Webook to automatically redeploy stack in Portainer
#########################################################
dockerhub_centosbase_build:
name: CentOS Base - Publish to Docker Hub
runs-on: ubuntu-latest
env:
CONTAINER_REPOSITORY: "lancemccarthy/aspnetcore-reporting-from-centosbase"
DOCKERFILE_PATH: "src/AspNetCore/MyAspNetCoreApp/Dockerfile_CentOS"
CONTAINER_REGISTRY: "docker.io"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up QEMU for multi-arch support
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Get package metadata from Docker Hub
id: meta
uses: docker/metadata-action@v5
with:
images: ${{env.CONTAINER_REGISTRY}}/${{env.CONTAINER_REPOSITORY}}
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{secrets.DOCKER_HUB_USERNAME}}
password: ${{secrets.DOCKER_HUB_PAT}}
- name: Build and push to Docker Hub
uses: docker/build-push-action@v5
with:
file: ${{env.DOCKERFILE_PATH}}
context: ${{env.WORKING_DIRECTORY}}
platforms: ${{env.TARGET_PLATFORMS}}
push: true
secrets: |
telerik-nuget-key=${{secrets.TELERIK_NUGET_KEY}}
telerik-license-key=${{secrets.TELERIK_LICENSE_KEY}}
tags: ${{steps.meta.outputs.tags}}
# ############################################################################
# ghcr.io option
# ############################################################################
# Set worklow permissions for publishing to GitHub Container Registry
# permissions:
# contents: read
# packages: write # to publish to GitHub container registry
# ghcr_msftbase_build:
# name: Microsoft Base - Publish to GitHub Container Registry
# runs-on: ubuntu-latest
# env:
# CONTAINER_REGISTRY: "ghcr.io"
# CONTAINER_REPOSITORY: "lancemccarthy/reporting-msft-base"
# DOCKERFILE_PATH: "src/AspNetCore/MyAspNetCoreApp/Dockerfile_MSRuntimeBase"
# steps:
# - name: Checkout
# uses: actions/checkout@v6
# with:
# fetch-depth: 0
# - name: Set up QEMU
# uses: docker/setup-qemu-action@v3
# - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@v3
# - name: Get package metadata from Docker Hub
# id: meta
# uses: docker/metadata-action@v5
# with:
# images: ${{env.CONTAINER_REGISTRY}}/${{env.CONTAINER_REPOSITORY}}
# - name: Login to DockerHub
# uses: docker/login-action@v3
# with:
# registry: ${{env.CONTAINER_REGISTRY}}
# username: ${{secrets.DOCKER_HUB_USERNAME}}
# password: ${{secrets.DOCKER_HUB_PAT}}
# - name: Build and push to Docker Hub
# uses: docker/build-push-action@v5
# with:
# file: ${{env.DOCKERFILE_PATH}}
# context: ${{env.WORKING_DIRECTORY}}
# platforms: ${{env.TARGET_PLATFORMS}}
# push: true
# secrets: |
# telerik-nuget-key=${{secrets.TELERIK_NUGET_KEY}}
# telerik-license-key=${{secrets.TELERIK_LICENSE_KEY}}
# tags: ${{steps.meta.outputs.tags}}
# - name: Delete old Docker images
# uses: actions/delete-package-versions@v5
# with:
# package-name: "myaspnetcoreapp"
# package-type: container
# min-versions-to-keep: 3
# token: ${{secrets.GITHUB_TOKEN}}
+213
View File
@@ -0,0 +1,213 @@
name: Blazor (with Reporting) - Docker
on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'src/Blazor/**/*'
env:
DOTNET_VERSION: "10.0.x"
BLAZOR_PROJ_PATH: src/Blazor/MyBlazorApp/MyBlazorApp.csproj
NUGET_CONFIG_PATH: src/NuGet.Config
CONTAINER_REPOSITORY: "lancemccarthy/myblazorapp"
# For ghcr.io access
permissions:
contents: read
packages: write
jobs:
#################### Example 1 ##########################
# Dockerfile build and publish to Docker Hub
# - Webook to automatically redeploy stack in Portainer
#########################################################
dockerfile_to_dockerhub:
name: "Dockerfile Build and Publish"
runs-on: ubuntu-latest
env:
CONTAINER_REGISTRY: "docker.io"
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Generate tag version
id: tag-creator
run: |
buildDay=`date +%Y.%m.%d`
tags="$buildDay.$GITHUB_RUN_NUMBER"
echo "VERSION_TAG=$tags" >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Get package metadata from Docker Hub
id: meta
uses: docker/metadata-action@v5
with:
images: ${{env.CONTAINER_REGISTRY}}/${{env.CONTAINER_REPOSITORY}}
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{secrets.DOCKER_HUB_USERNAME}}
password: ${{secrets.DOCKER_HUB_PAT}}
- name: Build and Publish arm64, amd64 Container Images
uses: docker/build-push-action@v5
with:
context: src/Blazor/MyBlazorApp
platforms: linux/arm64,linux/amd64
push: true
secrets: |
telerik-nuget-key=${{secrets.TELERIK_NUGET_KEY}}
telerik-license-key=${{secrets.TELERIK_LICENSE_KEY}}
tags: ${{steps.meta.outputs.tags}}
# Required because actions/delete-package-versions@v5 does not work with Docker Hub
- name: Delete old (untagged) images
run: |
IMAGE_TAGS=$(curl -s "https://hub.docker.com/v2/repositories/${{env.CONTAINER_REPOSITORY}}/tags/?page_size=100" | jq -r '.results|.[]|.name')
for TAGG in $IMAGE_TAGS; do
if [[ "$TAGG" == "null" ]]; then
docker rmi "${{env.CONTAINER_REPOSITORY}}:$TAGG"
curl -s -X DELETE "https://hub.docker.com/v2/repositories/${{env.CONTAINER_REPOSITORY}}/tags/$TAGG/"
fi
done
- name: Trigger Portainer to pull images and redeploy the stack
run: curl -X POST ${{secrets.PORTAINER_WEBHOOK_BLAZOR}}
#################### Example 2 ##########################
# .NET SDK build and publish to ghcr.io
############################################################
# # 2.1 - Build the lancemccarthy/myblazorapp:latest-x64 image
# build_x64:
# runs-on: ubuntu-latest
# name: "[NET SDK] Create x64 image"
# outputs:
# build_tag: ${{steps.build.outputs.build_tag}}
# env:
# target_arch: "x64"
# CONTAINER_REGISTRY: "ghcr.io"
# CONTAINER_BASE_IMAGE: "docker.io/lancemccarthy/skia-aspnet:10.0"
# steps:
# - name: Checkout
# uses: actions/checkout@v6
# with:
# fetch-depth: 0
# - name: Setup .NET Core SDK
# uses: actions/setup-dotnet@v4
# with:
# dotnet-version: ${{env.DOTNET_VERSION}}
# # Needed because dotnet publish -t:PublishContainer uses the CLI (not docker/login-action@v3)
# - name: Login to ghcr.io
# run: docker login ${{env.CONTAINER_REGISTRY}} -u ${{github.actor}} -p ${{secrets.GITHUB_TOKEN}}
# - name: Restore NuGet Packages
# run: dotnet restore ${{env.BLAZOR_PROJ_PATH}} -r "linux-${{env.target_arch}}"
# env:
# TELERIK_USERNAME: "api-key"
# TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}}
# - name: build the x64 image
# id: build
# run: |
# TAG="latest-${{env.target_arch}}"
# dotnet publish ${{env.BLAZOR_PROJ_PATH}} \
# -t:PublishContainer \
# -p PublishProfile=DefaultContainer \
# -p ContainerBaseImage=${{env.CONTAINER_BASE_IMAGE}} \
# -p ContainerRegistry="${{env.CONTAINER_REGISTRY}}" \
# -p ContainerRepository="${{env.CONTAINER_REPOSITORY}}" \
# -p ContainerImageTag="$TAG" \
# --arch ${{env.target_arch}} \
# --no-restore
# echo "build_tag=$TAG" >> $GITHUB_OUTPUT
# env:
# TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
# # Builds the lancemccarthy/myblazorapp:latest-arm64 image
# build_arm64:
# name: "[NET SDK] Create arm64 image"
# runs-on: ubuntu-latest
# outputs:
# build_tag: ${{steps.build.outputs.build_tag}}
# env:
# target_arch: "arm64"
# CONTAINER_REGISTRY: "ghcr.io"
# CONTAINER_BASE_IMAGE: "docker.io/lancemccarthy/skia-aspnet:10.0"
# steps:
# - name: Checkout
# uses: actions/checkout@v4
# with:
# fetch-depth: 0
# - name: Setup .NET Core SDK
# uses: actions/setup-dotnet@v4
# with:
# dotnet-version: ${{env.DOTNET_VERSION}}
# # Needed because dotnet publish -t:PublishContainer uses the CLI (not docker/login-action@v3)
# - name: Login to ghcr.io
# run: docker login ${{env.CONTAINER_REGISTRY}} -u ${{github.actor}} -p ${{secrets.GITHUB_TOKEN}}
# - name: Restore NuGet Packages
# run: dotnet restore ${{env.BLAZOR_PROJ_PATH}} -r "linux-${{env.target_arch}}"
# env:
# TELERIK_USERNAME: "api-key"
# TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}}
# - name: build the arm64 image
# id: build
# run: |
# TAG="latest-${{env.target_arch}}"
# dotnet publish ${{env.BLAZOR_PROJ_PATH}} \
# -t:PublishContainer \
# -p PublishProfile=DefaultContainer \
# -p ContainerBaseImage=${{env.CONTAINER_BASE_IMAGE}} \
# -p ContainerRegistry="${{env.CONTAINER_REGISTRY}}" \
# -p ContainerRepository="${{env.CONTAINER_REPOSITORY}}" \
# -p ContainerImageTag="$TAG" \
# --arch ${{env.target_arch}} \
# --no-restore
# echo "build_tag=$TAG" >> $GITHUB_OUTPUT
# env:
# TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
# # Create a manifest to combine both images into a single "lancemccarthy/myblazorapp:latest" tag
# publish_combined_manifest:
# runs-on: ubuntu-latest
# name: "[NET SDK] Publish multi-arch manifest"
# needs: [build_x64, build_arm64]
# env:
# CONTAINER_REGISTRY: "ghcr.io"
# steps:
# - name: Checkout
# uses: actions/checkout@v4
# - name: Login to ghcr.io
# run: docker login ${{env.CONTAINER_REGISTRY}} -u ${{github.actor}} -p ${{secrets.GITHUB_TOKEN}}
# - name: Create multi-arch manifest
# run: docker manifest create "${{env.CONTAINER_REPOSITORY}}:latest" "${{env.CONTAINER_REPOSITORY}}:${{needs.build_x64.outputs.build_tag}}" "${{env.CONTAINER_REPOSITORY}}:${{needs.build_arm64.outputs.build_tag}}"
# - name: Push multi-arch manifest
# run: docker manifest push "${{env.CONTAINER_REPOSITORY}}:latest"
# - name: Delete old images
# uses: actions/delete-package-versions@v5
# with:
# package-name: "myblazorapp"
# package-type: container
# min-versions-to-keep: 2
# token: ${{secrets.GITHUB_TOKEN}}