diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9e8ea8c395..f9128e7ca8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.
-name: Flink Agents
+name: Flink Agents CI
on:
push:
@@ -32,3 +32,14 @@ jobs:
- uses: actions/checkout@v4
- name: Check License Header
uses: apache/skywalking-eyes/header@main
+ - name: Set up JDK
+ uses: actions/setup-java@v4
+ with:
+ java-version: '8'
+ distribution: 'adopt'
+ - name: Set up Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.12
+ - name: Check code style
+ run: ./tools/lint.sh
diff --git a/pom.xml b/pom.xml
index b56899c1ef..ff8578a033 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,8 @@ under the License.
11
${target.java.version}
${target.java.version}
+ 2.27.1
+ false
@@ -48,4 +50,39 @@ under the License.
runtime
+
+
+
+ com.diffplug.spotless
+ spotless-maven-plugin
+ ${spotless.version}
+
+ ${spotless.skip}
+
+
+ 1.7
+
+
+
+
+
+ ,javax,java,scala,\#
+
+
+
+
+
+
+
+ spotless-check
+ validate
+
+ check
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/flink_agents/runtime/__init__.py b/python/flink_agents/runtime/__init__.py
index 24b39618b1..e154fadd32 100644
--- a/python/flink_agents/runtime/__init__.py
+++ b/python/flink_agents/runtime/__init__.py
@@ -14,4 +14,4 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#################################################################################
\ No newline at end of file
+#################################################################################
diff --git a/python/pyproject.toml b/python/pyproject.toml
index ace9ef7e5e..3f561fdf2b 100644
--- a/python/pyproject.toml
+++ b/python/pyproject.toml
@@ -55,3 +55,80 @@ archs = ["x86_64", "arm64"]
[tool.cibuildwheel.linux]
archs = ["x86_64"]
+
+[tool.ruff]
+line-length = 88
+fix = true
+
+[tool.ruff.lint]
+select = [
+ "ANN", # flake8-annotations
+ "B", # flake8-bugbear
+ "C4", # flake8-comprehensions
+ "D", # flake8-docstrings
+ "E", # pycodestyle
+ "EM", # flake8-errmsg
+ "F", # pyflakes
+ "FA", # flake8-future-annotations
+ "FBT001", # flake8-boolean-trap
+ "I", # isort
+ "ICN", # flake8-import-conventions
+ "INT", # flake8-gettext
+ "PERF", # perflint
+ "PIE", # flake8-pie
+ "PT", # flake8-pytest-style
+ "PTH", # flake8-use-pathlib
+ "PYI", # flake8-pyi
+ "RUF", # ruff-specific rules
+ "SIM", # flake8-simplify
+ "TCH", # flake8-type-checking
+ "TD", # flake8-todos
+ "TID", # flake8-tidy-imports
+ "TRY", # tryceratops
+ "UP", # pyupgrade
+ "W", # pycodestyle
+]
+
+ignore = [
+ "D100", # Missing docstring in public module
+ "D104", # Missing docstring in public package
+ "D105", # Missing docstring in magic method
+ "D205", # Missing-blank-line-after-summary
+ # ------------------------------------------------------------------
+ "ANN401", # Dynamically typed expressions (Any) are disallowed
+ "D401", # Relax NumPy docstring convention: first line should be imperative
+ "E501", # Line length regulated by formatter
+ "FA100", # Future-rewritable-type-annotation, unsafe for Pydantic
+ "PT011", # pytest.raises is too broad, set match or use a more specific exception
+ "PYI041", # Use float instead of int | float
+ "RUF022", # `__all__` is not sorted
+ "RUF005", # Consider expression instead of concatenation
+ "SIM102", # Use a single `if` statement instead of nested `if` statements
+ "SIM108", # Use ternary operator
+ "SIM114", # Combine `if` branches
+ "TD002", # Missing author in TODO
+ "TD003", # Missing issue link on the line following this TODO
+ "TID252", # Prefer absolute imports over relative imports
+ "TRY003", # Avoid specifying long messages outside the exception class
+ "UP006", # Non-pep585-annotation, unsafe for Pydantic
+ "UP035", # Deprecated-import, unsafe for Pydantic
+]
+
+[tool.ruff.lint.per-file-ignores]
+"dependencies.py" = ["ICN001"]
+"tests/**/*.py" = ["ANN001", "ANN201", "D100", "D102", "D103", "B018", "FBT001"]
+
+[tool.ruff.lint.pycodestyle]
+max-doc-length = 88
+
+[tool.ruff.lint.pydocstyle]
+convention = "google"
+
+[tool.ruff.lint.flake8-tidy-imports]
+ban-relative-imports = "all"
+
+[tool.ruff.lint.flake8-type-checking]
+strict = true
+
+[tool.ruff.format]
+docstring-code-format = true
diff --git a/python/requirements/all_requirements.txt b/python/requirements/all_requirements.txt
new file mode 100644
index 0000000000..3ea6b6667f
--- /dev/null
+++ b/python/requirements/all_requirements.txt
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+-r build_requirements.txt
+-r linter_requirements.txt
+-r test_requirements.txt
\ No newline at end of file
diff --git a/python/requirements/build_requirements.txt b/python/requirements/build_requirements.txt
new file mode 100644
index 0000000000..82b0c5c2c9
--- /dev/null
+++ b/python/requirements/build_requirements.txt
@@ -0,0 +1,15 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+build
diff --git a/python/requirements/linter_requirements.txt b/python/requirements/linter_requirements.txt
new file mode 100644
index 0000000000..f735997e44
--- /dev/null
+++ b/python/requirements/linter_requirements.txt
@@ -0,0 +1,15 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+ruff==0.11.13
diff --git a/python/requirements/test_requirements.txt b/python/requirements/test_requirements.txt
new file mode 100644
index 0000000000..12f570f999
--- /dev/null
+++ b/python/requirements/test_requirements.txt
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+wheel
+setuptools>=75.3
+pytest==8.4.0
diff --git a/tools/lint.sh b/tools/lint.sh
new file mode 100755
index 0000000000..911d1a4838
--- /dev/null
+++ b/tools/lint.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+################################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+################################################################################
+
+echo "Install format tools"
+pip install -r python/requirements/linter_requirements.txt
+echo "Executing Python format check"
+ruff check python
+echo "Executing Java format check"
+mvn -T10 -B --no-transfer-progress spotless:check
+echo "Executing format check succeeds"