Skip to content

Commit 96e870d

Browse files
committed
feat: prepare validated nightly catalog refresh automation
1 parent 004209e commit 96e870d

14 files changed

Lines changed: 935 additions & 79 deletions

.github/upstream-watch.official-skills.json

Lines changed: 75 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,78 +9,104 @@
99
"issue_name": "Review official dotnet/skills upstream update",
1010
"notes": "Review external-sources/vendir.lock.yml, sync external-sources/upstreams/dotnet-skills via vendir, rerun scripts/import_external_catalog_sources.py, then regenerate catalog outputs and verify the imported official skills and agents.",
1111
"skills": [
12+
"analyzing-dotnet-performance",
13+
"android-tombstone-symbolication",
14+
"apple-crash-symbolication",
15+
"assertion-quality",
16+
"author-component",
17+
"binlog-failure-analysis",
18+
"binlog-generation",
19+
"build-parallelism",
20+
"build-perf-baseline",
21+
"build-perf-diagnostics",
22+
"check-bin-obj-clash",
23+
"clr-activation-debugging",
24+
"code-testing-agent",
25+
"code-testing-extensions",
26+
"collect-user-input",
27+
"configure-auth",
1228
"configuring-opentelemetry-dotnet",
13-
"minimal-api-file-upload",
29+
"convert-blazor-server-to-webapp",
30+
"convert-to-cpm",
31+
"coordinate-components",
32+
"copy-to-output-directory",
33+
"coverage-analysis",
34+
"crap-score",
35+
"create-blazor-project",
36+
"create-datadriven-aspnetcore",
37+
"csharp-scripts",
38+
"detect-static-dependencies",
39+
"directory-build-organization",
40+
"dotnet-aot-compat",
1441
"dotnet-maui-doctor",
42+
"dotnet-pinvoke",
43+
"dotnet-trace-collect",
44+
"dotnet-webapi",
45+
"dump-collect",
46+
"eval-performance",
47+
"exp-mock-usage-analysis",
48+
"exp-test-maintainability",
49+
"extension-points",
50+
"fetch-and-send-data",
51+
"filter-syntax",
52+
"find-untested-sources",
53+
"generate-testability-wrappers",
54+
"grade-tests",
55+
"including-generated-files",
56+
"incremental-build",
57+
"item-management",
1558
"maui-app-lifecycle",
1659
"maui-collectionview",
1760
"maui-data-binding",
1861
"maui-dependency-injection",
1962
"maui-safe-area",
2063
"maui-shell-navigation",
2164
"maui-theming",
22-
"optimizing-ef-core-queries",
23-
"csharp-scripts",
24-
"dotnet-pinvoke",
25-
"nuget-trusted-publishing",
26-
"mcp-csharp-create",
27-
"mcp-csharp-debug",
28-
"mcp-csharp-publish",
29-
"mcp-csharp-test",
30-
"technology-selection",
31-
"exp-assertion-quality",
32-
"exp-dotnet-test-frameworks",
33-
"exp-mock-usage-analysis",
34-
"exp-simd-vectorization",
35-
"exp-test-gap-analysis",
36-
"exp-test-maintainability",
37-
"exp-test-smell-detection",
38-
"exp-test-tagging",
39-
"dotnet-aot-compat",
65+
"microbenchmarking",
4066
"migrate-dotnet10-to-dotnet11",
4167
"migrate-dotnet8-to-dotnet9",
4268
"migrate-dotnet9-to-dotnet10",
43-
"migrate-nullable-references",
44-
"thread-abort-migration",
45-
"code-testing-agent",
46-
"coverage-analysis",
47-
"crap-score",
48-
"dotnet-test-frameworks",
49-
"filter-syntax",
5069
"migrate-mstest-v1v2-to-v3",
5170
"migrate-mstest-v3-to-v4",
71+
"migrate-nullable-references",
72+
"migrate-static-to-wrapper",
5273
"migrate-vstest-to-mtp",
74+
"migrate-xunit-to-mstest",
5375
"migrate-xunit-to-xunit-v3",
54-
"mtp-hot-reload",
55-
"platform-detection",
56-
"run-tests",
57-
"test-anti-patterns",
58-
"writing-mstest-tests",
59-
"analyzing-dotnet-performance",
60-
"android-tombstone-symbolication",
61-
"clr-activation-debugging",
62-
"dotnet-trace-collect",
63-
"dump-collect",
64-
"microbenchmarking",
65-
"binlog-failure-analysis",
66-
"binlog-generation",
67-
"build-parallelism",
68-
"build-perf-baseline",
69-
"build-perf-diagnostics",
70-
"check-bin-obj-clash",
71-
"directory-build-organization",
72-
"eval-performance",
73-
"including-generated-files",
74-
"incremental-build",
76+
"minimal-api-file-upload",
7577
"msbuild-antipatterns",
7678
"msbuild-modernization",
7779
"msbuild-server",
80+
"mtp-hot-reload",
81+
"nuget-trusted-publishing",
82+
"optimizing-ef-core-queries",
83+
"plan-ui-change",
84+
"platform-detection",
85+
"property-patterns",
7886
"resolve-project-references",
79-
"convert-to-cpm",
87+
"run-tests",
88+
"scaffold-dotnet-test-project",
89+
"setup-local-sdk",
90+
"support-prerendering",
91+
"system-text-json-net11",
92+
"target-authoring",
93+
"technology-selection",
8094
"template-authoring",
95+
"template-comparison",
8196
"template-discovery",
8297
"template-instantiation",
83-
"template-validation"
98+
"template-smart-defaults",
99+
"template-validation",
100+
"test-analysis-extensions",
101+
"test-anti-patterns",
102+
"test-gap-analysis",
103+
"test-smell-detection",
104+
"test-tagging",
105+
"testability-obstacle",
106+
"thread-abort-migration",
107+
"use-js-interop",
108+
"vectorization",
109+
"writing-mstest-tests"
84110
]
85111
}
86112
]

.github/workflows/catalog-check.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
name: PR Checks
22

33
on:
4+
workflow_call:
5+
inputs:
6+
ref:
7+
description: Immutable catalog refresh commit to validate
8+
required: true
9+
type: string
410
workflow_dispatch:
511
pull_request:
612

@@ -18,6 +24,9 @@ jobs:
1824
steps:
1925
- name: Check out repository
2026
uses: actions/checkout@v6
27+
with:
28+
ref: ${{ inputs.ref || github.sha }}
29+
persist-credentials: false
2130

2231
- name: Set up Python
2332
uses: actions/setup-python@v6
@@ -50,7 +59,7 @@ jobs:
5059
run: python3 scripts/import_external_catalog_sources.py --validate-config
5160

5261
- name: Sync and normalize external sources
53-
run: bash scripts/sync_external_catalog_sources.sh
62+
run: bash scripts/sync_external_catalog_sources.sh --locked
5463

5564
- name: Verify imported external sources are committed
5665
run: git diff --exit-code -- catalog external-sources
@@ -95,6 +104,9 @@ jobs:
95104
steps:
96105
- name: Check out repository
97106
uses: actions/checkout@v6
107+
with:
108+
ref: ${{ inputs.ref || github.sha }}
109+
persist-credentials: false
98110

99111
- name: Set up .NET
100112
uses: actions/setup-dotnet@v5

.github/workflows/publish-catalog.yml

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ jobs:
4848
- name: Check out repository
4949
uses: actions/checkout@v6
5050
with:
51+
ref: main
5152
fetch-depth: 0
5253

5354
- name: Check for new commits on main since latest catalog release
@@ -57,16 +58,19 @@ jobs:
5758
shell: bash
5859
run: |
5960
git fetch --force --tags origin main
61+
git checkout --detach origin/main
6062
6163
latest_tag=$(gh api "repos/${{ github.repository }}/releases?per_page=100" --jq 'map(select(.draft == false and (.tag_name | startswith("'"${{ env.CATALOG_TAG_PREFIX }}"'")))) | sort_by(.published_at) | reverse | (.[0].tag_name // "")')
6264
main_ref="origin/main"
6365
force_release='${{ inputs.force_release }}'
6466
6567
if [[ "${{ github.event_name }}" == "workflow_dispatch" && "$force_release" == "true" ]]; then
66-
echo "has_new_commits=true" >> "$GITHUB_OUTPUT"
67-
echo "latest_tag=$latest_tag" >> "$GITHUB_OUTPUT"
68-
echo "override=true" >> "$GITHUB_OUTPUT"
69-
echo "reason=manual_force_release" >> "$GITHUB_OUTPUT"
68+
{
69+
echo "has_new_commits=true"
70+
echo "latest_tag=$latest_tag"
71+
echo "override=true"
72+
echo "reason=manual_force_release"
73+
} >> "$GITHUB_OUTPUT"
7074
exit 0
7175
fi
7276
@@ -76,9 +80,11 @@ jobs:
7680
else
7781
echo "has_new_commits=false" >> "$GITHUB_OUTPUT"
7882
fi
79-
echo "latest_tag=" >> "$GITHUB_OUTPUT"
80-
echo "override=false" >> "$GITHUB_OUTPUT"
81-
echo "reason=initial_catalog_release" >> "$GITHUB_OUTPUT"
83+
{
84+
echo "latest_tag="
85+
echo "override=false"
86+
echo "reason=initial_catalog_release"
87+
} >> "$GITHUB_OUTPUT"
8288
exit 0
8389
fi
8490
@@ -90,9 +96,11 @@ jobs:
9096
echo "has_new_commits=false" >> "$GITHUB_OUTPUT"
9197
fi
9298
93-
echo "latest_tag=$latest_tag" >> "$GITHUB_OUTPUT"
94-
echo "override=false" >> "$GITHUB_OUTPUT"
95-
echo "reason=$([ "$commits_since_tag" -gt 0 ] && echo commits_since_last_release || echo no_new_commits)" >> "$GITHUB_OUTPUT"
99+
{
100+
echo "latest_tag=$latest_tag"
101+
echo "override=false"
102+
echo "reason=$([ "$commits_since_tag" -gt 0 ] && echo commits_since_last_release || echo no_new_commits)"
103+
} >> "$GITHUB_OUTPUT"
96104
97105
- name: Skip release when no new commits exist
98106
if: steps.changes.outputs.has_new_commits != 'true'

.github/workflows/upstream-watch.yml

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
name: Upstream Watch
1+
name: Nightly Catalog Refresh
22

33
on:
44
workflow_dispatch:
55
schedule:
6-
- cron: "17 9 * * *"
6+
- cron: "17 0 * * *"
77

88
permissions:
99
contents: write
1010
issues: write
11+
pull-requests: write
1112

1213
concurrency:
1314
group: upstream-watch
@@ -19,10 +20,19 @@ env:
1920
jobs:
2021
watch:
2122
runs-on: ubuntu-latest
23+
timeout-minutes: 180
24+
outputs:
25+
changed: ${{ steps.publish.outputs.changed }}
26+
head: ${{ steps.publish.outputs.head }}
27+
base: ${{ steps.publish.outputs.base }}
28+
pr: ${{ steps.publish.outputs.pr }}
2229

2330
steps:
2431
- name: Check out repository
2532
uses: actions/checkout@v6
33+
with:
34+
ref: main
35+
fetch-depth: 0
2636

2737
- name: Set up Python
2838
uses: actions/setup-python@v6
@@ -57,8 +67,89 @@ jobs:
5767
run: python3 scripts/upstream_watch.py --state "${{ env.UPSTREAM_WATCH_STATE_FILE }}"
5868

5969
- name: Save upstream watch state cache
60-
if: always()
6170
uses: actions/cache/save@v4
6271
with:
6372
path: ${{ env.UPSTREAM_WATCH_STATE_FILE }}
6473
key: upstream-watch-state-${{ github.repository_id }}-${{ github.ref_name }}-${{ github.run_id }}
74+
75+
- name: Install vendir
76+
run: |
77+
mkdir -p .tools
78+
curl -fsSL https://carvel.dev/install.sh | K14SIO_INSTALL_BIN_DIR="$PWD/.tools" bash
79+
echo "$PWD/.tools" >> "$GITHUB_PATH"
80+
81+
- name: Refresh external catalog sources
82+
run: bash scripts/sync_external_catalog_sources.sh
83+
84+
- name: Refresh pending watched skills
85+
env:
86+
GH_TOKEN: ${{ github.token }}
87+
NIGHTLY_SKILL_REFRESH_COMMAND: ${{ vars.NIGHTLY_SKILL_REFRESH_COMMAND }}
88+
run: python3 scripts/nightly_skill_refresh.py --state "${{ env.UPSTREAM_WATCH_STATE_FILE }}"
89+
90+
- name: Validate refreshed catalog
91+
run: |
92+
python3 scripts/generate_catalog.py --validate-only
93+
python3 scripts/generate_agent_catalog.py --validate-only
94+
python3 scripts/generate_pages.py
95+
96+
- name: Create or update nightly pull request
97+
id: publish
98+
env:
99+
GH_TOKEN: ${{ github.token }}
100+
run: python3 scripts/nightly_catalog_pr.py publish
101+
102+
- name: Upload refresh report
103+
if: always()
104+
uses: actions/upload-artifact@v4
105+
with:
106+
name: nightly-refresh
107+
path: artifacts/nightly-refresh/
108+
retention-days: 7
109+
110+
validate:
111+
needs: watch
112+
if: needs.watch.outputs.changed == 'true'
113+
uses: ./.github/workflows/catalog-check.yml
114+
with:
115+
ref: ${{ needs.watch.outputs.head }}
116+
117+
merge:
118+
needs: [watch, validate]
119+
if: needs.validate.result == 'success'
120+
runs-on: ubuntu-latest
121+
timeout-minutes: 10
122+
steps:
123+
- uses: actions/checkout@v6
124+
with:
125+
persist-credentials: false
126+
- name: Merge the verified catalog commit
127+
env:
128+
GH_TOKEN: ${{ github.token }}
129+
PR_NUMBER: ${{ needs.watch.outputs.pr }}
130+
VALIDATED_HEAD: ${{ needs.watch.outputs.head }}
131+
VALIDATED_BASE: ${{ needs.watch.outputs.base }}
132+
run: python3 scripts/nightly_catalog_pr.py merge --pr "$PR_NUMBER" --head "$VALIDATED_HEAD" --base "$VALIDATED_BASE"
133+
134+
report:
135+
needs: [watch, validate, merge]
136+
if: always()
137+
runs-on: ubuntu-latest
138+
timeout-minutes: 10
139+
steps:
140+
- uses: actions/checkout@v6
141+
with:
142+
persist-credentials: false
143+
- name: Report failure or close recovered failure issue
144+
env:
145+
GH_TOKEN: ${{ github.token }}
146+
WATCH_RESULT: ${{ needs.watch.result }}
147+
VALIDATION_RESULT: ${{ needs.validate.result }}
148+
MERGE_RESULT: ${{ needs.merge.result }}
149+
CHANGED: ${{ needs.watch.outputs.changed }}
150+
run: |
151+
args=()
152+
if [[ "$WATCH_RESULT" != success ]] || [[ "$CHANGED" == true && ( "$VALIDATION_RESULT" != success || "$MERGE_RESULT" != success ) ]]; then
153+
args+=(--failed)
154+
fi
155+
python3 scripts/nightly_catalog_pr.py report "${args[@]}" --details "Refresh: $WATCH_RESULT; validation: $VALIDATION_RESULT; merge: $MERGE_RESULT."

0 commit comments

Comments
 (0)