├── .clang-format
├── .github
├── dependabot.yaml
├── sync-files.yaml
└── workflows
│ ├── github-release.yaml
│ ├── pre-commit-optional.yaml
│ ├── pre-commit.yaml
│ ├── pytest.yaml
│ ├── semantic-pull-request.yaml
│ ├── spell-check-differential.yaml
│ ├── sync-files.yaml
│ ├── test-pre-commit-hooks.yaml
│ └── update-depend-versions.yaml
├── .gitignore
├── .markdown-link-check.json
├── .markdownlint.yaml
├── .pre-commit-config-optional.yaml
├── .pre-commit-config.yaml
├── .pre-commit-hooks.yaml
├── .prettierignore
├── .prettierrc.yaml
├── .yamllint.yaml
├── LICENSE
├── README.md
├── package.json
├── pre_commit_hooks
├── __init__.py
├── ros_include_guard.py
└── sort_package_xml.py
├── requirements.txt
├── setup.cfg
├── setup.py
└── tests
├── pre-commit-hooks
├── prettier-launch-xml
│ └── sample.launch.xml
├── prettier-package-xml
│ └── normal
│ │ └── package.xml
├── prettier-xacro
│ ├── calibration.xacro
│ └── sample.xacro
├── ros-include-guard
│ └── include
│ │ └── foo_bar
│ │ └── baz.hpp
├── sort-package-xml
│ └── normal
│ │ └── package.xml
└── test-pre-commit-hooks.yaml
├── test_ros_include_guard.py
├── test_ros_include_guard
├── include
│ └── rospkg
│ │ ├── foobar.right.h
│ │ ├── foobar.right.hpp
│ │ ├── foobar.wrong.h
│ │ ├── foobar.wrong.hpp
│ │ ├── nolint.right.hpp
│ │ ├── nolint.wrong.hpp
│ │ ├── none.hpp
│ │ ├── pragma.only.hpp
│ │ ├── pragma.right.hpp
│ │ └── pragma.wrong.hpp
└── src
│ ├── foo
│ ├── bar
│ │ ├── baz.right.hpp
│ │ └── baz.wrong.hpp
│ ├── bar_baz.right.hpp
│ └── bar_baz.wrong.hpp
│ ├── foo_bar
│ ├── baz.right.hpp
│ └── baz.wrong.hpp
│ ├── foo_bar_baz.right.hpp
│ └── foo_bar_baz.wrong.hpp
├── test_sort_package_xml.py
└── test_sort_package_xml
├── no-format.answer.xml
├── no-format.input.xml
├── normal.answer.xml
└── normal.input.xml
/.clang-format:
--------------------------------------------------------------------------------
1 | # Modified from https://github.com/ament/ament_lint/blob/master/ament_clang_format/ament_clang_format/configuration/.clang-format
2 | Language: Cpp
3 | BasedOnStyle: Google
4 |
5 | AccessModifierOffset: -2
6 | AlignAfterOpenBracket: AlwaysBreak
7 | AllowShortFunctionsOnASingleLine: InlineOnly
8 | BraceWrapping:
9 | AfterClass: true
10 | AfterFunction: true
11 | AfterNamespace: true
12 | AfterStruct: true
13 | BreakBeforeBraces: Custom
14 | ColumnLimit: 100
15 | ConstructorInitializerIndentWidth: 0
16 | ContinuationIndentWidth: 2
17 | DerivePointerAlignment: false
18 | PointerAlignment: Middle
19 | ReflowComments: true
20 | IncludeCategories:
21 | # C++ system headers
22 | - Regex: <[a-z_]*>
23 | Priority: 6
24 | CaseSensitive: true
25 | # C system headers
26 | - Regex: <.*\.h>
27 | Priority: 5
28 | CaseSensitive: true
29 | # Boost headers
30 | - Regex: boost/.*
31 | Priority: 4
32 | CaseSensitive: true
33 | # Message headers
34 | - Regex: .*_msgs/.*
35 | Priority: 3
36 | CaseSensitive: true
37 | - Regex: .*_srvs/.*
38 | Priority: 3
39 | CaseSensitive: true
40 | # Other Package headers
41 | - Regex: <.*>
42 | Priority: 2
43 | CaseSensitive: true
44 | # Local package headers
45 | - Regex: '".*"'
46 | Priority: 1
47 | CaseSensitive: true
48 |
--------------------------------------------------------------------------------
/.github/dependabot.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: github-actions
4 | directory: /
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 1
8 | labels:
9 | - bot
10 | - github-actions
11 |
--------------------------------------------------------------------------------
/.github/sync-files.yaml:
--------------------------------------------------------------------------------
1 | - repository: autowarefoundation/autoware
2 | files:
3 | - source: .github/dependabot.yaml
4 | - source: .github/workflows/github-release.yaml
5 | - source: .github/workflows/pre-commit.yaml
6 | - source: .github/workflows/pre-commit-optional.yaml
7 | - source: .github/workflows/semantic-pull-request.yaml
8 | - source: .github/workflows/spell-check-differential.yaml
9 | - source: .github/workflows/sync-files.yaml
10 | - source: .clang-format
11 | - source: .markdown-link-check.json
12 | - source: .markdownlint.yaml
13 | - source: .pre-commit-config-optional.yaml
14 | - source: .prettierignore
15 | - source: .prettierrc.yaml
16 | - source: .yamllint.yaml
17 |
--------------------------------------------------------------------------------
/.github/workflows/github-release.yaml:
--------------------------------------------------------------------------------
1 | name: github-release
2 |
3 | on:
4 | push:
5 | branches:
6 | - beta/v*
7 | tags:
8 | - v*
9 | workflow_dispatch:
10 | inputs:
11 | beta-branch-or-tag-name:
12 | description: The name of the beta branch or tag to release
13 | type: string
14 | required: true
15 |
16 | jobs:
17 | github-release:
18 | runs-on: ubuntu-latest
19 | steps:
20 | - name: Set tag name
21 | id: set-tag-name
22 | run: |
23 | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
24 | REF_NAME="${{ github.event.inputs.beta-branch-or-tag-name }}"
25 | else
26 | REF_NAME="${{ github.ref_name }}"
27 | fi
28 |
29 | echo "ref-name=$REF_NAME" >> $GITHUB_OUTPUT
30 | echo "tag-name=${REF_NAME#beta/}" >> $GITHUB_OUTPUT
31 |
32 | - name: Check out repository
33 | uses: actions/checkout@v3
34 | with:
35 | fetch-depth: 0
36 | ref: ${{ steps.set-tag-name.outputs.ref-name }}
37 |
38 | - name: Set target name for beta branches
39 | id: set-target-name
40 | run: |
41 | if [[ "${{ steps.set-tag-name.outputs.ref-name }}" =~ "beta/" ]]; then
42 | echo "target-name=${{ steps.set-tag-name.outputs.ref-name }}" >> $GITHUB_OUTPUT
43 | fi
44 |
45 | - name: Create a local tag for beta branches
46 | run: |
47 | if [ "${{ steps.set-target-name.outputs.target-name }}" != "" ]; then
48 | git tag "${{ steps.set-tag-name.outputs.tag-name }}"
49 | fi
50 |
51 | - name: Run generate-changelog
52 | id: generate-changelog
53 | uses: autowarefoundation/autoware-github-actions/generate-changelog@v1
54 |
55 | - name: Select verb
56 | id: select-verb
57 | run: |
58 | has_previous_draft=$(gh release view --json isDraft -q ".isDraft" "${{ steps.set-tag-name.outputs.tag-name }}") || true
59 |
60 | verb=create
61 | if [ "$has_previous_draft" = "true" ]; then
62 | verb=edit
63 | fi
64 |
65 | echo "verb=$verb" >> $GITHUB_OUTPUT
66 | env:
67 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
68 |
69 | - name: Release to GitHub
70 | run: |
71 | gh release ${{ steps.select-verb.outputs.verb }} "${{ steps.set-tag-name.outputs.tag-name }}" \
72 | --draft \
73 | --target "${{ steps.set-target-name.outputs.target-name }}" \
74 | --title "Release ${{ steps.set-tag-name.outputs.tag-name }}" \
75 | --notes "$NOTES"
76 | env:
77 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
78 | NOTES: ${{ steps.generate-changelog.outputs.changelog }}
79 |
--------------------------------------------------------------------------------
/.github/workflows/pre-commit-optional.yaml:
--------------------------------------------------------------------------------
1 | name: pre-commit-optional
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | pre-commit-optional:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Check out repository
11 | uses: actions/checkout@v3
12 |
13 | - name: Run pre-commit
14 | uses: autowarefoundation/autoware-github-actions/pre-commit@v1
15 | with:
16 | pre-commit-config: .pre-commit-config-optional.yaml
17 |
--------------------------------------------------------------------------------
/.github/workflows/pre-commit.yaml:
--------------------------------------------------------------------------------
1 | name: pre-commit
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | pre-commit:
8 | if: ${{ github.event.repository.private }} # Use pre-commit.ci for public repositories
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Generate token
12 | id: generate-token
13 | uses: tibdex/github-app-token@v1
14 | with:
15 | app_id: ${{ secrets.APP_ID }}
16 | private_key: ${{ secrets.PRIVATE_KEY }}
17 |
18 | - name: Check out repository
19 | uses: actions/checkout@v3
20 | with:
21 | ref: ${{ github.event.pull_request.head.ref }}
22 |
23 | - name: Run pre-commit
24 | uses: autowarefoundation/autoware-github-actions/pre-commit@v1
25 | with:
26 | pre-commit-config: .pre-commit-config.yaml
27 | token: ${{ steps.generate-token.outputs.token }}
28 |
--------------------------------------------------------------------------------
/.github/workflows/pytest.yaml:
--------------------------------------------------------------------------------
1 | name: pytest
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | pytest:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Check out repository
11 | uses: actions/checkout@v3
12 |
13 | - name: Install dependent packages
14 | run: python3 -m pip install -r requirements.txt
15 |
16 | - name: Run pytest
17 | run: python3 -m pytest
18 |
--------------------------------------------------------------------------------
/.github/workflows/semantic-pull-request.yaml:
--------------------------------------------------------------------------------
1 | name: semantic-pull-request
2 |
3 | on:
4 | pull_request_target:
5 | types:
6 | - opened
7 | - edited
8 | - synchronize
9 |
10 | jobs:
11 | semantic-pull-request:
12 | uses: autowarefoundation/autoware-github-actions/.github/workflows/semantic-pull-request.yaml@v1
13 |
--------------------------------------------------------------------------------
/.github/workflows/spell-check-differential.yaml:
--------------------------------------------------------------------------------
1 | name: spell-check-differential
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | spell-check-differential:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Check out repository
11 | uses: actions/checkout@v3
12 |
13 | - name: Run spell-check
14 | uses: autowarefoundation/autoware-github-actions/spell-check@v1
15 | with:
16 | cspell-json-url: https://raw.githubusercontent.com/tier4/autoware-spell-check-dict/main/.cspell.json
17 |
--------------------------------------------------------------------------------
/.github/workflows/sync-files.yaml:
--------------------------------------------------------------------------------
1 | name: sync-files
2 |
3 | on:
4 | schedule:
5 | - cron: 0 0 * * *
6 | workflow_dispatch:
7 |
8 | jobs:
9 | check-secret:
10 | uses: autowarefoundation/autoware-github-actions/.github/workflows/check-secret.yaml@v1
11 | secrets:
12 | secret: ${{ secrets.APP_ID }}
13 |
14 | sync-files:
15 | needs: check-secret
16 | if: ${{ needs.check-secret.outputs.set == 'true' }}
17 | runs-on: ubuntu-latest
18 | steps:
19 | - name: Generate token
20 | id: generate-token
21 | uses: tibdex/github-app-token@v1
22 | with:
23 | app_id: ${{ secrets.APP_ID }}
24 | private_key: ${{ secrets.PRIVATE_KEY }}
25 |
26 | - name: Run sync-files
27 | uses: autowarefoundation/autoware-github-actions/sync-files@v1
28 | with:
29 | token: ${{ steps.generate-token.outputs.token }}
30 | pr-labels: |
31 | bot
32 | sync-files
33 | auto-merge-method: squash
34 |
--------------------------------------------------------------------------------
/.github/workflows/test-pre-commit-hooks.yaml:
--------------------------------------------------------------------------------
1 | name: test-pre-commit-hooks
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | test-pre-commit-hooks:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Check out repository
11 | uses: actions/checkout@v3
12 |
13 | - name: Run pre-commit
14 | uses: autowarefoundation/autoware-github-actions/pre-commit@v1
15 | with:
16 | pre-commit-config: tests/pre-commit-hooks/test-pre-commit-hooks.yaml
17 |
18 | - name: Try pre-commit
19 | run: |
20 | pre-commit try-repo https://github.com/tier4/pre-commit-hooks-ros flake8-ros -a --ref ${{ github.ref }}
21 |
22 | - name: Try pre-commit with fault injected
23 | run: |
24 | echo "import os" >> pre_commit_hooks/sort_package_xml.py
25 | if pre-commit try-repo https://github.com/tier4/pre-commit-hooks-ros flake8-ros -a --ref ${{ github.ref }}; then
26 | exit 1
27 | fi
28 |
--------------------------------------------------------------------------------
/.github/workflows/update-depend-versions.yaml:
--------------------------------------------------------------------------------
1 | name: update-depend-versions
2 |
3 | on:
4 | schedule:
5 | - cron: 0 0 * * *
6 | workflow_dispatch:
7 |
8 | jobs:
9 | update-depend-versions:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Generate token
13 | id: generate-token
14 | uses: tibdex/github-app-token@v1
15 | with:
16 | app_id: ${{ secrets.APP_ID }}
17 | private_key: ${{ secrets.PRIVATE_KEY }}
18 |
19 | - name: Check out repository
20 | uses: actions/checkout@v3
21 |
22 | - name: Set up sd
23 | uses: kenji-miyake/setup-sd@v1
24 |
25 | - name: Set up Python
26 | uses: actions/setup-python@v4
27 | with:
28 | python-version: "3.10"
29 |
30 | - name: Get flake8 versions
31 | id: get-flake8-versions
32 | run: |
33 | function get-latest-pip-version() {
34 | pip index versions "$1" 2>/dev/null | head -n1 | sd '.*\((.*)\)' '$1'
35 | }
36 |
37 | echo ::set-output name=flake8-version::$(get-latest-pip-version flake8)
38 | echo ::set-output name=flake8-blind-except-version::$(get-latest-pip-version flake8-blind-except)
39 | echo ::set-output name=flake8-builtins-version::$(get-latest-pip-version flake8-builtins)
40 | echo ::set-output name=flake8-class-newline-version::$(get-latest-pip-version flake8-class-newline)
41 | echo ::set-output name=flake8-comprehensions-version::$(get-latest-pip-version flake8-comprehensions)
42 | echo ::set-output name=flake8-deprecated-version::$(get-latest-pip-version flake8-deprecated)
43 | echo ::set-output name=flake8-docstrings-version::$(get-latest-pip-version flake8-docstrings)
44 | echo ::set-output name=flake8-import-order-version::$(get-latest-pip-version flake8-import-order)
45 | echo ::set-output name=flake8-quotes-version::$(get-latest-pip-version flake8-quotes)
46 |
47 | - name: Update flake8 version
48 | run: |
49 | sd '(flake8)==\d+\.\d+(\.\d+)?' '$1==${{ steps.get-flake8-versions.outputs.flake8-version }}' .pre-commit-hooks.yaml
50 | sd '(flake8-blind-except)==\d+\.\d+(\.\d+)?' '$1==${{ steps.get-flake8-versions.outputs.flake8-blind-except-version }}' .pre-commit-hooks.yaml
51 | sd '(flake8-builtins)==\d+\.\d+(\.\d+)?' '$1==${{ steps.get-flake8-versions.outputs.flake8-builtins-version }}' .pre-commit-hooks.yaml
52 | sd '(flake8-class-newline)==\d+\.\d+(\.\d+)?' '$1==${{ steps.get-flake8-versions.outputs.flake8-class-newline-version }}' .pre-commit-hooks.yaml
53 | sd '(flake8-comprehensions)==\d+\.\d+(\.\d+)?' '$1==${{ steps.get-flake8-versions.outputs.flake8-comprehensions-version }}' .pre-commit-hooks.yaml
54 | sd '(flake8-deprecated)==\d+\.\d+(\.\d+)?' '$1==${{ steps.get-flake8-versions.outputs.flake8-deprecated-version }}' .pre-commit-hooks.yaml
55 | sd '(flake8-docstrings)==\d+\.\d+(\.\d+)?' '$1==${{ steps.get-flake8-versions.outputs.flake8-docstrings-version }}' .pre-commit-hooks.yaml
56 | sd '(flake8-import-order)==\d+\.\d+(\.\d+)?' '$1==${{ steps.get-flake8-versions.outputs.flake8-import-order-version }}' .pre-commit-hooks.yaml
57 | sd '(flake8-quotes)==\d+\.\d+(\.\d+)?' '$1==${{ steps.get-flake8-versions.outputs.flake8-quotes-version }}' .pre-commit-hooks.yaml
58 |
59 | - name: Get prettier versions
60 | id: get-prettier-versions
61 | run: |
62 | function get-latest-npm-version() {
63 | npm show "$1" version
64 | }
65 |
66 | echo ::set-output name=prettier-version::$(get-latest-npm-version prettier)
67 | echo ::set-output name=prettier-xml-version::$(get-latest-npm-version @prettier/plugin-xml)
68 |
69 | - name: Update prettier version
70 | run: |
71 | sd '(prettier)@\d+\.\d+\.\d+' '$1@${{ steps.get-prettier-versions.outputs.prettier-version }}' .pre-commit-hooks.yaml
72 | sd '(@prettier/plugin-xml)@\d+\.\d+\.\d+' '$1@${{ steps.get-prettier-versions.outputs.prettier-xml-version }}' .pre-commit-hooks.yaml
73 |
74 | - name: Create PR
75 | id: create-pr
76 | uses: peter-evans/create-pull-request@v5
77 | with:
78 | token: ${{ steps.generate-token.outputs.token }}
79 | base: ${{ github.event.repository.default_branch }}
80 | branch: update-depend-versions
81 | title: "chore: update depend versions"
82 | commit-message: "chore: update depend versions"
83 | body: ""
84 | labels: |
85 | bot
86 | update-depend-versions
87 | signoff: true
88 | delete-branch: true
89 |
90 | - name: Check outputs
91 | run: |
92 | echo "Pull Request Number - ${{ steps.create-pr.outputs.pull-request-number }}"
93 | echo "Pull Request URL - ${{ steps.create-pr.outputs.pull-request-url }}"
94 | shell: bash
95 |
96 | - name: Enable auto-merge
97 | if: ${{ steps.create-pr.outputs.pull-request-operation == 'created' }}
98 | run: gh pr merge --squash --auto "${{ steps.create-pr.outputs.pull-request-number }}"
99 | env:
100 | GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
101 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
131 | # Visual Studio Code
132 | .vscode/
133 | log/
134 |
--------------------------------------------------------------------------------
/.markdown-link-check.json:
--------------------------------------------------------------------------------
1 | {
2 | "aliveStatusCodes": [200, 206, 403],
3 | "ignorePatterns": [
4 | {
5 | "pattern": "^http://localhost"
6 | },
7 | {
8 | "pattern": "^http://127\\.0\\.0\\.1"
9 | },
10 | {
11 | "pattern": "^https://github.com/.*/discussions/new"
12 | }
13 | ],
14 | "retryOn429": true,
15 | "retryCount": 10
16 | }
17 |
--------------------------------------------------------------------------------
/.markdownlint.yaml:
--------------------------------------------------------------------------------
1 | # See https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md for all rules.
2 | default: true
3 | MD013: false
4 | MD024:
5 | siblings_only: true
6 | MD029:
7 | style: ordered
8 | MD033: false
9 | MD041: false
10 | MD046: false
11 | MD049: false
12 |
--------------------------------------------------------------------------------
/.pre-commit-config-optional.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/tcort/markdown-link-check
3 | rev: v3.11.1
4 | hooks:
5 | - id: markdown-link-check
6 | args: [--config=.markdown-link-check.json]
7 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | ci:
2 | autofix_commit_msg: "ci(pre-commit): autofix"
3 | autoupdate_commit_msg: "ci(pre-commit): autoupdate"
4 |
5 | repos:
6 | - repo: https://github.com/pre-commit/pre-commit-hooks
7 | rev: v4.4.0
8 | hooks:
9 | - id: check-json
10 | - id: check-merge-conflict
11 | - id: check-toml
12 | - id: check-xml
13 | - id: check-yaml
14 | - id: detect-private-key
15 | - id: end-of-file-fixer
16 | - id: mixed-line-ending
17 | - id: trailing-whitespace
18 | args: [--markdown-linebreak-ext=md]
19 |
20 | - repo: https://github.com/igorshubovych/markdownlint-cli
21 | rev: v0.33.0
22 | hooks:
23 | - id: markdownlint
24 | args: [-c, .markdownlint.yaml, --fix]
25 |
26 | - repo: https://github.com/pre-commit/mirrors-prettier
27 | rev: v3.0.0-alpha.6
28 | hooks:
29 | - id: prettier
30 |
31 | - repo: https://github.com/adrienverge/yamllint
32 | rev: v1.30.0
33 | hooks:
34 | - id: yamllint
35 |
36 | - repo: https://github.com/shellcheck-py/shellcheck-py
37 | rev: v0.9.0.2
38 | hooks:
39 | - id: shellcheck
40 |
41 | - repo: https://github.com/scop/pre-commit-shfmt
42 | rev: v3.6.0-2
43 | hooks:
44 | - id: shfmt
45 | args: [-w, -s, -i=4]
46 |
47 | - repo: https://github.com/pycqa/isort
48 | rev: 5.12.0
49 | hooks:
50 | - id: isort
51 |
52 | - repo: https://github.com/psf/black
53 | rev: 23.3.0
54 | hooks:
55 | - id: black
56 | args: [--line-length=100]
57 |
58 | - repo: https://github.com/PyCQA/flake8
59 | rev: 6.1.0
60 | hooks:
61 | - id: flake8
62 | additional_dependencies:
63 | [
64 | flake8-blind-except,
65 | flake8-builtins,
66 | flake8-class-newline,
67 | flake8-comprehensions,
68 | flake8-deprecated,
69 | flake8-docstrings,
70 | flake8-import-order,
71 | flake8-quotes,
72 | ]
73 |
74 | - repo: https://github.com/pre-commit/mirrors-clang-format
75 | rev: v16.0.0
76 | hooks:
77 | - id: clang-format
78 | types_or: [c++, c, cuda]
79 |
80 | exclude: .svg
81 |
--------------------------------------------------------------------------------
/.pre-commit-hooks.yaml:
--------------------------------------------------------------------------------
1 | # Modified from https://github.com/PyCQA/flake8/blob/463d9b1ce9ee44665f2c6825edd7010ef5dff760/.pre-commit-hooks.yaml
2 | - id: flake8-ros
3 | name: flake8 ros
4 | description: Apply flake8 with the ROS 2 settings.
5 | entry: flake8
6 | language: python
7 | types: [python]
8 | require_serial: true
9 | additional_dependencies:
10 | [
11 | flake8==6.1.0,
12 | flake8-blind-except==0.2.1,
13 | flake8-builtins==2.1.0,
14 | flake8-class-newline==1.6.0,
15 | flake8-comprehensions==3.12.0,
16 | flake8-deprecated==2.0.1,
17 | flake8-docstrings==1.7.0,
18 | flake8-import-order==0.18.2,
19 | flake8-quotes==3.3.2,
20 | ]
21 |
22 | - id: prettier-xacro
23 | name: prettier xacro
24 | description: Apply Prettier with plugin-xml to xacro.
25 | entry: prettier --write --list-different --ignore-unknown --print-width 200 --xml-self-closing-space false --xml-whitespace-sensitivity ignore
26 | language: node
27 | files: .xacro$
28 | additional_dependencies: [prettier@2.8.7, "@prettier/plugin-xml@2.2.0"]
29 |
30 | - id: prettier-launch-xml
31 | name: prettier launch.xml
32 | description: Apply Prettier with plugin-xml to launch.xml.
33 | entry: prettier --write --list-different --ignore-unknown --print-width 200 --xml-self-closing-space false --xml-whitespace-sensitivity ignore
34 | language: node
35 | files: launch.xml$
36 | additional_dependencies: [prettier@2.8.7, "@prettier/plugin-xml@2.2.0"]
37 |
38 | - id: prettier-package-xml
39 | name: prettier package.xml
40 | description: Apply Prettier with plugin-xml to package.xml.
41 | entry: prettier --write --list-different --ignore-unknown --print-width 1000 --xml-self-closing-space false --xml-whitespace-sensitivity ignore
42 | language: node
43 | files: package.xml$
44 | additional_dependencies: [prettier@2.8.7, "@prettier/plugin-xml@2.2.0"]
45 |
46 | - id: ros-include-guard
47 | name: fix include guard
48 | description: Fix the include guard macro name.
49 | entry: ros-include-guard
50 | language: python
51 | files: .*\.(h|hpp)$
52 |
53 | - id: sort-package-xml
54 | name: sort package.xml
55 | description: Sort the dependent packages in package.xml.
56 | entry: sort-package-xml
57 | language: python
58 | files: package.xml$
59 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | *.param.yaml
2 | *.rviz
3 |
--------------------------------------------------------------------------------
/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | printWidth: 100
2 | tabWidth: 2
3 | overrides:
4 | - files: package.xml
5 | options:
6 | printWidth: 1000
7 | xmlSelfClosingSpace: false
8 | xmlWhitespaceSensitivity: ignore
9 |
10 | - files: "*.launch.xml"
11 | options:
12 | printWidth: 200
13 | xmlSelfClosingSpace: false
14 | xmlWhitespaceSensitivity: ignore
15 |
16 | - files: "*.xacro"
17 | options:
18 | printWidth: 200
19 | xmlSelfClosingSpace: false
20 | xmlWhitespaceSensitivity: ignore
21 |
--------------------------------------------------------------------------------
/.yamllint.yaml:
--------------------------------------------------------------------------------
1 | extends: default
2 |
3 | ignore: |
4 | *.param.yaml
5 |
6 | rules:
7 | braces:
8 | level: error
9 | max-spaces-inside: 1 # To format with Prettier
10 | comments:
11 | level: error
12 | min-spaces-from-content: 1 # To be compatible with C++ and Python
13 | document-start:
14 | level: error
15 | present: false # Don't need document start markers
16 | line-length: disable # Delegate to Prettier
17 | truthy:
18 | level: error
19 | check-keys: false # To allow 'on' of GitHub Actions
20 | quoted-strings:
21 | level: error
22 | required: only-when-needed # To keep consistent style
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pre-commit-hooks-ros
2 |
3 | [ROS]-related hooks for [pre-commit]
4 |
5 | ## Usage
6 |
7 | Write your `.pre-commit-config.yaml` as below.
8 |
9 | ```yaml
10 | repos:
11 | - repo: https://github.com/tier4/pre-commit-hooks-ros
12 | rev: v0.7.0
13 | hooks:
14 | - id: flake8-ros
15 | - id: prettier-xacro
16 | - id: prettier-launch-xml
17 | - id: prettier-package-xml
18 | - id: ros-include-guard
19 | - id: sort-package-xml
20 | ```
21 |
22 | ## Hooks available
23 |
24 | ### flake8-ros
25 |
26 | Apply [flake8] with the [ROS 2 settings](https://docs.ros.org/en/rolling/Installation/Ubuntu-Development-Setup.html#install-development-tools-and-ros-tools).
27 |
28 | ### prettier-xacro
29 |
30 | Apply [Prettier] with [plugin-xml] to [xacro].
31 |
32 | ### prettier-launch-xml
33 |
34 | Apply [Prettier] with [plugin-xml] to [launch.xml].
35 |
36 | ### prettier-package-xml
37 |
38 | Apply [Prettier] with [plugin-xml] to [package.xml].
39 |
40 | ### ros-include-guard
41 |
42 | Fix the macro name of include guards.
43 |
44 | ### sort-package-xml
45 |
46 | Sort the dependent packages in [package.xml].
47 | If you want to exclude a tag from sorting, add `` at the beginning of the line.
48 |
49 | ```xml
50 | rclcpp
51 | ```
52 |
53 |
54 |
55 | [flake8]: https://github.com/PyCQA/flake8
56 | [launch.xml]: https://design.ros2.org/articles/roslaunch_xml.html
57 | [package.xml]: https://www.ros.org/reps/rep-0149.html
58 | [plugin-xml]: https://github.com/prettier/plugin-xml/
59 | [pre-commit]: https://github.com/pre-commit/pre-commit
60 | [prettier]: https://prettier.io/
61 | [ros]: https://ros.org/
62 | [xacro]: http://wiki.ros.org/xacro
63 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dummy_package",
3 | "description": "",
4 | "version": "0.0.0"
5 | }
6 |
--------------------------------------------------------------------------------
/pre_commit_hooks/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tier4/pre-commit-hooks-ros/eb39301759e1d4d1c45efe57db062d19ad8ef421/pre_commit_hooks/__init__.py
--------------------------------------------------------------------------------
/pre_commit_hooks/ros_include_guard.py:
--------------------------------------------------------------------------------
1 | # Copyright 2022 TIER IV, Inc. All rights reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | import argparse
16 | from pathlib import Path
17 |
18 |
19 | class DirectiveBase:
20 | def __init__(self, directive):
21 | self.directive = directive
22 | self.line = None
23 | self.text = None
24 | self.expected = None
25 |
26 | def update(self, line, text):
27 | self.line = line
28 | self.text = text
29 |
30 | def is_none(self):
31 | return self.line is None
32 |
33 | def mismatch(self):
34 | return self.text != self.expected
35 |
36 | def overwrite(self, lines):
37 | lines[self.line] = self.expected
38 |
39 |
40 | class OpeningDirective(DirectiveBase):
41 | def prepare(self, macro_name):
42 | self.expected = f"{self.directive} {macro_name}"
43 | if "// NOLINT" in self.text:
44 | self.expected += " // NOLINT"
45 |
46 |
47 | class ClosingDirective(DirectiveBase):
48 | def prepare(self, macro_name):
49 | self.expected = f"{self.directive} // {macro_name}"
50 | if "// NOLINT" in self.text:
51 | self.expected += " // NOLINT"
52 |
53 |
54 | class IncludeGuard:
55 | def __init__(self):
56 | self.ifndef = OpeningDirective("#ifndef")
57 | self.define = OpeningDirective("#define")
58 | self.endif = ClosingDirective("#endif")
59 | self.has_pragma_once = False
60 |
61 | def items(self):
62 | yield self.ifndef
63 | yield self.define
64 | yield self.endif
65 |
66 | def is_none(self):
67 | return any(item.is_none() for item in self.items())
68 |
69 | def mismatch(self):
70 | return any(item.mismatch() for item in self.items())
71 |
72 | def overwrite(self, lines):
73 | return any(item.overwrite(lines) for item in self.items())
74 |
75 | def prepare(self, macro_name):
76 | for item in self.items():
77 | item.prepare(macro_name)
78 |
79 |
80 | def get_include_guard_info(lines):
81 | guard = IncludeGuard()
82 | for line, text in enumerate(lines):
83 | if text.startswith("#pragma once"):
84 | guard.has_pragma_once = True
85 | if text.startswith("#ifndef") and guard.ifndef.is_none():
86 | guard.ifndef.update(line, text)
87 | if text.startswith("#define") and guard.define.is_none():
88 | guard.define.update(line, text)
89 | if text.startswith("#endif"):
90 | guard.endif.update(line, text)
91 | return guard
92 |
93 |
94 | def get_parts_after(parts, targets):
95 | result = []
96 | for part in reversed(parts):
97 | if part in targets:
98 | break
99 | result.append(part)
100 | return reversed(result)
101 |
102 |
103 | def get_include_guard_macro_name(filepath):
104 | targets = {"include", "src", "test"}
105 | parts = get_parts_after(filepath.parts, targets)
106 | return "__".join(parts).replace(".", "_").upper() + "_"
107 |
108 |
109 | def main(argv=None):
110 | parser = argparse.ArgumentParser()
111 | parser.add_argument("filenames", nargs="*", type=Path, help="Filenames to fix")
112 | args = parser.parse_args(argv)
113 |
114 | return_code = 0
115 | for filepath in args.filenames:
116 | # Search the include guard lines.
117 | lines = filepath.read_text().split("\n")
118 | guard = get_include_guard_info(lines)
119 | # Error if the include guard is not found.
120 | if guard.is_none():
121 | if not guard.has_pragma_once:
122 | print("No include guard in {}".format(filepath))
123 | return_code = 1
124 | continue
125 | # Error and auto fix if the macro name is not correct.
126 | macro_name = get_include_guard_macro_name(filepath)
127 | guard.prepare(macro_name)
128 | if guard.mismatch():
129 | print("Fix include guard in {}".format(filepath))
130 | return_code = 1
131 | guard.overwrite(lines)
132 | filepath.write_text("\n".join(lines))
133 | continue
134 |
135 | return return_code
136 |
137 |
138 | if __name__ == "__main__":
139 | exit(main())
140 |
--------------------------------------------------------------------------------
/pre_commit_hooks/sort_package_xml.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright 2021 TIER IV, Inc. All rights reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | # Copyright (c) 2014 pre-commit dev team: Anthony Sottile, Ken Struys
18 |
19 | # Permission is hereby granted, free of charge, to any person obtaining a copy
20 | # of this software and associated documentation files (the "Software"), to deal
21 | # in the Software without restriction, including without limitation the rights
22 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | # copies of the Software, and to permit persons to whom the Software is
24 | # furnished to do so, subject to the following conditions:
25 |
26 | # The above copyright notice and this permission notice shall be included in
27 | # all copies or substantial portions of the Software.
28 |
29 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 | # THE SOFTWARE.
36 |
37 |
38 | import argparse
39 | import re
40 | from typing import List
41 | from typing import Optional
42 | from typing import Sequence
43 |
44 | # Closing bracket absent as conditions can be present within the xml tag
45 | TAGS = [
46 | " List[str]:
58 | """Sort a XML file in alphabetical order, keeping blocks together.
59 |
60 | :param lines: array of strings
61 | :return: sorted array of strings
62 | """
63 | # make a copy of lines since we will clobber it
64 | lines = list(lines)
65 | new_lines = []
66 |
67 | for block in parse_blocks(lines):
68 | new_lines.extend(sorted(block, key=parse_content))
69 |
70 | return new_lines
71 |
72 |
73 | def parse_block(lines: List[str], tag: str) -> List[str]:
74 | block_lines = []
75 | for line in lines:
76 | if line.strip().startswith(tag):
77 | block_lines.append(line)
78 | for line in block_lines:
79 | lines.remove(line)
80 | return block_lines
81 |
82 |
83 | def parse_tag(line: str) -> str:
84 | for tag in TAGS:
85 | if line.startswith(tag):
86 | return tag
87 | return ""
88 |
89 |
90 | def parse_blocks(lines: List[str]) -> List[List[str]]:
91 | """Parse and return all possible blocks, popping off the start of `lines`.
92 |
93 | :param lines: list of lines
94 | :return: list of blocks, where each block is a list of lines
95 | """
96 | blocks = []
97 |
98 | while lines:
99 | tag = parse_tag(lines[0].strip())
100 | if tag:
101 | blocks.append(parse_block(lines, tag))
102 | else:
103 | blocks.append([lines.pop(0)])
104 |
105 | return blocks
106 |
107 |
108 | def parse_content(line: str) -> str:
109 | reg_obj = re.compile(r"<[^>]*?>")
110 | return reg_obj.sub("", line)
111 |
112 |
113 | def main(argv: Optional[Sequence[str]] = None) -> int:
114 | parser = argparse.ArgumentParser()
115 | parser.add_argument("filenames", nargs="*", help="Filenames to fix")
116 | args = parser.parse_args(argv)
117 |
118 | ret_val = 0
119 | for filename in args.filenames:
120 | with open(filename, "r+") as f:
121 | lines = [line.rstrip() for line in f.readlines()]
122 | new_lines = sort(lines)
123 |
124 | if lines != new_lines:
125 | print(f"Fixing file `{filename}`")
126 | f.seek(0)
127 | f.write("\n".join(new_lines) + "\n")
128 | ret_val = 1
129 |
130 | return ret_val
131 |
132 |
133 | if __name__ == "__main__":
134 | exit(main())
135 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pytest
2 | pytest-datadir
3 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = pre_commit_hooks_ros
3 | version = 0.1.0
4 | url = https://github.com/pre-commit/pre-commit-hooks
5 | license_file = LICENSE
6 | description = ros-related hooks for pre-commit
7 | long_description = file: README.md
8 |
9 | [options]
10 | packages = find:
11 | python_requires = >=3.8
12 |
13 | [options.entry_points]
14 | console_scripts =
15 | ros-include-guard = pre_commit_hooks.ros_include_guard:main
16 | sort-package-xml = pre_commit_hooks.sort_package_xml:main
17 |
18 | [flake8]
19 | # Modified from https://github.com/ament/ament_lint/blob/ebd524bb9973d5ec1dc48a670ce54f958a5a0243/ament_flake8/ament_flake8/configuration/ament_flake8.ini
20 | application_import_names = pre_commit_hooks
21 | extend-ignore = B902,C816,D100,D101,D102,D103,D104,D105,D106,D107,D203,D212,D404,I202,CNL100,E203,E501,Q000
22 | import-order-style = google
23 | max-line-length = 100
24 | show-source = true
25 | statistics = true
26 |
27 | [isort]
28 | profile=black
29 | line_length=100
30 | force_sort_within_sections=true
31 | force_single_line=true
32 | reverse_relative=true
33 | known_third_party=launch
34 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup()
4 |
--------------------------------------------------------------------------------
/tests/pre-commit-hooks/prettier-launch-xml/sample.launch.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/tests/pre-commit-hooks/prettier-package-xml/normal/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | rospkg
5 | 0.0.0
6 | The dummy package to test sort_package_xml
7 | Foo Bar
8 | Apache License 2.0
9 |
10 | ament_cmake
11 |
12 | aaaaa
13 | bbbbb
14 | ccccc
15 | ddddd
16 | eeeee
17 |
18 | ament_lint_auto
19 | ament_lint_common
20 |
21 |
22 | ament_cmake
23 |
24 |
25 |
--------------------------------------------------------------------------------
/tests/pre-commit-hooks/prettier-xacro/calibration.xacro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
20 |
21 |
22 |
23 |
35 |
36 |
--------------------------------------------------------------------------------
/tests/pre-commit-hooks/prettier-xacro/sample.xacro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/pre-commit-hooks/ros-include-guard/include/foo_bar/baz.hpp:
--------------------------------------------------------------------------------
1 | #ifndef FOO_BAR__BAZ_HPP_
2 | #define FOO_BAR__BAZ_HPP_
3 |
4 | #endif // FOO_BAR__BAZ_HPP_
5 |
--------------------------------------------------------------------------------
/tests/pre-commit-hooks/sort-package-xml/normal/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | rospkg
5 | 0.0.0
6 | The dummy package to test sort_package_xml
7 | Foo Bar
8 | Apache License 2.0
9 |
10 | ament_cmake
11 |
12 | aaaaa
13 | bbbbb
14 | ccccc
15 | ddddd
16 | eeeee
17 |
18 | ament_lint_auto
19 | ament_lint_common
20 |
21 |
22 | ament_cmake
23 |
24 |
25 |
--------------------------------------------------------------------------------
/tests/pre-commit-hooks/test-pre-commit-hooks.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: .
3 | rev: HEAD
4 | hooks:
5 | - id: prettier-xacro
6 | files: tests/pre-commit-hooks/prettier-xacro/.*
7 | - id: prettier-launch-xml
8 | files: tests/pre-commit-hooks/prettier-launch-xml/.*
9 | - id: prettier-package-xml
10 | files: tests/pre-commit-hooks/prettier-package-xml/.*
11 | - id: ros-include-guard
12 | files: tests/pre-commit-hooks/ros-include-guard/.*
13 | - id: sort-package-xml
14 | files: tests/pre-commit-hooks/sort-package-xml/.*
15 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard.py:
--------------------------------------------------------------------------------
1 | import shutil
2 |
3 | import pytest
4 |
5 | from pre_commit_hooks import ros_include_guard
6 |
7 | cases_auto_fix = [
8 | "include/rospkg/foobar.h",
9 | "include/rospkg/foobar.hpp",
10 | "include/rospkg/nolint.hpp",
11 | "include/rospkg/pragma.hpp",
12 | "src/foo_bar_baz.hpp",
13 | "src/foo_bar/baz.hpp",
14 | "src/foo/bar_baz.hpp",
15 | "src/foo/bar/baz.hpp",
16 | ]
17 |
18 | cases_no_fix = [
19 | ("include/rospkg/pragma.only.hpp", 0),
20 | ("include/rospkg/none.hpp", 1),
21 | ]
22 |
23 |
24 | @pytest.mark.parametrize(("target_file"), cases_auto_fix)
25 | def test_auto_fix(target_file, datadir):
26 | target_path = datadir.joinpath(target_file)
27 | right_path = target_path.with_suffix(".right" + target_path.suffix)
28 | wrong_path = target_path.with_suffix(".wrong" + target_path.suffix)
29 |
30 | # Test wrong file.
31 | shutil.copy(wrong_path, target_path)
32 | return_code = ros_include_guard.main([str(target_path)])
33 | assert return_code == 1
34 | assert target_path.read_text() == right_path.read_text()
35 |
36 | # Test right file.
37 | shutil.copy(right_path, target_path)
38 | return_code = ros_include_guard.main([str(target_path)])
39 | assert return_code == 0
40 | assert target_path.read_text() == right_path.read_text()
41 |
42 |
43 | @pytest.mark.parametrize(("target_file", "answer_code"), cases_no_fix)
44 | def test_no_fix(target_file, answer_code, datadir):
45 | target_path = datadir.joinpath(target_file)
46 | return_code = ros_include_guard.main([str(target_path)])
47 | assert return_code == answer_code
48 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/include/rospkg/foobar.right.h:
--------------------------------------------------------------------------------
1 | #ifndef ROSPKG__FOOBAR_H_
2 | #define ROSPKG__FOOBAR_H_
3 |
4 | #endif // ROSPKG__FOOBAR_H_
5 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/include/rospkg/foobar.right.hpp:
--------------------------------------------------------------------------------
1 | #ifndef ROSPKG__FOOBAR_HPP_
2 | #define ROSPKG__FOOBAR_HPP_
3 |
4 | #endif // ROSPKG__FOOBAR_HPP_
5 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/include/rospkg/foobar.wrong.h:
--------------------------------------------------------------------------------
1 | #ifndef DUMMY
2 | #define DUMMY
3 |
4 | #endif // DUMMY
5 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/include/rospkg/foobar.wrong.hpp:
--------------------------------------------------------------------------------
1 | #ifndef DUMMY
2 | #define DUMMY
3 |
4 | #endif // DUMMY
5 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/include/rospkg/nolint.right.hpp:
--------------------------------------------------------------------------------
1 | #ifndef ROSPKG__NOLINT_HPP_ // NOLINT
2 | #define ROSPKG__NOLINT_HPP_ // NOLINT
3 |
4 | #endif // ROSPKG__NOLINT_HPP_ // NOLINT
5 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/include/rospkg/nolint.wrong.hpp:
--------------------------------------------------------------------------------
1 | #ifndef DUMMY // NOLINT
2 | #define DUMMY // NOLINT
3 |
4 | #endif // DUMMY // NOLINT
5 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/include/rospkg/none.hpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tier4/pre-commit-hooks-ros/eb39301759e1d4d1c45efe57db062d19ad8ef421/tests/test_ros_include_guard/include/rospkg/none.hpp
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/include/rospkg/pragma.only.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/include/rospkg/pragma.right.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef ROSPKG__PRAGMA_HPP_
4 | #define ROSPKG__PRAGMA_HPP_
5 |
6 | #endif // ROSPKG__PRAGMA_HPP_
7 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/include/rospkg/pragma.wrong.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef DUMMY
4 | #define DUMMY
5 |
6 | #endif // DUMMY
7 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/src/foo/bar/baz.right.hpp:
--------------------------------------------------------------------------------
1 | #ifndef FOO__BAR__BAZ_HPP_
2 | #define FOO__BAR__BAZ_HPP_
3 | #endif // FOO__BAR__BAZ_HPP_
4 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/src/foo/bar/baz.wrong.hpp:
--------------------------------------------------------------------------------
1 | #ifndef DUMMY
2 | #define DUMMY
3 | #endif // DUMMY
4 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/src/foo/bar_baz.right.hpp:
--------------------------------------------------------------------------------
1 | #ifndef FOO__BAR_BAZ_HPP_
2 | #define FOO__BAR_BAZ_HPP_
3 | #endif // FOO__BAR_BAZ_HPP_
4 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/src/foo/bar_baz.wrong.hpp:
--------------------------------------------------------------------------------
1 | #ifndef DUMMY
2 | #define DUMMY
3 | #endif // DUMMY
4 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/src/foo_bar/baz.right.hpp:
--------------------------------------------------------------------------------
1 | #ifndef FOO_BAR__BAZ_HPP_
2 | #define FOO_BAR__BAZ_HPP_
3 | #endif // FOO_BAR__BAZ_HPP_
4 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/src/foo_bar/baz.wrong.hpp:
--------------------------------------------------------------------------------
1 | #ifndef DUMMY
2 | #define DUMMY
3 | #endif // DUMMY
4 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/src/foo_bar_baz.right.hpp:
--------------------------------------------------------------------------------
1 | #ifndef FOO_BAR_BAZ_HPP_
2 | #define FOO_BAR_BAZ_HPP_
3 | #endif // FOO_BAR_BAZ_HPP_
4 |
--------------------------------------------------------------------------------
/tests/test_ros_include_guard/src/foo_bar_baz.wrong.hpp:
--------------------------------------------------------------------------------
1 | #ifndef DUMMY
2 | #define DUMMY
3 | #endif // DUMMY
4 |
--------------------------------------------------------------------------------
/tests/test_sort_package_xml.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import pytest
4 |
5 | from pre_commit_hooks import sort_package_xml
6 |
7 |
8 | @pytest.mark.parametrize(
9 | "case",
10 | [
11 | "normal",
12 | "no-format",
13 | ],
14 | )
15 | def test(case: str, datadir: Path, tmp_path: Path):
16 | input_file = datadir / f"{case}.input.xml"
17 | answer_file = datadir / f"{case}.answer.xml"
18 |
19 | # Format
20 | return_code = sort_package_xml.main([str(input_file)])
21 | print(input_file.read_text())
22 | assert return_code == 1
23 | assert input_file.read_text() == answer_file.read_text()
24 |
25 | # Re-format
26 | return_code = sort_package_xml.main([str(input_file)])
27 | assert return_code == 0
28 | assert input_file.read_text() == answer_file.read_text()
29 |
--------------------------------------------------------------------------------
/tests/test_sort_package_xml/no-format.answer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | rospkg
5 | 0.0.0
6 | The dummy package to test pre-commit hooks
7 | Foo Bar
8 | Apache License 2.0
9 |
10 | ament_cmake
11 |
12 | aaaaa
13 | ccccc
14 |
15 | bbbbb
16 |
17 |
18 | ament_cmake
19 |
20 |
21 |
--------------------------------------------------------------------------------
/tests/test_sort_package_xml/no-format.input.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | rospkg
5 | 0.0.0
6 | The dummy package to test pre-commit hooks
7 | Foo Bar
8 | Apache License 2.0
9 |
10 | ament_cmake
11 |
12 | ccccc
13 |
14 | bbbbb
15 | aaaaa
16 |
17 |
18 | ament_cmake
19 |
20 |
21 |
--------------------------------------------------------------------------------
/tests/test_sort_package_xml/normal.answer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | rospkg
5 | 0.0.0
6 | The dummy package to test sort_package_xml
7 | Foo Bar
8 | Apache License 2.0
9 |
10 | ament_cmake
11 |
12 | aaaaa
13 | bbbbb
14 | ccccc
15 | ddddd
16 | eeeee
17 |
18 | ament_lint_auto
19 | ament_lint_common
20 |
21 |
22 | ament_cmake
23 |
24 |
25 |
--------------------------------------------------------------------------------
/tests/test_sort_package_xml/normal.input.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | rospkg
5 | 0.0.0
6 | The dummy package to test sort_package_xml
7 | Foo Bar
8 | Apache License 2.0
9 |
10 | ament_cmake
11 |
12 | ccccc
13 | bbbbb
14 | ddddd
15 | eeeee
16 | aaaaa
17 |
18 | ament_lint_common
19 | ament_lint_auto
20 |
21 |
22 | ament_cmake
23 |
24 |
25 |
--------------------------------------------------------------------------------