Skip to content

About

GitHub Actions 可重用工作流程範本,支援 NuGet 發佈與 DocFX 網站建置。

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

20 Commits

Folders and files

Repository files navigation

🛠️ DevOps Templates

Self Test

個人的共用 CI/CD 流程庫,用來統一所有 .NET 專案的建置、測試、發佈與文件流程。

📦 內容一覽

類型 路徑 用途
Reusable Workflow .github/workflows/reusable-dotnet-ci.yml Restore → Build → Test(→ Pack),PR 與發版前驗證
Reusable Workflow .github/workflows/reusable-create-release.yml 從 CHANGELOG 建立 GitHub Release,可附上 artifact
Reusable Workflow .github/workflows/reusable-docfx-publish.yml 建置 DocFX 文件並部署至 GitHub Pages
Reusable Workflow .github/workflows/reusable-winget-submit.yml 以現成的 WinGet manifest 向 microsoft/winget-pkgs 提交 PR
Composite Action .github/actions/nuget-publish 以 Trusted Publishing 打包並發佈至 nuget.org
Composite Action .github/actions/velopack-pack 以 Velopack 打包桌面應用程式安裝檔
Reusable Workflow .github/workflows/reusable-nuget-publish.yml ⚠️ Deprecated:以 API Key 發佈 NuGet,請改用 nuget-publish

🏷️ 版本策略

  • 依 SemVer 發佈 vX.Y.Z tag,並自動將 major tag(如 v1)移到最新的相容版本。
  • Caller 請以 @v1 引用,不要用 @main:main 上的任何變更都會立刻影響所有 caller。
  • 有破壞性變更時才會升 major(v2),既有 @v1 不受影響。
  • 搭配 Dependabot(package-ecosystem: github-actions)可自動收到升版 PR。

🧪 1. .NET CI

on:
  pull_request:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  ci:
    uses: CloudyWing/devops-templates/.github/workflows/reusable-dotnet-ci.yml@v1
    with:
      solution: MyLib.slnx
      warnaserror: true
參數 預設值 說明
solution '' 方案或專案路徑;留空時於根目錄執行
dotnet-version 10.0.x SDK 版本,可多行指定多個版本
global-json-file '' 設定後依 global.json 安裝 SDK,忽略 dotnet-version
runs-on windows-latest 含 .NET Framework 目標時請維持 Windows
configuration Release 建置組態
warnaserror false 以 -warnaserror 建置
build-args / test-args '' 額外傳給 dotnet build / dotnet test 的參數
test true 是否執行測試;測試結果(trx)會上傳為 test-results artifact
pack false 是否打包並上傳為 packages artifact
artifact-suffix '' 同一 workflow 呼叫多次時,避免 artifact 名稱衝突

🚀 2. 發版:GitHub Release + NuGet(Trusted Publishing)

前置需求

  1. 至 nuget.org 建立 Trusted Publishing policy:
    • Repository Owner:CloudyWing
    • Repository:套件 repo 名稱
    • Workflow File:release.yml(caller 自己的 workflow 檔名)
  2. 不需要 NUGET_API_KEY secret,已存在的可以刪除。

引用範例

on:
  push:
    tags: ['v*.*.*']
  workflow_dispatch:   # 手動觸發時只執行 probe,驗證 Trusted Publishing 綁定

permissions:
  contents: read

jobs:
  verify:
    if: github.event_name == 'push'
    uses: CloudyWing/devops-templates/.github/workflows/reusable-dotnet-ci.yml@v1
    with:
      solution: MyLib.slnx

  create-release:
    needs: verify
    permissions:
      contents: write
    uses: CloudyWing/devops-templates/.github/workflows/reusable-create-release.yml@v1

  publish-nuget:
    needs: create-release
    runs-on: windows-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-dotnet@v6
        with:
          dotnet-version: 10.0.x
      - uses: CloudyWing/devops-templates/.github/actions/nuget-publish@v1
        with:
          nuget-user: CloudyWing
          expected-version: ${{ github.ref_name }}

  probe:
    if: github.event_name == 'workflow_dispatch'
    runs-on: windows-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-dotnet@v6
        with:
          dotnet-version: 10.0.x
      - uses: CloudyWing/devops-templates/.github/actions/nuget-publish@v1
        with:
          nuget-user: CloudyWing
          dry-run: 'true'

nuget-publish 刻意做成 composite action:nuget.org 會驗證 OIDC token 的 job_workflow_ref,必須指向套件 repo 自己的 workflow。若改成 reusable workflow,claim 會指向本 repo,policy 就得授權給本 repo,等於所有 caller 都能發佈。

reusable-create-release 參數

