Skip to content

Conversation

@dcolina
Copy link
Member

@dcolina dcolina commented Dec 10, 2025

Deployment Guard Workflow - Initial Release

Summary

This PR introduces a new reusable workflow called deployment-guard.yml that provides configurable deployment validation for Kubernetes manifests. The workflow enforces guardrails around file changes, YAML structure modifications, and container image updates.

Key Features

🔧 Configurable Validation Checks

Three independent validation stages that can be enabled/disabled:

  1. File Allowlist (enable_file_allowlist)

    • Restricts which files can be modified using glob patterns
    • Blocks PRs that modify files outside the allowlist
  2. Image-Only Changes (enable_image_only_check)

    • Ensures only container image fields are modified
    • Blocks PRs that change infrastructure, resources, or other YAML fields
  3. Image Validation (enable_image_validation)

    • Validates image repository, version pattern, and existence
    • Supports custom regex patterns for version formats
    • Optional Docker registry verification

📦 Flexible Configuration

All parameters are optional with sensible defaults:

inputs:
  # Validation toggles
  enable_file_allowlist: true (default)
  enable_image_only_check: true (default)
  enable_image_validation: true (default)
  
  # Configuration
  allowed_files_pattern: '**/*' (default: all files)
  allowed_image_repositories: '' (default: no restriction)
  allowed_version_pattern: '.*' (default: any version)
  verify_image_existence: false (default: skip verification)

🎯 Use Cases

Strict Validation (all checks enabled):

uses: dotCMS/ai-workflows/.github/workflows/deployment-guard.yml@v1.0.0
with:
  enable_file_allowlist: true
  enable_image_only_check: true
  enable_image_validation: true
  allowed_files_pattern: 'kubernetes/**/statefulset.yaml'
  allowed_image_repositories: 'mycompany/myapp'
  allowed_version_pattern: '^(2[5-9]|[3-9][0-9])\.[0-9]{2}\.[0-9]{2}'
  verify_image_existence: true

File Allowlist Only:

uses: dotCMS/ai-workflows/.github/workflows/deployment-guard.yml@v1.0.0
with:
  enable_file_allowlist: true
  enable_image_only_check: false
  enable_image_validation: false
  allowed_files_pattern: 'apps/**/deployment.yaml'

Image Validation Only:

uses: dotCMS/ai-workflows/.github/workflows/deployment-guard.yml@v1.0.0
with:
  enable_file_allowlist: false
  enable_image_only_check: false
  enable_image_validation: true
  allowed_image_repositories: 'org/app1,org/app2'
  allowed_version_pattern: '^\d+\.\d+\.\d+$'

Implementation Details

4-Stage Validation Pipeline

  1. validate-changed-files (conditional)

    • Detects files modified in PR
    • Validates against glob pattern allowlist
    • Blocks if unauthorized files changed
  2. validate-image-only-changed (conditional)

    • Extracts old and new YAML from changed files
    • Normalizes by replacing images with placeholders
    • Compares normalized YAMLs
    • Blocks if any non-image fields changed
  3. validate-images (conditional)

    • Validates image format (repo/name:tag)
    • Checks repository against allowlist
    • Validates version against regex pattern
    • Optionally verifies image exists in registry
  4. finalize-validation (always runs)

    • Aggregates results from all validation stages
    • Posts detailed comment to PR
    • Sets final pass/fail status

Cross-Platform Compatibility

  • Uses # as sed delimiter for macOS/Linux compatibility
  • Forces decimal number interpretation with 10# prefix
  • Handles both .yaml and .yml extensions
  • Supports glob patterns: **, *, ?

Security Features

  • Automated blocking (no manual override possible)
  • Detailed validation feedback in PR comments
  • Comprehensive error messages with remediation steps
  • Safe handling of empty/missing fields

Testing

Comprehensive test coverage includes:

  • File allowlist validation (valid/invalid patterns)
  • Image-only change detection
  • Multi-format version patterns
  • Repository allowlist enforcement
  • Image existence verification
  • Cross-platform compatibility

Next Steps

