├── .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 | --------------------------------------------------------------------------------