mirror of
https://github.com/ivuorinen/tree-sitter-shellspec.git
synced 2026-01-30 13:45:13 +00:00
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:
42
.github/CODE_OF_CONDUCT.md
vendored
42
.github/CODE_OF_CONDUCT.md
vendored
@@ -33,15 +33,15 @@ fullest extent, we want to know.
|
||||
|
||||
The following behaviors are expected and requested of all community members:
|
||||
|
||||
* Participate in an authentic and active way. In doing so, you contribute to the
|
||||
- Participate in an authentic and active way. In doing so, you contribute to the
|
||||
health and longevity of this community.
|
||||
* Exercise consideration and respect in your speech and actions.
|
||||
* Attempt collaboration before conflict.
|
||||
* Refrain from demeaning, discriminatory, or harassing behavior and speech.
|
||||
* Be mindful of your surroundings and of your fellow participants. Alert
|
||||
- Exercise consideration and respect in your speech and actions.
|
||||
- Attempt collaboration before conflict.
|
||||
- Refrain from demeaning, discriminatory, or harassing behavior and speech.
|
||||
- Be mindful of your surroundings and of your fellow participants. Alert
|
||||
community leaders if you notice a dangerous situation, someone in distress, or
|
||||
violations of this Code of Conduct, even if they seem inconsequential.
|
||||
* Remember that community event venues may be shared with members of the public;
|
||||
- Remember that community event venues may be shared with members of the public;
|
||||
please be respectful to all patrons of these locations.
|
||||
|
||||
## 4. Unacceptable Behavior
|
||||
@@ -49,23 +49,23 @@ The following behaviors are expected and requested of all community members:
|
||||
The following behaviors are considered harassment and are unacceptable within
|
||||
our community:
|
||||
|
||||
* Violence, threats of violence or violent language directed against another
|
||||
- Violence, threats of violence or violent language directed against another
|
||||
person.
|
||||
* Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory
|
||||
- Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory
|
||||
jokes and language.
|
||||
* Posting or displaying sexually explicit or violent material.
|
||||
* Posting or threatening to post other people's personally identifying
|
||||
- Posting or displaying sexually explicit or violent material.
|
||||
- Posting or threatening to post other people's personally identifying
|
||||
information ("doxing").
|
||||
* Personal insults, particularly those related to gender, sexual orientation,
|
||||
- Personal insults, particularly those related to gender, sexual orientation,
|
||||
race, religion, or disability.
|
||||
* Inappropriate photography or recording.
|
||||
* Inappropriate physical contact. You should have someone's consent before
|
||||
- Inappropriate photography or recording.
|
||||
- Inappropriate physical contact. You should have someone's consent before
|
||||
touching them.
|
||||
* Unwelcome sexual attention. This includes, sexualized comments or jokes;
|
||||
- Unwelcome sexual attention. This includes, sexualized comments or jokes;
|
||||
inappropriate touching, groping, and unwelcomed sexual advances.
|
||||
* Deliberate intimidation, stalking or following (online or in person).
|
||||
* Advocating for, or encouraging, any of the above behavior.
|
||||
* Sustained disruption of community events, including talks and presentations.
|
||||
- Deliberate intimidation, stalking or following (online or in person).
|
||||
- Advocating for, or encouraging, any of the above behavior.
|
||||
- Sustained disruption of community events, including talks and presentations.
|
||||
|
||||
## 5. Weapons Policy
|
||||
|
||||
@@ -133,10 +133,10 @@ under a [Creative Commons Attribution-ShareAlike license][cc-by-sa].
|
||||
Portions of text derived from the [Django Code of Conduct][django] and
|
||||
the [Geek Feminism Anti-Harassment Policy][geek-feminism].
|
||||
|
||||
* _Revision 2.3. Posted 6 March 2017._
|
||||
* _Revision 2.2. Posted 4 February 2016._
|
||||
* _Revision 2.1. Posted 23 June 2014._
|
||||
* _Revision 2.0, adopted by the [Stumptown Syndicate][stumptown] board on 10
|
||||
- _Revision 2.3. Posted 6 March 2017._
|
||||
- _Revision 2.2. Posted 4 February 2016._
|
||||
- _Revision 2.1. Posted 23 June 2014._
|
||||
- _Revision 2.0, adopted by the [Stumptown Syndicate][stumptown] board on 10
|
||||
January 2013. Posted 17 March 2013._
|
||||
|
||||
[stumptown]: https://github.com/stumpsyn
|
||||
|
||||
56
.github/ISSUE_TEMPLATE/bug_report.md
vendored
56
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,38 +1,44 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
about: Report a parsing issue or bug in tree-sitter-shellspec
|
||||
title: "[BUG] "
|
||||
labels: bug
|
||||
assignees: ivuorinen
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
A clear and concise description of the parsing issue or bug.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
**ShellSpec code that doesn't parse correctly**
|
||||
Please provide the ShellSpec code that causes the issue:
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
```shellspec
|
||||
# Paste your ShellSpec code here
|
||||
```
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
**Expected parsing behavior**
|
||||
A clear description of how the code should be parsed or what syntax highlighting you expected.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
**Actual behavior**
|
||||
What actually happens when the parser encounters this code? Include any error messages.
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
**Environment:**
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
- OS: [e.g. Linux, macOS, Windows]
|
||||
- Editor: [e.g. Neovim, VS Code, Emacs]
|
||||
- tree-sitter-shellspec version: [e.g. 0.1.0]
|
||||
- tree-sitter version: [e.g. 0.20.0]
|
||||
- ShellSpec version: [e.g. 0.28.1]
|
||||
|
||||
**Tree-sitter parse output (if applicable)**
|
||||
If you can run `tree-sitter parse`, please include the output:
|
||||
|
||||
```text
|
||||
# tree-sitter parse output here
|
||||
```
|
||||
|
||||
## Additional context
|
||||
|
||||
- Is this code from a real ShellSpec test file?
|
||||
- Does the code work correctly with the ShellSpec test runner?
|
||||
- Any other context that might help debug the issue.
|
||||
|
||||
32
.github/ISSUE_TEMPLATE/feature_request.md
vendored
32
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,20 +1,32 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
about: Suggest a grammar enhancement or new feature for tree-sitter-shellspec
|
||||
title: "[FEATURE] "
|
||||
labels: enhancement
|
||||
assignees: ivuorinen
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
**Is your feature request related to a ShellSpec parsing issue?**
|
||||
A clear description of what ShellSpec syntax is not currently supported. Ex. "Data blocks with :expand modifier are not parsed correctly"
|
||||
|
||||
**ShellSpec syntax example**
|
||||
Please provide an example of the ShellSpec syntax you'd like to see supported:
|
||||
|
||||
```shellspec
|
||||
# Example ShellSpec code that should be supported
|
||||
```
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
A clear description of how this syntax should be parsed or highlighted.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
**Current behavior**
|
||||
How does the parser currently handle this syntax? (if at all)
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
**Use case**
|
||||
Why is this syntax important? How commonly is it used in ShellSpec tests?
|
||||
|
||||
## Additional context
|
||||
|
||||
- Link to ShellSpec documentation for this feature (if available)
|
||||
- Examples from real-world ShellSpec test suites
|
||||
- Any other context or screenshots about the feature request
|
||||
|
||||
63
.github/ISSUE_TEMPLATE/grammar_issue.md
vendored
Normal file
63
.github/ISSUE_TEMPLATE/grammar_issue.md
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
name: Grammar Issue
|
||||
about: Report a specific grammar parsing problem or conflict
|
||||
title: "[GRAMMAR] "
|
||||
labels: grammar, bug
|
||||
assignees: ivuorinen
|
||||
---
|
||||
|
||||
## Grammar Issue Type
|
||||
|
||||
- [ ] Parsing error (code doesn't parse at all)
|
||||
- [ ] Incorrect parse tree structure
|
||||
- [ ] Grammar conflicts during generation
|
||||
- [ ] Performance issue with large files
|
||||
- [ ] Integration issue with tree-sitter-bash
|
||||
|
||||
## ShellSpec code causing the issue
|
||||
|
||||
```shellspec
|
||||
# Paste the problematic ShellSpec code here
|
||||
```
|
||||
|
||||
### Current parse tree output
|
||||
|
||||
If you can run `tree-sitter parse`, please include the current output:
|
||||
|
||||
```text
|
||||
# Current parse tree here
|
||||
```
|
||||
|
||||
### Expected parse tree structure
|
||||
Describe or show what the parse tree should look like:
|
||||
|
||||
```text
|
||||
# Expected parse tree structure here
|
||||
```
|
||||
|
||||
### Grammar generation output
|
||||
|
||||
If this causes issues during `npm run generate`, include any conflict warnings or errors:
|
||||
|
||||
```text
|
||||
# Grammar generation output here
|
||||
```
|
||||
|
||||
## Environment
|
||||
|
||||
- tree-sitter-shellspec version: [e.g. 0.1.0]
|
||||
- tree-sitter CLI version: [e.g. 0.20.8]
|
||||
- Node.js version: [e.g. 18.17.0]
|
||||
- OS: [e.g. macOS 13.5]
|
||||
|
||||
## Impact
|
||||
|
||||
- How does this affect syntax highlighting?
|
||||
- Does it break editor functionality?
|
||||
- Is this blocking real-world usage?
|
||||
|
||||
## Additional context
|
||||
|
||||
- Related to specific ShellSpec features?
|
||||
- Reproducible with minimal example?
|
||||
- Any workarounds discovered?
|
||||
120
.github/actions/README.md
vendored
Normal file
120
.github/actions/README.md
vendored
Normal 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
38
.github/actions/setup-dev/action.yml
vendored
Normal 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
30
.github/actions/setup-node/action.yml
vendored
Normal 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
|
||||
14
.github/actions/setup-treesitter/action.yml
vendored
Normal file
14
.github/actions/setup-treesitter/action.yml
vendored
Normal 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
|
||||
71
.github/actions/test-coverage/action.yml
vendored
Normal file
71
.github/actions/test-coverage/action.yml
vendored
Normal 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
77
.github/actions/test-grammar/action.yml
vendored
Normal 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
|
||||
8
.github/renovate.json
vendored
8
.github/renovate.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"github>ivuorinen/renovate-config"
|
||||
]
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"github>ivuorinen/renovate-config"
|
||||
]
|
||||
}
|
||||
|
||||
13
.github/workflows/codeql.yml
vendored
13
.github/workflows/codeql.yml
vendored
@@ -1,15 +1,14 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||
name: 'CodeQL'
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['main']
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: ['main']
|
||||
branches: ["main"]
|
||||
schedule:
|
||||
- cron: '30 1 * * 0' # Run at 1:30 AM UTC every Sunday
|
||||
merge_group:
|
||||
- cron: "30 1 * * 0" # Run at 1:30 AM UTC every Sunday
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -25,7 +24,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ['actions'] # Add languages used in your actions
|
||||
language: ['actions,javascript'] # Add languages used in your actions
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -43,4 +42,4 @@ jobs:
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
|
||||
with:
|
||||
category: '/language:${{matrix.language}}'
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
30
.github/workflows/pr-lint.yml
vendored
30
.github/workflows/pr-lint.yml
vendored
@@ -1,30 +0,0 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||
name: Lint Code Base
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, main]
|
||||
pull_request:
|
||||
branches: [master, main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
Linter:
|
||||
name: PR Lint
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
statuses: write
|
||||
contents: read
|
||||
packages: read
|
||||
|
||||
steps:
|
||||
- name: Run PR Lint
|
||||
# https://github.com/ivuorinen/actions
|
||||
uses: ivuorinen/actions/pr-lint@fb25736f7e7a438979c11764e9fe6a100278b4c5 # v2026.01.01
|
||||
198
.github/workflows/release.yml
vendored
Normal file
198
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version to release (e.g., 1.0.0)"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
name: 🔍 Validate Release
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
|
||||
steps:
|
||||
- name: ⤵️ Checkout Repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🔢 Extract Version
|
||||
id: version
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
echo "version=v${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
echo "Releasing version: ${VERSION}"
|
||||
|
||||
- name: ✅ Validate Version Format
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
if [[ ! $VERSION =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "❌ Invalid version format: $VERSION"
|
||||
echo "Expected format: v1.0.0 or v1.0.0-beta.1"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Version format is valid: $VERSION"
|
||||
|
||||
# Tests and linting are handled by the CI workflow that runs on push
|
||||
# This workflow only needs to run once CI passes on the tag
|
||||
check-ci:
|
||||
name: ✅ Verify CI Status
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
needs: validate
|
||||
|
||||
steps:
|
||||
- name: 📋 Check CI Workflow Status
|
||||
uses: actions/github-script@e1ec48de9e3eaf9b93b1c5f88eaf97ae19d7b7bb # v7.0.5
|
||||
with:
|
||||
script: |
|
||||
const { data: workflows } = await github.rest.actions.listWorkflowRuns({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'test.yml',
|
||||
head_sha: context.sha,
|
||||
status: 'completed'
|
||||
});
|
||||
|
||||
const latestRun = workflows.workflow_runs[0];
|
||||
if (!latestRun || latestRun.conclusion !== 'success') {
|
||||
core.setFailed('CI workflow has not passed for this commit');
|
||||
}
|
||||
|
||||
console.log(`CI status: ${latestRun?.conclusion || 'not found'}`)
|
||||
|
||||
security:
|
||||
name: 🔒 Security Scan
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
needs: validate
|
||||
|
||||
steps:
|
||||
- name: 🏗️ Setup Node.js Environment
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: 🔍 Run Security Audit
|
||||
run: npm audit --audit-level=high
|
||||
|
||||
release:
|
||||
name: 🚀 Release
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
needs: [validate, check-ci, security]
|
||||
if: always() && needs.validate.result == 'success' && needs.check-ci.result == 'success' && needs.security.result == 'success'
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: ⤵️ Checkout Repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🏗️ Setup Development Environment
|
||||
uses: ./.github/actions/setup-dev
|
||||
with:
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
skip-checkout: "true"
|
||||
|
||||
- name: 🏗️ Build Parser
|
||||
run: npm run build
|
||||
|
||||
- name: 📋 Update Package Version
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
npm version ${VERSION} --no-git-tag-version
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add package.json package-lock.json
|
||||
git commit -m "chore: bump version to ${VERSION}" || true
|
||||
|
||||
- name: 🏷️ Create Tag
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
VERSION="v${{ github.event.inputs.version }}"
|
||||
git tag ${VERSION}
|
||||
git push origin ${VERSION}
|
||||
|
||||
- name: 📝 Generate Release Notes
|
||||
id: release_notes
|
||||
run: |
|
||||
VERSION="${{ needs.validate.outputs.version }}"
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
||||
|
||||
{
|
||||
echo "## Release ${VERSION}"
|
||||
echo ""
|
||||
} > release_notes.md
|
||||
|
||||
if [ -n "$PREV_TAG" ]; then
|
||||
{
|
||||
echo "### Changes since ${PREV_TAG}"
|
||||
echo ""
|
||||
} >> release_notes.md
|
||||
git log --oneline --pretty=format:"- %s" "${PREV_TAG}..HEAD" >> release_notes.md
|
||||
else
|
||||
{
|
||||
echo "### Initial Release"
|
||||
echo ""
|
||||
echo "- Initial release of tree-sitter-shellspec"
|
||||
echo "- Complete ShellSpec grammar support"
|
||||
echo "- 59 comprehensive test cases"
|
||||
echo "- Real-world compatibility with official ShellSpec examples"
|
||||
} >> release_notes.md
|
||||
fi
|
||||
|
||||
{
|
||||
echo ""
|
||||
echo "### Installation"
|
||||
echo ""
|
||||
echo "\`\`\`bash"
|
||||
echo "npm install @ivuorinen/tree-sitter-shellspec"
|
||||
echo "\`\`\`"
|
||||
} >> release_notes.md
|
||||
|
||||
- name: 🚀 Create GitHub Release
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||
with:
|
||||
tag_name: ${{ needs.validate.outputs.version }}
|
||||
name: Release ${{ needs.validate.outputs.version }}
|
||||
body_path: release_notes.md
|
||||
draft: false
|
||||
prerelease: ${{ contains(needs.validate.outputs.version, '-') }}
|
||||
generate_release_notes: false
|
||||
|
||||
- name: 📦 Publish to npm
|
||||
run: npm publish --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: 📊 Release Summary
|
||||
run: |
|
||||
echo "🎉 Successfully released ${{ needs.validate.outputs.version }}"
|
||||
echo "📦 Published to npm: https://www.npmjs.com/package/@ivuorinen/tree-sitter-shellspec"
|
||||
echo "🏷️ GitHub Release: ${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ needs.validate.outputs.version }}"
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -4,7 +4,7 @@ name: Stale
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 8 * * *' # Every day at 08:00
|
||||
- cron: "0 8 * * *" # Every day at 08:00
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
7
.github/workflows/sync-labels.yml
vendored
7
.github/workflows/sync-labels.yml
vendored
@@ -8,13 +8,12 @@ on:
|
||||
- main
|
||||
- master
|
||||
paths:
|
||||
- '.github/labels.yml'
|
||||
- '.github/workflows/sync-labels.yml'
|
||||
- ".github/labels.yml"
|
||||
- ".github/workflows/sync-labels.yml"
|
||||
schedule:
|
||||
- cron: '34 5 * * *' # Run every day at 05:34 AM UTC
|
||||
- cron: "34 5 * * *" # Run every day at 05:34 AM UTC
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
merge_group:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
65
.github/workflows/test.yml
vendored
Normal file
65
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
merge_group:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: 🧪 Test Suite
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [22, 24]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- name: 🏗️ Setup Development Environment
|
||||
uses: ./.github/actions/setup-dev
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: 🧪 Test Grammar
|
||||
uses: ./.github/actions/test-grammar
|
||||
|
||||
lint:
|
||||
name: 🧹 Code Quality
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: 🏗️ Setup Node.js Environment
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: 🧹 Run Linter
|
||||
uses: ivuorinen/actions/pr-lint@22e6add79fabcca4bf5761452a51e4fa0207e155 # 25.9.8
|
||||
|
||||
coverage:
|
||||
name: 📊 Test Coverage
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
needs: test
|
||||
|
||||
steps:
|
||||
- name: 🏗️ Setup Development Environment
|
||||
uses: ./.github/actions/setup-dev
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: 📊 Test Coverage Analysis
|
||||
uses: ./.github/actions/test-coverage
|
||||
with:
|
||||
minimum-tests: 55
|
||||
Reference in New Issue
Block a user