After merging:

  1. Create version tag:

    git checkout main
    git pull origin main
    git tag -a v1.0.0 -m "Release: Deployment Guard v1.0.0"
    git push origin v1.0.0
  2. Update downstream repositories to reference @v1.0.0

  3. Configure branch protection rules to require status checks

Documentation

Complete documentation available in consuming repositories:

  • Setup guide with 6 test scenarios
  • Troubleshooting section
  • Customization examples
  • Security considerations

Release Version: v1.0.0
Issue: #339
Type: New Feature

- 4-step validation: file allowlist → image-only → image validation → finalize
- Only allows changes to dotCMS container image field
- Validates image repository, tag pattern, and existence
- Workflow-based control
- Supports configurable patterns and repositories
- Handle .ya?ml pattern correctly by converting to (yaml|yml)
- Use # as sed delimiter to avoid conflicts with pipe character
- Tested on both macOS and Linux sed variants
BREAKING CHANGE: Workflow renamed from deutsche-bank-guard.yml to deployment-guard.yml

Features:
- More generic workflow name for reusability across projects
- Configurable validation checks via enable_* parameters
- Optional repository allowlist (skipped if empty)
- Flexible defaults for different use cases

Enabled checks (all true by default):
- enable_file_allowlist: Validate only allowed files changed
- enable_image_only_check: Ensure only image field modified
- enable_image_validation: Validate image repo, format, version

Configuration parameters now optional with sensible defaults:
- allowed_files_pattern: defaults to '**/*' (all files)
- allowed_image_repositories: defaults to '' (no restriction)
- allowed_version_pattern: defaults to '.*' (any version)
- verify_image_existence: defaults to false

Example configurations:
1. Deutsche Bank (strict): All checks enabled, specific patterns
2. Generic use: Only file allowlist or image validation
3. Flexible: Mix and match checks as needed
@dcolina dcolina requested review from a team as code owners December 10, 2025 11:16
- Remove unused variables (ALL_ALLOWED, ALL_PASS, ALL_VALID, DISALLOWED_FILES, NEW_IMAGES)
- Replace 'cat file | cmd' with 'cmd < file' pattern
- Add -r flag to all read commands to properly handle backslashes

This fixes all actionlint/shellcheck warnings while maintaining functionality.
The previous conversion of ** to .* was incorrect - it would match
any characters but wouldn't properly match path segments with slashes.

Changed ** → ([^/]+/)* to properly match zero or more path segments.

Example:
- Pattern: kubernetes/dotcms/**/statefulset.yaml
- Now matches: kubernetes/dotcms/poc/uat/statefulset.yaml
- Regex: kubernetes/dotcms/([^/]+/)*statefulset\.(yaml|yml)

