Skip to content

Merge pull request #45 from 1ngaOS/feat/develoop #12

Merge pull request #45 from 1ngaOS/feat/develoop

Merge pull request #45 from 1ngaOS/feat/develoop #12

name: wangamail-csharp
on:
pull_request:
types: [opened, synchronize, reopened]
paths:
- 'wangamail-csharp/**'
- '.github/workflows/wangamail-csharp.yml'
push:
branches: [main]
paths:
- 'wangamail-csharp/**'
- '.github/workflows/wangamail-csharp.yml'
workflow_dispatch:
jobs:
format:
name: Format
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
run: |
dotnet restore wangamail-csharp/WangaMail.CSharp.csproj
dotnet restore wangamail-csharp/tests/WangaMail.CSharp.Tests/WangaMail.CSharp.Tests.csproj
- name: Check formatting
run: dotnet format wangamail-csharp/WangaMail.CSharp.csproj --verify-no-changes --verbosity minimal
- name: Job summary
if: always()
run: |
echo "## Format" >> $GITHUB_STEP_SUMMARY
echo "**Status:** ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
build:
name: Build
runs-on: ubuntu-latest
needs: format
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
run: |
dotnet restore wangamail-csharp/WangaMail.CSharp.csproj
dotnet restore wangamail-csharp/tests/WangaMail.CSharp.Tests/WangaMail.CSharp.Tests.csproj
- name: Build
run: dotnet build wangamail-csharp/WangaMail.CSharp.csproj -c Release --no-restore
- name: Job summary
if: always()
run: |
echo "## Build" >> $GITHUB_STEP_SUMMARY
echo "**Status:** ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
test:
name: Test
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
run: |
dotnet restore wangamail-csharp/WangaMail.CSharp.csproj
dotnet restore wangamail-csharp/tests/WangaMail.CSharp.Tests/WangaMail.CSharp.Tests.csproj
- name: Test
run: dotnet test wangamail-csharp/tests/WangaMail.CSharp.Tests/WangaMail.CSharp.Tests.csproj -c Release --no-restore
- name: Job summary
if: always()
run: |
echo "## Test" >> $GITHUB_STEP_SUMMARY
echo "**Status:** ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
version-bump:
name: Bump Version
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
outputs:
version: ${{ steps.bump.outputs.new_version }}
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get current version
id: current
run: |
python3 - <<'PY'
import os
import re
from pathlib import Path
p = Path("wangamail-csharp/WangaMail.CSharp.csproj")
text = p.read_text(encoding="utf-8")
m = re.search(r"<Version>(\d+\.\d+\.\d+)</Version>", text)
if not m:
raise SystemExit("Error: Failed to extract Version from WangaMail.CSharp.csproj")
version = m.group(1)
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
f.write(f"version={version}\n")
print(f"Current version: {version}")
PY
- name: Bump patch version
id: bump
run: |
V="${{ steps.current.outputs.version }}"
IFS='.' read -r MAJOR MINOR PATCH <<< "$V"
PATCH=$((PATCH + 1))
NEW="${MAJOR}.${MINOR}.${PATCH}"
echo "new_version=$NEW" >> $GITHUB_OUTPUT
python3 - <<PY
import re
from pathlib import Path
p = Path("wangamail-csharp/WangaMail.CSharp.csproj")
text = p.read_text(encoding="utf-8")
text2, n = re.subn(
r"<Version>\d+\.\d+\.\d+</Version>",
"<Version>${NEW}</Version>",
text,
count=1,
)
if n != 1:
raise SystemExit("Error: Failed to update Version in WangaMail.CSharp.csproj")
p.write_text(text2, encoding="utf-8")
print("New version: ${NEW}")
PY
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Commit and push version
run: |
git add wangamail-csharp/WangaMail.CSharp.csproj
if git diff --staged --quiet; then
echo "No version change to commit"
else
git commit -m "chore(wangamail-csharp): bump version to ${{ steps.bump.outputs.new_version }} [skip ci]"
git fetch origin main || true
git pull origin main --rebase || git pull origin main --no-rebase || true
# Retry push to avoid stale-ref/lock failures when remote updates
for _ in 1 2 3; do
if git push origin main; then
break
fi
git fetch origin main || true
git pull origin main --rebase || git pull origin main --no-rebase || true
git push origin main --force-with-lease || true
done
fi
- name: Job summary
if: always()
run: |
echo "## Bump Version" >> $GITHUB_STEP_SUMMARY
echo "**New version:** ${{ steps.bump.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY
build-release:
name: Build
runs-on: ubuntu-latest
needs: version-bump
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Pull latest (version bump)
run: git pull origin main || true
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
run: dotnet restore wangamail-csharp/WangaMail.CSharp.csproj
- name: Build release
run: dotnet build wangamail-csharp/WangaMail.CSharp.csproj -c Release --no-restore
- name: Job summary
if: always()
run: |
echo "## Build" >> $GITHUB_STEP_SUMMARY
echo "**Status:** ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
get-version:
name: Get Version
runs-on: ubuntu-latest
needs: [version-bump, build-release]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Pull latest
run: git pull origin main || true
- name: Get version
id: version
run: |
V=$(python3 - <<'PY'
import re
from pathlib import Path
text = Path("wangamail-csharp/WangaMail.CSharp.csproj").read_text(encoding="utf-8")
m = re.search(r"<Version>(\d+\.\d+\.\d+)</Version>", text)
print(m.group(1) if m else "")
PY
)
if [ -z "$V" ]; then
echo "Error: Failed to extract Version from WangaMail.CSharp.csproj"
exit 1
fi
echo "version=$V" >> $GITHUB_OUTPUT
echo "Version: $V"
- name: Job summary
if: always()
run: |
echo "## Get Version" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
publish:
name: Publish to NuGet
runs-on: ubuntu-latest
needs: get-version
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Pull latest
run: git pull origin main || true
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
run: dotnet restore wangamail-csharp/WangaMail.CSharp.csproj
- name: Pack
run: dotnet pack wangamail-csharp/WangaMail.CSharp.csproj -c Release --no-restore
- name: Publish to NuGet
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
dotnet nuget push "wangamail-csharp/bin/Release/*.nupkg" \
--api-key "$NUGET_API_KEY" \
--source "https://api.nuget.org/v3/index.json" \
--skip-duplicate
- name: Job summary
if: always()
run: |
echo "## Publish" >> $GITHUB_STEP_SUMMARY
echo "**Status:** ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ needs.get-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
release:
name: Create Release
runs-on: ubuntu-latest
needs: [get-version, publish]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Create tag and release
uses: softprops/action-gh-release@v2
with:
tag_name: wangamail-csharp-v${{ needs.get-version.outputs.version }}
name: wangamail-csharp v${{ needs.get-version.outputs.version }}
body: |
## wangamail-csharp ${{ needs.get-version.outputs.version }}
Send email via Microsoft Graph API using app registration (client credentials).
- [NuGet](https://www.nuget.org/packages/wangamail-csharp)
draft: false
prerelease: false
- name: Job summary
if: always()
run: |
echo "## Release" >> $GITHUB_STEP_SUMMARY
echo "**Status:** ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
echo "**Tag:** wangamail-csharp-v${{ needs.get-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
monitor-failures:
name: Monitor Failed Jobs
runs-on: ubuntu-latest
if: always() && failure()
needs: [format, build, test, version-bump, build-release, get-version, publish, release]
permissions:
contents: read
issues: write
actions: read
steps:
- uses: actions/checkout@v4
- name: Create failure issue
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: run } = await github.rest.actions.getWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId
});
const { data: jobsData } = await github.rest.actions.listJobsForWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId
});
const failedJobs = jobsData.jobs.filter(job =>
job.conclusion === 'failure' ||
job.conclusion === 'cancelled' ||
job.conclusion === 'timed_out'
);
if (failedJobs.length === 0) {
return;
}
const branch = context.ref.replace('refs/heads/', '');
const title = `🚨 CI/CD Failure: wangamail-csharp - ${branch} branch`;
let body = `## CI/CD Pipeline Failure\n\n`;
body += `**Workflow:** [wangamail-csharp](${run.html_url})\n`;
body += `**Branch:** \`${branch}\`\n`;
body += `**Commit:** [${context.sha.substring(0, 7)}](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/commit/${context.sha})\n\n`;
body += `### Failed Jobs (${failedJobs.length})\n\n`;
for (const job of failedJobs) {
body += `- **${job.name}**: ${job.conclusion} ([logs](${job.html_url}))\n`;
}
const milestoneTitle = 'CI/CD Failures';
let milestoneNumber = null;
try {
const { data: milestones } = await github.rest.issues.listMilestones({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open'
});
let milestone = milestones.find(m => m.title === milestoneTitle);
if (!milestone) {
const { data: created } = await github.rest.issues.createMilestone({
owner: context.repo.owner,
repo: context.repo.repo,
title: milestoneTitle,
description: 'Track all CI/CD pipeline failures across the repository',
state: 'open'
});
milestone = created;
}
milestoneNumber = milestone.number;
} catch (e) {
console.log('Milestone setup failed:', e.message);
}
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'ci-failure,wangamail-csharp'
});
const existing = issues.find(issue => issue.title === title);
if (existing) {
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existing.number,
body,
state: 'open',
milestone: milestoneNumber
});
} else {
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
body,
labels: ['ci-failure', 'wangamail-csharp', 'bug', 'priority-high'],
milestone: milestoneNumber
});
}
- name: Job summary
if: always()
run: |
echo "## Monitor Failed Jobs" >> $GITHUB_STEP_SUMMARY
echo "**Status:** ${{ job.status }}" >> $GITHUB_STEP_SUMMARY