diff --git a/.cz.toml b/.cz.toml deleted file mode 100644 index 57cd30e..0000000 --- a/.cz.toml +++ /dev/null @@ -1,6 +0,0 @@ -[tool.commitizen] -name = "cz_conventional_commits" -tag_format = "v$version" -version_scheme = "semver" -version = "v0.0.1" -update_changelog_on_bump = true diff --git a/.cz.yaml b/.cz.yaml new file mode 100644 index 0000000..4b03064 --- /dev/null +++ b/.cz.yaml @@ -0,0 +1,8 @@ +--- +commitizen: + major_version_zero: true + name: cz_conventional_commits + tag_format: v$version + update_changelog_on_bump: true + version_provider: scm + version_scheme: semver diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a1d9002..bdb27e6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,8 +1,8 @@ -/.github/ @Splinter3D/Admins -/scripts/ @Splinter3D/Admins -/.clang-format @Splinter3D/Admins -/.cz.toml @Splinter3D/Admins -/.gitattributes @Splinter3D/Admins -/.gitignore @Splinter3D/Admins -/.pre-commit-config.yaml @Splinter3D/Admins -/LICENSE @Splinter3D/Admins +/.github/ @Splinter3D/Admins @Splinter3D/Maintainers +/scripts/ @Splinter3D/Admins @Splinter3D/Maintainers +/.clang-format @Splinter3D/Admins @Splinter3D/Maintainers +/.cz.toml @Splinter3D/Admins @Splinter3D/Maintainers +/.gitattributes @Splinter3D/Admins @Splinter3D/Maintainers +/.gitignore @Splinter3D/Admins @Splinter3D/Maintainers +/.pre-commit-config.yaml @Splinter3D/Admins @Splinter3D/Maintainers +/LICENSE @Splinter3D/Admins @Splinter3D/Maintainers diff --git a/.github/README.md b/.github/README.md index 4737f34..f418489 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1 +1,94 @@ -# TODO: README +# StyleCheck + +`StyleCheck` provides the `splinter3d_style` CLI and a published `pre-commit` hook. + +## Local Development + +Use Poetry for local development so the CLI, test dependencies, and release tooling stay aligned with the project metadata. + +```bash +poetry install +poetry run splinter3d_style +poetry run pytest +``` + +If you need to enter the virtual environment directly: + +```bash +poetry shell +splinter3d_style +pytest +``` + +## Build And Test Locally + +Build the distribution files locally before creating a release: + +```bash +poetry run python -m build +ls dist/ +``` + +Test the built package rather than the source tree: + +```bash +pipx install ./dist/splinter3d_style-X.Y.Z-py3-none-any.whl +splinter3d_style +pipx uninstall splinter3d_style +``` + +You can also validate the source distribution or wheel with a throwaway virtualenv: + +```bash +python3 -m venv .venv-build-test +. .venv-build-test/bin/activate +pip install ./dist/splinter3d_style-X.Y.Z-py3-none-any.whl +splinter3d_style +deactivate +``` + +`X.Y.Z` is a placeholder. Use the actual filename created in `dist/`, or install the first built wheel directly: + +```bash +pipx install "$(ls dist/*.whl | head -n 1)" +``` + +## Install From A Release + +Install a published release artifact with `pipx`: + +```bash +pipx install https://github.com//StyleCheck/releases/download/vX.Y.Z/splinter3d_style-X.Y.Z-py3-none-any.whl +splinter3d_style +``` + +## Use With pre-commit + +Add this repository and a release tag to your `.pre-commit-config.yaml`: + +```yaml +repos: + - repo: https://github.com//StyleCheck + rev: vX.Y.Z + hooks: + - id: splinter3d-style +``` + +Then install and run: + +```bash +pre-commit install +pre-commit run --all-files +``` + +## Release + +Releases are driven by pull requests and tags created by GitHub Actions: + +- Pull requests into `main` are only valid when they come from `dev` or `hotfix/*`. +- When a pull request is merged into `dev`, CI creates a `vX.Y.Z-rc.N` tag. +- When a pull request is merged into `main`, CI creates a `vX.Y.Z` tag. +- Tag creation uses Commitizen to determine the next base version and defaults to `0.1.0` when no stable tag exists. +- Every pushed release tag triggers the packaging workflow, which builds the distribution files and attaches them to a GitHub release. + +Protected GitHub environments can be applied to the `release-rc` and `release` jobs to gate prerelease and production publication independently. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8a83d17 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + target-branch: "dev" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + + - package-ecosystem: "pip" + directory: "/" + target-branch: "dev" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 diff --git a/.github/workflows/main-pr-policy.yml b/.github/workflows/main-pr-policy.yml new file mode 100644 index 0000000..3a22ec9 --- /dev/null +++ b/.github/workflows/main-pr-policy.yml @@ -0,0 +1,50 @@ +name: main-pr-policy + +on: + pull_request_target: + branches: + - main + types: + - opened + - reopened + - synchronize + - edited + - ready_for_review + +jobs: + enforce-source-branch: + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + - name: Close invalid pull request + run: | + head_ref="${{ github.head_ref }}" + pr_number="${{ github.event.pull_request.number }}" + repo="${{ github.repository }}" + + if [[ "$head_ref" == "dev" || "$head_ref" =~ ^hotfix/ ]]; then + exit 0 + fi + + body='{"body":"Closing this PR automatically: pull requests into `main` must come from `dev` or `hotfix/*`."}' + state='{"state":"closed"}' + + curl --fail-with-body \ + --request POST \ + --url "https://api.github.com/repos/${repo}/issues/${pr_number}/comments" \ + --header "Authorization: Bearer ${GITHUB_TOKEN}" \ + --header "Accept: application/vnd.github+json" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + --data "$body" + + curl --fail-with-body \ + --request PATCH \ + --url "https://api.github.com/repos/${repo}/pulls/${pr_number}" \ + --header "Authorization: Bearer ${GITHUB_TOKEN}" \ + --header "Accept: application/vnd.github+json" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + --data "$state" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..16f6be4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,76 @@ +name: release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Build distributions + run: | + python -m pip install --upgrade pip build + python -m build + + - name: Upload build artifacts + uses: actions/upload-artifact@v7 + with: + name: dist + path: dist/* + + publish-prerelease: + if: contains(github.ref_name, '-rc.') + runs-on: ubuntu-latest + environment: + name: release-rc + needs: build + + steps: + - name: Download build artifacts + uses: actions/download-artifact@v7 + with: + name: dist + path: dist + + - name: Create GitHub prerelease + uses: softprops/action-gh-release@v3 + with: + files: dist/* + generate_release_notes: true + prerelease: true + + publish-release: + if: ${{ !contains(github.ref_name, '-rc.') }} + runs-on: ubuntu-latest + needs: build + environment: + name: release + + steps: + - name: Download build artifacts + uses: actions/download-artifact@v7 + with: + name: dist + path: dist + + - name: Create GitHub release + uses: softprops/action-gh-release@v3 + with: + files: dist/* + generate_release_notes: true diff --git a/.github/workflows/tag-on-merge.yml b/.github/workflows/tag-on-merge.yml new file mode 100644 index 0000000..dd8c731 --- /dev/null +++ b/.github/workflows/tag-on-merge.yml @@ -0,0 +1,154 @@ +name: tag-on-merge + +on: + pull_request_target: + branches: + - dev + - main + types: + - closed + +permissions: + contents: write + pull-requests: read + +concurrency: + group: tag-${{ github.event.pull_request.base.ref }} + cancel-in-progress: false + +jobs: + tag-dev: + if: >- + github.event.pull_request.merged == true && + github.event.pull_request.base.ref == 'dev' + runs-on: ubuntu-latest + environment: + name: release-rc + + steps: + - name: Check out base branch + uses: actions/checkout@v7 + with: + ref: ${{ github.event.pull_request.merge_commit_sha || github.event.pull_request.base.ref }} + fetch-depth: 0 + + - name: Fetch tags + run: git fetch --force --tags origin + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install Commitizen + run: python -m pip install --upgrade pip commitizen tomlkit + + - name: Compute release candidate tag + id: version + run: | + tag="$(./scripts/next-tag.sh rc)" + python_version="$(./scripts/tag-to-python-version.sh "$tag")" + echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "python_version=$python_version" >> "$GITHUB_OUTPUT" + + - name: Update changelog and project version + run: | + tag="${{ steps.version.outputs.tag }}" + python_version="${{ steps.version.outputs.python_version }}" + + cz changelog --unreleased-version="$tag" + ./scripts/check-versions.sh --fix "$tag" + git add CHANGELOG.md pyproject.toml + + if git diff --cached --quiet; then + echo "No release file changes detected." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git commit -m "chore(release): ${python_version}" + git push origin HEAD:dev + + - name: Create tag + run: | + tag="${{ steps.version.outputs.tag }}" + + if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then + echo "Tag $tag already exists, skipping." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git fetch origin dev --tags + git tag -a "$tag" -m "Release candidate $tag" + git push origin "$tag" + + tag-main: + if: >- + github.event.pull_request.merged == true && + github.event.pull_request.base.ref == 'main' + runs-on: ubuntu-latest + environment: + name: release + + steps: + - name: Check out base branch + uses: actions/checkout@v7 + with: + ref: ${{ github.event.pull_request.merge_commit_sha || github.event.pull_request.base.ref }} + fetch-depth: 0 + + - name: Fetch tags + run: git fetch --force --tags origin + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install Commitizen + run: python -m pip install --upgrade pip commitizen tomlkit + + - name: Compute stable tag + id: version + run: | + tag="$(./scripts/next-tag.sh stable)" + python_version="$(./scripts/tag-to-python-version.sh "$tag")" + echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "python_version=$python_version" >> "$GITHUB_OUTPUT" + + - name: Update changelog and project version + run: | + tag="${{ steps.version.outputs.tag }}" + python_version="${{ steps.version.outputs.python_version }}" + + cz changelog --unreleased-version="$tag" + ./scripts/check-versions.sh --fix "$tag" + git add CHANGELOG.md pyproject.toml + + if git diff --cached --quiet; then + echo "No release file changes detected." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git commit -m "chore(release): ${python_version}" + git push origin HEAD:main + + - name: Create tag + run: | + tag="${{ steps.version.outputs.tag }}" + + if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then + echo "Tag $tag already exists, skipping." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git fetch origin main --tags + git tag -a "$tag" -m "Release $tag" + git push origin "$tag" diff --git a/.gitignore b/.gitignore index a7d59cc..3ffb499 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,8 @@ vcpkg/ !/scripts/build.sh.d !/scripts/style.sh.d + +# Python cache +__pycache__/ +.pytest_cache/ +dist/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f2dbbc3..1cc4a04 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,16 +19,17 @@ repos: - id: check-yaml - id: trailing-whitespace - # Optional: if project uses json - # - id: pretty-format-json - # args: ['--autofix'] - # - id: check-json - - repo: local hooks: - - id: check-style - name: check-style - entry: bash -c 'scripts/style.sh --fix' + - id: check_versions + name: check_versions + entry: bash -c 'scripts/check_versions.sh' + language: system + pass_filenames: false + always_run: true + - id: run_tests + name: Run tests + entry: bash -c 'poetry run pytest tests/ -v' language: system pass_filenames: false always_run: true diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml new file mode 100644 index 0000000..407fd83 --- /dev/null +++ b/.pre-commit-hooks.yaml @@ -0,0 +1,7 @@ +- id: splinter3d-style + name: splinter3d-style + description: Run the Splinter3D style checker. + entry: splinter3d_style + language: python + types: [python] + require_serial: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..aec3302 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,282 @@ +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. + +[[package]] +name = "build" +version = "1.5.0" +description = "A simple, correct Python build frontend" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "build-1.5.0-py3-none-any.whl", hash = "sha256:13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f"}, + {file = "build-1.5.0.tar.gz", hash = "sha256:302c22c3ba2a0fd5f3911918651341ebb3896176cbdec15bd421f80b1afc7647"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "os_name == \"nt\""} +importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""} +packaging = ">=24.0" +pyproject_hooks = "*" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[package.extras] +keyring = ["keyring"] +uv = ["uv (>=0.1.18)"] +virtualenv = ["virtualenv (>=20.17) ; python_version >= \"3.10\" and python_version < \"3.14\"", "virtualenv (>=20.31) ; python_version >= \"3.14\""] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "os_name == \"nt\" or sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version == \"3.10\"" +files = [ + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "importlib-metadata" +version = "9.0.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "python_full_version < \"3.10.2\"" +files = [ + {file = "importlib_metadata-9.0.0-py3-none-any.whl", hash = "sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7"}, + {file = "importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.14)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=3.4)"] +perf = ["ipython"] +test = ["packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy (>=1.0.1) ; platform_python_implementation != \"PyPy\""] + +[[package]] +name = "iniconfig" +version = "2.3.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, +] + +[[package]] +name = "packaging" +version = "26.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "pygments" +version = "2.20.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyproject-hooks" +version = "1.2.0" +description = "Wrappers to call pyproject.toml-based build backend hooks." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"}, + {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"}, +] + +[[package]] +name = "pytest" +version = "9.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"}, + {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1.0.1" +packaging = ">=22" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "tomli" +version = "2.4.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version == \"3.10\"" +files = [ + {file = "tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30"}, + {file = "tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a"}, + {file = "tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076"}, + {file = "tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9"}, + {file = "tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c"}, + {file = "tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc"}, + {file = "tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049"}, + {file = "tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e"}, + {file = "tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece"}, + {file = "tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a"}, + {file = "tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085"}, + {file = "tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9"}, + {file = "tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5"}, + {file = "tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585"}, + {file = "tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1"}, + {file = "tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917"}, + {file = "tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9"}, + {file = "tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257"}, + {file = "tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54"}, + {file = "tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a"}, + {file = "tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897"}, + {file = "tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f"}, + {file = "tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d"}, + {file = "tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5"}, + {file = "tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd"}, + {file = "tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36"}, + {file = "tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd"}, + {file = "tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf"}, + {file = "tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac"}, + {file = "tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662"}, + {file = "tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853"}, + {file = "tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15"}, + {file = "tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba"}, + {file = "tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6"}, + {file = "tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7"}, + {file = "tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232"}, + {file = "tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4"}, + {file = "tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c"}, + {file = "tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d"}, + {file = "tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41"}, + {file = "tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c"}, + {file = "tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f"}, + {file = "tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8"}, + {file = "tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26"}, + {file = "tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396"}, + {file = "tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe"}, + {file = "tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f"}, +] + +[[package]] +name = "tomlkit" +version = "0.15.0" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "tomlkit-0.15.0-py3-none-any.whl", hash = "sha256:4dbc8f0fc024412b57ced8757ac7461305126a648ff8c2c807fcb8e133a78738"}, + {file = "tomlkit-0.15.0.tar.gz", hash = "sha256:7d1a9ecba3086638211b13814ea79c90dd54dd11993564376f3aa92271f5c7a3"}, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version == \"3.10\"" +files = [ + {file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"}, + {file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"}, +] + +[[package]] +name = "zipp" +version = "4.1.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "python_full_version < \"3.10.2\"" +files = [ + {file = "zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f"}, + {file = "zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.14)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=3.4)"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy (>=1.0.1) ; platform_python_implementation != \"PyPy\""] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.10" +content-hash = "01cd39324095ca47cf177c667a3ff54973a65f534f4a6db8ededdd4638d483ef" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..733b350 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,33 @@ +[project] +name = "splinter3d_style" +version = "0.1.0" +description = "A style checker for Splinter3D projects." +readme = ".github/README.md" +requires-python = ">=3.10" +[[project.authors]] +name = "Mathieu BARLEON" +email = "mathieu.barleon@splinter3d.com" + +[project.scripts] +splinter3d_style = "splinter3d_style.main:main" + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" +[[tool.poetry.packages]] +include = "splinter3d_style" +from = "src" + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +pythonpath = ["src"] +addopts = "-v" + +[dependency-groups] +dev = [ + "build (>=1.3.0,<2.0.0)", + "commitizen (>=4.9.1,<5.0.0)", + "pytest (>=9.1.1,<10.0.0)", + "tomlkit (>=0.15.0,<0.16.0)" +] diff --git a/scripts/check-versions.sh b/scripts/check-versions.sh new file mode 100755 index 0000000..f601ac3 --- /dev/null +++ b/scripts/check-versions.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +set -euo pipefail + +FIX=false +TAG="" + +for arg in "$@"; do + case "$arg" in + -h | --help) + echo "Usage: $0 [--fix] [TAG]" + echo " --fix: Check versions and attempt to fix mismatches by aligning all version sources to the SCM version." + echo " TAG: Optional. If provided, uses this tag as the SCM version for comparison. Otherwise, uses the latest git tag." + exit 0 + ;; + --fix) + FIX=true + ;; + *) + TAG="$arg" + ;; + esac +done + +if ! command -v git &> /dev/null; then + echo "git is required but not found. Please install git and try again." + exit 1 +fi + +die() { echo "ERROR: $*" >&2; exit 1; } + +SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)" + +getSCMVersion() { + if [[ -n "$TAG" ]]; then + if [[ "$TAG" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then + TAG="v$TAG" + echo "Normalized tag to $TAG (prefixed 'v')" >&2 + fi + + if ! [[ "$TAG" =~ ^v[0-9]+(\.[0-9]+)*(-rc\.[0-9]+)?$ ]]; then + die "Invalid tag format: '$TAG'. Expected format like 'v1.2.3' or 'v1.2.3-rc.1'." + fi + "$SCRIPTS_DIR/tag-to-python-version.sh" "$TAG" + return + fi + + latest="$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n1)" + if [[ -z "$latest" ]]; then + latest="v0.0.0" + fi + "$SCRIPTS_DIR/tag-to-python-version.sh" "$latest" +} + +getPyProjectVersion() { + python3 -c " +import tomllib, sys +with open('pyproject.toml', 'rb') as f: + data = tomllib.load(f) +print(data['project']['version']) +" +} + +setPyProjectVersion() { + local new_version="$1" + python3 -c " +import tomlkit, sys +with open('pyproject.toml', 'r') as f: + doc = tomlkit.load(f) +doc['project']['version'] = '$new_version' +with open('pyproject.toml', 'w') as f: + tomlkit.dump(doc, f) +" +} + +SCM_V=$(getSCMVersion) +PYPROJECT_V=$(getPyProjectVersion) + +if [[ "$PYPROJECT_V" != "$SCM_V" ]]; then + echo "Version mismatch detected!" + echo " PyProject: $PYPROJECT_V" + echo " SCM: $SCM_V" + + if [[ "$FIX" == true ]]; then + echo "Attempting to fix version mismatches..." + setPyProjectVersion "$SCM_V" + + # Verify the fix + NEW_V=$(getPyProjectVersion) + if [[ "$NEW_V" != "$SCM_V" ]]; then + echo "Failed to fix version mismatches. Please check the files manually." + exit 1 + else + echo "All versions are now consistent with SCM version: $SCM_V" + exit 0 + fi + else + exit 1 + fi +fi + +echo "All versions are consistent: $SCM_V" +exit 0 diff --git a/scripts/next-tag.sh b/scripts/next-tag.sh new file mode 100755 index 0000000..7f9d886 --- /dev/null +++ b/scripts/next-tag.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -euo pipefail + +MODE="${1:-}" +DEFAULT_VERSION="${DEFAULT_VERSION:-0.1.0}" + +die() { + echo "ERROR: $*" >&2 + exit 1 +} + +command -v git >/dev/null 2>&1 || die "git not found" +command -v cz >/dev/null 2>&1 || die "cz not found" + +if [[ "$MODE" != "stable" && "$MODE" != "rc" ]]; then + die "Usage: $0 " +fi + +latest_stable_tag="$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-version:refname | head -n 1)" +base_version="$DEFAULT_VERSION" + +if [[ -n "$latest_stable_tag" ]]; then + while IFS= read -r rc_tag; do + [[ -n "$rc_tag" ]] || continue + git tag -d "$rc_tag" >/dev/null + done < <(git tag --list 'v*-rc.*') + + base_version="$(cz bump --get-next)" + + if git remote get-url origin >/dev/null 2>&1; then + git fetch --force --tags origin >/dev/null 2>&1 + fi +fi + +if [[ -z "$base_version" ]]; then + die "unable to determine next version" +fi + +if [[ "$MODE" == "stable" ]]; then + echo "v${base_version}" + exit 0 +fi + +latest_rc_number="$( + git tag --list "v${base_version}-rc.*" \ + | sed -E 's/.*-rc\.([0-9]+)$/\1/' \ + | sort -n \ + | tail -n 1 +)" + +if [[ -z "$latest_rc_number" ]]; then + latest_rc_number=0 +fi + +echo "v${base_version}-rc.$((latest_rc_number + 1))" diff --git a/scripts/release.sh b/scripts/release.sh deleted file mode 100755 index af8ad28..0000000 --- a/scripts/release.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# release.sh — Create changelog + tag + push using Commitizen (cz) -# -# Usage: -# ./release.sh # auto bump + changelog + tag + push -# ./release.sh 1.2.3 # manual tag mode -# ./release.sh --dry-run # simulate auto mode -# ./release.sh 1.2.3 --dry-run # simulate manual mode -# -# Notes: -# - If passing a manual tag, it must match your configured tag format. -# - --dry-run will NOT create commits, tags, or push. - -DRY_RUN=false -TAG="" - -for arg in "$@"; do - case "$arg" in - -h | --help) - echo "Usage: $0 [TAG] [--dry-run]" - echo " TAG: Optional. If provided, creates a release with this tag. Otherwise, auto-bumps." - echo " --dry-run: Simulate the release process without making any changes." - exit 0 - ;; - --dry-run) - DRY_RUN=true - ;; - *) - TAG="$arg" - ;; - esac -done - -die() { echo "ERROR: $*" >&2; exit 1; } - -run() { - if $DRY_RUN; then - echo "[DRY-RUN] $*" - else - "$@" - fi -} - -command -v git >/dev/null 2>&1 || die "git not found" -command -v cz >/dev/null 2>&1 || die "cz not found" - -git rev-parse --is-inside-work-tree >/dev/null 2>&1 || die "Not in git repo" - -if ! $DRY_RUN; then - if ! git diff --quiet || ! git diff --cached --quiet; then - die "Working tree not clean." - fi -fi - -REMOTE="${REMOTE:-origin}" -BRANCH="$(git rev-parse --abbrev-ref HEAD)" - -if [[ -z "$TAG" ]]; then - echo "==> Auto release mode" - - if $DRY_RUN; then - echo "[DRY-RUN] cz bump --changelog --yes" - cz bump --changelog --yes --dry-run || true - echo "[DRY-RUN] git push $REMOTE $BRANCH" - echo "[DRY-RUN] git push $REMOTE --tags" - else - cz bump --changelog --yes - git push "$REMOTE" "$BRANCH" - git push "$REMOTE" --tags - fi - - echo "==> Done" - exit 0 -fi - -if [[ "$TAG" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then - TAG="v$TAG" - echo "Normalized tag to $TAG (prefixed 'v')" -fi - -if ! [[ "$TAG" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then - die "Invalid tag format: '$TAG'. Expected format like 'v1.2.3'." -fi - -echo "==> Manual release for tag: $TAG" - -if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then - die "Tag '$TAG' already exists." -fi - -if $DRY_RUN; then - echo "[DRY-RUN] cz changelog --unreleased-version=\"$TAG\"" - cz changelog --unreleased-version="$TAG" --dry-run || true - echo "[DRY-RUN] git add CHANGELOG.md" - echo "[DRY-RUN] git commit -m \"chore(release): $TAG\"" - echo "[DRY-RUN] git tag -a \"$TAG\" -m \"Release $TAG\"" - echo "[DRY-RUN] git push $REMOTE $BRANCH" - echo "[DRY-RUN] git push $REMOTE \"$TAG\"" -else - cz changelog --unreleased-version="$TAG" - - if ! git diff --quiet -- "CHANGELOG.md"; then - git add CHANGELOG.md - git commit -m "chore(release): $TAG" - fi - - git tag -a "$TAG" -m "Release $TAG" - git push "$REMOTE" "$BRANCH" - git push "$REMOTE" "$TAG" -fi - -echo "==> Done" diff --git a/scripts/style.sh b/scripts/style.sh deleted file mode 100755 index 3a9b260..0000000 --- a/scripts/style.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -. "$SCRIPT_DIR/style.sh.d/run.sh" - -_run "$@" diff --git a/scripts/style.sh.d/run.sh b/scripts/style.sh.d/run.sh deleted file mode 100644 index dc15b29..0000000 --- a/scripts/style.sh.d/run.sh +++ /dev/null @@ -1,65 +0,0 @@ -set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")" - -function _print_helper () { - cat << EOF -USAGE: - $0 formats all C++ files in the project using clang-format - -ARGUMENTS: - $0 [-h|--help] displays this message - $0 [--check] check formatting without modifying files - $0 [--fix] format all files (default behavior) -EOF -} - -function _format_all () { - echo "Formatting all C++ files in the project..." - - if [ "${CHECK_MODE:-0}" -eq 1 ]; then - # Check mode: preview formatting without modifying - find "$PROJECT_ROOT/src" "$PROJECT_ROOT/include" \ - -type f \( -name "*.cpp" -o -name "*.hpp" \) \ - -exec clang-format --output-replacements-xml {} + > /dev/null && \ - echo "✓ All files are properly formatted" || \ - echo "✗ Some files need formatting" - else - # Fix mode: apply formatting - find "$PROJECT_ROOT/src" "$PROJECT_ROOT/include" \ - -type f \( -name "*.cpp" -o -name "*.hpp" \) \ - -exec clang-format -i {} + - echo "✓ Formatting complete" - fi -} - -function _run () { - if [ $# -eq 0 ]; then - _format_all - return 0 - fi - - for args in "$@"; do - case $args in - -h|--help) - _print_helper - exit 0 - ;; - --check) - CHECK_MODE=1 - _format_all - exit 0 - ;; - --fix) - CHECK_MODE=0 - _format_all - exit 0 - ;; - *) - echo "Unknown argument: $args" - _print_helper - exit 1 - ;; - esac - done -} diff --git a/scripts/tag-to-python-version.sh b/scripts/tag-to-python-version.sh new file mode 100755 index 0000000..f99056f --- /dev/null +++ b/scripts/tag-to-python-version.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +tag="${1:-}" + +if [[ -z "$tag" ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +if [[ "$tag" =~ ^v([0-9]+\.[0-9]+\.[0-9]+)-rc\.([0-9]+)$ ]]; then + echo "${BASH_REMATCH[1]}rc${BASH_REMATCH[2]}" + exit 0 +fi + +if [[ "$tag" =~ ^v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + echo "${BASH_REMATCH[1]}" + exit 0 +fi + +echo "Unsupported tag format: $tag" >&2 +exit 1 diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..6674291 --- /dev/null +++ b/src/main.py @@ -0,0 +1,4 @@ +from splinter3d_style.main import main + +if __name__ == "__main__": + exit(main()) diff --git a/src/splinter3d_style/__init__.py b/src/splinter3d_style/__init__.py new file mode 100644 index 0000000..3bcac08 --- /dev/null +++ b/src/splinter3d_style/__init__.py @@ -0,0 +1 @@ +"""splinter3d_style package.""" diff --git a/src/splinter3d_style/main.py b/src/splinter3d_style/main.py new file mode 100644 index 0000000..2bb4de9 --- /dev/null +++ b/src/splinter3d_style/main.py @@ -0,0 +1,3 @@ +def main() -> int: + print("All checks passed!") # True because there are no checks + return 0 diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 0000000..c8638c2 --- /dev/null +++ b/tests/test_main.py @@ -0,0 +1,5 @@ +from splinter3d_style.main import main + + +def test_main_returns_zero() -> None: + assert main() == 0