feat: implement complete tree-sitter-shellspec grammar with comprehensive testing

- Add full ShellSpec grammar extending tree-sitter-bash
- Support all ShellSpec constructs: Describe, Context, It, hooks, utilities
- Include Data block parsing with statements and argument styles
- Add 61 comprehensive test cases covering real-world patterns
- Implement optimized GitHub workflows with CI/CD automation
- Configure complete development tooling (linting, formatting, pre-commit)
- Add comprehensive documentation and contribution guidelines
- Optimize grammar conflicts to zero warnings
- Support editor integration for Neovim, VS Code, Emacs

Breaking Changes:
- Initial release, no previous API to break

BREAKING CHANGE: Initial implementation of tree-sitter-shellspec grammar

# Conflicts:
#	.github/workflows/codeql.yml
#	.github/workflows/pr-lint.yml
#	.pre-commit-config.yaml

# Conflicts:
#	.github/workflows/pr-lint.yml

# Conflicts:
#	.github/workflows/pr-lint.yml
This commit is contained in:
2025-09-12 18:18:08 +03:00
parent 12d20a17b4
commit c8ba576b4e
77 changed files with 433271 additions and 242 deletions

120
.github/actions/README.md vendored Normal file
View File

@@ -0,0 +1,120 @@
# Composite Actions
This directory contains reusable composite actions to reduce duplication across workflows.
## Available Actions
### setup-node
Sets up Node.js with caching and installs dependencies.
**Inputs:**
- `node-version` (optional): Node.js version, defaults to '24'
- `registry-url` (optional): NPM registry URL
**Usage:**
```yaml
- uses: ./.github/actions/setup-node
with:
node-version: 22
```
### setup-treesitter
Installs Tree-sitter CLI and generates the grammar.
**Usage:**
```yaml
- uses: ./.github/actions/setup-treesitter
```
### setup-dev
Complete development environment setup (combines setup-node + setup-treesitter).
**Inputs:**
- `node-version` (optional): Node.js version, defaults to '24'
- `registry-url` (optional): NPM registry URL
- `skip-checkout` (optional): Skip repository checkout, defaults to 'false'
**Usage:**
```yaml
- uses: ./.github/actions/setup-dev
with:
node-version: 24
skip-checkout: 'true'
```
### test-grammar
Runs comprehensive grammar tests including parser validation.
**Inputs:**
- `skip-sample-test` (optional): Skip complex sample test, defaults to 'false'
**Usage:**
```yaml
- uses: ./.github/actions/test-grammar
with:
skip-sample-test: 'true'
```
### test-coverage
Analyzes test coverage and validates minimum requirements.
**Inputs:**
- `minimum-tests` (optional): Minimum tests required, defaults to '55'
**Outputs:**
- `total-tests`: Total number of tests found
- `passing-tests`: Number of passing tests
- `coverage-percent`: Test coverage percentage
**Usage:**
```yaml
- uses: ./.github/actions/test-coverage
with:
minimum-tests: 60
```
## Workflow Usage Examples
### Test Workflow (Simplified)
```yaml
jobs:
test:
steps:
- uses: ./.github/actions/setup-dev
with:
node-version: ${{ matrix.node-version }}
- uses: ./.github/actions/test-grammar
```
### Release Workflow (Simplified)
```yaml
jobs:
test:
steps:
- uses: ./.github/actions/setup-dev
- uses: ./.github/actions/test-grammar
with:
skip-sample-test: 'true'
lint:
steps:
- uses: ./.github/actions/setup-node
- uses: ivuorinen/actions/pr-lint@latest
```

38
.github/actions/setup-dev/action.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
---
name: "Setup Development Environment"
description: "Complete setup for tree-sitter-shellspec development including Node.js and Tree-sitter"
inputs:
node-version:
description: "Node.js version to setup"
required: false
default: "24"
registry-url:
description: "NPM registry URL"
required: false
default: ""
skip-checkout:
description: "Skip repository checkout (if already done)"
required: false
default: "false"
runs:
using: "composite"
steps:
- name: Checkout Repository
if: inputs.skip-checkout != 'true'
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Node.js ${{ inputs.node-version }}
uses: actions/setup-node@7c12f8017d5436eb855f1ed4399f037a36fbd9e8 # v5.2.1
with:
node-version: ${{ inputs.node-version }}
cache: npm
registry-url: ${{ inputs.registry-url }}
- name: Install Dependencies
run: npm ci || { echo "❌ npm install failed" && npm install }
shell: bash
- name: Setup Tree-sitter Environment
uses: ./.github/actions/setup-treesitter

30
.github/actions/setup-node/action.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
---
name: "Setup Node.js Environment"
description: "Sets up Node.js with caching and installs dependencies"
inputs:
node-version:
description: "Node.js version to setup"
required: false
default: "24"
registry-url:
description: "NPM registry URL"
required: false
default: ""
runs:
using: "composite"
steps:
- name: Checkout Repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Node.js ${{ inputs.node-version }}
uses: actions/setup-node@7c12f8017d5436eb855f1ed4399f037a36fbd9e8 # v5.2.1
with:
node-version: ${{ inputs.node-version }}
cache: npm
registry-url: ${{ inputs.registry-url }}
- name: Install Dependencies
run: npm ci || { echo "❌ npm install failed" && npm install }
shell: bash

View File

@@ -0,0 +1,14 @@
---
name: "Setup Tree-sitter Environment"
description: "Installs Tree-sitter CLI and generates grammar"
runs:
using: "composite"
steps:
- name: Install Tree-sitter CLI
run: npm install -g tree-sitter-cli
shell: bash
- name: Generate Grammar
run: npm run generate
shell: bash

View File

@@ -0,0 +1,71 @@
---
name: "Test Coverage Analysis"
description: "Analyzes test coverage and generates coverage report"
inputs:
minimum-tests:
description: "Minimum number of tests required"
required: false
default: "55"
outputs:
total-tests:
description: "Total number of tests found"
value: ${{ steps.coverage.outputs.total-tests }}
passing-tests:
description: "Number of passing tests"
value: ${{ steps.coverage.outputs.passing-tests }}
coverage-percent:
description: "Test coverage percentage"
value: ${{ steps.coverage.outputs.coverage-percent }}
runs:
using: "composite"
steps:
- name: Test Coverage Analysis
id: coverage
run: |
echo "## Test Coverage Report" > coverage_report.md
echo "" >> coverage_report.md
# Run tests and capture output
TEST_OUTPUT=$(npm test 2>&1)
TOTAL_TESTS=$(echo "$TEST_OUTPUT" | grep -E "^\s+[0-9]+\." | wc -l)
PASSING_TESTS=$(echo "$TEST_OUTPUT" | grep -E "^\s+[0-9]+\. ✓" | wc -l)
FAILING_TESTS=$(echo "$TEST_OUTPUT" | grep -E "^\s+[0-9]+\. ✗" | wc -l)
echo "- **Total Tests:** $TOTAL_TESTS" >> coverage_report.md
echo "- **Passing:** $PASSING_TESTS ✅" >> coverage_report.md
echo "- **Failing:** $FAILING_TESTS ❌" >> coverage_report.md
if [ $TOTAL_TESTS -gt 0 ]; then
COVERAGE_PERCENT=$(( (PASSING_TESTS * 100) / TOTAL_TESTS ))
echo "- **Coverage:** $COVERAGE_PERCENT%" >> coverage_report.md
else
COVERAGE_PERCENT=0
fi
echo "" >> coverage_report.md
echo "### Test Files" >> coverage_report.md
echo "$TEST_OUTPUT" | grep -E "^\s+[a-z_]+:" | sed 's/^/- /' >> coverage_report.md
cat coverage_report.md
# Set outputs
echo "total-tests=$TOTAL_TESTS" >> $GITHUB_OUTPUT
echo "passing-tests=$PASSING_TESTS" >> $GITHUB_OUTPUT
echo "coverage-percent=$COVERAGE_PERCENT" >> $GITHUB_OUTPUT
# Validate test coverage requirements
if [ $TOTAL_TESTS -lt ${{ inputs.minimum-tests }} ]; then
echo "❌ Expected at least ${{ inputs.minimum-tests }} tests, found $TOTAL_TESTS"
exit 1
fi
if [ $FAILING_TESTS -gt 0 ]; then
echo "❌ Found $FAILING_TESTS failing tests"
exit 1
fi
echo "✅ Test coverage is acceptable: $PASSING_TESTS/$TOTAL_TESTS tests passing ($COVERAGE_PERCENT%)"
shell: bash

77
.github/actions/test-grammar/action.yml vendored Normal file
View File

@@ -0,0 +1,77 @@
---
name: "Test Tree-sitter Grammar"
description: "Runs comprehensive grammar tests including parser validation"
inputs:
skip-sample-test:
description: "Skip the sample ShellSpec code test"
required: false
default: "false"
runs:
using: "composite"
steps:
- name: Run Tests
run: npm test
shell: bash
- name: Build Parser
run: npm run build
shell: bash
- name: Test Parser with Sample Code
if: inputs.skip-sample-test != 'true'
run: |
cat << 'EOF' > test_sample.shellspec
#!/usr/bin/env shellspec
Describe 'Calculator'
Include ./lib/calculator.sh
Before 'setup_calculator'
After 'cleanup_calculator'
Context 'when adding numbers'
It 'adds two positive numbers'
When call add 2 3
The output should eq 5
End
It 'handles zero'
When call add 0 5
The output should eq 5
End
End
Context 'when input is invalid'
Skip if "validation not implemented" ! command -v validate
It 'handles empty input'
When call add "" ""
The status should be failure
End
End
End
It 'works without describe block'
When call echo "test"
The output should eq "test"
End
EOF
tree-sitter parse test_sample.shellspec --quiet || {
echo "❌ Parser failed on sample ShellSpec code"
exit 1
}
echo "✅ Parser successfully handled sample code"
shell: bash
- name: Validate Parser (Simple)
if: inputs.skip-sample-test == 'true'
run: |
echo "Describe 'test' It 'works' End End" | tree-sitter parse --language=shellspec || {
echo "❌ Parser validation failed"
exit 1
}
echo "✅ Parser validation successful"
shell: bash