diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..3a2a8f8 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,110 @@ +name: Test Action + +on: + push: + branches: [main, cursor/**] + pull_request: + workflow_dispatch: + +jobs: + test-validate: + name: Test Validate Command + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Create test structure file + run: | + cat > .struct.yaml << 'EOF' + files: + - test-file.txt: + content: "Hello from StructKit!" + EOF + + - name: Run validate + uses: ./ + with: + command: validate + struct_file: .struct.yaml + + test-generate-dry-run: + name: Test Generate Dry Run + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Create test structure file + run: | + cat > .struct.yaml << 'EOF' + files: + - test-output/sample.txt: + content: "Generated by StructKit Action!" + EOF + + - name: Run generate (dry-run) + id: generate + uses: ./ + with: + command: generate + struct_file: .struct.yaml + dry_run: true + + - name: Check outputs + run: | + echo "Success: ${{ steps.generate.outputs.success }}" + echo "Has changes: ${{ steps.generate.outputs.has_changes }}" + echo "Exit code: ${{ steps.generate.outputs.exit_code }}" + + test-generate-real: + name: Test Generate (Real) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Create test structure file + run: | + cat > .struct.yaml << 'EOF' + files: + - generated/test.txt: + content: "This file was generated!" + - generated/nested/file.txt: + content: "Nested file content" + EOF + + - name: Run generate + uses: ./ + with: + command: generate + struct_file: .struct.yaml + output_dir: . + + - name: Verify generated files + run: | + test -f generated/test.txt || (echo "File not created!" && exit 1) + test -f generated/nested/file.txt || (echo "Nested file not created!" && exit 1) + echo "Generated files verified successfully!" + + test-custom-version: + name: Test Custom StructKit Version + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Create test structure file + run: | + cat > .struct.yaml << 'EOF' + files: + - version-test.txt: + content: "Testing version" + EOF + + - name: Run with git installation + uses: ./ + with: + command: validate + struct_file: .struct.yaml + structkit_version: "https://github.com/httpdss/structkit.git@main" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9494692 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Virtual environments +venv/ +ENV/ +env/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Test outputs +.structkit-diff.txt +test-output/ +generated/ + +# Temporary files +*.tmp +*.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..66f5f2f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Kenneth Belitzky + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index ce6a39a..0ed6182 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,331 @@ -# struct-action -🚀 STRUCT Action – Automate project structure generation with STRUCT in your GitHub workflows! 📂 Ensure consistency across your projects by integrating STRUCT into your CI/CD pipeline. ⚡ Easily generate file and folder structures with just a few lines of configuration. 🔧 Simplify project setup and maintain uniformity effortlessly! +# StructKit Action + +[![GitHub](https://img.shields.io/github/license/httpdss/structkit-action)](LICENSE) + +A GitHub Action for running [StructKit](https://github.com/httpdss/structkit) commands in your CI/CD pipeline. Use this action to validate project structure definitions or generate files and folders automatically. + +## Features + +- 🔍 **Validate** structure definitions on every PR +- 🚀 **Generate** project structures in CI/CD workflows +- 🔄 **Drift Detection** - detect when generated files don't match definitions +- 🛡️ **Safe Defaults** - runs with `--no-hooks` and `--non-interactive` by default +- 📦 **Custom Structures** - supports external structure repositories +- 🎯 **Flexible Versioning** - install from PyPI, git, or specific versions + +## Quick Start + +### Validate on Pull Request + +```yaml +name: Validate Structure +on: [pull_request] + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: httpdss/structkit-action@v1 + with: + command: validate +``` + +### Detect Structure Drift + +Check if generated files match their definitions (fail if they don't): + +```yaml +name: Check Structure Drift +on: [pull_request] + +jobs: + drift-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: httpdss/structkit-action@v1 + with: + command: generate + dry_run: true + diff: true + fail_on_diff: true +``` + +### Generate Files + +Generate structure without creating a PR (useful for pre-commit hooks or local automation): + +```yaml +name: Generate Structure +on: + workflow_dispatch: + +jobs: + generate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: httpdss/structkit-action@v1 + with: + command: generate + struct_file: .struct.yaml + output_dir: . + + - name: Commit changes + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add . + git commit -m "chore: regenerate structure" || echo "No changes to commit" + git push +``` + +## Inputs + +| Input | Description | Required | Default | +|-------|-------------|----------|---------| +| `command` | Command to run: `validate` or `generate` | No | `validate` | +| `struct_file` | Path to the StructKit configuration file | No | `.struct.yaml` | +| `output_dir` | Output directory for generated files | No | `.` | +| `dry_run` | Run in dry-run mode (preview changes without writing) | No | `false` | +| `diff` | Show diff of changes (use with dry_run for drift detection) | No | `false` | +| `no_hooks` | Disable hooks during execution (check if your StructKit version supports this flag) | No | `false` | +| `non_interactive` | Run in non-interactive mode (check if your StructKit version supports this flag) | No | `false` | +| `structkit_version` | StructKit version to install (version number, `latest`, or git URL) | No | `latest` | +| `structures_path` | Path to custom structures directory | No | `''` | +| `structures_repository` | Custom structures repository to checkout (format: `owner/repo`) | No | `''` | +| `structures_repository_path` | Path within structures_repository where structures are located | No | `structures` | +| `structures_repository_ref` | Git ref (branch/tag/commit) to checkout from structures_repository | No | `main` | +| `extra_args` | Additional arguments to pass to StructKit command | No | `''` | +| `python_version` | Python version to use | No | `3.x` | +| `fail_on_diff` | Fail the action if changes would be made (drift detection) | No | `false` | + +## Outputs + +| Output | Description | +|--------|-------------| +| `success` | Whether the command completed successfully (`true`/`false`) | +| `has_changes` | Whether the command would make changes (dry-run) or made changes (`true`/`false`) | +| `diff_file` | Path to the diff output file (if generated) | +| `exit_code` | Exit code from the StructKit command | + +## Advanced Examples + +### Use Specific StructKit Version + +```yaml +- uses: httpdss/structkit-action@v1 + with: + command: validate + structkit_version: "1.2.3" +``` + +### Install from Git + +```yaml +- uses: httpdss/structkit-action@v1 + with: + command: generate + structkit_version: "https://github.com/httpdss/structkit.git@main" +``` + +### Use Custom Structures Repository + +```yaml +- uses: httpdss/structkit-action@v1 + with: + command: generate + structures_repository: myorg/my-structures + structures_repository_path: templates + structures_repository_ref: v2.0 +``` + +### Generate with Extra Arguments + +```yaml +- uses: httpdss/structkit-action@v1 + with: + command: generate + extra_args: "--verbose --force" +``` + +### Use Action Outputs + +```yaml +- uses: httpdss/structkit-action@v1 + id: structkit + with: + command: generate + dry_run: true + diff: true + +- name: Check for changes + if: steps.structkit.outputs.has_changes == 'true' + run: | + echo "Structure would be modified!" + cat ${{ steps.structkit.outputs.diff_file }} +``` + +### Validate Multiple Structure Files + +```yaml +jobs: + validate: + runs-on: ubuntu-latest + strategy: + matrix: + struct_file: + - .struct.yaml + - config/api.struct.yaml + - config/frontend.struct.yaml + steps: + - uses: actions/checkout@v7 + - uses: httpdss/structkit-action@v1 + with: + command: validate + struct_file: ${{ matrix.struct_file }} +``` + +## Comparison with Reusable Workflow + +This action is designed to be a **step** in your workflow, not a complete workflow. Key differences: + +| Feature | This Action | Reusable Workflow | +|---------|-------------|-------------------| +| Type | Composite action (step) | Complete workflow (job) | +| PR Creation | No (you control it) | Yes (built-in) | +| Flexibility | High (mix with other steps) | Lower (standalone job) | +| Use Case | Custom workflows | Quick automation | + +If you need automatic PR creation, use the [reusable workflow](https://github.com/httpdss/structkit/blob/main/.github/workflows/struct-generate.yaml). If you need fine-grained control over your pipeline, use this action. + +## Requirements + +- Python 3.x (automatically installed by the action) +- GitHub Actions runner with bash support + +## Compatibility Note + +This action is designed to work with different versions of StructKit. Some command-line flags (`--no-hooks`, `--non-interactive`, `--diff`, `--dry-run`) may not be available in all versions. The action defaults to not using these flags unless explicitly enabled. Check your [StructKit version's documentation](https://github.com/httpdss/structkit) to confirm which flags are supported. + +## How It Works + +1. **Setup** - Installs Python and StructKit +2. **Checkout** - Optionally checks out custom structures repository +3. **Execute** - Runs the specified StructKit command +4. **Outputs** - Provides execution results for downstream steps +5. **Cleanup** - Removes temporary files + +## Common Patterns + +### Pre-merge Validation + +```yaml +name: Validate +on: + pull_request: + branches: [main] + +jobs: + validate-structure: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: httpdss/structkit-action@v1 + with: + command: validate +``` + +### Scheduled Drift Detection + +```yaml +name: Daily Drift Check +on: + schedule: + - cron: '0 0 * * *' + +jobs: + drift-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: httpdss/structkit-action@v1 + with: + command: generate + dry_run: true + diff: true + fail_on_diff: true +``` + +### Manual Generation with Approval + +```yaml +name: Generate Structure +on: + workflow_dispatch: + +jobs: + generate: + runs-on: ubuntu-latest + environment: production + steps: + - uses: actions/checkout@v7 + + - uses: httpdss/structkit-action@v1 + with: + command: generate + + - uses: peter-evans/create-pull-request@v7 + with: + commit-message: "chore: regenerate structure" + title: "Update generated structure" + body: "Automated structure regeneration" + branch: "structkit/update-${{ github.run_id }}" +``` + +## Troubleshooting + +### Command Not Found + +If `structkit` command is not found, ensure Python is properly set up: + +```yaml +- uses: actions/setup-python@v5 + with: + python-version: '3.x' +- uses: httpdss/structkit-action@v1 +``` + +### Permission Denied + +When using `structures_repository`, ensure your workflow has access: + +```yaml +permissions: + contents: read +``` + +### Dry Run Shows No Changes + +The action detects changes by parsing StructKit output. If the output format changes, the detection might not work. Check the action logs for the actual command output. + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +## License + +MIT License - see [LICENSE](LICENSE) file for details. + +## Related Projects + +- [StructKit](https://github.com/httpdss/structkit) - The main StructKit project +- [StructKit Reusable Workflow](https://github.com/httpdss/structkit/blob/main/.github/workflows/struct-generate.yaml) - Full workflow with PR creation + +## Support + +For issues and questions: +- [Open an issue](https://github.com/httpdss/structkit-action/issues) +- [StructKit Documentation](https://github.com/httpdss/structkit) diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..c678c8f --- /dev/null +++ b/action.yml @@ -0,0 +1,205 @@ +name: 'StructKit Action' +description: 'Run StructKit validate or generate commands in your CI/CD pipeline' +author: 'Kenneth Belitzky' +branding: + icon: 'folder' + color: 'blue' + +inputs: + command: + description: 'Command to run: validate or generate' + required: false + default: 'validate' + struct_file: + description: 'Path to the StructKit configuration file' + required: false + default: '.struct.yaml' + output_dir: + description: 'Output directory for generated files' + required: false + default: '.' + dry_run: + description: 'Run in dry-run mode (preview changes without writing)' + required: false + default: 'false' + diff: + description: 'Show diff of changes (use with dry_run for drift detection)' + required: false + default: 'false' + no_hooks: + description: 'Disable hooks during execution (check if your StructKit version supports this flag)' + required: false + default: 'false' + non_interactive: + description: 'Run in non-interactive mode (check if your StructKit version supports this flag)' + required: false + default: 'false' + structkit_version: + description: 'StructKit version to install (PyPI version number, "latest", or git URL)' + required: false + default: 'latest' + structures_path: + description: 'Path to custom structures directory (leave empty for default)' + required: false + default: '' + structures_repository: + description: 'Custom structures repository to checkout (format: owner/repo)' + required: false + default: '' + structures_repository_path: + description: 'Path within structures_repository where structures are located' + required: false + default: 'structures' + structures_repository_ref: + description: 'Git ref (branch/tag/commit) to checkout from structures_repository' + required: false + default: 'main' + extra_args: + description: 'Additional arguments to pass to StructKit command' + required: false + default: '' + python_version: + description: 'Python version to use' + required: false + default: '3.x' + fail_on_diff: + description: 'Fail the action if changes would be made (drift detection)' + required: false + default: 'false' + +outputs: + success: + description: 'Whether the command completed successfully' + value: ${{ steps.run-structkit.outputs.success }} + has_changes: + description: 'Whether the command would make changes (dry-run) or made changes' + value: ${{ steps.run-structkit.outputs.has_changes }} + diff_file: + description: 'Path to the diff output file (if generated)' + value: ${{ steps.run-structkit.outputs.diff_file }} + exit_code: + description: 'Exit code from the StructKit command' + value: ${{ steps.run-structkit.outputs.exit_code }} + +runs: + using: 'composite' + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python_version }} + + - name: Install StructKit + shell: bash + run: | + if [[ "${{ inputs.structkit_version }}" == "latest" ]]; then + echo "Installing StructKit from PyPI (latest)" + pip install structkit + elif [[ "${{ inputs.structkit_version }}" == http* ]]; then + echo "Installing StructKit from git URL: ${{ inputs.structkit_version }}" + pip install "git+${{ inputs.structkit_version }}" + else + echo "Installing StructKit version: ${{ inputs.structkit_version }}" + pip install "structkit==${{ inputs.structkit_version }}" + fi + echo "StructKit installed successfully" + structkit --help > /dev/null + + - name: Checkout custom structures repository + if: inputs.structures_repository != '' + uses: actions/checkout@v7 + with: + repository: ${{ inputs.structures_repository }} + ref: ${{ inputs.structures_repository_ref }} + path: .structkit-custom-structures + token: ${{ github.token }} + + - name: Run StructKit + id: run-structkit + shell: bash + run: | + set +e + + # Build command arguments + COMMAND="${{ inputs.command }}" + ARGS=() + + # Add non-interactive flag if enabled + if [[ "${{ inputs.non_interactive }}" == "true" ]]; then + ARGS+=("--non-interactive") + fi + + # Add no-hooks flag if enabled + if [[ "${{ inputs.no_hooks }}" == "true" ]]; then + ARGS+=("--no-hooks") + fi + + # Add dry-run flag if enabled + if [[ "${{ inputs.dry_run }}" == "true" ]]; then + ARGS+=("--dry-run") + fi + + # Add diff flag if enabled + if [[ "${{ inputs.diff }}" == "true" ]]; then + ARGS+=("--diff") + fi + + # Add custom structures path if specified + if [[ -n "${{ inputs.structures_path }}" ]]; then + ARGS+=("-s" "${{ inputs.structures_path }}") + elif [[ -d ".structkit-custom-structures/${{ inputs.structures_repository_path }}" ]]; then + ARGS+=("-s" ".structkit-custom-structures/${{ inputs.structures_repository_path }}") + fi + + # Add extra args + if [[ -n "${{ inputs.extra_args }}" ]]; then + ARGS+=(${{ inputs.extra_args }}) + fi + + # Add positional arguments for generate command + if [[ "$COMMAND" == "generate" ]]; then + ARGS+=("${{ inputs.struct_file }}" "${{ inputs.output_dir }}") + elif [[ "$COMMAND" == "validate" ]]; then + ARGS+=("${{ inputs.struct_file }}") + fi + + # Run the command + echo "Running: structkit $COMMAND ${ARGS[@]}" + OUTPUT_FILE=$(mktemp) + DIFF_FILE="" + + if [[ "${{ inputs.diff }}" == "true" ]]; then + DIFF_FILE="${GITHUB_WORKSPACE}/.structkit-diff.txt" + structkit "$COMMAND" "${ARGS[@]}" | tee "$OUTPUT_FILE" > "$DIFF_FILE" + EXIT_CODE=${PIPESTATUS[0]} + else + structkit "$COMMAND" "${ARGS[@]}" | tee "$OUTPUT_FILE" + EXIT_CODE=$? + fi + + # Check for changes + HAS_CHANGES="false" + if grep -qE "(would be created|would be modified|would be deleted|Creating|Modifying|Deleting)" "$OUTPUT_FILE" 2>/dev/null; then + HAS_CHANGES="true" + fi + + # Set outputs + echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT + echo "success=$([ $EXIT_CODE -eq 0 ] && echo true || echo false)" >> $GITHUB_OUTPUT + echo "has_changes=$HAS_CHANGES" >> $GITHUB_OUTPUT + echo "diff_file=${DIFF_FILE}" >> $GITHUB_OUTPUT + + rm -f "$OUTPUT_FILE" + + # Clean up custom structures if checked out + if [[ -d ".structkit-custom-structures" ]]; then + rm -rf .structkit-custom-structures + fi + + # Fail if requested and changes detected + if [[ "${{ inputs.fail_on_diff }}" == "true" ]] && [[ "$HAS_CHANGES" == "true" ]]; then + echo "::error::Changes detected! Structure drift found." + exit 1 + fi + + exit $EXIT_CODE diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..e6f6422 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,26 @@ +# StructKit Action Examples + +This directory contains example workflows demonstrating various use cases for the StructKit Action. + +## Examples + +1. **validate-pr.yml** - Validate structure definitions on pull requests +2. **drift-detection.yml** - Detect structure drift in CI +3. **generate-and-commit.yml** - Generate structures and create a PR +4. **scheduled-check.yml** - Scheduled drift detection +5. **custom-structures.yml** - Using external structure repositories + +## Usage + +Copy any example to your `.github/workflows/` directory and customize as needed. + +```bash +cp examples/validate-pr.yml .github/workflows/ +``` + +## Customization Tips + +- Replace version pins (`@v1`) with specific commit SHAs for production use +- Adjust `struct_file` paths to match your repository structure +- Configure branch names and PR settings according to your workflow +- Add environment-specific secrets if using private structure repositories diff --git a/examples/custom-structures.yml b/examples/custom-structures.yml new file mode 100644 index 0000000..6098340 --- /dev/null +++ b/examples/custom-structures.yml @@ -0,0 +1,38 @@ +name: Generate with Custom Structures + +on: + workflow_dispatch: + push: + branches: [main] + paths: + - '.struct.yaml' + +jobs: + generate-with-custom: + name: Generate Using Custom Structures + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Generate with custom structures + uses: httpdss/structkit-action@v1 + with: + command: generate + struct_file: .struct.yaml + structures_repository: myorg/my-custom-structures + structures_repository_path: templates + structures_repository_ref: v1.0.0 + + - name: Commit changes + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + if [[ -n $(git status -s) ]]; then + git add . + git commit -m "chore: regenerate structure with custom templates" + git push + else + echo "No changes to commit" + fi diff --git a/examples/drift-detection.yml b/examples/drift-detection.yml new file mode 100644 index 0000000..a6a5490 --- /dev/null +++ b/examples/drift-detection.yml @@ -0,0 +1,61 @@ +name: Drift Detection + +on: + pull_request: + branches: [main] + push: + branches: [main] + +jobs: + detect-drift: + name: Check for Structure Drift + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Check for drift + id: drift-check + uses: httpdss/structkit-action@v1 + with: + command: generate + struct_file: .struct.yaml + dry_run: true + diff: true + fail_on_diff: true + + - name: Upload drift report + if: failure() && steps.drift-check.outputs.has_changes == 'true' + uses: actions/upload-artifact@v4 + with: + name: drift-report + path: ${{ steps.drift-check.outputs.diff_file }} + + - name: Comment on PR with drift + if: failure() && steps.drift-check.outputs.has_changes == 'true' && github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const diffFile = '${{ steps.drift-check.outputs.diff_file }}'; + const diff = fs.existsSync(diffFile) ? fs.readFileSync(diffFile, 'utf8') : 'No diff available'; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## ⚠️ Structure Drift Detected + +The generated files do not match the structure definition. + +
+View Changes + +\`\`\`diff +${diff.slice(0, 5000)} +\`\`\` + +
+ +Please regenerate the structure or update the definition.` + }) diff --git a/examples/generate-and-commit.yml b/examples/generate-and-commit.yml new file mode 100644 index 0000000..2d2b2b7 --- /dev/null +++ b/examples/generate-and-commit.yml @@ -0,0 +1,48 @@ +name: Generate Structure + +on: + workflow_dispatch: + inputs: + struct_file: + description: 'Structure file to use' + required: false + default: '.struct.yaml' + +jobs: + generate: + name: Generate and Create PR + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Generate structure + uses: httpdss/structkit-action@v1 + with: + command: generate + struct_file: ${{ github.event.inputs.struct_file || '.struct.yaml' }} + output_dir: . + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: regenerate structure from ${{ github.event.inputs.struct_file || '.struct.yaml' }}" + title: "🤖 Regenerate Project Structure" + body: | + ## Automated Structure Regeneration + + This PR was automatically generated by the StructKit Action. + + **Structure File:** `${{ github.event.inputs.struct_file || '.struct.yaml' }}` + **Workflow Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + Please review the changes before merging. + branch: structkit/regenerate-${{ github.run_id }} + delete-branch: true + labels: | + automated + structkit diff --git a/examples/scheduled-check.yml b/examples/scheduled-check.yml new file mode 100644 index 0000000..c38477b --- /dev/null +++ b/examples/scheduled-check.yml @@ -0,0 +1,59 @@ +name: Scheduled Drift Check + +on: + schedule: + # Run every day at 9 AM UTC + - cron: '0 9 * * *' + workflow_dispatch: + +jobs: + scheduled-drift-check: + name: Daily Structure Drift Check + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Check for drift + id: drift + continue-on-error: true + uses: httpdss/structkit-action@v1 + with: + command: generate + dry_run: true + diff: true + fail_on_diff: true + + - name: Create issue if drift detected + if: steps.drift.outputs.has_changes == 'true' + uses: actions/github-script@v7 + with: + script: | + const today = new Date().toISOString().split('T')[0]; + + github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: `Structure Drift Detected - ${today}`, + body: `## ⚠️ Structure Drift Alert + +A scheduled check has detected that the project structure has drifted from its definition. + +**Detected:** ${today} +**Workflow Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + +Please review and either: +- Regenerate the structure to match the definition +- Update the structure definition to match the current state + +The drift report is available in the workflow artifacts.`, + labels: ['drift-detection', 'structkit', 'automated'] + }) + + - name: Upload drift report + if: steps.drift.outputs.has_changes == 'true' + uses: actions/upload-artifact@v4 + with: + name: drift-report-${{ github.run_id }} + path: ${{ steps.drift.outputs.diff_file }} + retention-days: 30 diff --git a/examples/validate-pr.yml b/examples/validate-pr.yml new file mode 100644 index 0000000..3c6ba10 --- /dev/null +++ b/examples/validate-pr.yml @@ -0,0 +1,34 @@ +name: Validate Structure + +on: + pull_request: + branches: [main, develop] + paths: + - '**.struct.yaml' + - '.github/workflows/validate-pr.yml' + +jobs: + validate: + name: Validate StructKit Definition + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Validate structure + uses: httpdss/structkit-action@v1 + with: + command: validate + struct_file: .struct.yaml + + - name: Comment on PR + if: failure() + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '❌ Structure validation failed! Please check the logs for details.' + })