參數 預設值 說明
changelog-path CHANGELOG.md CHANGELOG 路徑
tag '' 留空時使用觸發的 tag
artifact-name '' 同一 run 內上傳的 artifact,會成為 Release 附件
draft false 保留為草稿
dry-run false 只解析 Release Notes,不建立 Release
  • 支援的 CHANGELOG 標題:## v1.2.3 (2026-01-01)、## 1.2.3、## [v1.2.3] - 2026-01-01(Keep a Changelog)。找不到對應段落時改用自動產生的 Release Notes。
  • 版號含 -(如 v1.0.0-beta.1)時自動標為 prerelease。
  • Release 已存在(例如重跑 workflow)時會更新內容並覆蓋附件,不會失敗。
  • Outputs:tag、prerelease、notes-source(changelog / generated)。

nuget-publish 參數

參數 預設值 說明
nuget-user '' nuget.org profile name;pack-only 時可省略
project-path ./src 打包對象的搜尋根目錄
artifacts-path ./artifacts nupkg 輸出目錄
expected-version '' 檢查每個 nupkg 版號是否等於此值,避免 tag 與 csproj 不一致
dry-run 'false' 取得憑證後即結束,用來驗證 policy 綁定
pack-only 'false' 只打包,不登入也不 push
  • Outputs:packages(產出的 nupkg 檔名,以換行分隔)。

🖥️ 3. 桌面應用程式:Velopack + GitHub Release

