├── .github
├── dependabot.yml
└── workflows
│ ├── package-crates.yml
│ ├── release.yml
│ ├── regenerate.yml
│ ├── package-maven.yml
│ ├── package-npm.yml
│ └── package-pypi.yml
└── README.md
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: github-actions
4 | directory: /
5 | schedule:
6 | interval: weekly
7 | day: sunday
8 | commit-message:
9 | prefix: ci
10 | labels: [dependencies]
11 | open-pull-requests-limit: 1
12 | groups:
13 | actions:
14 | patterns: ["*"]
15 |
--------------------------------------------------------------------------------
/.github/workflows/package-crates.yml:
--------------------------------------------------------------------------------
1 | name: Publish package (crates.io)
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | package-name:
7 | description: The name of the package
8 | default: ${{github.event.repository.name}}
9 | type: string
10 | environment-name:
11 | description: The name of the environment
12 | default: crates
13 | type: string
14 | rust-toolchain:
15 | description: The Rust toolchain
16 | default: ${{vars.RUST_TOOLCHAIN || 'stable'}}
17 | type: string
18 | generate:
19 | description: Generate the parser artifacts
20 | default: false
21 | type: boolean
22 | abi-version:
23 | description: The tree-sitter ABI version
24 | default: 15
25 | type: number
26 | secrets:
27 | CARGO_REGISTRY_TOKEN:
28 | description: An authentication token for crates.io
29 | required: true
30 |
31 | jobs:
32 | package:
33 | name: Publish Rust package
34 | runs-on: ubuntu-latest
35 | environment:
36 | name: ${{inputs.environment-name}}
37 | url: https://crates.io/crates/${{inputs.package-name}}
38 | steps:
39 | - name: Checkout repository
40 | uses: actions/checkout@v6
41 | - name: Set up Rust
42 | uses: actions-rust-lang/setup-rust-toolchain@v1
43 | with:
44 | toolchain: ${{inputs.rust-toolchain}}
45 | - name: Set up tree-sitter CLI
46 | if: ${{inputs.generate}}
47 | uses: tree-sitter/setup-action/cli@v2
48 | - name: Install dependencies
49 | run: |-
50 | if jq -e "$JQ_SCRIPT" package.json >/dev/null; then
51 | npm i --ignore-scripts --omit peer --omit optional
52 | fi
53 | env:
54 | JQ_SCRIPT: >-
55 | .dependencies + .devDependencies |
56 | with_entries(select(
57 | (.key | startswith("tree-sitter-"))
58 | and (.key != "tree-sitter-cli")
59 | )) | length > 0
60 | - name: Regenerate parser
61 | if: ${{inputs.generate}}
62 | run: |
63 | while read -r grammar_dir; do
64 | pushd "$grammar_dir"
65 | tree-sitter generate
66 | popd > /dev/null
67 | done < <(jq -r '.grammars[].path // "."' tree-sitter.json)
68 | env:
69 | TREE_SITTER_ABI_VERSION: ${{inputs.abi-version}}
70 | - name: Publish to crates.io
71 | run: cargo publish
72 | env:
73 | CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_REGISTRY_TOKEN}}
74 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Publish GitHub release
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | generate:
7 | description: Generate the parser artifacts
8 | default: false
9 | type: boolean
10 | release-body:
11 | description: The body of the release notes
12 | type: string
13 | abi-version:
14 | description: The tree-sitter ABI version
15 | default: 15
16 | type: number
17 |
18 | permissions:
19 | contents: write
20 | id-token: write
21 | attestations: write
22 |
23 | jobs:
24 | release:
25 | name: Build release artifacts
26 | runs-on: ubuntu-latest
27 | env:
28 | REPO_NAME: ${{github.event.repository.name}}
29 | steps:
30 | - name: Checkout repository
31 | uses: actions/checkout@v6
32 | - name: Set up tree-sitter CLI
33 | uses: tree-sitter/setup-action/cli@v2
34 | - name: Install dependencies
35 | run: |-
36 | if jq -e "$JQ_SCRIPT" package.json >/dev/null; then
37 | npm i --ignore-scripts --omit peer --omit optional
38 | fi
39 | env:
40 | JQ_SCRIPT: >-
41 | .dependencies + .devDependencies |
42 | with_entries(select(
43 | (.key | startswith("tree-sitter-"))
44 | and (.key != "tree-sitter-cli")
45 | )) | length > 0
46 | - name: Generate parser
47 | if: inputs.generate
48 | shell: bash
49 | run: |
50 | while read -r grammar_dir; do
51 | pushd "$grammar_dir"
52 | tree-sitter generate
53 | popd > /dev/null
54 | done < <(jq -r '.grammars[].path // "."' tree-sitter.json)
55 | env:
56 | TREE_SITTER_ABI_VERSION: ${{inputs.abi-version}}
57 | - name: Build Wasm binaries
58 | shell: bash
59 | run: |-
60 | while read -r grammar_dir; do
61 | tree-sitter build --wasm "$grammar_dir"
62 | done < <(jq -r '.grammars[].path // "."' tree-sitter.json)
63 | - name: Create source code tarball
64 | run: |
65 | git ls-files > "$RUNNER_TEMP/files"
66 | while read -r src_dir; do
67 | printf '%s\n' >> "$RUNNER_TEMP/files" \
68 | "$src_dir"/parser.c "$src_dir"/grammar.json \
69 | "$src_dir"/node-types.json "$src_dir"/tree_sitter/*
70 | done < <(jq -r "$JQ_SCRIPT" tree-sitter.json)
71 | tar cvzf "$REPO_NAME.tar.gz" -T <(sort -u "$RUNNER_TEMP/files")
72 | env:
73 | JQ_SCRIPT: >-
74 | .grammars[] |
75 | if (.path? // ".") == "."
76 | then "src"
77 | else "\(.path)/src"
78 | end
79 | - name: Generate attestations
80 | uses: actions/attest-build-provenance@v3
81 | with:
82 | subject-path: |
83 | *.wasm
84 | ${{env.REPO_NAME}}.tar.gz
85 | - name: Create GitHub release
86 | run: |-
87 | RELEASE_NOTE="**NOTE:** Download \`$REPO_NAME.tar.gz\` for the *complete* source code."
88 | RELEASE_NOTE="${RELEASE_BODY}${RELEASE_BODY:+$'\n\n---\n\n'}${RELEASE_NOTE}"
89 | gh release create "$GITHUB_REF_NAME" *.wasm "$REPO_NAME.tar.gz" -n "$RELEASE_NOTE"
90 | env:
91 | GH_TOKEN: ${{github.token}}
92 | RELEASE_BODY: ${{inputs.release-body}}
93 |
--------------------------------------------------------------------------------
/.github/workflows/regenerate.yml:
--------------------------------------------------------------------------------
1 | name: Regenerate parser
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | abi-version:
7 | description: The tree-sitter ABI version
8 | default: 15
9 | type: number
10 | node-version:
11 | description: The NodeJS version
12 | default: ${{vars.NODE_VERSION || 'lts/*'}}
13 | type: string
14 | update-scanner:
15 | description: Update the scanner
16 | default: false
17 | type: boolean
18 | commit-message:
19 | description: The commit message
20 | default: "build: regenerate parser [dependabot skip]"
21 | type: string
22 | commit-author-name:
23 | description: "The commit author's username"
24 | default: dependabot[bot]
25 | type: string
26 | commit-author-email:
27 | description: "The commit author's email"
28 | default: 49699333+dependabot[bot]@users.noreply.github.com
29 | type: string
30 |
31 | permissions:
32 | contents: write
33 | pull-requests: write
34 |
35 | jobs:
36 | metadata:
37 | runs-on: ubuntu-latest
38 | steps:
39 | - name: Fetch dependabot metadata
40 | id: metadata
41 | uses: dependabot/fetch-metadata@v2
42 | outputs:
43 | package-ecosystem: ${{steps.metadata.outputs.package-ecosystem}}
44 | regenerate:
45 | runs-on: ubuntu-latest
46 | needs: [metadata]
47 | if: needs.metadata.outputs.package-ecosystem == 'npm_and_yarn'
48 | steps:
49 | - name: Checkout repository
50 | uses: actions/checkout@v6
51 | with:
52 | ref: ${{github.head_ref}}
53 | - name: Set up NodeJS
54 | uses: actions/setup-node@v6
55 | with:
56 | node-version: ${{inputs.node-version}}
57 | - name: Install parser dependencies
58 | run: |-
59 | npm i --legacy-peer-deps
60 | while read -r grammar_dir; do
61 | pushd "$grammar_dir"
62 | npm x -- tree-sitter generate
63 | popd > /dev/null
64 | done < <(jq -r '.grammars[].path // "."' tree-sitter.json)
65 | env:
66 | TREE_SITTER_ABI_VERSION: ${{inputs.abi-version}}
67 | - name: Update scanner
68 | if: inputs.update-scanner == true
69 | run: |-
70 | language=$(jq -r '.name' src/grammar.json)
71 | parent=$(jq -r '.inherits//empty' src/grammar.json)
72 | if [[ -z $parent ]]; then
73 | parent=$(gh api --jq '.[]|select(.property_name == "Inherits")|.value' $PROPS_API)
74 | if [[ -z $parent ]]; then
75 | parent=$(sed -rn 's/.*require\(.tree-sitter-(\w+)\/grammar.\).*/\1/p' grammar.js)
76 | if [[ -z $parent ]]; then
77 | printf '::error::Failed to detect the parent language.\n'; exit 1
78 | fi
79 | fi
80 | fi
81 | sed "s/tree_sitter_${parent}_/tree_sitter_${language}_/" \
82 | "node_modules/tree-sitter-${parent}/src/scanner.c" > src/scanner.c
83 | env:
84 | GH_TOKEN: ${{github.token}}
85 | PROPS_API: repos/${{github.repository}}/properties/values
86 | - name: Commit changes
87 | run: |-
88 | if ! git diff --quiet -- src && git add src; then
89 | git commit -m '${{inputs.commit-message}}'
90 | git push origin HEAD:$GITHUB_HEAD_REF
91 | fi
92 | env:
93 | GIT_AUTHOR_NAME: ${{inputs.commit-author-name}}
94 | GIT_AUTHOR_EMAIL: ${{inputs.commit-author-email}}
95 | GIT_COMMITTER_NAME: ${{inputs.commit-author-name}}
96 | GIT_COMMITTER_EMAIL: ${{inputs.commit-author-email}}
97 |
--------------------------------------------------------------------------------
/.github/workflows/package-maven.yml:
--------------------------------------------------------------------------------
1 | name: Publish package (maven)
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | package-name:
7 | description: The name of the package
8 | default: "" # jtreesitter-${language_name}
9 | type: string
10 | package-namespace:
11 | description: The namespace of the package
12 | default: "" # io.github.${repo_owner}
13 | type: string
14 | environment-name:
15 | description: The name of the environment
16 | default: maven
17 | type: string
18 | java-version:
19 | description: The Java version
20 | default: ${{vars.JAVA_VERSION || '25'}}
21 | type: string
22 | java-distribution:
23 | description: The Java distribution
24 | default: temurin
25 | type: string
26 | generate:
27 | description: Generate the parser artifacts
28 | default: false
29 | type: boolean
30 | abi-version:
31 | description: The tree-sitter ABI version
32 | default: 15
33 | type: number
34 | secrets:
35 | MAVEN_CENTRAL_USERNAME:
36 | description: A username for Maven Central
37 | required: true
38 | MAVEN_CENTRAL_TOKEN:
39 | description: An authorization token for Maven Central
40 | required: true
41 | GPG_PRIVATE_KEY:
42 | description: A GPG private key
43 | required: true
44 | GPG_PASSPHRASE:
45 | description: The passphrase of the GPG key
46 | required: true
47 |
48 | jobs:
49 | prepare:
50 | name: Prepare variables
51 | runs-on: ubuntu-latest
52 | outputs:
53 | url: ${{steps.prepare-url.outputs.url}}
54 | steps:
55 | - name: Prepare environment URL
56 | id: prepare-url
57 | shell: bash
58 | run: |-
59 | if [[ -n $NAMESPACE ]]; then
60 | NAMESPACE="io.github.${GITHUB_REPOSITORY_OWNER}"
61 | fi
62 | if [[ -n $NAME ]]; then
63 | : '${{github.event.repository.name}}'
64 | NAME="${_/tree-sitter-/jtreesitter-}"
65 | fi
66 | printf 'url=%s/%s/%s\n' "$BASE" "$NAMESPACE" "$NAME" >> $GITHUB_OUTPUT
67 | env:
68 | BASE: https://central.sonatype.com/artifact
69 | NAMESPACE: ${{inputs.package-namespace}}
70 | NAME: ${{inputs.package-name}}
71 |
72 | package:
73 | name: Publish Maven package
74 | needs: [prepare]
75 | runs-on: ubuntu-latest
76 | environment:
77 | name: ${{inputs.environment-name}}
78 | url: ${{needs.prepare.outputs.url}}
79 | steps:
80 | - name: Checkout repository
81 | uses: actions/checkout@v6
82 | - name: Set up Java
83 | uses: actions/setup-java@v5
84 | with:
85 | distribution: ${{inputs.java-distribution}}
86 | java-version: ${{inputs.java-version}}
87 | cache: maven
88 | server-id: central
89 | server-username: MAVEN_CENTRAL_USERNAME
90 | server-password: MAVEN_CENTRAL_TOKEN
91 | gpg-private-key: ${{secrets.GPG_PRIVATE_KEY}}
92 | - name: Set up tree-sitter CLI
93 | if: ${{inputs.generate}}
94 | uses: tree-sitter/setup-action/cli@v2
95 | - name: Install dependencies
96 | if: ${{inputs.generate}}
97 | shell: bash
98 | run: |-
99 | if jq -e "$JQ_SCRIPT" package.json >/dev/null; then
100 | npm i --ignore-scripts --omit peer --omit optional
101 | fi
102 | env:
103 | JQ_SCRIPT: >-
104 | .dependencies + .devDependencies |
105 | with_entries(select(
106 | (.key | startswith("tree-sitter-"))
107 | and (.key != "tree-sitter-cli")
108 | )) | length > 0
109 | - name: Regenerate parser
110 | if: ${{inputs.generate}}
111 | shell: bash
112 | run: |
113 | while read -r grammar; do
114 | pushd "$grammar_dir"
115 | tree-sitter generate
116 | popd > /dev/null
117 | done < <(jq -r '.grammars[].path // "."' tree-sitter.json)
118 | env:
119 | TREE_SITTER_ABI_VERSION: ${{inputs.abi-version}}
120 | - name: Publish to Maven Central
121 | run: mvn --no-transfer-progress deploy -DskipTests=true
122 | env:
123 | MAVEN_CENTRAL_USERNAME: ${{secrets.MAVEN_CENTRAL_USERNAME}}
124 | MAVEN_CENTRAL_TOKEN: ${{secrets.MAVEN_CENTRAL_TOKEN}}
125 | MAVEN_GPG_PASSPHRASE: ${{secrets.GPG_PASSPHRASE}}
126 |
--------------------------------------------------------------------------------
/.github/workflows/package-npm.yml:
--------------------------------------------------------------------------------
1 | name: Publish package (npm)
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | package-name:
7 | description: The name of the package
8 | default: ${{github.event.repository.name}}
9 | type: string
10 | environment-name:
11 | description: The name of the environment
12 | default: npm
13 | type: string
14 | node-version:
15 | description: The NodeJS version
16 | default: ${{vars.NODE_VERSION || '22'}}
17 | type: string
18 | generate:
19 | description: Generate the parser artifacts
20 | default: false
21 | type: boolean
22 | abi-version:
23 | description: The tree-sitter ABI version
24 | default: 15
25 | type: number
26 | secrets:
27 | NODE_AUTH_TOKEN:
28 | description: An authentication token for npm
29 | required: true
30 |
31 | defaults:
32 | run:
33 | shell: bash
34 |
35 | jobs:
36 | build_wasm:
37 | name: Build Wasm binaries
38 | runs-on: ubuntu-24.04
39 | continue-on-error: true
40 | steps:
41 | - name: Checkout repository
42 | uses: actions/checkout@v6
43 | - name: Set up NodeJS
44 | uses: actions/setup-node@v6
45 | with:
46 | cache: npm
47 | node-version: ${{inputs.node-version}}
48 | - name: Install dependencies
49 | run: npm i --omit peer --omit optional
50 | - name: Regenerate parser
51 | if: ${{inputs.generate}}
52 | run: |
53 | while read -r grammar_dir; do
54 | pushd "$grammar_dir"
55 | tree-sitter generate
56 | popd > /dev/null
57 | done < <(jq -r '.grammars[].path // "."' tree-sitter.json)
58 | env:
59 | TREE_SITTER_ABI_VERSION: ${{inputs.abi-version}}
60 | - name: Build Wasm binaries
61 | run: |-
62 | while read -r grammar_dir; do
63 | npm x -- tree-sitter build --wasm "$grammar_dir"
64 | done < <(jq -r '.grammars[].path // "."' tree-sitter.json)
65 | - name: Upload binaries
66 | uses: actions/upload-artifact@v6
67 | with:
68 | path: "*.wasm"
69 | name: prebuilds-wasm
70 | retention-days: 2
71 |
72 | build_node:
73 | name: Build NodeJS binaries on ${{matrix.os}}
74 | runs-on: ${{matrix.os}}
75 | strategy:
76 | matrix:
77 | os:
78 | - windows-2025
79 | - windows-11-arm
80 | - ubuntu-24.04
81 | - ubuntu-24.04-arm
82 | - macos-15
83 | - macos-15-intel
84 | steps:
85 | - name: Checkout repository
86 | uses: actions/checkout@v6
87 | - name: Set up NodeJS
88 | uses: actions/setup-node@v6
89 | with:
90 | cache: npm
91 | node-version: ${{inputs.node-version}}
92 | - name: Install dependencies
93 | run: npm i --omit peer --omit optional
94 | - name: Regenerate parser
95 | if: ${{inputs.generate}}
96 | shell: bash
97 | run: |
98 | while read -r grammar_dir; do
99 | pushd "$grammar_dir"
100 | tree-sitter generate
101 | popd > /dev/null
102 | done < <(jq -r '.grammars[].path // "."' tree-sitter.json)
103 | - name: Build binary
104 | run: npm x -- prebuildify -t 20.19.5
105 | - name: Upload binaries
106 | uses: actions/upload-artifact@v6
107 | with:
108 | path: prebuilds/**
109 | name: prebuilds-${{matrix.os}}
110 | retention-days: 2
111 |
112 | package:
113 | name: Publish NodeJS package
114 | needs: [build_wasm, build_node]
115 | runs-on: ubuntu-24.04
116 | environment:
117 | name: ${{inputs.environment-name}}
118 | url: https://www.npmjs.com/package/${{inputs.package-name}}
119 | steps:
120 | - name: Checkout repository
121 | uses: actions/checkout@v6
122 | - name: Set up NodeJS
123 | uses: actions/setup-node@v6
124 | with:
125 | cache: npm
126 | node-version: ${{inputs.node-version}}
127 | registry-url: https://registry.npmjs.org/
128 | - name: Download binaries
129 | uses: actions/download-artifact@v7
130 | with:
131 | path: prebuilds
132 | pattern: prebuilds-*
133 | merge-multiple: true
134 | - name: Check binaries
135 | run: tree prebuilds
136 | - name: Move Wasm binaries to root
137 | continue-on-error: true
138 | run: mv -v prebuilds/*.wasm .
139 | - name: Publish to npm
140 | run: npm publish
141 | env:
142 | NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}}
143 |
--------------------------------------------------------------------------------
/.github/workflows/package-pypi.yml:
--------------------------------------------------------------------------------
1 | name: Publish package (pypi)
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | package-name:
7 | description: The name of the package
8 | default: ${{github.event.repository.name}}
9 | type: string
10 | environment-name:
11 | description: The name of the environment
12 | default: pypi
13 | type: string
14 | python-version:
15 | description: The Python version
16 | default: ${{vars.PYTHON_VERSION || '3.11'}}
17 | type: string
18 | generate:
19 | description: Generate the parser artifacts
20 | default: false
21 | type: boolean
22 | abi-version:
23 | description: The tree-sitter ABI version
24 | default: 15
25 | type: number
26 | secrets:
27 | # TODO: make optional when pypi/warehouse#11096 is fixed
28 | PYPI_API_TOKEN:
29 | description: An authentication token for pypi
30 | required: true
31 |
32 | jobs:
33 | build_sdist:
34 | name: Build source dist
35 | runs-on: ubuntu-latest
36 | continue-on-error: true
37 | steps:
38 | - name: Checkout repository
39 | uses: actions/checkout@v6
40 | - name: Set up Python
41 | uses: actions/setup-python@v6
42 | with:
43 | python-version: ${{inputs.python-version}}
44 | - name: Set up tree-sitter CLI
45 | if: ${{inputs.generate}}
46 | uses: tree-sitter/setup-action/cli@v2
47 | - name: Install dependencies
48 | run: |-
49 | if jq -e "$JQ_SCRIPT" package.json >/dev/null; then
50 | npm i --ignore-scripts --omit peer --omit optional
51 | fi
52 | env:
53 | JQ_SCRIPT: >-
54 | .dependencies + .devDependencies |
55 | with_entries(select(
56 | (.key | startswith("tree-sitter-"))
57 | and (.key != "tree-sitter-cli")
58 | )) | length > 0
59 | - name: Regenerate parser
60 | if: ${{inputs.generate}}
61 | shell: bash
62 | run: |
63 | while read -r grammar_dir; do
64 | pushd "$grammar_dir"
65 | tree-sitter generate
66 | popd > /dev/null
67 | done < <(jq -r '.grammars[].path // "."' tree-sitter.json)
68 | env:
69 | TREE_SITTER_ABI_VERSION: ${{inputs.abi-version}}
70 | - name: Install dependencies
71 | run: pip install --upgrade pip build setuptools wheel
72 | - name: Build sources
73 | run: python -mbuild -n -s
74 | - name: Upload sources
75 | uses: actions/upload-artifact@v6
76 | with:
77 | name: dist-sources
78 | path: dist/*.tar.gz
79 | retention-days: 2
80 |
81 | build_wheels:
82 | name: Build wheels on ${{matrix.os}}
83 | runs-on: ${{matrix.os}}
84 | strategy:
85 | matrix:
86 | os:
87 | - windows-2025
88 | - windows-11-arm
89 | - ubuntu-24.04
90 | - ubuntu-24.04-arm
91 | - macos-15
92 | - macos-15-intel
93 | steps:
94 | - name: Checkout repository
95 | uses: actions/checkout@v6
96 | - name: Set up tree-sitter CLI
97 | if: ${{inputs.generate}}
98 | uses: tree-sitter/setup-action/cli@v2
99 | - name: Install dependencies
100 | shell: bash
101 | run: |-
102 | if jq -e "$JQ_SCRIPT" package.json >/dev/null; then
103 | npm i --ignore-scripts --omit peer --omit optional
104 | fi
105 | env:
106 | JQ_SCRIPT: >-
107 | .dependencies + .devDependencies |
108 | with_entries(select(
109 | (.key | startswith("tree-sitter-"))
110 | and (.key != "tree-sitter-cli")
111 | )) | length > 0
112 | - name: Regenerate parser
113 | if: ${{inputs.generate}}
114 | shell: bash
115 | run: |
116 | while read -r grammar_dir; do
117 | pushd "$grammar_dir"
118 | tree-sitter generate
119 | popd > /dev/null
120 | done < <(jq -r '.grammars[].path // "."' tree-sitter.json)
121 | env:
122 | TREE_SITTER_ABI_VERSION: ${{inputs.abi-version}}
123 | - name: Build wheels
124 | uses: pypa/cibuildwheel@v3.3
125 | with:
126 | output-dir: dist
127 | env:
128 | CIBW_ARCHS: native
129 | - name: Upload wheel artifacts
130 | uses: actions/upload-artifact@v6
131 | with:
132 | path: dist/*.whl
133 | name: dist-wheels-${{matrix.os}}
134 | retention-days: 2
135 |
136 | package:
137 | name: Publish Python package
138 | needs: [build_sdist, build_wheels]
139 | runs-on: ubuntu-latest
140 | environment:
141 | name: ${{inputs.environment-name}}
142 | url: https://pypi.org/project/${{inputs.package-name}}/
143 | # permissions:
144 | # id-token: write
145 | steps:
146 | - name: Download build artifacts
147 | uses: actions/download-artifact@v7
148 | with:
149 | path: dist
150 | pattern: dist-*
151 | merge-multiple: true
152 | - name: Check build artifacts
153 | run: ls -l dist
154 | - name: Publish to PyPI
155 | uses: pypa/gh-action-pypi-publish@release/v1
156 | with:
157 | password: ${{secrets.PYPI_API_TOKEN}}
158 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Reusable workflows
2 |
3 | This repository contains reusable and reference workflows for parsers.
4 |
5 | ## Release workflow
6 |
7 | This workflow creates a GitHub release and uploads the source code and Wasm binaries.
8 |
9 | ```yaml
10 | name: Create release
11 |
12 | on:
13 | push:
14 | tags: ["*"]
15 |
16 | concurrency:
17 | group: ${{github.workflow}}-${{github.ref}}
18 | cancel-in-progress: true
19 |
20 | permissions:
21 | contents: write
22 | id-token: write
23 | attestations: write
24 |
25 | jobs:
26 | release:
27 | uses: tree-sitter/workflows/.github/workflows/release.yml@main
28 | ```
29 |
30 | ### options
31 |
32 | ```yaml
33 | inputs:
34 | generate:
35 | description: Generate the parser artifacts
36 | default: false
37 | type: boolean
38 | release-body:
39 | description: The body of the release notes
40 | type: string
41 | abi-version:
42 | description: The tree-sitter ABI version
43 | default: 15
44 | type: number
45 | ```
46 |
47 |
48 | Verifying attestations
49 |
50 | ```sh
51 | gh attestation verify $file --repo $repo --signer-workflow tree-sitter/workflows/.github/workflows/release.yml
52 | ```
53 |
54 |
55 |
56 | ## Packaging workflows
57 |
58 | These workflows can be used to publish parser packages to registries.
59 | Remove the jobs you don't need and create an environment for each job you do.
60 |
61 | ```yaml
62 | name: Publish package
63 |
64 | on:
65 | push:
66 | tags: ["*"]
67 |
68 | concurrency:
69 | group: ${{github.workflow}}-${{github.ref}}
70 | cancel-in-progress: true
71 |
72 | jobs:
73 | npm:
74 | uses: tree-sitter/workflows/.github/workflows/package-npm.yml@main
75 | secrets:
76 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
77 | crates:
78 | uses: tree-sitter/workflows/.github/workflows/package-crates.yml@main
79 | secrets:
80 | CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_TOKEN}}
81 | pypi:
82 | uses: tree-sitter/workflows/.github/workflows/package-pypi.yml@main
83 | secrets:
84 | PYPI_API_TOKEN: ${{secrets.PYPI_TOKEN}}
85 | maven:
86 | uses: tree-sitter/workflows/.github/workflows/package-maven.yml@main
87 | secrets:
88 | MAVEN_CENTRAL_USERNAME: ${{secrets.MAVEN_USERNAME}}
89 | MAVEN_CENTRAL_TOKEN: ${{secrets.MAVEN_TOKEN}}
90 | GPG_PRIVATE_KEY: ${{secrets.GPG_PRIVATE_KEY}}
91 | GPG_PASSPHRASE: ${{secrets.GPG_PASSPHRASE}}
92 | ```
93 |
94 | ### npm options
95 |
96 | ```yaml
97 | inputs:
98 | package-name:
99 | description: The name of the package
100 | default: ${{github.event.repository.name}}
101 | type: string
102 | environment-name:
103 | description: The name of the environment
104 | default: npm
105 | type: string
106 | node-version:
107 | description: The NodeJS version
108 | default: ${{vars.NODE_VERSION || '22'}}
109 | type: string
110 | generate:
111 | description: Generate the parser artifacts
112 | default: false
113 | type: boolean
114 | abi-version:
115 | description: The tree-sitter ABI version
116 | default: 15
117 | type: number
118 | secrets:
119 | NODE_AUTH_TOKEN:
120 | description: An authentication token for npm
121 | required: true
122 | ```
123 |
124 | ### crates options
125 |
126 | ```yaml
127 | inputs:
128 | package-name:
129 | description: The name of the package
130 | default: ${{github.event.repository.name}}
131 | type: string
132 | environment-name:
133 | description: The name of the environment
134 | default: crates
135 | type: string
136 | rust-toolchain:
137 | description: The Rust toolchain
138 | default: ${{vars.RUST_TOOLCHAIN || 'stable'}}
139 | type: string
140 | generate:
141 | description: Generate the parser artifacts
142 | default: false
143 | type: boolean
144 | abi-version:
145 | description: The tree-sitter ABI version
146 | default: 15
147 | type: number
148 | secrets:
149 | CARGO_REGISTRY_TOKEN:
150 | description: An authentication token for crates.io
151 | required: true
152 | ```
153 |
154 | ### pypi options
155 |
156 | ```yaml
157 | inputs:
158 | package-name:
159 | description: The name of the package
160 | default: ${{github.event.repository.name}}
161 | type: string
162 | environment-name:
163 | description: The name of the environment
164 | default: pypi
165 | type: string
166 | python-version:
167 | description: The Python version
168 | default: ${{vars.PYTHON_VERSION || '3.x'}}
169 | type: string
170 | generate:
171 | description: Generate the parser artifacts
172 | default: false
173 | type: boolean
174 | abi-version:
175 | description: The tree-sitter ABI version
176 | default: 15
177 | type: number
178 | secrets:
179 | PYPI_API_TOKEN:
180 | description: An authentication token for pypi
181 | required: true
182 | ```
183 |
184 | ### maven options
185 |
186 | ```yaml
187 | inputs:
188 | package-name:
189 | description: The name of the package
190 | default: "" # jtreesitter-${language_name}
191 | type: string
192 | package-namespace:
193 | description: The namespace of the package
194 | default: "" # io.github.${repo_owner}
195 | type: string
196 | environment-name:
197 | description: The name of the environment
198 | default: maven
199 | type: string
200 | java-version:
201 | description: The Java version
202 | default: ${{vars.JAVA_VERSION || '25'}}
203 | type: string
204 | java-distribution:
205 | description: The Java distribution
206 | default: temurin
207 | type: string
208 | generate:
209 | description: Generate the parser artifacts
210 | default: false
211 | type: boolean
212 | abi-version:
213 | description: The tree-sitter ABI version
214 | default: 15
215 | type: number
216 | secrets:
217 | MAVEN_CENTRAL_USERNAME:
218 | description: A username for Maven Central
219 | required: true
220 | MAVEN_CENTRAL_TOKEN:
221 | description: An authorization token for Maven Central
222 | required: true
223 | GPG_PRIVATE_KEY:
224 | description: A GPG private key
225 | required: true
226 | GPG_PASSPHRASE:
227 | description: The passphrase of the GPG key
228 | required: true
229 | ```
230 |
231 | ## Regenerate workflow
232 |
233 | This workflow can be used alongside dependabot updates to regenerate the parser.
234 |
235 | ```yaml
236 | name: Regenerate parser
237 |
238 | on:
239 | pull_request:
240 |
241 | permissions:
242 | contents: write
243 | pull-requests: write
244 |
245 | concurrency:
246 | group: ${{github.workflow}}-${{github.ref}}
247 | cancel-in-progress: true
248 |
249 | jobs:
250 | regenerate:
251 | uses: tree-sitter/workflows/.github/workflows/regenerate.yml@main
252 | if: github.actor == 'dependabot[bot]'
253 | ```
254 |
255 |
256 | Sample .github/dependabot.yml file
257 |
258 | ```yaml
259 | version: 2
260 | updates:
261 | - package-ecosystem: github-actions
262 | directory: /
263 | schedule:
264 | interval: weekly
265 | day: sunday
266 | commit-message:
267 | prefix: ci
268 | labels:
269 | - dependencies
270 | groups:
271 | actions:
272 | patterns: ["*"]
273 |
274 | - package-ecosystem: npm
275 | versioning-strategy: increase
276 | directory: /
277 | schedule:
278 | interval: weekly
279 | day: sunday
280 | commit-message:
281 | prefix: build(deps)
282 | labels:
283 | - dependencies
284 | groups:
285 | npm:
286 | patterns: ["*"]
287 |
288 | # - package-ecosystem: cargo
289 | # directory: /
290 | # schedule:
291 | # interval: weekly
292 | # day: sunday
293 | # commit-message:
294 | # prefix: build(deps)
295 | # labels:
296 | # - dependencies
297 | # groups:
298 | # cargo:
299 | # patterns: ["*"]
300 |
301 | # - package-ecosystem: pip
302 | # directory: /
303 | # schedule:
304 | # interval: weekly
305 | # day: sunday
306 | # commit-message:
307 | # prefix: build(deps)
308 | # labels:
309 | # - dependencies
310 | # groups:
311 | # pip:
312 | # patterns: ["*"]
313 |
314 | # - package-ecosystem: swift
315 | # directory: /
316 | # schedule:
317 | # interval: weekly
318 | # day: sunday
319 | # commit-message:
320 | # prefix: build(deps)
321 | # labels:
322 | # - dependencies
323 | # groups:
324 | # swift:
325 | # patterns: ["*"]
326 |
327 | # - package-ecosystem: gomod
328 | # directory: /
329 | # schedule:
330 | # interval: weekly
331 | # day: sunday
332 | # commit-message:
333 | # prefix: build(deps)
334 | # labels:
335 | # - dependencies
336 | # groups:
337 | # go:
338 | # patterns: ["*"]
339 |
340 | # - package-ecosystem: maven
341 | # directory: /
342 | # schedule:
343 | # interval: weekly
344 | # day: sunday
345 | # commit-message:
346 | # prefix: build(deps)
347 | # labels:
348 | # - dependencies
349 | # groups:
350 | # java:
351 | # patterns: ["*"]
352 | ```
353 |
354 | *You can also uncomment any other ecosystems you want to keep up to date.*
355 |
356 |
357 |
358 | ### options
359 |
360 | ```yaml
361 | inputs:
362 | abi-version:
363 | description: The tree-sitter ABI version
364 | default: 15
365 | type: number
366 | node-version:
367 | description: The NodeJS version
368 | default: ${{vars.NODE_VERSION || 'lts/*'}}
369 | type: string
370 | update-scanner:
371 | description: Update the scanner
372 | default: false
373 | type: boolean
374 | commit-message:
375 | description: The commit message
376 | default: "build: regenerate parser [dependabot skip]"
377 | type: string
378 | commit-author-name:
379 | description: The commit author's username
380 | default: dependabot[bot]
381 | type: string
382 | commit-author-email:
383 | description: The commit author's email
384 | default: 49699333+dependabot[bot]@users.noreply.github.com
385 | type: string
386 | ```
387 |
388 | ## Reference workflows
389 |
390 | These workflows make use of our parser actions.
391 |
392 | ### CI workflow
393 |
394 | This workflow uses all the following actions:
395 |
396 | - [setup](https://github.com/tree-sitter/setup-action)
397 | - [parser-test](https://github.com/tree-sitter/parser-test-action)
398 | - [parse](https://github.com/tree-sitter/parse-action)
399 | - [fuzz](https://github.com/tree-sitter/fuzz-action)
400 |
401 | ```yaml
402 | name: CI
403 |
404 | on:
405 | push:
406 | branches: [master]
407 | paths:
408 | - grammar.js
409 | - src/**
410 | - test/**
411 | - bindings/**
412 | - tree-sitter.json
413 | pull_request:
414 | paths:
415 | - grammar.js
416 | - src/**
417 | - test/**
418 | - bindings/**
419 | - tree-sitter.json
420 |
421 | concurrency:
422 | group: ${{github.workflow}}-${{github.ref}}
423 | cancel-in-progress: true
424 |
425 | jobs:
426 | test:
427 | name: Test parser
428 | runs-on: ${{matrix.os}}
429 | strategy:
430 | fail-fast: false
431 | matrix:
432 | os: [ubuntu-latest, windows-latest, macos-latest]
433 | steps:
434 | - name: Checkout repository
435 | uses: actions/checkout@v4
436 | - name: Set up tree-sitter
437 | uses: tree-sitter/setup-action/cli@v2
438 | - name: Run parser and binding tests
439 | uses: tree-sitter/parser-test-action@v2
440 | with:
441 | test-rust: true
442 | - name: Parse sample files
443 | uses: tree-sitter/parse-action@v4
444 | id: parse-files
445 | with:
446 | files: examples/**
447 | - name: Upload failures artifact
448 | uses: actions/upload-artifact@v4
449 | if: "!cancelled() && steps.parse-files.outcome == 'failure'"
450 | with:
451 | name: failures-${{runner.os}}
452 | path: ${{steps.parse-files.outputs.failures}}
453 | fuzz:
454 | name: Fuzz scanner
455 | runs-on: ubuntu-latest
456 | steps:
457 | - name: Checkout repository
458 | uses: actions/checkout@v4
459 | with:
460 | fetch-depth: 2
461 | - name: Check for scanner changes
462 | id: scanner-check
463 | run: |-
464 | if git diff --quiet HEAD^ -- src/scanner.c; then
465 | printf 'changed=false\n' >> "$GITHUB_OUTPUT"
466 | else
467 | printf 'changed=true\n' >> "$GITHUB_OUTPUT"
468 | fi
469 | - name: Run the fuzzer
470 | uses: tree-sitter/fuzz-action@v4
471 | if: steps.scanner-check.outputs.changed == 'true'
472 | ```
473 |
474 | ### Dependency update workflow
475 |
476 | This workflow uses the [parser-update](https://github.com/tree-sitter/parser-update-action) action.
477 |
478 | > [!TIP]
479 | > It's recommended to use dependabot with the [regenerate workflow][] instead.
480 |
481 | [regenerate workflow]: #regenerate-workflow
482 |
483 | ```yaml
484 | name: Update dependencies
485 |
486 | on:
487 | schedule:
488 | - cron: "0 0 * * 0" # every week
489 | workflow_dispatch:
490 |
491 | permissions:
492 | contents: write
493 | pull-requests: write
494 |
495 | jobs:
496 | update:
497 | runs-on: ubuntu-latest
498 | steps:
499 | - name: Checkout repository
500 | uses: actions/checkout@v4
501 | - name: Set up NodeJS
502 | uses: actions/setup-node@v4
503 | - name: Update dependencies
504 | uses: tree-sitter/parser-update-action@v1.1
505 | with:
506 | parent-name: c
507 | language-name: cpp
508 | ```
509 |
--------------------------------------------------------------------------------