This fixes file allowlist validation for nested directories.
Two critical fixes:
1. Replace **/ as a unit (not just **) to avoid extra slash
2. Remove backslash escape from | in grep -E (it's an alternation operator)

The pattern kubernetes/dotcms/**/statefulset.yaml now correctly converts to:
kubernetes/dotcms/([^/]+/)*statefulset\.(yaml|yml)

This properly matches:
- kubernetes/dotcms/statefulset.yaml (zero subdirs)
- kubernetes/dotcms/poc/statefulset.yaml (one subdir)
- kubernetes/dotcms/poc/uat/statefulset.yaml (two subdirs)

Fixes file allowlist validation for all directory depths.
yolabingo
yolabingo previously approved these changes Dec 10, 2025
Changes:
- Renamed parameter: trusted_github_team → trusted_github_teams (plural)
- Added support for comma-separated list of teams
- Check membership in ANY team (short-circuit on first match)
- Added matched-team output to track which team granted bypass
- Updated PR comments to show matched team
- Updated logs to show all teams being checked

This allows using existing GitHub teams (platform-engineers, dotSysadmins,
cloudeng-support) without creating new teams.

Validates with actionlint ✅
Due to GitHub API limitations, GITHUB_TOKEN cannot check organization
team membership. Changed implementation to verify repository permission
levels instead:

- Users with 'admin' or 'maintain' permissions can bypass validations
- These permissions typically align with membership in trusted teams
- No additional tokens or secrets required
- More straightforward and maintainable solution

This approach works because:
- platform-engineers team has Admin permission
- dotSysadmins team has Maintain permission
- cloudeng-support team has Write permission (will need Maintain for bypass)
- All team members with sufficient permissions can bypass checks
…ogic

Add optional testing parameter to force non-bypass mode, allowing us to test
validation logic even with users who have admin/maintain permissions.

This enables comprehensive testing of:
- File allowlist validation
- Image-only change validation
- Image version validation
- Multiple field change detection

The parameter:
- Defaults to false (normal operation)
- Can be enabled for testing purposes
- Clearly marked in logs when active
- Can safely remain in production code
Switch from repository permission-based bypass to organization membership
checking for simpler and more flexible access control.

Changes:
- Replace trusted_github_teams input with trusted_organization
- Use checkPublicMembershipForUser API (works with GITHUB_TOKEN)
- Update all job references from check-team-membership to check-org-membership
- Update PR comment to reference organization membership
- Update outputs: is-org-member, organization (instead of is-team-member, matched-team)
- Add helpful troubleshooting messages for public membership requirement

Benefits:
- No additional tokens required (works with GITHUB_TOKEN)
- More flexible: any org member can have bypass (not just admin/maintain)
- Clearer intent: organization membership = trusted
- Better error messages: guides users to make membership public

Requirements:
- Organization membership must be set to PUBLIC visibility
- Users can make their membership public at github.com/orgs/ORG/people
Remove redundant 'automatically approved' message from PR comment
when user has organization membership bypass.

The bypass status is already clearly indicated in the main message,
no need for additional validation complete section.

if [ -z "$CHANGED_FILES" ]; then
echo "No YAML files changed"
echo "files=" >> "$GITHUB_OUTPUT"
Copy link
Member

Choose a reason for hiding this comment

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

should files be json or is empty string ok?

echo "files=[]" >> "$GITHUB_OUTPUT"

Choose a reason for hiding this comment

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

@yolabingo GiTHUB_OUTPUT acts like an appendable properties file it is not yaml.

Choose a reason for hiding this comment

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

I see now though we are treating the value as yaml.

Copy link
Member

Choose a reason for hiding this comment

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

The else case there (if files are found) returned a json array. I wasn't sure if whatever ingested this output assumed that files would contain json.

HEAD_SHA="${{ github.event.pull_request.head.sha }}"
else
BASE_SHA="origin/${{ github.event.repository.default_branch }}"
HEAD_SHA="HEAD"
Copy link
Member

@yolabingo yolabingo Dec 11, 2025

Choose a reason for hiding this comment

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

chatGPT suggested this, not sure if it is correct:

On push events, BASE_SHA/HEAD_SHA didn’t correctly reflect the commit range

Better to use ${{ github.event.before }} and ${{ github.event.after }}

yolabingo
yolabingo previously approved these changes Dec 11, 2025
Copy link
Member

@yolabingo yolabingo left a comment

Choose a reason for hiding this comment

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

I added some comments but approved to keep you unblocked.

Resolve review comments from @yolabingo:

1. Use JSON empty array [] instead of empty string for files output
   when no YAML files are changed. This ensures consistent JSON format
   for downstream processing.

2. For push events, use github.event.before and github.event.after
   instead of default branch comparison. This provides the correct
   commit range for push events as suggested.
@dcolina
Copy link
Member Author

dcolina commented Dec 11, 2025

Thanks @yolabingo for the review feedback! I've addressed both comments in commit 3c86caf:

  1. Line 198: Changed from empty string to JSON empty array for consistent format
  2. Line 191: Using github.event.before and github.event.after for push events instead of default branch comparison

Both changes improve correctness and data handling. Please let me know if there's anything else to address!

@dcolina dcolina merged commit 305561b into main Dec 11, 2025
3 checks passed
@dcolina dcolina deleted the issue-339-deutsche-bank-specific-guard branch December 11, 2025 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants