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

View File

@@ -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

View File

@@ -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.

View File

@@ -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
View 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
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

View File

@@ -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"
]
}

View File

@@ -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}}"

View File

@@ -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
View 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 }}"

View File

@@ -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:

View File

@@ -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
View 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