cleanup-unreferenced-packages
Intelligently removes unreferenced packages from source-build-assets (SBA). Accounts for pending-uptake packages, dependency graphs, and flow timing between SBA and the VMR.
소스 정보
- 저장소
- dotnet/source-build-assets
- 최근 소스 활동
- 2026년 6월 15일 21:53
- 감지된 SKILL.md 언어
- 영어
- 스타
- 54
- 포크
- 74
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
파일 탐색기
2 개 파일SKILL.md 표시 중
SKILL.md
소스 지침 · 읽기 전용 미리보기- name
- cleanup-unreferenced-packages
- description
- Intelligently removes unreferenced packages from source-build-assets (SBA). Accounts for pending-uptake packages, dependency graphs, and flow timing between SBA and the VMR.
# Cleanup Unreferenced Packages
## Prerequisites
- The `gh` CLI must be authenticated with access to `dotnet/dotnet` (the VMR) and
`dotnet/source-build-assets` repos.
- The working directory must be the root of the `source-build-assets` repository.
- The local repo **must** be synced with upstream (`dotnet/source-build-assets`) main,
not a personal fork's main. Before starting, ensure the working tree reflects upstream:
```bash
git remote add upstream https://github.com/dotnet/source-build-assets.git 2>/dev/null || true
git fetch upstream
git checkout upstream/main
```
All package existence checks and deletions must be performed against **upstream main**,
not a fork's potentially stale main branch.
## Workflow
### Step 1: Obtain the SBA Usage Report
Download `sbaPackageUsage.json` from the latest successful VMR unified build
([dotnet-unified-build, definition 1330](https://dev.azure.com/dnceng/internal/_build?definitionId=1330))
on the **main branch**. This is an Azure DevOps pipeline.
```bash
# Find the latest successful build on main
az pipelines runs list \
--organization https://dev.azure.com/dnceng \
--project internal \
--pipeline-ids 1330 \
--branch main \
--status completed \
--result succeeded \
--top 1 \
--query "[0].id" -o tsv
```
Next, find the artifact containing the usage report. The artifact name follows the pattern
`SB_<distro>_Online_MsftSdk_x64_BuildLogs_Attempt1` where `<distro>` is the current
CentOS Stream version (e.g., `CentOSStream10`). List the build's artifacts to find the
matching one:
```bash
# List artifacts to find the CentOS Stream source-build leg
az pipelines runs artifact list \
--organization https://dev.azure.com/dnceng \
--project internal \
--run-id <build_id> \
--query "[?contains(name, 'SB_CentOSStream')].name" -o tsv
```
Download the report from the identified artifact:
```bash
az pipelines runs artifact download \
--organization https://dev.azure.com/dnceng \
--project internal \
--run-id <build_id> \
--artifact-name "<artifact_name>" \
--path /tmp/vmr-report
```
If the `az` CLI is not available, the artifact can also be downloaded via the AzDO REST API
using `curl` with a PAT, or the user can provide a local path to the report directly.
Parse the JSON file and extract the `UnreferencedSbaPackages` array. Each entry is a path
indicating the package type and identity:
- `/__w/1/s/src/source-build-assets/src/referencePackages/src/<name>/<version>`
- `/__w/1/s/src/source-build-assets/src/textOnlyPackages/src/<name>/<version>`
- `/__w/1/s/src/source-build-assets/src/externalPackages/...`
- `/__w/1/s/src/source-build-assets/src/targetPacks/...`
Normalize these to `<type>/<name>/<version>` tuples (e.g., `referencePackages/system.buffers/4.5.1`).
### Step 2: Load the Cleanup Config
Read `.github/skills/cleanup-unreferenced-packages/cleanup-packages-config.json` from the repo.
```json
{
"knownFalsePositives": ["textOnlyPackages/microsoft.build.notargets/3.7.0", ...]
}
```
These packages are **protected** — they must not be deleted.
### Step 3: Determine Packages Not Yet Flowed to VMR
SBA tip-of-main may contain package additions or deletions that haven't flowed into the
VMR yet. Additions would appear unreferenced because the VMR hasn't seen them. Deletions
would appear unreferenced because the VMR still has the old state — this is expected and
those should still be candidates for deletion (they're already gone from SBA).
**How to detect:**
1. Fetch the VMR's `src/source-manifest.json` to find which SBA commit is incorporated:
```bash
gh api repos/dotnet/dotnet/contents/src/source-manifest.json --jq '.content' | base64 -d | jq -r '.repositories[] | select(.path == "src/source-build-assets") | .commitSha'
```
2. Compare that commit to the current HEAD:
```bash
git log --oneline <vmr-sba-sha>..HEAD -- src/
```
3. Identify package directories that were **added** in that range (additions that haven't
flowed are the ones we need to protect):
```bash
git diff --name-status --diff-filter=A <vmr-sba-sha>..HEAD -- src/referencePackages/src/ src/textOnlyPackages/src/ src/externalPackages/ src/targetPacks/
```
4. Extract the package identifiers (`<name>/<version>`) from added paths. These are
**not-yet-flowed** packages — add them to the protected set.
Note: Packages that were **deleted** in SBA since the VMR commit may still appear in the
unreferenced report (because the VMR still has them). This is fine — leave them in the
deletion candidates since they've already been intentionally removed from SBA.
### Step 4: Classify Candidates by Age
Apply a tiered grace-period strategy based on when each package was added to the repository.
Compute the initial deletion candidates:
```
initialProtected = knownFalsePositives + notYetFlowedPackages (combined set)
candidatesForDeletion = unreferencedPackages - initialProtected
```
**Determine the age of each candidate** by finding the commit that first introduced its
directory:
```bash
# Get the date when a package was first added
git log --diff-filter=A --follow --format=%aI -- "src/<type>/src/<name>/<version>/" | tail -1
```
If no commit is found (e.g., the package predates the repo's git history), treat the
package as having an age exceeding the mature threshold.
Apply the following classification:
| Age | Classification | Action |
|-----|---------------|--------|
| < 14 days | Recent | Protected — skip entirely |
| 14–60 days | Middle window | Delete, but @-mention author in PR |
| > 60 days | Mature | Delete |
### Step 5: Identify Authors for Middle-Window Packages
For packages in the middle window (added between 14 and 60 days ago), identify the
original author so they can be @-mentioned in the PR.
**Identify the original author:**
```bash
# Get the commit that added the package
git log --diff-filter=A --follow --format="%H %an <%ae>" -- "src/<type>/src/<name>/<version>/" | tail -1
```
Then find the associated PR to get the GitHub username:
```bash
# Find PR associated with the commit
gh pr list --repo dotnet/source-build-assets --search "<commit_sha>" --state merged --json number,author --jq '.[0].author.login'
```
Record the author and addition date for each middle-window package — these will be
included in the PR body (see Step 7).
### Step 5a: Resolve Dependencies of Middle-Window Packages
Middle-window packages may have transitive dependencies that are also in the deletion
candidate list. Those dependencies should also be treated as middle-window (and their
authors notified in the PR).
For each middle-window package, resolve its dependencies:
1. Build the package to produce a `.nupkg`:
```bash
./build.sh -sb --projects /full/path/to/<package>.csproj
```
2. Extract dependencies from the nupkg:
```bash
unzip -p artifacts/packages/Release/Shipping/<PackageId>.<Version>.nupkg "*.nuspec" | grep -A1 "<dependency "
```
3. Any dependency that appears in the mature deletion list should be reclassified as
middle-window (attributed to the same author). These will appear in the Action
Required table in the PR alongside the package that depends on them.
Repeat transitively.
Display the final classification:
```
=== Cleanup Report ===
Protected (will NOT be deleted):
- textOnlyPackages/microsoft.build.notargets/3.7.0 (known false positive)
- referencePackages/some.new.package/1.0.0 (recent: added 5 days ago)
Deleting (<N> packages):
Mature:
- <type>/<name>/<version> (age: 95 days)
Middle window (authors will be notified in PR):
- <type>/<name>/<version> (age: 25 days, author: @username)
Dependencies of middle-window packages:
- <type>/<name>/<version> (dependency of <parent-package>, <parent-package-2>)
```
Proceed to Step 6 with both **mature** and **middle-window** packages.
### Step 6: Delete Packages
For each package in the confirmed deletion list:
1. Determine the package type from its path:
- `src/referencePackages/src/<name>/<version>/` → reference package
- `src/textOnlyPackages/src/<name>/<version>/` → text-only package
- `src/targetPacks/src/<name>/<version>/` → targeting pack
- `src/externalPackages/` → external package (includes submodule, project, patches)
2. Delete the package directory:
```bash
rm -rf src/referencePackages/src/<name>/<version>
# or
rm -rf src/textOnlyPackages/src/<name>/<version>
# or
rm -rf src/targetPacks/src/<name>/<version>
```
3. For external packages, also:
- Remove the submodule: `git rm src/externalPackages/src/<component>`
- Remove the project file: `rm src/externalPackages/projects/<component>.proj`
- Remove patches: `rm -rf src/externalPackages/patches/<component>/`
4. Check if deleting a versioned package leaves an empty parent directory (e.g., the
`<name>/` dir has no more versions) and clean it up if so.
### Step 7: Commit and Create PR
```bash
git add -A
git commit -m "Remove unreferenced packages
Removed <N> packages that are no longer referenced by the VMR build.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
```
Create a PR **against the upstream repo** (`dotnet/source-build-assets`), not a personal fork.
If there are middle-window packages, include an "Action Required" section in the PR body
that @-mentions the original authors. This gives them visibility and a chance to revert
specific deletions before the PR merges.
```bash
gh pr create \
--repo dotnet/source-build-assets \
--title "[main] Remove unreferenced packages" \
--body "Removed the following packages which were detected as unreferenced:
<list each deleted package as type/name/version, one per line>
## Action Required
The following packages were added recently but are not yet referenced in the VMR.
If uptake is still in progress, ask Copilot to revert these package deletions from
this PR.
| Package | Added | Author |
|---------|-------|--------|
| \`<type>/<name>/<version>\` | <N> days ago | @<author> |
Dependencies of the above packages (also being removed):
- \`<type>/<name>/<version>\` (dependency of \`<parent-package>\`, \`<parent-package-2>\`)
---
Generated by the [cleanup-unreferenced-packages](.github/skills/cleanup-unreferenced-packages/SKILL.md) skill."
```
Omit the "Action Required" section entirely if there are no middle-window packages.
Omit the "Dependencies" list if there are no reclassified transitive dependencies.
After the PR is created, trigger a full VMR source build to validate the removals:
```bash
gh pr comment <pr_number> --repo dotnet/source-build-assets --body "/azp run source-build-assets-unified-build"
```
## Important Notes
- The `knownFalsePositives` in the config exist because `microsoft.build.notargets` and
`microsoft.build.traversal` are MSBuild SDKs referenced in `global.json` — the usage
reporter doesn't detect SDK-style usage.
- When in doubt, err on the side of keeping a package. It's much easier to delete later
than to re-add and coordinate flows.
- Middle-window author notifications happen directly in the PR body via @-mentions.
If a package's uptake is still in progress, the author can ask Copilot to revert
those specific deletions from the PR.
## Config File Schema
**Location:** `.github/skills/cleanup-unreferenced-packages/cleanup-packages-config.json`
| Field | Type | Description |
|-------|------|-------------|
| `knownFalsePositives` | `string[]` | Packages that are always reported unreferenced due to detection limitations (e.g., MSBuild SDKs loaded implicitly by `global.json`). Format: `<type>/<name>/<version>` (e.g., `textOnlyPackages/microsoft.build.notargets/3.7.0`). These are permanent until the underlying detection issue is resolved. |
GitHub에서 보기