Objectives:
- we have source code in one srcRepo
- we have destination repo [destRepo] already deployment pipeline and other things defined in another repo
- we need to generate dll files and push that generated dll files to destRepo
- We want to use self hosted github runner, in windows
- self hosted runner should be running as local system account
- We will be deploying over PreRelease Branch
- We will not copy private, uploads appSettings.json file to published repo
What we need to do:
- Generate Github Personal Access Token [PAT] to manage repo
- Configure Github Repo with secrects
- configure windows vps server with repo clone
- Configure github repo with workflow
Generate Github Personal Access Token [PAT]
- Login to Github
- Navigate to Settings -> Developer settings -> fine Grain Access Token
- Generate New -> expiry date -> choose matching org and repo
- Add permission for Content with read and write permission
Configure Windows VPS server with Repo Clone
- Login to windows vps server
- open git bash/terminal and clone destRepo, provide PAT from step 1
- add directory to git safe listing using command: git config –global –add safe.directory C:/{your path}
- check and give ownership of the folder to same user which github self hosted runner is running
Configure github repo with secrets
- Login to github
- navigate to repo
- open gettings -> action secrets
- Add new secrets -> DEPLOY_PAT with value from step 1
- Add new secrets -> DEPLOY_REPO_PATH with folder location from step 2
configure github workflow
- add github repo /.github/workflows/build-artifacts.yml
name: Build .NET and sync to deployment repo (self-hosted)
on:
push:
branches: ["dev"]
workflow_dispatch:
concurrency:
group: build-sync-deploy-repo
cancel-in-progress: false
jobs:
build:
runs-on: [self-hosted, windows, {runner-name}]
env:
CONFIGURATION: Release
# Local path on VPS where deployment repo is already cloned
DEPLOY_REPO_PATH: ${{ secrets.DEPLOY_REPO_PATH }}
# Branch your deployment pipeline listens to
DEPLOY_BRANCH: PreRelease
# Keep last 5 rollback snapshots as branches: PreRelease-bk-1..5
SNAPSHOT_KEEP: "5"
# speed
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: "1"
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
NUGET_XMLDOC_MODE: skip
GIT_TERMINAL_PROMPT: "0"
GCM_INTERACTIVE: "Never"
steps:
- name: Checkout code repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Show .NET SDKs (using preinstalled)
shell: powershell
run: |
dotnet --info
dotnet --list-sdks
# ✅ Set fast persistent NuGet cache
- name: Set NuGet cache location
shell: powershell
run: |
echo "NUGET_PACKAGES=C:\nuget-cache" >> $env:GITHUB_ENV
- name: Restore
shell: powershell
run: dotnet restore {.sln file path from repo root} # eg. .\srv\myapp.sln
- name: Build (no restore)
shell: powershell
run: dotnet build {.sln file path from repo root} -c $env:CONFIGURATION --no-restore
- name: Publish (no build)
shell: powershell
run: |
$ErrorActionPreference = "Stop"
$publishDir = Join-Path $env:GITHUB_WORKSPACE "_publish"
if (Test-Path $publishDir) { Remove-Item $publishDir -Recurse -Force }
New-Item -ItemType Directory -Force -Path $publishDir | Out-Null
# Publish your deployable project (adjust if needed)
dotnet publish {.main_prj file path from repo root} -c $env:CONFIGURATION --no-build -o $publishDir # eg. src\myapp\myapp.csprj
Write-Host "Publish output: $publishDir"
Get-ChildItem $publishDir | Select-Object Name
- name: Verify deployment repo path
shell: powershell
run: |
$ErrorActionPreference = "Stop"
if (!(Test-Path $env:DEPLOY_REPO_PATH)) {
throw "DEPLOY_REPO_PATH not found: $env:DEPLOY_REPO_PATH. Clone the deployment repo there first."
}
if (!(Test-Path (Join-Path $env:DEPLOY_REPO_PATH ".git"))) {
throw "DEPLOY_REPO_PATH is not a git repo: $env:DEPLOY_REPO_PATH"
}
- name: Update deployment repo www and push PreRelease (HTTPS + PAT)
shell: powershell
env:
DEPLOY_PAT: ${{ secrets.DEPLOY_PAT }}
DEPLOY_BRANCH: PreRelease
run: |
$ErrorActionPreference = "Stop"
# Hard-disable prompts/UI
$env:GIT_TERMINAL_PROMPT = "0"
$env:GCM_INTERACTIVE = "Never"
$deployRepo = $env:DEPLOY_REPO_PATH
$branch = $env:DEPLOY_BRANCH
$publishDir = Join-Path $env:GITHUB_WORKSPACE "_publish"
$www = Join-Path $deployRepo "www"
if (!(Test-Path (Join-Path $deployRepo ".git"))) { throw "Not a git repo: $deployRepo" }
if (!(Test-Path $publishDir)) { throw "Publish folder not found: $publishDir" }
cd $deployRepo
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Prevent saving credentials to the VPS
git config --local credential.helper ""
# Ensure origin is HTTPS to github.com (no token stored)
$remote = git remote get-url origin
function ToHttpsGitHub([string]$url) {
if ($url -like "https://github.com/*") { return $url }
if ($url -match "^git@github\.com:(.+)$") { return "https://github.com/" + $Matches[1] }
return $url
}
$cleanRemote = ToHttpsGitHub $remote
if ($cleanRemote -notlike "https://github.com/*") {
throw "Origin remote must be GitHub HTTPS/SSH. Current: $remote"
}
$authRemote = $cleanRemote -replace "^https://", ("https://x-access-token:" + $env:DEPLOY_PAT + "@")
# Use auth remote for ALL git network operations
git remote set-url origin $authRemote
try {
Write-Host "== FETCH =="
git fetch origin $branch
Write-Host "== CHECKOUT =="
git checkout -B $branch FETCH_HEAD
git reset --hard FETCH_HEAD
git clean -fdx
Write-Host "== SYNC WWW =="
if (!(Test-Path $www)) { New-Item -ItemType Directory -Force -Path $www | Out-Null }
# Clean only www contents
Get-ChildItem $www -Force | Remove-Item -Recurse -Force
# Copy published output into www
Copy-Item -Path (Join-Path $publishDir "*") -Destination $www -Recurse -Force
# Remove appsettings*.json from www
Get-ChildItem -Path $www -Filter "appsettings*.json" -File -ErrorAction SilentlyContinue | Remove-Item -Force
Write-Host "== COMMIT =="
git add -A
$status = git status --porcelain
if ([string]::IsNullOrWhiteSpace($status)) {
Write-Host "No changes to commit."
exit 0
}
git commit -m "Sync www from $env:GITHUB_REPOSITORY@$env:GITHUB_SHA"
Write-Host "== PUSH =="
git push origin $branch
}
finally {
# Restore clean remote URL (no token)
git remote set-url origin $cleanRemote
}
- name: Sync publish output to deployment repo www (no appsettings)
shell: powershell
run: |
$ErrorActionPreference = "Stop"
$deployRepo = $env:DEPLOY_REPO_PATH
$www = Join-Path $deployRepo "www"
$publishDir = Join-Path $env:GITHUB_WORKSPACE "_publish"
if (!(Test-Path $www)) { New-Item -ItemType Directory -Force -Path $www | Out-Null }
# Clean only www contents (leave docs and example.appsettings.json outside www untouched)
Get-ChildItem $www -Force | Remove-Item -Recurse -Force
# Copy published output into www
Copy-Item -Path (Join-Path $publishDir "*") -Destination $www -Recurse -Force
# Remove appsettings*.json from www (deployment repo keeps example.appsettings.json outside www)
Get-ChildItem -Path $www -Filter "appsettings*.json" -File -ErrorAction SilentlyContinue | Remove-Item -Force
- name: Snapshot PreRelease (keep 5) + push updated PreRelease (HTTPS PAT, no local credential storage)
shell: powershell
env:
DEPLOY_PAT: ${{ secrets.DEPLOY_PAT }}
DEPLOY_BRANCH: PreRelease
run: |
$ErrorActionPreference = "Stop"
$deployRepo = $env:DEPLOY_REPO_PATH
$branch = $env:DEPLOY_BRANCH
$keep = [int]$env:SNAPSHOT_KEEP
cd $deployRepo
git push
