Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/gpclean.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 67 additions & 14 deletions .github/workflows/gpclean.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,31 @@ strict: true

imports:
- shared/mood.md

# Pre-download SBOM to get accurate dependency information
steps:
- name: Download SBOM from GitHub Dependency Graph API
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -e
echo "📦 Downloading SBOM from GitHub Dependency Graph API..."

# Download SBOM using gh CLI (requires contents: read permission)
gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${{ github.repository }}/dependency-graph/sbom" \
> /tmp/sbom.json
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot use /tmp/gh-aw/agent/


echo "✅ SBOM downloaded successfully to /tmp/sbom.json"

# Show SBOM summary
if command -v jq &> /dev/null; then
PACKAGE_COUNT=$(jq '.sbom.packages | length' /tmp/sbom.json 2>/dev/null || echo "unknown")
echo "📊 SBOM contains ${PACKAGE_COUNT} packages"
Comment on lines +52 to +64
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The step uses set -e which will cause the entire workflow to fail if the SBOM download fails. However, the workflow documentation states "If SBOM download fails, fall back to parsing go.mod directly" (lines 355, 407).

To enable the fallback strategy, the step should handle errors gracefully. Either:

  1. Remove set -e and add explicit error checking with conditional output, OR
  2. Add error handling within the step to continue on failure (e.g., gh api ... > /tmp/sbom.json || echo '{"sbom":{"packages":[]}}' > /tmp/sbom.json)

The agent should then check if the SBOM is valid/non-empty before using it.

Suggested change
# Download SBOM using gh CLI (requires contents: read permission)
gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${{ github.repository }}/dependency-graph/sbom" \
> /tmp/sbom.json
echo "✅ SBOM downloaded successfully to /tmp/sbom.json"
# Show SBOM summary
if command -v jq &> /dev/null; then
PACKAGE_COUNT=$(jq '.sbom.packages | length' /tmp/sbom.json 2>/dev/null || echo "unknown")
echo "📊 SBOM contains ${PACKAGE_COUNT} packages"
# Download SBOM using gh CLI (requires contents: read permission).
# If download fails, write an empty SBOM and allow downstream fallback
# (e.g., parsing go.mod directly) to be used instead.
if ! gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${{ github.repository }}/dependency-graph/sbom" \
> /tmp/sbom.json; then
echo "⚠️ Failed to download SBOM from GitHub Dependency Graph API; falling back to go.mod parsing."
echo '{"sbom":{"packages":[]}}' > /tmp/sbom.json
else
echo "✅ SBOM downloaded successfully to /tmp/sbom.json"
fi
# Show SBOM summary if jq is available and the SBOM is valid/non-empty
if command -v jq &> /dev/null; then
if jq -e '.sbom.packages' /tmp/sbom.json > /dev/null 2>&1; then
PACKAGE_COUNT=$(jq '.sbom.packages | length' /tmp/sbom.json 2>/dev/null || echo "unknown")
echo "📊 SBOM contains ${PACKAGE_COUNT} packages"
else
echo "⚠️ SBOM is invalid or empty; dependency analysis will rely on go.mod parsing if configured."
fi

Copilot uses AI. Check for mistakes.
fi
---

# GPL Dependency Cleaner (gpclean)
Expand All @@ -49,28 +74,45 @@ Systematically detect Go dependencies that introduce non-MIT friendly (GPL-type)
## Current Context
- **Repository**: ${{ github.repository }}
- **Go Module File**: `go.mod` in repository root
- **SBOM Source**: GitHub Dependency Graph API (SPDX format)
- **Cache Memory**: `/tmp/gh-aw/cache-memory/gpclean/` for round-robin module tracking

## Your Tasks

### Phase 0: Round-Robin Module Selection
### Phase 0: Download SBOM and Round-Robin Module Selection
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The phase title "Download SBOM and Round-Robin Module Selection" is misleading because the SBOM is pre-downloaded in the frontmatter steps section (lines 43-65) before the agent runs. The title should be updated to reflect that the agent uses the pre-downloaded SBOM rather than downloading it.

Suggested title: "Phase 0: Extract Dependencies from SBOM and Round-Robin Module Selection" or "Phase 0: Parse Pre-Downloaded SBOM and Round-Robin Module Selection"

This issue also appears in the following locations of the same file:

  • line 86
  • line 349
  • line 415

See below for a potential fix:

### Phase 0: Extract Dependencies from SBOM and Round-Robin Module Selection

Use the repository's SBOM (Software Bill of Materials) to get accurate dependency information, then select one module to analyze in a round-robin fashion.

1. **Use pre-downloaded SBOM**:
   The workflow frontmatter has already downloaded the SBOM from the GitHub Dependency Graph API
   and saved it to `/tmp/sbom.json` before this agent runs. Assume this file exists and use it as
   the source of truth for dependency information; do not attempt to re-download the SBOM here.
   
   **Note**: The pre-download step relies on the workflow's `contents: read` permission, which is
   required to access the dependency graph SBOM API.

Copilot uses AI. Check for mistakes.

Use the repository's SBOM (Software Bill of Materials) to get accurate dependency information, then select one module to analyze in a round-robin fashion.

Use cache-memory to ensure we focus on one dependency per run in a systematic round-robin fashion.
1. **Download SBOM from GitHub**:
```bash
# Download SBOM using gh CLI (requires contents: read permission)
gh api "repos/${{ github.repository }}/dependency-graph/sbom" \
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistency in the API endpoint path: the frontmatter step uses /repos/${{ github.repository }}/dependency-graph/sbom (line 56) with a leading slash, but the agent instructions use repos/${{ github.repository }}/dependency-graph/sbom (line 89) without a leading slash.

While both formats may work with gh api, they should be consistent. The leading slash format is more explicit and aligns with REST API conventions. Update line 89 to include the leading slash for consistency.

Suggested change
gh api "repos/${{ github.repository }}/dependency-graph/sbom" \
gh api "/repos/${{ github.repository }}/dependency-graph/sbom" \

Copilot uses AI. Check for mistakes.
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
> /tmp/sbom.json
```

**Note**: The workflow already has `contents: read` permission which is required to access the dependency graph SBOM API.

1. **Load tracking state** from `/tmp/gh-aw/cache-memory/gpclean/state.json`:
2. **Extract dependencies from SBOM**:
- Parse the SBOM JSON file (SPDX format)
- Look for packages in `sbom.packages[]` array
- Filter for Go packages (those with `purl` starting with `pkg:golang/`)
- Extract the package names (module paths) from the `purl` field
- Focus on direct dependencies (not dev dependencies or build tools)
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The instruction to "Focus on direct dependencies (not dev dependencies or build tools)" may be misleading. The GitHub Dependency Graph SBOM includes all dependencies (both direct and transitive), and there's no standardized way to distinguish direct from transitive dependencies in the SPDX SBOM format using just the purl field.

The instruction should clarify how to identify direct dependencies. Options:

  1. Use the SBOM's relationship data if available (check sbom.packages[].dependencies or relationship fields)
  2. Cross-reference with go.mod to identify direct dependencies
  3. Remove this filtering requirement and process all Go dependencies from the SBOM

Additionally, Go doesn't have the concept of "dev dependencies" like npm does, so that part of the instruction should be removed or clarified.

Suggested change
- Focus on direct dependencies (not dev dependencies or build tools)
- Include all Go dependencies listed in the SBOM. The SBOM may contain both direct and transitive dependencies; if you need to restrict to direct dependencies, use SBOM relationship data (e.g., `dependencies` fields) or cross-reference with `go.mod`.

Copilot uses AI. Check for mistakes.
- Save the list of dependencies to `/tmp/go-dependencies.txt`

3. **Load tracking state** from `/tmp/gh-aw/cache-memory/gpclean/state.json`:
- If file doesn't exist, create it with initial state: `{"last_checked_module": "", "checked_modules": []}`
- State tracks which modules have been checked recently

2. **Get all direct dependencies** from `go.mod`:
- Extract all `require` statements (excluding indirect dependencies for now)
- Create list of direct dependency module paths

3. **Select next module to check**:
4. **Select next module to check**:
- Use the dependencies list from SBOM (`/tmp/go-dependencies.txt`)
- Find the first module NOT in `checked_modules` list
- If all modules have been checked, reset `checked_modules` to empty array and start over
- Update state with selected module and save to cache-memory

4. **Focused analysis**: Analyze only the selected module and its transitive dependencies in this run
5. **Focused analysis**: Analyze only the selected module and its transitive dependencies in this run

### Phase 1: License Detection for Selected Module

Expand Down Expand Up @@ -302,6 +344,16 @@ After creating the issue:

## Important Guidelines

### SBOM Usage

- **Download SBOM first** at the beginning of each run to get the latest dependency information
- **Use `gh api`** to download SBOM - the workflow has `contents: read` permission which is required for the dependency graph API
- SBOM is in SPDX format with packages listed in `sbom.packages[]` array
- Go packages have `purl` (Package URL) in format: `pkg:golang/github.com/org/repo@version`
- Parse the SBOM to extract all Go dependencies before license checking
- SBOM provides a comprehensive view including transitive dependencies
- If SBOM download fails, fall back to parsing `go.mod` directly

### Focus on One Dependency

- **Only analyze ONE direct dependency per run** (round-robin via cache-memory)
Expand Down Expand Up @@ -352,17 +404,18 @@ After creating the issue:

## Error Handling

- If SBOM download fails, fall back to parsing `go.mod` directly to extract dependencies
- If `go mod graph` fails, report the error and exit
- If license detection fails for a module, document it in the issue and recommend manual review
- If no direct dependencies exist, exit successfully
- If cache-memory state is corrupted, reinitialize it

## Example Module Selection Flow

**Run 1**: Check `github.com/spf13/cobra` → No GPL found → Add to checked_modules
**Run 2**: Check `github.com/spf13/viper` → No GPL found → Add to checked_modules
**Run 3**: Check `github.com/cli/go-gh` → GPL found in transitive dep → Create issue, add to checked_modules
**Run 4**: Check `gopkg.in/yaml.v3` → No GPL found → Add to checked_modules
**Run 5**: All modules checked → Reset checked_modules, start from beginning
**Run 1**: Download SBOM → Extract Go dependencies → Check `github.com/spf13/cobra` → No GPL found → Add to checked_modules
**Run 2**: Check `github.com/spf13/viper` (from SBOM) → No GPL found → Add to checked_modules
**Run 3**: Check `github.com/cli/go-gh` (from SBOM) → GPL found in transitive dep → Create issue, add to checked_modules
**Run 4**: Check `gopkg.in/yaml.v3` (from SBOM) → No GPL found → Add to checked_modules
**Run 5**: All modules from SBOM checked → Reset checked_modules, start from beginning

This ensures systematic coverage without duplicate work.
Loading