jobs:
  build:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-dotnet@v6
        with:
          dotnet-version: 10.0.x
      - run: dotnet publish src/MyApp/MyApp.csproj -c Release -r win-x64 --self-contained -o artifacts/publish
      - uses: CloudyWing/devops-templates/.github/actions/velopack-pack@v1
        with:
          version: ${{ github.ref_name }}
          pack-id: CloudyWing.MyApp
          main-exe: MyApp.exe
          pack-dir: artifacts/publish
          output-dir: artifacts/velopack
          vpk-version: '1.2.161'   # 必須與專案引用的 Velopack 套件版本一致
          delta: 'true'
          github-token: ${{ github.token }}
          # 每行一個參數;旗標與其值各佔一行,值含空白時直接寫原文,不加引號
          extra-args: |
            --packTitle
            My Desktop App
            --packAuthors
            My Company
            --framework
            webview2
            --runtime
            win-x64
            --icon
            assets/app icon.ico
            --instReadme
            docs/install readme.md
      - uses: actions/upload-artifact@v7
        with:
          name: velopack
          path: artifacts/velopack/*

  release:
    needs: build
    permissions:
      contents: write
    uses: CloudyWing/devops-templates/.github/workflows/reusable-create-release.yml@v1
    with:
      artifact-name: velopack
參數 預設值 說明
version / pack-id / main-exe / pack-dir / output-dir — 必填
vpk-version '' 留空時使用 .config/dotnet-tools.json 的 local tool
channel '' Velopack channel
delta 'false' 先下載前一版 Release,產生 delta 更新包;找不到前一版時只會警告
github-token '' 下載前一版時使用(private repo 必填)
extra-args '' 額外傳給 vpk pack 的參數,每行一個參數(規則見下方說明)
  • extra-args 以換行切分,每個非空白行是一個獨立的 vpk 參數,原樣傳入。旗標與其值各佔一行,例如 --packTitle 換行 My Desktop App。
  • 值含空白(如標題或含空白的路徑)時獨立一行,直接寫原文。引號字元會成為參數內容,因此不加引號。
  • 單行寫法如 --icon app.ico 會被視為單一參數,由 vpk 以非零 exit code 回報失敗。
  • Outputs:vpk-command(實際使用的 vpk 指令,vpk 或 dotnet vpk)。

📚 4. DocFX 文件網站

前置需求

  1. Settings > Pages > Source 設為 GitHub Actions(不再使用 gh-pages branch)。
  2. 呼叫的 job 需授予 pages: write、id-token: write。
on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  docs:
    permissions:
      contents: read
      pages: write
      id-token: write
    uses: CloudyWing/devops-templates/.github/workflows/reusable-docfx-publish.yml@v1
    with:
      docfx-config: docs/docfx.json
參數 預設值 說明
docfx-config docfx.json docfx.json 路徑
site-dir _site DocFX 輸出目錄,需與 docfx.json 的 build.output 一致
dotnet-version 10.0.x SDK 版本
docfx-version 2.81.0 固定 DocFX 版本,確保建置可重現
runs-on windows-latest Runner label
deploy true 為 false 時只建置不部署,適合 PR 驗證
  • Outputs:page-url(部署後的網址,deploy 為 true 時)。

📤 5. WinGet 套件提交

以 caller 提供的現成 WinGet manifest 向 microsoft/winget-pkgs 提交 PR。本範本不重新產生 manifest,PackageIdentifier、PackageVersion、InstallerUrl 與各 locale 內容都以 caller 的檔案為準。

前置需求

  1. 建立 classic Personal Access Token 並勾選 public_repo(fine-grained token 不支援),存成 caller repo 的 secret,例如 WINGET_TOKEN。
  2. token 擁有者帳號下沒有 winget-pkgs fork 時,wingetcreate 會自動建立,不需要事先 fork。
  3. 在建置 job 將 manifest 上傳為 artifact,內容須已是最終版號。檔案可直接位於 artifact 根目錄,或位於一層版本子目錄(如 1.2.3/),且恰有一個 *.installer.yaml。
  4. 呼叫的 job 以 needs 相依於建立 Release 的 job,確保 InstallerUrl 指向的 Release 資產已公開。

引用範例

on:
  push:
    tags: ['v*.*.*']

permissions:
  contents: read

jobs:
  build:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v7
      # 建置、打包,並產生含最終版號的 WinGet manifest 到 artifacts/winget/
      - uses: actions/upload-artifact@v7
        with:
          name: release-assets
          path: artifacts/release/*
      - uses: actions/upload-artifact@v7
        with:
          name: winget-manifest
          path: artifacts/winget/*

  create-release:
    needs: build
    permissions:
      contents: write
    uses: CloudyWing/devops-templates/.github/workflows/reusable-create-release.yml@v1
    with:
      artifact-name: release-assets

  submit-winget:
    needs: create-release
    permissions:
      contents: read
    uses: CloudyWing/devops-templates/.github/workflows/reusable-winget-submit.yml@v1
    with:
      manifest-artifact-name: winget-manifest
      package-identifier: MyCompany.MyApp
    secrets:
      winget-token: ${{ secrets.WINGET_TOKEN }}

reusable-winget-submit 參數

參數 預設值 說明
manifest-artifact-name — 必填。同一 workflow run 內已上傳的 artifact 名稱,內容為 WinGet manifest
package-identifier — 必填。WinGet PackageIdentifier,須與 installer manifest 的 PackageIdentifier 逐字相同
tag '' 要提交的版本 tag;留空時,觸發的 ref 為 tag 才使用該 tag。明確指定時視為 tag 觸發,不論 ref 型別
dry-run false 為 true 時執行到準備提交為止,不呼叫 wingetcreate submit,也不需要 winget-token
url-check-timeout-seconds 300 InstallerUrl 檢查的總逾時秒數,所有 URL 共用;下載 wingetcreate 時另以相同秒數作為重試期限
runs-on windows-latest Runner label,wingetcreate 為 Windows 執行檔,須為 Windows runner
  • Secret:winget-token(選填,classic PAT;判定需提交且 dry-run 為 false 時必填,跳過或 dry-run 時不檢查)。
  • Outputs:result(submitted、dry-run 或 skipped)、skip-reason(result 為 skipped 時為 not-a-tag、prerelease 或 open-pr-exists)、pull-request-url(submitted 時為新 PR 網址,open-pr-exists 時為既有 PR 網址)、version(提交版號,tag 去除開頭的 v)。
  • 只在正式版 tag 提交。沒有 tag 時以 not-a-tag 跳過,版號含 -(如 v1.0.0-beta.1)時以 prerelease 跳過,兩者都視為成功結束。
  • manifest 的 PackageIdentifier 須等於 package-identifier,PackageVersion 須等於 tag 去除 v 後的版號,不一致時失敗,避免提交來自其他版本的 manifest。
  • 提交前確認 manifest 內每個 InstallerUrl 都以 https:// 開頭且可下載,在 url-check-timeout-seconds 內每 10 秒重試一次,逾時仍未通過則失敗。
  • microsoft/winget-pkgs 已有同套件同版本的 open PR 時以 open-pr-exists 跳過,不重複提交。
  • wingetcreate 的版本與 SHA-256 固定於 workflow 內,下載失敗時在 url-check-timeout-seconds 內每 10 秒重試一次,下載後會校驗;升級時需修改 workflow 並發新版,不提供 input。
  • 本範本的 dry-run 涵蓋版號判定、manifest 解析、去重查詢、InstallerUrl 檢查與工具校驗,不涵蓋 wingetcreate 對 manifest 的解析與實際提交,這兩項在第一次真實提交時才會驗證。

✅ 驗證方式

self-test.yml 會在每次 push / PR 時,以 tests/fixtures 的範例專案實際執行所有範本,不發佈任何套件:

  • actionlint(含 shellcheck)檢查所有 workflow
  • reusable-dotnet-ci:Ubuntu 與 Windows 各跑一次 build、test、pack
  • nuget-publish:pack-only 模式,並驗證版號不符、前面專案編譯失敗時一定會失敗
  • reusable-create-release:以 dry-run 驗證四種 CHANGELOG 情境
  • reusable-docfx-publish:deploy: false 只建置
  • velopack-pack:實際打包範例程式
  • reusable-winget-submit:以範例 manifest 執行 dry-run,並驗證預發布版本與非 tag 的跳過輸出

手動觸發並勾選 e2e-release 時,會在本 repo 建立暫時的 prerelease(含附件)、重跑一次驗證更新流程,最後刪除該 Release 與 tag。

Trusted Publishing 的 OIDC 綁定只能在各套件 repo 驗證:手動觸發套件 repo 的 release.yml,probe job 會以 dry-run 取得臨時憑證但不 push。

License

This project is MIT licensed.

About

GitHub Actions 可重用工作流程範本,支援 NuGet 發佈與 DocFX 網站建置。

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors