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
99 changes: 4 additions & 95 deletions .github/workflows/pr-component-validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
paths:
- 'sbom/components.yaml'
- 'root/values.yaml'
- 'sbom/generate-compare-components.sh'
- 'sbom/*.sh'

jobs:
validate-components:
Expand All @@ -22,99 +22,8 @@ jobs:
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq

- name: Run generate-compare-components.sh
- name: Validate SBOM Sync (Gatekeeper)
working-directory: ./sbom
run: |
chmod +x generate-compare-components.sh
./generate-compare-components.sh

- name: Validate component URLs
working-directory: ./sbom
run: |
echo "Validating components.yaml for missing sourceUrl and projectUrl..."

# Check if components.yaml exists
if [[ ! -f "components.yaml" ]]; then
echo "❌ Error: components.yaml not found"
exit 1
fi

# Get all component names
component_names=$(yq eval '.components | keys | .[]' components.yaml)

missing_fields=false
missing_components=()

# Check each component for missing fields
for component in $component_names; do
source_url=$(yq eval ".components.\"$component\".sourceUrl // \"\"" components.yaml)
project_url=$(yq eval ".components.\"$component\".projectUrl // \"\"" components.yaml)
license=$(yq eval ".components.\"$component\".license // \"\"" components.yaml)
license_url=$(yq eval ".components.\"$component\".licenseUrl // \"\"" components.yaml)

component_missing=false

if [[ -z "$source_url" ]]; then
echo "❌ Missing sourceUrl for component: $component"
missing_fields=true
component_missing=true
fi

if [[ -z "$project_url" ]]; then
echo "❌ Missing projectUrl for component: $component"
missing_fields=true
component_missing=true
fi

if [[ -z "$license" ]]; then
echo "❌ Missing license for component: $component"
missing_fields=true
component_missing=true
fi

if [[ -z "$license_url" ]]; then
echo "❌ Missing licenseUrl for component: $component"
missing_fields=true
component_missing=true
fi

if [[ "$component_missing" == true ]]; then
missing_components+=("$component")
else
echo "✅ Component $component has all required fields (sourceUrl, projectUrl, license, licenseUrl)"
fi
done

if [[ "$missing_fields" == true ]]; then
echo ""
echo "❌ VALIDATION FAILED!"
echo "The following components are missing required fields:"
for comp in "${missing_components[@]}"; do
echo " - $comp"
done
echo ""
echo "Please ensure all components have 'sourceUrl', 'projectUrl', 'license', and 'licenseUrl' populated in components.yaml"
echo ""
echo "📝 Manual steps required:"
echo " 1. Fill in 'sourceUrl' and 'projectUrl' manually for missing components"
echo " 2. Run ./update_licenses.sh to auto-populate license fields from GitHub"
echo ""
echo "💡 Note: 'sourceUrl' and 'projectUrl' must be provided manually as they require"
echo " human knowledge about where to find charts/manifests and project repositories."
exit 1
else
echo ""
echo "✅ All components have required fields (sourceUrl, projectUrl, license, licenseUrl)!"
fi

- name: Check for uncommitted changes
working-directory: ./sbom
run: |
if [[ -n $(git status --porcelain components.yaml) ]]; then
echo "❌ components.yaml has uncommitted changes after running generate-compare-components.sh"
echo "Please commit the updated components.yaml file"
git diff components.yaml
exit 1
else
echo "✅ components.yaml is up to date"
fi
chmod +x validate-*.sh
./validate-sync.sh
105 changes: 95 additions & 10 deletions sbom/SBOM-QUICK-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,112 @@

Scripts to manage component metadata for automated Software Bill of Materials (SBOM) generation.

## Essential Workflow

When `root/values.yaml` has a new tool, you need to run these commands manually:

```bash
# 1. Generate/sync components from enabledApps
./generate-compare-components.sh

# 2. Manually fill out sourceUrl and projectUrl in components.yaml
# (requires human knowledge about chart/manifest locations)

# 3. Auto-populate license information
./update_licenses.sh

# 4. Validate everything is ready
./validate-sync.sh
```

## Quick Start

1. **Add new component**: Update `values.yaml` → run `./generate-compare-components.sh` → manually add sourceUrl and projectUrl to `components.yaml` → run `./update_licenses.sh`
2. **Remove component**: Remove from `values.yaml` → run `./generate-compare-components.sh`
### Adding a New Component
1. **Update values.yaml**: Add to `enabledApps` list AND add app definition in `apps` section
2. **Run workflow**: Execute the 4 commands above
3. **Commit changes**: All files should be ready for PR

### Removing a Component
1. **Remove from enabledApps**: Remove from `enabledApps` list in `root/values.yaml`
2. **Regenerate**: Run `./generate-compare-components.sh` (automatically removes from components.yaml)
3. **Validate**: Run `./validate-sync.sh` to confirm removal

## Scripts

**`generate-compare-components.sh`** - Syncs `components.yaml` with `root/values.yaml`
- Run after modifying apps in `root/values.yaml`
- Preserves existing metadata
### Generation Scripts
**`generate-compare-components.sh`** - Syncs `components.yaml` with enabled apps from `root/values.yaml`
- Processes only apps listed in `enabledApps` (excludes `-config` apps)
- Includes pre-validation to catch configuration issues early
- Preserves existing metadata (sourceUrl, projectUrl, license fields)
- Creates timestamped backups when needed

**`update_licenses.sh`** - Auto-populates license info from GitHub
- Run after updating URLs or to refresh licenses
- Only works with GitHub project URLs

### Validation Scripts (New Modular System)
**`validate-sync.sh`** - 🛡️ **Main validation command** - Comprehensive SBOM sync validator
- Orchestrates all validation checks
- Use this for complete validation before commits

**Individual Validators** (for targeted debugging):
- **`validate-enabled-apps.sh`** - Checks enabledApps have corresponding app definitions
- **`validate-components-sync.sh`** - Verifies components.yaml reflects current enabledApps
- **`validate-metadata.sh`** - Ensures all required metadata fields are populated

## Validation Workflow

The new modular validation system ensures data consistency:

```
1. EnabledApps Consistency Check
├── Validates all enabledApps have app definitions
└── Filters out -config apps appropriately

2. Components Sync Check
├── Verifies components.yaml matches enabledApps
├── Checks for missing/extra components
└── Validates path/valuesFile consistency

3. Metadata Completeness Check
├── Ensures sourceUrl and projectUrl are populated
└── Verifies license and licenseUrl fields exist
```

## Required Fields in components.yaml

- **sourceUrl**: Where to download the chart/manifest
- **projectUrl**: Main project repository (use GitHub for auto-license detection)
- **sourceUrl**: Where to download the chart/manifest (⚠️ Manual entry required)
- **projectUrl**: Main project repository (⚠️ Manual entry required - use GitHub for auto-license detection)
- **license/licenseUrl**: Auto-populated from GitHub by `update_licenses.sh`
- **path**: Auto-synced from values.yaml by generation script
- **valuesFile**: Auto-synced from values.yaml when present

## CI/CD Integration

The GitHub workflow `.github/workflows/pr-component-validation.yaml` now includes:
1. **EnabledApps validation** (prevents mismatched configurations)
2. **Component generation** (ensures SBOM reflects enabled apps)
3. **Metadata validation** (ensures completeness)
4. **Sync verification** (catches uncommitted changes)

## Important Notes

- **EnabledApps is the source of truth**: Components are generated only for apps in the `enabledApps` list
- **Manual metadata required**: `sourceUrl` and `projectUrl` must be added manually (requires human knowledge)
- **Scripts are idempotent**: Safe to run multiple times
- **Validation before commit**: Always run `./validate-sync.sh` before creating PRs
- **Backup safety**: Existing data is preserved through timestamped backups

## Troubleshooting

**Error: "Enabled app has no definition"**
→ Add the app definition to the `apps` section in `root/values.yaml`

**Error: "Component missing/extra"**
→ Run `./generate-compare-components.sh` to sync components.yaml

**Error: "Missing sourceUrl/projectUrl"**
→ Manually add the missing URLs to components.yaml

## Notes
- Scripts are safe to run multiple times
- Verify auto-populated data before creating PRs
**Error: "Path/configuration mismatch"**
→ Run `./generate-compare-components.sh` to sync path information
10 changes: 8 additions & 2 deletions sbom/components.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Generated components metadata for SBOM creation
# This file contains simplified component information extracted from values.yaml
# Apps with "config" suffix are excluded
# This file contains simplified component information for apps in enabledApps
# Apps with "config" suffix are excluded from this SBOM

components:
aim-cluster-model-source:
path: aim-cluster-model-source
sourceUrl: https://github.com/silogen/kaiwo/releases/download/v0.2.0-rc11/crds.yaml
projectUrl: https://github.com/silogen/kaiwo/
license: MIT License
licenseUrl: https://github.com/silogen/kaiwo/blob/main/LICENSE
argocd:
path: argocd/8.3.5
valuesFile: ../values_cf.yaml
Expand Down
81 changes: 67 additions & 14 deletions sbom/generate-compare-components.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,64 @@

set -euo pipefail

# Script to update components.yaml from values.yaml
# Script to update components.yaml from enabledApps in values.yaml
# Only updates if there are new items or changes to existing ones
# Preserves existing sourceUrl and projectUrl values
# Only includes apps that are in the enabledApps list (excluding -config apps)

VALUES_FILE="../root/values.yaml"
OUTPUT_FILE="./components.yaml"
TEMP_FILE="./components.yaml.tmp"

echo "⚙️ Generating/Updating components.yaml from enabledApps..."

# Self-validation: Check enabledApps consistency before processing (fail-fast)
echo "🔍 Pre-validation: Checking enabledApps consistency..."
if [[ -f "./validate-enabled-apps.sh" ]]; then
if ! ./validate-enabled-apps.sh; then
echo ""
echo "❌ Pre-validation failed! Cannot generate components.yaml with invalid enabledApps."
echo "Please fix the enabledApps issues above before running generation."
exit 1
fi
echo "✅ Pre-validation passed - proceeding with generation..."
else
echo "⚠️ Warning: validate-enabled-apps.sh not found, skipping pre-validation"
fi

echo ""
echo "Checking for updates to components.yaml..."

# Check if values.yaml exists
if [[ ! -f "$VALUES_FILE" ]]; then
echo "Error: $VALUES_FILE not found"
echo "Error: $VALUES_FILE not found"
exit 1
fi

# Get all app names that don't end with -config from values.yaml
app_names=$(yq eval '.apps | keys | .[] | select(. | test("-config$") | not)' "$VALUES_FILE")
# Get all enabled app names that don't end with -config from values.yaml
enabled_apps=$(yq eval '.enabledApps[]' "$VALUES_FILE" 2>/dev/null || echo "")

if [[ -z "$enabled_apps" ]]; then
echo "Warning: No enabled apps found in enabledApps list"
if [[ -f "$OUTPUT_FILE" ]]; then
backup_file="./components-old-$(date +%Y%m%d-%H%M%S).yaml"
echo "Backing up existing $OUTPUT_FILE to $backup_file"
mv "$OUTPUT_FILE" "$backup_file"
fi
exit 0
fi

app_names=$(echo "$enabled_apps" | grep -v -- '-config$' || echo "")

if [[ -z "$app_names" ]]; then
echo "Warning: No non-config apps found in enabledApps list"
if [[ -f "$OUTPUT_FILE" ]]; then
backup_file="./components-old-$(date +%Y%m%d-%H%M%S).yaml"
echo "Backing up existing $OUTPUT_FILE to $backup_file (only config apps enabled)"
mv "$OUTPUT_FILE" "$backup_file"
fi
exit 0
fi

# Check if components.yaml exists
if [[ ! -f "$OUTPUT_FILE" ]]; then
Expand All @@ -29,9 +69,9 @@ else
echo "Existing $OUTPUT_FILE found. Checking for changes..."
needs_update=false

# Check each app from values.yaml
# Check each enabled app from values.yaml
for app in $app_names; do
# Get current values from values.yaml
# Get current values from values.yaml apps section
current_path=$(yq eval ".apps.\"$app\".path" "$VALUES_FILE")
current_values_file=$(yq eval ".apps.\"$app\".valuesFile // \"null\"" "$VALUES_FILE")

Expand Down Expand Up @@ -61,12 +101,12 @@ else
fi
done

# Check for removed apps (apps in components.yaml that are no longer in values.yaml)
# Check for removed apps (apps in components.yaml that are no longer in enabledApps)
if [[ "$needs_update" == "false" ]]; then
existing_components=$(yq eval '.components | keys | .[]' "$OUTPUT_FILE")
existing_components=$(yq eval '.components | keys | .[]' "$OUTPUT_FILE" 2>/dev/null || echo "")
for existing_component in $existing_components; do
if ! echo "$app_names" | grep -q "^$existing_component$"; then
echo " Removed app found: $existing_component"
echo " Removed app found: $existing_component (no longer in enabledApps)"
needs_update=true
break
fi
Expand All @@ -84,8 +124,8 @@ echo "Updating $OUTPUT_FILE..."
# Create components.yaml header
cat > "$TEMP_FILE" << 'EOF'
# Generated components metadata for SBOM creation
# This file contains simplified component information extracted from values.yaml
# Apps with "config" suffix are excluded
# This file contains simplified component information for apps in enabledApps
# Apps with "config" suffix are excluded from this SBOM

components:
EOF
Expand Down Expand Up @@ -146,9 +186,9 @@ done
# Replace the original file
mv "$TEMP_FILE" "$OUTPUT_FILE"

echo "Updated $OUTPUT_FILE successfully"
echo "Updated $OUTPUT_FILE successfully"
echo ""
echo "Summary of components:"
echo "📊 Summary of components:"
echo "$app_names" | wc -l | xargs echo "Total components:"
echo ""
echo "Components with valuesFile:"
Expand All @@ -157,4 +197,17 @@ for app in $app_names; do
if [[ "$values_file" != "null" ]]; then
echo " - $app"
fi
done
done

echo ""
echo "✅ Components generated/updated successfully!"
echo ""
echo "📝 Next steps:"
echo " 1. Fill in 'sourceUrl' and 'projectUrl' for any components with empty values"
echo " 2. Run ./update_licenses.sh to auto-populate license fields from GitHub"
echo " 3. Run ./validate-sync.sh to verify everything is ready for commit"
echo ""
echo "💡 Tip: Use individual validation scripts for targeted debugging:"
echo " - ./validate-enabled-apps.sh (check app definitions)"
echo " - ./validate-components-sync.sh (check sync status)"
echo " - ./validate-metadata.sh (check required fields)"
Loading