From 6eecdbdffc212f7a4aef8316823fa0a3a16d0d3c Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:12:27 +0000 Subject: [PATCH 01/14] Bump Python version to 3.12 in GitHub Actions workflow --- .github/workflows/deployaws.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deployaws.yml b/.github/workflows/deployaws.yml index 0d360fd..d80ad04 100644 --- a/.github/workflows/deployaws.yml +++ b/.github/workflows/deployaws.yml @@ -16,13 +16,13 @@ jobs: submodules: 'true' - uses: actions/setup-python@v3 with: - python-version: '3.9' + python-version: '3.12' - uses: aws-actions/setup-sam@v2 - uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-southeast-1 - - run: find ./ -name requirements.txt -type f -exec pip install -t dependencies/python -r "{}" \; + - run: pip install -t dependencies/python --upgrade --force-reinstall $(python -c "import toml; print(' '.join(dep.split('>=')[0] for dep in toml.load('pyproject.toml')['project']['dependencies']))") - run: sam build - run: sam deploy --parameter-overrides 'Password=${{ secrets.WEB_PASSWORD }}' From f3aadfc37d4b9c3a555ec62f2e86bbca41f027a1 Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:14:48 +0000 Subject: [PATCH 02/14] change mail --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index af342a9..8f4b596 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "taskbox" version = "0.1.0" description = "A personal scheduled task framework running on Serverless platform" authors = [ - {name = "jneeee", email = "your-email@example.com"}, + {name = "jneeee", email = "jneeee@outlook.com"}, ] readme = "README.md" requires-python = ">=3.9" From e55162b7ffd1d939090a8b7a52a3c21b4f7ee1ce Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:16:31 +0000 Subject: [PATCH 03/14] Simplify dependency installation using pip install -e . --- .github/workflows/deployaws.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deployaws.yml b/.github/workflows/deployaws.yml index d80ad04..c1b722c 100644 --- a/.github/workflows/deployaws.yml +++ b/.github/workflows/deployaws.yml @@ -23,6 +23,6 @@ jobs: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-southeast-1 - - run: pip install -t dependencies/python --upgrade --force-reinstall $(python -c "import toml; print(' '.join(dep.split('>=')[0] for dep in toml.load('pyproject.toml')['project']['dependencies']))") + - run: pip install -t dependencies/python -e . - run: sam build - run: sam deploy --parameter-overrides 'Password=${{ secrets.WEB_PASSWORD }}' From c46003c0dd3a48679c996dbacba103059c24e1b7 Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:22:50 +0000 Subject: [PATCH 04/14] Fix Lambda import error by setting PYTHONPATH --- template.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/template.yml b/template.yml index 464ab61..13d3dfc 100644 --- a/template.yml +++ b/template.yml @@ -34,6 +34,7 @@ Resources: auth_passwd: !Sub '${Password}' FUNC_ARN: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:taskbox' ROLE_ARN: !GetAtt excrole.Arn + PYTHONPATH: '/opt/python' # 如果日志出现缺少dynamodb get权限,在函数配置界面创个不用鉴权的函数url再试试 Events: ExplicitApi: From 301a3d93dc348e0cc509aade056ad858e48b7f13 Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:28:44 +0000 Subject: [PATCH 05/14] Add pyaes dependency to fix Runtime.ImportModuleError --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 8f4b596..941d91d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ dependencies = [ "Jinja2>=3.1.2", "boto3>=1.26.8", "botocore>=1.29.8", + "pyaes>=1.6.1", ] [tool.uv] From eb742eaab05b73a3461711ca4f8fb5e076db5bb3 Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:32:20 +0000 Subject: [PATCH 06/14] Revert "Fix Lambda import error by setting PYTHONPATH" This reverts commit c46003c0dd3a48679c996dbacba103059c24e1b7. --- template.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/template.yml b/template.yml index 13d3dfc..464ab61 100644 --- a/template.yml +++ b/template.yml @@ -34,7 +34,6 @@ Resources: auth_passwd: !Sub '${Password}' FUNC_ARN: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:taskbox' ROLE_ARN: !GetAtt excrole.Arn - PYTHONPATH: '/opt/python' # 如果日志出现缺少dynamodb get权限,在函数配置界面创个不用鉴权的函数url再试试 Events: ExplicitApi: From 35a57cb0556eef0e219107a232f00b81ae0d37d5 Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:38:46 +0000 Subject: [PATCH 07/14] Move unit tests from src/taskbox/tests to root tests directory --- pyproject.toml | 2 +- tests/test_manage.py | 28 ++++++++++++++ tests/test_object.py | 54 ++++++++++++++++++++++++++ tests/test_web.py | 92 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 tests/test_manage.py create mode 100644 tests/test_object.py create mode 100644 tests/test_web.py diff --git a/pyproject.toml b/pyproject.toml index 941d91d..bf512dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,4 +65,4 @@ skip_gitignore = true [tool.pytest.ini_options] pythonpath = ["src"] -testpaths = ["src/taskbox/tests"] +testpaths = ["tests"] diff --git a/tests/test_manage.py b/tests/test_manage.py new file mode 100644 index 0000000..3703c06 --- /dev/null +++ b/tests/test_manage.py @@ -0,0 +1,28 @@ +import os +import sys +import unittest +from unittest import mock + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) + +from taskbox.tests.fixture import create_table +from taskbox.taskbase.manage import TaskManager + +class Test_manage(unittest.TestCase): + + def setUp(self) -> None: + self.table = create_table() + d = { + 'DDB_TABLE': 'table_name', + 'AWS_ACCESS_KEY_ID': "keyid", + 'AWS_SECRET_ACCESS_KEY': "AWS_SECRET_ACCESS_KEY", + 'AWS_SESSION_TOKEN': "AWS_SESSION_TOKEN", + } + os.environ.update(d) + return super().setUp() + + def test_not_exsit_task(self): + self.assertRaises(FileNotFoundError, TaskManager, 'asdasd') + + def test_taskdemo(self): + TaskManager('Task_demo') diff --git a/tests/test_object.py b/tests/test_object.py new file mode 100644 index 0000000..c643afd --- /dev/null +++ b/tests/test_object.py @@ -0,0 +1,54 @@ +import os +import sys +import unittest +from unittest import mock + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) + +from taskbox.webx.object import Request + + +FAKE_EVENT = { + 'version': '2.0', 'routeKey': '$default', 'rawPath': '/cmd', + 'rawQueryString': '', + 'cookies': ['uuid=465e50e8-f344-4009-af2b-c4eaafa7d9e8'], + 'headers': { + 'accept': 'text/html', 'accept-encoding': 'gzip, deflate, br', + 'cache-control': 'max-age=0', 'content-length': '86', + 'content-type': 'application/x-www-form-urlencoded', + 'host': 'demo.taskbox.cn', 'origin': 'https://demo.taskbox.cn', + 'referer': 'https://demo.taskbox.cn/cmd', + 'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108","Microsoft Edge";v="108"', + 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'document', 'sec-fetch-mode': 'navigate', + 'sec-fetch-site': 'same-origin', 'sec-fetch-user': '?1', + 'upgrade-insecure-requests': '1', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54', + 'x-amzn-trace-id': 'Root=1-63a65ac0-232f5asd387d5df61d75fa7a', + 'x-forwarded-for': '58.247.232.192', 'x-forwarded-port': '443', + 'x-forwarded-proto': 'https'}, + 'requestContext': { + 'accountId': '123456789','apiId': 'csm5rlhe6d', + 'domainName': 'demo.taskbox.cn', 'domainPrefix': 'demo', + 'http': { + 'method': 'POST', 'path': '/cmd', 'protocol': 'HTTP/1.1', + 'sourceIp': '58.247.232.192', + 'userAgent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54' + }, + 'requestId': 'doMeCjIXSQ0EPNg=', 'routeKey': '$default', + 'stage': '$default', 'time': '24/Dec/2022:01:49:52 +0000', + 'timeEpoch': 1671846592036 + }, + 'body': 'cHl0aG9uPXJlcXVlc3RzLmdldCUyOCUyMmh0dHBzJTNBJTJGJTJGd3d3LmJhaWR1LmNvbSUyRnNlYXJjaCUzRnElM0RvcmFjbGUlMjIlMjkudGV4dA==', + 'isBase64Encoded': True +} + +class test_request(unittest.TestCase): + + @mock.patch('taskbox.webx.object._check_authid_is_valid') + def test_parse_body(self, mock_auth): + req = Request(FAKE_EVENT, {}) + self.assertEqual( + 'requests.get("https://www.baidu.com/search?q=oracle").text', + req.body.get('python') + ) diff --git a/tests/test_web.py b/tests/test_web.py new file mode 100644 index 0000000..85aae66 --- /dev/null +++ b/tests/test_web.py @@ -0,0 +1,92 @@ +import unittest +from unittest import mock +import copy + +from moto import mock_dynamodb + +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) + +from taskbox.index import lambda_handler +from taskbox.utils.tools import LOG +from taskbox.tests.fixture import create_table + + +Fake_event = { + "requestContext": { + "http": { + "method": "GET", + "path": "/", + "protocol": "HTTP/1.1", + "sourceIp": "112.64.93.19", + "userAgent": "Mozilla/5.0" + }, + }, + 'body': b'aWQ9VGFza190ZXN0', +} + +Fake_context = {} + + +@mock_dynamodb +class Test_web_tasks(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.table = create_table() + + @classmethod + def tearDownClass(cls): + # cls.table.delete() + pass + + def setUp(self): + pass + + def test_get_tasks(self): + resp = lambda_handler(Fake_event, Fake_context) + self.assertIn('测试任务', resp.get('body')) + + def test_quary_single_task(self): + tmp_event = copy.deepcopy(Fake_event) + tmp_event['requestContext']['http']['path'] = '/task/Task_demo' + resp = lambda_handler(tmp_event, Fake_context) + self.assertIn('Task_demo', resp.get('body')) + + def test_run_cmd(self): + tmp_event = copy.deepcopy(Fake_event) + tmp_event['headers'] = {'cmd': 'ls'} + tmp_event['requestContext']['http']['path'] = '/cmd' + + def test_db_query(self): + tmp_event = copy.deepcopy(Fake_event) + tmp_event['requestContext']['http'].update( + {'path': '/db', 'method': 'POST'} + ) + resp = lambda_handler(tmp_event, Fake_context) + self.assertIn('Quary db', resp.get('body', None)) + + def test_db_putitem(self): + tmp_event = copy.deepcopy(Fake_event) + tmp_event['requestContext']['http'].update( + {'path': '/db', 'method': 'POST'} + ) + resp = lambda_handler(tmp_event, Fake_context) + self.assertIn('Quary db', resp.get('body')) + + def test_auth_login_get(self): + tmp_event = copy.deepcopy(Fake_event) + tmp_event['requestContext']['http'].update( + {'path': '/auth/login', 'method': 'GET'} + ) + resp = lambda_handler(tmp_event, Fake_context) + self.assertIn('会话会在一天后过期', resp.get('body')) + + def test_auth_login_post(self): + tmp_event = copy.deepcopy(Fake_event) + tmp_event['requestContext']['http'].update( + {'path': '/auth/login', 'method': 'POST'} + ) + resp = lambda_handler(tmp_event, Fake_context) + self.assertIn('登录失败', resp.get('body')) From 4b33399d17a785d37a7acd86daca62d11d0e5c40 Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:39:58 +0000 Subject: [PATCH 08/14] Add GitHub Actions workflow to run unit tests on PRs and merges --- .github/workflows/run_tests.yml | 50 +++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/run_tests.yml diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml new file mode 100644 index 0000000..42ed566 --- /dev/null +++ b/.github/workflows/run_tests.yml @@ -0,0 +1,50 @@ +name: Run Unit Tests + +on: + pull_request: + branches: [ dev, master ] + push: + branches: [ dev, master ] + paths-ignore: + - 'docs/**' + - '**.md' + +jobs: + test: + name: Run Python Tests + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + submodules: 'true' + + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.12' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + pip install pytest moto + + - name: Initialize submodules + run: git submodule update --init --recursive + + - name: Run tests + run: pytest tests/ -v + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: us-east-1 + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-results + path: | + test-results/ + .pytest_cache/ From 0cf3aa9965b660cec0e774b8ba573afb44ead1d8 Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:41:17 +0000 Subject: [PATCH 09/14] move ut out of src --- src/taskbox/tests/test_manage.py | 25 --------- src/taskbox/tests/test_object.py | 50 ------------------ src/taskbox/tests/test_web.py | 88 -------------------------------- 3 files changed, 163 deletions(-) delete mode 100644 src/taskbox/tests/test_manage.py delete mode 100644 src/taskbox/tests/test_object.py delete mode 100644 src/taskbox/tests/test_web.py diff --git a/src/taskbox/tests/test_manage.py b/src/taskbox/tests/test_manage.py deleted file mode 100644 index 2f3f2ba..0000000 --- a/src/taskbox/tests/test_manage.py +++ /dev/null @@ -1,25 +0,0 @@ -import os -import unittest -from unittest import mock - -from taskbox.tests import fixture -from taskbox.taskbase.manage import TaskManager - -class Test_manage(unittest.TestCase): - - def setUp(self) -> None: - self.table = fixture.create_table() - d = { - 'DDB_TABLE': 'table_name', - 'AWS_ACCESS_KEY_ID': "keyid", - 'AWS_SECRET_ACCESS_KEY': "AWS_SECRET_ACCESS_KEY", - 'AWS_SESSION_TOKEN': "AWS_SESSION_TOKEN", - } - os.environ.update(d) - return super().setUp() - - def test_not_exsit_task(self): - self.assertRaises(FileNotFoundError, TaskManager, 'asdasd') - - def test_taskdemo(self): - TaskManager('Task_demo') diff --git a/src/taskbox/tests/test_object.py b/src/taskbox/tests/test_object.py deleted file mode 100644 index 349032e..0000000 --- a/src/taskbox/tests/test_object.py +++ /dev/null @@ -1,50 +0,0 @@ -import unittest -from unittest import mock - -from taskbox.webx.object import Request - - -FAKE_EVENT = { - 'version': '2.0', 'routeKey': '$default', 'rawPath': '/cmd', - 'rawQueryString': '', - 'cookies': ['uuid=465e50e8-f344-4009-af2b-c4eaafa7d9e8'], - 'headers': { - 'accept': 'text/html', 'accept-encoding': 'gzip, deflate, br', - 'cache-control': 'max-age=0', 'content-length': '86', - 'content-type': 'application/x-www-form-urlencoded', - 'host': 'demo.taskbox.cn', 'origin': 'https://demo.taskbox.cn', - 'referer': 'https://demo.taskbox.cn/cmd', - 'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108","Microsoft Edge";v="108"', - 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', - 'sec-fetch-dest': 'document', 'sec-fetch-mode': 'navigate', - 'sec-fetch-site': 'same-origin', 'sec-fetch-user': '?1', - 'upgrade-insecure-requests': '1', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54', - 'x-amzn-trace-id': 'Root=1-63a65ac0-232f5asd387d5df61d75fa7a', - 'x-forwarded-for': '58.247.232.192', 'x-forwarded-port': '443', - 'x-forwarded-proto': 'https'}, - 'requestContext': { - 'accountId': '123456789','apiId': 'csm5rlhe6d', - 'domainName': 'demo.taskbox.cn', 'domainPrefix': 'demo', - 'http': { - 'method': 'POST', 'path': '/cmd', 'protocol': 'HTTP/1.1', - 'sourceIp': '58.247.232.192', - 'userAgent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54' - }, - 'requestId': 'doMeCjIXSQ0EPNg=', 'routeKey': '$default', - 'stage': '$default', 'time': '24/Dec/2022:01:49:52 +0000', - 'timeEpoch': 1671846592036 - }, - 'body': 'cHl0aG9uPXJlcXVlc3RzLmdldCUyOCUyMmh0dHBzJTNBJTJGJTJGd3d3LmJhaWR1LmNvbSUyRnNlYXJjaCUzRnElM0RvcmFjbGUlMjIlMjkudGV4dA==', - 'isBase64Encoded': True -} - -class test_request(unittest.TestCase): - - @mock.patch('taskbox.webx.object._check_authid_is_valid') - def test_parse_body(self, mock_auth): - req = Request(FAKE_EVENT, {}) - self.assertEqual( - 'requests.get("https://www.baidu.com/search?q=oracle").text', - req.body.get('python') - ) diff --git a/src/taskbox/tests/test_web.py b/src/taskbox/tests/test_web.py deleted file mode 100644 index e35ec66..0000000 --- a/src/taskbox/tests/test_web.py +++ /dev/null @@ -1,88 +0,0 @@ -import unittest -from unittest import mock -import copy - -from moto import mock_dynamodb - -from taskbox.index import lambda_handler -from taskbox.utils.tools import LOG -from taskbox.tests import fixture - - -Fake_event = { - "requestContext": { - "http": { - "method": "GET", - "path": "/", - "protocol": "HTTP/1.1", - "sourceIp": "112.64.93.19", - "userAgent": "Mozilla/5.0" - }, - }, - 'body': b'aWQ9VGFza190ZXN0', -} - -Fake_context = {} - - -@mock_dynamodb -class Test_web_tasks(unittest.TestCase): - - @classmethod - def setUpClass(cls): - cls.table = fixture.create_table() - - @classmethod - def tearDownClass(cls): - # cls.table.delete() - pass - - def setUp(self): - pass - - def test_get_tasks(self): - resp = lambda_handler(Fake_event, Fake_context) - self.assertIn('测试任务', resp.get('body')) - - def test_quary_single_task(self): - tmp_event = copy.deepcopy(Fake_event) - tmp_event['requestContext']['http']['path'] = '/task/Task_demo' - resp = lambda_handler(tmp_event, Fake_context) - self.assertIn('Task_demo', resp.get('body')) - - def test_run_cmd(self): - tmp_event = copy.deepcopy(Fake_event) - tmp_event['headers'] = {'cmd': 'ls'} - tmp_event['requestContext']['http']['path'] = '/cmd' - - def test_db_query(self): - tmp_event = copy.deepcopy(Fake_event) - tmp_event['requestContext']['http'].update( - {'path': '/db', 'method': 'POST'} - ) - resp = lambda_handler(tmp_event, Fake_context) - self.assertIn('Quary db', resp.get('body', None)) - - def test_db_putitem(self): - tmp_event = copy.deepcopy(Fake_event) - tmp_event['requestContext']['http'].update( - {'path': '/db', 'method': 'POST'} - ) - resp = lambda_handler(tmp_event, Fake_context) - self.assertIn('Quary db', resp.get('body')) - - def test_auth_login_get(self): - tmp_event = copy.deepcopy(Fake_event) - tmp_event['requestContext']['http'].update( - {'path': '/auth/login', 'method': 'GET'} - ) - resp = lambda_handler(tmp_event, Fake_context) - self.assertIn('会话会在一天后过期', resp.get('body')) - - def test_auth_login_post(self): - tmp_event = copy.deepcopy(Fake_event) - tmp_event['requestContext']['http'].update( - {'path': '/auth/login', 'method': 'POST'} - ) - resp = lambda_handler(tmp_event, Fake_context) - self.assertIn('登录失败', resp.get('body')) From 57f778eaea18676eaad0e9ea28fcaec42d5e35f9 Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:45:03 +0000 Subject: [PATCH 10/14] Fix deprecated upload-artifact and use Astral's uv Docker image --- .github/workflows/run_tests.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 42ed566..0730725 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -13,22 +13,25 @@ jobs: test: name: Run Python Tests runs-on: ubuntu-latest + container: + image: ghcr.io/astral-sh/uv:latest + options: --user root steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: 'true' - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: '3.12' - - name: Install dependencies + - name: Install dependencies with uv run: | - python -m pip install --upgrade pip - pip install -e . - pip install pytest moto + uv pip install --upgrade pip + uv pip install -e . + uv pip install pytest moto - name: Initialize submodules run: git submodule update --init --recursive @@ -42,7 +45,7 @@ jobs: - name: Upload test results if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-results path: | From 934f451f92abe3008fce002fdb8d4aae18efa3d5 Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:47:49 +0000 Subject: [PATCH 11/14] fix --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 0730725..17c29fd 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -14,7 +14,7 @@ jobs: name: Run Python Tests runs-on: ubuntu-latest container: - image: ghcr.io/astral-sh/uv:latest + image: ghcr.io/astral-sh/uv:python3.12-bookworm options: --user root steps: From 4b3146d899427e1cd6c770f2f67fffbf6744f58f Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:50:34 +0000 Subject: [PATCH 12/14] Fix uv installation by removing container and installing uv directly on runner --- .github/workflows/run_tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 17c29fd..20b2bf8 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -13,9 +13,6 @@ jobs: test: name: Run Python Tests runs-on: ubuntu-latest - container: - image: ghcr.io/astral-sh/uv:python3.12-bookworm - options: --user root steps: - uses: actions/checkout@v4 @@ -27,6 +24,9 @@ jobs: with: python-version: '3.12' + - name: Install uv + run: curl -LsSf https://astral.sh/uv/install.sh | sh + - name: Install dependencies with uv run: | uv pip install --upgrade pip From 8e0d080497a621b663b0f917889d82cb136f86e5 Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:52:35 +0000 Subject: [PATCH 13/14] Use Astral uv container image with proper configuration --- .github/workflows/run_tests.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 20b2bf8..bacbaeb 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -13,20 +13,17 @@ jobs: test: name: Run Python Tests runs-on: ubuntu-latest + container: + image: ghcr.io/astral-sh/uv:python3.12-bookworm + options: --user root + env: + UV_INDEX_URL: https://pypi.org/simple steps: - uses: actions/checkout@v4 with: submodules: 'true' - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install uv - run: curl -LsSf https://astral.sh/uv/install.sh | sh - - name: Install dependencies with uv run: | uv pip install --upgrade pip From f5063c02549be0f81b686a148b7c4d10cd11837b Mon Sep 17 00:00:00 2001 From: jneeee Date: Sun, 28 Dec 2025 12:57:36 +0000 Subject: [PATCH 14/14] Use uv sync in CI and replace deprecated tool.uv.dev-dependencies with dependency-groups.dev --- .github/workflows/run_tests.yml | 5 ++--- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index bacbaeb..2fa85b1 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -24,10 +24,9 @@ jobs: with: submodules: 'true' - - name: Install dependencies with uv + - name: Install dependencies with uv sync run: | - uv pip install --upgrade pip - uv pip install -e . + uv sync --python-preference only uv pip install pytest moto - name: Initialize submodules diff --git a/pyproject.toml b/pyproject.toml index bf512dd..a17ad55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ dependencies = [ ] [tool.uv] -dev-dependencies = [ +dependency-groups.dev = [ "pytest>=7.0.0", "black>=23.0.0", "isort>=5.0.0",